name = "clippy_dev"
version = "0.0.1"
dependencies = [
+ "aho-corasick",
"clap 2.34.0",
"indoc",
"itertools",
[[package]]
name = "libc"
-version = "0.2.121"
+version = "0.2.125"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "efaa7b300f3b5fe8eb6bf21ce3895e1751d9665086af2d64b42f19701015ff4f"
+checksum = "5916d2ae698f6de9bfb891ad7a8d65c09d232dc58cc4ac433c7da3b2fd84bc2b"
dependencies = [
"rustc-std-workspace-core",
]
"rustc_attr",
"rustc_data_structures",
"rustc_errors",
+ "rustc_expand",
"rustc_feature",
"rustc_hir",
"rustc_index",
Brace,
/// `[ ... ]`
Bracket,
- /// `Ø ... Ø`
+ /// `/*«*/ ... /*»*/`
/// An invisible delimiter, that may, for example, appear around tokens coming from a
/// "macro variable" `$var`. It is important to preserve operator priorities in cases like
/// `$var * 3` where `$var` is `1 + 2`.
- /// Invisible delimiters might not survive roundtrip of a token stream through a string.
+ /// Invisible delimiters are not directly writable in normal Rust code except as comments.
+ /// Therefore, they might not survive a roundtrip of a token stream through a string.
Invisible,
}
self.nbsp();
}
self.word("{");
- if !tts.is_empty() {
+ let empty = tts.is_empty();
+ if !empty {
self.space();
}
self.ibox(0);
self.print_tts(tts, convert_dollar_crate);
self.end();
- let empty = tts.is_empty();
self.bclose(span, empty);
}
+ Some(Delimiter::Invisible) => {
+ self.word("/*«*/");
+ let empty = tts.is_empty();
+ if !empty {
+ self.space();
+ }
+ self.ibox(0);
+ self.print_tts(tts, convert_dollar_crate);
+ self.end();
+ if !empty {
+ self.space();
+ }
+ self.word("/*»*/");
+ }
Some(delim) => {
let token_str = self.token_kind_to_string(&token::OpenDelim(delim));
self.word(token_str);
token::CloseDelim(Delimiter::Bracket) => "]".into(),
token::OpenDelim(Delimiter::Brace) => "{".into(),
token::CloseDelim(Delimiter::Brace) => "}".into(),
- token::OpenDelim(Delimiter::Invisible) | token::CloseDelim(Delimiter::Invisible) => {
- "".into()
- }
+ token::OpenDelim(Delimiter::Invisible) => "/*«*/".into(),
+ token::CloseDelim(Delimiter::Invisible) => "/*»*/".into(),
token::Pound => "#".into(),
token::Dollar => "$".into(),
token::Question => "?".into(),
crate fn get_index_of(&self, location: &Location) -> Option<BorrowIndex> {
self.location_map.get_index_of(location).map(BorrowIndex::from)
}
-
- crate fn contains(&self, location: &Location) -> bool {
- self.location_map.contains_key(location)
- }
}
struct GatherBorrows<'a, 'tcx> {
use rustc_middle::mir::{InlineAsmOperand, Terminator, TerminatorKind};
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::{self, CapturedPlace, ParamEnv, RegionVid, TyCtxt};
-use rustc_session::lint::builtin::{MUTABLE_BORROW_RESERVATION_CONFLICT, UNUSED_MUT};
-use rustc_span::{Span, Symbol, DUMMY_SP};
+use rustc_session::lint::builtin::UNUSED_MUT;
+use rustc_span::{Span, Symbol};
use either::Either;
use smallvec::SmallVec;
use std::cell::RefCell;
use std::collections::BTreeMap;
-use std::mem;
use std::rc::Rc;
use rustc_mir_dataflow::impls::{
locals_are_invalidated_at_exit,
access_place_error_reported: Default::default(),
reservation_error_reported: Default::default(),
- reservation_warnings: Default::default(),
uninitialized_error_reported: Default::default(),
regioncx: regioncx.clone(),
used_mut: Default::default(),
fn_self_span_reported: Default::default(),
access_place_error_reported: Default::default(),
reservation_error_reported: Default::default(),
- reservation_warnings: Default::default(),
uninitialized_error_reported: Default::default(),
regioncx: Rc::clone(®ioncx),
used_mut: Default::default(),
&mut mbcx,
);
- // Convert any reservation warnings into lints.
- let reservation_warnings = mem::take(&mut mbcx.reservation_warnings);
- for (_, (place, span, location, bk, borrow)) in reservation_warnings {
- let initial_diag = mbcx.report_conflicting_borrow(location, (place, span), bk, &borrow);
-
- let scope = mbcx.body.source_info(location).scope;
- let lint_root = match &mbcx.body.source_scopes[scope].local_data {
- ClearCrossCrate::Set(data) => data.lint_root,
- _ => tcx.hir().local_def_id_to_hir_id(def.did),
- };
-
- // Span and message don't matter; we overwrite them below anyway
- mbcx.infcx.tcx.struct_span_lint_hir(
- MUTABLE_BORROW_RESERVATION_CONFLICT,
- lint_root,
- DUMMY_SP,
- |lint| {
- let mut diag = lint.build("");
-
- diag.message = initial_diag.styled_message().clone();
- diag.span = initial_diag.span.clone();
-
- mbcx.buffer_non_error_diag(diag);
- },
- );
- initial_diag.cancel();
- }
-
// For each non-user used mutable variable, check if it's been assigned from
// a user-declared local. If so, then put that local into the used_mut set.
// Note that this set is expected to be small - only upvars from closures
/// used to report extra information for `FnSelfUse`, to avoid
/// unnecessarily verbose errors.
fn_self_span_reported: FxHashSet<Span>,
- /// Migration warnings to be reported for #56254. We delay reporting these
- /// so that we can suppress the warning if there's a corresponding error
- /// for the activation of the borrow.
- reservation_warnings:
- FxHashMap<BorrowIndex, (Place<'tcx>, Span, Location, BorrowKind, BorrowData<'tcx>)>,
/// This field keeps track of errors reported in the checking of uninitialized variables,
/// so that we don't report seemingly duplicate errors.
uninitialized_error_reported: FxHashSet<PlaceRef<'tcx>>,
let conflict_error =
self.check_access_for_conflict(location, place_span, sd, rw, flow_state);
- if let (Activation(_, borrow_idx), true) = (kind.1, conflict_error) {
- // Suppress this warning when there's an error being emitted for the
- // same borrow: fixing the error is likely to fix the warning.
- self.reservation_warnings.remove(&borrow_idx);
- }
-
if conflict_error || mutability_error {
debug!("access_place: logging error place_span=`{:?}` kind=`{:?}`", place_span, kind);
self.access_place_error_reported.insert((place_span.0, place_span.1));
BorrowKind::Unique | BorrowKind::Mut { .. },
) => Control::Continue,
+ (Reservation(_), BorrowKind::Shallow | BorrowKind::Shared) => {
+ // This used to be a future compatibility warning (to be
+ // disallowed on NLL). See rust-lang/rust#56254
+ Control::Continue
+ }
+
(Write(WriteKind::Move), BorrowKind::Shallow) => {
// Handled by initialization checks.
Control::Continue
Control::Break
}
- (
- Reservation(WriteKind::MutableBorrow(bk)),
- BorrowKind::Shallow | BorrowKind::Shared,
- ) if { tcx.migrate_borrowck() && this.borrow_set.contains(&location) } => {
- let bi = this.borrow_set.get_index_of(&location).unwrap();
- debug!(
- "recording invalid reservation of place: {:?} with \
- borrow index {:?} as warning",
- place_span.0, bi,
- );
- // rust-lang/rust#56254 - This was previously permitted on
- // the 2018 edition so we emit it as a warning. We buffer
- // these separately so that we only emit a warning if borrow
- // checking was otherwise successful.
- this.reservation_warnings
- .insert(bi, (place_span.0, place_span.1, location, bk, borrow.clone()));
-
- // Don't suppress actual errors.
- Control::Continue
- }
-
(Reservation(kind) | Activation(kind, _) | Write(kind), _) => {
match rw {
Reservation(..) => {
use rustc_ast::token;
use rustc_ast::tokenstream::TokenStream;
use rustc_ast_pretty::pprust;
-use rustc_errors::PResult;
use rustc_expand::base::{self, *};
use rustc_expand::module::DirOwnership;
use rustc_parse::parser::{ForceCollect, Parser};
use rustc_parse::{self, new_parser_from_file};
use rustc_session::lint::builtin::INCOMPLETE_INCLUDE;
use rustc_span::symbol::Symbol;
-use rustc_span::{self, FileName, Pos, Span};
+use rustc_span::{self, Pos, Span};
use smallvec::SmallVec;
-use std::path::PathBuf;
use std::rc::Rc;
// These macros all relate to the file system; they either return
return DummyResult::any(sp);
};
// The file will be added to the code map by the parser
- let file = match resolve_path(cx, file.as_str(), sp) {
+ let file = match resolve_path(&cx.sess.parse_sess, file.as_str(), sp) {
Ok(f) => f,
Err(mut err) => {
err.emit();
let Some(file) = get_single_str_from_tts(cx, sp, tts, "include_str!") else {
return DummyResult::any(sp);
};
- let file = match resolve_path(cx, file.as_str(), sp) {
+ let file = match resolve_path(&cx.sess.parse_sess, file.as_str(), sp) {
Ok(f) => f,
Err(mut err) => {
err.emit();
let Some(file) = get_single_str_from_tts(cx, sp, tts, "include_bytes!") else {
return DummyResult::any(sp);
};
- let file = match resolve_path(cx, file.as_str(), sp) {
+ let file = match resolve_path(&cx.sess.parse_sess, file.as_str(), sp) {
Ok(f) => f,
Err(mut err) => {
err.emit();
}
}
}
-
-/// Resolves a `path` mentioned inside Rust code, returning an absolute path.
-///
-/// This unifies the logic used for resolving `include_X!`.
-fn resolve_path<'a>(
- cx: &mut ExtCtxt<'a>,
- path: impl Into<PathBuf>,
- span: Span,
-) -> PResult<'a, PathBuf> {
- let path = path.into();
-
- // Relative paths are resolved relative to the file in which they are found
- // after macro expansion (that is, they are unhygienic).
- if !path.is_absolute() {
- let callsite = span.source_callsite();
- let mut result = match cx.source_map().span_to_filename(callsite) {
- FileName::Real(name) => name
- .into_local_path()
- .expect("attempting to resolve a file path in an external file"),
- FileName::DocTest(path, _) => path,
- other => {
- return Err(cx.struct_span_err(
- span,
- &format!(
- "cannot resolve relative path in non-file source `{}`",
- cx.source_map().filename_for_diagnostics(&other)
- ),
- ));
- }
- };
- result.pop();
- result.push(path);
- Ok(result)
- } else {
- Ok(path)
- }
-}
use rustc_data_structures::temp_dir::MaybeTempDir;
use rustc_errors::{ErrorGuaranteed, Handler};
use rustc_fs_util::fix_windows_verbatim_for_gcc;
-use rustc_hir::def_id::CrateNum;
+use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
use rustc_middle::middle::dependency_format::Linkage;
use rustc_middle::middle::exported_symbols::SymbolExportKind;
use rustc_session::config::{self, CFGuard, CrateType, DebugInfo, LdImpl, Strip};
// Pass optimization flags down to the linker.
cmd.optimize();
+ let debugger_visualizer_paths = if sess.target.is_like_msvc {
+ collect_debugger_visualizers(tmpdir, sess, &codegen_results.crate_info)
+ } else {
+ Vec::new()
+ };
+
// Pass debuginfo and strip flags down to the linker.
- cmd.debuginfo(strip_value(sess));
+ cmd.debuginfo(strip_value(sess), &debugger_visualizer_paths);
// We want to prevent the compiler from accidentally leaking in any system libraries,
// so by default we tell linkers not to link to any default libraries.
add_rpath_args(cmd, sess, codegen_results, out_filename);
}
+// Write the debugger visualizer files for each crate to the temp directory and gather the file paths.
+fn collect_debugger_visualizers(
+ tmpdir: &Path,
+ sess: &Session,
+ crate_info: &CrateInfo,
+) -> Vec<PathBuf> {
+ let mut visualizer_paths = Vec::new();
+ let debugger_visualizers = &crate_info.debugger_visualizers;
+ let mut index = 0;
+
+ for (&cnum, visualizers) in debugger_visualizers {
+ let crate_name = if cnum == LOCAL_CRATE {
+ crate_info.local_crate_name.as_str()
+ } else {
+ crate_info.crate_name[&cnum].as_str()
+ };
+
+ for visualizer in visualizers {
+ let visualizer_out_file = tmpdir.join(format!("{}-{}.natvis", crate_name, index));
+
+ match fs::write(&visualizer_out_file, &visualizer.src) {
+ Ok(()) => {
+ visualizer_paths.push(visualizer_out_file.clone());
+ index += 1;
+ }
+ Err(error) => {
+ sess.warn(
+ format!(
+ "Unable to write debugger visualizer file `{}`: {} ",
+ visualizer_out_file.display(),
+ error
+ )
+ .as_str(),
+ );
+ }
+ };
+ }
+ }
+ visualizer_paths
+}
+
/// # Native library linking
///
/// User-supplied library search paths (-L on the command line). These are the same paths used to
fn optimize(&mut self);
fn pgo_gen(&mut self);
fn control_flow_guard(&mut self);
- fn debuginfo(&mut self, strip: Strip);
+ fn debuginfo(&mut self, strip: Strip, debugger_visualizers: &[PathBuf]);
fn no_crt_objects(&mut self);
fn no_default_libraries(&mut self);
fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType, symbols: &[String]);
fn control_flow_guard(&mut self) {}
- fn debuginfo(&mut self, strip: Strip) {
+ fn debuginfo(&mut self, strip: Strip, _: &[PathBuf]) {
// MacOS linker doesn't support stripping symbols directly anymore.
if self.sess.target.is_like_osx {
return;
self.cmd.arg("/guard:cf");
}
- fn debuginfo(&mut self, strip: Strip) {
+ fn debuginfo(&mut self, strip: Strip, debugger_visualizers: &[PathBuf]) {
match strip {
Strip::None => {
// This will cause the Microsoft linker to generate a PDB file
}
}
}
+
+ // This will cause the Microsoft linker to embed .natvis info for all crates into the PDB file
+ for path in debugger_visualizers {
+ let mut arg = OsString::from("/NATVIS:");
+ arg.push(path);
+ self.cmd.arg(arg);
+ }
}
Strip::Debuginfo | Strip::Symbols => {
self.cmd.arg("/DEBUG:NONE");
fn control_flow_guard(&mut self) {}
- fn debuginfo(&mut self, _strip: Strip) {
+ fn debuginfo(&mut self, _strip: Strip, _: &[PathBuf]) {
// Preserve names or generate source maps depending on debug info
self.cmd.arg(match self.sess.opts.debuginfo {
DebugInfo::None => "-g0",
fn pgo_gen(&mut self) {}
- fn debuginfo(&mut self, strip: Strip) {
+ fn debuginfo(&mut self, strip: Strip, _: &[PathBuf]) {
match strip {
Strip::None => {}
Strip::Debuginfo => {
fn pgo_gen(&mut self) {}
- fn debuginfo(&mut self, strip: Strip) {
+ fn debuginfo(&mut self, strip: Strip, _: &[PathBuf]) {
match strip {
Strip::None => {}
Strip::Debuginfo => {
self.cmd.arg("-L").arg(path);
}
- fn debuginfo(&mut self, _strip: Strip) {
+ fn debuginfo(&mut self, _strip: Strip, _: &[PathBuf]) {
self.cmd.arg("--debug");
}
self.cmd.arg("-L").arg(path);
}
- fn debuginfo(&mut self, _strip: Strip) {
+ fn debuginfo(&mut self, _strip: Strip, _: &[PathBuf]) {
self.cmd.arg("--debug");
}
missing_lang_items: Default::default(),
dependency_formats: tcx.dependency_formats(()).clone(),
windows_subsystem,
+ debugger_visualizers: Default::default(),
};
+ let debugger_visualizers = tcx.debugger_visualizers(LOCAL_CRATE).clone();
+ if !debugger_visualizers.is_empty() {
+ info.debugger_visualizers.insert(LOCAL_CRATE, debugger_visualizers);
+ }
+
let lang_items = tcx.lang_items();
let crates = tcx.crates(());
info.native_libraries
.insert(cnum, tcx.native_libraries(cnum).iter().map(Into::into).collect());
info.crate_name.insert(cnum, tcx.crate_name(cnum));
- info.used_crate_source.insert(cnum, tcx.used_crate_source(cnum).clone());
+
+ let used_crate_source = tcx.used_crate_source(cnum);
+ info.used_crate_source.insert(cnum, used_crate_source.clone());
if tcx.is_compiler_builtins(cnum) {
info.compiler_builtins = Some(cnum);
}
let missing =
missing.iter().cloned().filter(|&l| lang_items::required(tcx, l)).collect();
info.missing_lang_items.insert(cnum, missing);
+
+ // Only include debugger visualizer files from crates that will be statically linked.
+ if used_crate_source.rlib.is_some() || used_crate_source.rmeta.is_some() {
+ let debugger_visualizers = tcx.debugger_visualizers(cnum).clone();
+ if !debugger_visualizers.is_empty() {
+ info.debugger_visualizers.insert(cnum, debugger_visualizers);
+ }
+ }
}
info
use rustc_session::cstore::{self, CrateSource};
use rustc_session::utils::NativeLibKind;
use rustc_span::symbol::Symbol;
+use rustc_span::DebuggerVisualizerFile;
use std::path::{Path, PathBuf};
pub mod back;
pub missing_lang_items: FxHashMap<CrateNum, Vec<LangItem>>,
pub dependency_formats: Lrc<Dependencies>,
pub windows_subsystem: Option<String>,
+ pub debugger_visualizers: FxHashMap<CrateNum, Vec<DebuggerVisualizerFile>>,
}
#[derive(Encodable, Decodable)]
}
#[inline]
- pub fn to_scalar_pair(self) -> InterpResult<'tcx, (Scalar<Tag>, Scalar<Tag>)> {
+ pub fn to_scalar_or_uninit_pair(self) -> (ScalarMaybeUninit<Tag>, ScalarMaybeUninit<Tag>) {
match self {
- Immediate::ScalarPair(val1, val2) => Ok((val1.check_init()?, val2.check_init()?)),
- Immediate::Scalar(..) => {
- bug!("Got a scalar where a scalar pair was expected")
- }
+ Immediate::ScalarPair(val1, val2) => (val1, val2),
+ Immediate::Scalar(..) => bug!("Got a scalar where a scalar pair was expected"),
}
}
+
+ #[inline]
+ pub fn to_scalar_pair(self) -> InterpResult<'tcx, (Scalar<Tag>, Scalar<Tag>)> {
+ let (val1, val2) = self.to_scalar_or_uninit_pair();
+ Ok((val1.check_init()?, val2.check_init()?))
+ }
}
// ScalarPair needs a type to interpret, so we often have an immediate and a type together
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
/// Try reading an immediate in memory; this is interesting particularly for `ScalarPair`.
/// Returns `None` if the layout does not permit loading this as a value.
- fn try_read_immediate_from_mplace(
+ ///
+ /// This is an internal function; call `read_immediate` instead.
+ fn read_immediate_from_mplace_raw(
&self,
mplace: &MPlaceTy<'tcx, M::PointerTag>,
+ force: bool,
) -> InterpResult<'tcx, Option<ImmTy<'tcx, M::PointerTag>>> {
if mplace.layout.is_unsized() {
// Don't touch unsized
// case where some of the bytes are initialized and others are not. So, we need an extra
// check that walks over the type of `mplace` to make sure it is truly correct to treat this
// like a `Scalar` (or `ScalarPair`).
- match mplace.layout.abi {
- Abi::Scalar(abi::Scalar::Initialized { .. }) => {
- let scalar = alloc.read_scalar(alloc_range(Size::ZERO, mplace.layout.size))?;
- Ok(Some(ImmTy { imm: scalar.into(), layout: mplace.layout }))
- }
+ let scalar_layout = match mplace.layout.abi {
+ // `if` does not work nested inside patterns, making this a bit awkward to express.
+ Abi::Scalar(abi::Scalar::Initialized { value: s, .. }) => Some(s),
+ Abi::Scalar(s) if force => Some(s.primitive()),
+ _ => None,
+ };
+ if let Some(_) = scalar_layout {
+ let scalar = alloc.read_scalar(alloc_range(Size::ZERO, mplace.layout.size))?;
+ return Ok(Some(ImmTy { imm: scalar.into(), layout: mplace.layout }));
+ }
+ let scalar_pair_layout = match mplace.layout.abi {
Abi::ScalarPair(
abi::Scalar::Initialized { value: a, .. },
abi::Scalar::Initialized { value: b, .. },
- ) => {
- // We checked `ptr_align` above, so all fields will have the alignment they need.
- // We would anyway check against `ptr_align.restrict_for_offset(b_offset)`,
- // which `ptr.offset(b_offset)` cannot possibly fail to satisfy.
- let (a_size, b_size) = (a.size(self), b.size(self));
- let b_offset = a_size.align_to(b.align(self).abi);
- assert!(b_offset.bytes() > 0); // we later use the offset to tell apart the fields
- let a_val = alloc.read_scalar(alloc_range(Size::ZERO, a_size))?;
- let b_val = alloc.read_scalar(alloc_range(b_offset, b_size))?;
- Ok(Some(ImmTy { imm: Immediate::ScalarPair(a_val, b_val), layout: mplace.layout }))
- }
- _ => Ok(None),
+ ) => Some((a, b)),
+ Abi::ScalarPair(a, b) if force => Some((a.primitive(), b.primitive())),
+ _ => None,
+ };
+ if let Some((a, b)) = scalar_pair_layout {
+ // We checked `ptr_align` above, so all fields will have the alignment they need.
+ // We would anyway check against `ptr_align.restrict_for_offset(b_offset)`,
+ // which `ptr.offset(b_offset)` cannot possibly fail to satisfy.
+ let (a_size, b_size) = (a.size(self), b.size(self));
+ let b_offset = a_size.align_to(b.align(self).abi);
+ assert!(b_offset.bytes() > 0); // we later use the offset to tell apart the fields
+ let a_val = alloc.read_scalar(alloc_range(Size::ZERO, a_size))?;
+ let b_val = alloc.read_scalar(alloc_range(b_offset, b_size))?;
+ return Ok(Some(ImmTy {
+ imm: Immediate::ScalarPair(a_val, b_val),
+ layout: mplace.layout,
+ }));
}
+ // Neither a scalar nor scalar pair.
+ return Ok(None);
}
- /// Try returning an immediate for the operand.
- /// If the layout does not permit loading this as an immediate, return where in memory
- /// we can find the data.
+ /// Try returning an immediate for the operand. If the layout does not permit loading this as an
+ /// immediate, return where in memory we can find the data.
/// Note that for a given layout, this operation will either always fail or always
/// succeed! Whether it succeeds depends on whether the layout can be represented
/// in an `Immediate`, not on which data is stored there currently.
- pub fn try_read_immediate(
+ ///
+ /// If `force` is `true`, then even scalars with fields that can be ununit will be
+ /// read. This means the load is lossy and should not be written back!
+ /// This flag exists only for validity checking.
+ ///
+ /// This is an internal function that should not usually be used; call `read_immediate` instead.
+ pub fn read_immediate_raw(
&self,
src: &OpTy<'tcx, M::PointerTag>,
+ force: bool,
) -> InterpResult<'tcx, Result<ImmTy<'tcx, M::PointerTag>, MPlaceTy<'tcx, M::PointerTag>>> {
Ok(match src.try_as_mplace() {
Ok(ref mplace) => {
- if let Some(val) = self.try_read_immediate_from_mplace(mplace)? {
+ if let Some(val) = self.read_immediate_from_mplace_raw(mplace, force)? {
Ok(val)
} else {
Err(*mplace)
&self,
op: &OpTy<'tcx, M::PointerTag>,
) -> InterpResult<'tcx, ImmTy<'tcx, M::PointerTag>> {
- if let Ok(imm) = self.try_read_immediate(op)? {
+ if let Ok(imm) = self.read_immediate_raw(op, /*force*/ false)? {
Ok(imm)
} else {
span_bug!(self.cur_span(), "primitive read failed for type: {:?}", op.layout.ty);
}
trace!("write_immediate: {:?} <- {:?}: {}", *dest, src, dest.layout.ty);
- // See if we can avoid an allocation. This is the counterpart to `try_read_immediate`,
+ // See if we can avoid an allocation. This is the counterpart to `read_immediate_raw`,
// but not factored as a separate function.
let mplace = match dest.place {
Place::Local { frame, local } => {
}
// Let us see if the layout is simple so we take a shortcut, avoid force_allocation.
- let src = match self.try_read_immediate(src)? {
+ let src = match self.read_immediate_raw(src, /*force*/ false)? {
Ok(src_val) => {
assert!(!src.layout.is_unsized(), "cannot have unsized immediates");
// Yay, we got a value that we can write directly.
use std::hash::Hash;
use super::{
- alloc_range, CheckInAllocMsg, GlobalAlloc, InterpCx, InterpResult, MPlaceTy, Machine,
- MemPlaceMeta, OpTy, Scalar, ScalarMaybeUninit, ValueVisitor,
+ alloc_range, CheckInAllocMsg, GlobalAlloc, Immediate, InterpCx, InterpResult, MPlaceTy,
+ Machine, MemPlaceMeta, OpTy, Scalar, ScalarMaybeUninit, ValueVisitor,
};
macro_rules! throw_validation_failure {
))
}
+ fn read_immediate_forced(
+ &self,
+ op: &OpTy<'tcx, M::PointerTag>,
+ ) -> InterpResult<'tcx, Immediate<M::PointerTag>> {
+ Ok(*try_validation!(
+ self.ecx.read_immediate_raw(op, /*force*/ true),
+ self.path,
+ err_unsup!(ReadPointerAsBytes) => { "(potentially part of) a pointer" } expected { "plain (non-pointer) bytes" },
+ ).unwrap())
+ }
+
/// Check if this is a value of primitive type, and if yes check the validity of the value
/// at that type. Return `true` if the type is indeed primitive.
fn try_visit_primitive(
fn visit_scalar(
&mut self,
- op: &OpTy<'tcx, M::PointerTag>,
+ scalar: ScalarMaybeUninit<M::PointerTag>,
scalar_layout: ScalarAbi,
) -> InterpResult<'tcx> {
// We check `is_full_range` in a slightly complicated way because *if* we are checking
// number validity, then we want to ensure that `Scalar::Initialized` is indeed initialized,
// i.e. that we go over the `check_init` below.
+ let size = scalar_layout.size(self.ecx);
let is_full_range = match scalar_layout {
ScalarAbi::Initialized { valid_range, .. } => {
if M::enforce_number_validity(self.ecx) {
false // not "full" since uninit is not accepted
} else {
- valid_range.is_full_for(op.layout.size)
+ valid_range.is_full_for(size)
}
}
ScalarAbi::Union { .. } => true,
// Nothing to check
return Ok(());
}
- // We have something to check.
+ // We have something to check: it must at least be initialized.
let valid_range = scalar_layout.valid_range(self.ecx);
let WrappingRange { start, end } = valid_range;
- let max_value = op.layout.size.unsigned_int_max();
+ let max_value = size.unsigned_int_max();
assert!(end <= max_value);
- // Determine the allowed range
- let value = self.read_scalar(op)?;
let value = try_validation!(
- value.check_init(),
+ scalar.check_init(),
self.path,
- err_ub!(InvalidUninitBytes(None)) => { "{:x}", value }
+ err_ub!(InvalidUninitBytes(None)) => { "{:x}", scalar }
expected { "something {}", wrapping_range_format(valid_range, max_value) },
);
let bits = match value.try_to_int() {
- Ok(int) => int.assert_bits(op.layout.size),
+ Ok(int) => int.assert_bits(size),
Err(_) => {
// So this is a pointer then, and casting to an int failed.
// Can only happen during CTFE.
} else {
return Ok(());
}
- } else if scalar_layout.valid_range(self.ecx).is_full_for(op.layout.size) {
+ } else if scalar_layout.valid_range(self.ecx).is_full_for(size) {
// Easy. (This is reachable if `enforce_number_validity` is set.)
return Ok(());
} else {
);
}
Abi::Scalar(scalar_layout) => {
- self.visit_scalar(op, scalar_layout)?;
+ let scalar = self.read_immediate_forced(op)?.to_scalar_or_uninit();
+ self.visit_scalar(scalar, scalar_layout)?;
+ }
+ Abi::ScalarPair(a_layout, b_layout) => {
+ // We would validate these things as we descend into the fields,
+ // but that can miss bugs in layout computation. Layout computation
+ // is subtle due to enums having ScalarPair layout, where one field
+ // is the discriminant.
+ if cfg!(debug_assertions) {
+ let (a, b) = self.read_immediate_forced(op)?.to_scalar_or_uninit_pair();
+ self.visit_scalar(a, a_layout)?;
+ self.visit_scalar(b, b_layout)?;
+ }
}
- Abi::ScalarPair { .. } | Abi::Vector { .. } => {
- // These have fields that we already visited above, so we already checked
- // all their scalar-level restrictions.
- // There is also no equivalent to `rustc_layout_scalar_valid_range_start`
- // that would make skipping them here an issue.
+ Abi::Vector { .. } => {
+ // No checks here, we assume layout computation gets this right.
+ // (This is harder to check since Miri does not represent these as `Immediate`.)
}
Abi::Aggregate { .. } => {
// Nothing to do.
#![feature(generators)]
#![feature(let_else)]
#![feature(hash_raw_entry)]
+#![feature(hasher_prefixfree_extras)]
#![feature(maybe_uninit_uninit_array)]
#![feature(min_specialization)]
#![feature(never_type)]
self.slice_write(msg);
}
+ #[inline]
+ fn write_str(&mut self, s: &str) {
+ // This hasher works byte-wise, and `0xFF` cannot show up in a `str`,
+ // so just hashing the one extra byte is enough to be prefix-free.
+ self.write(s.as_bytes());
+ self.write_u8(0xFF);
+ }
+
fn finish(&self) -> u64 {
panic!("SipHasher128 cannot provide valid 64 bit hashes")
}
self.state.write(bytes);
}
+ #[inline]
+ fn write_str(&mut self, s: &str) {
+ self.state.write_str(s);
+ }
+
+ #[inline]
+ fn write_length_prefix(&mut self, len: usize) {
+ // Our impl for `usize` will extend it if needed.
+ self.write_usize(len);
+ }
+
#[inline]
fn write_u8(&mut self, i: u8) {
self.state.write_u8(i);
use rustc_attr::{self as attr, Deprecation, Stability};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::sync::{self, Lrc};
-use rustc_errors::{Applicability, DiagnosticBuilder, ErrorGuaranteed, MultiSpan};
+use rustc_errors::{Applicability, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, PResult};
use rustc_lint_defs::builtin::PROC_MACRO_BACK_COMPAT;
use rustc_lint_defs::BuiltinLintDiagnostics;
use rustc_parse::{self, nt_to_tokenstream, parser, MACRO_ARGUMENTS};
use rustc_span::hygiene::{AstPass, ExpnData, ExpnKind, LocalExpnId};
use rustc_span::source_map::SourceMap;
use rustc_span::symbol::{kw, sym, Ident, Symbol};
-use rustc_span::{Span, DUMMY_SP};
+use rustc_span::{FileName, Span, DUMMY_SP};
use smallvec::{smallvec, SmallVec};
use std::default::Default;
}
}
+/// Resolves a `path` mentioned inside Rust code, returning an absolute path.
+///
+/// This unifies the logic used for resolving `include_X!`.
+pub fn resolve_path(
+ parse_sess: &ParseSess,
+ path: impl Into<PathBuf>,
+ span: Span,
+) -> PResult<'_, PathBuf> {
+ let path = path.into();
+
+ // Relative paths are resolved relative to the file in which they are found
+ // after macro expansion (that is, they are unhygienic).
+ if !path.is_absolute() {
+ let callsite = span.source_callsite();
+ let mut result = match parse_sess.source_map().span_to_filename(callsite) {
+ FileName::Real(name) => name
+ .into_local_path()
+ .expect("attempting to resolve a file path in an external file"),
+ FileName::DocTest(path, _) => path,
+ other => {
+ return Err(parse_sess.span_diagnostic.struct_span_err(
+ span,
+ &format!(
+ "cannot resolve relative path in non-file source `{}`",
+ parse_sess.source_map().filename_for_diagnostics(&other)
+ ),
+ ));
+ }
+ };
+ result.pop();
+ result.push(path);
+ Ok(result)
+ } else {
+ Ok(path)
+ }
+}
+
/// Extracts a string literal from the macro expanded version of `expr`,
/// returning a diagnostic error of `err_msg` if `expr` is not a string literal.
/// The returned bool indicates whether an applicable suggestion has already been
(active, custom_inner_attributes, "1.30.0", Some(54726), None),
/// Allows custom test frameworks with `#![test_runner]` and `#[test_case]`.
(active, custom_test_frameworks, "1.30.0", Some(50297), None),
+ /// Allows using `#[debugger_visualizer]`.
+ (active, debugger_visualizer, "1.62.0", Some(95939), None),
/// Allows declarative macros 2.0 (`macro`).
(active, decl_macro, "1.17.0", Some(39412), None),
/// Allows rustc to inject a default alloc_error_handler
// Unstable attributes:
// ==========================================================================
+ // RFC #3191: #[debugger_visualizer] support
+ gated!(
+ debugger_visualizer, Normal, template!(List: r#"natvis_file = "...""#),
+ DuplicatesOk, experimental!(debugger_visualizer)
+ ),
+
// Linking:
gated!(naked, Normal, template!(Word), WarnFollowing, naked_functions, experimental!(naked)),
gated!(
rustc_allow_incoherent_impl, AttributeType::Normal, template!(Word), ErrorFollowing,
"#[rustc_allow_incoherent_impl] has to be added to all impl items of an incoherent inherent impl."
),
+ rustc_attr!(
+ rustc_has_incoherent_inherent_impls, AttributeType::Normal, template!(Word), ErrorFollowing,
+ "#[rustc_has_incoherent_inherent_impls] allows the addition of incoherent inherent impls for \
+ the given type by annotating all impl items with #[rustc_allow_incoherent_impl]."
+ ),
BuiltinAttribute {
name: sym::rustc_diagnostic_item,
type_: Normal,
Range, sym::Range, range_struct, Target::Struct, GenericRequirement::None;
RangeToInclusive, sym::RangeToInclusive, range_to_inclusive_struct, Target::Struct, GenericRequirement::None;
RangeTo, sym::RangeTo, range_to_struct, Target::Struct, GenericRequirement::None;
-
- CStr, sym::CStr, c_str, Target::Struct, GenericRequirement::None;
}
pub enum GenericRequirement {
"converted into hard error, see RFC 2972 \
<https://github.com/rust-lang/rfcs/blob/master/text/2972-constrained-naked.md> for more information",
);
+ store.register_removed(
+ "mutable_borrow_reservation_conflict",
+ "now allowed, see issue #59159 \
+ <https://github.com/rust-lang/rust/issues/59159> for more information",
+ );
}
fn register_internals(store: &mut LintStore) {
};
}
-declare_lint! {
- /// The `mutable_borrow_reservation_conflict` lint detects the reservation
- /// of a two-phased borrow that conflicts with other shared borrows.
- ///
- /// ### Example
- ///
- /// ```rust
- /// let mut v = vec![0, 1, 2];
- /// let shared = &v;
- /// v.push(shared.len());
- /// ```
- ///
- /// {{produces}}
- ///
- /// ### Explanation
- ///
- /// This is a [future-incompatible] lint to transition this to a hard error
- /// in the future. See [issue #59159] for a complete description of the
- /// problem, and some possible solutions.
- ///
- /// [issue #59159]: https://github.com/rust-lang/rust/issues/59159
- /// [future-incompatible]: ../index.md#future-incompatible-lints
- pub MUTABLE_BORROW_RESERVATION_CONFLICT,
- Warn,
- "reservation of a two-phased borrow conflicts with other shared borrows",
- @future_incompatible = FutureIncompatibleInfo {
- reason: FutureIncompatibilityReason::Custom(
- "this borrowing pattern was not meant to be accepted, \
- and may become a hard error in the future"
- ),
- reference: "issue #59159 <https://github.com/rust-lang/rust/issues/59159>",
- };
-}
-
declare_lint! {
/// The `soft_unstable` lint detects unstable features that were
/// unintentionally allowed on stable.
META_VARIABLE_MISUSE,
DEPRECATED_IN_FUTURE,
AMBIGUOUS_ASSOCIATED_ITEMS,
- MUTABLE_BORROW_RESERVATION_CONFLICT,
INDIRECT_STRUCTURAL_MATCH,
POINTER_STRUCTURAL_MATCH,
NONTRIVIAL_STRUCTURAL_MATCH,
self.root.tables.expn_that_defined.get(self, id).unwrap().decode((self, sess))
}
+ fn get_debugger_visualizers(self) -> Vec<rustc_span::DebuggerVisualizerFile> {
+ self.root.debugger_visualizers.decode(self).collect::<Vec<_>>()
+ }
+
/// Iterates over all the stability attributes in the given crate.
fn get_lib_features(self, tcx: TyCtxt<'tcx>) -> &'tcx [(Symbol, Option<Symbol>)] {
tcx.arena.alloc_from_iter(self.root.lib_features.decode(self))
}
used_crate_source => { Lrc::clone(&cdata.source) }
+ debugger_visualizers => { cdata.get_debugger_visualizers() }
exported_symbols => {
let syms = cdata.exported_symbols(tcx);
use rustc_session::config::CrateType;
use rustc_session::cstore::{ForeignModule, LinkagePreference, NativeLib};
use rustc_span::symbol::{sym, Ident, Symbol};
-use rustc_span::{self, ExternalSource, FileName, SourceFile, Span, SyntaxContext};
+use rustc_span::{
+ self, DebuggerVisualizerFile, ExternalSource, FileName, SourceFile, Span, SyntaxContext,
+};
use rustc_span::{
hygiene::{ExpnIndex, HygieneEncodeContext, MacroKind},
RealFileName,
let tables = self.tables.encode(&mut self.opaque);
let tables_bytes = self.position() - i;
+ i = self.position();
+ let debugger_visualizers = self.encode_debugger_visualizers();
+ let debugger_visualizers_bytes = self.position() - i;
+
// Encode exported symbols info. This is prefetched in `encode_metadata` so we encode
// this as late as possible to give the prefetching as much time as possible to complete.
i = self.position();
has_panic_handler: tcx.has_panic_handler(LOCAL_CRATE),
has_default_lib_allocator,
proc_macro_data,
+ debugger_visualizers,
compiler_builtins: tcx.sess.contains_name(&attrs, sym::compiler_builtins),
needs_allocator: tcx.sess.contains_name(&attrs, sym::needs_allocator),
needs_panic_runtime: tcx.sess.contains_name(&attrs, sym::needs_panic_runtime),
}
eprintln!("metadata stats:");
- eprintln!(" dep bytes: {}", dep_bytes);
- eprintln!(" lib feature bytes: {}", lib_feature_bytes);
- eprintln!(" lang item bytes: {}", lang_item_bytes);
- eprintln!(" diagnostic item bytes: {}", diagnostic_item_bytes);
- eprintln!(" native bytes: {}", native_lib_bytes);
- eprintln!(" source_map bytes: {}", source_map_bytes);
- eprintln!(" traits bytes: {}", traits_bytes);
- eprintln!(" impls bytes: {}", impls_bytes);
- eprintln!("incoherent_impls bytes: {}", incoherent_impls_bytes);
- eprintln!(" exp. symbols bytes: {}", exported_symbols_bytes);
- eprintln!(" def-path table bytes: {}", def_path_table_bytes);
- eprintln!(" def-path hashes bytes: {}", def_path_hash_map_bytes);
- eprintln!(" proc-macro-data-bytes: {}", proc_macro_data_bytes);
- eprintln!(" mir bytes: {}", mir_bytes);
- eprintln!(" item bytes: {}", item_bytes);
- eprintln!(" table bytes: {}", tables_bytes);
- eprintln!(" hygiene bytes: {}", hygiene_bytes);
- eprintln!(" zero bytes: {}", zero_bytes);
- eprintln!(" total bytes: {}", total_bytes);
+ eprintln!(" dep bytes: {}", dep_bytes);
+ eprintln!(" lib feature bytes: {}", lib_feature_bytes);
+ eprintln!(" lang item bytes: {}", lang_item_bytes);
+ eprintln!(" diagnostic item bytes: {}", diagnostic_item_bytes);
+ eprintln!(" native bytes: {}", native_lib_bytes);
+ eprintln!(" debugger visualizers bytes: {}", debugger_visualizers_bytes);
+ eprintln!(" source_map bytes: {}", source_map_bytes);
+ eprintln!(" traits bytes: {}", traits_bytes);
+ eprintln!(" impls bytes: {}", impls_bytes);
+ eprintln!(" incoherent_impls bytes: {}", incoherent_impls_bytes);
+ eprintln!(" exp. symbols bytes: {}", exported_symbols_bytes);
+ eprintln!(" def-path table bytes: {}", def_path_table_bytes);
+ eprintln!(" def-path hashes bytes: {}", def_path_hash_map_bytes);
+ eprintln!(" proc-macro-data-bytes: {}", proc_macro_data_bytes);
+ eprintln!(" mir bytes: {}", mir_bytes);
+ eprintln!(" item bytes: {}", item_bytes);
+ eprintln!(" table bytes: {}", tables_bytes);
+ eprintln!(" hygiene bytes: {}", hygiene_bytes);
+ eprintln!(" zero bytes: {}", zero_bytes);
+ eprintln!(" total bytes: {}", total_bytes);
}
root
}
}
+ fn encode_debugger_visualizers(&mut self) -> Lazy<[DebuggerVisualizerFile]> {
+ empty_proc_macro!(self);
+ self.lazy(self.tcx.debugger_visualizers(LOCAL_CRATE).iter())
+ }
+
fn encode_crate_deps(&mut self) -> Lazy<[CrateDep]> {
empty_proc_macro!(self);
proc_macro_data: Option<ProcMacroData>,
tables: LazyTables<'tcx>,
+ debugger_visualizers: Lazy<[rustc_span::DebuggerVisualizerFile]>,
exported_symbols: Lazy!([(ExportedSymbol<'tcx>, SymbolExportInfo)]),
desc { "looking at the source for a crate" }
separate_provide_extern
}
+ /// Returns the debugger visualizers defined for this crate.
+ query debugger_visualizers(_: CrateNum) -> Vec<rustc_span::DebuggerVisualizerFile> {
+ storage(ArenaCacheSelector<'tcx>)
+ desc { "looking up the debugger visualizers for this crate" }
+ separate_provide_extern
+ }
query postorder_cnums(_: ()) -> &'tcx [CrateNum] {
eval_always
desc { "generating a postorder list of CrateNums" }
match key.disambiguated_data.data {
// Closures' own generics are only captures, don't print them.
DefPathData::ClosureExpr => {}
+ // This covers both `DefKind::AnonConst` and `DefKind::InlineConst`.
+ // Anon consts doesn't have their own generics, and inline consts' own
+ // generics are their inferred types, so don't print them.
+ DefPathData::AnonConst => {}
// If we have any generic arguments to print, we do that
// on top of the same path, but without its own generics.
// Try to read the local as an immediate so that if it is representable as a scalar, we can
// handle it as such, but otherwise, just return the value as is.
- Some(match self.ecx.try_read_immediate(&op) {
+ Some(match self.ecx.read_immediate_raw(&op, /*force*/ false) {
Ok(Ok(imm)) => imm.into(),
_ => op,
})
return;
}
- // FIXME> figure out what to do when try_read_immediate fails
- let imm = self.use_ecx(|this| this.ecx.try_read_immediate(value));
+ // FIXME> figure out what to do when read_immediate_raw fails
+ let imm = self.use_ecx(|this| this.ecx.read_immediate_raw(value, /*force*/ false));
if let Some(Ok(imm)) = imm {
match *imm {
// Try to read the local as an immediate so that if it is representable as a scalar, we can
// handle it as such, but otherwise, just return the value as is.
- Some(match self.ecx.try_read_immediate(&op) {
+ Some(match self.ecx.read_immediate_raw(&op, /*force*/ false) {
Ok(Ok(imm)) => imm.into(),
_ => op,
})
// depend on any other items.
}
hir::InlineAsmOperand::SymFn { anon_const } => {
- let def_id = tcx.hir().body_owner_def_id(anon_const.body).to_def_id();
- if let Ok(val) = tcx.const_eval_poly(def_id) {
- rustc_data_structures::stack::ensure_sufficient_stack(|| {
- collect_const_value(tcx, val, &mut neighbors);
- });
- }
+ let fn_ty =
+ tcx.typeck_body(anon_const.body).node_type(anon_const.hir_id);
+ visit_fn_use(tcx, fn_ty, false, *op_sp, &mut neighbors);
}
hir::InlineAsmOperand::SymStatic { path: _, def_id } => {
let instance = Instance::mono(tcx, *def_id);
rustc_attr = { path = "../rustc_attr" }
rustc_data_structures = { path = "../rustc_data_structures" }
rustc_errors = { path = "../rustc_errors" }
+rustc_expand = { path = "../rustc_expand" }
rustc_hir = { path = "../rustc_hir" }
rustc_index = { path = "../rustc_index" }
rustc_parse = { path = "../rustc_parse" }
sym::allow_internal_unstable => {
self.check_allow_internal_unstable(hir_id, &attr, span, target, &attrs)
}
+ sym::debugger_visualizer => self.check_debugger_visualizer(&attr, target),
sym::rustc_allow_const_fn_unstable => {
self.check_rustc_allow_const_fn_unstable(hir_id, &attr, span, target)
}
sym::rustc_allow_incoherent_impl => {
self.check_allow_incoherent_impl(&attr, span, target)
}
+ sym::rustc_has_incoherent_inherent_impls => {
+ self.check_has_incoherent_inherent_impls(&attr, span, target)
+ }
sym::rustc_const_unstable
| sym::rustc_const_stable
| sym::unstable
}
}
- /// Warns against some misuses of `#[pass_by_value]`
fn check_allow_incoherent_impl(&self, attr: &Attribute, span: Span, target: Target) -> bool {
match target {
Target::Method(MethodKind::Inherent) => true,
}
}
+ fn check_has_incoherent_inherent_impls(
+ &self,
+ attr: &Attribute,
+ span: Span,
+ target: Target,
+ ) -> bool {
+ match target {
+ Target::Trait | Target::Struct | Target::Enum | Target::Union | Target::ForeignTy => {
+ true
+ }
+ _ => {
+ self.tcx
+ .sess
+ .struct_span_err(
+ attr.span,
+ "`rustc_has_incoherent_inherent_impls` attribute should be applied to types or traits.",
+ )
+ .span_label(span, "only adts, extern types and traits are supported")
+ .emit();
+ false
+ }
+ }
+ }
+
/// Warns against some misuses of `#[must_use]`
fn check_must_use(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool {
let node = self.tcx.hir().get(hir_id);
}
}
+ /// Checks if the items on the `#[debugger_visualizer]` attribute are valid.
+ fn check_debugger_visualizer(&self, attr: &Attribute, target: Target) -> bool {
+ match target {
+ Target::Mod => {}
+ _ => {
+ self.tcx
+ .sess
+ .struct_span_err(attr.span, "attribute should be applied to a module")
+ .emit();
+ return false;
+ }
+ }
+
+ let hints = match attr.meta_item_list() {
+ Some(meta_item_list) => meta_item_list,
+ None => {
+ self.emit_debugger_visualizer_err(attr);
+ return false;
+ }
+ };
+
+ let hint = match hints.len() {
+ 1 => &hints[0],
+ _ => {
+ self.emit_debugger_visualizer_err(attr);
+ return false;
+ }
+ };
+
+ if !hint.has_name(sym::natvis_file) {
+ self.emit_debugger_visualizer_err(attr);
+ return false;
+ }
+
+ let meta_item = match hint.meta_item() {
+ Some(meta_item) => meta_item,
+ None => {
+ self.emit_debugger_visualizer_err(attr);
+ return false;
+ }
+ };
+
+ match (meta_item.name_or_empty(), meta_item.value_str()) {
+ (sym::natvis_file, Some(_)) => true,
+ (_, _) => {
+ self.emit_debugger_visualizer_err(attr);
+ false
+ }
+ }
+ }
+
+ fn emit_debugger_visualizer_err(&self, attr: &Attribute) {
+ self.tcx
+ .sess
+ .struct_span_err(attr.span, "invalid argument")
+ .note(r#"expected: `natvis_file = "..."`"#)
+ .emit();
+ }
+
/// Outputs an error for `#[allow_internal_unstable]` which can only be applied to macros.
/// (Allows proc_macro functions)
fn check_rustc_allow_const_fn_unstable(
--- /dev/null
+//! Detecting usage of the `#[debugger_visualizer]` attribute.
+
+use hir::CRATE_HIR_ID;
+use rustc_data_structures::fx::FxHashSet;
+use rustc_expand::base::resolve_path;
+use rustc_hir as hir;
+use rustc_hir::def_id::CrateNum;
+use rustc_hir::itemlikevisit::ItemLikeVisitor;
+use rustc_hir::{HirId, Target};
+use rustc_middle::ty::query::Providers;
+use rustc_middle::ty::TyCtxt;
+use rustc_span::def_id::LOCAL_CRATE;
+use rustc_span::{sym, DebuggerVisualizerFile, DebuggerVisualizerType};
+
+use std::sync::Arc;
+
+struct DebuggerVisualizerCollector<'tcx> {
+ debugger_visualizers: FxHashSet<DebuggerVisualizerFile>,
+ tcx: TyCtxt<'tcx>,
+}
+
+impl<'v, 'tcx> ItemLikeVisitor<'v> for DebuggerVisualizerCollector<'tcx> {
+ fn visit_item(&mut self, item: &hir::Item<'_>) {
+ let target = Target::from_item(item);
+ match target {
+ Target::Mod => {
+ self.check_for_debugger_visualizer(item.hir_id());
+ }
+ _ => {}
+ }
+ }
+
+ fn visit_trait_item(&mut self, _: &hir::TraitItem<'_>) {}
+
+ fn visit_impl_item(&mut self, _: &hir::ImplItem<'_>) {}
+
+ fn visit_foreign_item(&mut self, _: &hir::ForeignItem<'_>) {}
+}
+
+impl<'tcx> DebuggerVisualizerCollector<'tcx> {
+ fn new(tcx: TyCtxt<'tcx>) -> DebuggerVisualizerCollector<'tcx> {
+ DebuggerVisualizerCollector { tcx, debugger_visualizers: FxHashSet::default() }
+ }
+
+ fn check_for_debugger_visualizer(&mut self, hir_id: HirId) {
+ let attrs = self.tcx.hir().attrs(hir_id);
+ for attr in attrs {
+ if attr.has_name(sym::debugger_visualizer) {
+ let list = match attr.meta_item_list() {
+ Some(list) => list,
+ _ => continue,
+ };
+
+ let meta_item = match list.len() {
+ 1 => match list[0].meta_item() {
+ Some(meta_item) => meta_item,
+ _ => continue,
+ },
+ _ => continue,
+ };
+
+ let file = match (meta_item.name_or_empty(), meta_item.value_str()) {
+ (sym::natvis_file, Some(value)) => {
+ match resolve_path(&self.tcx.sess.parse_sess, value.as_str(), attr.span) {
+ Ok(file) => file,
+ Err(mut err) => {
+ err.emit();
+ continue;
+ }
+ }
+ }
+ (_, _) => continue,
+ };
+
+ if file.is_file() {
+ let contents = match std::fs::read(&file) {
+ Ok(contents) => contents,
+ Err(err) => {
+ self.tcx
+ .sess
+ .struct_span_err(
+ attr.span,
+ &format!(
+ "Unable to read contents of file `{}`. {}",
+ file.display(),
+ err
+ ),
+ )
+ .emit();
+ continue;
+ }
+ };
+
+ self.debugger_visualizers.insert(DebuggerVisualizerFile::new(
+ Arc::from(contents),
+ DebuggerVisualizerType::Natvis,
+ ));
+ } else {
+ self.tcx
+ .sess
+ .struct_span_err(
+ attr.span,
+ &format!("{} is not a valid file", file.display()),
+ )
+ .emit();
+ }
+ }
+ }
+ }
+}
+
+/// Traverses and collects the debugger visualizers for a specific crate.
+fn debugger_visualizers<'tcx>(tcx: TyCtxt<'tcx>, cnum: CrateNum) -> Vec<DebuggerVisualizerFile> {
+ assert_eq!(cnum, LOCAL_CRATE);
+
+ // Initialize the collector.
+ let mut collector = DebuggerVisualizerCollector::new(tcx);
+
+ // Collect debugger visualizers in this crate.
+ tcx.hir().visit_all_item_likes(&mut collector);
+
+ // Collect debugger visualizers on the crate attributes.
+ collector.check_for_debugger_visualizer(CRATE_HIR_ID);
+
+ // Extract out the found debugger_visualizer items.
+ let DebuggerVisualizerCollector { debugger_visualizers, .. } = collector;
+
+ let mut visualizers = debugger_visualizers.into_iter().collect::<Vec<_>>();
+
+ // Sort the visualizers so we always get a deterministic query result.
+ visualizers.sort();
+ visualizers
+}
+
+pub fn provide(providers: &mut Providers) {
+ providers.debugger_visualizers = debugger_visualizers;
+}
mod check_attr;
mod check_const;
pub mod dead;
+mod debugger_visualizer;
mod diagnostic_items;
pub mod entry;
pub mod hir_id_validator;
check_attr::provide(providers);
check_const::provide(providers);
dead::provide(providers);
+ debugger_visualizer::provide(providers);
diagnostic_items::provide(providers);
entry::provide(providers);
lang_items::provide(providers);
current_where_predicate: Option<&'ast WherePredicate>,
current_type_path: Option<&'ast Ty>,
+
+ /// The current impl items (used to suggest).
+ current_impl_items: Option<&'ast [P<AssocItem>]>,
}
struct LateResolutionVisitor<'a, 'b, 'ast> {
items: ref impl_items,
..
}) => {
+ self.diagnostic_metadata.current_impl_items = Some(impl_items);
self.resolve_implementation(generics, of_trait, &self_ty, item.id, impl_items);
+ self.diagnostic_metadata.current_impl_items = None;
}
ItemKind::Trait(box Trait { ref generics, ref bounds, ref items, .. }) => {
);
}
+ fn resolve_inline_const(&mut self, constant: &'ast AnonConst) {
+ debug!("resolve_anon_const {constant:?}");
+ self.with_constant_rib(IsRepeatExpr::No, HasGenericParams::Yes, None, |this| {
+ visit::walk_anon_const(this, constant);
+ });
+ }
+
fn resolve_expr(&mut self, expr: &'ast Expr, parent: Option<&'ast Expr>) {
// First, record candidate traits for this expression if it could
// result in the invocation of a method call.
});
}
ExprKind::ConstBlock(ref ct) => {
- self.resolve_anon_const(ct, IsRepeatExpr::No);
+ self.resolve_inline_const(ct);
}
ExprKind::Index(ref elem, ref idx) => {
self.resolve_expr(elem, Some(expr));
use crate::{Module, ModuleKind, ModuleOrUniformRoot};
use crate::{PathResult, PathSource, Segment};
-use rustc_ast::visit::FnKind;
+use rustc_ast::visit::{FnCtxt, FnKind};
use rustc_ast::{
self as ast, AssocItemKind, Expr, ExprKind, GenericParam, GenericParamKind, Item, ItemKind,
NodeId, Path, Ty, TyKind,
let is_enum_variant = &|res| matches!(res, Res::Def(DefKind::Variant, _));
// Make the base error.
+ struct BaseError<'a> {
+ msg: String,
+ fallback_label: String,
+ span: Span,
+ could_be_expr: bool,
+ suggestion: Option<(Span, &'a str, String)>,
+ }
let mut expected = source.descr_expected();
let path_str = Segment::names_to_string(path);
let item_str = path.last().unwrap().ident;
- let (base_msg, fallback_label, base_span, could_be_expr) = if let Some(res) = res {
- (
- format!("expected {}, found {} `{}`", expected, res.descr(), path_str),
- format!("not a {}", expected),
+ let base_error = if let Some(res) = res {
+ BaseError {
+ msg: format!("expected {}, found {} `{}`", expected, res.descr(), path_str),
+ fallback_label: format!("not a {expected}"),
span,
- match res {
+ could_be_expr: match res {
Res::Def(DefKind::Fn, _) => {
// Verify whether this is a fn call or an Fn used as a type.
self.r
| Res::Local(_) => true,
_ => false,
},
- )
+ suggestion: None,
+ }
} else {
let item_span = path.last().unwrap().ident.span;
- let (mod_prefix, mod_str) = if path.len() == 1 {
- (String::new(), "this scope".to_string())
+ let (mod_prefix, mod_str, suggestion) = if path.len() == 1 {
+ debug!(?self.diagnostic_metadata.current_impl_items);
+ debug!(?self.diagnostic_metadata.current_function);
+ let suggestion = if let Some(items) = self.diagnostic_metadata.current_impl_items
+ && let Some((fn_kind, _)) = self.diagnostic_metadata.current_function
+ && self.current_trait_ref.is_none()
+ && let Some(FnCtxt::Assoc(_)) = fn_kind.ctxt()
+ && let Some(item) = items.iter().find(|i| {
+ if let AssocItemKind::Fn(fn_) = &i.kind
+ && !fn_.sig.decl.has_self()
+ && i.ident.name == item_str.name
+ {
+ debug!(?item_str.name);
+ debug!(?fn_.sig.decl.inputs);
+ return true
+ }
+ false
+ })
+ {
+ Some((
+ item_span,
+ "consider using the associated function",
+ format!("Self::{}", item.ident)
+ ))
+ } else {
+ None
+ };
+ (String::new(), "this scope".to_string(), suggestion)
} else if path.len() == 2 && path[0].ident.name == kw::PathRoot {
if self.r.session.edition() > Edition::Edition2015 {
// In edition 2018 onwards, the `::foo` syntax may only pull from the extern prelude
// which overrides all other expectations of item type
expected = "crate";
- (String::new(), "the list of imported crates".to_string())
+ (String::new(), "the list of imported crates".to_string(), None)
} else {
- (String::new(), "the crate root".to_string())
+ (String::new(), "the crate root".to_string(), None)
}
} else if path.len() == 2 && path[0].ident.name == kw::Crate {
- (String::new(), "the crate root".to_string())
+ (String::new(), "the crate root".to_string(), None)
} else {
let mod_path = &path[..path.len() - 1];
let mod_prefix = match self.resolve_path(mod_path, Some(TypeNS), None) {
_ => None,
}
.map_or_else(String::new, |res| format!("{} ", res.descr()));
- (mod_prefix, format!("`{}`", Segment::names_to_string(mod_path)))
+ (mod_prefix, format!("`{}`", Segment::names_to_string(mod_path)), None)
};
- (
- format!("cannot find {} `{}` in {}{}", expected, item_str, mod_prefix, mod_str),
- if path_str == "async" && expected.starts_with("struct") {
+ BaseError {
+ msg: format!("cannot find {expected} `{item_str}` in {mod_prefix}{mod_str}"),
+ fallback_label: if path_str == "async" && expected.starts_with("struct") {
"`async` blocks are only allowed in Rust 2018 or later".to_string()
} else {
- format!("not found in {}", mod_str)
+ format!("not found in {mod_str}")
},
- item_span,
- false,
- )
+ span: item_span,
+ could_be_expr: false,
+ suggestion,
+ }
};
let code = source.error_code(res.is_some());
- let mut err = self.r.session.struct_span_err_with_code(base_span, &base_msg, code);
+ let mut err =
+ self.r.session.struct_span_err_with_code(base_error.span, &base_error.msg, code);
+
+ if let Some(sugg) = base_error.suggestion {
+ err.span_suggestion_verbose(sugg.0, sugg.1, sugg.2, Applicability::MaybeIncorrect);
+ }
if let Some(span) = self.diagnostic_metadata.current_block_could_be_bare_struct_literal {
err.multipart_suggestion(
}
}
- self.detect_assoct_type_constraint_meant_as_path(base_span, &mut err);
+ self.detect_assoct_type_constraint_meant_as_path(base_error.span, &mut err);
// Emit special messages for unresolved `Self` and `self`.
if is_self_type(path, ns) {
source,
res,
&path_str,
- &fallback_label,
+ &base_error.fallback_label,
) {
// We do this to avoid losing a secondary span when we override the main error span.
self.r.add_typo_suggestion(&mut err, typo_sugg, ident_span);
}
}
- let is_macro = base_span.from_expansion() && base_span.desugaring_kind().is_none();
- if !self.type_ascription_suggestion(&mut err, base_span) {
+ let is_macro =
+ base_error.span.from_expansion() && base_error.span.desugaring_kind().is_none();
+ if !self.type_ascription_suggestion(&mut err, base_error.span) {
let mut fallback = false;
if let (
PathSource::Trait(AliasPossibility::Maybe),
let spans: Vec<Span> = bounds
.iter()
.map(|bound| bound.span())
- .filter(|&sp| sp != base_span)
+ .filter(|&sp| sp != base_error.span)
.collect();
let start_span = bounds.iter().map(|bound| bound.span()).next().unwrap();
multi_span.push_span_label(sp, msg);
}
multi_span.push_span_label(
- base_span,
+ base_error.span,
"expected this type to be a trait...".to_string(),
);
err.span_help(
);
if bounds.iter().all(|bound| match bound {
ast::GenericBound::Outlives(_) => true,
- ast::GenericBound::Trait(tr, _) => tr.span == base_span,
+ ast::GenericBound::Trait(tr, _) => tr.span == base_error.span,
}) {
let mut sugg = vec![];
- if base_span != start_span {
- sugg.push((start_span.until(base_span), String::new()));
+ if base_error.span != start_span {
+ sugg.push((start_span.until(base_error.span), String::new()));
}
- if base_span != end_span {
- sugg.push((base_span.shrink_to_hi().to(end_span), String::new()));
+ if base_error.span != end_span {
+ sugg.push((base_error.span.shrink_to_hi().to(end_span), String::new()));
}
err.multipart_suggestion(
fallback = true;
match self.diagnostic_metadata.current_let_binding {
Some((pat_sp, Some(ty_sp), None))
- if ty_sp.contains(base_span) && could_be_expr =>
+ if ty_sp.contains(base_error.span) && base_error.could_be_expr =>
{
err.span_suggestion_short(
pat_sp.between(ty_sp),
}
if fallback {
// Fallback label.
- err.span_label(base_span, fallback_label);
+ err.span_label(base_error.span, base_error.fallback_label);
}
}
if let Some(err_code) = &err.code {
}
#[inline]
+ #[track_caller]
pub fn expect_local(self) -> LocalDefId {
self.as_local().unwrap_or_else(|| panic!("DefId::expect_local: `{:?}` isn't local", self))
}
use std::ops::{Add, Range, Sub};
use std::path::{Path, PathBuf};
use std::str::FromStr;
+use std::sync::Arc;
use md5::Digest;
use md5::Md5;
}
}
+#[derive(HashStable_Generic)]
+#[derive(Copy, PartialEq, PartialOrd, Clone, Ord, Eq, Hash, Debug, Encodable, Decodable)]
+pub enum DebuggerVisualizerType {
+ Natvis,
+}
+
+/// A single debugger visualizer file.
+#[derive(HashStable_Generic)]
+#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Encodable, Decodable)]
+pub struct DebuggerVisualizerFile {
+ /// The complete debugger visualizer source.
+ pub src: Arc<[u8]>,
+ /// Indicates which visualizer type this targets.
+ pub visualizer_type: DebuggerVisualizerType,
+}
+
+impl DebuggerVisualizerFile {
+ pub fn new(src: Arc<[u8]>, visualizer_type: DebuggerVisualizerType) -> Self {
+ DebuggerVisualizerFile { src, visualizer_type }
+ }
+}
+
/// A single source in the [`SourceMap`].
#[derive(Clone)]
pub struct SourceFile {
debug_struct,
debug_trait_builder,
debug_tuple,
+ debugger_visualizer,
decl_macro,
declare_lint_pass,
decode,
native_link_modifiers_bundle,
native_link_modifiers_verbatim,
native_link_modifiers_whole_archive,
+ natvis_file,
ne,
nearbyintf32,
nearbyintf64,
rustc_error,
rustc_evaluate_where_clauses,
rustc_expected_cgu_reuse,
+ rustc_has_incoherent_inherent_impls,
rustc_if_this_changed,
rustc_inherit_overflow_checks,
rustc_insignificant_dtor,
use rustc_span::lev_distance::{
find_best_match_for_name_with_substrings, lev_distance_with_substrings,
};
+use rustc_span::symbol::sym;
use rustc_span::{symbol::Ident, Span, Symbol, DUMMY_SP};
use rustc_trait_selection::autoderef::{self, Autoderef};
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
self.assemble_inherent_candidates_from_object(generalized_self_ty);
self.assemble_inherent_impl_candidates_for_type(p.def_id());
+ if self.tcx.has_attr(p.def_id(), sym::rustc_has_incoherent_inherent_impls) {
+ self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty);
+ }
}
ty::Adt(def, _) => {
let def_id = def.did();
self.assemble_inherent_impl_candidates_for_type(def_id);
- if Some(def_id) == self.tcx.lang_items().c_str() {
+ if self.tcx.has_attr(def_id, sym::rustc_has_incoherent_inherent_impls) {
self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty);
}
}
ty::Foreign(did) => {
self.assemble_inherent_impl_candidates_for_type(did);
+ if self.tcx.has_attr(did, sym::rustc_has_incoherent_inherent_impls) {
+ self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty);
+ }
}
ty::Param(p) => {
self.assemble_inherent_candidates_from_param(p);
let self_ty = self.tcx.type_of(item.def_id);
match *self_ty.kind() {
ty::Adt(def, _) => {
- let def_id = def.did();
- if !def_id.is_local() && Some(def_id) == self.tcx.lang_items().c_str() {
- self.check_primitive_impl(item.def_id, self_ty, items, ty.span)
- } else {
- self.check_def_id(item, def_id);
- }
+ self.check_def_id(item, self_ty, def.did());
}
ty::Foreign(did) => {
- self.check_def_id(item, did);
+ self.check_def_id(item, self_ty, did);
}
ty::Dynamic(data, ..) if data.principal_def_id().is_some() => {
- self.check_def_id(item, data.principal_def_id().unwrap());
+ self.check_def_id(item, self_ty, data.principal_def_id().unwrap());
}
ty::Dynamic(..) => {
struct_span_err!(
fn visit_foreign_item(&mut self, _foreign_item: &hir::ForeignItem<'_>) {}
}
+const INTO_CORE: &str = "consider moving this inherent impl into `core` if possible";
+const INTO_DEFINING_CRATE: &str =
+ "consider moving this inherent impl into the crate defining the type if possible";
+const ADD_ATTR_TO_TY: &str = "alternatively add `#[rustc_has_incoherent_inherent_impls]` to the type \
+ and `#[rustc_allow_incoherent_impl]` to the relevant impl items";
+const ADD_ATTR: &str =
+ "alternatively add `#[rustc_allow_incoherent_impl]` to the relevant impl items";
+
impl<'tcx> InherentCollect<'tcx> {
- fn check_def_id(&mut self, item: &hir::Item<'_>, def_id: DefId) {
+ fn check_def_id(&mut self, item: &hir::Item<'_>, self_ty: Ty<'tcx>, def_id: DefId) {
+ let impl_def_id = item.def_id;
if let Some(def_id) = def_id.as_local() {
// Add the implementation to the mapping from implementation to base
// type def ID, if there is a base type for this implementation and
// the implementation does not have any associated traits.
let vec = self.impls_map.inherent_impls.entry(def_id).or_default();
- vec.push(item.def_id.to_def_id());
+ vec.push(impl_def_id.to_def_id());
+ return;
+ }
+
+ if self.tcx.features().rustc_attrs {
+ let hir::ItemKind::Impl(&hir::Impl { items, .. }) = item.kind else {
+ bug!("expected `impl` item: {:?}", item);
+ };
+
+ if !self.tcx.has_attr(def_id, sym::rustc_has_incoherent_inherent_impls) {
+ struct_span_err!(
+ self.tcx.sess,
+ item.span,
+ E0390,
+ "cannot define inherent `impl` for a type outside of the crate where the type is defined",
+ )
+ .help(INTO_DEFINING_CRATE)
+ .span_help(item.span, ADD_ATTR_TO_TY)
+ .emit();
+ return;
+ }
+
+ for impl_item in items {
+ if !self
+ .tcx
+ .has_attr(impl_item.id.def_id.to_def_id(), sym::rustc_allow_incoherent_impl)
+ {
+ struct_span_err!(
+ self.tcx.sess,
+ item.span,
+ E0390,
+ "cannot define inherent `impl` for a type outside of the crate where the type is defined",
+ )
+ .help(INTO_DEFINING_CRATE)
+ .span_help(impl_item.span, ADD_ATTR)
+ .emit();
+ return;
+ }
+ }
+
+ if let Some(simp) = simplify_type(self.tcx, self_ty, TreatParams::AsPlaceholders) {
+ self.impls_map.incoherent_impls.entry(simp).or_default().push(impl_def_id);
+ } else {
+ bug!("unexpected self type: {:?}", self_ty);
+ }
} else {
struct_span_err!(
self.tcx.sess,
items: &[hir::ImplItemRef],
span: Span,
) {
- const INTO_CORE: &str = "consider moving this inherent impl into `core` if possible";
- const ADD_ATTR: &str =
- "alternatively add `#[rustc_allow_incoherent_impl]` to the relevant impl items";
if !self.tcx.hir().rustc_coherence_is_core() {
if self.tcx.features().rustc_attrs {
for item in items {
};
let sp = tcx.sess.source_map().guess_head_span(item.span);
let tr = impl_.of_trait.as_ref().unwrap();
+
+ // Ensure no opaque types are present in this impl header. See issues #76202 and #86411 for examples,
+ // and #84660 where it would otherwise allow unsoundness.
+ if trait_ref.has_opaque_types() {
+ trace!("{:#?}", item);
+ // First we find the opaque type in question.
+ for ty in trait_ref.substs {
+ for ty in ty.walk() {
+ let ty::subst::GenericArgKind::Type(ty) = ty.unpack() else { continue };
+ let ty::Opaque(def_id, _) = *ty.kind() else { continue };
+ trace!(?def_id);
+
+ // Then we search for mentions of the opaque type's type alias in the HIR
+ struct SpanFinder<'tcx> {
+ sp: Span,
+ def_id: DefId,
+ tcx: TyCtxt<'tcx>,
+ }
+ impl<'v, 'tcx> hir::intravisit::Visitor<'v> for SpanFinder<'tcx> {
+ #[instrument(level = "trace", skip(self, _id))]
+ fn visit_path(&mut self, path: &'v hir::Path<'v>, _id: hir::HirId) {
+ // You can't mention an opaque type directly, so we look for type aliases
+ if let hir::def::Res::Def(hir::def::DefKind::TyAlias, def_id) = path.res {
+ // And check if that type alias's type contains the opaque type we're looking for
+ for arg in self.tcx.type_of(def_id).walk() {
+ if let GenericArgKind::Type(ty) = arg.unpack() {
+ if let ty::Opaque(def_id, _) = *ty.kind() {
+ if def_id == self.def_id {
+ // Finally we update the span to the mention of the type alias
+ self.sp = path.span;
+ return;
+ }
+ }
+ }
+ }
+ }
+ hir::intravisit::walk_path(self, path)
+ }
+ }
+
+ let mut visitor = SpanFinder { sp, def_id, tcx };
+ hir::intravisit::walk_item(&mut visitor, item);
+ let reported = tcx
+ .sess
+ .struct_span_err(visitor.sp, "cannot implement trait on type alias impl trait")
+ .span_note(tcx.def_span(def_id), "type alias impl trait defined here")
+ .emit();
+ return Err(reported);
+ }
+ }
+ span_bug!(sp, "opaque type not found, but `has_opaque_types` is set")
+ }
+
match traits::orphan_check(tcx, item.def_id.to_def_id()) {
Ok(()) => {}
Err(err) => emit_orphan_check_error(
}
}
- // Ensure no opaque types are present in this impl header. See issues #76202 and #86411 for examples,
- // and #84660 where it would otherwise allow unsoundness.
- if trait_ref.has_opaque_types() {
- trace!("{:#?}", item);
- // First we find the opaque type in question.
- for ty in trait_ref.substs {
- for ty in ty.walk() {
- let ty::subst::GenericArgKind::Type(ty) = ty.unpack() else { continue };
- let ty::Opaque(def_id, _) = *ty.kind() else { continue };
- trace!(?def_id);
-
- // Then we search for mentions of the opaque type's type alias in the HIR
- struct SpanFinder<'tcx> {
- sp: Span,
- def_id: DefId,
- tcx: TyCtxt<'tcx>,
- }
- impl<'v, 'tcx> hir::intravisit::Visitor<'v> for SpanFinder<'tcx> {
- #[instrument(level = "trace", skip(self, _id))]
- fn visit_path(&mut self, path: &'v hir::Path<'v>, _id: hir::HirId) {
- // You can't mention an opaque type directly, so we look for type aliases
- if let hir::def::Res::Def(hir::def::DefKind::TyAlias, def_id) = path.res {
- // And check if that type alias's type contains the opaque type we're looking for
- for arg in self.tcx.type_of(def_id).walk() {
- if let GenericArgKind::Type(ty) = arg.unpack() {
- if let ty::Opaque(def_id, _) = *ty.kind() {
- if def_id == self.def_id {
- // Finally we update the span to the mention of the type alias
- self.sp = path.span;
- return;
- }
- }
- }
- }
- }
- hir::intravisit::walk_path(self, path)
- }
- }
-
- let mut visitor = SpanFinder { sp, def_id, tcx };
- hir::intravisit::walk_item(&mut visitor, item);
- let reported = tcx
- .sess
- .struct_span_err(visitor.sp, "cannot implement trait on type alias impl trait")
- .span_note(tcx.def_span(def_id), "type alias impl trait defined here")
- .emit();
- return Err(reported);
- }
- }
- span_bug!(sp, "opaque type not found, but `has_opaque_types` is set")
- }
-
Ok(())
}
impl<'tcx> TypeVisitor<'tcx> for ParameterCollector {
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
match *t.kind() {
- ty::Projection(..) | ty::Opaque(..) if !self.include_nonconstraining => {
+ ty::Projection(..) if !self.include_nonconstraining => {
// projections are not injective
return ControlFlow::CONTINUE;
}
fn write_isize(&mut self, i: isize) {
(**self).write_isize(i)
}
+ fn write_length_prefix(&mut self, len: usize) {
+ (**self).write_length_prefix(len)
+ }
+ fn write_str(&mut self, s: &str) {
+ (**self).write_str(s)
+ }
}
#[cfg(not(no_global_oom_handling))]
#[stable(feature = "rust1", since = "1.0.0")]
impl<K: Hash, V: Hash> Hash for BTreeMap<K, V> {
fn hash<H: Hasher>(&self, state: &mut H) {
- self.len().hash(state);
+ state.write_length_prefix(self.len());
for elt in self {
elt.hash(state);
}
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: Hash> Hash for LinkedList<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
- self.len().hash(state);
+ state.write_length_prefix(self.len());
for elt in self {
elt.hash(state);
}
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: Hash, A: Allocator> Hash for VecDeque<T, A> {
fn hash<H: Hasher>(&self, state: &mut H) {
- self.len().hash(state);
+ state.write_length_prefix(self.len());
// It's not possible to use Hash::hash_slice on slices
// returned by as_slices method as their length can vary
// in otherwise identical deques.
#![feature(extend_one)]
#![feature(fmt_internals)]
#![feature(fn_traits)]
+#![feature(hasher_prefixfree_extras)]
#![feature(inplace_iteration)]
#![feature(iter_advance_by)]
#![feature(layout_for_ptr)]
}
}
- /// Constructs a new `Rc<T>` using a closure `data_fn` that has access to a
- /// weak reference to the constructing `Rc<T>`.
+ /// Constructs a new `Rc<T>` while giving you a `Weak<T>` to the allocation,
+ /// to allow you to construct a `T` which holds a weak pointer to itself.
///
/// Generally, a structure circularly referencing itself, either directly or
- /// indirectly, should not hold a strong reference to prevent a memory leak.
- /// In `data_fn`, initialization of `T` can make use of the weak reference
- /// by cloning and storing it inside `T` for use at a later time.
+ /// indirectly, should not hold a strong reference to itself to prevent a memory leak.
+ /// Using this function, you get access to the weak pointer during the
+ /// initialization of `T`, before the `Rc<T>` is created, such that you can
+ /// clone and store it inside the `T`.
+ ///
+ /// `new_cyclic` first allocates the managed allocation for the `Rc<T>`,
+ /// then calls your closure, giving it a `Weak<T>` to this allocation,
+ /// and only afterwards completes the construction of the `Rc<T>` by placing
+ /// the `T` returned from your closure into the allocation.
///
/// Since the new `Rc<T>` is not fully-constructed until `Rc<T>::new_cyclic`
- /// returns, calling [`upgrade`] on the weak reference inside `data_fn` will
+ /// returns, calling [`upgrade`] on the weak reference inside your closure will
/// fail and result in a `None` value.
///
/// # Panics
+ ///
/// If `data_fn` panics, the panic is propagated to the caller, and the
/// temporary [`Weak<T>`] is dropped normally.
///
/// impl Gadget {
/// /// Construct a reference counted Gadget.
/// fn new() -> Rc<Self> {
- /// Rc::new_cyclic(|me| Gadget { me: me.clone() })
+ /// // `me` is a `Weak<Gadget>` pointing at the new allocation of the
+ /// // `Rc` we're constructing.
+ /// Rc::new_cyclic(|me| {
+ /// // Create the actual struct here.
+ /// Gadget { me: me.clone() }
+ /// })
/// }
///
/// /// Return a reference counted pointer to Self.
#[cfg(not(no_global_oom_handling))]
use core::slice::from_raw_parts_mut;
use core::sync::atomic;
-use core::sync::atomic::Ordering::{Acquire, Relaxed, Release, SeqCst};
+use core::sync::atomic::Ordering::{Acquire, Relaxed, Release};
#[cfg(not(no_global_oom_handling))]
use crate::alloc::handle_alloc_error;
unsafe { Self::from_inner(Box::leak(x).into()) }
}
- /// Constructs a new `Arc<T>` using a closure `data_fn` that has access to
- /// a weak reference to the constructing `Arc<T>`.
+ /// Constructs a new `Arc<T>` while giving you a `Weak<T>` to the allocation,
+ /// to allow you to construct a `T` which holds a weak pointer to itself.
///
/// Generally, a structure circularly referencing itself, either directly or
- /// indirectly, should not hold a strong reference to prevent a memory leak.
- /// In `data_fn`, initialization of `T` can make use of the weak reference
- /// by cloning and storing it inside `T` for use at a later time.
+ /// indirectly, should not hold a strong reference to itself to prevent a memory leak.
+ /// Using this function, you get access to the weak pointer during the
+ /// initialization of `T`, before the `Arc<T>` is created, such that you can
+ /// clone and store it inside the `T`.
///
- /// Since the new `Arc<T>` is not fully-constructed until
- /// `Arc<T>::new_cyclic` returns, calling [`upgrade`] on the weak
- /// reference inside `data_fn` will fail and result in a `None` value.
+ /// `new_cyclic` first allocates the managed allocation for the `Arc<T>`,
+ /// then calls your closure, giving it a `Weak<T>` to this allocation,
+ /// and only afterwards completes the construction of the `Arc<T>` by placing
+ /// the `T` returned from your closure into the allocation.
+ ///
+ /// Since the new `Arc<T>` is not fully-constructed until `Arc<T>::new_cyclic`
+ /// returns, calling [`upgrade`] on the weak reference inside your closure will
+ /// fail and result in a `None` value.
///
/// # Panics
+ ///
/// If `data_fn` panics, the panic is propagated to the caller, and the
/// temporary [`Weak<T>`] is dropped normally.
///
/// # Example
+ ///
/// ```
/// # #![allow(dead_code)]
/// use std::sync::{Arc, Weak};
/// impl Gadget {
/// /// Construct a reference counted Gadget.
/// fn new() -> Arc<Self> {
- /// Arc::new_cyclic(|me| Gadget { me: me.clone() })
+ /// // `me` is a `Weak<Gadget>` pointing at the new allocation of the
+ /// // `Arc` we're constructing.
+ /// Arc::new_cyclic(|me| {
+ /// // Create the actual struct here.
+ /// Gadget { me: me.clone() }
+ /// })
/// }
///
/// /// Return a reference counted pointer to Self.
#[must_use]
#[stable(feature = "arc_counts", since = "1.15.0")]
pub fn weak_count(this: &Self) -> usize {
- let cnt = this.inner().weak.load(SeqCst);
+ let cnt = this.inner().weak.load(Acquire);
// If the weak count is currently locked, the value of the
// count was 0 just before taking the lock.
if cnt == usize::MAX { 0 } else { cnt - 1 }
#[must_use]
#[stable(feature = "arc_counts", since = "1.15.0")]
pub fn strong_count(this: &Self) -> usize {
- this.inner().strong.load(SeqCst)
+ this.inner().strong.load(Acquire)
}
/// Increments the strong reference count on the `Arc<T>` associated with the
#[must_use]
#[stable(feature = "weak_counts", since = "1.41.0")]
pub fn strong_count(&self) -> usize {
- if let Some(inner) = self.inner() { inner.strong.load(SeqCst) } else { 0 }
+ if let Some(inner) = self.inner() { inner.strong.load(Acquire) } else { 0 }
}
/// Gets an approximation of the number of `Weak` pointers pointing to this
pub fn weak_count(&self) -> usize {
self.inner()
.map(|inner| {
- let weak = inner.weak.load(SeqCst);
- let strong = inner.strong.load(SeqCst);
+ let weak = inner.weak.load(Acquire);
+ let strong = inner.strong.load(Acquire);
if strong == 0 {
0
} else {
#[derive(Hash)]
#[cfg_attr(not(test), rustc_diagnostic_item = "CStr")]
#[unstable(feature = "core_c_str", issue = "94079")]
-#[cfg_attr(not(bootstrap), lang = "CStr")]
+#[cfg_attr(not(bootstrap), rustc_has_incoherent_inherent_impls)]
// FIXME:
// `fn from` in `impl From<&CStr> for Box<CStr>` current implementation relies
// on `CStr` being layout-compatible with `[u8]`.
///
/// println!("Hash is {:x}!", hasher.finish());
/// ```
+ ///
+ /// # Note to Implementers
+ ///
+ /// You generally should not do length-prefixing as part of implementing
+ /// this method. It's up to the [`Hash`] implementation to call
+ /// [`Hasher::write_length_prefix`] before sequences that need it.
#[stable(feature = "rust1", since = "1.0.0")]
fn write(&mut self, bytes: &[u8]);
fn write_isize(&mut self, i: isize) {
self.write_usize(i as usize)
}
+
+ /// Writes a length prefix into this hasher, as part of being prefix-free.
+ ///
+ /// If you're implementing [`Hash`] for a custom collection, call this before
+ /// writing its contents to this `Hasher`. That way
+ /// `(collection![1, 2, 3], collection![4, 5])` and
+ /// `(collection![1, 2], collection![3, 4, 5])` will provide different
+ /// sequences of values to the `Hasher`
+ ///
+ /// The `impl<T> Hash for [T]` includes a call to this method, so if you're
+ /// hashing a slice (or array or vector) via its `Hash::hash` method,
+ /// you should **not** call this yourself.
+ ///
+ /// This method is only for providing domain separation. If you want to
+ /// hash a `usize` that represents part of the *data*, then it's important
+ /// that you pass it to [`Hasher::write_usize`] instead of to this method.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(hasher_prefixfree_extras)]
+ /// # // Stubs to make the `impl` below pass the compiler
+ /// # struct MyCollection<T>(Option<T>);
+ /// # impl<T> MyCollection<T> {
+ /// # fn len(&self) -> usize { todo!() }
+ /// # }
+ /// # impl<'a, T> IntoIterator for &'a MyCollection<T> {
+ /// # type Item = T;
+ /// # type IntoIter = std::iter::Empty<T>;
+ /// # fn into_iter(self) -> Self::IntoIter { todo!() }
+ /// # }
+ ///
+ /// use std::hash::{Hash, Hasher};
+ /// impl<T: Hash> Hash for MyCollection<T> {
+ /// fn hash<H: Hasher>(&self, state: &mut H) {
+ /// state.write_length_prefix(self.len());
+ /// for elt in self {
+ /// elt.hash(state);
+ /// }
+ /// }
+ /// }
+ /// ```
+ ///
+ /// # Note to Implementers
+ ///
+ /// If you've decided that your `Hasher` is willing to be susceptible to
+ /// Hash-DoS attacks, then you might consider skipping hashing some or all
+ /// of the `len` provided in the name of increased performance.
+ #[inline]
+ #[unstable(feature = "hasher_prefixfree_extras", issue = "96762")]
+ fn write_length_prefix(&mut self, len: usize) {
+ self.write_usize(len);
+ }
+
+ /// Writes a single `str` into this hasher.
+ ///
+ /// If you're implementing [`Hash`], you generally do not need to call this,
+ /// as the `impl Hash for str` does, so you should prefer that instead.
+ ///
+ /// This includes the domain separator for prefix-freedom, so you should
+ /// **not** call `Self::write_length_prefix` before calling this.
+ ///
+ /// # Note to Implementers
+ ///
+ /// There are at least two reasonable default ways to implement this.
+ /// Which one will be the default is not yet decided, so for now
+ /// you probably want to override it specifically.
+ ///
+ /// ## The general answer
+ ///
+ /// It's always correct to implement this with a length prefix:
+ ///
+ /// ```
+ /// # #![feature(hasher_prefixfree_extras)]
+ /// # struct Foo;
+ /// # impl std::hash::Hasher for Foo {
+ /// # fn finish(&self) -> u64 { unimplemented!() }
+ /// # fn write(&mut self, _bytes: &[u8]) { unimplemented!() }
+ /// fn write_str(&mut self, s: &str) {
+ /// self.write_length_prefix(s.len());
+ /// self.write(s.as_bytes());
+ /// }
+ /// # }
+ /// ```
+ ///
+ /// And, if your `Hasher` works in `usize` chunks, this is likely a very
+ /// efficient way to do it, as anything more complicated may well end up
+ /// slower than just running the round with the length.
+ ///
+ /// ## If your `Hasher` works byte-wise
+ ///
+ /// One nice thing about `str` being UTF-8 is that the `b'\xFF'` byte
+ /// never happens. That means that you can append that to the byte stream
+ /// being hashed and maintain prefix-freedom:
+ ///
+ /// ```
+ /// # #![feature(hasher_prefixfree_extras)]
+ /// # struct Foo;
+ /// # impl std::hash::Hasher for Foo {
+ /// # fn finish(&self) -> u64 { unimplemented!() }
+ /// # fn write(&mut self, _bytes: &[u8]) { unimplemented!() }
+ /// fn write_str(&mut self, s: &str) {
+ /// self.write(s.as_bytes());
+ /// self.write_u8(0xff);
+ /// }
+ /// # }
+ /// ```
+ ///
+ /// This does require that your implementation not add extra padding, and
+ /// thus generally requires that you maintain a buffer, running a round
+ /// only once that buffer is full (or `finish` is called).
+ ///
+ /// That's because if `write` pads data out to a fixed chunk size, it's
+ /// likely that it does it in such a way that `"a"` and `"a\x00"` would
+ /// end up hashing the same sequence of things, introducing conflicts.
+ #[inline]
+ #[unstable(feature = "hasher_prefixfree_extras", issue = "96762")]
+ fn write_str(&mut self, s: &str) {
+ self.write(s.as_bytes());
+ self.write_u8(0xff);
+ }
}
#[stable(feature = "indirect_hasher_impl", since = "1.22.0")]
fn write_isize(&mut self, i: isize) {
(**self).write_isize(i)
}
+ fn write_length_prefix(&mut self, len: usize) {
+ (**self).write_length_prefix(len)
+ }
+ fn write_str(&mut self, s: &str) {
+ (**self).write_str(s)
+ }
}
/// A trait for creating instances of [`Hasher`].
impl Hash for str {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
- state.write(self.as_bytes());
- state.write_u8(0xff)
+ state.write_str(self);
}
}
impl<T: Hash> Hash for [T] {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
- self.len().hash(state);
+ state.write_length_prefix(self.len());
Hash::hash_slice(self, state)
}
}
self.0.hasher.write(msg)
}
+ #[inline]
+ fn write_str(&mut self, s: &str) {
+ self.0.hasher.write_str(s);
+ }
+
#[inline]
fn finish(&self) -> u64 {
self.0.hasher.finish()
self.hasher.write(msg)
}
+ #[inline]
+ fn write_str(&mut self, s: &str) {
+ self.hasher.write_str(s);
+ }
+
#[inline]
fn finish(&self) -> u64 {
self.hasher.finish()
self.ntail = left;
}
+ #[inline]
+ fn write_str(&mut self, s: &str) {
+ // This hasher works byte-wise, and `0xFF` cannot show up in a `str`,
+ // so just hashing the one extra byte is enough to be prefix-free.
+ self.write(s.as_bytes());
+ self.write_u8(0xFF);
+ }
+
#[inline]
fn finish(&self) -> u64 {
let mut state = self.state;
///
/// # Example
///
-/// ```
+/// ```ignore(cannot-test-this-because-non-exported-macro)
/// cfg_if! {
/// if #[cfg(unix)] {
/// fn foo() { /* unix specific functionality */ }
///
/// Turning a pointer into a `usize`:
///
- /// ```
+ /// ```no_run
/// let ptr = &0;
/// let ptr_num_transmute = unsafe {
/// std::mem::transmute::<&i32, usize>(ptr)
///
/// # Panics
///
- /// This function will panic if `rhs` is 0 or the division results in overflow.
+ /// This function will panic if `rhs` is zero.
+ ///
+ /// ## Overflow behavior
+ ///
+ /// On overflow, this function will panic if overflow checks are enabled (default in debug
+ /// mode) and wrap if overflow checks are disabled (default in release mode).
///
/// # Examples
///
///
/// # Panics
///
- /// This function will panic if `rhs` is 0 or the division results in overflow.
+ /// This function will panic if `rhs` is zero.
+ ///
+ /// ## Overflow behavior
+ ///
+ /// On overflow, this function will panic if overflow checks are enabled (default in debug
+ /// mode) and wrap if overflow checks are disabled (default in release mode).
///
/// # Examples
///
///
/// # Panics
///
- /// This function will panic if `rhs` is 0 or the operation results in overflow.
+ /// This function will panic if `rhs` is zero.
+ ///
+ /// ## Overflow behavior
+ ///
+ /// On overflow, this function will panic if overflow checks are enabled (default in debug
+ /// mode) and wrap if overflow checks are disabled (default in release mode).
///
/// # Examples
///
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
- #[rustc_inherit_overflow_checks]
pub const fn checked_next_multiple_of(self, rhs: Self) -> Option<Self> {
// This would otherwise fail when calculating `r` when self == T::MIN.
if rhs == -1 {
///
/// # Panics
///
- /// This function will panic if `rhs` is 0.
+ /// This function will panic if `rhs` is zero.
///
/// # Examples
///
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline(always)]
- #[rustc_inherit_overflow_checks]
pub const fn div_floor(self, rhs: Self) -> Self {
self / rhs
}
///
/// # Panics
///
- /// This function will panic if `rhs` is 0.
+ /// This function will panic if `rhs` is zero.
+ ///
+ /// ## Overflow behavior
+ ///
+ /// On overflow, this function will panic if overflow checks are enabled (default in debug
+ /// mode) and wrap if overflow checks are disabled (default in release mode).
///
/// # Examples
///
///
/// # Panics
///
- /// This function will panic if `rhs` is 0 or the operation results in overflow.
+ /// This function will panic if `rhs` is zero.
+ ///
+ /// ## Overflow behavior
+ ///
+ /// On overflow, this function will panic if overflow checks are enabled (default in debug
+ /// mode) and wrap if overflow checks are disabled (default in release mode).
///
/// # Examples
///
}
/// Calculates the smallest value greater than or equal to `self` that
- /// is a multiple of `rhs`. Returns `None` is `rhs` is zero or the
+ /// is a multiple of `rhs`. Returns `None` if `rhs` is zero or the
/// operation would result in overflow.
///
/// # Examples
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
- #[rustc_inherit_overflow_checks]
pub const fn checked_next_multiple_of(self, rhs: Self) -> Option<Self> {
match try_opt!(self.checked_rem(rhs)) {
0 => Some(self),
}
/// Calculates the distance between two pointers. The returned value is in
- /// units of T: the distance in bytes is divided by `mem::size_of::<T>()`.
+ /// units of T: the distance in bytes divided by `mem::size_of::<T>()`.
///
/// This function is the inverse of [`offset`].
///
}
/// Calculates the distance between two pointers. The returned value is in
- /// units of T: the distance in bytes is divided by `mem::size_of::<T>()`.
+ /// units of T: the distance in bytes divided by `mem::size_of::<T>()`.
///
/// This function is the inverse of [`offset`].
///
self.hash += *byte as u64;
}
}
+ fn write_str(&mut self, s: &str) {
+ self.write(s.as_bytes());
+ self.write_u8(0xFF);
+ }
fn finish(&self) -> u64 {
self.hash
}
#![feature(future_join)]
#![feature(future_poll_fn)]
#![feature(array_from_fn)]
+#![feature(hasher_prefixfree_extras)]
#![feature(hashmap_internals)]
#![feature(try_find)]
#![feature(inline_const)]
-int_module!(i128, i128);
+int_module!(i128);
-int_module!(i16, i16);
+int_module!(i16);
-int_module!(i32, i32);
+int_module!(i32);
#[test]
fn test_arith_operation() {
-int_module!(i64, i64);
+int_module!(i64);
-int_module!(i8, i8);
+int_module!(i8);
macro_rules! int_module {
- ($T:ident, $T_i:ident) => {
+ ($T:ident) => {
#[cfg(test)]
mod tests {
use core::ops::{BitAnd, BitOr, BitXor, Not, Shl, Shr};
- use core::$T_i::*;
+ use core::$T::*;
use crate::num;
-uint_module!(u128, u128);
+uint_module!(u128);
-uint_module!(u16, u16);
+uint_module!(u16);
-uint_module!(u32, u32);
+uint_module!(u32);
-uint_module!(u64, u64);
+uint_module!(u64);
-uint_module!(u8, u8);
+uint_module!(u8);
macro_rules! uint_module {
- ($T:ident, $T_i:ident) => {
+ ($T:ident) => {
#[cfg(test)]
mod tests {
use core::ops::{BitAnd, BitOr, BitXor, Not, Shl, Shr};
- use core::$T_i::*;
+ use core::$T::*;
use std::str::FromStr;
use crate::num;
/// `[ ... ]`
#[stable(feature = "proc_macro_lib2", since = "1.29.0")]
Bracket,
- /// `Ø ... Ø`
+ /// `/*«*/ ... /*»*/`
/// An invisible delimiter, that may, for example, appear around tokens coming from a
/// "macro variable" `$var`. It is important to preserve operator priorities in cases like
/// `$var * 3` where `$var` is `1 + 2`.
- /// Invisible delimiters might not survive roundtrip of a token stream through a string.
+ /// Invisible delimiters are not directly writable in normal Rust code except as comments.
+ /// Therefore, they might not survive a roundtrip of a token stream through a string.
#[stable(feature = "proc_macro_lib2", since = "1.29.0")]
None,
}
panic_unwind = { path = "../panic_unwind", optional = true }
panic_abort = { path = "../panic_abort" }
core = { path = "../core" }
-libc = { version = "0.2.116", default-features = false, features = ['rustc-dep-of-std'] }
+libc = { version = "0.2.125", default-features = false, features = ['rustc-dep-of-std'] }
compiler_builtins = { version = "0.1.71" }
profiler_builtins = { path = "../profiler_builtins", optional = true }
unwind = { path = "../unwind" }
#[stable(feature = "hashmap_default_hasher", since = "1.13.0")]
impl Hasher for DefaultHasher {
+ // The underlying `SipHasher13` doesn't override the other
+ // `write_*` methods, so it's ok not to forward them here.
+
#[inline]
fn write(&mut self, msg: &[u8]) {
self.0.write(msg)
}
+ #[inline]
+ fn write_str(&mut self, s: &str) {
+ self.0.write_str(s);
+ }
+
#[inline]
fn finish(&self) -> u64 {
self.0.finish()
}
}
+#[unstable(feature = "slice_concat_ext", issue = "27747")]
+impl<S: Borrow<OsStr>> alloc::slice::Join<&OsStr> for [S] {
+ type Output = OsString;
+
+ fn join(slice: &Self, sep: &OsStr) -> OsString {
+ let Some(first) = slice.first() else {
+ return OsString::new();
+ };
+ let first = first.borrow().to_owned();
+ slice[1..].iter().fold(first, |mut a, b| {
+ a.push(sep);
+ a.push(b.borrow());
+ a
+ })
+ }
+}
+
#[stable(feature = "rust1", since = "1.0.0")]
impl Borrow<OsStr> for OsString {
#[inline]
assert!(os_string.capacity() >= 33)
}
+#[test]
+fn test_os_string_join() {
+ let strings = [OsStr::new("hello"), OsStr::new("dear"), OsStr::new("world")];
+ assert_eq!("hello", strings[..1].join(OsStr::new(" ")));
+ assert_eq!("hello dear world", strings.join(OsStr::new(" ")));
+ assert_eq!("hellodearworld", strings.join(OsStr::new("")));
+ assert_eq!("hello.\n dear.\n world", strings.join(OsStr::new(".\n ")));
+
+ assert_eq!("dear world", strings[1..].join(&OsString::from(" ")));
+
+ let strings_abc = [OsString::from("a"), OsString::from("b"), OsString::from("c")];
+ assert_eq!("a b c", strings_abc.join(OsStr::new(" ")));
+}
+
#[test]
fn test_os_string_default() {
let os_string: OsString = Default::default();
#![feature(intra_doc_pointers)]
#![feature(lang_items)]
#![feature(let_chains)]
+#![feature(let_else)]
#![feature(linkage)]
#![feature(min_specialization)]
#![feature(must_not_suspend)]
#![feature(exact_size_is_empty)]
#![feature(extend_one)]
#![feature(float_minimum_maximum)]
+#![feature(hasher_prefixfree_extras)]
#![feature(hashmap_internals)]
#![feature(int_error_internals)]
#![feature(maybe_uninit_slice)]
#![feature(toowned_clone_into)]
#![feature(try_reserve_kind)]
#![feature(vec_into_raw_parts)]
+#![feature(slice_concat_trait)]
//
// Library features (unwind):
#![feature(panic_unwind)]
///
/// To convert from an IPv4 address to an IPv4-mapped IPv6 address, use [`Ipv4Addr::to_ipv6_mapped`].
/// Use [`Ipv6Addr::to_ipv4`] to convert an IPv4-mapped IPv6 address to the canonical IPv4 address.
+/// Note that this will also convert the IPv6 loopback address `::1` to `0.0.0.1`.
///
/// [IETF RFC 4291 Section 2.5.5.2]: https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.5.2
///
/// or an [IPv4-mapped] address as defined in [IETF RFC 4291 section 2.5.5.2],
/// otherwise returns [`None`].
///
- /// `::a.b.c.d` and `::ffff:a.b.c.d` become `a.b.c.d`
+ /// Note that this will return an [`IPv4` address] for the IPv6 loopback address `::1`.
+ ///
+ /// `::a.b.c.d` and `::ffff:a.b.c.d` become `a.b.c.d`. `::1` becomes `0.0.0.1`.
/// All addresses *not* starting with either all zeroes or `::ffff` will return `None`.
///
/// [`IPv4` address]: Ipv4Addr
break;
}
}
-
-#[test]
-#[should_panic]
-#[cfg(all(unix, not(target_os = "linux"), not(target_os = "android")))]
-fn two_mutexes() {
- let m = Arc::new(Mutex::new(()));
- let m2 = m.clone();
- let c = Arc::new(Condvar::new());
- let c2 = c.clone();
-
- let mut g = m.lock().unwrap();
- let _t = thread::spawn(move || {
- let _g = m2.lock().unwrap();
- c2.notify_one();
- });
- g = c.wait(g).unwrap();
- drop(g);
-
- let m = Mutex::new(());
- let _ = c.wait(m.lock().unwrap()).unwrap();
-}
#![cfg(any(
target_os = "linux",
target_os = "android",
- all(target_os = "emscripten", target_feature = "atomics")
+ all(target_os = "emscripten", target_feature = "atomics"),
+ target_os = "freebsd",
+ target_os = "openbsd",
+ target_os = "dragonfly",
))]
use crate::sync::atomic::AtomicU32;
/// Returns directly if the futex doesn't hold the expected value.
///
/// Returns false on timeout, and true in all other cases.
-#[cfg(any(target_os = "linux", target_os = "android"))]
+#[cfg(any(target_os = "linux", target_os = "android", target_os = "freebsd"))]
pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) -> bool {
use super::time::Timespec;
use crate::ptr::null;
return true;
}
- // Use FUTEX_WAIT_BITSET rather than FUTEX_WAIT to be able to give an
- // absolute time rather than a relative time.
let r = unsafe {
- libc::syscall(
- libc::SYS_futex,
- futex as *const AtomicU32,
- libc::FUTEX_WAIT_BITSET | libc::FUTEX_PRIVATE_FLAG,
- expected,
- timespec.as_ref().map_or(null(), |t| &t.t as *const libc::timespec),
- null::<u32>(), // This argument is unused for FUTEX_WAIT_BITSET.
- !0u32, // A full bitmask, to make it behave like a regular FUTEX_WAIT.
- )
+ cfg_if::cfg_if! {
+ if #[cfg(target_os = "freebsd")] {
+ // FreeBSD doesn't have futex(), but it has
+ // _umtx_op(UMTX_OP_WAIT_UINT_PRIVATE), which is nearly
+ // identical. It supports absolute timeouts through a flag
+ // in the _umtx_time struct.
+ let umtx_timeout = timespec.map(|t| libc::_umtx_time {
+ _timeout: t.t,
+ _flags: libc::UMTX_ABSTIME,
+ _clockid: libc::CLOCK_MONOTONIC as u32,
+ });
+ let umtx_timeout_ptr = umtx_timeout.as_ref().map_or(null(), |t| t as *const _);
+ let umtx_timeout_size = umtx_timeout.as_ref().map_or(0, |t| crate::mem::size_of_val(t));
+ libc::_umtx_op(
+ futex as *const AtomicU32 as *mut _,
+ libc::UMTX_OP_WAIT_UINT_PRIVATE,
+ expected as libc::c_ulong,
+ crate::ptr::invalid_mut(umtx_timeout_size),
+ umtx_timeout_ptr as *mut _,
+ )
+ } else if #[cfg(any(target_os = "linux", target_os = "android"))] {
+ // Use FUTEX_WAIT_BITSET rather than FUTEX_WAIT to be able to give an
+ // absolute time rather than a relative time.
+ libc::syscall(
+ libc::SYS_futex,
+ futex as *const AtomicU32,
+ libc::FUTEX_WAIT_BITSET | libc::FUTEX_PRIVATE_FLAG,
+ expected,
+ timespec.as_ref().map_or(null(), |t| &t.t as *const libc::timespec),
+ null::<u32>(), // This argument is unused for FUTEX_WAIT_BITSET.
+ !0u32, // A full bitmask, to make it behave like a regular FUTEX_WAIT.
+ )
+ } else {
+ compile_error!("unknown target_os");
+ }
+ }
};
match (r < 0).then(super::os::errno) {
///
/// Returns true if this actually woke up such a thread,
/// or false if no thread was waiting on this futex.
+///
+/// On some platforms, this always returns false.
+#[cfg(any(target_os = "linux", target_os = "android"))]
+pub fn futex_wake(futex: &AtomicU32) -> bool {
+ let ptr = futex as *const AtomicU32;
+ let op = libc::FUTEX_WAKE | libc::FUTEX_PRIVATE_FLAG;
+ unsafe { libc::syscall(libc::SYS_futex, ptr, op, 1) > 0 }
+}
+
+/// Wake up all threads that are waiting on futex_wait on this futex.
#[cfg(any(target_os = "linux", target_os = "android"))]
+pub fn futex_wake_all(futex: &AtomicU32) {
+ let ptr = futex as *const AtomicU32;
+ let op = libc::FUTEX_WAKE | libc::FUTEX_PRIVATE_FLAG;
+ unsafe {
+ libc::syscall(libc::SYS_futex, ptr, op, i32::MAX);
+ }
+}
+
+// FreeBSD doesn't tell us how many threads are woken up, so this always returns false.
+#[cfg(target_os = "freebsd")]
pub fn futex_wake(futex: &AtomicU32) -> bool {
+ use crate::ptr::null_mut;
unsafe {
- libc::syscall(
- libc::SYS_futex,
- futex as *const AtomicU32,
- libc::FUTEX_WAKE | libc::FUTEX_PRIVATE_FLAG,
+ libc::_umtx_op(
+ futex as *const AtomicU32 as *mut _,
+ libc::UMTX_OP_WAKE_PRIVATE,
1,
- ) > 0
+ null_mut(),
+ null_mut(),
+ )
+ };
+ false
+}
+
+#[cfg(target_os = "freebsd")]
+pub fn futex_wake_all(futex: &AtomicU32) {
+ use crate::ptr::null_mut;
+ unsafe {
+ libc::_umtx_op(
+ futex as *const AtomicU32 as *mut _,
+ libc::UMTX_OP_WAKE_PRIVATE,
+ i32::MAX as libc::c_ulong,
+ null_mut(),
+ null_mut(),
+ )
+ };
+}
+
+#[cfg(target_os = "openbsd")]
+pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) -> bool {
+ use crate::convert::TryInto;
+ use crate::ptr::{null, null_mut};
+ let timespec = timeout.and_then(|d| {
+ Some(libc::timespec {
+ // Sleep forever if the timeout is longer than fits in a timespec.
+ tv_sec: d.as_secs().try_into().ok()?,
+ // This conversion never truncates, as subsec_nanos is always <1e9.
+ tv_nsec: d.subsec_nanos() as _,
+ })
+ });
+
+ let r = unsafe {
+ libc::futex(
+ futex as *const AtomicU32 as *mut u32,
+ libc::FUTEX_WAIT,
+ expected as i32,
+ timespec.as_ref().map_or(null(), |t| t as *const libc::timespec),
+ null_mut(),
+ )
+ };
+
+ r == 0 || super::os::errno() != libc::ETIMEDOUT
+}
+
+#[cfg(target_os = "openbsd")]
+pub fn futex_wake(futex: &AtomicU32) -> bool {
+ use crate::ptr::{null, null_mut};
+ unsafe {
+ libc::futex(futex as *const AtomicU32 as *mut u32, libc::FUTEX_WAKE, 1, null(), null_mut())
+ > 0
}
}
-/// Wake up all threads that are waiting on futex_wait on this futex.
-#[cfg(any(target_os = "linux", target_os = "android"))]
+#[cfg(target_os = "openbsd")]
pub fn futex_wake_all(futex: &AtomicU32) {
+ use crate::ptr::{null, null_mut};
unsafe {
- libc::syscall(
- libc::SYS_futex,
- futex as *const AtomicU32,
- libc::FUTEX_WAKE | libc::FUTEX_PRIVATE_FLAG,
+ libc::futex(
+ futex as *const AtomicU32 as *mut u32,
+ libc::FUTEX_WAKE,
i32::MAX,
+ null(),
+ null_mut(),
);
}
}
+#[cfg(target_os = "dragonfly")]
+pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) -> bool {
+ use crate::convert::TryFrom;
+
+ // A timeout of 0 means infinite.
+ // We round smaller timeouts up to 1 millisecond.
+ // Overflows are rounded up to an infinite timeout.
+ let timeout_ms =
+ timeout.and_then(|d| Some(i32::try_from(d.as_millis()).ok()?.max(1))).unwrap_or(0);
+
+ let r = unsafe {
+ libc::umtx_sleep(futex as *const AtomicU32 as *const i32, expected as i32, timeout_ms)
+ };
+
+ r == 0 || super::os::errno() != libc::ETIMEDOUT
+}
+
+// DragonflyBSD doesn't tell us how many threads are woken up, so this always returns false.
+#[cfg(target_os = "dragonfly")]
+pub fn futex_wake(futex: &AtomicU32) -> bool {
+ unsafe { libc::umtx_wakeup(futex as *const AtomicU32 as *const i32, 1) };
+ false
+}
+
+#[cfg(target_os = "dragonfly")]
+pub fn futex_wake_all(futex: &AtomicU32) {
+ unsafe { libc::umtx_wakeup(futex as *const AtomicU32 as *const i32, i32::MAX) };
+}
+
#[cfg(target_os = "emscripten")]
extern "C" {
fn emscripten_futex_wake(addr: *const AtomicU32, count: libc::c_int) -> libc::c_int;
fn wake_writer(&self) -> bool {
self.writer_notify.fetch_add(1, Release);
futex_wake(&self.writer_notify)
+ // Note that FreeBSD and DragonFlyBSD don't tell us whether they woke
+ // up any threads or not, and always return `false` here. That still
+ // results in correct behaviour: it just means readers get woken up as
+ // well in case both readers and writers were waiting.
}
/// Spin for a while, but stop directly at the given condition.
target_os = "linux",
target_os = "android",
all(target_os = "emscripten", target_feature = "atomics"),
+ target_os = "freebsd",
+ target_os = "openbsd",
+ target_os = "dragonfly",
))] {
mod futex;
mod futex_rwlock;
#![cfg(not(any(
target_os = "linux",
target_os = "android",
- all(target_os = "emscripten", target_feature = "atomics")
+ all(target_os = "emscripten", target_feature = "atomics"),
+ target_os = "freebsd",
+ target_os = "openbsd",
+ target_os = "dragonfly",
)))]
use crate::cell::UnsafeCell;
target_os = "linux",
target_os = "android",
all(target_arch = "wasm32", target_feature = "atomics"),
+ target_os = "freebsd",
+ target_os = "openbsd",
+ target_os = "dragonfly",
))] {
mod futex;
pub use futex::Parker;
--- /dev/null
+# `debugger_visualizer`
+
+The tracking issue for this feature is: [#95939]
+
+[#95939]: https://github.com/rust-lang/rust/issues/95939
+
+------------------------
+
+The `debugger_visualizer` attribute can be used to instruct the compiler
+to embed a debugger visualizer file into the PDB/ELF generated by `rustc`.
+
+## Examples
+
+``` rust,ignore (partial-example)
+#![feature(debugger_visualizer)]
+#![debugger_visualizer(natvis_file = "foo.natvis")]
+struct Foo {
+
+}
+```
+
+## Limitations
+
+Currently, this feature only supports embedding Natvis files on `-windows-msvc`
+targets when using the MSVC linker via the `natvis_file` meta item.
fn visit_item(&mut self, item: &'hir hir::Item<'_>) {
let name = match &item.kind {
- hir::ItemKind::Macro(ref macro_def, _) => {
- // FIXME(#88038): Non exported macros have historically not been tested,
- // but we really ought to start testing them.
- let def_id = item.def_id.to_def_id();
- if macro_def.macro_rules && !self.tcx.has_attr(def_id, sym::macro_export) {
- intravisit::walk_item(self, item);
- return;
- }
- item.ident.to_string()
- }
hir::ItemKind::Impl(impl_) => {
rustc_hir_pretty::id_to_string(&self.map, impl_.self_ty.hir_id)
}
border-top: 2px solid;
}
+#titles > button:first-child:last-child {
+ margin-right: 1px;
+ width: calc(100% - 1px);
+}
+
#titles > button:not(:last-child) {
margin-right: 1px;
width: calc(33.3% - 1px);
cursor: pointer;
}
+@keyframes rotating {
+ from {
+ transform: rotate(0deg);
+ }
+ to {
+ transform: rotate(360deg);
+ }
+}
+#settings-menu.rotate img {
+ animation: rotating 2s linear infinite;
+}
+
#help-button {
font-family: "Fira Sans", Arial, sans-serif;
text-align: center;
/* global onEach, onEachLazy, removeClass */
/* global switchTheme, useSystemTheme */
+"use strict";
+
if (!String.prototype.startsWith) {
String.prototype.startsWith = function(searchString, position) {
position = position || 0;
}
(function() {
- "use strict";
-
function loadScript(url) {
const script = document.createElement('script');
script.src = url;
document.head.append(script);
}
-
getSettingsButton().onclick = event => {
+ addClass(getSettingsButton(), "rotate");
event.preventDefault();
+ // Sending request for the CSS and the JS files at the same time so it will
+ // hopefully be loaded when the JS will generate the settings content.
+ loadCss("settings");
loadScript(window.settingsJS);
};
/* eslint prefer-arrow-callback: "error" */
/* global addClass, hasClass, removeClass, onEachLazy */
+"use strict";
+
(function () {
// Number of lines shown when code viewer is not expanded
const MAX_LINES = 10;
/* global addClass, getNakedUrl, getSettingValue, hasOwnPropertyRustdoc, initSearch, onEach */
/* global onEachLazy, removeClass, searchState, browserSupportsHistoryApi */
-(function () {
+"use strict";
+
+(function() {
// This mapping table should match the discriminants of
// `rustdoc::formats::item_type::ItemType` type in Rust.
const itemTypes = [
// In the search display, allows to switch between tabs.
function printTab(nb) {
- if (nb === 0 || nb === 1 || nb === 2) {
- searchState.currentTab = nb;
- }
- let nb_copy = nb;
+ let iter = 0;
+ let foundCurrentTab = false;
+ let foundCurrentResultSet = false;
onEachLazy(document.getElementById("titles").childNodes, elem => {
- if (nb_copy === 0) {
+ if (nb === iter) {
addClass(elem, "selected");
+ foundCurrentTab = true;
} else {
removeClass(elem, "selected");
}
- nb_copy -= 1;
+ iter += 1;
});
+ iter = 0;
onEachLazy(document.getElementById("results").childNodes, elem => {
- if (nb === 0) {
+ if (nb === iter) {
addClass(elem, "active");
+ foundCurrentResultSet = true;
} else {
removeClass(elem, "active");
}
- nb -= 1;
+ iter += 1;
});
+ if (foundCurrentTab && foundCurrentResultSet) {
+ searchState.currentTab = nb;
+ } else if (nb != 0) {
+ printTab(0);
+ }
}
/**
for (i = 0, nSearchWords = searchWords.length; i < nSearchWords; ++i) {
row = searchIndex[i];
in_returned = checkReturned(row, elem, parsedQuery.typeFilter);
- addIntoResults(results_returned, row.id, i, -1, in_returned);
+ addIntoResults(results_others, row.id, i, -1, in_returned);
}
}
} else if (parsedQuery.foundElems > 0) {
- let container = results_others;
- // In the special case where only a "returned" information is available, we want to
- // put the information into the "results_returned" dict.
- if (parsedQuery.returned.length !== 0 && parsedQuery.elems.length === 0) {
- container = results_returned;
- }
for (i = 0, nSearchWords = searchWords.length; i < nSearchWords; ++i) {
- handleArgs(searchIndex[i], i, container);
+ handleArgs(searchIndex[i], i, results_others);
}
}
}
`${typeFilter}</h1> in ${crates} </div>`;
if (results.query.error !== null) {
output += `<h3>Query parser error: "${results.query.error}".</h3>`;
+ output += '<div id="titles">' +
+ makeTabHeader(0, "In Names", ret_others[1]) +
+ "</div>";
+ currentTab = 0;
+ } else if (results.query.foundElems <= 1 && results.query.returned.length === 0) {
+ output += `<div id="titles">` +
+ makeTabHeader(0, "In Names", ret_others[1]) +
+ makeTabHeader(1, "In Parameters", ret_in_args[1]) +
+ makeTabHeader(2, "In Return Types", ret_returned[1]) +
+ "</div>";
+ } else {
+ const signatureTabTitle =
+ results.query.elems.length === 0 ? "In Function Return Types" :
+ results.query.returned.length === 0 ? "In Function Parameters" :
+ "In Function Signatures";
+ output += '<div id="titles">' +
+ makeTabHeader(0, signatureTabTitle, ret_others[1]) +
+ "</div>";
+ currentTab = 0;
}
- output += `<div id="titles">` +
- makeTabHeader(0, "In Names", ret_others[1]) +
- makeTabHeader(1, "In Parameters", ret_in_args[1]) +
- makeTabHeader(2, "In Return Types", ret_returned[1]) +
- "</div>";
const resultsElem = document.createElement("div");
resultsElem.id = "results";
}
search.appendChild(resultsElem);
// Reset focused elements.
- searchState.focusedByTab = [null, null, null];
searchState.showResults(search);
const elems = document.getElementById("titles").childNodes;
- elems[0].onclick = () => { printTab(0); };
- elems[1].onclick = () => { printTab(1); };
- elems[2].onclick = () => { printTab(2); };
+ searchState.focusedByTab = [];
+ let i = 0;
+ for (const elem of elems) {
+ const j = i;
+ elem.onclick = () => { printTab(j); };
+ searchState.focusedByTab.push(null);
+ i += 1;
+ }
printTab(currentTab);
}
/* eslint prefer-const: "error" */
/* eslint prefer-arrow-callback: "error" */
// Local js definitions:
-/* global getSettingValue, getVirtualKey, updateLocalStorage, updateSystemTheme, loadCss */
+/* global getSettingValue, getVirtualKey, updateLocalStorage, updateSystemTheme */
/* global addClass, removeClass, onEach, onEachLazy, NOT_DISPLAYED_ID */
/* global MAIN_ID, getVar, getSettingsButton, switchDisplayedElement, getNotDisplayedElem */
+"use strict";
+
(function () {
const isSettingsPage = window.location.pathname.endsWith("/settings.html");
},
];
- // First, we add the settings.css file.
- loadCss("settings");
-
// Then we build the DOM.
const el = document.createElement("section");
el.id = "settings";
if (!isSettingsPage) {
switchDisplayedElement(settingsMenu);
}
+ removeClass(getSettingsButton(), "rotate");
}, 0);
})();
// Local js definitions:
/* global addClass, getCurrentValue, hasClass, onEachLazy, removeClass, browserSupportsHistoryApi */
/* global updateLocalStorage */
-(function () {
+
+"use strict";
+
+(function() {
function getCurrentFilePath() {
const parts = window.location.pathname.split("/");
/* eslint prefer-const: "error" */
/* eslint prefer-arrow-callback: "error" */
+"use strict";
+
const darkThemes = ["dark", "ayu"];
window.currentTheme = document.getElementById("themeStyle");
window.mainTheme = document.getElementById("mainThemeStyle");
///
/// Example:
///
-/// ```
+/// ```ignore(cannot-test-this-because-non-exported-macro)
/// let letters = map!{"a" => "b", "c" => "d"};
/// ```
///
// only-linux
// assembly-output: emit-asm
// compile-flags: -C llvm-args=--x86-asm-syntax=intel
+// compile-flags: -C symbol-mangling-version=v0
#![feature(asm_const, asm_sym)]
#![crate_type = "rlib"]
global_asm!("call {}", sym my_func);
// CHECK: lea rax, [rip + MY_STATIC]
global_asm!("lea rax, [rip + {}]", sym MY_STATIC);
+// CHECK: call _RNvCsiubXh4Yz005_10global_asm6foobar
+global_asm!("call {}", sym foobar);
+// CHECK: _RNvCsiubXh4Yz005_10global_asm6foobar:
+fn foobar() { loop {} }
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
+ <Type Name="msvc_embedded_natvis::Point">
+ <DisplayString>({x}, {y})</DisplayString>
+ <Expand>
+ <Item Name="[x]">x</Item>
+ <Item Name="[y]">y</Item>
+ </Expand>
+ </Type>
+
+ <Type Name="msvc_embedded_natvis::Line">
+ <DisplayString>({a}, {b})</DisplayString>
+ <Expand>
+ <Item Name="[a]">a</Item>
+ <Item Name="[b]">b</Item>
+ </Expand>
+ </Type>
+</AutoVisualizer>
--- /dev/null
+// only-cdb
+// compile-flags:-g
+
+// === CDB TESTS ==================================================================================
+
+// cdb-command: g
+
+// cdb-command: .nvlist
+// cdb-check: [...].exe (embedded NatVis "[...]msvc_embedded_natvis-0.natvis")
+
+// cdb-command: dx point_a
+// cdb-check:point_a : (0, 0) [Type: msvc_embedded_natvis::Point]
+// cdb-check: [<Raw View>] [Type: msvc_embedded_natvis::Point]
+// cdb-check: [x] : 0 [Type: int]
+// cdb-check: [y] : 0 [Type: int]
+
+// cdb-command: dx point_b
+// cdb-check:point_b : (5, 8) [Type: msvc_embedded_natvis::Point]
+// cdb-check: [<Raw View>] [Type: msvc_embedded_natvis::Point]
+// cdb-check: [x] : 5 [Type: int]
+// cdb-check: [y] : 8 [Type: int]
+
+// cdb-command: dx line
+// cdb-check:line : ((0, 0), (5, 8)) [Type: msvc_embedded_natvis::Line]
+// cdb-check: [<Raw View>] [Type: msvc_embedded_natvis::Line]
+// cdb-check: [a] : (0, 0) [Type: msvc_embedded_natvis::Point]
+// cdb-check: [b] : (5, 8) [Type: msvc_embedded_natvis::Point]
+
+#![feature(debugger_visualizer)]
+#![debugger_visualizer(natvis_file = "msvc-embedded-natvis.natvis")]
+
+pub struct Point {
+ x: i32,
+ y: i32,
+}
+
+impl Point {
+ pub fn new(x: i32, y: i32) -> Point {
+ Point { x: x, y: y }
+ }
+}
+
+pub struct Line {
+ a: Point,
+ b: Point,
+}
+
+impl Line {
+ pub fn new(a: Point, b: Point) -> Line {
+ Line { a: a, b: b }
+ }
+}
+
+fn main() {
+ let point_a = Point::new(0, 0);
+ let point_b = Point::new(5, 8);
+ let line = Line::new(point_a, point_b);
+
+ zzz(); // #break
+}
+
+fn zzz() {
+ ()
+}
--- /dev/null
+// Checks that the search tab results work correctly with function signature syntax
+// First, try a search-by-name
+goto: file://|DOC_PATH|/test_docs/index.html
+write: (".search-input", "Foo")
+// Waiting for the search results to appear...
+wait-for: "#titles"
+assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
+assert-text: ("#titles > button:nth-of-type(1)", "In Names", STARTS_WITH)
+assert: "input.search-input:focus"
+// Use left-right keys
+press-key: "ArrowDown"
+assert: "#results > .search-results.active > a:nth-of-type(1):focus"
+press-key: "ArrowRight"
+wait-for-attribute: ("#titles > button:nth-of-type(2)", {"class": "selected"})
+press-key: "ArrowRight"
+wait-for-attribute: ("#titles > button:nth-of-type(3)", {"class": "selected"})
+press-key: "ArrowRight"
+wait-for-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
+press-key: "ArrowLeft"
+wait-for-attribute: ("#titles > button:nth-of-type(3)", {"class": "selected"})
+
+// Now try search-by-return
+goto: file://|DOC_PATH|/test_docs/index.html
+write: (".search-input", "-> String")
+// Waiting for the search results to appear...
+wait-for: "#titles"
+assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
+assert-text: ("#titles > button:nth-of-type(1)", "In Function Return Types", STARTS_WITH)
+assert: "input.search-input:focus"
+// Use left-right keys
+press-key: "ArrowDown"
+assert: "#results > .search-results.active > a:nth-of-type(1):focus"
+press-key: "ArrowRight"
+wait-for-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
+press-key: "ArrowRight"
+wait-for-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
+press-key: "ArrowRight"
+wait-for-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
+press-key: "ArrowLeft"
+wait-for-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
+
+// Try with a search-by-return with no results
+goto: file://|DOC_PATH|/test_docs/index.html
+write: (".search-input", "-> Something")
+// Waiting for the search results to appear...
+wait-for: "#titles"
+assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
+assert-text: ("#titles > button:nth-of-type(1)", "In Function Return Types", STARTS_WITH)
+
+// Try with a search-by-parameter
+goto: file://|DOC_PATH|/test_docs/index.html
+write: (".search-input", "usize pattern")
+// Waiting for the search results to appear...
+wait-for: "#titles"
+assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
+assert-text: ("#titles > button:nth-of-type(1)", "In Function Parameters", STARTS_WITH)
+
+// Try with a search-by-parameter-and-return
+goto: file://|DOC_PATH|/test_docs/index.html
+write: (".search-input", "pattern -> str")
+// Waiting for the search results to appear...
+wait-for: "#titles"
+assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
+assert-text: ("#titles > button:nth-of-type(1)", "In Function Signatures", STARTS_WITH)
+++ /dev/null
-// Checks that the first non-empty search result tab is selected if the default/currently selected
-// one is empty.
-goto: file://|DOC_PATH|/test_docs/index.html
-write: (".search-input", "Foo")
-// Waiting for the search results to appear...
-wait-for: "#titles"
-assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
-
-// To go back to the original "state"
-goto: file://|DOC_PATH|/test_docs/index.html
-write: (".search-input", "-> String")
-// Waiting for the search results to appear...
-wait-for: "#titles"
-// With this search, only the last tab shouldn't be empty so it should be selected.
-assert-attribute: ("#titles > button:nth-of-type(3)", {"class": "selected"})
-
-// To go back to the original "state"
-goto: file://|DOC_PATH|/test_docs/index.html
-write: (".search-input", "-> Something")
-// Waiting for the search results to appear...
-wait-for: "#titles"
-// With this search, all the tabs are empty so the first one should remain selected.
-assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
| | | mutable borrow occurs here
| | immutable borrow later used by call
| immutable borrow occurs here
- |
-help: try adding a local storing this argument...
- --> $DIR/suggest-local-var-imm-and-mut.rs:12:22
- |
-LL | self.foo(self.bar());
- | ^^^^^^^^^^
-help: ...and then using that local as the argument to this call
- --> $DIR/suggest-local-var-imm-and-mut.rs:12:13
- |
-LL | self.foo(self.bar());
- | ^^^^^^^^^^^^^^^^^^^^
error[E0502]: cannot borrow `*self` as mutable because it is also borrowed as immutable
- --> $DIR/suggest-local-var-imm-and-mut.rs:24:39
+ --> $DIR/suggest-local-var-imm-and-mut.rs:24:29
|
LL | Self::foo(self, Self::bar(self));
- | --------- ---- ^^^^ mutable borrow occurs here
+ | --------- ---- ^^^^^^^^^^^^^^^ mutable borrow occurs here
| | |
| | immutable borrow occurs here
| immutable borrow later used by call
LL | | 0
LL | | });
| |______- immutable borrow occurs here
- |
-help: try adding a local storing this argument...
- --> $DIR/two-phase-cannot-nest-mut-self-calls.rs:16:9
- |
-LL | vec.push(2);
- | ^^^^^^^^^^^
-help: ...and then using that local as the argument to this call
- --> $DIR/two-phase-cannot-nest-mut-self-calls.rs:14:5
- |
-LL | / vec.get({
-LL | |
-LL | | vec.push(2);
-LL | |
-LL | |
-LL | | 0
-LL | | });
- | |______^
error: aborting due to previous error
--- /dev/null
+error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
+ --> $DIR/two-phase-reservation-sharing-interference-2.rs:19:5
+ |
+LL | let shared = &v;
+ | -- immutable borrow occurs here
+LL |
+LL | v.extend(shared);
+ | ^^------^^^^^^^^
+ | | |
+ | | immutable borrow later used by call
+ | mutable borrow occurs here
+
+error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
+ --> $DIR/two-phase-reservation-sharing-interference-2.rs:27:5
+ |
+LL | v.extend(&v);
+ | ^^------^--^
+ | | | |
+ | | | immutable borrow occurs here
+ | | immutable borrow later used by call
+ | mutable borrow occurs here
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0502`.
| | immutable borrow later used by call
| mutable borrow occurs here
-warning: cannot borrow `v` as mutable because it is also borrowed as immutable
- --> $DIR/two-phase-reservation-sharing-interference-2.rs:40:5
- |
-LL | let shared = &v;
- | -- immutable borrow occurs here
-LL |
-LL | v.push(shared.len());
- | ^^^^^^^------------^
- | | |
- | | immutable borrow later used here
- | mutable borrow occurs here
- |
- = note: `#[warn(mutable_borrow_reservation_conflict)]` on by default
- = warning: this borrowing pattern was not meant to be accepted, and may become a hard error in the future
- = note: for more information, see issue #59159 <https://github.com/rust-lang/rust/issues/59159>
-
-error: aborting due to 2 previous errors; 1 warning emitted
+error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0502`.
| | immutable borrow later used by call
| mutable borrow occurs here
-warning: cannot borrow `v` as mutable because it is also borrowed as immutable
- --> $DIR/two-phase-reservation-sharing-interference-2.rs:40:5
- |
-LL | let shared = &v;
- | -- immutable borrow occurs here
-LL |
-LL | v.push(shared.len());
- | ^^^^^^^------------^
- | | |
- | | immutable borrow later used here
- | mutable borrow occurs here
- |
- = note: `#[warn(mutable_borrow_reservation_conflict)]` on by default
- = warning: this borrowing pattern was not meant to be accepted, and may become a hard error in the future
- = note: for more information, see issue #59159 <https://github.com/rust-lang/rust/issues/59159>
-
-error: aborting due to 2 previous errors; 1 warning emitted
+error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0502`.
--- /dev/null
+error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
+ --> $DIR/two-phase-reservation-sharing-interference-2.rs:19:5
+ |
+LL | let shared = &v;
+ | -- immutable borrow occurs here
+LL |
+LL | v.extend(shared);
+ | ^^------^^^^^^^^
+ | | |
+ | | immutable borrow later used by call
+ | mutable borrow occurs here
+
+error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
+ --> $DIR/two-phase-reservation-sharing-interference-2.rs:27:5
+ |
+LL | v.extend(&v);
+ | ^^------^--^
+ | | | |
+ | | | immutable borrow occurs here
+ | | immutable borrow later used by call
+ | mutable borrow occurs here
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0502`.
| -- immutable borrow occurs here
LL |
LL | v.extend(shared);
- | ^^^^^^^^^------^
- | | |
- | | immutable borrow later used here
+ | ^^------^^^^^^^^
+ | | |
+ | | immutable borrow later used by call
| mutable borrow occurs here
error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
| | immutable borrow later used by call
| mutable borrow occurs here
-error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
- --> $DIR/two-phase-reservation-sharing-interference-2.rs:40:5
- |
-LL | let shared = &v;
- | -- immutable borrow occurs here
-LL |
-LL | v.push(shared.len());
- | ^^^^^^^------------^
- | | |
- | | immutable borrow later used here
- | mutable borrow occurs here
-
-error: aborting due to 3 previous errors
+error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0502`.
| -- immutable borrow occurs here
LL |
LL | v.extend(shared);
- | ^^^^^^^^^------^
- | | |
- | | immutable borrow later used here
+ | ^^------^^^^^^^^
+ | | |
+ | | immutable borrow later used by call
| mutable borrow occurs here
error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
| | immutable borrow later used by call
| mutable borrow occurs here
-error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
- --> $DIR/two-phase-reservation-sharing-interference-2.rs:40:5
- |
-LL | let shared = &v;
- | -- immutable borrow occurs here
-LL |
-LL | v.push(shared.len());
- | ^^^^^^^------------^
- | | |
- | | immutable borrow later used here
- | mutable borrow occurs here
-
-error: aborting due to 3 previous errors
+error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0502`.
-// Test for #56254, we previously allowed the last example on the 2018
-// edition. Make sure that we now emit a warning in that case and an error for
-// everyone else.
+// Test for #56254. The last example originally failed with the ast checker, was
+// accidentally allowed under migrate/nll, then linted against in migrate mode
+// but disallowed under NLL. Now, we accept it everywhere.
//ignore-compare-mode-nll
//ignore-compare-mode-polonius
-//revisions: migrate2015 migrate2018 nll2015 nll2018
+//revisions: base nll
//[migrate2018] edition:2018
//[nll2018] edition:2018
-#![cfg_attr(any(nll2015, nll2018), feature(nll))]
+#![cfg_attr(nll, feature(nll))]
fn double_conflicts() {
let mut v = vec![0, 1, 2];
let shared = &v;
v.extend(shared);
- //[migrate2015]~^ ERROR cannot borrow `v` as mutable
- //[nll2015]~^^ ERROR cannot borrow `v` as mutable
- //[migrate2018]~^^^ ERROR cannot borrow `v` as mutable
- //[nll2018]~^^^^ ERROR cannot borrow `v` as mutable
+ //[base]~^ ERROR cannot borrow `v` as mutable
+ //[nll]~^^ ERROR cannot borrow `v` as mutable
}
fn activation_conflict() {
let mut v = vec![0, 1, 2];
v.extend(&v);
- //[migrate2015]~^ ERROR cannot borrow `v` as mutable
- //[nll2015]~^^ ERROR cannot borrow `v` as mutable
- //[migrate2018]~^^^ ERROR cannot borrow `v` as mutable
- //[nll2018]~^^^^ ERROR cannot borrow `v` as mutable
+ //[base]~^ ERROR cannot borrow `v` as mutable
+ //[nll]~^^ ERROR cannot borrow `v` as mutable
}
-fn reservation_conflict() {
+fn reservation_allowed() {
let mut v = vec![0, 1, 2];
let shared = &v;
v.push(shared.len());
- //[nll2015]~^ ERROR cannot borrow `v` as mutable
- //[nll2018]~^^ ERROR cannot borrow `v` as mutable
- //[migrate2015]~^^^ WARNING cannot borrow `v` as mutable
- //[migrate2015]~| WARNING may become a hard error in the future
-
- //[migrate2018]~^^^^^^ WARNING cannot borrow `v` as mutable
- //[migrate2018]~| WARNING may become a hard error in the future
}
fn main() {}
+++ /dev/null
-error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
- --> $DIR/two-phase-reservation-sharing-interference-future-compat-lint.rs:13:9
- |
-LL | let shared = &v;
- | -- immutable borrow occurs here
-LL |
-LL | v.push(shared.len());
- | ^^^^^^^------------^
- | | |
- | | immutable borrow later used here
- | mutable borrow occurs here
-
-error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
- --> $DIR/two-phase-reservation-sharing-interference-future-compat-lint.rs:24:9
- |
-LL | let shared = &v;
- | -- immutable borrow occurs here
-LL |
-LL | v.push(shared.len());
- | ^^^^^^^------------^
- | | |
- | | immutable borrow later used here
- | mutable borrow occurs here
-
-error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
- --> $DIR/two-phase-reservation-sharing-interference-future-compat-lint.rs:37:9
- |
-LL | let shared = &v;
- | -- immutable borrow occurs here
-LL |
-LL | v.push(shared.len());
- | ^^^^^^^------------^
- | | |
- | | immutable borrow later used here
- | mutable borrow occurs here
-
-error: aborting due to 3 previous errors
-
-For more information about this error, try `rustc --explain E0502`.
+++ /dev/null
-// Check that the future-compat-lint for the reservation conflict is
-// handled like any other lint.
-
-// edition:2018
-
-mod future_compat_allow {
- #![allow(mutable_borrow_reservation_conflict)]
-
- fn reservation_conflict() {
- let mut v = vec![0, 1, 2];
- let shared = &v;
-
- v.push(shared.len());
- }
-}
-
-mod future_compat_warn {
- #![warn(mutable_borrow_reservation_conflict)]
-
- fn reservation_conflict() {
- let mut v = vec![0, 1, 2];
- let shared = &v;
-
- v.push(shared.len());
- //~^ WARNING cannot borrow `v` as mutable
- //~| WARNING may become a hard error in the future
- }
-}
-
-mod future_compat_deny {
- #![deny(mutable_borrow_reservation_conflict)]
-
- fn reservation_conflict() {
- let mut v = vec![0, 1, 2];
- let shared = &v;
-
- v.push(shared.len());
- //~^ ERROR cannot borrow `v` as mutable
- //~| WARNING may become a hard error in the future
- }
-}
-
-fn main() {}
+++ /dev/null
-warning: cannot borrow `v` as mutable because it is also borrowed as immutable
- --> $DIR/two-phase-reservation-sharing-interference-future-compat-lint.rs:24:9
- |
-LL | let shared = &v;
- | -- immutable borrow occurs here
-LL |
-LL | v.push(shared.len());
- | ^^^^^^^------------^
- | | |
- | | immutable borrow later used here
- | mutable borrow occurs here
- |
-note: the lint level is defined here
- --> $DIR/two-phase-reservation-sharing-interference-future-compat-lint.rs:18:13
- |
-LL | #![warn(mutable_borrow_reservation_conflict)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- = warning: this borrowing pattern was not meant to be accepted, and may become a hard error in the future
- = note: for more information, see issue #59159 <https://github.com/rust-lang/rust/issues/59159>
-
-error: cannot borrow `v` as mutable because it is also borrowed as immutable
- --> $DIR/two-phase-reservation-sharing-interference-future-compat-lint.rs:37:9
- |
-LL | let shared = &v;
- | -- immutable borrow occurs here
-LL |
-LL | v.push(shared.len());
- | ^^^^^^^------------^
- | | |
- | | immutable borrow later used here
- | mutable borrow occurs here
- |
-note: the lint level is defined here
- --> $DIR/two-phase-reservation-sharing-interference-future-compat-lint.rs:31:13
- |
-LL | #![deny(mutable_borrow_reservation_conflict)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- = warning: this borrowing pattern was not meant to be accepted, and may become a hard error in the future
- = note: for more information, see issue #59159 <https://github.com/rust-lang/rust/issues/59159>
-
-error: aborting due to previous error; 1 warning emitted
-
error[E0502]: cannot borrow `*a` as mutable because it is also borrowed as immutable
- --> $DIR/E0502.rs:4:9
+ --> $DIR/E0502.rs:4:5
|
LL | let ref y = a;
| ----- immutable borrow occurs here
LL | bar(a);
- | ^ mutable borrow occurs here
+ | ^^^^^^ mutable borrow occurs here
LL | y.use_ref();
| ----------- immutable borrow later used here
--- /dev/null
+#![debugger_visualizer(natvis_file = "../foo.natvis")] //~ ERROR the `#[debugger_visualizer]` attribute is an experimental feature
+
+fn main() {}
--- /dev/null
+error[E0658]: the `#[debugger_visualizer]` attribute is an experimental feature
+ --> $DIR/feature-gate-debugger-visualizer.rs:1:1
+ |
+LL | #![debugger_visualizer(natvis_file = "../foo.natvis")]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: see issue #95939 <https://github.com/rust-lang/rust/issues/95939> for more information
+ = help: add `#![feature(debugger_visualizer)]` to the crate attributes to enable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0658`.
--- /dev/null
+#![feature(rustc_attrs)]
+
+#[rustc_has_incoherent_inherent_impls]
+pub struct StructWithAttr;
+pub struct StructNoAttr;
+
+#[rustc_has_incoherent_inherent_impls]
+pub enum EnumWithAttr {}
+pub enum EnumNoAttr {}
--- /dev/null
+// aux-build:extern-crate.rs
+#![feature(rustc_attrs)]
+extern crate extern_crate;
+
+impl extern_crate::StructWithAttr {
+ //~^ ERROR cannot define inherent `impl` for a type outside of the crate
+ fn foo() {}
+}
+impl extern_crate::StructWithAttr {
+ #[rustc_allow_incoherent_impl]
+ fn bar() {}
+}
+impl extern_crate::StructNoAttr {
+ //~^ ERROR cannot define inherent `impl` for a type outside of the crate
+ fn foo() {}
+}
+impl extern_crate::StructNoAttr {
+ //~^ ERROR cannot define inherent `impl` for a type outside of the crate
+ #[rustc_allow_incoherent_impl]
+ fn bar() {}
+}
+impl extern_crate::EnumWithAttr {
+ //~^ ERROR cannot define inherent `impl` for a type outside of the crate
+ fn foo() {}
+}
+impl extern_crate::EnumWithAttr {
+ #[rustc_allow_incoherent_impl]
+ fn bar() {}
+}
+impl extern_crate::EnumNoAttr {
+ //~^ ERROR cannot define inherent `impl` for a type outside of the crate
+ fn foo() {}
+}
+impl extern_crate::EnumNoAttr {
+ //~^ ERROR cannot define inherent `impl` for a type outside of the crate
+ #[rustc_allow_incoherent_impl]
+ fn bar() {}
+}
+
+fn main() {}
--- /dev/null
+error[E0390]: cannot define inherent `impl` for a type outside of the crate where the type is defined
+ --> $DIR/needs-has-incoherent-impls.rs:5:1
+ |
+LL | / impl extern_crate::StructWithAttr {
+LL | |
+LL | | fn foo() {}
+LL | | }
+ | |_^
+ |
+ = help: consider moving this inherent impl into the crate defining the type if possible
+help: alternatively add `#[rustc_allow_incoherent_impl]` to the relevant impl items
+ --> $DIR/needs-has-incoherent-impls.rs:7:5
+ |
+LL | fn foo() {}
+ | ^^^^^^^^^^^
+
+error[E0390]: cannot define inherent `impl` for a type outside of the crate where the type is defined
+ --> $DIR/needs-has-incoherent-impls.rs:13:1
+ |
+LL | / impl extern_crate::StructNoAttr {
+LL | |
+LL | | fn foo() {}
+LL | | }
+ | |_^
+ |
+ = help: consider moving this inherent impl into the crate defining the type if possible
+help: alternatively add `#[rustc_has_incoherent_inherent_impls]` to the type and `#[rustc_allow_incoherent_impl]` to the relevant impl items
+ --> $DIR/needs-has-incoherent-impls.rs:13:1
+ |
+LL | / impl extern_crate::StructNoAttr {
+LL | |
+LL | | fn foo() {}
+LL | | }
+ | |_^
+
+error[E0390]: cannot define inherent `impl` for a type outside of the crate where the type is defined
+ --> $DIR/needs-has-incoherent-impls.rs:17:1
+ |
+LL | / impl extern_crate::StructNoAttr {
+LL | |
+LL | | #[rustc_allow_incoherent_impl]
+LL | | fn bar() {}
+LL | | }
+ | |_^
+ |
+ = help: consider moving this inherent impl into the crate defining the type if possible
+help: alternatively add `#[rustc_has_incoherent_inherent_impls]` to the type and `#[rustc_allow_incoherent_impl]` to the relevant impl items
+ --> $DIR/needs-has-incoherent-impls.rs:17:1
+ |
+LL | / impl extern_crate::StructNoAttr {
+LL | |
+LL | | #[rustc_allow_incoherent_impl]
+LL | | fn bar() {}
+LL | | }
+ | |_^
+
+error[E0390]: cannot define inherent `impl` for a type outside of the crate where the type is defined
+ --> $DIR/needs-has-incoherent-impls.rs:22:1
+ |
+LL | / impl extern_crate::EnumWithAttr {
+LL | |
+LL | | fn foo() {}
+LL | | }
+ | |_^
+ |
+ = help: consider moving this inherent impl into the crate defining the type if possible
+help: alternatively add `#[rustc_allow_incoherent_impl]` to the relevant impl items
+ --> $DIR/needs-has-incoherent-impls.rs:24:5
+ |
+LL | fn foo() {}
+ | ^^^^^^^^^^^
+
+error[E0390]: cannot define inherent `impl` for a type outside of the crate where the type is defined
+ --> $DIR/needs-has-incoherent-impls.rs:30:1
+ |
+LL | / impl extern_crate::EnumNoAttr {
+LL | |
+LL | | fn foo() {}
+LL | | }
+ | |_^
+ |
+ = help: consider moving this inherent impl into the crate defining the type if possible
+help: alternatively add `#[rustc_has_incoherent_inherent_impls]` to the type and `#[rustc_allow_incoherent_impl]` to the relevant impl items
+ --> $DIR/needs-has-incoherent-impls.rs:30:1
+ |
+LL | / impl extern_crate::EnumNoAttr {
+LL | |
+LL | | fn foo() {}
+LL | | }
+ | |_^
+
+error[E0390]: cannot define inherent `impl` for a type outside of the crate where the type is defined
+ --> $DIR/needs-has-incoherent-impls.rs:34:1
+ |
+LL | / impl extern_crate::EnumNoAttr {
+LL | |
+LL | | #[rustc_allow_incoherent_impl]
+LL | | fn bar() {}
+LL | | }
+ | |_^
+ |
+ = help: consider moving this inherent impl into the crate defining the type if possible
+help: alternatively add `#[rustc_has_incoherent_inherent_impls]` to the type and `#[rustc_allow_incoherent_impl]` to the relevant impl items
+ --> $DIR/needs-has-incoherent-impls.rs:34:1
+ |
+LL | / impl extern_crate::EnumNoAttr {
+LL | |
+LL | | #[rustc_allow_incoherent_impl]
+LL | | fn bar() {}
+LL | | }
+ | |_^
+
+error: aborting due to 6 previous errors
+
+For more information about this error, try `rustc --explain E0390`.
--- /dev/null
+// aux-build:extern-crate.rs
+extern crate extern_crate;
+
+impl extern_crate::StructWithAttr {}
+//~^ ERROR cannot define inherent `impl` for a type outside of the crate
+
+impl extern_crate::StructNoAttr {}
+//~^ ERROR cannot define inherent `impl` for a type outside of the crate
+
+impl extern_crate::EnumWithAttr {}
+//~^ ERROR cannot define inherent `impl` for a type outside of the crate
+
+impl extern_crate::EnumNoAttr {}
+//~^ ERROR cannot define inherent `impl` for a type outside of the crate
+
+impl f32 {} //~ ERROR cannot define inherent `impl` for primitive types
+
+fn main() {}
--- /dev/null
+error[E0116]: cannot define inherent `impl` for a type outside of the crate where the type is defined
+ --> $DIR/no-attr-empty-impl.rs:4:1
+ |
+LL | impl extern_crate::StructWithAttr {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl for type defined outside of crate.
+ |
+ = note: define and implement a trait or new type instead
+
+error[E0116]: cannot define inherent `impl` for a type outside of the crate where the type is defined
+ --> $DIR/no-attr-empty-impl.rs:7:1
+ |
+LL | impl extern_crate::StructNoAttr {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl for type defined outside of crate.
+ |
+ = note: define and implement a trait or new type instead
+
+error[E0116]: cannot define inherent `impl` for a type outside of the crate where the type is defined
+ --> $DIR/no-attr-empty-impl.rs:10:1
+ |
+LL | impl extern_crate::EnumWithAttr {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl for type defined outside of crate.
+ |
+ = note: define and implement a trait or new type instead
+
+error[E0116]: cannot define inherent `impl` for a type outside of the crate where the type is defined
+ --> $DIR/no-attr-empty-impl.rs:13:1
+ |
+LL | impl extern_crate::EnumNoAttr {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl for type defined outside of crate.
+ |
+ = note: define and implement a trait or new type instead
+
+error[E0390]: cannot define inherent `impl` for primitive types
+ --> $DIR/no-attr-empty-impl.rs:16:6
+ |
+LL | impl f32 {}
+ | ^^^
+ |
+ = help: consider using an extension trait instead
+
+error: aborting due to 5 previous errors
+
+Some errors have detailed explanations: E0116, E0390.
+For more information about an error, try `rustc --explain E0116`.
--- /dev/null
+// build-fail
+#![feature(inline_const)]
+
+fn foo<T>() {
+ const { assert!(std::mem::size_of::<T>() == 0); } //~ ERROR E0080
+}
+
+fn bar<const N: usize>() -> usize {
+ const { N - 1 } //~ ERROR E0080
+}
+
+fn main() {
+ foo::<i32>();
+ bar::<0>();
+}
--- /dev/null
+error[E0080]: evaluation of `foo::<i32>::{constant#0}` failed
+ --> $DIR/const-expr-generic-err.rs:5:13
+ |
+LL | const { assert!(std::mem::size_of::<T>() == 0); }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'assertion failed: std::mem::size_of::<T>() == 0', $DIR/const-expr-generic-err.rs:5:13
+ |
+ = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+note: the above error was encountered while instantiating `fn foo::<i32>`
+ --> $DIR/const-expr-generic-err.rs:13:5
+ |
+LL | foo::<i32>();
+ | ^^^^^^^^^^^^
+
+error[E0080]: evaluation of `bar::<0_usize>::{constant#0}` failed
+ --> $DIR/const-expr-generic-err.rs:9:13
+ |
+LL | const { N - 1 }
+ | ^^^^^ attempt to compute `0_usize - 1_usize`, which would overflow
+
+note: the above error was encountered while instantiating `fn bar::<0_usize>`
+ --> $DIR/const-expr-generic-err.rs:14:5
+ |
+LL | bar::<0>();
+ | ^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0080`.
--- /dev/null
+#![feature(inline_const)]
+
+fn foo<T>() {
+ let _ = [0u8; const { std::mem::size_of::<T>() }];
+ //~^ ERROR: constant expression depends on a generic parameter
+}
+
+fn main() {
+ foo::<i32>();
+}
--- /dev/null
+error: constant expression depends on a generic parameter
+ --> $DIR/const-expr-generic-err2.rs:4:19
+ |
+LL | let _ = [0u8; const { std::mem::size_of::<T>() }];
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: this may fail depending on what value the parameter takes
+
+error: aborting due to previous error
+
--- /dev/null
+// check-pass
+#![feature(inline_const)]
+
+fn foo<T>() -> usize {
+ const { std::mem::size_of::<T>() }
+}
+
+fn bar<const N: usize>() -> usize {
+ const { N + 1 }
+}
+
+fn main() {
+ foo::<i32>();
+ bar::<1>();
+}
#![allow(incomplete_features)]
#![feature(inline_const_pat)]
-#![feature(generic_const_exprs)]
// rust-lang/rust#82518: ICE with inline-const in match referencing const-generic parameter
x + 1
}
-fn bar<const V: usize>() where [(); f(V)]: {
+fn bar<const V: usize>() {
match 0 {
const { f(V) } => {},
//~^ ERROR constant pattern depends on a generic parameter
error[E0158]: const parameters cannot be referenced in patterns
- --> $DIR/const-match-pat-generic.rs:9:9
+ --> $DIR/const-match-pat-generic.rs:8:9
|
LL | const { V } => {},
| ^^^^^^^^^^^
error: constant pattern depends on a generic parameter
- --> $DIR/const-match-pat-generic.rs:21:9
+ --> $DIR/const-match-pat-generic.rs:20:9
|
LL | const { f(V) } => {},
| ^^^^^^^^^^^^^^
error: constant pattern depends on a generic parameter
- --> $DIR/const-match-pat-generic.rs:21:9
+ --> $DIR/const-match-pat-generic.rs:20:9
|
LL | const { f(V) } => {},
| ^^^^^^^^^^^^^^
--- /dev/null
+#![feature(debugger_visualizer)]
+#![debugger_visualizer(random_file = "../foo.random")] //~ ERROR invalid argument
+
+fn main() {}
--- /dev/null
+error: invalid argument
+ --> $DIR/invalid-debugger-visualizer-option.rs:2:1
+ |
+LL | #![debugger_visualizer(random_file = "../foo.random")]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: expected: `natvis_file = "..."`
+
+error: aborting due to previous error
+
--- /dev/null
+#![feature(debugger_visualizer)]
+
+#[debugger_visualizer(natvis_file = "../foo.natvis")] //~ ERROR attribute should be applied to a module
+
+fn main() {}
--- /dev/null
+error: attribute should be applied to a module
+ --> $DIR/invalid-debugger-visualizer-target.rs:3:1
+ |
+LL | #[debugger_visualizer(natvis_file = "../foo.natvis")]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
fn f3<'a>(x: &'a ((), &'a mut ())) {
f2(|| x.0, f1(x.1))
//~^ ERROR cannot borrow `*x.1` as mutable, as it is behind a `&` reference
-//~| ERROR cannot borrow `*x.1` as mutable because it is also borrowed as immutable
}
fn main() {}
LL | f2(|| x.0, f1(x.1))
| ^^^ `x` is a `&` reference, so the data it refers to cannot be borrowed as mutable
-error[E0502]: cannot borrow `*x.1` as mutable because it is also borrowed as immutable
- --> $DIR/issue-61623.rs:6:19
- |
-LL | f2(|| x.0, f1(x.1))
- | -- -- --- ^^^ mutable borrow occurs here
- | | | |
- | | | first borrow occurs due to use of `x` in closure
- | | immutable borrow occurs here
- | immutable borrow later used by call
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
-Some errors have detailed explanations: E0502, E0596.
-For more information about an error, try `rustc --explain E0502`.
+For more information about this error, try `rustc --explain E0596`.
--- /dev/null
+use std::mem::size_of;
+
+struct Foo<'s> { //~ ERROR: parameter `'s` is never used
+ array: [(); size_of::<&Self>()],
+ //~^ ERROR: generic `Self` types are currently not permitted in anonymous constants
+}
+
+// The below is taken from https://github.com/rust-lang/rust/issues/66152#issuecomment-550275017
+// as the root cause seems the same.
+
+const fn foo<T>() -> usize {
+ 0
+}
+
+struct Bar<'a> { //~ ERROR: parameter `'a` is never used
+ beta: [(); foo::<&'a ()>()], //~ ERROR: a non-static lifetime is not allowed in a `const`
+}
+
+fn main() {}
--- /dev/null
+error[E0658]: a non-static lifetime is not allowed in a `const`
+ --> $DIR/issue-64173-unused-lifetimes.rs:16:23
+ |
+LL | beta: [(); foo::<&'a ()>()],
+ | ^^
+ |
+ = note: see issue #76560 <https://github.com/rust-lang/rust/issues/76560> for more information
+ = help: add `#![feature(generic_const_exprs)]` to the crate attributes to enable
+
+error: generic `Self` types are currently not permitted in anonymous constants
+ --> $DIR/issue-64173-unused-lifetimes.rs:4:28
+ |
+LL | array: [(); size_of::<&Self>()],
+ | ^^^^
+
+error[E0392]: parameter `'s` is never used
+ --> $DIR/issue-64173-unused-lifetimes.rs:3:12
+ |
+LL | struct Foo<'s> {
+ | ^^ unused parameter
+ |
+ = help: consider removing `'s`, referring to it in a field, or using a marker such as `PhantomData`
+
+error[E0392]: parameter `'a` is never used
+ --> $DIR/issue-64173-unused-lifetimes.rs:15:12
+ |
+LL | struct Bar<'a> {
+ | ^^ unused parameter
+ |
+ = help: consider removing `'a`, referring to it in a field, or using a marker such as `PhantomData`
+
+error: aborting due to 4 previous errors
+
+Some errors have detailed explanations: E0392, E0658.
+For more information about an error, try `rustc --explain E0392`.
let mut conflict = Repro;
let prev = conflict.get();
conflict.insert(*prev + *x);
- //~^ WARN cannot borrow `conflict` as mutable because it is also borrowed as immutable
- //~| WARN this borrowing pattern was not meant to be accepted
}
+++ /dev/null
-warning: cannot borrow `conflict` as mutable because it is also borrowed as immutable
- --> $DIR/lint-no-err.rs:25:5
- |
-LL | let prev = conflict.get();
- | -------------- immutable borrow occurs here
-LL | conflict.insert(*prev + *x);
- | ^^^^^^^^^^^^^^^^-----^^^^^^
- | | |
- | | immutable borrow later used here
- | mutable borrow occurs here
- |
- = note: `#[warn(mutable_borrow_reservation_conflict)]` on by default
- = warning: this borrowing pattern was not meant to be accepted, and may become a hard error in the future
- = note: for more information, see issue #59159 <https://github.com/rust-lang/rust/issues/59159>
-
-warning: 1 warning emitted
-
#[proc_macro]
pub fn expand_expr_is(input: TokenStream) -> TokenStream {
+ expand_expr_is_inner(input, false)
+}
+
+#[proc_macro]
+pub fn expand_expr_is_trim(input: TokenStream) -> TokenStream {
+ expand_expr_is_inner(input, true)
+}
+
+fn expand_expr_is_inner(input: TokenStream, trim_invisible: bool) -> TokenStream {
let mut iter = input.into_iter();
let mut expected_tts = Vec::new();
loop {
}
}
- let expected = expected_tts.into_iter().collect::<TokenStream>();
- let expanded = iter.collect::<TokenStream>().expand_expr().expect("expand_expr failed");
- assert!(
- expected.to_string() == expanded.to_string(),
- "assert failed\nexpected: `{}`\nexpanded: `{}`",
- expected.to_string(),
- expanded.to_string()
- );
+ // If requested, trim the "invisible" delimiters at the start and end.
+ let expected = expected_tts.into_iter().collect::<TokenStream>().to_string();
+ let expected = if trim_invisible {
+ let len1 = "/*«*/ ".len();
+ let len2 = " /*»*/".len();
+ &expected[len1..expected.len() - len2]
+ } else {
+ &expected[..]
+ };
+ let expanded = iter.collect::<TokenStream>().expand_expr().unwrap().to_string();
+
+ assert_eq!(expected, expanded);
TokenStream::new()
}
PRINT-BANG INPUT (DISPLAY): self
+PRINT-BANG RE-COLLECTED (DISPLAY): /*«*/ self /*»*/
PRINT-BANG INPUT (DEBUG): TokenStream [
Group {
delimiter: None,
]
PRINT-BANG INPUT (DISPLAY): 1 + 1, { "a" }, let a = 1;, String, my_name, 'a, my_val = 30,
std::option::Option, pub(in some::path) , [a b c], -30
-PRINT-BANG RE-COLLECTED (DISPLAY): 1 + 1, { "a" }, let a = 1, String, my_name, 'a, my_val = 30,
-std :: option :: Option, pub(in some :: path), [a b c], - 30
+PRINT-BANG RE-COLLECTED (DISPLAY): /*«*/ 1 + 1 /*»*/, /*«*/ { "a" } /*»*/, /*«*/ let a = 1 /*»*/, /*«*/
+String /*»*/, my_name, /*«*/ 'a /*»*/, /*«*/ my_val = 30 /*»*/, /*«*/
+std :: option :: Option /*»*/, /*«*/ pub(in some :: path) /*»*/, [a b c],
+/*«*/ - 30 /*»*/
PRINT-BANG INPUT (DEBUG): TokenStream [
Group {
delimiter: None,
},
]
PRINT-BANG INPUT (DISPLAY): (a, b)
+PRINT-BANG RE-COLLECTED (DISPLAY): /*«*/ (a, b) /*»*/
PRINT-BANG INPUT (DEBUG): TokenStream [
Group {
delimiter: None,
PRINT-BANG INPUT (DISPLAY): Vec<u8>
-PRINT-BANG RE-COLLECTED (DISPLAY): Vec < u8 >
+PRINT-BANG RE-COLLECTED (DISPLAY): /*«*/ Vec < u8 > /*»*/
PRINT-BANG INPUT (DEBUG): TokenStream [
Group {
delimiter: None,
extern crate expand_expr;
-use expand_expr::{
- check_expand_expr_file, echo_pm, expand_expr_fail, expand_expr_is, recursive_expand,
-};
+use expand_expr::{check_expand_expr_file, echo_pm, expand_expr_fail, expand_expr_is};
+use expand_expr::{expand_expr_is_trim, recursive_expand};
+
// Check builtin macros can be expanded.
macro_rules! simple_lit {
($l:literal) => {
- expand_expr_is!($l, $l);
- expand_expr_is!($l, echo_lit!($l));
- expand_expr_is!($l, echo_expr!($l));
- expand_expr_is!($l, echo_tts!($l));
- expand_expr_is!($l, echo_pm!($l));
+ expand_expr_is_trim!($l, $l);
+ expand_expr_is_trim!($l, echo_lit!($l));
+ expand_expr_is_trim!($l, echo_expr!($l));
+ expand_expr_is_trim!($l, echo_tts!($l));
+ expand_expr_is_trim!($l, echo_pm!($l));
const _: () = {
macro_rules! mac {
() => {
$l
};
}
- expand_expr_is!($l, mac!());
- expand_expr_is!($l, echo_expr!(mac!()));
- expand_expr_is!($l, echo_tts!(mac!()));
- expand_expr_is!($l, echo_pm!(mac!()));
+ expand_expr_is_trim!($l, mac!());
+ expand_expr_is_trim!($l, echo_expr!(mac!()));
+ expand_expr_is_trim!($l, echo_tts!(mac!()));
+ expand_expr_is_trim!($l, echo_pm!(mac!()));
};
};
}
PRINT-DERIVE INPUT (DISPLAY): enum E { V = { let _ = #[allow(warnings)] 0 ; 0 }, }
-PRINT-DERIVE DEEP-RE-COLLECTED (DISPLAY): enum E { V = { let _ = #[allow(warnings)] #[allow(warnings)] 0 ; 0 }, }
+PRINT-DERIVE DEEP-RE-COLLECTED (DISPLAY): enum E
+{ V = { let _ = /*«*/ #[allow(warnings)] #[allow(warnings)] 0 /*»*/ ; 0 }, }
PRINT-DERIVE INPUT (DEBUG): TokenStream [
Ident {
ident: "enum",
},
]
PRINT-DERIVE INPUT (DISPLAY): enum E { V = { let _ = { 0; } ; 0 }, }
-PRINT-DERIVE DEEP-RE-COLLECTED (DISPLAY): enum E { V = { let _ = { 0 } ; 0 }, }
+PRINT-DERIVE DEEP-RE-COLLECTED (DISPLAY): enum E { V = { let _ = { /*«*/ 0 /*»*/ } ; 0 }, }
PRINT-DERIVE INPUT (DEBUG): TokenStream [
Ident {
ident: "enum",
},
]
PRINT-DERIVE INPUT (DISPLAY): enum E { V = { let _ = { {} } ; 0 }, }
+PRINT-DERIVE DEEP-RE-COLLECTED (DISPLAY): enum E { V = { let _ = { /*«*/ {} /*»*/ } ; 0 }, }
PRINT-DERIVE INPUT (DEBUG): TokenStream [
Ident {
ident: "enum",
},
]
PRINT-DERIVE INPUT (DISPLAY): enum E { V = { let _ = { PATH; } ; 0 }, }
-PRINT-DERIVE DEEP-RE-COLLECTED (DISPLAY): enum E { V = { let _ = { PATH } ; 0 }, }
+PRINT-DERIVE DEEP-RE-COLLECTED (DISPLAY): enum E { V = { let _ = { /*«*/ PATH /*»*/ } ; 0 }, }
PRINT-DERIVE INPUT (DEBUG): TokenStream [
Ident {
ident: "enum",
},
]
PRINT-DERIVE INPUT (DISPLAY): enum E { V = { let _ = { 0 + 1; } ; 0 }, }
-PRINT-DERIVE DEEP-RE-COLLECTED (DISPLAY): enum E { V = { let _ = { 0 + 1 } ; 0 }, }
+PRINT-DERIVE DEEP-RE-COLLECTED (DISPLAY): enum E { V = { let _ = { /*«*/ 0 + 1 /*»*/ } ; 0 }, }
PRINT-DERIVE INPUT (DEBUG): TokenStream [
Ident {
ident: "enum",
},
]
PRINT-DERIVE INPUT (DISPLAY): enum E { V = { let _ = { PATH + 1; } ; 0 }, }
-PRINT-DERIVE DEEP-RE-COLLECTED (DISPLAY): enum E { V = { let _ = { PATH + 1 } ; 0 }, }
+PRINT-DERIVE DEEP-RE-COLLECTED (DISPLAY): enum E { V = { let _ = { /*«*/ PATH + 1 /*»*/ } ; 0 }, }
PRINT-DERIVE INPUT (DEBUG): TokenStream [
Ident {
ident: "enum",
},
]
PRINT-BANG INPUT (DISPLAY): 1 + 1 * 2
+PRINT-BANG RE-COLLECTED (DISPLAY): /*«*/ 1 + 1 /*»*/ * 2
PRINT-BANG INPUT (DEBUG): TokenStream [
Group {
delimiter: None,
PRINT-BANG INPUT (DISPLAY): foo! { #[fake_attr] mod bar {
#![doc = r" Foo"]
} }
-PRINT-BANG DEEP-RE-COLLECTED (DISPLAY): foo! { #[fake_attr] mod bar { #! [doc = r" Foo"] } }
+PRINT-BANG DEEP-RE-COLLECTED (DISPLAY): foo! { #[fake_attr] /*«*/ mod bar { #! [doc = r" Foo"] } /*»*/ }
PRINT-BANG INPUT (DEBUG): TokenStream [
Ident {
ident: "foo",
PRINT-BANG INPUT (DISPLAY): ;
+PRINT-BANG RE-COLLECTED (DISPLAY): /*«*/ ; /*»*/
PRINT-BANG INPUT (DEBUG): TokenStream [
Group {
delimiter: None,
PRINT-BANG INPUT (DISPLAY): 0 + 1 + 2 + 3
+PRINT-BANG RE-COLLECTED (DISPLAY): /*«*/ 0 + 1 + 2 /*»*/ + 3
+PRINT-BANG DEEP-RE-COLLECTED (DISPLAY): /*«*/ /*«*/ /*«*/ 0 /*»*/ + 1 /*»*/ + 2 /*»*/ + 3
PRINT-BANG INPUT (DEBUG): TokenStream [
Group {
delimiter: None,
PRINT-BANG INPUT (DISPLAY): "hi" 1 + (25) + 1 (1 + 1)
+PRINT-BANG RE-COLLECTED (DISPLAY): "hi" /*«*/ 1 + (25) + 1 /*»*/ (1 + 1)
PRINT-BANG INPUT (DEBUG): TokenStream [
Literal {
kind: Str,
},
]
PRINT-BANG INPUT (DISPLAY): "hi" "hello".len() + "world".len() (1 + 1)
+PRINT-BANG RE-COLLECTED (DISPLAY): "hi" /*«*/ "hello".len() + "world".len() /*»*/ (1 + 1)
+PRINT-BANG DEEP-RE-COLLECTED (DISPLAY): "hi" /*«*/ /*«*/ "hello".len() /*»*/ + /*«*/ "world".len() /*»*/ /*»*/
+(1 + 1)
PRINT-BANG INPUT (DEBUG): TokenStream [
Literal {
kind: Str,
PRINT-ATTR_ARGS INPUT (DISPLAY): a, line!(), b
-PRINT-ATTR_ARGS RE-COLLECTED (DISPLAY): a, line! (), b
+PRINT-ATTR_ARGS RE-COLLECTED (DISPLAY): a, /*«*/ line! () /*»*/, b
PRINT-ATTR_ARGS INPUT (DEBUG): TokenStream [
Ident {
ident: "a",
PRINT-BANG INPUT (DISPLAY): struct S;
-PRINT-BANG RE-COLLECTED (DISPLAY): struct S ;
+PRINT-BANG RE-COLLECTED (DISPLAY): /*«*/ struct S ; /*»*/
PRINT-BANG INPUT (DEBUG): TokenStream [
Group {
delimiter: None,
macro one($a:expr, $b:expr) {
two!($a, $b);
- //~^ ERROR first parent: "hello"
- //~| ERROR second parent: "world"
+ //~^ ERROR first parent: /*«*/ "hello" /*»*/
+ //~| ERROR second parent: /*«*/ "world" /*»*/
}
macro two($a:expr, $b:expr) {
three!($a, $b);
- //~^ ERROR first final: "hello"
- //~| ERROR second final: "world"
- //~| ERROR first final: "yay"
- //~| ERROR second final: "rust"
+ //~^ ERROR first final: /*«*/ "hello" /*»*/
+ //~| ERROR second final: /*«*/ "world" /*»*/
+ //~| ERROR first final: /*«*/ "yay" /*»*/
+ //~| ERROR second final: /*«*/ "rust" /*»*/
}
// forwarding tokens directly doesn't create a new source chain
fn main() {
one!("hello", "world");
- //~^ ERROR first grandparent: "hello"
- //~| ERROR second grandparent: "world"
- //~| ERROR first source: "hello"
- //~| ERROR second source: "world"
+ //~^ ERROR first grandparent: /*«*/ "hello" /*»*/
+ //~| ERROR second grandparent: /*«*/ "world" /*»*/
+ //~| ERROR first source: /*«*/ "hello" /*»*/
+ //~| ERROR second source: /*«*/ "world" /*»*/
two!("yay", "rust");
- //~^ ERROR first parent: "yay"
- //~| ERROR second parent: "rust"
- //~| ERROR first source: "yay"
- //~| ERROR second source: "rust"
+ //~^ ERROR first parent: /*«*/ "yay" /*»*/
+ //~| ERROR second parent: /*«*/ "rust" /*»*/
+ //~| ERROR first source: /*«*/ "yay" /*»*/
+ //~| ERROR second source: /*«*/ "rust" /*»*/
three!("hip", "hop");
//~^ ERROR first final: "hip"
-error: first final: "hello"
+error: first final: /*«*/ "hello" /*»*/
--> $DIR/parent-source-spans.rs:16:12
|
LL | three!($a, $b);
|
= note: this error originates in the macro `two` (in Nightly builds, run with -Z macro-backtrace for more info)
-error: second final: "world"
+error: second final: /*«*/ "world" /*»*/
--> $DIR/parent-source-spans.rs:16:16
|
LL | three!($a, $b);
|
= note: this error originates in the macro `two` (in Nightly builds, run with -Z macro-backtrace for more info)
-error: first parent: "hello"
+error: first parent: /*«*/ "hello" /*»*/
--> $DIR/parent-source-spans.rs:10:5
|
LL | two!($a, $b);
|
= note: this error originates in the macro `one` (in Nightly builds, run with -Z macro-backtrace for more info)
-error: second parent: "world"
+error: second parent: /*«*/ "world" /*»*/
--> $DIR/parent-source-spans.rs:10:5
|
LL | two!($a, $b);
|
= note: this error originates in the macro `one` (in Nightly builds, run with -Z macro-backtrace for more info)
-error: first grandparent: "hello"
+error: first grandparent: /*«*/ "hello" /*»*/
--> $DIR/parent-source-spans.rs:36:5
|
LL | one!("hello", "world");
| ^^^^^^^^^^^^^^^^^^^^^^
-error: second grandparent: "world"
+error: second grandparent: /*«*/ "world" /*»*/
--> $DIR/parent-source-spans.rs:36:5
|
LL | one!("hello", "world");
| ^^^^^^^^^^^^^^^^^^^^^^
-error: first source: "hello"
+error: first source: /*«*/ "hello" /*»*/
--> $DIR/parent-source-spans.rs:36:5
|
LL | one!("hello", "world");
| ^^^^^^^^^^^^^^^^^^^^^^
-error: second source: "world"
+error: second source: /*«*/ "world" /*»*/
--> $DIR/parent-source-spans.rs:36:5
|
LL | one!("hello", "world");
| ^^^^^^^^^^^^^^^^^^^^^^
-error: first final: "yay"
+error: first final: /*«*/ "yay" /*»*/
--> $DIR/parent-source-spans.rs:16:12
|
LL | three!($a, $b);
|
= note: this error originates in the macro `two` (in Nightly builds, run with -Z macro-backtrace for more info)
-error: second final: "rust"
+error: second final: /*«*/ "rust" /*»*/
--> $DIR/parent-source-spans.rs:16:16
|
LL | three!($a, $b);
|
= note: this error originates in the macro `two` (in Nightly builds, run with -Z macro-backtrace for more info)
-error: first parent: "yay"
+error: first parent: /*«*/ "yay" /*»*/
--> $DIR/parent-source-spans.rs:42:5
|
LL | two!("yay", "rust");
| ^^^^^^^^^^^^^^^^^^^
-error: second parent: "rust"
+error: second parent: /*«*/ "rust" /*»*/
--> $DIR/parent-source-spans.rs:42:5
|
LL | two!("yay", "rust");
| ^^^^^^^^^^^^^^^^^^^
-error: first source: "yay"
+error: first source: /*«*/ "yay" /*»*/
--> $DIR/parent-source-spans.rs:42:5
|
LL | two!("yay", "rust");
| ^^^^^^^^^^^^^^^^^^^
-error: second source: "rust"
+error: second source: /*«*/ "rust" /*»*/
--> $DIR/parent-source-spans.rs:42:5
|
LL | two!("yay", "rust");
|
LL | static_method();
| ^^^^^^^^^^^^^ not found in this scope
+ |
+help: consider using the associated function
+ |
+LL | Self::static_method();
+ | ~~~~~~~~~~~~~~~~~~~
error[E0425]: cannot find function `purr` in this scope
--> $DIR/issue-2356.rs:54:9
|
LL | grow_older();
| ^^^^^^^^^^ not found in this scope
+ |
+help: consider using the associated function
+ |
+LL | Self::grow_older();
+ | ~~~~~~~~~~~~~~~~
error[E0425]: cannot find function `shave` in this scope
--> $DIR/issue-2356.rs:74:5
--- /dev/null
+fn main() {}
+
+struct S;
+
+impl S {
+ fn foo() {}
+
+ fn bar(&self) {}
+
+ fn baz(a: u8, b: u8) {}
+
+ fn b() {
+ fn c() {
+ foo(); //~ ERROR cannot find function `foo` in this scope
+ }
+ foo(); //~ ERROR cannot find function `foo` in this scope
+ bar(); //~ ERROR cannot find function `bar` in this scope
+ baz(2, 3); //~ ERROR cannot find function `baz` in this scope
+ }
+}
--- /dev/null
+error[E0425]: cannot find function `foo` in this scope
+ --> $DIR/assoc_fn_without_self.rs:14:13
+ |
+LL | foo();
+ | ^^^ not found in this scope
+
+error[E0425]: cannot find function `foo` in this scope
+ --> $DIR/assoc_fn_without_self.rs:16:9
+ |
+LL | foo();
+ | ^^^ not found in this scope
+ |
+help: consider using the associated function
+ |
+LL | Self::foo();
+ | ~~~~~~~~~
+
+error[E0425]: cannot find function `bar` in this scope
+ --> $DIR/assoc_fn_without_self.rs:17:9
+ |
+LL | bar();
+ | ^^^ not found in this scope
+
+error[E0425]: cannot find function `baz` in this scope
+ --> $DIR/assoc_fn_without_self.rs:18:9
+ |
+LL | baz(2, 3);
+ | ^^^ not found in this scope
+ |
+help: consider using the associated function
+ |
+LL | Self::baz(2, 3);
+ | ~~~~~~~~~
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0425`.
}
impl<T> foreign_crate::ForeignTrait for AliasOfForeignType<T> {}
-//~^ ERROR the type parameter `T` is not constrained by the impl trait, self type, or predicates
+//~^ ERROR cannot implement trait on type alias impl trait
fn main() {}
-error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predicates
- --> $DIR/coherence.rs:14:6
+error: cannot implement trait on type alias impl trait
+ --> $DIR/coherence.rs:14:41
|
LL | impl<T> foreign_crate::ForeignTrait for AliasOfForeignType<T> {}
- | ^ unconstrained type parameter
+ | ^^^^^^^^^^^^^^^^^^^^^
+ |
+note: type alias impl trait defined here
+ --> $DIR/coherence.rs:9:30
+ |
+LL | type AliasOfForeignType<T> = impl LocalTrait;
+ | ^^^^^^^^^^^^^^^
error: aborting due to previous error
-For more information about this error, try `rustc --explain E0207`.
attributes:
label: Description
description: >
- Please provide a discription of the issue, along with any information
+ Please provide a description of the issue, along with any information
you feel relevant to replicate it.
validations:
required: true
id: reproducer
attributes:
label: Reproducer
- description: Please provide the code and steps to repoduce the bug
+ description: Please provide the code and steps to reproduce the bug
value: |
I tried this code:
attributes:
label: Reproducer
description: >
- Please provide the code and steps to repoduce the bug together with the
+ Please provide the code and steps to reproduce the bug together with the
output from Clippy.
value: |
I tried this code:
branches-ignore:
- auto
- try
- # Don't run Clippy tests, when only textfiles were modified
+ # Don't run Clippy tests, when only text files were modified
paths-ignore:
- 'COPYRIGHT'
- 'LICENSE-*'
- '**.md'
- '**.txt'
pull_request:
- # Don't run Clippy tests, when only textfiles were modified
+ # Don't run Clippy tests, when only text files were modified
paths-ignore:
- 'COPYRIGHT'
- 'LICENSE-*'
github_token: "${{ secrets.github_token }}"
- name: Checkout
- uses: actions/checkout@v2.3.3
+ uses: actions/checkout@v3.0.2
- name: Install toolchain
run: rustup show active-toolchain
github_token: "${{ secrets.github_token }}"
- name: Checkout
- uses: actions/checkout@v2.3.3
+ uses: actions/checkout@v3.0.2
with:
ref: ${{ github.ref }}
if: matrix.host == 'i686-unknown-linux-gnu'
- name: Checkout
- uses: actions/checkout@v2.3.3
+ uses: actions/checkout@v3.0.2
- name: Install toolchain
run: rustup show active-toolchain
github_token: "${{ secrets.github_token }}"
- name: Checkout
- uses: actions/checkout@v2.3.3
+ uses: actions/checkout@v3.0.2
- name: Install toolchain
run: rustup show active-toolchain
github_token: "${{ secrets.github_token }}"
- name: Checkout
- uses: actions/checkout@v2.3.3
+ uses: actions/checkout@v3.0.2
- name: Install toolchain
run: rustup show active-toolchain
steps:
# Setup
- name: Checkout
- uses: actions/checkout@v2.3.3
+ uses: actions/checkout@v3.0.2
# Run
- name: Build
steps:
# Setup
- name: Checkout
- uses: actions/checkout@v2.3.3
+ uses: actions/checkout@v3.0.2
- name: Checkout
- uses: actions/checkout@v2.3.3
+ uses: actions/checkout@v3.0.2
with:
ref: ${{ env.TARGET_BRANCH }}
path: 'out'
steps:
# Setup
- name: Checkout
- uses: actions/checkout@v2.3.3
+ uses: actions/checkout@v3.0.2
- name: Setup Node.js
uses: actions/setup-node@v1.4.4
## Unreleased / In Rust Nightly
-[57b3c4b...master](https://github.com/rust-lang/rust-clippy/compare/57b3c4b...master)
+[d0cf3481...master](https://github.com/rust-lang/rust-clippy/compare/d0cf3481...master)
+
+## Rust 1.61 (beta)
+
+Current beta, released 2022-05-19
+
+[57b3c4b...d0cf3481](https://github.com/rust-lang/rust-clippy/compare/57b3c4b...d0cf3481)
+
+### New Lints
+
+* [`only_used_in_recursion`]
+ [#8422](https://github.com/rust-lang/rust-clippy/pull/8422)
+* [`cast_enum_truncation`]
+ [#8381](https://github.com/rust-lang/rust-clippy/pull/8381)
+* [`missing_spin_loop`]
+ [#8174](https://github.com/rust-lang/rust-clippy/pull/8174)
+* [`deref_by_slicing`]
+ [#8218](https://github.com/rust-lang/rust-clippy/pull/8218)
+* [`needless_match`]
+ [#8471](https://github.com/rust-lang/rust-clippy/pull/8471)
+* [`allow_attributes_without_reason`] (Requires `#![feature(lint_reasons)]`)
+ [#8504](https://github.com/rust-lang/rust-clippy/pull/8504)
+* [`print_in_format_impl`]
+ [#8253](https://github.com/rust-lang/rust-clippy/pull/8253)
+* [`unnecessary_find_map`]
+ [#8489](https://github.com/rust-lang/rust-clippy/pull/8489)
+* [`or_then_unwrap`]
+ [#8561](https://github.com/rust-lang/rust-clippy/pull/8561)
+* [`unnecessary_join`]
+ [#8579](https://github.com/rust-lang/rust-clippy/pull/8579)
+* [`iter_with_drain`]
+ [#8483](https://github.com/rust-lang/rust-clippy/pull/8483)
+* [`cast_enum_constructor`]
+ [#8562](https://github.com/rust-lang/rust-clippy/pull/8562)
+* [`cast_slice_different_sizes`]
+ [#8445](https://github.com/rust-lang/rust-clippy/pull/8445)
+
+### Moves and Deprecations
+
+* Moved [`transmute_undefined_repr`] to `nursery` (now allow-by-default)
+ [#8432](https://github.com/rust-lang/rust-clippy/pull/8432)
+* Moved [`try_err`] to `restriction`
+ [#8544](https://github.com/rust-lang/rust-clippy/pull/8544)
+* Move [`iter_with_drain`] to `nursery`
+ [#8541](https://github.com/rust-lang/rust-clippy/pull/8541)
+* Renamed `to_string_in_display` to [`recursive_format_impl`]
+ [#8188](https://github.com/rust-lang/rust-clippy/pull/8188)
+
+### Enhancements
+
+* [`dbg_macro`]: The lint level can now be set with crate attributes and works inside macros
+ [#8411](https://github.com/rust-lang/rust-clippy/pull/8411)
+* [`ptr_as_ptr`]: Now works inside macros
+ [#8442](https://github.com/rust-lang/rust-clippy/pull/8442)
+* [`use_self`]: Now works for variants in match expressions
+ [#8456](https://github.com/rust-lang/rust-clippy/pull/8456)
+* [`await_holding_lock`]: Now lints for `parking_lot::{Mutex, RwLock}`
+ [#8419](https://github.com/rust-lang/rust-clippy/pull/8419)
+* [`recursive_format_impl`]: Now checks for format calls on `self`
+ [#8188](https://github.com/rust-lang/rust-clippy/pull/8188)
+
+### False Positive Fixes
+
+* [`new_without_default`]: No longer lints for `new()` methods with `#[doc(hidden)]`
+ [#8472](https://github.com/rust-lang/rust-clippy/pull/8472)
+* [`transmute_undefined_repr`]: No longer lints for single field structs with `#[repr(C)]`,
+ generic parameters, wide pointers, unions, tuples and allow several forms of type erasure
+ [#8425](https://github.com/rust-lang/rust-clippy/pull/8425)
+ [#8553](https://github.com/rust-lang/rust-clippy/pull/8553)
+ [#8440](https://github.com/rust-lang/rust-clippy/pull/8440)
+ [#8547](https://github.com/rust-lang/rust-clippy/pull/8547)
+* [`match_single_binding`], [`match_same_arms`], [`match_as_ref`], [`match_bool`]: No longer
+ lint `match` expressions with `cfg`ed arms
+ [#8443](https://github.com/rust-lang/rust-clippy/pull/8443)
+* [`single_component_path_imports`]: No longer lint on macros
+ [#8537](https://github.com/rust-lang/rust-clippy/pull/8537)
+* [`ptr_arg`]: Allow `&mut` arguments for `Cow<_>`
+ [#8552](https://github.com/rust-lang/rust-clippy/pull/8552)
+* [`needless_borrow`]: No longer lints for method calls
+ [#8441](https://github.com/rust-lang/rust-clippy/pull/8441)
+* [`match_same_arms`]: Now ensures that interposing arm patterns don't overlap
+ [#8232](https://github.com/rust-lang/rust-clippy/pull/8232)
+* [`default_trait_access`]: Now allows `Default::default` in update expressions
+ [#8433](https://github.com/rust-lang/rust-clippy/pull/8433)
+
+### Suggestion Fixes/Improvements
+
+* [`redundant_slicing`]: Fixed suggestion for a method calls
+ [#8218](https://github.com/rust-lang/rust-clippy/pull/8218)
+* [`map_flatten`]: Long suggestions will now be split up into two help messages
+ [#8520](https://github.com/rust-lang/rust-clippy/pull/8520)
+* [`unnecessary_lazy_evaluations`]: Now shows suggestions for longer code snippets
+ [#8543](https://github.com/rust-lang/rust-clippy/pull/8543)
+* [`unnecessary_sort_by`]: Now suggests `Reverse` including the path
+ [#8462](https://github.com/rust-lang/rust-clippy/pull/8462)
+* [`search_is_some`]: More suggestions are now `MachineApplicable`
+ [#8536](https://github.com/rust-lang/rust-clippy/pull/8536)
+
+### Documentation Improvements
+
+* [`new_without_default`]: Document `pub` requirement for the struct and fields
+ [#8429](https://github.com/rust-lang/rust-clippy/pull/8429)
## Rust 1.60
[`assign_op_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_op_pattern
[`assign_ops`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_ops
[`async_yields_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#async_yields_async
+[`await_holding_invalid_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_invalid_type
[`await_holding_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_lock
[`await_holding_refcell_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_refcell_ref
[`bad_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#bad_bit_mask
[`boxed_local`]: https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local
[`branches_sharing_code`]: https://rust-lang.github.io/rust-clippy/master/index.html#branches_sharing_code
[`builtin_type_shadow`]: https://rust-lang.github.io/rust-clippy/master/index.html#builtin_type_shadow
+[`bytes_count_to_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_count_to_len
[`bytes_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_nth
[`cargo_common_metadata`]: https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata
[`case_sensitive_file_extension_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#case_sensitive_file_extension_comparisons
[`duplicate_underscore_argument`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicate_underscore_argument
[`duration_subsec`]: https://rust-lang.github.io/rust-clippy/master/index.html#duration_subsec
[`else_if_without_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#else_if_without_else
+[`empty_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_drop
[`empty_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_enum
[`empty_line_after_outer_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_line_after_outer_attr
[`empty_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_loop
[`forget_non_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_non_drop
[`forget_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_ref
[`format_in_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#format_in_format_args
+[`format_push_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#format_push_string
[`from_iter_instead_of_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_iter_instead_of_collect
[`from_over_into`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into
[`from_str_radix_10`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_str_radix_10
[`invalid_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_regex
[`invalid_upcast_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_upcast_comparisons
[`invisible_characters`]: https://rust-lang.github.io/rust-clippy/master/index.html#invisible_characters
+[`is_digit_ascii_radix`]: https://rust-lang.github.io/rust-clippy/master/index.html#is_digit_ascii_radix
[`items_after_statements`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements
[`iter_cloned_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_cloned_collect
[`iter_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_count
[`large_const_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_const_arrays
[`large_digit_groups`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_digit_groups
[`large_enum_variant`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_enum_variant
+[`large_include_file`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_include_file
[`large_stack_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays
[`large_types_passed_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value
[`len_without_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_without_is_empty
[`needless_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes
[`needless_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_match
[`needless_option_as_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_as_deref
+[`needless_option_take`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_take
[`needless_pass_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_value
[`needless_question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_question_mark
[`needless_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_range_loop
[`ptr_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_eq
[`ptr_offset_with_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_offset_with_cast
[`pub_enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_enum_variant_names
+[`pub_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_use
[`question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#question_mark
[`range_minus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_minus_one
[`range_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_plus_one
[`transmute_undefined_repr`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_undefined_repr
[`transmutes_expressible_as_ptr_casts`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmutes_expressible_as_ptr_casts
[`transmuting_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmuting_null
+[`trim_split_whitespace`]: https://rust-lang.github.io/rust-clippy/master/index.html#trim_split_whitespace
[`trivial_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivial_regex
[`trivially_copy_pass_by_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref
[`try_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#try_err
[`unnecessary_lazy_evaluations`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations
[`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed
[`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation
+[`unnecessary_owned_empty_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_owned_empty_strings
[`unnecessary_self_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_self_imports
[`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by
[`unnecessary_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_to_owned
edition = "2021"
[dependencies]
+aho-corasick = "0.7"
clap = "2.33"
indoc = "1.0"
itertools = "0.10.1"
+#![feature(let_chains)]
#![feature(let_else)]
#![feature(once_cell)]
#![feature(rustc_private)]
if matches.is_present("print-only") {
update_lints::print_lints();
} else if matches.is_present("check") {
- update_lints::run(update_lints::UpdateMode::Check);
+ update_lints::update(update_lints::UpdateMode::Check);
} else {
- update_lints::run(update_lints::UpdateMode::Change);
+ update_lints::update(update_lints::UpdateMode::Change);
}
},
("new_lint", Some(matches)) => {
matches.value_of("category"),
matches.is_present("msrv"),
) {
- Ok(_) => update_lints::run(update_lints::UpdateMode::Change),
+ Ok(_) => update_lints::update(update_lints::UpdateMode::Change),
Err(e) => eprintln!("Unable to create lint: {}", e),
}
},
("setup", Some(sub_command)) => match sub_command.subcommand() {
- ("intellij", Some(matches)) => setup::intellij::setup_rustc_src(
- matches
- .value_of("rustc-repo-path")
- .expect("this field is mandatory and therefore always valid"),
- ),
- ("git-hook", Some(matches)) => setup::git_hook::install_hook(matches.is_present("force-override")),
- ("vscode-tasks", Some(matches)) => setup::vscode::install_tasks(matches.is_present("force-override")),
+ ("intellij", Some(matches)) => {
+ if matches.is_present("remove") {
+ setup::intellij::remove_rustc_src();
+ } else {
+ setup::intellij::setup_rustc_src(
+ matches
+ .value_of("rustc-repo-path")
+ .expect("this field is mandatory and therefore always valid"),
+ );
+ }
+ },
+ ("git-hook", Some(matches)) => {
+ if matches.is_present("remove") {
+ setup::git_hook::remove_hook();
+ } else {
+ setup::git_hook::install_hook(matches.is_present("force-override"));
+ }
+ },
+ ("vscode-tasks", Some(matches)) => {
+ if matches.is_present("remove") {
+ setup::vscode::remove_tasks();
+ } else {
+ setup::vscode::install_tasks(matches.is_present("force-override"));
+ }
+ },
_ => {},
},
("remove", Some(sub_command)) => match sub_command.subcommand() {
let path = matches.value_of("path").unwrap();
lint::run(path);
},
+ ("rename_lint", Some(matches)) => {
+ let old_name = matches.value_of("old_name").unwrap();
+ let new_name = matches.value_of("new_name").unwrap_or(old_name);
+ let uplift = matches.is_present("uplift");
+ update_lints::rename(old_name, new_name, uplift);
+ },
_ => {},
}
}
.subcommand(
SubCommand::with_name("intellij")
.about("Alter dependencies so Intellij Rust can find rustc internals")
+ .arg(
+ Arg::with_name("remove")
+ .long("remove")
+ .help("Remove the dependencies added with 'cargo dev setup intellij'")
+ .required(false),
+ )
.arg(
Arg::with_name("rustc-repo-path")
.long("repo-path")
.help("The path to a rustc repo that will be used for setting the dependencies")
.takes_value(true)
.value_name("path")
+ .conflicts_with("remove")
.required(true),
),
)
.subcommand(
SubCommand::with_name("git-hook")
.about("Add a pre-commit git hook that formats your code to make it look pretty")
+ .arg(
+ Arg::with_name("remove")
+ .long("remove")
+ .help("Remove the pre-commit hook added with 'cargo dev setup git-hook'")
+ .required(false),
+ )
.arg(
Arg::with_name("force-override")
.long("force-override")
.subcommand(
SubCommand::with_name("vscode-tasks")
.about("Add several tasks to vscode for formatting, validation and testing")
+ .arg(
+ Arg::with_name("remove")
+ .long("remove")
+ .help("Remove the tasks added with 'cargo dev setup vscode-tasks'")
+ .required(false),
+ )
.arg(
Arg::with_name("force-override")
.long("force-override")
.help("The path to a file or package directory to lint"),
),
)
+ .subcommand(
+ SubCommand::with_name("rename_lint")
+ .about("Renames the given lint")
+ .arg(
+ Arg::with_name("old_name")
+ .index(1)
+ .required(true)
+ .help("The name of the lint to rename"),
+ )
+ .arg(
+ Arg::with_name("new_name")
+ .index(2)
+ .required_unless("uplift")
+ .help("The new name of the lint"),
+ )
+ .arg(
+ Arg::with_name("uplift")
+ .long("uplift")
+ .help("This lint will be uplifted into rustc"),
+ ),
+ )
.get_matches()
}
use crate::clippy_project_root;
use indoc::indoc;
+use std::fmt::Write as _;
use std::fs::{self, OpenOptions};
use std::io::prelude::*;
use std::io::{self, ErrorKind};
)
});
- result.push_str(&format!(
+ let _ = write!(
+ result,
indoc! {r#"
declare_clippy_lint! {{
/// ### What it does
version = version,
name_upper = name_upper,
category = category,
- ));
+ );
result.push_str(&if enable_msrv {
format!(
const CLIPPY_DEV_DIR: &str = "clippy_dev";
/// This function verifies that the tool is being executed in the clippy directory.
-/// This is useful to ensure that setups only modify Clippys resources. The verification
+/// This is useful to ensure that setups only modify Clippy's resources. The verification
/// is done by checking that `clippy_dev` is a sub directory of the current directory.
///
/// It will print an error message and return `false` if the directory could not be
if path.exists() && path.is_dir() {
true
} else {
- eprintln!("error: unable to verify that the working directory is clippys directory");
+ eprintln!("error: unable to verify that the working directory is clippy's directory");
false
}
}
-use core::fmt::Write;
+use aho_corasick::AhoCorasickBuilder;
+use core::fmt::Write as _;
use itertools::Itertools;
use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind};
-use std::collections::HashMap;
+use std::collections::{HashMap, HashSet};
use std::ffi::OsStr;
use std::fs;
-use std::path::Path;
-use walkdir::WalkDir;
+use std::io::{self, Read as _, Seek as _, Write as _};
+use std::path::{Path, PathBuf};
+use walkdir::{DirEntry, WalkDir};
use crate::clippy_project_root;
/// # Panics
///
/// Panics if a file path could not read from or then written to
-#[allow(clippy::too_many_lines)]
-pub fn run(update_mode: UpdateMode) {
- let (lints, deprecated_lints) = gather_all();
+pub fn update(update_mode: UpdateMode) {
+ let (lints, deprecated_lints, renamed_lints) = gather_all();
+ generate_lint_files(update_mode, &lints, &deprecated_lints, &renamed_lints);
+}
- let internal_lints = Lint::internal_lints(&lints);
- let usable_lints = Lint::usable_lints(&lints);
+fn generate_lint_files(
+ update_mode: UpdateMode,
+ lints: &[Lint],
+ deprecated_lints: &[DeprecatedLint],
+ renamed_lints: &[RenamedLint],
+) {
+ let internal_lints = Lint::internal_lints(lints);
+ let usable_lints = Lint::usable_lints(lints);
let mut sorted_usable_lints = usable_lints.clone();
sorted_usable_lints.sort_by_key(|lint| lint.name.clone());
process_file(
"clippy_lints/src/lib.deprecated.rs",
update_mode,
- &gen_deprecated(&deprecated_lints),
+ &gen_deprecated(deprecated_lints),
);
let all_group_lints = usable_lints.iter().filter(|l| {
&content,
);
}
+
+ let content = gen_deprecated_lints_test(deprecated_lints);
+ process_file("tests/ui/deprecated.rs", update_mode, &content);
+
+ let content = gen_renamed_lints_test(renamed_lints);
+ process_file("tests/ui/rename.rs", update_mode, &content);
}
pub fn print_lints() {
- let (lint_list, _) = gather_all();
+ let (lint_list, _, _) = gather_all();
let usable_lints = Lint::usable_lints(&lint_list);
let usable_lint_count = usable_lints.len();
let grouped_by_lint_group = Lint::by_lint_group(usable_lints.into_iter());
println!("there are {} lints", usable_lint_count);
}
+/// Runs the `rename_lint` command.
+///
+/// This does the following:
+/// * Adds an entry to `renamed_lints.rs`.
+/// * Renames all lint attributes to the new name (e.g. `#[allow(clippy::lint_name)]`).
+/// * Renames the lint struct to the new name.
+/// * Renames the module containing the lint struct to the new name if it shares a name with the
+/// lint.
+///
+/// # Panics
+/// Panics for the following conditions:
+/// * If a file path could not read from or then written to
+/// * If either lint name has a prefix
+/// * If `old_name` doesn't name an existing lint.
+/// * If `old_name` names a deprecated or renamed lint.
+#[allow(clippy::too_many_lines)]
+pub fn rename(old_name: &str, new_name: &str, uplift: bool) {
+ if let Some((prefix, _)) = old_name.split_once("::") {
+ panic!("`{}` should not contain the `{}` prefix", old_name, prefix);
+ }
+ if let Some((prefix, _)) = new_name.split_once("::") {
+ panic!("`{}` should not contain the `{}` prefix", new_name, prefix);
+ }
+
+ let (mut lints, deprecated_lints, mut renamed_lints) = gather_all();
+ let mut old_lint_index = None;
+ let mut found_new_name = false;
+ for (i, lint) in lints.iter().enumerate() {
+ if lint.name == old_name {
+ old_lint_index = Some(i);
+ } else if lint.name == new_name {
+ found_new_name = true;
+ }
+ }
+ let old_lint_index = old_lint_index.unwrap_or_else(|| panic!("could not find lint `{}`", old_name));
+
+ let lint = RenamedLint {
+ old_name: format!("clippy::{}", old_name),
+ new_name: if uplift {
+ new_name.into()
+ } else {
+ format!("clippy::{}", new_name)
+ },
+ };
+
+ // Renamed lints and deprecated lints shouldn't have been found in the lint list, but check just in
+ // case.
+ assert!(
+ !renamed_lints.iter().any(|l| lint.old_name == l.old_name),
+ "`{}` has already been renamed",
+ old_name
+ );
+ assert!(
+ !deprecated_lints.iter().any(|l| lint.old_name == l.name),
+ "`{}` has already been deprecated",
+ old_name
+ );
+
+ // Update all lint level attributes. (`clippy::lint_name`)
+ for file in WalkDir::new(clippy_project_root())
+ .into_iter()
+ .map(Result::unwrap)
+ .filter(|f| {
+ let name = f.path().file_name();
+ let ext = f.path().extension();
+ (ext == Some(OsStr::new("rs")) || ext == Some(OsStr::new("fixed")))
+ && name != Some(OsStr::new("rename.rs"))
+ && name != Some(OsStr::new("renamed_lints.rs"))
+ })
+ {
+ rewrite_file(file.path(), |s| {
+ replace_ident_like(s, &[(&lint.old_name, &lint.new_name)])
+ });
+ }
+
+ renamed_lints.push(lint);
+ renamed_lints.sort_by(|lhs, rhs| {
+ lhs.new_name
+ .starts_with("clippy::")
+ .cmp(&rhs.new_name.starts_with("clippy::"))
+ .reverse()
+ .then_with(|| lhs.old_name.cmp(&rhs.old_name))
+ });
+
+ write_file(
+ Path::new("clippy_lints/src/renamed_lints.rs"),
+ &gen_renamed_lints_list(&renamed_lints),
+ );
+
+ if uplift {
+ write_file(Path::new("tests/ui/rename.rs"), &gen_renamed_lints_test(&renamed_lints));
+ println!(
+ "`{}` has be uplifted. All the code inside `clippy_lints` related to it needs to be removed manually.",
+ old_name
+ );
+ } else if found_new_name {
+ write_file(Path::new("tests/ui/rename.rs"), &gen_renamed_lints_test(&renamed_lints));
+ println!(
+ "`{}` is already defined. The old linting code inside `clippy_lints` needs to be updated/removed manually.",
+ new_name
+ );
+ } else {
+ // Rename the lint struct and source files sharing a name with the lint.
+ let lint = &mut lints[old_lint_index];
+ let old_name_upper = old_name.to_uppercase();
+ let new_name_upper = new_name.to_uppercase();
+ lint.name = new_name.into();
+
+ // Rename test files. only rename `.stderr` and `.fixed` files if the new test name doesn't exist.
+ if try_rename_file(
+ Path::new(&format!("tests/ui/{}.rs", old_name)),
+ Path::new(&format!("tests/ui/{}.rs", new_name)),
+ ) {
+ try_rename_file(
+ Path::new(&format!("tests/ui/{}.stderr", old_name)),
+ Path::new(&format!("tests/ui/{}.stderr", new_name)),
+ );
+ try_rename_file(
+ Path::new(&format!("tests/ui/{}.fixed", old_name)),
+ Path::new(&format!("tests/ui/{}.fixed", new_name)),
+ );
+ }
+
+ // Try to rename the file containing the lint if the file name matches the lint's name.
+ let replacements;
+ let replacements = if lint.module == old_name
+ && try_rename_file(
+ Path::new(&format!("clippy_lints/src/{}.rs", old_name)),
+ Path::new(&format!("clippy_lints/src/{}.rs", new_name)),
+ ) {
+ // Edit the module name in the lint list. Note there could be multiple lints.
+ for lint in lints.iter_mut().filter(|l| l.module == old_name) {
+ lint.module = new_name.into();
+ }
+ replacements = [(&*old_name_upper, &*new_name_upper), (old_name, new_name)];
+ replacements.as_slice()
+ } else if !lint.module.contains("::")
+ // Catch cases like `methods/lint_name.rs` where the lint is stored in `methods/mod.rs`
+ && try_rename_file(
+ Path::new(&format!("clippy_lints/src/{}/{}.rs", lint.module, old_name)),
+ Path::new(&format!("clippy_lints/src/{}/{}.rs", lint.module, new_name)),
+ )
+ {
+ // Edit the module name in the lint list. Note there could be multiple lints, or none.
+ let renamed_mod = format!("{}::{}", lint.module, old_name);
+ for lint in lints.iter_mut().filter(|l| l.module == renamed_mod) {
+ lint.module = format!("{}::{}", lint.module, new_name);
+ }
+ replacements = [(&*old_name_upper, &*new_name_upper), (old_name, new_name)];
+ replacements.as_slice()
+ } else {
+ replacements = [(&*old_name_upper, &*new_name_upper), ("", "")];
+ &replacements[0..1]
+ };
+
+ // Don't change `clippy_utils/src/renamed_lints.rs` here as it would try to edit the lint being
+ // renamed.
+ for (_, file) in clippy_lints_src_files().filter(|(rel_path, _)| rel_path != OsStr::new("renamed_lints.rs")) {
+ rewrite_file(file.path(), |s| replace_ident_like(s, replacements));
+ }
+
+ generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints);
+ println!("{} has been successfully renamed", old_name);
+ }
+
+ println!("note: `cargo uitest` still needs to be run to update the test results");
+}
+
+/// Replace substrings if they aren't bordered by identifier characters. Returns `None` if there
+/// were no replacements.
+fn replace_ident_like(contents: &str, replacements: &[(&str, &str)]) -> Option<String> {
+ fn is_ident_char(c: u8) -> bool {
+ matches!(c, b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'_')
+ }
+
+ let searcher = AhoCorasickBuilder::new()
+ .dfa(true)
+ .match_kind(aho_corasick::MatchKind::LeftmostLongest)
+ .build_with_size::<u16, _, _>(replacements.iter().map(|&(x, _)| x.as_bytes()))
+ .unwrap();
+
+ let mut result = String::with_capacity(contents.len() + 1024);
+ let mut pos = 0;
+ let mut edited = false;
+ for m in searcher.find_iter(contents) {
+ let (old, new) = replacements[m.pattern()];
+ result.push_str(&contents[pos..m.start()]);
+ result.push_str(
+ if !is_ident_char(contents.as_bytes().get(m.start().wrapping_sub(1)).copied().unwrap_or(0))
+ && !is_ident_char(contents.as_bytes().get(m.end()).copied().unwrap_or(0))
+ {
+ edited = true;
+ new
+ } else {
+ old
+ },
+ );
+ pos = m.end();
+ }
+ result.push_str(&contents[pos..]);
+ edited.then(|| result)
+}
+
fn round_to_fifty(count: usize) -> usize {
count / 50 * 50
}
}
}
+struct RenamedLint {
+ old_name: String,
+ new_name: String,
+}
+impl RenamedLint {
+ fn new(old_name: &str, new_name: &str) -> Self {
+ Self {
+ old_name: remove_line_splices(old_name),
+ new_name: remove_line_splices(new_name),
+ }
+ }
+}
+
/// Generates the code for registering a group
fn gen_lint_group_list<'a>(group_name: &str, lints: impl Iterator<Item = &'a Lint>) -> String {
let mut details: Vec<_> = lints.map(|l| (&l.module, l.name.to_uppercase())).collect();
let mut output = GENERATED_FILE_COMMENT.to_string();
- output.push_str(&format!(
- "store.register_group(true, \"clippy::{0}\", Some(\"clippy_{0}\"), vec![\n",
+ let _ = writeln!(
+ output,
+ "store.register_group(true, \"clippy::{0}\", Some(\"clippy_{0}\"), vec![",
group_name
- ));
+ );
for (module, name) in details {
- output.push_str(&format!(" LintId::of({}::{}),\n", module, name));
+ let _ = writeln!(output, " LintId::of({}::{}),", module, name);
}
output.push_str("])\n");
let mut output = GENERATED_FILE_COMMENT.to_string();
output.push_str("{\n");
for lint in lints {
- output.push_str(&format!(
+ let _ = write!(
+ output,
concat!(
" store.register_removed(\n",
" \"clippy::{}\",\n",
" );\n"
),
lint.name, lint.reason,
- ));
+ );
}
output.push_str("}\n");
if !is_public {
output.push_str(" #[cfg(feature = \"internal\")]\n");
}
- output.push_str(&format!(" {}::{},\n", module_name, lint_name));
+ let _ = writeln!(output, " {}::{},", module_name, lint_name);
}
output.push_str("])\n");
output
}
+fn gen_deprecated_lints_test(lints: &[DeprecatedLint]) -> String {
+ let mut res: String = GENERATED_FILE_COMMENT.into();
+ for lint in lints {
+ writeln!(res, "#![warn(clippy::{})]", lint.name).unwrap();
+ }
+ res.push_str("\nfn main() {}\n");
+ res
+}
+
+fn gen_renamed_lints_test(lints: &[RenamedLint]) -> String {
+ let mut seen_lints = HashSet::new();
+ let mut res: String = GENERATED_FILE_COMMENT.into();
+ res.push_str("// run-rustfix\n\n");
+ for lint in lints {
+ if seen_lints.insert(&lint.new_name) {
+ writeln!(res, "#![allow({})]", lint.new_name).unwrap();
+ }
+ }
+ seen_lints.clear();
+ for lint in lints {
+ if seen_lints.insert(&lint.old_name) {
+ writeln!(res, "#![warn({})]", lint.old_name).unwrap();
+ }
+ }
+ res.push_str("\nfn main() {}\n");
+ res
+}
+
+fn gen_renamed_lints_list(lints: &[RenamedLint]) -> String {
+ const HEADER: &str = "\
+ // This file is managed by `cargo dev rename_lint`. Prefer using that when possible.\n\n\
+ #[rustfmt::skip]\n\
+ pub static RENAMED_LINTS: &[(&str, &str)] = &[\n";
+
+ let mut res = String::from(HEADER);
+ for lint in lints {
+ writeln!(res, " (\"{}\", \"{}\"),", lint.old_name, lint.new_name).unwrap();
+ }
+ res.push_str("];\n");
+ res
+}
+
/// Gathers all lints defined in `clippy_lints/src`
-fn gather_all() -> (Vec<Lint>, Vec<DeprecatedLint>) {
+fn gather_all() -> (Vec<Lint>, Vec<DeprecatedLint>, Vec<RenamedLint>) {
let mut lints = Vec::with_capacity(1000);
let mut deprecated_lints = Vec::with_capacity(50);
- let root_path = clippy_project_root().join("clippy_lints/src");
+ let mut renamed_lints = Vec::with_capacity(50);
- for (rel_path, file) in WalkDir::new(&root_path)
- .into_iter()
- .map(Result::unwrap)
- .filter(|f| f.path().extension() == Some(OsStr::new("rs")))
- .map(|f| (f.path().strip_prefix(&root_path).unwrap().to_path_buf(), f))
- {
+ for (rel_path, file) in clippy_lints_src_files() {
let path = file.path();
let contents =
fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {}", path.display(), e));
module.strip_suffix(".rs").unwrap_or(&module)
};
- if module == "deprecated_lints" {
- parse_deprecated_contents(&contents, &mut deprecated_lints);
- } else {
- parse_contents(&contents, module, &mut lints);
+ match module {
+ "deprecated_lints" => parse_deprecated_contents(&contents, &mut deprecated_lints),
+ "renamed_lints" => parse_renamed_contents(&contents, &mut renamed_lints),
+ _ => parse_contents(&contents, module, &mut lints),
}
}
- (lints, deprecated_lints)
+ (lints, deprecated_lints, renamed_lints)
+}
+
+fn clippy_lints_src_files() -> impl Iterator<Item = (PathBuf, DirEntry)> {
+ let root_path = clippy_project_root().join("clippy_lints/src");
+ let iter = WalkDir::new(&root_path).into_iter();
+ iter.map(Result::unwrap)
+ .filter(|f| f.path().extension() == Some(OsStr::new("rs")))
+ .map(move |f| (f.path().strip_prefix(&root_path).unwrap().to_path_buf(), f))
}
macro_rules! match_tokens {
}
}
+fn parse_renamed_contents(contents: &str, lints: &mut Vec<RenamedLint>) {
+ for line in contents.lines() {
+ let mut offset = 0usize;
+ let mut iter = tokenize(line).map(|t| {
+ let range = offset..offset + t.len;
+ offset = range.end;
+ (t.kind, &line[range])
+ });
+ let (old_name, new_name) = match_tokens!(
+ iter,
+ // ("old_name",
+ Whitespace OpenParen Literal{kind: LiteralKind::Str{..},..}(old_name) Comma
+ // "new_name"),
+ Whitespace Literal{kind: LiteralKind::Str{..},..}(new_name) CloseParen Comma
+ );
+ lints.push(RenamedLint::new(old_name, new_name));
+ }
+}
+
/// Removes the line splices and surrounding quotes from a string literal
fn remove_line_splices(s: &str) -> String {
let s = s
Ok(res)
}
+fn try_rename_file(old_name: &Path, new_name: &Path) -> bool {
+ match fs::OpenOptions::new().create_new(true).write(true).open(new_name) {
+ Ok(file) => drop(file),
+ Err(e) if matches!(e.kind(), io::ErrorKind::AlreadyExists | io::ErrorKind::NotFound) => return false,
+ Err(e) => panic_file(e, new_name, "create"),
+ };
+ match fs::rename(old_name, new_name) {
+ Ok(()) => true,
+ Err(e) => {
+ drop(fs::remove_file(new_name));
+ if e.kind() == io::ErrorKind::NotFound {
+ false
+ } else {
+ panic_file(e, old_name, "rename");
+ }
+ },
+ }
+}
+
+#[allow(clippy::needless_pass_by_value)]
+fn panic_file(error: io::Error, name: &Path, action: &str) -> ! {
+ panic!("failed to {} file `{}`: {}", action, name.display(), error)
+}
+
+fn rewrite_file(path: &Path, f: impl FnOnce(&str) -> Option<String>) {
+ let mut file = fs::OpenOptions::new()
+ .write(true)
+ .read(true)
+ .open(path)
+ .unwrap_or_else(|e| panic_file(e, path, "open"));
+ let mut buf = String::new();
+ file.read_to_string(&mut buf)
+ .unwrap_or_else(|e| panic_file(e, path, "read"));
+ if let Some(new_contents) = f(&buf) {
+ file.rewind().unwrap_or_else(|e| panic_file(e, path, "write"));
+ file.write_all(new_contents.as_bytes())
+ .unwrap_or_else(|e| panic_file(e, path, "write"));
+ file.set_len(new_contents.len() as u64)
+ .unwrap_or_else(|e| panic_file(e, path, "write"));
+ }
+}
+
+fn write_file(path: &Path, contents: &str) {
+ fs::write(path, contents).unwrap_or_else(|e| panic_file(e, path, "write"));
+}
+
#[cfg(test)]
mod tests {
use super::*;
use clippy_utils::source::{first_line_of_span, is_present_in_source, snippet_opt, without_block_comments};
use clippy_utils::{extract_msrv_attr, meets_msrv};
use if_chain::if_chain;
-use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem};
+use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MacArgs, MacArgsEq, MetaItemKind, NestedMetaItem};
use rustc_errors::Applicability;
use rustc_hir::{
Block, Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, StmtKind, TraitFn, TraitItem, TraitItemKind,
}
if let Some(lint_list) = &attr.meta_item_list() {
if attr.ident().map_or(false, |ident| is_lint_level(ident.name)) {
- // permit `unused_imports`, `deprecated`, `unreachable_pub`,
- // `clippy::wildcard_imports`, and `clippy::enum_glob_use` for `use` items
- // and `unused_imports` for `extern crate` items with `macro_use`
for lint in lint_list {
match item.kind {
ItemKind::Use(..) => {
|| is_word(lint, sym::deprecated)
|| is_word(lint, sym!(unreachable_pub))
|| is_word(lint, sym!(unused))
- || extract_clippy_lint(lint)
- .map_or(false, |s| s.as_str() == "wildcard_imports")
- || extract_clippy_lint(lint)
- .map_or(false, |s| s.as_str() == "enum_glob_use")
+ || extract_clippy_lint(lint).map_or(false, |s| {
+ matches!(
+ s.as_str(),
+ "wildcard_imports" | "enum_glob_use" | "redundant_pub_crate",
+ )
+ })
{
return;
}
};
if attr.style == AttrStyle::Outer {
+ if let MacArgs::Eq(_, MacArgsEq::Ast(expr)) = &attr_item.args
+ && !matches!(expr.kind, rustc_ast::ExprKind::Lit(..)) {
+ return;
+ }
if attr_item.args.inner_tokens().is_empty() || !is_present_in_source(cx, attr.span) {
return;
}
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::{match_def_path, paths};
+use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def_id::DefId;
-use rustc_hir::{AsyncGeneratorKind, Body, BodyId, GeneratorKind};
+use rustc_hir::{def::Res, AsyncGeneratorKind, Body, BodyId, GeneratorKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::GeneratorInteriorTypeCause;
-use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::Span;
+use crate::utils::conf::DisallowedType;
+
declare_clippy_lint! {
/// ### What it does
/// Checks for calls to await while holding a non-async-aware MutexGuard.
"inside an async function, holding a `RefCell` ref while calling `await`"
}
-declare_lint_pass!(AwaitHolding => [AWAIT_HOLDING_LOCK, AWAIT_HOLDING_REFCELL_REF]);
+declare_clippy_lint! {
+ /// ### What it does
+ /// Allows users to configure types which should not be held across `await`
+ /// suspension points.
+ ///
+ /// ### Why is this bad?
+ /// There are some types which are perfectly "safe" to be used concurrently
+ /// from a memory access perspective but will cause bugs at runtime if they
+ /// are held in such a way.
+ ///
+ /// ### Known problems
+ ///
+ /// ### Example
+ ///
+ /// ```toml
+ /// await-holding-invalid-types = [
+ /// # You can specify a type name
+ /// "CustomLockType",
+ /// # You can (optionally) specify a reason
+ /// { path = "OtherCustomLockType", reason = "Relies on a thread local" }
+ /// ]
+ /// ```
+ ///
+ /// ```rust
+ /// # async fn baz() {}
+ /// struct CustomLockType;
+ /// struct OtherCustomLockType;
+ /// async fn foo() {
+ /// let _x = CustomLockType;
+ /// let _y = OtherCustomLockType;
+ /// baz().await; // Lint violation
+ /// }
+ /// ```
+ #[clippy::version = "1.49.0"]
+ pub AWAIT_HOLDING_INVALID_TYPE,
+ suspicious,
+ "holding a type across an await point which is not allowed to be held as per the configuration"
+}
+
+impl_lint_pass!(AwaitHolding => [AWAIT_HOLDING_LOCK, AWAIT_HOLDING_REFCELL_REF, AWAIT_HOLDING_INVALID_TYPE]);
+
+#[derive(Debug)]
+pub struct AwaitHolding {
+ conf_invalid_types: Vec<DisallowedType>,
+ def_ids: FxHashMap<DefId, DisallowedType>,
+}
+
+impl AwaitHolding {
+ pub(crate) fn new(conf_invalid_types: Vec<DisallowedType>) -> Self {
+ Self {
+ conf_invalid_types,
+ def_ids: FxHashMap::default(),
+ }
+ }
+}
impl LateLintPass<'_> for AwaitHolding {
+ fn check_crate(&mut self, cx: &LateContext<'_>) {
+ for conf in &self.conf_invalid_types {
+ let path = match conf {
+ DisallowedType::Simple(path) | DisallowedType::WithReason { path, .. } => path,
+ };
+ let segs: Vec<_> = path.split("::").collect();
+ if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs) {
+ self.def_ids.insert(id, conf.clone());
+ }
+ }
+ }
+
fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) {
use AsyncGeneratorKind::{Block, Closure, Fn};
if let Some(GeneratorKind::Async(Block | Closure | Fn)) = body.generator_kind {
hir_id: body.value.hir_id,
};
let typeck_results = cx.tcx.typeck_body(body_id);
- check_interior_types(
+ self.check_interior_types(
cx,
typeck_results.generator_interior_types.as_ref().skip_binder(),
body.value.span,
}
}
-fn check_interior_types(cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) {
- for ty_cause in ty_causes {
- if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind() {
- if is_mutex_guard(cx, adt.did()) {
- span_lint_and_then(
- cx,
- AWAIT_HOLDING_LOCK,
- ty_cause.span,
- "this `MutexGuard` is held across an `await` point",
- |diag| {
- diag.help(
- "consider using an async-aware `Mutex` type or ensuring the \
+impl AwaitHolding {
+ fn check_interior_types(&self, cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) {
+ for ty_cause in ty_causes {
+ if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind() {
+ if is_mutex_guard(cx, adt.did()) {
+ span_lint_and_then(
+ cx,
+ AWAIT_HOLDING_LOCK,
+ ty_cause.span,
+ "this `MutexGuard` is held across an `await` point",
+ |diag| {
+ diag.help(
+ "consider using an async-aware `Mutex` type or ensuring the \
`MutexGuard` is dropped before calling await",
- );
- diag.span_note(
- ty_cause.scope_span.unwrap_or(span),
- "these are all the `await` points this lock is held through",
- );
- },
- );
- }
- if is_refcell_ref(cx, adt.did()) {
- span_lint_and_then(
- cx,
- AWAIT_HOLDING_REFCELL_REF,
- ty_cause.span,
- "this `RefCell` reference is held across an `await` point",
- |diag| {
- diag.help("ensure the reference is dropped before calling `await`");
- diag.span_note(
- ty_cause.scope_span.unwrap_or(span),
- "these are all the `await` points this reference is held through",
- );
- },
- );
+ );
+ diag.span_note(
+ ty_cause.scope_span.unwrap_or(span),
+ "these are all the `await` points this lock is held through",
+ );
+ },
+ );
+ } else if is_refcell_ref(cx, adt.did()) {
+ span_lint_and_then(
+ cx,
+ AWAIT_HOLDING_REFCELL_REF,
+ ty_cause.span,
+ "this `RefCell` reference is held across an `await` point",
+ |diag| {
+ diag.help("ensure the reference is dropped before calling `await`");
+ diag.span_note(
+ ty_cause.scope_span.unwrap_or(span),
+ "these are all the `await` points this reference is held through",
+ );
+ },
+ );
+ } else if let Some(disallowed) = self.def_ids.get(&adt.did()) {
+ emit_invalid_type(cx, ty_cause.span, disallowed);
+ }
}
}
}
}
+fn emit_invalid_type(cx: &LateContext<'_>, span: Span, disallowed: &DisallowedType) {
+ let (type_name, reason) = match disallowed {
+ DisallowedType::Simple(path) => (path, &None),
+ DisallowedType::WithReason { path, reason } => (path, reason),
+ };
+
+ span_lint_and_then(
+ cx,
+ AWAIT_HOLDING_INVALID_TYPE,
+ span,
+ &format!("`{type_name}` may not be held across an `await` point per `clippy.toml`",),
+ |diag| {
+ if let Some(reason) = reason {
+ diag.note(reason.clone());
+ }
+ },
+ );
+}
+
fn is_mutex_guard(cx: &LateContext<'_>, def_id: DefId) -> bool {
match_def_path(cx, def_id, &paths::MUTEX_GUARD)
|| match_def_path(cx, def_id, &paths::RWLOCK_READ_GUARD)
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{match_def_path, paths};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::sym;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// It checks for `str::bytes().count()` and suggests replacing it with
+ /// `str::len()`.
+ ///
+ /// ### Why is this bad?
+ /// `str::bytes().count()` is longer and may not be as performant as using
+ /// `str::len()`.
+ ///
+ /// ### Example
+ /// ```rust
+ /// "hello".bytes().count();
+ /// String::from("hello").bytes().count();
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// "hello".len();
+ /// String::from("hello").len();
+ /// ```
+ #[clippy::version = "1.62.0"]
+ pub BYTES_COUNT_TO_LEN,
+ complexity,
+ "Using `bytes().count()` when `len()` performs the same functionality"
+}
+
+declare_lint_pass!(BytesCountToLen => [BYTES_COUNT_TO_LEN]);
+
+impl<'tcx> LateLintPass<'tcx> for BytesCountToLen {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
+ if_chain! {
+ if let hir::ExprKind::MethodCall(_, expr_args, _) = &expr.kind;
+ if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
+ if match_def_path(cx, expr_def_id, &paths::ITER_COUNT);
+
+ if let [bytes_expr] = &**expr_args;
+ if let hir::ExprKind::MethodCall(_, bytes_args, _) = &bytes_expr.kind;
+ if let Some(bytes_def_id) = cx.typeck_results().type_dependent_def_id(bytes_expr.hir_id);
+ if match_def_path(cx, bytes_def_id, &paths::STR_BYTES);
+
+ if let [str_expr] = &**bytes_args;
+ let ty = cx.typeck_results().expr_ty(str_expr).peel_refs();
+
+ if is_type_diagnostic_item(cx, ty, sym::String) || ty.kind() == &ty::Str;
+ then {
+ let mut applicability = Applicability::MachineApplicable;
+ span_lint_and_sugg(
+ cx,
+ BYTES_COUNT_TO_LEN,
+ expr.span,
+ "using long and hard to read `.bytes().count()`",
+ "consider calling `.len()` instead",
+ format!("{}.len()", snippet_with_applicability(cx, str_expr.span, "..", &mut applicability)),
+ applicability
+ );
+ }
+ };
+ }
+}
if let ExprKind::Lit(Spanned { node: LitKind::Str(ext_literal, ..), ..}) = extension.kind;
if (2..=6).contains(&ext_literal.as_str().len());
if ext_literal.as_str().starts_with('.');
- if ext_literal.as_str().chars().skip(1).all(|c| c.is_uppercase() || c.is_digit(10))
- || ext_literal.as_str().chars().skip(1).all(|c| c.is_lowercase() || c.is_digit(10));
+ if ext_literal.as_str().chars().skip(1).all(|c| c.is_uppercase() || c.is_ascii_digit())
+ || ext_literal.as_str().chars().skip(1).all(|c| c.is_lowercase() || c.is_ascii_digit());
then {
let mut ty = ctx.typeck_results().expr_ty(obj);
ty = match ty.kind() {
ExprKind::Block(block, _) => block.expr.map_or(nbits, |e| apply_reductions(cx, nbits, e, signed)),
ExprKind::Binary(op, left, right) => match op.node {
BinOpKind::Div => {
- apply_reductions(cx, nbits, left, signed)
- - (if signed {
- 0 // let's be conservative here
- } else {
- // by dividing by 1, we remove 0 bits, etc.
- get_constant_bits(cx, right).map_or(0, |b| b.saturating_sub(1))
- })
+ apply_reductions(cx, nbits, left, signed).saturating_sub(if signed {
+ // let's be conservative here
+ 0
+ } else {
+ // by dividing by 1, we remove 0 bits, etc.
+ get_constant_bits(cx, right).map_or(0, |b| b.saturating_sub(1))
+ })
},
BinOpKind::Rem | BinOpKind::BitAnd => get_constant_bits(cx, right)
.unwrap_or(u64::max_value())
.min(apply_reductions(cx, nbits, left, signed)),
- BinOpKind::Shr => {
- apply_reductions(cx, nbits, left, signed)
- - constant_int(cx, right).map_or(0, |s| u64::try_from(s).expect("shift too high"))
- },
+ BinOpKind::Shr => apply_reductions(cx, nbits, left, signed)
+ .saturating_sub(constant_int(cx, right).map_or(0, |s| u64::try_from(s).expect("shift too high"))),
_ => nbits,
},
ExprKind::MethodCall(method, [left, right], _) => {
-use clippy_utils::{diagnostics::span_lint_and_then, meets_msrv, msrvs, source::snippet_opt};
+use clippy_utils::{diagnostics::span_lint_and_then, meets_msrv, msrvs, source};
use if_chain::if_chain;
use rustc_ast::Mutability;
use rustc_hir::{Expr, ExprKind, Node};
use super::CAST_SLICE_DIFFERENT_SIZES;
-fn is_child_of_cast(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
- let map = cx.tcx.hir();
- if_chain! {
- if let Some(parent_id) = map.find_parent_node(expr.hir_id);
- if let Some(parent) = map.find(parent_id);
- then {
- let expr = match parent {
- Node::Block(block) => {
- if let Some(parent_expr) = block.expr {
- parent_expr
- } else {
- return false;
- }
- },
- Node::Expr(expr) => expr,
- _ => return false,
- };
-
- matches!(expr.kind, ExprKind::Cast(..))
- } else {
- false
- }
- }
-}
-
-pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Option<RustcVersion>) {
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: &Option<RustcVersion>) {
// suggestion is invalid if `ptr::slice_from_raw_parts` does not exist
if !meets_msrv(msrv.as_ref(), &msrvs::PTR_SLICE_RAW_PARTS) {
return;
return;
}
- if let Some((from_slice_ty, to_slice_ty)) = expr_cast_chain_tys(cx, expr) {
- if let (Ok(from_layout), Ok(to_layout)) = (cx.layout_of(from_slice_ty.ty), cx.layout_of(to_slice_ty.ty)) {
+ if let Some(CastChainInfo {
+ left_cast,
+ start_ty,
+ end_ty,
+ }) = expr_cast_chain_tys(cx, expr)
+ {
+ if let (Ok(from_layout), Ok(to_layout)) = (cx.layout_of(start_ty.ty), cx.layout_of(end_ty.ty)) {
let from_size = from_layout.size.bytes();
let to_size = to_layout.size.bytes();
if from_size != to_size && from_size != 0 && to_size != 0 {
expr.span,
&format!(
"casting between raw pointers to `[{}]` (element size {}) and `[{}]` (element size {}) does not adjust the count",
- from_slice_ty, from_size, to_slice_ty, to_size,
+ start_ty.ty, from_size, end_ty.ty, to_size,
),
|diag| {
- let cast_expr = match expr.kind {
- ExprKind::Cast(cast_expr, ..) => cast_expr,
- _ => unreachable!("expr should be a cast as checked by expr_cast_chain_tys"),
- };
- let ptr_snippet = snippet_opt(cx, cast_expr.span).unwrap();
+ let ptr_snippet = source::snippet(cx, left_cast.span, "..");
- let (mutbl_fn_str, mutbl_ptr_str) = match to_slice_ty.mutbl {
+ let (mutbl_fn_str, mutbl_ptr_str) = match end_ty.mutbl {
Mutability::Mut => ("_mut", "mut"),
Mutability::Not => ("", "const"),
};
let sugg = format!(
- "core::ptr::slice_from_raw_parts{mutbl_fn_str}({ptr_snippet} as *{mutbl_ptr_str} {to_slice_ty}, ..)"
+ "core::ptr::slice_from_raw_parts{mutbl_fn_str}({ptr_snippet} as *{mutbl_ptr_str} {}, ..)",
+ // get just the ty from the TypeAndMut so that the printed type isn't something like `mut
+ // T`, extract just the `T`
+ end_ty.ty
);
diag.span_suggestion(
}
}
+fn is_child_of_cast(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+ let map = cx.tcx.hir();
+ if_chain! {
+ if let Some(parent_id) = map.find_parent_node(expr.hir_id);
+ if let Some(parent) = map.find(parent_id);
+ then {
+ let expr = match parent {
+ Node::Block(block) => {
+ if let Some(parent_expr) = block.expr {
+ parent_expr
+ } else {
+ return false;
+ }
+ },
+ Node::Expr(expr) => expr,
+ _ => return false,
+ };
+
+ matches!(expr.kind, ExprKind::Cast(..))
+ } else {
+ false
+ }
+ }
+}
+
/// Returns the type T of the pointed to *const [T] or *mut [T] and the mutability of the slice if
/// the type is one of those slices
fn get_raw_slice_ty_mut(ty: Ty<'_>) -> Option<TypeAndMut<'_>> {
}
}
-/// Returns the pair (original ptr T, final ptr U) if the expression is composed of casts
+struct CastChainInfo<'tcx> {
+ /// The left most part of the cast chain, or in other words, the first cast in the chain
+ /// Used for diagnostics
+ left_cast: &'tcx Expr<'tcx>,
+ /// The starting type of the cast chain
+ start_ty: TypeAndMut<'tcx>,
+ /// The final type of the cast chain
+ end_ty: TypeAndMut<'tcx>,
+}
+
+/// Returns a `CastChainInfo` with the left-most cast in the chain and the original ptr T and final
+/// ptr U if the expression is composed of casts.
/// Returns None if the expr is not a Cast
-fn expr_cast_chain_tys<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option<(TypeAndMut<'tcx>, TypeAndMut<'tcx>)> {
+fn expr_cast_chain_tys<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<CastChainInfo<'tcx>> {
if let ExprKind::Cast(cast_expr, _cast_to_hir_ty) = expr.peel_blocks().kind {
let cast_to = cx.typeck_results().expr_ty(expr);
let to_slice_ty = get_raw_slice_ty_mut(cast_to)?;
- if let Some((inner_from_ty, _inner_to_ty)) = expr_cast_chain_tys(cx, cast_expr) {
- Some((inner_from_ty, to_slice_ty))
+
+ // If the expression that makes up the source of this cast is itself a cast, recursively
+ // call `expr_cast_chain_tys` and update the end type with the final tartet type.
+ // Otherwise, this cast is not immediately nested, just construct the info for this cast
+ if let Some(prev_info) = expr_cast_chain_tys(cx, cast_expr) {
+ Some(CastChainInfo {
+ end_ty: to_slice_ty,
+ ..prev_info
+ })
} else {
let cast_from = cx.typeck_results().expr_ty(cast_expr);
let from_slice_ty = get_raw_slice_ty_mut(cast_from)?;
- Some((from_slice_ty, to_slice_ty))
+ Some(CastChainInfo {
+ left_cast: cast_expr,
+ start_ty: from_slice_ty,
+ end_ty: to_slice_ty,
+ })
}
} else {
None
///
/// ### Why is this bad?
/// Casting a function pointer to an integer can have surprising results and can occur
- /// accidentally if parantheses are omitted from a function call. If you aren't doing anything
+ /// accidentally if parentheses are omitted from a function call. If you aren't doing anything
/// low-level with function pointers then you can opt-out of casting functions to integers in
/// order to avoid mistakes. Alternatively, you can use this lint to audit all uses of function
/// pointer casts in your code.
/// let y: u32 = x.abs() as u32;
/// ```
/// Use instead:
+ /// ```rust
/// let x: i32 = -42;
/// let y: u32 = x.unsigned_abs();
/// ```
//! This lint is **warn** by default
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
-use clippy_utils::source::{snippet_block, snippet_block_with_applicability};
+use clippy_utils::source::{snippet, snippet_block, snippet_block_with_applicability};
use clippy_utils::sugg::Sugg;
use if_chain::if_chain;
use rustc_ast::ast;
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::Span;
declare_clippy_lint! {
/// ### What it does
fn check_if(cx: &EarlyContext<'_>, expr: &ast::Expr) {
if let ast::ExprKind::If(check, then, else_) = &expr.kind {
if let Some(else_) = else_ {
- check_collapsible_maybe_if_let(cx, else_);
+ check_collapsible_maybe_if_let(cx, then.span, else_);
} else if let ast::ExprKind::Let(..) = check.kind {
// Prevent triggering on `if let a = b { if c { .. } }`.
} else {
trimmed_block_text.starts_with("//") || trimmed_block_text.starts_with("/*")
}
-fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, else_: &ast::Expr) {
+fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, then_span: Span, else_: &ast::Expr) {
if_chain! {
if let ast::ExprKind::Block(ref block, _) = else_.kind;
if !block_starts_with_comment(cx, block);
if !else_.span.from_expansion();
if let ast::ExprKind::If(..) = else_.kind;
then {
+ // Prevent "elseif"
+ // Check that the "else" is followed by whitespace
+ let up_to_else = then_span.between(block.span);
+ let requires_space = if let Some(c) = snippet(cx, up_to_else, "..").chars().last() { !c.is_whitespace() } else { false };
+
let mut applicability = Applicability::MachineApplicable;
span_lint_and_sugg(
cx,
block.span,
"this `else { if .. }` block can be collapsed",
"collapse nested if block",
- snippet_block_with_applicability(cx, else_.span, "..", Some(block.span), &mut applicability).into_owned(),
+ format!(
+ "{}{}",
+ if requires_space { " " } else { "" },
+ snippet_block_with_applicability(cx, else_.span, "..", Some(block.span), &mut applicability)
+ ),
applicability,
);
}
lint_for_missing_headers(cx, item.def_id, item.span, sig, headers, Some(body_id), fpu.panic_span);
}
},
- hir::ItemKind::Impl(ref impl_) => {
+ hir::ItemKind::Impl(impl_) => {
self.in_trait_impl = impl_.of_trait.is_some();
},
hir::ItemKind::Trait(_, unsafety, ..) => {
let filename = FileName::anon_source_code(&code);
let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
- let fallback_bundle = rustc_errors::fallback_fluent_bundle(
- rustc_errors::DEFAULT_LOCALE_RESOURCES,
- false
- );
+ let fallback_bundle =
+ rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false);
let emitter = EmitterWriter::new(
Box::new(io::sink()),
None,
/// letters (`Clippy` is ok) and one lower-case letter (`NASA` is ok).
/// Plurals are also excluded (`IDs` is ok).
fn is_camel_case(s: &str) -> bool {
- if s.starts_with(|c: char| c.is_digit(10)) {
+ if s.starts_with(|c: char| c.is_ascii_digit()) {
return false;
}
const FORGET_COPY_SUMMARY: &str = "calls to `std::mem::forget` with a value that implements `Copy`. \
Forgetting a copy leaves the original intact";
const DROP_NON_DROP_SUMMARY: &str = "call to `std::mem::drop` with a value that does not implement `Drop`. \
- Dropping such a type only extends it's contained lifetimes";
+ Dropping such a type only extends its contained lifetimes";
const FORGET_NON_DROP_SUMMARY: &str = "call to `std::mem::forget` with a value that does not implement `Drop`. \
Forgetting such a type is the same as dropping it";
--- /dev/null
+use clippy_utils::{diagnostics::span_lint_and_sugg, peel_blocks};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::{Body, ExprKind, Impl, ImplItemKind, Item, ItemKind, Node};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for empty `Drop` implementations.
+ ///
+ /// ### Why is this bad?
+ /// Empty `Drop` implementations have no effect when dropping an instance of the type. They are
+ /// most likely useless. However, an empty `Drop` implementation prevents a type from being
+ /// destructured, which might be the intention behind adding the implementation as a marker.
+ ///
+ /// ### Example
+ /// ```rust
+ /// struct S;
+ ///
+ /// impl Drop for S {
+ /// fn drop(&mut self) {}
+ /// }
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// struct S;
+ /// ```
+ #[clippy::version = "1.61.0"]
+ pub EMPTY_DROP,
+ restriction,
+ "empty `Drop` implementations"
+}
+declare_lint_pass!(EmptyDrop => [EMPTY_DROP]);
+
+impl LateLintPass<'_> for EmptyDrop {
+ fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
+ if_chain! {
+ if let ItemKind::Impl(Impl {
+ of_trait: Some(ref trait_ref),
+ items: [child],
+ ..
+ }) = item.kind;
+ if trait_ref.trait_def_id() == cx.tcx.lang_items().drop_trait();
+ if let impl_item_hir = child.id.hir_id();
+ if let Some(Node::ImplItem(impl_item)) = cx.tcx.hir().find(impl_item_hir);
+ if let ImplItemKind::Fn(_, b) = &impl_item.kind;
+ if let Body { value: func_expr, .. } = cx.tcx.hir().body(*b);
+ let func_expr = peel_blocks(func_expr);
+ if let ExprKind::Block(block, _) = func_expr.kind;
+ if block.stmts.is_empty() && block.expr.is_none();
+ then {
+ span_lint_and_sugg(
+ cx,
+ EMPTY_DROP,
+ item.span,
+ "empty drop implementation",
+ "try removing this impl",
+ String::new(),
+ Applicability::MaybeIncorrect
+ );
+ }
+ }
+ }
+}
}
// there might still be field declarations hidden from the AST
- // (conditionaly compiled code using #[cfg(..)])
+ // (conditionally compiled code using #[cfg(..)])
let Some(braces_span_str) = snippet_opt(cx, braces_span) else {
return false;
use clippy_utils::source::snippet_opt;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::usage::local_used_after_expr;
-use clippy_utils::{get_enclosing_loop_or_closure, higher, path_to_local, path_to_local_id};
+use clippy_utils::{higher, is_adjusted, path_to_local, path_to_local_id};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::def_id::DefId;
let closure_ty = cx.typeck_results().expr_ty(expr);
if_chain!(
+ if !is_adjusted(cx, &body.value);
if let ExprKind::Call(callee, args) = body.value.kind;
if let ExprKind::Path(_) = callee.kind;
if check_inputs(cx, body.params, args);
if_chain! {
if let ty::Closure(_, substs) = callee_ty.peel_refs().kind();
if substs.as_closure().kind() == ClosureKind::FnMut;
- if get_enclosing_loop_or_closure(cx.tcx, expr).is_some()
- || path_to_local(callee).map_or(false, |l| local_used_after_expr(cx, l, callee));
+ if path_to_local(callee).map_or(false, |l| local_used_after_expr(cx, l, expr));
then {
// Mutable closure is used after current expr; we cannot consume it.
);
if_chain!(
+ if !is_adjusted(cx, &body.value);
if let ExprKind::MethodCall(path, args, _) = body.value.kind;
if check_inputs(cx, body.params, args);
let method_def_id = cx.typeck_results().type_dependent_def_id(body.value.hir_id).unwrap();
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{match_def_path, paths, peel_hir_expr_refs};
+use rustc_hir::{BinOpKind, Expr, ExprKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::sym;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Detects cases where the result of a `format!` call is
+ /// appended to an existing `String`.
+ ///
+ /// ### Why is this bad?
+ /// Introduces an extra, avoidable heap allocation.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let mut s = String::new();
+ /// s += &format!("0x{:X}", 1024);
+ /// s.push_str(&format!("0x{:X}", 1024));
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// use std::fmt::Write as _; // import without risk of name clashing
+ ///
+ /// let mut s = String::new();
+ /// let _ = write!(s, "0x{:X}", 1024);
+ /// ```
+ #[clippy::version = "1.61.0"]
+ pub FORMAT_PUSH_STRING,
+ perf,
+ "`format!(..)` appended to existing `String`"
+}
+declare_lint_pass!(FormatPushString => [FORMAT_PUSH_STRING]);
+
+fn is_string(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
+ is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(e).peel_refs(), sym::String)
+}
+fn is_format(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
+ if let Some(macro_def_id) = e.span.ctxt().outer_expn_data().macro_def_id {
+ cx.tcx.get_diagnostic_name(macro_def_id) == Some(sym::format_macro)
+ } else {
+ false
+ }
+}
+
+impl<'tcx> LateLintPass<'tcx> for FormatPushString {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+ let arg = match expr.kind {
+ ExprKind::MethodCall(_, [_, arg], _) => {
+ if let Some(fn_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) &&
+ match_def_path(cx, fn_def_id, &paths::PUSH_STR) {
+ arg
+ } else {
+ return;
+ }
+ }
+ ExprKind::AssignOp(op, left, arg)
+ if op.node == BinOpKind::Add && is_string(cx, left) => {
+ arg
+ },
+ _ => return,
+ };
+ let (arg, _) = peel_hir_expr_refs(arg);
+ if is_format(cx, arg) {
+ span_lint_and_help(
+ cx,
+ FORMAT_PUSH_STRING,
+ expr.span,
+ "`format!(..)` appended to existing `String`",
+ None,
+ "consider using `write!` to avoid the extra allocation",
+ );
+ }
+ }
+}
pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
let attrs = cx.tcx.hir().attrs(item.hir_id());
let attr = must_use_attr(attrs);
- if let hir::ItemKind::Fn(ref sig, ref _generics, ref body_id) = item.kind {
+ if let hir::ItemKind::Fn(ref sig, _generics, ref body_id) = item.kind {
let is_public = cx.access_levels.is_exported(item.def_id);
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
if let Some(attr) = attr {
fn_header_span,
"this unit-returning function has a `#[must_use]` attribute",
|diag| {
- diag.span_suggestion(
- attr.span,
- "remove the attribute",
- "",
- Applicability::MachineApplicable,
- );
+ diag.span_suggestion(attr.span, "remove the attribute", "", Applicability::MachineApplicable);
},
);
} else if attr.value_str().is_none() && is_must_use_ty(cx, return_ty(cx, item_id)) {
use super::RESULT_UNIT_ERR;
pub(super) fn check_item(cx: &LateContext<'_>, item: &hir::Item<'_>) {
- if let hir::ItemKind::Fn(ref sig, ref _generics, _) = item.kind {
+ if let hir::ItemKind::Fn(ref sig, _generics, _) = item.kind {
let is_public = cx.access_levels.is_exported(item.def_id);
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
if is_public {
+use clippy_utils::get_parent_expr;
use clippy_utils::source::snippet;
use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
/// # let x = 1;
/// x / 1 + 0 * 1 - 0 | 0;
/// ```
+ ///
+ /// ### Known problems
+ /// False negatives: `f(0 + if b { 1 } else { 2 } + 3);` is reducible to
+ /// `f(if b { 1 } else { 2 } + 3);`. But the lint doesn't trigger for the code.
+ /// See [#8724](https://github.com/rust-lang/rust-clippy/issues/8724)
#[clippy::version = "pre 1.29.0"]
pub IDENTITY_OP,
complexity,
declare_lint_pass!(IdentityOp => [IDENTITY_OP]);
impl<'tcx> LateLintPass<'tcx> for IdentityOp {
- fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
- if e.span.from_expansion() {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+ if expr.span.from_expansion() {
return;
}
- if let ExprKind::Binary(cmp, left, right) = e.kind {
- if is_allowed(cx, cmp, left, right) {
- return;
- }
- match cmp.node {
- BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => {
- check(cx, left, 0, e.span, right.span);
- check(cx, right, 0, e.span, left.span);
- },
- BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => check(cx, right, 0, e.span, left.span),
- BinOpKind::Mul => {
- check(cx, left, 1, e.span, right.span);
- check(cx, right, 1, e.span, left.span);
- },
- BinOpKind::Div => check(cx, right, 1, e.span, left.span),
- BinOpKind::BitAnd => {
- check(cx, left, -1, e.span, right.span);
- check(cx, right, -1, e.span, left.span);
- },
- BinOpKind::Rem => check_remainder(cx, left, right, e.span, left.span),
- _ => (),
+ if let ExprKind::Binary(cmp, left, right) = &expr.kind {
+ if !is_allowed(cx, *cmp, left, right) {
+ match cmp.node {
+ BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => {
+ if reducible_to_right(cx, expr, right) {
+ check(cx, left, 0, expr.span, right.span);
+ }
+ check(cx, right, 0, expr.span, left.span);
+ },
+ BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => {
+ check(cx, right, 0, expr.span, left.span);
+ },
+ BinOpKind::Mul => {
+ if reducible_to_right(cx, expr, right) {
+ check(cx, left, 1, expr.span, right.span);
+ }
+ check(cx, right, 1, expr.span, left.span);
+ },
+ BinOpKind::Div => check(cx, right, 1, expr.span, left.span),
+ BinOpKind::BitAnd => {
+ if reducible_to_right(cx, expr, right) {
+ check(cx, left, -1, expr.span, right.span);
+ }
+ check(cx, right, -1, expr.span, left.span);
+ },
+ BinOpKind::Rem => {
+ // Don't call reducible_to_right because N % N is always reducible to 1
+ check_remainder(cx, left, right, expr.span, left.span);
+ },
+ _ => (),
+ }
}
}
}
}
+/// Checks if `left op ..right` can be actually reduced to `right`
+/// e.g. `0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }`
+/// cannot be reduced to `if b { 1 } else { 2 } + if b { 3 } else { 4 }`
+/// See #8724
+fn reducible_to_right(cx: &LateContext<'_>, binary: &Expr<'_>, right: &Expr<'_>) -> bool {
+ if let ExprKind::If(..) | ExprKind::Match(..) | ExprKind::Block(..) | ExprKind::Loop(..) = right.kind {
+ is_toplevel_binary(cx, binary)
+ } else {
+ true
+ }
+}
+
+fn is_toplevel_binary(cx: &LateContext<'_>, must_be_binary: &Expr<'_>) -> bool {
+ if let Some(parent) = get_parent_expr(cx, must_be_binary) && let ExprKind::Binary(..) = &parent.kind {
+ false
+ } else {
+ true
+ }
+}
+
fn is_allowed(cx: &LateContext<'_>, cmp: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> bool {
// This lint applies to integers
!cx.typeck_results().expr_ty(left).peel_refs().is_integral()
}
match item.kind {
- ItemKind::Impl(ref impl_) => {
+ ItemKind::Impl(impl_) => {
let mut vis = ImplicitHasherTypeVisitor::new(cx);
vis.visit_ty(impl_.self_ty);
);
}
},
- ItemKind::Fn(ref sig, ref generics, body_id) => {
+ ItemKind::Fn(ref sig, generics, body_id) => {
let body = cx.tcx.hir().body(body_id);
for ty in sig.decl.inputs {
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::symbol::Symbol;
+use std::fmt::Write as _;
declare_clippy_lint! {
/// ### What it does
let mut fields_snippet = String::new();
let (last_ident, idents) = ordered_fields.split_last().unwrap();
for ident in idents {
- fields_snippet.push_str(&format!("{}, ", ident));
+ let _ = write!(fields_snippet, "{}, ", ident);
}
fields_snippet.push_str(&last_ident.to_string());
let bound_ty = cx.typeck_results().node_type(pat.hir_id);
if let ty::Slice(inner_ty) | ty::Array(inner_ty, _) = bound_ty.peel_refs().kind() {
// The values need to use the `ref` keyword if they can't be copied.
- // This will need to be adjusted if the lint want to support multable access in the future
+ // This will need to be adjusted if the lint want to support mutable access in the future
let src_is_ref = bound_ty.is_ref() && binding != hir::BindingAnnotation::Ref;
let needs_ref = !(src_is_ref || is_copy(cx, *inner_ty));
fn get_impl_span(cx: &LateContext<'_>, id: LocalDefId) -> Option<Span> {
let id = cx.tcx.hir().local_def_id_to_hir_id(id);
if let Node::Item(&Item {
- kind: ItemKind::Impl(ref impl_item),
+ kind: ItemKind::Impl(impl_item),
span,
..
}) = cx.tcx.hir().get(id)
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
use rustc_errors::Applicability;
+use rustc_hir::def::{DefKind, Res};
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
&& fields
.iter()
.all(|f| f.ident.as_str().as_bytes().iter().all(u8::is_ascii_digit))
+ && !matches!(cx.qpath_res(path, e.hir_id), Res::Def(DefKind::TyAlias, ..))
{
let expr_spans = fields
.iter()
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_note;
+use clippy_utils::is_lint_allowed;
+use clippy_utils::macros::root_macro_call_first_node;
+use rustc_ast::LitKind;
+use rustc_hir::Expr;
+use rustc_hir::ExprKind;
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::sym;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for the inclusion of large files via `include_bytes!()`
+ /// and `include_str!()`
+ ///
+ /// ### Why is this bad?
+ /// Including large files can increase the size of the binary
+ ///
+ /// ### Example
+ /// ```rust,ignore
+ /// let included_str = include_str!("very_large_file.txt");
+ /// let included_bytes = include_bytes!("very_large_file.txt");
+ /// ```
+ ///
+ /// Instead, you can load the file at runtime:
+ /// ```rust,ignore
+ /// use std::fs;
+ ///
+ /// let string = fs::read_to_string("very_large_file.txt")?;
+ /// let bytes = fs::read("very_large_file.txt")?;
+ /// ```
+ #[clippy::version = "1.62.0"]
+ pub LARGE_INCLUDE_FILE,
+ restriction,
+ "including a large file"
+}
+
+pub struct LargeIncludeFile {
+ max_file_size: u64,
+}
+
+impl LargeIncludeFile {
+ #[must_use]
+ pub fn new(max_file_size: u64) -> Self {
+ Self { max_file_size }
+ }
+}
+
+impl_lint_pass!(LargeIncludeFile => [LARGE_INCLUDE_FILE]);
+
+impl LateLintPass<'_> for LargeIncludeFile {
+ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
+ if_chain! {
+ if let Some(macro_call) = root_macro_call_first_node(cx, expr);
+ if !is_lint_allowed(cx, LARGE_INCLUDE_FILE, expr.hir_id);
+ if cx.tcx.is_diagnostic_item(sym::include_bytes_macro, macro_call.def_id)
+ || cx.tcx.is_diagnostic_item(sym::include_str_macro, macro_call.def_id);
+ if let ExprKind::Lit(lit) = &expr.kind;
+ then {
+ let len = match &lit.node {
+ // include_bytes
+ LitKind::ByteStr(bstr) => bstr.len(),
+ // include_str
+ LitKind::Str(sym, _) => sym.as_str().len(),
+ _ => return,
+ };
+
+ if len as u64 <= self.max_file_size {
+ return;
+ }
+
+ span_lint_and_note(
+ cx,
+ LARGE_INCLUDE_FILE,
+ expr.span,
+ "attempted to include a large file",
+ None,
+ &format!(
+ "the configuration allows a maximum size of {} bytes",
+ self.max_file_size
+ ),
+ );
+ }
+ }
+ }
+}
LintId::of(attrs::DEPRECATED_SEMVER),
LintId::of(attrs::MISMATCHED_TARGET_OS),
LintId::of(attrs::USELESS_ATTRIBUTE),
+ LintId::of(await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE),
LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK),
LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF),
LintId::of(bit_mask::BAD_BIT_MASK),
LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON),
LintId::of(booleans::LOGIC_BUG),
LintId::of(booleans::NONMINIMAL_BOOL),
+ LintId::of(bytes_count_to_len::BYTES_COUNT_TO_LEN),
LintId::of(casts::CAST_ABS_TO_UNSIGNED),
LintId::of(casts::CAST_ENUM_CONSTRUCTOR),
LintId::of(casts::CAST_ENUM_TRUNCATION),
LintId::of(format_args::TO_STRING_IN_FORMAT_ARGS),
LintId::of(format_impl::PRINT_IN_FORMAT_IMPL),
LintId::of(format_impl::RECURSIVE_FORMAT_IMPL),
+ LintId::of(format_push_string::FORMAT_PUSH_STRING),
LintId::of(formatting::POSSIBLE_MISSING_COMMA),
LintId::of(formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING),
LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING),
LintId::of(methods::FLAT_MAP_IDENTITY),
LintId::of(methods::INSPECT_FOR_EACH),
LintId::of(methods::INTO_ITER_ON_REF),
+ LintId::of(methods::IS_DIGIT_ASCII_RADIX),
LintId::of(methods::ITERATOR_STEP_BY_ZERO),
LintId::of(methods::ITER_CLONED_COLLECT),
LintId::of(methods::ITER_COUNT),
LintId::of(methods::MAP_FLATTEN),
LintId::of(methods::MAP_IDENTITY),
LintId::of(methods::NEEDLESS_OPTION_AS_DEREF),
+ LintId::of(methods::NEEDLESS_OPTION_TAKE),
LintId::of(methods::NEEDLESS_SPLITN),
LintId::of(methods::NEW_RET_NO_SELF),
LintId::of(methods::OK_EXPECT),
LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
LintId::of(octal_escapes::OCTAL_ESCAPES),
- LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION),
LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP),
LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL),
LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT),
LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
- LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE),
LintId::of(strings::STRING_FROM_UTF8_AS_BYTES),
+ LintId::of(strings::TRIM_SPLIT_WHITESPACE),
LintId::of(strlen_on_c_strings::STRLEN_ON_C_STRINGS),
LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
LintId::of(uninit_vec::UNINIT_VEC),
LintId::of(unit_hash::UNIT_HASH),
LintId::of(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD),
+ LintId::of(unit_types::LET_UNIT_VALUE),
LintId::of(unit_types::UNIT_ARG),
LintId::of(unit_types::UNIT_CMP),
LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS),
LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS),
+ LintId::of(unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS),
LintId::of(unnecessary_sort_by::UNNECESSARY_SORT_BY),
LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
LintId::of(unused_io_amount::UNUSED_IO_AMOUNT),
store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec![
LintId::of(attrs::DEPRECATED_CFG_ATTR),
LintId::of(booleans::NONMINIMAL_BOOL),
+ LintId::of(bytes_count_to_len::BYTES_COUNT_TO_LEN),
LintId::of(casts::CHAR_LIT_AS_U8),
LintId::of(casts::UNNECESSARY_CAST),
LintId::of(derivable_impls::DERIVABLE_IMPLS),
LintId::of(methods::MAP_FLATTEN),
LintId::of(methods::MAP_IDENTITY),
LintId::of(methods::NEEDLESS_OPTION_AS_DEREF),
+ LintId::of(methods::NEEDLESS_OPTION_TAKE),
LintId::of(methods::NEEDLESS_SPLITN),
LintId::of(methods::OPTION_AS_REF_DEREF),
LintId::of(methods::OPTION_FILTER_MAP),
LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD),
LintId::of(no_effect::NO_EFFECT),
LintId::of(no_effect::UNNECESSARY_OPERATION),
- LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION),
LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL),
LintId::of(partialeq_ne_impl::PARTIALEQ_NE_IMPL),
LintId::of(precedence::PRECEDENCE),
attrs::INLINE_ALWAYS,
attrs::MISMATCHED_TARGET_OS,
attrs::USELESS_ATTRIBUTE,
+ await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE,
await_holding_invalid::AWAIT_HOLDING_LOCK,
await_holding_invalid::AWAIT_HOLDING_REFCELL_REF,
bit_mask::BAD_BIT_MASK,
booleans::NONMINIMAL_BOOL,
borrow_as_ptr::BORROW_AS_PTR,
bytecount::NAIVE_BYTECOUNT,
+ bytes_count_to_len::BYTES_COUNT_TO_LEN,
cargo::CARGO_COMMON_METADATA,
cargo::MULTIPLE_CRATE_VERSIONS,
cargo::NEGATIVE_FEATURE_NAMES,
drop_forget_ref::UNDROPPED_MANUALLY_DROPS,
duration_subsec::DURATION_SUBSEC,
else_if_without_else::ELSE_IF_WITHOUT_ELSE,
+ empty_drop::EMPTY_DROP,
empty_enum::EMPTY_ENUM,
empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS,
entry::MAP_ENTRY,
format_args::TO_STRING_IN_FORMAT_ARGS,
format_impl::PRINT_IN_FORMAT_IMPL,
format_impl::RECURSIVE_FORMAT_IMPL,
+ format_push_string::FORMAT_PUSH_STRING,
formatting::POSSIBLE_MISSING_COMMA,
formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING,
formatting::SUSPICIOUS_ELSE_FORMATTING,
iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR,
large_const_arrays::LARGE_CONST_ARRAYS,
large_enum_variant::LARGE_ENUM_VARIANT,
+ large_include_file::LARGE_INCLUDE_FILE,
large_stack_arrays::LARGE_STACK_ARRAYS,
len_zero::COMPARISON_TO_EMPTY,
len_zero::LEN_WITHOUT_IS_EMPTY,
methods::INEFFICIENT_TO_STRING,
methods::INSPECT_FOR_EACH,
methods::INTO_ITER_ON_REF,
+ methods::IS_DIGIT_ASCII_RADIX,
methods::ITERATOR_STEP_BY_ZERO,
methods::ITER_CLONED_COLLECT,
methods::ITER_COUNT,
methods::MAP_IDENTITY,
methods::MAP_UNWRAP_OR,
methods::NEEDLESS_OPTION_AS_DEREF,
+ methods::NEEDLESS_OPTION_TAKE,
methods::NEEDLESS_SPLITN,
methods::NEW_RET_NO_SELF,
methods::OK_EXPECT,
ptr::PTR_ARG,
ptr_eq::PTR_EQ,
ptr_offset_with_cast::PTR_OFFSET_WITH_CAST,
+ pub_use::PUB_USE,
question_mark::QUESTION_MARK,
ranges::MANUAL_RANGE_CONTAINS,
ranges::RANGE_MINUS_ONE,
strings::STRING_SLICE,
strings::STRING_TO_STRING,
strings::STR_TO_STRING,
+ strings::TRIM_SPLIT_WHITESPACE,
strlen_on_c_strings::STRLEN_ON_C_STRINGS,
suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS,
suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL,
unit_types::UNIT_CMP,
unnamed_address::FN_ADDRESS_COMPARISONS,
unnamed_address::VTABLE_ADDRESS_COMPARISONS,
+ unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS,
unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS,
unnecessary_sort_by::UNNECESSARY_SORT_BY,
unnecessary_wraps::UNNECESSARY_WRAPS,
LintId::of(mutex_atomic::MUTEX_INTEGER),
LintId::of(non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY),
LintId::of(nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES),
+ LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION),
LintId::of(option_if_let_else::OPTION_IF_LET_ELSE),
LintId::of(path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE),
LintId::of(redundant_pub_crate::REDUNDANT_PUB_CRATE),
LintId::of(strings::STRING_LIT_AS_BYTES),
LintId::of(suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS),
LintId::of(trailing_empty_array::TRAILING_EMPTY_ARRAY),
+ LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS),
+ LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS),
LintId::of(transmute::TRANSMUTE_UNDEFINED_REPR),
LintId::of(transmute::USELESS_TRANSMUTE),
LintId::of(use_self::USE_SELF),
LintId::of(ref_option_ref::REF_OPTION_REF),
LintId::of(return_self_not_must_use::RETURN_SELF_NOT_MUST_USE),
LintId::of(semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED),
+ LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE),
LintId::of(strings::STRING_ADD_ASSIGN),
- LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS),
- LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS),
LintId::of(transmute::TRANSMUTE_PTR_TO_PTR),
LintId::of(types::LINKEDLIST),
LintId::of(types::OPTION_OPTION),
LintId::of(unicode::UNICODE_NOT_NFC),
- LintId::of(unit_types::LET_UNIT_VALUE),
LintId::of(unnecessary_wraps::UNNECESSARY_WRAPS),
LintId::of(unnested_or_patterns::UNNESTED_OR_PATTERNS),
LintId::of(unused_async::UNUSED_ASYNC),
LintId::of(escape::BOXED_LOCAL),
LintId::of(format_args::FORMAT_IN_FORMAT_ARGS),
LintId::of(format_args::TO_STRING_IN_FORMAT_ARGS),
+ LintId::of(format_push_string::FORMAT_PUSH_STRING),
LintId::of(large_const_arrays::LARGE_CONST_ARRAYS),
LintId::of(large_enum_variant::LARGE_ENUM_VARIANT),
LintId::of(loops::MANUAL_MEMCPY),
LintId::of(misc::CMP_OWNED),
LintId::of(redundant_clone::REDUNDANT_CLONE),
LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
- LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE),
LintId::of(types::BOX_COLLECTION),
LintId::of(types::REDUNDANT_ALLOCATION),
LintId::of(vec::USELESS_VEC),
LintId::of(default_union_representation::DEFAULT_UNION_REPRESENTATION),
LintId::of(disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS),
LintId::of(else_if_without_else::ELSE_IF_WITHOUT_ELSE),
+ LintId::of(empty_drop::EMPTY_DROP),
LintId::of(empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS),
LintId::of(exhaustive_items::EXHAUSTIVE_ENUMS),
LintId::of(exhaustive_items::EXHAUSTIVE_STRUCTS),
LintId::of(indexing_slicing::INDEXING_SLICING),
LintId::of(inherent_impl::MULTIPLE_INHERENT_IMPL),
LintId::of(integer_division::INTEGER_DIVISION),
+ LintId::of(large_include_file::LARGE_INCLUDE_FILE),
LintId::of(let_underscore::LET_UNDERSCORE_MUST_USE),
LintId::of(literal_representation::DECIMAL_LITERAL_REPRESENTATION),
LintId::of(map_err_ignore::MAP_ERR_IGNORE),
LintId::of(panic_unimplemented::UNIMPLEMENTED),
LintId::of(panic_unimplemented::UNREACHABLE),
LintId::of(pattern_type_mismatch::PATTERN_TYPE_MISMATCH),
+ LintId::of(pub_use::PUB_USE),
LintId::of(redundant_slicing::DEREF_BY_SLICING),
LintId::of(same_name_method::SAME_NAME_METHOD),
LintId::of(shadow::SHADOW_REUSE),
LintId::of(methods::CHARS_NEXT_CMP),
LintId::of(methods::ERR_EXPECT),
LintId::of(methods::INTO_ITER_ON_REF),
+ LintId::of(methods::IS_DIGIT_ASCII_RADIX),
LintId::of(methods::ITER_CLONED_COLLECT),
LintId::of(methods::ITER_NEXT_SLICE),
LintId::of(methods::ITER_NTH_ZERO),
LintId::of(returns::NEEDLESS_RETURN),
LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS),
LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
+ LintId::of(strings::TRIM_SPLIT_WHITESPACE),
LintId::of(tabs_in_doc_comments::TABS_IN_DOC_COMMENTS),
LintId::of(to_digit_is_some::TO_DIGIT_IS_SOME),
+ LintId::of(unit_types::LET_UNIT_VALUE),
+ LintId::of(unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS),
LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
LintId::of(unused_unit::UNUSED_UNIT),
LintId::of(upper_case_acronyms::UPPER_CASE_ACRONYMS),
store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec![
LintId::of(assign_ops::MISREFACTORED_ASSIGN_OP),
LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
+ LintId::of(await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE),
LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK),
LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF),
LintId::of(casts::CAST_ABS_TO_UNSIGNED),
#[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))]
mod utils;
+mod renamed_lints;
+
// begin lints modules, do not remove this comment, it’s used in `update_lints`
mod absurd_extreme_comparisons;
mod approx_const;
mod booleans;
mod borrow_as_ptr;
mod bytecount;
+mod bytes_count_to_len;
mod cargo;
mod case_sensitive_file_extension_comparisons;
mod casts;
mod drop_forget_ref;
mod duration_subsec;
mod else_if_without_else;
+mod empty_drop;
mod empty_enum;
mod empty_structs_with_brackets;
mod entry;
mod format;
mod format_args;
mod format_impl;
+mod format_push_string;
mod formatting;
mod from_over_into;
mod from_str_radix_10;
mod iter_not_returning_iterator;
mod large_const_arrays;
mod large_enum_variant;
+mod large_include_file;
mod large_stack_arrays;
mod len_zero;
mod let_if_seq;
mod ptr;
mod ptr_eq;
mod ptr_offset_with_cast;
+mod pub_use;
mod question_mark;
mod ranges;
mod redundant_clone;
mod unit_return_expecting_ord;
mod unit_types;
mod unnamed_address;
+mod unnecessary_owned_empty_strings;
mod unnecessary_self_imports;
mod unnecessary_sort_by;
mod unnecessary_wraps;
{
store.register_early_pass(|| Box::new(utils::internal_lints::ClippyLintsInternal));
store.register_early_pass(|| Box::new(utils::internal_lints::ProduceIce));
- store.register_late_pass(|| Box::new(utils::inspector::DeepCodeInspector));
store.register_late_pass(|| Box::new(utils::internal_lints::CollapsibleCalls));
store.register_late_pass(|| Box::new(utils::internal_lints::CompilerLintFunctions::new()));
store.register_late_pass(|| Box::new(utils::internal_lints::IfChainStyle));
store.register_late_pass(|| Box::new(utils::internal_lints::MsrvAttrImpl));
}
+ store.register_late_pass(|| Box::new(utils::dump_hir::DumpHir));
store.register_late_pass(|| Box::new(utils::author::Author));
- store.register_late_pass(|| Box::new(await_holding_invalid::AwaitHolding));
+ let await_holding_invalid_types = conf.await_holding_invalid_types.clone();
+ store.register_late_pass(move || {
+ Box::new(await_holding_invalid::AwaitHolding::new(
+ await_holding_invalid_types.clone(),
+ ))
+ });
store.register_late_pass(|| Box::new(serde_api::SerdeApi));
let vec_box_size_threshold = conf.vec_box_size_threshold;
let type_complexity_threshold = conf.type_complexity_threshold;
store.register_late_pass(move || Box::new(approx_const::ApproxConstant::new(msrv)));
store.register_late_pass(move || Box::new(methods::Methods::new(avoid_breaking_exported_api, msrv)));
store.register_late_pass(move || Box::new(matches::Matches::new(msrv)));
- store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustive::new(msrv)));
+ store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveStruct::new(msrv)));
+ store.register_late_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveEnum::new(msrv)));
store.register_late_pass(move || Box::new(manual_strip::ManualStrip::new(msrv)));
store.register_early_pass(move || Box::new(redundant_static_lifetimes::RedundantStaticLifetimes::new(msrv)));
store.register_early_pass(move || Box::new(redundant_field_names::RedundantFieldNames::new(msrv)));
store.register_late_pass(move || Box::new(disallowed_methods::DisallowedMethods::new(disallowed_methods.clone())));
store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86AttSyntax));
store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86IntelSyntax));
+ store.register_late_pass(|| Box::new(empty_drop::EmptyDrop));
store.register_late_pass(|| Box::new(strings::StrToString));
store.register_late_pass(|| Box::new(strings::StringToString));
store.register_late_pass(|| Box::new(zero_sized_map_values::ZeroSizedMapValues));
});
store.register_early_pass(|| Box::new(crate_in_macro_def::CrateInMacroDef));
store.register_early_pass(|| Box::new(empty_structs_with_brackets::EmptyStructsWithBrackets));
+ store.register_late_pass(|| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings));
+ store.register_early_pass(|| Box::new(pub_use::PubUse));
+ store.register_late_pass(|| Box::new(format_push_string::FormatPushString));
+ store.register_late_pass(|| Box::new(bytes_count_to_len::BytesCountToLen));
+ let max_include_file_size = conf.max_include_file_size;
+ store.register_late_pass(move || Box::new(large_include_file::LargeIncludeFile::new(max_include_file_size)));
+ store.register_late_pass(|| Box::new(strings::TrimSplitWhitespace));
// add lints here, do not remove this comment, it's used in `new_lint`
}
///
/// Used in `./src/driver.rs`.
pub fn register_renamed(ls: &mut rustc_lint::LintStore) {
- // NOTE: when renaming a lint, add a corresponding test to tests/ui/rename.rs
- ls.register_renamed("clippy::stutter", "clippy::module_name_repetitions");
- ls.register_renamed("clippy::new_without_default_derive", "clippy::new_without_default");
- ls.register_renamed("clippy::cyclomatic_complexity", "clippy::cognitive_complexity");
- ls.register_renamed("clippy::const_static_lifetime", "clippy::redundant_static_lifetimes");
- ls.register_renamed("clippy::option_and_then_some", "clippy::bind_instead_of_map");
- ls.register_renamed("clippy::box_vec", "clippy::box_collection");
- ls.register_renamed("clippy::block_in_if_condition_expr", "clippy::blocks_in_if_conditions");
- ls.register_renamed("clippy::block_in_if_condition_stmt", "clippy::blocks_in_if_conditions");
- ls.register_renamed("clippy::option_map_unwrap_or", "clippy::map_unwrap_or");
- ls.register_renamed("clippy::option_map_unwrap_or_else", "clippy::map_unwrap_or");
- ls.register_renamed("clippy::result_map_unwrap_or_else", "clippy::map_unwrap_or");
- ls.register_renamed("clippy::option_unwrap_used", "clippy::unwrap_used");
- ls.register_renamed("clippy::result_unwrap_used", "clippy::unwrap_used");
- ls.register_renamed("clippy::option_expect_used", "clippy::expect_used");
- ls.register_renamed("clippy::result_expect_used", "clippy::expect_used");
- ls.register_renamed("clippy::for_loop_over_option", "clippy::for_loops_over_fallibles");
- ls.register_renamed("clippy::for_loop_over_result", "clippy::for_loops_over_fallibles");
- ls.register_renamed("clippy::identity_conversion", "clippy::useless_conversion");
- ls.register_renamed("clippy::zero_width_space", "clippy::invisible_characters");
- ls.register_renamed("clippy::single_char_push_str", "clippy::single_char_add_str");
- ls.register_renamed("clippy::if_let_some_result", "clippy::match_result_ok");
- ls.register_renamed("clippy::disallowed_type", "clippy::disallowed_types");
- ls.register_renamed("clippy::disallowed_method", "clippy::disallowed_methods");
- ls.register_renamed("clippy::ref_in_deref", "clippy::needless_borrow");
- ls.register_renamed("clippy::to_string_in_display", "clippy::recursive_format_impl");
-
- // uplifted lints
- ls.register_renamed("clippy::invalid_ref", "invalid_value");
- ls.register_renamed("clippy::into_iter_on_array", "array_into_iter");
- ls.register_renamed("clippy::unused_label", "unused_labels");
- ls.register_renamed("clippy::drop_bounds", "drop_bounds");
- ls.register_renamed("clippy::temporary_cstring_as_ptr", "temporary_cstring_as_ptr");
- ls.register_renamed("clippy::panic_params", "non_fmt_panics");
- ls.register_renamed("clippy::unknown_clippy_lints", "unknown_lints");
- ls.register_renamed("clippy::invalid_atomic_ordering", "invalid_atomic_ordering");
- ls.register_renamed("clippy::mem_discriminant_non_enum", "enum_intrinsics_non_enums");
+ for (old_name, new_name) in renamed_lints::RENAMED_LINTS {
+ ls.register_renamed(old_name, new_name);
+ }
}
// only exists to let the dogfood integration test works.
use clippy_utils::diagnostics::span_lint;
use clippy_utils::trait_ref_of_method;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_hir::intravisit::nested_filter::{self as hir_nested_filter, NestedFilter};
use rustc_hir::intravisit::{
- walk_fn_decl, walk_generic_param, walk_generics, walk_item, walk_param_bound, walk_poly_trait_ref, walk_ty, Visitor,
+ walk_fn_decl, walk_generic_param, walk_generics, walk_impl_item_ref, walk_item, walk_param_bound,
+ walk_poly_trait_ref, walk_trait_ref, walk_ty, Visitor,
};
use rustc_hir::FnRetTy::Return;
use rustc_hir::{
- BareFnTy, BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, ImplItem,
+ BareFnTy, BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, Impl, ImplItem,
ImplItemKind, Item, ItemKind, LangItem, Lifetime, LifetimeName, ParamName, PolyTraitRef, TraitBoundModifier,
TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WherePredicate,
};
use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::hir::nested_filter as middle_nested_filter;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span;
use rustc_span::symbol::{kw, Ident, Symbol};
impl<'tcx> LateLintPass<'tcx> for Lifetimes {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
- if let ItemKind::Fn(ref sig, ref generics, id) = item.kind {
+ if let ItemKind::Fn(ref sig, generics, id) = item.kind {
check_fn_inner(cx, sig.decl, Some(id), None, generics, item.span, true);
+ } else if let ItemKind::Impl(impl_) = item.kind {
+ report_extra_impl_lifetimes(cx, impl_);
}
}
sig.decl,
Some(id),
None,
- &item.generics,
+ item.generics,
item.span,
report_extra_lifetimes,
);
TraitFn::Required(sig) => (None, Some(sig)),
TraitFn::Provided(id) => (Some(id), None),
};
- check_fn_inner(cx, sig.decl, body, trait_sig, &item.generics, item.span, true);
+ check_fn_inner(cx, sig.decl, body, trait_sig, item.generics, item.span, true);
}
}
}
visitor.visit_ty(self_ty);
!visitor.all_lts().is_empty()
- }
- else {
+ } else {
false
}
}
false
}
-struct LifetimeChecker {
+struct LifetimeChecker<'cx, 'tcx, F> {
+ cx: &'cx LateContext<'tcx>,
map: FxHashMap<Symbol, Span>,
+ phantom: std::marker::PhantomData<F>,
}
-impl<'tcx> Visitor<'tcx> for LifetimeChecker {
+impl<'cx, 'tcx, F> LifetimeChecker<'cx, 'tcx, F> {
+ fn new(cx: &'cx LateContext<'tcx>, map: FxHashMap<Symbol, Span>) -> LifetimeChecker<'cx, 'tcx, F> {
+ Self {
+ cx,
+ map,
+ phantom: std::marker::PhantomData,
+ }
+ }
+}
+
+impl<'cx, 'tcx, F> Visitor<'tcx> for LifetimeChecker<'cx, 'tcx, F>
+where
+ F: NestedFilter<'tcx>,
+{
+ type Map = rustc_middle::hir::map::Map<'tcx>;
+ type NestedFilter = F;
+
// for lifetimes as parameters of generics
fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) {
self.map.remove(&lifetime.name.ident().name);
walk_generic_param(self, param);
}
}
+
+ fn nested_visit_map(&mut self) -> Self::Map {
+ self.cx.tcx.hir()
+ }
}
fn report_extra_lifetimes<'tcx>(cx: &LateContext<'tcx>, func: &'tcx FnDecl<'_>, generics: &'tcx Generics<'_>) {
_ => None,
})
.collect();
- let mut checker = LifetimeChecker { map: hs };
+ let mut checker = LifetimeChecker::<hir_nested_filter::None>::new(cx, hs);
walk_generics(&mut checker, generics);
walk_fn_decl(&mut checker, func);
}
}
+fn report_extra_impl_lifetimes<'tcx>(cx: &LateContext<'tcx>, impl_: &'tcx Impl<'_>) {
+ let hs = impl_
+ .generics
+ .params
+ .iter()
+ .filter_map(|par| match par.kind {
+ GenericParamKind::Lifetime { .. } => Some((par.name.ident().name, par.span)),
+ _ => None,
+ })
+ .collect();
+ let mut checker = LifetimeChecker::<middle_nested_filter::All>::new(cx, hs);
+
+ walk_generics(&mut checker, impl_.generics);
+ if let Some(ref trait_ref) = impl_.of_trait {
+ walk_trait_ref(&mut checker, trait_ref);
+ }
+ walk_ty(&mut checker, impl_.self_ty);
+ for item in impl_.items {
+ walk_impl_item_ref(&mut checker, item);
+ }
+
+ for &v in checker.map.values() {
+ span_lint(cx, EXTRA_UNUSED_LIFETIMES, v, "this lifetime isn't used in the impl");
+ }
+}
+
struct BodyLifetimeChecker {
lifetimes_used_in_body: bool,
}
/// This is most probably a typo
///
/// ### Known problems
- /// - Recommends a signed suffix, even though the number might be too big and an unsigned
- /// suffix is required
+ /// - Does not match on integers too large to fit in the corresponding unsigned type
/// - Does not match on `_127` since that is a valid grouping for decimal and octal numbers
///
/// ### Example
- /// ```rust
- /// // Probably mistyped
- /// 2_32;
- ///
- /// // Good
- /// 2_i32;
+ /// `2_32` => `2_i32`
+ /// `250_8 => `250_u8`
/// ```
#[clippy::version = "1.30.0"]
pub MISTYPED_LITERAL_SUFFIXES,
return true;
}
- let (part, mistyped_suffixes, missing_char) = if let Some((_, exponent)) = &mut num_lit.exponent {
- (exponent, &["32", "64"][..], 'f')
+ let (part, mistyped_suffixes, is_float) = if let Some((_, exponent)) = &mut num_lit.exponent {
+ (exponent, &["32", "64"][..], true)
} else if num_lit.fraction.is_some() {
- (&mut num_lit.integer, &["32", "64"][..], 'f')
+ return true;
} else {
- (&mut num_lit.integer, &["8", "16", "32", "64"][..], 'i')
+ (&mut num_lit.integer, &["8", "16", "32", "64"][..], false)
};
let mut split = part.rsplit('_');
let last_group = split.next().expect("At least one group");
if split.next().is_some() && mistyped_suffixes.contains(&last_group) {
- *part = &part[..part.len() - last_group.len()];
+ let main_part = &part[..part.len() - last_group.len()];
+ let missing_char;
+ if is_float {
+ missing_char = 'f';
+ } else {
+ let radix = match num_lit.radix {
+ Radix::Binary => 2,
+ Radix::Octal => 8,
+ Radix::Decimal => 10,
+ Radix::Hexadecimal => 16,
+ };
+ if let Ok(int) = u64::from_str_radix(&main_part.replace('_', ""), radix) {
+ missing_char = match (last_group, int) {
+ ("8", i) if i8::try_from(i).is_ok() => 'i',
+ ("16", i) if i16::try_from(i).is_ok() => 'i',
+ ("32", i) if i32::try_from(i).is_ok() => 'i',
+ ("64", i) if i64::try_from(i).is_ok() => 'i',
+ ("8", u) if u8::try_from(u).is_ok() => 'u',
+ ("16", u) if u16::try_from(u).is_ok() => 'u',
+ ("32", u) if u32::try_from(u).is_ok() => 'u',
+ ("64", _) => 'u',
+ _ => {
+ return true;
+ },
+ }
+ } else {
+ return true;
+ }
+ }
+ *part = main_part;
let mut sugg = num_lit.format();
sugg.push('_');
sugg.push(missing_char);
.operands
.iter()
.map(|(o, _)| match o {
- InlineAsmOperand::In { expr, .. }
- | InlineAsmOperand::InOut { expr, .. } => never_loop_expr(expr, main_loop_id),
+ InlineAsmOperand::In { expr, .. } | InlineAsmOperand::InOut { expr, .. } => {
+ never_loop_expr(expr, main_loop_id)
+ },
InlineAsmOperand::Out { expr, .. } => never_loop_expr_all(&mut expr.iter(), main_loop_id),
InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => {
never_loop_expr_all(&mut once(in_expr).chain(out_expr.iter()), main_loop_id)
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_opt;
-use clippy_utils::{meets_msrv, msrvs};
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::{get_parent_expr, meets_msrv, msrvs};
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind, GenericArg, QPath};
/// ```
/// Use instead:
/// ```rust
- /// usize::BITS;
+ /// usize::BITS as usize;
/// ```
#[clippy::version = "1.60.0"]
pub MANUAL_BITS,
if matches!(resolved_ty.kind(), ty::Int(_) | ty::Uint(_));
if let ExprKind::Lit(lit) = &other_expr.kind;
if let LitKind::Int(8, _) = lit.node;
-
then {
+ let mut app = Applicability::MachineApplicable;
+ let ty_snip = snippet_with_applicability(cx, real_ty.span, "..", &mut app);
+ let sugg = create_sugg(cx, expr, format!("{ty_snip}::BITS"));
+
span_lint_and_sugg(
cx,
MANUAL_BITS,
expr.span,
"usage of `mem::size_of::<T>()` to obtain the size of `T` in bits",
"consider using",
- format!("{}::BITS", snippet_opt(cx, real_ty.span).unwrap()),
- Applicability::MachineApplicable,
+ sugg,
+ app,
);
}
}
}
}
}
+
+fn create_sugg(cx: &LateContext<'_>, expr: &Expr<'_>, base_sugg: String) -> String {
+ if let Some(parent_expr) = get_parent_expr(cx, expr) {
+ if is_ty_conversion(parent_expr) {
+ return base_sugg;
+ }
+
+ // These expressions have precedence over casts, the suggestion therefore
+ // needs to be wrapped into parentheses
+ match parent_expr.kind {
+ ExprKind::Unary(..) | ExprKind::AddrOf(..) | ExprKind::MethodCall(..) => {
+ return format!("({base_sugg} as usize)");
+ },
+ _ => {},
+ }
+ }
+
+ format!("{base_sugg} as usize")
+}
+
+fn is_ty_conversion(expr: &Expr<'_>) -> bool {
+ if let ExprKind::Cast(..) = expr.kind {
+ true
+ } else if let ExprKind::MethodCall(path, [_], _) = expr.kind
+ && path.ident.name == rustc_span::sym::try_into
+ {
+ // This is only called for `usize` which implements `TryInto`. Therefore,
+ // we don't have to check here if `self` implements the `TryInto` trait.
+ true
+ } else {
+ false
+ }
+}
use clippy_utils::attrs::is_doc_hidden;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet_opt;
-use clippy_utils::{meets_msrv, msrvs};
-use if_chain::if_chain;
-use rustc_ast::ast::{FieldDef, Item, ItemKind, Variant, VariantData, VisibilityKind};
+use clippy_utils::{is_lint_allowed, meets_msrv, msrvs};
+use rustc_ast::ast::{self, VisibilityKind};
+use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability;
-use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
+use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
+use rustc_hir::{self as hir, Expr, ExprKind, QPath};
+use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
+use rustc_middle::ty::DefIdTree;
use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::def_id::{DefId, LocalDefId};
use rustc_span::{sym, Span};
declare_clippy_lint! {
"manual implementations of the non-exhaustive pattern can be simplified using #[non_exhaustive]"
}
-#[derive(Clone)]
-pub struct ManualNonExhaustive {
+#[allow(clippy::module_name_repetitions)]
+pub struct ManualNonExhaustiveStruct {
msrv: Option<RustcVersion>,
}
-impl ManualNonExhaustive {
+impl ManualNonExhaustiveStruct {
#[must_use]
pub fn new(msrv: Option<RustcVersion>) -> Self {
Self { msrv }
}
}
-impl_lint_pass!(ManualNonExhaustive => [MANUAL_NON_EXHAUSTIVE]);
+impl_lint_pass!(ManualNonExhaustiveStruct => [MANUAL_NON_EXHAUSTIVE]);
-impl EarlyLintPass for ManualNonExhaustive {
- fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
- if !meets_msrv(self.msrv.as_ref(), &msrvs::NON_EXHAUSTIVE) {
- return;
- }
+#[allow(clippy::module_name_repetitions)]
+pub struct ManualNonExhaustiveEnum {
+ msrv: Option<RustcVersion>,
+ constructed_enum_variants: FxHashSet<(DefId, DefId)>,
+ potential_enums: Vec<(LocalDefId, LocalDefId, Span, Span)>,
+}
- match &item.kind {
- ItemKind::Enum(def, _) => {
- check_manual_non_exhaustive_enum(cx, item, &def.variants);
- },
- ItemKind::Struct(variant_data, _) => {
- if let VariantData::Unit(..) = variant_data {
- return;
- }
-
- check_manual_non_exhaustive_struct(cx, item, variant_data);
- },
- _ => {},
+impl ManualNonExhaustiveEnum {
+ #[must_use]
+ pub fn new(msrv: Option<RustcVersion>) -> Self {
+ Self {
+ msrv,
+ constructed_enum_variants: FxHashSet::default(),
+ potential_enums: Vec::new(),
}
}
-
- extract_msrv_attr!(EarlyContext);
}
-fn check_manual_non_exhaustive_enum(cx: &EarlyContext<'_>, item: &Item, variants: &[Variant]) {
- fn is_non_exhaustive_marker(variant: &Variant) -> bool {
- matches!(variant.data, VariantData::Unit(_))
- && variant.ident.as_str().starts_with('_')
- && is_doc_hidden(&variant.attrs)
- }
+impl_lint_pass!(ManualNonExhaustiveEnum => [MANUAL_NON_EXHAUSTIVE]);
- let mut markers = variants.iter().filter(|v| is_non_exhaustive_marker(v));
- if_chain! {
- if let Some(marker) = markers.next();
- if markers.count() == 0 && variants.len() > 1;
- then {
- span_lint_and_then(
- cx,
- MANUAL_NON_EXHAUSTIVE,
- item.span,
- "this seems like a manual implementation of the non-exhaustive pattern",
- |diag| {
- if_chain! {
- if !item.attrs.iter().any(|attr| attr.has_name(sym::non_exhaustive));
- let header_span = cx.sess().source_map().span_until_char(item.span, '{');
- if let Some(snippet) = snippet_opt(cx, header_span);
- then {
+impl EarlyLintPass for ManualNonExhaustiveStruct {
+ fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
+ if !meets_msrv(self.msrv.as_ref(), &msrvs::NON_EXHAUSTIVE) {
+ return;
+ }
+
+ if let ast::ItemKind::Struct(variant_data, _) = &item.kind {
+ let (fields, delimiter) = match variant_data {
+ ast::VariantData::Struct(fields, _) => (&**fields, '{'),
+ ast::VariantData::Tuple(fields, _) => (&**fields, '('),
+ ast::VariantData::Unit(_) => return,
+ };
+ if fields.len() <= 1 {
+ return;
+ }
+ let mut iter = fields.iter().filter_map(|f| match f.vis.kind {
+ VisibilityKind::Public => None,
+ VisibilityKind::Inherited => Some(Ok(f)),
+ _ => Some(Err(())),
+ });
+ if let Some(Ok(field)) = iter.next()
+ && iter.next().is_none()
+ && field.ty.kind.is_unit()
+ && field.ident.map_or(true, |name| name.as_str().starts_with('_'))
+ {
+ span_lint_and_then(
+ cx,
+ MANUAL_NON_EXHAUSTIVE,
+ item.span,
+ "this seems like a manual implementation of the non-exhaustive pattern",
+ |diag| {
+ if !item.attrs.iter().any(|attr| attr.has_name(sym::non_exhaustive))
+ && let header_span = cx.sess().source_map().span_until_char(item.span, delimiter)
+ && let Some(snippet) = snippet_opt(cx, header_span)
+ {
diag.span_suggestion(
header_span,
"add the attribute",
Applicability::Unspecified,
);
}
+ diag.span_help(field.span, "remove this field");
}
- diag.span_help(marker.span, "remove this variant");
- });
+ );
+ }
}
}
+
+ extract_msrv_attr!(EarlyContext);
}
-fn check_manual_non_exhaustive_struct(cx: &EarlyContext<'_>, item: &Item, data: &VariantData) {
- fn is_private(field: &FieldDef) -> bool {
- matches!(field.vis.kind, VisibilityKind::Inherited)
- }
+impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum {
+ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
+ if !meets_msrv(self.msrv.as_ref(), &msrvs::NON_EXHAUSTIVE) {
+ return;
+ }
- fn is_non_exhaustive_marker(field: &FieldDef) -> bool {
- is_private(field) && field.ty.kind.is_unit() && field.ident.map_or(true, |n| n.as_str().starts_with('_'))
+ if let hir::ItemKind::Enum(def, _) = &item.kind
+ && def.variants.len() > 1
+ {
+ let mut iter = def.variants.iter().filter_map(|v| {
+ let id = cx.tcx.hir().local_def_id(v.id);
+ (matches!(v.data, hir::VariantData::Unit(_))
+ && v.ident.as_str().starts_with('_')
+ && is_doc_hidden(cx.tcx.get_attrs(id.to_def_id())))
+ .then(|| (id, v.span))
+ });
+ if let Some((id, span)) = iter.next()
+ && iter.next().is_none()
+ {
+ self.potential_enums.push((item.def_id, id, item.span, span));
+ }
+ }
}
- fn find_header_span(cx: &EarlyContext<'_>, item: &Item, data: &VariantData) -> Span {
- let delimiter = match data {
- VariantData::Struct(..) => '{',
- VariantData::Tuple(..) => '(',
- VariantData::Unit(_) => unreachable!("`VariantData::Unit` is already handled above"),
- };
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
+ if let ExprKind::Path(QPath::Resolved(None, p)) = &e.kind
+ && let [.., name] = p.segments
+ && let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), id) = p.res
+ && name.ident.as_str().starts_with('_')
+ {
+ let variant_id = cx.tcx.parent(id);
+ let enum_id = cx.tcx.parent(variant_id);
- cx.sess().source_map().span_until_char(item.span, delimiter)
+ self.constructed_enum_variants.insert((enum_id, variant_id));
+ }
}
- let fields = data.fields();
- let private_fields = fields.iter().filter(|f| is_private(f)).count();
- let public_fields = fields.iter().filter(|f| f.vis.kind.is_pub()).count();
-
- if_chain! {
- if private_fields == 1 && public_fields >= 1 && public_fields == fields.len() - 1;
- if let Some(marker) = fields.iter().find(|f| is_non_exhaustive_marker(f));
- then {
+ fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
+ for &(enum_id, _, enum_span, variant_span) in
+ self.potential_enums.iter().filter(|&&(enum_id, variant_id, _, _)| {
+ !self
+ .constructed_enum_variants
+ .contains(&(enum_id.to_def_id(), variant_id.to_def_id()))
+ && !is_lint_allowed(cx, MANUAL_NON_EXHAUSTIVE, cx.tcx.hir().local_def_id_to_hir_id(enum_id))
+ })
+ {
span_lint_and_then(
cx,
MANUAL_NON_EXHAUSTIVE,
- item.span,
+ enum_span,
"this seems like a manual implementation of the non-exhaustive pattern",
|diag| {
- if_chain! {
- if !item.attrs.iter().any(|attr| attr.has_name(sym::non_exhaustive));
- let header_span = find_header_span(cx, item, data);
- if let Some(snippet) = snippet_opt(cx, header_span);
- then {
+ if !cx.tcx.adt_def(enum_id).is_variant_list_non_exhaustive()
+ && let header_span = cx.sess().source_map().span_until_char(enum_span, '{')
+ && let Some(snippet) = snippet_opt(cx, header_span)
+ {
diag.span_suggestion(
header_span,
"add the attribute",
format!("#[non_exhaustive] {}", snippet),
Applicability::Unspecified,
);
- }
}
- diag.span_help(marker.span, "remove this field");
- });
+ diag.span_help(variant_span, "remove this variant");
+ },
+ );
}
}
+
+ extract_msrv_attr!(LateContext);
}
impl MapClone {
fn lint_explicit_closure(&self, cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool) {
let mut applicability = Applicability::MachineApplicable;
- let message = if is_copy {
- "you are using an explicit closure for copying elements"
- } else {
- "you are using an explicit closure for cloning elements"
- };
- let sugg_method = if is_copy && meets_msrv(self.msrv.as_ref(), &msrvs::ITERATOR_COPIED) {
- "copied"
+
+ let (message, sugg_method) = if is_copy && meets_msrv(self.msrv.as_ref(), &msrvs::ITERATOR_COPIED) {
+ ("you are using an explicit closure for copying elements", "copied")
} else {
- "cloned"
+ ("you are using an explicit closure for cloning elements", "cloned")
};
span_lint_and_sugg(
/// vec.push(value)
/// }
///
- /// if let Some(valie) = iter.next().ok() {
+ /// if let Some(value) = iter.next().ok() {
/// vec.push(value)
/// }
/// ```
if_chain! {
if let ExprKind::MethodCall(ok_path, [ref result_types_0, ..], _) = let_expr.kind; //check is expr.ok() has type Result<T,E>.ok(, _)
if let PatKind::TupleStruct(QPath::Resolved(_, x), y, _) = let_pat.kind; //get operation
- if method_chain_args(let_expr, &["ok"]).is_some(); //test to see if using ok() methoduse std::marker::Sized;
+ if method_chain_args(let_expr, &["ok"]).is_some(); //test to see if using ok() method use std::marker::Sized;
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(result_types_0), sym::Result);
if rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(x, false)) == "Some";
+++ /dev/null
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{path_to_local_id, peel_blocks, strip_pat_refs};
-use rustc_errors::Applicability;
-use rustc_hir::{ExprKind, Local, MatchSource, PatKind, QPath};
-use rustc_lint::LateContext;
-
-use super::INFALLIBLE_DESTRUCTURING_MATCH;
-
-pub(crate) fn check(cx: &LateContext<'_>, local: &Local<'_>) -> bool {
- if_chain! {
- if !local.span.from_expansion();
- if let Some(expr) = local.init;
- if let ExprKind::Match(target, arms, MatchSource::Normal) = expr.kind;
- if arms.len() == 1 && arms[0].guard.is_none();
- if let PatKind::TupleStruct(
- QPath::Resolved(None, variant_name), args, _) = arms[0].pat.kind;
- if args.len() == 1;
- if let PatKind::Binding(_, arg, ..) = strip_pat_refs(&args[0]).kind;
- let body = peel_blocks(arms[0].body);
- if path_to_local_id(body, arg);
-
- then {
- let mut applicability = Applicability::MachineApplicable;
- span_lint_and_sugg(
- cx,
- INFALLIBLE_DESTRUCTURING_MATCH,
- local.span,
- "you seem to be trying to use `match` to destructure a single infallible pattern. \
- Consider using `let`",
- "try this",
- format!(
- "let {}({}) = {};",
- snippet_with_applicability(cx, variant_name.span, "..", &mut applicability),
- snippet_with_applicability(cx, local.pat.span, "..", &mut applicability),
- snippet_with_applicability(cx, target.span, "..", &mut applicability),
- ),
- applicability,
- );
- return true;
- }
- }
- false
-}
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::{path_to_local_id, peel_blocks, strip_pat_refs};
+use rustc_errors::Applicability;
+use rustc_hir::{ExprKind, Local, MatchSource, PatKind, QPath};
+use rustc_lint::LateContext;
+
+use super::INFALLIBLE_DESTRUCTURING_MATCH;
+
+pub(crate) fn check(cx: &LateContext<'_>, local: &Local<'_>) -> bool {
+ if_chain! {
+ if !local.span.from_expansion();
+ if let Some(expr) = local.init;
+ if let ExprKind::Match(target, arms, MatchSource::Normal) = expr.kind;
+ if arms.len() == 1 && arms[0].guard.is_none();
+ if let PatKind::TupleStruct(
+ QPath::Resolved(None, variant_name), args, _) = arms[0].pat.kind;
+ if args.len() == 1;
+ if let PatKind::Binding(_, arg, ..) = strip_pat_refs(&args[0]).kind;
+ let body = peel_blocks(arms[0].body);
+ if path_to_local_id(body, arg);
+
+ then {
+ let mut applicability = Applicability::MachineApplicable;
+ span_lint_and_sugg(
+ cx,
+ INFALLIBLE_DESTRUCTURING_MATCH,
+ local.span,
+ "you seem to be trying to use `match` to destructure a single infallible pattern. \
+ Consider using `let`",
+ "try this",
+ format!(
+ "let {}({}) = {};",
+ snippet_with_applicability(cx, variant_name.span, "..", &mut applicability),
+ snippet_with_applicability(cx, local.pat.span, "..", &mut applicability),
+ snippet_with_applicability(cx, target.span, "..", &mut applicability),
+ ),
+ applicability,
+ );
+ return true;
+ }
+ }
+ false
+}
.map(|a| NormalizedPat::from_pat(cx, &arena, a.pat))
.collect();
- // The furthast forwards a pattern can move without semantic changes
+ // The furthest forwards a pattern can move without semantic changes
let forwards_blocking_idxs: Vec<_> = normalized_pats
.iter()
.enumerate()
})
.collect();
- // The furthast backwards a pattern can move without semantic changes
+ // The furthest backwards a pattern can move without semantic changes
let backwards_blocking_idxs: Vec<_> = normalized_pats
.iter()
.enumerate()
-use clippy_utils::source::{snippet_opt, walk_span_to_context};
+use clippy_utils::source::{snippet_opt, span_starts_with, walk_span_to_context};
use clippy_utils::{meets_msrv, msrvs};
use rustc_hir::{Arm, Expr, ExprKind, Local, MatchSource, Pat};
use rustc_lexer::{tokenize, TokenKind};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::{Span, SpanData, SyntaxContext};
-mod infalliable_detructuring_match;
+mod infallible_destructuring_match;
mod match_as_ref;
mod match_bool;
mod match_like_matches;
}
if let ExprKind::Match(ex, arms, source) = expr.kind {
+ if !span_starts_with(cx, expr.span, "match") {
+ return;
+ }
if !contains_cfg_arm(cx, expr, ex, arms) {
if source == MatchSource::Normal {
if !(meets_msrv(self.msrv.as_ref(), &msrvs::MATCHES_MACRO)
}
fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'_>) {
- self.infallible_destructuring_match_linted |= infalliable_detructuring_match::check(cx, local);
+ self.infallible_destructuring_match_linted |= infallible_destructuring_match::check(cx, local);
}
fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
return false;
}
- // Recurrsively check for each `else if let` phrase,
+ // Recursively check for each `else if let` phrase,
if let Some(ref nested_if_let) = higher::IfLet::hir(cx, if_else) {
return check_if_let(cx, nested_if_let);
}
if let ExprKind::Path(ref qpath) = ret.kind {
return is_lang_ctor(cx, qpath, OptionNone) || eq_expr_value(cx, if_let.let_expr, ret);
}
- return true;
+ return false;
}
return eq_expr_value(cx, if_let.let_expr, ret);
}
}
/// Manually check for coercion casting by checking if the type of the match operand or let expr
-/// differs with the assigned local variable or the funtion return type.
+/// differs with the assigned local variable or the function return type.
fn expr_ty_matches_p_ty(cx: &LateContext<'_>, expr: &Expr<'_>, p_expr: &Expr<'_>) -> bool {
if let Some(p_node) = get_parent_node(cx.tcx, p_expr.hir_id) {
match p_node {
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet;
use clippy_utils::sugg::Sugg;
-use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, is_type_lang_item, match_type};
+use clippy_utils::ty::needs_ordered_drop;
use clippy_utils::{higher, match_def_path};
use clippy_utils::{is_lang_ctor, is_trait_method, paths};
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
-use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability;
use rustc_hir::LangItem::{OptionNone, PollPending};
use rustc_hir::{
intravisit::{walk_expr, Visitor},
- Arm, Block, Expr, ExprKind, LangItem, Node, Pat, PatKind, QPath, UnOp,
+ Arm, Block, Expr, ExprKind, Node, Pat, PatKind, QPath, UnOp,
};
use rustc_lint::LateContext;
use rustc_middle::ty::{self, subst::GenericArgKind, DefIdTree, Ty};
}
}
-/// Checks if the drop order for a type matters. Some std types implement drop solely to
-/// deallocate memory. For these types, and composites containing them, changing the drop order
-/// won't result in any observable side effects.
-fn type_needs_ordered_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
- type_needs_ordered_drop_inner(cx, ty, &mut FxHashSet::default())
-}
-
-fn type_needs_ordered_drop_inner<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, seen: &mut FxHashSet<Ty<'tcx>>) -> bool {
- if !seen.insert(ty) {
- return false;
- }
- if !ty.needs_drop(cx.tcx, cx.param_env) {
- false
- } else if !cx
- .tcx
- .lang_items()
- .drop_trait()
- .map_or(false, |id| implements_trait(cx, ty, id, &[]))
- {
- // This type doesn't implement drop, so no side effects here.
- // Check if any component type has any.
- match ty.kind() {
- ty::Tuple(fields) => fields.iter().any(|ty| type_needs_ordered_drop_inner(cx, ty, seen)),
- ty::Array(ty, _) => type_needs_ordered_drop_inner(cx, *ty, seen),
- ty::Adt(adt, subs) => adt
- .all_fields()
- .map(|f| f.ty(cx.tcx, subs))
- .any(|ty| type_needs_ordered_drop_inner(cx, ty, seen)),
- _ => true,
- }
- }
- // Check for std types which implement drop, but only for memory allocation.
- else if is_type_diagnostic_item(cx, ty, sym::Vec)
- || is_type_lang_item(cx, ty, LangItem::OwnedBox)
- || is_type_diagnostic_item(cx, ty, sym::Rc)
- || is_type_diagnostic_item(cx, ty, sym::Arc)
- || is_type_diagnostic_item(cx, ty, sym::cstring_type)
- || is_type_diagnostic_item(cx, ty, sym::BTreeMap)
- || is_type_diagnostic_item(cx, ty, sym::LinkedList)
- || match_type(cx, ty, &paths::WEAK_RC)
- || match_type(cx, ty, &paths::WEAK_ARC)
- {
- // Check all of the generic arguments.
- if let ty::Adt(_, subs) = ty.kind() {
- subs.types().any(|ty| type_needs_ordered_drop_inner(cx, ty, seen))
- } else {
- true
- }
- } else {
- true
- }
-}
-
// Extract the generic arguments out of a type
fn try_get_generic_ty(ty: Ty<'_>, index: usize) -> Option<Ty<'_>> {
if_chain! {
// e.g. In `(String::new(), 0).1` the string is a temporary value.
ExprKind::AddrOf(_, _, expr) | ExprKind::Field(expr, _) => {
if !matches!(expr.kind, ExprKind::Path(_)) {
- if type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(expr)) {
+ if needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(expr)) {
self.res = true;
} else {
self.visit_expr(expr);
// e.g. In `(vec![0])[0]` the vector is a temporary value.
ExprKind::Index(base, index) => {
if !matches!(base.kind, ExprKind::Path(_)) {
- if type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(base)) {
+ if needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(base)) {
self.res = true;
} else {
self.visit_expr(base);
.typeck_results()
.type_dependent_def_id(expr.hir_id)
.map_or(false, |id| self.cx.tcx.fn_sig(id).skip_binder().inputs()[0].is_ref());
- if self_by_ref && type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(self_arg)) {
+ if self_by_ref && needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(self_arg)) {
self.res = true;
} else {
self.visit_expr(self_arg);
// scrutinee would be, so they have to be considered as well.
// e.g. in `if let Some(x) = foo.lock().unwrap().baz.as_ref() { .. }` the lock will be held
// for the duration if body.
- let needs_drop = type_needs_ordered_drop(cx, check_ty) || temporaries_need_ordered_drop(cx, let_expr);
+ let needs_drop = needs_ordered_drop(cx, check_ty) || temporaries_need_ordered_drop(cx, let_expr);
// check that `while_let_on_iterator` lint does not trigger
if_chain! {
if let ty::Adt(def, _) = ty.kind();
if def.is_struct() || def.is_union();
if fields.len() == def.non_enum_variant().fields.len();
+ if !def.non_enum_variant().is_field_list_non_exhaustive();
then {
span_lint_and_help(
--- /dev/null
+//! Lint for `c.is_digit(10)`
+
+use super::IS_DIGIT_ASCII_RADIX;
+use clippy_utils::{
+ consts::constant_full_int, consts::FullInt, diagnostics::span_lint_and_sugg, meets_msrv, msrvs,
+ source::snippet_with_applicability,
+};
+use rustc_errors::Applicability;
+use rustc_hir::Expr;
+use rustc_lint::LateContext;
+use rustc_semver::RustcVersion;
+
+pub(super) fn check<'tcx>(
+ cx: &LateContext<'tcx>,
+ expr: &'tcx Expr<'_>,
+ self_arg: &'tcx Expr<'_>,
+ radix: &'tcx Expr<'_>,
+ msrv: Option<&RustcVersion>,
+) {
+ if !meets_msrv(msrv, &msrvs::IS_ASCII_DIGIT) {
+ return;
+ }
+
+ if !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_char() {
+ return;
+ }
+
+ if let Some(radix_val) = constant_full_int(cx, cx.typeck_results(), radix) {
+ let (num, replacement) = match radix_val {
+ FullInt::S(10) | FullInt::U(10) => (10, "is_ascii_digit"),
+ FullInt::S(16) | FullInt::U(16) => (16, "is_ascii_hexdigit"),
+ _ => return,
+ };
+ let mut applicability = Applicability::MachineApplicable;
+
+ span_lint_and_sugg(
+ cx,
+ IS_DIGIT_ASCII_RADIX,
+ expr.span,
+ &format!("use of `char::is_digit` with literal radix of {}", num),
+ "try",
+ format!(
+ "{}.{}()",
+ snippet_with_applicability(cx, self_arg.span, "..", &mut applicability),
+ replacement
+ ),
+ applicability,
+ );
+ }
+}
use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::higher::Range;
use clippy_utils::is_integer_const;
-use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{
- higher::{self, Range},
- SpanlessEq,
-};
use rustc_ast::ast::RangeLimits;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, QPath};
use rustc_lint::LateContext;
-use rustc_span::symbol::{sym, Symbol};
+use rustc_span::symbol::sym;
use rustc_span::Span;
use super::ITER_WITH_DRAIN;
-const DRAIN_TYPES: &[Symbol] = &[sym::Vec, sym::VecDeque];
-
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, arg: &Expr<'_>) {
- let ty = cx.typeck_results().expr_ty(recv).peel_refs();
- if let Some(drained_type) = DRAIN_TYPES.iter().find(|&&sym| is_type_diagnostic_item(cx, ty, sym)) {
- // Refuse to emit `into_iter` suggestion on draining struct fields due
- // to the strong possibility of processing unmovable field.
- if let ExprKind::Field(..) = recv.kind {
- return;
- }
+ if !matches!(recv.kind, ExprKind::Field(..))
+ && let Some(adt) = cx.typeck_results().expr_ty(recv).ty_adt_def()
+ && let Some(ty_name) = cx.tcx.get_diagnostic_name(adt.did())
+ && matches!(ty_name, sym::Vec | sym::VecDeque)
+ && let Some(range) = Range::hir(arg)
+ && is_full_range(cx, recv, range)
+ {
+ span_lint_and_sugg(
+ cx,
+ ITER_WITH_DRAIN,
+ span.with_hi(expr.span.hi()),
+ &format!("`drain(..)` used on a `{}`", ty_name),
+ "try this",
+ "into_iter()".to_string(),
+ Applicability::MaybeIncorrect,
+ );
+ };
+}
- if let Some(range) = higher::Range::hir(arg) {
- let left_full = match range {
- Range { start: Some(start), .. } if is_integer_const(cx, start, 0) => true,
- Range { start: None, .. } => true,
- _ => false,
- };
- let full = left_full
- && match range {
- Range {
- end: Some(end),
- limits: RangeLimits::HalfOpen,
- ..
- } => {
- // `x.drain(..x.len())` call
- if_chain! {
- if let ExprKind::MethodCall(len_path, len_args, _) = end.kind;
- if len_path.ident.name == sym::len && len_args.len() == 1;
- if let ExprKind::Path(QPath::Resolved(_, drain_path)) = recv.kind;
- if let ExprKind::Path(QPath::Resolved(_, len_path)) = len_args[0].kind;
- if SpanlessEq::new(cx).eq_path(drain_path, len_path);
- then { true }
- else { false }
- }
- },
- Range {
- end: None,
- limits: RangeLimits::HalfOpen,
- ..
- } => true,
- _ => false,
- };
- if full {
- span_lint_and_sugg(
- cx,
- ITER_WITH_DRAIN,
- span.with_hi(expr.span.hi()),
- &format!("`drain(..)` used on a `{}`", drained_type),
- "try this",
- "into_iter()".to_string(),
- Applicability::MaybeIncorrect,
- );
+fn is_full_range(cx: &LateContext<'_>, container: &Expr<'_>, range: Range<'_>) -> bool {
+ range.start.map_or(true, |e| is_integer_const(cx, e, 0))
+ && range.end.map_or(true, |e| {
+ if range.limits == RangeLimits::HalfOpen
+ && let ExprKind::Path(QPath::Resolved(None, container_path)) = container.kind
+ && let ExprKind::MethodCall(name, [self_arg], _) = e.kind
+ && name.ident.name == sym::len
+ && let ExprKind::Path(QPath::Resolved(None, path)) = self_arg.kind
+ {
+ container_path.res == path.res
+ } else {
+ false
}
- }
- }
+ })
}
mod inefficient_to_string;
mod inspect_for_each;
mod into_iter_on_ref;
+mod is_digit_ascii_radix;
mod iter_cloned_collect;
mod iter_count;
mod iter_next_slice;
mod map_identity;
mod map_unwrap_or;
mod needless_option_as_deref;
+mod needless_option_take;
mod ok_expect;
mod option_as_ref_deref;
mod option_map_or_none;
/// Checks for methods with certain name prefixes and which
/// doesn't match how self is taken. The actual rules are:
///
- /// |Prefix |Postfix |`self` taken | `self` type |
- /// |-------|------------|-----------------------|--------------|
- /// |`as_` | none |`&self` or `&mut self` | any |
- /// |`from_`| none | none | any |
- /// |`into_`| none |`self` | any |
- /// |`is_` | none |`&self` or none | any |
- /// |`to_` | `_mut` |`&mut self` | any |
- /// |`to_` | not `_mut` |`self` | `Copy` |
- /// |`to_` | not `_mut` |`&self` | not `Copy` |
+ /// |Prefix |Postfix |`self` taken | `self` type |
+ /// |-------|------------|-------------------------------|--------------|
+ /// |`as_` | none |`&self` or `&mut self` | any |
+ /// |`from_`| none | none | any |
+ /// |`into_`| none |`self` | any |
+ /// |`is_` | none |`&mut self` or `&self` or none | any |
+ /// |`to_` | `_mut` |`&mut self` | any |
+ /// |`to_` | not `_mut` |`self` | `Copy` |
+ /// |`to_` | not `_mut` |`&self` | not `Copy` |
///
/// Note: Clippy doesn't trigger methods with `to_` prefix in:
/// - Traits definition.
#[clippy::version = "1.55.0"]
pub EXTEND_WITH_DRAIN,
perf,
- "using vec.append(&mut vec) to move the full range of a vecor to another"
+ "using vec.append(&mut vec) to move the full range of a vector to another"
}
declare_clippy_lint! {
/// ### Example
/// ```rust,ignore
/// // Bad
- /// let (key, value) = _.splitn(2, '=').next_tuple()?;
- /// let value = _.splitn(2, '=').nth(1)?;
+ /// let s = "key=value=add";
+ /// let (key, value) = s.splitn(2, '=').next_tuple()?;
+ /// let value = s.splitn(2, '=').nth(1)?;
///
+ /// let mut parts = s.splitn(2, '=');
+ /// let key = parts.next()?;
+ /// let value = parts.next()?;
+ /// ```
+ /// Use instead:
+ /// ```rust,ignore
/// // Good
- /// let (key, value) = _.split_once('=')?;
- /// let value = _.split_once('=')?.1;
+ /// let s = "key=value=add";
+ /// let (key, value) = s.split_once('=')?;
+ /// let value = s.split_once('=')?.1;
+ ///
+ /// let (key, value) = s.split_once('=')?;
/// ```
+ ///
+ /// ### Limitations
+ /// The multiple statement variant currently only detects `iter.next()?`/`iter.next().unwrap()`
+ /// in two separate `let` statements that immediately follow the `splitn()`
#[clippy::version = "1.57.0"]
pub MANUAL_SPLIT_ONCE,
complexity,
/// using `.collect::<String>()` over `.collect::<Vec<String>>().join("")`
/// will prevent loop unrolling and will result in a negative performance impact.
///
- /// Additionlly, differences have been observed between aarch64 and x86_64 assembly output,
+ /// Additionally, differences have been observed between aarch64 and x86_64 assembly output,
/// with aarch64 tending to producing faster assembly in more cases when using `.collect::<String>()`
#[clippy::version = "1.61.0"]
pub UNNECESSARY_JOIN,
"no-op use of `deref` or `deref_mut` method to `Option`."
}
+declare_clippy_lint! {
+ /// ### What it does
+ /// Finds usages of [`char::is_digit`]
+ /// (https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_digit) that
+ /// can be replaced with [`is_ascii_digit`]
+ /// (https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_ascii_digit) or
+ /// [`is_ascii_hexdigit`]
+ /// (https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_ascii_hexdigit).
+ ///
+ /// ### Why is this bad?
+ /// `is_digit(..)` is slower and requires specifying the radix.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let c: char = '6';
+ /// c.is_digit(10);
+ /// c.is_digit(16);
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// let c: char = '6';
+ /// c.is_ascii_digit();
+ /// c.is_ascii_hexdigit();
+ /// ```
+ #[clippy::version = "1.61.0"]
+ pub IS_DIGIT_ASCII_RADIX,
+ style,
+ "use of `char::is_digit(..)` with literal radix of 10 or 16"
+}
+
+declare_clippy_lint! {
+ ///
+ /// ### Why is this bad?
+ ///
+ /// ### Example
+ /// ```rust
+ /// let x = Some(3);
+ /// x.as_ref().take();
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// let x = Some(3);
+ /// x.as_ref();
+ /// ```
+ #[clippy::version = "1.61.0"]
+ pub NEEDLESS_OPTION_TAKE,
+ complexity,
+ "using `.as_ref().take()` on a temporary value"
+}
+
pub struct Methods {
avoid_breaking_exported_api: bool,
msrv: Option<RustcVersion>,
UNNECESSARY_JOIN,
ERR_EXPECT,
NEEDLESS_OPTION_AS_DEREF,
+ IS_DIGIT_ASCII_RADIX,
+ NEEDLESS_OPTION_TAKE,
]);
/// Extracts a method call name, args, and `Span` of the method name.
single_char_add_str::check(cx, expr, args);
into_iter_on_ref::check(cx, expr, method_span, method_call.ident.name, args);
single_char_pattern::check(cx, expr, method_call.ident.name, args);
- unnecessary_to_owned::check(cx, expr, method_call.ident.name, args);
+ unnecessary_to_owned::check(cx, expr, method_call.ident.name, args, self.msrv.as_ref());
},
hir::ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => {
let mut info = BinaryExprInfo {
},
("get_or_insert_with", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert"),
("is_file", []) => filetype_is_file::check(cx, expr, recv),
+ ("is_digit", [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, msrv),
("is_none", []) => check_is_some_is_none(cx, expr, recv, false),
("is_some", []) => check_is_some_is_none(cx, expr, recv, true),
("join", [join_arg]) => {
("splitn" | "rsplitn", [count_arg, pat_arg]) => {
if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) {
suspicious_splitn::check(cx, name, expr, recv, count);
- if count == 2 && meets_msrv(msrv, &msrvs::STR_SPLIT_ONCE) {
- str_splitn::check_manual_split_once(cx, name, expr, recv, pat_arg);
- }
- if count >= 2 {
- str_splitn::check_needless_splitn(cx, name, expr, recv, pat_arg, count);
- }
+ str_splitn::check(cx, name, expr, recv, pat_arg, count, msrv);
}
},
("splitn_mut" | "rsplitn_mut", [count_arg, _]) => {
}
}
},
+ ("take", []) => needless_option_take::check(cx, expr, recv),
("to_os_string" | "to_owned" | "to_path_buf" | "to_vec", []) => {
implicit_clone::check(cx, name, expr, recv);
},
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::match_def_path;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::is_type_diagnostic_item;
+use rustc_errors::Applicability;
+use rustc_hir::Expr;
+use rustc_lint::LateContext;
+use rustc_span::sym;
+
+use super::NEEDLESS_OPTION_TAKE;
+
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) {
+ // Checks if expression type is equal to sym::Option and if the expr is not a syntactic place
+ if !recv.is_syntactic_place_expr() && is_expr_option(cx, recv) && has_expr_as_ref_path(cx, recv) {
+ let mut applicability = Applicability::MachineApplicable;
+ span_lint_and_sugg(
+ cx,
+ NEEDLESS_OPTION_TAKE,
+ expr.span,
+ "called `Option::take()` on a temporary value",
+ "try",
+ format!(
+ "{}",
+ snippet_with_applicability(cx, recv.span, "..", &mut applicability)
+ ),
+ applicability,
+ );
+ }
+}
+
+fn is_expr_option(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+ let expr_type = cx.typeck_results().expr_ty(expr);
+ is_type_diagnostic_item(cx, expr_type, sym::Option)
+}
+
+fn has_expr_as_ref_path(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+ if let Some(ref_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) {
+ return match_def_path(cx, ref_id, &["core", "option", "Option", "as_ref"]);
+ }
+ false
+}
use clippy_utils::consts::{constant, Constant};
-use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::source::snippet_with_context;
-use clippy_utils::{is_diag_item_method, match_def_path, paths};
+use clippy_utils::usage::local_used_after_expr;
+use clippy_utils::visitors::expr_visitor;
+use clippy_utils::{is_diag_item_method, match_def_path, meets_msrv, msrvs, path_to_local_id, paths};
use if_chain::if_chain;
use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind, HirId, LangItem, Node, QPath};
+use rustc_hir::intravisit::Visitor;
+use rustc_hir::{
+ BindingAnnotation, Expr, ExprKind, HirId, LangItem, Local, MatchSource, Node, Pat, PatKind, QPath, Stmt, StmtKind,
+};
use rustc_lint::LateContext;
-use rustc_middle::ty::{self, adjustment::Adjust};
-use rustc_span::{symbol::sym, Span, SyntaxContext};
+use rustc_middle::ty;
+use rustc_semver::RustcVersion;
+use rustc_span::{sym, Span, Symbol, SyntaxContext};
-use super::MANUAL_SPLIT_ONCE;
+use super::{MANUAL_SPLIT_ONCE, NEEDLESS_SPLITN};
-pub(super) fn check_manual_split_once(
+pub(super) fn check(
cx: &LateContext<'_>,
method_name: &str,
expr: &Expr<'_>,
self_arg: &Expr<'_>,
pat_arg: &Expr<'_>,
+ count: u128,
+ msrv: Option<&RustcVersion>,
) {
- if !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_str() {
+ if count < 2 || !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_str() {
return;
}
+ let needless = |usage_kind| match usage_kind {
+ IterUsageKind::Nth(n) => count > n + 1,
+ IterUsageKind::NextTuple => count > 2,
+ };
+ let manual = count == 2 && meets_msrv(msrv, &msrvs::STR_SPLIT_ONCE);
+
+ match parse_iter_usage(cx, expr.span.ctxt(), cx.tcx.hir().parent_iter(expr.hir_id)) {
+ Some(usage) if needless(usage.kind) => lint_needless(cx, method_name, expr, self_arg, pat_arg),
+ Some(usage) if manual => check_manual_split_once(cx, method_name, expr, self_arg, pat_arg, &usage),
+ None if manual => {
+ check_manual_split_once_indirect(cx, method_name, expr, self_arg, pat_arg);
+ },
+ _ => {},
+ }
+}
+
+fn lint_needless(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, self_arg: &Expr<'_>, pat_arg: &Expr<'_>) {
+ let mut app = Applicability::MachineApplicable;
+ let r = if method_name == "splitn" { "" } else { "r" };
+
+ span_lint_and_sugg(
+ cx,
+ NEEDLESS_SPLITN,
+ expr.span,
+ &format!("unnecessary use of `{r}splitn`"),
+ "try this",
+ format!(
+ "{}.{r}split({})",
+ snippet_with_context(cx, self_arg.span, expr.span.ctxt(), "..", &mut app).0,
+ snippet_with_context(cx, pat_arg.span, expr.span.ctxt(), "..", &mut app).0,
+ ),
+ app,
+ );
+}
+
+fn check_manual_split_once(
+ cx: &LateContext<'_>,
+ method_name: &str,
+ expr: &Expr<'_>,
+ self_arg: &Expr<'_>,
+ pat_arg: &Expr<'_>,
+ usage: &IterUsage,
+) {
let ctxt = expr.span.ctxt();
- let (method_name, msg, reverse) = if method_name == "splitn" {
- ("split_once", "manual implementation of `split_once`", false)
+ let (msg, reverse) = if method_name == "splitn" {
+ ("manual implementation of `split_once`", false)
} else {
- ("rsplit_once", "manual implementation of `rsplit_once`", true)
- };
- let usage = match parse_iter_usage(cx, ctxt, cx.tcx.hir().parent_iter(expr.hir_id), reverse) {
- Some(x) => x,
- None => return,
+ ("manual implementation of `rsplit_once`", true)
};
let mut app = Applicability::MachineApplicable;
let sugg = match usage.kind {
IterUsageKind::NextTuple => {
- format!("{}.{}({})", self_snip, method_name, pat_snip)
- },
- IterUsageKind::RNextTuple => format!("{}.{}({}).map(|(x, y)| (y, x))", self_snip, method_name, pat_snip),
- IterUsageKind::Next | IterUsageKind::Second => {
- let self_deref = {
- let adjust = cx.typeck_results().expr_adjustments(self_arg);
- if adjust.len() < 2 {
- String::new()
- } else if cx.typeck_results().expr_ty(self_arg).is_box()
- || adjust
- .iter()
- .any(|a| matches!(a.kind, Adjust::Deref(Some(_))) || a.target.is_box())
- {
- format!("&{}", "*".repeat(adjust.len().saturating_sub(1)))
- } else {
- "*".repeat(adjust.len().saturating_sub(2))
- }
- };
- if matches!(usage.kind, IterUsageKind::Next) {
- match usage.unwrap_kind {
- Some(UnwrapKind::Unwrap) => {
- if reverse {
- format!("{}.{}({}).unwrap().0", self_snip, method_name, pat_snip)
- } else {
- format!(
- "{}.{}({}).map_or({}{}, |x| x.0)",
- self_snip, method_name, pat_snip, self_deref, &self_snip
- )
- }
- },
- Some(UnwrapKind::QuestionMark) => {
- format!(
- "{}.{}({}).map_or({}{}, |x| x.0)",
- self_snip, method_name, pat_snip, self_deref, &self_snip
- )
- },
- None => {
- format!(
- "Some({}.{}({}).map_or({}{}, |x| x.0))",
- &self_snip, method_name, pat_snip, self_deref, &self_snip
- )
- },
- }
+ if reverse {
+ format!("{self_snip}.rsplit_once({pat_snip}).map(|(x, y)| (y, x))")
} else {
- match usage.unwrap_kind {
- Some(UnwrapKind::Unwrap) => {
- if reverse {
- // In this case, no better suggestion is offered.
- return;
- }
- format!("{}.{}({}).unwrap().1", self_snip, method_name, pat_snip)
- },
- Some(UnwrapKind::QuestionMark) => {
- format!("{}.{}({})?.1", self_snip, method_name, pat_snip)
- },
- None => {
- format!("{}.{}({}).map(|x| x.1)", self_snip, method_name, pat_snip)
- },
- }
+ format!("{self_snip}.split_once({pat_snip})")
+ }
+ },
+ IterUsageKind::Nth(1) => {
+ let (r, field) = if reverse { ("r", 0) } else { ("", 1) };
+
+ match usage.unwrap_kind {
+ Some(UnwrapKind::Unwrap) => {
+ format!("{self_snip}.{r}split_once({pat_snip}).unwrap().{field}")
+ },
+ Some(UnwrapKind::QuestionMark) => {
+ format!("{self_snip}.{r}split_once({pat_snip})?.{field}")
+ },
+ None => {
+ format!("{self_snip}.{r}split_once({pat_snip}).map(|x| x.{field})")
+ },
}
},
+ IterUsageKind::Nth(_) => return,
};
span_lint_and_sugg(cx, MANUAL_SPLIT_ONCE, usage.span, msg, "try this", sugg, app);
}
+/// checks for
+///
+/// ```
+/// let mut iter = "a.b.c".splitn(2, '.');
+/// let a = iter.next();
+/// let b = iter.next();
+/// ```
+fn check_manual_split_once_indirect(
+ cx: &LateContext<'_>,
+ method_name: &str,
+ expr: &Expr<'_>,
+ self_arg: &Expr<'_>,
+ pat_arg: &Expr<'_>,
+) -> Option<()> {
+ let ctxt = expr.span.ctxt();
+ let mut parents = cx.tcx.hir().parent_iter(expr.hir_id);
+ if let (_, Node::Local(local)) = parents.next()?
+ && let PatKind::Binding(BindingAnnotation::Mutable, iter_binding_id, iter_ident, None) = local.pat.kind
+ && let (iter_stmt_id, Node::Stmt(_)) = parents.next()?
+ && let (_, Node::Block(enclosing_block)) = parents.next()?
+
+ && let mut stmts = enclosing_block
+ .stmts
+ .iter()
+ .skip_while(|stmt| stmt.hir_id != iter_stmt_id)
+ .skip(1)
+
+ && let first = indirect_usage(cx, stmts.next()?, iter_binding_id, ctxt)?
+ && let second = indirect_usage(cx, stmts.next()?, iter_binding_id, ctxt)?
+ && first.unwrap_kind == second.unwrap_kind
+ && first.name != second.name
+ && !local_used_after_expr(cx, iter_binding_id, second.init_expr)
+ {
+ let (r, lhs, rhs) = if method_name == "splitn" {
+ ("", first.name, second.name)
+ } else {
+ ("r", second.name, first.name)
+ };
+ let msg = format!("manual implementation of `{r}split_once`");
+
+ let mut app = Applicability::MachineApplicable;
+ let self_snip = snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0;
+ let pat_snip = snippet_with_context(cx, pat_arg.span, ctxt, "..", &mut app).0;
+
+ span_lint_and_then(cx, MANUAL_SPLIT_ONCE, local.span, &msg, |diag| {
+ diag.span_label(first.span, "first usage here");
+ diag.span_label(second.span, "second usage here");
+
+ let unwrap = match first.unwrap_kind {
+ UnwrapKind::Unwrap => ".unwrap()",
+ UnwrapKind::QuestionMark => "?",
+ };
+ diag.span_suggestion_verbose(
+ local.span,
+ &format!("try `{r}split_once`"),
+ format!("let ({lhs}, {rhs}) = {self_snip}.{r}split_once({pat_snip}){unwrap};"),
+ app,
+ );
+
+ let remove_msg = format!("remove the `{iter_ident}` usages");
+ diag.span_suggestion(
+ first.span,
+ &remove_msg,
+ String::new(),
+ app,
+ );
+ diag.span_suggestion(
+ second.span,
+ &remove_msg,
+ String::new(),
+ app,
+ );
+ });
+ }
+
+ Some(())
+}
+
+#[derive(Debug)]
+struct IndirectUsage<'a> {
+ name: Symbol,
+ span: Span,
+ init_expr: &'a Expr<'a>,
+ unwrap_kind: UnwrapKind,
+}
+
+/// returns `Some(IndirectUsage)` for e.g.
+///
+/// ```ignore
+/// let name = binding.next()?;
+/// let name = binding.next().unwrap();
+/// ```
+fn indirect_usage<'tcx>(
+ cx: &LateContext<'tcx>,
+ stmt: &Stmt<'tcx>,
+ binding: HirId,
+ ctxt: SyntaxContext,
+) -> Option<IndirectUsage<'tcx>> {
+ if let StmtKind::Local(Local {
+ pat:
+ Pat {
+ kind: PatKind::Binding(BindingAnnotation::Unannotated, _, ident, None),
+ ..
+ },
+ init: Some(init_expr),
+ hir_id: local_hir_id,
+ ..
+ }) = stmt.kind
+ {
+ let mut path_to_binding = None;
+ expr_visitor(cx, |expr| {
+ if path_to_local_id(expr, binding) {
+ path_to_binding = Some(expr);
+ }
+
+ path_to_binding.is_none()
+ })
+ .visit_expr(init_expr);
+
+ let mut parents = cx.tcx.hir().parent_iter(path_to_binding?.hir_id);
+ let iter_usage = parse_iter_usage(cx, ctxt, &mut parents)?;
+
+ let (parent_id, _) = parents.find(|(_, node)| {
+ !matches!(
+ node,
+ Node::Expr(Expr {
+ kind: ExprKind::Match(.., MatchSource::TryDesugar),
+ ..
+ })
+ )
+ })?;
+
+ if let IterUsage {
+ kind: IterUsageKind::Nth(0),
+ unwrap_kind: Some(unwrap_kind),
+ ..
+ } = iter_usage
+ {
+ if parent_id == *local_hir_id {
+ return Some(IndirectUsage {
+ name: ident.name,
+ span: stmt.span,
+ init_expr,
+ unwrap_kind,
+ });
+ }
+ }
+ }
+
+ None
+}
+
+#[derive(Debug, Clone, Copy)]
enum IterUsageKind {
- Next,
- Second,
+ Nth(u128),
NextTuple,
- RNextTuple,
}
+#[derive(Debug, PartialEq)]
enum UnwrapKind {
Unwrap,
QuestionMark,
}
+#[derive(Debug)]
struct IterUsage {
kind: IterUsageKind,
unwrap_kind: Option<UnwrapKind>,
cx: &LateContext<'tcx>,
ctxt: SyntaxContext,
mut iter: impl Iterator<Item = (HirId, Node<'tcx>)>,
- reverse: bool,
) -> Option<IterUsage> {
let (kind, span) = match iter.next() {
Some((_, Node::Expr(e))) if e.span.ctxt() == ctxt => {
let iter_id = cx.tcx.get_diagnostic_item(sym::Iterator)?;
match (name.ident.as_str(), args) {
- ("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => {
- if reverse {
- (IterUsageKind::Second, e.span)
- } else {
- (IterUsageKind::Next, e.span)
- }
- },
+ ("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => (IterUsageKind::Nth(0), e.span),
("next_tuple", []) => {
return if_chain! {
if match_def_path(cx, did, &paths::ITERTOOLS_NEXT_TUPLE);
if subs.len() == 2;
then {
Some(IterUsage {
- kind: if reverse { IterUsageKind::RNextTuple } else { IterUsageKind::NextTuple },
+ kind: IterUsageKind::NextTuple,
span: e.span,
unwrap_kind: None
})
}
}
};
- match if reverse { idx ^ 1 } else { idx } {
- 0 => (IterUsageKind::Next, span),
- 1 => (IterUsageKind::Second, span),
- _ => return None,
- }
+ (IterUsageKind::Nth(idx), span)
} else {
return None;
}
span,
})
}
-
-use super::NEEDLESS_SPLITN;
-
-pub(super) fn check_needless_splitn(
- cx: &LateContext<'_>,
- method_name: &str,
- expr: &Expr<'_>,
- self_arg: &Expr<'_>,
- pat_arg: &Expr<'_>,
- count: u128,
-) {
- if !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_str() {
- return;
- }
- let ctxt = expr.span.ctxt();
- let mut app = Applicability::MachineApplicable;
- let (reverse, message) = if method_name == "splitn" {
- (false, "unnecessary use of `splitn`")
- } else {
- (true, "unnecessary use of `rsplitn`")
- };
- if_chain! {
- if count >= 2;
- if check_iter(cx, ctxt, cx.tcx.hir().parent_iter(expr.hir_id), count);
- then {
- span_lint_and_sugg(
- cx,
- NEEDLESS_SPLITN,
- expr.span,
- message,
- "try this",
- format!(
- "{}.{}({})",
- snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0,
- if reverse {"rsplit"} else {"split"},
- snippet_with_context(cx, pat_arg.span, ctxt, "..", &mut app).0
- ),
- app,
- );
- }
- }
-}
-
-fn check_iter<'tcx>(
- cx: &LateContext<'tcx>,
- ctxt: SyntaxContext,
- mut iter: impl Iterator<Item = (HirId, Node<'tcx>)>,
- count: u128,
-) -> bool {
- match iter.next() {
- Some((_, Node::Expr(e))) if e.span.ctxt() == ctxt => {
- let (name, args) = if let ExprKind::MethodCall(name, [_, args @ ..], _) = e.kind {
- (name, args)
- } else {
- return false;
- };
- if_chain! {
- if let Some(did) = cx.typeck_results().type_dependent_def_id(e.hir_id);
- if let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator);
- then {
- match (name.ident.as_str(), args) {
- ("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => {
- return true;
- },
- ("next_tuple", []) if count > 2 => {
- return true;
- },
- ("nth", [idx_expr]) if cx.tcx.trait_of_item(did) == Some(iter_id) => {
- if let Some((Constant::Int(idx), _)) = constant(cx, cx.typeck_results(), idx_expr) {
- if count > idx + 1 {
- return true;
- }
- }
- },
- _ => return false,
- }
- }
- }
- },
- _ => return false,
- };
- false
-}
use clippy_utils::ty::{
contains_ty, get_associated_type, get_iterator_item_ty, implements_trait, is_copy, peel_mid_ty_refs,
};
+use clippy_utils::{meets_msrv, msrvs};
+
use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item};
use rustc_errors::Applicability;
use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind};
use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref};
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef};
use rustc_middle::ty::{self, PredicateKind, ProjectionPredicate, TraitPredicate, Ty};
+use rustc_semver::RustcVersion;
use rustc_span::{sym, Symbol};
use std::cmp::max;
use super::UNNECESSARY_TO_OWNED;
-pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol, args: &'tcx [Expr<'tcx>]) {
+pub fn check<'tcx>(
+ cx: &LateContext<'tcx>,
+ expr: &'tcx Expr<'tcx>,
+ method_name: Symbol,
+ args: &'tcx [Expr<'tcx>],
+ msrv: Option<&RustcVersion>,
+) {
if_chain! {
if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
if let [receiver] = args;
if check_addr_of_expr(cx, expr, method_name, method_def_id, receiver) {
return;
}
- if check_into_iter_call_arg(cx, expr, method_name, receiver) {
+ if check_into_iter_call_arg(cx, expr, method_name, receiver, msrv) {
return;
}
check_other_call_arg(cx, expr, method_name, receiver);
/// Checks whether `expr` is an argument in an `into_iter` call and, if so, determines whether its
/// call of a `to_owned`-like function is unnecessary.
-fn check_into_iter_call_arg(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol, receiver: &Expr<'_>) -> bool {
+fn check_into_iter_call_arg(
+ cx: &LateContext<'_>,
+ expr: &Expr<'_>,
+ method_name: Symbol,
+ receiver: &Expr<'_>,
+ msrv: Option<&RustcVersion>,
+) -> bool {
if_chain! {
if let Some(parent) = get_parent_expr(cx, expr);
if let Some(callee_def_id) = fn_def_id(cx, parent);
if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver, true) {
return true;
}
- let cloned_or_copied = if is_copy(cx, item_ty) { "copied" } else { "cloned" };
+ let cloned_or_copied = if is_copy(cx, item_ty) && meets_msrv(msrv, &msrvs::ITERATOR_COPIED) { "copied" } else { "cloned" };
// The next suggestion may be incorrect because the removal of the `to_owned`-like
// function could cause the iterator to hold a reference to a resource that is used
// mutably. See https://github.com/rust-lang/rust-clippy/issues/8148.
(&[Convention::StartsWith("as_")], &[SelfKind::Ref, SelfKind::RefMut]),
(&[Convention::StartsWith("from_")], &[SelfKind::No]),
(&[Convention::StartsWith("into_")], &[SelfKind::Value]),
- (&[Convention::StartsWith("is_")], &[SelfKind::Ref, SelfKind::No]),
+ (&[Convention::StartsWith("is_")], &[SelfKind::RefMut, SelfKind::Ref, SelfKind::No]),
(&[Convention::Eq("to_mut")], &[SelfKind::RefMut]),
(&[Convention::StartsWith("to_"), Convention::EndsWith("_mut")], &[SelfKind::RefMut]),
// See <https://github.com/rust-lang/rust-clippy/issues/4507> for a regression.
// FIXME: Find a better way to detect those cases.
let lit_snip = match snippet_opt(cx, lit.span) {
- Some(snip) if snip.chars().next().map_or(false, |c| c.is_digit(10)) => snip,
+ Some(snip) if snip.chars().next().map_or(false, |c| c.is_ascii_digit()) => snip,
_ => return,
};
/// pub struct PubBaz;
/// impl PubBaz {
/// fn private() {} // ok
- /// pub fn not_ptrivate() {} // missing #[inline]
+ /// pub fn not_private() {} // missing #[inline]
/// }
///
/// impl Bar for PubBaz {
let attrs = cx.tcx.hir().attrs(it.hir_id());
check_missing_inline_attrs(cx, attrs, it.span, desc);
},
- hir::ItemKind::Trait(ref _is_auto, ref _unsafe, ref _generics, _bounds, trait_items) => {
+ hir::ItemKind::Trait(ref _is_auto, ref _unsafe, _generics, _bounds, trait_items) => {
// note: we need to check if the trait is exported so we can't use
// `LateLintPass::check_trait_item` here.
for tit in trait_items {
false
}
-fn suggession_snippet(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
+fn suggesstion_snippet(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
if let ExprKind::Binary(ref op, left, right) = expr.kind {
if let (Some(l_snippet), Some(r_snippet)) = (snippet_opt(cx, left.span), snippet_opt(cx, right.span)) {
let op_snippet = match op.node {
expr.span,
"use of bitwise operator instead of lazy operator between booleans",
|diag| {
- if let Some(sugg) = suggession_snippet(cx, expr) {
+ if let Some(sugg) = suggesstion_snippet(cx, expr) {
diag.span_suggestion(expr.span, "try", sugg, Applicability::MachineApplicable);
}
},
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::path_to_local;
use clippy_utils::source::snippet_opt;
-use clippy_utils::visitors::{expr_visitor, is_local_used};
-use rustc_errors::Applicability;
+use clippy_utils::ty::needs_ordered_drop;
+use clippy_utils::visitors::{expr_visitor, expr_visitor_no_bodies, is_local_used};
+use rustc_errors::{Applicability, MultiSpan};
use rustc_hir::intravisit::Visitor;
-use rustc_hir::{Block, Expr, ExprKind, HirId, Local, LocalSource, MatchSource, Node, Pat, PatKind, Stmt, StmtKind};
+use rustc_hir::{
+ BindingAnnotation, Block, Expr, ExprKind, HirId, Local, LocalSource, MatchSource, Node, Pat, PatKind, Stmt,
+ StmtKind,
+};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::Span;
seen
}
+fn contains_let(cond: &Expr<'_>) -> bool {
+ let mut seen = false;
+ expr_visitor_no_bodies(|expr| {
+ if let ExprKind::Let(_) = expr.kind {
+ seen = true;
+ }
+
+ !seen
+ })
+ .visit_expr(cond);
+
+ seen
+}
+
+fn stmt_needs_ordered_drop(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool {
+ let StmtKind::Local(local) = stmt.kind else { return false };
+ !local.pat.walk_short(|pat| {
+ if let PatKind::Binding(.., None) = pat.kind {
+ !needs_ordered_drop(cx, cx.typeck_results().pat_ty(pat))
+ } else {
+ true
+ }
+ })
+}
+
#[derive(Debug)]
struct LocalAssign {
lhs_id: HirId,
local_stmt_id: HirId,
block: &'tcx Block<'tcx>,
) -> Option<Usage<'tcx>> {
+ let significant_drop = needs_ordered_drop(cx, cx.typeck_results().node_type(binding_id));
+
block
.stmts
.iter()
.skip_while(|stmt| stmt.hir_id != local_stmt_id)
.skip(1)
+ .take_while(|stmt| !significant_drop || !stmt_needs_ordered_drop(cx, stmt))
.find(|&stmt| is_local_used(cx, stmt, binding_id))
.and_then(|stmt| match stmt.kind {
StmtKind::Expr(expr) => Some(Usage {
match usage.expr.kind {
ExprKind::Assign(..) => {
let assign = LocalAssign::new(cx, usage.expr, binding_id)?;
+ let mut msg_span = MultiSpan::from_spans(vec![local_stmt.span, assign.span]);
+ msg_span.push_span_label(local_stmt.span, "created here");
+ msg_span.push_span_label(assign.span, "initialised here");
span_lint_and_then(
cx,
NEEDLESS_LATE_INIT,
- local_stmt.span,
- "unneeded late initalization",
+ msg_span,
+ "unneeded late initialization",
|diag| {
diag.tool_only_span_suggestion(
local_stmt.span,
},
);
},
- ExprKind::If(_, then_expr, Some(else_expr)) => {
+ ExprKind::If(cond, then_expr, Some(else_expr)) if !contains_let(cond) => {
let (applicability, suggestions) = assignment_suggestions(cx, binding_id, [then_expr, else_expr])?;
span_lint_and_then(
cx,
NEEDLESS_LATE_INIT,
local_stmt.span,
- "unneeded late initalization",
+ "unneeded late initialization",
|diag| {
diag.tool_only_span_suggestion(local_stmt.span, "remove the local", String::new(), applicability);
cx,
NEEDLESS_LATE_INIT,
local_stmt.span,
- "unneeded late initalization",
+ "unneeded late initialization",
|diag| {
diag.tool_only_span_suggestion(local_stmt.span, "remove the local", String::new(), applicability);
impl<'tcx> LateLintPass<'tcx> for NeedlessLateInit {
fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) {
let mut parents = cx.tcx.hir().parent_iter(local.hir_id);
-
if_chain! {
if let Local {
init: None,
pat: &Pat {
- kind: PatKind::Binding(_, binding_id, _, None),
+ kind: PatKind::Binding(BindingAnnotation::Unannotated, binding_id, _, None),
..
},
source: LocalSource::Normal,
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
if let hir::ItemKind::Impl(hir::Impl {
of_trait: None,
- ref generics,
+ generics,
self_ty: impl_self_ty,
items,
..
if interned_name.chars().any(char::is_uppercase) {
return;
}
- if interned_name.chars().all(|c| c.is_digit(10) || c == '_') {
+ if interned_name.chars().all(|c| c.is_ascii_digit() || c == '_') {
span_lint(
self.0.cx,
JUST_UNDERSCORES_AND_DIGITS,
///
/// ### Known problems
/// The actual meaning can be the intended one. `\x00` can be used in these
- /// cases to be unambigious.
+ /// cases to be unambiguous.
///
/// The lint does not trigger for format strings in `print!()`, `write!()`
/// and friends since the string is already preprocessed when Clippy lints
use std::collections::VecDeque;
use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_lint_allowed;
use itertools::{izip, Itertools};
use rustc_ast::{walk_list, Label, Mutability};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData};
-use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
+use rustc_hir::intravisit::{walk_expr, walk_stmt, FnKind, Visitor};
use rustc_hir::{
Arm, Block, Body, Expr, ExprKind, Guard, HirId, ImplicitSelfKind, Let, Local, Pat, PatKind, Path, PathSegment,
QPath, Stmt, StmtKind, TyKind, UnOp,
/// and the assigned variables are also only in recursion, it is useless.
///
/// ### Known problems
+ /// Too many code paths in the linting code are currently untested and prone to produce false
+ /// positives or are prone to have performance implications.
+ ///
/// In some cases, this would not catch all useless arguments.
///
/// ```rust
/// ```
#[clippy::version = "1.60.0"]
pub ONLY_USED_IN_RECURSION,
- complexity,
+ nursery,
"arguments that is only used in recursion can be removed"
}
declare_lint_pass!(OnlyUsedInRecursion => [ONLY_USED_IN_RECURSION]);
_: Span,
id: HirId,
) {
+ if is_lint_allowed(cx, ONLY_USED_IN_RECURSION, id) {
+ return;
+ }
if let FnKind::ItemFn(ident, ..) | FnKind::Method(ident, ..) = kind {
let def_id = id.owner.to_def_id();
let data = cx.tcx.def_path(def_id).data;
is_method: matches!(kind, FnKind::Method(..)),
has_self,
ty_res,
- ty_ctx: cx.tcx,
+ tcx: cx.tcx,
+ visited_exprs: FxHashSet::default(),
};
visitor.visit_expr(&body.value);
}
pub fn is_primitive(ty: Ty<'_>) -> bool {
- match ty.kind() {
- ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => true,
- ty::Ref(_, t, _) => is_primitive(*t),
- _ => false,
- }
+ let ty = ty.peel_refs();
+ ty.is_primitive() || ty.is_str()
}
pub fn is_array(ty: Ty<'_>) -> bool {
- match ty.kind() {
- ty::Array(..) | ty::Slice(..) => true,
- ty::Ref(_, t, _) => is_array(*t),
- _ => false,
- }
+ let ty = ty.peel_refs();
+ ty.is_array() || ty.is_array_slice()
}
/// This builds the graph of side effect.
is_method: bool,
has_self: bool,
ty_res: &'tcx TypeckResults<'tcx>,
- ty_ctx: TyCtxt<'tcx>,
+ tcx: TyCtxt<'tcx>,
+ visited_exprs: FxHashSet<HirId>,
}
impl<'tcx> Visitor<'tcx> for SideEffectVisit<'tcx> {
- fn visit_block(&mut self, b: &'tcx Block<'tcx>) {
- b.stmts.iter().for_each(|stmt| {
- self.visit_stmt(stmt);
- self.ret_vars.clear();
- });
- walk_list!(self, visit_expr, b.expr);
- }
-
fn visit_stmt(&mut self, s: &'tcx Stmt<'tcx>) {
match s.kind {
StmtKind::Local(Local {
pat, init: Some(init), ..
}) => {
self.visit_pat_expr(pat, init, false);
- self.ret_vars.clear();
},
- StmtKind::Item(i) => {
- let item = self.ty_ctx.hir().item(i);
- self.visit_item(item);
- self.ret_vars.clear();
- },
- StmtKind::Expr(e) | StmtKind::Semi(e) => {
- self.visit_expr(e);
- self.ret_vars.clear();
+ StmtKind::Item(_) | StmtKind::Expr(_) | StmtKind::Semi(_) => {
+ walk_stmt(self, s);
},
StmtKind::Local(_) => {},
}
+ self.ret_vars.clear();
}
fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
+ if !self.visited_exprs.insert(ex.hir_id) {
+ return;
+ }
match ex.kind {
ExprKind::Array(exprs) | ExprKind::Tup(exprs) => {
self.ret_vars = exprs
ExprKind::Match(expr, arms, _) => self.visit_match(expr, arms),
// since analysing the closure is not easy, just set all variables in it to side-effect
ExprKind::Closure(_, _, body_id, _, _) => {
- let body = self.ty_ctx.hir().body(body_id);
+ let body = self.tcx.hir().body(body_id);
self.visit_body(body);
let vars = std::mem::take(&mut self.ret_vars);
self.add_side_effect(vars);
/// A struct containing information about occurrences of the
/// `if let Some(..) = .. else` construct that this lint detects.
-struct OptionIfLetElseOccurence {
+struct OptionIfLetElseOccurrence {
option: String,
method_sugg: String,
some_expr: String,
}
/// If this expression is the option if let/else construct we're detecting, then
-/// this function returns an `OptionIfLetElseOccurence` struct with details if
+/// this function returns an `OptionIfLetElseOccurrence` struct with details if
/// this construct is found, or None if this construct is not found.
-fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<OptionIfLetElseOccurence> {
+fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<OptionIfLetElseOccurrence> {
if_chain! {
if !expr.span.from_expansion(); // Don't lint macros, because it behaves weirdly
if !in_constant(cx, expr.hir_id);
}
}
}
- Some(OptionIfLetElseOccurence {
+ Some(OptionIfLetElseOccurrence {
option: format_option_in_sugg(cx, cond_expr, as_ref, as_mut),
method_sugg: method_sugg.to_string(),
some_expr: format!("|{}{}| {}", capture_mut, capture_name, Sugg::hir_with_macro_callsite(cx, some_body, "..")),
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
use clippy_utils::source::snippet_opt;
use clippy_utils::ty::expr_sig;
+use clippy_utils::visitors::contains_unsafe_block;
use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, path_def_id, path_to_local, paths};
use if_chain::if_chain;
use rustc_errors::{Applicability, MultiSpan};
use rustc_hir::hir_id::HirIdMap;
use rustc_hir::intravisit::{walk_expr, Visitor};
use rustc_hir::{
- self as hir, AnonConst, BinOpKind, BindingAnnotation, Body, Expr, ExprKind, FnDecl, FnRetTy, GenericArg,
+ self as hir, AnonConst, BinOpKind, BindingAnnotation, Body, Expr, ExprKind, FnRetTy, FnSig, GenericArg,
ImplItemKind, ItemKind, Lifetime, LifetimeName, Mutability, Node, Param, ParamName, PatKind, QPath, TraitFn,
- TraitItem, TraitItemKind, TyKind,
+ TraitItem, TraitItemKind, TyKind, Unsafety,
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::hir::nested_filter;
declare_clippy_lint! {
/// ### What it does
- /// This lint checks for functions that take immutable
- /// references and return mutable ones.
+ /// This lint checks for functions that take immutable references and return
+ /// mutable ones. This will not trigger if no unsafe code exists as there
+ /// are multiple safe functions which will do this transformation
+ ///
+ /// To be on the conservative side, if there's at least one mutable
+ /// reference with the output lifetime, this lint will not trigger.
///
/// ### Why is this bad?
- /// This is trivially unsound, as one can create two
- /// mutable references from the same (immutable!) source.
- /// This [error](https://github.com/rust-lang/rust/issues/39465)
- /// actually lead to an interim Rust release 1.15.1.
+ /// Creating a mutable reference which can be repeatably derived from an
+ /// immutable reference is unsound as it allows creating multiple live
+ /// mutable references to the same object.
+ ///
+ /// This [error](https://github.com/rust-lang/rust/issues/39465) actually
+ /// lead to an interim Rust release 1.15.1.
///
/// ### Known problems
- /// To be on the conservative side, if there's at least one
- /// mutable reference with the output lifetime, this lint will not trigger.
- /// In practice, this case is unlikely anyway.
+ /// This pattern is used by memory allocators to allow allocating multiple
+ /// objects while returning mutable references to each one. So long as
+ /// different mutable references are returned each time such a function may
+ /// be safe.
///
/// ### Example
/// ```ignore
return;
}
- check_mut_from_ref(cx, sig.decl);
+ check_mut_from_ref(cx, sig, None);
for arg in check_fn_args(
cx,
cx.tcx.fn_sig(item.def_id).skip_binder().inputs(),
fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
let hir = cx.tcx.hir();
let mut parents = hir.parent_iter(body.value.hir_id);
- let (item_id, decl, is_trait_item) = match parents.next() {
+ let (item_id, sig, is_trait_item) = match parents.next() {
Some((_, Node::Item(i))) => {
if let ItemKind::Fn(sig, ..) = &i.kind {
- (i.def_id, sig.decl, false)
+ (i.def_id, sig, false)
} else {
return;
}
return;
}
if let ImplItemKind::Fn(sig, _) = &i.kind {
- (i.def_id, sig.decl, false)
+ (i.def_id, sig, false)
} else {
return;
}
},
Some((_, Node::TraitItem(i))) => {
if let TraitItemKind::Fn(sig, _) = &i.kind {
- (i.def_id, sig.decl, true)
+ (i.def_id, sig, true)
} else {
return;
}
_ => return,
};
- check_mut_from_ref(cx, decl);
+ check_mut_from_ref(cx, sig, Some(body));
+ let decl = sig.decl;
let sig = cx.tcx.fn_sig(item_id).skip_binder();
let lint_args: Vec<_> = check_fn_args(cx, sig.inputs(), decl.inputs, body.params)
.filter(|arg| !is_trait_item || arg.mutability() == Mutability::Not)
})
}
-fn check_mut_from_ref(cx: &LateContext<'_>, decl: &FnDecl<'_>) {
- if let FnRetTy::Return(ty) = decl.output {
- if let Some((out, Mutability::Mut, _)) = get_rptr_lm(ty) {
- let mut immutables = vec![];
- for (_, mutbl, argspan) in decl
- .inputs
- .iter()
- .filter_map(get_rptr_lm)
- .filter(|&(lt, _, _)| lt.name == out.name)
- {
- if mutbl == Mutability::Mut {
- return;
- }
- immutables.push(argspan);
- }
- if immutables.is_empty() {
- return;
- }
+fn check_mut_from_ref<'tcx>(cx: &LateContext<'tcx>, sig: &FnSig<'_>, body: Option<&'tcx Body<'_>>) {
+ if let FnRetTy::Return(ty) = sig.decl.output
+ && let Some((out, Mutability::Mut, _)) = get_rptr_lm(ty)
+ {
+ let args: Option<Vec<_>> = sig
+ .decl
+ .inputs
+ .iter()
+ .filter_map(get_rptr_lm)
+ .filter(|&(lt, _, _)| lt.name == out.name)
+ .map(|(_, mutability, span)| (mutability == Mutability::Not).then(|| span))
+ .collect();
+ if let Some(args) = args
+ && !args.is_empty()
+ && body.map_or(true, |body| {
+ sig.header.unsafety == Unsafety::Unsafe || contains_unsafe_block(cx, &body.value)
+ })
+ {
span_lint_and_then(
cx,
MUT_FROM_REF,
ty.span,
"mutable borrow from immutable input(s)",
|diag| {
- let ms = MultiSpan::from_spans(immutables);
+ let ms = MultiSpan::from_spans(args);
diag.span_note(ms, "immutable borrow here");
},
);
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_help;
+use rustc_ast::ast::{Item, ItemKind, VisibilityKind};
+use rustc_lint::{EarlyContext, EarlyLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+ /// ### What it does
+ ///
+ /// Restricts the usage of `pub use ...`
+ ///
+ /// ### Why is this bad?
+ ///
+ /// `pub use` is usually fine, but a project may wish to limit `pub use` instances to prevent
+ /// unintentional exports or to encourage placing exported items directly in public modules
+ ///
+ /// ### Example
+ /// ```rust
+ /// pub mod outer {
+ /// mod inner {
+ /// pub struct Test {}
+ /// }
+ /// pub use inner::Test;
+ /// }
+ ///
+ /// use outer::Test;
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// pub mod outer {
+ /// pub struct Test {}
+ /// }
+ ///
+ /// use outer::Test;
+ /// ```
+ #[clippy::version = "1.62.0"]
+ pub PUB_USE,
+ restriction,
+ "restricts the usage of `pub use`"
+}
+declare_lint_pass!(PubUse => [PUB_USE]);
+
+impl EarlyLintPass for PubUse {
+ fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
+ if let ItemKind::Use(_) = item.kind &&
+ let VisibilityKind::Public = item.vis.kind {
+ span_lint_and_help(
+ cx,
+ PUB_USE,
+ item.span,
+ "using `pub use`",
+ None,
+ "move the exported item to a public module instead",
+ );
+ }
+ }
+}
use clippy_utils::diagnostics::span_lint_and_then;
use rustc_errors::Applicability;
+use rustc_hir::def::{DefKind, Res};
use rustc_hir::{Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::def_id::CRATE_DEF_ID;
+use rustc_span::hygiene::MacroKind;
declare_clippy_lint! {
/// ### What it does
impl<'tcx> LateLintPass<'tcx> for RedundantPubCrate {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
- if cx.tcx.visibility(item.def_id) == ty::Visibility::Restricted(CRATE_DEF_ID.to_def_id()) {
- if !cx.access_levels.is_exported(item.def_id) && self.is_exported.last() == Some(&false) {
+ if_chain! {
+ if cx.tcx.visibility(item.def_id) == ty::Visibility::Restricted(CRATE_DEF_ID.to_def_id());
+ if !cx.access_levels.is_exported(item.def_id) && self.is_exported.last() == Some(&false);
+ if is_not_macro_export(item);
+ then {
let span = item.span.with_hi(item.ident.span.hi());
let descr = cx.tcx.def_kind(item.def_id).descr(item.def_id.to_def_id());
span_lint_and_then(
}
}
}
+
+fn is_not_macro_export<'tcx>(item: &'tcx Item<'tcx>) -> bool {
+ if let ItemKind::Use(path, _) = item.kind {
+ if let Res::Def(DefKind::Macro(MacroKind::Bang), _) = path.res {
+ return false;
+ }
+ } else if let ItemKind::Macro(..) = item.kind {
+ return false;
+ }
+
+ true
+}
--- /dev/null
+// This file is managed by `cargo dev rename_lint`. Prefer using that when possible.
+
+#[rustfmt::skip]
+pub static RENAMED_LINTS: &[(&str, &str)] = &[
+ ("clippy::block_in_if_condition_expr", "clippy::blocks_in_if_conditions"),
+ ("clippy::block_in_if_condition_stmt", "clippy::blocks_in_if_conditions"),
+ ("clippy::box_vec", "clippy::box_collection"),
+ ("clippy::const_static_lifetime", "clippy::redundant_static_lifetimes"),
+ ("clippy::cyclomatic_complexity", "clippy::cognitive_complexity"),
+ ("clippy::disallowed_method", "clippy::disallowed_methods"),
+ ("clippy::disallowed_type", "clippy::disallowed_types"),
+ ("clippy::for_loop_over_option", "clippy::for_loops_over_fallibles"),
+ ("clippy::for_loop_over_result", "clippy::for_loops_over_fallibles"),
+ ("clippy::identity_conversion", "clippy::useless_conversion"),
+ ("clippy::if_let_some_result", "clippy::match_result_ok"),
+ ("clippy::new_without_default_derive", "clippy::new_without_default"),
+ ("clippy::option_and_then_some", "clippy::bind_instead_of_map"),
+ ("clippy::option_expect_used", "clippy::expect_used"),
+ ("clippy::option_map_unwrap_or", "clippy::map_unwrap_or"),
+ ("clippy::option_map_unwrap_or_else", "clippy::map_unwrap_or"),
+ ("clippy::option_unwrap_used", "clippy::unwrap_used"),
+ ("clippy::ref_in_deref", "clippy::needless_borrow"),
+ ("clippy::result_expect_used", "clippy::expect_used"),
+ ("clippy::result_map_unwrap_or_else", "clippy::map_unwrap_or"),
+ ("clippy::result_unwrap_used", "clippy::unwrap_used"),
+ ("clippy::single_char_push_str", "clippy::single_char_add_str"),
+ ("clippy::stutter", "clippy::module_name_repetitions"),
+ ("clippy::to_string_in_display", "clippy::recursive_format_impl"),
+ ("clippy::zero_width_space", "clippy::invisible_characters"),
+ ("clippy::drop_bounds", "drop_bounds"),
+ ("clippy::into_iter_on_array", "array_into_iter"),
+ ("clippy::invalid_atomic_ordering", "invalid_atomic_ordering"),
+ ("clippy::invalid_ref", "invalid_value"),
+ ("clippy::mem_discriminant_non_enum", "enum_intrinsics_non_enums"),
+ ("clippy::panic_params", "non_fmt_panics"),
+ ("clippy::temporary_cstring_as_ptr", "temporary_cstring_as_ptr"),
+ ("clippy::unknown_clippy_lints", "unknown_lints"),
+ ("clippy::unused_label", "unused_labels"),
+];
let mut map = FxHashMap::<Res, ExistingName>::default();
for id in cx.tcx.hir().items() {
- if !matches!(cx.tcx.hir().def_kind(id.def_id), DefKind::Impl) {
- continue;
- }
-
- let item = cx.tcx.hir().item(id);
- if let ItemKind::Impl(Impl {
- items,
- of_trait,
- self_ty,
- ..
- }) = &item.kind
+ if matches!(cx.tcx.hir().def_kind(id.def_id), DefKind::Impl)
+ && let item = cx.tcx.hir().item(id)
+ && let ItemKind::Impl(Impl {
+ items,
+ of_trait,
+ self_ty,
+ ..
+ }) = &item.kind
+ && let TyKind::Path(QPath::Resolved(_, Path { res, .. })) = self_ty.kind
{
- if let TyKind::Path(QPath::Resolved(_, Path { res, .. })) = self_ty.kind {
- if !map.contains_key(res) {
- map.insert(
- *res,
- ExistingName {
- impl_methods: BTreeMap::new(),
- trait_methods: BTreeMap::new(),
- },
- );
- }
- let existing_name = map.get_mut(res).unwrap();
-
- match of_trait {
- Some(trait_ref) => {
- let mut methods_in_trait: BTreeSet<Symbol> = if_chain! {
- if let Some(Node::TraitRef(TraitRef { path, .. })) =
- cx.tcx.hir().find(trait_ref.hir_ref_id);
- if let Res::Def(DefKind::Trait, did) = path.res;
- then{
- // FIXME: if
- // `rustc_middle::ty::assoc::AssocItems::items` is public,
- // we can iterate its keys instead of `in_definition_order`,
- // which's more efficient
- cx.tcx
- .associated_items(did)
- .in_definition_order()
- .filter(|assoc_item| {
- matches!(assoc_item.kind, AssocKind::Fn)
- })
- .map(|assoc_item| assoc_item.name)
- .collect()
- }else{
- BTreeSet::new()
- }
- };
-
- let mut check_trait_method = |method_name: Symbol, trait_method_span: Span| {
- if let Some(impl_span) = existing_name.impl_methods.get(&method_name) {
- span_lint_and_then(
- cx,
- SAME_NAME_METHOD,
- *impl_span,
- "method's name is the same as an existing method in a trait",
- |diag| {
- diag.span_note(
- trait_method_span,
- &format!("existing `{}` defined here", method_name),
- );
- },
- );
- }
- if let Some(v) = existing_name.trait_methods.get_mut(&method_name) {
- v.push(trait_method_span);
- } else {
- existing_name.trait_methods.insert(method_name, vec![trait_method_span]);
- }
- };
+ if !map.contains_key(res) {
+ map.insert(
+ *res,
+ ExistingName {
+ impl_methods: BTreeMap::new(),
+ trait_methods: BTreeMap::new(),
+ },
+ );
+ }
+ let existing_name = map.get_mut(res).unwrap();
- for impl_item_ref in (*items).iter().filter(|impl_item_ref| {
- matches!(impl_item_ref.kind, rustc_hir::AssocItemKind::Fn { .. })
- }) {
- let method_name = impl_item_ref.ident.name;
- methods_in_trait.remove(&method_name);
- check_trait_method(method_name, impl_item_ref.span);
+ match of_trait {
+ Some(trait_ref) => {
+ let mut methods_in_trait: BTreeSet<Symbol> = if_chain! {
+ if let Some(Node::TraitRef(TraitRef { path, .. })) =
+ cx.tcx.hir().find(trait_ref.hir_ref_id);
+ if let Res::Def(DefKind::Trait, did) = path.res;
+ then{
+ // FIXME: if
+ // `rustc_middle::ty::assoc::AssocItems::items` is public,
+ // we can iterate its keys instead of `in_definition_order`,
+ // which's more efficient
+ cx.tcx
+ .associated_items(did)
+ .in_definition_order()
+ .filter(|assoc_item| {
+ matches!(assoc_item.kind, AssocKind::Fn)
+ })
+ .map(|assoc_item| assoc_item.name)
+ .collect()
+ }else{
+ BTreeSet::new()
}
+ };
- for method_name in methods_in_trait {
- check_trait_method(method_name, item.span);
+ let mut check_trait_method = |method_name: Symbol, trait_method_span: Span| {
+ if let Some(impl_span) = existing_name.impl_methods.get(&method_name) {
+ span_lint_and_then(
+ cx,
+ SAME_NAME_METHOD,
+ *impl_span,
+ "method's name is the same as an existing method in a trait",
+ |diag| {
+ diag.span_note(
+ trait_method_span,
+ &format!("existing `{}` defined here", method_name),
+ );
+ },
+ );
}
- },
- None => {
- for impl_item_ref in (*items).iter().filter(|impl_item_ref| {
- matches!(impl_item_ref.kind, rustc_hir::AssocItemKind::Fn { .. })
- }) {
- let method_name = impl_item_ref.ident.name;
- let impl_span = impl_item_ref.span;
- if let Some(trait_spans) = existing_name.trait_methods.get(&method_name) {
- span_lint_and_then(
- cx,
- SAME_NAME_METHOD,
- impl_span,
- "method's name is the same as an existing method in a trait",
- |diag| {
- // TODO should we `span_note` on every trait?
- // iterate on trait_spans?
- diag.span_note(
- trait_spans[0],
- &format!("existing `{}` defined here", method_name),
- );
- },
- );
- }
- existing_name.impl_methods.insert(method_name, impl_span);
+ if let Some(v) = existing_name.trait_methods.get_mut(&method_name) {
+ v.push(trait_method_span);
+ } else {
+ existing_name.trait_methods.insert(method_name, vec![trait_method_span]);
}
- },
- }
+ };
+
+ for impl_item_ref in (*items).iter().filter(|impl_item_ref| {
+ matches!(impl_item_ref.kind, rustc_hir::AssocItemKind::Fn { .. })
+ }) {
+ let method_name = impl_item_ref.ident.name;
+ methods_in_trait.remove(&method_name);
+ check_trait_method(method_name, impl_item_ref.span);
+ }
+
+ for method_name in methods_in_trait {
+ check_trait_method(method_name, item.span);
+ }
+ },
+ None => {
+ for impl_item_ref in (*items).iter().filter(|impl_item_ref| {
+ matches!(impl_item_ref.kind, rustc_hir::AssocItemKind::Fn { .. })
+ }) {
+ let method_name = impl_item_ref.ident.name;
+ let impl_span = impl_item_ref.span;
+ if let Some(trait_spans) = existing_name.trait_methods.get(&method_name) {
+ span_lint_and_then(
+ cx,
+ SAME_NAME_METHOD,
+ impl_span,
+ "method's name is the same as an existing method in a trait",
+ |diag| {
+ // TODO should we `span_note` on every trait?
+ // iterate on trait_spans?
+ diag.span_note(
+ trait_spans[0],
+ &format!("existing `{}` defined here", method_name),
+ );
+ },
+ );
+ }
+ existing_name.impl_methods.insert(method_name, impl_span);
+ }
+ },
}
}
}
declare_clippy_lint! {
/// ### What it does
/// When sorting primitive values (integers, bools, chars, as well
- /// as arrays, slices, and tuples of such items), it is better to
+ /// as arrays, slices, and tuples of such items), it is typically better to
/// use an unstable sort than a stable sort.
///
/// ### Why is this bad?
- /// Using a stable sort consumes more memory and cpu cycles. Because
- /// values which compare equal are identical, preserving their
+ /// Typically, using a stable sort consumes more memory and cpu cycles.
+ /// Because values which compare equal are identical, preserving their
/// relative order (the guarantee that a stable sort provides) means
/// nothing, while the extra costs still apply.
///
+ /// ### Known problems
+ ///
+ /// As pointed out in
+ /// [issue #8241](https://github.com/rust-lang/rust-clippy/issues/8241),
+ /// a stable sort can instead be significantly faster for certain scenarios
+ /// (eg. when a sorted vector is extended with new data and resorted).
+ ///
+ /// For more information and benchmarking results, please refer to the
+ /// issue linked above.
+ ///
/// ### Example
/// ```rust
/// let mut vec = vec![2, 1, 3];
/// ```
#[clippy::version = "1.47.0"]
pub STABLE_SORT_PRIMITIVE,
- perf,
+ pedantic,
"use of sort() when sort_unstable() is equivalent"
}
Applicability::MachineApplicable,
);
diag.note(
- "an unstable sort would perform faster without any observable difference for this data type",
+ "an unstable sort typically performs faster without any observable difference for this data type",
);
},
);
use clippy_utils::{peel_blocks, SpanlessEq};
use if_chain::if_chain;
use rustc_errors::Applicability;
+use rustc_hir::def_id::DefId;
use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, LangItem, QPath};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
}
}
}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Warns about calling `str::trim` (or variants) before `str::split_whitespace`.
+ ///
+ /// ### Why is this bad?
+ /// `split_whitespace` already ignores leading and trailing whitespace.
+ ///
+ /// ### Example
+ /// ```rust
+ /// " A B C ".trim().split_whitespace();
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// " A B C ".split_whitespace();
+ /// ```
+ #[clippy::version = "1.62.0"]
+ pub TRIM_SPLIT_WHITESPACE,
+ style,
+ "using `str::trim()` or alike before `str::split_whitespace`"
+}
+declare_lint_pass!(TrimSplitWhitespace => [TRIM_SPLIT_WHITESPACE]);
+
+impl<'tcx> LateLintPass<'tcx> for TrimSplitWhitespace {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) {
+ let tyckres = cx.typeck_results();
+ if_chain! {
+ if let ExprKind::MethodCall(path, [split_recv], split_ws_span) = expr.kind;
+ if path.ident.name == sym!(split_whitespace);
+ if let Some(split_ws_def_id) = tyckres.type_dependent_def_id(expr.hir_id);
+ if cx.tcx.is_diagnostic_item(sym::str_split_whitespace, split_ws_def_id);
+ if let ExprKind::MethodCall(path, [_trim_recv], trim_span) = split_recv.kind;
+ if let trim_fn_name @ ("trim" | "trim_start" | "trim_end") = path.ident.name.as_str();
+ if let Some(trim_def_id) = tyckres.type_dependent_def_id(split_recv.hir_id);
+ if is_one_of_trim_diagnostic_items(cx, trim_def_id);
+ then {
+ span_lint_and_sugg(
+ cx,
+ TRIM_SPLIT_WHITESPACE,
+ trim_span.with_hi(split_ws_span.lo()),
+ &format!("found call to `str::{}` before `str::split_whitespace`", trim_fn_name),
+ &format!("remove `{}()`", trim_fn_name),
+ String::new(),
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+ }
+}
+
+fn is_one_of_trim_diagnostic_items(cx: &LateContext<'_>, trim_def_id: DefId) -> bool {
+ cx.tcx.is_diagnostic_item(sym::str_trim, trim_def_id)
+ || cx.tcx.is_diagnostic_item(sym::str_trim_start, trim_def_id)
+ || cx.tcx.is_diagnostic_item(sym::str_trim_end, trim_def_id)
+}
// IdentIter, then the output of this function will be almost always be correct
// in practice.
//
- // If it turns out that problematic cases are more prelavent than we assume,
+ // If it turns out that problematic cases are more prevalent than we assume,
// then we should be able to change this function to do the correct traversal,
// without needing to change the rest of the code.
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::Span;
+use std::fmt::Write as _;
declare_clippy_lint! {
/// ### What it does
/// ```
#[clippy::version = "1.38.0"]
pub TYPE_REPETITION_IN_BOUNDS,
- pedantic,
+ nursery,
"Types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`"
}
/// ```
#[clippy::version = "1.47.0"]
pub TRAIT_DUPLICATION_IN_BOUNDS,
- pedantic,
+ nursery,
"Check if the same trait bounds are specified twice during a function declaration"
}
for b in v.iter() {
if let GenericBound::Trait(ref poly_trait_ref, _) = b {
let path = &poly_trait_ref.trait_ref.path;
- hint_string.push_str(&format!(
+ let _ = write!(hint_string,
" {} +",
snippet_with_applicability(cx, path.span, "..", &mut applicability)
- ));
+ );
}
}
for b in p.bounds.iter() {
if let GenericBound::Trait(ref poly_trait_ref, _) = b {
let path = &poly_trait_ref.trait_ref.path;
- hint_string.push_str(&format!(
+ let _ = write!(hint_string,
" {} +",
snippet_with_applicability(cx, path.span, "..", &mut applicability)
- ));
+ );
}
}
hint_string.truncate(hint_string.len() - 2);
);
}
else {
- trait_resolutions_direct.push((res_where, span_where))
+ trait_resolutions_direct.push((res_where, span_where));
}
}
}
use clippy_utils::last_path_segment;
use clippy_utils::source::snippet;
-use clippy_utils::ty::is_normalizable;
use if_chain::if_chain;
use rustc_hir::{Expr, GenericArg, QPath, TyKind};
use rustc_lint::LateContext;
-use rustc_middle::ty::{self, cast::CastKind, Ty};
+use rustc_middle::ty::{cast::CastKind, Ty};
use rustc_span::DUMMY_SP;
use rustc_typeck::check::{cast::CastCheck, FnCtxt, Inherited};
// check if the component types of the transmuted collection and the result have different ABI,
// size or alignment
pub(super) fn is_layout_incompatible<'tcx>(cx: &LateContext<'tcx>, from: Ty<'tcx>, to: Ty<'tcx>) -> bool {
- let empty_param_env = ty::ParamEnv::empty();
- // check if `from` and `to` are normalizable to avoid ICE (#4968)
- if !(is_normalizable(cx, empty_param_env, from) && is_normalizable(cx, empty_param_env, to)) {
- return false;
- }
- let from_ty_layout = cx.tcx.layout_of(empty_param_env.and(from));
- let to_ty_layout = cx.tcx.layout_of(empty_param_env.and(to));
- if let (Ok(from_layout), Ok(to_layout)) = (from_ty_layout, to_ty_layout) {
- from_layout.size != to_layout.size || from_layout.align != to_layout.align || from_layout.abi != to_layout.abi
+ if let Ok(from) = cx.tcx.try_normalize_erasing_regions(cx.param_env, from)
+ && let Ok(to) = cx.tcx.try_normalize_erasing_regions(cx.param_env, to)
+ && let Ok(from_layout) = cx.tcx.layout_of(cx.param_env.and(from))
+ && let Ok(to_layout) = cx.tcx.layout_of(cx.param_env.and(to))
+ {
+ from_layout.size != to_layout.size || from_layout.align.abi != to_layout.align.abi
} else {
// no idea about layout, so don't lint
false
let res = check.do_check(&fn_ctxt);
// do_check's documentation says that it might return Ok and create
- // errors in the fcx instead of returing Err in some cases. Those cases
+ // errors in the fcx instead of returning Err in some cases. Those cases
// should be filtered out before getting here.
assert!(
!fn_ctxt.errors_reported_since_creation(),
fn check_fn_decl(&mut self, cx: &LateContext<'_>, decl: &FnDecl<'_>, context: CheckTyContext) {
// Ignore functions in trait implementations as they are usually forced by the trait definition.
//
- // FIXME: idially we would like to warn *if the compicated type can be simplified*, but it's hard to
- // check.
+ // FIXME: ideally we would like to warn *if the complicated type can be simplified*, but it's hard
+ // to check.
if context.is_in_trait_impl {
return;
}
.array_windows::<2>()
.rev()
.map_while(|[start, end]| {
- src.get(start.to_usize() - offset..end.to_usize() - offset)
- .map(|text| (start.to_usize(), text.trim_start()))
+ let start = start.to_usize() - offset;
+ let end = end.to_usize() - offset;
+ src.get(start..end).map(|text| (start, text.trim_start()))
})
.filter(|(_, text)| !text.is_empty());
let (mut line_start, mut line) = (line_start, line);
loop {
if line.starts_with("/*") {
- let src = src[line_start..line_starts.last().unwrap().to_usize()].trim_start();
+ let src = src[line_start..line_starts.last().unwrap().to_usize() - offset].trim_start();
let mut tokens = tokenize(src);
return src[..tokens.next().unwrap().len]
.to_ascii_uppercase()
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet_with_macro_callsite;
+use clippy_utils::visitors::for_each_value_source;
+use core::ops::ControlFlow;
use rustc_errors::Applicability;
-use rustc_hir::{Stmt, StmtKind};
+use rustc_hir::{Expr, ExprKind, PatKind, Stmt, StmtKind};
use rustc_lint::{LateContext, LintContext};
use rustc_middle::lint::in_external_macro;
+use rustc_middle::ty::{self, Ty, TypeFoldable, TypeVisitor};
use super::LET_UNIT_VALUE;
pub(super) fn check(cx: &LateContext<'_>, stmt: &Stmt<'_>) {
- if let StmtKind::Local(local) = stmt.kind {
- if cx.typeck_results().pat_ty(local.pat).is_unit() {
- if in_external_macro(cx.sess(), stmt.span) || local.pat.span.from_expansion() {
- return;
+ if let StmtKind::Local(local) = stmt.kind
+ && let Some(init) = local.init
+ && !local.pat.span.from_expansion()
+ && !in_external_macro(cx.sess(), stmt.span)
+ && cx.typeck_results().pat_ty(local.pat).is_unit()
+ {
+ let needs_inferred = for_each_value_source(init, &mut |e| if needs_inferred_result_ty(cx, e) {
+ ControlFlow::Continue(())
+ } else {
+ ControlFlow::Break(())
+ }).is_continue();
+
+ if needs_inferred {
+ if !matches!(local.pat.kind, PatKind::Wild) {
+ span_lint_and_then(
+ cx,
+ LET_UNIT_VALUE,
+ stmt.span,
+ "this let-binding has unit value",
+ |diag| {
+ diag.span_suggestion(
+ local.pat.span,
+ "use a wild (`_`) binding",
+ "_",
+ Applicability::MaybeIncorrect, // snippet
+ );
+ },
+ );
}
+ } else {
span_lint_and_then(
cx,
LET_UNIT_VALUE,
}
}
}
+
+fn needs_inferred_result_ty(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
+ let id = match e.kind {
+ ExprKind::Call(
+ Expr {
+ kind: ExprKind::Path(ref path),
+ hir_id,
+ ..
+ },
+ _,
+ ) => cx.qpath_res(path, *hir_id).opt_def_id(),
+ ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(e.hir_id),
+ _ => return false,
+ };
+ if let Some(id) = id
+ && let sig = cx.tcx.fn_sig(id).skip_binder()
+ && let ty::Param(output_ty) = *sig.output().kind()
+ {
+ sig.inputs().iter().all(|&ty| !ty_contains_param(ty, output_ty.index))
+ } else {
+ false
+ }
+}
+
+fn ty_contains_param(ty: Ty<'_>, index: u32) -> bool {
+ struct Visitor(u32);
+ impl<'tcx> TypeVisitor<'tcx> for Visitor {
+ type BreakTy = ();
+ fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+ if let ty::Param(ty) = *ty.kind() {
+ if ty.index == self.0 {
+ ControlFlow::BREAK
+ } else {
+ ControlFlow::CONTINUE
+ }
+ } else {
+ ty.super_visit_with(self)
+ }
+ }
+ }
+ ty.visit_with(&mut Visitor(index)).is_break()
+}
/// ```
#[clippy::version = "pre 1.29.0"]
pub LET_UNIT_VALUE,
- pedantic,
+ style,
"creating a `let` binding to a value of unit type, which usually can't be used afterwards"
}
--- /dev/null
+use clippy_utils::{diagnostics::span_lint_and_sugg, ty::is_type_diagnostic_item};
+use clippy_utils::{match_def_path, paths};
+use if_chain::if_chain;
+use rustc_ast::ast::LitKind;
+use rustc_errors::Applicability;
+use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::sym;
+
+declare_clippy_lint! {
+ /// ### What it does
+ ///
+ /// Detects cases of owned empty strings being passed as an argument to a function expecting `&str`
+ ///
+ /// ### Why is this bad?
+ ///
+ /// This results in longer and less readable code
+ ///
+ /// ### Example
+ /// ```rust
+ /// vec!["1", "2", "3"].join(&String::new());
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// vec!["1", "2", "3"].join("");
+ /// ```
+ #[clippy::version = "1.62.0"]
+ pub UNNECESSARY_OWNED_EMPTY_STRINGS,
+ style,
+ "detects cases of references to owned empty strings being passed as an argument to a function expecting `&str`"
+}
+declare_lint_pass!(UnnecessaryOwnedEmptyStrings => [UNNECESSARY_OWNED_EMPTY_STRINGS]);
+
+impl<'tcx> LateLintPass<'tcx> for UnnecessaryOwnedEmptyStrings {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
+ if_chain! {
+ if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner_expr) = expr.kind;
+ if let ExprKind::Call(fun, args) = inner_expr.kind;
+ if let ExprKind::Path(ref qpath) = fun.kind;
+ if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
+ if let ty::Ref(_, inner_str, _) = cx.typeck_results().expr_ty_adjusted(expr).kind();
+ if inner_str.is_str();
+ then {
+ if match_def_path(cx, fun_def_id, &paths::STRING_NEW) {
+ span_lint_and_sugg(
+ cx,
+ UNNECESSARY_OWNED_EMPTY_STRINGS,
+ expr.span,
+ "usage of `&String::new()` for a function expecting a `&str` argument",
+ "try",
+ "\"\"".to_owned(),
+ Applicability::MachineApplicable,
+ );
+ } else {
+ if_chain! {
+ if match_def_path(cx, fun_def_id, &paths::FROM_FROM);
+ if let [.., last_arg] = args;
+ if let ExprKind::Lit(spanned) = &last_arg.kind;
+ if let LitKind::Str(symbol, _) = spanned.node;
+ if symbol.is_empty();
+ let inner_expr_type = cx.typeck_results().expr_ty(inner_expr);
+ if is_type_diagnostic_item(cx, inner_expr_type, sym::String);
+ then {
+ span_lint_and_sugg(
+ cx,
+ UNNECESSARY_OWNED_EMPTY_STRINGS,
+ expr.span,
+ "usage of `&String::from(\"\")` for a function expecting a `&str` argument",
+ "try",
+ "\"\"".to_owned(),
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+ }
+ }
+ }
+ }
+}
use clippy_utils::{meets_msrv, msrvs, over};
use rustc_ast::mut_visit::*;
use rustc_ast::ptr::P;
-use rustc_ast::{self as ast, Pat, PatKind, PatKind::*, DUMMY_NODE_ID};
+use rustc_ast::{self as ast, Mutability, Pat, PatKind, PatKind::*, DUMMY_NODE_ID};
use rustc_ast_pretty::pprust;
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass};
/// *disjunctive normal form (DNF)* into *conjunctive normal form (CNF)*.
///
/// ### Why is this bad?
- /// In the example above, `Some` is repeated, which unncessarily complicates the pattern.
+ /// In the example above, `Some` is repeated, which unnecessarily complicates the pattern.
///
/// ### Example
/// ```rust
// with which a pattern `C(p_0)` may be formed,
// which we would want to join with other `C(p_j)`s.
Ident(.., None) | Lit(_) | Wild | Path(..) | Range(..) | Rest | MacCall(_)
+ // Skip immutable refs, as grouping them saves few characters,
+ // and almost always requires adding parens (increasing noisiness).
+ // In the case of only two patterns, replacement adds net characters.
+ | Ref(_, Mutability::Not)
// Dealt with elsewhere.
| Or(_) | Paren(_) => false,
// Transform `box x | ... | box y` into `box (x | y)`.
|k| matches!(k, Box(_)),
|k| always_pat!(k, Box(p) => p),
),
- // Transform `&m x | ... | &m y` into `&m (x | y)`.
- Ref(target, m1) => extend_with_matching(
+ // Transform `&mut x | ... | &mut y` into `&mut (x | y)`.
+ Ref(target, Mutability::Mut) => extend_with_matching(
target, start, alternatives,
- |k| matches!(k, Ref(_, m2) if m1 == m2), // Mutabilities must match.
+ |k| matches!(k, Ref(_, Mutability::Mut)),
|k| always_pat!(k, Ref(p, _) => p),
),
// Transform `b @ p0 | ... b @ p1` into `b @ (p0 | p1)`.
///
/// ### Known problems
/// - Unaddressed false negative in fn bodies of trait implementations
- /// - False positive with assotiated types in traits (#4140)
+ /// - False positive with associated types in traits (#4140)
///
/// ### Example
/// ```rust
};
}
-/// Transforms the given `Option<T>` varibles into `OptionPat<Binding<T>>`.
+/// Transforms the given `Option<T>` variables into `OptionPat<Binding<T>>`.
/// This displays as `Some($name)` or `None` when printed. The name of the inner binding
/// is set to the name of the variable passed to the macro.
macro_rules! opt_bind {
/// the slice pattern that is suggested. If more elements would be necessary, the lint is suppressed.
/// For example, `[_, _, _, e, ..]` is a slice pattern with 4 elements.
(max_suggested_slice_pattern_length: u64 = 3),
+ /// Lint: AWAIT_HOLDING_INVALID_TYPE
+ (await_holding_invalid_types: Vec<crate::utils::conf::DisallowedType> = Vec::new()),
+ /// Lint: LARGE_INCLUDE_FILE.
+ ///
+ /// The maximum size of a file included via `include_bytes!()` or `include_str!()`, in bytes
+ (max_include_file_size: u64 = 1_000_000),
}
/// Search for the configuration file.
--- /dev/null
+use clippy_utils::get_attr;
+use rustc_hir as hir;
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// It formats the attached node with `{:#?}` and writes the result to the
+ /// standard output. This is intended for debugging.
+ ///
+ /// ### Examples
+ /// ```rs
+ /// #[clippy::dump]
+ /// use std::mem;
+ ///
+ /// #[clippy::dump]
+ /// fn foo(input: u32) -> u64 {
+ /// input as u64
+ /// }
+ /// ```
+ pub DUMP_HIR,
+ internal_warn,
+ "helper to dump info about code"
+}
+
+declare_lint_pass!(DumpHir => [DUMP_HIR]);
+
+impl<'tcx> LateLintPass<'tcx> for DumpHir {
+ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
+ if has_attr(cx, item.hir_id()) {
+ println!("{item:#?}");
+ }
+ }
+
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
+ if has_attr(cx, expr.hir_id) {
+ println!("{expr:#?}");
+ }
+ }
+
+ fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx hir::Stmt<'_>) {
+ match stmt.kind {
+ hir::StmtKind::Expr(e) | hir::StmtKind::Semi(e) if has_attr(cx, e.hir_id) => return,
+ _ => {},
+ }
+ if has_attr(cx, stmt.hir_id) {
+ println!("{stmt:#?}");
+ }
+ }
+}
+
+fn has_attr(cx: &LateContext<'_>, hir_id: hir::HirId) -> bool {
+ let attrs = cx.tcx.hir().attrs(hir_id);
+ get_attr(cx.sess(), attrs, "dump").count() > 0
+}
+++ /dev/null
-//! checks for attributes
-
-use clippy_utils::get_attr;
-use rustc_ast::ast::{Attribute, InlineAsmTemplatePiece};
-use rustc_hir as hir;
-use rustc_lint::{LateContext, LateLintPass, LintContext};
-use rustc_middle::ty;
-use rustc_session::Session;
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-
-declare_clippy_lint! {
- /// ### What it does
- /// Dumps every ast/hir node which has the `#[clippy::dump]`
- /// attribute
- ///
- /// ### Example
- /// ```rust,ignore
- /// #[clippy::dump]
- /// extern crate foo;
- /// ```
- ///
- /// prints
- ///
- /// ```text
- /// item `foo`
- /// visibility inherited from outer item
- /// extern crate dylib source: "/path/to/foo.so"
- /// ```
- pub DEEP_CODE_INSPECTION,
- internal_warn,
- "helper to dump info about code"
-}
-
-declare_lint_pass!(DeepCodeInspector => [DEEP_CODE_INSPECTION]);
-
-impl<'tcx> LateLintPass<'tcx> for DeepCodeInspector {
- fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
- if !has_attr(cx.sess(), cx.tcx.hir().attrs(item.hir_id())) {
- return;
- }
- print_item(cx, item);
- }
-
- fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) {
- if !has_attr(cx.sess(), cx.tcx.hir().attrs(item.hir_id())) {
- return;
- }
- println!("impl item `{}`", item.ident.name);
- match cx.tcx.visibility(item.def_id) {
- ty::Visibility::Public => println!("public"),
- ty::Visibility::Restricted(def_id) => {
- if def_id.is_top_level_module() {
- println!("visible crate wide")
- } else {
- println!("visible in module `{}`", cx.tcx.def_path_str(def_id))
- }
- },
- ty::Visibility::Invisible => println!("invisible"),
- }
- match item.kind {
- hir::ImplItemKind::Const(_, body_id) => {
- println!("associated constant");
- print_expr(cx, &cx.tcx.hir().body(body_id).value, 1);
- },
- hir::ImplItemKind::Fn(..) => println!("method"),
- hir::ImplItemKind::TyAlias(_) => println!("associated type"),
- }
- }
-
- fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
- if !has_attr(cx.sess(), cx.tcx.hir().attrs(expr.hir_id)) {
- return;
- }
- print_expr(cx, expr, 0);
- }
-
- fn check_arm(&mut self, cx: &LateContext<'tcx>, arm: &'tcx hir::Arm<'_>) {
- if !has_attr(cx.sess(), cx.tcx.hir().attrs(arm.hir_id)) {
- return;
- }
- print_pat(cx, arm.pat, 1);
- if let Some(ref guard) = arm.guard {
- println!("guard:");
- print_guard(cx, guard, 1);
- }
- println!("body:");
- print_expr(cx, arm.body, 1);
- }
-
- fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx hir::Stmt<'_>) {
- if !has_attr(cx.sess(), cx.tcx.hir().attrs(stmt.hir_id)) {
- return;
- }
- match stmt.kind {
- hir::StmtKind::Local(local) => {
- println!("local variable of type {}", cx.typeck_results().node_type(local.hir_id));
- println!("pattern:");
- print_pat(cx, local.pat, 0);
- if let Some(e) = local.init {
- println!("init expression:");
- print_expr(cx, e, 0);
- }
- },
- hir::StmtKind::Item(_) => println!("item decl"),
- hir::StmtKind::Expr(e) | hir::StmtKind::Semi(e) => print_expr(cx, e, 0),
- }
- }
-}
-
-fn has_attr(sess: &Session, attrs: &[Attribute]) -> bool {
- get_attr(sess, attrs, "dump").count() > 0
-}
-
-#[allow(clippy::similar_names)]
-#[allow(clippy::too_many_lines)]
-fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) {
- let ind = " ".repeat(indent);
- println!("{}+", ind);
- println!("{}ty: {}", ind, cx.typeck_results().expr_ty(expr));
- println!(
- "{}adjustments: {:?}",
- ind,
- cx.typeck_results().adjustments().get(expr.hir_id)
- );
- match expr.kind {
- hir::ExprKind::Box(e) => {
- println!("{}Box", ind);
- print_expr(cx, e, indent + 1);
- },
- hir::ExprKind::Array(v) => {
- println!("{}Array", ind);
- for e in v {
- print_expr(cx, e, indent + 1);
- }
- },
- hir::ExprKind::Call(func, args) => {
- println!("{}Call", ind);
- println!("{}function:", ind);
- print_expr(cx, func, indent + 1);
- println!("{}arguments:", ind);
- for arg in args {
- print_expr(cx, arg, indent + 1);
- }
- },
- hir::ExprKind::Let(hir::Let { pat, init, ty, .. }) => {
- print_pat(cx, pat, indent + 1);
- if let Some(ty) = ty {
- println!("{} type annotation: {:?}", ind, ty);
- }
- print_expr(cx, init, indent + 1);
- },
- hir::ExprKind::MethodCall(path, args, _) => {
- println!("{}MethodCall", ind);
- println!("{}method name: {}", ind, path.ident.name);
- for arg in args {
- print_expr(cx, arg, indent + 1);
- }
- },
- hir::ExprKind::Tup(v) => {
- println!("{}Tup", ind);
- for e in v {
- print_expr(cx, e, indent + 1);
- }
- },
- hir::ExprKind::Binary(op, lhs, rhs) => {
- println!("{}Binary", ind);
- println!("{}op: {:?}", ind, op.node);
- println!("{}lhs:", ind);
- print_expr(cx, lhs, indent + 1);
- println!("{}rhs:", ind);
- print_expr(cx, rhs, indent + 1);
- },
- hir::ExprKind::Unary(op, inner) => {
- println!("{}Unary", ind);
- println!("{}op: {:?}", ind, op);
- print_expr(cx, inner, indent + 1);
- },
- hir::ExprKind::Lit(ref lit) => {
- println!("{}Lit", ind);
- println!("{}{:?}", ind, lit);
- },
- hir::ExprKind::Cast(e, target) => {
- println!("{}Cast", ind);
- print_expr(cx, e, indent + 1);
- println!("{}target type: {:?}", ind, target);
- },
- hir::ExprKind::Type(e, target) => {
- println!("{}Type", ind);
- print_expr(cx, e, indent + 1);
- println!("{}target type: {:?}", ind, target);
- },
- hir::ExprKind::Loop(..) => {
- println!("{}Loop", ind);
- },
- hir::ExprKind::If(cond, _, ref else_opt) => {
- println!("{}If", ind);
- println!("{}condition:", ind);
- print_expr(cx, cond, indent + 1);
- if let Some(els) = *else_opt {
- println!("{}else:", ind);
- print_expr(cx, els, indent + 1);
- }
- },
- hir::ExprKind::Match(cond, _, ref source) => {
- println!("{}Match", ind);
- println!("{}condition:", ind);
- print_expr(cx, cond, indent + 1);
- println!("{}source: {:?}", ind, source);
- },
- hir::ExprKind::Closure(ref clause, _, _, _, _) => {
- println!("{}Closure", ind);
- println!("{}clause: {:?}", ind, clause);
- },
- hir::ExprKind::Yield(sub, _) => {
- println!("{}Yield", ind);
- print_expr(cx, sub, indent + 1);
- },
- hir::ExprKind::Block(_, _) => {
- println!("{}Block", ind);
- },
- hir::ExprKind::Assign(lhs, rhs, _) => {
- println!("{}Assign", ind);
- println!("{}lhs:", ind);
- print_expr(cx, lhs, indent + 1);
- println!("{}rhs:", ind);
- print_expr(cx, rhs, indent + 1);
- },
- hir::ExprKind::AssignOp(ref binop, lhs, rhs) => {
- println!("{}AssignOp", ind);
- println!("{}op: {:?}", ind, binop.node);
- println!("{}lhs:", ind);
- print_expr(cx, lhs, indent + 1);
- println!("{}rhs:", ind);
- print_expr(cx, rhs, indent + 1);
- },
- hir::ExprKind::Field(e, ident) => {
- println!("{}Field", ind);
- println!("{}field name: {}", ind, ident.name);
- println!("{}struct expr:", ind);
- print_expr(cx, e, indent + 1);
- },
- hir::ExprKind::Index(arr, idx) => {
- println!("{}Index", ind);
- println!("{}array expr:", ind);
- print_expr(cx, arr, indent + 1);
- println!("{}index expr:", ind);
- print_expr(cx, idx, indent + 1);
- },
- hir::ExprKind::Path(hir::QPath::Resolved(ref ty, path)) => {
- println!("{}Resolved Path, {:?}", ind, ty);
- println!("{}path: {:?}", ind, path);
- },
- hir::ExprKind::Path(hir::QPath::TypeRelative(ty, seg)) => {
- println!("{}Relative Path, {:?}", ind, ty);
- println!("{}seg: {:?}", ind, seg);
- },
- hir::ExprKind::Path(hir::QPath::LangItem(lang_item, ..)) => {
- println!("{}Lang Item Path, {:?}", ind, lang_item.name());
- },
- hir::ExprKind::AddrOf(kind, ref muta, e) => {
- println!("{}AddrOf", ind);
- println!("kind: {:?}", kind);
- println!("mutability: {:?}", muta);
- print_expr(cx, e, indent + 1);
- },
- hir::ExprKind::Break(_, ref e) => {
- println!("{}Break", ind);
- if let Some(e) = *e {
- print_expr(cx, e, indent + 1);
- }
- },
- hir::ExprKind::Continue(_) => println!("{}Again", ind),
- hir::ExprKind::Ret(ref e) => {
- println!("{}Ret", ind);
- if let Some(e) = *e {
- print_expr(cx, e, indent + 1);
- }
- },
- hir::ExprKind::InlineAsm(asm) => {
- println!("{}InlineAsm", ind);
- println!("{}template: {}", ind, InlineAsmTemplatePiece::to_string(asm.template));
- println!("{}options: {:?}", ind, asm.options);
- println!("{}operands:", ind);
- for (op, _op_sp) in asm.operands {
- match op {
- hir::InlineAsmOperand::In { expr, .. }
- | hir::InlineAsmOperand::InOut { expr, .. } => {
- print_expr(cx, expr, indent + 1);
- }
- hir::InlineAsmOperand::Out { expr, .. } => {
- if let Some(expr) = expr {
- print_expr(cx, expr, indent + 1);
- }
- },
- hir::InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => {
- print_expr(cx, in_expr, indent + 1);
- if let Some(out_expr) = out_expr {
- print_expr(cx, out_expr, indent + 1);
- }
- },
- hir::InlineAsmOperand::Const { anon_const }
- | hir::InlineAsmOperand::SymFn { anon_const } => {
- println!("{}anon_const:", ind);
- print_expr(cx, &cx.tcx.hir().body(anon_const.body).value, indent + 1);
- },
- hir::InlineAsmOperand::SymStatic { path, .. } => {
- match path {
- hir::QPath::Resolved(ref ty, path) => {
- println!("{}Resolved Path, {:?}", ind, ty);
- println!("{}path: {:?}", ind, path);
- },
- hir::QPath::TypeRelative(ty, seg) => {
- println!("{}Relative Path, {:?}", ind, ty);
- println!("{}seg: {:?}", ind, seg);
- },
- hir::QPath::LangItem(lang_item, ..) => {
- println!("{}Lang Item Path, {:?}", ind, lang_item.name());
- },
- }
- }
- }
- }
- },
- hir::ExprKind::Struct(path, fields, ref base) => {
- println!("{}Struct", ind);
- println!("{}path: {:?}", ind, path);
- for field in fields {
- println!("{}field \"{}\":", ind, field.ident.name);
- print_expr(cx, field.expr, indent + 1);
- }
- if let Some(base) = *base {
- println!("{}base:", ind);
- print_expr(cx, base, indent + 1);
- }
- },
- hir::ExprKind::ConstBlock(ref anon_const) => {
- println!("{}ConstBlock", ind);
- println!("{}anon_const:", ind);
- print_expr(cx, &cx.tcx.hir().body(anon_const.body).value, indent + 1);
- },
- hir::ExprKind::Repeat(val, length) => {
- println!("{}Repeat", ind);
- println!("{}value:", ind);
- print_expr(cx, val, indent + 1);
- println!("{}repeat count:", ind);
- match length {
- hir::ArrayLen::Infer(_, _) => println!("{}repeat count: _", ind),
- hir::ArrayLen::Body(anon_const) => {
- print_expr(cx, &cx.tcx.hir().body(anon_const.body).value, indent + 1);
- },
- }
- },
- hir::ExprKind::Err => {
- println!("{}Err", ind);
- },
- hir::ExprKind::DropTemps(e) => {
- println!("{}DropTemps", ind);
- print_expr(cx, e, indent + 1);
- },
- }
-}
-
-fn print_item(cx: &LateContext<'_>, item: &hir::Item<'_>) {
- let did = item.def_id;
- println!("item `{}`", item.ident.name);
- match cx.tcx.visibility(item.def_id) {
- ty::Visibility::Public => println!("public"),
- ty::Visibility::Restricted(def_id) => {
- if def_id.is_top_level_module() {
- println!("visible crate wide")
- } else {
- println!("visible in module `{}`", cx.tcx.def_path_str(def_id))
- }
- },
- ty::Visibility::Invisible => println!("invisible"),
- }
- match item.kind {
- hir::ItemKind::ExternCrate(ref _renamed_from) => {
- if let Some(crate_id) = cx.tcx.extern_mod_stmt_cnum(did) {
- let source = cx.tcx.used_crate_source(crate_id);
- if let Some(ref src) = source.dylib {
- println!("extern crate dylib source: {:?}", src.0);
- }
- if let Some(ref src) = source.rlib {
- println!("extern crate rlib source: {:?}", src.0);
- }
- } else {
- println!("weird extern crate without a crate id");
- }
- },
- hir::ItemKind::Use(path, ref kind) => println!("{:?}, {:?}", path, kind),
- hir::ItemKind::Static(..) => println!("static item of type {:#?}", cx.tcx.type_of(did)),
- hir::ItemKind::Const(..) => println!("const item of type {:#?}", cx.tcx.type_of(did)),
- hir::ItemKind::Fn(..) => {
- let item_ty = cx.tcx.type_of(did);
- println!("function of type {:#?}", item_ty);
- },
- hir::ItemKind::Macro(ref macro_def, _) => {
- if macro_def.macro_rules {
- println!("macro introduced by `macro_rules!`");
- } else {
- println!("macro introduced by `macro`");
- }
- },
- hir::ItemKind::Mod(..) => println!("module"),
- hir::ItemKind::ForeignMod { abi, .. } => println!("foreign module with abi: {}", abi),
- hir::ItemKind::GlobalAsm(asm) => println!("global asm: {:?}", asm),
- hir::ItemKind::TyAlias(..) => {
- println!("type alias for {:?}", cx.tcx.type_of(did));
- },
- hir::ItemKind::OpaqueTy(..) => {
- println!("existential type with real type {:?}", cx.tcx.type_of(did));
- },
- hir::ItemKind::Enum(..) => {
- println!("enum definition of type {:?}", cx.tcx.type_of(did));
- },
- hir::ItemKind::Struct(..) => {
- println!("struct definition of type {:?}", cx.tcx.type_of(did));
- },
- hir::ItemKind::Union(..) => {
- println!("union definition of type {:?}", cx.tcx.type_of(did));
- },
- hir::ItemKind::Trait(..) => {
- println!("trait decl");
- if cx.tcx.trait_is_auto(did.to_def_id()) {
- println!("trait is auto");
- } else {
- println!("trait is not auto");
- }
- },
- hir::ItemKind::TraitAlias(..) => {
- println!("trait alias");
- },
- hir::ItemKind::Impl(hir::Impl {
- of_trait: Some(ref _trait_ref),
- ..
- }) => {
- println!("trait impl");
- },
- hir::ItemKind::Impl(hir::Impl { of_trait: None, .. }) => {
- println!("impl");
- },
- }
-}
-
-#[allow(clippy::similar_names)]
-#[allow(clippy::too_many_lines)]
-fn print_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, indent: usize) {
- let ind = " ".repeat(indent);
- println!("{}+", ind);
- match pat.kind {
- hir::PatKind::Wild => println!("{}Wild", ind),
- hir::PatKind::Binding(ref mode, .., ident, ref inner) => {
- println!("{}Binding", ind);
- println!("{}mode: {:?}", ind, mode);
- println!("{}name: {}", ind, ident.name);
- if let Some(inner) = *inner {
- println!("{}inner:", ind);
- print_pat(cx, inner, indent + 1);
- }
- },
- hir::PatKind::Or(fields) => {
- println!("{}Or", ind);
- for field in fields {
- print_pat(cx, field, indent + 1);
- }
- },
- hir::PatKind::Struct(ref path, fields, ignore) => {
- println!("{}Struct", ind);
- println!(
- "{}name: {}",
- ind,
- rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false))
- );
- println!("{}ignore leftover fields: {}", ind, ignore);
- println!("{}fields:", ind);
- for field in fields {
- println!("{} field name: {}", ind, field.ident.name);
- if field.is_shorthand {
- println!("{} in shorthand notation", ind);
- }
- print_pat(cx, field.pat, indent + 1);
- }
- },
- hir::PatKind::TupleStruct(ref path, fields, opt_dots_position) => {
- println!("{}TupleStruct", ind);
- println!(
- "{}path: {}",
- ind,
- rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false))
- );
- if let Some(dot_position) = opt_dots_position {
- println!("{}dot position: {}", ind, dot_position);
- }
- for field in fields {
- print_pat(cx, field, indent + 1);
- }
- },
- hir::PatKind::Path(hir::QPath::Resolved(ref ty, path)) => {
- println!("{}Resolved Path, {:?}", ind, ty);
- println!("{}path: {:?}", ind, path);
- },
- hir::PatKind::Path(hir::QPath::TypeRelative(ty, seg)) => {
- println!("{}Relative Path, {:?}", ind, ty);
- println!("{}seg: {:?}", ind, seg);
- },
- hir::PatKind::Path(hir::QPath::LangItem(lang_item, ..)) => {
- println!("{}Lang Item Path, {:?}", ind, lang_item.name());
- },
- hir::PatKind::Tuple(pats, opt_dots_position) => {
- println!("{}Tuple", ind);
- if let Some(dot_position) = opt_dots_position {
- println!("{}dot position: {}", ind, dot_position);
- }
- for field in pats {
- print_pat(cx, field, indent + 1);
- }
- },
- hir::PatKind::Box(inner) => {
- println!("{}Box", ind);
- print_pat(cx, inner, indent + 1);
- },
- hir::PatKind::Ref(inner, ref muta) => {
- println!("{}Ref", ind);
- println!("{}mutability: {:?}", ind, muta);
- print_pat(cx, inner, indent + 1);
- },
- hir::PatKind::Lit(e) => {
- println!("{}Lit", ind);
- print_expr(cx, e, indent + 1);
- },
- hir::PatKind::Range(ref l, ref r, ref range_end) => {
- println!("{}Range", ind);
- if let Some(expr) = l {
- print_expr(cx, expr, indent + 1);
- }
- if let Some(expr) = r {
- print_expr(cx, expr, indent + 1);
- }
- match *range_end {
- hir::RangeEnd::Included => println!("{} end included", ind),
- hir::RangeEnd::Excluded => println!("{} end excluded", ind),
- }
- },
- hir::PatKind::Slice(first_pats, ref range, last_pats) => {
- println!("{}Slice [a, b, ..i, y, z]", ind);
- println!("[a, b]:");
- for pat in first_pats {
- print_pat(cx, pat, indent + 1);
- }
- println!("i:");
- if let Some(pat) = *range {
- print_pat(cx, pat, indent + 1);
- }
- println!("[y, z]:");
- for pat in last_pats {
- print_pat(cx, pat, indent + 1);
- }
- },
- }
-}
-
-fn print_guard(cx: &LateContext<'_>, guard: &hir::Guard<'_>, indent: usize) {
- let ind = " ".repeat(indent);
- println!("{}+", ind);
- match guard {
- hir::Guard::If(expr) => {
- println!("{}If", ind);
- print_expr(cx, expr, indent + 1);
- },
- hir::Guard::IfLet(pat, expr) => {
- println!("{}IfLet", ind);
- print_pat(cx, pat, indent + 1);
- print_expr(cx, expr, indent + 1);
- },
- }
-}
/// Checks for unnecessary conversion from Symbol to a string.
///
/// ### Why is this bad?
- /// It's faster use symbols directly intead of strings.
+ /// It's faster use symbols directly instead of strings.
///
/// ### Example
/// Bad:
cx,
COLLAPSIBLE_SPAN_LINT_CALLS,
expr.span,
- "this call is collspible",
+ "this call is collapsible",
"collapse into",
format!(
"span_lint_and_note({}, {}, {}, {}, {}, {})",
/// This is the output file of the lint collector.
const OUTPUT_FILE: &str = "../util/gh-pages/lints.json";
/// These lints are excluded from the export.
-const BLACK_LISTED_LINTS: [&str; 3] = ["lint_author", "deep_code_inspection", "internal_metadata_collector"];
+const BLACK_LISTED_LINTS: &[&str] = &["lint_author", "dump_hir", "internal_metadata_collector"];
/// These groups will be ignored by the lint group matcher. This is useful for collections like
/// `clippy::all`
const IGNORED_LINT_GROUPS: [&str; 1] = ["clippy::all"];
/// This applicability will be set for unresolved applicability values.
const APPLICABILITY_UNRESOLVED_STR: &str = "Unresolved";
/// The version that will be displayed if none has been defined
-const VERION_DEFAULT_STR: &str = "Unknown";
+const VERSION_DEFAULT_STR: &str = "Unknown";
declare_clippy_lint! {
/// ### What it does
fn get_lint_version(cx: &LateContext<'_>, item: &Item<'_>) -> String {
extract_clippy_version_value(cx, item).map_or_else(
- || VERION_DEFAULT_STR.to_string(),
+ || VERSION_DEFAULT_STR.to_string(),
|version| version.as_str().to_string(),
)
}
self.suggestion_count += 2;
}
- /// Checks if the suggestions include multiple spanns
+ /// Checks if the suggestions include multiple spans
fn is_multi_part(&self) -> bool {
self.suggestion_count > 1
}
pub mod author;
pub mod conf;
-pub mod inspector;
+pub mod dump_hir;
#[cfg(feature = "internal")]
pub mod internal_lints;
use rustc_ast::ast::{self, LitFloatType, LitKind};
use rustc_data_structures::sync::Lrc;
use rustc_hir::def::{DefKind, Res};
-use rustc_hir::{BinOp, BinOpKind, Block, Expr, ExprKind, HirId, QPath, UnOp};
+use rustc_hir::{BinOp, BinOpKind, Block, Expr, ExprKind, HirId, Item, ItemKind, Node, QPath, UnOp};
use rustc_lint::LateContext;
use rustc_middle::mir::interpret::Scalar;
use rustc_middle::ty::subst::{Subst, SubstsRef};
let res = self.typeck_results.qpath_res(qpath, id);
match res {
Res::Def(DefKind::Const | DefKind::AssocConst, def_id) => {
+ // Check if this constant is based on `cfg!(..)`,
+ // which is NOT constant for our purposes.
+ if let Some(node) = self.lcx.tcx.hir().get_if_local(def_id) &&
+ let Node::Item(&Item {
+ kind: ItemKind::Const(_, body_id),
+ ..
+ }) = node &&
+ let Node::Expr(&Expr {
+ kind: ExprKind::Lit(_),
+ span,
+ ..
+ }) = self.lcx.tcx.hir().get(body_id.hir_id) &&
+ is_direct_expn_of(span, "cfg").is_some() {
+ return None;
+ }
+
let substs = self.typeck_results.node_substs(id);
let substs = if self.substs.is_empty() {
substs
-use crate::consts::{constant_context, constant_simple};
+use crate::consts::constant_simple;
use crate::source::snippet_opt;
use rustc_ast::ast::InlineAsmTemplatePiece;
use rustc_data_structures::fx::FxHasher;
use std::hash::{Hash, Hasher};
/// Type used to check whether two ast are the same. This is different from the
-/// operator
-/// `==` on ast types as this operator would compare true equality with ID and
-/// span.
+/// operator `==` on ast types as this operator would compare true equality with
+/// ID and span.
///
/// Note that some expressions kinds are not considered but could be added.
pub struct SpanlessEq<'a, 'tcx> {
/// Context used to evaluate constant expressions.
cx: &'a LateContext<'tcx>,
- maybe_typeck_results: Option<&'tcx TypeckResults<'tcx>>,
+ maybe_typeck_results: Option<(&'tcx TypeckResults<'tcx>, &'tcx TypeckResults<'tcx>)>,
allow_side_effects: bool,
expr_fallback: Option<Box<dyn FnMut(&Expr<'_>, &Expr<'_>) -> bool + 'a>>,
}
pub fn new(cx: &'a LateContext<'tcx>) -> Self {
Self {
cx,
- maybe_typeck_results: cx.maybe_typeck_results(),
+ maybe_typeck_results: cx.maybe_typeck_results().map(|x| (x, x)),
allow_side_effects: true,
expr_fallback: None,
}
(&StmtKind::Local(l), &StmtKind::Local(r)) => {
// This additional check ensures that the type of the locals are equivalent even if the init
// expression or type have some inferred parts.
- if let Some(typeck) = self.inner.maybe_typeck_results {
- let l_ty = typeck.pat_ty(l.pat);
- let r_ty = typeck.pat_ty(r.pat);
+ if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results {
+ let l_ty = typeck_lhs.pat_ty(l.pat);
+ let r_ty = typeck_rhs.pat_ty(r.pat);
if l_ty != r_ty {
return false;
}
}
pub fn eq_body(&mut self, left: BodyId, right: BodyId) -> bool {
- let cx = self.inner.cx;
- let eval_const = |body| constant_context(cx, cx.tcx.typeck_body(body)).expr(&cx.tcx.hir().body(body).value);
- eval_const(left) == eval_const(right)
+ // swap out TypeckResults when hashing a body
+ let old_maybe_typeck_results = self.inner.maybe_typeck_results.replace((
+ self.inner.cx.tcx.typeck_body(left),
+ self.inner.cx.tcx.typeck_body(right),
+ ));
+ let res = self.eq_expr(
+ &self.inner.cx.tcx.hir().body(left).value,
+ &self.inner.cx.tcx.hir().body(right).value,
+ );
+ self.inner.maybe_typeck_results = old_maybe_typeck_results;
+ res
}
#[allow(clippy::similar_names)]
return false;
}
- if let Some(typeck_results) = self.inner.maybe_typeck_results {
+ if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results {
if let (Some(l), Some(r)) = (
- constant_simple(self.inner.cx, typeck_results, left),
- constant_simple(self.inner.cx, typeck_results, right),
+ constant_simple(self.inner.cx, typeck_lhs, left),
+ constant_simple(self.inner.cx, typeck_rhs, right),
) {
if l == r {
return true;
self.hash_expr(out_expr);
}
},
- InlineAsmOperand::Const { anon_const } => self.hash_body(anon_const.body),
- InlineAsmOperand::SymFn { anon_const } => self.hash_body(anon_const.body),
+ InlineAsmOperand::Const { anon_const } | InlineAsmOperand::SymFn { anon_const } => {
+ self.hash_body(anon_const.body);
+ },
InlineAsmOperand::SymStatic { path, def_id: _ } => self.hash_qpath(path),
}
}
expr_visitor_no_bodies(|e| {
// if we're still inside of the macro definition...
if e.span.ctxt() == expr.span.ctxt() {
- // ArgumnetV1::new_<format_trait>(<value>)
+ // ArgumentV1::new_<format_trait>(<value>)
if_chain! {
if let ExprKind::Call(callee, [val]) = e.kind;
if let ExprKind::Path(QPath::TypeRelative(ty, seg)) = callee.kind;
1,28,0 { FROM_BOOL }
1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST, EXPECT_ERR }
1,16,0 { STR_REPEAT }
+ 1,24,0 { IS_ASCII_DIGIT }
}
.trim_start()
.chars()
.next()
- .map_or(false, |c| c.is_digit(10))
+ .map_or(false, |c| c.is_ascii_digit())
{
let (unsuffixed, suffix) = split_suffix(src, lit_kind);
let float = matches!(lit_kind, LitKind::Float(..));
pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"];
pub const IPADDR_V4: [&str; 5] = ["std", "net", "ip", "IpAddr", "V4"];
pub const IPADDR_V6: [&str; 5] = ["std", "net", "ip", "IpAddr", "V6"];
+pub const ITER_COUNT: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "count"];
pub const ITER_REPEAT: [&str; 5] = ["core", "iter", "sources", "repeat", "repeat"];
#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates
pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"];
pub const STD_FS_CREATE_DIR: [&str; 3] = ["std", "fs", "create_dir"];
pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"];
pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"];
+pub const STRING_NEW: [&str; 4] = ["alloc", "string", "String", "new"];
+pub const STR_BYTES: [&str; 4] = ["core", "str", "<impl str>", "bytes"];
pub const STR_ENDS_WITH: [&str; 4] = ["core", "str", "<impl str>", "ends_with"];
pub const STR_FROM_UTF8: [&str; 4] = ["core", "str", "converts", "from_utf8"];
pub const STR_LEN: [&str; 4] = ["core", "str", "<impl str>", "len"];
StatementKind::FakeRead(box (_, place)) => check_place(tcx, *place, span, body),
// just an assignment
- StatementKind::SetDiscriminant { place, .. } | StatementKind::Deinit(place) =>
- check_place(tcx, **place, span, body),
+ StatementKind::SetDiscriminant { place, .. } | StatementKind::Deinit(place) => {
+ check_place(tcx, **place, span, body)
+ },
StatementKind::CopyNonOverlapping(box rustc_middle::mir::CopyNonOverlapping { dst, src, count }) => {
check_operand(tcx, dst, span, body)?;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LintContext};
use rustc_span::hygiene;
+use rustc_span::source_map::SourceMap;
use rustc_span::{BytePos, Pos, Span, SyntaxContext};
use std::borrow::Cow;
+/// Checks if the span starts with the given text. This will return false if the span crosses
+/// multiple files or if source is not available.
+///
+/// This is used to check for proc macros giving unhelpful spans to things.
+pub fn span_starts_with<T: LintContext>(cx: &T, span: Span, text: &str) -> bool {
+ fn helper(sm: &SourceMap, span: Span, text: &str) -> bool {
+ let pos = sm.lookup_byte_offset(span.lo());
+ let Some(ref src) = pos.sf.src else {
+ return false;
+ };
+ let end = span.hi() - pos.sf.start_pos;
+ src.get(pos.pos.0 as usize..end.0 as usize)
+ // Expression spans can include wrapping parenthesis. Remove them first.
+ .map_or(false, |s| s.trim_start_matches('(').starts_with(text))
+ }
+ helper(cx.sess().source_map(), span, text)
+}
+
/// Like `snippet_block`, but add braces if the expr is not an `ExprKind::Block`.
/// Also takes an `Option<String>` which can be put inside the braces.
pub fn expr_block<'a, T: LintContext>(
true
}
-/// Returns the positon just before rarrow
+/// Returns the position just before rarrow
///
/// ```rust,ignore
/// fn into(self) -> () {}
use rustc_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
use std::borrow::Cow;
use std::convert::TryInto;
-use std::fmt::Display;
+use std::fmt::{Display, Write as _};
use std::iter;
use std::ops::{Add, Neg, Not, Sub};
if cmt.place.projections.is_empty() {
// handle item without any projection, that needs an explicit borrowing
// i.e.: suggest `&x` instead of `x`
- self.suggestion_start.push_str(&format!("{}&{}", start_snip, ident_str));
+ let _ = write!(self.suggestion_start, "{}&{}", start_snip, ident_str);
} else {
// cases where a parent `Call` or `MethodCall` is using the item
// i.e.: suggest `.contains(&x)` for `.find(|x| [1, 2, 3].contains(x)).is_none()`
// given expression is the self argument and will be handled completely by the compiler
// i.e.: `|x| x.is_something()`
ExprKind::MethodCall(_, [self_expr, ..], _) if self_expr.hir_id == cmt.hir_id => {
- self.suggestion_start
- .push_str(&format!("{}{}", start_snip, ident_str_with_proj));
+ let _ = write!(self.suggestion_start, "{}{}", start_snip, ident_str_with_proj);
self.next_pos = span.hi();
return;
},
}
}
- self.suggestion_start
- .push_str(&format!("{}{}", start_snip, replacement_str));
+ let _ = write!(self.suggestion_start, "{}{}", start_snip, replacement_str);
}
self.next_pos = span.hi();
}
#![allow(clippy::module_name_repetitions)]
use rustc_ast::ast::Mutability;
-use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir as hir;
use rustc_hir::def::{CtorKind, DefKind, Res};
use rustc_hir::def_id::DefId;
-use rustc_hir::{Expr, TyKind, Unsafety};
+use rustc_hir::{Expr, LangItem, TyKind, Unsafety};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::LateContext;
use rustc_middle::mir::interpret::{ConstValue, Scalar};
use rustc_trait_selection::traits::query::normalize::AtExt;
use std::iter;
-use crate::{match_def_path, must_use_attr, path_res};
+use crate::{match_def_path, must_use_attr, path_res, paths};
// Checks if the given type implements copy.
pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
})
}
+/// Get the diagnostic name of a type, e.g. `sym::HashMap`. To check if a type
+/// implements a trait marked with a diagnostic item use [`implements_trait`].
+///
+/// For a further exploitation what diagnostic items are see [diagnostic items] in
+/// rustc-dev-guide.
+///
+/// [Diagnostic Items]: https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html
+pub fn get_type_diagnostic_name(cx: &LateContext<'_>, ty: Ty<'_>) -> Option<Symbol> {
+ match ty.kind() {
+ ty::Adt(adt, _) => cx.tcx.get_diagnostic_name(adt.did()),
+ _ => None,
+ }
+}
+
/// Returns true if ty has `iter` or `iter_mut` methods
pub fn has_iter_method(cx: &LateContext<'_>, probably_ref_ty: Ty<'_>) -> Option<Symbol> {
// FIXME: instead of this hard-coded list, we should check if `<adt>::iter`
}
}
+/// Checks if the drop order for a type matters. Some std types implement drop solely to
+/// deallocate memory. For these types, and composites containing them, changing the drop order
+/// won't result in any observable side effects.
+pub fn needs_ordered_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
+ fn needs_ordered_drop_inner<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, seen: &mut FxHashSet<Ty<'tcx>>) -> bool {
+ if !seen.insert(ty) {
+ return false;
+ }
+ if !ty.has_significant_drop(cx.tcx, cx.param_env) {
+ false
+ }
+ // Check for std types which implement drop, but only for memory allocation.
+ else if is_type_lang_item(cx, ty, LangItem::OwnedBox)
+ || matches!(
+ get_type_diagnostic_name(cx, ty),
+ Some(sym::HashSet | sym::Rc | sym::Arc | sym::cstring_type)
+ )
+ || match_type(cx, ty, &paths::WEAK_RC)
+ || match_type(cx, ty, &paths::WEAK_ARC)
+ {
+ // Check all of the generic arguments.
+ if let ty::Adt(_, subs) = ty.kind() {
+ subs.types().any(|ty| needs_ordered_drop_inner(cx, ty, seen))
+ } else {
+ true
+ }
+ } else if !cx
+ .tcx
+ .lang_items()
+ .drop_trait()
+ .map_or(false, |id| implements_trait(cx, ty, id, &[]))
+ {
+ // This type doesn't implement drop, so no side effects here.
+ // Check if any component type has any.
+ match ty.kind() {
+ ty::Tuple(fields) => fields.iter().any(|ty| needs_ordered_drop_inner(cx, ty, seen)),
+ ty::Array(ty, _) => needs_ordered_drop_inner(cx, *ty, seen),
+ ty::Adt(adt, subs) => adt
+ .all_fields()
+ .map(|f| f.ty(cx.tcx, subs))
+ .any(|ty| needs_ordered_drop_inner(cx, ty, seen)),
+ _ => true,
+ }
+ } else {
+ true
+ }
+ }
+
+ needs_ordered_drop_inner(cx, ty, &mut FxHashSet::default())
+}
+
/// Peels off all references on the type. Returns the underlying type and the number of references
/// removed.
pub fn peel_mid_ty_refs(ty: Ty<'_>) -> (Ty<'_>, usize) {
use rustc_hir as hir;
use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::HirIdSet;
-use rustc_hir::{Expr, ExprKind, HirId};
+use rustc_hir::{Expr, ExprKind, HirId, Node};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::LateContext;
use rustc_middle::hir::nested_filter;
pub fn local_used_after_expr(cx: &LateContext<'_>, local_id: HirId, after: &Expr<'_>) -> bool {
let Some(block) = utils::get_enclosing_block(cx, local_id) else { return false };
+
+ // for _ in 1..3 {
+ // local
+ // }
+ //
+ // let closure = || local;
+ // closure();
+ // closure();
+ let in_loop_or_closure = cx
+ .tcx
+ .hir()
+ .parent_iter(after.hir_id)
+ .take_while(|&(id, _)| id != block.hir_id)
+ .any(|(_, node)| {
+ matches!(
+ node,
+ Node::Expr(Expr {
+ kind: ExprKind::Loop(..) | ExprKind::Closure(..),
+ ..
+ })
+ )
+ });
+ if in_loop_or_closure {
+ return true;
+ }
+
let mut used_after_expr = false;
let mut past_expr = false;
expr_visitor(cx, |expr| {
if expr.hir_id == after.hir_id {
past_expr = true;
- } else if past_expr && utils::path_to_local_id(expr, local_id) {
+ return false;
+ }
+
+ if past_expr && utils::path_to_local_id(expr, local_id) {
used_after_expr = true;
}
!used_after_expr
use crate::path_to_local_id;
+use core::ops::ControlFlow;
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::intravisit::{self, walk_block, walk_expr, Visitor};
use rustc_hir::{
- Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, Stmt, UnOp, Unsafety,
+ Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, Stmt, UnOp, UnsafeSource,
+ Unsafety,
};
use rustc_lint::LateContext;
use rustc_middle::hir::map::Map;
v.visit_expr(e);
v.is_unsafe
}
+
+/// Checks if the given expression contains an unsafe block
+pub fn contains_unsafe_block<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
+ struct V<'cx, 'tcx> {
+ cx: &'cx LateContext<'tcx>,
+ found_unsafe: bool,
+ }
+ impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
+ type NestedFilter = nested_filter::OnlyBodies;
+ fn nested_visit_map(&mut self) -> Self::Map {
+ self.cx.tcx.hir()
+ }
+
+ fn visit_block(&mut self, b: &'tcx Block<'_>) {
+ if self.found_unsafe {
+ return;
+ }
+ if b.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) {
+ self.found_unsafe = true;
+ return;
+ }
+ walk_block(self, b);
+ }
+ }
+ let mut v = V {
+ cx,
+ found_unsafe: false,
+ };
+ v.visit_expr(e);
+ v.found_unsafe
+}
+
+/// Runs the given function for each sub-expression producing the final value consumed by the parent
+/// of the give expression.
+///
+/// e.g. for the following expression
+/// ```rust,ignore
+/// if foo {
+/// f(0)
+/// } else {
+/// 1 + 1
+/// }
+/// ```
+/// this will pass both `f(0)` and `1+1` to the given function.
+pub fn for_each_value_source<'tcx, B>(
+ e: &'tcx Expr<'tcx>,
+ f: &mut impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>,
+) -> ControlFlow<B> {
+ match e.kind {
+ ExprKind::Block(Block { expr: Some(e), .. }, _) => for_each_value_source(e, f),
+ ExprKind::Match(_, arms, _) => {
+ for arm in arms {
+ for_each_value_source(arm.body, f)?;
+ }
+ ControlFlow::Continue(())
+ },
+ ExprKind::If(_, if_expr, Some(else_expr)) => {
+ for_each_value_source(if_expr, f)?;
+ for_each_value_source(else_expr, f)
+ },
+ ExprKind::DropTemps(e) => for_each_value_source(e, f),
+ _ => f(e),
+ }
+}
- [Adding the lint logic](#adding-the-lint-logic)
- [Specifying the lint's minimum supported Rust version (MSRV)](#specifying-the-lints-minimum-supported-rust-version-msrv)
- [Author lint](#author-lint)
+ - [Print HIR lint](#print-hir-lint)
- [Documentation](#documentation)
- [Running rustfmt](#running-rustfmt)
- [Debugging](#debugging)
[author_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=9a12cb60e5c6ad4e3003ac6d5e63cf55
+## Print HIR lint
+
+To implement a lint, it's helpful to first understand the internal representation
+that rustc uses. Clippy has the `#[clippy::dump]` attribute that prints the
+[_High-Level Intermediate Representation (HIR)_] of the item, statement, or
+expression that the attribute is attached to. To attach the attribute to expressions
+you often need to enable `#![feature(stmt_expr_attributes)]`.
+
+[Here][print_hir_example] you can find an example, just select _Tools_ and run _Clippy_.
+
+[_High-Level Intermediate Representation (HIR)_]: https://rustc-dev-guide.rust-lang.org/hir.html
+[print_hir_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=daf14db3a7f39ca467cd1b86c34b9afb
+
## Documentation
The final thing before submitting our PR is to add some documentation to our
#![allow(clippy::collapsible_else_if)]
use std::ffi::OsStr;
+use std::fmt::Write as _;
use std::process::Command;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::{collections::HashMap, io::ErrorKind};
let lint = format!("`{}`", self.linttype);
let mut output = String::from("| ");
- output.push_str(&format!(
+ let _ = write!(
+ output,
"[`{}`](../target/lintcheck/sources/{}#L{})",
file_with_pos, file, self.line
- ));
- output.push_str(&format!(r#" | {:<50} | "{}" |"#, lint, self.message));
+ );
+ let _ = write!(output, r#" | {:<50} | "{}" |"#, lint, self.message);
output.push('\n');
output
} else {
let shared_target_dir = clippy_project_root().join("target/lintcheck/shared_target_dir");
let mut args = if fix {
- vec!["--fix", "--allow-no-vcs", "--"]
+ vec!["--fix", "--"]
} else {
vec!["--", "--message-format=json", "--"]
};
lintcheck_results_path: PathBuf,
/// whether to just run --fix and not collect all the warnings
fix: bool,
- /// A list of lint that this lintcheck run shound focus on
+ /// A list of lints that this lintcheck run should focus on
lint_filter: Vec<String>,
/// Indicate if the output should support markdown syntax
markdown: bool,
text.push_str("| file | lint | message |\n");
text.push_str("| --- | --- | --- |\n");
}
- text.push_str(&format!("{}", all_msgs.join("")));
+ write!(text, "{}", all_msgs.join(""));
text.push_str("\n\n### ICEs:\n");
- ices.iter()
- .for_each(|(cratename, msg)| text.push_str(&format!("{}: '{}'", cratename, msg)));
+ for (cratename, msg) in ices.iter() {
+ let _ = write!(text, "{}: '{}'", cratename, msg);
+ }
println!("Writing logs to {}", config.lintcheck_results_path.display());
std::fs::create_dir_all(config.lintcheck_results_path.parent().unwrap()).unwrap();
[toolchain]
-channel = "nightly-2022-04-07"
+channel = "nightly-2022-05-05"
components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
// Separate the output with an empty line
eprintln!();
- let fallback_bundle =
- rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false);
+ let fallback_bundle = rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false);
let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr(
rustc_errors::ColorConfig::Auto,
None,
.args(&["-D", "clippy::pedantic"])
.arg("-Cdebuginfo=0"); // disable debuginfo to generate less data in the target dir
- // internal lints only exist if we build with the internal feature
if cfg!(feature = "internal") {
+ // internal lints only exist if we build with the internal feature
command.args(&["-D", "clippy::internal"]);
+ } else {
+ // running a clippy built without internal lints on the clippy source
+ // that contains e.g. `allow(clippy::invalid_paths)`
+ command.args(&["-A", "unknown_lints"]);
}
let output = command.output().unwrap();
// make sure that lint messages:
// * are not capitalized
- // * don't have puncuation at the end of the last sentence
+ // * don't have punctuation at the end of the last sentence
// these directories have interesting tests
let test_dirs = ["ui", "ui-cargo", "ui-internal", "ui-toml"]
LL | | });
| |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg)`
-error: this call is collspible
+error: this call is collapsible
--> $DIR/collapsible_span_lint_calls.rs:45:9
|
LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
LL | | });
| |__________^ help: collapse into: `span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg)`
-error: this call is collspible
+error: this call is collapsible
--> $DIR/collapsible_span_lint_calls.rs:48:9
|
LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
// run-rustfix
#![deny(clippy::internal)]
-#![allow(clippy::missing_clippy_version_attribute)]
+#![allow(clippy::missing_clippy_version_attribute, clippy::let_unit_value)]
#![feature(rustc_private)]
extern crate rustc_span;
// run-rustfix
#![deny(clippy::internal)]
-#![allow(clippy::missing_clippy_version_attribute)]
+#![allow(clippy::missing_clippy_version_attribute, clippy::let_unit_value)]
#![feature(rustc_private)]
extern crate rustc_span;
--- /dev/null
+#![warn(clippy::await_holding_invalid_type)]
+use std::net::Ipv4Addr;
+
+async fn bad() -> u32 {
+ let _x = String::from("hello");
+ baz().await
+}
+
+async fn bad_reason() -> u32 {
+ let _x = Ipv4Addr::new(127, 0, 0, 1);
+ baz().await
+}
+
+async fn good() -> u32 {
+ {
+ let _x = String::from("hi!");
+ let _y = Ipv4Addr::new(127, 0, 0, 1);
+ }
+ baz().await;
+ let _x = String::from("hi!");
+ 47
+}
+
+async fn baz() -> u32 {
+ 42
+}
+
+#[allow(clippy::manual_async_fn)]
+fn block_bad() -> impl std::future::Future<Output = u32> {
+ async move {
+ let _x = String::from("hi!");
+ baz().await
+ }
+}
+
+fn main() {
+ good();
+ bad();
+ bad_reason();
+ block_bad();
+}
--- /dev/null
+error: `std::string::String` may not be held across an `await` point per `clippy.toml`
+ --> $DIR/await_holding_invalid_type.rs:5:9
+ |
+LL | let _x = String::from("hello");
+ | ^^
+ |
+ = note: `-D clippy::await-holding-invalid-type` implied by `-D warnings`
+ = note: strings are bad
+
+error: `std::net::Ipv4Addr` may not be held across an `await` point per `clippy.toml`
+ --> $DIR/await_holding_invalid_type.rs:10:9
+ |
+LL | let _x = Ipv4Addr::new(127, 0, 0, 1);
+ | ^^
+
+error: `std::string::String` may not be held across an `await` point per `clippy.toml`
+ --> $DIR/await_holding_invalid_type.rs:31:13
+ |
+LL | let _x = String::from("hi!");
+ | ^^
+ |
+ = note: strings are bad
+
+error: aborting due to 3 previous errors
+
--- /dev/null
+await-holding-invalid-types = [
+ { path = "std::string::String", reason = "strings are bad" },
+ "std::net::Ipv4Addr",
+]
#![warn(clippy::too_many_lines)]
+#![allow(clippy::let_unit_value)]
// This function should be considered one line.
fn many_comments_but_one_line_of_code() {
error: this function has too many lines (2/1)
- --> $DIR/test.rs:18:1
+ --> $DIR/test.rs:19:1
|
LL | / fn too_many_lines() {
LL | | println!("This is bad.");
= note: `-D clippy::too-many-lines` implied by `-D warnings`
error: this function has too many lines (4/1)
- --> $DIR/test.rs:24:1
+ --> $DIR/test.rs:25:1
|
LL | / async fn async_too_many_lines() {
LL | | println!("This is bad.");
| |_^
error: this function has too many lines (4/1)
- --> $DIR/test.rs:30:1
+ --> $DIR/test.rs:31:1
|
LL | / fn closure_too_many_lines() {
LL | | let _ = {
| |_^
error: this function has too many lines (2/1)
- --> $DIR/test.rs:52:1
+ --> $DIR/test.rs:53:1
|
LL | / fn comment_before_code() {
LL | | let _ = "test";
--- /dev/null
+max-include-file-size = 600
--- /dev/null
+#![warn(clippy::large_include_file)]
+
+// Good
+const GOOD_INCLUDE_BYTES: &[u8; 581] = include_bytes!("large_include_file.rs");
+const GOOD_INCLUDE_STR: &str = include_str!("large_include_file.rs");
+
+#[allow(clippy::large_include_file)]
+const ALLOWED_TOO_BIG_INCLUDE_BYTES: &[u8; 654] = include_bytes!("too_big.txt");
+#[allow(clippy::large_include_file)]
+const ALLOWED_TOO_BIG_INCLUDE_STR: &str = include_str!("too_big.txt");
+
+// Bad
+const TOO_BIG_INCLUDE_BYTES: &[u8; 654] = include_bytes!("too_big.txt");
+const TOO_BIG_INCLUDE_STR: &str = include_str!("too_big.txt");
+
+fn main() {}
--- /dev/null
+error: attempted to include a large file
+ --> $DIR/large_include_file.rs:13:43
+ |
+LL | const TOO_BIG_INCLUDE_BYTES: &[u8; 654] = include_bytes!("too_big.txt");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::large-include-file` implied by `-D warnings`
+ = note: the configuration allows a maximum size of 600 bytes
+ = note: this error originates in the macro `include_bytes` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: attempted to include a large file
+ --> $DIR/large_include_file.rs:14:35
+ |
+LL | const TOO_BIG_INCLUDE_STR: &str = include_str!("too_big.txt");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: the configuration allows a maximum size of 600 bytes
+ = note: this error originates in the macro `include_str` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 2 previous errors
+
--- /dev/null
+Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Maecenas accumsan lacus vel facilisis volutpat. Etiam dignissim diam quis enim lobortis scelerisque fermentum dui faucibus. Tellus id interdum velit laoreet id donec ultrices. Est ultricies integer quis auctor elit sed vulputate. Erat velit scelerisque in dictum non consectetur a erat nam. Sed blandit libero volutpat sed. Tortor condimentum lacinia quis vel eros. Enim ut tellus elementum sagittis vitae et leo duis. Congue mauris rhoncus aenean vel elit scelerisque. Id consectetur purus ut faucibus pulvinar elementum integer.
\ No newline at end of file
-error: you are using an explicit closure for copying elements
+error: you are using an explicit closure for cloning elements
--> $DIR/min_rust_version.rs:74:26
|
LL | let _: Option<u64> = Some(&16).map(|b| *b);
-error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `avoid-breaking-exported-api`, `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `disallowed-types`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `standard-macro-braces`, `enforced-import-renames`, `allowed-scripts`, `enable-raw-pointer-heuristic-for-send`, `max-suggested-slice-pattern-length`, `third-party` at line 5 column 1
+error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `avoid-breaking-exported-api`, `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `disallowed-types`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `standard-macro-braces`, `enforced-import-renames`, `allowed-scripts`, `enable-raw-pointer-heuristic-for-send`, `max-suggested-slice-pattern-length`, `await-holding-invalid-types`, `max-include-file-size`, `third-party` at line 5 column 1
error: aborting due to previous error
-#![allow(non_fmt_panics)]
+#![allow(non_fmt_panics, clippy::needless_bool)]
macro_rules! assert_const {
($len:expr) => {
assert_const!(3);
assert_const!(-1);
- // Don't lint on this:
+ // Don't lint if based on `cfg!(..)`:
assert!(cfg!(feature = "hey") || cfg!(not(feature = "asdf")));
+
+ let flag: bool = cfg!(not(feature = "asdf"));
+ assert!(flag);
+
+ const CFG_FLAG: &bool = &cfg!(feature = "hey");
+ assert!(!CFG_FLAG);
}
#[proc_macro_derive(DeriveSomething)]
pub fn derive(_: TokenStream) -> TokenStream {
- // Shound not trigger `used_underscore_binding`
+ // Should not trigger `used_underscore_binding`
let _inside_derive = 1;
assert_eq!(_inside_derive, _inside_derive);
--- /dev/null
+// compile-flags: --emit=link
+// no-prefer-dynamic
+
+#![crate_type = "proc-macro"]
+
+extern crate proc_macro;
+
+use proc_macro::{token_stream::IntoIter, Group, Span, TokenStream, TokenTree};
+
+#[proc_macro]
+pub fn with_span(input: TokenStream) -> TokenStream {
+ let mut iter = input.into_iter();
+ let span = iter.next().unwrap().span();
+ let mut res = TokenStream::new();
+ write_with_span(span, iter, &mut res);
+ res
+}
+
+fn write_with_span(s: Span, input: IntoIter, out: &mut TokenStream) {
+ for mut tt in input {
+ if let TokenTree::Group(g) = tt {
+ let mut stream = TokenStream::new();
+ write_with_span(s, g.stream().into_iter(), &mut stream);
+ let mut group = Group::new(g.delimiter(), stream);
+ group.set_span(s);
+ out.extend([TokenTree::Group(group)]);
+ } else {
+ tt.set_span(s);
+ out.extend([tt]);
+ }
+ }
+}
--- /dev/null
+// run-rustfix
+#![warn(clippy::bytes_count_to_len)]
+use std::fs::File;
+use std::io::Read;
+
+fn main() {
+ // should fix, because type is String
+ let _ = String::from("foo").len();
+
+ let s1 = String::from("foo");
+ let _ = s1.len();
+
+ // should fix, because type is &str
+ let _ = "foo".len();
+
+ let s2 = "foo";
+ let _ = s2.len();
+
+ // make sure using count() normally doesn't trigger warning
+ let vector = [0, 1, 2];
+ let _ = vector.iter().count();
+
+ // The type is slice, so should not fix
+ let _ = &[1, 2, 3].bytes().count();
+
+ let bytes: &[u8] = &[1, 2, 3];
+ bytes.bytes().count();
+
+ // The type is File, so should not fix
+ let _ = File::open("foobar").unwrap().bytes().count();
+
+ let f = File::open("foobar").unwrap();
+ let _ = f.bytes().count();
+}
--- /dev/null
+// run-rustfix
+#![warn(clippy::bytes_count_to_len)]
+use std::fs::File;
+use std::io::Read;
+
+fn main() {
+ // should fix, because type is String
+ let _ = String::from("foo").bytes().count();
+
+ let s1 = String::from("foo");
+ let _ = s1.bytes().count();
+
+ // should fix, because type is &str
+ let _ = "foo".bytes().count();
+
+ let s2 = "foo";
+ let _ = s2.bytes().count();
+
+ // make sure using count() normally doesn't trigger warning
+ let vector = [0, 1, 2];
+ let _ = vector.iter().count();
+
+ // The type is slice, so should not fix
+ let _ = &[1, 2, 3].bytes().count();
+
+ let bytes: &[u8] = &[1, 2, 3];
+ bytes.bytes().count();
+
+ // The type is File, so should not fix
+ let _ = File::open("foobar").unwrap().bytes().count();
+
+ let f = File::open("foobar").unwrap();
+ let _ = f.bytes().count();
+}
--- /dev/null
+error: using long and hard to read `.bytes().count()`
+ --> $DIR/bytes_count_to_len.rs:8:13
+ |
+LL | let _ = String::from("foo").bytes().count();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.len()` instead: `String::from("foo").len()`
+ |
+ = note: `-D clippy::bytes-count-to-len` implied by `-D warnings`
+
+error: using long and hard to read `.bytes().count()`
+ --> $DIR/bytes_count_to_len.rs:11:13
+ |
+LL | let _ = s1.bytes().count();
+ | ^^^^^^^^^^^^^^^^^^ help: consider calling `.len()` instead: `s1.len()`
+
+error: using long and hard to read `.bytes().count()`
+ --> $DIR/bytes_count_to_len.rs:14:13
+ |
+LL | let _ = "foo".bytes().count();
+ | ^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.len()` instead: `"foo".len()`
+
+error: using long and hard to read `.bytes().count()`
+ --> $DIR/bytes_count_to_len.rs:17:13
+ |
+LL | let _ = s2.bytes().count();
+ | ^^^^^^^^^^^^^^^^^^ help: consider calling `.len()` instead: `s2.len()`
+
+error: aborting due to 4 previous errors
+
#![feature(repr128)]
#![allow(incomplete_features)]
-
-#[warn(
+#![warn(
clippy::cast_precision_loss,
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
clippy::cast_possible_wrap
)]
-#[allow(clippy::cast_abs_to_unsigned, clippy::no_effect, clippy::unnecessary_operation)]
+#![allow(clippy::cast_abs_to_unsigned, clippy::no_effect, clippy::unnecessary_operation)]
+
fn main() {
// Test clippy::cast_precision_loss
let x0 = 1i32;
}
}
}
+
+fn avoid_subtract_overflow(q: u32) {
+ let c = (q >> 16) as u8;
+ c as usize;
+
+ let c = (q / 1000) as u8;
+ c as usize;
+}
LL | let _ = self as u16;
| ^^^^^^^^^^^
-error: aborting due to 31 previous errors
+error: casting `u32` to `u8` may truncate the value
+ --> $DIR/cast.rs:257:13
+ |
+LL | let c = (q >> 16) as u8;
+ | ^^^^^^^^^^^^^^^
+
+error: casting `u32` to `u8` may truncate the value
+ --> $DIR/cast.rs:260:13
+ |
+LL | let c = (q / 1000) as u8;
+ | ^^^^^^^^^^^^^^^^
+
+error: aborting due to 33 previous errors
let _ = core::ptr::read_unaligned(ptr as *const u16);
let _ = core::intrinsics::unaligned_volatile_load(ptr as *const u16);
let ptr = &mut data as *mut [u8; 2] as *mut u8;
- let _ = (ptr as *mut u16).write_unaligned(0);
- let _ = core::ptr::write_unaligned(ptr as *mut u16, 0);
- let _ = core::intrinsics::unaligned_volatile_store(ptr as *mut u16, 0);
+ (ptr as *mut u16).write_unaligned(0);
+ core::ptr::write_unaligned(ptr as *mut u16, 0);
+ core::intrinsics::unaligned_volatile_store(ptr as *mut u16, 0);
}
}
+#![allow(clippy::let_unit_value)]
+
fn main() {
let x: [i32; 3] = [1_i32, 2, 3];
let r_x = &x;
let long_chain_restore =
r_x as *const [i32] as *const [u32] as *const [u16] as *const [i8] as *const [u8] as *const [u32];
}
+
+// foo and foo2 should not fire, they're the same size
+fn foo(x: *mut [u8]) -> *mut [u8] {
+ x as *mut [u8]
+}
+
+fn foo2(x: *mut [u8]) -> *mut [u8] {
+ x as *mut _
+}
+
+// Test that casts as part of function returns work
+fn bar(x: *mut [u16]) -> *mut [u8] {
+ x as *mut [u8]
+}
+
+fn uwu(x: *mut [u16]) -> *mut [u8] {
+ x as *mut _
+}
+
+fn bar2(x: *mut [u16]) -> *mut [u8] {
+ x as _
+}
+
+// constify
+fn bar3(x: *mut [u16]) -> *const [u8] {
+ x as _
+}
+
+// unconstify
+fn bar4(x: *const [u16]) -> *mut [u8] {
+ x as _
+}
+
+// function returns plus blocks
+fn blocks(x: *mut [u16]) -> *mut [u8] {
+ ({ x }) as _
+}
+
+fn more_blocks(x: *mut [u16]) -> *mut [u8] {
+ { ({ x }) as _ }
+}
error: casting between raw pointers to `[i32]` (element size 4) and `[u8]` (element size 1) does not adjust the count
- --> $DIR/cast_slice_different_sizes.rs:7:13
+ --> $DIR/cast_slice_different_sizes.rs:9:13
|
LL | let b = a as *const [u8];
| ^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts(a as *const u8, ..)`
= note: `#[deny(clippy::cast_slice_different_sizes)]` on by default
error: casting between raw pointers to `[u8]` (element size 1) and `[u32]` (element size 4) does not adjust the count
- --> $DIR/cast_slice_different_sizes.rs:8:13
+ --> $DIR/cast_slice_different_sizes.rs:10:13
|
LL | let c = b as *const [u32];
| ^^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts(b as *const u32, ..)`
error: casting between raw pointers to `[i32]` (element size 4) and `[u8]` (element size 1) does not adjust the count
- --> $DIR/cast_slice_different_sizes.rs:11:16
+ --> $DIR/cast_slice_different_sizes.rs:13:16
|
LL | let loss = r_x as *const [i32] as *const [u8];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts(r_x as *const [i32] as *const u8, ..)`
error: casting between raw pointers to `[i32]` (element size 4) and `[u8]` (element size 1) does not adjust the count
- --> $DIR/cast_slice_different_sizes.rs:18:24
+ --> $DIR/cast_slice_different_sizes.rs:20:24
|
LL | let loss_block_1 = { r_x as *const [i32] } as *const [u8];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts({ r_x as *const [i32] } as *const u8, ..)`
error: casting between raw pointers to `[i32]` (element size 4) and `[u8]` (element size 1) does not adjust the count
- --> $DIR/cast_slice_different_sizes.rs:19:24
+ --> $DIR/cast_slice_different_sizes.rs:21:24
|
LL | let loss_block_2 = {
| ________________________^
|
error: casting between raw pointers to `[i32]` (element size 4) and `[u8]` (element size 1) does not adjust the count
- --> $DIR/cast_slice_different_sizes.rs:36:27
+ --> $DIR/cast_slice_different_sizes.rs:38:27
|
LL | let long_chain_loss = r_x as *const [i32] as *const [u32] as *const [u16] as *const [i8] as *const [u8];
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts(r_x as *const [i32] as *const [u32] as *const [u16] as *const [i8] as *const u8, ..)`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts(r_x as *const [i32] as *const u8, ..)`
-error: aborting due to 6 previous errors
+error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count
+ --> $DIR/cast_slice_different_sizes.rs:53:36
+ |
+LL | fn bar(x: *mut [u16]) -> *mut [u8] {
+ | ____________________________________^
+LL | | x as *mut [u8]
+LL | | }
+ | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(x as *mut u8, ..)`
+
+error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count
+ --> $DIR/cast_slice_different_sizes.rs:57:36
+ |
+LL | fn uwu(x: *mut [u16]) -> *mut [u8] {
+ | ____________________________________^
+LL | | x as *mut _
+LL | | }
+ | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(x as *mut u8, ..)`
+
+error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count
+ --> $DIR/cast_slice_different_sizes.rs:61:37
+ |
+LL | fn bar2(x: *mut [u16]) -> *mut [u8] {
+ | _____________________________________^
+LL | | x as _
+LL | | }
+ | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(x as *mut u8, ..)`
+
+error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count
+ --> $DIR/cast_slice_different_sizes.rs:66:39
+ |
+LL | fn bar3(x: *mut [u16]) -> *const [u8] {
+ | _______________________________________^
+LL | | x as _
+LL | | }
+ | |_^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts(x as *const u8, ..)`
+
+error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count
+ --> $DIR/cast_slice_different_sizes.rs:71:39
+ |
+LL | fn bar4(x: *const [u16]) -> *mut [u8] {
+ | _______________________________________^
+LL | | x as _
+LL | | }
+ | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(x as *mut u8, ..)`
+
+error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count
+ --> $DIR/cast_slice_different_sizes.rs:76:39
+ |
+LL | fn blocks(x: *mut [u16]) -> *mut [u8] {
+ | _______________________________________^
+LL | | ({ x }) as _
+LL | | }
+ | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(({ x }) as *mut u8, ..)`
+
+error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count
+ --> $DIR/cast_slice_different_sizes.rs:80:44
+ |
+LL | fn more_blocks(x: *mut [u16]) -> *mut [u8] {
+ | ____________________________________________^
+LL | | { ({ x }) as _ }
+LL | | }
+ | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(({ x }) as *mut u8, ..)`
+
+error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count
+ --> $DIR/cast_slice_different_sizes.rs:81:5
+ |
+LL | { ({ x }) as _ }
+ | ^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(({ x }) as *mut u8, ..)`
+
+error: aborting due to 14 previous errors
}
}
}
+
+#[rustfmt::skip]
+#[allow(dead_code)]
+fn issue_7318() {
+ if true { println!("I've been resolved!")
+ }else if false {}
+}
}
}
}
+
+#[rustfmt::skip]
+#[allow(dead_code)]
+fn issue_7318() {
+ if true { println!("I've been resolved!")
+ }else{
+ if false {}
+ }
+}
LL + }
|
-error: aborting due to 7 previous errors
+error: this `else { if .. }` block can be collapsed
+ --> $DIR/collapsible_else_if.rs:97:10
+ |
+LL | }else{
+ | __________^
+LL | | if false {}
+LL | | }
+ | |_____^ help: collapse nested if block: `if false {}`
+
+error: aborting due to 8 previous errors
--- /dev/null
+pub fn foo(x: &u32) -> u32 {
+ /* Safety:
+ * This is totally ok.
+ */
+ unsafe { *(x as *const u32) }
+}
-#[allow(dead_code)]
+#![allow(dead_code, clippy::extra_unused_lifetimes)]
/// Test for https://github.com/rust-lang/rust-clippy/issues/2865
-/// Test for https://github.com/rust-lang/rust-clippy/issues/2865
+/// Test for https://github.com/rust-lang/rust-clippy/issues/3151
#[derive(Clone)]
pub struct HashMap<V, S> {
#![warn(clippy::repeat_once)]
+#![allow(clippy::let_unit_value)]
trait Repeat {
fn repeat(&self) {}
-error: manual implementation of `split_once`
- --> $DIR/ice-8250.rs:2:13
- |
-LL | let _ = s[1..].splitn(2, '.').next()?;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s[1..].split_once('.').map_or(s[1..], |x| x.0)`
- |
- = note: `-D clippy::manual-split-once` implied by `-D warnings`
-
error: unnecessary use of `splitn`
--> $DIR/ice-8250.rs:2:13
|
|
= note: `-D clippy::needless-splitn` implied by `-D warnings`
-error: aborting due to 2 previous errors
+error: aborting due to previous error
--- /dev/null
+// aux-build: ice-8681-aux.rs
+
+#![warn(clippy::undocumented_unsafe_blocks)]
+
+#[path = "auxiliary/ice-8681-aux.rs"]
+mod ice_8681_aux;
+
+fn main() {
+ let _ = ice_8681_aux::foo(&0u32);
+}
--- /dev/null
+macro_rules! foo {
+ () => {
+ "bar.rs"
+ };
+}
+
+#[path = foo!()] //~ ERROR malformed `path` attribute
+mod abc {}
+
+fn main() {}
--- /dev/null
+error: malformed `path` attribute input
+ --> $DIR/ice-96721.rs:7:1
+ |
+LL | #[path = foo!()] //~ ERROR malformed `path` attribute
+ | ^^^^^^^^^^^^^^^^ help: must be of the form: `#[path = "file"]`
+
+error: aborting due to previous error
+
// aux-build:macro_rules.rs
#![warn(clippy::default_numeric_fallback)]
-#![allow(unused)]
-#![allow(clippy::never_loop)]
-#![allow(clippy::no_effect)]
-#![allow(clippy::unnecessary_operation)]
-#![allow(clippy::branches_sharing_code)]
-#![allow(clippy::match_single_binding)]
+#![allow(
+ unused,
+ clippy::never_loop,
+ clippy::no_effect,
+ clippy::unnecessary_operation,
+ clippy::branches_sharing_code,
+ clippy::match_single_binding,
+ clippy::let_unit_value
+)]
#[macro_use]
extern crate macro_rules;
// aux-build:macro_rules.rs
#![warn(clippy::default_numeric_fallback)]
-#![allow(unused)]
-#![allow(clippy::never_loop)]
-#![allow(clippy::no_effect)]
-#![allow(clippy::unnecessary_operation)]
-#![allow(clippy::branches_sharing_code)]
-#![allow(clippy::match_single_binding)]
+#![allow(
+ unused,
+ clippy::never_loop,
+ clippy::no_effect,
+ clippy::unnecessary_operation,
+ clippy::branches_sharing_code,
+ clippy::match_single_binding,
+ clippy::let_unit_value
+)]
#[macro_use]
extern crate macro_rules;
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_f64.rs:18:17
+ --> $DIR/default_numeric_fallback_f64.rs:21:17
|
LL | let x = 0.12;
| ^^^^ help: consider adding suffix: `0.12_f64`
= note: `-D clippy::default-numeric-fallback` implied by `-D warnings`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_f64.rs:19:18
+ --> $DIR/default_numeric_fallback_f64.rs:22:18
|
LL | let x = [1., 2., 3.];
| ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_f64.rs:19:22
+ --> $DIR/default_numeric_fallback_f64.rs:22:22
|
LL | let x = [1., 2., 3.];
| ^^ help: consider adding suffix: `2.0_f64`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_f64.rs:19:26
+ --> $DIR/default_numeric_fallback_f64.rs:22:26
|
LL | let x = [1., 2., 3.];
| ^^ help: consider adding suffix: `3.0_f64`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_f64.rs:20:28
+ --> $DIR/default_numeric_fallback_f64.rs:23:28
|
LL | let x = if true { (1., 2.) } else { (3., 4.) };
| ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_f64.rs:20:32
+ --> $DIR/default_numeric_fallback_f64.rs:23:32
|
LL | let x = if true { (1., 2.) } else { (3., 4.) };
| ^^ help: consider adding suffix: `2.0_f64`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_f64.rs:20:46
+ --> $DIR/default_numeric_fallback_f64.rs:23:46
|
LL | let x = if true { (1., 2.) } else { (3., 4.) };
| ^^ help: consider adding suffix: `3.0_f64`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_f64.rs:20:50
+ --> $DIR/default_numeric_fallback_f64.rs:23:50
|
LL | let x = if true { (1., 2.) } else { (3., 4.) };
| ^^ help: consider adding suffix: `4.0_f64`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_f64.rs:21:23
+ --> $DIR/default_numeric_fallback_f64.rs:24:23
|
LL | let x = match 1. {
| ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_f64.rs:22:18
+ --> $DIR/default_numeric_fallback_f64.rs:25:18
|
LL | _ => 1.,
| ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_f64.rs:40:21
+ --> $DIR/default_numeric_fallback_f64.rs:43:21
|
LL | let y = 1.;
| ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_f64.rs:48:21
+ --> $DIR/default_numeric_fallback_f64.rs:51:21
|
LL | let y = 1.;
| ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_f64.rs:54:21
+ --> $DIR/default_numeric_fallback_f64.rs:57:21
|
LL | let y = 1.;
| ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_f64.rs:66:9
+ --> $DIR/default_numeric_fallback_f64.rs:69:9
|
LL | 1.
| ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_f64.rs:72:27
+ --> $DIR/default_numeric_fallback_f64.rs:75:27
|
LL | let f = || -> _ { 1. };
| ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_f64.rs:76:29
+ --> $DIR/default_numeric_fallback_f64.rs:79:29
|
LL | let f = || -> f64 { 1. };
| ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_f64.rs:90:21
+ --> $DIR/default_numeric_fallback_f64.rs:93:21
|
LL | generic_arg(1.);
| ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_f64.rs:93:32
+ --> $DIR/default_numeric_fallback_f64.rs:96:32
|
LL | let x: _ = generic_arg(1.);
| ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_f64.rs:111:28
+ --> $DIR/default_numeric_fallback_f64.rs:114:28
|
LL | GenericStruct { x: 1. };
| ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_f64.rs:114:36
+ --> $DIR/default_numeric_fallback_f64.rs:117:36
|
LL | let _ = GenericStruct { x: 1. };
| ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_f64.rs:132:24
+ --> $DIR/default_numeric_fallback_f64.rs:135:24
|
LL | GenericEnum::X(1.);
| ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_f64.rs:152:23
+ --> $DIR/default_numeric_fallback_f64.rs:155:23
|
LL | s.generic_arg(1.);
| ^^ help: consider adding suffix: `1.0_f64`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_f64.rs:159:21
+ --> $DIR/default_numeric_fallback_f64.rs:162:21
|
LL | let x = 22.;
| ^^^ help: consider adding suffix: `22.0_f64`
// aux-build:macro_rules.rs
#![warn(clippy::default_numeric_fallback)]
-#![allow(unused)]
-#![allow(clippy::never_loop)]
-#![allow(clippy::no_effect)]
-#![allow(clippy::unnecessary_operation)]
-#![allow(clippy::branches_sharing_code)]
+#![allow(
+ unused,
+ clippy::never_loop,
+ clippy::no_effect,
+ clippy::unnecessary_operation,
+ clippy::branches_sharing_code,
+ clippy::let_unit_value
+)]
#[macro_use]
extern crate macro_rules;
// aux-build:macro_rules.rs
#![warn(clippy::default_numeric_fallback)]
-#![allow(unused)]
-#![allow(clippy::never_loop)]
-#![allow(clippy::no_effect)]
-#![allow(clippy::unnecessary_operation)]
-#![allow(clippy::branches_sharing_code)]
+#![allow(
+ unused,
+ clippy::never_loop,
+ clippy::no_effect,
+ clippy::unnecessary_operation,
+ clippy::branches_sharing_code,
+ clippy::let_unit_value
+)]
#[macro_use]
extern crate macro_rules;
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_i32.rs:17:17
+ --> $DIR/default_numeric_fallback_i32.rs:20:17
|
LL | let x = 22;
| ^^ help: consider adding suffix: `22_i32`
= note: `-D clippy::default-numeric-fallback` implied by `-D warnings`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_i32.rs:18:18
+ --> $DIR/default_numeric_fallback_i32.rs:21:18
|
LL | let x = [1, 2, 3];
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_i32.rs:18:21
+ --> $DIR/default_numeric_fallback_i32.rs:21:21
|
LL | let x = [1, 2, 3];
| ^ help: consider adding suffix: `2_i32`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_i32.rs:18:24
+ --> $DIR/default_numeric_fallback_i32.rs:21:24
|
LL | let x = [1, 2, 3];
| ^ help: consider adding suffix: `3_i32`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_i32.rs:19:28
+ --> $DIR/default_numeric_fallback_i32.rs:22:28
|
LL | let x = if true { (1, 2) } else { (3, 4) };
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_i32.rs:19:31
+ --> $DIR/default_numeric_fallback_i32.rs:22:31
|
LL | let x = if true { (1, 2) } else { (3, 4) };
| ^ help: consider adding suffix: `2_i32`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_i32.rs:19:44
+ --> $DIR/default_numeric_fallback_i32.rs:22:44
|
LL | let x = if true { (1, 2) } else { (3, 4) };
| ^ help: consider adding suffix: `3_i32`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_i32.rs:19:47
+ --> $DIR/default_numeric_fallback_i32.rs:22:47
|
LL | let x = if true { (1, 2) } else { (3, 4) };
| ^ help: consider adding suffix: `4_i32`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_i32.rs:20:23
+ --> $DIR/default_numeric_fallback_i32.rs:23:23
|
LL | let x = match 1 {
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_i32.rs:21:13
+ --> $DIR/default_numeric_fallback_i32.rs:24:13
|
LL | 1 => 1,
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_i32.rs:21:18
+ --> $DIR/default_numeric_fallback_i32.rs:24:18
|
LL | 1 => 1,
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_i32.rs:22:18
+ --> $DIR/default_numeric_fallback_i32.rs:25:18
|
LL | _ => 2,
| ^ help: consider adding suffix: `2_i32`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_i32.rs:39:21
+ --> $DIR/default_numeric_fallback_i32.rs:42:21
|
LL | let y = 1;
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_i32.rs:47:21
+ --> $DIR/default_numeric_fallback_i32.rs:50:21
|
LL | let y = 1;
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_i32.rs:53:21
+ --> $DIR/default_numeric_fallback_i32.rs:56:21
|
LL | let y = 1;
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_i32.rs:65:9
+ --> $DIR/default_numeric_fallback_i32.rs:68:9
|
LL | 1
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_i32.rs:71:27
+ --> $DIR/default_numeric_fallback_i32.rs:74:27
|
LL | let f = || -> _ { 1 };
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_i32.rs:75:29
+ --> $DIR/default_numeric_fallback_i32.rs:78:29
|
LL | let f = || -> i32 { 1 };
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_i32.rs:89:21
+ --> $DIR/default_numeric_fallback_i32.rs:92:21
|
LL | generic_arg(1);
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_i32.rs:92:32
+ --> $DIR/default_numeric_fallback_i32.rs:95:32
|
LL | let x: _ = generic_arg(1);
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_i32.rs:110:28
+ --> $DIR/default_numeric_fallback_i32.rs:113:28
|
LL | GenericStruct { x: 1 };
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_i32.rs:113:36
+ --> $DIR/default_numeric_fallback_i32.rs:116:36
|
LL | let _ = GenericStruct { x: 1 };
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_i32.rs:131:24
+ --> $DIR/default_numeric_fallback_i32.rs:134:24
|
LL | GenericEnum::X(1);
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_i32.rs:151:23
+ --> $DIR/default_numeric_fallback_i32.rs:154:23
|
LL | s.generic_arg(1);
| ^ help: consider adding suffix: `1_i32`
error: default numeric fallback might occur
- --> $DIR/default_numeric_fallback_i32.rs:158:21
+ --> $DIR/default_numeric_fallback_i32.rs:161:21
|
LL | let x = 22;
| ^^ help: consider adding suffix: `22_i32`
+// This file was generated by `cargo dev update_lints`.
+// Use that command to update this file and do not edit by hand.
+// Manual edits will be overwritten.
+
#![warn(clippy::should_assert_eq)]
#![warn(clippy::extend_from_slice)]
#![warn(clippy::range_step_by_zero)]
error: lint `clippy::should_assert_eq` has been removed: `assert!()` will be more flexible with RFC 2011
- --> $DIR/deprecated.rs:1:9
+ --> $DIR/deprecated.rs:5:9
|
LL | #![warn(clippy::should_assert_eq)]
| ^^^^^^^^^^^^^^^^^^^^^^^^
= note: `-D renamed-and-removed-lints` implied by `-D warnings`
error: lint `clippy::extend_from_slice` has been removed: `.extend_from_slice(_)` is a faster way to extend a Vec by a slice
- --> $DIR/deprecated.rs:2:9
+ --> $DIR/deprecated.rs:6:9
|
LL | #![warn(clippy::extend_from_slice)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
error: lint `clippy::range_step_by_zero` has been removed: `iterator.step_by(0)` panics nowadays
- --> $DIR/deprecated.rs:3:9
+ --> $DIR/deprecated.rs:7:9
|
LL | #![warn(clippy::range_step_by_zero)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
error: lint `clippy::unstable_as_slice` has been removed: `Vec::as_slice` has been stabilized in 1.7
- --> $DIR/deprecated.rs:4:9
+ --> $DIR/deprecated.rs:8:9
|
LL | #![warn(clippy::unstable_as_slice)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
error: lint `clippy::unstable_as_mut_slice` has been removed: `Vec::as_mut_slice` has been stabilized in 1.7
- --> $DIR/deprecated.rs:5:9
+ --> $DIR/deprecated.rs:9:9
|
LL | #![warn(clippy::unstable_as_mut_slice)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: lint `clippy::misaligned_transmute` has been removed: this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr
- --> $DIR/deprecated.rs:6:9
+ --> $DIR/deprecated.rs:10:9
|
LL | #![warn(clippy::misaligned_transmute)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: lint `clippy::assign_ops` has been removed: using compound assignment operators (e.g., `+=`) is harmless
- --> $DIR/deprecated.rs:7:9
+ --> $DIR/deprecated.rs:11:9
|
LL | #![warn(clippy::assign_ops)]
| ^^^^^^^^^^^^^^^^^^
error: lint `clippy::if_let_redundant_pattern_matching` has been removed: this lint has been changed to redundant_pattern_matching
- --> $DIR/deprecated.rs:8:9
+ --> $DIR/deprecated.rs:12:9
|
LL | #![warn(clippy::if_let_redundant_pattern_matching)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: lint `clippy::unsafe_vector_initialization` has been removed: the replacement suggested by this lint had substantially different behavior
- --> $DIR/deprecated.rs:9:9
+ --> $DIR/deprecated.rs:13:9
|
LL | #![warn(clippy::unsafe_vector_initialization)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: lint `clippy::unused_collect` has been removed: `collect` has been marked as #[must_use] in rustc and that covers all cases of this lint
- --> $DIR/deprecated.rs:10:9
+ --> $DIR/deprecated.rs:14:9
|
LL | #![warn(clippy::unused_collect)]
| ^^^^^^^^^^^^^^^^^^^^^^
error: lint `clippy::replace_consts` has been removed: associated-constants `MIN`/`MAX` of integers are preferred to `{min,max}_value()` and module constants
- --> $DIR/deprecated.rs:11:9
+ --> $DIR/deprecated.rs:15:9
|
LL | #![warn(clippy::replace_consts)]
| ^^^^^^^^^^^^^^^^^^^^^^
error: lint `clippy::regex_macro` has been removed: the regex! macro has been removed from the regex crate in 2018
- --> $DIR/deprecated.rs:12:9
+ --> $DIR/deprecated.rs:16:9
|
LL | #![warn(clippy::regex_macro)]
| ^^^^^^^^^^^^^^^^^^^
error: lint `clippy::find_map` has been removed: this lint has been replaced by `manual_find_map`, a more specific lint
- --> $DIR/deprecated.rs:13:9
+ --> $DIR/deprecated.rs:17:9
|
LL | #![warn(clippy::find_map)]
| ^^^^^^^^^^^^^^^^
error: lint `clippy::filter_map` has been removed: this lint has been replaced by `manual_filter_map`, a more specific lint
- --> $DIR/deprecated.rs:14:9
+ --> $DIR/deprecated.rs:18:9
|
LL | #![warn(clippy::filter_map)]
| ^^^^^^^^^^^^^^^^^^
error: lint `clippy::pub_enum_variant_names` has been removed: set the `avoid-breaking-exported-api` config option to `false` to enable the `enum_variant_names` lint for public items
- --> $DIR/deprecated.rs:15:9
+ --> $DIR/deprecated.rs:19:9
|
LL | #![warn(clippy::pub_enum_variant_names)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: lint `clippy::wrong_pub_self_convention` has been removed: set the `avoid-breaking-exported-api` config option to `false` to enable the `wrong_self_convention` lint for public items
- --> $DIR/deprecated.rs:16:9
+ --> $DIR/deprecated.rs:20:9
|
LL | #![warn(clippy::wrong_pub_self_convention)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// aux-build:doc_unsafe_macros.rs
+#![allow(clippy::let_unit_value)]
+
#[macro_use]
extern crate doc_unsafe_macros;
error: unsafe function's docs miss `# Safety` section
- --> $DIR/doc_unsafe.rs:7:1
+ --> $DIR/doc_unsafe.rs:9:1
|
LL | / pub unsafe fn destroy_the_planet() {
LL | | unimplemented!();
= note: `-D clippy::missing-safety-doc` implied by `-D warnings`
error: unsafe function's docs miss `# Safety` section
- --> $DIR/doc_unsafe.rs:30:5
+ --> $DIR/doc_unsafe.rs:32:5
|
LL | / pub unsafe fn republished() {
LL | | unimplemented!();
| |_____^
error: unsafe function's docs miss `# Safety` section
- --> $DIR/doc_unsafe.rs:38:5
+ --> $DIR/doc_unsafe.rs:40:5
|
LL | unsafe fn woefully_underdocumented(self);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: docs for unsafe trait missing `# Safety` section
- --> $DIR/doc_unsafe.rs:44:1
+ --> $DIR/doc_unsafe.rs:46:1
|
LL | / pub unsafe trait UnsafeTrait {
LL | | fn method();
| |_^
error: unsafe function's docs miss `# Safety` section
- --> $DIR/doc_unsafe.rs:74:5
+ --> $DIR/doc_unsafe.rs:76:5
|
LL | / pub unsafe fn more_undocumented_unsafe() -> Self {
LL | | unimplemented!();
| |_____^
error: unsafe function's docs miss `# Safety` section
- --> $DIR/doc_unsafe.rs:90:9
+ --> $DIR/doc_unsafe.rs:92:9
|
LL | / pub unsafe fn whee() {
LL | | unimplemented!()
-error: call to `std::mem::drop` with a value that does not implement `Drop`. Dropping such a type only extends it's contained lifetimes
+error: call to `std::mem::drop` with a value that does not implement `Drop`. Dropping such a type only extends its contained lifetimes
--> $DIR/drop_non_drop.rs:22:5
|
LL | drop(Foo);
LL | drop(Foo);
| ^^^
-error: call to `std::mem::drop` with a value that does not implement `Drop`. Dropping such a type only extends it's contained lifetimes
+error: call to `std::mem::drop` with a value that does not implement `Drop`. Dropping such a type only extends its contained lifetimes
--> $DIR/drop_non_drop.rs:37:5
|
LL | drop(Baz(Foo));
--- /dev/null
+// run-rustfix
+#![warn(clippy::empty_drop)]
+#![allow(unused)]
+
+// should cause an error
+struct Foo;
+
+
+
+// shouldn't cause an error
+struct Bar;
+
+impl Drop for Bar {
+ fn drop(&mut self) {
+ println!("dropping bar!");
+ }
+}
+
+// should error
+struct Baz;
+
+
+
+fn main() {}
--- /dev/null
+// run-rustfix
+#![warn(clippy::empty_drop)]
+#![allow(unused)]
+
+// should cause an error
+struct Foo;
+
+impl Drop for Foo {
+ fn drop(&mut self) {}
+}
+
+// shouldn't cause an error
+struct Bar;
+
+impl Drop for Bar {
+ fn drop(&mut self) {
+ println!("dropping bar!");
+ }
+}
+
+// should error
+struct Baz;
+
+impl Drop for Baz {
+ fn drop(&mut self) {
+ {}
+ }
+}
+
+fn main() {}
--- /dev/null
+error: empty drop implementation
+ --> $DIR/empty_drop.rs:8:1
+ |
+LL | / impl Drop for Foo {
+LL | | fn drop(&mut self) {}
+LL | | }
+ | |_^ help: try removing this impl
+ |
+ = note: `-D clippy::empty-drop` implied by `-D warnings`
+
+error: empty drop implementation
+ --> $DIR/empty_drop.rs:24:1
+ |
+LL | / impl Drop for Baz {
+LL | | fn drop(&mut self) {
+LL | | {}
+LL | | }
+LL | | }
+ | |_^ help: try removing this impl
+
+error: aborting due to 2 previous errors
+
}
// See #815
- let e = Some(1u8).map(divergent);
+ let e = Some(1u8).map(|a| divergent(a));
let e = Some(1u8).map(generic);
let e = Some(1u8).map(generic);
// See #515
let mut closure = |n| value += n;
for _ in 0..5 {
Some(1).map(&mut closure);
+
+ let mut value = 0;
+ let mut in_loop = |n| value += n;
+ Some(1).map(in_loop);
}
}
{
}
map_str(|s| take_asref_path(s));
- map_str_to_path(std::convert::AsRef::as_ref);
+ map_str_to_path(|s| s.as_ref());
}
mod type_param_bound {
Some(A).map(|ref a| B::from(a));
}
}
+
+// #7812 False positive on coerced closure
+fn coerced_closure() {
+ fn function_returning_unit<F: FnMut(i32)>(f: F) {}
+ function_returning_unit(|x| std::process::exit(x));
+
+ fn arr() -> &'static [u8; 0] {
+ &[]
+ }
+ fn slice_fn(_: impl FnOnce() -> &'static [u8]) {}
+ slice_fn(|| arr());
+}
let mut closure = |n| value += n;
for _ in 0..5 {
Some(1).map(|n| closure(n));
+
+ let mut value = 0;
+ let mut in_loop = |n| value += n;
+ Some(1).map(|n| in_loop(n));
}
}
Some(A).map(|ref a| B::from(a));
}
}
+
+// #7812 False positive on coerced closure
+fn coerced_closure() {
+ fn function_returning_unit<F: FnMut(i32)>(f: F) {}
+ function_returning_unit(|x| std::process::exit(x));
+
+ fn arr() -> &'static [u8; 0] {
+ &[]
+ }
+ fn slice_fn(_: impl FnOnce() -> &'static [u8]) {}
+ slice_fn(|| arr());
+}
LL | all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted
| ^^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `below`
-error: redundant closure
- --> $DIR/eta.rs:40:27
- |
-LL | let e = Some(1u8).map(|a| divergent(a));
- | ^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `divergent`
-
error: redundant closure
--> $DIR/eta.rs:41:27
|
| ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut closure`
error: redundant closure
- --> $DIR/eta.rs:232:21
+ --> $DIR/eta.rs:217:21
|
-LL | map_str_to_path(|s| s.as_ref());
- | ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::convert::AsRef::as_ref`
+LL | Some(1).map(|n| in_loop(n));
+ | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `in_loop`
-error: aborting due to 20 previous errors
+error: aborting due to 19 previous errors
}
}
+mod issue6437 {
+ pub struct Scalar;
+
+ impl<'a> std::ops::AddAssign<&Scalar> for &mut Scalar {
+ fn add_assign(&mut self, _rhs: &Scalar) {
+ unimplemented!();
+ }
+ }
+
+ impl<'b> Scalar {
+ pub fn something<'c>() -> Self {
+ Self
+ }
+ }
+}
+
+// https://github.com/rust-lang/rust-clippy/pull/8737#pullrequestreview-951268213
+mod first_case {
+ use serde::de::Visitor;
+ pub trait Expected {
+ fn fmt(&self, formatter: &mut std::fmt::Formatter);
+ }
+
+ impl<'de, T> Expected for T
+ where
+ T: Visitor<'de>,
+ {
+ fn fmt(&self, formatter: &mut std::fmt::Formatter) {}
+ }
+}
+
+// https://github.com/rust-lang/rust-clippy/pull/8737#pullrequestreview-951268213
+mod second_case {
+ pub trait Source {
+ fn hey();
+ }
+
+ impl<'a, T: Source + ?Sized + 'a> Source for Box<T> {
+ fn hey() {}
+ }
+}
+
fn main() {}
LL | fn unused_lt<'a>(x: u8) {}
| ^^
-error: aborting due to 3 previous errors
+error: this lifetime isn't used in the impl
+ --> $DIR/extra_unused_lifetimes.rs:78:10
+ |
+LL | impl<'a> std::ops::AddAssign<&Scalar> for &mut Scalar {
+ | ^^
+
+error: this lifetime isn't used in the impl
+ --> $DIR/extra_unused_lifetimes.rs:84:10
+ |
+LL | impl<'b> Scalar {
+ | ^^
+
+error: this lifetime isn't used in the function definition
+ --> $DIR/extra_unused_lifetimes.rs:85:26
+ |
+LL | pub fn something<'c>() -> Self {
+ | ^^
+
+error: aborting due to 6 previous errors
--- /dev/null
+#![warn(clippy::format_push_string)]
+
+fn main() {
+ let mut string = String::new();
+ string += &format!("{:?}", 1234);
+ string.push_str(&format!("{:?}", 5678));
+}
--- /dev/null
+error: `format!(..)` appended to existing `String`
+ --> $DIR/format_push_string.rs:5:5
+ |
+LL | string += &format!("{:?}", 1234);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::format-push-string` implied by `-D warnings`
+ = help: consider using `write!` to avoid the extra allocation
+
+error: `format!(..)` appended to existing `String`
+ --> $DIR/format_push_string.rs:6:5
+ |
+LL | string.push_str(&format!("{:?}", 5678));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using `write!` to avoid the extra allocation
+
+error: aborting due to 2 previous errors
+
+use std::fmt::Write as _;
+
const ONE: i64 = 1;
const NEG_ONE: i64 = -1;
const ZERO: i64 = 0;
impl std::ops::Shl<i32> for A {
type Output = A;
fn shl(mut self, other: i32) -> Self {
- self.0.push_str(&format!("{}", other));
+ let _ = write!(self.0, "{}", other);
self
}
}
(x + 1) % 3; // no error
4 % 3; // no error
4 % -3; // no error
+
+ // See #8724
+ let a = 0;
+ let b = true;
+ 0 + if b { 1 } else { 2 };
+ 0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }; // no error
+ 0 + match a { 0 => 10, _ => 20 };
+ 0 + match a { 0 => 10, _ => 20 } + match a { 0 => 30, _ => 40 }; // no error
+ 0 + if b { 1 } else { 2 } + match a { 0 => 30, _ => 40 }; // no error
+ 0 + match a { 0 => 10, _ => 20 } + if b { 3 } else { 4 }; // no error
+
+ 0 + if b { 0 + 1 } else { 2 };
+ 0 + match a { 0 => 0 + 10, _ => 20 };
+ 0 + if b { 0 + 1 } else { 2 } + match a { 0 => 0 + 30, _ => 40 };
+
+ let _ = 0 + if 0 + 1 > 0 { 1 } else { 2 } + if 0 + 1 > 0 { 3 } else { 4 };
+ let _ = 0 + match 0 + 1 { 0 => 10, _ => 20 } + match 0 + 1 { 0 => 30, _ => 40 };
+
+ 0 + if b { 1 } else { 2 } + if b { 3 } else { 4 } + 0;
+
+ 0 + { a } + 3; // no error
+ 0 + loop { let mut c = 0; if c == 10 { break c; } c += 1; } + { a * 2 }; // no error
+
+ fn f(_: i32) {
+ todo!();
+ }
+ f(1 * a + { 8 * 5 });
+ f(0 + if b { 1 } else { 2 } + 3); // no error
+ const _: i32 = { 2 * 4 } + 0 + 3;
+ const _: i32 = 0 + { 1 + 2 * 3 } + 3; // no error
}
error: the operation is ineffective. Consider reducing it to `x`
- --> $DIR/identity_op.rs:37:5
+ --> $DIR/identity_op.rs:39:5
|
LL | x + 0;
| ^^^^^
= note: `-D clippy::identity-op` implied by `-D warnings`
error: the operation is ineffective. Consider reducing it to `x`
- --> $DIR/identity_op.rs:38:5
+ --> $DIR/identity_op.rs:40:5
|
LL | x + (1 - 1);
| ^^^^^^^^^^^
error: the operation is ineffective. Consider reducing it to `x`
- --> $DIR/identity_op.rs:40:5
+ --> $DIR/identity_op.rs:42:5
|
LL | 0 + x;
| ^^^^^
error: the operation is ineffective. Consider reducing it to `x`
- --> $DIR/identity_op.rs:43:5
+ --> $DIR/identity_op.rs:45:5
|
LL | x | (0);
| ^^^^^^^
error: the operation is ineffective. Consider reducing it to `x`
- --> $DIR/identity_op.rs:46:5
+ --> $DIR/identity_op.rs:48:5
|
LL | x * 1;
| ^^^^^
error: the operation is ineffective. Consider reducing it to `x`
- --> $DIR/identity_op.rs:47:5
+ --> $DIR/identity_op.rs:49:5
|
LL | 1 * x;
| ^^^^^
error: the operation is ineffective. Consider reducing it to `x`
- --> $DIR/identity_op.rs:53:5
+ --> $DIR/identity_op.rs:55:5
|
LL | -1 & x;
| ^^^^^^
error: the operation is ineffective. Consider reducing it to `u`
- --> $DIR/identity_op.rs:56:5
+ --> $DIR/identity_op.rs:58:5
|
LL | u & 255;
| ^^^^^^^
error: the operation is ineffective. Consider reducing it to `42`
- --> $DIR/identity_op.rs:59:5
+ --> $DIR/identity_op.rs:61:5
|
LL | 42 << 0;
| ^^^^^^^
error: the operation is ineffective. Consider reducing it to `1`
- --> $DIR/identity_op.rs:60:5
+ --> $DIR/identity_op.rs:62:5
|
LL | 1 >> 0;
| ^^^^^^
error: the operation is ineffective. Consider reducing it to `42`
- --> $DIR/identity_op.rs:61:5
+ --> $DIR/identity_op.rs:63:5
|
LL | 42 >> 0;
| ^^^^^^^
error: the operation is ineffective. Consider reducing it to `&x`
- --> $DIR/identity_op.rs:62:5
+ --> $DIR/identity_op.rs:64:5
|
LL | &x >> 0;
| ^^^^^^^
error: the operation is ineffective. Consider reducing it to `x`
- --> $DIR/identity_op.rs:63:5
+ --> $DIR/identity_op.rs:65:5
|
LL | x >> &0;
| ^^^^^^^
error: the operation is ineffective. Consider reducing it to `2`
- --> $DIR/identity_op.rs:70:5
+ --> $DIR/identity_op.rs:72:5
|
LL | 2 % 3;
| ^^^^^
error: the operation is ineffective. Consider reducing it to `-2`
- --> $DIR/identity_op.rs:71:5
+ --> $DIR/identity_op.rs:73:5
|
LL | -2 % 3;
| ^^^^^^
error: the operation is ineffective. Consider reducing it to `2`
- --> $DIR/identity_op.rs:72:5
+ --> $DIR/identity_op.rs:74:5
|
LL | 2 % -3 + x;
| ^^^^^^
error: the operation is ineffective. Consider reducing it to `-2`
- --> $DIR/identity_op.rs:73:5
+ --> $DIR/identity_op.rs:75:5
|
LL | -2 % -3 + x;
| ^^^^^^^
error: the operation is ineffective. Consider reducing it to `1`
- --> $DIR/identity_op.rs:74:9
+ --> $DIR/identity_op.rs:76:9
|
LL | x + 1 % 3;
| ^^^^^
-error: aborting due to 18 previous errors
+error: the operation is ineffective. Consider reducing it to `if b { 1 } else { 2 }`
+ --> $DIR/identity_op.rs:84:5
+ |
+LL | 0 + if b { 1 } else { 2 };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: the operation is ineffective. Consider reducing it to `match a { 0 => 10, _ => 20 }`
+ --> $DIR/identity_op.rs:86:5
+ |
+LL | 0 + match a { 0 => 10, _ => 20 };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: the operation is ineffective. Consider reducing it to `if b { 0 + 1 } else { 2 }`
+ --> $DIR/identity_op.rs:91:5
+ |
+LL | 0 + if b { 0 + 1 } else { 2 };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: the operation is ineffective. Consider reducing it to `1`
+ --> $DIR/identity_op.rs:91:16
+ |
+LL | 0 + if b { 0 + 1 } else { 2 };
+ | ^^^^^
+
+error: the operation is ineffective. Consider reducing it to `match a { 0 => 0 + 10, _ => 20 }`
+ --> $DIR/identity_op.rs:92:5
+ |
+LL | 0 + match a { 0 => 0 + 10, _ => 20 };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: the operation is ineffective. Consider reducing it to `10`
+ --> $DIR/identity_op.rs:92:25
+ |
+LL | 0 + match a { 0 => 0 + 10, _ => 20 };
+ | ^^^^^^
+
+error: the operation is ineffective. Consider reducing it to `1`
+ --> $DIR/identity_op.rs:93:16
+ |
+LL | 0 + if b { 0 + 1 } else { 2 } + match a { 0 => 0 + 30, _ => 40 };
+ | ^^^^^
+
+error: the operation is ineffective. Consider reducing it to `30`
+ --> $DIR/identity_op.rs:93:52
+ |
+LL | 0 + if b { 0 + 1 } else { 2 } + match a { 0 => 0 + 30, _ => 40 };
+ | ^^^^^^
+
+error: the operation is ineffective. Consider reducing it to `1`
+ --> $DIR/identity_op.rs:95:20
+ |
+LL | let _ = 0 + if 0 + 1 > 0 { 1 } else { 2 } + if 0 + 1 > 0 { 3 } else { 4 };
+ | ^^^^^
+
+error: the operation is ineffective. Consider reducing it to `1`
+ --> $DIR/identity_op.rs:95:52
+ |
+LL | let _ = 0 + if 0 + 1 > 0 { 1 } else { 2 } + if 0 + 1 > 0 { 3 } else { 4 };
+ | ^^^^^
+
+error: the operation is ineffective. Consider reducing it to `1`
+ --> $DIR/identity_op.rs:96:23
+ |
+LL | let _ = 0 + match 0 + 1 { 0 => 10, _ => 20 } + match 0 + 1 { 0 => 30, _ => 40 };
+ | ^^^^^
+
+error: the operation is ineffective. Consider reducing it to `1`
+ --> $DIR/identity_op.rs:96:58
+ |
+LL | let _ = 0 + match 0 + 1 { 0 => 10, _ => 20 } + match 0 + 1 { 0 => 30, _ => 40 };
+ | ^^^^^
+
+error: the operation is ineffective. Consider reducing it to `0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }`
+ --> $DIR/identity_op.rs:98:5
+ |
+LL | 0 + if b { 1 } else { 2 } + if b { 3 } else { 4 } + 0;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: the operation is ineffective. Consider reducing it to `a`
+ --> $DIR/identity_op.rs:106:7
+ |
+LL | f(1 * a + { 8 * 5 });
+ | ^^^^^
+
+error: the operation is ineffective. Consider reducing it to `{ 2 * 4 }`
+ --> $DIR/identity_op.rs:108:20
+ |
+LL | const _: i32 = { 2 * 4 } + 0 + 3;
+ | ^^^^^^^^^^^^^
+
+error: aborting due to 33 previous errors
-#![allow(dead_code)]
+#![allow(dead_code, clippy::extra_unused_lifetimes)]
#![warn(clippy::multiple_inherent_impl)]
struct MyStruct;
-error[E0080]: evaluation of `main::{constant#3}::<&i32>` failed
+error[E0080]: evaluation of `main::{constant#3}` failed
--> $DIR/indexing_slicing_index.rs:31:14
|
LL | const { &ARR[idx4()] }; // Ok, let rustc handle const contexts.
--- /dev/null
+// run-rustfix
+
+#![warn(clippy::is_digit_ascii_radix)]
+
+const TEN: u32 = 10;
+
+fn main() {
+ let c: char = '6';
+
+ // Should trigger the lint.
+ let _ = c.is_ascii_digit();
+ let _ = c.is_ascii_hexdigit();
+ let _ = c.is_ascii_hexdigit();
+
+ // Should not trigger the lint.
+ let _ = c.is_digit(11);
+ let _ = c.is_digit(TEN);
+}
--- /dev/null
+// run-rustfix
+
+#![warn(clippy::is_digit_ascii_radix)]
+
+const TEN: u32 = 10;
+
+fn main() {
+ let c: char = '6';
+
+ // Should trigger the lint.
+ let _ = c.is_digit(10);
+ let _ = c.is_digit(16);
+ let _ = c.is_digit(0x10);
+
+ // Should not trigger the lint.
+ let _ = c.is_digit(11);
+ let _ = c.is_digit(TEN);
+}
--- /dev/null
+error: use of `char::is_digit` with literal radix of 10
+ --> $DIR/is_digit_ascii_radix.rs:11:13
+ |
+LL | let _ = c.is_digit(10);
+ | ^^^^^^^^^^^^^^ help: try: `c.is_ascii_digit()`
+ |
+ = note: `-D clippy::is-digit-ascii-radix` implied by `-D warnings`
+
+error: use of `char::is_digit` with literal radix of 16
+ --> $DIR/is_digit_ascii_radix.rs:12:13
+ |
+LL | let _ = c.is_digit(16);
+ | ^^^^^^^^^^^^^^ help: try: `c.is_ascii_hexdigit()`
+
+error: use of `char::is_digit` with literal radix of 16
+ --> $DIR/is_digit_ascii_radix.rs:13:13
+ |
+LL | let _ = c.is_digit(0x10);
+ | ^^^^^^^^^^^^^^^^ help: try: `c.is_ascii_hexdigit()`
+
+error: aborting due to 3 previous errors
+
// run-rustfix
#![warn(clippy::iter_overeager_cloned, clippy::redundant_clone, clippy::filter_next)]
-#![allow(dead_code)]
+#![allow(dead_code, clippy::let_unit_value)]
fn main() {
let vec = vec!["1".to_string(), "2".to_string(), "3".to_string()];
// run-rustfix
#![warn(clippy::iter_overeager_cloned, clippy::redundant_clone, clippy::filter_next)]
-#![allow(dead_code)]
+#![allow(dead_code, clippy::let_unit_value)]
fn main() {
let vec = vec!["1".to_string(), "2".to_string(), "3".to_string()];
let _: Vec<_> = b.drain(0..a.len()).collect();
}
+fn _closed_range(mut x: Vec<String>) {
+ let _: Vec<String> = x.drain(0..=x.len()).collect();
+}
+
+fn _with_mut(x: &mut Vec<String>, y: &mut VecDeque<String>) {
+ let _: Vec<String> = x.drain(..).collect();
+ let _: Vec<String> = y.drain(..).collect();
+}
+
#[derive(Default)]
struct Bomb {
fire: Vec<u8>,
let _: Vec<_> = b.drain(0..a.len()).collect();
}
+fn _closed_range(mut x: Vec<String>) {
+ let _: Vec<String> = x.drain(0..=x.len()).collect();
+}
+
+fn _with_mut(x: &mut Vec<String>, y: &mut VecDeque<String>) {
+ let _: Vec<String> = x.drain(..).collect();
+ let _: Vec<String> = y.drain(..).collect();
+}
+
#[derive(Default)]
struct Bomb {
fire: Vec<u8>,
#![warn(clippy::let_underscore_drop)]
+#![allow(clippy::let_unit_value)]
struct Droppable;
error: non-binding `let` on a type that implements `Drop`
- --> $DIR/let_underscore_drop.rs:16:5
+ --> $DIR/let_underscore_drop.rs:17:5
|
LL | let _ = Box::new(());
| ^^^^^^^^^^^^^^^^^^^^^
= help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
error: non-binding `let` on a type that implements `Drop`
- --> $DIR/let_underscore_drop.rs:17:5
+ --> $DIR/let_underscore_drop.rs:18:5
|
LL | let _ = Droppable;
| ^^^^^^^^^^^^^^^^^^
= help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
error: non-binding `let` on a type that implements `Drop`
- --> $DIR/let_underscore_drop.rs:18:5
+ --> $DIR/let_underscore_drop.rs:19:5
|
LL | let _ = Some(Droppable);
| ^^^^^^^^^^^^^^^^^^^^^^^^
#[derive(Copy, Clone)]
pub struct ContainsUnit(()); // should be fine
+
+fn _returns_generic() {
+ fn f<T>() -> T {
+ unimplemented!()
+ }
+ fn f2<T, U>(_: T) -> U {
+ unimplemented!()
+ }
+ fn f3<T>(x: T) -> T {
+ x
+ }
+ fn f4<T>(mut x: Vec<T>) -> T {
+ x.pop().unwrap()
+ }
+
+ let _: () = f(); // Ok
+ let _: () = f(); // Lint.
+
+ let _: () = f2(0i32); // Ok
+ let _: () = f2(0i32); // Lint.
+
+ f3(()); // Lint
+ f3(()); // Lint
+
+ f4(vec![()]); // Lint
+ f4(vec![()]); // Lint
+
+ // Ok
+ let _: () = {
+ let x = 5;
+ f2(x)
+ };
+
+ let _: () = if true { f() } else { f2(0) }; // Ok
+ let _: () = if true { f() } else { f2(0) }; // Lint
+
+ // Ok
+ let _: () = match Some(0) {
+ None => f2(1),
+ Some(0) => f(),
+ Some(1) => f2(3),
+ Some(_) => f2('x'),
+ };
+
+ // Lint
+ match Some(0) {
+ None => f2(1),
+ Some(0) => f(),
+ Some(1) => f2(3),
+ Some(_) => (),
+ };
+}
#[derive(Copy, Clone)]
pub struct ContainsUnit(()); // should be fine
+
+fn _returns_generic() {
+ fn f<T>() -> T {
+ unimplemented!()
+ }
+ fn f2<T, U>(_: T) -> U {
+ unimplemented!()
+ }
+ fn f3<T>(x: T) -> T {
+ x
+ }
+ fn f4<T>(mut x: Vec<T>) -> T {
+ x.pop().unwrap()
+ }
+
+ let _: () = f(); // Ok
+ let x: () = f(); // Lint.
+
+ let _: () = f2(0i32); // Ok
+ let x: () = f2(0i32); // Lint.
+
+ let _: () = f3(()); // Lint
+ let x: () = f3(()); // Lint
+
+ let _: () = f4(vec![()]); // Lint
+ let x: () = f4(vec![()]); // Lint
+
+ // Ok
+ let _: () = {
+ let x = 5;
+ f2(x)
+ };
+
+ let _: () = if true { f() } else { f2(0) }; // Ok
+ let x: () = if true { f() } else { f2(0) }; // Lint
+
+ // Ok
+ let _: () = match Some(0) {
+ None => f2(1),
+ Some(0) => f(),
+ Some(1) => f2(3),
+ Some(_) => f2('x'),
+ };
+
+ // Lint
+ let _: () = match Some(0) {
+ None => f2(1),
+ Some(0) => f(),
+ Some(1) => f2(3),
+ Some(_) => (),
+ };
+}
LL + .next()
...
-error: aborting due to 3 previous errors
+error: this let-binding has unit value
+ --> $DIR/let_unit.rs:80:5
+ |
+LL | let x: () = f(); // Lint.
+ | ^^^^-^^^^^^^^^^^
+ | |
+ | help: use a wild (`_`) binding: `_`
+
+error: this let-binding has unit value
+ --> $DIR/let_unit.rs:83:5
+ |
+LL | let x: () = f2(0i32); // Lint.
+ | ^^^^-^^^^^^^^^^^^^^^^
+ | |
+ | help: use a wild (`_`) binding: `_`
+
+error: this let-binding has unit value
+ --> $DIR/let_unit.rs:85:5
+ |
+LL | let _: () = f3(()); // Lint
+ | ^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `f3(());`
+
+error: this let-binding has unit value
+ --> $DIR/let_unit.rs:86:5
+ |
+LL | let x: () = f3(()); // Lint
+ | ^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `f3(());`
+
+error: this let-binding has unit value
+ --> $DIR/let_unit.rs:88:5
+ |
+LL | let _: () = f4(vec![()]); // Lint
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `f4(vec![()]);`
+
+error: this let-binding has unit value
+ --> $DIR/let_unit.rs:89:5
+ |
+LL | let x: () = f4(vec![()]); // Lint
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `f4(vec![()]);`
+
+error: this let-binding has unit value
+ --> $DIR/let_unit.rs:98:5
+ |
+LL | let x: () = if true { f() } else { f2(0) }; // Lint
+ | ^^^^-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | |
+ | help: use a wild (`_`) binding: `_`
+
+error: this let-binding has unit value
+ --> $DIR/let_unit.rs:109:5
+ |
+LL | / let _: () = match Some(0) {
+LL | | None => f2(1),
+LL | | Some(0) => f(),
+LL | | Some(1) => f2(3),
+LL | | Some(_) => (),
+LL | | };
+ | |______^
+ |
+help: omit the `let` binding
+ |
+LL ~ match Some(0) {
+LL + None => f2(1),
+LL + Some(0) => f(),
+LL + Some(1) => f2(3),
+LL + Some(_) => (),
+LL + };
+ |
+
+error: aborting due to 11 previous errors
// run-rustfix
#![warn(clippy::manual_bits)]
-#![allow(clippy::no_effect, path_statements, unused_must_use, clippy::unnecessary_operation)]
+#![allow(
+ clippy::no_effect,
+ clippy::useless_conversion,
+ path_statements,
+ unused_must_use,
+ clippy::unnecessary_operation
+)]
use std::mem::{size_of, size_of_val};
fn main() {
- i8::BITS;
- i16::BITS;
- i32::BITS;
- i64::BITS;
- i128::BITS;
- isize::BITS;
-
- u8::BITS;
- u16::BITS;
- u32::BITS;
- u64::BITS;
- u128::BITS;
- usize::BITS;
-
- i8::BITS;
- i16::BITS;
- i32::BITS;
- i64::BITS;
- i128::BITS;
- isize::BITS;
-
- u8::BITS;
- u16::BITS;
- u32::BITS;
- u64::BITS;
- u128::BITS;
- usize::BITS;
+ i8::BITS as usize;
+ i16::BITS as usize;
+ i32::BITS as usize;
+ i64::BITS as usize;
+ i128::BITS as usize;
+ isize::BITS as usize;
+
+ u8::BITS as usize;
+ u16::BITS as usize;
+ u32::BITS as usize;
+ u64::BITS as usize;
+ u128::BITS as usize;
+ usize::BITS as usize;
+
+ i8::BITS as usize;
+ i16::BITS as usize;
+ i32::BITS as usize;
+ i64::BITS as usize;
+ i128::BITS as usize;
+ isize::BITS as usize;
+
+ u8::BITS as usize;
+ u16::BITS as usize;
+ u32::BITS as usize;
+ u64::BITS as usize;
+ u128::BITS as usize;
+ usize::BITS as usize;
size_of::<usize>() * 4;
4 * size_of::<usize>();
size_of_val(&0u32) * 8;
type Word = u32;
- Word::BITS;
+ Word::BITS as usize;
type Bool = bool;
size_of::<Bool>() * 8;
+
+ let _: u32 = u128::BITS as u32;
+ let _: u32 = u128::BITS.try_into().unwrap();
+ let _ = (u128::BITS as usize).pow(5);
+ let _ = &(u128::BITS as usize);
}
// run-rustfix
#![warn(clippy::manual_bits)]
-#![allow(clippy::no_effect, path_statements, unused_must_use, clippy::unnecessary_operation)]
+#![allow(
+ clippy::no_effect,
+ clippy::useless_conversion,
+ path_statements,
+ unused_must_use,
+ clippy::unnecessary_operation
+)]
use std::mem::{size_of, size_of_val};
size_of::<Word>() * 8;
type Bool = bool;
size_of::<Bool>() * 8;
+
+ let _: u32 = (size_of::<u128>() * 8) as u32;
+ let _: u32 = (size_of::<u128>() * 8).try_into().unwrap();
+ let _ = (size_of::<u128>() * 8).pow(5);
+ let _ = &(size_of::<u128>() * 8);
}
error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
- --> $DIR/manual_bits.rs:9:5
+ --> $DIR/manual_bits.rs:15:5
|
LL | size_of::<i8>() * 8;
- | ^^^^^^^^^^^^^^^^^^^ help: consider using: `i8::BITS`
+ | ^^^^^^^^^^^^^^^^^^^ help: consider using: `i8::BITS as usize`
|
= note: `-D clippy::manual-bits` implied by `-D warnings`
error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
- --> $DIR/manual_bits.rs:10:5
+ --> $DIR/manual_bits.rs:16:5
|
LL | size_of::<i16>() * 8;
- | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i16::BITS`
+ | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i16::BITS as usize`
error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
- --> $DIR/manual_bits.rs:11:5
+ --> $DIR/manual_bits.rs:17:5
|
LL | size_of::<i32>() * 8;
- | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i32::BITS`
+ | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i32::BITS as usize`
error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
- --> $DIR/manual_bits.rs:12:5
+ --> $DIR/manual_bits.rs:18:5
|
LL | size_of::<i64>() * 8;
- | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i64::BITS`
+ | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i64::BITS as usize`
error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
- --> $DIR/manual_bits.rs:13:5
+ --> $DIR/manual_bits.rs:19:5
|
LL | size_of::<i128>() * 8;
- | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `i128::BITS`
+ | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `i128::BITS as usize`
error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
- --> $DIR/manual_bits.rs:14:5
+ --> $DIR/manual_bits.rs:20:5
|
LL | size_of::<isize>() * 8;
- | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `isize::BITS`
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `isize::BITS as usize`
error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
- --> $DIR/manual_bits.rs:16:5
+ --> $DIR/manual_bits.rs:22:5
|
LL | size_of::<u8>() * 8;
- | ^^^^^^^^^^^^^^^^^^^ help: consider using: `u8::BITS`
+ | ^^^^^^^^^^^^^^^^^^^ help: consider using: `u8::BITS as usize`
error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
- --> $DIR/manual_bits.rs:17:5
+ --> $DIR/manual_bits.rs:23:5
|
LL | size_of::<u16>() * 8;
- | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u16::BITS`
+ | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u16::BITS as usize`
error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
- --> $DIR/manual_bits.rs:18:5
+ --> $DIR/manual_bits.rs:24:5
|
LL | size_of::<u32>() * 8;
- | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u32::BITS`
+ | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u32::BITS as usize`
error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
- --> $DIR/manual_bits.rs:19:5
+ --> $DIR/manual_bits.rs:25:5
|
LL | size_of::<u64>() * 8;
- | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u64::BITS`
+ | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u64::BITS as usize`
error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
- --> $DIR/manual_bits.rs:20:5
+ --> $DIR/manual_bits.rs:26:5
|
LL | size_of::<u128>() * 8;
- | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS`
+ | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS as usize`
error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
- --> $DIR/manual_bits.rs:21:5
+ --> $DIR/manual_bits.rs:27:5
|
LL | size_of::<usize>() * 8;
- | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `usize::BITS`
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `usize::BITS as usize`
error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
- --> $DIR/manual_bits.rs:23:5
+ --> $DIR/manual_bits.rs:29:5
|
LL | 8 * size_of::<i8>();
- | ^^^^^^^^^^^^^^^^^^^ help: consider using: `i8::BITS`
+ | ^^^^^^^^^^^^^^^^^^^ help: consider using: `i8::BITS as usize`
error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
- --> $DIR/manual_bits.rs:24:5
+ --> $DIR/manual_bits.rs:30:5
|
LL | 8 * size_of::<i16>();
- | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i16::BITS`
+ | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i16::BITS as usize`
error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
- --> $DIR/manual_bits.rs:25:5
+ --> $DIR/manual_bits.rs:31:5
|
LL | 8 * size_of::<i32>();
- | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i32::BITS`
+ | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i32::BITS as usize`
error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
- --> $DIR/manual_bits.rs:26:5
+ --> $DIR/manual_bits.rs:32:5
|
LL | 8 * size_of::<i64>();
- | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i64::BITS`
+ | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i64::BITS as usize`
error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
- --> $DIR/manual_bits.rs:27:5
+ --> $DIR/manual_bits.rs:33:5
|
LL | 8 * size_of::<i128>();
- | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `i128::BITS`
+ | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `i128::BITS as usize`
error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
- --> $DIR/manual_bits.rs:28:5
+ --> $DIR/manual_bits.rs:34:5
|
LL | 8 * size_of::<isize>();
- | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `isize::BITS`
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `isize::BITS as usize`
error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
- --> $DIR/manual_bits.rs:30:5
+ --> $DIR/manual_bits.rs:36:5
|
LL | 8 * size_of::<u8>();
- | ^^^^^^^^^^^^^^^^^^^ help: consider using: `u8::BITS`
+ | ^^^^^^^^^^^^^^^^^^^ help: consider using: `u8::BITS as usize`
error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
- --> $DIR/manual_bits.rs:31:5
+ --> $DIR/manual_bits.rs:37:5
|
LL | 8 * size_of::<u16>();
- | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u16::BITS`
+ | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u16::BITS as usize`
error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
- --> $DIR/manual_bits.rs:32:5
+ --> $DIR/manual_bits.rs:38:5
|
LL | 8 * size_of::<u32>();
- | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u32::BITS`
+ | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u32::BITS as usize`
error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
- --> $DIR/manual_bits.rs:33:5
+ --> $DIR/manual_bits.rs:39:5
|
LL | 8 * size_of::<u64>();
- | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u64::BITS`
+ | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u64::BITS as usize`
error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
- --> $DIR/manual_bits.rs:34:5
+ --> $DIR/manual_bits.rs:40:5
|
LL | 8 * size_of::<u128>();
- | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS`
+ | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS as usize`
error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
- --> $DIR/manual_bits.rs:35:5
+ --> $DIR/manual_bits.rs:41:5
|
LL | 8 * size_of::<usize>();
- | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `usize::BITS`
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `usize::BITS as usize`
error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
- --> $DIR/manual_bits.rs:45:5
+ --> $DIR/manual_bits.rs:51:5
|
LL | size_of::<Word>() * 8;
- | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `Word::BITS`
+ | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `Word::BITS as usize`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+ --> $DIR/manual_bits.rs:55:18
+ |
+LL | let _: u32 = (size_of::<u128>() * 8) as u32;
+ | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+ --> $DIR/manual_bits.rs:56:18
+ |
+LL | let _: u32 = (size_of::<u128>() * 8).try_into().unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+ --> $DIR/manual_bits.rs:57:13
+ |
+LL | let _ = (size_of::<u128>() * 8).pow(5);
+ | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(u128::BITS as usize)`
+
+error: usage of `mem::size_of::<T>()` to obtain the size of `T` in bits
+ --> $DIR/manual_bits.rs:58:14
+ |
+LL | let _ = &(size_of::<u128>() * 8);
+ | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(u128::BITS as usize)`
-error: aborting due to 25 previous errors
+error: aborting due to 29 previous errors
+++ /dev/null
-#![warn(clippy::manual_non_exhaustive)]
-#![allow(unused)]
-
-mod enums {
- enum E {
- A,
- B,
- #[doc(hidden)]
- _C,
- }
-
- // user forgot to remove the marker
- #[non_exhaustive]
- enum Ep {
- A,
- B,
- #[doc(hidden)]
- _C,
- }
-
- // marker variant does not have doc hidden attribute, should be ignored
- enum NoDocHidden {
- A,
- B,
- _C,
- }
-
- // name of variant with doc hidden does not start with underscore, should be ignored
- enum NoUnderscore {
- A,
- B,
- #[doc(hidden)]
- C,
- }
-
- // variant with doc hidden is not unit, should be ignored
- enum NotUnit {
- A,
- B,
- #[doc(hidden)]
- _C(bool),
- }
-
- // variant with doc hidden is the only one, should be ignored
- enum OnlyMarker {
- #[doc(hidden)]
- _A,
- }
-
- // variant with multiple markers, should be ignored
- enum MultipleMarkers {
- A,
- #[doc(hidden)]
- _B,
- #[doc(hidden)]
- _C,
- }
-
- // already non_exhaustive and no markers, should be ignored
- #[non_exhaustive]
- enum NonExhaustive {
- A,
- B,
- }
-}
-
-mod structs {
- struct S {
- pub a: i32,
- pub b: i32,
- _c: (),
- }
-
- // user forgot to remove the private field
- #[non_exhaustive]
- struct Sp {
- pub a: i32,
- pub b: i32,
- _c: (),
- }
-
- // some other fields are private, should be ignored
- struct PrivateFields {
- a: i32,
- pub b: i32,
- _c: (),
- }
-
- // private field name does not start with underscore, should be ignored
- struct NoUnderscore {
- pub a: i32,
- pub b: i32,
- c: (),
- }
-
- // private field is not unit type, should be ignored
- struct NotUnit {
- pub a: i32,
- pub b: i32,
- _c: i32,
- }
-
- // private field is the only field, should be ignored
- struct OnlyMarker {
- _a: (),
- }
-
- // already non exhaustive and no private fields, should be ignored
- #[non_exhaustive]
- struct NonExhaustive {
- pub a: i32,
- pub b: i32,
- }
-}
-
-mod tuple_structs {
- struct T(pub i32, pub i32, ());
-
- // user forgot to remove the private field
- #[non_exhaustive]
- struct Tp(pub i32, pub i32, ());
-
- // some other fields are private, should be ignored
- struct PrivateFields(pub i32, i32, ());
-
- // private field is not unit type, should be ignored
- struct NotUnit(pub i32, pub i32, i32);
-
- // private field is the only field, should be ignored
- struct OnlyMarker(());
-
- // already non exhaustive and no private fields, should be ignored
- #[non_exhaustive]
- struct NonExhaustive(pub i32, pub i32);
-}
-
-fn main() {}
+++ /dev/null
-error: this seems like a manual implementation of the non-exhaustive pattern
- --> $DIR/manual_non_exhaustive.rs:5:5
- |
-LL | enum E {
- | ^-----
- | |
- | _____help: add the attribute: `#[non_exhaustive] enum E`
- | |
-LL | | A,
-LL | | B,
-LL | | #[doc(hidden)]
-LL | | _C,
-LL | | }
- | |_____^
- |
- = note: `-D clippy::manual-non-exhaustive` implied by `-D warnings`
-help: remove this variant
- --> $DIR/manual_non_exhaustive.rs:9:9
- |
-LL | _C,
- | ^^
-
-error: this seems like a manual implementation of the non-exhaustive pattern
- --> $DIR/manual_non_exhaustive.rs:14:5
- |
-LL | / enum Ep {
-LL | | A,
-LL | | B,
-LL | | #[doc(hidden)]
-LL | | _C,
-LL | | }
- | |_____^
- |
-help: remove this variant
- --> $DIR/manual_non_exhaustive.rs:18:9
- |
-LL | _C,
- | ^^
-
-error: this seems like a manual implementation of the non-exhaustive pattern
- --> $DIR/manual_non_exhaustive.rs:68:5
- |
-LL | struct S {
- | ^-------
- | |
- | _____help: add the attribute: `#[non_exhaustive] struct S`
- | |
-LL | | pub a: i32,
-LL | | pub b: i32,
-LL | | _c: (),
-LL | | }
- | |_____^
- |
-help: remove this field
- --> $DIR/manual_non_exhaustive.rs:71:9
- |
-LL | _c: (),
- | ^^^^^^
-
-error: this seems like a manual implementation of the non-exhaustive pattern
- --> $DIR/manual_non_exhaustive.rs:76:5
- |
-LL | / struct Sp {
-LL | | pub a: i32,
-LL | | pub b: i32,
-LL | | _c: (),
-LL | | }
- | |_____^
- |
-help: remove this field
- --> $DIR/manual_non_exhaustive.rs:79:9
- |
-LL | _c: (),
- | ^^^^^^
-
-error: this seems like a manual implementation of the non-exhaustive pattern
- --> $DIR/manual_non_exhaustive.rs:117:5
- |
-LL | struct T(pub i32, pub i32, ());
- | --------^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | help: add the attribute: `#[non_exhaustive] struct T`
- |
-help: remove this field
- --> $DIR/manual_non_exhaustive.rs:117:32
- |
-LL | struct T(pub i32, pub i32, ());
- | ^^
-
-error: this seems like a manual implementation of the non-exhaustive pattern
- --> $DIR/manual_non_exhaustive.rs:121:5
- |
-LL | struct Tp(pub i32, pub i32, ());
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
-help: remove this field
- --> $DIR/manual_non_exhaustive.rs:121:33
- |
-LL | struct Tp(pub i32, pub i32, ());
- | ^^
-
-error: aborting due to 6 previous errors
-
--- /dev/null
+#![warn(clippy::manual_non_exhaustive)]
+#![allow(unused)]
+
+enum E {
+ A,
+ B,
+ #[doc(hidden)]
+ _C,
+}
+
+// user forgot to remove the marker
+#[non_exhaustive]
+enum Ep {
+ A,
+ B,
+ #[doc(hidden)]
+ _C,
+}
+
+// marker variant does not have doc hidden attribute, should be ignored
+enum NoDocHidden {
+ A,
+ B,
+ _C,
+}
+
+// name of variant with doc hidden does not start with underscore, should be ignored
+enum NoUnderscore {
+ A,
+ B,
+ #[doc(hidden)]
+ C,
+}
+
+// variant with doc hidden is not unit, should be ignored
+enum NotUnit {
+ A,
+ B,
+ #[doc(hidden)]
+ _C(bool),
+}
+
+// variant with doc hidden is the only one, should be ignored
+enum OnlyMarker {
+ #[doc(hidden)]
+ _A,
+}
+
+// variant with multiple markers, should be ignored
+enum MultipleMarkers {
+ A,
+ #[doc(hidden)]
+ _B,
+ #[doc(hidden)]
+ _C,
+}
+
+// already non_exhaustive and no markers, should be ignored
+#[non_exhaustive]
+enum NonExhaustive {
+ A,
+ B,
+}
+
+// marked is used, don't lint
+enum UsedHidden {
+ #[doc(hidden)]
+ _A,
+ B,
+ C,
+}
+fn foo(x: &mut UsedHidden) {
+ if matches!(*x, UsedHidden::B) {
+ *x = UsedHidden::_A;
+ }
+}
+
+fn main() {}
--- /dev/null
+error: this seems like a manual implementation of the non-exhaustive pattern
+ --> $DIR/manual_non_exhaustive_enum.rs:4:1
+ |
+LL | enum E {
+ | ^-----
+ | |
+ | _help: add the attribute: `#[non_exhaustive] enum E`
+ | |
+LL | | A,
+LL | | B,
+LL | | #[doc(hidden)]
+LL | | _C,
+LL | | }
+ | |_^
+ |
+ = note: `-D clippy::manual-non-exhaustive` implied by `-D warnings`
+help: remove this variant
+ --> $DIR/manual_non_exhaustive_enum.rs:8:5
+ |
+LL | _C,
+ | ^^
+
+error: this seems like a manual implementation of the non-exhaustive pattern
+ --> $DIR/manual_non_exhaustive_enum.rs:13:1
+ |
+LL | / enum Ep {
+LL | | A,
+LL | | B,
+LL | | #[doc(hidden)]
+LL | | _C,
+LL | | }
+ | |_^
+ |
+help: remove this variant
+ --> $DIR/manual_non_exhaustive_enum.rs:17:5
+ |
+LL | _C,
+ | ^^
+
+error: aborting due to 2 previous errors
+
--- /dev/null
+#![warn(clippy::manual_non_exhaustive)]
+#![allow(unused)]
+
+mod structs {
+ struct S {
+ pub a: i32,
+ pub b: i32,
+ _c: (),
+ }
+
+ // user forgot to remove the private field
+ #[non_exhaustive]
+ struct Sp {
+ pub a: i32,
+ pub b: i32,
+ _c: (),
+ }
+
+ // some other fields are private, should be ignored
+ struct PrivateFields {
+ a: i32,
+ pub b: i32,
+ _c: (),
+ }
+
+ // private field name does not start with underscore, should be ignored
+ struct NoUnderscore {
+ pub a: i32,
+ pub b: i32,
+ c: (),
+ }
+
+ // private field is not unit type, should be ignored
+ struct NotUnit {
+ pub a: i32,
+ pub b: i32,
+ _c: i32,
+ }
+
+ // private field is the only field, should be ignored
+ struct OnlyMarker {
+ _a: (),
+ }
+
+ // already non exhaustive and no private fields, should be ignored
+ #[non_exhaustive]
+ struct NonExhaustive {
+ pub a: i32,
+ pub b: i32,
+ }
+}
+
+mod tuple_structs {
+ struct T(pub i32, pub i32, ());
+
+ // user forgot to remove the private field
+ #[non_exhaustive]
+ struct Tp(pub i32, pub i32, ());
+
+ // some other fields are private, should be ignored
+ struct PrivateFields(pub i32, i32, ());
+
+ // private field is not unit type, should be ignored
+ struct NotUnit(pub i32, pub i32, i32);
+
+ // private field is the only field, should be ignored
+ struct OnlyMarker(());
+
+ // already non exhaustive and no private fields, should be ignored
+ #[non_exhaustive]
+ struct NonExhaustive(pub i32, pub i32);
+}
+
+fn main() {}
--- /dev/null
+error: this seems like a manual implementation of the non-exhaustive pattern
+ --> $DIR/manual_non_exhaustive_struct.rs:5:5
+ |
+LL | struct S {
+ | ^-------
+ | |
+ | _____help: add the attribute: `#[non_exhaustive] struct S`
+ | |
+LL | | pub a: i32,
+LL | | pub b: i32,
+LL | | _c: (),
+LL | | }
+ | |_____^
+ |
+ = note: `-D clippy::manual-non-exhaustive` implied by `-D warnings`
+help: remove this field
+ --> $DIR/manual_non_exhaustive_struct.rs:8:9
+ |
+LL | _c: (),
+ | ^^^^^^
+
+error: this seems like a manual implementation of the non-exhaustive pattern
+ --> $DIR/manual_non_exhaustive_struct.rs:13:5
+ |
+LL | / struct Sp {
+LL | | pub a: i32,
+LL | | pub b: i32,
+LL | | _c: (),
+LL | | }
+ | |_____^
+ |
+help: remove this field
+ --> $DIR/manual_non_exhaustive_struct.rs:16:9
+ |
+LL | _c: (),
+ | ^^^^^^
+
+error: this seems like a manual implementation of the non-exhaustive pattern
+ --> $DIR/manual_non_exhaustive_struct.rs:54:5
+ |
+LL | struct T(pub i32, pub i32, ());
+ | --------^^^^^^^^^^^^^^^^^^^^^^^
+ | |
+ | help: add the attribute: `#[non_exhaustive] struct T`
+ |
+help: remove this field
+ --> $DIR/manual_non_exhaustive_struct.rs:54:32
+ |
+LL | struct T(pub i32, pub i32, ());
+ | ^^
+
+error: this seems like a manual implementation of the non-exhaustive pattern
+ --> $DIR/manual_non_exhaustive_struct.rs:58:5
+ |
+LL | struct Tp(pub i32, pub i32, ());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: remove this field
+ --> $DIR/manual_non_exhaustive_struct.rs:58:33
+ |
+LL | struct Tp(pub i32, pub i32, ());
+ | ^^
+
+error: aborting due to 4 previous errors
+
#![feature(custom_inner_attributes)]
#![warn(clippy::manual_split_once)]
-#![allow(clippy::iter_skip_next, clippy::iter_nth_zero, clippy::needless_splitn)]
+#![allow(unused, clippy::iter_skip_next, clippy::iter_nth_zero)]
extern crate itertools;
use itertools::Itertools;
fn main() {
- let _ = Some("key=value".split_once('=').map_or("key=value", |x| x.0));
let _ = "key=value".splitn(2, '=').nth(2);
- let _ = "key=value".split_once('=').map_or("key=value", |x| x.0);
- let _ = "key=value".split_once('=').map_or("key=value", |x| x.0);
let _ = "key=value".split_once('=').unwrap().1;
let _ = "key=value".split_once('=').unwrap().1;
let (_, _) = "key=value".split_once('=').unwrap();
let s = String::from("key=value");
- let _ = s.split_once('=').map_or(&*s, |x| x.0);
+ let _ = s.split_once('=').unwrap().1;
let s = Box::<str>::from("key=value");
- let _ = s.split_once('=').map_or(&*s, |x| x.0);
+ let _ = s.split_once('=').unwrap().1;
let s = &"key=value";
- let _ = s.split_once('=').map_or(*s, |x| x.0);
+ let _ = s.split_once('=').unwrap().1;
fn _f(s: &str) -> Option<&str> {
- let _ = s.split_once("key=value").map_or(s, |x| x.0);
- let _ = s.split_once("key=value")?.1;
- let _ = s.split_once("key=value")?.1;
+ let _ = s.split_once('=')?.1;
+ let _ = s.split_once('=')?.1;
+ let _ = s.rsplit_once('=')?.0;
+ let _ = s.rsplit_once('=')?.0;
None
}
let _ = [0, 1, 2].splitn(2, |&x| x == 1).nth(1).unwrap();
// `rsplitn` gives the results in the reverse order of `rsplit_once`
- let _ = "key=value".rsplitn(2, '=').next().unwrap();
let _ = "key=value".rsplit_once('=').unwrap().0;
- let _ = "key=value".rsplit_once('=').map(|x| x.1);
let (_, _) = "key=value".rsplit_once('=').map(|(x, y)| (y, x)).unwrap();
+ let _ = s.rsplit_once('=').map(|x| x.0);
+}
+
+fn indirect() -> Option<()> {
+ let (l, r) = "a.b.c".split_once('.').unwrap();
+
+
+
+ let (l, r) = "a.b.c".split_once('.')?;
+
+
+
+ let (l, r) = "a.b.c".rsplit_once('.').unwrap();
+
+
+
+ let (l, r) = "a.b.c".rsplit_once('.')?;
+
+
+
+ // could lint, currently doesn't
+
+ let mut iter = "a.b.c".splitn(2, '.');
+ let other = 1;
+ let l = iter.next()?;
+ let r = iter.next()?;
+
+ let mut iter = "a.b.c".splitn(2, '.');
+ let mut mut_binding = iter.next()?;
+ let r = iter.next()?;
+
+ let mut iter = "a.b.c".splitn(2, '.');
+ let tuple = (iter.next()?, iter.next()?);
+
+ // should not lint
+
+ let mut missing_unwrap = "a.b.c".splitn(2, '.');
+ let l = missing_unwrap.next();
+ let r = missing_unwrap.next();
+
+ let mut mixed_unrap = "a.b.c".splitn(2, '.');
+ let unwrap = mixed_unrap.next().unwrap();
+ let question_mark = mixed_unrap.next()?;
+
+ let mut iter = "a.b.c".splitn(2, '.');
+ let same_name = iter.next()?;
+ let same_name = iter.next()?;
+
+ let mut iter = "a.b.c".splitn(2, '.');
+ let shadows_existing = "d";
+ let shadows_existing = iter.next()?;
+ let r = iter.next()?;
+
+ let mut iter = "a.b.c".splitn(2, '.');
+ let becomes_shadowed = iter.next()?;
+ let becomes_shadowed = "d";
+ let r = iter.next()?;
+
+ let mut iter = "a.b.c".splitn(2, '.');
+ let l = iter.next()?;
+ let r = iter.next()?;
+ let third_usage = iter.next()?;
+
+ let mut n_three = "a.b.c".splitn(3, '.');
+ let l = n_three.next()?;
+ let r = n_three.next()?;
+
+ let mut iter = "a.b.c".splitn(2, '.');
+ {
+ let in_block = iter.next()?;
+ }
+ let r = iter.next()?;
+
+ let mut lacks_binding = "a.b.c".splitn(2, '.');
+ let _ = lacks_binding.next()?;
+ let r = lacks_binding.next()?;
+
+ let mut mapped = "a.b.c".splitn(2, '.').map(|_| "~");
+ let l = iter.next()?;
+ let r = iter.next()?;
+
+ let mut assigned = "";
+ let mut iter = "a.b.c".splitn(2, '.');
+ let l = iter.next()?;
+ assigned = iter.next()?;
+
+ None
}
fn _msrv_1_51() {
#![clippy::msrv = "1.51"]
- // `str::split_once` was stabilized in 1.16. Do not lint this
+ // `str::split_once` was stabilized in 1.52. Do not lint this
let _ = "key=value".splitn(2, '=').nth(1).unwrap();
+
+ let mut iter = "a.b.c".splitn(2, '.');
+ let a = iter.next().unwrap();
+ let b = iter.next().unwrap();
}
fn _msrv_1_52() {
#![clippy::msrv = "1.52"]
let _ = "key=value".split_once('=').unwrap().1;
+
+ let (a, b) = "a.b.c".split_once('.').unwrap();
+
+
}
#![feature(custom_inner_attributes)]
#![warn(clippy::manual_split_once)]
-#![allow(clippy::iter_skip_next, clippy::iter_nth_zero, clippy::needless_splitn)]
+#![allow(unused, clippy::iter_skip_next, clippy::iter_nth_zero)]
extern crate itertools;
use itertools::Itertools;
fn main() {
- let _ = "key=value".splitn(2, '=').next();
let _ = "key=value".splitn(2, '=').nth(2);
- let _ = "key=value".splitn(2, '=').next().unwrap();
- let _ = "key=value".splitn(2, '=').nth(0).unwrap();
let _ = "key=value".splitn(2, '=').nth(1).unwrap();
let _ = "key=value".splitn(2, '=').skip(1).next().unwrap();
let (_, _) = "key=value".splitn(2, '=').next_tuple().unwrap();
let s = String::from("key=value");
- let _ = s.splitn(2, '=').next().unwrap();
+ let _ = s.splitn(2, '=').nth(1).unwrap();
let s = Box::<str>::from("key=value");
- let _ = s.splitn(2, '=').nth(0).unwrap();
+ let _ = s.splitn(2, '=').nth(1).unwrap();
let s = &"key=value";
- let _ = s.splitn(2, '=').skip(0).next().unwrap();
+ let _ = s.splitn(2, '=').skip(1).next().unwrap();
fn _f(s: &str) -> Option<&str> {
- let _ = s.splitn(2, "key=value").next()?;
- let _ = s.splitn(2, "key=value").nth(1)?;
- let _ = s.splitn(2, "key=value").skip(1).next()?;
+ let _ = s.splitn(2, '=').nth(1)?;
+ let _ = s.splitn(2, '=').skip(1).next()?;
+ let _ = s.rsplitn(2, '=').nth(1)?;
+ let _ = s.rsplitn(2, '=').skip(1).next()?;
None
}
let _ = [0, 1, 2].splitn(2, |&x| x == 1).nth(1).unwrap();
// `rsplitn` gives the results in the reverse order of `rsplit_once`
- let _ = "key=value".rsplitn(2, '=').next().unwrap();
let _ = "key=value".rsplitn(2, '=').nth(1).unwrap();
- let _ = "key=value".rsplitn(2, '=').nth(0);
let (_, _) = "key=value".rsplitn(2, '=').next_tuple().unwrap();
+ let _ = s.rsplitn(2, '=').nth(1);
+}
+
+fn indirect() -> Option<()> {
+ let mut iter = "a.b.c".splitn(2, '.');
+ let l = iter.next().unwrap();
+ let r = iter.next().unwrap();
+
+ let mut iter = "a.b.c".splitn(2, '.');
+ let l = iter.next()?;
+ let r = iter.next()?;
+
+ let mut iter = "a.b.c".rsplitn(2, '.');
+ let r = iter.next().unwrap();
+ let l = iter.next().unwrap();
+
+ let mut iter = "a.b.c".rsplitn(2, '.');
+ let r = iter.next()?;
+ let l = iter.next()?;
+
+ // could lint, currently doesn't
+
+ let mut iter = "a.b.c".splitn(2, '.');
+ let other = 1;
+ let l = iter.next()?;
+ let r = iter.next()?;
+
+ let mut iter = "a.b.c".splitn(2, '.');
+ let mut mut_binding = iter.next()?;
+ let r = iter.next()?;
+
+ let mut iter = "a.b.c".splitn(2, '.');
+ let tuple = (iter.next()?, iter.next()?);
+
+ // should not lint
+
+ let mut missing_unwrap = "a.b.c".splitn(2, '.');
+ let l = missing_unwrap.next();
+ let r = missing_unwrap.next();
+
+ let mut mixed_unrap = "a.b.c".splitn(2, '.');
+ let unwrap = mixed_unrap.next().unwrap();
+ let question_mark = mixed_unrap.next()?;
+
+ let mut iter = "a.b.c".splitn(2, '.');
+ let same_name = iter.next()?;
+ let same_name = iter.next()?;
+
+ let mut iter = "a.b.c".splitn(2, '.');
+ let shadows_existing = "d";
+ let shadows_existing = iter.next()?;
+ let r = iter.next()?;
+
+ let mut iter = "a.b.c".splitn(2, '.');
+ let becomes_shadowed = iter.next()?;
+ let becomes_shadowed = "d";
+ let r = iter.next()?;
+
+ let mut iter = "a.b.c".splitn(2, '.');
+ let l = iter.next()?;
+ let r = iter.next()?;
+ let third_usage = iter.next()?;
+
+ let mut n_three = "a.b.c".splitn(3, '.');
+ let l = n_three.next()?;
+ let r = n_three.next()?;
+
+ let mut iter = "a.b.c".splitn(2, '.');
+ {
+ let in_block = iter.next()?;
+ }
+ let r = iter.next()?;
+
+ let mut lacks_binding = "a.b.c".splitn(2, '.');
+ let _ = lacks_binding.next()?;
+ let r = lacks_binding.next()?;
+
+ let mut mapped = "a.b.c".splitn(2, '.').map(|_| "~");
+ let l = iter.next()?;
+ let r = iter.next()?;
+
+ let mut assigned = "";
+ let mut iter = "a.b.c".splitn(2, '.');
+ let l = iter.next()?;
+ assigned = iter.next()?;
+
+ None
}
fn _msrv_1_51() {
#![clippy::msrv = "1.51"]
- // `str::split_once` was stabilized in 1.16. Do not lint this
+ // `str::split_once` was stabilized in 1.52. Do not lint this
let _ = "key=value".splitn(2, '=').nth(1).unwrap();
+
+ let mut iter = "a.b.c".splitn(2, '.');
+ let a = iter.next().unwrap();
+ let b = iter.next().unwrap();
}
fn _msrv_1_52() {
#![clippy::msrv = "1.52"]
let _ = "key=value".splitn(2, '=').nth(1).unwrap();
+
+ let mut iter = "a.b.c".splitn(2, '.');
+ let a = iter.next().unwrap();
+ let b = iter.next().unwrap();
}
error: manual implementation of `split_once`
- --> $DIR/manual_split_once.rs:13:13
+ --> $DIR/manual_split_once.rs:14:13
|
-LL | let _ = "key=value".splitn(2, '=').next();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `Some("key=value".split_once('=').map_or("key=value", |x| x.0))`
+LL | let _ = "key=value".splitn(2, '=').nth(1).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1`
|
= note: `-D clippy::manual-split-once` implied by `-D warnings`
error: manual implementation of `split_once`
--> $DIR/manual_split_once.rs:15:13
|
-LL | let _ = "key=value".splitn(2, '=').next().unwrap();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').map_or("key=value", |x| x.0)`
-
-error: manual implementation of `split_once`
- --> $DIR/manual_split_once.rs:16:13
- |
-LL | let _ = "key=value".splitn(2, '=').nth(0).unwrap();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').map_or("key=value", |x| x.0)`
-
-error: manual implementation of `split_once`
- --> $DIR/manual_split_once.rs:17:13
- |
-LL | let _ = "key=value".splitn(2, '=').nth(1).unwrap();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1`
-
-error: manual implementation of `split_once`
- --> $DIR/manual_split_once.rs:18:13
- |
LL | let _ = "key=value".splitn(2, '=').skip(1).next().unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1`
error: manual implementation of `split_once`
- --> $DIR/manual_split_once.rs:19:18
+ --> $DIR/manual_split_once.rs:16:18
|
LL | let (_, _) = "key=value".splitn(2, '=').next_tuple().unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=')`
+error: manual implementation of `split_once`
+ --> $DIR/manual_split_once.rs:19:13
+ |
+LL | let _ = s.splitn(2, '=').nth(1).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').unwrap().1`
+
error: manual implementation of `split_once`
--> $DIR/manual_split_once.rs:22:13
|
-LL | let _ = s.splitn(2, '=').next().unwrap();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').map_or(&*s, |x| x.0)`
+LL | let _ = s.splitn(2, '=').nth(1).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').unwrap().1`
error: manual implementation of `split_once`
--> $DIR/manual_split_once.rs:25:13
|
-LL | let _ = s.splitn(2, '=').nth(0).unwrap();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').map_or(&*s, |x| x.0)`
+LL | let _ = s.splitn(2, '=').skip(1).next().unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').unwrap().1`
error: manual implementation of `split_once`
- --> $DIR/manual_split_once.rs:28:13
+ --> $DIR/manual_split_once.rs:28:17
|
-LL | let _ = s.splitn(2, '=').skip(0).next().unwrap();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').map_or(*s, |x| x.0)`
+LL | let _ = s.splitn(2, '=').nth(1)?;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=')?.1`
error: manual implementation of `split_once`
- --> $DIR/manual_split_once.rs:31:17
+ --> $DIR/manual_split_once.rs:29:17
|
-LL | let _ = s.splitn(2, "key=value").next()?;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once("key=value").map_or(s, |x| x.0)`
+LL | let _ = s.splitn(2, '=').skip(1).next()?;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=')?.1`
-error: manual implementation of `split_once`
- --> $DIR/manual_split_once.rs:32:17
+error: manual implementation of `rsplit_once`
+ --> $DIR/manual_split_once.rs:30:17
|
-LL | let _ = s.splitn(2, "key=value").nth(1)?;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once("key=value")?.1`
+LL | let _ = s.rsplitn(2, '=').nth(1)?;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit_once('=')?.0`
-error: manual implementation of `split_once`
- --> $DIR/manual_split_once.rs:33:17
+error: manual implementation of `rsplit_once`
+ --> $DIR/manual_split_once.rs:31:17
|
-LL | let _ = s.splitn(2, "key=value").skip(1).next()?;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once("key=value")?.1`
+LL | let _ = s.rsplitn(2, '=').skip(1).next()?;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit_once('=')?.0`
error: manual implementation of `rsplit_once`
- --> $DIR/manual_split_once.rs:42:13
+ --> $DIR/manual_split_once.rs:39:13
|
LL | let _ = "key=value".rsplitn(2, '=').nth(1).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').unwrap().0`
error: manual implementation of `rsplit_once`
- --> $DIR/manual_split_once.rs:43:13
+ --> $DIR/manual_split_once.rs:40:18
|
-LL | let _ = "key=value".rsplitn(2, '=').nth(0);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').map(|x| x.1)`
+LL | let (_, _) = "key=value".rsplitn(2, '=').next_tuple().unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').map(|(x, y)| (y, x))`
error: manual implementation of `rsplit_once`
- --> $DIR/manual_split_once.rs:44:18
+ --> $DIR/manual_split_once.rs:41:13
|
-LL | let (_, _) = "key=value".rsplitn(2, '=').next_tuple().unwrap();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').map(|(x, y)| (y, x))`
+LL | let _ = s.rsplitn(2, '=').nth(1);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit_once('=').map(|x| x.0)`
+
+error: manual implementation of `split_once`
+ --> $DIR/manual_split_once.rs:45:5
+ |
+LL | let mut iter = "a.b.c".splitn(2, '.');
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | let l = iter.next().unwrap();
+ | ----------------------------- first usage here
+LL | let r = iter.next().unwrap();
+ | ----------------------------- second usage here
+ |
+help: try `split_once`
+ |
+LL | let (l, r) = "a.b.c".split_once('.').unwrap();
+ |
+help: remove the `iter` usages
+ |
+LL - let l = iter.next().unwrap();
+LL +
+ |
+help: remove the `iter` usages
+ |
+LL - let r = iter.next().unwrap();
+LL +
+ |
error: manual implementation of `split_once`
- --> $DIR/manual_split_once.rs:55:13
+ --> $DIR/manual_split_once.rs:49:5
+ |
+LL | let mut iter = "a.b.c".splitn(2, '.');
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | let l = iter.next()?;
+ | --------------------- first usage here
+LL | let r = iter.next()?;
+ | --------------------- second usage here
+ |
+help: try `split_once`
+ |
+LL | let (l, r) = "a.b.c".split_once('.')?;
+ |
+help: remove the `iter` usages
+ |
+LL - let l = iter.next()?;
+LL +
+ |
+help: remove the `iter` usages
+ |
+LL - let r = iter.next()?;
+LL +
+ |
+
+error: manual implementation of `rsplit_once`
+ --> $DIR/manual_split_once.rs:53:5
+ |
+LL | let mut iter = "a.b.c".rsplitn(2, '.');
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | let r = iter.next().unwrap();
+ | ----------------------------- first usage here
+LL | let l = iter.next().unwrap();
+ | ----------------------------- second usage here
+ |
+help: try `rsplit_once`
+ |
+LL | let (l, r) = "a.b.c".rsplit_once('.').unwrap();
+ |
+help: remove the `iter` usages
+ |
+LL - let r = iter.next().unwrap();
+LL +
+ |
+help: remove the `iter` usages
+ |
+LL - let l = iter.next().unwrap();
+LL +
+ |
+
+error: manual implementation of `rsplit_once`
+ --> $DIR/manual_split_once.rs:57:5
+ |
+LL | let mut iter = "a.b.c".rsplitn(2, '.');
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | let r = iter.next()?;
+ | --------------------- first usage here
+LL | let l = iter.next()?;
+ | --------------------- second usage here
+ |
+help: try `rsplit_once`
+ |
+LL | let (l, r) = "a.b.c".rsplit_once('.')?;
+ |
+help: remove the `iter` usages
+ |
+LL - let r = iter.next()?;
+LL +
+ |
+help: remove the `iter` usages
+ |
+LL - let l = iter.next()?;
+LL +
+ |
+
+error: manual implementation of `split_once`
+ --> $DIR/manual_split_once.rs:142:13
|
LL | let _ = "key=value".splitn(2, '=').nth(1).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1`
-error: aborting due to 16 previous errors
+error: manual implementation of `split_once`
+ --> $DIR/manual_split_once.rs:144:5
+ |
+LL | let mut iter = "a.b.c".splitn(2, '.');
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | let a = iter.next().unwrap();
+ | ----------------------------- first usage here
+LL | let b = iter.next().unwrap();
+ | ----------------------------- second usage here
+ |
+help: try `split_once`
+ |
+LL | let (a, b) = "a.b.c".split_once('.').unwrap();
+ |
+help: remove the `iter` usages
+ |
+LL - let a = iter.next().unwrap();
+LL +
+ |
+help: remove the `iter` usages
+ |
+LL - let b = iter.next().unwrap();
+LL +
+ |
+
+error: aborting due to 19 previous errors
unused_variables,
overflowing_literals,
clippy::excessive_precision,
- clippy::inconsistent_digit_grouping
+ clippy::inconsistent_digit_grouping,
+ clippy::unusual_byte_groupings
)]
fn main() {
let fail28 = 241_251_235E723_f64;
let ok29 = 42279.911_32;
+ // testing that the suggestion actually fits in its type
+ let fail30 = 127_i8; // should be i8
+ let fail31 = 240_u8; // should be u8
+ let ok32 = 360_8; // doesnt fit in either, should be ignored
+ let fail33 = 0x1234_i16;
+ let fail34 = 0xABCD_u16;
+ let ok35 = 0x12345_16;
+ let fail36 = 0xFFFF_FFFF_FFFF_FFFF_u64; // u64
+
+ // issue #6129
+ let ok37 = 123_32.123;
+ let ok38 = 124_64.0;
+
let _ = 1.123_45E1_f32;
}
unused_variables,
overflowing_literals,
clippy::excessive_precision,
- clippy::inconsistent_digit_grouping
+ clippy::inconsistent_digit_grouping,
+ clippy::unusual_byte_groupings
)]
fn main() {
let fail28 = 241251235E723_64;
let ok29 = 42279.911_32;
+ // testing that the suggestion actually fits in its type
+ let fail30 = 127_8; // should be i8
+ let fail31 = 240_8; // should be u8
+ let ok32 = 360_8; // doesnt fit in either, should be ignored
+ let fail33 = 0x1234_16;
+ let fail34 = 0xABCD_16;
+ let ok35 = 0x12345_16;
+ let fail36 = 0xFFFF_FFFF_FFFF_FFFF_64; // u64
+
+ // issue #6129
+ let ok37 = 123_32.123;
+ let ok38 = 124_64.0;
+
let _ = 1.12345E1_32;
}
error: mistyped literal suffix
- --> $DIR/mistyped_literal_suffix.rs:12:18
+ --> $DIR/mistyped_literal_suffix.rs:13:18
|
LL | let fail14 = 2_32;
| ^^^^ help: did you mean to write: `2_i32`
= note: `#[deny(clippy::mistyped_literal_suffixes)]` on by default
error: mistyped literal suffix
- --> $DIR/mistyped_literal_suffix.rs:13:18
+ --> $DIR/mistyped_literal_suffix.rs:14:18
|
LL | let fail15 = 4_64;
| ^^^^ help: did you mean to write: `4_i64`
error: mistyped literal suffix
- --> $DIR/mistyped_literal_suffix.rs:14:18
+ --> $DIR/mistyped_literal_suffix.rs:15:18
|
LL | let fail16 = 7_8; //
| ^^^ help: did you mean to write: `7_i8`
error: mistyped literal suffix
- --> $DIR/mistyped_literal_suffix.rs:15:18
+ --> $DIR/mistyped_literal_suffix.rs:16:18
|
LL | let fail17 = 23_16; //
| ^^^^^ help: did you mean to write: `23_i16`
error: mistyped literal suffix
- --> $DIR/mistyped_literal_suffix.rs:18:18
+ --> $DIR/mistyped_literal_suffix.rs:19:18
|
LL | let fail20 = 2__8; //
| ^^^^ help: did you mean to write: `2_i8`
error: mistyped literal suffix
- --> $DIR/mistyped_literal_suffix.rs:19:18
+ --> $DIR/mistyped_literal_suffix.rs:20:18
|
LL | let fail21 = 4___16; //
| ^^^^^^ help: did you mean to write: `4_i16`
error: mistyped literal suffix
- --> $DIR/mistyped_literal_suffix.rs:22:18
+ --> $DIR/mistyped_literal_suffix.rs:23:18
|
LL | let fail25 = 1E2_32;
| ^^^^^^ help: did you mean to write: `1E2_f32`
error: mistyped literal suffix
- --> $DIR/mistyped_literal_suffix.rs:23:18
+ --> $DIR/mistyped_literal_suffix.rs:24:18
|
LL | let fail26 = 43E7_64;
| ^^^^^^^ help: did you mean to write: `43E7_f64`
error: mistyped literal suffix
- --> $DIR/mistyped_literal_suffix.rs:24:18
+ --> $DIR/mistyped_literal_suffix.rs:25:18
|
LL | let fail27 = 243E17_32;
| ^^^^^^^^^ help: did you mean to write: `243E17_f32`
error: mistyped literal suffix
- --> $DIR/mistyped_literal_suffix.rs:25:18
+ --> $DIR/mistyped_literal_suffix.rs:26:18
|
LL | let fail28 = 241251235E723_64;
| ^^^^^^^^^^^^^^^^ help: did you mean to write: `241_251_235E723_f64`
error: mistyped literal suffix
- --> $DIR/mistyped_literal_suffix.rs:28:13
+ --> $DIR/mistyped_literal_suffix.rs:30:18
+ |
+LL | let fail30 = 127_8; // should be i8
+ | ^^^^^ help: did you mean to write: `127_i8`
+
+error: mistyped literal suffix
+ --> $DIR/mistyped_literal_suffix.rs:31:18
+ |
+LL | let fail31 = 240_8; // should be u8
+ | ^^^^^ help: did you mean to write: `240_u8`
+
+error: mistyped literal suffix
+ --> $DIR/mistyped_literal_suffix.rs:33:18
+ |
+LL | let fail33 = 0x1234_16;
+ | ^^^^^^^^^ help: did you mean to write: `0x1234_i16`
+
+error: mistyped literal suffix
+ --> $DIR/mistyped_literal_suffix.rs:34:18
+ |
+LL | let fail34 = 0xABCD_16;
+ | ^^^^^^^^^ help: did you mean to write: `0xABCD_u16`
+
+error: mistyped literal suffix
+ --> $DIR/mistyped_literal_suffix.rs:36:18
+ |
+LL | let fail36 = 0xFFFF_FFFF_FFFF_FFFF_64; // u64
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean to write: `0xFFFF_FFFF_FFFF_FFFF_u64`
+
+error: mistyped literal suffix
+ --> $DIR/mistyped_literal_suffix.rs:42:13
|
LL | let _ = 1.12345E1_32;
| ^^^^^^^^^^^^ help: did you mean to write: `1.123_45E1_f32`
-error: aborting due to 11 previous errors
+error: aborting due to 16 previous errors
impl Foo {
fn this_wont_hurt_a_bit(&self) -> &mut Foo {
- unimplemented!()
+ unsafe { unimplemented!() }
}
}
impl Ouch for Foo {
fn ouch(x: &Foo) -> &mut Foo {
- unimplemented!()
+ unsafe { unimplemented!() }
}
}
fn fail(x: &u32) -> &mut u16 {
- unimplemented!()
+ unsafe { unimplemented!() }
}
fn fail_lifetime<'a>(x: &'a u32, y: &mut u32) -> &'a mut u32 {
- unimplemented!()
+ unsafe { unimplemented!() }
}
fn fail_double<'a, 'b>(x: &'a u32, y: &'a u32, z: &'b mut u32) -> &'a mut u32 {
- unimplemented!()
+ unsafe { unimplemented!() }
}
// this is OK, because the result borrows y
fn works<'a>(x: &u32, y: &'a mut u32) -> &'a mut u32 {
- unimplemented!()
+ unsafe { unimplemented!() }
}
// this is also OK, because the result could borrow y
fn also_works<'a>(x: &'a u32, y: &'a mut u32) -> &'a mut u32 {
+ unsafe { unimplemented!() }
+}
+
+unsafe fn also_broken(x: &u32) -> &mut u32 {
+ unimplemented!()
+}
+
+fn without_unsafe(x: &u32) -> &mut u32 {
unimplemented!()
}
LL | fn fail_double<'a, 'b>(x: &'a u32, y: &'a u32, z: &'b mut u32) -> &'a mut u32 {
| ^^^^^^^ ^^^^^^^
-error: aborting due to 5 previous errors
+error: mutable borrow from immutable input(s)
+ --> $DIR/mut_from_ref.rs:44:35
+ |
+LL | unsafe fn also_broken(x: &u32) -> &mut u32 {
+ | ^^^^^^^^
+ |
+note: immutable borrow here
+ --> $DIR/mut_from_ref.rs:44:26
+ |
+LL | unsafe fn also_broken(x: &u32) -> &mut u32 {
+ | ^^^^
+
+error: aborting due to 6 previous errors
// run-rustfix
#![warn(clippy::needless_for_each)]
-#![allow(unused, clippy::needless_return, clippy::match_single_binding)]
+#![allow(
+ unused,
+ clippy::needless_return,
+ clippy::match_single_binding,
+ clippy::let_unit_value
+)]
use std::collections::HashMap;
// run-rustfix
#![warn(clippy::needless_for_each)]
-#![allow(unused, clippy::needless_return, clippy::match_single_binding)]
+#![allow(
+ unused,
+ clippy::needless_return,
+ clippy::match_single_binding,
+ clippy::let_unit_value
+)]
use std::collections::HashMap;
error: needless use of `for_each`
- --> $DIR/needless_for_each_fixable.rs:10:5
+ --> $DIR/needless_for_each_fixable.rs:15:5
|
LL | / v.iter().for_each(|elem| {
LL | | acc += elem;
|
error: needless use of `for_each`
- --> $DIR/needless_for_each_fixable.rs:13:5
+ --> $DIR/needless_for_each_fixable.rs:18:5
|
LL | / v.into_iter().for_each(|elem| {
LL | | acc += elem;
|
error: needless use of `for_each`
- --> $DIR/needless_for_each_fixable.rs:17:5
+ --> $DIR/needless_for_each_fixable.rs:22:5
|
LL | / [1, 2, 3].iter().for_each(|elem| {
LL | | acc += elem;
|
error: needless use of `for_each`
- --> $DIR/needless_for_each_fixable.rs:22:5
+ --> $DIR/needless_for_each_fixable.rs:27:5
|
LL | / hash_map.iter().for_each(|(k, v)| {
LL | | acc += k + v;
|
error: needless use of `for_each`
- --> $DIR/needless_for_each_fixable.rs:25:5
+ --> $DIR/needless_for_each_fixable.rs:30:5
|
LL | / hash_map.iter_mut().for_each(|(k, v)| {
LL | | acc += *k + *v;
|
error: needless use of `for_each`
- --> $DIR/needless_for_each_fixable.rs:28:5
+ --> $DIR/needless_for_each_fixable.rs:33:5
|
LL | / hash_map.keys().for_each(|k| {
LL | | acc += k;
|
error: needless use of `for_each`
- --> $DIR/needless_for_each_fixable.rs:31:5
+ --> $DIR/needless_for_each_fixable.rs:36:5
|
LL | / hash_map.values().for_each(|v| {
LL | | acc += v;
|
error: needless use of `for_each`
- --> $DIR/needless_for_each_fixable.rs:38:5
+ --> $DIR/needless_for_each_fixable.rs:43:5
|
LL | / my_vec().iter().for_each(|elem| {
LL | | acc += elem;
-#![allow(unused)]
+#![feature(let_chains)]
+#![allow(unused, clippy::nonminimal_bool, clippy::let_unit_value)]
+
+use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
+use std::rc::Rc;
+
+struct SignificantDrop;
+impl std::ops::Drop for SignificantDrop {
+ fn drop(&mut self) {
+ println!("dropped");
+ }
+}
fn main() {
let a;
b = "five"
}
- let c;
- if let Some(n) = Some(5) {
- c = n;
- } else {
- c = -50;
- }
-
let d;
if true {
let temp = 5;
if true {
e = format!("{} {}", a, b);
} else {
- e = format!("{}", c);
+ e = format!("{}", n);
}
let f;
panic!();
}
- println!("{}", a);
+ // Drop order only matters if both are significant
+ let x;
+ let y = SignificantDrop;
+ x = 1;
+
+ let x;
+ let y = 1;
+ x = SignificantDrop;
+
+ let x;
+ // types that should be considered insignificant
+ let y = 1;
+ let y = "2";
+ let y = String::new();
+ let y = vec![3.0];
+ let y = HashMap::<usize, usize>::new();
+ let y = BTreeMap::<usize, usize>::new();
+ let y = HashSet::<usize>::new();
+ let y = BTreeSet::<usize>::new();
+ let y = Box::new(4);
+ x = SignificantDrop;
}
async fn in_async() -> &'static str {
}
in_macro!();
- println!("{}", x);
+ // ignore if-lets - https://github.com/rust-lang/rust-clippy/issues/8613
+ let x;
+ if let Some(n) = Some("v") {
+ x = 1;
+ } else {
+ x = 2;
+ }
+
+ let x;
+ if true && let Some(n) = Some("let chains too") {
+ x = 1;
+ } else {
+ x = 2;
+ }
+
+ // ignore mut bindings
+ // https://github.com/shepmaster/twox-hash/blob/b169c16d86eb8ea4a296b0acb9d00ca7e3c3005f/src/sixty_four.rs#L88-L93
+ // https://github.com/dtolnay/thiserror/blob/21c26903e29cb92ba1a7ff11e82ae2001646b60d/tests/test_generics.rs#L91-L100
+ let mut x: usize;
+ x = 1;
+ x = 2;
+ x = 3;
+
+ // should not move the declaration if `x` has a significant drop, and there
+ // is another binding with a significant drop between it and the first usage
+ let x;
+ let y = SignificantDrop;
+ x = SignificantDrop;
}
-error: unneeded late initalization
- --> $DIR/needless_late_init.rs:4:5
+error: unneeded late initialization
+ --> $DIR/needless_late_init.rs:15:5
|
LL | let a;
| ^^^^^^
LL | };
| +
-error: unneeded late initalization
- --> $DIR/needless_late_init.rs:13:5
+error: unneeded late initialization
+ --> $DIR/needless_late_init.rs:24:5
|
LL | let b;
| ^^^^^^
LL | };
| +
-error: unneeded late initalization
- --> $DIR/needless_late_init.rs:20:5
- |
-LL | let c;
- | ^^^^^^
- |
-help: declare `c` here
- |
-LL | let c = if let Some(n) = Some(5) {
- | +++++++
-help: remove the assignments from the branches
- |
-LL ~ n
-LL | } else {
-LL ~ -50
- |
-help: add a semicolon after the `if` expression
- |
-LL | };
- | +
-
-error: unneeded late initalization
- --> $DIR/needless_late_init.rs:27:5
+error: unneeded late initialization
+ --> $DIR/needless_late_init.rs:31:5
|
LL | let d;
| ^^^^^^
LL | };
| +
-error: unneeded late initalization
- --> $DIR/needless_late_init.rs:35:5
+error: unneeded late initialization
+ --> $DIR/needless_late_init.rs:39:5
|
LL | let e;
| ^^^^^^
|
LL ~ format!("{} {}", a, b)
LL | } else {
-LL ~ format!("{}", c)
+LL ~ format!("{}", n)
|
help: add a semicolon after the `if` expression
|
LL | };
| +
-error: unneeded late initalization
- --> $DIR/needless_late_init.rs:42:5
+error: unneeded late initialization
+ --> $DIR/needless_late_init.rs:46:5
|
LL | let f;
| ^^^^^^
LL + 1 => "three",
|
-error: unneeded late initalization
- --> $DIR/needless_late_init.rs:48:5
+error: unneeded late initialization
+ --> $DIR/needless_late_init.rs:52:5
|
LL | let g: usize;
| ^^^^^^^^^^^^^
LL | };
| +
-error: unneeded late initalization
- --> $DIR/needless_late_init.rs:63:5
+error: unneeded late initialization
+ --> $DIR/needless_late_init.rs:60:5
+ |
+LL | let x;
+ | ^^^^^^ created here
+LL | let y = SignificantDrop;
+LL | x = 1;
+ | ^^^^^ initialised here
+ |
+help: declare `x` here
+ |
+LL | let x = 1;
+ | ~~~~~
+
+error: unneeded late initialization
+ --> $DIR/needless_late_init.rs:64:5
+ |
+LL | let x;
+ | ^^^^^^ created here
+LL | let y = 1;
+LL | x = SignificantDrop;
+ | ^^^^^^^^^^^^^^^^^^^ initialised here
+ |
+help: declare `x` here
+ |
+LL | let x = SignificantDrop;
+ | ~~~~~
+
+error: unneeded late initialization
+ --> $DIR/needless_late_init.rs:68:5
+ |
+LL | let x;
+ | ^^^^^^ created here
+...
+LL | x = SignificantDrop;
+ | ^^^^^^^^^^^^^^^^^^^ initialised here
+ |
+help: declare `x` here
+ |
+LL | let x = SignificantDrop;
+ | ~~~~~
+
+error: unneeded late initialization
+ --> $DIR/needless_late_init.rs:87:5
|
LL | let a;
| ^^^^^^
LL | };
| +
-error: unneeded late initalization
- --> $DIR/needless_late_init.rs:80:5
+error: unneeded late initialization
+ --> $DIR/needless_late_init.rs:104:5
|
LL | let a;
| ^^^^^^
LL | };
| +
-error: aborting due to 9 previous errors
+error: aborting due to 11 previous errors
let d: usize = 1;
- let mut e = 1;
- e = 2;
-
-
- let h = format!("{}", e);
-
- println!("{}", a);
+ let e = format!("{}", d);
}
let d: usize;
d = 1;
- let mut e;
- e = 1;
- e = 2;
-
- let h;
- h = format!("{}", e);
-
- println!("{}", a);
+ let e;
+ e = format!("{}", d);
}
-error: unneeded late initalization
+error: unneeded late initialization
--> $DIR/needless_late_init_fixable.rs:6:5
|
LL | let a;
- | ^^^^^^
+ | ^^^^^^ created here
+LL | a = "zero";
+ | ^^^^^^^^^^ initialised here
|
= note: `-D clippy::needless-late-init` implied by `-D warnings`
help: declare `a` here
LL | let a = "zero";
| ~~~~~
-error: unneeded late initalization
+error: unneeded late initialization
--> $DIR/needless_late_init_fixable.rs:9:5
|
LL | let b;
- | ^^^^^^
+ | ^^^^^^ created here
+LL | let c;
+LL | b = 1;
+ | ^^^^^ initialised here
|
help: declare `b` here
|
LL | let b = 1;
| ~~~~~
-error: unneeded late initalization
+error: unneeded late initialization
--> $DIR/needless_late_init_fixable.rs:10:5
|
LL | let c;
- | ^^^^^^
+ | ^^^^^^ created here
+LL | b = 1;
+LL | c = 2;
+ | ^^^^^ initialised here
|
help: declare `c` here
|
LL | let c = 2;
| ~~~~~
-error: unneeded late initalization
+error: unneeded late initialization
--> $DIR/needless_late_init_fixable.rs:14:5
|
LL | let d: usize;
- | ^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^ created here
+LL | d = 1;
+ | ^^^^^ initialised here
|
help: declare `d` here
|
LL | let d: usize = 1;
| ~~~~~~~~~~~~
-error: unneeded late initalization
+error: unneeded late initialization
--> $DIR/needless_late_init_fixable.rs:17:5
|
-LL | let mut e;
- | ^^^^^^^^^^
+LL | let e;
+ | ^^^^^^ created here
+LL | e = format!("{}", d);
+ | ^^^^^^^^^^^^^^^^^^^^ initialised here
|
help: declare `e` here
|
-LL | let mut e = 1;
- | ~~~~~~~~~
-
-error: unneeded late initalization
- --> $DIR/needless_late_init_fixable.rs:21:5
- |
-LL | let h;
- | ^^^^^^
- |
-help: declare `h` here
- |
-LL | let h = format!("{}", e);
+LL | let e = format!("{}", d);
| ~~~~~
-error: aborting due to 6 previous errors
+error: aborting due to 5 previous errors
} else {
None
};
+
+ // Don't trigger
+ let _ = if let Some(a) = Some(1) { Some(a) } else { Some(2) };
+}
+
+fn if_let_option_result() -> Result<(), ()> {
+ fn f(x: i32) -> Result<Option<i32>, ()> {
+ Ok(Some(x))
+ }
+ // Don't trigger
+ let _ = if let Some(v) = f(1)? { Some(v) } else { f(2)? };
+ Ok(())
}
fn if_let_result() {
} else {
None
};
+
+ // Don't trigger
+ let _ = if let Some(a) = Some(1) { Some(a) } else { Some(2) };
+}
+
+fn if_let_option_result() -> Result<(), ()> {
+ fn f(x: i32) -> Result<Option<i32>, ()> {
+ Ok(Some(x))
+ }
+ // Don't trigger
+ let _ = if let Some(v) = f(1)? { Some(v) } else { f(2)? };
+ Ok(())
}
fn if_let_result() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `Some(1)`
error: this if-let expression is unnecessary
- --> $DIR/needless_match.rs:110:31
+ --> $DIR/needless_match.rs:122:31
|
LL | let _: Result<i32, i32> = if let Err(e) = x { Err(e) } else { x };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x`
error: this if-let expression is unnecessary
- --> $DIR/needless_match.rs:111:31
+ --> $DIR/needless_match.rs:123:31
|
LL | let _: Result<i32, i32> = if let Ok(val) = x { Ok(val) } else { x };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x`
error: this if-let expression is unnecessary
- --> $DIR/needless_match.rs:117:21
+ --> $DIR/needless_match.rs:129:21
|
LL | let _: Simple = if let Simple::A = x {
| _____________________^
| |_____^ help: replace it with: `x`
error: this match expression is unnecessary
- --> $DIR/needless_match.rs:156:26
+ --> $DIR/needless_match.rs:168:26
|
LL | let _: Complex = match ce {
| __________________________^
let _ = Some(Box::new(1)).as_deref();
let _ = Some(Box::new(1)).as_deref_mut();
+ let mut y = 0;
+ let mut x = Some(&mut y);
+ for _ in 0..3 {
+ let _ = x.as_deref_mut();
+ }
+
+ let mut y = 0;
+ let mut x = Some(&mut y);
+ let mut closure = || {
+ let _ = x.as_deref_mut();
+ };
+ closure();
+ closure();
+
// #7846
let mut i = 0;
let mut opt_vec = vec![Some(&mut i)];
let _ = Some(Box::new(1)).as_deref();
let _ = Some(Box::new(1)).as_deref_mut();
+ let mut y = 0;
+ let mut x = Some(&mut y);
+ for _ in 0..3 {
+ let _ = x.as_deref_mut();
+ }
+
+ let mut y = 0;
+ let mut x = Some(&mut y);
+ let mut closure = || {
+ let _ = x.as_deref_mut();
+ };
+ closure();
+ closure();
+
// #7846
let mut i = 0;
let mut opt_vec = vec![Some(&mut i)];
--- /dev/null
+// run-rustfix
+
+fn main() {
+ println!("Testing non erroneous option_take_on_temporary");
+ let mut option = Some(1);
+ let _ = Box::new(move || option.take().unwrap());
+
+ println!("Testing non erroneous option_take_on_temporary");
+ let x = Some(3);
+ x.as_ref();
+
+ println!("Testing erroneous option_take_on_temporary");
+ let x = Some(3);
+ x.as_ref();
+}
--- /dev/null
+// run-rustfix
+
+fn main() {
+ println!("Testing non erroneous option_take_on_temporary");
+ let mut option = Some(1);
+ let _ = Box::new(move || option.take().unwrap());
+
+ println!("Testing non erroneous option_take_on_temporary");
+ let x = Some(3);
+ x.as_ref();
+
+ println!("Testing erroneous option_take_on_temporary");
+ let x = Some(3);
+ x.as_ref().take();
+}
--- /dev/null
+error: called `Option::take()` on a temporary value
+ --> $DIR/needless_option_take.rs:14:5
+ |
+LL | x.as_ref().take();
+ | ^^^^^^^^^^^^^^^^^ help: try: `x.as_ref()`
+ |
+ = note: `-D clippy::needless-option-take` implied by `-D warnings`
+
+error: aborting due to previous error
+
let _ = str.rsplitn(2, '=').nth(1);
let (_, _) = str.rsplitn(2, '=').next_tuple().unwrap();
let (_, _) = str.rsplit('=').next_tuple().unwrap();
+
+ let _ = str.split('=').next();
+ let _ = str.split('=').nth(3);
+ let _ = str.splitn(5, '=').nth(4);
+ let _ = str.splitn(5, '=').nth(5);
+}
+
+fn _question_mark(s: &str) -> Option<()> {
+ let _ = s.split('=').next()?;
+ let _ = s.split('=').nth(0)?;
+ let _ = s.rsplit('=').next()?;
+ let _ = s.rsplit('=').nth(0)?;
+
+ Some(())
+}
+
+fn _test_msrv() {
+ #![clippy::msrv = "1.51"]
+ // `manual_split_once` MSRV shouldn't apply to `needless_splitn`
+ let _ = "key=value".split('=').nth(0).unwrap();
}
let _ = str.rsplitn(2, '=').nth(1);
let (_, _) = str.rsplitn(2, '=').next_tuple().unwrap();
let (_, _) = str.rsplitn(3, '=').next_tuple().unwrap();
+
+ let _ = str.splitn(5, '=').next();
+ let _ = str.splitn(5, '=').nth(3);
+ let _ = str.splitn(5, '=').nth(4);
+ let _ = str.splitn(5, '=').nth(5);
+}
+
+fn _question_mark(s: &str) -> Option<()> {
+ let _ = s.splitn(2, '=').next()?;
+ let _ = s.splitn(2, '=').nth(0)?;
+ let _ = s.rsplitn(2, '=').next()?;
+ let _ = s.rsplitn(2, '=').nth(0)?;
+
+ Some(())
+}
+
+fn _test_msrv() {
+ #![clippy::msrv = "1.51"]
+ // `manual_split_once` MSRV shouldn't apply to `needless_splitn`
+ let _ = "key=value".splitn(2, '=').nth(0).unwrap();
}
LL | let (_, _) = str.rsplitn(3, '=').next_tuple().unwrap();
| ^^^^^^^^^^^^^^^^^^^ help: try this: `str.rsplit('=')`
-error: aborting due to 6 previous errors
+error: unnecessary use of `splitn`
+ --> $DIR/needless_splitn.rs:28:13
+ |
+LL | let _ = str.splitn(5, '=').next();
+ | ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')`
+
+error: unnecessary use of `splitn`
+ --> $DIR/needless_splitn.rs:29:13
+ |
+LL | let _ = str.splitn(5, '=').nth(3);
+ | ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')`
+
+error: unnecessary use of `splitn`
+ --> $DIR/needless_splitn.rs:35:13
+ |
+LL | let _ = s.splitn(2, '=').next()?;
+ | ^^^^^^^^^^^^^^^^ help: try this: `s.split('=')`
+
+error: unnecessary use of `splitn`
+ --> $DIR/needless_splitn.rs:36:13
+ |
+LL | let _ = s.splitn(2, '=').nth(0)?;
+ | ^^^^^^^^^^^^^^^^ help: try this: `s.split('=')`
+
+error: unnecessary use of `rsplitn`
+ --> $DIR/needless_splitn.rs:37:13
+ |
+LL | let _ = s.rsplitn(2, '=').next()?;
+ | ^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit('=')`
+
+error: unnecessary use of `rsplitn`
+ --> $DIR/needless_splitn.rs:38:13
+ |
+LL | let _ = s.rsplitn(2, '=').nth(0)?;
+ | ^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit('=')`
+
+error: unnecessary use of `splitn`
+ --> $DIR/needless_splitn.rs:46:13
+ |
+LL | let _ = "key=value".splitn(2, '=').nth(0).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split('=')`
+
+error: aborting due to 13 previous errors
-#![allow(dead_code, clippy::missing_safety_doc)]
+#![allow(dead_code, clippy::missing_safety_doc, clippy::extra_unused_lifetimes)]
#![warn(clippy::new_without_default)]
pub struct Foo;
#![warn(clippy::all)]
-#![allow(unused, clippy::println_empty_string, non_snake_case)]
+#![allow(unused, clippy::println_empty_string, non_snake_case, clippy::let_unit_value)]
#[derive(Clone, Debug)]
enum MaybeInst {
// Ok because it's in macro
let _ = tuple_struct_init!();
+
+ type Alias = TupleStruct;
+
+ // Aliases can't be tuple constructed #8638
+ let _ = Alias { 0: 0, 1: 1, 2: 2 };
}
// Ok because it's in macro
let _ = tuple_struct_init!();
+
+ type Alias = TupleStruct;
+
+ // Aliases can't be tuple constructed #8638
+ let _ = Alias { 0: 0, 1: 1, 2: 2 };
}
// run-rustfix
#![warn(clippy::option_if_let_else)]
-#![allow(clippy::redundant_closure, clippy::ref_option_ref, clippy::equatable_if_let)]
+#![allow(
+ clippy::redundant_closure,
+ clippy::ref_option_ref,
+ clippy::equatable_if_let,
+ clippy::let_unit_value
+)]
fn bad1(string: Option<&str>) -> (bool, &str) {
string.map_or((false, "hello"), |x| (true, x))
// run-rustfix
#![warn(clippy::option_if_let_else)]
-#![allow(clippy::redundant_closure, clippy::ref_option_ref, clippy::equatable_if_let)]
+#![allow(
+ clippy::redundant_closure,
+ clippy::ref_option_ref,
+ clippy::equatable_if_let,
+ clippy::let_unit_value
+)]
fn bad1(string: Option<&str>) -> (bool, &str) {
if let Some(x) = string {
error: use Option::map_or instead of an if let/else
- --> $DIR/option_if_let_else.rs:6:5
+ --> $DIR/option_if_let_else.rs:11:5
|
LL | / if let Some(x) = string {
LL | | (true, x)
= note: `-D clippy::option-if-let-else` implied by `-D warnings`
error: use Option::map_or instead of an if let/else
- --> $DIR/option_if_let_else.rs:24:13
+ --> $DIR/option_if_let_else.rs:29:13
|
LL | let _ = if let Some(s) = *string { s.len() } else { 0 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `string.map_or(0, |s| s.len())`
error: use Option::map_or instead of an if let/else
- --> $DIR/option_if_let_else.rs:25:13
+ --> $DIR/option_if_let_else.rs:30:13
|
LL | let _ = if let Some(s) = &num { s } else { &0 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)`
error: use Option::map_or instead of an if let/else
- --> $DIR/option_if_let_else.rs:26:13
+ --> $DIR/option_if_let_else.rs:31:13
|
LL | let _ = if let Some(s) = &mut num {
| _____________^
|
error: use Option::map_or instead of an if let/else
- --> $DIR/option_if_let_else.rs:32:13
+ --> $DIR/option_if_let_else.rs:37:13
|
LL | let _ = if let Some(ref s) = num { s } else { &0 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)`
error: use Option::map_or instead of an if let/else
- --> $DIR/option_if_let_else.rs:33:13
+ --> $DIR/option_if_let_else.rs:38:13
|
LL | let _ = if let Some(mut s) = num {
| _____________^
|
error: use Option::map_or instead of an if let/else
- --> $DIR/option_if_let_else.rs:39:13
+ --> $DIR/option_if_let_else.rs:44:13
|
LL | let _ = if let Some(ref mut s) = num {
| _____________^
|
error: use Option::map_or instead of an if let/else
- --> $DIR/option_if_let_else.rs:48:5
+ --> $DIR/option_if_let_else.rs:53:5
|
LL | / if let Some(x) = arg {
LL | | let y = x * x;
|
error: use Option::map_or_else instead of an if let/else
- --> $DIR/option_if_let_else.rs:61:13
+ --> $DIR/option_if_let_else.rs:66:13
|
LL | let _ = if let Some(x) = arg {
| _____________^
| |_____^ help: try: `arg.map_or_else(|| side_effect(), |x| x)`
error: use Option::map_or_else instead of an if let/else
- --> $DIR/option_if_let_else.rs:70:13
+ --> $DIR/option_if_let_else.rs:75:13
|
LL | let _ = if let Some(x) = arg {
| _____________^
|
error: use Option::map_or_else instead of an if let/else
- --> $DIR/option_if_let_else.rs:103:13
+ --> $DIR/option_if_let_else.rs:108:13
|
LL | / if let Some(idx) = s.find('.') {
LL | | vec![s[..idx].to_string(), s[idx..].to_string()]
| |_____________^ help: try: `s.find('.').map_or_else(|| vec![s.to_string()], |idx| vec![s[..idx].to_string(), s[idx..].to_string()])`
error: use Option::map_or instead of an if let/else
- --> $DIR/option_if_let_else.rs:127:13
+ --> $DIR/option_if_let_else.rs:132:13
|
LL | let _ = if let Some(x) = optional { x + 2 } else { 5 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)`
error: use Option::map_or instead of an if let/else
- --> $DIR/option_if_let_else.rs:136:13
+ --> $DIR/option_if_let_else.rs:141:13
|
LL | let _ = if let Some(x) = Some(0) {
| _____________^
|
error: use Option::map_or instead of an if let/else
- --> $DIR/option_if_let_else.rs:164:13
+ --> $DIR/option_if_let_else.rs:169:13
|
LL | let _ = if let Some(x) = Some(0) { s.len() + x } else { s.len() };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(0).map_or(s.len(), |x| s.len() + x)`
error: use Option::map_or instead of an if let/else
- --> $DIR/option_if_let_else.rs:168:13
+ --> $DIR/option_if_let_else.rs:173:13
|
LL | let _ = if let Some(x) = Some(0) {
| _____________^
--- /dev/null
+// run-rustfix
+
+fn main() {
+ println!("Testing non erroneous option_take_on_temporary");
+ let mut option = Some(1);
+ let _ = Box::new(move || option.take().unwrap());
+
+ println!("Testing non erroneous option_take_on_temporary");
+ let x = Some(3);
+ x.as_ref();
+
+ println!("Testing erroneous option_take_on_temporary");
+ let x = Some(3);
+ x.as_ref();
+}
// run-rustfix
#![warn(clippy::or_then_unwrap)]
-#![allow(clippy::map_identity)]
+#![allow(clippy::map_identity, clippy::let_unit_value)]
struct SomeStruct;
impl SomeStruct {
// run-rustfix
#![warn(clippy::or_then_unwrap)]
-#![allow(clippy::map_identity)]
+#![allow(clippy::map_identity, clippy::let_unit_value)]
struct SomeStruct;
impl SomeStruct {
-#![allow(clippy::assertions_on_constants, clippy::eq_op)]
+#![allow(clippy::assertions_on_constants, clippy::eq_op, clippy::let_unit_value)]
#![feature(inline_const)]
#![warn(clippy::unimplemented, clippy::unreachable, clippy::todo, clippy::panic)]
--- /dev/null
+#![warn(clippy::pub_use)]
+#![allow(unused_imports)]
+#![no_main]
+
+pub mod outer {
+ mod inner {
+ pub struct Test {}
+ }
+ // should be linted
+ pub use inner::Test;
+}
+
+// should not be linted
+use std::fmt;
--- /dev/null
+error: using `pub use`
+ --> $DIR/pub_use.rs:10:5
+ |
+LL | pub use inner::Test;
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::pub-use` implied by `-D warnings`
+ = help: move the exported item to a public module instead
+
+error: aborting due to previous error
+
pub use m4::*;
+mod issue_8732 {
+ #[allow(unused_macros)]
+ macro_rules! some_macro {
+ () => {};
+ }
+
+ #[allow(unused_imports)]
+ pub(crate) use some_macro; // ok: macro exports are exempt
+}
+
fn main() {}
pub use m4::*;
+mod issue_8732 {
+ #[allow(unused_macros)]
+ macro_rules! some_macro {
+ () => {};
+ }
+
+ #[allow(unused_imports)]
+ pub(crate) use some_macro; // ok: macro exports are exempt
+}
+
fn main() {}
-//! Test for Clippy lint renames.
+// This file was generated by `cargo dev update_lints`.
+// Use that command to update this file and do not edit by hand.
+// Manual edits will be overwritten.
+
// run-rustfix
-#![allow(dead_code)]
-// allow the new lint name here, to test if the new name works
-#![allow(clippy::module_name_repetitions)]
-#![allow(clippy::new_without_default)]
+#![allow(clippy::blocks_in_if_conditions)]
+#![allow(clippy::box_collection)]
#![allow(clippy::redundant_static_lifetimes)]
#![allow(clippy::cognitive_complexity)]
+#![allow(clippy::disallowed_methods)]
+#![allow(clippy::disallowed_types)]
+#![allow(clippy::for_loops_over_fallibles)]
+#![allow(clippy::useless_conversion)]
+#![allow(clippy::match_result_ok)]
+#![allow(clippy::new_without_default)]
#![allow(clippy::bind_instead_of_map)]
-#![allow(clippy::box_collection)]
-#![allow(clippy::blocks_in_if_conditions)]
+#![allow(clippy::expect_used)]
#![allow(clippy::map_unwrap_or)]
#![allow(clippy::unwrap_used)]
-#![allow(clippy::expect_used)]
-#![allow(clippy::for_loops_over_fallibles)]
-#![allow(clippy::useless_conversion)]
-#![allow(clippy::invisible_characters)]
+#![allow(clippy::needless_borrow)]
#![allow(clippy::single_char_add_str)]
-#![allow(clippy::match_result_ok)]
-#![allow(clippy::disallowed_types)]
-#![allow(clippy::disallowed_methods)]
+#![allow(clippy::module_name_repetitions)]
#![allow(clippy::recursive_format_impl)]
-// uplifted lints
-#![allow(invalid_value)]
-#![allow(array_into_iter)]
-#![allow(unused_labels)]
+#![allow(clippy::invisible_characters)]
#![allow(drop_bounds)]
-#![allow(temporary_cstring_as_ptr)]
-#![allow(non_fmt_panics)]
-#![allow(unknown_lints)]
+#![allow(array_into_iter)]
#![allow(invalid_atomic_ordering)]
+#![allow(invalid_value)]
#![allow(enum_intrinsics_non_enums)]
-// warn for the old lint name here, to test if the renaming worked
-#![warn(clippy::module_name_repetitions)]
-#![warn(clippy::new_without_default)]
+#![allow(non_fmt_panics)]
+#![allow(temporary_cstring_as_ptr)]
+#![allow(unknown_lints)]
+#![allow(unused_labels)]
+#![warn(clippy::blocks_in_if_conditions)]
+#![warn(clippy::blocks_in_if_conditions)]
+#![warn(clippy::box_collection)]
#![warn(clippy::redundant_static_lifetimes)]
#![warn(clippy::cognitive_complexity)]
+#![warn(clippy::disallowed_methods)]
+#![warn(clippy::disallowed_types)]
+#![warn(clippy::for_loops_over_fallibles)]
+#![warn(clippy::for_loops_over_fallibles)]
+#![warn(clippy::useless_conversion)]
+#![warn(clippy::match_result_ok)]
+#![warn(clippy::new_without_default)]
#![warn(clippy::bind_instead_of_map)]
-#![warn(clippy::box_collection)]
-#![warn(clippy::blocks_in_if_conditions)]
-#![warn(clippy::blocks_in_if_conditions)]
-#![warn(clippy::map_unwrap_or)]
+#![warn(clippy::expect_used)]
#![warn(clippy::map_unwrap_or)]
#![warn(clippy::map_unwrap_or)]
#![warn(clippy::unwrap_used)]
-#![warn(clippy::unwrap_used)]
-#![warn(clippy::expect_used)]
+#![warn(clippy::needless_borrow)]
#![warn(clippy::expect_used)]
-#![warn(clippy::for_loops_over_fallibles)]
-#![warn(clippy::for_loops_over_fallibles)]
-#![warn(clippy::useless_conversion)]
-#![warn(clippy::invisible_characters)]
+#![warn(clippy::map_unwrap_or)]
+#![warn(clippy::unwrap_used)]
#![warn(clippy::single_char_add_str)]
-#![warn(clippy::match_result_ok)]
-#![warn(clippy::disallowed_types)]
-#![warn(clippy::disallowed_methods)]
-#![warn(clippy::needless_borrow)]
+#![warn(clippy::module_name_repetitions)]
#![warn(clippy::recursive_format_impl)]
-// uplifted lints
-#![warn(invalid_value)]
-#![warn(array_into_iter)]
-#![warn(unused_labels)]
+#![warn(clippy::invisible_characters)]
#![warn(drop_bounds)]
-#![warn(temporary_cstring_as_ptr)]
-#![warn(non_fmt_panics)]
-#![warn(unknown_lints)]
+#![warn(array_into_iter)]
#![warn(invalid_atomic_ordering)]
+#![warn(invalid_value)]
#![warn(enum_intrinsics_non_enums)]
+#![warn(non_fmt_panics)]
+#![warn(temporary_cstring_as_ptr)]
+#![warn(unknown_lints)]
+#![warn(unused_labels)]
fn main() {}
-//! Test for Clippy lint renames.
+// This file was generated by `cargo dev update_lints`.
+// Use that command to update this file and do not edit by hand.
+// Manual edits will be overwritten.
+
// run-rustfix
-#![allow(dead_code)]
-// allow the new lint name here, to test if the new name works
-#![allow(clippy::module_name_repetitions)]
-#![allow(clippy::new_without_default)]
+#![allow(clippy::blocks_in_if_conditions)]
+#![allow(clippy::box_collection)]
#![allow(clippy::redundant_static_lifetimes)]
#![allow(clippy::cognitive_complexity)]
+#![allow(clippy::disallowed_methods)]
+#![allow(clippy::disallowed_types)]
+#![allow(clippy::for_loops_over_fallibles)]
+#![allow(clippy::useless_conversion)]
+#![allow(clippy::match_result_ok)]
+#![allow(clippy::new_without_default)]
#![allow(clippy::bind_instead_of_map)]
-#![allow(clippy::box_collection)]
-#![allow(clippy::blocks_in_if_conditions)]
+#![allow(clippy::expect_used)]
#![allow(clippy::map_unwrap_or)]
#![allow(clippy::unwrap_used)]
-#![allow(clippy::expect_used)]
-#![allow(clippy::for_loops_over_fallibles)]
-#![allow(clippy::useless_conversion)]
-#![allow(clippy::invisible_characters)]
+#![allow(clippy::needless_borrow)]
#![allow(clippy::single_char_add_str)]
-#![allow(clippy::match_result_ok)]
-#![allow(clippy::disallowed_types)]
-#![allow(clippy::disallowed_methods)]
+#![allow(clippy::module_name_repetitions)]
#![allow(clippy::recursive_format_impl)]
-// uplifted lints
-#![allow(invalid_value)]
-#![allow(array_into_iter)]
-#![allow(unused_labels)]
+#![allow(clippy::invisible_characters)]
#![allow(drop_bounds)]
-#![allow(temporary_cstring_as_ptr)]
-#![allow(non_fmt_panics)]
-#![allow(unknown_lints)]
+#![allow(array_into_iter)]
#![allow(invalid_atomic_ordering)]
+#![allow(invalid_value)]
#![allow(enum_intrinsics_non_enums)]
-// warn for the old lint name here, to test if the renaming worked
-#![warn(clippy::stutter)]
-#![warn(clippy::new_without_default_derive)]
+#![allow(non_fmt_panics)]
+#![allow(temporary_cstring_as_ptr)]
+#![allow(unknown_lints)]
+#![allow(unused_labels)]
+#![warn(clippy::block_in_if_condition_expr)]
+#![warn(clippy::block_in_if_condition_stmt)]
+#![warn(clippy::box_vec)]
#![warn(clippy::const_static_lifetime)]
#![warn(clippy::cyclomatic_complexity)]
+#![warn(clippy::disallowed_method)]
+#![warn(clippy::disallowed_type)]
+#![warn(clippy::for_loop_over_option)]
+#![warn(clippy::for_loop_over_result)]
+#![warn(clippy::identity_conversion)]
+#![warn(clippy::if_let_some_result)]
+#![warn(clippy::new_without_default_derive)]
#![warn(clippy::option_and_then_some)]
-#![warn(clippy::box_vec)]
-#![warn(clippy::block_in_if_condition_expr)]
-#![warn(clippy::block_in_if_condition_stmt)]
+#![warn(clippy::option_expect_used)]
#![warn(clippy::option_map_unwrap_or)]
#![warn(clippy::option_map_unwrap_or_else)]
-#![warn(clippy::result_map_unwrap_or_else)]
#![warn(clippy::option_unwrap_used)]
-#![warn(clippy::result_unwrap_used)]
-#![warn(clippy::option_expect_used)]
+#![warn(clippy::ref_in_deref)]
#![warn(clippy::result_expect_used)]
-#![warn(clippy::for_loop_over_option)]
-#![warn(clippy::for_loop_over_result)]
-#![warn(clippy::identity_conversion)]
-#![warn(clippy::zero_width_space)]
+#![warn(clippy::result_map_unwrap_or_else)]
+#![warn(clippy::result_unwrap_used)]
#![warn(clippy::single_char_push_str)]
-#![warn(clippy::if_let_some_result)]
-#![warn(clippy::disallowed_type)]
-#![warn(clippy::disallowed_method)]
-#![warn(clippy::ref_in_deref)]
+#![warn(clippy::stutter)]
#![warn(clippy::to_string_in_display)]
-// uplifted lints
-#![warn(clippy::invalid_ref)]
-#![warn(clippy::into_iter_on_array)]
-#![warn(clippy::unused_label)]
+#![warn(clippy::zero_width_space)]
#![warn(clippy::drop_bounds)]
-#![warn(clippy::temporary_cstring_as_ptr)]
-#![warn(clippy::panic_params)]
-#![warn(clippy::unknown_clippy_lints)]
+#![warn(clippy::into_iter_on_array)]
#![warn(clippy::invalid_atomic_ordering)]
+#![warn(clippy::invalid_ref)]
#![warn(clippy::mem_discriminant_non_enum)]
+#![warn(clippy::panic_params)]
+#![warn(clippy::temporary_cstring_as_ptr)]
+#![warn(clippy::unknown_clippy_lints)]
+#![warn(clippy::unused_label)]
fn main() {}
-error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions`
+error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_if_conditions`
--> $DIR/rename.rs:35:9
|
-LL | #![warn(clippy::stutter)]
- | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions`
+LL | #![warn(clippy::block_in_if_condition_expr)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions`
|
= note: `-D renamed-and-removed-lints` implied by `-D warnings`
-error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default`
+error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_if_conditions`
--> $DIR/rename.rs:36:9
|
-LL | #![warn(clippy::new_without_default_derive)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default`
+LL | #![warn(clippy::block_in_if_condition_stmt)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions`
-error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes`
+error: lint `clippy::box_vec` has been renamed to `clippy::box_collection`
--> $DIR/rename.rs:37:9
|
+LL | #![warn(clippy::box_vec)]
+ | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection`
+
+error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes`
+ --> $DIR/rename.rs:38:9
+ |
LL | #![warn(clippy::const_static_lifetime)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes`
error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity`
- --> $DIR/rename.rs:38:9
+ --> $DIR/rename.rs:39:9
|
LL | #![warn(clippy::cyclomatic_complexity)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity`
-error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map`
- --> $DIR/rename.rs:39:9
- |
-LL | #![warn(clippy::option_and_then_some)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map`
-
-error: lint `clippy::box_vec` has been renamed to `clippy::box_collection`
+error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods`
--> $DIR/rename.rs:40:9
|
-LL | #![warn(clippy::box_vec)]
- | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection`
+LL | #![warn(clippy::disallowed_method)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods`
-error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_if_conditions`
+error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types`
--> $DIR/rename.rs:41:9
|
-LL | #![warn(clippy::block_in_if_condition_expr)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions`
+LL | #![warn(clippy::disallowed_type)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types`
-error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_if_conditions`
+error: lint `clippy::for_loop_over_option` has been renamed to `clippy::for_loops_over_fallibles`
--> $DIR/rename.rs:42:9
|
-LL | #![warn(clippy::block_in_if_condition_stmt)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions`
+LL | #![warn(clippy::for_loop_over_option)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles`
-error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or`
+error: lint `clippy::for_loop_over_result` has been renamed to `clippy::for_loops_over_fallibles`
--> $DIR/rename.rs:43:9
|
-LL | #![warn(clippy::option_map_unwrap_or)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
+LL | #![warn(clippy::for_loop_over_result)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles`
-error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or`
+error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion`
--> $DIR/rename.rs:44:9
|
-LL | #![warn(clippy::option_map_unwrap_or_else)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
+LL | #![warn(clippy::identity_conversion)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion`
-error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or`
+error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok`
--> $DIR/rename.rs:45:9
|
-LL | #![warn(clippy::result_map_unwrap_or_else)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
+LL | #![warn(clippy::if_let_some_result)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok`
-error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used`
+error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default`
--> $DIR/rename.rs:46:9
|
-LL | #![warn(clippy::option_unwrap_used)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used`
+LL | #![warn(clippy::new_without_default_derive)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default`
-error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used`
+error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map`
--> $DIR/rename.rs:47:9
|
-LL | #![warn(clippy::result_unwrap_used)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used`
+LL | #![warn(clippy::option_and_then_some)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map`
error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used`
--> $DIR/rename.rs:48:9
LL | #![warn(clippy::option_expect_used)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used`
-error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used`
+error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or`
--> $DIR/rename.rs:49:9
|
-LL | #![warn(clippy::result_expect_used)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used`
+LL | #![warn(clippy::option_map_unwrap_or)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
-error: lint `clippy::for_loop_over_option` has been renamed to `clippy::for_loops_over_fallibles`
+error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or`
--> $DIR/rename.rs:50:9
|
-LL | #![warn(clippy::for_loop_over_option)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles`
+LL | #![warn(clippy::option_map_unwrap_or_else)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
-error: lint `clippy::for_loop_over_result` has been renamed to `clippy::for_loops_over_fallibles`
+error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used`
--> $DIR/rename.rs:51:9
|
-LL | #![warn(clippy::for_loop_over_result)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles`
+LL | #![warn(clippy::option_unwrap_used)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used`
-error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion`
+error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow`
--> $DIR/rename.rs:52:9
|
-LL | #![warn(clippy::identity_conversion)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion`
+LL | #![warn(clippy::ref_in_deref)]
+ | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow`
-error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters`
+error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used`
--> $DIR/rename.rs:53:9
|
-LL | #![warn(clippy::zero_width_space)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters`
+LL | #![warn(clippy::result_expect_used)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used`
-error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str`
+error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or`
--> $DIR/rename.rs:54:9
|
-LL | #![warn(clippy::single_char_push_str)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str`
+LL | #![warn(clippy::result_map_unwrap_or_else)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
-error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok`
+error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used`
--> $DIR/rename.rs:55:9
|
-LL | #![warn(clippy::if_let_some_result)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok`
+LL | #![warn(clippy::result_unwrap_used)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used`
-error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types`
+error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str`
--> $DIR/rename.rs:56:9
|
-LL | #![warn(clippy::disallowed_type)]
- | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types`
+LL | #![warn(clippy::single_char_push_str)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str`
-error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods`
+error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions`
--> $DIR/rename.rs:57:9
|
-LL | #![warn(clippy::disallowed_method)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods`
+LL | #![warn(clippy::stutter)]
+ | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions`
-error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow`
+error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl`
--> $DIR/rename.rs:58:9
|
-LL | #![warn(clippy::ref_in_deref)]
- | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow`
+LL | #![warn(clippy::to_string_in_display)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl`
-error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl`
+error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters`
--> $DIR/rename.rs:59:9
|
-LL | #![warn(clippy::to_string_in_display)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl`
+LL | #![warn(clippy::zero_width_space)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters`
-error: lint `clippy::invalid_ref` has been renamed to `invalid_value`
- --> $DIR/rename.rs:61:9
+error: lint `clippy::drop_bounds` has been renamed to `drop_bounds`
+ --> $DIR/rename.rs:60:9
|
-LL | #![warn(clippy::invalid_ref)]
- | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value`
+LL | #![warn(clippy::drop_bounds)]
+ | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds`
error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter`
- --> $DIR/rename.rs:62:9
+ --> $DIR/rename.rs:61:9
|
LL | #![warn(clippy::into_iter_on_array)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter`
-error: lint `clippy::unused_label` has been renamed to `unused_labels`
+error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering`
+ --> $DIR/rename.rs:62:9
+ |
+LL | #![warn(clippy::invalid_atomic_ordering)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering`
+
+error: lint `clippy::invalid_ref` has been renamed to `invalid_value`
--> $DIR/rename.rs:63:9
|
-LL | #![warn(clippy::unused_label)]
- | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels`
+LL | #![warn(clippy::invalid_ref)]
+ | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value`
-error: lint `clippy::drop_bounds` has been renamed to `drop_bounds`
+error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums`
--> $DIR/rename.rs:64:9
|
-LL | #![warn(clippy::drop_bounds)]
- | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds`
+LL | #![warn(clippy::mem_discriminant_non_enum)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums`
-error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr`
+error: lint `clippy::panic_params` has been renamed to `non_fmt_panics`
--> $DIR/rename.rs:65:9
|
-LL | #![warn(clippy::temporary_cstring_as_ptr)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr`
+LL | #![warn(clippy::panic_params)]
+ | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics`
-error: lint `clippy::panic_params` has been renamed to `non_fmt_panics`
+error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr`
--> $DIR/rename.rs:66:9
|
-LL | #![warn(clippy::panic_params)]
- | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics`
+LL | #![warn(clippy::temporary_cstring_as_ptr)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr`
error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints`
--> $DIR/rename.rs:67:9
LL | #![warn(clippy::unknown_clippy_lints)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints`
-error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering`
+error: lint `clippy::unused_label` has been renamed to `unused_labels`
--> $DIR/rename.rs:68:9
|
-LL | #![warn(clippy::invalid_atomic_ordering)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering`
-
-error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums`
- --> $DIR/rename.rs:69:9
- |
-LL | #![warn(clippy::mem_discriminant_non_enum)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums`
+LL | #![warn(clippy::unused_label)]
+ | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels`
error: aborting due to 34 previous errors
// No lint
foo!(a_struct);
+
+ #[non_exhaustive]
+ struct B {
+ a: u32,
+ b: u32,
+ c: u64,
+ }
+
+ let b_struct = B { a: 5, b: 42, c: 342 };
+
+ match b_struct {
+ B { a: 5, b: 42, .. } => {},
+ B { a: 0, b: 0, c: 128, .. } => {}, // No Lint
+ _ => {},
+ }
}
+#![feature(adt_const_params)]
+#![allow(incomplete_features)]
#![warn(clippy::same_functions_in_if_condition)]
#![allow(clippy::ifs_same_cond)] // This warning is different from `ifs_same_cond`.
#![allow(clippy::if_same_then_else, clippy::comparison_chain)] // all empty blocks
"linux"
};
println!("{}", os);
+
+ #[derive(PartialEq, Eq)]
+ enum E {
+ A,
+ B,
+ }
+ fn generic<const P: E>() -> bool {
+ match P {
+ E::A => true,
+ E::B => false,
+ }
+ }
+ if generic::<{ E::A }>() {
+ println!("A");
+ } else if generic::<{ E::B }>() {
+ println!("B");
+ }
}
error: this `if` has the same function call as a previous `if`
- --> $DIR/same_functions_in_if_condition.rs:29:15
+ --> $DIR/same_functions_in_if_condition.rs:31:15
|
LL | } else if function() {
| ^^^^^^^^^^
|
= note: `-D clippy::same-functions-in-if-condition` implied by `-D warnings`
note: same as this
- --> $DIR/same_functions_in_if_condition.rs:28:8
+ --> $DIR/same_functions_in_if_condition.rs:30:8
|
LL | if function() {
| ^^^^^^^^^^
error: this `if` has the same function call as a previous `if`
- --> $DIR/same_functions_in_if_condition.rs:34:15
+ --> $DIR/same_functions_in_if_condition.rs:36:15
|
LL | } else if fn_arg(a) {
| ^^^^^^^^^
|
note: same as this
- --> $DIR/same_functions_in_if_condition.rs:33:8
+ --> $DIR/same_functions_in_if_condition.rs:35:8
|
LL | if fn_arg(a) {
| ^^^^^^^^^
error: this `if` has the same function call as a previous `if`
- --> $DIR/same_functions_in_if_condition.rs:39:15
+ --> $DIR/same_functions_in_if_condition.rs:41:15
|
LL | } else if obj.method() {
| ^^^^^^^^^^^^
|
note: same as this
- --> $DIR/same_functions_in_if_condition.rs:38:8
+ --> $DIR/same_functions_in_if_condition.rs:40:8
|
LL | if obj.method() {
| ^^^^^^^^^^^^
error: this `if` has the same function call as a previous `if`
- --> $DIR/same_functions_in_if_condition.rs:44:15
+ --> $DIR/same_functions_in_if_condition.rs:46:15
|
LL | } else if obj.method_arg(a) {
| ^^^^^^^^^^^^^^^^^
|
note: same as this
- --> $DIR/same_functions_in_if_condition.rs:43:8
+ --> $DIR/same_functions_in_if_condition.rs:45:8
|
LL | if obj.method_arg(a) {
| ^^^^^^^^^^^^^^^^^
error: this `if` has the same function call as a previous `if`
- --> $DIR/same_functions_in_if_condition.rs:51:15
+ --> $DIR/same_functions_in_if_condition.rs:53:15
|
LL | } else if v.pop() == None {
| ^^^^^^^^^^^^^^^
|
note: same as this
- --> $DIR/same_functions_in_if_condition.rs:49:8
+ --> $DIR/same_functions_in_if_condition.rs:51:8
|
LL | if v.pop() == None {
| ^^^^^^^^^^^^^^^
error: this `if` has the same function call as a previous `if`
- --> $DIR/same_functions_in_if_condition.rs:56:15
+ --> $DIR/same_functions_in_if_condition.rs:58:15
|
LL | } else if v.len() == 42 {
| ^^^^^^^^^^^^^
|
note: same as this
- --> $DIR/same_functions_in_if_condition.rs:54:8
+ --> $DIR/same_functions_in_if_condition.rs:56:8
|
LL | if v.len() == 42 {
| ^^^^^^^^^^^^^
#![warn(clippy::shadow_same, clippy::shadow_reuse, clippy::shadow_unrelated)]
+#![allow(clippy::let_unit_value)]
fn shadow_same() {
let x = 1;
error: `x` is shadowed by itself in `x`
- --> $DIR/shadow.rs:5:9
+ --> $DIR/shadow.rs:6:9
|
LL | let x = x;
| ^
|
= note: `-D clippy::shadow-same` implied by `-D warnings`
note: previous binding is here
- --> $DIR/shadow.rs:4:9
+ --> $DIR/shadow.rs:5:9
|
LL | let x = 1;
| ^
error: `mut x` is shadowed by itself in `&x`
- --> $DIR/shadow.rs:6:13
+ --> $DIR/shadow.rs:7:13
|
LL | let mut x = &x;
| ^
|
note: previous binding is here
- --> $DIR/shadow.rs:5:9
+ --> $DIR/shadow.rs:6:9
|
LL | let x = x;
| ^
error: `x` is shadowed by itself in `&mut x`
- --> $DIR/shadow.rs:7:9
+ --> $DIR/shadow.rs:8:9
|
LL | let x = &mut x;
| ^
|
note: previous binding is here
- --> $DIR/shadow.rs:6:9
+ --> $DIR/shadow.rs:7:9
|
LL | let mut x = &x;
| ^^^^^
error: `x` is shadowed by itself in `*x`
- --> $DIR/shadow.rs:8:9
+ --> $DIR/shadow.rs:9:9
|
LL | let x = *x;
| ^
|
note: previous binding is here
- --> $DIR/shadow.rs:7:9
+ --> $DIR/shadow.rs:8:9
|
LL | let x = &mut x;
| ^
error: `x` is shadowed
- --> $DIR/shadow.rs:13:9
+ --> $DIR/shadow.rs:14:9
|
LL | let x = x.0;
| ^
|
= note: `-D clippy::shadow-reuse` implied by `-D warnings`
note: previous binding is here
- --> $DIR/shadow.rs:12:9
+ --> $DIR/shadow.rs:13:9
|
LL | let x = ([[0]], ());
| ^
error: `x` is shadowed
- --> $DIR/shadow.rs:14:9
+ --> $DIR/shadow.rs:15:9
|
LL | let x = x[0];
| ^
|
note: previous binding is here
- --> $DIR/shadow.rs:13:9
+ --> $DIR/shadow.rs:14:9
|
LL | let x = x.0;
| ^
error: `x` is shadowed
- --> $DIR/shadow.rs:15:10
+ --> $DIR/shadow.rs:16:10
|
LL | let [x] = x;
| ^
|
note: previous binding is here
- --> $DIR/shadow.rs:14:9
+ --> $DIR/shadow.rs:15:9
|
LL | let x = x[0];
| ^
error: `x` is shadowed
- --> $DIR/shadow.rs:16:9
+ --> $DIR/shadow.rs:17:9
|
LL | let x = Some(x);
| ^
|
note: previous binding is here
- --> $DIR/shadow.rs:15:10
+ --> $DIR/shadow.rs:16:10
|
LL | let [x] = x;
| ^
error: `x` is shadowed
- --> $DIR/shadow.rs:17:9
+ --> $DIR/shadow.rs:18:9
|
LL | let x = foo(x);
| ^
|
note: previous binding is here
- --> $DIR/shadow.rs:16:9
+ --> $DIR/shadow.rs:17:9
|
LL | let x = Some(x);
| ^
error: `x` is shadowed
- --> $DIR/shadow.rs:18:9
+ --> $DIR/shadow.rs:19:9
|
LL | let x = || x;
| ^
|
note: previous binding is here
- --> $DIR/shadow.rs:17:9
+ --> $DIR/shadow.rs:18:9
|
LL | let x = foo(x);
| ^
error: `x` is shadowed
- --> $DIR/shadow.rs:19:9
+ --> $DIR/shadow.rs:20:9
|
LL | let x = Some(1).map(|_| x)?;
| ^
|
note: previous binding is here
- --> $DIR/shadow.rs:18:9
+ --> $DIR/shadow.rs:19:9
|
LL | let x = || x;
| ^
error: `y` is shadowed
- --> $DIR/shadow.rs:21:9
+ --> $DIR/shadow.rs:22:9
|
LL | let y = match y {
| ^
|
note: previous binding is here
- --> $DIR/shadow.rs:20:9
+ --> $DIR/shadow.rs:21:9
|
LL | let y = 1;
| ^
error: `x` shadows a previous, unrelated binding
- --> $DIR/shadow.rs:30:9
+ --> $DIR/shadow.rs:31:9
|
LL | let x = 2;
| ^
|
= note: `-D clippy::shadow-unrelated` implied by `-D warnings`
note: previous binding is here
- --> $DIR/shadow.rs:29:9
+ --> $DIR/shadow.rs:30:9
|
LL | let x = 1;
| ^
error: `x` shadows a previous, unrelated binding
- --> $DIR/shadow.rs:35:13
+ --> $DIR/shadow.rs:36:13
|
LL | let x = 1;
| ^
|
note: previous binding is here
- --> $DIR/shadow.rs:34:10
+ --> $DIR/shadow.rs:35:10
|
LL | fn f(x: u32) {
| ^
error: `x` shadows a previous, unrelated binding
- --> $DIR/shadow.rs:40:14
+ --> $DIR/shadow.rs:41:14
|
LL | Some(x) => {
| ^
|
note: previous binding is here
- --> $DIR/shadow.rs:37:9
+ --> $DIR/shadow.rs:38:9
|
LL | let x = 1;
| ^
error: `x` shadows a previous, unrelated binding
- --> $DIR/shadow.rs:41:17
+ --> $DIR/shadow.rs:42:17
|
LL | let x = 1;
| ^
|
note: previous binding is here
- --> $DIR/shadow.rs:40:14
+ --> $DIR/shadow.rs:41:14
|
LL | Some(x) => {
| ^
error: `x` shadows a previous, unrelated binding
- --> $DIR/shadow.rs:45:17
+ --> $DIR/shadow.rs:46:17
|
LL | if let Some(x) = Some(1) {}
| ^
|
note: previous binding is here
- --> $DIR/shadow.rs:37:9
+ --> $DIR/shadow.rs:38:9
|
LL | let x = 1;
| ^
error: `x` shadows a previous, unrelated binding
- --> $DIR/shadow.rs:46:20
+ --> $DIR/shadow.rs:47:20
|
LL | while let Some(x) = Some(1) {}
| ^
|
note: previous binding is here
- --> $DIR/shadow.rs:37:9
+ --> $DIR/shadow.rs:38:9
|
LL | let x = 1;
| ^
error: `x` shadows a previous, unrelated binding
- --> $DIR/shadow.rs:47:15
+ --> $DIR/shadow.rs:48:15
|
LL | let _ = |[x]: [u32; 1]| {
| ^
|
note: previous binding is here
- --> $DIR/shadow.rs:37:9
+ --> $DIR/shadow.rs:38:9
|
LL | let x = 1;
| ^
error: `x` shadows a previous, unrelated binding
- --> $DIR/shadow.rs:48:13
+ --> $DIR/shadow.rs:49:13
|
LL | let x = 1;
| ^
|
note: previous binding is here
- --> $DIR/shadow.rs:47:15
+ --> $DIR/shadow.rs:48:15
|
LL | let _ = |[x]: [u32; 1]| {
| ^
error: `y` is shadowed
- --> $DIR/shadow.rs:51:17
+ --> $DIR/shadow.rs:52:17
|
LL | if let Some(y) = y {}
| ^
|
note: previous binding is here
- --> $DIR/shadow.rs:50:9
+ --> $DIR/shadow.rs:51:9
|
LL | let y = Some(1);
| ^
error: `_b` shadows a previous, unrelated binding
- --> $DIR/shadow.rs:87:9
+ --> $DIR/shadow.rs:88:9
|
LL | let _b = _a;
| ^^
|
note: previous binding is here
- --> $DIR/shadow.rs:86:28
+ --> $DIR/shadow.rs:87:28
|
LL | pub async fn foo2(_a: i32, _b: i64) {
| ^^
unused,
clippy::println_empty_string,
clippy::empty_loop,
- clippy::diverging_sub_expression
+ clippy::diverging_sub_expression,
+ clippy::let_unit_value
)]
struct Foo {
error: binding's name is too similar to existing binding
- --> $DIR/similar_names.rs:20:9
+ --> $DIR/similar_names.rs:21:9
|
LL | let bpple: i32;
| ^^^^^
|
= note: `-D clippy::similar-names` implied by `-D warnings`
note: existing binding defined here
- --> $DIR/similar_names.rs:18:9
+ --> $DIR/similar_names.rs:19:9
|
LL | let apple: i32;
| ^^^^^
error: binding's name is too similar to existing binding
- --> $DIR/similar_names.rs:22:9
+ --> $DIR/similar_names.rs:23:9
|
LL | let cpple: i32;
| ^^^^^
|
note: existing binding defined here
- --> $DIR/similar_names.rs:18:9
+ --> $DIR/similar_names.rs:19:9
|
LL | let apple: i32;
| ^^^^^
error: binding's name is too similar to existing binding
- --> $DIR/similar_names.rs:46:9
+ --> $DIR/similar_names.rs:47:9
|
LL | let bluby: i32;
| ^^^^^
|
note: existing binding defined here
- --> $DIR/similar_names.rs:45:9
+ --> $DIR/similar_names.rs:46:9
|
LL | let blubx: i32;
| ^^^^^
error: binding's name is too similar to existing binding
- --> $DIR/similar_names.rs:50:9
+ --> $DIR/similar_names.rs:51:9
|
LL | let coke: i32;
| ^^^^
|
note: existing binding defined here
- --> $DIR/similar_names.rs:48:9
+ --> $DIR/similar_names.rs:49:9
|
LL | let cake: i32;
| ^^^^
error: binding's name is too similar to existing binding
- --> $DIR/similar_names.rs:68:9
+ --> $DIR/similar_names.rs:69:9
|
LL | let xyzeabc: i32;
| ^^^^^^^
|
note: existing binding defined here
- --> $DIR/similar_names.rs:66:9
+ --> $DIR/similar_names.rs:67:9
|
LL | let xyz1abc: i32;
| ^^^^^^^
error: binding's name is too similar to existing binding
- --> $DIR/similar_names.rs:72:9
+ --> $DIR/similar_names.rs:73:9
|
LL | let parsee: i32;
| ^^^^^^
|
note: existing binding defined here
- --> $DIR/similar_names.rs:70:9
+ --> $DIR/similar_names.rs:71:9
|
LL | let parser: i32;
| ^^^^^^
error: binding's name is too similar to existing binding
- --> $DIR/similar_names.rs:93:16
+ --> $DIR/similar_names.rs:94:16
|
LL | bpple: sprang,
| ^^^^^^
|
note: existing binding defined here
- --> $DIR/similar_names.rs:92:16
+ --> $DIR/similar_names.rs:93:16
|
LL | apple: spring,
| ^^^^^^
#![warn(clippy::single_char_lifetime_names)]
+#![allow(clippy::let_unit_value)]
// Lifetimes should only be linted when they're introduced
struct DiagnosticCtx<'a, 'b>
error: single-character lifetime names are likely uninformative
- --> $DIR/single_char_lifetime_names.rs:4:22
+ --> $DIR/single_char_lifetime_names.rs:5:22
|
LL | struct DiagnosticCtx<'a, 'b>
| ^^
= help: use a more informative name
error: single-character lifetime names are likely uninformative
- --> $DIR/single_char_lifetime_names.rs:4:26
+ --> $DIR/single_char_lifetime_names.rs:5:26
|
LL | struct DiagnosticCtx<'a, 'b>
| ^^
= help: use a more informative name
error: single-character lifetime names are likely uninformative
- --> $DIR/single_char_lifetime_names.rs:13:6
+ --> $DIR/single_char_lifetime_names.rs:14:6
|
LL | impl<'a, 'b> DiagnosticCtx<'a, 'b> {
| ^^
= help: use a more informative name
error: single-character lifetime names are likely uninformative
- --> $DIR/single_char_lifetime_names.rs:13:10
+ --> $DIR/single_char_lifetime_names.rs:14:10
|
LL | impl<'a, 'b> DiagnosticCtx<'a, 'b> {
| ^^
= help: use a more informative name
error: single-character lifetime names are likely uninformative
- --> $DIR/single_char_lifetime_names.rs:33:15
+ --> $DIR/single_char_lifetime_names.rs:34:15
|
LL | fn split_once<'a>(base: &'a str, other: &'_ str) -> (&'a str, Option<&'a str>) {
| ^^
+// aux-build: proc_macro_with_span.rs
+
#![warn(clippy::single_match_else)]
#![allow(clippy::needless_return)]
#![allow(clippy::no_effect)]
+extern crate proc_macro_with_span;
+use proc_macro_with_span::with_span;
+
enum ExprNode {
ExprAddrOf,
Butterflies,
static NODE: ExprNode = ExprNode::Unicorns;
fn unwrap_addr() -> Option<&'static ExprNode> {
- match ExprNode::Butterflies {
+ let _ = match ExprNode::Butterflies {
ExprNode::ExprAddrOf => Some(&NODE),
_ => {
let x = 5;
None
},
- }
+ };
+
+ // Don't lint
+ with_span!(span match ExprNode::Butterflies {
+ ExprNode::ExprAddrOf => Some(&NODE),
+ _ => {
+ let x = 5;
+ None
+ },
+ })
}
macro_rules! unwrap_addr {
error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
- --> $DIR/single_match_else.rs:14:5
+ --> $DIR/single_match_else.rs:19:13
|
-LL | / match ExprNode::Butterflies {
+LL | let _ = match ExprNode::Butterflies {
+ | _____________^
LL | | ExprNode::ExprAddrOf => Some(&NODE),
LL | | _ => {
LL | | let x = 5;
LL | | None
LL | | },
-LL | | }
+LL | | };
| |_____^
|
= note: `-D clippy::single-match-else` implied by `-D warnings`
help: try this
|
-LL ~ if let ExprNode::ExprAddrOf = ExprNode::Butterflies { Some(&NODE) } else {
+LL ~ let _ = if let ExprNode::ExprAddrOf = ExprNode::Butterflies { Some(&NODE) } else {
LL + let x = 5;
LL + None
-LL + }
+LL ~ };
|
error: aborting due to previous error
| ^^^^^^^^^^ help: try: `vec.sort_unstable()`
|
= note: `-D clippy::stable-sort-primitive` implied by `-D warnings`
- = note: an unstable sort would perform faster without any observable difference for this data type
+ = note: an unstable sort typically performs faster without any observable difference for this data type
error: used `sort` on primitive type `bool`
--> $DIR/stable_sort_primitive.rs:9:5
LL | vec.sort();
| ^^^^^^^^^^ help: try: `vec.sort_unstable()`
|
- = note: an unstable sort would perform faster without any observable difference for this data type
+ = note: an unstable sort typically performs faster without any observable difference for this data type
error: used `sort` on primitive type `char`
--> $DIR/stable_sort_primitive.rs:11:5
LL | vec.sort();
| ^^^^^^^^^^ help: try: `vec.sort_unstable()`
|
- = note: an unstable sort would perform faster without any observable difference for this data type
+ = note: an unstable sort typically performs faster without any observable difference for this data type
error: used `sort` on primitive type `str`
--> $DIR/stable_sort_primitive.rs:13:5
LL | vec.sort();
| ^^^^^^^^^^ help: try: `vec.sort_unstable()`
|
- = note: an unstable sort would perform faster without any observable difference for this data type
+ = note: an unstable sort typically performs faster without any observable difference for this data type
error: used `sort` on primitive type `tuple`
--> $DIR/stable_sort_primitive.rs:15:5
LL | vec.sort();
| ^^^^^^^^^^ help: try: `vec.sort_unstable()`
|
- = note: an unstable sort would perform faster without any observable difference for this data type
+ = note: an unstable sort typically performs faster without any observable difference for this data type
error: used `sort` on primitive type `array`
--> $DIR/stable_sort_primitive.rs:17:5
LL | vec.sort();
| ^^^^^^^^^^ help: try: `vec.sort_unstable()`
|
- = note: an unstable sort would perform faster without any observable difference for this data type
+ = note: an unstable sort typically performs faster without any observable difference for this data type
error: used `sort` on primitive type `i32`
--> $DIR/stable_sort_primitive.rs:19:5
LL | arr.sort();
| ^^^^^^^^^^ help: try: `arr.sort_unstable()`
|
- = note: an unstable sort would perform faster without any observable difference for this data type
+ = note: an unstable sort typically performs faster without any observable difference for this data type
error: aborting due to 7 previous errors
// aux-build:proc_macro_suspicious_else_formatting.rs
#![warn(clippy::suspicious_else_formatting)]
-#![allow(clippy::if_same_then_else)]
+#![allow(clippy::if_same_then_else, clippy::let_unit_value)]
extern crate proc_macro_suspicious_else_formatting;
use proc_macro_suspicious_else_formatting::DeriveBadSpan;
let c = 'x';
let d = &c;
- let _ = d.is_digit(10);
- let _ = char::is_digit(c, 10);
+ let _ = d.is_digit(8);
+ let _ = char::is_digit(c, 8);
}
let c = 'x';
let d = &c;
- let _ = d.to_digit(10).is_some();
- let _ = char::to_digit(c, 10).is_some();
+ let _ = d.to_digit(8).is_some();
+ let _ = char::to_digit(c, 8).is_some();
}
error: use of `.to_digit(..).is_some()`
--> $DIR/to_digit_is_some.rs:9:13
|
-LL | let _ = d.to_digit(10).is_some();
- | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `d.is_digit(10)`
+LL | let _ = d.to_digit(8).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `d.is_digit(8)`
|
= note: `-D clippy::to-digit-is-some` implied by `-D warnings`
error: use of `.to_digit(..).is_some()`
--> $DIR/to_digit_is_some.rs:10:13
|
-LL | let _ = char::to_digit(c, 10).is_some();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `char::is_digit(c, 10)`
+LL | let _ = char::to_digit(c, 8).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `char::is_digit(c, 8)`
error: aborting due to 2 previous errors
}
}
+// This should not lint
+fn impl_trait(_: impl AsRef<str>, _: impl AsRef<str>) {}
+
fn main() {}
|
= help: consider removing this trait bound
-error: aborting due to 8 previous errors
+error: this trait bound is already specified in the where clause
+ --> $DIR/trait_duplication_in_bounds.rs:99:23
+ |
+LL | fn impl_trait(_: impl AsRef<str>, _: impl AsRef<str>) {}
+ | ^^^^^^^^^^
+ |
+ = help: consider removing this trait bound
+
+error: aborting due to 9 previous errors
#![warn(clippy::unsound_collection_transmute)]
use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, VecDeque};
-use std::mem::transmute;
+use std::mem::{transmute, MaybeUninit};
fn main() {
unsafe {
// wrong layout
let _ = transmute::<_, HashMap<u8, u32>>(HashMap::<u8, [u8; 4]>::new());
let _ = transmute::<_, HashMap<u32, u32>>(HashMap::<[u8; 4], u32>::new());
+
+ let _ = transmute::<_, Vec<u8>>(Vec::<MaybeUninit<u8>>::new());
+ let _ = transmute::<_, Vec<*mut u32>>(Vec::<Box<u32>>::new());
}
}
--- /dev/null
+// run-rustfix
+#![warn(clippy::trim_split_whitespace)]
+#![allow(clippy::let_unit_value)]
+
+struct Custom;
+impl Custom {
+ fn trim(self) -> Self {
+ self
+ }
+ fn split_whitespace(self) {}
+}
+
+struct DerefStr(&'static str);
+impl std::ops::Deref for DerefStr {
+ type Target = str;
+ fn deref(&self) -> &Self::Target {
+ self.0
+ }
+}
+
+struct DerefStrAndCustom(&'static str);
+impl std::ops::Deref for DerefStrAndCustom {
+ type Target = str;
+ fn deref(&self) -> &Self::Target {
+ self.0
+ }
+}
+impl DerefStrAndCustom {
+ fn trim(self) -> Self {
+ self
+ }
+ fn split_whitespace(self) {}
+}
+
+struct DerefStrAndCustomSplit(&'static str);
+impl std::ops::Deref for DerefStrAndCustomSplit {
+ type Target = str;
+ fn deref(&self) -> &Self::Target {
+ self.0
+ }
+}
+impl DerefStrAndCustomSplit {
+ #[allow(dead_code)]
+ fn split_whitespace(self) {}
+}
+
+struct DerefStrAndCustomTrim(&'static str);
+impl std::ops::Deref for DerefStrAndCustomTrim {
+ type Target = str;
+ fn deref(&self) -> &Self::Target {
+ self.0
+ }
+}
+impl DerefStrAndCustomTrim {
+ fn trim(self) -> Self {
+ self
+ }
+}
+
+fn main() {
+ // &str
+ let _ = " A B C ".split_whitespace(); // should trigger lint
+ let _ = " A B C ".split_whitespace(); // should trigger lint
+ let _ = " A B C ".split_whitespace(); // should trigger lint
+
+ // String
+ let _ = (" A B C ").to_string().split_whitespace(); // should trigger lint
+ let _ = (" A B C ").to_string().split_whitespace(); // should trigger lint
+ let _ = (" A B C ").to_string().split_whitespace(); // should trigger lint
+
+ // Custom
+ let _ = Custom.trim().split_whitespace(); // should not trigger lint
+
+ // Deref<Target=str>
+ let s = DerefStr(" A B C ");
+ let _ = s.split_whitespace(); // should trigger lint
+
+ // Deref<Target=str> + custom impl
+ let s = DerefStrAndCustom(" A B C ");
+ let _ = s.trim().split_whitespace(); // should not trigger lint
+
+ // Deref<Target=str> + only custom split_ws() impl
+ let s = DerefStrAndCustomSplit(" A B C ");
+ let _ = s.split_whitespace(); // should trigger lint
+ // Expl: trim() is called on str (deref) and returns &str.
+ // Thus split_ws() is called on str as well and the custom impl on S is unused
+
+ // Deref<Target=str> + only custom trim() impl
+ let s = DerefStrAndCustomTrim(" A B C ");
+ let _ = s.trim().split_whitespace(); // should not trigger lint
+}
--- /dev/null
+// run-rustfix
+#![warn(clippy::trim_split_whitespace)]
+#![allow(clippy::let_unit_value)]
+
+struct Custom;
+impl Custom {
+ fn trim(self) -> Self {
+ self
+ }
+ fn split_whitespace(self) {}
+}
+
+struct DerefStr(&'static str);
+impl std::ops::Deref for DerefStr {
+ type Target = str;
+ fn deref(&self) -> &Self::Target {
+ self.0
+ }
+}
+
+struct DerefStrAndCustom(&'static str);
+impl std::ops::Deref for DerefStrAndCustom {
+ type Target = str;
+ fn deref(&self) -> &Self::Target {
+ self.0
+ }
+}
+impl DerefStrAndCustom {
+ fn trim(self) -> Self {
+ self
+ }
+ fn split_whitespace(self) {}
+}
+
+struct DerefStrAndCustomSplit(&'static str);
+impl std::ops::Deref for DerefStrAndCustomSplit {
+ type Target = str;
+ fn deref(&self) -> &Self::Target {
+ self.0
+ }
+}
+impl DerefStrAndCustomSplit {
+ #[allow(dead_code)]
+ fn split_whitespace(self) {}
+}
+
+struct DerefStrAndCustomTrim(&'static str);
+impl std::ops::Deref for DerefStrAndCustomTrim {
+ type Target = str;
+ fn deref(&self) -> &Self::Target {
+ self.0
+ }
+}
+impl DerefStrAndCustomTrim {
+ fn trim(self) -> Self {
+ self
+ }
+}
+
+fn main() {
+ // &str
+ let _ = " A B C ".trim().split_whitespace(); // should trigger lint
+ let _ = " A B C ".trim_start().split_whitespace(); // should trigger lint
+ let _ = " A B C ".trim_end().split_whitespace(); // should trigger lint
+
+ // String
+ let _ = (" A B C ").to_string().trim().split_whitespace(); // should trigger lint
+ let _ = (" A B C ").to_string().trim_start().split_whitespace(); // should trigger lint
+ let _ = (" A B C ").to_string().trim_end().split_whitespace(); // should trigger lint
+
+ // Custom
+ let _ = Custom.trim().split_whitespace(); // should not trigger lint
+
+ // Deref<Target=str>
+ let s = DerefStr(" A B C ");
+ let _ = s.trim().split_whitespace(); // should trigger lint
+
+ // Deref<Target=str> + custom impl
+ let s = DerefStrAndCustom(" A B C ");
+ let _ = s.trim().split_whitespace(); // should not trigger lint
+
+ // Deref<Target=str> + only custom split_ws() impl
+ let s = DerefStrAndCustomSplit(" A B C ");
+ let _ = s.trim().split_whitespace(); // should trigger lint
+ // Expl: trim() is called on str (deref) and returns &str.
+ // Thus split_ws() is called on str as well and the custom impl on S is unused
+
+ // Deref<Target=str> + only custom trim() impl
+ let s = DerefStrAndCustomTrim(" A B C ");
+ let _ = s.trim().split_whitespace(); // should not trigger lint
+}
--- /dev/null
+error: found call to `str::trim` before `str::split_whitespace`
+ --> $DIR/trim_split_whitespace.rs:62:23
+ |
+LL | let _ = " A B C ".trim().split_whitespace(); // should trigger lint
+ | ^^^^^^^ help: remove `trim()`
+ |
+ = note: `-D clippy::trim-split-whitespace` implied by `-D warnings`
+
+error: found call to `str::trim_start` before `str::split_whitespace`
+ --> $DIR/trim_split_whitespace.rs:63:23
+ |
+LL | let _ = " A B C ".trim_start().split_whitespace(); // should trigger lint
+ | ^^^^^^^^^^^^^ help: remove `trim_start()`
+
+error: found call to `str::trim_end` before `str::split_whitespace`
+ --> $DIR/trim_split_whitespace.rs:64:23
+ |
+LL | let _ = " A B C ".trim_end().split_whitespace(); // should trigger lint
+ | ^^^^^^^^^^^ help: remove `trim_end()`
+
+error: found call to `str::trim` before `str::split_whitespace`
+ --> $DIR/trim_split_whitespace.rs:67:37
+ |
+LL | let _ = (" A B C ").to_string().trim().split_whitespace(); // should trigger lint
+ | ^^^^^^^ help: remove `trim()`
+
+error: found call to `str::trim_start` before `str::split_whitespace`
+ --> $DIR/trim_split_whitespace.rs:68:37
+ |
+LL | let _ = (" A B C ").to_string().trim_start().split_whitespace(); // should trigger lint
+ | ^^^^^^^^^^^^^ help: remove `trim_start()`
+
+error: found call to `str::trim_end` before `str::split_whitespace`
+ --> $DIR/trim_split_whitespace.rs:69:37
+ |
+LL | let _ = (" A B C ").to_string().trim_end().split_whitespace(); // should trigger lint
+ | ^^^^^^^^^^^ help: remove `trim_end()`
+
+error: found call to `str::trim` before `str::split_whitespace`
+ --> $DIR/trim_split_whitespace.rs:76:15
+ |
+LL | let _ = s.trim().split_whitespace(); // should trigger lint
+ | ^^^^^^^ help: remove `trim()`
+
+error: found call to `str::trim` before `str::split_whitespace`
+ --> $DIR/trim_split_whitespace.rs:84:15
+ |
+LL | let _ = s.trim().split_whitespace(); // should trigger lint
+ | ^^^^^^^ help: remove `trim()`
+
+error: aborting due to 8 previous errors
+
u: U,
}
+// This should not lint
+fn impl_trait(_: impl AsRef<str>, _: impl AsRef<str>) {}
+
fn main() {}
|
= help: consider combining the bounds: `Self: Clone + Copy + Default + Ord`
-error: aborting due to 2 previous errors
+error: this type has already been used as a bound predicate
+ --> $DIR/type_repetition_in_bounds.rs:83:43
+ |
+LL | fn impl_trait(_: impl AsRef<str>, _: impl AsRef<str>) {}
+ | ^^^^^^^^^^
+ |
+ = help: consider combining the bounds: `impl AsRef<str>: AsRef<str> + AsRef<str>`
+
+error: aborting due to 3 previous errors
// aux-build:proc_macro_unsafe.rs
#![warn(clippy::undocumented_unsafe_blocks)]
+#![allow(clippy::let_unit_value)]
extern crate proc_macro_unsafe;
error: unsafe block missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:256:19
+ --> $DIR/undocumented_unsafe_blocks.rs:257:19
|
LL | /* Safety: */ unsafe {}
| ^^^^^^^^^
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:260:5
+ --> $DIR/undocumented_unsafe_blocks.rs:261:5
|
LL | unsafe {}
| ^^^^^^^^^
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:264:14
+ --> $DIR/undocumented_unsafe_blocks.rs:265:14
|
LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
| ^^^^^^^^^^^^^
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:264:29
+ --> $DIR/undocumented_unsafe_blocks.rs:265:29
|
LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
| ^^^^^^^^^^^^^
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:264:48
+ --> $DIR/undocumented_unsafe_blocks.rs:265:48
|
LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
| ^^^^^^^^^^^^^
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:268:18
+ --> $DIR/undocumented_unsafe_blocks.rs:269:18
|
LL | let _ = (42, unsafe {}, "test", unsafe {});
| ^^^^^^^^^
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:268:37
+ --> $DIR/undocumented_unsafe_blocks.rs:269:37
|
LL | let _ = (42, unsafe {}, "test", unsafe {});
| ^^^^^^^^^
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:272:14
+ --> $DIR/undocumented_unsafe_blocks.rs:273:14
|
LL | let _ = *unsafe { &42 };
| ^^^^^^^^^^^^^^
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:277:19
+ --> $DIR/undocumented_unsafe_blocks.rs:278:19
|
LL | let _ = match unsafe {} {
| ^^^^^^^^^
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:283:14
+ --> $DIR/undocumented_unsafe_blocks.rs:284:14
|
LL | let _ = &unsafe {};
| ^^^^^^^^^
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:287:14
+ --> $DIR/undocumented_unsafe_blocks.rs:288:14
|
LL | let _ = [unsafe {}; 5];
| ^^^^^^^^^
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:291:13
+ --> $DIR/undocumented_unsafe_blocks.rs:292:13
|
LL | let _ = unsafe {};
| ^^^^^^^^^
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:301:8
+ --> $DIR/undocumented_unsafe_blocks.rs:302:8
|
LL | t!(unsafe {});
| ^^^^^^^^^
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:307:13
+ --> $DIR/undocumented_unsafe_blocks.rs:308:13
|
LL | unsafe {}
| ^^^^^^^^^
= note: this error originates in the macro `t` (in Nightly builds, run with -Z macro-backtrace for more info)
error: unsafe block missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:315:5
+ --> $DIR/undocumented_unsafe_blocks.rs:316:5
|
LL | unsafe {} // SAFETY:
| ^^^^^^^^^
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:319:5
+ --> $DIR/undocumented_unsafe_blocks.rs:320:5
|
LL | unsafe {
| ^^^^^^^^
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:329:5
+ --> $DIR/undocumented_unsafe_blocks.rs:330:5
|
LL | unsafe {};
| ^^^^^^^^^
= help: consider adding a safety comment on the preceding line
error: unsafe block missing a safety comment
- --> $DIR/undocumented_unsafe_blocks.rs:333:20
+ --> $DIR/undocumented_unsafe_blocks.rs:334:20
|
LL | println!("{}", unsafe { String::from_utf8_unchecked(vec![]) });
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#![feature(stmt_expr_attributes)]
+#![allow(clippy::let_unit_value)]
use std::mem::{self, MaybeUninit};
error: this call for this type may be undefined behavior
- --> $DIR/uninit.rs:6:29
+ --> $DIR/uninit.rs:7:29
|
LL | let _: usize = unsafe { MaybeUninit::uninit().assume_init() };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: `#[deny(clippy::uninit_assumed_init)]` on by default
error: this call for this type may be undefined behavior
- --> $DIR/uninit.rs:9:31
+ --> $DIR/uninit.rs:10:31
|
LL | let _: [u8; 0] = unsafe { MaybeUninit::uninit().assume_init() };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: this call for this type may be undefined behavior
- --> $DIR/uninit.rs:24:29
+ --> $DIR/uninit.rs:25:29
|
LL | let _: usize = unsafe { mem::MaybeUninit::uninit().assume_init() };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
clippy::unnecessary_wraps,
clippy::or_fun_call,
clippy::needless_question_mark,
- clippy::self_named_constructors
+ clippy::self_named_constructors,
+ clippy::let_unit_value
)]
use std::fmt::Debug;
error: passing a unit value to a function
- --> $DIR/unit_arg.rs:56:5
+ --> $DIR/unit_arg.rs:57:5
|
LL | / foo({
LL | | 1;
|
error: passing a unit value to a function
- --> $DIR/unit_arg.rs:59:5
+ --> $DIR/unit_arg.rs:60:5
|
LL | foo(foo(1));
| ^^^^^^^^^^^
|
error: passing a unit value to a function
- --> $DIR/unit_arg.rs:60:5
+ --> $DIR/unit_arg.rs:61:5
|
LL | / foo({
LL | | foo(1);
|
error: passing a unit value to a function
- --> $DIR/unit_arg.rs:65:5
+ --> $DIR/unit_arg.rs:66:5
|
LL | / b.bar({
LL | | 1;
|
error: passing unit values to a function
- --> $DIR/unit_arg.rs:68:5
+ --> $DIR/unit_arg.rs:69:5
|
LL | taking_multiple_units(foo(0), foo(1));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: passing unit values to a function
- --> $DIR/unit_arg.rs:69:5
+ --> $DIR/unit_arg.rs:70:5
|
LL | / taking_multiple_units(foo(0), {
LL | | foo(1);
|
error: passing unit values to a function
- --> $DIR/unit_arg.rs:73:5
+ --> $DIR/unit_arg.rs:74:5
|
LL | / taking_multiple_units(
LL | | {
...
error: passing a unit value to a function
- --> $DIR/unit_arg.rs:84:13
+ --> $DIR/unit_arg.rs:85:13
|
LL | None.or(Some(foo(2)));
| ^^^^^^^^^^^^
|
error: passing a unit value to a function
- --> $DIR/unit_arg.rs:87:5
+ --> $DIR/unit_arg.rs:88:5
|
LL | foo(foo(()));
| ^^^^^^^^^^^^
|
error: passing a unit value to a function
- --> $DIR/unit_arg.rs:124:5
+ --> $DIR/unit_arg.rs:125:5
|
LL | Some(foo(1))
| ^^^^^^^^^^^^
#![warn(clippy::unit_hash)]
+#![allow(clippy::let_unit_value)]
use std::collections::hash_map::DefaultHasher;
use std::hash::Hash;
error: this call to `hash` on the unit type will do nothing
- --> $DIR/unit_hash.rs:18:23
+ --> $DIR/unit_hash.rs:19:23
|
LL | Foo::Empty => ().hash(&mut state),
| ^^^^^^^^^^^^^^^^^^^ help: remove the call to `hash` or consider using: `0_u8.hash(&mut state)`
= note: the implementation of `Hash` for `()` is a no-op
error: this call to `hash` on the unit type will do nothing
- --> $DIR/unit_hash.rs:23:5
+ --> $DIR/unit_hash.rs:24:5
|
LL | res.hash(&mut state);
| ^^^^^^^^^^^^^^^^^^^^ help: remove the call to `hash` or consider using: `0_u8.hash(&mut state)`
= note: the implementation of `Hash` for `()` is a no-op
error: this call to `hash` on the unit type will do nothing
- --> $DIR/unit_hash.rs:26:5
+ --> $DIR/unit_hash.rs:27:5
|
LL | do_nothing().hash(&mut state);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `hash` or consider using: `0_u8.hash(&mut state)`
--- /dev/null
+// run-rustfix
+
+#![warn(clippy::unnecessary_owned_empty_strings)]
+
+fn ref_str_argument(_value: &str) {}
+
+#[allow(clippy::ptr_arg)]
+fn ref_string_argument(_value: &String) {}
+
+fn main() {
+ // should be linted
+ ref_str_argument("");
+
+ // should be linted
+ ref_str_argument("");
+
+ // should not be linted
+ ref_str_argument("");
+
+ // should not be linted
+ ref_string_argument(&String::new());
+}
--- /dev/null
+// run-rustfix
+
+#![warn(clippy::unnecessary_owned_empty_strings)]
+
+fn ref_str_argument(_value: &str) {}
+
+#[allow(clippy::ptr_arg)]
+fn ref_string_argument(_value: &String) {}
+
+fn main() {
+ // should be linted
+ ref_str_argument(&String::new());
+
+ // should be linted
+ ref_str_argument(&String::from(""));
+
+ // should not be linted
+ ref_str_argument("");
+
+ // should not be linted
+ ref_string_argument(&String::new());
+}
--- /dev/null
+error: usage of `&String::new()` for a function expecting a `&str` argument
+ --> $DIR/unnecessary_owned_empty_strings.rs:12:22
+ |
+LL | ref_str_argument(&String::new());
+ | ^^^^^^^^^^^^^^ help: try: `""`
+ |
+ = note: `-D clippy::unnecessary-owned-empty-strings` implied by `-D warnings`
+
+error: usage of `&String::from("")` for a function expecting a `&str` argument
+ --> $DIR/unnecessary_owned_empty_strings.rs:15:22
+ |
+LL | ref_str_argument(&String::from(""));
+ | ^^^^^^^^^^^^^^^^^ help: try: `""`
+
+error: aborting due to 2 previous errors
+
#![allow(clippy::ptr_arg)]
#![warn(clippy::unnecessary_to_owned)]
+#![feature(custom_inner_attributes)]
use std::borrow::Cow;
use std::ffi::{CStr, CString, OsStr, OsString};
fn require_string(_: &String) {}
+fn _msrv_1_35() {
+ #![clippy::msrv = "1.35"]
+ // `copied` was stabilized in 1.36, so clippy should use `cloned`.
+ let _ = &["x"][..].iter().cloned();
+}
+
+fn _msrv_1_36() {
+ #![clippy::msrv = "1.36"]
+ let _ = &["x"][..].iter().copied();
+}
+
// https://github.com/rust-lang/rust-clippy/issues/8507
mod issue_8507 {
#![allow(dead_code)]
#![allow(clippy::ptr_arg)]
#![warn(clippy::unnecessary_to_owned)]
+#![feature(custom_inner_attributes)]
use std::borrow::Cow;
use std::ffi::{CStr, CString, OsStr, OsString};
fn require_string(_: &String) {}
+fn _msrv_1_35() {
+ #![clippy::msrv = "1.35"]
+ // `copied` was stabilized in 1.36, so clippy should use `cloned`.
+ let _ = &["x"][..].to_vec().into_iter();
+}
+
+fn _msrv_1_36() {
+ #![clippy::msrv = "1.36"]
+ let _ = &["x"][..].to_vec().into_iter();
+}
+
// https://github.com/rust-lang/rust-clippy/issues/8507
mod issue_8507 {
#![allow(dead_code)]
error: redundant clone
- --> $DIR/unnecessary_to_owned.rs:150:64
+ --> $DIR/unnecessary_to_owned.rs:151:64
|
LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned());
| ^^^^^^^^^^^ help: remove this
|
= note: `-D clippy::redundant-clone` implied by `-D warnings`
note: this value is dropped without further use
- --> $DIR/unnecessary_to_owned.rs:150:20
+ --> $DIR/unnecessary_to_owned.rs:151:20
|
LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: redundant clone
- --> $DIR/unnecessary_to_owned.rs:151:40
+ --> $DIR/unnecessary_to_owned.rs:152:40
|
LL | require_os_str(&OsString::from("x").to_os_string());
| ^^^^^^^^^^^^^^^ help: remove this
|
note: this value is dropped without further use
- --> $DIR/unnecessary_to_owned.rs:151:21
+ --> $DIR/unnecessary_to_owned.rs:152:21
|
LL | require_os_str(&OsString::from("x").to_os_string());
| ^^^^^^^^^^^^^^^^^^^
error: redundant clone
- --> $DIR/unnecessary_to_owned.rs:152:48
+ --> $DIR/unnecessary_to_owned.rs:153:48
|
LL | require_path(&std::path::PathBuf::from("x").to_path_buf());
| ^^^^^^^^^^^^^^ help: remove this
|
note: this value is dropped without further use
- --> $DIR/unnecessary_to_owned.rs:152:19
+ --> $DIR/unnecessary_to_owned.rs:153:19
|
LL | require_path(&std::path::PathBuf::from("x").to_path_buf());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: redundant clone
- --> $DIR/unnecessary_to_owned.rs:153:35
+ --> $DIR/unnecessary_to_owned.rs:154:35
|
LL | require_str(&String::from("x").to_string());
| ^^^^^^^^^^^^ help: remove this
|
note: this value is dropped without further use
- --> $DIR/unnecessary_to_owned.rs:153:18
+ --> $DIR/unnecessary_to_owned.rs:154:18
|
LL | require_str(&String::from("x").to_string());
| ^^^^^^^^^^^^^^^^^
error: unnecessary use of `into_owned`
- --> $DIR/unnecessary_to_owned.rs:59:36
+ --> $DIR/unnecessary_to_owned.rs:60:36
|
LL | require_c_str(&Cow::from(c_str).into_owned());
| ^^^^^^^^^^^^^ help: remove this
= note: `-D clippy::unnecessary-to-owned` implied by `-D warnings`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:60:19
+ --> $DIR/unnecessary_to_owned.rs:61:19
|
LL | require_c_str(&c_str.to_owned());
| ^^^^^^^^^^^^^^^^^ help: use: `c_str`
error: unnecessary use of `to_os_string`
- --> $DIR/unnecessary_to_owned.rs:62:20
+ --> $DIR/unnecessary_to_owned.rs:63:20
|
LL | require_os_str(&os_str.to_os_string());
| ^^^^^^^^^^^^^^^^^^^^^^ help: use: `os_str`
error: unnecessary use of `into_owned`
- --> $DIR/unnecessary_to_owned.rs:63:38
+ --> $DIR/unnecessary_to_owned.rs:64:38
|
LL | require_os_str(&Cow::from(os_str).into_owned());
| ^^^^^^^^^^^^^ help: remove this
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:64:20
+ --> $DIR/unnecessary_to_owned.rs:65:20
|
LL | require_os_str(&os_str.to_owned());
| ^^^^^^^^^^^^^^^^^^ help: use: `os_str`
error: unnecessary use of `to_path_buf`
- --> $DIR/unnecessary_to_owned.rs:66:18
+ --> $DIR/unnecessary_to_owned.rs:67:18
|
LL | require_path(&path.to_path_buf());
| ^^^^^^^^^^^^^^^^^^^ help: use: `path`
error: unnecessary use of `into_owned`
- --> $DIR/unnecessary_to_owned.rs:67:34
+ --> $DIR/unnecessary_to_owned.rs:68:34
|
LL | require_path(&Cow::from(path).into_owned());
| ^^^^^^^^^^^^^ help: remove this
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:68:18
+ --> $DIR/unnecessary_to_owned.rs:69:18
|
LL | require_path(&path.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `path`
error: unnecessary use of `to_string`
- --> $DIR/unnecessary_to_owned.rs:70:17
+ --> $DIR/unnecessary_to_owned.rs:71:17
|
LL | require_str(&s.to_string());
| ^^^^^^^^^^^^^^ help: use: `s`
error: unnecessary use of `into_owned`
- --> $DIR/unnecessary_to_owned.rs:71:30
+ --> $DIR/unnecessary_to_owned.rs:72:30
|
LL | require_str(&Cow::from(s).into_owned());
| ^^^^^^^^^^^^^ help: remove this
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:72:17
+ --> $DIR/unnecessary_to_owned.rs:73:17
|
LL | require_str(&s.to_owned());
| ^^^^^^^^^^^^^ help: use: `s`
error: unnecessary use of `to_string`
- --> $DIR/unnecessary_to_owned.rs:73:17
+ --> $DIR/unnecessary_to_owned.rs:74:17
|
LL | require_str(&x_ref.to_string());
| ^^^^^^^^^^^^^^^^^^ help: use: `x_ref.as_ref()`
error: unnecessary use of `to_vec`
- --> $DIR/unnecessary_to_owned.rs:75:19
+ --> $DIR/unnecessary_to_owned.rs:76:19
|
LL | require_slice(&slice.to_vec());
| ^^^^^^^^^^^^^^^ help: use: `slice`
error: unnecessary use of `into_owned`
- --> $DIR/unnecessary_to_owned.rs:76:36
+ --> $DIR/unnecessary_to_owned.rs:77:36
|
LL | require_slice(&Cow::from(slice).into_owned());
| ^^^^^^^^^^^^^ help: remove this
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:77:19
+ --> $DIR/unnecessary_to_owned.rs:78:19
|
LL | require_slice(&array.to_owned());
| ^^^^^^^^^^^^^^^^^ help: use: `array.as_ref()`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:78:19
+ --> $DIR/unnecessary_to_owned.rs:79:19
|
LL | require_slice(&array_ref.to_owned());
| ^^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref.as_ref()`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:79:19
+ --> $DIR/unnecessary_to_owned.rs:80:19
|
LL | require_slice(&slice.to_owned());
| ^^^^^^^^^^^^^^^^^ help: use: `slice`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:80:19
+ --> $DIR/unnecessary_to_owned.rs:81:19
|
LL | require_slice(&x_ref.to_owned());
| ^^^^^^^^^^^^^^^^^ help: use: `x_ref`
error: unnecessary use of `into_owned`
- --> $DIR/unnecessary_to_owned.rs:82:42
+ --> $DIR/unnecessary_to_owned.rs:83:42
|
LL | require_x(&Cow::<X>::Owned(x.clone()).into_owned());
| ^^^^^^^^^^^^^ help: remove this
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:83:15
+ --> $DIR/unnecessary_to_owned.rs:84:15
|
LL | require_x(&x_ref.to_owned());
| ^^^^^^^^^^^^^^^^^ help: use: `x_ref`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:85:25
+ --> $DIR/unnecessary_to_owned.rs:86:25
|
LL | require_deref_c_str(c_str.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `c_str`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:86:26
+ --> $DIR/unnecessary_to_owned.rs:87:26
|
LL | require_deref_os_str(os_str.to_owned());
| ^^^^^^^^^^^^^^^^^ help: use: `os_str`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:87:24
+ --> $DIR/unnecessary_to_owned.rs:88:24
|
LL | require_deref_path(path.to_owned());
| ^^^^^^^^^^^^^^^ help: use: `path`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:88:23
+ --> $DIR/unnecessary_to_owned.rs:89:23
|
LL | require_deref_str(s.to_owned());
| ^^^^^^^^^^^^ help: use: `s`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:89:25
+ --> $DIR/unnecessary_to_owned.rs:90:25
|
LL | require_deref_slice(slice.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `slice`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:91:30
+ --> $DIR/unnecessary_to_owned.rs:92:30
|
LL | require_impl_deref_c_str(c_str.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `c_str`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:92:31
+ --> $DIR/unnecessary_to_owned.rs:93:31
|
LL | require_impl_deref_os_str(os_str.to_owned());
| ^^^^^^^^^^^^^^^^^ help: use: `os_str`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:93:29
+ --> $DIR/unnecessary_to_owned.rs:94:29
|
LL | require_impl_deref_path(path.to_owned());
| ^^^^^^^^^^^^^^^ help: use: `path`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:94:28
+ --> $DIR/unnecessary_to_owned.rs:95:28
|
LL | require_impl_deref_str(s.to_owned());
| ^^^^^^^^^^^^ help: use: `s`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:95:30
+ --> $DIR/unnecessary_to_owned.rs:96:30
|
LL | require_impl_deref_slice(slice.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `slice`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:97:29
+ --> $DIR/unnecessary_to_owned.rs:98:29
|
LL | require_deref_str_slice(s.to_owned(), slice.to_owned());
| ^^^^^^^^^^^^ help: use: `s`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:97:43
+ --> $DIR/unnecessary_to_owned.rs:98:43
|
LL | require_deref_str_slice(s.to_owned(), slice.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `slice`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:98:29
+ --> $DIR/unnecessary_to_owned.rs:99:29
|
LL | require_deref_slice_str(slice.to_owned(), s.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `slice`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:98:47
+ --> $DIR/unnecessary_to_owned.rs:99:47
|
LL | require_deref_slice_str(slice.to_owned(), s.to_owned());
| ^^^^^^^^^^^^ help: use: `s`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:100:26
+ --> $DIR/unnecessary_to_owned.rs:101:26
|
LL | require_as_ref_c_str(c_str.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `c_str`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:101:27
+ --> $DIR/unnecessary_to_owned.rs:102:27
|
LL | require_as_ref_os_str(os_str.to_owned());
| ^^^^^^^^^^^^^^^^^ help: use: `os_str`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:102:25
+ --> $DIR/unnecessary_to_owned.rs:103:25
|
LL | require_as_ref_path(path.to_owned());
| ^^^^^^^^^^^^^^^ help: use: `path`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:103:24
+ --> $DIR/unnecessary_to_owned.rs:104:24
|
LL | require_as_ref_str(s.to_owned());
| ^^^^^^^^^^^^ help: use: `s`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:104:24
+ --> $DIR/unnecessary_to_owned.rs:105:24
|
LL | require_as_ref_str(x.to_owned());
| ^^^^^^^^^^^^ help: use: `&x`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:105:26
+ --> $DIR/unnecessary_to_owned.rs:106:26
|
LL | require_as_ref_slice(array.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `array`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:106:26
+ --> $DIR/unnecessary_to_owned.rs:107:26
|
LL | require_as_ref_slice(array_ref.to_owned());
| ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:107:26
+ --> $DIR/unnecessary_to_owned.rs:108:26
|
LL | require_as_ref_slice(slice.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `slice`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:109:31
+ --> $DIR/unnecessary_to_owned.rs:110:31
|
LL | require_impl_as_ref_c_str(c_str.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `c_str`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:110:32
+ --> $DIR/unnecessary_to_owned.rs:111:32
|
LL | require_impl_as_ref_os_str(os_str.to_owned());
| ^^^^^^^^^^^^^^^^^ help: use: `os_str`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:111:30
+ --> $DIR/unnecessary_to_owned.rs:112:30
|
LL | require_impl_as_ref_path(path.to_owned());
| ^^^^^^^^^^^^^^^ help: use: `path`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:112:29
+ --> $DIR/unnecessary_to_owned.rs:113:29
|
LL | require_impl_as_ref_str(s.to_owned());
| ^^^^^^^^^^^^ help: use: `s`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:113:29
+ --> $DIR/unnecessary_to_owned.rs:114:29
|
LL | require_impl_as_ref_str(x.to_owned());
| ^^^^^^^^^^^^ help: use: `&x`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:114:31
+ --> $DIR/unnecessary_to_owned.rs:115:31
|
LL | require_impl_as_ref_slice(array.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `array`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:115:31
+ --> $DIR/unnecessary_to_owned.rs:116:31
|
LL | require_impl_as_ref_slice(array_ref.to_owned());
| ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:116:31
+ --> $DIR/unnecessary_to_owned.rs:117:31
|
LL | require_impl_as_ref_slice(slice.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `slice`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:118:30
+ --> $DIR/unnecessary_to_owned.rs:119:30
|
LL | require_as_ref_str_slice(s.to_owned(), array.to_owned());
| ^^^^^^^^^^^^ help: use: `s`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:118:44
+ --> $DIR/unnecessary_to_owned.rs:119:44
|
LL | require_as_ref_str_slice(s.to_owned(), array.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `array`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:119:30
+ --> $DIR/unnecessary_to_owned.rs:120:30
|
LL | require_as_ref_str_slice(s.to_owned(), array_ref.to_owned());
| ^^^^^^^^^^^^ help: use: `s`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:119:44
+ --> $DIR/unnecessary_to_owned.rs:120:44
|
LL | require_as_ref_str_slice(s.to_owned(), array_ref.to_owned());
| ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:120:30
+ --> $DIR/unnecessary_to_owned.rs:121:30
|
LL | require_as_ref_str_slice(s.to_owned(), slice.to_owned());
| ^^^^^^^^^^^^ help: use: `s`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:120:44
+ --> $DIR/unnecessary_to_owned.rs:121:44
|
LL | require_as_ref_str_slice(s.to_owned(), slice.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `slice`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:121:30
+ --> $DIR/unnecessary_to_owned.rs:122:30
|
LL | require_as_ref_slice_str(array.to_owned(), s.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `array`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:121:48
+ --> $DIR/unnecessary_to_owned.rs:122:48
|
LL | require_as_ref_slice_str(array.to_owned(), s.to_owned());
| ^^^^^^^^^^^^ help: use: `s`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:122:30
+ --> $DIR/unnecessary_to_owned.rs:123:30
|
LL | require_as_ref_slice_str(array_ref.to_owned(), s.to_owned());
| ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:122:52
+ --> $DIR/unnecessary_to_owned.rs:123:52
|
LL | require_as_ref_slice_str(array_ref.to_owned(), s.to_owned());
| ^^^^^^^^^^^^ help: use: `s`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:123:30
+ --> $DIR/unnecessary_to_owned.rs:124:30
|
LL | require_as_ref_slice_str(slice.to_owned(), s.to_owned());
| ^^^^^^^^^^^^^^^^ help: use: `slice`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:123:48
+ --> $DIR/unnecessary_to_owned.rs:124:48
|
LL | require_as_ref_slice_str(slice.to_owned(), s.to_owned());
| ^^^^^^^^^^^^ help: use: `s`
error: unnecessary use of `to_string`
- --> $DIR/unnecessary_to_owned.rs:125:20
+ --> $DIR/unnecessary_to_owned.rs:126:20
|
LL | let _ = x.join(&x_ref.to_string());
| ^^^^^^^^^^^^^^^^^^ help: use: `x_ref`
error: unnecessary use of `to_vec`
- --> $DIR/unnecessary_to_owned.rs:127:13
+ --> $DIR/unnecessary_to_owned.rs:128:13
|
LL | let _ = slice.to_vec().into_iter();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:128:13
+ --> $DIR/unnecessary_to_owned.rs:129:13
|
LL | let _ = slice.to_owned().into_iter();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()`
error: unnecessary use of `to_vec`
- --> $DIR/unnecessary_to_owned.rs:129:13
+ --> $DIR/unnecessary_to_owned.rs:130:13
|
LL | let _ = [std::path::PathBuf::new()][..].to_vec().into_iter();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:130:13
+ --> $DIR/unnecessary_to_owned.rs:131:13
|
LL | let _ = [std::path::PathBuf::new()][..].to_owned().into_iter();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()`
error: unnecessary use of `to_vec`
- --> $DIR/unnecessary_to_owned.rs:132:13
+ --> $DIR/unnecessary_to_owned.rs:133:13
|
LL | let _ = IntoIterator::into_iter(slice.to_vec());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:133:13
+ --> $DIR/unnecessary_to_owned.rs:134:13
|
LL | let _ = IntoIterator::into_iter(slice.to_owned());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()`
error: unnecessary use of `to_vec`
- --> $DIR/unnecessary_to_owned.rs:134:13
+ --> $DIR/unnecessary_to_owned.rs:135:13
|
LL | let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_vec());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()`
error: unnecessary use of `to_owned`
- --> $DIR/unnecessary_to_owned.rs:135:13
+ --> $DIR/unnecessary_to_owned.rs:136:13
|
LL | let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_owned());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()`
error: unnecessary use of `to_vec`
- --> $DIR/unnecessary_to_owned.rs:196:14
+ --> $DIR/unnecessary_to_owned.rs:197:14
|
LL | for t in file_types.to_vec() {
| ^^^^^^^^^^^^^^^^^^^
LL + let path = match get_file_path(t) {
|
+error: unnecessary use of `to_vec`
+ --> $DIR/unnecessary_to_owned.rs:220:14
+ |
+LL | let _ = &["x"][..].to_vec().into_iter();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().cloned()`
+
+error: unnecessary use of `to_vec`
+ --> $DIR/unnecessary_to_owned.rs:225:14
+ |
+LL | let _ = &["x"][..].to_vec().into_iter();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().copied()`
+
error: unnecessary use of `to_string`
- --> $DIR/unnecessary_to_owned.rs:260:24
+ --> $DIR/unnecessary_to_owned.rs:272:24
|
LL | Box::new(build(y.to_string()))
| ^^^^^^^^^^^^^ help: use: `y`
-error: aborting due to 77 previous errors
+error: aborting due to 79 previous errors
#![allow(unreachable_patterns, irrefutable_let_patterns, unused_variables)]
fn main() {
+ // Should be ignored by this lint, as nesting requires more characters.
+ if let &0 | &2 = &0 {}
+
if let box (0 | 2) = Box::new(0) {}
if let box (0 | 1 | 2 | 3 | 4) = Box::new(0) {}
- const C0: &u8 = &1;
- if let &(0 | 2) | C0 = &0 {}
+ const C0: Option<u8> = Some(1);
+ if let Some(1 | 2) | C0 = None {}
if let &mut (0 | 2) = &mut 0 {}
if let x @ (0 | 2) = 0 {}
if let (0, 1 | 2 | 3) = (0, 0) {}
#![allow(unreachable_patterns, irrefutable_let_patterns, unused_variables)]
fn main() {
+ // Should be ignored by this lint, as nesting requires more characters.
+ if let &0 | &2 = &0 {}
+
if let box 0 | box 2 = Box::new(0) {}
if let box ((0 | 1)) | box (2 | 3) | box 4 = Box::new(0) {}
- const C0: &u8 = &1;
- if let &0 | C0 | &2 = &0 {}
+ const C0: Option<u8> = Some(1);
+ if let Some(1) | C0 | Some(2) = None {}
if let &mut 0 | &mut 2 = &mut 0 {}
if let x @ 0 | x @ 2 = 0 {}
if let (0, 1) | (0, 2) | (0, 3) = (0, 0) {}
error: unnested or-patterns
- --> $DIR/unnested_or_patterns.rs:9:12
+ --> $DIR/unnested_or_patterns.rs:12:12
|
LL | if let box 0 | box 2 = Box::new(0) {}
| ^^^^^^^^^^^^^
| ~~~~~~~~~~~
error: unnested or-patterns
- --> $DIR/unnested_or_patterns.rs:10:12
+ --> $DIR/unnested_or_patterns.rs:13:12
|
LL | if let box ((0 | 1)) | box (2 | 3) | box 4 = Box::new(0) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| ~~~~~~~~~~~~~~~~~~~~~~~
error: unnested or-patterns
- --> $DIR/unnested_or_patterns.rs:12:12
+ --> $DIR/unnested_or_patterns.rs:15:12
|
-LL | if let &0 | C0 | &2 = &0 {}
- | ^^^^^^^^^^^^
+LL | if let Some(1) | C0 | Some(2) = None {}
+ | ^^^^^^^^^^^^^^^^^^^^^^
|
help: nest the patterns
|
-LL | if let &(0 | 2) | C0 = &0 {}
- | ~~~~~~~~~~~~~
+LL | if let Some(1 | 2) | C0 = None {}
+ | ~~~~~~~~~~~~~~~~
error: unnested or-patterns
- --> $DIR/unnested_or_patterns.rs:13:12
+ --> $DIR/unnested_or_patterns.rs:16:12
|
LL | if let &mut 0 | &mut 2 = &mut 0 {}
| ^^^^^^^^^^^^^^^
| ~~~~~~~~~~~~
error: unnested or-patterns
- --> $DIR/unnested_or_patterns.rs:14:12
+ --> $DIR/unnested_or_patterns.rs:17:12
|
LL | if let x @ 0 | x @ 2 = 0 {}
| ^^^^^^^^^^^^^
| ~~~~~~~~~~~
error: unnested or-patterns
- --> $DIR/unnested_or_patterns.rs:15:12
+ --> $DIR/unnested_or_patterns.rs:18:12
|
LL | if let (0, 1) | (0, 2) | (0, 3) = (0, 0) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^
| ~~~~~~~~~~~~~~
error: unnested or-patterns
- --> $DIR/unnested_or_patterns.rs:16:12
+ --> $DIR/unnested_or_patterns.rs:19:12
|
LL | if let (1, 0) | (2, 0) | (3, 0) = (0, 0) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^
| ~~~~~~~~~~~~~~
error: unnested or-patterns
- --> $DIR/unnested_or_patterns.rs:17:12
+ --> $DIR/unnested_or_patterns.rs:20:12
|
LL | if let (x, ..) | (x, 1) | (x, 2) = (0, 1) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^
| ~~~~~~~~~~~~~~~~~~~~
error: unnested or-patterns
- --> $DIR/unnested_or_patterns.rs:18:12
+ --> $DIR/unnested_or_patterns.rs:21:12
|
LL | if let [0] | [1] = [0] {}
| ^^^^^^^^^
| ~~~~~~~
error: unnested or-patterns
- --> $DIR/unnested_or_patterns.rs:19:12
+ --> $DIR/unnested_or_patterns.rs:22:12
|
LL | if let [x, 0] | [x, 1] = [0, 1] {}
| ^^^^^^^^^^^^^^^
| ~~~~~~~~~~
error: unnested or-patterns
- --> $DIR/unnested_or_patterns.rs:20:12
+ --> $DIR/unnested_or_patterns.rs:23:12
|
LL | if let [x, 0] | [x, 1] | [x, 2] = [0, 1] {}
| ^^^^^^^^^^^^^^^^^^^^^^^^
| ~~~~~~~~~~~~~~
error: unnested or-patterns
- --> $DIR/unnested_or_patterns.rs:21:12
+ --> $DIR/unnested_or_patterns.rs:24:12
|
LL | if let [x, ..] | [x, 1] | [x, 2] = [0, 1] {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^
| ~~~~~~~~~~~~~~~~~~~~
error: unnested or-patterns
- --> $DIR/unnested_or_patterns.rs:23:12
+ --> $DIR/unnested_or_patterns.rs:26:12
|
LL | if let TS(0, x) | TS(1, x) = TS(0, 0) {}
| ^^^^^^^^^^^^^^^^^^^
| ~~~~~~~~~~~~
error: unnested or-patterns
- --> $DIR/unnested_or_patterns.rs:24:12
+ --> $DIR/unnested_or_patterns.rs:27:12
|
LL | if let TS(1, 0) | TS(2, 0) | TS(3, 0) = TS(0, 0) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| ~~~~~~~~~~~~~~~~
error: unnested or-patterns
- --> $DIR/unnested_or_patterns.rs:25:12
+ --> $DIR/unnested_or_patterns.rs:28:12
|
LL | if let TS(x, ..) | TS(x, 1) | TS(x, 2) = TS(0, 0) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| ~~~~~~~~~~~~~~~~~~~~~~~~
error: unnested or-patterns
- --> $DIR/unnested_or_patterns.rs:30:12
+ --> $DIR/unnested_or_patterns.rs:33:12
|
LL | if let S { x: 0, y } | S { y, x: 1 } = (S { x: 0, y: 1 }) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#[allow(clippy::enum_glob_use)]
pub use std::cmp::Ordering::*;
+// don't lint on clippy::redundant_pub_crate
+mod c {
+ #[allow(clippy::redundant_pub_crate)]
+ pub(crate) struct S;
+}
+
fn test_indented_attr() {
#![allow(clippy::almost_swapped)]
use std::collections::HashSet;
#[allow(clippy::enum_glob_use)]
pub use std::cmp::Ordering::*;
+// don't lint on clippy::redundant_pub_crate
+mod c {
+ #[allow(clippy::redundant_pub_crate)]
+ pub(crate) struct S;
+}
+
fn test_indented_attr() {
#[allow(clippy::almost_swapped)]
use std::collections::HashSet;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if you just forgot a `!`, use: `#![cfg_attr(feature = "cargo-clippy", allow(dead_code)`
error: useless lint attribute
- --> $DIR/useless_attribute.rs:61:5
+ --> $DIR/useless_attribute.rs:67:5
|
LL | #[allow(clippy::almost_swapped)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if you just forgot a `!`, use: `#![allow(clippy::almost_swapped)]`
// FIXME: We should likely add another edition 2021 test case for this lint
#![warn(clippy::wildcard_imports)]
-#![allow(unused)]
-#![allow(clippy::unnecessary_wraps)]
+#![allow(unused, clippy::unnecessary_wraps, clippy::let_unit_value)]
#![warn(unused_imports)]
extern crate wildcard_imports_helper;
// FIXME: We should likely add another edition 2021 test case for this lint
#![warn(clippy::wildcard_imports)]
-#![allow(unused)]
-#![allow(clippy::unnecessary_wraps)]
+#![allow(unused, clippy::unnecessary_wraps, clippy::let_unit_value)]
#![warn(unused_imports)]
extern crate wildcard_imports_helper;
error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:17:5
+ --> $DIR/wildcard_imports.rs:16:5
|
LL | use crate::fn_mod::*;
| ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo`
= note: `-D clippy::wildcard-imports` implied by `-D warnings`
error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:18:5
+ --> $DIR/wildcard_imports.rs:17:5
|
LL | use crate::mod_mod::*;
| ^^^^^^^^^^^^^^^^^ help: try: `crate::mod_mod::inner_mod`
error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:19:5
+ --> $DIR/wildcard_imports.rs:18:5
|
LL | use crate::multi_fn_mod::*;
| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}`
error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:21:5
+ --> $DIR/wildcard_imports.rs:20:5
|
LL | use crate::struct_mod::*;
| ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::struct_mod::{A, inner_struct_mod}`
error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:25:5
+ --> $DIR/wildcard_imports.rs:24:5
|
LL | use wildcard_imports_helper::inner::inner_for_self_import::*;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar`
error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:26:5
+ --> $DIR/wildcard_imports.rs:25:5
|
LL | use wildcard_imports_helper::*;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}`
error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:97:13
+ --> $DIR/wildcard_imports.rs:96:13
|
LL | use crate::fn_mod::*;
| ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo`
error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:103:75
+ --> $DIR/wildcard_imports.rs:102:75
|
LL | use wildcard_imports_helper::inner::inner_for_self_import::{self, *};
| ^ help: try: `inner_extern_foo`
error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:104:13
+ --> $DIR/wildcard_imports.rs:103:13
|
LL | use wildcard_imports_helper::*;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}`
error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:115:20
+ --> $DIR/wildcard_imports.rs:114:20
|
LL | use self::{inner::*, inner2::*};
| ^^^^^^^^ help: try: `inner::inner_foo`
error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:115:30
+ --> $DIR/wildcard_imports.rs:114:30
|
LL | use self::{inner::*, inner2::*};
| ^^^^^^^^^ help: try: `inner2::inner_bar`
error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:122:13
+ --> $DIR/wildcard_imports.rs:121:13
|
LL | use wildcard_imports_helper::*;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}`
error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:151:9
+ --> $DIR/wildcard_imports.rs:150:9
|
LL | use crate::in_fn_test::*;
| ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}`
error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:160:9
+ --> $DIR/wildcard_imports.rs:159:9
|
LL | use crate:: in_fn_test:: * ;
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported`
error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:161:9
+ --> $DIR/wildcard_imports.rs:160:9
|
LL | use crate:: fn_mod::
| _________^
| |_________^ help: try: `crate:: fn_mod::foo`
error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:172:13
+ --> $DIR/wildcard_imports.rs:171:13
|
LL | use super::*;
| ^^^^^^^^ help: try: `super::foofoo`
error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:207:17
+ --> $DIR/wildcard_imports.rs:206:17
|
LL | use super::*;
| ^^^^^^^^ help: try: `super::insidefoo`
error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:215:13
+ --> $DIR/wildcard_imports.rs:214:13
|
LL | use super_imports::*;
| ^^^^^^^^^^^^^^^^ help: try: `super_imports::foofoo`
error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:224:17
+ --> $DIR/wildcard_imports.rs:223:17
|
LL | use super::super::*;
| ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo`
error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:233:13
+ --> $DIR/wildcard_imports.rs:232:13
|
LL | use super::super::super_imports::*;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo`
error: usage of wildcard import
- --> $DIR/wildcard_imports.rs:241:13
+ --> $DIR/wildcard_imports.rs:240:13
|
LL | use super::*;
| ^^^^^^^^ help: try: `super::foofoo`
struct S;
impl S {
- // Should lint: is_ methods should only take &self, or no self at all.
- fn is_still_buggy(&mut self) -> bool {
- false
- }
-
// Should not lint: "no self at all" is allowed.
fn is_forty_two(x: u32) -> bool {
x == 42
|
= help: consider choosing a less ambiguous name
-error: methods called `is_*` usually take `self` by reference or no `self`
+error: methods called `is_*` usually take `self` by mutable reference or `self` by reference or no `self`
--> $DIR/wrong_self_convention.rs:38:15
|
LL | fn is_i32(self) {}
|
= help: consider choosing a less ambiguous name
-error: methods called `is_*` usually take `self` by reference or no `self`
+error: methods called `is_*` usually take `self` by mutable reference or `self` by reference or no `self`
--> $DIR/wrong_self_convention.rs:46:19
|
LL | pub fn is_i64(self) {}
|
= help: consider choosing a less ambiguous name
-error: methods called `is_*` usually take `self` by reference or no `self`
+error: methods called `is_*` usually take `self` by mutable reference or `self` by reference or no `self`
--> $DIR/wrong_self_convention.rs:98:19
|
LL | fn is_i32(self) {}
|
= help: consider choosing a less ambiguous name
-error: methods called `is_*` usually take `self` by reference or no `self`
+error: methods called `is_*` usually take `self` by mutable reference or `self` by reference or no `self`
--> $DIR/wrong_self_convention.rs:122:19
|
LL | fn is_i32(self);
|
= help: consider choosing a less ambiguous name
-error: methods called `is_*` usually take `self` by reference or no `self`
- --> $DIR/wrong_self_convention.rs:197:27
- |
-LL | fn is_still_buggy(&mut self) -> bool {
- | ^^^^^^^^^
- |
- = help: consider choosing a less ambiguous name
-
-error: aborting due to 25 previous errors
+error: aborting due to 24 previous errors
pub fn to_other_thingy(self: Pin<&Self>) {}
}
}
+
+mod issue_8480_8513 {
+ struct Cat(String);
+
+ impl Cat {
+ fn is_animal(&mut self) -> bool {
+ todo!();
+ }
+ }
+}