diff options
| author | Philipp Krones <hello@philkrones.com> | 2024-07-25 18:09:52 +0200 |
|---|---|---|
| committer | Philipp Krones <hello@philkrones.com> | 2024-07-25 18:23:27 +0200 |
| commit | 9f53fc32cfb62724d95de44779259ce4e24bc085 (patch) | |
| tree | df5069c84c1a281c78b72e8935a9664b3110f66a /clippy_lints/src | |
| parent | 6d674685ae4e9156dbb6ecd3aa38d87864ecab3e (diff) | |
| parent | df0cb6c51ee798264586ee9144466cf003d68181 (diff) | |
Merge remote-tracking branch 'upstream/master' into rustup
Diffstat (limited to 'clippy_lints/src')
152 files changed, 1933 insertions, 1927 deletions
diff --git a/clippy_lints/src/absolute_paths.rs b/clippy_lints/src/absolute_paths.rs index 461117cf965..c0a9d888e0b 100644 --- a/clippy_lints/src/absolute_paths.rs +++ b/clippy_lints/src/absolute_paths.rs @@ -1,3 +1,4 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint; use clippy_utils::source::snippet_opt; use rustc_data_structures::fx::FxHashSet; @@ -47,7 +48,16 @@ impl_lint_pass!(AbsolutePaths => [ABSOLUTE_PATHS]); pub struct AbsolutePaths { pub absolute_paths_max_segments: u64, - pub absolute_paths_allowed_crates: FxHashSet<String>, + pub absolute_paths_allowed_crates: &'static FxHashSet<String>, +} + +impl AbsolutePaths { + pub fn new(conf: &'static Conf) -> Self { + Self { + absolute_paths_max_segments: conf.absolute_paths_max_segments, + absolute_paths_allowed_crates: &conf.absolute_paths_allowed_crates, + } + } } impl LateLintPass<'_> for AbsolutePaths { diff --git a/clippy_lints/src/almost_complete_range.rs b/clippy_lints/src/almost_complete_range.rs index 96e9c949b75..451bae95987 100644 --- a/clippy_lints/src/almost_complete_range.rs +++ b/clippy_lints/src/almost_complete_range.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{trim_span, walk_span_to_context}; use rustc_ast::ast::{Expr, ExprKind, LitKind, Pat, PatKind, RangeEnd, RangeLimits}; @@ -34,8 +35,10 @@ pub struct AlmostCompleteRange { msrv: Msrv, } impl AlmostCompleteRange { - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } } } impl EarlyLintPass for AlmostCompleteRange { diff --git a/clippy_lints/src/approx_const.rs b/clippy_lints/src/approx_const.rs index ec28fd46111..e6d52bcef71 100644 --- a/clippy_lints/src/approx_const.rs +++ b/clippy_lints/src/approx_const.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_help; use rustc_ast::ast::{FloatTy, LitFloatType, LitKind}; use rustc_hir::{Expr, ExprKind}; @@ -67,9 +68,10 @@ pub struct ApproxConstant { } impl ApproxConstant { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } } fn check_lit(&self, cx: &LateContext<'_>, lit: &LitKind, e: &Expr<'_>) { diff --git a/clippy_lints/src/arc_with_non_send_sync.rs b/clippy_lints/src/arc_with_non_send_sync.rs index d57ab539fff..4eafa330faf 100644 --- a/clippy_lints/src/arc_with_non_send_sync.rs +++ b/clippy_lints/src/arc_with_non_send_sync.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::is_from_proc_macro; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; -use clippy_utils::{is_from_proc_macro, last_path_segment}; -use rustc_hir::{Expr, ExprKind}; +use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_middle::ty::print::with_forced_trimmed_paths; @@ -42,12 +42,11 @@ declare_lint_pass!(ArcWithNonSendSync => [ARC_WITH_NON_SEND_SYNC]); impl<'tcx> LateLintPass<'tcx> for ArcWithNonSendSync { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if !expr.span.from_expansion() - && let ty = cx.typeck_results().expr_ty(expr) - && is_type_diagnostic_item(cx, ty, sym::Arc) - && let ExprKind::Call(func, [arg]) = expr.kind - && let ExprKind::Path(func_path) = func.kind - && last_path_segment(&func_path).ident.name == sym::new + if let ExprKind::Call(func, [arg]) = expr.kind + && let ExprKind::Path(QPath::TypeRelative(func_ty, func_name)) = func.kind + && func_name.ident.name == sym::new + && !expr.span.from_expansion() + && is_type_diagnostic_item(cx, cx.typeck_results().node_type(func_ty.hir_id), sym::Arc) && let arg_ty = cx.typeck_results().expr_ty(arg) // make sure that the type is not and does not contain any type parameters && arg_ty.walk().all(|arg| { diff --git a/clippy_lints/src/assigning_clones.rs b/clippy_lints/src/assigning_clones.rs index 0de0031ed24..03f777600f0 100644 --- a/clippy_lints/src/assigning_clones.rs +++ b/clippy_lints/src/assigning_clones.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::mir::{enclosing_mir, PossibleBorrowerMap}; use clippy_utils::sugg::Sugg; @@ -57,9 +58,10 @@ pub struct AssigningClones { } impl AssigningClones { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } } } diff --git a/clippy_lints/src/attrs/mod.rs b/clippy_lints/src/attrs/mod.rs index 8ec60314cc9..8f430ae601a 100644 --- a/clippy_lints/src/attrs/mod.rs +++ b/clippy_lints/src/attrs/mod.rs @@ -16,6 +16,7 @@ mod useless_attribute; mod utils; use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use rustc_ast::{Attribute, MetaItemKind, NestedMetaItem}; use rustc_hir::{ImplItem, Item, ItemKind, TraitItem}; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; @@ -499,7 +500,6 @@ declare_clippy_lint! { "duplicated attribute" } -#[derive(Clone)] pub struct Attributes { msrv: Msrv, } @@ -517,9 +517,10 @@ impl_lint_pass!(Attributes => [ ]); impl Attributes { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } } } @@ -589,7 +590,15 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { } pub struct EarlyAttributes { - pub msrv: Msrv, + msrv: Msrv, +} + +impl EarlyAttributes { + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } + } } impl_lint_pass!(EarlyAttributes => [ diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index d4a1e2780d0..d5f017b2650 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -1,11 +1,11 @@ -use clippy_config::types::DisallowedPath; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{match_def_path, paths}; -use rustc_data_structures::fx::FxHashMap; +use clippy_utils::{create_disallowed_map, match_def_path, paths}; use rustc_hir as hir; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::{DefId, DefIdMap}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::CoroutineLayout; +use rustc_middle::ty::TyCtxt; use rustc_session::impl_lint_pass; use rustc_span::{sym, Span}; @@ -172,31 +172,19 @@ declare_clippy_lint! { impl_lint_pass!(AwaitHolding => [AWAIT_HOLDING_LOCK, AWAIT_HOLDING_REFCELL_REF, AWAIT_HOLDING_INVALID_TYPE]); -#[derive(Debug)] pub struct AwaitHolding { - conf_invalid_types: Vec<DisallowedPath>, - def_ids: FxHashMap<DefId, DisallowedPath>, + def_ids: DefIdMap<(&'static str, Option<&'static str>)>, } impl AwaitHolding { - pub(crate) fn new(conf_invalid_types: Vec<DisallowedPath>) -> Self { + pub(crate) fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { Self { - conf_invalid_types, - def_ids: FxHashMap::default(), + def_ids: create_disallowed_map(tcx, &conf.await_holding_invalid_types), } } } impl<'tcx> LateLintPass<'tcx> for AwaitHolding { - fn check_crate(&mut self, cx: &LateContext<'tcx>) { - for conf in &self.conf_invalid_types { - let segs: Vec<_> = conf.path().split("::").collect(); - for id in clippy_utils::def_path_def_ids(cx, &segs) { - self.def_ids.insert(id, conf.clone()); - } - } - } - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { if let hir::ExprKind::Closure(hir::Closure { kind: hir::ClosureKind::Coroutine(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _)), @@ -258,25 +246,22 @@ impl AwaitHolding { ); }, ); - } else if let Some(disallowed) = self.def_ids.get(&adt.did()) { - emit_invalid_type(cx, ty_cause.source_info.span, disallowed); + } else if let Some(&(path, reason)) = self.def_ids.get(&adt.did()) { + emit_invalid_type(cx, ty_cause.source_info.span, path, reason); } } } } } -fn emit_invalid_type(cx: &LateContext<'_>, span: Span, disallowed: &DisallowedPath) { +fn emit_invalid_type(cx: &LateContext<'_>, span: Span, path: &'static str, reason: Option<&'static str>) { span_lint_and_then( cx, AWAIT_HOLDING_INVALID_TYPE, span, - format!( - "`{}` may not be held across an await point per `clippy.toml`", - disallowed.path() - ), + format!("holding a disallowed type across an await point `{path}`"), |diag| { - if let Some(reason) = disallowed.reason() { + if let Some(reason) = reason { diag.note(reason); } }, diff --git a/clippy_lints/src/borrow_deref_ref.rs b/clippy_lints/src/borrow_deref_ref.rs index 0ca4a0e067d..bd123a725a7 100644 --- a/clippy_lints/src/borrow_deref_ref.rs +++ b/clippy_lints/src/borrow_deref_ref.rs @@ -49,35 +49,31 @@ declare_lint_pass!(BorrowDerefRef => [BORROW_DEREF_REF]); impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &rustc_hir::Expr<'tcx>) { - if !e.span.from_expansion() - && let ExprKind::AddrOf(_, Mutability::Not, addrof_target) = e.kind - && !addrof_target.span.from_expansion() + if let ExprKind::AddrOf(_, Mutability::Not, addrof_target) = e.kind && let ExprKind::Unary(UnOp::Deref, deref_target) = addrof_target.kind - && !deref_target.span.from_expansion() && !matches!(deref_target.kind, ExprKind::Unary(UnOp::Deref, ..)) + && !e.span.from_expansion() + && !deref_target.span.from_expansion() + && !addrof_target.span.from_expansion() && let ref_ty = cx.typeck_results().expr_ty(deref_target) && let ty::Ref(_, inner_ty, Mutability::Not) = ref_ty.kind() - { - if let Some(parent_expr) = get_parent_expr(cx, e) { - if matches!(parent_expr.kind, ExprKind::Unary(UnOp::Deref, ..)) - && !is_lint_allowed(cx, DEREF_ADDROF, parent_expr.hir_id) - { - return; + && get_parent_expr(cx, e).map_or(true, |parent| { + match parent.kind { + // `*&*foo` should lint `deref_addrof` instead. + ExprKind::Unary(UnOp::Deref, _) => is_lint_allowed(cx, DEREF_ADDROF, parent.hir_id), + // `&*foo` creates a distinct temporary from `foo` + ExprKind::AddrOf(_, Mutability::Mut, _) => !matches!( + deref_target.kind, + ExprKind::Path(..) + | ExprKind::Field(..) + | ExprKind::Index(..) + | ExprKind::Unary(UnOp::Deref, ..) + ), + _ => true, } - - // modification to `&mut &*x` is different from `&mut x` - if matches!( - deref_target.kind, - ExprKind::Path(..) | ExprKind::Field(..) | ExprKind::Index(..) | ExprKind::Unary(UnOp::Deref, ..) - ) && matches!(parent_expr.kind, ExprKind::AddrOf(_, Mutability::Mut, _)) - { - return; - } - } - if is_from_proc_macro(cx, e) { - return; - } - + }) + && !is_from_proc_macro(cx, e) + { span_lint_and_then( cx, BORROW_DEREF_REF, diff --git a/clippy_lints/src/cargo/mod.rs b/clippy_lints/src/cargo/mod.rs index 593bc6c81ee..312ad4c2990 100644 --- a/clippy_lints/src/cargo/mod.rs +++ b/clippy_lints/src/cargo/mod.rs @@ -5,6 +5,7 @@ mod multiple_crate_versions; mod wildcard_dependencies; use cargo_metadata::MetadataCommand; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint; use clippy_utils::is_lint_allowed; use rustc_data_structures::fx::FxHashSet; @@ -204,8 +205,8 @@ declare_clippy_lint! { } pub struct Cargo { - pub allowed_duplicate_crates: FxHashSet<String>, - pub ignore_publish: bool, + allowed_duplicate_crates: &'static FxHashSet<String>, + ignore_publish: bool, } impl_lint_pass!(Cargo => [ @@ -217,6 +218,15 @@ impl_lint_pass!(Cargo => [ LINT_GROUPS_PRIORITY, ]); +impl Cargo { + pub fn new(conf: &'static Conf) -> Self { + Self { + allowed_duplicate_crates: &conf.allowed_duplicate_crates, + ignore_publish: conf.cargo_ignore_publish, + } + } +} + impl LateLintPass<'_> for Cargo { fn check_crate(&mut self, cx: &LateContext<'_>) { static NO_DEPS_LINTS: &[&Lint] = &[ @@ -253,7 +263,7 @@ impl LateLintPass<'_> for Cargo { { match MetadataCommand::new().exec() { Ok(metadata) => { - multiple_crate_versions::check(cx, &metadata, &self.allowed_duplicate_crates); + multiple_crate_versions::check(cx, &metadata, self.allowed_duplicate_crates); }, Err(e) => { for lint in WITH_DEPS_LINTS { diff --git a/clippy_lints/src/casts/cast_lossless.rs b/clippy_lints/src/casts/cast_lossless.rs index d52ad1c6f23..ff460a3fd8e 100644 --- a/clippy_lints/src/casts/cast_lossless.rs +++ b/clippy_lints/src/casts/cast_lossless.rs @@ -1,19 +1,21 @@ use clippy_config::msrvs::{self, Msrv}; -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::in_constant; -use clippy_utils::source::{snippet_opt, snippet_with_applicability}; +use clippy_utils::source::snippet_opt; +use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_isize_or_usize; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, QPath, TyKind}; +use rustc_hir::{Expr, QPath, TyKind}; use rustc_lint::LateContext; -use rustc_middle::ty::{self, FloatTy, Ty, UintTy}; +use rustc_middle::ty::{self, FloatTy, Ty}; +use rustc_span::hygiene; use super::{utils, CAST_LOSSLESS}; pub(super) fn check( cx: &LateContext<'_>, expr: &Expr<'_>, - cast_op: &Expr<'_>, + cast_from_expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>, cast_to_hir: &rustc_hir::Ty<'_>, @@ -23,64 +25,54 @@ pub(super) fn check( return; } - // The suggestion is to use a function call, so if the original expression - // has parens on the outside, they are no longer needed. - let mut app = Applicability::MachineApplicable; - let opt = snippet_opt(cx, cast_op.span.source_callsite()); - let sugg = opt.as_ref().map_or_else( - || { - app = Applicability::HasPlaceholders; - ".." - }, - |snip| { - if should_strip_parens(cast_op, snip) { - &snip[1..snip.len() - 1] - } else { - snip.as_str() - } - }, - ); - - // Display the type alias instead of the aliased type. Fixes #11285 - // - // FIXME: Once `lazy_type_alias` is stabilized(?) we should use `rustc_middle` types instead, - // this will allow us to display the right type with `cast_from` as well. - let cast_to_fmt = if let TyKind::Path(QPath::Resolved(None, path)) = cast_to_hir.kind - // It's a bit annoying but the turbofish is optional for types. A type in an `as` cast - // shouldn't have these if they're primitives, which are the only things we deal with. - // - // This could be removed for performance if this check is determined to have a pretty major - // effect. - && path.segments.iter().all(|segment| segment.args.is_none()) - { - snippet_with_applicability(cx, cast_to_hir.span, "..", &mut app) - } else { - cast_to.to_string().into() - }; - - let message = if cast_from.is_bool() { - format!("casting `{cast_from}` to `{cast_to_fmt}` is more cleanly stated with `{cast_to_fmt}::from(_)`") - } else { - format!("casting `{cast_from}` to `{cast_to_fmt}` may become silently lossy if you later change the type") - }; - - span_lint_and_sugg( + span_lint_and_then( cx, CAST_LOSSLESS, expr.span, - message, - "try", - format!("{cast_to_fmt}::from({sugg})"), - app, + format!("casts from `{cast_from}` to `{cast_to}` can be expressed infallibly using `From`"), + |diag| { + diag.help("an `as` cast can become silently lossy if the types change in the future"); + let mut applicability = Applicability::MachineApplicable; + let from_sugg = Sugg::hir_with_context(cx, cast_from_expr, expr.span.ctxt(), "<from>", &mut applicability); + let Some(ty) = snippet_opt(cx, hygiene::walk_chain(cast_to_hir.span, expr.span.ctxt())) else { + return; + }; + match cast_to_hir.kind { + TyKind::Infer => { + diag.span_suggestion_verbose( + expr.span, + "use `Into::into` instead", + format!("{}.into()", from_sugg.maybe_par()), + applicability, + ); + }, + // Don't suggest `A<_>::B::From(x)` or `macro!()::from(x)` + kind if matches!(kind, TyKind::Path(QPath::Resolved(_, path)) if path.segments.iter().any(|s| s.args.is_some())) + || !cast_to_hir.span.eq_ctxt(expr.span) => + { + diag.span_suggestion_verbose( + expr.span, + format!("use `<{ty}>::from` instead"), + format!("<{ty}>::from({from_sugg})"), + applicability, + ); + }, + _ => { + diag.span_suggestion_verbose( + expr.span, + format!("use `{ty}::from` instead"), + format!("{ty}::from({from_sugg})"), + applicability, + ); + }, + } + }, ); } fn should_lint(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>, msrv: &Msrv) -> bool { // Do not suggest using From in consts/statics until it is valid to do so (see #2267). - // - // If destination is u128, do not lint because source type cannot be larger - // If source is bool, still lint due to the lint message differing (refers to style) - if in_constant(cx, expr.hir_id) || (!cast_from.is_bool() && matches!(cast_to.kind(), ty::Uint(UintTy::U128))) { + if in_constant(cx, expr.hir_id) { return false; } @@ -110,12 +102,3 @@ fn should_lint(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to }, } } - -fn should_strip_parens(cast_expr: &Expr<'_>, snip: &str) -> bool { - if let ExprKind::Binary(_, _, _) = cast_expr.kind { - if snip.starts_with('(') && snip.ends_with(')') { - return true; - } - } - false -} diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index 54f0c7c4687..c31716fbcee 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -24,6 +24,7 @@ mod utils; mod zero_ptr; use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::is_hir_ty_cfg_dependant; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -658,11 +659,11 @@ declare_clippy_lint! { /// /// ### Example /// ```rust,ignore - /// let _: (0.0_f32 / 0.0) as u64; + /// let _ = (0.0_f32 / 0.0) as u64; /// ``` /// Use instead: /// ```rust,ignore - /// let _: = 0_u64; + /// let _ = 0_u64; /// ``` #[clippy::version = "1.66.0"] pub CAST_NAN_TO_INT, @@ -722,9 +723,10 @@ pub struct Casts { } impl Casts { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } } } @@ -761,45 +763,45 @@ impl<'tcx> LateLintPass<'tcx> for Casts { return; } - if let ExprKind::Cast(cast_expr, cast_to_hir) = expr.kind { + if let ExprKind::Cast(cast_from_expr, cast_to_hir) = expr.kind { if is_hir_ty_cfg_dependant(cx, cast_to_hir) { return; } let (cast_from, cast_to) = ( - cx.typeck_results().expr_ty(cast_expr), + cx.typeck_results().expr_ty(cast_from_expr), cx.typeck_results().expr_ty(expr), ); - if !expr.span.from_expansion() && unnecessary_cast::check(cx, expr, cast_expr, cast_from, cast_to) { + if !expr.span.from_expansion() && unnecessary_cast::check(cx, expr, cast_from_expr, cast_from, cast_to) { return; } - cast_slice_from_raw_parts::check(cx, expr, cast_expr, cast_to, &self.msrv); - ptr_cast_constness::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv); - as_ptr_cast_mut::check(cx, expr, cast_expr, cast_to); - fn_to_numeric_cast_any::check(cx, expr, cast_expr, cast_from, cast_to); - fn_to_numeric_cast::check(cx, expr, cast_expr, cast_from, cast_to); - fn_to_numeric_cast_with_truncation::check(cx, expr, cast_expr, cast_from, cast_to); - zero_ptr::check(cx, expr, cast_expr, cast_to_hir); + cast_slice_from_raw_parts::check(cx, expr, cast_from_expr, cast_to, &self.msrv); + ptr_cast_constness::check(cx, expr, cast_from_expr, cast_from, cast_to, &self.msrv); + as_ptr_cast_mut::check(cx, expr, cast_from_expr, cast_to); + fn_to_numeric_cast_any::check(cx, expr, cast_from_expr, cast_from, cast_to); + fn_to_numeric_cast::check(cx, expr, cast_from_expr, cast_from, cast_to); + fn_to_numeric_cast_with_truncation::check(cx, expr, cast_from_expr, cast_from, cast_to); + zero_ptr::check(cx, expr, cast_from_expr, cast_to_hir); if cast_to.is_numeric() { - cast_possible_truncation::check(cx, expr, cast_expr, cast_from, cast_to, cast_to_hir.span); + cast_possible_truncation::check(cx, expr, cast_from_expr, cast_from, cast_to, cast_to_hir.span); if cast_from.is_numeric() { cast_possible_wrap::check(cx, expr, cast_from, cast_to); cast_precision_loss::check(cx, expr, cast_from, cast_to); - cast_sign_loss::check(cx, expr, cast_expr, cast_from, cast_to); - cast_abs_to_unsigned::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv); - cast_nan_to_int::check(cx, expr, cast_expr, cast_from, cast_to); + cast_sign_loss::check(cx, expr, cast_from_expr, cast_from, cast_to); + cast_abs_to_unsigned::check(cx, expr, cast_from_expr, cast_from, cast_to, &self.msrv); + cast_nan_to_int::check(cx, expr, cast_from_expr, cast_from, cast_to); } - cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, cast_to_hir, &self.msrv); - cast_enum_constructor::check(cx, expr, cast_expr, cast_from); + cast_lossless::check(cx, expr, cast_from_expr, cast_from, cast_to, cast_to_hir, &self.msrv); + cast_enum_constructor::check(cx, expr, cast_from_expr, cast_from); } as_underscore::check(cx, expr, cast_to_hir); if self.msrv.meets(msrvs::PTR_FROM_REF) { - ref_as_ptr::check(cx, expr, cast_expr, cast_to_hir); + ref_as_ptr::check(cx, expr, cast_from_expr, cast_to_hir); } else if self.msrv.meets(msrvs::BORROW_AS_PTR) { - borrow_as_ptr::check(cx, expr, cast_expr, cast_to_hir); + borrow_as_ptr::check(cx, expr, cast_from_expr, cast_to_hir); } } diff --git a/clippy_lints/src/casts/ptr_cast_constness.rs b/clippy_lints/src/casts/ptr_cast_constness.rs index 921693567fc..7513e18d408 100644 --- a/clippy_lints/src/casts/ptr_cast_constness.rs +++ b/clippy_lints/src/casts/ptr_cast_constness.rs @@ -4,7 +4,7 @@ use clippy_utils::sugg::Sugg; use rustc_errors::Applicability; use rustc_hir::{Expr, Mutability}; use rustc_lint::LateContext; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, Ty, TypeVisitableExt}; use super::PTR_CAST_CONSTNESS; @@ -24,6 +24,7 @@ pub(super) fn check<'tcx>( (Mutability::Not, Mutability::Mut) | (Mutability::Mut, Mutability::Not) ) && from_ty == to_ty + && !from_ty.has_erased_regions() { let sugg = Sugg::hir(cx, cast_expr, "_"); let constness = match *to_mutbl { diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs index 92810ea2aa0..0b1ab5411bf 100644 --- a/clippy_lints/src/checked_conversions.rs +++ b/clippy_lints/src/checked_conversions.rs @@ -1,11 +1,12 @@ //! lint on manually implemented checked conversions that could be transformed into `try_from` use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{in_constant, is_integer_literal, SpanlessEq}; use rustc_errors::Applicability; -use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, QPath, TyKind}; +use rustc_hir::{BinOpKind, Expr, ExprKind, QPath, TyKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::impl_lint_pass; @@ -40,9 +41,10 @@ pub struct CheckedConversions { } impl CheckedConversions { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } } } @@ -50,61 +52,54 @@ impl_lint_pass!(CheckedConversions => [CHECKED_CONVERSIONS]); impl<'tcx> LateLintPass<'tcx> for CheckedConversions { fn check_expr(&mut self, cx: &LateContext<'_>, item: &Expr<'_>) { - if !self.msrv.meets(msrvs::TRY_FROM) { - return; - } - - let result = if !in_constant(cx, item.hir_id) - && !in_external_macro(cx.sess(), item.span) - && let ExprKind::Binary(op, left, right) = &item.kind - { - match op.node { - BinOpKind::Ge | BinOpKind::Le => single_check(item), - BinOpKind::And => double_check(cx, left, right), - _ => None, + if let ExprKind::Binary(op, lhs, rhs) = item.kind + && let (lt1, gt1, op2) = match op.node { + BinOpKind::Le => (lhs, rhs, None), + BinOpKind::Ge => (rhs, lhs, None), + BinOpKind::And + if let ExprKind::Binary(op1, lhs1, rhs1) = lhs.kind + && let ExprKind::Binary(op2, lhs2, rhs2) = rhs.kind + && let Some((lt1, gt1)) = read_le_ge(op1.node, lhs1, rhs1) + && let Some((lt2, gt2)) = read_le_ge(op2.node, lhs2, rhs2) => + { + (lt1, gt1, Some((lt2, gt2))) + }, + _ => return, } - } else { - None - }; - - if let Some(cv) = result { - if let Some(to_type) = cv.to_type { - let mut applicability = Applicability::MachineApplicable; - let snippet = snippet_with_applicability(cx, cv.expr_to_cast.span, "_", &mut applicability); - span_lint_and_sugg( - cx, - CHECKED_CONVERSIONS, - item.span, - "checked cast can be simplified", - "try", - format!("{to_type}::try_from({snippet}).is_ok()"), - applicability, - ); + && !in_external_macro(cx.sess(), item.span) + && !in_constant(cx, item.hir_id) + && self.msrv.meets(msrvs::TRY_FROM) + && let Some(cv) = match op2 { + // todo: check for case signed -> larger unsigned == only x >= 0 + None => check_upper_bound(lt1, gt1).filter(|cv| cv.cvt == ConversionType::FromUnsigned), + Some((lt2, gt2)) => { + let upper_lower = |lt1, gt1, lt2, gt2| { + check_upper_bound(lt1, gt1) + .zip(check_lower_bound(lt2, gt2)) + .and_then(|(l, r)| l.combine(r, cx)) + }; + upper_lower(lt1, gt1, lt2, gt2).or_else(|| upper_lower(lt2, gt2, lt1, gt1)) + }, } + && let Some(to_type) = cv.to_type + { + let mut applicability = Applicability::MachineApplicable; + let snippet = snippet_with_applicability(cx, cv.expr_to_cast.span, "_", &mut applicability); + span_lint_and_sugg( + cx, + CHECKED_CONVERSIONS, + item.span, + "checked cast can be simplified", + "try", + format!("{to_type}::try_from({snippet}).is_ok()"), + applicability, + ); } } extract_msrv_attr!(LateContext); } -/// Searches for a single check from unsigned to _ is done -/// todo: check for case signed -> larger unsigned == only x >= 0 -fn single_check<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<Conversion<'tcx>> { - check_upper_bound(expr).filter(|cv| cv.cvt == ConversionType::FromUnsigned) -} - -/// Searches for a combination of upper & lower bound checks -fn double_check<'a>(cx: &LateContext<'_>, left: &'a Expr<'_>, right: &'a Expr<'_>) -> Option<Conversion<'a>> { - let upper_lower = |l, r| { - let upper = check_upper_bound(l); - let lower = check_lower_bound(r); - - upper.zip(lower).and_then(|(l, r)| l.combine(r, cx)) - }; - - upper_lower(left, right).or_else(|| upper_lower(right, left)) -} - /// Contains the result of a tried conversion check #[derive(Clone, Debug)] struct Conversion<'a> { @@ -121,6 +116,19 @@ enum ConversionType { FromUnsigned, } +/// Attempts to read either `<=` or `>=` with a normalized operand order. +fn read_le_ge<'tcx>( + op: BinOpKind, + lhs: &'tcx Expr<'tcx>, + rhs: &'tcx Expr<'tcx>, +) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> { + match op { + BinOpKind::Le => Some((lhs, rhs)), + BinOpKind::Ge => Some((rhs, lhs)), + _ => None, + } +} + impl<'a> Conversion<'a> { /// Combine multiple conversions if the are compatible pub fn combine(self, other: Self, cx: &LateContext<'_>) -> Option<Conversion<'a>> { @@ -188,29 +196,17 @@ impl ConversionType { } /// Check for `expr <= (to_type::MAX as from_type)` -fn check_upper_bound<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<Conversion<'tcx>> { - if let ExprKind::Binary(ref op, left, right) = &expr.kind - && let Some((candidate, check)) = normalize_le_ge(op, left, right) - && let Some((from, to)) = get_types_from_cast(check, INTS, "max_value", "MAX") - { - Conversion::try_new(candidate, from, to) +fn check_upper_bound<'tcx>(lt: &'tcx Expr<'tcx>, gt: &'tcx Expr<'tcx>) -> Option<Conversion<'tcx>> { + if let Some((from, to)) = get_types_from_cast(gt, INTS, "max_value", "MAX") { + Conversion::try_new(lt, from, to) } else { None } } /// Check for `expr >= 0|(to_type::MIN as from_type)` -fn check_lower_bound<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<Conversion<'tcx>> { - fn check_function<'a>(candidate: &'a Expr<'a>, check: &'a Expr<'a>) -> Option<Conversion<'a>> { - (check_lower_bound_zero(candidate, check)).or_else(|| (check_lower_bound_min(candidate, check))) - } - - // First of we need a binary containing the expression & the cast - if let ExprKind::Binary(ref op, left, right) = &expr.kind { - normalize_le_ge(op, right, left).and_then(|(l, r)| check_function(l, r)) - } else { - None - } +fn check_lower_bound<'tcx>(lt: &'tcx Expr<'tcx>, gt: &'tcx Expr<'tcx>) -> Option<Conversion<'tcx>> { + check_lower_bound_zero(gt, lt).or_else(|| check_lower_bound_min(gt, lt)) } /// Check for `expr >= 0` @@ -309,15 +305,6 @@ fn int_ty_to_sym<'tcx>(path: &QPath<'_>) -> Option<&'tcx str> { } } -/// Will return the expressions as if they were expr1 <= expr2 -fn normalize_le_ge<'a>(op: &BinOp, left: &'a Expr<'a>, right: &'a Expr<'a>) -> Option<(&'a Expr<'a>, &'a Expr<'a>)> { - match op.node { - BinOpKind::Le => Some((left, right)), - BinOpKind::Ge => Some((right, left)), - _ => None, - } -} - // Constants const UINTS: &[&str] = &["u8", "u16", "u32", "u64", "usize"]; const SINTS: &[&str] = &["i8", "i16", "i32", "i64", "isize"]; diff --git a/clippy_lints/src/cognitive_complexity.rs b/clippy_lints/src/cognitive_complexity.rs index 60815f4f2af..5fa0522e4e5 100644 --- a/clippy_lints/src/cognitive_complexity.rs +++ b/clippy_lints/src/cognitive_complexity.rs @@ -1,5 +1,6 @@ //! calculate cognitive complexity and warn about overly complex functions +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::{IntoSpan, SpanRangeExt}; use clippy_utils::ty::is_type_diagnostic_item; @@ -39,10 +40,9 @@ pub struct CognitiveComplexity { } impl CognitiveComplexity { - #[must_use] - pub fn new(limit: u64) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - limit: LimitStack::new(limit), + limit: LimitStack::new(conf.cognitive_complexity_threshold), } } } diff --git a/clippy_lints/src/collapsible_if.rs b/clippy_lints/src/collapsible_if.rs index 07b02c98df1..f311c052ad6 100644 --- a/clippy_lints/src/collapsible_if.rs +++ b/clippy_lints/src/collapsible_if.rs @@ -93,20 +93,14 @@ declare_lint_pass!(CollapsibleIf => [COLLAPSIBLE_IF, COLLAPSIBLE_ELSE_IF]); impl EarlyLintPass for CollapsibleIf { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) { - if !expr.span.from_expansion() { - check_if(cx, expr); - } - } -} - -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, then.span, else_); - } else if let ast::ExprKind::Let(..) = check.kind { - // Prevent triggering on `if let a = b { if c { .. } }`. - } else { - check_collapsible_no_if_let(cx, expr, check, then); + if let ast::ExprKind::If(cond, then, else_) = &expr.kind + && !expr.span.from_expansion() + { + if let Some(else_) = else_ { + check_collapsible_maybe_if_let(cx, then.span, else_); + } else if !matches!(cond.kind, ast::ExprKind::Let(..)) { + check_collapsible_no_if_let(cx, expr, cond, then); + } } } } @@ -189,13 +183,10 @@ fn check_collapsible_no_if_let(cx: &EarlyContext<'_>, expr: &ast::Expr, check: & /// If the block contains only one expression, return it. fn expr_block(block: &ast::Block) -> Option<&ast::Expr> { - let mut it = block.stmts.iter(); - - if let (Some(stmt), None) = (it.next(), it.next()) { - match stmt.kind { - ast::StmtKind::Expr(ref expr) | ast::StmtKind::Semi(ref expr) => Some(expr), - _ => None, - } + if let [stmt] = &*block.stmts + && let ast::StmtKind::Expr(expr) | ast::StmtKind::Semi(expr) = &stmt.kind + { + Some(expr) } else { None } diff --git a/clippy_lints/src/collection_is_never_read.rs b/clippy_lints/src/collection_is_never_read.rs index 28d9f68d504..eebda3ff76f 100644 --- a/clippy_lints/src/collection_is_never_read.rs +++ b/clippy_lints/src/collection_is_never_read.rs @@ -59,9 +59,9 @@ static COLLECTIONS: [Symbol; 9] = [ impl<'tcx> LateLintPass<'tcx> for CollectionIsNeverRead { fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx LetStmt<'tcx>) { - // Look for local variables whose type is a container. Search surrounding bock for read access. - if match_acceptable_type(cx, local, &COLLECTIONS) - && let PatKind::Binding(_, local_id, _, _) = local.pat.kind + // Look for local variables whose type is a container. Search surrounding block for read access. + if let PatKind::Binding(_, local_id, _, _) = local.pat.kind + && match_acceptable_type(cx, local, &COLLECTIONS) && let Some(enclosing_block) = get_enclosing_block(cx, local.hir_id) && has_no_read_access(cx, local_id, enclosing_block) { diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index d896452be92..86e0368c4e4 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -1,3 +1,4 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then}; use clippy_utils::source::{first_line_of_span, indent_of, reindent_multiline, snippet, IntoSpan, SpanRangeExt}; use clippy_utils::ty::{needs_ordered_drop, InteriorMut}; @@ -11,6 +12,7 @@ use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::{intravisit, BinOpKind, Block, Expr, ExprKind, HirId, HirIdSet, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::TyCtxt; use rustc_session::impl_lint_pass; use rustc_span::hygiene::walk_chain; use rustc_span::source_map::SourceMap; @@ -159,15 +161,13 @@ declare_clippy_lint! { } pub struct CopyAndPaste<'tcx> { - ignore_interior_mutability: Vec<String>, interior_mut: InteriorMut<'tcx>, } -impl CopyAndPaste<'_> { - pub fn new(ignore_interior_mutability: Vec<String>) -> Self { +impl<'tcx> CopyAndPaste<'tcx> { + pub fn new(tcx: TyCtxt<'tcx>, conf: &'static Conf) -> Self { Self { - ignore_interior_mutability, - interior_mut: InteriorMut::default(), + interior_mut: InteriorMut::new(tcx, &conf.ignore_interior_mutability), } } } @@ -180,10 +180,6 @@ impl_lint_pass!(CopyAndPaste<'_> => [ ]); impl<'tcx> LateLintPass<'tcx> for CopyAndPaste<'tcx> { - fn check_crate(&mut self, cx: &LateContext<'tcx>) { - self.interior_mut = InteriorMut::new(cx, &self.ignore_interior_mutability); - } - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if !expr.span.from_expansion() && matches!(expr.kind, ExprKind::If(..)) && !is_else_clause(cx.tcx, expr) { let (conds, blocks) = if_sequence(expr); diff --git a/clippy_lints/src/crate_in_macro_def.rs b/clippy_lints/src/crate_in_macro_def.rs index adf6f7c4737..678bdbc0ffb 100644 --- a/clippy_lints/src/crate_in_macro_def.rs +++ b/clippy_lints/src/crate_in_macro_def.rs @@ -53,10 +53,9 @@ declare_lint_pass!(CrateInMacroDef => [CRATE_IN_MACRO_DEF]); impl EarlyLintPass for CrateInMacroDef { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - if item.attrs.iter().any(is_macro_export) - && let ItemKind::MacroDef(macro_def) = &item.kind - && let tts = macro_def.body.tokens.clone() - && let Some(span) = contains_unhygienic_crate_reference(&tts) + if let ItemKind::MacroDef(macro_def) = &item.kind + && item.attrs.iter().any(is_macro_export) + && let Some(span) = contains_unhygienic_crate_reference(¯o_def.body.tokens) { span_lint_and_sugg( cx, diff --git a/clippy_lints/src/dbg_macro.rs b/clippy_lints/src/dbg_macro.rs index b0590b0a71c..788c6f3ada2 100644 --- a/clippy_lints/src/dbg_macro.rs +++ b/clippy_lints/src/dbg_macro.rs @@ -1,3 +1,4 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::is_in_test; use clippy_utils::macros::{macro_backtrace, MacroCall}; @@ -33,7 +34,6 @@ declare_clippy_lint! { "`dbg!` macro is intended as a debugging tool" } -#[derive(Clone)] pub struct DbgMacro { allow_dbg_in_tests: bool, /// Tracks the `dbg!` macro callsites that are already checked. @@ -45,9 +45,9 @@ pub struct DbgMacro { impl_lint_pass!(DbgMacro => [DBG_MACRO]); impl DbgMacro { - pub fn new(allow_dbg_in_tests: bool) -> Self { + pub fn new(conf: &'static Conf) -> Self { DbgMacro { - allow_dbg_in_tests, + allow_dbg_in_tests: conf.allow_dbg_in_tests, checked_dbg_call_site: FxHashSet::default(), prev_ctxt: SyntaxContext::root(), } diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index eabc67601a2..69f9eb6842b 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -598,6 +598,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::partialeq_to_none::PARTIALEQ_TO_NONE_INFO, crate::pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE_INFO, crate::pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF_INFO, + crate::pathbuf_init_then_push::PATHBUF_INIT_THEN_PUSH_INFO, crate::pattern_type_mismatch::PATTERN_TYPE_MISMATCH_INFO, crate::permissions_set_readonly_false::PERMISSIONS_SET_READONLY_FALSE_INFO, crate::precedence::PRECEDENCE_INFO, diff --git a/clippy_lints/src/derivable_impls.rs b/clippy_lints/src/derivable_impls.rs index 0c9ad5e8d00..f27f68e2cbc 100644 --- a/clippy_lints/src/derivable_impls.rs +++ b/clippy_lints/src/derivable_impls.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::indent_of; use clippy_utils::{is_default_equivalent, peel_blocks}; @@ -60,9 +61,10 @@ pub struct DerivableImpls { } impl DerivableImpls { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - DerivableImpls { msrv } + pub fn new(conf: &'static Conf) -> Self { + DerivableImpls { + msrv: conf.msrv.clone(), + } } } diff --git a/clippy_lints/src/disallowed_macros.rs b/clippy_lints/src/disallowed_macros.rs index 871f529da6c..b51d343132b 100644 --- a/clippy_lints/src/disallowed_macros.rs +++ b/clippy_lints/src/disallowed_macros.rs @@ -1,4 +1,5 @@ -use clippy_config::types::DisallowedPath; +use clippy_config::Conf; +use clippy_utils::create_disallowed_map; use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::macros::macro_backtrace; use rustc_ast::Attribute; @@ -9,6 +10,7 @@ use rustc_hir::{ Expr, ExprKind, ForeignItem, HirId, ImplItem, Item, ItemKind, OwnerId, Pat, Path, Stmt, TraitItem, Ty, }; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::TyCtxt; use rustc_session::impl_lint_pass; use rustc_span::{ExpnId, MacroKind, Span}; @@ -57,27 +59,24 @@ declare_clippy_lint! { } pub struct DisallowedMacros { - conf_disallowed: Vec<DisallowedPath>, - disallowed: DefIdMap<usize>, + disallowed: DefIdMap<(&'static str, Option<&'static str>)>, seen: FxHashSet<ExpnId>, - // Track the most recently seen node that can have a `derive` attribute. // Needed to use the correct lint level. derive_src: Option<OwnerId>, } impl DisallowedMacros { - pub fn new(conf_disallowed: Vec<DisallowedPath>) -> Self { + pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { Self { - conf_disallowed, - disallowed: DefIdMap::default(), + disallowed: create_disallowed_map(tcx, &conf.disallowed_macros), seen: FxHashSet::default(), derive_src: None, } } fn check(&mut self, cx: &LateContext<'_>, span: Span, derive_src: Option<OwnerId>) { - if self.conf_disallowed.is_empty() { + if self.disallowed.is_empty() { return; } @@ -86,11 +85,10 @@ impl DisallowedMacros { return; } - if let Some(&index) = self.disallowed.get(&mac.def_id) { - let conf = &self.conf_disallowed[index]; - let msg = format!("use of a disallowed macro `{}`", conf.path()); + if let Some(&(path, reason)) = self.disallowed.get(&mac.def_id) { + let msg = format!("use of a disallowed macro `{path}`"); let add_note = |diag: &mut Diag<'_, _>| { - if let Some(reason) = conf.reason() { + if let Some(reason) = reason { diag.note(reason); } }; @@ -116,15 +114,6 @@ impl DisallowedMacros { impl_lint_pass!(DisallowedMacros => [DISALLOWED_MACROS]); impl LateLintPass<'_> for DisallowedMacros { - fn check_crate(&mut self, cx: &LateContext<'_>) { - for (index, conf) in self.conf_disallowed.iter().enumerate() { - let segs: Vec<_> = conf.path().split("::").collect(); - for id in clippy_utils::def_path_def_ids(cx, &segs) { - self.disallowed.insert(id, index); - } - } - } - fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { self.check(cx, expr.span, None); // `$t + $t` can have the context of $t, check also the span of the binary operator diff --git a/clippy_lints/src/disallowed_methods.rs b/clippy_lints/src/disallowed_methods.rs index 38fe687f7cc..5a01d76a2a6 100644 --- a/clippy_lints/src/disallowed_methods.rs +++ b/clippy_lints/src/disallowed_methods.rs @@ -1,9 +1,11 @@ -use clippy_config::types::DisallowedPath; +use clippy_config::Conf; +use clippy_utils::create_disallowed_map; use clippy_utils::diagnostics::span_lint_and_then; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefIdMap; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::TyCtxt; use rustc_session::impl_lint_pass; declare_clippy_lint! { @@ -55,17 +57,14 @@ declare_clippy_lint! { "use of a disallowed method call" } -#[derive(Clone, Debug)] pub struct DisallowedMethods { - conf_disallowed: Vec<DisallowedPath>, - disallowed: DefIdMap<usize>, + disallowed: DefIdMap<(&'static str, Option<&'static str>)>, } impl DisallowedMethods { - pub fn new(conf_disallowed: Vec<DisallowedPath>) -> Self { + pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { Self { - conf_disallowed, - disallowed: DefIdMap::default(), + disallowed: create_disallowed_map(tcx, &conf.disallowed_methods), } } } @@ -73,15 +72,6 @@ impl DisallowedMethods { impl_lint_pass!(DisallowedMethods => [DISALLOWED_METHODS]); impl<'tcx> LateLintPass<'tcx> for DisallowedMethods { - fn check_crate(&mut self, cx: &LateContext<'_>) { - for (index, conf) in self.conf_disallowed.iter().enumerate() { - let segs: Vec<_> = conf.path().split("::").collect(); - for id in clippy_utils::def_path_def_ids(cx, &segs) { - self.disallowed.insert(id, index); - } - } - } - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { let (id, span) = match &expr.kind { ExprKind::Path(path) @@ -95,14 +85,18 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethods { }, _ => return, }; - if let Some(&index) = self.disallowed.get(&id) { - let conf = &self.conf_disallowed[index]; - let msg = format!("use of a disallowed method `{}`", conf.path()); - span_lint_and_then(cx, DISALLOWED_METHODS, span, msg, |diag| { - if let Some(reason) = conf.reason() { - diag.note(reason); - } - }); + if let Some(&(path, reason)) = self.disallowed.get(&id) { + span_lint_and_then( + cx, + DISALLOWED_METHODS, + span, + format!("use of a disallowed method `{path}`"), + |diag| { + if let Some(reason) = reason { + diag.note(reason); + } + }, + ); } } } diff --git a/clippy_lints/src/disallowed_names.rs b/clippy_lints/src/disallowed_names.rs index 58809604c37..f55b0cf1c50 100644 --- a/clippy_lints/src/disallowed_names.rs +++ b/clippy_lints/src/disallowed_names.rs @@ -1,9 +1,11 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint; use clippy_utils::is_in_test; use rustc_data_structures::fx::FxHashSet; use rustc_hir::{Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; +use rustc_span::Symbol; declare_clippy_lint! { /// ### What it does @@ -24,15 +26,14 @@ declare_clippy_lint! { "usage of a disallowed/placeholder name" } -#[derive(Clone, Debug)] pub struct DisallowedNames { - disallow: FxHashSet<String>, + disallow: FxHashSet<Symbol>, } impl DisallowedNames { - pub fn new(disallowed_names: &[String]) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - disallow: disallowed_names.iter().cloned().collect(), + disallow: conf.disallowed_names.iter().map(|x| Symbol::intern(x)).collect(), } } } @@ -42,7 +43,7 @@ impl_lint_pass!(DisallowedNames => [DISALLOWED_NAMES]); impl<'tcx> LateLintPass<'tcx> for DisallowedNames { fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) { if let PatKind::Binding(.., ident, _) = pat.kind - && self.disallow.contains(&ident.name.to_string()) + && self.disallow.contains(&ident.name) && !is_in_test(cx.tcx, pat.hir_id) { span_lint( diff --git a/clippy_lints/src/disallowed_script_idents.rs b/clippy_lints/src/disallowed_script_idents.rs index 5ce11900adf..f79264e6b04 100644 --- a/clippy_lints/src/disallowed_script_idents.rs +++ b/clippy_lints/src/disallowed_script_idents.rs @@ -1,3 +1,4 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint; use rustc_ast::ast; use rustc_data_structures::fx::FxHashSet; @@ -44,19 +45,20 @@ declare_clippy_lint! { "usage of non-allowed Unicode scripts" } -#[derive(Clone, Debug)] pub struct DisallowedScriptIdents { whitelist: FxHashSet<Script>, } impl DisallowedScriptIdents { - pub fn new(whitelist: &[String]) -> Self { - let whitelist = whitelist - .iter() - .map(String::as_str) - .filter_map(Script::from_full_name) - .collect(); - Self { whitelist } + pub fn new(conf: &'static Conf) -> Self { + Self { + whitelist: conf + .allowed_scripts + .iter() + .map(String::as_str) + .filter_map(Script::from_full_name) + .collect(), + } } } diff --git a/clippy_lints/src/disallowed_types.rs b/clippy_lints/src/disallowed_types.rs index 4196309a22a..3265404f2b2 100644 --- a/clippy_lints/src/disallowed_types.rs +++ b/clippy_lints/src/disallowed_types.rs @@ -1,10 +1,11 @@ -use clippy_config::types::DisallowedPath; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::Res; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::DefIdMap; use rustc_hir::{Item, ItemKind, PolyTraitRef, PrimTy, Ty, TyKind, UseKind}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::TyCtxt; use rustc_session::impl_lint_pass; use rustc_span::Span; @@ -49,60 +50,56 @@ declare_clippy_lint! { "use of disallowed types" } -#[derive(Clone, Debug)] pub struct DisallowedTypes { - conf_disallowed: Vec<DisallowedPath>, - def_ids: FxHashMap<DefId, usize>, - prim_tys: FxHashMap<PrimTy, usize>, + def_ids: DefIdMap<(&'static str, Option<&'static str>)>, + prim_tys: FxHashMap<PrimTy, (&'static str, Option<&'static str>)>, } impl DisallowedTypes { - pub fn new(conf_disallowed: Vec<DisallowedPath>) -> Self { - Self { - conf_disallowed, - def_ids: FxHashMap::default(), - prim_tys: FxHashMap::default(), + pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { + let mut def_ids = DefIdMap::default(); + let mut prim_tys = FxHashMap::default(); + for x in &conf.disallowed_types { + let path: Vec<_> = x.path().split("::").collect::<Vec<_>>(); + let reason = x.reason(); + for res in clippy_utils::def_path_res(tcx, &path) { + match res { + Res::Def(_, id) => { + def_ids.insert(id, (x.path(), reason)); + }, + Res::PrimTy(ty) => { + prim_tys.insert(ty, (x.path(), reason)); + }, + _ => {}, + } + } } + Self { def_ids, prim_tys } } fn check_res_emit(&self, cx: &LateContext<'_>, res: &Res, span: Span) { - match res { - Res::Def(_, did) => { - if let Some(&index) = self.def_ids.get(did) { - emit(cx, &cx.tcx.def_path_str(*did), span, &self.conf_disallowed[index]); - } - }, - Res::PrimTy(prim) => { - if let Some(&index) = self.prim_tys.get(prim) { - emit(cx, prim.name_str(), span, &self.conf_disallowed[index]); + let (path, reason) = match res { + Res::Def(_, did) if let Some(&x) = self.def_ids.get(did) => x, + Res::PrimTy(prim) if let Some(&x) = self.prim_tys.get(prim) => x, + _ => return, + }; + span_lint_and_then( + cx, + DISALLOWED_TYPES, + span, + format!("use of a disallowed type `{path}`"), + |diag| { + if let Some(reason) = reason { + diag.note(reason); } }, - _ => {}, - } + ); } } impl_lint_pass!(DisallowedTypes => [DISALLOWED_TYPES]); impl<'tcx> LateLintPass<'tcx> for DisallowedTypes { - fn check_crate(&mut self, cx: &LateContext<'_>) { - for (index, conf) in self.conf_disallowed.iter().enumerate() { - let segs: Vec<_> = conf.path().split("::").collect(); - - for res in clippy_utils::def_path_res(cx, &segs) { - match res { - Res::Def(_, id) => { - self.def_ids.insert(id, index); - }, - Res::PrimTy(ty) => { - self.prim_tys.insert(ty, index); - }, - _ => {}, - } - } - } - } - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { if let ItemKind::Use(path, UseKind::Single) = &item.kind { for res in &path.res { @@ -121,17 +118,3 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedTypes { self.check_res_emit(cx, &poly.trait_ref.path.res, poly.trait_ref.path.span); } } - -fn emit(cx: &LateContext<'_>, name: &str, span: Span, conf: &DisallowedPath) { - span_lint_and_then( - cx, - DISALLOWED_TYPES, - span, - format!("`{name}` is not allowed according to config"), - |diag| { - if let Some(reason) = conf.reason() { - diag.note(reason); - } - }, - ); -} diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index a2a1a51920f..5b6a5b08aa9 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -1,4 +1,5 @@ mod lazy_continuation; +use clippy_config::Conf; use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::macros::{is_panic, root_macro_call_first_node}; @@ -421,17 +422,16 @@ declare_clippy_lint! { "require every line of a paragraph to be indented and marked" } -#[derive(Clone)] pub struct Documentation { - valid_idents: FxHashSet<String>, + valid_idents: &'static FxHashSet<String>, check_private_items: bool, } impl Documentation { - pub fn new(valid_idents: &[String], check_private_items: bool) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - valid_idents: valid_idents.iter().cloned().collect(), - check_private_items, + valid_idents: &conf.doc_valid_idents, + check_private_items: conf.check_private_items, } } } @@ -452,7 +452,7 @@ impl_lint_pass!(Documentation => [ impl<'tcx> LateLintPass<'tcx> for Documentation { fn check_attributes(&mut self, cx: &LateContext<'tcx>, attrs: &'tcx [Attribute]) { - let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else { + let Some(headers) = check_attrs(cx, self.valid_idents, attrs) else { return; }; diff --git a/clippy_lints/src/double_parens.rs b/clippy_lints/src/double_parens.rs index b51bb7951b7..4dd8f01ee70 100644 --- a/clippy_lints/src/double_parens.rs +++ b/clippy_lints/src/double_parens.rs @@ -40,35 +40,29 @@ declare_lint_pass!(DoubleParens => [DOUBLE_PARENS]); impl EarlyLintPass for DoubleParens { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { - if expr.span.from_expansion() { - return; - } - - let msg: &str = "consider removing unnecessary double parentheses"; - - match expr.kind { - ExprKind::Paren(ref in_paren) => match in_paren.kind { - ExprKind::Paren(_) | ExprKind::Tup(_) => { - span_lint(cx, DOUBLE_PARENS, expr.span, msg); - }, - _ => {}, - }, - ExprKind::Call(_, ref params) => { - if params.len() == 1 { - let param = ¶ms[0]; - if let ExprKind::Paren(_) = param.kind { - span_lint(cx, DOUBLE_PARENS, param.span, msg); - } - } + let span = match &expr.kind { + ExprKind::Paren(in_paren) if matches!(in_paren.kind, ExprKind::Paren(_) | ExprKind::Tup(_)) => expr.span, + ExprKind::Call(_, params) + if let [param] = &**params + && let ExprKind::Paren(_) = param.kind => + { + param.span }, - ExprKind::MethodCall(ref call) => { - if let [ref arg] = call.args[..] { - if let ExprKind::Paren(_) = arg.kind { - span_lint(cx, DOUBLE_PARENS, arg.span, msg); - } - } + ExprKind::MethodCall(call) + if let [arg] = &*call.args + && let ExprKind::Paren(_) = arg.kind => + { + arg.span }, - _ => {}, + _ => return, + }; + if !expr.span.from_expansion() { + span_lint( + cx, + DOUBLE_PARENS, + span, + "consider removing unnecessary double parentheses", + ); } } } diff --git a/clippy_lints/src/else_if_without_else.rs b/clippy_lints/src/else_if_without_else.rs index bb6f9aac223..7a6dc469727 100644 --- a/clippy_lints/src/else_if_without_else.rs +++ b/clippy_lints/src/else_if_without_else.rs @@ -50,12 +50,9 @@ declare_lint_pass!(ElseIfWithoutElse => [ELSE_IF_WITHOUT_ELSE]); impl EarlyLintPass for ElseIfWithoutElse { fn check_expr(&mut self, cx: &EarlyContext<'_>, item: &Expr) { - if in_external_macro(cx.sess(), item.span) { - return; - } - if let ExprKind::If(_, _, Some(ref els)) = item.kind && let ExprKind::If(_, _, None) = els.kind + && !in_external_macro(cx.sess(), item.span) { span_lint_and_help( cx, diff --git a/clippy_lints/src/empty_enum.rs b/clippy_lints/src/empty_enum.rs index d16714695cb..1869faab1d3 100644 --- a/clippy_lints/src/empty_enum.rs +++ b/clippy_lints/src/empty_enum.rs @@ -64,25 +64,21 @@ declare_lint_pass!(EmptyEnum => [EMPTY_ENUM]); impl<'tcx> LateLintPass<'tcx> for EmptyEnum { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { - // Only suggest the `never_type` if the feature is enabled - if !cx.tcx.features().never_type { - return; - } - - if let ItemKind::Enum(..) = item.kind { - let ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); - let adt = ty.ty_adt_def().expect("already checked whether this is an enum"); - if adt.variants().is_empty() { - span_lint_and_help( - cx, - EMPTY_ENUM, - item.span, - "enum with no variants", - None, - "consider using the uninhabited type `!` (never type) or a wrapper \ - around it to introduce a type which can't be instantiated", - ); - } + if let ItemKind::Enum(..) = item.kind + // Only suggest the `never_type` if the feature is enabled + && cx.tcx.features().never_type + && let Some(adt) = cx.tcx.type_of(item.owner_id).instantiate_identity().ty_adt_def() + && adt.variants().is_empty() + { + span_lint_and_help( + cx, + EMPTY_ENUM, + item.span, + "enum with no variants", + None, + "consider using the uninhabited type `!` (never type) or a wrapper \ + around it to introduce a type which can't be instantiated", + ); } } } diff --git a/clippy_lints/src/endian_bytes.rs b/clippy_lints/src/endian_bytes.rs index 99328e3e643..5bba9c562b9 100644 --- a/clippy_lints/src/endian_bytes.rs +++ b/clippy_lints/src/endian_bytes.rs @@ -109,32 +109,27 @@ impl LintKind { impl LateLintPass<'_> for EndianBytes { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - if in_external_macro(cx.sess(), expr.span) { - return; - } - - if let ExprKind::MethodCall(method_name, receiver, args, ..) = expr.kind - && args.is_empty() - && let ty = cx.typeck_results().expr_ty(receiver) + let (prefix, name, ty_expr) = match expr.kind { + ExprKind::MethodCall(method_name, receiver, [], ..) => (Prefix::To, method_name.ident.name, receiver), + ExprKind::Call(function, ..) + if let ExprKind::Path(qpath) = function.kind + && let Some(def_id) = cx.qpath_res(&qpath, function.hir_id).opt_def_id() + && let Some(function_name) = cx.get_def_path(def_id).last() => + { + (Prefix::From, *function_name, expr) + }, + _ => return, + }; + if !in_external_macro(cx.sess(), expr.span) + && let ty = cx.typeck_results().expr_ty(ty_expr) && ty.is_primitive_ty() - && maybe_lint_endian_bytes(cx, expr, Prefix::To, method_name.ident.name, ty) { - return; - } - - if let ExprKind::Call(function, ..) = expr.kind - && let ExprKind::Path(qpath) = function.kind - && let Some(def_id) = cx.qpath_res(&qpath, function.hir_id).opt_def_id() - && let Some(function_name) = cx.get_def_path(def_id).last() - && let ty = cx.typeck_results().expr_ty(expr) - && ty.is_primitive_ty() - { - maybe_lint_endian_bytes(cx, expr, Prefix::From, *function_name, ty); + maybe_lint_endian_bytes(cx, expr, prefix, name, ty); } } } -fn maybe_lint_endian_bytes(cx: &LateContext<'_>, expr: &Expr<'_>, prefix: Prefix, name: Symbol, ty: Ty<'_>) -> bool { +fn maybe_lint_endian_bytes(cx: &LateContext<'_>, expr: &Expr<'_>, prefix: Prefix, name: Symbol, ty: Ty<'_>) { let ne = LintKind::Host.as_name(prefix); let le = LintKind::Little.as_name(prefix); let be = LintKind::Big.as_name(prefix); @@ -143,7 +138,7 @@ fn maybe_lint_endian_bytes(cx: &LateContext<'_>, expr: &Expr<'_>, prefix: Prefix name if name == ne => ((&LintKind::Host), [(&LintKind::Little), (&LintKind::Big)]), name if name == le => ((&LintKind::Little), [(&LintKind::Host), (&LintKind::Big)]), name if name == be => ((&LintKind::Big), [(&LintKind::Host), (&LintKind::Little)]), - _ => return false, + _ => return, }; let mut help = None; @@ -208,6 +203,4 @@ fn maybe_lint_endian_bytes(cx: &LateContext<'_>, expr: &Expr<'_>, prefix: Prefix } }, ); - - true } diff --git a/clippy_lints/src/equatable_if_let.rs b/clippy_lints/src/equatable_if_let.rs index 37442bf3e28..fb9f2b1526e 100644 --- a/clippy_lints/src/equatable_if_let.rs +++ b/clippy_lints/src/equatable_if_let.rs @@ -70,9 +70,9 @@ fn is_structural_partial_eq<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, other: T impl<'tcx> LateLintPass<'tcx> for PatternEquality { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if !in_external_macro(cx.sess(), expr.span) - && let ExprKind::Let(let_expr) = expr.kind + if let ExprKind::Let(let_expr) = expr.kind && unary_pattern(let_expr.pat) + && !in_external_macro(cx.sess(), expr.span) { let exp_ty = cx.typeck_results().expr_ty(let_expr.init); let pat_ty = cx.typeck_results().pat_ty(let_expr.pat); diff --git a/clippy_lints/src/error_impl_error.rs b/clippy_lints/src/error_impl_error.rs index 8e49138cd26..1e6447dc253 100644 --- a/clippy_lints/src/error_impl_error.rs +++ b/clippy_lints/src/error_impl_error.rs @@ -36,15 +36,12 @@ declare_lint_pass!(ErrorImplError => [ERROR_IMPL_ERROR]); impl<'tcx> LateLintPass<'tcx> for ErrorImplError { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { - let Some(error_def_id) = cx.tcx.get_diagnostic_item(sym::Error) else { - return; - }; - match item.kind { ItemKind::TyAlias(..) if item.ident.name == sym::Error && is_visible_outside_module(cx, item.owner_id.def_id) && let ty = cx.tcx.type_of(item.owner_id).instantiate_identity() + && let Some(error_def_id) = cx.tcx.get_diagnostic_item(sym::Error) && implements_trait(cx, ty, error_def_id, &[]) => { span_lint( @@ -56,9 +53,9 @@ impl<'tcx> LateLintPass<'tcx> for ErrorImplError { }, ItemKind::Impl(imp) if let Some(trait_def_id) = imp.of_trait.and_then(|t| t.trait_def_id()) + && let Some(error_def_id) = cx.tcx.get_diagnostic_item(sym::Error) && error_def_id == trait_def_id && let Some(def_id) = path_res(cx, imp.self_ty).opt_def_id().and_then(DefId::as_local) - && let hir_id = cx.tcx.local_def_id_to_hir_id(def_id) && let Some(ident) = cx.tcx.opt_item_ident(def_id.to_def_id()) && ident.name == sym::Error && is_visible_outside_module(cx, def_id) => @@ -66,7 +63,7 @@ impl<'tcx> LateLintPass<'tcx> for ErrorImplError { span_lint_hir_and_then( cx, ERROR_IMPL_ERROR, - hir_id, + cx.tcx.local_def_id_to_hir_id(def_id), ident.span, "exported type named `Error` that implements `Error`", |diag| { diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index 8d6e27700d8..a5da52b0be5 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -1,3 +1,4 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_hir; use rustc_hir::{intravisit, AssocItemKind, Body, FnDecl, HirId, HirIdSet, Impl, ItemKind, Node, Pat, PatKind}; use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; @@ -11,9 +12,16 @@ use rustc_span::symbol::kw; use rustc_span::Span; use rustc_target::spec::abi::Abi; -#[derive(Copy, Clone)] pub struct BoxedLocal { - pub too_large_for_stack: u64, + too_large_for_stack: u64, +} + +impl BoxedLocal { + pub fn new(conf: &'static Conf) -> Self { + Self { + too_large_for_stack: conf.too_large_for_stack, + } + } } declare_clippy_lint! { diff --git a/clippy_lints/src/excessive_bools.rs b/clippy_lints/src/excessive_bools.rs index 62d5ce24d40..8f469efb1b5 100644 --- a/clippy_lints/src/excessive_bools.rs +++ b/clippy_lints/src/excessive_bools.rs @@ -1,3 +1,4 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::{get_parent_as_impl, has_repr_attr, is_bool}; use rustc_hir::intravisit::FnKind; @@ -87,69 +88,57 @@ pub struct ExcessiveBools { max_fn_params_bools: u64, } -#[derive(Eq, PartialEq, Debug, Copy, Clone)] -enum Kind { - Struct, - Fn, -} - impl ExcessiveBools { - #[must_use] - pub fn new(max_struct_bools: u64, max_fn_params_bools: u64) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - max_struct_bools, - max_fn_params_bools, + max_struct_bools: conf.max_struct_bools, + max_fn_params_bools: conf.max_fn_params_bools, } } +} - fn too_many_bools<'tcx>(&self, tys: impl Iterator<Item = &'tcx Ty<'tcx>>, kind: Kind) -> bool { - if let Ok(bools) = tys.filter(|ty| is_bool(ty)).count().try_into() { - (if Kind::Fn == kind { - self.max_fn_params_bools - } else { - self.max_struct_bools - }) < bools - } else { - false - } - } +impl_lint_pass!(ExcessiveBools => [STRUCT_EXCESSIVE_BOOLS, FN_PARAMS_EXCESSIVE_BOOLS]); - fn check_fn_sig(&self, cx: &LateContext<'_>, fn_decl: &FnDecl<'_>, span: Span) { - if !span.from_expansion() && self.too_many_bools(fn_decl.inputs.iter(), Kind::Fn) { - span_lint_and_help( - cx, - FN_PARAMS_EXCESSIVE_BOOLS, - span, - format!("more than {} bools in function parameters", self.max_fn_params_bools), - None, - "consider refactoring bools into two-variant enums", - ); - } - } +fn has_n_bools<'tcx>(iter: impl Iterator<Item = &'tcx Ty<'tcx>>, mut count: u64) -> bool { + iter.filter(|ty| is_bool(ty)).any(|_| { + let (x, overflow) = count.overflowing_sub(1); + count = x; + overflow + }) } -impl_lint_pass!(ExcessiveBools => [STRUCT_EXCESSIVE_BOOLS, FN_PARAMS_EXCESSIVE_BOOLS]); +fn check_fn_decl(cx: &LateContext<'_>, decl: &FnDecl<'_>, sp: Span, max: u64) { + if has_n_bools(decl.inputs.iter(), max) && !sp.from_expansion() { + span_lint_and_help( + cx, + FN_PARAMS_EXCESSIVE_BOOLS, + sp, + format!("more than {max} bools in function parameters"), + None, + "consider refactoring bools into two-variant enums", + ); + } +} impl<'tcx> LateLintPass<'tcx> for ExcessiveBools { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { - if item.span.from_expansion() { - return; - } - if let ItemKind::Struct(variant_data, _) = &item.kind { - if has_repr_attr(cx, item.hir_id()) { - return; - } - - if self.too_many_bools(variant_data.fields().iter().map(|field| field.ty), Kind::Struct) { - span_lint_and_help( - cx, - STRUCT_EXCESSIVE_BOOLS, - item.span, - format!("more than {} bools in a struct", self.max_struct_bools), - None, - "consider using a state machine or refactoring bools into two-variant enums", - ); - } + if let ItemKind::Struct(variant_data, _) = &item.kind + && variant_data.fields().len() as u64 > self.max_struct_bools + && has_n_bools( + variant_data.fields().iter().map(|field| field.ty), + self.max_struct_bools, + ) + && !has_repr_attr(cx, item.hir_id()) + && !item.span.from_expansion() + { + span_lint_and_help( + cx, + STRUCT_EXCESSIVE_BOOLS, + item.span, + format!("more than {} bools in a struct", self.max_struct_bools), + None, + "consider using a state machine or refactoring bools into two-variant enums", + ); } } @@ -157,8 +146,9 @@ impl<'tcx> LateLintPass<'tcx> for ExcessiveBools { // functions with a body are already checked by `check_fn` if let TraitItemKind::Fn(fn_sig, TraitFn::Required(_)) = &trait_item.kind && fn_sig.header.abi == Abi::Rust + && fn_sig.decl.inputs.len() as u64 > self.max_fn_params_bools { - self.check_fn_sig(cx, fn_sig.decl, fn_sig.span); + check_fn_decl(cx, fn_sig.decl, fn_sig.span, self.max_fn_params_bools); } } @@ -171,12 +161,13 @@ impl<'tcx> LateLintPass<'tcx> for ExcessiveBools { span: Span, def_id: LocalDefId, ) { - let hir_id = cx.tcx.local_def_id_to_hir_id(def_id); if let Some(fn_header) = fn_kind.header() && fn_header.abi == Abi::Rust - && get_parent_as_impl(cx.tcx, hir_id).map_or(true, |impl_item| impl_item.of_trait.is_none()) + && fn_decl.inputs.len() as u64 > self.max_fn_params_bools + && get_parent_as_impl(cx.tcx, cx.tcx.local_def_id_to_hir_id(def_id)) + .map_or(true, |impl_item| impl_item.of_trait.is_none()) { - self.check_fn_sig(cx, fn_decl, span); + check_fn_decl(cx, fn_decl, span, self.max_fn_params_bools); } } } diff --git a/clippy_lints/src/excessive_nesting.rs b/clippy_lints/src/excessive_nesting.rs index 4b0d11c5d1b..5154edd4399 100644 --- a/clippy_lints/src/excessive_nesting.rs +++ b/clippy_lints/src/excessive_nesting.rs @@ -1,3 +1,4 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::snippet; use rustc_ast::node_id::NodeSet; @@ -63,13 +64,19 @@ declare_clippy_lint! { } impl_lint_pass!(ExcessiveNesting => [EXCESSIVE_NESTING]); -#[derive(Clone)] pub struct ExcessiveNesting { pub excessive_nesting_threshold: u64, pub nodes: NodeSet, } impl ExcessiveNesting { + pub fn new(conf: &'static Conf) -> Self { + Self { + excessive_nesting_threshold: conf.excessive_nesting_threshold, + nodes: NodeSet::default(), + } + } + pub fn check_node_id(&self, cx: &EarlyContext<'_>, span: Span, node_id: NodeId) { if self.nodes.contains(&node_id) { span_lint_and_help( diff --git a/clippy_lints/src/exhaustive_items.rs b/clippy_lints/src/exhaustive_items.rs index 436dc8611bd..0f4176ec73b 100644 --- a/clippy_lints/src/exhaustive_items.rs +++ b/clippy_lints/src/exhaustive_items.rs @@ -70,20 +70,24 @@ declare_lint_pass!(ExhaustiveItems => [EXHAUSTIVE_ENUMS, EXHAUSTIVE_STRUCTS]); impl LateLintPass<'_> for ExhaustiveItems { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { - if let ItemKind::Enum(..) | ItemKind::Struct(..) = item.kind - && cx.effective_visibilities.is_exported(item.owner_id.def_id) + let (lint, msg, fields) = match item.kind { + ItemKind::Enum(..) => ( + EXHAUSTIVE_ENUMS, + "exported enums should not be exhaustive", + [].as_slice(), + ), + ItemKind::Struct(v, ..) => ( + EXHAUSTIVE_STRUCTS, + "exported structs should not be exhaustive", + v.fields(), + ), + _ => return, + }; + if cx.effective_visibilities.is_exported(item.owner_id.def_id) && let attrs = cx.tcx.hir().attrs(item.hir_id()) && !attrs.iter().any(|a| a.has_name(sym::non_exhaustive)) + && fields.iter().all(|f| cx.tcx.visibility(f.def_id).is_public()) { - let (lint, msg) = if let ItemKind::Struct(ref v, ..) = item.kind { - if v.fields().iter().any(|f| !cx.tcx.visibility(f.def_id).is_public()) { - // skip structs with private fields - return; - } - (EXHAUSTIVE_STRUCTS, "exported structs should not be exhaustive") - } else { - (EXHAUSTIVE_ENUMS, "exported enums should not be exhaustive") - }; let suggestion_span = item.span.shrink_to_lo(); let indent = " ".repeat(indent_of(cx, item.span).unwrap_or(0)); span_lint_and_then(cx, lint, item.span, msg, |diag| { diff --git a/clippy_lints/src/exit.rs b/clippy_lints/src/exit.rs index 91c94d66458..f37d11f7eb9 100644 --- a/clippy_lints/src/exit.rs +++ b/clippy_lints/src/exit.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::is_entrypoint_fn; -use rustc_hir::{Expr, ExprKind, Item, ItemKind, Node}; +use rustc_hir::{Expr, ExprKind, Item, ItemKind, OwnerNode}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::sym; @@ -47,8 +47,8 @@ impl<'tcx> LateLintPass<'tcx> for Exit { && let ExprKind::Path(ref path) = path_expr.kind && let Some(def_id) = cx.qpath_res(path, path_expr.hir_id).opt_def_id() && cx.tcx.is_diagnostic_item(sym::process_exit, def_id) - && let parent = cx.tcx.hir().get_parent_item(e.hir_id).def_id - && let Node::Item(Item{kind: ItemKind::Fn(..), ..}) = cx.tcx.hir_node_by_def_id(parent) + && let parent = cx.tcx.hir().get_parent_item(e.hir_id) + && let OwnerNode::Item(Item{kind: ItemKind::Fn(..), ..}) = cx.tcx.hir_owner_node(parent) // If the next item up is a function we check if it is an entry point // and only then emit a linter warning && !is_entrypoint_fn(cx, parent.to_def_id()) diff --git a/clippy_lints/src/extra_unused_type_parameters.rs b/clippy_lints/src/extra_unused_type_parameters.rs index 7484f772e08..bfe4e253ae4 100644 --- a/clippy_lints/src/extra_unused_type_parameters.rs +++ b/clippy_lints/src/extra_unused_type_parameters.rs @@ -1,3 +1,4 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; use clippy_utils::{is_from_proc_macro, trait_ref_of_method}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -45,26 +46,11 @@ pub struct ExtraUnusedTypeParameters { } impl ExtraUnusedTypeParameters { - pub fn new(avoid_breaking_exported_api: bool) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - avoid_breaking_exported_api, + avoid_breaking_exported_api: conf.avoid_breaking_exported_api, } } - - /// Don't lint external macros or functions with empty bodies. Also, don't lint exported items - /// if the `avoid_breaking_exported_api` config option is set. - fn is_empty_exported_or_macro( - &self, - cx: &LateContext<'_>, - span: Span, - def_id: LocalDefId, - body_id: BodyId, - ) -> bool { - let body = cx.tcx.hir().body(body_id).value; - let fn_empty = matches!(&body.kind, ExprKind::Block(blk, None) if blk.stmts.is_empty() && blk.expr.is_none()); - let is_exported = cx.effective_visibilities.is_exported(def_id); - in_external_macro(cx.sess(), span) || fn_empty || (is_exported && self.avoid_breaking_exported_api) - } } impl_lint_pass!(ExtraUnusedTypeParameters => [EXTRA_UNUSED_TYPE_PARAMETERS]); @@ -266,10 +252,17 @@ impl<'cx, 'tcx> Visitor<'tcx> for TypeWalker<'cx, 'tcx> { } } +fn is_empty_body(cx: &LateContext<'_>, body: BodyId) -> bool { + matches!(cx.tcx.hir().body(body).value.kind, ExprKind::Block(b, _) if b.stmts.is_empty() && b.expr.is_none()) +} + impl<'tcx> LateLintPass<'tcx> for ExtraUnusedTypeParameters { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { if let ItemKind::Fn(_, generics, body_id) = item.kind - && !self.is_empty_exported_or_macro(cx, item.span, item.owner_id.def_id, body_id) + && !generics.params.is_empty() + && !is_empty_body(cx, body_id) + && (!self.avoid_breaking_exported_api || !cx.effective_visibilities.is_exported(item.owner_id.def_id)) + && !in_external_macro(cx.sess(), item.span) && !is_from_proc_macro(cx, item) { let mut walker = TypeWalker::new(cx, generics); @@ -281,8 +274,12 @@ impl<'tcx> LateLintPass<'tcx> for ExtraUnusedTypeParameters { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'tcx>) { // Only lint on inherent methods, not trait methods. if let ImplItemKind::Fn(.., body_id) = item.kind + && !item.generics.params.is_empty() && trait_ref_of_method(cx, item.owner_id.def_id).is_none() - && !self.is_empty_exported_or_macro(cx, item.span, item.owner_id.def_id, body_id) + && !is_empty_body(cx, body_id) + && (!self.avoid_breaking_exported_api || !cx.effective_visibilities.is_exported(item.owner_id.def_id)) + && !in_external_macro(cx.sess(), item.span) + && !is_from_proc_macro(cx, item) { let mut walker = TypeWalker::new(cx, item.generics); walk_impl_item(&mut walker, item); diff --git a/clippy_lints/src/float_literal.rs b/clippy_lints/src/float_literal.rs index 2261fcdbdab..6adcd2235dc 100644 --- a/clippy_lints/src/float_literal.rs +++ b/clippy_lints/src/float_literal.rs @@ -62,10 +62,9 @@ declare_lint_pass!(FloatLiteral => [EXCESSIVE_PRECISION, LOSSY_FLOAT_LITERAL]); impl<'tcx> LateLintPass<'tcx> for FloatLiteral { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - let ty = cx.typeck_results().expr_ty(expr); - if let ty::Float(fty) = *ty.kind() - && let hir::ExprKind::Lit(lit) = expr.kind + if let hir::ExprKind::Lit(lit) = expr.kind && let LitKind::Float(sym, lit_float_ty) = lit.node + && let ty::Float(fty) = *cx.typeck_results().expr_ty(expr).kind() { let sym_str = sym.as_str(); let formatter = FloatFormat::new(sym_str); diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index 99def199af0..a31d5cb6ec7 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -1,5 +1,6 @@ use arrayvec::ArrayVec; use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::is_diag_trait_item; use clippy_utils::macros::{ @@ -175,12 +176,11 @@ pub struct FormatArgs { } impl FormatArgs { - #[must_use] - pub fn new(format_args: FormatArgsStorage, msrv: Msrv, allow_mixed_uninlined_format_args: bool) -> Self { + pub fn new(conf: &'static Conf, format_args: FormatArgsStorage) -> Self { Self { format_args, - msrv, - ignore_mixed: allow_mixed_uninlined_format_args, + msrv: conf.msrv.clone(), + ignore_mixed: conf.allow_mixed_uninlined_format_args, } } } diff --git a/clippy_lints/src/format_impl.rs b/clippy_lints/src/format_impl.rs index 09be7237b5c..e6f27cb82d1 100644 --- a/clippy_lints/src/format_impl.rs +++ b/clippy_lints/src/format_impl.rs @@ -97,7 +97,6 @@ struct FormatTraitNames { formatter_name: Option<Symbol>, } -#[derive(Default)] pub struct FormatImpl { format_args: FormatArgsStorage, // Whether we are inside Display or Debug trait impl - None for neither diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs index 1933a00891b..b6c8437fb8f 100644 --- a/clippy_lints/src/from_over_into.rs +++ b/clippy_lints/src/from_over_into.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::span_is_local; use clippy_utils::path_def_id; @@ -54,9 +55,10 @@ pub struct FromOverInto { } impl FromOverInto { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - FromOverInto { msrv } + pub fn new(conf: &'static Conf) -> Self { + FromOverInto { + msrv: conf.msrv.clone(), + } } } @@ -64,10 +66,6 @@ impl_lint_pass!(FromOverInto => [FROM_OVER_INTO]); impl<'tcx> LateLintPass<'tcx> for FromOverInto { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - if !self.msrv.meets(msrvs::RE_REBALANCING_COHERENCE) || !span_is_local(item.span) { - return; - } - if let ItemKind::Impl(Impl { of_trait: Some(hir_trait_ref), self_ty, @@ -77,6 +75,8 @@ impl<'tcx> LateLintPass<'tcx> for FromOverInto { && let Some(into_trait_seg) = hir_trait_ref.path.segments.last() // `impl Into<target_ty> for self_ty` && let Some(GenericArgs { args: [GenericArg::Type(target_ty)], .. }) = into_trait_seg.args + && self.msrv.meets(msrvs::RE_REBALANCING_COHERENCE) + && span_is_local(item.span) && let Some(middle_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id) .map(ty::EarlyBinder::instantiate_identity) && cx.tcx.is_diagnostic_item(sym::Into, middle_trait_ref.def_id) @@ -181,6 +181,9 @@ fn convert_to_from( let from = snippet_opt(cx, self_ty.span)?; let into = snippet_opt(cx, target_ty.span)?; + let return_type = matches!(sig.decl.output, FnRetTy::Return(_)) + .then_some(String::from("Self")) + .unwrap_or_default(); let mut suggestions = vec![ // impl Into<T> for U -> impl From<T> for U // ~~~~ ~~~~ @@ -197,13 +200,10 @@ fn convert_to_from( // fn into([mut] self) -> T -> fn into([mut] v: T) -> T // ~~~~ ~~~~ (self_ident.span, format!("val: {from}")), - ]; - - if let FnRetTy::Return(_) = sig.decl.output { // fn into(self) -> T -> fn into(self) -> Self // ~ ~~~~ - suggestions.push((sig.decl.output.span(), String::from("Self"))); - } + (sig.decl.output.span(), return_type), + ]; let mut finder = SelfFinder { cx, diff --git a/clippy_lints/src/from_str_radix_10.rs b/clippy_lints/src/from_str_radix_10.rs index 82ce501bac5..9acb72b2e37 100644 --- a/clippy_lints/src/from_str_radix_10.rs +++ b/clippy_lints/src/from_str_radix_10.rs @@ -47,9 +47,13 @@ impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 { fn check_expr(&mut self, cx: &LateContext<'tcx>, exp: &Expr<'tcx>) { if let ExprKind::Call(maybe_path, [src, radix]) = &exp.kind && let ExprKind::Path(QPath::TypeRelative(ty, pathseg)) = &maybe_path.kind - // do not lint in constant context, because the suggestion won't work. - // NB: keep this check until a new `const_trait_impl` is available and stablized. - && !in_constant(cx, exp.hir_id) + + // check if the second argument is a primitive `10` + && is_integer_literal(radix, 10) + + // check if the second part of the path indeed calls the associated + // function `from_str_radix` + && pathseg.ident.name.as_str() == "from_str_radix" // check if the first part of the path is some integer primitive && let TyKind::Path(ty_qpath) = &ty.kind @@ -57,12 +61,9 @@ impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 { && let def::Res::PrimTy(prim_ty) = ty_res && matches!(prim_ty, PrimTy::Int(_) | PrimTy::Uint(_)) - // check if the second part of the path indeed calls the associated - // function `from_str_radix` - && pathseg.ident.name.as_str() == "from_str_radix" - - // check if the second argument is a primitive `10` - && is_integer_literal(radix, 10) + // do not lint in constant context, because the suggestion won't work. + // NB: keep this check until a new `const_trait_impl` is available and stablized. + && !in_constant(cx, exp.hir_id) { let expr = if let ExprKind::AddrOf(_, _, expr) = &src.kind { let ty = cx.typeck_results().expr_ty(expr); diff --git a/clippy_lints/src/functions/mod.rs b/clippy_lints/src/functions/mod.rs index 26534492ddd..0f48941783b 100644 --- a/clippy_lints/src/functions/mod.rs +++ b/clippy_lints/src/functions/mod.rs @@ -7,10 +7,12 @@ mod result; mod too_many_arguments; mod too_many_lines; +use clippy_config::Conf; use clippy_utils::def_path_def_ids; use rustc_hir as hir; use rustc_hir::intravisit; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::TyCtxt; use rustc_session::impl_lint_pass; use rustc_span::def_id::{DefIdSet, LocalDefId}; use rustc_span::Span; @@ -391,39 +393,34 @@ declare_clippy_lint! { /// } /// } /// ``` - #[clippy::version = "1.74.0"] + #[clippy::version = "1.80.0"] pub RENAMED_FUNCTION_PARAMS, restriction, "renamed function parameters in trait implementation" } -#[derive(Clone)] pub struct Functions { too_many_arguments_threshold: u64, too_many_lines_threshold: u64, large_error_threshold: u64, avoid_breaking_exported_api: bool, - allow_renamed_params_for: Vec<String>, /// A set of resolved `def_id` of traits that are configured to allow /// function params renaming. trait_ids: DefIdSet, } impl Functions { - pub fn new( - too_many_arguments_threshold: u64, - too_many_lines_threshold: u64, - large_error_threshold: u64, - avoid_breaking_exported_api: bool, - allow_renamed_params_for: Vec<String>, - ) -> Self { + pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { Self { - too_many_arguments_threshold, - too_many_lines_threshold, - large_error_threshold, - avoid_breaking_exported_api, - allow_renamed_params_for, - trait_ids: DefIdSet::default(), + too_many_arguments_threshold: conf.too_many_arguments_threshold, + too_many_lines_threshold: conf.too_many_lines_threshold, + large_error_threshold: conf.large_error_threshold, + avoid_breaking_exported_api: conf.avoid_breaking_exported_api, + trait_ids: conf + .allow_renamed_params_for + .iter() + .flat_map(|p| def_path_def_ids(tcx, &p.split("::").collect::<Vec<_>>())) + .collect(), } } } @@ -479,12 +476,4 @@ impl<'tcx> LateLintPass<'tcx> for Functions { result::check_trait_item(cx, item, self.large_error_threshold); impl_trait_in_params::check_trait_item(cx, item, self.avoid_breaking_exported_api); } - - fn check_crate(&mut self, cx: &LateContext<'tcx>) { - for path in &self.allow_renamed_params_for { - let path_segments: Vec<&str> = path.split("::").collect(); - let ids = def_path_def_ids(cx, &path_segments); - self.trait_ids.extend(ids); - } - } } diff --git a/clippy_lints/src/future_not_send.rs b/clippy_lints/src/future_not_send.rs index e6506709774..9488ba75686 100644 --- a/clippy_lints/src/future_not_send.rs +++ b/clippy_lints/src/future_not_send.rs @@ -66,15 +66,11 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend { let ret_ty = return_ty(cx, cx.tcx.local_def_id_to_hir_id(fn_def_id).expect_owner()); if let ty::Alias(ty::Opaque, AliasTy { def_id, args, .. }) = *ret_ty.kind() { let preds = cx.tcx.explicit_item_super_predicates(def_id); - let mut is_future = false; - for (p, _span) in preds.iter_instantiated_copied(cx.tcx, args) { - if let Some(trait_pred) = p.as_trait_clause() { - if Some(trait_pred.skip_binder().trait_ref.def_id) == cx.tcx.lang_items().future_trait() { - is_future = true; - break; - } - } - } + let is_future = preds.iter_instantiated_copied(cx.tcx, args).any(|(p, _)| { + p.as_trait_clause().is_some_and(|trait_pred| { + Some(trait_pred.skip_binder().trait_ref.def_id) == cx.tcx.lang_items().future_trait() + }) + }); if is_future { let send_trait = cx.tcx.get_diagnostic_item(sym::Send).unwrap(); let span = decl.output.span(); diff --git a/clippy_lints/src/if_let_mutex.rs b/clippy_lints/src/if_let_mutex.rs index a55836a972f..b38cc7b36a1 100644 --- a/clippy_lints/src/if_let_mutex.rs +++ b/clippy_lints/src/if_let_mutex.rs @@ -1,8 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{higher, SpanlessEq}; +use core::ops::ControlFlow; use rustc_errors::Diag; -use rustc_hir::intravisit::{self as visit, Visitor}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -44,8 +45,6 @@ declare_lint_pass!(IfLetMutex => [IF_LET_MUTEX]); impl<'tcx> LateLintPass<'tcx> for IfLetMutex { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - let mut arm_visit = ArmVisitor { found_mutex: None, cx }; - let mut op_visit = OppVisitor { found_mutex: None, cx }; if let Some(higher::IfLet { let_expr, if_then, @@ -53,12 +52,20 @@ impl<'tcx> LateLintPass<'tcx> for IfLetMutex { .. }) = higher::IfLet::hir(cx, expr) { - op_visit.visit_expr(let_expr); - if let Some(op_mutex) = op_visit.found_mutex { - arm_visit.visit_expr(if_then); - arm_visit.visit_expr(if_else); + let is_mutex_lock = |e: &'tcx Expr<'tcx>| { + if let Some(mutex) = is_mutex_lock_call(cx, e) { + ControlFlow::Break(mutex) + } else { + ControlFlow::Continue(()) + } + }; - if let Some(arm_mutex) = arm_visit.found_mutex_if_same_as(op_mutex) { + let op_mutex = for_each_expr_without_closures(let_expr, is_mutex_lock); + if let Some(op_mutex) = op_mutex { + let arm_mutex = for_each_expr_without_closures((if_then, if_else), is_mutex_lock); + if let Some(arm_mutex) = arm_mutex + && SpanlessEq::new(cx).eq_expr(op_mutex, arm_mutex) + { let diag = |diag: &mut Diag<'_, ()>| { diag.span_label( op_mutex.span, @@ -83,48 +90,6 @@ impl<'tcx> LateLintPass<'tcx> for IfLetMutex { } } -/// Checks if `Mutex::lock` is called in the `if let` expr. -pub struct OppVisitor<'a, 'tcx> { - found_mutex: Option<&'tcx Expr<'tcx>>, - cx: &'a LateContext<'tcx>, -} - -impl<'tcx> Visitor<'tcx> for OppVisitor<'_, 'tcx> { - fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if let Some(mutex) = is_mutex_lock_call(self.cx, expr) { - self.found_mutex = Some(mutex); - return; - } - visit::walk_expr(self, expr); - } -} - -/// Checks if `Mutex::lock` is called in any of the branches. -pub struct ArmVisitor<'a, 'tcx> { - found_mutex: Option<&'tcx Expr<'tcx>>, - cx: &'a LateContext<'tcx>, -} - -impl<'tcx> Visitor<'tcx> for ArmVisitor<'_, 'tcx> { - fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { - if let Some(mutex) = is_mutex_lock_call(self.cx, expr) { - self.found_mutex = Some(mutex); - return; - } - visit::walk_expr(self, expr); - } -} - -impl<'tcx, 'l> ArmVisitor<'tcx, 'l> { - fn found_mutex_if_same_as(&self, op_mutex: &Expr<'_>) -> Option<&Expr<'_>> { - self.found_mutex.and_then(|arm_mutex| { - SpanlessEq::new(self.cx) - .eq_expr(op_mutex, arm_mutex) - .then_some(arm_mutex) - }) - } -} - fn is_mutex_lock_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { if let ExprKind::MethodCall(path, self_arg, ..) = &expr.kind && path.ident.as_str() == "lock" diff --git a/clippy_lints/src/if_not_else.rs b/clippy_lints/src/if_not_else.rs index 4dc1ff83771..2f6daeeb90d 100644 --- a/clippy_lints/src/if_not_else.rs +++ b/clippy_lints/src/if_not_else.rs @@ -56,44 +56,33 @@ fn is_zero_const(expr: &Expr<'_>, cx: &LateContext<'_>) -> bool { } impl LateLintPass<'_> for IfNotElse { - fn check_expr(&mut self, cx: &LateContext<'_>, item: &Expr<'_>) { - // While loops will be desugared to ExprKind::If. This will cause the lint to fire. - // To fix this, return early if this span comes from a macro or desugaring. - if item.span.from_expansion() { - return; - } - if let ExprKind::If(cond, _, Some(els)) = item.kind { - if let ExprKind::Block(..) = els.kind { - // Disable firing the lint in "else if" expressions. - if is_else_clause(cx.tcx, item) { - return; - } + fn check_expr(&mut self, cx: &LateContext<'_>, e: &Expr<'_>) { + if let ExprKind::If(cond, _, Some(els)) = e.kind + && let ExprKind::DropTemps(cond) = cond.kind + && let ExprKind::Block(..) = els.kind + { + let (msg, help) = match cond.kind { + ExprKind::Unary(UnOp::Not, _) => ( + "unnecessary boolean `not` operation", + "remove the `!` and swap the blocks of the `if`/`else`", + ), + // Don't lint on `… != 0`, as these are likely to be bit tests. + // For example, `if foo & 0x0F00 != 0 { … } else { … }` is already in the "proper" order. + ExprKind::Binary(op, _, rhs) if op.node == BinOpKind::Ne && !is_zero_const(rhs, cx) => ( + "unnecessary `!=` operation", + "change to `==` and swap the blocks of the `if`/`else`", + ), + _ => return, + }; - match cond.peel_drop_temps().kind { - ExprKind::Unary(UnOp::Not, _) => { - span_lint_and_help( - cx, - IF_NOT_ELSE, - item.span, - "unnecessary boolean `not` operation", - None, - "remove the `!` and swap the blocks of the `if`/`else`", - ); - }, - ExprKind::Binary(ref kind, _, lhs) if kind.node == BinOpKind::Ne && !is_zero_const(lhs, cx) => { - // Disable firing the lint on `… != 0`, as these are likely to be bit tests. - // For example, `if foo & 0x0F00 != 0 { … } else { … }` already is in the "proper" order. - span_lint_and_help( - cx, - IF_NOT_ELSE, - item.span, - "unnecessary `!=` operation", - None, - "change to `==` and swap the blocks of the `if`/`else`", - ); - }, - _ => (), - } + // `from_expansion` will also catch `while` loops which appear in the HIR as: + // ```rust + // loop { + // if cond { ... } else { break; } + // } + // ``` + if !e.span.from_expansion() && !is_else_clause(cx.tcx, e) { + span_lint_and_help(cx, IF_NOT_ELSE, e.span, msg, None, help); } } } diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs index 0b200815219..39ea16b05d1 100644 --- a/clippy_lints/src/if_then_some_else_none.rs +++ b/clippy_lints/src/if_then_some_else_none.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::source::snippet_with_context; @@ -51,9 +52,10 @@ pub struct IfThenSomeElseNone { } impl IfThenSomeElseNone { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } } } @@ -61,26 +63,6 @@ impl_lint_pass!(IfThenSomeElseNone => [IF_THEN_SOME_ELSE_NONE]); impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if !self.msrv.meets(msrvs::BOOL_THEN) { - return; - } - - if in_external_macro(cx.sess(), expr.span) { - return; - } - - // We only care about the top-most `if` in the chain - if is_else_clause(cx.tcx, expr) { - return; - } - - // `bool::then()` and `bool::then_some()` are not const - if in_constant(cx, expr.hir_id) { - return; - } - - let ctxt = expr.span.ctxt(); - if let Some(higher::If { cond, then, @@ -89,9 +71,14 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { && let ExprKind::Block(then_block, _) = then.kind && let Some(then_expr) = then_block.expr && let ExprKind::Call(then_call, [then_arg]) = then_expr.kind + && let ctxt = expr.span.ctxt() && then_expr.span.ctxt() == ctxt && is_res_lang_ctor(cx, path_res(cx, then_call), OptionSome) && is_res_lang_ctor(cx, path_res(cx, peel_blocks(els)), OptionNone) + && !is_else_clause(cx.tcx, expr) + && !in_constant(cx, expr.hir_id) + && !in_external_macro(cx.sess(), expr.span) + && self.msrv.meets(msrvs::BOOL_THEN) && !contains_return(then_block.stmts) { let mut app = Applicability::Unspecified; diff --git a/clippy_lints/src/ignored_unit_patterns.rs b/clippy_lints/src/ignored_unit_patterns.rs index a32201d8079..54b8adbc8ac 100644 --- a/clippy_lints/src/ignored_unit_patterns.rs +++ b/clippy_lints/src/ignored_unit_patterns.rs @@ -37,22 +37,21 @@ declare_lint_pass!(IgnoredUnitPatterns => [IGNORED_UNIT_PATTERNS]); impl<'tcx> LateLintPass<'tcx> for IgnoredUnitPatterns { fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx hir::Pat<'tcx>) { - if pat.span.from_expansion() { - return; - } - - match cx.tcx.parent_hir_node(pat.hir_id) { - Node::Param(param) if matches!(cx.tcx.parent_hir_node(param.hir_id), Node::Item(_)) => { - // Ignore function parameters - return; - }, - Node::LetStmt(local) if local.ty.is_some() => { - // Ignore let bindings with explicit type - return; - }, - _ => {}, - } - if matches!(pat.kind, PatKind::Wild) && cx.typeck_results().pat_ty(pat).peel_refs().is_unit() { + if matches!(pat.kind, PatKind::Wild) + && !pat.span.from_expansion() + && cx.typeck_results().pat_ty(pat).peel_refs().is_unit() + { + match cx.tcx.parent_hir_node(pat.hir_id) { + Node::Param(param) if matches!(cx.tcx.parent_hir_node(param.hir_id), Node::Item(_)) => { + // Ignore function parameters + return; + }, + Node::LetStmt(local) if local.ty.is_some() => { + // Ignore let bindings with explicit type + return; + }, + _ => {}, + } span_lint_and_sugg( cx, IGNORED_UNIT_PATTERNS, diff --git a/clippy_lints/src/incompatible_msrv.rs b/clippy_lints/src/incompatible_msrv.rs index 5c63d48adaf..12ca6d43b27 100644 --- a/clippy_lints/src/incompatible_msrv.rs +++ b/clippy_lints/src/incompatible_msrv.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::Msrv; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint; use clippy_utils::is_in_test; use rustc_attr::{StabilityLevel, StableSince}; @@ -47,9 +48,9 @@ pub struct IncompatibleMsrv { impl_lint_pass!(IncompatibleMsrv => [INCOMPATIBLE_MSRV]); impl IncompatibleMsrv { - pub fn new(msrv: Msrv) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - msrv, + msrv: conf.msrv.clone(), is_above_msrv: FxHashMap::default(), } } diff --git a/clippy_lints/src/inconsistent_struct_constructor.rs b/clippy_lints/src/inconsistent_struct_constructor.rs index 1075975f0a2..5b0aadf35c6 100644 --- a/clippy_lints/src/inconsistent_struct_constructor.rs +++ b/clippy_lints/src/inconsistent_struct_constructor.rs @@ -65,13 +65,13 @@ declare_lint_pass!(InconsistentStructConstructor => [INCONSISTENT_STRUCT_CONSTRU impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - if !expr.span.from_expansion() - && let ExprKind::Struct(qpath, fields, base) = expr.kind + if let ExprKind::Struct(qpath, fields, base) = expr.kind + && fields.iter().all(|f| f.is_shorthand) + && !expr.span.from_expansion() && let ty = cx.typeck_results().expr_ty(expr) && let Some(adt_def) = ty.ty_adt_def() && adt_def.is_struct() && let Some(variant) = adt_def.variants().iter().next() - && fields.iter().all(|f| f.is_shorthand) { let mut def_order_map = FxHashMap::default(); for (idx, field) in variant.fields.iter().enumerate() { diff --git a/clippy_lints/src/index_refutable_slice.rs b/clippy_lints/src/index_refutable_slice.rs index 128461ce7bc..526b4e1fba0 100644 --- a/clippy_lints/src/index_refutable_slice.rs +++ b/clippy_lints/src/index_refutable_slice.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::IfLet; @@ -58,10 +59,10 @@ pub struct IndexRefutableSlice { } impl IndexRefutableSlice { - pub fn new(max_suggested_slice_pattern_length: u64, msrv: Msrv) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - max_suggested_slice: max_suggested_slice_pattern_length, - msrv, + max_suggested_slice: conf.max_suggested_slice_pattern_length, + msrv: conf.msrv.clone(), } } } @@ -70,8 +71,8 @@ impl_lint_pass!(IndexRefutableSlice => [INDEX_REFUTABLE_SLICE]); impl<'tcx> LateLintPass<'tcx> for IndexRefutableSlice { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - if (!expr.span.from_expansion() || is_expn_of(expr.span, "if_chain").is_some()) - && let Some(IfLet { let_pat, if_then, .. }) = IfLet::hir(cx, expr) + if let Some(IfLet { let_pat, if_then, .. }) = IfLet::hir(cx, expr) + && (!expr.span.from_expansion() || is_expn_of(expr.span, "if_chain").is_some()) && !is_lint_allowed(cx, INDEX_REFUTABLE_SLICE, expr.hir_id) && self.msrv.meets(msrvs::SLICE_PATTERNS) && let found_slices = find_slice_values(cx, let_pat) diff --git a/clippy_lints/src/indexing_slicing.rs b/clippy_lints/src/indexing_slicing.rs index d54f2af65cd..6729c7c8d10 100644 --- a/clippy_lints/src/indexing_slicing.rs +++ b/clippy_lints/src/indexing_slicing.rs @@ -1,5 +1,6 @@ //! lint on indexing and slicing operations +use clippy_config::Conf; use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::ty::{deref_chain, get_adt_inherent_method}; @@ -87,28 +88,22 @@ declare_clippy_lint! { impl_lint_pass!(IndexingSlicing => [INDEXING_SLICING, OUT_OF_BOUNDS_INDEXING]); -#[derive(Copy, Clone)] pub struct IndexingSlicing { suppress_restriction_lint_in_const: bool, } impl IndexingSlicing { - pub fn new(suppress_restriction_lint_in_const: bool) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - suppress_restriction_lint_in_const, + suppress_restriction_lint_in_const: conf.suppress_restriction_lint_in_const, } } } impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if (self.suppress_restriction_lint_in_const && cx.tcx.hir().is_inside_const_context(expr.hir_id)) - || is_from_proc_macro(cx, expr) - { - return; - } - if let ExprKind::Index(array, index, _) = &expr.kind + && (!self.suppress_restriction_lint_in_const || !cx.tcx.hir().is_inside_const_context(expr.hir_id)) && let expr_ty = cx.typeck_results().expr_ty(array) && let mut deref = deref_chain(cx, expr_ty) && deref.any(|l| { @@ -116,6 +111,7 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { || l.peel_refs().is_array() || ty_has_applicable_get_function(cx, l.peel_refs(), expr_ty, expr) }) + && !is_from_proc_macro(cx, expr) { let note = "the suggestion might not be applicable in constant blocks"; let ty = cx.typeck_results().expr_ty(array).peel_refs(); diff --git a/clippy_lints/src/infinite_iter.rs b/clippy_lints/src/infinite_iter.rs index 9ad02735878..fa7e7f6b76d 100644 --- a/clippy_lints/src/infinite_iter.rs +++ b/clippy_lints/src/infinite_iter.rs @@ -226,13 +226,14 @@ const INFINITE_COLLECTORS: &[Symbol] = &[ fn complete_infinite_iter(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { match expr.kind { ExprKind::MethodCall(method, receiver, args, _) => { + let method_str = method.ident.name.as_str(); for &(name, len) in &COMPLETING_METHODS { - if method.ident.name.as_str() == name && args.len() == len { + if method_str == name && args.len() == len { return is_infinite(cx, receiver); } } for &(name, len) in &POSSIBLY_COMPLETING_METHODS { - if method.ident.name.as_str() == name && args.len() == len { + if method_str == name && args.len() == len { return MaybeInfinite.and(is_infinite(cx, receiver)); } } diff --git a/clippy_lints/src/instant_subtraction.rs b/clippy_lints/src/instant_subtraction.rs index 5fe152d1e30..f41fdf3203c 100644 --- a/clippy_lints/src/instant_subtraction.rs +++ b/clippy_lints/src/instant_subtraction.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_context; use clippy_utils::sugg::Sugg; @@ -68,9 +69,10 @@ pub struct InstantSubtraction { } impl InstantSubtraction { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } } } diff --git a/clippy_lints/src/item_name_repetitions.rs b/clippy_lints/src/item_name_repetitions.rs index 33764d3eb09..4d44bae02b8 100644 --- a/clippy_lints/src/item_name_repetitions.rs +++ b/clippy_lints/src/item_name_repetitions.rs @@ -1,5 +1,6 @@ //! lint on enum variants that are prefixed or suffixed by the same characters +use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_hir}; use clippy_utils::is_bool; use clippy_utils::macros::span_is_local; @@ -152,21 +153,14 @@ pub struct ItemNameRepetitions { } impl ItemNameRepetitions { - #[must_use] - pub fn new( - enum_threshold: u64, - struct_threshold: u64, - avoid_breaking_exported_api: bool, - allow_private_module_inception: bool, - allowed_prefixes: &[String], - ) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { modules: Vec::new(), - enum_threshold, - struct_threshold, - avoid_breaking_exported_api, - allow_private_module_inception, - allowed_prefixes: allowed_prefixes.iter().map(|s| to_camel_case(s)).collect(), + enum_threshold: conf.enum_variant_name_threshold, + struct_threshold: conf.struct_field_name_threshold, + avoid_breaking_exported_api: conf.avoid_breaking_exported_api, + allow_private_module_inception: conf.allow_private_module_inception, + allowed_prefixes: conf.allowed_prefixes.iter().map(|s| to_camel_case(s)).collect(), } } diff --git a/clippy_lints/src/large_const_arrays.rs b/clippy_lints/src/large_const_arrays.rs index b18ab625e60..5d2b521b250 100644 --- a/clippy_lints/src/large_const_arrays.rs +++ b/clippy_lints/src/large_const_arrays.rs @@ -1,3 +1,4 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind}; @@ -32,13 +33,14 @@ declare_clippy_lint! { } pub struct LargeConstArrays { - maximum_allowed_size: u128, + maximum_allowed_size: u64, } impl LargeConstArrays { - #[must_use] - pub fn new(maximum_allowed_size: u128) -> Self { - Self { maximum_allowed_size } + pub fn new(conf: &'static Conf) -> Self { + Self { + maximum_allowed_size: conf.array_size_threshold, + } } } @@ -57,7 +59,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeConstArrays { && let ConstKind::Value(_, ty::ValTree::Leaf(element_count)) = cst.kind() && let element_count = element_count.to_target_usize(cx.tcx) && let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes()) - && self.maximum_allowed_size < u128::from(element_count) * u128::from(element_size) + && u128::from(self.maximum_allowed_size) < u128::from(element_count) * u128::from(element_size) { let hi_pos = item.ident.span.lo() - BytePos::from_usize(1); let sugg_span = Span::new( diff --git a/clippy_lints/src/large_enum_variant.rs b/clippy_lints/src/large_enum_variant.rs index 85daadcc537..225d79aa71d 100644 --- a/clippy_lints/src/large_enum_variant.rs +++ b/clippy_lints/src/large_enum_variant.rs @@ -1,5 +1,6 @@ //! lint when there is a large size difference between variants on an enum +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{approx_ty_size, is_copy, AdtVariantInfo}; @@ -59,16 +60,14 @@ declare_clippy_lint! { "large size difference between variants on an enum" } -#[derive(Copy, Clone)] pub struct LargeEnumVariant { maximum_size_difference_allowed: u64, } impl LargeEnumVariant { - #[must_use] - pub fn new(maximum_size_difference_allowed: u64) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - maximum_size_difference_allowed, + maximum_size_difference_allowed: conf.enum_variant_size_threshold, } } } diff --git a/clippy_lints/src/large_futures.rs b/clippy_lints/src/large_futures.rs index 602227e4249..6f5065e4936 100644 --- a/clippy_lints/src/large_futures.rs +++ b/clippy_lints/src/large_futures.rs @@ -1,3 +1,4 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::implements_trait; @@ -39,14 +40,15 @@ declare_clippy_lint! { "large future may lead to unexpected stack overflows" } -#[derive(Copy, Clone)] pub struct LargeFuture { future_size_threshold: u64, } impl LargeFuture { - pub fn new(future_size_threshold: u64) -> Self { - Self { future_size_threshold } + pub fn new(conf: &'static Conf) -> Self { + Self { + future_size_threshold: conf.future_size_threshold, + } } } diff --git a/clippy_lints/src/large_include_file.rs b/clippy_lints/src/large_include_file.rs index 2688283a6ce..c67da689aae 100644 --- a/clippy_lints/src/large_include_file.rs +++ b/clippy_lints/src/large_include_file.rs @@ -1,3 +1,4 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_note; use clippy_utils::macros::root_macro_call_first_node; use rustc_ast::LitKind; @@ -41,9 +42,10 @@ pub struct LargeIncludeFile { } impl LargeIncludeFile { - #[must_use] - pub fn new(max_file_size: u64) -> Self { - Self { max_file_size } + pub fn new(conf: &'static Conf) -> Self { + Self { + max_file_size: conf.max_include_file_size, + } } } diff --git a/clippy_lints/src/large_stack_arrays.rs b/clippy_lints/src/large_stack_arrays.rs index d94b0cce948..15a75b06089 100644 --- a/clippy_lints/src/large_stack_arrays.rs +++ b/clippy_lints/src/large_stack_arrays.rs @@ -1,3 +1,4 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; use clippy_utils::macros::macro_backtrace; @@ -27,15 +28,14 @@ declare_clippy_lint! { } pub struct LargeStackArrays { - maximum_allowed_size: u128, + maximum_allowed_size: u64, prev_vec_macro_callsite: Option<Span>, } impl LargeStackArrays { - #[must_use] - pub fn new(maximum_allowed_size: u128) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - maximum_allowed_size, + maximum_allowed_size: conf.array_size_threshold, prev_vec_macro_callsite: None, } } @@ -76,7 +76,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeStackArrays { }) ) }) - && self.maximum_allowed_size < u128::from(element_count) * u128::from(element_size) + && u128::from(self.maximum_allowed_size) < u128::from(element_count) * u128::from(element_size) { span_lint_and_then( cx, @@ -106,7 +106,7 @@ fn might_be_expanded<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> bool { /// /// This is a fail-safe to a case where even the `is_from_proc_macro` is unable to determain the /// correct result. - fn repeat_expr_might_be_expanded<'tcx>(expr: &Expr<'tcx>) -> bool { + fn repeat_expr_might_be_expanded(expr: &Expr<'_>) -> bool { let ExprKind::Repeat(_, ArrayLen::Body(len_ct)) = expr.kind else { return false; }; diff --git a/clippy_lints/src/large_stack_frames.rs b/clippy_lints/src/large_stack_frames.rs index 49408d7e243..4abf7edc9b4 100644 --- a/clippy_lints/src/large_stack_frames.rs +++ b/clippy_lints/src/large_stack_frames.rs @@ -1,5 +1,6 @@ use std::{fmt, ops}; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::fn_has_unsatisfiable_preds; use clippy_utils::source::snippet_opt; @@ -85,10 +86,9 @@ pub struct LargeStackFrames { } impl LargeStackFrames { - #[must_use] - pub fn new(size: u64) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - maximum_allowed_size: size, + maximum_allowed_size: conf.stack_size_threshold, } } } diff --git a/clippy_lints/src/legacy_numeric_constants.rs b/clippy_lints/src/legacy_numeric_constants.rs index a08b40bef37..752e1326e3e 100644 --- a/clippy_lints/src/legacy_numeric_constants.rs +++ b/clippy_lints/src/legacy_numeric_constants.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::{Msrv, NUMERIC_ASSOCIATED_CONSTANTS}; +use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::{get_parent_expr, is_from_proc_macro}; use hir::def_id::DefId; @@ -38,9 +39,10 @@ pub struct LegacyNumericConstants { } impl LegacyNumericConstants { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } } } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index c2dc26d6605..ecfb6cdbdf0 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1,6 +1,7 @@ #![feature(array_windows)] #![feature(binary_heap_into_iter_sorted)] #![feature(box_patterns)] +#![feature(control_flow_enum)] #![feature(f128)] #![feature(f16)] #![feature(if_let_guard)] @@ -8,7 +9,6 @@ #![feature(iter_intersperse)] #![feature(iter_partition_in_place)] #![feature(let_chains)] -#![cfg_attr(bootstrap, feature(lint_reasons))] #![feature(never_type)] #![feature(rustc_private)] #![feature(stmt_expr_attributes)] @@ -288,6 +288,7 @@ mod partial_pub_fields; mod partialeq_ne_impl; mod partialeq_to_none; mod pass_by_ref_or_value; +mod pathbuf_init_then_push; mod pattern_type_mismatch; mod permissions_set_readonly_false; mod precedence; @@ -394,7 +395,6 @@ use clippy_config::{get_configuration_metadata, Conf}; use clippy_utils::macros::FormatArgsStorage; use rustc_data_structures::fx::FxHashSet; use rustc_lint::{Lint, LintId}; -use std::collections::BTreeMap; /// Register all pre expansion lints /// @@ -406,9 +406,7 @@ use std::collections::BTreeMap; /// Used in `./src/driver.rs`. pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { // NOTE: Do not add any more pre-expansion passes. These should be removed eventually. - let msrv = || conf.msrv.clone(); - - store.register_pre_expansion_pass(move || Box::new(attrs::EarlyAttributes { msrv: msrv() })); + store.register_pre_expansion_pass(move || Box::new(attrs::EarlyAttributes::new(conf))); } #[derive(Default)] @@ -534,88 +532,6 @@ fn register_categories(store: &mut rustc_lint::LintStore) { /// Used in `./src/driver.rs`. #[expect(clippy::too_many_lines)] pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { - let Conf { - ref absolute_paths_allowed_crates, - absolute_paths_max_segments, - accept_comment_above_attributes, - accept_comment_above_statement, - allow_dbg_in_tests, - allow_expect_in_tests, - allow_mixed_uninlined_format_args, - allow_one_hash_in_raw_strings, - allow_panic_in_tests, - allow_print_in_tests, - allow_private_module_inception, - allow_unwrap_in_tests, - allow_useless_vec_in_tests, - ref allowed_dotfiles, - ref allowed_idents_below_min_chars, - ref allowed_scripts, - ref allowed_wildcard_imports, - ref arithmetic_side_effects_allowed_binary, - ref arithmetic_side_effects_allowed_unary, - ref arithmetic_side_effects_allowed, - array_size_threshold, - avoid_breaking_exported_api, - ref await_holding_invalid_types, - cargo_ignore_publish, - cognitive_complexity_threshold, - ref disallowed_macros, - ref disallowed_methods, - ref disallowed_names, - ref disallowed_types, - ref doc_valid_idents, - enable_raw_pointer_heuristic_for_send, - enforce_iter_loop_reborrow, - ref enforced_import_renames, - enum_variant_name_threshold, - enum_variant_size_threshold, - excessive_nesting_threshold, - future_size_threshold, - ref ignore_interior_mutability, - large_error_threshold, - literal_representation_threshold, - matches_for_let_else, - max_fn_params_bools, - max_include_file_size, - max_struct_bools, - max_suggested_slice_pattern_length, - max_trait_bounds, - min_ident_chars_threshold, - missing_docs_in_crate_items, - ref msrv, - pass_by_value_size_limit, - semicolon_inside_block_ignore_singleline, - semicolon_outside_block_ignore_multiline, - single_char_binding_names_threshold, - stack_size_threshold, - ref standard_macro_braces, - struct_field_name_threshold, - suppress_restriction_lint_in_const, - too_large_for_stack, - too_many_arguments_threshold, - too_many_lines_threshold, - trivial_copy_size_limit, - type_complexity_threshold, - unnecessary_box_size, - unreadable_literal_lint_fractions, - upper_case_acronyms_aggressive, - vec_box_size_threshold, - verbose_bit_mask_threshold, - warn_on_all_wildcard_imports, - check_private_items, - pub_underscore_fields_behavior, - ref allowed_duplicate_crates, - allow_comparison_to_zero, - ref allowed_prefixes, - ref allow_renamed_params_for, - - blacklisted_names: _, - cyclomatic_complexity_threshold: _, - warn_unsafe_macro_metavars_in_private_macros, - } = *conf; - let msrv = || msrv.clone(); - register_removed_non_tool_lints(store); register_categories(store); @@ -660,35 +576,12 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { }); } - store.register_late_pass(move |_| { - Box::new(operators::arithmetic_side_effects::ArithmeticSideEffects::new( - arithmetic_side_effects_allowed - .iter() - .flat_map(|el| [[el.clone(), "*".to_string()], ["*".to_string(), el.clone()]]) - .chain(arithmetic_side_effects_allowed_binary.clone()) - .collect(), - arithmetic_side_effects_allowed - .iter() - .chain(arithmetic_side_effects_allowed_unary.iter()) - .cloned() - .collect(), - )) - }); + store.register_late_pass(move |_| Box::new(operators::arithmetic_side_effects::ArithmeticSideEffects::new(conf))); store.register_late_pass(|_| Box::new(utils::dump_hir::DumpHir)); store.register_late_pass(|_| Box::new(utils::author::Author)); - store.register_late_pass(move |_| { - Box::new(await_holding_invalid::AwaitHolding::new( - await_holding_invalid_types.clone(), - )) - }); + store.register_late_pass(move |tcx| Box::new(await_holding_invalid::AwaitHolding::new(tcx, conf))); store.register_late_pass(|_| Box::new(serde_api::SerdeApi)); - store.register_late_pass(move |_| { - Box::new(types::Types::new( - vec_box_size_threshold, - type_complexity_threshold, - avoid_breaking_exported_api, - )) - }); + store.register_late_pass(move |_| Box::new(types::Types::new(conf))); store.register_late_pass(|_| Box::new(booleans::NonminimalBool)); store.register_late_pass(|_| Box::new(enum_clike::UnportableVariant)); store.register_late_pass(|_| Box::new(float_literal::FloatLiteral)); @@ -702,7 +595,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(mut_reference::UnnecessaryMutPassed)); store.register_late_pass(|_| Box::<significant_drop_tightening::SignificantDropTightening<'_>>::default()); store.register_late_pass(|_| Box::new(len_zero::LenZero)); - store.register_late_pass(move |_| Box::new(attrs::Attributes::new(msrv()))); + store.register_late_pass(move |_| Box::new(attrs::Attributes::new(conf))); store.register_late_pass(|_| Box::new(blocks_in_conditions::BlocksInConditions)); store.register_late_pass(|_| Box::new(unicode::Unicode)); store.register_late_pass(|_| Box::new(uninit_vec::UninitVec)); @@ -714,44 +607,30 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(inconsistent_struct_constructor::InconsistentStructConstructor)); store.register_late_pass(|_| Box::new(non_octal_unix_permissions::NonOctalUnixPermissions)); store.register_early_pass(|| Box::new(unnecessary_self_imports::UnnecessarySelfImports)); - store.register_late_pass(move |_| Box::new(approx_const::ApproxConstant::new(msrv()))); + store.register_late_pass(move |_| Box::new(approx_const::ApproxConstant::new(conf))); let format_args = format_args_storage.clone(); - store.register_late_pass(move |_| { - Box::new(methods::Methods::new( - avoid_breaking_exported_api, - msrv(), - allow_expect_in_tests, - allow_unwrap_in_tests, - allowed_dotfiles.clone(), - format_args.clone(), - )) - }); - store.register_late_pass(move |_| Box::new(matches::Matches::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(checked_conversions::CheckedConversions::new(msrv()))); - store.register_late_pass(move |_| Box::new(mem_replace::MemReplace::new(msrv()))); - store.register_late_pass(move |_| Box::new(ranges::Ranges::new(msrv()))); - store.register_late_pass(move |_| Box::new(from_over_into::FromOverInto::new(msrv()))); - store.register_late_pass(move |_| Box::new(use_self::UseSelf::new(msrv()))); - store.register_late_pass(move |_| Box::new(missing_const_for_fn::MissingConstForFn::new(msrv()))); + store.register_late_pass(move |_| Box::new(methods::Methods::new(conf, format_args.clone()))); + store.register_late_pass(move |_| Box::new(matches::Matches::new(conf))); + store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveStruct::new(conf))); + store.register_late_pass(move |_| Box::new(manual_non_exhaustive::ManualNonExhaustiveEnum::new(conf))); + store.register_late_pass(move |_| Box::new(manual_strip::ManualStrip::new(conf))); + store.register_early_pass(move || Box::new(redundant_static_lifetimes::RedundantStaticLifetimes::new(conf))); + store.register_early_pass(move || Box::new(redundant_field_names::RedundantFieldNames::new(conf))); + store.register_late_pass(move |_| Box::new(checked_conversions::CheckedConversions::new(conf))); + store.register_late_pass(move |_| Box::new(mem_replace::MemReplace::new(conf))); + store.register_late_pass(move |_| Box::new(ranges::Ranges::new(conf))); + store.register_late_pass(move |_| Box::new(from_over_into::FromOverInto::new(conf))); + store.register_late_pass(move |_| Box::new(use_self::UseSelf::new(conf))); + store.register_late_pass(move |_| Box::new(missing_const_for_fn::MissingConstForFn::new(conf))); store.register_late_pass(move |_| Box::new(needless_question_mark::NeedlessQuestionMark)); - store.register_late_pass(move |_| Box::new(casts::Casts::new(msrv()))); - store.register_early_pass(move || Box::new(unnested_or_patterns::UnnestedOrPatterns::new(msrv()))); + store.register_late_pass(move |_| Box::new(casts::Casts::new(conf))); + store.register_early_pass(move || Box::new(unnested_or_patterns::UnnestedOrPatterns::new(conf))); store.register_late_pass(|_| Box::new(size_of_in_element_count::SizeOfInElementCount)); store.register_late_pass(|_| Box::new(same_name_method::SameNameMethod)); - store.register_late_pass(move |_| { - Box::new(index_refutable_slice::IndexRefutableSlice::new( - max_suggested_slice_pattern_length, - msrv(), - )) - }); + store.register_late_pass(move |_| Box::new(index_refutable_slice::IndexRefutableSlice::new(conf))); store.register_late_pass(|_| Box::<shadow::Shadow>::default()); store.register_late_pass(|_| Box::new(unit_types::UnitTypes)); - store.register_late_pass(move |_| Box::new(loops::Loops::new(msrv(), enforce_iter_loop_reborrow))); + store.register_late_pass(move |_| Box::new(loops::Loops::new(conf))); store.register_late_pass(|_| Box::<main_recursion::MainRecursion>::default()); store.register_late_pass(|_| Box::new(lifetimes::Lifetimes)); store.register_late_pass(|_| Box::new(entry::HashMapPass)); @@ -763,75 +642,49 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(borrow_deref_ref::BorrowDerefRef)); store.register_late_pass(|_| Box::<no_effect::NoEffect>::default()); store.register_late_pass(|_| Box::new(temporary_assignment::TemporaryAssignment)); - store.register_late_pass(move |_| Box::new(transmute::Transmute::new(msrv()))); - store.register_late_pass(move |_| { - Box::new(cognitive_complexity::CognitiveComplexity::new( - cognitive_complexity_threshold, - )) - }); - store.register_late_pass(move |_| Box::new(escape::BoxedLocal { too_large_for_stack })); - store.register_late_pass(move |_| { - Box::new(vec::UselessVec { - too_large_for_stack, - msrv: msrv(), - span_to_lint_map: BTreeMap::new(), - allow_in_test: allow_useless_vec_in_tests, - }) - }); - store.register_late_pass(move |_| Box::new(panic_unimplemented::PanicUnimplemented { allow_panic_in_tests })); + store.register_late_pass(move |_| Box::new(transmute::Transmute::new(conf))); + store.register_late_pass(move |_| Box::new(cognitive_complexity::CognitiveComplexity::new(conf))); + store.register_late_pass(move |_| Box::new(escape::BoxedLocal::new(conf))); + store.register_late_pass(move |_| Box::new(vec::UselessVec::new(conf))); + store.register_late_pass(move |_| Box::new(panic_unimplemented::PanicUnimplemented::new(conf))); store.register_late_pass(|_| Box::new(strings::StringLitAsBytes)); store.register_late_pass(|_| Box::new(derive::Derive)); - store.register_late_pass(move |_| Box::new(derivable_impls::DerivableImpls::new(msrv()))); + store.register_late_pass(move |_| Box::new(derivable_impls::DerivableImpls::new(conf))); store.register_late_pass(|_| Box::new(drop_forget_ref::DropForgetRef)); store.register_late_pass(|_| Box::new(empty_enum::EmptyEnum)); store.register_late_pass(|_| Box::new(invalid_upcast_comparisons::InvalidUpcastComparisons)); store.register_late_pass(|_| Box::<regex::Regex>::default()); - store.register_late_pass(move |_| Box::new(copies::CopyAndPaste::new(ignore_interior_mutability.clone()))); + store.register_late_pass(move |tcx| Box::new(copies::CopyAndPaste::new(tcx, conf))); store.register_late_pass(|_| Box::new(copy_iterator::CopyIterator)); let format_args = format_args_storage.clone(); store.register_late_pass(move |_| Box::new(format::UselessFormat::new(format_args.clone()))); store.register_late_pass(|_| Box::new(swap::Swap)); store.register_late_pass(|_| Box::new(panicking_overflow_checks::PanickingOverflowChecks)); store.register_late_pass(|_| Box::<new_without_default::NewWithoutDefault>::default()); - store.register_late_pass(move |_| Box::new(disallowed_names::DisallowedNames::new(disallowed_names))); - store.register_late_pass(move |_| { - Box::new(functions::Functions::new( - too_many_arguments_threshold, - too_many_lines_threshold, - large_error_threshold, - avoid_breaking_exported_api, - allow_renamed_params_for.clone(), - )) - }); - store.register_late_pass(move |_| Box::new(doc::Documentation::new(doc_valid_idents, check_private_items))); + store.register_late_pass(move |_| Box::new(disallowed_names::DisallowedNames::new(conf))); + store.register_late_pass(move |tcx| Box::new(functions::Functions::new(tcx, conf))); + store.register_late_pass(move |_| Box::new(doc::Documentation::new(conf))); store.register_late_pass(|_| Box::new(neg_multiply::NegMultiply)); store.register_late_pass(|_| Box::new(let_if_seq::LetIfSeq)); store.register_late_pass(|_| Box::new(mixed_read_write_in_expression::EvalOrderDependence)); - store.register_late_pass(move |_| Box::new(missing_doc::MissingDoc::new(missing_docs_in_crate_items))); + store.register_late_pass(move |_| Box::new(missing_doc::MissingDoc::new(conf))); store.register_late_pass(|_| Box::new(missing_inline::MissingInline)); store.register_late_pass(move |_| Box::new(exhaustive_items::ExhaustiveItems)); store.register_late_pass(|_| Box::new(match_result_ok::MatchResultOk)); store.register_late_pass(|_| Box::new(partialeq_ne_impl::PartialEqNeImpl)); store.register_late_pass(|_| Box::new(unused_io_amount::UnusedIoAmount)); - store.register_late_pass(move |_| Box::new(large_enum_variant::LargeEnumVariant::new(enum_variant_size_threshold))); + store.register_late_pass(move |_| Box::new(large_enum_variant::LargeEnumVariant::new(conf))); let format_args = format_args_storage.clone(); store.register_late_pass(move |_| Box::new(explicit_write::ExplicitWrite::new(format_args.clone()))); store.register_late_pass(|_| Box::new(needless_pass_by_value::NeedlessPassByValue)); - store.register_late_pass(move |tcx| { - Box::new(pass_by_ref_or_value::PassByRefOrValue::new( - trivial_copy_size_limit, - pass_by_value_size_limit, - avoid_breaking_exported_api, - tcx.sess.target.pointer_width, - )) - }); + store.register_late_pass(move |tcx| Box::new(pass_by_ref_or_value::PassByRefOrValue::new(tcx, conf))); store.register_late_pass(|_| Box::new(ref_option_ref::RefOptionRef)); store.register_late_pass(|_| Box::new(infinite_iter::InfiniteIter)); store.register_late_pass(|_| Box::new(inline_fn_without_body::InlineFnWithoutBody)); store.register_late_pass(|_| Box::<useless_conversion::UselessConversion>::default()); store.register_late_pass(|_| Box::new(implicit_hasher::ImplicitHasher)); store.register_late_pass(|_| Box::new(fallible_impl_from::FallibleImplFrom)); - store.register_late_pass(move |_| Box::new(question_mark::QuestionMark::new(msrv(), matches_for_let_else))); + store.register_late_pass(move |_| Box::new(question_mark::QuestionMark::new(conf))); store.register_late_pass(|_| Box::new(question_mark_used::QuestionMarkUsed)); store.register_early_pass(|| Box::new(suspicious_operation_groupings::SuspiciousOperationGroupings)); store.register_late_pass(|_| Box::new(suspicious_trait_impl::SuspiciousImpl)); @@ -839,22 +692,18 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(inherent_impl::MultipleInherentImpl)); store.register_late_pass(|_| Box::new(neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd)); store.register_late_pass(|_| Box::new(unwrap::Unwrap)); - store.register_late_pass(move |_| { - Box::new(indexing_slicing::IndexingSlicing::new( - suppress_restriction_lint_in_const, - )) - }); - store.register_late_pass(move |_| Box::new(non_copy_const::NonCopyConst::new(ignore_interior_mutability.clone()))); + store.register_late_pass(move |_| Box::new(indexing_slicing::IndexingSlicing::new(conf))); + store.register_late_pass(move |tcx| Box::new(non_copy_const::NonCopyConst::new(tcx, conf))); store.register_late_pass(|_| Box::new(ptr_offset_with_cast::PtrOffsetWithCast)); store.register_late_pass(|_| Box::new(redundant_clone::RedundantClone)); store.register_late_pass(|_| Box::new(slow_vector_initialization::SlowVectorInit)); - store.register_late_pass(move |_| Box::new(unnecessary_wraps::UnnecessaryWraps::new(avoid_breaking_exported_api))); + store.register_late_pass(move |_| Box::new(unnecessary_wraps::UnnecessaryWraps::new(conf))); store.register_late_pass(|_| Box::new(assertions_on_constants::AssertionsOnConstants)); store.register_late_pass(|_| Box::new(assertions_on_result_states::AssertionsOnResultStates)); store.register_late_pass(|_| Box::new(inherent_to_string::InherentToString)); - store.register_late_pass(move |_| Box::new(trait_bounds::TraitBounds::new(max_trait_bounds, msrv()))); + store.register_late_pass(move |_| Box::new(trait_bounds::TraitBounds::new(conf))); store.register_late_pass(|_| Box::new(comparison_chain::ComparisonChain)); - store.register_late_pass(move |_| Box::new(mut_key::MutableKeyType::new(ignore_interior_mutability.clone()))); + store.register_late_pass(move |tcx| Box::new(mut_key::MutableKeyType::new(tcx, conf))); store.register_early_pass(|| Box::new(reference::DerefAddrOf)); store.register_early_pass(|| Box::new(double_parens::DoubleParens)); let format_args = format_args_storage.clone(); @@ -875,80 +724,45 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_early_pass(|| Box::new(redundant_else::RedundantElse)); store.register_late_pass(|_| Box::new(create_dir::CreateDir)); store.register_early_pass(|| Box::new(needless_arbitrary_self_type::NeedlessArbitrarySelfType)); - store.register_early_pass(move || { - Box::new(literal_representation::LiteralDigitGrouping::new( - unreadable_literal_lint_fractions, - )) - }); - store.register_early_pass(move || { - Box::new(literal_representation::DecimalLiteralRepresentation::new( - literal_representation_threshold, - )) - }); - store.register_late_pass(move |_| { - Box::new(item_name_repetitions::ItemNameRepetitions::new( - enum_variant_name_threshold, - struct_field_name_threshold, - avoid_breaking_exported_api, - allow_private_module_inception, - allowed_prefixes, - )) - }); + store.register_early_pass(move || Box::new(literal_representation::LiteralDigitGrouping::new(conf))); + store.register_early_pass(move || Box::new(literal_representation::DecimalLiteralRepresentation::new(conf))); + store.register_late_pass(move |_| Box::new(item_name_repetitions::ItemNameRepetitions::new(conf))); store.register_early_pass(|| Box::new(tabs_in_doc_comments::TabsInDocComments)); - store.register_late_pass(move |_| { - Box::new(upper_case_acronyms::UpperCaseAcronyms::new( - avoid_breaking_exported_api, - upper_case_acronyms_aggressive, - )) - }); + store.register_late_pass(move |_| Box::new(upper_case_acronyms::UpperCaseAcronyms::new(conf))); store.register_late_pass(|_| Box::<default::Default>::default()); - store.register_late_pass(move |_| Box::new(unused_self::UnusedSelf::new(avoid_breaking_exported_api))); + store.register_late_pass(move |_| Box::new(unused_self::UnusedSelf::new(conf))); store.register_late_pass(|_| Box::new(mutable_debug_assertion::DebugAssertWithMutCall)); store.register_late_pass(|_| Box::new(exit::Exit)); store.register_late_pass(|_| Box::new(to_digit_is_some::ToDigitIsSome)); - store.register_late_pass(move |_| Box::new(large_stack_arrays::LargeStackArrays::new(array_size_threshold.into()))); - store.register_late_pass(move |_| Box::new(large_const_arrays::LargeConstArrays::new(array_size_threshold.into()))); + store.register_late_pass(move |_| Box::new(large_stack_arrays::LargeStackArrays::new(conf))); + store.register_late_pass(move |_| Box::new(large_const_arrays::LargeConstArrays::new(conf))); store.register_late_pass(|_| Box::new(floating_point_arithmetic::FloatingPointArithmetic)); store.register_late_pass(|_| Box::new(as_conversions::AsConversions)); store.register_late_pass(|_| Box::new(let_underscore::LetUnderscore)); store.register_early_pass(|| Box::<single_component_path_imports::SingleComponentPathImports>::default()); - store.register_late_pass(move |_| { - Box::new(excessive_bools::ExcessiveBools::new( - max_struct_bools, - max_fn_params_bools, - )) - }); + store.register_late_pass(move |_| Box::new(excessive_bools::ExcessiveBools::new(conf))); store.register_early_pass(|| Box::new(option_env_unwrap::OptionEnvUnwrap)); - store.register_late_pass(move |_| { - Box::new(wildcard_imports::WildcardImports::new( - warn_on_all_wildcard_imports, - allowed_wildcard_imports.clone(), - )) - }); + store.register_late_pass(move |_| Box::new(wildcard_imports::WildcardImports::new(conf))); store.register_late_pass(|_| Box::<redundant_pub_crate::RedundantPubCrate>::default()); store.register_late_pass(|_| Box::new(unnamed_address::UnnamedAddress)); store.register_late_pass(|_| Box::<dereference::Dereferencing<'_>>::default()); store.register_late_pass(|_| Box::new(option_if_let_else::OptionIfLetElse)); store.register_late_pass(|_| Box::new(future_not_send::FutureNotSend)); - store.register_late_pass(move |_| Box::new(large_futures::LargeFuture::new(future_size_threshold))); + store.register_late_pass(move |_| Box::new(large_futures::LargeFuture::new(conf))); store.register_late_pass(|_| Box::new(if_let_mutex::IfLetMutex)); store.register_late_pass(|_| Box::new(if_not_else::IfNotElse)); store.register_late_pass(|_| Box::new(equatable_if_let::PatternEquality)); store.register_late_pass(|_| Box::new(manual_async_fn::ManualAsyncFn)); store.register_late_pass(|_| Box::new(panic_in_result_fn::PanicInResultFn)); - store.register_early_pass(move || { - Box::new(non_expressive_names::NonExpressiveNames { - single_char_binding_names_threshold, - }) - }); - store.register_early_pass(move || Box::new(nonstandard_macro_braces::MacroBraces::new(standard_macro_braces))); + store.register_early_pass(move || Box::new(non_expressive_names::NonExpressiveNames::new(conf))); + store.register_early_pass(move || Box::new(nonstandard_macro_braces::MacroBraces::new(conf))); store.register_late_pass(|_| Box::<macro_use::MacroUseImports>::default()); store.register_late_pass(|_| Box::new(pattern_type_mismatch::PatternTypeMismatch)); store.register_late_pass(|_| Box::new(unwrap_in_result::UnwrapInResult)); store.register_late_pass(|_| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned)); store.register_late_pass(|_| Box::new(async_yields_async::AsyncYieldsAsync)); - store.register_late_pass(move |_| Box::new(disallowed_macros::DisallowedMacros::new(disallowed_macros.clone()))); - store.register_late_pass(move |_| Box::new(disallowed_methods::DisallowedMethods::new(disallowed_methods.clone()))); + store.register_late_pass(move |tcx| Box::new(disallowed_macros::DisallowedMacros::new(tcx, conf))); + store.register_late_pass(move |tcx| Box::new(disallowed_methods::DisallowedMethods::new(tcx, conf))); 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)); @@ -958,86 +772,57 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::<vec_init_then_push::VecInitThenPush>::default()); store.register_late_pass(|_| Box::new(redundant_slicing::RedundantSlicing)); store.register_late_pass(|_| Box::new(from_str_radix_10::FromStrRadix10)); - store.register_late_pass(move |_| Box::new(if_then_some_else_none::IfThenSomeElseNone::new(msrv()))); + store.register_late_pass(move |_| Box::new(if_then_some_else_none::IfThenSomeElseNone::new(conf))); store.register_late_pass(|_| Box::new(bool_assert_comparison::BoolAssertComparison)); store.register_early_pass(move || Box::new(module_style::ModStyle)); store.register_late_pass(|_| Box::<unused_async::UnusedAsync>::default()); - store.register_late_pass(move |_| Box::new(disallowed_types::DisallowedTypes::new(disallowed_types.clone()))); - store.register_late_pass(move |_| { - Box::new(missing_enforced_import_rename::ImportRename::new( - enforced_import_renames.clone(), - )) - }); - store.register_early_pass(move || Box::new(disallowed_script_idents::DisallowedScriptIdents::new(allowed_scripts))); + store.register_late_pass(move |tcx| Box::new(disallowed_types::DisallowedTypes::new(tcx, conf))); + store.register_late_pass(move |tcx| Box::new(missing_enforced_import_rename::ImportRename::new(tcx, conf))); + store.register_early_pass(move || Box::new(disallowed_script_idents::DisallowedScriptIdents::new(conf))); store.register_late_pass(|_| Box::new(strlen_on_c_strings::StrlenOnCStrings)); store.register_late_pass(move |_| Box::new(self_named_constructors::SelfNamedConstructors)); store.register_late_pass(move |_| Box::new(iter_not_returning_iterator::IterNotReturningIterator)); store.register_late_pass(move |_| Box::new(manual_assert::ManualAssert)); - store.register_late_pass(move |_| { - Box::new(non_send_fields_in_send_ty::NonSendFieldInSendTy::new( - enable_raw_pointer_heuristic_for_send, - )) - }); - store.register_late_pass(move |_| { - Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks::new( - accept_comment_above_statement, - accept_comment_above_attributes, - )) - }); + store.register_late_pass(move |_| Box::new(non_send_fields_in_send_ty::NonSendFieldInSendTy::new(conf))); + store.register_late_pass(move |_| Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks::new(conf))); let format_args = format_args_storage.clone(); - store.register_late_pass(move |_| { - Box::new(format_args::FormatArgs::new( - format_args.clone(), - msrv(), - allow_mixed_uninlined_format_args, - )) - }); + store.register_late_pass(move |_| Box::new(format_args::FormatArgs::new(conf, format_args.clone()))); store.register_late_pass(|_| Box::new(trailing_empty_array::TrailingEmptyArray)); store.register_early_pass(|| Box::new(octal_escapes::OctalEscapes)); store.register_late_pass(|_| Box::new(needless_late_init::NeedlessLateInit)); store.register_late_pass(|_| Box::new(return_self_not_must_use::ReturnSelfNotMustUse)); store.register_late_pass(|_| Box::new(init_numbered_fields::NumberedFields)); store.register_early_pass(|| Box::new(single_char_lifetime_names::SingleCharLifetimeNames)); - store.register_late_pass(move |_| Box::new(manual_bits::ManualBits::new(msrv()))); + store.register_late_pass(move |_| Box::new(manual_bits::ManualBits::new(conf))); store.register_late_pass(|_| Box::new(default_union_representation::DefaultUnionRepresentation)); store.register_late_pass(|_| Box::<only_used_in_recursion::OnlyUsedInRecursion>::default()); - store.register_late_pass(move |_| Box::new(dbg_macro::DbgMacro::new(allow_dbg_in_tests))); + store.register_late_pass(move |_| Box::new(dbg_macro::DbgMacro::new(conf))); let format_args = format_args_storage.clone(); - store.register_late_pass(move |_| Box::new(write::Write::new(format_args.clone(), allow_print_in_tests))); - store.register_late_pass(move |_| { - Box::new(cargo::Cargo { - ignore_publish: cargo_ignore_publish, - allowed_duplicate_crates: allowed_duplicate_crates.clone(), - }) - }); + store.register_late_pass(move |_| Box::new(write::Write::new(conf, format_args.clone()))); + store.register_late_pass(move |_| Box::new(cargo::Cargo::new(conf))); store.register_early_pass(|| Box::new(crate_in_macro_def::CrateInMacroDef)); store.register_early_pass(|| Box::new(empty_with_brackets::EmptyWithBrackets)); 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(move |_| Box::new(large_include_file::LargeIncludeFile::new(max_include_file_size))); + store.register_late_pass(move |_| Box::new(large_include_file::LargeIncludeFile::new(conf))); store.register_late_pass(|_| Box::new(strings::TrimSplitWhitespace)); store.register_late_pass(|_| Box::new(rc_clone_in_vec_init::RcCloneInVecInit)); store.register_early_pass(|| Box::<duplicate_mod::DuplicateMod>::default()); store.register_early_pass(|| Box::new(unused_rounding::UnusedRounding)); - store.register_early_pass(move || Box::new(almost_complete_range::AlmostCompleteRange::new(msrv()))); + store.register_early_pass(move || Box::new(almost_complete_range::AlmostCompleteRange::new(conf))); store.register_late_pass(|_| Box::new(swap_ptr_to_ref::SwapPtrToRef)); store.register_late_pass(|_| Box::new(mismatching_type_param_order::TypeParamMismatch)); store.register_late_pass(|_| Box::new(read_zero_byte_vec::ReadZeroByteVec)); store.register_late_pass(|_| Box::new(default_instead_of_iter_empty::DefaultIterEmpty)); - store.register_late_pass(move |_| Box::new(manual_rem_euclid::ManualRemEuclid::new(msrv()))); - store.register_late_pass(move |_| Box::new(manual_retain::ManualRetain::new(msrv()))); + store.register_late_pass(move |_| Box::new(manual_rem_euclid::ManualRemEuclid::new(conf))); + store.register_late_pass(move |_| Box::new(manual_retain::ManualRetain::new(conf))); store.register_late_pass(move |_| Box::new(manual_rotate::ManualRotate)); - store.register_late_pass(move |_| { - Box::new(operators::Operators::new( - verbose_bit_mask_threshold, - allow_comparison_to_zero, - )) - }); + store.register_late_pass(move |_| Box::new(operators::Operators::new(conf))); store.register_late_pass(|_| Box::<std_instead_of_core::StdReexports>::default()); - store.register_late_pass(move |_| Box::new(instant_subtraction::InstantSubtraction::new(msrv()))); + store.register_late_pass(move |_| Box::new(instant_subtraction::InstantSubtraction::new(conf))); store.register_late_pass(|_| Box::new(partialeq_to_none::PartialeqToNone)); - store.register_late_pass(move |_| Box::new(manual_clamp::ManualClamp::new(msrv()))); + store.register_late_pass(move |_| Box::new(manual_clamp::ManualClamp::new(conf))); store.register_late_pass(|_| Box::new(manual_string_new::ManualStringNew)); store.register_late_pass(|_| Box::new(unused_peekable::UnusedPeekable)); store.register_early_pass(|| Box::new(multi_assignments::MultiAssignments)); @@ -1048,44 +833,25 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(missing_trait_methods::MissingTraitMethods)); store.register_late_pass(|_| Box::new(from_raw_with_void_ptr::FromRawWithVoidPtr)); store.register_late_pass(|_| Box::new(suspicious_xor_used_as_pow::ConfusingXorAndPow)); - store.register_late_pass(move |_| Box::new(manual_is_ascii_check::ManualIsAsciiCheck::new(msrv()))); - store.register_late_pass(move |_| { - Box::new(semicolon_block::SemicolonBlock::new( - semicolon_inside_block_ignore_singleline, - semicolon_outside_block_ignore_multiline, - )) - }); + store.register_late_pass(move |_| Box::new(manual_is_ascii_check::ManualIsAsciiCheck::new(conf))); + store.register_late_pass(move |_| Box::new(semicolon_block::SemicolonBlock::new(conf))); store.register_late_pass(|_| Box::new(permissions_set_readonly_false::PermissionsSetReadonlyFalse)); store.register_late_pass(|_| Box::new(size_of_ref::SizeOfRef)); store.register_late_pass(|_| Box::new(multiple_unsafe_ops_per_block::MultipleUnsafeOpsPerBlock)); - store.register_late_pass(move |_| { - Box::new(extra_unused_type_parameters::ExtraUnusedTypeParameters::new( - avoid_breaking_exported_api, - )) - }); + store.register_late_pass(move |_| Box::new(extra_unused_type_parameters::ExtraUnusedTypeParameters::new(conf))); store.register_late_pass(|_| Box::new(no_mangle_with_rust_abi::NoMangleWithRustAbi)); store.register_late_pass(|_| Box::new(collection_is_never_read::CollectionIsNeverRead)); store.register_late_pass(|_| Box::new(missing_assert_message::MissingAssertMessage)); store.register_late_pass(|_| Box::new(needless_maybe_sized::NeedlessMaybeSized)); store.register_late_pass(|_| Box::new(redundant_async_block::RedundantAsyncBlock)); store.register_late_pass(|_| Box::new(let_with_type_underscore::UnderscoreTyped)); - store.register_late_pass(move |_| Box::new(manual_main_separator_str::ManualMainSeparatorStr::new(msrv()))); + store.register_late_pass(move |_| Box::new(manual_main_separator_str::ManualMainSeparatorStr::new(conf))); store.register_late_pass(|_| Box::new(unnecessary_struct_initialization::UnnecessaryStruct)); - store.register_late_pass(move |_| { - Box::new(unnecessary_box_returns::UnnecessaryBoxReturns::new( - avoid_breaking_exported_api, - unnecessary_box_size, - )) - }); + store.register_late_pass(move |_| Box::new(unnecessary_box_returns::UnnecessaryBoxReturns::new(conf))); store.register_late_pass(|_| Box::new(lines_filter_map_ok::LinesFilterMapOk)); store.register_late_pass(|_| Box::new(tests_outside_test_module::TestsOutsideTestModule)); store.register_late_pass(|_| Box::new(manual_slice_size_calculation::ManualSliceSizeCalculation)); - store.register_early_pass(move || { - Box::new(excessive_nesting::ExcessiveNesting { - excessive_nesting_threshold, - nodes: rustc_ast::node_id::NodeSet::new(), - }) - }); + store.register_early_pass(move || Box::new(excessive_nesting::ExcessiveNesting::new(conf))); store.register_late_pass(|_| Box::new(items_after_test_module::ItemsAfterTestModule)); store.register_early_pass(|| Box::new(ref_patterns::RefPatterns)); store.register_late_pass(|_| Box::new(default_constructed_unit_structs::DefaultConstructedUnitStructs)); @@ -1095,44 +861,21 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(redundant_type_annotations::RedundantTypeAnnotations)); store.register_late_pass(|_| Box::new(arc_with_non_send_sync::ArcWithNonSendSync)); store.register_late_pass(|_| Box::new(needless_if::NeedlessIf)); - store.register_late_pass(move |_| { - Box::new(min_ident_chars::MinIdentChars { - allowed_idents_below_min_chars: allowed_idents_below_min_chars.clone(), - min_ident_chars_threshold, - }) - }); - store.register_late_pass(move |_| Box::new(large_stack_frames::LargeStackFrames::new(stack_size_threshold))); + store.register_late_pass(move |_| Box::new(min_ident_chars::MinIdentChars::new(conf))); + store.register_late_pass(move |_| Box::new(large_stack_frames::LargeStackFrames::new(conf))); store.register_late_pass(|_| Box::new(single_range_in_vec_init::SingleRangeInVecInit)); - store.register_late_pass(move |_| { - Box::new(needless_pass_by_ref_mut::NeedlessPassByRefMut::new( - avoid_breaking_exported_api, - )) - }); + store.register_late_pass(move |_| Box::new(needless_pass_by_ref_mut::NeedlessPassByRefMut::new(conf))); store.register_late_pass(|_| Box::new(non_canonical_impls::NonCanonicalImpls)); - store.register_late_pass(move |_| { - Box::new(single_call_fn::SingleCallFn { - avoid_breaking_exported_api, - def_id_to_usage: rustc_data_structures::fx::FxIndexMap::default(), - }) - }); - store.register_early_pass(move || { - Box::new(raw_strings::RawStrings { - allow_one_hash_in_raw_strings, - }) - }); - store.register_late_pass(move |_| Box::new(legacy_numeric_constants::LegacyNumericConstants::new(msrv()))); + store.register_late_pass(move |_| Box::new(single_call_fn::SingleCallFn::new(conf))); + store.register_early_pass(move || Box::new(raw_strings::RawStrings::new(conf))); + store.register_late_pass(move |_| Box::new(legacy_numeric_constants::LegacyNumericConstants::new(conf))); store.register_late_pass(|_| Box::new(manual_range_patterns::ManualRangePatterns)); store.register_early_pass(|| Box::new(visibility::Visibility)); - store.register_late_pass(move |_| Box::new(tuple_array_conversions::TupleArrayConversions { msrv: msrv() })); + store.register_late_pass(move |_| Box::new(tuple_array_conversions::TupleArrayConversions::new(conf))); store.register_late_pass(|_| Box::new(manual_float_methods::ManualFloatMethods)); store.register_late_pass(|_| Box::new(four_forward_slashes::FourForwardSlashes)); store.register_late_pass(|_| Box::new(error_impl_error::ErrorImplError)); - store.register_late_pass(move |_| { - Box::new(absolute_paths::AbsolutePaths { - absolute_paths_max_segments, - absolute_paths_allowed_crates: absolute_paths_allowed_crates.clone(), - }) - }); + store.register_late_pass(move |_| Box::new(absolute_paths::AbsolutePaths::new(conf))); store.register_late_pass(|_| Box::new(redundant_locals::RedundantLocals)); store.register_late_pass(|_| Box::new(ignored_unit_patterns::IgnoredUnitPatterns)); store.register_late_pass(|_| Box::<reserve_after_initialization::ReserveAfterInitialization>::default()); @@ -1141,38 +884,29 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(unnecessary_map_on_constructor::UnnecessaryMapOnConstructor)); store.register_late_pass(move |_| { Box::new(needless_borrows_for_generic_args::NeedlessBorrowsForGenericArgs::new( - msrv(), + conf, )) }); - store.register_late_pass(move |_| Box::new(manual_hash_one::ManualHashOne::new(msrv()))); + store.register_late_pass(move |_| Box::new(manual_hash_one::ManualHashOne::new(conf))); store.register_late_pass(|_| Box::new(iter_without_into_iter::IterWithoutIntoIter)); + store.register_late_pass(|_| Box::<pathbuf_init_then_push::PathbufThenPush<'_>>::default()); store.register_late_pass(|_| Box::new(iter_over_hash_type::IterOverHashType)); store.register_late_pass(|_| Box::new(impl_hash_with_borrow_str_and_bytes::ImplHashWithBorrowStrBytes)); store.register_late_pass(|_| Box::new(repeat_vec_with_capacity::RepeatVecWithCapacity)); store.register_late_pass(|_| Box::new(uninhabited_references::UninhabitedReferences)); store.register_late_pass(|_| Box::new(ineffective_open_options::IneffectiveOpenOptions)); store.register_late_pass(|_| Box::<unconditional_recursion::UnconditionalRecursion>::default()); - store.register_late_pass(move |_| { - Box::new(pub_underscore_fields::PubUnderscoreFields { - behavior: pub_underscore_fields_behavior, - }) - }); - store - .register_late_pass(move |_| Box::new(missing_const_for_thread_local::MissingConstForThreadLocal::new(msrv()))); - store.register_late_pass(move |_| Box::new(incompatible_msrv::IncompatibleMsrv::new(msrv()))); + store.register_late_pass(move |_| Box::new(pub_underscore_fields::PubUnderscoreFields::new(conf))); + store.register_late_pass(move |_| Box::new(missing_const_for_thread_local::MissingConstForThreadLocal::new(conf))); + store.register_late_pass(move |_| Box::new(incompatible_msrv::IncompatibleMsrv::new(conf))); store.register_late_pass(|_| Box::new(to_string_trait_impl::ToStringTraitImpl)); store.register_early_pass(|| Box::new(multiple_bound_locations::MultipleBoundLocations)); - store.register_late_pass(move |_| Box::new(assigning_clones::AssigningClones::new(msrv()))); + store.register_late_pass(move |_| Box::new(assigning_clones::AssigningClones::new(conf))); store.register_late_pass(|_| Box::new(zero_repeat_side_effects::ZeroRepeatSideEffects)); store.register_late_pass(|_| Box::new(manual_unwrap_or_default::ManualUnwrapOrDefault)); store.register_late_pass(|_| Box::new(integer_division_remainder_used::IntegerDivisionRemainderUsed)); - store.register_late_pass(move |_| { - Box::new(macro_metavars_in_unsafe::ExprMetavarsInUnsafe { - warn_unsafe_macro_metavars_in_private_macros, - ..Default::default() - }) - }); - store.register_late_pass(move |_| Box::new(string_patterns::StringPatterns::new(msrv()))); + store.register_late_pass(move |_| Box::new(macro_metavars_in_unsafe::ExprMetavarsInUnsafe::new(conf))); + store.register_late_pass(move |_| Box::new(string_patterns::StringPatterns::new(conf))); store.register_early_pass(|| Box::new(field_scoped_visibility_modifiers::FieldScopedVisibilityModifiers)); store.register_late_pass(|_| Box::new(set_contains_or_insert::HashsetInsertAfterContains)); store.register_early_pass(|| Box::new(byte_char_slices::ByteCharSlice)); diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 443d6189c1f..9f063ced313 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -22,6 +22,7 @@ use rustc_session::declare_lint_pass; use rustc_span::def_id::LocalDefId; use rustc_span::symbol::{kw, Ident, Symbol}; use rustc_span::Span; +use std::ops::ControlFlow; declare_clippy_lint! { /// ### What it does @@ -380,11 +381,8 @@ fn could_use_elision<'tcx>( return None; } - let mut checker = BodyLifetimeChecker { - lifetimes_used_in_body: false, - }; - checker.visit_expr(body.value); - if checker.lifetimes_used_in_body { + let mut checker = BodyLifetimeChecker; + if checker.visit_expr(body.value).is_break() { return None; } } @@ -694,15 +692,15 @@ fn report_extra_impl_lifetimes<'tcx>(cx: &LateContext<'tcx>, impl_: &'tcx Impl<' } } -struct BodyLifetimeChecker { - lifetimes_used_in_body: bool, -} +struct BodyLifetimeChecker; impl<'tcx> Visitor<'tcx> for BodyLifetimeChecker { + type Result = ControlFlow<()>; // for lifetimes as parameters of generics - fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) { + fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) -> ControlFlow<()> { if !lifetime.is_anonymous() && lifetime.ident.name != kw::StaticLifetime { - self.lifetimes_used_in_body = true; + return ControlFlow::Break(()); } + ControlFlow::Continue(()) } } diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index 7481543941a..b685d1dad1a 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -1,6 +1,7 @@ //! Lints concerned with the grouping of digits with underscores in integral or //! floating-point literal expressions. +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::numeric_literal::{NumericLiteral, Radix}; use clippy_utils::source::snippet_opt; @@ -218,7 +219,6 @@ impl WarningType { } } -#[derive(Copy, Clone)] pub struct LiteralDigitGrouping { lint_fraction_readability: bool, } @@ -245,13 +245,13 @@ impl EarlyLintPass for LiteralDigitGrouping { const UUID_GROUP_LENS: [usize; 5] = [8, 4, 4, 4, 12]; impl LiteralDigitGrouping { - pub fn new(lint_fraction_readability: bool) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - lint_fraction_readability, + lint_fraction_readability: conf.unreadable_literal_lint_fractions, } } - fn check_lit(self, cx: &EarlyContext<'_>, lit: token::Lit, span: Span) { + fn check_lit(&self, cx: &EarlyContext<'_>, lit: token::Lit, span: Span) { if let Some(src) = snippet_opt(cx, span) && let Ok(lit_kind) = LitKind::from_token_lit(lit) && let Some(mut num_lit) = NumericLiteral::from_lit_kind(&src, &lit_kind) @@ -437,7 +437,6 @@ impl LiteralDigitGrouping { } #[expect(clippy::module_name_repetitions)] -#[derive(Copy, Clone)] pub struct DecimalLiteralRepresentation { threshold: u64, } @@ -455,11 +454,12 @@ impl EarlyLintPass for DecimalLiteralRepresentation { } impl DecimalLiteralRepresentation { - #[must_use] - pub fn new(threshold: u64) -> Self { - Self { threshold } + pub fn new(conf: &'static Conf) -> Self { + Self { + threshold: conf.literal_representation_threshold, + } } - fn check_lit(self, cx: &EarlyContext<'_>, lit: token::Lit, span: Span) { + fn check_lit(&self, cx: &EarlyContext<'_>, lit: token::Lit, span: Span) { // Lint integral literals. if let Ok(lit_kind) = LitKind::from_token_lit(lit) && let LitKind::Int(val, _) = lit_kind diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index 64ea591993d..7c2a8098af2 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -23,6 +23,7 @@ mod while_let_loop; mod while_let_on_iterator; use clippy_config::msrvs::Msrv; +use clippy_config::Conf; use clippy_utils::higher; use rustc_hir::{Expr, ExprKind, LoopSource, Pat}; use rustc_lint::{LateContext, LateLintPass}; @@ -717,10 +718,10 @@ pub struct Loops { enforce_iter_loop_reborrow: bool, } impl Loops { - pub fn new(msrv: Msrv, enforce_iter_loop_reborrow: bool) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - msrv, - enforce_iter_loop_reborrow, + msrv: conf.msrv.clone(), + enforce_iter_loop_reborrow: conf.enforce_iter_loop_reborrow, } } } diff --git a/clippy_lints/src/loops/mut_range_bound.rs b/clippy_lints/src/loops/mut_range_bound.rs index 6c6a9a1a2e0..21f9a71f2c5 100644 --- a/clippy_lints/src/loops/mut_range_bound.rs +++ b/clippy_lints/src/loops/mut_range_bound.rs @@ -8,6 +8,7 @@ use rustc_lint::LateContext; use rustc_middle::mir::FakeReadCause; use rustc_middle::ty; use rustc_span::Span; +use std::ops::ControlFlow; pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>, body: &Expr<'_>) { if let Some(higher::Range { @@ -114,7 +115,6 @@ impl MutatePairDelegate<'_, '_> { struct BreakAfterExprVisitor { hir_id: HirId, past_expr: bool, - past_candidate: bool, break_after_expr: bool, } @@ -123,7 +123,6 @@ impl BreakAfterExprVisitor { let mut visitor = BreakAfterExprVisitor { hir_id, past_expr: false, - past_candidate: false, break_after_expr: false, }; @@ -135,21 +134,19 @@ impl BreakAfterExprVisitor { } impl<'tcx> Visitor<'tcx> for BreakAfterExprVisitor { - fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { - if self.past_candidate { - return; - } - + type Result = ControlFlow<()>; + fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) -> ControlFlow<()> { if expr.hir_id == self.hir_id { self.past_expr = true; + ControlFlow::Continue(()) } else if self.past_expr { if matches!(&expr.kind, ExprKind::Break(..)) { self.break_after_expr = true; } - self.past_candidate = true; + ControlFlow::Break(()) } else { - intravisit::walk_expr(self, expr); + intravisit::walk_expr(self, expr) } } } diff --git a/clippy_lints/src/loops/single_element_loop.rs b/clippy_lints/src/loops/single_element_loop.rs index 108fdb69775..2b41911e8ec 100644 --- a/clippy_lints/src/loops/single_element_loop.rs +++ b/clippy_lints/src/loops/single_element_loop.rs @@ -5,7 +5,7 @@ use clippy_utils::visitors::contains_break_or_continue; use rustc_ast::util::parser::PREC_PREFIX; use rustc_ast::Mutability; use rustc_errors::Applicability; -use rustc_hir::{is_range_literal, BorrowKind, Expr, ExprKind, Pat}; +use rustc_hir::{is_range_literal, BorrowKind, Expr, ExprKind, Pat, PatKind}; use rustc_lint::LateContext; use rustc_span::edition::Edition; use rustc_span::sym; @@ -70,7 +70,10 @@ pub(super) fn check<'tcx>( && !contains_break_or_continue(body) { let mut applicability = Applicability::MachineApplicable; - let pat_snip = snippet_with_applicability(cx, pat.span, "..", &mut applicability); + let mut pat_snip = snippet_with_applicability(cx, pat.span, "..", &mut applicability); + if matches!(pat.kind, PatKind::Or(..)) { + pat_snip = format!("({pat_snip})").into(); + } let mut arg_snip = snippet_with_applicability(cx, arg_expression.span, "..", &mut applicability); let mut block_str = snippet_with_applicability(cx, block.span, "..", &mut applicability).into_owned(); block_str.remove(0); diff --git a/clippy_lints/src/loops/while_immutable_condition.rs b/clippy_lints/src/loops/while_immutable_condition.rs index 3dff826cb85..e7b3a2c4973 100644 --- a/clippy_lints/src/loops/while_immutable_condition.rs +++ b/clippy_lints/src/loops/while_immutable_condition.rs @@ -7,6 +7,7 @@ use rustc_hir::def_id::DefIdMap; use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_hir::{Expr, ExprKind, HirIdSet, QPath}; use rustc_lint::LateContext; +use std::ops::ControlFlow; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, expr: &'tcx Expr<'_>) { if constant(cx, cx.typeck_results(), cond).is_some() { @@ -35,11 +36,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, expr: &' }; let mutable_static_in_cond = var_visitor.def_ids.items().any(|(_, v)| *v); - let mut has_break_or_return_visitor = HasBreakOrReturnVisitor { - has_break_or_return: false, - }; - has_break_or_return_visitor.visit_expr(expr); - let has_break_or_return = has_break_or_return_visitor.has_break_or_return; + let mut has_break_or_return_visitor = HasBreakOrReturnVisitor; + let has_break_or_return = has_break_or_return_visitor.visit_expr(expr).is_break(); if no_cond_variable_mutated && !mutable_static_in_cond { span_lint_and_then( @@ -59,25 +57,19 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, expr: &' } } -struct HasBreakOrReturnVisitor { - has_break_or_return: bool, -} +struct HasBreakOrReturnVisitor; impl<'tcx> Visitor<'tcx> for HasBreakOrReturnVisitor { - fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - if self.has_break_or_return { - return; - } - + type Result = ControlFlow<()>; + fn visit_expr(&mut self, expr: &'tcx Expr<'_>) -> ControlFlow<()> { match expr.kind { ExprKind::Ret(_) | ExprKind::Break(_, _) => { - self.has_break_or_return = true; - return; + return ControlFlow::Break(()); }, _ => {}, } - walk_expr(self, expr); + walk_expr(self, expr) } } diff --git a/clippy_lints/src/macro_metavars_in_unsafe.rs b/clippy_lints/src/macro_metavars_in_unsafe.rs index d1ae243877d..fed58f7ff14 100644 --- a/clippy_lints/src/macro_metavars_in_unsafe.rs +++ b/clippy_lints/src/macro_metavars_in_unsafe.rs @@ -1,6 +1,4 @@ -use std::collections::btree_map::Entry; -use std::collections::BTreeMap; - +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::is_lint_allowed; use itertools::Itertools; @@ -10,6 +8,8 @@ use rustc_hir::{BlockCheckMode, Expr, ExprKind, HirId, Stmt, UnsafeSource}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; use rustc_span::{sym, Span, SyntaxContext}; +use std::collections::btree_map::Entry; +use std::collections::BTreeMap; declare_clippy_lint! { /// ### What it does @@ -90,9 +90,8 @@ pub enum MetavarState { ReferencedInSafe, } -#[derive(Default)] pub struct ExprMetavarsInUnsafe { - pub warn_unsafe_macro_metavars_in_private_macros: bool, + warn_unsafe_macro_metavars_in_private_macros: bool, /// A metavariable can be expanded more than once, potentially across multiple bodies, so it /// requires some state kept across HIR nodes to make it possible to delay a warning /// and later undo: @@ -106,7 +105,16 @@ pub struct ExprMetavarsInUnsafe { /// } /// } /// ``` - pub metavar_expns: BTreeMap<Span, MetavarState>, + metavar_expns: BTreeMap<Span, MetavarState>, +} + +impl ExprMetavarsInUnsafe { + pub fn new(conf: &'static Conf) -> Self { + Self { + warn_unsafe_macro_metavars_in_private_macros: conf.warn_unsafe_macro_metavars_in_private_macros, + metavar_expns: BTreeMap::new(), + } + } } struct BodyVisitor<'a, 'tcx> { diff --git a/clippy_lints/src/manual_bits.rs b/clippy_lints/src/manual_bits.rs index d9f6be6dc2b..5cbdc7f864a 100644 --- a/clippy_lints/src/manual_bits.rs +++ b/clippy_lints/src/manual_bits.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::get_parent_expr; use clippy_utils::source::snippet_with_context; @@ -34,15 +35,15 @@ declare_clippy_lint! { "manual implementation of `size_of::<T>() * 8` can be simplified with `T::BITS`" } -#[derive(Clone)] pub struct ManualBits { msrv: Msrv, } impl ManualBits { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } } } diff --git a/clippy_lints/src/manual_clamp.rs b/clippy_lints/src/manual_clamp.rs index e2ab4415518..a79ad018a04 100644 --- a/clippy_lints/src/manual_clamp.rs +++ b/clippy_lints/src/manual_clamp.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::higher::If; @@ -97,8 +98,10 @@ pub struct ManualClamp { } impl ManualClamp { - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } } } diff --git a/clippy_lints/src/manual_hash_one.rs b/clippy_lints/src/manual_hash_one.rs index daa8101aa5f..1c568b1b74f 100644 --- a/clippy_lints/src/manual_hash_one.rs +++ b/clippy_lints/src/manual_hash_one.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::source::snippet_opt; use clippy_utils::visitors::{is_local_used, local_used_once}; @@ -51,9 +52,10 @@ pub struct ManualHashOne { } impl ManualHashOne { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } } } diff --git a/clippy_lints/src/manual_is_ascii_check.rs b/clippy_lints/src/manual_is_ascii_check.rs index 6f6ba1852a6..a9f21d34e4c 100644 --- a/clippy_lints/src/manual_is_ascii_check.rs +++ b/clippy_lints/src/manual_is_ascii_check.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::matching_root_macro_call; use clippy_utils::sugg::Sugg; @@ -62,9 +63,10 @@ pub struct ManualIsAsciiCheck { } impl ManualIsAsciiCheck { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } } } diff --git a/clippy_lints/src/manual_main_separator_str.rs b/clippy_lints/src/manual_main_separator_str.rs index 8e8cdc3fb07..db491a8c8f6 100644 --- a/clippy_lints/src/manual_main_separator_str.rs +++ b/clippy_lints/src/manual_main_separator_str.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::{is_trait_method, match_def_path, paths, peel_hir_expr_refs}; use rustc_errors::Applicability; @@ -37,9 +38,10 @@ pub struct ManualMainSeparatorStr { } impl ManualMainSeparatorStr { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } } } diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs index 73a505fd73f..28cfe22835a 100644 --- a/clippy_lints/src/manual_non_exhaustive.rs +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::is_doc_hidden; use clippy_utils::source::snippet_opt; @@ -67,9 +68,10 @@ pub struct ManualNonExhaustiveStruct { } impl ManualNonExhaustiveStruct { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } } } @@ -83,10 +85,9 @@ pub struct ManualNonExhaustiveEnum { } impl ManualNonExhaustiveEnum { - #[must_use] - pub fn new(msrv: Msrv) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - msrv, + msrv: conf.msrv.clone(), constructed_enum_variants: FxHashSet::default(), potential_enums: Vec::new(), } diff --git a/clippy_lints/src/manual_rem_euclid.rs b/clippy_lints/src/manual_rem_euclid.rs index b518dc26937..78a750f0dcd 100644 --- a/clippy_lints/src/manual_rem_euclid.rs +++ b/clippy_lints/src/manual_rem_euclid.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::consts::{constant_full_int, FullInt}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_context; @@ -38,9 +39,10 @@ pub struct ManualRemEuclid { } impl ManualRemEuclid { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } } } diff --git a/clippy_lints/src/manual_retain.rs b/clippy_lints/src/manual_retain.rs index 8f7b8abd0c3..09f6362b4dd 100644 --- a/clippy_lints/src/manual_retain.rs +++ b/clippy_lints/src/manual_retain.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; @@ -59,9 +60,10 @@ pub struct ManualRetain { } impl ManualRetain { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } } } diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index 6a523ad1564..686ecccf829 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; use clippy_utils::source::snippet; @@ -50,9 +51,10 @@ pub struct ManualStrip { } impl ManualStrip { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } } } diff --git a/clippy_lints/src/matches/manual_unwrap_or.rs b/clippy_lints/src/matches/manual_unwrap_or.rs index 0940fc3219b..85a08f81c2f 100644 --- a/clippy_lints/src/matches/manual_unwrap_or.rs +++ b/clippy_lints/src/matches/manual_unwrap_or.rs @@ -35,6 +35,17 @@ pub(super) fn check_if_let<'tcx>( else_expr: &'tcx Expr<'_>, ) { let ty = cx.typeck_results().expr_ty(let_expr); + let then_ty = cx.typeck_results().expr_ty(then_expr); + // The signature is `fn unwrap_or<T>(self: Option<T>, default: T) -> T`. + // When `expr_adjustments(then_expr).is_empty()`, `T` should equate to `default`'s type. + // Otherwise, type error will occur. + if cx.typeck_results().expr_adjustments(then_expr).is_empty() + && let rustc_middle::ty::Adt(_did, args) = ty.kind() + && let Some(some_ty) = args.first().and_then(|arg| arg.as_type()) + && some_ty != then_ty + { + return; + } check_and_lint(cx, expr, let_pat, let_expr, then_expr, peel_blocks(else_expr), ty); } diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index 22a299ae3d8..cec3504ed8d 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -25,6 +25,7 @@ mod try_err; mod wild_in_or_pats; use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::source::walk_span_to_context; use clippy_utils::{higher, in_constant, is_direct_expn_of, is_span_match, span_contains_cfg}; use rustc_hir::{Arm, Expr, ExprKind, LetStmt, MatchSource, Pat, PatKind}; @@ -980,10 +981,9 @@ pub struct Matches { } impl Matches { - #[must_use] - pub fn new(msrv: Msrv) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - msrv, + msrv: conf.msrv.clone(), infallible_destructuring_match_linted: false, } } diff --git a/clippy_lints/src/matches/significant_drop_in_scrutinee.rs b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs index 9047c9627d9..9c2f42d2187 100644 --- a/clippy_lints/src/matches/significant_drop_in_scrutinee.rs +++ b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs @@ -232,7 +232,7 @@ impl<'a, 'tcx> SigDropChecker<'a, 'tcx> { enum SigDropHolder { /// No values with significant drop present in this expression. /// - /// Expressions that we've emited lints do not count. + /// Expressions that we've emitted lints do not count. None, /// Some field in this expression references to values with significant drop. /// @@ -426,7 +426,7 @@ fn ty_has_erased_regions(ty: Ty<'_>) -> bool { impl<'a, 'tcx> Visitor<'tcx> for SigDropHelper<'a, 'tcx> { fn visit_expr(&mut self, ex: &'tcx Expr<'_>) { - // We've emited a lint on some neighborhood expression. That lint will suggest to move out the + // We've emitted a lint on some neighborhood expression. That lint will suggest to move out the // _parent_ expression (not the expression itself). Since we decide to move out the parent // expression, it is pointless to continue to process the current expression. if self.sig_drop_holder == SigDropHolder::Moved { @@ -450,7 +450,7 @@ impl<'a, 'tcx> Visitor<'tcx> for SigDropHelper<'a, 'tcx> { ExprKind::Assign(lhs, _, _) | ExprKind::AssignOp(_, lhs, _) if lhs.hir_id == ex.hir_id && self.sig_drop_holder == SigDropHolder::Moved => { - // Never move out only the assignee. Instead, we should always move out the whole assigment. + // Never move out only the assignee. Instead, we should always move out the whole assignment. self.replace_current_sig_drop(parent_ex.span, true, 0); }, _ => { diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs index 578aa7989e7..72a5945ad9b 100644 --- a/clippy_lints/src/mem_replace.rs +++ b/clippy_lints/src/mem_replace.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::Sugg; @@ -217,9 +218,10 @@ pub struct MemReplace { } impl MemReplace { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } } } diff --git a/clippy_lints/src/methods/bind_instead_of_map.rs b/clippy_lints/src/methods/bind_instead_of_map.rs index fb440ce656e..20f3722e173 100644 --- a/clippy_lints/src/methods/bind_instead_of_map.rs +++ b/clippy_lints/src/methods/bind_instead_of_map.rs @@ -10,59 +10,80 @@ use rustc_hir::{LangItem, QPath}; use rustc_lint::LateContext; use rustc_span::Span; -pub(crate) struct OptionAndThenSome; - -impl BindInsteadOfMap for OptionAndThenSome { - const VARIANT_LANG_ITEM: LangItem = LangItem::OptionSome; - const BAD_METHOD_NAME: &'static str = "and_then"; - const GOOD_METHOD_NAME: &'static str = "map"; +pub(super) fn check_and_then_some( + cx: &LateContext<'_>, + expr: &hir::Expr<'_>, + recv: &hir::Expr<'_>, + arg: &hir::Expr<'_>, +) -> bool { + BindInsteadOfMap { + variant_lang_item: LangItem::OptionSome, + bad_method_name: "and_then", + good_method_name: "map", + } + .check(cx, expr, recv, arg) } -pub(crate) struct ResultAndThenOk; - -impl BindInsteadOfMap for ResultAndThenOk { - const VARIANT_LANG_ITEM: LangItem = LangItem::ResultOk; - const BAD_METHOD_NAME: &'static str = "and_then"; - const GOOD_METHOD_NAME: &'static str = "map"; +pub(super) fn check_and_then_ok( + cx: &LateContext<'_>, + expr: &hir::Expr<'_>, + recv: &hir::Expr<'_>, + arg: &hir::Expr<'_>, +) -> bool { + BindInsteadOfMap { + variant_lang_item: LangItem::ResultOk, + bad_method_name: "and_then", + good_method_name: "map", + } + .check(cx, expr, recv, arg) } -pub(crate) struct ResultOrElseErrInfo; - -impl BindInsteadOfMap for ResultOrElseErrInfo { - const VARIANT_LANG_ITEM: LangItem = LangItem::ResultErr; - const BAD_METHOD_NAME: &'static str = "or_else"; - const GOOD_METHOD_NAME: &'static str = "map_err"; +pub(super) fn check_or_else_err( + cx: &LateContext<'_>, + expr: &hir::Expr<'_>, + recv: &hir::Expr<'_>, + arg: &hir::Expr<'_>, +) -> bool { + BindInsteadOfMap { + variant_lang_item: LangItem::ResultErr, + bad_method_name: "or_else", + good_method_name: "map_err", + } + .check(cx, expr, recv, arg) } -pub(crate) trait BindInsteadOfMap { - const VARIANT_LANG_ITEM: LangItem; - const BAD_METHOD_NAME: &'static str; - const GOOD_METHOD_NAME: &'static str; +struct BindInsteadOfMap { + variant_lang_item: LangItem, + bad_method_name: &'static str, + good_method_name: &'static str, +} - fn no_op_msg(cx: &LateContext<'_>) -> Option<String> { - let variant_id = cx.tcx.lang_items().get(Self::VARIANT_LANG_ITEM)?; +impl BindInsteadOfMap { + fn no_op_msg(&self, cx: &LateContext<'_>) -> Option<String> { + let variant_id = cx.tcx.lang_items().get(self.variant_lang_item)?; let item_id = cx.tcx.parent(variant_id); Some(format!( "using `{}.{}({})`, which is a no-op", cx.tcx.item_name(item_id), - Self::BAD_METHOD_NAME, + self.bad_method_name, cx.tcx.item_name(variant_id), )) } - fn lint_msg(cx: &LateContext<'_>) -> Option<String> { - let variant_id = cx.tcx.lang_items().get(Self::VARIANT_LANG_ITEM)?; + fn lint_msg(&self, cx: &LateContext<'_>) -> Option<String> { + let variant_id = cx.tcx.lang_items().get(self.variant_lang_item)?; let item_id = cx.tcx.parent(variant_id); Some(format!( "using `{}.{}(|x| {}(y))`, which is more succinctly expressed as `{}(|x| y)`", cx.tcx.item_name(item_id), - Self::BAD_METHOD_NAME, + self.bad_method_name, cx.tcx.item_name(variant_id), - Self::GOOD_METHOD_NAME + self.good_method_name, )) } fn lint_closure_autofixable( + &self, cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, @@ -71,9 +92,9 @@ pub(crate) trait BindInsteadOfMap { ) -> bool { if let hir::ExprKind::Call(some_expr, [inner_expr]) = closure_expr.kind && let hir::ExprKind::Path(QPath::Resolved(_, path)) = some_expr.kind - && Self::is_variant(cx, path.res) + && self.is_variant(cx, path.res) && !contains_return(inner_expr) - && let Some(msg) = Self::lint_msg(cx) + && let Some(msg) = self.lint_msg(cx) { let mut app = Applicability::MachineApplicable; let some_inner_snip = snippet_with_context(cx, inner_expr.span, closure_expr.span.ctxt(), "_", &mut app).0; @@ -82,7 +103,7 @@ pub(crate) trait BindInsteadOfMap { let option_snip = snippet(cx, recv.span, ".."); let note = format!( "{option_snip}.{}({closure_args_snip} {some_inner_snip})", - Self::GOOD_METHOD_NAME + self.good_method_name ); span_lint_and_sugg(cx, BIND_INSTEAD_OF_MAP, expr.span, msg, "try", note, app); true @@ -91,13 +112,13 @@ pub(crate) trait BindInsteadOfMap { } } - fn lint_closure(cx: &LateContext<'_>, expr: &hir::Expr<'_>, closure_expr: &hir::Expr<'_>) -> bool { + fn lint_closure(&self, cx: &LateContext<'_>, expr: &hir::Expr<'_>, closure_expr: &hir::Expr<'_>) -> bool { let mut suggs = Vec::new(); let can_sugg: bool = find_all_ret_expressions(cx, closure_expr, |ret_expr| { if !ret_expr.span.from_expansion() && let hir::ExprKind::Call(func_path, [arg]) = ret_expr.kind && let hir::ExprKind::Path(QPath::Resolved(_, path)) = func_path.kind - && Self::is_variant(cx, path.res) + && self.is_variant(cx, path.res) && !contains_return(arg) { suggs.push((ret_expr.span, arg.span.source_callsite())); @@ -108,7 +129,7 @@ pub(crate) trait BindInsteadOfMap { }); let (span, msg) = if can_sugg && let hir::ExprKind::MethodCall(segment, ..) = expr.kind - && let Some(msg) = Self::lint_msg(cx) + && let Some(msg) = self.lint_msg(cx) { (segment.ident.span, msg) } else { @@ -119,7 +140,7 @@ pub(crate) trait BindInsteadOfMap { diag, "try", Applicability::MachineApplicable, - std::iter::once((span, Self::GOOD_METHOD_NAME.into())).chain( + std::iter::once((span, self.good_method_name.into())).chain( suggs .into_iter() .map(|(span1, span2)| (span1, snippet(cx, span2, "_").into())), @@ -130,9 +151,9 @@ pub(crate) trait BindInsteadOfMap { } /// Lint use of `_.and_then(|x| Some(y))` for `Option`s - fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) -> bool { + fn check(&self, cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) -> bool { if let Some(adt) = cx.typeck_results().expr_ty(recv).ty_adt_def() - && let Some(vid) = cx.tcx.lang_items().get(Self::VARIANT_LANG_ITEM) + && let Some(vid) = cx.tcx.lang_items().get(self.variant_lang_item) && adt.did() == cx.tcx.parent(vid) { } else { @@ -144,15 +165,15 @@ pub(crate) trait BindInsteadOfMap { let closure_body = cx.tcx.hir().body(body); let closure_expr = peel_blocks(closure_body.value); - if Self::lint_closure_autofixable(cx, expr, recv, closure_expr, fn_decl_span) { + if self.lint_closure_autofixable(cx, expr, recv, closure_expr, fn_decl_span) { true } else { - Self::lint_closure(cx, expr, closure_expr) + self.lint_closure(cx, expr, closure_expr) } }, // `_.and_then(Some)` case, which is no-op. - hir::ExprKind::Path(QPath::Resolved(_, path)) if Self::is_variant(cx, path.res) => { - if let Some(msg) = Self::no_op_msg(cx) { + hir::ExprKind::Path(QPath::Resolved(_, path)) if self.is_variant(cx, path.res) => { + if let Some(msg) = self.no_op_msg(cx) { span_lint_and_sugg( cx, BIND_INSTEAD_OF_MAP, @@ -169,9 +190,9 @@ pub(crate) trait BindInsteadOfMap { } } - fn is_variant(cx: &LateContext<'_>, res: Res) -> bool { + fn is_variant(&self, cx: &LateContext<'_>, res: Res) -> bool { if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res { - if let Some(variant_id) = cx.tcx.lang_items().get(Self::VARIANT_LANG_ITEM) { + if let Some(variant_id) = cx.tcx.lang_items().get(self.variant_lang_item) { return cx.tcx.parent(id) == variant_id; } } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index a846552cddf..aa4cad9342a 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -131,8 +131,8 @@ mod waker_clone_wake; mod wrong_self_convention; mod zst_offset; -use bind_instead_of_map::BindInsteadOfMap; use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::macros::FormatArgsStorage; @@ -4131,27 +4131,20 @@ pub struct Methods { msrv: Msrv, allow_expect_in_tests: bool, allow_unwrap_in_tests: bool, - allowed_dotfiles: FxHashSet<String>, + allowed_dotfiles: FxHashSet<&'static str>, format_args: FormatArgsStorage, } impl Methods { - #[must_use] - pub fn new( - avoid_breaking_exported_api: bool, - msrv: Msrv, - allow_expect_in_tests: bool, - allow_unwrap_in_tests: bool, - mut allowed_dotfiles: FxHashSet<String>, - format_args: FormatArgsStorage, - ) -> Self { - allowed_dotfiles.extend(DEFAULT_ALLOWED_DOTFILES.iter().map(ToString::to_string)); + pub fn new(conf: &'static Conf, format_args: FormatArgsStorage) -> Self { + let mut allowed_dotfiles: FxHashSet<_> = conf.allowed_dotfiles.iter().map(|s| &**s).collect(); + allowed_dotfiles.extend(DEFAULT_ALLOWED_DOTFILES); Self { - avoid_breaking_exported_api, - msrv, - allow_expect_in_tests, - allow_unwrap_in_tests, + avoid_breaking_exported_api: conf.avoid_breaking_exported_api, + msrv: conf.msrv.clone(), + allow_expect_in_tests: conf.allow_expect_in_tests, + allow_unwrap_in_tests: conf.allow_unwrap_in_tests, allowed_dotfiles, format_args, } @@ -4512,8 +4505,8 @@ impl Methods { } }, ("and_then", [arg]) => { - let biom_option_linted = bind_instead_of_map::OptionAndThenSome::check(cx, expr, recv, arg); - let biom_result_linted = bind_instead_of_map::ResultAndThenOk::check(cx, expr, recv, arg); + let biom_option_linted = bind_instead_of_map::check_and_then_some(cx, expr, recv, arg); + let biom_result_linted = bind_instead_of_map::check_and_then_ok(cx, expr, recv, arg); if !biom_option_linted && !biom_result_linted { unnecessary_lazy_eval::check(cx, expr, recv, arg, "and"); } @@ -4853,7 +4846,7 @@ impl Methods { open_options::check(cx, expr, recv); }, ("or_else", [arg]) => { - if !bind_instead_of_map::ResultOrElseErrInfo::check(cx, expr, recv, arg) { + if !bind_instead_of_map::check_or_else_err(cx, expr, recv, arg) { unnecessary_lazy_eval::check(cx, expr, recv, arg, "or"); } }, diff --git a/clippy_lints/src/methods/option_map_unwrap_or.rs b/clippy_lints/src/methods/option_map_unwrap_or.rs index 3d326bc99f9..ed3bed42eb3 100644 --- a/clippy_lints/src/methods/option_map_unwrap_or.rs +++ b/clippy_lints/src/methods/option_map_unwrap_or.rs @@ -10,6 +10,7 @@ use rustc_hir::{ExprKind, HirId, Node, PatKind, Path, QPath}; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; use rustc_span::{sym, Span}; +use std::ops::ControlFlow; use super::MAP_UNWRAP_OR; @@ -54,15 +55,14 @@ pub(super) fn check<'tcx>( let mut reference_visitor = ReferenceVisitor { cx, identifiers: unwrap_visitor.identifiers, - found_reference: false, unwrap_or_span: unwrap_arg.span, }; let map = cx.tcx.hir(); let body = map.body_owned_by(map.enclosing_body_owner(expr.hir_id)); - reference_visitor.visit_body(body); - if reference_visitor.found_reference { + // Visit the body, and return if we've found a reference + if reference_visitor.visit_body(body).is_break() { return; } } @@ -151,29 +151,27 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrapVisitor<'a, 'tcx> { struct ReferenceVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, identifiers: FxHashSet<HirId>, - found_reference: bool, unwrap_or_span: Span, } impl<'a, 'tcx> Visitor<'tcx> for ReferenceVisitor<'a, 'tcx> { type NestedFilter = nested_filter::All; - fn visit_expr(&mut self, expr: &'tcx rustc_hir::Expr<'_>) { + type Result = ControlFlow<()>; + fn visit_expr(&mut self, expr: &'tcx rustc_hir::Expr<'_>) -> ControlFlow<()> { // If we haven't found a reference yet, check if this references // one of the locals that was moved in the `unwrap_or` argument. // We are only interested in exprs that appear before the `unwrap_or` call. - if !self.found_reference { - if expr.span < self.unwrap_or_span - && let ExprKind::Path(ref path) = expr.kind - && let QPath::Resolved(_, path) = path - && let Res::Local(local_id) = path.res - && let Node::Pat(pat) = self.cx.tcx.hir_node(local_id) - && let PatKind::Binding(_, local_id, ..) = pat.kind - && self.identifiers.contains(&local_id) - { - self.found_reference = true; - } - rustc_hir::intravisit::walk_expr(self, expr); + if expr.span < self.unwrap_or_span + && let ExprKind::Path(ref path) = expr.kind + && let QPath::Resolved(_, path) = path + && let Res::Local(local_id) = path.res + && let Node::Pat(pat) = self.cx.tcx.hir_node(local_id) + && let PatKind::Binding(_, local_id, ..) = pat.kind + && self.identifiers.contains(&local_id) + { + return ControlFlow::Break(()); } + rustc_hir::intravisit::walk_expr(self, expr) } fn nested_visit_map(&mut self) -> Self::Map { diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index 583e04fb4b1..e4326cb958e 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -1,8 +1,13 @@ +use std::ops::ControlFlow; + use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::eager_or_lazy::switch_to_lazy_eval; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{expr_type_is_certain, implements_trait, is_type_diagnostic_item}; -use clippy_utils::{contains_return, is_default_equivalent, is_default_equivalent_call, last_path_segment}; +use clippy_utils::visitors::for_each_expr; +use clippy_utils::{ + contains_return, is_default_equivalent, is_default_equivalent_call, last_path_segment, peel_blocks, +}; use rustc_errors::Applicability; use rustc_lint::LateContext; use rustc_middle::ty; @@ -13,7 +18,7 @@ use {rustc_ast as ast, rustc_hir as hir}; use super::{OR_FUN_CALL, UNWRAP_OR_DEFAULT}; /// Checks for the `OR_FUN_CALL` lint. -#[allow(clippy::too_many_lines)] +#[expect(clippy::too_many_lines)] pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, @@ -26,7 +31,6 @@ pub(super) fn check<'tcx>( /// `or_insert(T::new())` or `or_insert(T::default())`. /// Similarly checks for `unwrap_or_else(T::new)`, `unwrap_or_else(T::default)`, /// `or_insert_with(T::new)` or `or_insert_with(T::default)`. - #[allow(clippy::too_many_arguments)] fn check_unwrap_or_default( cx: &LateContext<'_>, name: &str, @@ -70,18 +74,31 @@ pub(super) fn check<'tcx>( }; let receiver_ty = cx.typeck_results().expr_ty_adjusted(receiver).peel_refs(); - let has_suggested_method = receiver_ty.ty_adt_def().is_some_and(|adt_def| { + let Some(suggested_method_def_id) = receiver_ty.ty_adt_def().and_then(|adt_def| { cx.tcx .inherent_impls(adt_def.did()) .into_iter() .flatten() .flat_map(|impl_id| cx.tcx.associated_items(impl_id).filter_by_name_unhygienic(sugg)) - .any(|assoc| { - assoc.fn_has_self_parameter + .find_map(|assoc| { + if assoc.fn_has_self_parameter && cx.tcx.fn_sig(assoc.def_id).skip_binder().inputs().skip_binder().len() == 1 + { + Some(assoc.def_id) + } else { + None + } }) - }); - if !has_suggested_method { + }) else { + return false; + }; + let in_sugg_method_implementation = { + matches!( + suggested_method_def_id.as_local(), + Some(local_def_id) if local_def_id == cx.tcx.hir().get_parent_item(receiver.hir_id).def_id + ) + }; + if in_sugg_method_implementation { return false; } @@ -110,8 +127,8 @@ pub(super) fn check<'tcx>( } /// Checks for `*or(foo())`. - #[allow(clippy::too_many_arguments)] - fn check_general_case<'tcx>( + #[expect(clippy::too_many_arguments)] + fn check_or_fn_call<'tcx>( cx: &LateContext<'tcx>, name: &str, method_span: Span, @@ -122,7 +139,7 @@ pub(super) fn check<'tcx>( span: Span, // None if lambda is required fun_span: Option<Span>, - ) { + ) -> bool { // (path, fn_has_argument, methods, suffix) const KNOW_TYPES: [(Symbol, bool, &[&str], &str); 4] = [ (sym::BTreeEntry, false, &["or_insert"], "with"), @@ -172,54 +189,68 @@ pub(super) fn check<'tcx>( format!("{name}_{suffix}({sugg})"), app, ); - } - } - - let extract_inner_arg = |arg: &'tcx hir::Expr<'_>| { - if let hir::ExprKind::Block( - hir::Block { - stmts: [], - expr: Some(expr), - .. - }, - _, - ) = arg.kind - { - expr + true } else { - arg + false } - }; + } if let [arg] = args { - let inner_arg = extract_inner_arg(arg); - match inner_arg.kind { - hir::ExprKind::Call(fun, or_args) => { - let or_has_args = !or_args.is_empty(); - if or_has_args - || !check_unwrap_or_default(cx, name, receiver, fun, Some(inner_arg), expr.span, method_span) - { - let fun_span = if or_has_args { None } else { Some(fun.span) }; - check_general_case(cx, name, method_span, receiver, arg, None, expr.span, fun_span); - } - }, - hir::ExprKind::Path(..) | hir::ExprKind::Closure(..) => { - check_unwrap_or_default(cx, name, receiver, inner_arg, None, expr.span, method_span); - }, - hir::ExprKind::Index(..) | hir::ExprKind::MethodCall(..) => { - check_general_case(cx, name, method_span, receiver, arg, None, expr.span, None); - }, - _ => (), - } + let inner_arg = peel_blocks(arg); + for_each_expr(cx, inner_arg, |ex| { + // `or_fun_call` lint needs to take nested expr into account, + // but `unwrap_or_default` lint doesn't, we don't want something like: + // `opt.unwrap_or(Foo { inner: String::default(), other: 1 })` to get replaced by + // `opt.unwrap_or_default()`. + let is_nested_expr = ex.hir_id != inner_arg.hir_id; + + let is_triggered = match ex.kind { + hir::ExprKind::Call(fun, fun_args) => { + let inner_fun_has_args = !fun_args.is_empty(); + let fun_span = if inner_fun_has_args || is_nested_expr { + None + } else { + Some(fun.span) + }; + (!inner_fun_has_args + && !is_nested_expr + && check_unwrap_or_default(cx, name, receiver, fun, Some(ex), expr.span, method_span)) + || check_or_fn_call(cx, name, method_span, receiver, arg, None, expr.span, fun_span) + }, + hir::ExprKind::Path(..) | hir::ExprKind::Closure(..) if !is_nested_expr => { + check_unwrap_or_default(cx, name, receiver, ex, None, expr.span, method_span) + }, + hir::ExprKind::Index(..) | hir::ExprKind::MethodCall(..) => { + check_or_fn_call(cx, name, method_span, receiver, arg, None, expr.span, None) + }, + _ => false, + }; + + if is_triggered { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) + } + }); } // `map_or` takes two arguments if let [arg, lambda] = args { - let inner_arg = extract_inner_arg(arg); - if let hir::ExprKind::Call(fun, or_args) = inner_arg.kind { - let fun_span = if or_args.is_empty() { Some(fun.span) } else { None }; - check_general_case(cx, name, method_span, receiver, arg, Some(lambda), expr.span, fun_span); - } + let inner_arg = peel_blocks(arg); + for_each_expr(cx, inner_arg, |ex| { + let is_top_most_expr = ex.hir_id == inner_arg.hir_id; + if let hir::ExprKind::Call(fun, fun_args) = ex.kind { + let fun_span = if fun_args.is_empty() && is_top_most_expr { + Some(fun.span) + } else { + None + }; + if check_or_fn_call(cx, name, method_span, receiver, arg, Some(lambda), expr.span, fun_span) { + return ControlFlow::Break(()); + } + } + ControlFlow::Continue(()) + }); } } diff --git a/clippy_lints/src/methods/path_ends_with_ext.rs b/clippy_lints/src/methods/path_ends_with_ext.rs index 29f44ec2a4d..cfb823dbf5d 100644 --- a/clippy_lints/src/methods/path_ends_with_ext.rs +++ b/clippy_lints/src/methods/path_ends_with_ext.rs @@ -21,7 +21,7 @@ pub(super) fn check( path: &Expr<'_>, expr: &Expr<'_>, msrv: &Msrv, - allowed_dotfiles: &FxHashSet<String>, + allowed_dotfiles: &FxHashSet<&'static str>, ) { if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv).peel_refs(), sym::Path) && !path.span.from_expansion() diff --git a/clippy_lints/src/methods/unnecessary_sort_by.rs b/clippy_lints/src/methods/unnecessary_sort_by.rs index 6911da69b94..a92fe9e55a4 100644 --- a/clippy_lints/src/methods/unnecessary_sort_by.rs +++ b/clippy_lints/src/methods/unnecessary_sort_by.rs @@ -5,7 +5,8 @@ use clippy_utils::ty::implements_trait; use rustc_errors::Applicability; use rustc_hir::{Closure, Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath}; use rustc_lint::LateContext; -use rustc_middle::ty::{self, GenericArgKind}; +use rustc_middle::ty; +use rustc_middle::ty::GenericArgKind; use rustc_span::sym; use rustc_span::symbol::Ident; use std::iter; diff --git a/clippy_lints/src/min_ident_chars.rs b/clippy_lints/src/min_ident_chars.rs index e43b712021a..53fa444e93c 100644 --- a/clippy_lints/src/min_ident_chars.rs +++ b/clippy_lints/src/min_ident_chars.rs @@ -1,3 +1,4 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint; use clippy_utils::is_from_proc_macro; use rustc_data_structures::fx::FxHashSet; @@ -39,13 +40,19 @@ declare_clippy_lint! { } impl_lint_pass!(MinIdentChars => [MIN_IDENT_CHARS]); -#[derive(Clone)] pub struct MinIdentChars { - pub allowed_idents_below_min_chars: FxHashSet<String>, - pub min_ident_chars_threshold: u64, + allowed_idents_below_min_chars: &'static FxHashSet<String>, + min_ident_chars_threshold: u64, } impl MinIdentChars { + pub fn new(conf: &'static Conf) -> Self { + Self { + allowed_idents_below_min_chars: &conf.allowed_idents_below_min_chars, + min_ident_chars_threshold: conf.min_ident_chars_threshold, + } + } + #[expect(clippy::cast_possible_truncation)] fn is_ident_too_short(&self, cx: &LateContext<'_>, str: &str, span: Span) -> bool { !in_external_macro(cx.sess(), span) @@ -132,11 +139,11 @@ impl Visitor<'_> for IdentVisitor<'_, '_> { return; } - // `struct Awa<T>(T)` - // ^ + // `struct Array<T, const N: usize>([T; N])` + // ^ ^ if let Node::PathSegment(path) = node { if let Res::Def(def_kind, ..) = path.res - && let DefKind::TyParam = def_kind + && let DefKind::TyParam | DefKind::ConstParam = def_kind { return; } diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index 32c8731c537..a9aafe7ed56 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_and_then, span_lint_hir_and_then}; +use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir, span_lint_hir_and_then}; use clippy_utils::source::{snippet, snippet_with_context}; use clippy_utils::sugg::Sugg; use clippy_utils::{ @@ -114,40 +114,35 @@ impl<'tcx> LateLintPass<'tcx> for LintPass { k: FnKind<'tcx>, decl: &'tcx FnDecl<'_>, body: &'tcx Body<'_>, - span: Span, + _: Span, _: LocalDefId, ) { - if let FnKind::Closure = k { - // Does not apply to closures - return; - } - if in_external_macro(cx.tcx.sess, span) { - return; - } - for arg in iter_input_pats(decl, body) { - // Do not emit if clippy::ref_patterns is not allowed to avoid having two lints for the same issue. - if !is_lint_allowed(cx, REF_PATTERNS, arg.pat.hir_id) { - return; - } - if let PatKind::Binding(BindingMode(ByRef::Yes(_), _), ..) = arg.pat.kind { - span_lint( - cx, - TOPLEVEL_REF_ARG, - arg.pat.span, - "`ref` directly on a function argument is ignored. \ - Consider using a reference type instead", - ); + if !matches!(k, FnKind::Closure) { + for arg in iter_input_pats(decl, body) { + if let PatKind::Binding(BindingMode(ByRef::Yes(_), _), ..) = arg.pat.kind + && is_lint_allowed(cx, REF_PATTERNS, arg.pat.hir_id) + && !in_external_macro(cx.tcx.sess, arg.span) + { + span_lint_hir( + cx, + TOPLEVEL_REF_ARG, + arg.hir_id, + arg.pat.span, + "`ref` directly on a function argument is ignored. \ + Consider using a reference type instead", + ); + } } } } fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { - if !in_external_macro(cx.tcx.sess, stmt.span) - && let StmtKind::Let(local) = stmt.kind + if let StmtKind::Let(local) = stmt.kind && let PatKind::Binding(BindingMode(ByRef::Yes(mutabl), _), .., name, None) = local.pat.kind && let Some(init) = local.init // Do not emit if clippy::ref_patterns is not allowed to avoid having two lints for the same issue. && is_lint_allowed(cx, REF_PATTERNS, local.pat.hir_id) + && !in_external_macro(cx.tcx.sess, stmt.span) { let ctxt = local.span.ctxt(); let mut app = Applicability::MachineApplicable; diff --git a/clippy_lints/src/mismatching_type_param_order.rs b/clippy_lints/src/mismatching_type_param_order.rs index 934b9f490ad..748289454be 100644 --- a/clippy_lints/src/mismatching_type_param_order.rs +++ b/clippy_lints/src/mismatching_type_param_order.rs @@ -49,12 +49,12 @@ declare_lint_pass!(TypeParamMismatch => [MISMATCHING_TYPE_PARAM_ORDER]); impl<'tcx> LateLintPass<'tcx> for TypeParamMismatch { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { - if !item.span.from_expansion() - && let ItemKind::Impl(imp) = &item.kind + if let ItemKind::Impl(imp) = &item.kind && let TyKind::Path(QPath::Resolved(_, path)) = &imp.self_ty.kind - && let Some(segment) = path.segments.iter().next() + && let [segment, ..] = path.segments && let Some(generic_args) = segment.args && !generic_args.args.is_empty() + && !item.span.from_expansion() { // get the name and span of the generic parameters in the Impl let mut impl_params = Vec::new(); diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs index ed5f46ddc8d..68c158330ab 100644 --- a/clippy_lints/src/missing_const_for_fn.rs +++ b/clippy_lints/src/missing_const_for_fn.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::qualify_min_const_fn::is_min_const_fn; use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, is_from_proc_macro, trait_ref_of_method}; @@ -79,9 +80,10 @@ pub struct MissingConstForFn { } impl MissingConstForFn { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } } } diff --git a/clippy_lints/src/missing_const_for_thread_local.rs b/clippy_lints/src/missing_const_for_thread_local.rs index ab1b4aa3dee..954216038fb 100644 --- a/clippy_lints/src/missing_const_for_thread_local.rs +++ b/clippy_lints/src/missing_const_for_thread_local.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::macros::macro_backtrace; use clippy_utils::qualify_min_const_fn::is_min_const_fn; @@ -49,9 +50,10 @@ pub struct MissingConstForThreadLocal { } impl MissingConstForThreadLocal { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } } } diff --git a/clippy_lints/src/missing_doc.rs b/clippy_lints/src/missing_doc.rs index 250fd5cbd48..2166b0fe5a0 100644 --- a/clippy_lints/src/missing_doc.rs +++ b/clippy_lints/src/missing_doc.rs @@ -5,6 +5,7 @@ // [`missing_doc`]: https://github.com/rust-lang/rust/blob/cf9cf7c923eb01146971429044f216a3ca905e06/compiler/rustc_lint/src/builtin.rs#L415 // +use clippy_config::Conf; use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::span_lint; use clippy_utils::is_from_proc_macro; @@ -51,18 +52,10 @@ pub struct MissingDoc { prev_span: Option<Span>, } -impl Default for MissingDoc { - #[must_use] - fn default() -> Self { - Self::new(false) - } -} - impl MissingDoc { - #[must_use] - pub fn new(crate_items_only: bool) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - crate_items_only, + crate_items_only: conf.missing_docs_in_crate_items, doc_hidden_stack: vec![false], prev_span: None, } diff --git a/clippy_lints/src/missing_enforced_import_rename.rs b/clippy_lints/src/missing_enforced_import_rename.rs index c6c188854fd..cdf6645b3df 100644 --- a/clippy_lints/src/missing_enforced_import_rename.rs +++ b/clippy_lints/src/missing_enforced_import_rename.rs @@ -1,12 +1,13 @@ -use clippy_config::types::Rename; +use clippy_config::Conf; +use clippy_utils::def_path_def_ids; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_opt; -use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_hir::def::Res; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::DefIdMap; use rustc_hir::{Item, ItemKind, UseKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::ty::TyCtxt; use rustc_session::impl_lint_pass; use rustc_span::Symbol; @@ -46,15 +47,18 @@ declare_clippy_lint! { } pub struct ImportRename { - conf_renames: Vec<Rename>, - renames: FxHashMap<DefId, Symbol>, + renames: DefIdMap<Symbol>, } impl ImportRename { - pub fn new(conf_renames: Vec<Rename>) -> Self { + pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { Self { - conf_renames, - renames: FxHashMap::default(), + renames: conf + .enforced_import_renames + .iter() + .map(|x| (x.path.split("::").collect::<Vec<_>>(), Symbol::intern(&x.rename))) + .flat_map(|(path, rename)| def_path_def_ids(tcx, &path).map(move |id| (id, rename))) + .collect(), } } } @@ -62,15 +66,6 @@ impl ImportRename { impl_lint_pass!(ImportRename => [MISSING_ENFORCED_IMPORT_RENAMES]); impl LateLintPass<'_> for ImportRename { - fn check_crate(&mut self, cx: &LateContext<'_>) { - for Rename { path, rename } in &self.conf_renames { - let segs = path.split("::").collect::<Vec<_>>(); - for id in clippy_utils::def_path_def_ids(cx, &segs) { - self.renames.insert(id, Symbol::intern(rename)); - } - } - } - fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { if let ItemKind::Use(path, UseKind::Single) = &item.kind { for &res in &path.res { diff --git a/clippy_lints/src/mut_key.rs b/clippy_lints/src/mut_key.rs index 2eb534da092..83af9979b9e 100644 --- a/clippy_lints/src/mut_key.rs +++ b/clippy_lints/src/mut_key.rs @@ -1,9 +1,10 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint; use clippy_utils::trait_ref_of_method; use clippy_utils::ty::InteriorMut; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::impl_lint_pass; use rustc_span::def_id::LocalDefId; use rustc_span::symbol::sym; @@ -67,17 +68,12 @@ declare_clippy_lint! { } pub struct MutableKeyType<'tcx> { - ignore_interior_mutability: Vec<String>, interior_mut: InteriorMut<'tcx>, } impl_lint_pass!(MutableKeyType<'_> => [ MUTABLE_KEY_TYPE ]); impl<'tcx> LateLintPass<'tcx> for MutableKeyType<'tcx> { - fn check_crate(&mut self, cx: &LateContext<'tcx>) { - self.interior_mut = InteriorMut::without_pointers(cx, &self.ignore_interior_mutability); - } - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { if let hir::ItemKind::Fn(ref sig, ..) = item.kind { self.check_sig(cx, item.owner_id.def_id, sig.decl); @@ -107,10 +103,9 @@ impl<'tcx> LateLintPass<'tcx> for MutableKeyType<'tcx> { } impl<'tcx> MutableKeyType<'tcx> { - pub fn new(ignore_interior_mutability: Vec<String>) -> Self { + pub fn new(tcx: TyCtxt<'tcx>, conf: &'static Conf) -> Self { Self { - ignore_interior_mutability, - interior_mut: InteriorMut::default(), + interior_mut: InteriorMut::without_pointers(tcx, &conf.ignore_interior_mutability), } } diff --git a/clippy_lints/src/mut_mut.rs b/clippy_lints/src/mut_mut.rs index bc7b2c6b7c1..60372121a7a 100644 --- a/clippy_lints/src/mut_mut.rs +++ b/clippy_lints/src/mut_mut.rs @@ -35,33 +35,18 @@ impl<'tcx> LateLintPass<'tcx> for MutMut { } fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx hir::Ty<'_>) { - if in_external_macro(cx.sess(), ty.span) { - return; - } - - if let hir::TyKind::Ref( - _, - hir::MutTy { - ty: pty, - mutbl: hir::Mutability::Mut, - }, - ) = ty.kind + if let hir::TyKind::Ref(_, mty) = ty.kind + && mty.mutbl == hir::Mutability::Mut + && let hir::TyKind::Ref(_, mty) = mty.ty.kind + && mty.mutbl == hir::Mutability::Mut + && !in_external_macro(cx.sess(), ty.span) { - if let hir::TyKind::Ref( - _, - hir::MutTy { - mutbl: hir::Mutability::Mut, - .. - }, - ) = pty.kind - { - span_lint( - cx, - MUT_MUT, - ty.span, - "generally you want to avoid `&mut &mut _` if possible", - ); - } + span_lint( + cx, + MUT_MUT, + ty.span, + "generally you want to avoid `&mut &mut _` if possible", + ); } } } diff --git a/clippy_lints/src/mutable_debug_assertion.rs b/clippy_lints/src/mutable_debug_assertion.rs index e92ba93942e..563ce2d82ea 100644 --- a/clippy_lints/src/mutable_debug_assertion.rs +++ b/clippy_lints/src/mutable_debug_assertion.rs @@ -96,10 +96,6 @@ impl<'a, 'tcx> Visitor<'tcx> for MutArgVisitor<'a, 'tcx> { self.found = true; return; }, - ExprKind::If(..) => { - self.found = true; - return; - }, ExprKind::Path(_) => { if let Some(adj) = self.cx.typeck_results().adjustments().get(expr.hir_id) { if adj diff --git a/clippy_lints/src/needless_borrowed_ref.rs b/clippy_lints/src/needless_borrowed_ref.rs index fb02f24c9dc..098098718af 100644 --- a/clippy_lints/src/needless_borrowed_ref.rs +++ b/clippy_lints/src/needless_borrowed_ref.rs @@ -37,83 +37,75 @@ declare_lint_pass!(NeedlessBorrowedRef => [NEEDLESS_BORROWED_REFERENCE]); impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowedRef { fn check_pat(&mut self, cx: &LateContext<'tcx>, ref_pat: &'tcx Pat<'_>) { - if ref_pat.span.from_expansion() { - // OK, simple enough, lints doesn't check in macro. - return; - } - - // Do not lint patterns that are part of an OR `|` pattern, the binding mode must match in all arms - for (_, node) in cx.tcx.hir().parent_iter(ref_pat.hir_id) { - let Node::Pat(pat) = node else { break }; - - if matches!(pat.kind, PatKind::Or(_)) { - return; + if let PatKind::Ref(pat, Mutability::Not) = ref_pat.kind + && !ref_pat.span.from_expansion() + && cx + .tcx + .hir() + .parent_iter(ref_pat.hir_id) + .map_while(|(_, parent)| if let Node::Pat(pat) = parent { Some(pat) } else { None }) + // Do not lint patterns that are part of an OR `|` pattern, the binding mode must match in all arms + .all(|pat| !matches!(pat.kind, PatKind::Or(_))) + { + match pat.kind { + // Check sub_pat got a `ref` keyword (excluding `ref mut`). + PatKind::Binding(BindingMode::REF, _, ident, None) => { + span_lint_and_then( + cx, + NEEDLESS_BORROWED_REFERENCE, + ref_pat.span, + "this pattern takes a reference on something that is being dereferenced", + |diag| { + // `&ref ident` + // ^^^^^ + let span = ref_pat.span.until(ident.span); + diag.span_suggestion_verbose( + span, + "try removing the `&ref` part", + String::new(), + Applicability::MachineApplicable, + ); + }, + ); + }, + // Slices where each element is `ref`: `&[ref a, ref b, ..., ref z]` + PatKind::Slice( + before, + None + | Some(Pat { + kind: PatKind::Wild, .. + }), + after, + ) => { + check_subpatterns( + cx, + "dereferencing a slice pattern where every element takes a reference", + ref_pat, + pat, + itertools::chain(before, after), + ); + }, + PatKind::Tuple(subpatterns, _) | PatKind::TupleStruct(_, subpatterns, _) => { + check_subpatterns( + cx, + "dereferencing a tuple pattern where every element takes a reference", + ref_pat, + pat, + subpatterns, + ); + }, + PatKind::Struct(_, fields, _) => { + check_subpatterns( + cx, + "dereferencing a struct pattern where every field's pattern takes a reference", + ref_pat, + pat, + fields.iter().map(|field| field.pat), + ); + }, + _ => {}, } } - - // Only lint immutable refs, because `&mut ref T` may be useful. - let PatKind::Ref(pat, Mutability::Not) = ref_pat.kind else { - return; - }; - - match pat.kind { - // Check sub_pat got a `ref` keyword (excluding `ref mut`). - PatKind::Binding(BindingMode::REF, _, ident, None) => { - span_lint_and_then( - cx, - NEEDLESS_BORROWED_REFERENCE, - ref_pat.span, - "this pattern takes a reference on something that is being dereferenced", - |diag| { - // `&ref ident` - // ^^^^^ - let span = ref_pat.span.until(ident.span); - diag.span_suggestion_verbose( - span, - "try removing the `&ref` part", - String::new(), - Applicability::MachineApplicable, - ); - }, - ); - }, - // Slices where each element is `ref`: `&[ref a, ref b, ..., ref z]` - PatKind::Slice( - before, - None - | Some(Pat { - kind: PatKind::Wild, .. - }), - after, - ) => { - check_subpatterns( - cx, - "dereferencing a slice pattern where every element takes a reference", - ref_pat, - pat, - itertools::chain(before, after), - ); - }, - PatKind::Tuple(subpatterns, _) | PatKind::TupleStruct(_, subpatterns, _) => { - check_subpatterns( - cx, - "dereferencing a tuple pattern where every element takes a reference", - ref_pat, - pat, - subpatterns, - ); - }, - PatKind::Struct(_, fields, _) => { - check_subpatterns( - cx, - "dereferencing a struct pattern where every field's pattern takes a reference", - ref_pat, - pat, - fields.iter().map(|field| field.pat), - ); - }, - _ => {}, - } } } diff --git a/clippy_lints/src/needless_borrows_for_generic_args.rs b/clippy_lints/src/needless_borrows_for_generic_args.rs index 064ce59c234..67f9b52c352 100644 --- a/clippy_lints/src/needless_borrows_for_generic_args.rs +++ b/clippy_lints/src/needless_borrows_for_generic_args.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::mir::PossibleBorrowerMap; use clippy_utils::source::snippet_with_context; @@ -67,11 +68,10 @@ pub struct NeedlessBorrowsForGenericArgs<'tcx> { impl_lint_pass!(NeedlessBorrowsForGenericArgs<'_> => [NEEDLESS_BORROWS_FOR_GENERIC_ARGS]); impl NeedlessBorrowsForGenericArgs<'_> { - #[must_use] - pub fn new(msrv: Msrv) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { possible_borrowers: Vec::new(), - msrv, + msrv: conf.msrv.clone(), } } } diff --git a/clippy_lints/src/needless_for_each.rs b/clippy_lints/src/needless_for_each.rs index 143acc2b1cb..6390e51f916 100644 --- a/clippy_lints/src/needless_for_each.rs +++ b/clippy_lints/src/needless_for_each.rs @@ -3,7 +3,7 @@ use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_hir::{Block, BlockCheckMode, Closure, Expr, ExprKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::{sym, Span, Symbol}; +use rustc_span::{sym, Span}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_trait_method; @@ -55,16 +55,8 @@ declare_lint_pass!(NeedlessForEach => [NEEDLESS_FOR_EACH]); impl<'tcx> LateLintPass<'tcx> for NeedlessForEach { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { - let (StmtKind::Expr(expr) | StmtKind::Semi(expr)) = stmt.kind else { - return; - }; - - if let ExprKind::MethodCall(method_name, for_each_recv, [for_each_arg], _) = expr.kind - // Check the method name is `for_each`. - && method_name.ident.name == Symbol::intern("for_each") - // Check `for_each` is an associated function of `Iterator`. - && is_trait_method(cx, expr, sym::Iterator) - // Checks the receiver of `for_each` is also a method call. + if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind + && let ExprKind::MethodCall(method_name, for_each_recv, [for_each_arg], _) = expr.kind && let ExprKind::MethodCall(_, iter_recv, [], _) = for_each_recv.kind // Skip the lint if the call chain is too long. e.g. `v.field.iter().for_each()` or // `v.foo().iter().for_each()` must be skipped. @@ -72,6 +64,8 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessForEach { iter_recv.kind, ExprKind::Array(..) | ExprKind::Call(..) | ExprKind::Path(..) ) + && method_name.ident.name.as_str() == "for_each" + && is_trait_method(cx, expr, sym::Iterator) // Checks the type of the `iter` method receiver is NOT a user defined type. && has_iter_method(cx, cx.typeck_results().expr_ty(iter_recv)).is_some() // Skip the lint if the body is not block because this is simpler than `for` loop. diff --git a/clippy_lints/src/needless_pass_by_ref_mut.rs b/clippy_lints/src/needless_pass_by_ref_mut.rs index 5ffd41d78e0..d543fd467ab 100644 --- a/clippy_lints/src/needless_pass_by_ref_mut.rs +++ b/clippy_lints/src/needless_pass_by_ref_mut.rs @@ -1,8 +1,10 @@ use super::needless_pass_by_value::requires_exact_signature; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::source::snippet; use clippy_utils::visitors::for_each_expr; use clippy_utils::{inherits_cfg, is_from_proc_macro, is_self}; +use core::ops::ControlFlow; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; @@ -20,8 +22,6 @@ use rustc_span::symbol::kw; use rustc_span::Span; use rustc_target::spec::abi::Abi; -use core::ops::ControlFlow; - declare_clippy_lint! { /// ### What it does /// Check if a `&mut` function argument is actually used mutably. @@ -51,7 +51,6 @@ declare_clippy_lint! { "using a `&mut` argument when it's not mutated" } -#[derive(Clone)] pub struct NeedlessPassByRefMut<'tcx> { avoid_breaking_exported_api: bool, used_fn_def_ids: FxHashSet<LocalDefId>, @@ -59,9 +58,9 @@ pub struct NeedlessPassByRefMut<'tcx> { } impl NeedlessPassByRefMut<'_> { - pub fn new(avoid_breaking_exported_api: bool) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - avoid_breaking_exported_api, + avoid_breaking_exported_api: conf.avoid_breaking_exported_api, used_fn_def_ids: FxHashSet::default(), fn_def_ids_to_maybe_unused_mut: FxIndexMap::default(), } diff --git a/clippy_lints/src/neg_cmp_op_on_partial_ord.rs b/clippy_lints/src/neg_cmp_op_on_partial_ord.rs index f7621822b66..fa90ee60612 100644 --- a/clippy_lints/src/neg_cmp_op_on_partial_ord.rs +++ b/clippy_lints/src/neg_cmp_op_on_partial_ord.rs @@ -45,10 +45,10 @@ declare_lint_pass!(NoNegCompOpForPartialOrd => [NEG_CMP_OP_ON_PARTIAL_ORD]); impl<'tcx> LateLintPass<'tcx> for NoNegCompOpForPartialOrd { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if !in_external_macro(cx.sess(), expr.span) - && let ExprKind::Unary(UnOp::Not, inner) = expr.kind + if let ExprKind::Unary(UnOp::Not, inner) = expr.kind && let ExprKind::Binary(ref op, left, _) = inner.kind && let BinOpKind::Le | BinOpKind::Ge | BinOpKind::Lt | BinOpKind::Gt = op.node + && !in_external_macro(cx.sess(), expr.span) { let ty = cx.typeck_results().expr_ty(left); diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index 6f5505e8a63..15bb328b446 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -4,6 +4,7 @@ use std::ptr; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::in_constant; use clippy_utils::macros::macro_backtrace; @@ -179,17 +180,15 @@ fn lint<'tcx>(cx: &LateContext<'tcx>, source: Source<'tcx>) { } pub struct NonCopyConst<'tcx> { - ignore_interior_mutability: Vec<String>, interior_mut: InteriorMut<'tcx>, } impl_lint_pass!(NonCopyConst<'_> => [DECLARE_INTERIOR_MUTABLE_CONST, BORROW_INTERIOR_MUTABLE_CONST]); impl<'tcx> NonCopyConst<'tcx> { - pub fn new(ignore_interior_mutability: Vec<String>) -> Self { + pub fn new(tcx: TyCtxt<'tcx>, conf: &'static Conf) -> Self { Self { - ignore_interior_mutability, - interior_mut: InteriorMut::default(), + interior_mut: InteriorMut::new(tcx, &conf.ignore_interior_mutability), } } @@ -308,10 +307,6 @@ impl<'tcx> NonCopyConst<'tcx> { } impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> { - fn check_crate(&mut self, cx: &LateContext<'tcx>) { - self.interior_mut = InteriorMut::new(cx, &self.ignore_interior_mutability); - } - fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx Item<'_>) { if let ItemKind::Const(.., body_id) = it.kind { let ty = cx.tcx.type_of(it.owner_id).instantiate_identity(); diff --git a/clippy_lints/src/non_expressive_names.rs b/clippy_lints/src/non_expressive_names.rs index eacfe9ff328..832518d2d35 100644 --- a/clippy_lints/src/non_expressive_names.rs +++ b/clippy_lints/src/non_expressive_names.rs @@ -1,3 +1,4 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use rustc_ast::ast::{ self, Arm, AssocItem, AssocItemKind, Attribute, Block, FnDecl, Item, ItemKind, Local, Pat, PatKind, @@ -73,13 +74,20 @@ declare_clippy_lint! { "unclear name" } -#[derive(Copy, Clone)] pub struct NonExpressiveNames { pub single_char_binding_names_threshold: u64, } impl_lint_pass!(NonExpressiveNames => [SIMILAR_NAMES, MANY_SINGLE_CHAR_NAMES, JUST_UNDERSCORES_AND_DIGITS]); +impl NonExpressiveNames { + pub fn new(conf: &'static Conf) -> Self { + Self { + single_char_binding_names_threshold: conf.single_char_binding_names_threshold, + } + } +} + struct ExistingName { interned: Symbol, span: Span, @@ -90,7 +98,7 @@ struct ExistingName { struct SimilarNamesLocalVisitor<'a, 'tcx> { names: Vec<ExistingName>, cx: &'a EarlyContext<'tcx>, - lint: NonExpressiveNames, + threshold: u64, /// A stack of scopes containing the single-character bindings in each scope. single_char_names: Vec<Vec<Ident>>, @@ -103,8 +111,7 @@ impl<'a, 'tcx> SimilarNamesLocalVisitor<'a, 'tcx> { } let num_single_char_names = self.single_char_names.iter().flatten().count(); - let threshold = self.lint.single_char_binding_names_threshold; - if num_single_char_names as u64 > threshold { + if num_single_char_names as u64 > self.threshold { let span = self .single_char_names .iter() @@ -384,7 +391,7 @@ impl EarlyLintPass for NonExpressiveNames { .. }) = item.kind { - do_check(*self, cx, &item.attrs, &sig.decl, blk); + do_check(self, cx, &item.attrs, &sig.decl, blk); } } @@ -399,17 +406,17 @@ impl EarlyLintPass for NonExpressiveNames { .. }) = item.kind { - do_check(*self, cx, &item.attrs, &sig.decl, blk); + do_check(self, cx, &item.attrs, &sig.decl, blk); } } } -fn do_check(lint: NonExpressiveNames, cx: &EarlyContext<'_>, attrs: &[Attribute], decl: &FnDecl, blk: &Block) { +fn do_check(lint: &NonExpressiveNames, cx: &EarlyContext<'_>, attrs: &[Attribute], decl: &FnDecl, blk: &Block) { if !attrs.iter().any(|attr| attr.has_name(sym::test)) { let mut visitor = SimilarNamesLocalVisitor { names: Vec::new(), cx, - lint, + threshold: lint.single_char_binding_names_threshold, single_char_names: vec![vec![]], }; diff --git a/clippy_lints/src/non_send_fields_in_send_ty.rs b/clippy_lints/src/non_send_fields_in_send_ty.rs index 74e6c57b52d..a60988ac5db 100644 --- a/clippy_lints/src/non_send_fields_in_send_ty.rs +++ b/clippy_lints/src/non_send_fields_in_send_ty.rs @@ -1,3 +1,4 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_lint_allowed; use clippy_utils::source::snippet; @@ -54,15 +55,14 @@ declare_clippy_lint! { "there is a field that is not safe to be sent to another thread in a `Send` struct" } -#[derive(Copy, Clone)] pub struct NonSendFieldInSendTy { enable_raw_pointer_heuristic: bool, } impl NonSendFieldInSendTy { - pub fn new(enable_raw_pointer_heuristic: bool) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - enable_raw_pointer_heuristic, + enable_raw_pointer_heuristic: conf.enable_raw_pointer_heuristic_for_send, } } } diff --git a/clippy_lints/src/nonstandard_macro_braces.rs b/clippy_lints/src/nonstandard_macro_braces.rs index 88f2eabaccb..2aaf1e6ff46 100644 --- a/clippy_lints/src/nonstandard_macro_braces.rs +++ b/clippy_lints/src/nonstandard_macro_braces.rs @@ -1,4 +1,5 @@ use clippy_config::types::MacroMatcher; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_opt; use rustc_ast::ast; @@ -35,17 +36,15 @@ declare_clippy_lint! { /// The (callsite span, (open brace, close brace), source snippet) type MacroInfo = (Span, (char, char), String); -#[derive(Debug)] pub struct MacroBraces { macro_braces: FxHashMap<String, (char, char)>, done: FxHashSet<Span>, } impl MacroBraces { - pub fn new(conf: &[MacroMatcher]) -> Self { - let macro_braces = macro_braces(conf); + pub fn new(conf: &'static Conf) -> Self { Self { - macro_braces, + macro_braces: macro_braces(&conf.standard_macro_braces), done: FxHashSet::default(), } } diff --git a/clippy_lints/src/operators/arithmetic_side_effects.rs b/clippy_lints/src/operators/arithmetic_side_effects.rs index 7d6f26cde3e..a7e381be743 100644 --- a/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -1,4 +1,5 @@ use super::ARITHMETIC_SIDE_EFFECTS; +use clippy_config::Conf; use clippy_utils::consts::{constant, constant_simple, Constant}; use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::is_type_diagnostic_item; @@ -11,19 +12,9 @@ use rustc_span::symbol::sym; use rustc_span::{Span, Symbol}; use {rustc_ast as ast, rustc_hir as hir}; -const HARD_CODED_ALLOWED_BINARY: &[[&str; 2]] = &[["f32", "f32"], ["f64", "f64"], ["std::string::String", "str"]]; -const HARD_CODED_ALLOWED_UNARY: &[&str] = &["f32", "f64", "std::num::Saturating", "std::num::Wrapping"]; -const DISALLOWED_INT_METHODS: &[Symbol] = &[ - sym::saturating_div, - sym::wrapping_div, - sym::wrapping_rem, - sym::wrapping_rem_euclid, -]; - -#[derive(Debug)] pub struct ArithmeticSideEffects { - allowed_binary: FxHashMap<String, FxHashSet<String>>, - allowed_unary: FxHashSet<String>, + allowed_binary: FxHashMap<&'static str, FxHashSet<&'static str>>, + allowed_unary: FxHashSet<&'static str>, // Used to check whether expressions are constants, such as in enum discriminants and consts const_span: Option<Span>, disallowed_int_methods: FxHashSet<Symbol>, @@ -33,26 +24,38 @@ pub struct ArithmeticSideEffects { impl_lint_pass!(ArithmeticSideEffects => [ARITHMETIC_SIDE_EFFECTS]); impl ArithmeticSideEffects { - #[must_use] - pub fn new(user_allowed_binary: Vec<[String; 2]>, user_allowed_unary: Vec<String>) -> Self { - let mut allowed_binary: FxHashMap<String, FxHashSet<String>> = <_>::default(); - for [lhs, rhs] in user_allowed_binary.into_iter().chain( - HARD_CODED_ALLOWED_BINARY - .iter() - .copied() - .map(|[lhs, rhs]| [lhs.to_string(), rhs.to_string()]), - ) { + pub fn new(conf: &'static Conf) -> Self { + let mut allowed_binary = FxHashMap::<&'static str, FxHashSet<&'static str>>::default(); + let mut allowed_unary = FxHashSet::<&'static str>::default(); + + allowed_unary.extend(["f32", "f64", "std::num::Saturating", "std::num::Wrapping"]); + allowed_unary.extend(conf.arithmetic_side_effects_allowed_unary.iter().map(|x| &**x)); + allowed_binary.extend([ + ("f32", FxHashSet::from_iter(["f32"])), + ("f64", FxHashSet::from_iter(["f64"])), + ("std::string::String", FxHashSet::from_iter(["str"])), + ]); + for [lhs, rhs] in &conf.arithmetic_side_effects_allowed_binary { allowed_binary.entry(lhs).or_default().insert(rhs); } - let allowed_unary = user_allowed_unary - .into_iter() - .chain(HARD_CODED_ALLOWED_UNARY.iter().copied().map(String::from)) - .collect(); + for s in &conf.arithmetic_side_effects_allowed { + allowed_binary.entry(s).or_default().insert("*"); + allowed_binary.entry("*").or_default().insert(s); + allowed_unary.insert(s); + } + Self { allowed_binary, allowed_unary, + disallowed_int_methods: [ + sym::saturating_div, + sym::wrapping_div, + sym::wrapping_rem, + sym::wrapping_rem_euclid, + ] + .into_iter() + .collect(), const_span: None, - disallowed_int_methods: DISALLOWED_INT_METHODS.iter().copied().collect(), expr_span: None, } } diff --git a/clippy_lints/src/operators/mod.rs b/clippy_lints/src/operators/mod.rs index 59834781a58..9baecff801f 100644 --- a/clippy_lints/src/operators/mod.rs +++ b/clippy_lints/src/operators/mod.rs @@ -23,6 +23,7 @@ mod verbose_bit_mask; pub(crate) mod arithmetic_side_effects; +use clippy_config::Conf; use rustc_hir::{Body, Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; @@ -841,6 +842,16 @@ pub struct Operators { verbose_bit_mask_threshold: u64, modulo_arithmetic_allow_comparison_to_zero: bool, } +impl Operators { + pub fn new(conf: &'static Conf) -> Self { + Self { + arithmetic_context: numeric_arithmetic::Context::default(), + verbose_bit_mask_threshold: conf.verbose_bit_mask_threshold, + modulo_arithmetic_allow_comparison_to_zero: conf.allow_comparison_to_zero, + } + } +} + impl_lint_pass!(Operators => [ ABSURD_EXTREME_COMPARISONS, ARITHMETIC_SIDE_EFFECTS, @@ -869,15 +880,7 @@ impl_lint_pass!(Operators => [ PTR_EQ, SELF_ASSIGNMENT, ]); -impl Operators { - pub fn new(verbose_bit_mask_threshold: u64, modulo_arithmetic_allow_comparison_to_zero: bool) -> Self { - Self { - arithmetic_context: numeric_arithmetic::Context::default(), - verbose_bit_mask_threshold, - modulo_arithmetic_allow_comparison_to_zero, - } - } -} + impl<'tcx> LateLintPass<'tcx> for Operators { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { eq_op::check_assert(cx, e); diff --git a/clippy_lints/src/option_env_unwrap.rs b/clippy_lints/src/option_env_unwrap.rs index 4bfb26209d2..d16f5f8e112 100644 --- a/clippy_lints/src/option_env_unwrap.rs +++ b/clippy_lints/src/option_env_unwrap.rs @@ -3,7 +3,7 @@ use clippy_utils::is_direct_expn_of; use rustc_ast::ast::{Expr, ExprKind, MethodCall}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::{sym, Span}; +use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -35,30 +35,18 @@ declare_lint_pass!(OptionEnvUnwrap => [OPTION_ENV_UNWRAP]); impl EarlyLintPass for OptionEnvUnwrap { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { - fn lint(cx: &EarlyContext<'_>, span: Span) { + if let ExprKind::MethodCall(box MethodCall { seg, receiver, .. }) = &expr.kind + && matches!(seg.ident.name, sym::expect | sym::unwrap) + && is_direct_expn_of(receiver.span, "option_env").is_some() + { span_lint_and_help( cx, OPTION_ENV_UNWRAP, - span, + expr.span, "this will panic at run-time if the environment variable doesn't exist at compile-time", None, "consider using the `env!` macro instead", ); } - - if let ExprKind::MethodCall(box MethodCall { seg, receiver, .. }) = &expr.kind - && matches!(seg.ident.name, sym::expect | sym::unwrap) - { - if let ExprKind::Call(caller, _) = &receiver.kind && - // If it exists, it will be ::core::option::Option::Some("<env var>").unwrap() (A method call in the HIR) - is_direct_expn_of(caller.span, "option_env").is_some() - { - lint(cx, expr.span); - } else if let ExprKind::Path(_, caller) = &receiver.kind && // If it doesn't exist, it will be ::core::option::Option::None::<&'static str>.unwrap() (A path in the HIR) - is_direct_expn_of(caller.span, "option_env").is_some() - { - lint(cx, expr.span); - } - } } } diff --git a/clippy_lints/src/panic_unimplemented.rs b/clippy_lints/src/panic_unimplemented.rs index 80ef761906e..4eefd0065f6 100644 --- a/clippy_lints/src/panic_unimplemented.rs +++ b/clippy_lints/src/panic_unimplemented.rs @@ -1,3 +1,4 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint; use clippy_utils::is_in_test; use clippy_utils::macros::{is_panic, root_macro_call_first_node}; @@ -5,9 +6,16 @@ use rustc_hir::Expr; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; -#[derive(Clone)] pub struct PanicUnimplemented { - pub allow_panic_in_tests: bool, + allow_panic_in_tests: bool, +} + +impl PanicUnimplemented { + pub fn new(conf: &'static Conf) -> Self { + Self { + allow_panic_in_tests: conf.allow_panic_in_tests, + } + } } declare_clippy_lint! { diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs index 128bfd49d9e..5ca244f0141 100644 --- a/clippy_lints/src/pass_by_ref_or_value.rs +++ b/clippy_lints/src/pass_by_ref_or_value.rs @@ -1,5 +1,6 @@ use std::{cmp, iter}; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::{for_each_top_level_late_bound_region, is_copy}; @@ -14,7 +15,7 @@ use rustc_hir::{BindingMode, Body, FnDecl, Impl, ItemKind, MutTy, Mutability, No use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::adjustment::{Adjust, PointerCoercion}; use rustc_middle::ty::layout::LayoutOf; -use rustc_middle::ty::{self, RegionKind}; +use rustc_middle::ty::{self, RegionKind, TyCtxt}; use rustc_session::impl_lint_pass; use rustc_span::def_id::LocalDefId; use rustc_span::{sym, Span}; @@ -103,7 +104,6 @@ declare_clippy_lint! { "functions taking large arguments by value" } -#[derive(Copy, Clone)] pub struct PassByRefOrValue { ref_min_size: u64, value_max_size: u64, @@ -111,14 +111,9 @@ pub struct PassByRefOrValue { } impl<'tcx> PassByRefOrValue { - pub fn new( - ref_min_size: Option<u64>, - value_max_size: u64, - avoid_breaking_exported_api: bool, - pointer_width: u32, - ) -> Self { - let ref_min_size = ref_min_size.unwrap_or_else(|| { - let bit_width = u64::from(pointer_width); + pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { + let ref_min_size = conf.trivial_copy_size_limit.unwrap_or_else(|| { + let bit_width = u64::from(tcx.sess.target.pointer_width); // Cap the calculated bit width at 32-bits to reduce // portability problems between 32 and 64-bit targets let bit_width = cmp::min(bit_width, 32); @@ -130,8 +125,8 @@ impl<'tcx> PassByRefOrValue { Self { ref_min_size, - value_max_size, - avoid_breaking_exported_api, + value_max_size: conf.pass_by_value_size_limit, + avoid_breaking_exported_api: conf.avoid_breaking_exported_api, } } diff --git a/clippy_lints/src/pathbuf_init_then_push.rs b/clippy_lints/src/pathbuf_init_then_push.rs new file mode 100644 index 00000000000..0008f154ae3 --- /dev/null +++ b/clippy_lints/src/pathbuf_init_then_push.rs @@ -0,0 +1,193 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::path_to_local_id; +use clippy_utils::source::{snippet, snippet_opt}; +use clippy_utils::ty::is_type_diagnostic_item; +use rustc_ast::{LitKind, StrStyle}; +use rustc_errors::Applicability; +use rustc_hir::def::Res; +use rustc_hir::{BindingMode, Block, Expr, ExprKind, HirId, LetStmt, PatKind, QPath, Stmt, StmtKind, TyKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_session::impl_lint_pass; +use rustc_span::{sym, Span, Symbol}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for calls to `push` immediately after creating a new `PathBuf`. + /// + /// ### Why is this bad? + /// Multiple `.join()` calls are usually easier to read than multiple `.push` + /// calls across multiple statements. It might also be possible to use + /// `PathBuf::from` instead. + /// + /// ### Known problems + /// `.join()` introduces an implicit `clone()`. `PathBuf::from` can alternativly be + /// used when the `PathBuf` is newly constructed. This will avoild the implicit clone. + /// + /// ### Example + /// ```rust + /// # use std::path::PathBuf; + /// let mut path_buf = PathBuf::new(); + /// path_buf.push("foo"); + /// ``` + /// Use instead: + /// ```rust + /// # use std::path::PathBuf; + /// let path_buf = PathBuf::from("foo"); + /// // or + /// let path_buf = PathBuf::new().join("foo"); + /// ``` + #[clippy::version = "1.81.0"] + pub PATHBUF_INIT_THEN_PUSH, + restriction, + "`push` immediately after `PathBuf` creation" +} + +impl_lint_pass!(PathbufThenPush<'_> => [PATHBUF_INIT_THEN_PUSH]); + +#[derive(Default)] +pub struct PathbufThenPush<'tcx> { + searcher: Option<PathbufPushSearcher<'tcx>>, +} + +struct PathbufPushSearcher<'tcx> { + local_id: HirId, + lhs_is_let: bool, + let_ty_span: Option<Span>, + init_val: Expr<'tcx>, + arg: Option<Expr<'tcx>>, + name: Symbol, + err_span: Span, +} + +impl<'tcx> PathbufPushSearcher<'tcx> { + /// Try to generate a suggestion with `PathBuf::from`. + /// Returns `None` if the suggestion would be invalid. + fn gen_pathbuf_from(&self, cx: &LateContext<'_>) -> Option<String> { + if let ExprKind::Call(iter_expr, []) = &self.init_val.kind + && let ExprKind::Path(QPath::TypeRelative(ty, segment)) = &iter_expr.kind + && let TyKind::Path(ty_path) = &ty.kind + && let QPath::Resolved(None, path) = ty_path + && let Res::Def(_, def_id) = &path.res + && cx.tcx.is_diagnostic_item(sym::PathBuf, *def_id) + && segment.ident.name == sym::new + && let Some(arg) = self.arg + && let ExprKind::Lit(x) = arg.kind + && let LitKind::Str(_, StrStyle::Cooked) = x.node + && let Some(s) = snippet_opt(cx, arg.span) + { + Some(format!(" = PathBuf::from({s});")) + } else { + None + } + } + + fn gen_pathbuf_join(&self, cx: &LateContext<'_>) -> Option<String> { + let arg = self.arg?; + let arg_str = snippet_opt(cx, arg.span)?; + let init_val = snippet_opt(cx, self.init_val.span)?; + Some(format!(" = {init_val}.join({arg_str});")) + } + + fn display_err(&self, cx: &LateContext<'_>) { + if clippy_utils::attrs::span_contains_cfg(cx, self.err_span) { + return; + } + let mut sugg = if self.lhs_is_let { + String::from("let mut ") + } else { + String::new() + }; + sugg.push_str(self.name.as_str()); + if let Some(span) = self.let_ty_span { + sugg.push_str(": "); + sugg.push_str(&snippet(cx, span, "_")); + } + match self.gen_pathbuf_from(cx) { + Some(value) => { + sugg.push_str(&value); + }, + None => { + if let Some(value) = self.gen_pathbuf_join(cx) { + sugg.push_str(&value); + } else { + return; + } + }, + } + + span_lint_and_sugg( + cx, + PATHBUF_INIT_THEN_PUSH, + self.err_span, + "calls to `push` immediately after creation", + "consider using the `.join()`", + sugg, + Applicability::HasPlaceholders, + ); + } +} + +impl<'tcx> LateLintPass<'tcx> for PathbufThenPush<'tcx> { + fn check_block(&mut self, _: &LateContext<'tcx>, _: &'tcx Block<'tcx>) { + self.searcher = None; + } + + fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx LetStmt<'tcx>) { + if let Some(init_expr) = local.init + && let PatKind::Binding(BindingMode::MUT, id, name, None) = local.pat.kind + && !in_external_macro(cx.sess(), local.span) + && let ty = cx.typeck_results().pat_ty(local.pat) + && is_type_diagnostic_item(cx, ty, sym::PathBuf) + { + self.searcher = Some(PathbufPushSearcher { + local_id: id, + lhs_is_let: true, + name: name.name, + let_ty_span: local.ty.map(|ty| ty.span), + err_span: local.span, + init_val: *init_expr, + arg: None, + }); + } + } + + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if let ExprKind::Assign(left, right, _) = expr.kind + && let ExprKind::Path(QPath::Resolved(None, path)) = left.kind + && let [name] = &path.segments + && let Res::Local(id) = path.res + && !in_external_macro(cx.sess(), expr.span) + && let ty = cx.typeck_results().expr_ty(left) + && is_type_diagnostic_item(cx, ty, sym::PathBuf) + { + self.searcher = Some(PathbufPushSearcher { + local_id: id, + lhs_is_let: false, + let_ty_span: None, + name: name.ident.name, + err_span: expr.span, + init_val: *right, + arg: None, + }); + } + } + + fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { + if let Some(mut searcher) = self.searcher.take() { + if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind + && let ExprKind::MethodCall(name, self_arg, [arg_expr], _) = expr.kind + && path_to_local_id(self_arg, searcher.local_id) + && name.ident.as_str() == "push" + { + searcher.err_span = searcher.err_span.to(stmt.span); + searcher.arg = Some(*arg_expr); + searcher.display_err(cx); + } + } + } + + fn check_block_post(&mut self, _: &LateContext<'tcx>, _: &'tcx Block<'tcx>) { + self.searcher = None; + } +} diff --git a/clippy_lints/src/permissions_set_readonly_false.rs b/clippy_lints/src/permissions_set_readonly_false.rs index 704acdc103e..dc142b6e157 100644 --- a/clippy_lints/src/permissions_set_readonly_false.rs +++ b/clippy_lints/src/permissions_set_readonly_false.rs @@ -31,10 +31,10 @@ declare_lint_pass!(PermissionsSetReadonlyFalse => [PERMISSIONS_SET_READONLY_FALS impl<'tcx> LateLintPass<'tcx> for PermissionsSetReadonlyFalse { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if let ExprKind::MethodCall(path, receiver, [arg], _) = &expr.kind - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(receiver), sym::FsPermissions) - && path.ident.name == sym!(set_readonly) && let ExprKind::Lit(lit) = &arg.kind && LitKind::Bool(false) == lit.node + && path.ident.name.as_str() == "set_readonly" + && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(receiver), sym::FsPermissions) { span_lint_and_then( cx, diff --git a/clippy_lints/src/pub_underscore_fields.rs b/clippy_lints/src/pub_underscore_fields.rs index d20d4a605a2..77b707567e4 100644 --- a/clippy_lints/src/pub_underscore_fields.rs +++ b/clippy_lints/src/pub_underscore_fields.rs @@ -1,4 +1,5 @@ use clippy_config::types::PubUnderscoreFieldsBehaviour; +use clippy_config::Conf; use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::is_path_lang_item; @@ -42,10 +43,18 @@ declare_clippy_lint! { } pub struct PubUnderscoreFields { - pub behavior: PubUnderscoreFieldsBehaviour, + behavior: PubUnderscoreFieldsBehaviour, } impl_lint_pass!(PubUnderscoreFields => [PUB_UNDERSCORE_FIELDS]); +impl PubUnderscoreFields { + pub fn new(conf: &'static Conf) -> Self { + Self { + behavior: conf.pub_underscore_fields_behavior, + } + } +} + impl<'tcx> LateLintPass<'tcx> for PubUnderscoreFields { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { // This lint only pertains to structs. diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index 7cf98ad9e09..ef6b4d3aeab 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -2,6 +2,7 @@ use crate::manual_let_else::MANUAL_LET_ELSE; use crate::question_mark_used::QUESTION_MARK_USED; use clippy_config::msrvs::Msrv; use clippy_config::types::MatchLintBehaviour; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; @@ -62,11 +63,10 @@ pub struct QuestionMark { impl_lint_pass!(QuestionMark => [QUESTION_MARK, MANUAL_LET_ELSE]); impl QuestionMark { - #[must_use] - pub fn new(msrv: Msrv, matches_behaviour: MatchLintBehaviour) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - msrv, - matches_behaviour, + msrv: conf.msrv.clone(), + matches_behaviour: conf.matches_for_let_else, try_block_depth_stack: Vec::new(), } } diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 4fdaa9f00a1..829fb58bc65 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{snippet, snippet_with_applicability, SpanRangeExt}; @@ -164,9 +165,10 @@ pub struct Ranges { } impl Ranges { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } } } diff --git a/clippy_lints/src/raw_strings.rs b/clippy_lints/src/raw_strings.rs index 3a004245459..3c19ee3522d 100644 --- a/clippy_lints/src/raw_strings.rs +++ b/clippy_lints/src/raw_strings.rs @@ -1,8 +1,6 @@ -use std::iter::once; -use std::ops::ControlFlow; - +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::snippet; +use clippy_utils::source::SpanRangeExt; use rustc_ast::ast::{Expr, ExprKind}; use rustc_ast::token::LitKind; use rustc_errors::Applicability; @@ -10,6 +8,8 @@ use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::impl_lint_pass; use rustc_span::{BytePos, Pos, Span}; +use std::iter::once; +use std::ops::ControlFlow; declare_clippy_lint! { /// ### What it does @@ -61,22 +61,27 @@ pub struct RawStrings { pub allow_one_hash_in_raw_strings: bool, } +impl RawStrings { + pub fn new(conf: &'static Conf) -> Self { + Self { + allow_one_hash_in_raw_strings: conf.allow_one_hash_in_raw_strings, + } + } +} + impl EarlyLintPass for RawStrings { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { - if !in_external_macro(cx.sess(), expr.span) - && let ExprKind::Lit(lit) = expr.kind - && let LitKind::StrRaw(max) | LitKind::ByteStrRaw(max) | LitKind::CStrRaw(max) = lit.kind + if let ExprKind::Lit(lit) = expr.kind + && let (prefix, max) = match lit.kind { + LitKind::StrRaw(max) => ("r", max), + LitKind::ByteStrRaw(max) => ("br", max), + LitKind::CStrRaw(max) => ("cr", max), + _ => return, + } + && !in_external_macro(cx.sess(), expr.span) + && expr.span.check_source_text(cx, |src| src.starts_with(prefix)) { let str = lit.symbol.as_str(); - let prefix = match lit.kind { - LitKind::StrRaw(..) => "r", - LitKind::ByteStrRaw(..) => "br", - LitKind::CStrRaw(..) => "cr", - _ => unreachable!(), - }; - if !snippet(cx, expr.span, prefix).trim().starts_with(prefix) { - return; - } let descr = lit.kind.descr(); if !str.contains(['\\', '"']) { @@ -86,7 +91,7 @@ impl EarlyLintPass for RawStrings { expr.span, "unnecessary raw string literal", |diag| { - let (start, end) = hash_spans(expr.span, prefix, 0, max); + let (start, end) = hash_spans(expr.span, prefix.len(), 0, max); // BytePos: skip over the `b` in `br`, we checked the prefix appears in the source text let r_pos = expr.span.lo() + BytePos::from_usize(prefix.len() - 1); @@ -148,7 +153,7 @@ impl EarlyLintPass for RawStrings { expr.span, "unnecessary hashes around raw string literal", |diag| { - let (start, end) = hash_spans(expr.span, prefix, req, max); + let (start, end) = hash_spans(expr.span, prefix.len(), req, max); let message = match max - req { _ if req == 0 => format!("remove all the hashes around the {descr} literal"), @@ -174,11 +179,11 @@ impl EarlyLintPass for RawStrings { /// r###".."### /// ^^ ^^ /// ``` -fn hash_spans(literal_span: Span, prefix: &str, req: u8, max: u8) -> (Span, Span) { +fn hash_spans(literal_span: Span, prefix_len: usize, req: u8, max: u8) -> (Span, Span) { let literal_span = literal_span.data(); // BytePos: we checked prefix appears literally in the source text - let hash_start = literal_span.lo + BytePos::from_usize(prefix.len()); + let hash_start = literal_span.lo + BytePos::from_usize(prefix_len); let hash_end = literal_span.hi; // BytePos: req/max are counts of the ASCII character # diff --git a/clippy_lints/src/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs index 47d3ed08b8e..bad9b979203 100644 --- a/clippy_lints/src/redundant_closure_call.rs +++ b/clippy_lints/src/redundant_closure_call.rs @@ -14,6 +14,7 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::ty; use rustc_session::declare_lint_pass; use rustc_span::ExpnKind; +use std::ops::ControlFlow; declare_clippy_lint! { /// ### What it does @@ -42,24 +43,15 @@ declare_clippy_lint! { declare_lint_pass!(RedundantClosureCall => [REDUNDANT_CLOSURE_CALL]); // Used to find `return` statements or equivalents e.g., `?` -struct ReturnVisitor { - found_return: bool, -} - -impl ReturnVisitor { - #[must_use] - fn new() -> Self { - Self { found_return: false } - } -} +struct ReturnVisitor; impl<'tcx> Visitor<'tcx> for ReturnVisitor { - fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) { + type Result = ControlFlow<()>; + fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) -> ControlFlow<()> { if let ExprKind::Ret(_) | ExprKind::Match(.., hir::MatchSource::TryDesugar(_)) = ex.kind { - self.found_return = true; - } else { - hir_visit::walk_expr(self, ex); + return ControlFlow::Break(()); } + hir_visit::walk_expr(self, ex) } } @@ -101,9 +93,8 @@ fn find_innermost_closure<'tcx>( while let ExprKind::Closure(closure) = expr.kind && let body = cx.tcx.hir().body(closure.body) && { - let mut visitor = ReturnVisitor::new(); - visitor.visit_expr(body.value); - !visitor.found_return + let mut visitor = ReturnVisitor; + !visitor.visit_expr(body.value).is_break() } && steps > 0 { diff --git a/clippy_lints/src/redundant_field_names.rs b/clippy_lints/src/redundant_field_names.rs index 17b031d5f01..0e637538615 100644 --- a/clippy_lints/src/redundant_field_names.rs +++ b/clippy_lints/src/redundant_field_names.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use rustc_ast::ast::{Expr, ExprKind}; use rustc_errors::Applicability; @@ -40,9 +41,10 @@ pub struct RedundantFieldNames { } impl RedundantFieldNames { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } } } diff --git a/clippy_lints/src/redundant_static_lifetimes.rs b/clippy_lints/src/redundant_static_lifetimes.rs index 136e7db83bd..d6e741dd974 100644 --- a/clippy_lints/src/redundant_static_lifetimes.rs +++ b/clippy_lints/src/redundant_static_lifetimes.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use rustc_ast::ast::{ConstItem, Item, ItemKind, StaticItem, Ty, TyKind}; @@ -38,9 +39,10 @@ pub struct RedundantStaticLifetimes { } impl RedundantStaticLifetimes { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } } } diff --git a/clippy_lints/src/regex.rs b/clippy_lints/src/regex.rs index e925ec0271c..8cacb646f51 100644 --- a/clippy_lints/src/regex.rs +++ b/clippy_lints/src/regex.rs @@ -78,7 +78,7 @@ impl<'tcx> LateLintPass<'tcx> for Regex { // `def_path_def_ids` will resolve through re-exports but is relatively heavy, so we only perform // the operation once and store the results let mut resolve = |path, kind| { - for id in def_path_def_ids(cx, path) { + for id in def_path_def_ids(cx.tcx, path) { self.definitions.insert(id, kind); } }; diff --git a/clippy_lints/src/semicolon_block.rs b/clippy_lints/src/semicolon_block.rs index 0e77acdfd77..7615c21276d 100644 --- a/clippy_lints/src/semicolon_block.rs +++ b/clippy_lints/src/semicolon_block.rs @@ -1,3 +1,4 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_and_then}; use rustc_errors::Applicability; use rustc_hir::{Block, Expr, ExprKind, Stmt, StmtKind}; @@ -64,21 +65,20 @@ declare_clippy_lint! { } impl_lint_pass!(SemicolonBlock => [SEMICOLON_INSIDE_BLOCK, SEMICOLON_OUTSIDE_BLOCK]); -#[derive(Copy, Clone)] pub struct SemicolonBlock { semicolon_inside_block_ignore_singleline: bool, semicolon_outside_block_ignore_multiline: bool, } impl SemicolonBlock { - pub fn new(semicolon_inside_block_ignore_singleline: bool, semicolon_outside_block_ignore_multiline: bool) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - semicolon_inside_block_ignore_singleline, - semicolon_outside_block_ignore_multiline, + semicolon_inside_block_ignore_singleline: conf.semicolon_inside_block_ignore_singleline, + semicolon_outside_block_ignore_multiline: conf.semicolon_outside_block_ignore_multiline, } } - fn semicolon_inside_block(self, cx: &LateContext<'_>, block: &Block<'_>, tail: &Expr<'_>, semi_span: Span) { + fn semicolon_inside_block(&self, cx: &LateContext<'_>, block: &Block<'_>, tail: &Expr<'_>, semi_span: Span) { let insert_span = tail.span.source_callsite().shrink_to_hi(); let remove_span = semi_span.with_lo(block.span.hi()); @@ -103,7 +103,7 @@ impl SemicolonBlock { } fn semicolon_outside_block( - self, + &self, cx: &LateContext<'_>, block: &Block<'_>, tail_stmt_expr: &Expr<'_>, diff --git a/clippy_lints/src/serde_api.rs b/clippy_lints/src/serde_api.rs index f1ec91d7aff..fcf1c4cc5c2 100644 --- a/clippy_lints/src/serde_api.rs +++ b/clippy_lints/src/serde_api.rs @@ -32,7 +32,7 @@ impl<'tcx> LateLintPass<'tcx> for SerdeApi { }) = item.kind { let did = trait_ref.path.res.def_id(); - if let Some(visit_did) = get_trait_def_id(cx, &paths::SERDE_DE_VISITOR) { + if let Some(visit_did) = get_trait_def_id(cx.tcx, &paths::SERDE_DE_VISITOR) { if did == visit_did { let mut seen_str = None; let mut seen_string = None; diff --git a/clippy_lints/src/single_call_fn.rs b/clippy_lints/src/single_call_fn.rs index 23bbeae0e31..abe13a97c0d 100644 --- a/clippy_lints/src/single_call_fn.rs +++ b/clippy_lints/src/single_call_fn.rs @@ -1,3 +1,4 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::{is_from_proc_macro, is_in_test_function}; use rustc_data_structures::fx::{FxIndexMap, IndexEntry}; @@ -66,13 +67,19 @@ pub enum CallState { Multiple, } -#[derive(Clone)] pub struct SingleCallFn { - pub avoid_breaking_exported_api: bool, - pub def_id_to_usage: FxIndexMap<LocalDefId, CallState>, + avoid_breaking_exported_api: bool, + def_id_to_usage: FxIndexMap<LocalDefId, CallState>, } impl SingleCallFn { + pub fn new(conf: &'static Conf) -> Self { + Self { + avoid_breaking_exported_api: conf.avoid_breaking_exported_api, + def_id_to_usage: FxIndexMap::default(), + } + } + fn is_function_allowed( &self, cx: &LateContext<'_>, diff --git a/clippy_lints/src/single_range_in_vec_init.rs b/clippy_lints/src/single_range_in_vec_init.rs index 0a9a3c6307a..e0558429638 100644 --- a/clippy_lints/src/single_range_in_vec_init.rs +++ b/clippy_lints/src/single_range_in_vec_init.rs @@ -99,7 +99,7 @@ impl LateLintPass<'_> for SingleRangeInVecInit { && let Some(start_snippet) = snippet_opt(cx, start.span) && let Some(end_snippet) = snippet_opt(cx, end.span) { - let should_emit_every_value = if let Some(step_def_id) = get_trait_def_id(cx, &["core", "iter", "Step"]) + let should_emit_every_value = if let Some(step_def_id) = get_trait_def_id(cx.tcx, &["core", "iter", "Step"]) && implements_trait(cx, ty, step_def_id, &[]) { true diff --git a/clippy_lints/src/string_patterns.rs b/clippy_lints/src/string_patterns.rs index 7ba58942a17..7e211d64da1 100644 --- a/clippy_lints/src/string_patterns.rs +++ b/clippy_lints/src/string_patterns.rs @@ -1,6 +1,7 @@ use std::ops::ControlFlow; use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::macros::matching_root_macro_call; @@ -75,9 +76,10 @@ pub struct StringPatterns { } impl StringPatterns { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } } } diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index 07390d6f430..922a0006ee5 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability}; use clippy_utils::{is_from_proc_macro, SpanlessEq, SpanlessHash}; @@ -86,16 +87,17 @@ declare_clippy_lint! { "check if the same trait bounds are specified more than once during a generic declaration" } -#[derive(Clone)] pub struct TraitBounds { max_trait_bounds: u64, msrv: Msrv, } impl TraitBounds { - #[must_use] - pub fn new(max_trait_bounds: u64, msrv: Msrv) -> Self { - Self { max_trait_bounds, msrv } + pub fn new(conf: &'static Conf) -> Self { + Self { + max_trait_bounds: conf.max_trait_bounds, + msrv: conf.msrv.clone(), + } } } diff --git a/clippy_lints/src/transmute/mod.rs b/clippy_lints/src/transmute/mod.rs index aa329ec3366..9c6813a54b9 100644 --- a/clippy_lints/src/transmute/mod.rs +++ b/clippy_lints/src/transmute/mod.rs @@ -20,6 +20,7 @@ mod utils; mod wrong_transmute; use clippy_config::msrvs::Msrv; +use clippy_config::Conf; use clippy_utils::in_constant; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; @@ -577,9 +578,10 @@ impl_lint_pass!(Transmute => [ MISSING_TRANSMUTE_ANNOTATIONS, ]); impl Transmute { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } } } impl<'tcx> LateLintPass<'tcx> for Transmute { @@ -615,7 +617,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { | missing_transmute_annotations::check(cx, path, from_ty, to_ty, e.hir_id) | transmute_int_to_char::check(cx, e, from_ty, to_ty, arg, const_context) | transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context) - | transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, arg) + | transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, arg, &self.msrv) | transmute_int_to_bool::check(cx, e, from_ty, to_ty, arg) | transmute_int_to_float::check(cx, e, from_ty, to_ty, arg, const_context) | transmute_int_to_non_zero::check(cx, e, from_ty, to_ty, arg) diff --git a/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs b/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs index 1476ea8e7a4..0772b284968 100644 --- a/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs +++ b/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs @@ -1,10 +1,11 @@ use super::TRANSMUTE_PTR_TO_PTR; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::sugg; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, Ty, TypeVisitableExt}; /// Checks for `transmute_ptr_to_ptr` lint. /// Returns `true` if it's triggered, otherwise returns `false`. @@ -14,9 +15,10 @@ pub(super) fn check<'tcx>( from_ty: Ty<'tcx>, to_ty: Ty<'tcx>, arg: &'tcx Expr<'_>, + msrv: &Msrv, ) -> bool { - match (&from_ty.kind(), &to_ty.kind()) { - (ty::RawPtr(_, _), ty::RawPtr(to_ty, to_mutbl)) => { + match (from_ty.kind(), to_ty.kind()) { + (ty::RawPtr(from_pointee_ty, from_mutbl), ty::RawPtr(to_pointee_ty, to_mutbl)) => { span_lint_and_then( cx, TRANSMUTE_PTR_TO_PTR, @@ -24,8 +26,39 @@ pub(super) fn check<'tcx>( "transmute from a pointer to a pointer", |diag| { if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) { - let sugg = arg.as_ty(Ty::new_ptr(cx.tcx, *to_ty, *to_mutbl)); - diag.span_suggestion(e.span, "try", sugg, Applicability::Unspecified); + if from_mutbl == to_mutbl + && to_pointee_ty.is_sized(cx.tcx, cx.param_env) + && msrv.meets(msrvs::POINTER_CAST) + { + diag.span_suggestion_verbose( + e.span, + "use `pointer::cast` instead", + format!("{}.cast::<{to_pointee_ty}>()", arg.maybe_par()), + Applicability::MaybeIncorrect, + ); + } else if from_pointee_ty == to_pointee_ty + && let Some(method) = match (from_mutbl, to_mutbl) { + (ty::Mutability::Not, ty::Mutability::Mut) => Some("cast_mut"), + (ty::Mutability::Mut, ty::Mutability::Not) => Some("cast_const"), + _ => None, + } + && !from_pointee_ty.has_erased_regions() + && msrv.meets(msrvs::POINTER_CAST_CONSTNESS) + { + diag.span_suggestion_verbose( + e.span, + format!("use `pointer::{method}` instead"), + format!("{}.{method}()", arg.maybe_par()), + Applicability::MaybeIncorrect, + ); + } else { + diag.span_suggestion_verbose( + e.span, + "use an `as` cast instead", + arg.as_ty(to_ty), + Applicability::MaybeIncorrect, + ); + } } }, ); diff --git a/clippy_lints/src/tuple_array_conversions.rs b/clippy_lints/src/tuple_array_conversions.rs index 564b065d0ba..1d0de932754 100644 --- a/clippy_lints/src/tuple_array_conversions.rs +++ b/clippy_lints/src/tuple_array_conversions.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::visitors::for_each_local_use_after_expr; use clippy_utils::{is_from_proc_macro, path_to_local}; @@ -42,9 +43,15 @@ declare_clippy_lint! { } impl_lint_pass!(TupleArrayConversions => [TUPLE_ARRAY_CONVERSIONS]); -#[derive(Clone)] pub struct TupleArrayConversions { - pub msrv: Msrv, + msrv: Msrv, +} +impl TupleArrayConversions { + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } + } } impl LateLintPass<'_> for TupleArrayConversions { diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index 62ef65ca122..3a14927802b 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -9,6 +9,7 @@ mod type_complexity; mod utils; mod vec_box; +use clippy_config::Conf; use rustc_hir as hir; use rustc_hir::intravisit::FnKind; use rustc_hir::{ @@ -446,11 +447,11 @@ impl<'tcx> LateLintPass<'tcx> for Types { } impl Types { - pub fn new(vec_box_size_threshold: u64, type_complexity_threshold: u64, avoid_breaking_exported_api: bool) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - vec_box_size_threshold, - type_complexity_threshold, - avoid_breaking_exported_api, + vec_box_size_threshold: conf.vec_box_size_threshold, + type_complexity_threshold: conf.type_complexity_threshold, + avoid_breaking_exported_api: conf.avoid_breaking_exported_api, } } diff --git a/clippy_lints/src/unconditional_recursion.rs b/clippy_lints/src/unconditional_recursion.rs index 6ba98a92423..42100e1d755 100644 --- a/clippy_lints/src/unconditional_recursion.rs +++ b/clippy_lints/src/unconditional_recursion.rs @@ -16,6 +16,7 @@ use rustc_session::impl_lint_pass; use rustc_span::symbol::{kw, Ident}; use rustc_span::{sym, Span}; use rustc_trait_selection::error_reporting::traits::suggestions::ReturnsVisitor; +use std::ops::ControlFlow; declare_clippy_lint! { /// ### What it does @@ -276,7 +277,6 @@ struct CheckCalls<'a, 'tcx> { cx: &'a LateContext<'tcx>, map: Map<'tcx>, implemented_ty_id: DefId, - found_default_call: bool, method_span: Span, } @@ -285,16 +285,14 @@ where 'tcx: 'a, { type NestedFilter = nested_filter::OnlyBodies; + type Result = ControlFlow<()>; fn nested_visit_map(&mut self) -> Self::Map { self.map } - fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { - if self.found_default_call { - return; - } - walk_expr(self, expr); + fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) -> ControlFlow<()> { + walk_expr(self, expr)?; if let ExprKind::Call(f, _) = expr.kind && let ExprKind::Path(qpath) = f.kind @@ -303,9 +301,10 @@ where && let Some(trait_def_id) = self.cx.tcx.trait_of_item(method_def_id) && self.cx.tcx.is_diagnostic_item(sym::Default, trait_def_id) { - self.found_default_call = true; span_error(self.cx, self.method_span, expr); + return ControlFlow::Break(()); } + ControlFlow::Continue(()) } } @@ -383,7 +382,6 @@ impl UnconditionalRecursion { cx, map: cx.tcx.hir(), implemented_ty_id, - found_default_call: false, method_span, }; walk_body(&mut c, body); diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs index 93a1089a970..3ab30bf5dba 100644 --- a/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -1,5 +1,6 @@ use std::ops::ControlFlow; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::is_lint_allowed; use clippy_utils::source::walk_span_to_context; @@ -91,17 +92,16 @@ declare_clippy_lint! { "annotating safe code with a safety comment" } -#[derive(Copy, Clone)] pub struct UndocumentedUnsafeBlocks { accept_comment_above_statement: bool, accept_comment_above_attributes: bool, } impl UndocumentedUnsafeBlocks { - pub fn new(accept_comment_above_statement: bool, accept_comment_above_attributes: bool) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - accept_comment_above_statement, - accept_comment_above_attributes, + accept_comment_above_statement: conf.accept_comment_above_statement, + accept_comment_above_attributes: conf.accept_comment_above_attributes, } } } diff --git a/clippy_lints/src/unnecessary_box_returns.rs b/clippy_lints/src/unnecessary_box_returns.rs index bfcefb26153..3f130bf5a67 100644 --- a/clippy_lints/src/unnecessary_box_returns.rs +++ b/clippy_lints/src/unnecessary_box_returns.rs @@ -1,3 +1,4 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::approx_ty_size; use rustc_errors::Applicability; @@ -47,10 +48,10 @@ pub struct UnnecessaryBoxReturns { impl_lint_pass!(UnnecessaryBoxReturns => [UNNECESSARY_BOX_RETURNS]); impl UnnecessaryBoxReturns { - pub fn new(avoid_breaking_exported_api: bool, maximum_size: u64) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - avoid_breaking_exported_api, - maximum_size, + avoid_breaking_exported_api: conf.avoid_breaking_exported_api, + maximum_size: conf.unnecessary_box_size, } } diff --git a/clippy_lints/src/unnecessary_struct_initialization.rs b/clippy_lints/src/unnecessary_struct_initialization.rs index 2da75334344..afdd3505cdd 100644 --- a/clippy_lints/src/unnecessary_struct_initialization.rs +++ b/clippy_lints/src/unnecessary_struct_initialization.rs @@ -2,14 +2,15 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::is_copy; use clippy_utils::{get_parent_expr, path_to_local}; -use rustc_hir::{BindingMode, Expr, ExprKind, Node, PatKind, UnOp}; +use rustc_hir::{BindingMode, Expr, ExprField, ExprKind, Node, PatKind, Path, QPath, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; declare_clippy_lint! { /// ### What it does - /// Checks for initialization of a `struct` by copying a base without setting - /// any field. + /// Checks for initialization of an identical `struct` from another instance + /// of the type, either by copying a base without setting any field or by + /// moving all fields individually. /// /// ### Why is this bad? /// Readability suffers from unnecessary struct building. @@ -29,9 +30,14 @@ declare_clippy_lint! { /// let b = a; /// ``` /// + /// The struct literal ``S { ..a }`` in the assignment to ``b`` could be replaced + /// with just ``a``. + /// /// ### Known Problems /// Has false positives when the base is a place expression that cannot be /// moved out of, see [#10547](https://github.com/rust-lang/rust-clippy/issues/10547). + /// + /// Empty structs are ignored by the lint. #[clippy::version = "1.70.0"] pub UNNECESSARY_STRUCT_INITIALIZATION, nursery, @@ -41,42 +47,111 @@ declare_lint_pass!(UnnecessaryStruct => [UNNECESSARY_STRUCT_INITIALIZATION]); impl LateLintPass<'_> for UnnecessaryStruct { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - if let ExprKind::Struct(_, &[], Some(base)) = expr.kind { - if let Some(parent) = get_parent_expr(cx, expr) - && let parent_ty = cx.typeck_results().expr_ty_adjusted(parent) - && parent_ty.is_any_ptr() - { - if is_copy(cx, cx.typeck_results().expr_ty(expr)) && path_to_local(base).is_some() { - // When the type implements `Copy`, a reference to the new struct works on the - // copy. Using the original would borrow it. - return; - } - - if parent_ty.is_mutable_ptr() && !is_mutable(cx, base) { - // The original can be used in a mutable reference context only if it is mutable. - return; - } - } + let ExprKind::Struct(_, fields, base) = expr.kind else { + return; + }; - // TODO: do not propose to replace *XX if XX is not Copy - if let ExprKind::Unary(UnOp::Deref, target) = base.kind - && matches!(target.kind, ExprKind::Path(..)) - && !is_copy(cx, cx.typeck_results().expr_ty(expr)) - { - // `*base` cannot be used instead of the struct in the general case if it is not Copy. - return; - } + if expr.span.from_expansion() { + // Prevent lint from hitting inside macro code + return; + } + + let field_path = same_path_in_all_fields(cx, expr, fields); + + let sugg = match (field_path, base) { + (Some(&path), None) => { + // all fields match, no base given + path.span + }, + (Some(path), Some(base)) if base_is_suitable(cx, expr, base) && path_matches_base(path, base) => { + // all fields match, has base: ensure that the path of the base matches + base.span + }, + (None, Some(base)) if fields.is_empty() && base_is_suitable(cx, expr, base) => { + // just the base, no explicit fields + base.span + }, + _ => return, + }; + + span_lint_and_sugg( + cx, + UNNECESSARY_STRUCT_INITIALIZATION, + expr.span, + "unnecessary struct building", + "replace with", + snippet(cx, sugg, "..").into_owned(), + rustc_errors::Applicability::MachineApplicable, + ); + } +} + +fn base_is_suitable(cx: &LateContext<'_>, expr: &Expr<'_>, base: &Expr<'_>) -> bool { + if !check_references(cx, expr, base) { + return false; + } + + // TODO: do not propose to replace *XX if XX is not Copy + if let ExprKind::Unary(UnOp::Deref, target) = base.kind + && matches!(target.kind, ExprKind::Path(..)) + && !is_copy(cx, cx.typeck_results().expr_ty(expr)) + { + // `*base` cannot be used instead of the struct in the general case if it is not Copy. + return false; + } + true +} + +/// Check whether all fields of a struct assignment match. +/// Returns a [Path] item that one can obtain a span from for the lint suggestion. +/// +/// Conditions that must be satisfied to trigger this variant of the lint: +/// +/// - source struct of the assignment must be of same type as the destination +/// - names of destination struct fields must match the field names of the source +/// +/// We don’t check here if all struct fields are assigned as the remainder may +/// be filled in from a base struct. +fn same_path_in_all_fields<'tcx>( + cx: &LateContext<'_>, + expr: &Expr<'_>, + fields: &[ExprField<'tcx>], +) -> Option<&'tcx Path<'tcx>> { + let ty = cx.typeck_results().expr_ty(expr); + + let mut found = None; - span_lint_and_sugg( - cx, - UNNECESSARY_STRUCT_INITIALIZATION, - expr.span, - "unnecessary struct building", - "replace with", - snippet(cx, base.span, "..").into_owned(), - rustc_errors::Applicability::MachineApplicable, - ); + for f in fields { + // fields are assigned from expression + if let ExprKind::Field(src_expr, ident) = f.expr.kind + // expression type matches + && ty == cx.typeck_results().expr_ty(src_expr) + // field name matches + && f.ident == ident + // assigned from a path expression + && let ExprKind::Path(QPath::Resolved(None, src_path)) = src_expr.kind + { + let Some((_, p)) = found else { + // this is the first field assignment in the list + found = Some((src_expr, src_path)); + continue; + }; + + if p.res == src_path.res { + // subsequent field assignment with same origin struct as before + continue; + } } + // source of field assignment doesn’t qualify + return None; + } + + if let Some((src_expr, src_path)) = found + && check_references(cx, expr, src_expr) + { + Some(src_path) + } else { + None } } @@ -89,3 +164,43 @@ fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { true } } + +fn check_references(cx: &LateContext<'_>, expr_a: &Expr<'_>, expr_b: &Expr<'_>) -> bool { + if let Some(parent) = get_parent_expr(cx, expr_a) + && let parent_ty = cx.typeck_results().expr_ty_adjusted(parent) + && parent_ty.is_any_ptr() + { + if is_copy(cx, cx.typeck_results().expr_ty(expr_a)) && path_to_local(expr_b).is_some() { + // When the type implements `Copy`, a reference to the new struct works on the + // copy. Using the original would borrow it. + return false; + } + + if parent_ty.is_mutable_ptr() && !is_mutable(cx, expr_b) { + // The original can be used in a mutable reference context only if it is mutable. + return false; + } + } + + true +} + +/// When some fields are assigned from a base struct and others individually +/// the lint applies only if the source of the field is the same as the base. +/// This is enforced here by comparing the path of the base expression; +/// needless to say the lint only applies if it (or whatever expression it is +/// a reference of) actually has a path. +fn path_matches_base(path: &Path<'_>, base: &Expr<'_>) -> bool { + let base_path = match base.kind { + ExprKind::Unary(UnOp::Deref, base_expr) => { + if let ExprKind::Path(QPath::Resolved(_, base_path)) = base_expr.kind { + base_path + } else { + return false; + } + }, + ExprKind::Path(QPath::Resolved(_, base_path)) => base_path, + _ => return false, + }; + path.res == base_path.res +} diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs index 5c7fbbab988..e4e7f7d06e7 100644 --- a/clippy_lints/src/unnecessary_wraps.rs +++ b/clippy_lints/src/unnecessary_wraps.rs @@ -1,3 +1,4 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use clippy_utils::visitors::find_all_ret_expressions; @@ -63,9 +64,9 @@ pub struct UnnecessaryWraps { impl_lint_pass!(UnnecessaryWraps => [UNNECESSARY_WRAPS]); impl UnnecessaryWraps { - pub fn new(avoid_breaking_exported_api: bool) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - avoid_breaking_exported_api, + avoid_breaking_exported_api: conf.avoid_breaking_exported_api, } } } diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs index 842046c941d..98f0be3135d 100644 --- a/clippy_lints/src/unnested_or_patterns.rs +++ b/clippy_lints/src/unnested_or_patterns.rs @@ -1,6 +1,7 @@ #![allow(clippy::wildcard_imports, clippy::enum_glob_use)] use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::ast_utils::{eq_field_pat, eq_id, eq_maybe_qself, eq_pat, eq_path}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::over; @@ -51,9 +52,10 @@ pub struct UnnestedOrPatterns { } impl UnnestedOrPatterns { - #[must_use] - pub fn new(msrv: Msrv) -> Self { - Self { msrv } + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } } } diff --git a/clippy_lints/src/unused_peekable.rs b/clippy_lints/src/unused_peekable.rs index e6f799335d7..86a811e17ca 100644 --- a/clippy_lints/src/unused_peekable.rs +++ b/clippy_lints/src/unused_peekable.rs @@ -8,6 +8,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter::OnlyBodies; use rustc_session::declare_lint_pass; use rustc_span::sym; +use std::ops::ControlFlow; declare_clippy_lint! { /// ### What it does @@ -70,15 +71,16 @@ impl<'tcx> LateLintPass<'tcx> for UnusedPeekable { return; } - for stmt in &block.stmts[idx..] { - vis.visit_stmt(stmt); - } + let mut found_peek_call = block.stmts[idx..].iter().any(|stmt| vis.visit_stmt(stmt).is_break()); - if let Some(expr) = block.expr { - vis.visit_expr(expr); + if !found_peek_call + && let Some(expr) = block.expr + && vis.visit_expr(expr).is_break() + { + found_peek_call = true; } - if !vis.found_peek_call { + if !found_peek_call { span_lint_hir_and_then( cx, UNUSED_PEEKABLE, @@ -98,31 +100,23 @@ impl<'tcx> LateLintPass<'tcx> for UnusedPeekable { struct PeekableVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, expected_hir_id: HirId, - found_peek_call: bool, } impl<'a, 'tcx> PeekableVisitor<'a, 'tcx> { fn new(cx: &'a LateContext<'tcx>, expected_hir_id: HirId) -> Self { - Self { - cx, - expected_hir_id, - found_peek_call: false, - } + Self { cx, expected_hir_id } } } impl<'tcx> Visitor<'tcx> for PeekableVisitor<'_, 'tcx> { type NestedFilter = OnlyBodies; + type Result = ControlFlow<()>; fn nested_visit_map(&mut self) -> Self::Map { self.cx.tcx.hir() } - fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { - if self.found_peek_call { - return; - } - + fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) -> ControlFlow<()> { if path_to_local_id(ex, self.expected_hir_id) { for (_, node) in self.cx.tcx.hir().parent_iter(ex.hir_id) { match node { @@ -137,14 +131,14 @@ impl<'tcx> Visitor<'tcx> for PeekableVisitor<'_, 'tcx> { && func_did == into_iter_did { // Probably a for loop desugar, stop searching - return; + return ControlFlow::Continue(()); } if args.iter().any(|arg| arg_is_mut_peekable(self.cx, arg)) { - self.found_peek_call = true; + return ControlFlow::Break(()); } - return; + return ControlFlow::Continue(()); }, // Catch anything taking a Peekable mutably ExprKind::MethodCall( @@ -162,16 +156,14 @@ impl<'tcx> Visitor<'tcx> for PeekableVisitor<'_, 'tcx> { if matches!(method_name, "peek" | "peek_mut" | "next_if" | "next_if_eq") && arg_is_mut_peekable(self.cx, self_arg) { - self.found_peek_call = true; - return; + return ControlFlow::Break(()); } // foo.some_method() excluding Iterator methods if remaining_args.iter().any(|arg| arg_is_mut_peekable(self.cx, arg)) && !is_trait_method(self.cx, expr, sym::Iterator) { - self.found_peek_call = true; - return; + return ControlFlow::Break(()); } // foo.by_ref(), keep checking for `peek` @@ -179,41 +171,42 @@ impl<'tcx> Visitor<'tcx> for PeekableVisitor<'_, 'tcx> { continue; } - return; + return ControlFlow::Continue(()); }, ExprKind::AddrOf(_, Mutability::Mut, _) | ExprKind::Unary(..) | ExprKind::DropTemps(_) => { }, - ExprKind::AddrOf(_, Mutability::Not, _) => return, + ExprKind::AddrOf(_, Mutability::Not, _) => return ControlFlow::Continue(()), _ => { - self.found_peek_call = true; - return; + return ControlFlow::Break(()); }, } }, Node::LetStmt(LetStmt { init: Some(init), .. }) => { if arg_is_mut_peekable(self.cx, init) { - self.found_peek_call = true; + return ControlFlow::Break(()); } - return; + return ControlFlow::Continue(()); }, Node::Stmt(stmt) => { match stmt.kind { - StmtKind::Let(_) | StmtKind::Item(_) => self.found_peek_call = true, + StmtKind::Let(_) | StmtKind::Item(_) => { + return ControlFlow::Break(()); + }, StmtKind::Expr(_) | StmtKind::Semi(_) => {}, } - return; + return ControlFlow::Continue(()); }, Node::Block(_) | Node::ExprField(_) => {}, _ => { - return; + return ControlFlow::Continue(()); }, } } } - walk_expr(self, ex); + walk_expr(self, ex) } } diff --git a/clippy_lints/src/unused_self.rs b/clippy_lints/src/unused_self.rs index 3e6102f5982..781f51aa9b0 100644 --- a/clippy_lints/src/unused_self.rs +++ b/clippy_lints/src/unused_self.rs @@ -1,3 +1,4 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::visitors::is_local_used; @@ -43,9 +44,9 @@ pub struct UnusedSelf { impl_lint_pass!(UnusedSelf => [UNUSED_SELF]); impl UnusedSelf { - pub fn new(avoid_breaking_exported_api: bool) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - avoid_breaking_exported_api, + avoid_breaking_exported_api: conf.avoid_breaking_exported_api, } } } diff --git a/clippy_lints/src/upper_case_acronyms.rs b/clippy_lints/src/upper_case_acronyms.rs index 72392f8e1f7..8de062a8fc1 100644 --- a/clippy_lints/src/upper_case_acronyms.rs +++ b/clippy_lints/src/upper_case_acronyms.rs @@ -1,3 +1,4 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_hir_and_then; use core::mem::replace; use rustc_errors::Applicability; @@ -39,17 +40,16 @@ declare_clippy_lint! { "capitalized acronyms are against the naming convention" } -#[derive(Default)] pub struct UpperCaseAcronyms { avoid_breaking_exported_api: bool, upper_case_acronyms_aggressive: bool, } impl UpperCaseAcronyms { - pub fn new(avoid_breaking_exported_api: bool, aggressive: bool) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - avoid_breaking_exported_api, - upper_case_acronyms_aggressive: aggressive, + avoid_breaking_exported_api: conf.avoid_breaking_exported_api, + upper_case_acronyms_aggressive: conf.upper_case_acronyms_aggressive, } } } diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index 0bab917607d..93785b45c27 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -1,4 +1,5 @@ use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::is_from_proc_macro; use clippy_utils::ty::same_type_and_consts; @@ -60,10 +61,9 @@ pub struct UseSelf { } impl UseSelf { - #[must_use] - pub fn new(msrv: Msrv) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - msrv, + msrv: conf.msrv.clone(), stack: Vec::new(), } } diff --git a/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs b/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs index 07879e81fc2..b017a6bf665 100644 --- a/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs +++ b/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs @@ -74,7 +74,7 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { } for &module in &[&paths::KW_MODULE, &paths::SYM_MODULE] { - for def_id in def_path_def_ids(cx, module) { + for def_id in def_path_def_ids(cx.tcx, module) { for item in cx.tcx.module_children(def_id) { if let Res::Def(DefKind::Const, item_def_id) = item.res && let ty = cx.tcx.type_of(item_def_id).instantiate_identity() diff --git a/clippy_lints/src/utils/internal_lints/invalid_paths.rs b/clippy_lints/src/utils/internal_lints/invalid_paths.rs index 0beb0bb8ed4..980437259c3 100644 --- a/clippy_lints/src/utils/internal_lints/invalid_paths.rs +++ b/clippy_lints/src/utils/internal_lints/invalid_paths.rs @@ -55,7 +55,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidPaths { // This is not a complete resolver for paths. It works on all the paths currently used in the paths // module. That's all it does and all it needs to do. pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool { - if !def_path_res(cx, path).is_empty() { + if !def_path_res(cx.tcx, path).is_empty() { return true; } diff --git a/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs b/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs index 8cf42832761..41183700f09 100644 --- a/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs +++ b/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs @@ -108,7 +108,7 @@ impl UnnecessaryDefPath { // Extract the path to the matched type && let Some(segments) = path_to_matched_type(cx, item_arg) && let segments = segments.iter().map(|sym| &**sym).collect::<Vec<_>>() - && let Some(def_id) = def_path_def_ids(cx, &segments[..]).next() + && let Some(def_id) = def_path_def_ids(cx.tcx, &segments[..]).next() { // Check if the target item is a diagnostic item or LangItem. #[rustfmt::skip] @@ -206,7 +206,7 @@ impl UnnecessaryDefPath { fn check_array(&mut self, cx: &LateContext<'_>, elements: &[Expr<'_>], span: Span) { let Some(path) = path_from_array(elements) else { return }; - for def_id in def_path_def_ids(cx, &path.iter().map(AsRef::as_ref).collect::<Vec<_>>()) { + for def_id in def_path_def_ids(cx.tcx, &path.iter().map(AsRef::as_ref).collect::<Vec<_>>()) { self.array_def_ids.insert((def_id, span)); } } diff --git a/clippy_lints/src/vec.rs b/clippy_lints/src/vec.rs index 9edf7579d48..a831234906b 100644 --- a/clippy_lints/src/vec.rs +++ b/clippy_lints/src/vec.rs @@ -2,6 +2,7 @@ use std::collections::BTreeMap; use std::ops::ControlFlow; use clippy_config::msrvs::{self, Msrv}; +use clippy_config::Conf; use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::source::snippet_opt; @@ -17,12 +18,21 @@ use rustc_session::impl_lint_pass; use rustc_span::{sym, DesugaringKind, Span}; #[expect(clippy::module_name_repetitions)] -#[derive(Clone)] pub struct UselessVec { - pub too_large_for_stack: u64, - pub msrv: Msrv, - pub span_to_lint_map: BTreeMap<Span, Option<(HirId, SuggestedType, String, Applicability)>>, - pub allow_in_test: bool, + too_large_for_stack: u64, + msrv: Msrv, + span_to_lint_map: BTreeMap<Span, Option<(HirId, SuggestedType, String, Applicability)>>, + allow_in_test: bool, +} +impl UselessVec { + pub fn new(conf: &'static Conf) -> Self { + Self { + too_large_for_stack: conf.too_large_for_stack, + msrv: conf.msrv.clone(), + span_to_lint_map: BTreeMap::new(), + allow_in_test: conf.allow_useless_vec_in_tests, + } + } } declare_clippy_lint! { diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs index a0a60ca8875..c4d64ee4609 100644 --- a/clippy_lints/src/wildcard_imports.rs +++ b/clippy_lints/src/wildcard_imports.rs @@ -1,3 +1,4 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::is_in_test; use clippy_utils::source::{snippet, snippet_with_applicability}; @@ -97,17 +98,16 @@ declare_clippy_lint! { "lint `use _::*` statements" } -#[derive(Default)] pub struct WildcardImports { warn_on_all: bool, - allowed_segments: FxHashSet<String>, + allowed_segments: &'static FxHashSet<String>, } impl WildcardImports { - pub fn new(warn_on_all: bool, allowed_wildcard_imports: FxHashSet<String>) -> Self { + pub fn new(conf: &'static Conf) -> Self { Self { - warn_on_all, - allowed_segments: allowed_wildcard_imports, + warn_on_all: conf.warn_on_all_wildcard_imports, + allowed_segments: &conf.allowed_wildcard_imports, } } } @@ -181,7 +181,7 @@ impl WildcardImports { fn check_exceptions(&self, cx: &LateContext<'_>, item: &Item<'_>, segments: &[PathSegment<'_>]) -> bool { item.span.from_expansion() || is_prelude_import(segments) - || is_allowed_via_config(segments, &self.allowed_segments) + || is_allowed_via_config(segments, self.allowed_segments) || (is_super_only_import(segments) && is_in_test(cx.tcx, item.hir_id())) } } diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 96e53b7ef0b..54e7e92f0c4 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -1,3 +1,4 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::is_in_test; use clippy_utils::macros::{format_arg_removal_span, root_macro_call_first_node, FormatArgsStorage, MacroCall}; @@ -237,7 +238,6 @@ declare_clippy_lint! { "writing a literal with a format string" } -#[derive(Default)] pub struct Write { format_args: FormatArgsStorage, in_debug_impl: bool, @@ -245,11 +245,11 @@ pub struct Write { } impl Write { - pub fn new(format_args: FormatArgsStorage, allow_print_in_tests: bool) -> Self { + pub fn new(conf: &'static Conf, format_args: FormatArgsStorage) -> Self { Self { format_args, - allow_print_in_tests, - ..Default::default() + allow_print_in_tests: conf.allow_print_in_tests, + in_debug_impl: false, } } } diff --git a/clippy_lints/src/zero_repeat_side_effects.rs b/clippy_lints/src/zero_repeat_side_effects.rs index ad041e55bda..fdc1d06e67a 100644 --- a/clippy_lints/src/zero_repeat_side_effects.rs +++ b/clippy_lints/src/zero_repeat_side_effects.rs @@ -3,10 +3,11 @@ use clippy_utils::higher::VecArgs; use clippy_utils::source::snippet; use clippy_utils::visitors::for_each_expr_without_closures; use rustc_ast::LitKind; +use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; -use rustc_hir::{ExprKind, Node}; +use rustc_hir::{ArrayLen, ConstArgKind, ExprKind, Node}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{self, ConstKind, Ty}; +use rustc_middle::ty::Ty; use rustc_session::declare_lint_pass; use rustc_span::Span; @@ -45,18 +46,27 @@ declare_clippy_lint! { declare_lint_pass!(ZeroRepeatSideEffects => [ZERO_REPEAT_SIDE_EFFECTS]); impl LateLintPass<'_> for ZeroRepeatSideEffects { - fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ rustc_hir::Expr<'_>) { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &rustc_hir::Expr<'_>) { + let hir_map = cx.tcx.hir(); if let Some(args) = VecArgs::hir(cx, expr) && let VecArgs::Repeat(inner_expr, len) = args && let ExprKind::Lit(l) = len.kind - && let LitKind::Int(i, _) = l.node - && i.0 == 0 + && let LitKind::Int(Pu128(0), _) = l.node { inner_check(cx, expr, inner_expr, true); - } else if let ExprKind::Repeat(inner_expr, _) = expr.kind - && let ty::Array(_, cst) = cx.typeck_results().expr_ty(expr).kind() - && let ConstKind::Value(_, ty::ValTree::Leaf(element_count)) = cst.kind() - && element_count.to_target_usize(cx.tcx) == 0 + } + // Lint only if the length is a literal zero, and not a path to any constants. + // NOTE(@y21): When reading `[f(); LEN]`, I intuitively expect that the function is called and it + // doesn't seem as confusing as `[f(); 0]`. It would also have false positives when eg. + // the const item depends on `#[cfg]s` and has different values in different compilation + // sessions). + else if let ExprKind::Repeat(inner_expr, length) = expr.kind + && let ArrayLen::Body(const_arg) = length + && let ConstArgKind::Anon(anon_const) = const_arg.kind + && let length_expr = hir_map.body(anon_const.body).value + && !length_expr.span.from_expansion() + && let ExprKind::Lit(literal) = length_expr.kind + && let LitKind::Int(Pu128(0), _) = literal.node { inner_check(cx, expr, inner_expr, false); } |
