From cef9cb8eb87c58326739ee62d2044ab2d1ef3f90 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sun, 7 Sep 2025 17:57:05 +0200 Subject: [PATCH 01/57] fix(needless_maybe_sized): don't lint in proc-macro-generated code --- clippy_lints/src/needless_maybe_sized.rs | 3 ++ clippy_utils/src/check_proc_macro.rs | 47 ++++++++++++++++++++++-- tests/ui/needless_maybe_sized.fixed | 9 +++++ tests/ui/needless_maybe_sized.rs | 9 +++++ 4 files changed, 64 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/needless_maybe_sized.rs b/clippy_lints/src/needless_maybe_sized.rs index 4bcd26c74f57f..e7ae54e0d0e16 100644 --- a/clippy_lints/src/needless_maybe_sized.rs +++ b/clippy_lints/src/needless_maybe_sized.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::is_from_proc_macro; use rustc_errors::Applicability; use rustc_hir::def_id::{DefId, DefIdMap}; use rustc_hir::{BoundPolarity, GenericBound, Generics, PolyTraitRef, TraitBoundModifiers, WherePredicateKind}; @@ -34,6 +35,7 @@ declare_clippy_lint! { declare_lint_pass!(NeedlessMaybeSized => [NEEDLESS_MAYBE_SIZED]); #[expect(clippy::struct_field_names)] +#[derive(Debug)] struct Bound<'tcx> { /// The [`DefId`] of the type parameter the bound refers to param: DefId, @@ -127,6 +129,7 @@ impl LateLintPass<'_> for NeedlessMaybeSized { if bound.trait_bound.modifiers == TraitBoundModifiers::NONE && let Some(sized_bound) = maybe_sized_params.get(&bound.param) && let Some(path) = path_to_sized_bound(cx, bound.trait_bound) + && !is_from_proc_macro(cx, bound.trait_bound) { span_lint_and_then( cx, diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs index ff3e7b94f03bf..2ae3713d427dc 100644 --- a/clippy_utils/src/check_proc_macro.rs +++ b/clippy_utils/src/check_proc_macro.rs @@ -21,10 +21,10 @@ use rustc_ast::ast::{ use rustc_ast::token::CommentKind; use rustc_hir::intravisit::FnKind; use rustc_hir::{ - Block, BlockCheckMode, Body, Closure, Destination, Expr, ExprKind, FieldDef, FnHeader, FnRetTy, HirId, Impl, - ImplItem, ImplItemImplKind, ImplItemKind, IsAuto, Item, ItemKind, Lit, LoopSource, MatchSource, MutTy, Node, Path, - QPath, Safety, TraitImplHeader, TraitItem, TraitItemKind, Ty, TyKind, UnOp, UnsafeSource, Variant, VariantData, - YieldSource, + Block, BlockCheckMode, Body, BoundConstness, BoundPolarity, Closure, Destination, Expr, ExprKind, FieldDef, + FnHeader, FnRetTy, HirId, Impl, ImplItem, ImplItemImplKind, ImplItemKind, IsAuto, Item, ItemKind, Lit, LoopSource, + MatchSource, MutTy, Node, Path, PolyTraitRef, QPath, Safety, TraitBoundModifiers, TraitImplHeader, TraitItem, + TraitItemKind, TraitRef, Ty, TyKind, UnOp, UnsafeSource, Variant, VariantData, YieldSource, }; use rustc_lint::{EarlyContext, LateContext, LintContext}; use rustc_middle::ty::TyCtxt; @@ -537,6 +537,44 @@ fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) { } } +// NOTE: can't `impl WithSearchPat for TraitRef`, because `TraitRef` doesn't have a `span` field +// (nor a method) +fn trait_ref_search_pat(trait_ref: &TraitRef<'_>) -> (Pat, Pat) { + path_search_pat(trait_ref.path) +} + +fn poly_trait_ref_search_pat(poly_trait_ref: &PolyTraitRef<'_>) -> (Pat, Pat) { + // NOTE: unfortunately we can't use `bound_generic_params` to see whether the pattern starts with + // `for<..>`, because if it's empty, we could have either `for<>` (nothing bound), or + // no `for` at all + let PolyTraitRef { + modifiers: TraitBoundModifiers { constness, polarity }, + trait_ref, + .. + } = poly_trait_ref; + + let trait_ref_search_pat = trait_ref_search_pat(trait_ref); + + #[expect( + clippy::unnecessary_lazy_evaluations, + reason = "the closure in `or_else` has `match polarity`, which isn't free" + )] + let start = match constness { + BoundConstness::Never => None, + BoundConstness::Maybe(_) => Some(Pat::Str("[const]")), + BoundConstness::Always(_) => Some(Pat::Str("const")), + } + .or_else(|| match polarity { + BoundPolarity::Negative(_) => Some(Pat::Str("!")), + BoundPolarity::Maybe(_) => Some(Pat::Str("?")), + BoundPolarity::Positive => None, + }) + .unwrap_or(trait_ref_search_pat.0); + let end = trait_ref_search_pat.1; + + (start, end) +} + fn ident_search_pat(ident: Ident) -> (Pat, Pat) { (Pat::Sym(ident.name), Pat::Sym(ident.name)) } @@ -569,6 +607,7 @@ impl_with_search_pat!((_cx: LateContext<'tcx>, self: Ty<'_>) => ty_search_pat(se impl_with_search_pat!((_cx: LateContext<'tcx>, self: Ident) => ident_search_pat(*self)); impl_with_search_pat!((_cx: LateContext<'tcx>, self: Lit) => lit_search_pat(&self.node)); impl_with_search_pat!((_cx: LateContext<'tcx>, self: Path<'_>) => path_search_pat(self)); +impl_with_search_pat!((_cx: LateContext<'tcx>, self: PolyTraitRef<'_>) => poly_trait_ref_search_pat(self)); impl_with_search_pat!((_cx: EarlyContext<'tcx>, self: Attribute) => attr_search_pat(self)); impl_with_search_pat!((_cx: EarlyContext<'tcx>, self: ast::Ty) => ast_ty_search_pat(self)); diff --git a/tests/ui/needless_maybe_sized.fixed b/tests/ui/needless_maybe_sized.fixed index f8b1643969903..92840a888ada5 100644 --- a/tests/ui/needless_maybe_sized.fixed +++ b/tests/ui/needless_maybe_sized.fixed @@ -133,4 +133,13 @@ struct InDerive { struct Refined(T); impl Refined {} +// in proc-macros +fn issue13360() { + #[derive(serde::Serialize)] + #[serde(bound = "T: A")] + struct Foo { + t: std::marker::PhantomData, + } +} + fn main() {} diff --git a/tests/ui/needless_maybe_sized.rs b/tests/ui/needless_maybe_sized.rs index e4312b40563f1..02242260af1fe 100644 --- a/tests/ui/needless_maybe_sized.rs +++ b/tests/ui/needless_maybe_sized.rs @@ -136,4 +136,13 @@ struct InDerive { struct Refined(T); impl Refined {} +// in proc-macros +fn issue13360() { + #[derive(serde::Serialize)] + #[serde(bound = "T: A")] + struct Foo { + t: std::marker::PhantomData, + } +} + fn main() {} From 72ff51ac77cde436ae740a0b85ff313d1bdf5aae Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 11 Dec 2025 21:08:01 +0100 Subject: [PATCH 02/57] Add new `disallowed_fields` lint --- CHANGELOG.md | 2 + book/src/lint_configuration.md | 17 +++ clippy_config/src/conf.rs | 11 ++ clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/disallowed_fields.rs | 139 ++++++++++++++++++ clippy_lints/src/lib.rs | 2 + clippy_utils/src/paths.rs | 12 ++ .../toml_disallowed_fields/clippy.toml | 12 ++ .../conf_disallowed_fields.rs | 39 +++++ .../conf_disallowed_fields.stderr | 55 +++++++ .../toml_unknown_key/conf_unknown_key.stderr | 3 + 11 files changed, 293 insertions(+) create mode 100644 clippy_lints/src/disallowed_fields.rs create mode 100644 tests/ui-toml/toml_disallowed_fields/clippy.toml create mode 100644 tests/ui-toml/toml_disallowed_fields/conf_disallowed_fields.rs create mode 100644 tests/ui-toml/toml_disallowed_fields/conf_disallowed_fields.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 91d793489be21..63fd65c7da718 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6374,6 +6374,7 @@ Released 2018-09-13 [`derive_ord_xor_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_ord_xor_partial_ord [`derive_partial_eq_without_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_partial_eq_without_eq [`derived_hash_with_manual_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derived_hash_with_manual_eq +[`disallowed_fields`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_fields [`disallowed_macros`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_macros [`disallowed_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_method [`disallowed_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_methods @@ -7190,6 +7191,7 @@ Released 2018-09-13 [`check-private-items`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-private-items [`cognitive-complexity-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#cognitive-complexity-threshold [`const-literal-digits-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#const-literal-digits-threshold +[`disallowed-fields`]: https://doc.rust-lang.org/clippy/lint_configuration.html#disallowed-fields [`disallowed-macros`]: https://doc.rust-lang.org/clippy/lint_configuration.html#disallowed-macros [`disallowed-methods`]: https://doc.rust-lang.org/clippy/lint_configuration.html#disallowed-methods [`disallowed-names`]: https://doc.rust-lang.org/clippy/lint_configuration.html#disallowed-names diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index a1c079898594f..f5a341941d423 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -505,6 +505,23 @@ The minimum digits a const float literal must have to supress the `excessive_pre * [`excessive_precision`](https://rust-lang.github.io/rust-clippy/master/index.html#excessive_precision) +## `disallowed-fields` +The list of disallowed fields, written as fully qualified paths. + +**Fields:** +- `path` (required): the fully qualified path to the field that should be disallowed +- `reason` (optional): explanation why this field is disallowed +- `replacement` (optional): suggested alternative method +- `allow-invalid` (optional, `false` by default): when set to `true`, it will ignore this entry + if the path doesn't exist, instead of emitting an error + +**Default Value:** `[]` + +--- +**Affected lints:** +* [`disallowed_fields`](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_fields) + + ## `disallowed-macros` The list of disallowed macros, written as fully qualified paths. diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index e1d7c1d88eb9a..7c06906e3b34a 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -581,6 +581,17 @@ define_Conf! { /// Use the Cognitive Complexity lint instead. #[conf_deprecated("Please use `cognitive-complexity-threshold` instead", cognitive_complexity_threshold)] cyclomatic_complexity_threshold: u64 = 25, + /// The list of disallowed fields, written as fully qualified paths. + /// + /// **Fields:** + /// - `path` (required): the fully qualified path to the field that should be disallowed + /// - `reason` (optional): explanation why this field is disallowed + /// - `replacement` (optional): suggested alternative method + /// - `allow-invalid` (optional, `false` by default): when set to `true`, it will ignore this entry + /// if the path doesn't exist, instead of emitting an error + #[disallowed_paths_allow_replacements = true] + #[lints(disallowed_fields)] + disallowed_fields: Vec = Vec::new(), /// The list of disallowed macros, written as fully qualified paths. /// /// **Fields:** diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 6b68940c6423a..6666b2d9958a1 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -105,6 +105,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ_INFO, crate::derive::EXPL_IMPL_CLONE_ON_COPY_INFO, crate::derive::UNSAFE_DERIVE_DESERIALIZE_INFO, + crate::disallowed_fields::DISALLOWED_FIELDS_INFO, crate::disallowed_macros::DISALLOWED_MACROS_INFO, crate::disallowed_methods::DISALLOWED_METHODS_INFO, crate::disallowed_names::DISALLOWED_NAMES_INFO, diff --git a/clippy_lints/src/disallowed_fields.rs b/clippy_lints/src/disallowed_fields.rs new file mode 100644 index 0000000000000..c3b6d97f195f0 --- /dev/null +++ b/clippy_lints/src/disallowed_fields.rs @@ -0,0 +1,139 @@ +use clippy_config::Conf; +use clippy_config::types::{DisallowedPath, create_disallowed_map}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::paths::PathNS; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def_id::DefIdMap; +use rustc_hir::{Expr, ExprKind, Pat, PatKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{self, TyCtxt}; +use rustc_session::impl_lint_pass; + +declare_clippy_lint! { + /// ### What it does + /// Denies the configured fields in clippy.toml + /// + /// Note: Even though this lint is warn-by-default, it will only trigger if + /// fields are defined in the clippy.toml file. + /// + /// ### Why is this bad? + /// Some fields are undesirable in certain contexts, and it's beneficial to + /// lint for them as needed. + /// + /// ### Example + /// An example clippy.toml configuration: + /// ```toml + /// # clippy.toml + /// disallowed-fields = [ + /// # Can use a string as the path of the disallowed field. + /// "std::ops::Range::start", + /// # Can also use an inline table with a `path` key. + /// { path = "std::ops::Range::start" }, + /// # When using an inline table, can add a `reason` for why the field + /// # is disallowed. + /// { path = "std::ops::Range::start", reason = "The start of the range is not used" }, + /// ] + /// ``` + /// + /// ```rust + /// use std::ops::Range; + /// + /// let range = Range { start: 0, end: 1 }; + /// println!("{}", range.start); // `start` is disallowed in the config. + /// ``` + /// + /// Use instead: + /// ```rust + /// use std::ops::Range; + /// + /// let range = Range { start: 0, end: 1 }; + /// println!("{}", range.end); // `end` is _not_ disallowed in the config. + /// ``` + #[clippy::version = "1.93.0"] + pub DISALLOWED_FIELDS, + style, + "declaration of a disallowed field use" +} + +pub struct DisallowedFields { + disallowed: DefIdMap<(&'static str, &'static DisallowedPath)>, +} + +impl DisallowedFields { + pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { + let (disallowed, _) = create_disallowed_map( + tcx, + &conf.disallowed_fields, + PathNS::Field, + |def_kind| matches!(def_kind, DefKind::Field), + "field", + false, + ); + Self { disallowed } + } +} + +impl_lint_pass!(DisallowedFields => [DISALLOWED_FIELDS]); + +impl<'tcx> LateLintPass<'tcx> for DisallowedFields { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + let (id, span) = match &expr.kind { + ExprKind::Path(path) if let Res::Def(_, id) = cx.qpath_res(path, expr.hir_id) => (id, expr.span), + ExprKind::Field(e, ident) => { + // Very round-about way to get the field `DefId` from the expr: first we get its + // parent `Ty`. Then we go through all its fields to find the one with the expected + // name and get the `DefId` from it. + if let Some(parent_ty) = cx.typeck_results().expr_ty_adjusted_opt(e) + && let ty::Adt(adt_def, ..) = parent_ty.kind() + && let Some(field_def_id) = adt_def.all_fields().find_map(|field| { + if field.name == ident.name { + Some(field.did) + } else { + None + } + }) + { + (field_def_id, ident.span) + } else { + return; + } + }, + _ => return, + }; + if let Some(&(path, disallowed_path)) = self.disallowed.get(&id) { + span_lint_and_then( + cx, + DISALLOWED_FIELDS, + span, + format!("use of a disallowed field `{path}`"), + disallowed_path.diag_amendment(span), + ); + } + } + + fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) { + if let PatKind::Struct(struct_path, pat_fields, _) = pat.kind + && let Res::Def(DefKind::Struct, struct_def_id) = cx.typeck_results().qpath_res(&struct_path, pat.hir_id) + { + let adt_def = cx.tcx.adt_def(struct_def_id); + for field in pat_fields { + if let Some(def_id) = adt_def.all_fields().find_map(|adt_field| { + if field.ident.name == adt_field.name { + Some(adt_field.did) + } else { + None + } + }) && let Some(&(path, disallowed_path)) = self.disallowed.get(&def_id) + { + span_lint_and_then( + cx, + DISALLOWED_FIELDS, + field.span, + format!("use of a disallowed field `{path}`"), + disallowed_path.diag_amendment(field.span), + ); + } + } + } + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index a957afdb19103..a10f71c01cd94 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -104,6 +104,7 @@ mod default_union_representation; mod dereference; mod derivable_impls; mod derive; +mod disallowed_fields; mod disallowed_macros; mod disallowed_methods; mod disallowed_names; @@ -855,6 +856,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co Box::new(|_| Box::new(toplevel_ref_arg::ToplevelRefArg)), Box::new(|_| Box::new(volatile_composites::VolatileComposites)), Box::new(|_| Box::::default()), + Box::new(move |tcx| Box::new(disallowed_fields::DisallowedFields::new(tcx, conf))), Box::new(move |_| Box::new(manual_ilog2::ManualIlog2::new(conf))), Box::new(|_| Box::new(same_length_and_capacity::SameLengthAndCapacity)), // add late passes here, used by `cargo dev new_lint` diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 8aa663163caf1..b8e3f6fc3a65f 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -318,6 +318,13 @@ fn local_item_child_by_name(tcx: TyCtxt<'_>, local_id: LocalDefId, ns: PathNS, n .filter_by_name_unhygienic(name) .find(|assoc_item| ns.matches(Some(assoc_item.namespace()))) .map(|assoc_item| assoc_item.def_id), + ItemKind::Struct(_, _, rustc_hir::VariantData::Struct { fields, .. }) => fields.iter().find_map(|field| { + if field.ident.name == name { + Some(field.def_id.to_def_id()) + } else { + None + } + }), _ => None, } } @@ -336,6 +343,11 @@ fn non_local_item_child_by_name(tcx: TyCtxt<'_>, def_id: DefId, ns: PathNS, name .iter() .copied() .find(|assoc_def_id| tcx.item_name(*assoc_def_id) == name && ns.matches(tcx.def_kind(assoc_def_id).ns())), + DefKind::Struct => tcx + .associated_item_def_ids(def_id) + .iter() + .copied() + .find(|assoc_def_id| tcx.item_name(*assoc_def_id) == name), _ => None, } } diff --git a/tests/ui-toml/toml_disallowed_fields/clippy.toml b/tests/ui-toml/toml_disallowed_fields/clippy.toml new file mode 100644 index 0000000000000..0dc494815a348 --- /dev/null +++ b/tests/ui-toml/toml_disallowed_fields/clippy.toml @@ -0,0 +1,12 @@ +disallowed-fields = [ + # just a string is shorthand for path only + "std::ops::Range::start", + # can give path and reason with an inline table + { path = "std::ops::Range::end", reason = "no end allowed" }, + # can use an inline table but omit reason + { path = "std::ops::RangeTo::end" }, + # local paths + "conf_disallowed_fields::X::y", + # re-exports + "conf_disallowed_fields::Y::y", +] diff --git a/tests/ui-toml/toml_disallowed_fields/conf_disallowed_fields.rs b/tests/ui-toml/toml_disallowed_fields/conf_disallowed_fields.rs new file mode 100644 index 0000000000000..359c861baf66c --- /dev/null +++ b/tests/ui-toml/toml_disallowed_fields/conf_disallowed_fields.rs @@ -0,0 +1,39 @@ +#![warn(clippy::disallowed_fields)] +#![allow(clippy::match_single_binding)] + +use std::ops::{Range, RangeTo}; + +struct X { + y: u32, +} + +use crate::X as Y; + +fn b(X { y }: X) {} +//~^ disallowed_fields + +fn main() { + let x = X { y: 0 }; + let _ = x.y; + //~^ disallowed_fields + + let x = Y { y: 0 }; + let _ = x.y; + //~^ disallowed_fields + + let x = Range { start: 0, end: 0 }; + let _ = x.start; + //~^ disallowed_fields + let _ = x.end; + //~^ disallowed_fields + let Range { start, .. } = x; + //~^ disallowed_fields + + let x = RangeTo { end: 0 }; + let _ = x.end; + //~^ disallowed_fields + + match x { + RangeTo { end } => {}, //~ disallowed_fields + } +} diff --git a/tests/ui-toml/toml_disallowed_fields/conf_disallowed_fields.stderr b/tests/ui-toml/toml_disallowed_fields/conf_disallowed_fields.stderr new file mode 100644 index 0000000000000..3cc22a55ab9cc --- /dev/null +++ b/tests/ui-toml/toml_disallowed_fields/conf_disallowed_fields.stderr @@ -0,0 +1,55 @@ +error: use of a disallowed field `conf_disallowed_fields::Y::y` + --> tests/ui-toml/toml_disallowed_fields/conf_disallowed_fields.rs:12:10 + | +LL | fn b(X { y }: X) {} + | ^ + | + = note: `-D clippy::disallowed-fields` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::disallowed_fields)]` + +error: use of a disallowed field `conf_disallowed_fields::Y::y` + --> tests/ui-toml/toml_disallowed_fields/conf_disallowed_fields.rs:17:15 + | +LL | let _ = x.y; + | ^ + +error: use of a disallowed field `conf_disallowed_fields::Y::y` + --> tests/ui-toml/toml_disallowed_fields/conf_disallowed_fields.rs:21:15 + | +LL | let _ = x.y; + | ^ + +error: use of a disallowed field `std::ops::Range::start` + --> tests/ui-toml/toml_disallowed_fields/conf_disallowed_fields.rs:25:15 + | +LL | let _ = x.start; + | ^^^^^ + +error: use of a disallowed field `std::ops::Range::end` + --> tests/ui-toml/toml_disallowed_fields/conf_disallowed_fields.rs:27:15 + | +LL | let _ = x.end; + | ^^^ + | + = note: no end allowed + +error: use of a disallowed field `std::ops::Range::start` + --> tests/ui-toml/toml_disallowed_fields/conf_disallowed_fields.rs:29:17 + | +LL | let Range { start, .. } = x; + | ^^^^^ + +error: use of a disallowed field `std::ops::RangeTo::end` + --> tests/ui-toml/toml_disallowed_fields/conf_disallowed_fields.rs:33:15 + | +LL | let _ = x.end; + | ^^^ + +error: use of a disallowed field `std::ops::RangeTo::end` + --> tests/ui-toml/toml_disallowed_fields/conf_disallowed_fields.rs:37:19 + | +LL | RangeTo { end } => {}, + | ^^^ + +error: aborting due to 8 previous errors + diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index e208bd510657e..eea1f4f00e11d 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -37,6 +37,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect check-private-items cognitive-complexity-threshold const-literal-digits-threshold + disallowed-fields disallowed-macros disallowed-methods disallowed-names @@ -136,6 +137,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect check-private-items cognitive-complexity-threshold const-literal-digits-threshold + disallowed-fields disallowed-macros disallowed-methods disallowed-names @@ -235,6 +237,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni check-private-items cognitive-complexity-threshold const-literal-digits-threshold + disallowed-fields disallowed-macros disallowed-methods disallowed-names From 50526babf6af54f3693d4159966bbeb82101cabc Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 30 Dec 2025 15:48:02 +0100 Subject: [PATCH 03/57] Add new `PathNS::Field` variant --- clippy_utils/src/paths.rs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index b8e3f6fc3a65f..1b95b6799c7ba 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -26,6 +26,7 @@ pub enum PathNS { Type, Value, Macro, + Field, /// Resolves to the name in the first available namespace, e.g. for `std::vec` this would return /// either the macro or the module but **not** both @@ -41,6 +42,7 @@ impl PathNS { PathNS::Type => TypeNS, PathNS::Value => ValueNS, PathNS::Macro => MacroNS, + PathNS::Field => return false, PathNS::Arbitrary => return true, }; @@ -299,7 +301,7 @@ fn local_item_child_by_name(tcx: TyCtxt<'_>, local_id: LocalDefId, ns: PathNS, n PathNS::Type => opt_def_id(path.res.type_ns), PathNS::Value => opt_def_id(path.res.value_ns), PathNS::Macro => opt_def_id(path.res.macro_ns), - PathNS::Arbitrary => unreachable!(), + PathNS::Arbitrary | PathNS::Field => unreachable!(), } } else { None @@ -318,13 +320,15 @@ fn local_item_child_by_name(tcx: TyCtxt<'_>, local_id: LocalDefId, ns: PathNS, n .filter_by_name_unhygienic(name) .find(|assoc_item| ns.matches(Some(assoc_item.namespace()))) .map(|assoc_item| assoc_item.def_id), - ItemKind::Struct(_, _, rustc_hir::VariantData::Struct { fields, .. }) => fields.iter().find_map(|field| { - if field.ident.name == name { - Some(field.def_id.to_def_id()) - } else { - None - } - }), + ItemKind::Struct(_, _, rustc_hir::VariantData::Struct { fields, .. }) if ns == PathNS::Field => { + fields.iter().find_map(|field| { + if field.ident.name == name { + Some(field.def_id.to_def_id()) + } else { + None + } + }) + }, _ => None, } } From fcccbf1d0c44e5ceb16a275d1cf75d002abb17f6 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Thu, 8 Jan 2026 18:25:16 +0100 Subject: [PATCH 04/57] The path from a type to itself is `Self` --- clippy_utils/src/lib.rs | 2 ++ tests/ui/eta.fixed | 9 +++++++++ tests/ui/eta.rs | 9 +++++++++ tests/ui/eta.stderr | 8 +++++++- 4 files changed, 27 insertions(+), 1 deletion(-) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 2d079deb0ce46..de75874e50980 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -3256,6 +3256,8 @@ fn maybe_get_relative_path(from: &DefPath, to: &DefPath, max_super: usize) -> St None } }))) + } else if go_up_by == 0 && path.is_empty() { + String::from("Self") } else { join_path_syms(repeat_n(kw::Super, go_up_by).chain(path)) } diff --git a/tests/ui/eta.fixed b/tests/ui/eta.fixed index 107318e5323d0..fdaf1c37b2018 100644 --- a/tests/ui/eta.fixed +++ b/tests/ui/eta.fixed @@ -643,3 +643,12 @@ where { maybe.map(|x| visitor(x)); } + +trait Issue16360: Sized { + fn method(&self); + + fn ice_machine(array: [Self; 1]) { + array.iter().for_each(Self::method); + //~^ redundant_closure_for_method_calls + } +} diff --git a/tests/ui/eta.rs b/tests/ui/eta.rs index b85e8e75153a1..1054060db4494 100644 --- a/tests/ui/eta.rs +++ b/tests/ui/eta.rs @@ -643,3 +643,12 @@ where { maybe.map(|x| visitor(x)); } + +trait Issue16360: Sized { + fn method(&self); + + fn ice_machine(array: [Self; 1]) { + array.iter().for_each(|item| item.method()); + //~^ redundant_closure_for_method_calls + } +} diff --git a/tests/ui/eta.stderr b/tests/ui/eta.stderr index 0b401cdea9875..2e0ccc557915a 100644 --- a/tests/ui/eta.stderr +++ b/tests/ui/eta.stderr @@ -256,5 +256,11 @@ error: redundant closure LL | .map(|n| f(n)) | ^^^^^^^^ help: replace the closure with the function itself: `f` -error: aborting due to 42 previous errors +error: redundant closure + --> tests/ui/eta.rs:651:31 + | +LL | array.iter().for_each(|item| item.method()); + | ^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `Self::method` + +error: aborting due to 43 previous errors From 759c2bf58fc38d2e3de98f04c70478f0a99f1e3c Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Fri, 16 Jan 2026 19:34:39 +0100 Subject: [PATCH 05/57] Assume that any external function might return a type alias We might not have the declaration around if `extern crate` is used, or if rust-src is not installed for the standard library. Better err on the safe side rather than having a behavior depending on what happens to be available at compile time. --- clippy_lints/src/casts/unnecessary_cast.rs | 41 +++++------- tests/ui/unnecessary_cast.fixed | 9 +-- tests/ui/unnecessary_cast.rs | 7 ++- tests/ui/unnecessary_cast.stderr | 72 +++++++++++++--------- 4 files changed, 65 insertions(+), 64 deletions(-) diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs index 7bfe9201d812a..b77e69b33e7b6 100644 --- a/clippy_lints/src/casts/unnecessary_cast.rs +++ b/clippy_lints/src/casts/unnecessary_cast.rs @@ -1,13 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::numeric_literal::NumericLiteral; -use clippy_utils::res::MaybeResPath; +use clippy_utils::res::MaybeResPath as _; use clippy_utils::source::{SpanRangeExt, snippet_opt}; use clippy_utils::visitors::{Visitable, for_each_expr_without_closures}; use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant, is_ty_alias}; use rustc_ast::{LitFloatType, LitIntType, LitKind}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{Expr, ExprKind, Lit, Node, Path, QPath, TyKind, UnOp}; +use rustc_hir::{Expr, ExprKind, FnRetTy, Lit, Node, Path, QPath, TyKind, UnOp}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty::adjustment::Adjust; use rustc_middle::ty::{self, FloatTy, InferTy, Ty}; @@ -97,7 +97,7 @@ pub(super) fn check<'tcx>( // skip cast of fn call that returns type alias if let ExprKind::Cast(inner, ..) = expr.kind - && is_cast_from_ty_alias(cx, inner, cast_from) + && is_cast_from_ty_alias(cx, inner) { return false; } @@ -270,34 +270,25 @@ fn fp_ty_mantissa_nbits(typ: Ty<'_>) -> u32 { /// Finds whether an `Expr` returns a type alias. /// -/// TODO: Maybe we should move this to `clippy_utils` so others won't need to go down this dark, -/// dark path reimplementing this (or something similar). -fn is_cast_from_ty_alias<'tcx>(cx: &LateContext<'tcx>, expr: impl Visitable<'tcx>, cast_from: Ty<'tcx>) -> bool { +/// When in doubt, for example because it calls a non-local function that we don't have the +/// declaration for, assume if might be a type alias. +fn is_cast_from_ty_alias<'tcx>(cx: &LateContext<'tcx>, expr: impl Visitable<'tcx>) -> bool { for_each_expr_without_closures(expr, |expr| { // Calls are a `Path`, and usage of locals are a `Path`. So, this checks // - call() as i32 // - local as i32 if let ExprKind::Path(qpath) = expr.kind { let res = cx.qpath_res(&qpath, expr.hir_id); - // Function call if let Res::Def(DefKind::Fn, def_id) = res { - let Some(snippet) = cx.tcx.def_span(def_id).get_source_text(cx) else { - return ControlFlow::Continue(()); + let Some(def_id) = def_id.as_local() else { + // External function, we can't know, better be safe + return ControlFlow::Break(()); }; - // This is the worst part of this entire function. This is the only way I know of to - // check whether a function returns a type alias. Sure, you can get the return type - // from a function in the current crate as an hir ty, but how do you get it for - // external functions?? Simple: It's impossible. So, we check whether a part of the - // function's declaration snippet is exactly equal to the `Ty`. That way, we can - // see whether it's a type alias. - // - // FIXME: This won't work if the type is given an alias through `use`, should we - // consider this a type alias as well? - if !snippet - .split("->") - .skip(1) - .any(|s| snippet_eq_ty(s, cast_from) || s.split("where").any(|ty| snippet_eq_ty(ty, cast_from))) + if let Some(FnRetTy::Return(ty)) = cx.tcx.hir_get_fn_output(def_id) + && let TyKind::Path(qpath) = ty.kind + && is_ty_alias(&qpath) { + // Function call to a local function returning a type alias return ControlFlow::Break(()); } // Local usage @@ -305,7 +296,7 @@ fn is_cast_from_ty_alias<'tcx>(cx: &LateContext<'tcx>, expr: impl Visitable<'tcx && let Node::LetStmt(l) = cx.tcx.parent_hir_node(hir_id) { if let Some(e) = l.init - && is_cast_from_ty_alias(cx, e, cast_from) + && is_cast_from_ty_alias(cx, e) { return ControlFlow::Break::<()>(()); } @@ -323,7 +314,3 @@ fn is_cast_from_ty_alias<'tcx>(cx: &LateContext<'tcx>, expr: impl Visitable<'tcx }) .is_some() } - -fn snippet_eq_ty(snippet: &str, ty: Ty<'_>) -> bool { - snippet.trim() == ty.to_string() || snippet.trim().contains(&format!("::{ty}")) -} diff --git a/tests/ui/unnecessary_cast.fixed b/tests/ui/unnecessary_cast.fixed index 91ff4b9ee7713..c6e9bc3cba077 100644 --- a/tests/ui/unnecessary_cast.fixed +++ b/tests/ui/unnecessary_cast.fixed @@ -133,12 +133,13 @@ fn main() { aaa(); //~^ unnecessary_cast let x = aaa(); - aaa(); + x; + //~^ unnecessary_cast + bbb(); //~^ unnecessary_cast - // Will not lint currently. - bbb() as u32; let x = bbb(); - bbb() as u32; + x; + //~^ unnecessary_cast let i8_ptr: *const i8 = &1; let u8_ptr: *const u8 = &1; diff --git a/tests/ui/unnecessary_cast.rs b/tests/ui/unnecessary_cast.rs index 5444a914db167..6936a23d42861 100644 --- a/tests/ui/unnecessary_cast.rs +++ b/tests/ui/unnecessary_cast.rs @@ -133,12 +133,13 @@ fn main() { aaa() as u32; //~^ unnecessary_cast let x = aaa(); - aaa() as u32; + x as u32; //~^ unnecessary_cast - // Will not lint currently. bbb() as u32; + //~^ unnecessary_cast let x = bbb(); - bbb() as u32; + x as u32; + //~^ unnecessary_cast let i8_ptr: *const i8 = &1; let u8_ptr: *const u8 = &1; diff --git a/tests/ui/unnecessary_cast.stderr b/tests/ui/unnecessary_cast.stderr index 3e3c5eb81c105..e4f4309ea7163 100644 --- a/tests/ui/unnecessary_cast.stderr +++ b/tests/ui/unnecessary_cast.stderr @@ -100,170 +100,182 @@ LL | aaa() as u32; error: casting to the same type is unnecessary (`u32` -> `u32`) --> tests/ui/unnecessary_cast.rs:136:5 | -LL | aaa() as u32; - | ^^^^^^^^^^^^ help: try: `aaa()` +LL | x as u32; + | ^^^^^^^^ help: try: `x` + +error: casting to the same type is unnecessary (`u32` -> `u32`) + --> tests/ui/unnecessary_cast.rs:138:5 + | +LL | bbb() as u32; + | ^^^^^^^^^^^^ help: try: `bbb()` + +error: casting to the same type is unnecessary (`u32` -> `u32`) + --> tests/ui/unnecessary_cast.rs:141:5 + | +LL | x as u32; + | ^^^^^^^^ help: try: `x` error: casting integer literal to `f32` is unnecessary - --> tests/ui/unnecessary_cast.rs:173:9 + --> tests/ui/unnecessary_cast.rs:174:9 | LL | 100 as f32; | ^^^^^^^^^^ help: try: `100_f32` error: casting integer literal to `f64` is unnecessary - --> tests/ui/unnecessary_cast.rs:175:9 + --> tests/ui/unnecessary_cast.rs:176:9 | LL | 100 as f64; | ^^^^^^^^^^ help: try: `100_f64` error: casting integer literal to `f64` is unnecessary - --> tests/ui/unnecessary_cast.rs:177:9 + --> tests/ui/unnecessary_cast.rs:178:9 | LL | 100_i32 as f64; | ^^^^^^^^^^^^^^ help: try: `100_f64` error: casting integer literal to `f32` is unnecessary - --> tests/ui/unnecessary_cast.rs:179:17 + --> tests/ui/unnecessary_cast.rs:180:17 | LL | let _ = -100 as f32; | ^^^^^^^^^^^ help: try: `-100_f32` error: casting integer literal to `f64` is unnecessary - --> tests/ui/unnecessary_cast.rs:181:17 + --> tests/ui/unnecessary_cast.rs:182:17 | LL | let _ = -100 as f64; | ^^^^^^^^^^^ help: try: `-100_f64` error: casting integer literal to `f64` is unnecessary - --> tests/ui/unnecessary_cast.rs:183:17 + --> tests/ui/unnecessary_cast.rs:184:17 | LL | let _ = -100_i32 as f64; | ^^^^^^^^^^^^^^^ help: try: `-100_f64` error: casting float literal to `f32` is unnecessary - --> tests/ui/unnecessary_cast.rs:185:9 + --> tests/ui/unnecessary_cast.rs:186:9 | LL | 100. as f32; | ^^^^^^^^^^^ help: try: `100_f32` error: casting float literal to `f64` is unnecessary - --> tests/ui/unnecessary_cast.rs:187:9 + --> tests/ui/unnecessary_cast.rs:188:9 | LL | 100. as f64; | ^^^^^^^^^^^ help: try: `100_f64` error: casting integer literal to `u32` is unnecessary - --> tests/ui/unnecessary_cast.rs:200:9 + --> tests/ui/unnecessary_cast.rs:201:9 | LL | 1 as u32; | ^^^^^^^^ help: try: `1_u32` error: casting integer literal to `i32` is unnecessary - --> tests/ui/unnecessary_cast.rs:202:9 + --> tests/ui/unnecessary_cast.rs:203:9 | LL | 0x10 as i32; | ^^^^^^^^^^^ help: try: `0x10_i32` error: casting integer literal to `usize` is unnecessary - --> tests/ui/unnecessary_cast.rs:204:9 + --> tests/ui/unnecessary_cast.rs:205:9 | LL | 0b10 as usize; | ^^^^^^^^^^^^^ help: try: `0b10_usize` error: casting integer literal to `u16` is unnecessary - --> tests/ui/unnecessary_cast.rs:206:9 + --> tests/ui/unnecessary_cast.rs:207:9 | LL | 0o73 as u16; | ^^^^^^^^^^^ help: try: `0o73_u16` error: casting integer literal to `u32` is unnecessary - --> tests/ui/unnecessary_cast.rs:208:9 + --> tests/ui/unnecessary_cast.rs:209:9 | LL | 1_000_000_000 as u32; | ^^^^^^^^^^^^^^^^^^^^ help: try: `1_000_000_000_u32` error: casting float literal to `f64` is unnecessary - --> tests/ui/unnecessary_cast.rs:211:9 + --> tests/ui/unnecessary_cast.rs:212:9 | LL | 1.0 as f64; | ^^^^^^^^^^ help: try: `1.0_f64` error: casting float literal to `f32` is unnecessary - --> tests/ui/unnecessary_cast.rs:213:9 + --> tests/ui/unnecessary_cast.rs:214:9 | LL | 0.5 as f32; | ^^^^^^^^^^ help: try: `0.5_f32` error: casting integer literal to `i32` is unnecessary - --> tests/ui/unnecessary_cast.rs:218:17 + --> tests/ui/unnecessary_cast.rs:219:17 | LL | let _ = -1 as i32; | ^^^^^^^^^ help: try: `-1_i32` error: casting float literal to `f32` is unnecessary - --> tests/ui/unnecessary_cast.rs:220:17 + --> tests/ui/unnecessary_cast.rs:221:17 | LL | let _ = -1.0 as f32; | ^^^^^^^^^^^ help: try: `-1.0_f32` error: casting to the same type is unnecessary (`i32` -> `i32`) - --> tests/ui/unnecessary_cast.rs:227:18 + --> tests/ui/unnecessary_cast.rs:228:18 | LL | let _ = &(x as i32); | ^^^^^^^^^^ help: try: `{ x }` error: casting integer literal to `i32` is unnecessary - --> tests/ui/unnecessary_cast.rs:234:22 + --> tests/ui/unnecessary_cast.rs:235:22 | LL | let _: i32 = -(1) as i32; | ^^^^^^^^^^^ help: try: `-1_i32` error: casting integer literal to `i64` is unnecessary - --> tests/ui/unnecessary_cast.rs:237:22 + --> tests/ui/unnecessary_cast.rs:238:22 | LL | let _: i64 = -(1) as i64; | ^^^^^^^^^^^ help: try: `-1_i64` error: casting float literal to `f64` is unnecessary - --> tests/ui/unnecessary_cast.rs:245:22 + --> tests/ui/unnecessary_cast.rs:246:22 | LL | let _: f64 = (-8.0 as f64).exp(); | ^^^^^^^^^^^^^ help: try: `(-8.0_f64)` error: casting float literal to `f64` is unnecessary - --> tests/ui/unnecessary_cast.rs:248:23 + --> tests/ui/unnecessary_cast.rs:249:23 | LL | let _: f64 = -(8.0 as f64).exp(); // should suggest `-8.0_f64.exp()` here not to change code behavior | ^^^^^^^^^^^^ help: try: `8.0_f64` error: casting to the same type is unnecessary (`f32` -> `f32`) - --> tests/ui/unnecessary_cast.rs:258:20 + --> tests/ui/unnecessary_cast.rs:259:20 | LL | let _num = foo() as f32; | ^^^^^^^^^^^^ help: try: `foo()` error: casting to the same type is unnecessary (`usize` -> `usize`) - --> tests/ui/unnecessary_cast.rs:269:9 + --> tests/ui/unnecessary_cast.rs:270:9 | LL | (*x as usize).pow(2) | ^^^^^^^^^^^^^ help: try: `(*x)` error: casting to the same type is unnecessary (`usize` -> `usize`) - --> tests/ui/unnecessary_cast.rs:277:31 + --> tests/ui/unnecessary_cast.rs:278:31 | LL | assert_eq!(vec.len(), x as usize); | ^^^^^^^^^^ help: try: `x` error: casting to the same type is unnecessary (`i64` -> `i64`) - --> tests/ui/unnecessary_cast.rs:280:17 + --> tests/ui/unnecessary_cast.rs:281:17 | LL | let _ = (5i32 as i64 as i64).abs(); | ^^^^^^^^^^^^^^^^^^^^ help: try: `(5i32 as i64)` error: casting to the same type is unnecessary (`i64` -> `i64`) - --> tests/ui/unnecessary_cast.rs:283:17 + --> tests/ui/unnecessary_cast.rs:284:17 | LL | let _ = 5i32 as i64 as i64; | ^^^^^^^^^^^^^^^^^^ help: try: `5i32 as i64` -error: aborting due to 44 previous errors +error: aborting due to 46 previous errors From b05aec3f7bf8bf1a97af01231418d95524b57e0e Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Tue, 27 Jan 2026 17:18:02 +0000 Subject: [PATCH 06/57] Fix panic/assert message detection in edition 2015/2018 Fixes a FP for assertions_on_result_states, missing_assert_message on edition 2015/2018 --- .../src/assertions_on_result_states.rs | 4 +- clippy_lints/src/missing_assert_message.rs | 4 +- clippy_lints/src/mutable_debug_assertion.rs | 54 +++---- clippy_utils/src/macros.rs | 67 +++++---- clippy_utils/src/sym.rs | 1 - ...missing_assert_message.edition2015.stderr} | 32 ++--- .../missing_assert_message.edition2021.stderr | 132 ++++++++++++++++++ tests/ui/missing_assert_message.rs | 5 +- 8 files changed, 223 insertions(+), 76 deletions(-) rename tests/ui/{missing_assert_message.stderr => missing_assert_message.edition2015.stderr} (81%) create mode 100644 tests/ui/missing_assert_message.edition2021.stderr diff --git a/clippy_lints/src/assertions_on_result_states.rs b/clippy_lints/src/assertions_on_result_states.rs index cc62306b33b53..d48ef10c8cd25 100644 --- a/clippy_lints/src/assertions_on_result_states.rs +++ b/clippy_lints/src/assertions_on_result_states.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::macros::{PanicExpn, find_assert_args, root_macro_call_first_node}; +use clippy_utils::macros::{find_assert_args, root_macro_call_first_node}; use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::snippet_with_context; use clippy_utils::sym; @@ -52,7 +52,7 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates { if let Some(macro_call) = root_macro_call_first_node(cx, e) && matches!(cx.tcx.get_diagnostic_name(macro_call.def_id), Some(sym::assert_macro)) && let Some((condition, panic_expn)) = find_assert_args(cx, e, macro_call.expn) - && matches!(panic_expn, PanicExpn::Empty) + && panic_expn.is_default_message() && let ExprKind::MethodCall(method_segment, recv, [], _) = condition.kind && let result_type_with_refs = cx.typeck_results().expr_ty(recv) && let result_type = result_type_with_refs.peel_refs() diff --git a/clippy_lints/src/missing_assert_message.rs b/clippy_lints/src/missing_assert_message.rs index 86348f04600b8..dcb185b51a096 100644 --- a/clippy_lints/src/missing_assert_message.rs +++ b/clippy_lints/src/missing_assert_message.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_in_test; -use clippy_utils::macros::{PanicExpn, find_assert_args, find_assert_eq_args, root_macro_call_first_node}; +use clippy_utils::macros::{find_assert_args, find_assert_eq_args, root_macro_call_first_node}; use rustc_hir::Expr; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -78,7 +78,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingAssertMessage { panic_expn }; - if let PanicExpn::Empty = panic_expn { + if panic_expn.is_default_message() { #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] span_lint_and_then( cx, diff --git a/clippy_lints/src/mutable_debug_assertion.rs b/clippy_lints/src/mutable_debug_assertion.rs index 9b327955608ae..1a1a912b7823b 100644 --- a/clippy_lints/src/mutable_debug_assertion.rs +++ b/clippy_lints/src/mutable_debug_assertion.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::macros::{find_assert_eq_args, root_macro_call_first_node}; +use clippy_utils::macros::{MacroCall, find_assert_args, find_assert_eq_args, root_macro_call_first_node}; use clippy_utils::sym; use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{BorrowKind, Expr, ExprKind, MatchSource, Mutability}; @@ -43,33 +43,39 @@ impl<'tcx> LateLintPass<'tcx> for DebugAssertWithMutCall { let Some(macro_call) = root_macro_call_first_node(cx, e) else { return; }; - if !matches!( - cx.tcx.get_diagnostic_name(macro_call.def_id), - Some(sym::debug_assert_macro | sym::debug_assert_eq_macro | sym::debug_assert_ne_macro) - ) { - return; - } - let Some((lhs, rhs, _)) = find_assert_eq_args(cx, e, macro_call.expn) else { - return; - }; - for arg in [lhs, rhs] { - let mut visitor = MutArgVisitor::new(cx); - visitor.visit_expr(arg); - if let Some(span) = visitor.expr_span() { - span_lint( - cx, - DEBUG_ASSERT_WITH_MUT_CALL, - span, - format!( - "do not call a function with mutable arguments inside of `{}!`", - cx.tcx.item_name(macro_call.def_id) - ), - ); - } + match cx.tcx.get_diagnostic_name(macro_call.def_id) { + Some(sym::debug_assert_macro) => { + if let Some((arg, _)) = find_assert_args(cx, e, macro_call.expn) { + check_arg(cx, arg, ¯o_call); + } + }, + Some(sym::debug_assert_ne_macro | sym::debug_assert_eq_macro) => { + if let Some((lhs, rhs, _)) = find_assert_eq_args(cx, e, macro_call.expn) { + check_arg(cx, lhs, ¯o_call); + check_arg(cx, rhs, ¯o_call); + } + }, + _ => {}, } } } +fn check_arg<'tcx>(cx: &LateContext<'tcx>, arg: &'tcx Expr<'tcx>, macro_call: &MacroCall) { + let mut visitor = MutArgVisitor::new(cx); + visitor.visit_expr(arg); + if let Some(span) = visitor.expr_span() { + span_lint( + cx, + DEBUG_ASSERT_WITH_MUT_CALL, + span, + format!( + "do not call a function with mutable arguments inside of `{}!`", + cx.tcx.item_name(macro_call.def_id) + ), + ); + } +} + struct MutArgVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, expr_span: Option, diff --git a/clippy_utils/src/macros.rs b/clippy_utils/src/macros.rs index 4e06f010bd591..b243e6c50e044 100644 --- a/clippy_utils/src/macros.rs +++ b/clippy_utils/src/macros.rs @@ -228,19 +228,30 @@ pub fn is_assert_macro(cx: &LateContext<'_>, def_id: DefId) -> bool { matches!(name, sym::assert_macro | sym::debug_assert_macro) } +/// A call to a function in [`std::rt`] or [`core::panicking`] that results in a panic, typically +/// part of a `panic!()` expansion (often wrapped in a block) but may be called directly by other +/// macros such as `assert`. #[derive(Debug)] -pub enum PanicExpn<'a> { - /// No arguments - `panic!()` - Empty, - /// A string literal or any `&str` - `panic!("message")` or `panic!(message)` - Str(&'a Expr<'a>), - /// A single argument that implements `Display` - `panic!("{}", object)` +pub enum PanicCall<'a> { + // The default message - `panic!()`, `assert!(true)`, etc. + DefaultMessage, + /// A string literal or any `&str` in edition 2015/2018 - `panic!("message")` or + /// `panic!(message)`. + /// + /// In edition 2021+ `panic!("message")` will be a [`PanicCall::Format`] and `panic!(message)` a + /// compile error. + Str2015(&'a Expr<'a>), + /// A single argument that implements `Display` - `panic!("{}", object)`. + /// + /// `panic!("{object}")` will still be a [`PanicCall::Format`]. Display(&'a Expr<'a>), - /// Anything else - `panic!("error {}: {}", a, b)` + /// Anything else - `panic!("error {}: {}", a, b)`, `panic!("on edition 2021+")`. + /// + /// See [`FormatArgsStorage::get`] to examine the contents of the formatting. Format(&'a Expr<'a>), } -impl<'a> PanicExpn<'a> { +impl<'a> PanicCall<'a> { pub fn parse(expr: &'a Expr<'a>) -> Option { let ExprKind::Call(callee, args) = &expr.kind else { return None; @@ -254,8 +265,13 @@ impl<'a> PanicExpn<'a> { return None; }; let result = match name { - sym::panic if arg.span.eq_ctxt(expr.span) => Self::Empty, - sym::panic | sym::panic_str => Self::Str(arg), + sym::panic | sym::begin_panic | sym::panic_str_2015 => { + if arg.span.eq_ctxt(expr.span) || arg.span.is_dummy() { + Self::DefaultMessage + } else { + Self::Str2015(arg) + } + }, sym::panic_display => { let ExprKind::AddrOf(_, _, e) = &arg.kind else { return None; @@ -275,13 +291,17 @@ impl<'a> PanicExpn<'a> { let msg_arg = &rest[2]; match msg_arg.kind { ExprKind::Call(_, [fmt_arg]) => Self::Format(fmt_arg), - _ => Self::Empty, + _ => Self::DefaultMessage, } }, _ => return None, }; Some(result) } + + pub fn is_default_message(&self) -> bool { + matches!(self, Self::DefaultMessage) + } } /// Finds the arguments of an `assert!` or `debug_assert!` macro call within the macro expansion @@ -289,18 +309,8 @@ pub fn find_assert_args<'a>( cx: &LateContext<'_>, expr: &'a Expr<'a>, expn: ExpnId, -) -> Option<(&'a Expr<'a>, PanicExpn<'a>)> { - find_assert_args_inner(cx, expr, expn).map(|([e], mut p)| { - // `assert!(..)` expands to `core::panicking::panic("assertion failed: ...")` (which we map to - // `PanicExpn::Str(..)`) and `assert!(.., "..")` expands to - // `core::panicking::panic_fmt(format_args!(".."))` (which we map to `PanicExpn::Format(..)`). - // So even we got `PanicExpn::Str(..)` that means there is no custom message provided - if let PanicExpn::Str(_) = p { - p = PanicExpn::Empty; - } - - (e, p) - }) +) -> Option<(&'a Expr<'a>, PanicCall<'a>)> { + find_assert_args_inner(cx, expr, expn).map(|([e], p)| (e, p)) } /// Finds the arguments of an `assert_eq!` or `debug_assert_eq!` macro call within the macro @@ -309,7 +319,7 @@ pub fn find_assert_eq_args<'a>( cx: &LateContext<'_>, expr: &'a Expr<'a>, expn: ExpnId, -) -> Option<(&'a Expr<'a>, &'a Expr<'a>, PanicExpn<'a>)> { +) -> Option<(&'a Expr<'a>, &'a Expr<'a>, PanicCall<'a>)> { find_assert_args_inner(cx, expr, expn).map(|([a, b], p)| (a, b, p)) } @@ -317,7 +327,7 @@ fn find_assert_args_inner<'a, const N: usize>( cx: &LateContext<'_>, expr: &'a Expr<'a>, expn: ExpnId, -) -> Option<([&'a Expr<'a>; N], PanicExpn<'a>)> { +) -> Option<([&'a Expr<'a>; N], PanicCall<'a>)> { let macro_id = expn.expn_data().macro_def_id?; let (expr, expn) = match cx.tcx.item_name(macro_id).as_str().strip_prefix("debug_") { None => (expr, expn), @@ -326,7 +336,7 @@ fn find_assert_args_inner<'a, const N: usize>( let mut args = ArrayVec::new(); let panic_expn = for_each_expr_without_closures(expr, |e| { if args.is_full() { - match PanicExpn::parse(e) { + match PanicCall::parse(e) { Some(expn) => ControlFlow::Break(expn), None => ControlFlow::Continue(Descend::Yes), } @@ -338,10 +348,7 @@ fn find_assert_args_inner<'a, const N: usize>( } }); let args = args.into_inner().ok()?; - // if no `panic!(..)` is found, use `PanicExpn::Empty` - // to indicate that the default assertion message is used - let panic_expn = panic_expn.unwrap_or(PanicExpn::Empty); - Some((args, panic_expn)) + Some((args, panic_expn?)) } fn find_assert_within_debug_assert<'a>( diff --git a/clippy_utils/src/sym.rs b/clippy_utils/src/sym.rs index 5d6f2241c7c0d..667daa7067a88 100644 --- a/clippy_utils/src/sym.rs +++ b/clippy_utils/src/sym.rs @@ -269,7 +269,6 @@ generate! { or_insert, or_insert_with, outer_expn, - panic_str, parse, partition, paths, diff --git a/tests/ui/missing_assert_message.stderr b/tests/ui/missing_assert_message.edition2015.stderr similarity index 81% rename from tests/ui/missing_assert_message.stderr rename to tests/ui/missing_assert_message.edition2015.stderr index c4fb7e3b154e4..c5978d706ecbc 100644 --- a/tests/ui/missing_assert_message.stderr +++ b/tests/ui/missing_assert_message.edition2015.stderr @@ -1,5 +1,5 @@ error: assert without any message - --> tests/ui/missing_assert_message.rs:12:5 + --> tests/ui/missing_assert_message.rs:15:5 | LL | assert!(foo()); | ^^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | assert!(foo()); = help: to override `-D warnings` add `#[allow(clippy::missing_assert_message)]` error: assert without any message - --> tests/ui/missing_assert_message.rs:14:5 + --> tests/ui/missing_assert_message.rs:17:5 | LL | assert_eq!(foo(), foo()); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | assert_eq!(foo(), foo()); = help: consider describing why the failing assert is problematic error: assert without any message - --> tests/ui/missing_assert_message.rs:16:5 + --> tests/ui/missing_assert_message.rs:19:5 | LL | assert_ne!(foo(), foo()); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL | assert_ne!(foo(), foo()); = help: consider describing why the failing assert is problematic error: assert without any message - --> tests/ui/missing_assert_message.rs:18:5 + --> tests/ui/missing_assert_message.rs:21:5 | LL | debug_assert!(foo()); | ^^^^^^^^^^^^^^^^^^^^ @@ -33,7 +33,7 @@ LL | debug_assert!(foo()); = help: consider describing why the failing assert is problematic error: assert without any message - --> tests/ui/missing_assert_message.rs:20:5 + --> tests/ui/missing_assert_message.rs:23:5 | LL | debug_assert_eq!(foo(), foo()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -41,7 +41,7 @@ LL | debug_assert_eq!(foo(), foo()); = help: consider describing why the failing assert is problematic error: assert without any message - --> tests/ui/missing_assert_message.rs:22:5 + --> tests/ui/missing_assert_message.rs:25:5 | LL | debug_assert_ne!(foo(), foo()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -49,7 +49,7 @@ LL | debug_assert_ne!(foo(), foo()); = help: consider describing why the failing assert is problematic error: assert without any message - --> tests/ui/missing_assert_message.rs:28:5 + --> tests/ui/missing_assert_message.rs:31:5 | LL | assert!(bar!(true)); | ^^^^^^^^^^^^^^^^^^^ @@ -57,7 +57,7 @@ LL | assert!(bar!(true)); = help: consider describing why the failing assert is problematic error: assert without any message - --> tests/ui/missing_assert_message.rs:30:5 + --> tests/ui/missing_assert_message.rs:33:5 | LL | assert!(bar!(true, false)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -65,7 +65,7 @@ LL | assert!(bar!(true, false)); = help: consider describing why the failing assert is problematic error: assert without any message - --> tests/ui/missing_assert_message.rs:32:5 + --> tests/ui/missing_assert_message.rs:35:5 | LL | assert_eq!(bar!(true), foo()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -73,7 +73,7 @@ LL | assert_eq!(bar!(true), foo()); = help: consider describing why the failing assert is problematic error: assert without any message - --> tests/ui/missing_assert_message.rs:34:5 + --> tests/ui/missing_assert_message.rs:37:5 | LL | assert_ne!(bar!(true, true), bar!(true)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -81,7 +81,7 @@ LL | assert_ne!(bar!(true, true), bar!(true)); = help: consider describing why the failing assert is problematic error: assert without any message - --> tests/ui/missing_assert_message.rs:40:5 + --> tests/ui/missing_assert_message.rs:43:5 | LL | assert!(foo(),); | ^^^^^^^^^^^^^^^ @@ -89,7 +89,7 @@ LL | assert!(foo(),); = help: consider describing why the failing assert is problematic error: assert without any message - --> tests/ui/missing_assert_message.rs:42:5 + --> tests/ui/missing_assert_message.rs:45:5 | LL | assert_eq!(foo(), foo(),); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -97,7 +97,7 @@ LL | assert_eq!(foo(), foo(),); = help: consider describing why the failing assert is problematic error: assert without any message - --> tests/ui/missing_assert_message.rs:44:5 + --> tests/ui/missing_assert_message.rs:47:5 | LL | assert_ne!(foo(), foo(),); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -105,7 +105,7 @@ LL | assert_ne!(foo(), foo(),); = help: consider describing why the failing assert is problematic error: assert without any message - --> tests/ui/missing_assert_message.rs:46:5 + --> tests/ui/missing_assert_message.rs:49:5 | LL | debug_assert!(foo(),); | ^^^^^^^^^^^^^^^^^^^^^ @@ -113,7 +113,7 @@ LL | debug_assert!(foo(),); = help: consider describing why the failing assert is problematic error: assert without any message - --> tests/ui/missing_assert_message.rs:48:5 + --> tests/ui/missing_assert_message.rs:51:5 | LL | debug_assert_eq!(foo(), foo(),); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -121,7 +121,7 @@ LL | debug_assert_eq!(foo(), foo(),); = help: consider describing why the failing assert is problematic error: assert without any message - --> tests/ui/missing_assert_message.rs:50:5 + --> tests/ui/missing_assert_message.rs:53:5 | LL | debug_assert_ne!(foo(), foo(),); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/missing_assert_message.edition2021.stderr b/tests/ui/missing_assert_message.edition2021.stderr new file mode 100644 index 0000000000000..c5978d706ecbc --- /dev/null +++ b/tests/ui/missing_assert_message.edition2021.stderr @@ -0,0 +1,132 @@ +error: assert without any message + --> tests/ui/missing_assert_message.rs:15:5 + | +LL | assert!(foo()); + | ^^^^^^^^^^^^^^ + | + = help: consider describing why the failing assert is problematic + = note: `-D clippy::missing-assert-message` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::missing_assert_message)]` + +error: assert without any message + --> tests/ui/missing_assert_message.rs:17:5 + | +LL | assert_eq!(foo(), foo()); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider describing why the failing assert is problematic + +error: assert without any message + --> tests/ui/missing_assert_message.rs:19:5 + | +LL | assert_ne!(foo(), foo()); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider describing why the failing assert is problematic + +error: assert without any message + --> tests/ui/missing_assert_message.rs:21:5 + | +LL | debug_assert!(foo()); + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: consider describing why the failing assert is problematic + +error: assert without any message + --> tests/ui/missing_assert_message.rs:23:5 + | +LL | debug_assert_eq!(foo(), foo()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider describing why the failing assert is problematic + +error: assert without any message + --> tests/ui/missing_assert_message.rs:25:5 + | +LL | debug_assert_ne!(foo(), foo()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider describing why the failing assert is problematic + +error: assert without any message + --> tests/ui/missing_assert_message.rs:31:5 + | +LL | assert!(bar!(true)); + | ^^^^^^^^^^^^^^^^^^^ + | + = help: consider describing why the failing assert is problematic + +error: assert without any message + --> tests/ui/missing_assert_message.rs:33:5 + | +LL | assert!(bar!(true, false)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider describing why the failing assert is problematic + +error: assert without any message + --> tests/ui/missing_assert_message.rs:35:5 + | +LL | assert_eq!(bar!(true), foo()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider describing why the failing assert is problematic + +error: assert without any message + --> tests/ui/missing_assert_message.rs:37:5 + | +LL | assert_ne!(bar!(true, true), bar!(true)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider describing why the failing assert is problematic + +error: assert without any message + --> tests/ui/missing_assert_message.rs:43:5 + | +LL | assert!(foo(),); + | ^^^^^^^^^^^^^^^ + | + = help: consider describing why the failing assert is problematic + +error: assert without any message + --> tests/ui/missing_assert_message.rs:45:5 + | +LL | assert_eq!(foo(), foo(),); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider describing why the failing assert is problematic + +error: assert without any message + --> tests/ui/missing_assert_message.rs:47:5 + | +LL | assert_ne!(foo(), foo(),); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider describing why the failing assert is problematic + +error: assert without any message + --> tests/ui/missing_assert_message.rs:49:5 + | +LL | debug_assert!(foo(),); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider describing why the failing assert is problematic + +error: assert without any message + --> tests/ui/missing_assert_message.rs:51:5 + | +LL | debug_assert_eq!(foo(), foo(),); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider describing why the failing assert is problematic + +error: assert without any message + --> tests/ui/missing_assert_message.rs:53:5 + | +LL | debug_assert_ne!(foo(), foo(),); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider describing why the failing assert is problematic + +error: aborting due to 16 previous errors + diff --git a/tests/ui/missing_assert_message.rs b/tests/ui/missing_assert_message.rs index 2ad8e0127edf7..20ef40af170c1 100644 --- a/tests/ui/missing_assert_message.rs +++ b/tests/ui/missing_assert_message.rs @@ -1,4 +1,7 @@ -#![allow(unused)] +//@revisions: edition2015 edition2021 +//@[edition2015] edition:2015 +//@[edition2021] edition:2021 + #![warn(clippy::missing_assert_message)] macro_rules! bar { From d0c6a64ff87e5bbbd4a17b6217caa4042ebd07e0 Mon Sep 17 00:00:00 2001 From: lapla Date: Mon, 2 Feb 2026 02:52:08 +0900 Subject: [PATCH 07/57] Fix `redundant_iter_cloned` false positive with move closures and coroutines --- .../src/methods/iter_overeager_cloned.rs | 39 +++++++++++- tests/ui/iter_overeager_cloned.fixed | 60 ++++++++++++++++++ tests/ui/iter_overeager_cloned.rs | 61 +++++++++++++++++++ tests/ui/iter_overeager_cloned.stderr | 44 ++++++++++++- 4 files changed, 201 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/methods/iter_overeager_cloned.rs b/clippy_lints/src/methods/iter_overeager_cloned.rs index e3bcca64e9235..241791e57c8e6 100644 --- a/clippy_lints/src/methods/iter_overeager_cloned.rs +++ b/clippy_lints/src/methods/iter_overeager_cloned.rs @@ -1,13 +1,15 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_opt; use clippy_utils::ty::{implements_trait, is_copy}; +use clippy_utils::visitors::for_each_expr_without_closures; +use core::ops::ControlFlow; use rustc_ast::BindingMode; use rustc_errors::Applicability; -use rustc_hir::{Body, Expr, ExprKind, HirId, HirIdSet, PatKind}; +use rustc_hir::{Body, CaptureBy, Closure, Expr, ExprKind, HirId, HirIdSet, Param, PatKind}; use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use rustc_lint::LateContext; use rustc_middle::mir::{FakeReadCause, Mutability}; -use rustc_middle::ty::{self, BorrowKind}; +use rustc_middle::ty::{self, BorrowKind, UpvarCapture}; use rustc_span::{Symbol, sym}; use super::{ITER_OVEREAGER_CLONED, REDUNDANT_ITER_CLONED}; @@ -64,6 +66,11 @@ pub(super) fn check<'tcx>( let body @ Body { params: [p], .. } = cx.tcx.hir_body(closure.body) else { return; }; + + if param_captured_by_move_block(cx, body.value, p) { + return; + } + let mut delegate = MoveDelegate { used_move: HirIdSet::default(), }; @@ -140,6 +147,34 @@ struct MoveDelegate { used_move: HirIdSet, } +/// Checks if the expression contains a closure or coroutine with `move` capture semantics that +/// captures the given parameter. +fn param_captured_by_move_block(cx: &LateContext<'_>, expr: &Expr<'_>, param: &Param<'_>) -> bool { + let mut param_hir_ids = HirIdSet::default(); + param.pat.walk(|pat| { + param_hir_ids.insert(pat.hir_id); + true + }); + + for_each_expr_without_closures(expr, |e| { + if let ExprKind::Closure(Closure { + capture_clause: CaptureBy::Value { .. }, + def_id, + .. + }) = e.kind + && cx.tcx.closure_captures(*def_id).iter().any(|capture| { + matches!(capture.info.capture_kind, UpvarCapture::ByValue) + && matches!(capture.place.base, PlaceBase::Upvar(upvar) if param_hir_ids.contains(&upvar.var_path.hir_id)) + }) + { + return ControlFlow::Break(()); + } + + ControlFlow::Continue(()) + }) + .is_some() +} + impl<'tcx> Delegate<'tcx> for MoveDelegate { fn consume(&mut self, place_with_id: &PlaceWithHirId<'tcx>, _: HirId) { if let PlaceBase::Local(l) = place_with_id.place.base { diff --git a/tests/ui/iter_overeager_cloned.fixed b/tests/ui/iter_overeager_cloned.fixed index 4171f19469a4d..520b4f8be8cff 100644 --- a/tests/ui/iter_overeager_cloned.fixed +++ b/tests/ui/iter_overeager_cloned.fixed @@ -102,3 +102,63 @@ fn main() { fn cloned_flatten(x: Option<&Option>) -> Option { x.cloned().flatten() } + +mod issue_16428 { + #[derive(Clone)] + struct Foo; + + impl Foo { + async fn do_async(&self) {} + } + + fn async_move_map() -> Vec> { + let map: std::collections::HashMap<(), Foo> = std::collections::HashMap::new(); + + // Should NOT lint: async move block captures `item` by value + map.values() + .cloned() + .map(|item| async move { item.do_async().await }) + .collect::>() + } + + fn async_move_for_each() { + let map: std::collections::HashMap<(), Foo> = std::collections::HashMap::new(); + + // Should NOT lint: async move block captures `item` by value + map.values() + .cloned() + .for_each(|item| drop(async move { item.do_async().await })); + } + + fn move_closure() { + let vec = vec!["1".to_string(), "2".to_string()]; + + // Should NOT lint: move closure captures `x` by value + let _: Vec<_> = vec.iter().cloned().map(|x| move || x.len()).collect(); + } + + fn async_move_not_capturing_param() { + let vec = vec!["1".to_string(), "2".to_string()]; + + // Should lint: async move captures `y`, not `x` + let _ = vec.iter().map(|x| { + //~^ redundant_iter_cloned + let y = x.len(); + async move { y } + }); + } + + fn move_closure_not_capturing_param() { + let vec = vec!["1".to_string(), "2".to_string()]; + + // Should lint: move closure captures `y`, not `x` + let _: Vec<_> = vec + //~^ redundant_iter_cloned + .iter() + .map(|x| { + let y = x.len(); + move || y + }) + .collect(); + } +} diff --git a/tests/ui/iter_overeager_cloned.rs b/tests/ui/iter_overeager_cloned.rs index fe6aba24dd3e2..3e79675dd7c60 100644 --- a/tests/ui/iter_overeager_cloned.rs +++ b/tests/ui/iter_overeager_cloned.rs @@ -103,3 +103,64 @@ fn main() { fn cloned_flatten(x: Option<&Option>) -> Option { x.cloned().flatten() } + +mod issue_16428 { + #[derive(Clone)] + struct Foo; + + impl Foo { + async fn do_async(&self) {} + } + + fn async_move_map() -> Vec> { + let map: std::collections::HashMap<(), Foo> = std::collections::HashMap::new(); + + // Should NOT lint: async move block captures `item` by value + map.values() + .cloned() + .map(|item| async move { item.do_async().await }) + .collect::>() + } + + fn async_move_for_each() { + let map: std::collections::HashMap<(), Foo> = std::collections::HashMap::new(); + + // Should NOT lint: async move block captures `item` by value + map.values() + .cloned() + .for_each(|item| drop(async move { item.do_async().await })); + } + + fn move_closure() { + let vec = vec!["1".to_string(), "2".to_string()]; + + // Should NOT lint: move closure captures `x` by value + let _: Vec<_> = vec.iter().cloned().map(|x| move || x.len()).collect(); + } + + fn async_move_not_capturing_param() { + let vec = vec!["1".to_string(), "2".to_string()]; + + // Should lint: async move captures `y`, not `x` + let _ = vec.iter().cloned().map(|x| { + //~^ redundant_iter_cloned + let y = x.len(); + async move { y } + }); + } + + fn move_closure_not_capturing_param() { + let vec = vec!["1".to_string(), "2".to_string()]; + + // Should lint: move closure captures `y`, not `x` + let _: Vec<_> = vec + //~^ redundant_iter_cloned + .iter() + .cloned() + .map(|x| { + let y = x.len(); + move || y + }) + .collect(); + } +} diff --git a/tests/ui/iter_overeager_cloned.stderr b/tests/ui/iter_overeager_cloned.stderr index f234d19e4aaaa..72b00ca2e32cf 100644 --- a/tests/ui/iter_overeager_cloned.stderr +++ b/tests/ui/iter_overeager_cloned.stderr @@ -165,5 +165,47 @@ LL | let _ = vec.iter().cloned().any(|x| x.len() == 1); | | | help: try: `.any(|x| x.len() == 1)` -error: aborting due to 19 previous errors +error: unneeded cloning of iterator items + --> tests/ui/iter_overeager_cloned.rs:145:17 + | +LL | let _ = vec.iter().cloned().map(|x| { + | _________________^ +LL | | +LL | | let y = x.len(); +LL | | async move { y } +LL | | }); + | |__________^ + | +help: try + | +LL ~ let _ = vec.iter().map(|x| { +LL + +LL + let y = x.len(); +LL + async move { y } +LL ~ }); + | + +error: unneeded cloning of iterator items + --> tests/ui/iter_overeager_cloned.rs:156:25 + | +LL | let _: Vec<_> = vec + | _________________________^ +LL | | +LL | | .iter() +LL | | .cloned() +... | +LL | | move || y +LL | | }) + | |______________^ + | +help: try + | +LL ~ .iter() +LL + .map(|x| { +LL + let y = x.len(); +LL + move || y +LL + }) + | + +error: aborting due to 21 previous errors From 91de07da549b296105051d837af85565a199a03e Mon Sep 17 00:00:00 2001 From: linshuy2 Date: Thu, 5 Feb 2026 22:40:58 +0000 Subject: [PATCH 08/57] Extend `iter_kv_map` to cover `flat_map` and `filter_map` --- clippy_lints/src/methods/iter_kv_map.rs | 3 +- clippy_lints/src/methods/mod.rs | 8 +- tests/ui/iter_kv_map.fixed | 38 ++++++- tests/ui/iter_kv_map.rs | 38 ++++++- tests/ui/iter_kv_map.stderr | 130 ++++++++++++++++-------- 5 files changed, 172 insertions(+), 45 deletions(-) diff --git a/clippy_lints/src/methods/iter_kv_map.rs b/clippy_lints/src/methods/iter_kv_map.rs index 366bfaed73d48..79034fa23300a 100644 --- a/clippy_lints/src/methods/iter_kv_map.rs +++ b/clippy_lints/src/methods/iter_kv_map.rs @@ -21,6 +21,7 @@ pub(super) fn check<'tcx>( recv: &'tcx Expr<'tcx>, // hashmap m_arg: &'tcx Expr<'tcx>, // |(_, v)| v msrv: Msrv, + method_name: Symbol, ) { if map_type == sym::into_iter && !msrv.meets(cx, msrvs::INTO_KEYS) { return; @@ -67,7 +68,7 @@ pub(super) fn check<'tcx>( format!("iterating on a map's {replacement_kind}s"), "try", format!( - "{recv_snippet}.{into_prefix}{replacement_kind}s().map(|{}{bound_ident}| {body_snippet})", + "{recv_snippet}.{into_prefix}{replacement_kind}s().{method_name}(|{}{bound_ident}| {body_snippet})", annotation.prefix_str(), ), applicability, diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 264405e6c3fb8..ff534abe53537 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -5275,6 +5275,9 @@ impl Methods { call_span, self.msrv, ); + if let Some((map_name @ (sym::iter | sym::into_iter), recv2, _, _, _)) = method_call(recv) { + iter_kv_map::check(cx, map_name, expr, recv2, arg, self.msrv, sym::filter_map); + } }, (sym::find_map, [arg]) => { unnecessary_filter_map::check(cx, expr, arg, call_span, unnecessary_filter_map::Kind::FindMap); @@ -5285,6 +5288,9 @@ impl Methods { lines_filter_map_ok::check_filter_or_flat_map( cx, expr, recv, "flat_map", arg, call_span, self.msrv, ); + if let Some((map_name @ (sym::iter | sym::into_iter), recv2, _, _, _)) = method_call(recv) { + iter_kv_map::check(cx, map_name, expr, recv2, arg, self.msrv, sym::flat_map); + } }, (sym::flatten, []) => { match method_call(recv) { @@ -5383,7 +5389,7 @@ impl Methods { manual_is_variant_and::check_map(cx, expr); match method_call(recv) { Some((map_name @ (sym::iter | sym::into_iter), recv2, _, _, _)) => { - iter_kv_map::check(cx, map_name, expr, recv2, m_arg, self.msrv); + iter_kv_map::check(cx, map_name, expr, recv2, m_arg, self.msrv, sym::map); }, Some((sym::cloned, recv2, [], _, _)) => iter_overeager_cloned::check( cx, diff --git a/tests/ui/iter_kv_map.fixed b/tests/ui/iter_kv_map.fixed index 189d76bc94319..e3ab5fd1e9eff 100644 --- a/tests/ui/iter_kv_map.fixed +++ b/tests/ui/iter_kv_map.fixed @@ -1,5 +1,11 @@ #![warn(clippy::iter_kv_map)] -#![allow(unused_mut, clippy::redundant_clone, clippy::suspicious_map, clippy::map_identity)] +#![allow( + unused_mut, + clippy::redundant_clone, + clippy::redundant_closure, + clippy::suspicious_map, + clippy::map_identity +)] use std::collections::{BTreeMap, HashMap}; @@ -195,3 +201,33 @@ fn issue16340() { let _ = hm.keys().map(|key| vec![key]); //~^ iter_kv_map } + +fn issue16515() { + let hash_map: HashMap = HashMap::new(); + hash_map.keys().flat_map(|k| Some(*k)); + //~^ iter_kv_map + + hash_map.values().flat_map(|v| Some(*v)); + //~^ iter_kv_map + + hash_map.keys().filter_map(|k| (k > &0).then_some(1)); + //~^ iter_kv_map + + hash_map.values().filter_map(|v| (v > &0).then_some(1)); + //~^ iter_kv_map + + hash_map.into_keys().flat_map(|k| Some(k)); + //~^ iter_kv_map + + let hash_map: HashMap = HashMap::new(); + hash_map.into_values().flat_map(|v| Some(v)); + //~^ iter_kv_map + + let hash_map: HashMap = HashMap::new(); + hash_map.into_keys().filter_map(|k| (k > 0).then_some(1)); + //~^ iter_kv_map + + let hash_map: HashMap = HashMap::new(); + hash_map.into_values().filter_map(|v| (v > 0).then_some(1)); + //~^ iter_kv_map +} diff --git a/tests/ui/iter_kv_map.rs b/tests/ui/iter_kv_map.rs index cfc3034470047..903813b1bf628 100644 --- a/tests/ui/iter_kv_map.rs +++ b/tests/ui/iter_kv_map.rs @@ -1,5 +1,11 @@ #![warn(clippy::iter_kv_map)] -#![allow(unused_mut, clippy::redundant_clone, clippy::suspicious_map, clippy::map_identity)] +#![allow( + unused_mut, + clippy::redundant_clone, + clippy::redundant_closure, + clippy::suspicious_map, + clippy::map_identity +)] use std::collections::{BTreeMap, HashMap}; @@ -199,3 +205,33 @@ fn issue16340() { let _ = hm.iter().map(|(key, _)| vec![key]); //~^ iter_kv_map } + +fn issue16515() { + let hash_map: HashMap = HashMap::new(); + hash_map.iter().flat_map(|(k, _)| Some(*k)); + //~^ iter_kv_map + + hash_map.iter().flat_map(|(_, v)| Some(*v)); + //~^ iter_kv_map + + hash_map.iter().filter_map(|(k, _)| (k > &0).then_some(1)); + //~^ iter_kv_map + + hash_map.iter().filter_map(|(_, v)| (v > &0).then_some(1)); + //~^ iter_kv_map + + hash_map.into_iter().flat_map(|(k, _)| Some(k)); + //~^ iter_kv_map + + let hash_map: HashMap = HashMap::new(); + hash_map.into_iter().flat_map(|(_, v)| Some(v)); + //~^ iter_kv_map + + let hash_map: HashMap = HashMap::new(); + hash_map.into_iter().filter_map(|(k, _)| (k > 0).then_some(1)); + //~^ iter_kv_map + + let hash_map: HashMap = HashMap::new(); + hash_map.into_iter().filter_map(|(_, v)| (v > 0).then_some(1)); + //~^ iter_kv_map +} diff --git a/tests/ui/iter_kv_map.stderr b/tests/ui/iter_kv_map.stderr index 866e69ea1922e..cdfd05fdd09ee 100644 --- a/tests/ui/iter_kv_map.stderr +++ b/tests/ui/iter_kv_map.stderr @@ -1,5 +1,5 @@ error: iterating on a map's keys - --> tests/ui/iter_kv_map.rs:14:13 + --> tests/ui/iter_kv_map.rs:20:13 | LL | let _ = map.iter().map(|(key, _)| key).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()` @@ -8,73 +8,73 @@ LL | let _ = map.iter().map(|(key, _)| key).collect::>(); = help: to override `-D warnings` add `#[allow(clippy::iter_kv_map)]` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:16:13 + --> tests/ui/iter_kv_map.rs:22:13 | LL | let _ = map.iter().map(|(_, value)| value).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values()` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:18:13 + --> tests/ui/iter_kv_map.rs:24:13 | LL | let _ = map.iter().map(|(_, v)| v + 2).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|v| v + 2)` error: iterating on a map's keys - --> tests/ui/iter_kv_map.rs:21:13 + --> tests/ui/iter_kv_map.rs:27:13 | LL | let _ = map.clone().into_iter().map(|(key, _)| key).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys()` error: iterating on a map's keys - --> tests/ui/iter_kv_map.rs:23:13 + --> tests/ui/iter_kv_map.rs:29:13 | LL | let _ = map.clone().into_iter().map(|(key, _)| key + 2).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys().map(|key| key + 2)` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:26:13 + --> tests/ui/iter_kv_map.rs:32:13 | LL | let _ = map.clone().into_iter().map(|(_, val)| val).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values()` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:28:13 + --> tests/ui/iter_kv_map.rs:34:13 | LL | let _ = map.clone().into_iter().map(|(_, val)| val + 2).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values().map(|val| val + 2)` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:31:13 + --> tests/ui/iter_kv_map.rs:37:13 | LL | let _ = map.clone().iter().map(|(_, val)| val).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().values()` error: iterating on a map's keys - --> tests/ui/iter_kv_map.rs:33:13 + --> tests/ui/iter_kv_map.rs:39:13 | LL | let _ = map.iter().map(|(key, _)| key).filter(|x| x.is_multiple_of(2)).count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()` error: iterating on a map's keys - --> tests/ui/iter_kv_map.rs:48:13 + --> tests/ui/iter_kv_map.rs:54:13 | LL | let _ = map.iter().map(|(key, _value)| key * 9).count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys().map(|key| key * 9)` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:50:13 + --> tests/ui/iter_kv_map.rs:56:13 | LL | let _ = map.iter().map(|(_key, value)| value * 17).count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|value| value * 17)` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:54:13 + --> tests/ui/iter_kv_map.rs:60:13 | LL | let _ = map.clone().into_iter().map(|(_, ref val)| ref_acceptor(val)).count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values().map(|ref val| ref_acceptor(val))` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:58:13 + --> tests/ui/iter_kv_map.rs:64:13 | LL | let _ = map | _____________^ @@ -97,85 +97,85 @@ LL + }) | error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:69:13 + --> tests/ui/iter_kv_map.rs:75:13 | LL | let _ = map.clone().into_iter().map(|(_, mut val)| val).count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values()` error: iterating on a map's keys - --> tests/ui/iter_kv_map.rs:74:13 + --> tests/ui/iter_kv_map.rs:80:13 | LL | let _ = map.iter().map(|(key, _)| key).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:76:13 + --> tests/ui/iter_kv_map.rs:82:13 | LL | let _ = map.iter().map(|(_, value)| value).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values()` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:78:13 + --> tests/ui/iter_kv_map.rs:84:13 | LL | let _ = map.iter().map(|(_, v)| v + 2).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|v| v + 2)` error: iterating on a map's keys - --> tests/ui/iter_kv_map.rs:81:13 + --> tests/ui/iter_kv_map.rs:87:13 | LL | let _ = map.clone().into_iter().map(|(key, _)| key).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys()` error: iterating on a map's keys - --> tests/ui/iter_kv_map.rs:83:13 + --> tests/ui/iter_kv_map.rs:89:13 | LL | let _ = map.clone().into_iter().map(|(key, _)| key + 2).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys().map(|key| key + 2)` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:86:13 + --> tests/ui/iter_kv_map.rs:92:13 | LL | let _ = map.clone().into_iter().map(|(_, val)| val).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values()` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:88:13 + --> tests/ui/iter_kv_map.rs:94:13 | LL | let _ = map.clone().into_iter().map(|(_, val)| val + 2).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values().map(|val| val + 2)` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:91:13 + --> tests/ui/iter_kv_map.rs:97:13 | LL | let _ = map.clone().iter().map(|(_, val)| val).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().values()` error: iterating on a map's keys - --> tests/ui/iter_kv_map.rs:93:13 + --> tests/ui/iter_kv_map.rs:99:13 | LL | let _ = map.iter().map(|(key, _)| key).filter(|x| x.is_multiple_of(2)).count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()` error: iterating on a map's keys - --> tests/ui/iter_kv_map.rs:108:13 + --> tests/ui/iter_kv_map.rs:114:13 | LL | let _ = map.iter().map(|(key, _value)| key * 9).count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys().map(|key| key * 9)` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:110:13 + --> tests/ui/iter_kv_map.rs:116:13 | LL | let _ = map.iter().map(|(_key, value)| value * 17).count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|value| value * 17)` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:114:13 + --> tests/ui/iter_kv_map.rs:120:13 | LL | let _ = map.clone().into_iter().map(|(_, ref val)| ref_acceptor(val)).count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values().map(|ref val| ref_acceptor(val))` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:118:13 + --> tests/ui/iter_kv_map.rs:124:13 | LL | let _ = map | _____________^ @@ -198,82 +198,130 @@ LL + }) | error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:129:13 + --> tests/ui/iter_kv_map.rs:135:13 | LL | let _ = map.clone().into_iter().map(|(_, mut val)| val).count(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values()` error: iterating on a map's keys - --> tests/ui/iter_kv_map.rs:145:13 + --> tests/ui/iter_kv_map.rs:151:13 | LL | let _ = map.iter().map(|(key, _)| key).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:148:13 + --> tests/ui/iter_kv_map.rs:154:13 | LL | let _ = map.iter().map(|(_, value)| value).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values()` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:151:13 + --> tests/ui/iter_kv_map.rs:157:13 | LL | let _ = map.iter().map(|(_, v)| v + 2).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|v| v + 2)` error: iterating on a map's keys - --> tests/ui/iter_kv_map.rs:160:13 + --> tests/ui/iter_kv_map.rs:166:13 | LL | let _ = map.clone().into_iter().map(|(key, _)| key).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys()` error: iterating on a map's keys - --> tests/ui/iter_kv_map.rs:163:13 + --> tests/ui/iter_kv_map.rs:169:13 | LL | let _ = map.clone().into_iter().map(|(key, _)| key + 2).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys().map(|key| key + 2)` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:166:13 + --> tests/ui/iter_kv_map.rs:172:13 | LL | let _ = map.clone().into_iter().map(|(_, val)| val).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values()` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:169:13 + --> tests/ui/iter_kv_map.rs:175:13 | LL | let _ = map.clone().into_iter().map(|(_, val)| val + 2).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values().map(|val| val + 2)` error: iterating on a map's keys - --> tests/ui/iter_kv_map.rs:172:13 + --> tests/ui/iter_kv_map.rs:178:13 | LL | let _ = map.iter().map(|(key, _)| key).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:175:13 + --> tests/ui/iter_kv_map.rs:181:13 | LL | let _ = map.iter().map(|(_, value)| value).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values()` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:178:13 + --> tests/ui/iter_kv_map.rs:184:13 | LL | let _ = map.iter().map(|(_, v)| v + 2).collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|v| v + 2)` error: iterating on a map's values - --> tests/ui/iter_kv_map.rs:193:13 + --> tests/ui/iter_kv_map.rs:199:13 | LL | let _ = map.as_ref().iter().map(|(_, v)| v).copied().collect::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.as_ref().values()` error: iterating on a map's keys - --> tests/ui/iter_kv_map.rs:199:13 + --> tests/ui/iter_kv_map.rs:205:13 | LL | let _ = hm.iter().map(|(key, _)| vec![key]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `hm.keys().map(|key| vec![key])` -error: aborting due to 40 previous errors +error: iterating on a map's keys + --> tests/ui/iter_kv_map.rs:211:5 + | +LL | hash_map.iter().flat_map(|(k, _)| Some(*k)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `hash_map.keys().flat_map(|k| Some(*k))` + +error: iterating on a map's values + --> tests/ui/iter_kv_map.rs:214:5 + | +LL | hash_map.iter().flat_map(|(_, v)| Some(*v)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `hash_map.values().flat_map(|v| Some(*v))` + +error: iterating on a map's keys + --> tests/ui/iter_kv_map.rs:217:5 + | +LL | hash_map.iter().filter_map(|(k, _)| (k > &0).then_some(1)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `hash_map.keys().filter_map(|k| (k > &0).then_some(1))` + +error: iterating on a map's values + --> tests/ui/iter_kv_map.rs:220:5 + | +LL | hash_map.iter().filter_map(|(_, v)| (v > &0).then_some(1)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `hash_map.values().filter_map(|v| (v > &0).then_some(1))` + +error: iterating on a map's keys + --> tests/ui/iter_kv_map.rs:223:5 + | +LL | hash_map.into_iter().flat_map(|(k, _)| Some(k)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `hash_map.into_keys().flat_map(|k| Some(k))` + +error: iterating on a map's values + --> tests/ui/iter_kv_map.rs:227:5 + | +LL | hash_map.into_iter().flat_map(|(_, v)| Some(v)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `hash_map.into_values().flat_map(|v| Some(v))` + +error: iterating on a map's keys + --> tests/ui/iter_kv_map.rs:231:5 + | +LL | hash_map.into_iter().filter_map(|(k, _)| (k > 0).then_some(1)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `hash_map.into_keys().filter_map(|k| (k > 0).then_some(1))` + +error: iterating on a map's values + --> tests/ui/iter_kv_map.rs:235:5 + | +LL | hash_map.into_iter().filter_map(|(_, v)| (v > 0).then_some(1)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `hash_map.into_values().filter_map(|v| (v > 0).then_some(1))` + +error: aborting due to 48 previous errors From 0df2894f4e8c194da9a507c38e8b7739a029a34a Mon Sep 17 00:00:00 2001 From: linshuy2 Date: Thu, 5 Feb 2026 22:49:51 +0000 Subject: [PATCH 09/57] Apply `iter_kv_map` to Clippy itself --- clippy_lints/src/macro_metavars_in_unsafe.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/macro_metavars_in_unsafe.rs b/clippy_lints/src/macro_metavars_in_unsafe.rs index a323c7cf8307c..69fca3aefcaf4 100644 --- a/clippy_lints/src/macro_metavars_in_unsafe.rs +++ b/clippy_lints/src/macro_metavars_in_unsafe.rs @@ -245,8 +245,8 @@ impl<'tcx> LateLintPass<'tcx> for ExprMetavarsInUnsafe { // We want to lint unsafe blocks #0 and #1 let bad_unsafe_blocks = self .metavar_expns - .iter() - .filter_map(|(_, state)| match state { + .values() + .filter_map(|state| match state { MetavarState::ReferencedInUnsafe { unsafe_blocks } => Some(unsafe_blocks.as_slice()), MetavarState::ReferencedInSafe => None, }) From 2753bf09542d585dd947c3fe09122e6b330c6a8b Mon Sep 17 00:00:00 2001 From: linshuy2 Date: Thu, 5 Feb 2026 23:21:24 +0000 Subject: [PATCH 10/57] Extend `manual_is_variant_and` to cover `filter` chaining `is_some` --- .../src/methods/manual_is_variant_and.rs | 40 ++++++++++ clippy_lints/src/methods/mod.rs | 16 +++- tests/ui/manual_is_variant_and.fixed | 17 ++++ tests/ui/manual_is_variant_and.rs | 17 ++++ tests/ui/manual_is_variant_and.stderr | 80 +++++++++++-------- 5 files changed, 133 insertions(+), 37 deletions(-) diff --git a/clippy_lints/src/methods/manual_is_variant_and.rs b/clippy_lints/src/methods/manual_is_variant_and.rs index d877b0a622918..472d21977c7ab 100644 --- a/clippy_lints/src/methods/manual_is_variant_and.rs +++ b/clippy_lints/src/methods/manual_is_variant_and.rs @@ -290,3 +290,43 @@ pub(super) fn check_or<'tcx>( }, ); } + +pub(super) fn check_is_some_is_none<'tcx>( + cx: &LateContext<'tcx>, + call_span: Span, + recv: &'tcx Expr<'tcx>, + arg: &'tcx Expr<'tcx>, + is_some: bool, + msrv: Msrv, +) { + if cx + .typeck_results() + .expr_ty_adjusted(recv) + .peel_refs() + .is_diag_item(cx, sym::Option) + && (is_some || msrv.meets(cx, msrvs::IS_NONE_OR)) + && let Ok(map_func) = MapFunc::try_from(arg) + { + let method = if is_some { "is_some_and" } else { "is_none_or" }; + let lint_span = recv.span.to(call_span); + span_lint_and_then( + cx, + MANUAL_IS_VARIANT_AND, + lint_span, + format!("manual implementation of `Option::{method}`"), + |diag| { + let mut app = Applicability::MachineApplicable; + let (recv_snip, _) = snippet_with_context(cx, recv.span, lint_span.ctxt(), "_", &mut app); + let map_func_snip = map_func.sugg(cx, !is_some, &mut app); + + // We need to use `as_ref()` because `filter` takes a reference + diag.span_suggestion( + lint_span, + "use", + format!("{recv_snip}.as_ref().{method}({map_func_snip})"), + app, + ); + }, + ); + } +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 264405e6c3fb8..59d3eef422330 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -5343,8 +5343,8 @@ impl Methods { }, (sym::is_file, []) => filetype_is_file::check(cx, expr, recv), (sym::is_digit, [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, self.msrv), - (sym::is_none, []) => check_is_some_is_none(cx, expr, recv, call_span, false), - (sym::is_some, []) => check_is_some_is_none(cx, expr, recv, call_span, true), + (sym::is_none, []) => check_is_some_is_none(cx, expr, recv, call_span, false, self.msrv), + (sym::is_some, []) => check_is_some_is_none(cx, expr, recv, call_span, true, self.msrv), (sym::iter | sym::iter_mut | sym::into_iter, []) => { iter_on_single_or_empty_collections::check(cx, expr, name, recv); }, @@ -5768,7 +5768,14 @@ impl Methods { } } -fn check_is_some_is_none(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, call_span: Span, is_some: bool) { +fn check_is_some_is_none<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'tcx>, + recv: &'tcx Expr<'tcx>, + call_span: Span, + is_some: bool, + msrv: Msrv, +) { match method_call(recv) { Some((name @ (sym::find | sym::position | sym::rposition), f_recv, [arg], span, _)) => { search_is_some::check(cx, expr, name, is_some, f_recv, arg, recv, span); @@ -5779,6 +5786,9 @@ fn check_is_some_is_none(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, Some((sym::first, f_recv, [], _, _)) => { unnecessary_first_then_check::check(cx, call_span, recv, f_recv, is_some); }, + Some((sym::filter, f_recv, [arg], _, _)) => { + manual_is_variant_and::check_is_some_is_none(cx, call_span, f_recv, arg, is_some, msrv); + }, _ => {}, } } diff --git a/tests/ui/manual_is_variant_and.fixed b/tests/ui/manual_is_variant_and.fixed index 884bef6af5f90..96ffd9680d229 100644 --- a/tests/ui/manual_is_variant_and.fixed +++ b/tests/ui/manual_is_variant_and.fixed @@ -1,5 +1,6 @@ //@aux-build:option_helpers.rs #![warn(clippy::manual_is_variant_and)] +#![allow(clippy::redundant_closure)] #[macro_use] extern crate option_helpers; @@ -245,3 +246,19 @@ fn issue16419_msrv() { let _ = opt.is_some_and(then_fn) || opt.is_none(); } + +fn issue16518(opt: Option) { + let condition = |x: &i32| *x > 10; + + opt.as_ref().is_some_and(|x| condition(x)); + //~^ manual_is_variant_and + opt.as_ref().is_none_or(|x| !condition(x)); + //~^ manual_is_variant_and +} + +#[clippy::msrv = "1.75.0"] +fn issue16518_msrv(opt: Option) { + let condition = |x: &i32| *x > 10; + + opt.filter(|x| condition(x)).is_none(); +} diff --git a/tests/ui/manual_is_variant_and.rs b/tests/ui/manual_is_variant_and.rs index 53aca94ea3708..5f33f38fb51e6 100644 --- a/tests/ui/manual_is_variant_and.rs +++ b/tests/ui/manual_is_variant_and.rs @@ -1,5 +1,6 @@ //@aux-build:option_helpers.rs #![warn(clippy::manual_is_variant_and)] +#![allow(clippy::redundant_closure)] #[macro_use] extern crate option_helpers; @@ -254,3 +255,19 @@ fn issue16419_msrv() { let _ = opt.is_some_and(then_fn) || opt.is_none(); } + +fn issue16518(opt: Option) { + let condition = |x: &i32| *x > 10; + + opt.filter(|x| condition(x)).is_some(); + //~^ manual_is_variant_and + opt.filter(|x| condition(x)).is_none(); + //~^ manual_is_variant_and +} + +#[clippy::msrv = "1.75.0"] +fn issue16518_msrv(opt: Option) { + let condition = |x: &i32| *x > 10; + + opt.filter(|x| condition(x)).is_none(); +} diff --git a/tests/ui/manual_is_variant_and.stderr b/tests/ui/manual_is_variant_and.stderr index 56c3b80c0aa5c..cf5371378c9f6 100644 --- a/tests/ui/manual_is_variant_and.stderr +++ b/tests/ui/manual_is_variant_and.stderr @@ -1,5 +1,5 @@ error: called `map().unwrap_or_default()` on an `Option` value - --> tests/ui/manual_is_variant_and.rs:51:17 + --> tests/ui/manual_is_variant_and.rs:52:17 | LL | let _ = opt.map(|x| x > 1) | _________________^ @@ -11,7 +11,7 @@ LL | | .unwrap_or_default(); = help: to override `-D warnings` add `#[allow(clippy::manual_is_variant_and)]` error: called `map().unwrap_or_default()` on an `Option` value - --> tests/ui/manual_is_variant_and.rs:56:17 + --> tests/ui/manual_is_variant_and.rs:57:17 | LL | let _ = opt.map(|x| { | _________________^ @@ -30,13 +30,13 @@ LL ~ }); | error: called `map().unwrap_or_default()` on an `Option` value - --> tests/ui/manual_is_variant_and.rs:61:17 + --> tests/ui/manual_is_variant_and.rs:62:17 | LL | let _ = opt.map(|x| x > 1).unwrap_or_default(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `is_some_and(|x| x > 1)` error: called `map().unwrap_or_default()` on an `Option` value - --> tests/ui/manual_is_variant_and.rs:64:10 + --> tests/ui/manual_is_variant_and.rs:65:10 | LL | .map(|x| x > 1) | __________^ @@ -45,37 +45,37 @@ LL | | .unwrap_or_default(); | |____________________________^ help: use: `is_some_and(|x| x > 1)` error: called `.map() == Some()` - --> tests/ui/manual_is_variant_and.rs:68:13 + --> tests/ui/manual_is_variant_and.rs:69:13 | LL | let _ = Some(2).map(|x| x % 2 == 0) == Some(true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `Some(2).is_some_and(|x| x % 2 == 0)` error: called `.map() != Some()` - --> tests/ui/manual_is_variant_and.rs:70:13 + --> tests/ui/manual_is_variant_and.rs:71:13 | LL | let _ = Some(2).map(|x| x % 2 == 0) != Some(true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `Some(2).is_none_or(|x| x % 2 != 0)` error: called `.map() == Some()` - --> tests/ui/manual_is_variant_and.rs:72:13 + --> tests/ui/manual_is_variant_and.rs:73:13 | LL | let _ = Some(2).map(|x| x % 2 == 0) == some_true!(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `Some(2).is_some_and(|x| x % 2 == 0)` error: called `.map() != Some()` - --> tests/ui/manual_is_variant_and.rs:74:13 + --> tests/ui/manual_is_variant_and.rs:75:13 | LL | let _ = Some(2).map(|x| x % 2 == 0) != some_false!(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `Some(2).is_none_or(|x| x % 2 == 0)` error: called `map().unwrap_or_default()` on an `Option` value - --> tests/ui/manual_is_variant_and.rs:81:18 + --> tests/ui/manual_is_variant_and.rs:82:18 | LL | let _ = opt2.map(char::is_alphanumeric).unwrap_or_default(); // should lint | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `is_some_and(char::is_alphanumeric)` error: called `map().unwrap_or_default()` on a `Result` value - --> tests/ui/manual_is_variant_and.rs:99:17 + --> tests/ui/manual_is_variant_and.rs:100:17 | LL | let _ = res.map(|x| { | _________________^ @@ -94,7 +94,7 @@ LL ~ }); | error: called `map().unwrap_or_default()` on a `Result` value - --> tests/ui/manual_is_variant_and.rs:104:17 + --> tests/ui/manual_is_variant_and.rs:105:17 | LL | let _ = res.map(|x| x > 1) | _________________^ @@ -103,136 +103,148 @@ LL | | .unwrap_or_default(); | |____________________________^ help: use: `is_ok_and(|x| x > 1)` error: called `.map() == Ok()` - --> tests/ui/manual_is_variant_and.rs:108:13 + --> tests/ui/manual_is_variant_and.rs:109:13 | LL | let _ = Ok::(2).map(|x| x.is_multiple_of(2)) == Ok(true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `Ok::(2).is_ok_and(|x| x.is_multiple_of(2))` error: called `.map() != Ok()` - --> tests/ui/manual_is_variant_and.rs:110:13 + --> tests/ui/manual_is_variant_and.rs:111:13 | LL | let _ = Ok::(2).map(|x| x.is_multiple_of(2)) != Ok(true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `!Ok::(2).is_ok_and(|x| x.is_multiple_of(2))` error: called `.map() != Ok()` - --> tests/ui/manual_is_variant_and.rs:112:13 + --> tests/ui/manual_is_variant_and.rs:113:13 | LL | let _ = Ok::(2).map(|x| x.is_multiple_of(2)) != Ok(true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `!Ok::(2).is_ok_and(|x| x.is_multiple_of(2))` error: called `map().unwrap_or_default()` on a `Result` value - --> tests/ui/manual_is_variant_and.rs:119:18 + --> tests/ui/manual_is_variant_and.rs:120:18 | LL | let _ = res2.map(char::is_alphanumeric).unwrap_or_default(); // should lint | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `is_ok_and(char::is_alphanumeric)` error: called `.map() != Some()` - --> tests/ui/manual_is_variant_and.rs:132:18 + --> tests/ui/manual_is_variant_and.rs:133:18 | LL | let a1 = x.map(|b| b.is_ascii_digit()) != Some(true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.is_none_or(|b| !b.is_ascii_digit())` error: called `.map() != Some()` - --> tests/ui/manual_is_variant_and.rs:139:18 + --> tests/ui/manual_is_variant_and.rs:140:18 | LL | let a1 = x.map(|b| b.is_ascii_digit()) != Some(false); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.is_none_or(|b| b.is_ascii_digit())` error: called `.map() == Some()` - --> tests/ui/manual_is_variant_and.rs:146:18 + --> tests/ui/manual_is_variant_and.rs:147:18 | LL | let a1 = x.map(|b| b.is_ascii_digit()) == Some(true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.is_some_and(|b| b.is_ascii_digit())` error: called `.map() == Some()` - --> tests/ui/manual_is_variant_and.rs:153:18 + --> tests/ui/manual_is_variant_and.rs:154:18 | LL | let a1 = x.map(|b| b.is_ascii_digit()) == Some(false); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.is_some_and(|b| !b.is_ascii_digit())` error: called `.map() != Ok()` - --> tests/ui/manual_is_variant_and.rs:161:18 + --> tests/ui/manual_is_variant_and.rs:162:18 | LL | let a1 = x.map(|b| b.is_ascii_digit()) != Ok(true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `!x.is_ok_and(|b| b.is_ascii_digit())` error: called `.map() != Ok()` - --> tests/ui/manual_is_variant_and.rs:168:18 + --> tests/ui/manual_is_variant_and.rs:169:18 | LL | let a1 = x.map(|b| b.is_ascii_digit()) != Ok(false); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `!x.is_ok_and(|b| !b.is_ascii_digit())` error: called `.map() == Ok()` - --> tests/ui/manual_is_variant_and.rs:175:18 + --> tests/ui/manual_is_variant_and.rs:176:18 | LL | let a1 = x.map(|b| b.is_ascii_digit()) == Ok(true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.is_ok_and(|b| b.is_ascii_digit())` error: called `.map() == Ok()` - --> tests/ui/manual_is_variant_and.rs:182:18 + --> tests/ui/manual_is_variant_and.rs:183:18 | LL | let a1 = x.map(|b| b.is_ascii_digit()) == Ok(false); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.is_ok_and(|b| !b.is_ascii_digit())` error: called `.map() == Some()` - --> tests/ui/manual_is_variant_and.rs:195:18 + --> tests/ui/manual_is_variant_and.rs:196:18 | LL | let a1 = b.map(iad) == Some(true); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `b.is_some_and(iad)` error: called `.map() == Some()` - --> tests/ui/manual_is_variant_and.rs:200:18 + --> tests/ui/manual_is_variant_and.rs:201:18 | LL | let a1 = b.map(iad) == Some(false); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `b.is_some_and(|x| !iad(x))` error: called `.map() != Some()` - --> tests/ui/manual_is_variant_and.rs:205:18 + --> tests/ui/manual_is_variant_and.rs:206:18 | LL | let a1 = b.map(iad) != Some(true); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `b.is_none_or(|x| !iad(x))` error: called `.map() != Some()` - --> tests/ui/manual_is_variant_and.rs:210:18 + --> tests/ui/manual_is_variant_and.rs:211:18 | LL | let a1 = b.map(iad) != Some(false); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `b.is_none_or(iad)` error: called `.map() == Ok()` - --> tests/ui/manual_is_variant_and.rs:217:18 + --> tests/ui/manual_is_variant_and.rs:218:18 | LL | let a1 = b.map(iad) == Ok(true); | ^^^^^^^^^^^^^^^^^^^^^^ help: use: `b.is_ok_and(iad)` error: called `.map() == Ok()` - --> tests/ui/manual_is_variant_and.rs:222:18 + --> tests/ui/manual_is_variant_and.rs:223:18 | LL | let a1 = b.map(iad) == Ok(false); | ^^^^^^^^^^^^^^^^^^^^^^^ help: use: `b.is_ok_and(|x| !iad(x))` error: called `.map() != Ok()` - --> tests/ui/manual_is_variant_and.rs:227:18 + --> tests/ui/manual_is_variant_and.rs:228:18 | LL | let a1 = b.map(iad) != Ok(true); | ^^^^^^^^^^^^^^^^^^^^^^ help: use: `!b.is_ok_and(iad)` error: called `.map() != Ok()` - --> tests/ui/manual_is_variant_and.rs:232:18 + --> tests/ui/manual_is_variant_and.rs:233:18 | LL | let a1 = b.map(iad) != Ok(false); | ^^^^^^^^^^^^^^^^^^^^^^^ help: use: `!b.is_ok_and(|x| !iad(x))` error: manual implementation of `Option::is_none_or` - --> tests/ui/manual_is_variant_and.rs:242:13 + --> tests/ui/manual_is_variant_and.rs:243:13 | LL | let _ = opt.is_none() || opt.is_some_and(then_fn); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `opt.is_none_or(then_fn)` error: manual implementation of `Option::is_none_or` - --> tests/ui/manual_is_variant_and.rs:245:13 + --> tests/ui/manual_is_variant_and.rs:246:13 | LL | let _ = opt.is_some_and(then_fn) || opt.is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `opt.is_none_or(then_fn)` -error: aborting due to 33 previous errors +error: manual implementation of `Option::is_some_and` + --> tests/ui/manual_is_variant_and.rs:262:5 + | +LL | opt.filter(|x| condition(x)).is_some(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `opt.as_ref().is_some_and(|x| condition(x))` + +error: manual implementation of `Option::is_none_or` + --> tests/ui/manual_is_variant_and.rs:264:5 + | +LL | opt.filter(|x| condition(x)).is_none(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `opt.as_ref().is_none_or(|x| !condition(x))` + +error: aborting due to 35 previous errors From 8a31b2f6989d2bb390883f98d5fabf51630bf464 Mon Sep 17 00:00:00 2001 From: linshuy2 Date: Thu, 5 Feb 2026 23:28:48 +0000 Subject: [PATCH 11/57] Apply `manual_is_variant_and` to Clippy itself --- clippy_lints/src/cargo/common_metadata.rs | 2 +- clippy_lints/src/methods/unnecessary_to_owned.rs | 3 +-- clippy_lints/src/question_mark.rs | 3 +-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/cargo/common_metadata.rs b/clippy_lints/src/cargo/common_metadata.rs index 80514cb52e6e4..e92e3d115b17c 100644 --- a/clippy_lints/src/cargo/common_metadata.rs +++ b/clippy_lints/src/cargo/common_metadata.rs @@ -9,7 +9,7 @@ pub(super) fn check(cx: &LateContext<'_>, metadata: &Metadata, ignore_publish: b for package in &metadata.packages { // only run the lint if publish is `None` (`publish = true` or skipped entirely) // or if the vector isn't empty (`publish = ["something"]`) - if package.publish.as_ref().filter(|publish| publish.is_empty()).is_none() || ignore_publish { + if package.publish.as_ref().is_none_or(|publish| !publish.is_empty()) || ignore_publish { if is_empty_str(package.description.as_ref()) { missing_warning(cx, package, "package.description"); } diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index 74e8dbc15a6ce..bb9c39a47d9a0 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -702,8 +702,7 @@ fn check_if_applicable_to_argument<'tcx>(cx: &LateContext<'tcx>, arg: &Expr<'tcx sym::to_vec => cx .tcx .impl_of_assoc(method_def_id) - .filter(|&impl_did| cx.tcx.type_of(impl_did).instantiate_identity().is_slice()) - .is_some(), + .is_some_and(|impl_did| cx.tcx.type_of(impl_did).instantiate_identity().is_slice()), _ => false, } && let original_arg_ty = cx.typeck_results().node_type(caller.hir_id).peel_refs() diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index 5517b7f260eaa..719f364357d3d 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -490,8 +490,7 @@ fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: || is_early_return(sym::Result, cx, &if_block)) && if_else .map(|e| eq_expr_value(cx, let_expr, peel_blocks(e))) - .filter(|e| *e) - .is_none() + .is_none_or(|e| !e) { if !is_copy(cx, caller_ty) && let Some(hir_id) = let_expr.res_local_id() From 5db9e1c7a3904f370f5f5ca7a2852f661bb069e6 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Fri, 6 Feb 2026 15:43:09 +0100 Subject: [PATCH 12/57] Add MSRV-based tests to `duration_suboptimal_units` lint --- tests/ui/duration_suboptimal_units.fixed | 11 +++++++++++ tests/ui/duration_suboptimal_units.rs | 11 +++++++++++ tests/ui/duration_suboptimal_units.stderr | 14 +++++++++++++- 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/tests/ui/duration_suboptimal_units.fixed b/tests/ui/duration_suboptimal_units.fixed index 515ec10e572b1..a4eb981ebfa21 100644 --- a/tests/ui/duration_suboptimal_units.fixed +++ b/tests/ui/duration_suboptimal_units.fixed @@ -94,3 +94,14 @@ fn issue16457() { // Methods taking something else than `u64` are not covered _ = Duration::from_nanos_u128(1 << 90); } + +#[clippy::msrv = "1.90"] +fn insufficient_msrv() { + _ = Duration::from_secs(67_768_040_922_076_800); +} + +#[clippy::msrv = "1.91"] +fn sufficient_msrv() { + _ = Duration::from_hours(18824455811688); + //~^ duration_suboptimal_units +} diff --git a/tests/ui/duration_suboptimal_units.rs b/tests/ui/duration_suboptimal_units.rs index 357c52cffb35a..e31ca679b5a1a 100644 --- a/tests/ui/duration_suboptimal_units.rs +++ b/tests/ui/duration_suboptimal_units.rs @@ -94,3 +94,14 @@ fn issue16457() { // Methods taking something else than `u64` are not covered _ = Duration::from_nanos_u128(1 << 90); } + +#[clippy::msrv = "1.90"] +fn insufficient_msrv() { + _ = Duration::from_secs(67_768_040_922_076_800); +} + +#[clippy::msrv = "1.91"] +fn sufficient_msrv() { + _ = Duration::from_secs(67_768_040_922_076_800); + //~^ duration_suboptimal_units +} diff --git a/tests/ui/duration_suboptimal_units.stderr b/tests/ui/duration_suboptimal_units.stderr index f129dbade8dc1..97b26f6667eb8 100644 --- a/tests/ui/duration_suboptimal_units.stderr +++ b/tests/ui/duration_suboptimal_units.stderr @@ -148,5 +148,17 @@ LL - Duration::from_secs(300) LL + Duration::from_mins(5) | -error: aborting due to 12 previous errors +error: constructing a `Duration` using a smaller unit when a larger unit would be more readable + --> tests/ui/duration_suboptimal_units.rs:105:9 + | +LL | _ = Duration::from_secs(67_768_040_922_076_800); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try using from_hours + | +LL - _ = Duration::from_secs(67_768_040_922_076_800); +LL + _ = Duration::from_hours(18824455811688); + | + +error: aborting due to 13 previous errors From 559f1c3a6dbe0a5f20d091ab2c0b0c8ccf6c2875 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Fri, 6 Feb 2026 09:03:59 -0800 Subject: [PATCH 13/57] Replace a stale clippy `CURRENT_RUSTC_VERSION` --- clippy_lints/src/deprecated_lints.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index 6e62e983d2f31..ff92cd0598393 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -123,7 +123,7 @@ declare_with_version! { RENAMED(RENAMED_VERSION) = [ ("clippy::into_iter_on_array", "array_into_iter"), #[clippy::version = ""] ("clippy::invalid_atomic_ordering", "invalid_atomic_ordering"), - #[clippy::version = "CURRENT_RUSTC_VERSION"] + #[clippy::version = "1.88.0"] ("clippy::invalid_null_ptr_usage", "invalid_null_arguments"), #[clippy::version = ""] ("clippy::invalid_ref", "invalid_value"), From 12109e4b2c135936f774fda7e3fcdfc7062c1310 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 7 Jan 2026 14:52:26 +0100 Subject: [PATCH 14/57] Create new `clippy_utils::ty::get_field_def_id_by_name` function --- clippy_lints/src/disallowed_fields.rs | 12 +++--------- clippy_utils/src/paths.rs | 3 ++- clippy_utils/src/ty/mod.rs | 7 +++++++ 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/disallowed_fields.rs b/clippy_lints/src/disallowed_fields.rs index c3b6d97f195f0..24bea4cd2729f 100644 --- a/clippy_lints/src/disallowed_fields.rs +++ b/clippy_lints/src/disallowed_fields.rs @@ -2,11 +2,12 @@ use clippy_config::Conf; use clippy_config::types::{DisallowedPath, create_disallowed_map}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::paths::PathNS; +use clippy_utils::ty::get_field_def_id_by_name; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefIdMap; use rustc_hir::{Expr, ExprKind, Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{self, TyCtxt}; +use rustc_middle::ty::TyCtxt; use rustc_session::impl_lint_pass; declare_clippy_lint! { @@ -84,14 +85,7 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedFields { // parent `Ty`. Then we go through all its fields to find the one with the expected // name and get the `DefId` from it. if let Some(parent_ty) = cx.typeck_results().expr_ty_adjusted_opt(e) - && let ty::Adt(adt_def, ..) = parent_ty.kind() - && let Some(field_def_id) = adt_def.all_fields().find_map(|field| { - if field.name == ident.name { - Some(field.did) - } else { - None - } - }) + && let Some(field_def_id) = get_field_def_id_by_name(parent_ty, ident.name) { (field_def_id, ident.span) } else { diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 1b95b6799c7ba..1255b05b8e1e5 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -301,7 +301,8 @@ fn local_item_child_by_name(tcx: TyCtxt<'_>, local_id: LocalDefId, ns: PathNS, n PathNS::Type => opt_def_id(path.res.type_ns), PathNS::Value => opt_def_id(path.res.value_ns), PathNS::Macro => opt_def_id(path.res.macro_ns), - PathNS::Arbitrary | PathNS::Field => unreachable!(), + PathNS::Field => None, + PathNS::Arbitrary => unreachable!(), } } else { None diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index a90d64e972c1f..ebb8706dad433 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -1247,6 +1247,13 @@ pub fn get_field_by_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, name: Symbol) -> } } +pub fn get_field_def_id_by_name(ty: Ty<'_>, name: Symbol) -> Option { + let ty::Adt(adt_def, ..) = ty.kind() else { return None }; + adt_def + .all_fields() + .find_map(|field| if field.name == name { Some(field.did) } else { None }) +} + /// Check if `ty` is an `Option` and return its argument type if it is. pub fn option_arg_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { match *ty.kind() { From cb68b21cb3ecae3b5158bcca2caec96667f2b08c Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 9 Feb 2026 18:51:44 +0100 Subject: [PATCH 15/57] Extend `disallowed_fields` lint on variant fields --- clippy_lints/src/disallowed_fields.rs | 67 +++++++++++++------ clippy_utils/src/paths.rs | 23 +++++++ .../toml_disallowed_fields/clippy.toml | 2 + .../conf_disallowed_fields.rs | 11 +++ .../conf_disallowed_fields.stderr | 24 ++++--- 5 files changed, 98 insertions(+), 29 deletions(-) diff --git a/clippy_lints/src/disallowed_fields.rs b/clippy_lints/src/disallowed_fields.rs index 24bea4cd2729f..f1136556e2ed8 100644 --- a/clippy_lints/src/disallowed_fields.rs +++ b/clippy_lints/src/disallowed_fields.rs @@ -106,28 +106,55 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedFields { } fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) { - if let PatKind::Struct(struct_path, pat_fields, _) = pat.kind - && let Res::Def(DefKind::Struct, struct_def_id) = cx.typeck_results().qpath_res(&struct_path, pat.hir_id) - { - let adt_def = cx.tcx.adt_def(struct_def_id); - for field in pat_fields { - if let Some(def_id) = adt_def.all_fields().find_map(|adt_field| { - if field.ident.name == adt_field.name { - Some(adt_field.did) - } else { - None + let PatKind::Struct(struct_path, pat_fields, _) = pat.kind else { + return; + }; + match cx.typeck_results().qpath_res(&struct_path, pat.hir_id) { + Res::Def(DefKind::Struct, struct_def_id) => { + let adt_def = cx.tcx.adt_def(struct_def_id); + for field in pat_fields { + if let Some(def_id) = adt_def.all_fields().find_map(|adt_field| { + if field.ident.name == adt_field.name { + Some(adt_field.did) + } else { + None + } + }) && let Some(&(path, disallowed_path)) = self.disallowed.get(&def_id) + { + span_lint_and_then( + cx, + DISALLOWED_FIELDS, + field.span, + format!("use of a disallowed field `{path}`"), + disallowed_path.diag_amendment(field.span), + ); } - }) && let Some(&(path, disallowed_path)) = self.disallowed.get(&def_id) - { - span_lint_and_then( - cx, - DISALLOWED_FIELDS, - field.span, - format!("use of a disallowed field `{path}`"), - disallowed_path.diag_amendment(field.span), - ); } - } + }, + Res::Def(DefKind::Variant, variant_def_id) => { + let enum_def_id = cx.tcx.parent(variant_def_id); + let variant = cx.tcx.adt_def(enum_def_id).variant_with_id(variant_def_id); + + for field in pat_fields { + if let Some(def_id) = variant.fields.iter().find_map(|adt_field| { + if field.ident.name == adt_field.name { + Some(adt_field.did) + } else { + None + } + }) && let Some(&(path, disallowed_path)) = self.disallowed.get(&def_id) + { + span_lint_and_then( + cx, + DISALLOWED_FIELDS, + field.span, + format!("use of a disallowed field `{path}`"), + disallowed_path.diag_amendment(field.span), + ); + } + } + }, + _ => {}, } } } diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 1255b05b8e1e5..b9173b14a6cbf 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -288,6 +288,20 @@ fn local_item_child_by_name(tcx: TyCtxt<'_>, local_id: LocalDefId, ns: PathNS, n &root_mod }, Node::Item(item) => &item.kind, + Node::Variant(variant) if ns == PathNS::Field => { + return if let rustc_hir::VariantData::Struct { fields, .. } = variant.data + && let Some(field_def_id) = fields.iter().find_map(|field| { + if field.ident.name == name { + Some(field.def_id.to_def_id()) + } else { + None + } + }) { + Some(field_def_id) + } else { + None + }; + }, _ => return None, }; @@ -330,6 +344,15 @@ fn local_item_child_by_name(tcx: TyCtxt<'_>, local_id: LocalDefId, ns: PathNS, n } }) }, + ItemKind::Enum(_, _, rustc_hir::EnumDef { variants }) if ns == PathNS::Type => { + variants.iter().find_map(|variant| { + if variant.ident.name == name { + Some(variant.def_id.to_def_id()) + } else { + None + } + }) + }, _ => None, } } diff --git a/tests/ui-toml/toml_disallowed_fields/clippy.toml b/tests/ui-toml/toml_disallowed_fields/clippy.toml index 0dc494815a348..615d405a87a49 100644 --- a/tests/ui-toml/toml_disallowed_fields/clippy.toml +++ b/tests/ui-toml/toml_disallowed_fields/clippy.toml @@ -9,4 +9,6 @@ disallowed-fields = [ "conf_disallowed_fields::X::y", # re-exports "conf_disallowed_fields::Y::y", + # field of a variant + "conf_disallowed_fields::Z::B::x", ] diff --git a/tests/ui-toml/toml_disallowed_fields/conf_disallowed_fields.rs b/tests/ui-toml/toml_disallowed_fields/conf_disallowed_fields.rs index 359c861baf66c..7804b605a67a6 100644 --- a/tests/ui-toml/toml_disallowed_fields/conf_disallowed_fields.rs +++ b/tests/ui-toml/toml_disallowed_fields/conf_disallowed_fields.rs @@ -7,6 +7,11 @@ struct X { y: u32, } +enum Z { + A { x: u32 }, + B { x: u32 }, +} + use crate::X as Y; fn b(X { y }: X) {} @@ -36,4 +41,10 @@ fn main() { match x { RangeTo { end } => {}, //~ disallowed_fields } + + let x = Z::B { x: 0 }; + match x { + Z::A { x } => {}, + Z::B { x } => {}, //~ disallowed_fields + } } diff --git a/tests/ui-toml/toml_disallowed_fields/conf_disallowed_fields.stderr b/tests/ui-toml/toml_disallowed_fields/conf_disallowed_fields.stderr index 3cc22a55ab9cc..ac43c702bbdc1 100644 --- a/tests/ui-toml/toml_disallowed_fields/conf_disallowed_fields.stderr +++ b/tests/ui-toml/toml_disallowed_fields/conf_disallowed_fields.stderr @@ -1,5 +1,5 @@ error: use of a disallowed field `conf_disallowed_fields::Y::y` - --> tests/ui-toml/toml_disallowed_fields/conf_disallowed_fields.rs:12:10 + --> tests/ui-toml/toml_disallowed_fields/conf_disallowed_fields.rs:17:10 | LL | fn b(X { y }: X) {} | ^ @@ -8,25 +8,25 @@ LL | fn b(X { y }: X) {} = help: to override `-D warnings` add `#[allow(clippy::disallowed_fields)]` error: use of a disallowed field `conf_disallowed_fields::Y::y` - --> tests/ui-toml/toml_disallowed_fields/conf_disallowed_fields.rs:17:15 + --> tests/ui-toml/toml_disallowed_fields/conf_disallowed_fields.rs:22:15 | LL | let _ = x.y; | ^ error: use of a disallowed field `conf_disallowed_fields::Y::y` - --> tests/ui-toml/toml_disallowed_fields/conf_disallowed_fields.rs:21:15 + --> tests/ui-toml/toml_disallowed_fields/conf_disallowed_fields.rs:26:15 | LL | let _ = x.y; | ^ error: use of a disallowed field `std::ops::Range::start` - --> tests/ui-toml/toml_disallowed_fields/conf_disallowed_fields.rs:25:15 + --> tests/ui-toml/toml_disallowed_fields/conf_disallowed_fields.rs:30:15 | LL | let _ = x.start; | ^^^^^ error: use of a disallowed field `std::ops::Range::end` - --> tests/ui-toml/toml_disallowed_fields/conf_disallowed_fields.rs:27:15 + --> tests/ui-toml/toml_disallowed_fields/conf_disallowed_fields.rs:32:15 | LL | let _ = x.end; | ^^^ @@ -34,22 +34,28 @@ LL | let _ = x.end; = note: no end allowed error: use of a disallowed field `std::ops::Range::start` - --> tests/ui-toml/toml_disallowed_fields/conf_disallowed_fields.rs:29:17 + --> tests/ui-toml/toml_disallowed_fields/conf_disallowed_fields.rs:34:17 | LL | let Range { start, .. } = x; | ^^^^^ error: use of a disallowed field `std::ops::RangeTo::end` - --> tests/ui-toml/toml_disallowed_fields/conf_disallowed_fields.rs:33:15 + --> tests/ui-toml/toml_disallowed_fields/conf_disallowed_fields.rs:38:15 | LL | let _ = x.end; | ^^^ error: use of a disallowed field `std::ops::RangeTo::end` - --> tests/ui-toml/toml_disallowed_fields/conf_disallowed_fields.rs:37:19 + --> tests/ui-toml/toml_disallowed_fields/conf_disallowed_fields.rs:42:19 | LL | RangeTo { end } => {}, | ^^^ -error: aborting due to 8 previous errors +error: use of a disallowed field `conf_disallowed_fields::Z::B::x` + --> tests/ui-toml/toml_disallowed_fields/conf_disallowed_fields.rs:48:16 + | +LL | Z::B { x } => {}, + | ^ + +error: aborting due to 9 previous errors From 99e47220fcf7135b052f4ceaf36b217030074127 Mon Sep 17 00:00:00 2001 From: Lukas Bergdoll Date: Fri, 23 Jan 2026 15:48:09 +0100 Subject: [PATCH 16/57] Stabilize assert_matches --- clippy_utils/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 16bedf199e208..269a7f333dc58 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -3,7 +3,7 @@ #![feature(macro_metavar_expr)] #![feature(never_type)] #![feature(rustc_private)] -#![feature(assert_matches)] +#![cfg_attr(bootstrap, feature(assert_matches))] #![feature(unwrap_infallible)] #![recursion_limit = "512"] #![allow(clippy::missing_errors_doc, clippy::missing_panics_doc, clippy::must_use_candidate)] From ae2d679bdd62227f6eaad47c37022221226e70ad Mon Sep 17 00:00:00 2001 From: mu001999 Date: Wed, 28 Jan 2026 22:45:25 +0800 Subject: [PATCH 17/57] Fix clippy ast utils Co-authored-by: Michael Goulet --- clippy_utils/src/ast_utils/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/clippy_utils/src/ast_utils/mod.rs b/clippy_utils/src/ast_utils/mod.rs index 3b043f7565ef9..15f877632ef11 100644 --- a/clippy_utils/src/ast_utils/mod.rs +++ b/clippy_utils/src/ast_utils/mod.rs @@ -819,7 +819,9 @@ pub fn eq_use_tree_kind(l: &UseTreeKind, r: &UseTreeKind) -> bool { pub fn eq_defaultness(l: Defaultness, r: Defaultness) -> bool { matches!( (l, r), - (Defaultness::Final, Defaultness::Final) | (Defaultness::Default(_), Defaultness::Default(_)) + (Defaultness::Implicit, Defaultness::Implicit) + | (Defaultness::Default(_), Defaultness::Default(_)) + | (Defaultness::Final(_), Defaultness::Final(_)) ) } From f9b1c9199ec830c966d5cb6e15da79089a7e513e Mon Sep 17 00:00:00 2001 From: cyrgani Date: Thu, 12 Feb 2026 10:24:05 +0000 Subject: [PATCH 18/57] allow `deprecated(since = "CURRENT_RUSTC_VERSION")` --- clippy_lints/src/attrs/deprecated_semver.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/attrs/deprecated_semver.rs b/clippy_lints/src/attrs/deprecated_semver.rs index bd6459d6f9dbc..e3b1a05bda7dd 100644 --- a/clippy_lints/src/attrs/deprecated_semver.rs +++ b/clippy_lints/src/attrs/deprecated_semver.rs @@ -2,13 +2,14 @@ use super::DEPRECATED_SEMVER; use clippy_utils::diagnostics::span_lint; use clippy_utils::sym; use rustc_ast::{LitKind, MetaItemLit}; +use rustc_hir::VERSION_PLACEHOLDER; use rustc_lint::EarlyContext; use rustc_span::Span; use semver::Version; pub(super) fn check(cx: &EarlyContext<'_>, span: Span, lit: &MetaItemLit) { if let LitKind::Str(is, _) = lit.kind - && (is == sym::TBD || Version::parse(is.as_str()).is_ok()) + && (is == sym::TBD || is.as_str() == VERSION_PLACEHOLDER || Version::parse(is.as_str()).is_ok()) { return; } From c8fd55b457f1216bc82444522c55d63596a6c1a8 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 12 Feb 2026 13:43:03 +0100 Subject: [PATCH 19/57] Merge commit 'a62c6af53676bb15a40488ce2d632de558f001de' into clippy-subtree-update --- Cargo.toml | 2 +- book/src/development/infrastructure/sync.md | 4 +- book/src/development/type_checking.md | 6 +- book/src/lint_configuration.md | 6 +- clippy_config/src/conf.rs | 6 +- clippy_dev/src/new_lint.rs | 4 +- clippy_lints/src/attrs/useless_attribute.rs | 1 + .../src/cargo/lint_groups_priority.rs | 2 +- .../src/casts/cast_possible_truncation.rs | 2 +- clippy_lints/src/casts/manual_dangling_ptr.rs | 2 + clippy_lints/src/dbg_macro.rs | 94 ++++++++++-------- .../doc/doc_paragraphs_missing_punctuation.rs | 22 +++-- .../src/doc/doc_suspicious_footnotes.rs | 2 +- clippy_lints/src/doc/mod.rs | 10 +- clippy_lints/src/duration_suboptimal_units.rs | 5 +- clippy_lints/src/formatting.rs | 2 +- clippy_lints/src/functions/must_use.rs | 3 +- clippy_lints/src/implicit_hasher.rs | 4 +- clippy_lints/src/indexing_slicing.rs | 2 +- clippy_lints/src/loops/never_loop.rs | 4 + clippy_lints/src/manual_let_else.rs | 2 +- clippy_lints/src/matches/manual_unwrap_or.rs | 2 +- .../src/matches/match_like_matches.rs | 2 +- clippy_lints/src/methods/bytes_nth.rs | 2 +- clippy_lints/src/methods/into_iter_on_ref.rs | 2 +- .../src/methods/manual_is_variant_and.rs | 66 ++++++++++++- clippy_lints/src/methods/manual_try_fold.rs | 2 +- clippy_lints/src/methods/mod.rs | 15 ++- .../src/methods/option_as_ref_deref.rs | 5 +- clippy_lints/src/methods/str_split.rs | 37 ++++--- clippy_lints/src/methods/unit_hash.rs | 2 +- .../src/methods/unwrap_expect_used.rs | 68 +++++++++++++ .../src/missing_asserts_for_indexing.rs | 6 +- .../src/missing_enforced_import_rename.rs | 2 +- clippy_lints/src/operators/cmp_owned.rs | 12 ++- clippy_lints/src/operators/mod.rs | 2 +- clippy_lints/src/ptr/cmp_null.rs | 2 +- clippy_lints/src/question_mark.rs | 8 +- clippy_lints/src/returns/let_and_return.rs | 2 +- clippy_lints/src/strings.rs | 18 ++++ clippy_lints/src/time_subtraction.rs | 4 +- clippy_lints/src/toplevel_ref_arg.rs | 2 +- clippy_lints/src/zero_div_zero.rs | 2 +- clippy_lints/src/zero_repeat_side_effects.rs | 2 +- clippy_utils/README.md | 2 +- clippy_utils/src/ast_utils/mod.rs | 14 +-- clippy_utils/src/check_proc_macro.rs | 23 +++-- clippy_utils/src/sym.rs | 1 + clippy_utils/src/ty/mod.rs | 2 +- rust-toolchain.toml | 2 +- rustc_tools_util/src/lib.rs | 2 +- src/driver.rs | 4 +- tests/ui/allow_attributes.fixed | 5 + tests/ui/allow_attributes.rs | 5 + tests/ui/allow_attributes.stderr | 8 +- tests/ui/cmp_owned/with_suggestion.fixed | 32 ++++++ tests/ui/cmp_owned/with_suggestion.rs | 32 ++++++ tests/ui/cmp_owned/with_suggestion.stderr | 15 ++- tests/ui/doc/doc-fixable.fixed | 2 +- tests/ui/doc/doc-fixable.rs | 2 +- .../doc_paragraphs_missing_punctuation.fixed | 25 +++-- .../doc/doc_paragraphs_missing_punctuation.rs | 25 +++-- .../doc_paragraphs_missing_punctuation.stderr | 32 +++--- tests/ui/duration_suboptimal_units.fixed | 5 + tests/ui/duration_suboptimal_units.rs | 5 + tests/ui/let_and_return.edition2021.fixed | 8 ++ tests/ui/let_and_return.edition2024.fixed | 8 ++ tests/ui/let_and_return.rs | 8 ++ tests/ui/manual_dangling_ptr.fixed | 12 +++ tests/ui/manual_dangling_ptr.rs | 12 +++ tests/ui/manual_dangling_ptr.stderr | 28 +++--- tests/ui/manual_is_variant_and.fixed | 19 ++++ tests/ui/manual_is_variant_and.rs | 19 ++++ tests/ui/manual_is_variant_and.stderr | 14 ++- tests/ui/manual_let_else_match.fixed | 15 ++- tests/ui/manual_let_else_match.rs | 19 +++- tests/ui/manual_let_else_match.stderr | 12 ++- tests/ui/must_use_candidates.fixed | 3 +- tests/ui/must_use_candidates.rs | 3 +- tests/ui/never_loop.rs | 10 ++ tests/ui/question_mark.fixed | 9 ++ tests/ui/question_mark.rs | 14 +++ tests/ui/question_mark.stderr | 14 ++- tests/ui/str_split.fixed | 2 - tests/ui/str_split.rs | 2 - tests/ui/str_split.stderr | 99 +++++++++++++++---- tests/ui/str_to_string.fixed | 29 ++++++ tests/ui/str_to_string.rs | 29 ++++++ tests/ui/str_to_string.stderr | 32 +++++- tests/ui/test_attr_in_doctest.rs | 8 ++ ...duplication_in_bounds_assoc_const_eq.fixed | 1 - ...it_duplication_in_bounds_assoc_const_eq.rs | 1 - ...uplication_in_bounds_assoc_const_eq.stderr | 2 +- tests/ui/unconditional_recursion.rs | 2 +- tests/ui/unwrap.rs | 22 ----- tests/ui/unwrap.stderr | 31 ------ tests/ui/unwrap_expect_used.rs | 12 +++ tests/ui/unwrap_expect_used.stderr | 50 +++++++++- tests/ui/useless_attribute.fixed | 4 + tests/ui/useless_attribute.rs | 4 + triagebot.toml | 5 + 101 files changed, 971 insertions(+), 284 deletions(-) delete mode 100644 tests/ui/unwrap.rs delete mode 100644 tests/ui/unwrap.stderr diff --git a/Cargo.toml b/Cargo.toml index 560f1e8d7fbe5..8f5ef9ca2f8f6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,7 +42,7 @@ walkdir = "2.3" filetime = "0.2.9" itertools = "0.12" pulldown-cmark = { version = "0.11", default-features = false, features = ["html"] } -askama = { version = "0.15", default-features = false, features = ["alloc", "config", "derive"] } +askama = { version = "0.15.4", default-features = false, features = ["alloc", "config", "derive"] } [dev-dependencies.toml] version = "0.9.7" diff --git a/book/src/development/infrastructure/sync.md b/book/src/development/infrastructure/sync.md index 2bbdf47a83581..4506ff15d8ff9 100644 --- a/book/src/development/infrastructure/sync.md +++ b/book/src/development/infrastructure/sync.md @@ -79,7 +79,7 @@ to be run inside the `rust` directory): ```bash git fetch upstream # assuming upstream is the rust-lang/rust remote git switch rustup - git merge upstream/master --no-ff + git merge upstream/main --no-ff ``` > Note: This is one of the few instances where a merge commit is allowed in > a PR. @@ -99,7 +99,7 @@ to be run inside the `rust` directory): All the following commands have to be run inside the `rust` directory. -1. Make sure you have checked out the latest `master` of `rust-lang/rust`. +1. Make sure you have checked out the latest `main` of `rust-lang/rust`. 2. Sync the `rust-lang/rust-clippy` master to the rust-copy of Clippy: ```bash git switch -c clippy-subtree-update diff --git a/book/src/development/type_checking.md b/book/src/development/type_checking.md index 578836ecc5686..e92e766988858 100644 --- a/book/src/development/type_checking.md +++ b/book/src/development/type_checking.md @@ -146,7 +146,7 @@ in this chapter: - [Stages of compilation](https://rustc-dev-guide.rust-lang.org/compiler-src.html#the-main-stages-of-compilation) - [Diagnostic items](https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html) -- [Type checking](https://rustc-dev-guide.rust-lang.org/type-checking.html) +- [Type checking](https://rustc-dev-guide.rust-lang.org/hir-typeck/summary.html) - [Ty module](https://rustc-dev-guide.rust-lang.org/ty.html) [Adt]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/ty_kind/enum.TyKind.html#variant.Adt @@ -154,7 +154,7 @@ in this chapter: [expr_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html#method.expr_ty [node_type]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html#method.node_type [is_char]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html#method.is_char -[is_char_source]: https://doc.rust-lang.org/nightly/nightly-rustc/src/rustc_middle/ty/sty.rs.html#1831-1834 +[is_char_source]: https://github.com/rust-lang/rust/blob/d34f1f931489618efffc4007e6b6bdb9e10f6467/compiler/rustc_middle/src/ty/sty.rs#L1429-L1432 [kind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html#method.kind [LateContext]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LateContext.html [LateLintPass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html @@ -163,5 +163,5 @@ in this chapter: [TyKind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/ty_kind/enum.TyKind.html [TypeckResults]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html [middle_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html -[hir_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/struct.Ty.html +[hir_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/struct.Ty.html [lower_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_analysis/fn.lower_ty.html diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index f81dd421f59b9..57ac01828e597 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -246,7 +246,7 @@ A list of crate names to allow duplicates of ## `allowed-idents-below-min-chars` Allowed names below the minimum allowed characters. The value `".."` can be used as part of -the list to indicate, that the configured values should be appended to the default +the list to indicate that the configured values should be appended to the default configuration of Clippy. By default, any configuration will replace the default value. **Default Value:** `["i", "j", "x", "y", "z", "w", "n"]` @@ -570,12 +570,12 @@ The list of disallowed types, written as fully qualified paths. ## `doc-valid-idents` The list of words this lint should not consider as identifiers needing ticks. The value -`".."` can be used as part of the list to indicate, that the configured values should be appended to the +`".."` can be used as part of the list to indicate that the configured values should be appended to the default configuration of Clippy. By default, any configuration will replace the default value. For example: * `doc-valid-idents = ["ClipPy"]` would replace the default list with `["ClipPy"]`. * `doc-valid-idents = ["ClipPy", ".."]` would append `ClipPy` to the default list. -**Default Value:** `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "MHz", "GHz", "THz", "AccessKit", "CoAP", "CoreFoundation", "CoreGraphics", "CoreText", "DevOps", "Direct2D", "Direct3D", "DirectWrite", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "InfiniBand", "RoCE", "ClojureScript", "CoffeeScript", "JavaScript", "PostScript", "PureScript", "TypeScript", "PowerPC", "WebAssembly", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenAL", "OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry", "OpenType", "WebGL", "WebGL2", "WebGPU", "WebRTC", "WebSocket", "WebTransport", "WebP", "OpenExr", "YCbCr", "sRGB", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "NetBSD", "OpenBSD", "NixOS", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]` +**Default Value:** `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "MHz", "GHz", "THz", "AccessKit", "CoAP", "CoreFoundation", "CoreGraphics", "CoreText", "DevOps", "Direct2D", "Direct3D", "DirectWrite", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "InfiniBand", "RoCE", "ClojureScript", "CoffeeScript", "JavaScript", "PostScript", "PureScript", "TypeScript", "PowerPC", "PowerShell", "WebAssembly", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenAL", "OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry", "OpenType", "WebGL", "WebGL2", "WebGPU", "WebRTC", "WebSocket", "WebTransport", "WebP", "OpenExr", "YCbCr", "sRGB", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "NetBSD", "OpenBSD", "NixOS", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]` --- **Affected lints:** diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 849d9a613d806..3f4997a395a8e 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -35,7 +35,7 @@ const DEFAULT_DOC_VALID_IDENTS: &[&str] = &[ "IPv4", "IPv6", "InfiniBand", "RoCE", "ClojureScript", "CoffeeScript", "JavaScript", "PostScript", "PureScript", "TypeScript", - "PowerPC", "WebAssembly", + "PowerPC", "PowerShell", "WebAssembly", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", @@ -423,7 +423,7 @@ define_Conf! { #[lints(multiple_crate_versions)] allowed_duplicate_crates: Vec = Vec::new(), /// Allowed names below the minimum allowed characters. The value `".."` can be used as part of - /// the list to indicate, that the configured values should be appended to the default + /// the list to indicate that the configured values should be appended to the default /// configuration of Clippy. By default, any configuration will replace the default value. #[lints(min_ident_chars)] allowed_idents_below_min_chars: Vec = @@ -620,7 +620,7 @@ define_Conf! { #[lints(disallowed_types)] disallowed_types: Vec = Vec::new(), /// The list of words this lint should not consider as identifiers needing ticks. The value - /// `".."` can be used as part of the list to indicate, that the configured values should be appended to the + /// `".."` can be used as part of the list to indicate that the configured values should be appended to the /// default configuration of Clippy. By default, any configuration will replace the default value. For example: /// * `doc-valid-idents = ["ClipPy"]` would replace the default list with `["ClipPy"]`. /// * `doc-valid-idents = ["ClipPy", ".."]` would append `ClipPy` to the default list. diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index 0b6d702d77218..72f281ca4d9d7 100644 --- a/clippy_dev/src/new_lint.rs +++ b/clippy_dev/src/new_lint.rs @@ -167,9 +167,9 @@ fn add_lint(lint: &LintData<'_>, enable_msrv: bool) -> io::Result<()> { let camel_name = to_camel_case(lint.name); let new_lint = if enable_msrv { - format!("Box::new(move |{ctor_arg}| Box::new({module_name}::{camel_name}::new(conf))),\n ",) + format!("Box::new(move |{ctor_arg}| Box::new({module_name}::{camel_name}::new(conf))),\n ") } else { - format!("Box::new(|{ctor_arg}| Box::new({module_name}::{camel_name})),\n ",) + format!("Box::new(|{ctor_arg}| Box::new({module_name}::{camel_name})),\n ") }; lib_rs.insert_str(comment_start, &new_lint); diff --git a/clippy_lints/src/attrs/useless_attribute.rs b/clippy_lints/src/attrs/useless_attribute.rs index aa9a6654bee32..9a1e315ae5306 100644 --- a/clippy_lints/src/attrs/useless_attribute.rs +++ b/clippy_lints/src/attrs/useless_attribute.rs @@ -31,6 +31,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, item: &Item, attrs: &[Attribute]) { | sym::dead_code | sym::deprecated | sym::deprecated_in_future + | sym::exported_private_dependencies | sym::hidden_glob_reexports | sym::unreachable_pub | sym::unused diff --git a/clippy_lints/src/cargo/lint_groups_priority.rs b/clippy_lints/src/cargo/lint_groups_priority.rs index 14c5e22fb9cd5..f937f065d6e0d 100644 --- a/clippy_lints/src/cargo/lint_groups_priority.rs +++ b/clippy_lints/src/cargo/lint_groups_priority.rs @@ -100,7 +100,7 @@ fn check_table(cx: &LateContext<'_>, table: &DeTable<'_>, known_groups: &FxHashS "to have lints override the group set `{}` to a lower priority", group.as_ref() ), - format!("{{ level = {:?}, priority = {low_priority} }}", group_config.level,), + format!("{{ level = {:?}, priority = {low_priority} }}", group_config.level), Applicability::MaybeIncorrect, ); }, diff --git a/clippy_lints/src/casts/cast_possible_truncation.rs b/clippy_lints/src/casts/cast_possible_truncation.rs index 2eebe84923274..0591afaa6c585 100644 --- a/clippy_lints/src/casts/cast_possible_truncation.rs +++ b/clippy_lints/src/casts/cast_possible_truncation.rs @@ -117,7 +117,7 @@ pub(super) fn check( return; } - format!("casting `{cast_from}` to `{cast_to}` may truncate the value{suffix}",) + format!("casting `{cast_from}` to `{cast_to}` may truncate the value{suffix}") }, (ty::Adt(def, _), Some(to_nbits)) if def.is_enum() => { diff --git a/clippy_lints/src/casts/manual_dangling_ptr.rs b/clippy_lints/src/casts/manual_dangling_ptr.rs index be1f406770ce6..56779e8ce3d95 100644 --- a/clippy_lints/src/casts/manual_dangling_ptr.rs +++ b/clippy_lints/src/casts/manual_dangling_ptr.rs @@ -15,6 +15,8 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, from: &Expr<'_>, to: let init_expr = expr_or_init(cx, from); if is_expr_const_aligned(cx, init_expr, ptr_ty.ty) && let Some(std_or_core) = std_or_core(cx) + && let pointee_ty = cx.typeck_results().node_type(ptr_ty.ty.hir_id) + && pointee_ty.is_sized(cx.tcx, cx.typing_env()) { let sugg_fn = match ptr_ty.mutbl { Mutability::Not => "ptr::dangling", diff --git a/clippy_lints/src/dbg_macro.rs b/clippy_lints/src/dbg_macro.rs index 9197870cb6952..45de4035d992e 100644 --- a/clippy_lints/src/dbg_macro.rs +++ b/clippy_lints/src/dbg_macro.rs @@ -75,45 +75,47 @@ impl LateLintPass<'_> for DbgMacro { "the `dbg!` macro is intended as a debugging tool", |diag| { let mut applicability = Applicability::MachineApplicable; - let (sugg_span, suggestion) = - match is_async_move_desugar(expr).unwrap_or(expr).peel_drop_temps().kind { - // dbg!() - ExprKind::Block(..) => { - // If the `dbg!` macro is a "free" statement and not contained within other expressions, - // remove the whole statement. - if let Node::Stmt(_) = cx.tcx.parent_hir_node(expr.hir_id) - && let Some(semi_span) = - cx.sess().source_map().mac_call_stmt_semi_span(macro_call.span) - { - (macro_call.span.to(semi_span), String::new()) - } else { - (macro_call.span, String::from("()")) - } - }, - ExprKind::Match(first, arms, _) => { - let vals = collect_vals(first, arms); - let suggestion = match vals.as_slice() { - // dbg!(1) => 1 - &[val] => { - snippet_with_applicability(cx, val.span.source_callsite(), "..", &mut applicability) - .to_string() - } - // dbg!(2, 3) => (2, 3) - &[first, .., last] => { - let snippet = snippet_with_applicability( - cx, - first.span.source_callsite().to(last.span.source_callsite()), - "..", - &mut applicability, - ); - format!("({snippet})") - } - _ => unreachable!(), - }; - (macro_call.span, suggestion) - }, - _ => unreachable!(), - }; + let (sugg_span, suggestion) = match is_async_move_desugar(expr) + .unwrap_or(expr) + .peel_drop_temps() + .kind + { + // dbg!() + ExprKind::Block(..) => { + // If the `dbg!` macro is a "free" statement and not contained within other expressions, + // remove the whole statement. + if let Node::Stmt(_) = cx.tcx.parent_hir_node(expr.hir_id) + && let Some(semi_span) = cx.sess().source_map().mac_call_stmt_semi_span(macro_call.span) + { + (macro_call.span.to(semi_span), String::new()) + } else { + (macro_call.span, String::from("()")) + } + }, + ExprKind::Match(first, arms, _) => { + let vals = collect_vals(first, arms); + let suggestion = match *vals.as_slice() { + // dbg!(1) => 1 + [val] => { + snippet_with_applicability(cx, val.span.source_callsite(), "..", &mut applicability) + .to_string() + }, + // dbg!(2, 3) => (2, 3) + [first, .., last] => { + let snippet = snippet_with_applicability( + cx, + first.span.source_callsite().to(last.span.source_callsite()), + "..", + &mut applicability, + ); + format!("({snippet})") + }, + _ => unreachable!(), + }; + (macro_call.span, suggestion) + }, + _ => unreachable!(), + }; diag.span_suggestion( sugg_span, @@ -165,7 +167,7 @@ fn first_dbg_macro_in_expansion(cx: &LateContext<'_>, span: Span) -> Option, span: Span) -> Option(first: &'hir Expr<'hir>, mut arms: &'hir [Arm<'hir>]) -> Vec<&'hir Expr<'hir>> { let mut vals = vec![first]; loop { - let [arm] = arms else { unreachable!("dbg! macro expansion only has single-arm matches") }; + let [arm] = arms else { + unreachable!("dbg! macro expansion only has single-arm matches") + }; - match is_async_move_desugar(arm.body).unwrap_or(arm.body).peel_drop_temps().kind { + match is_async_move_desugar(arm.body) + .unwrap_or(arm.body) + .peel_drop_temps() + .kind + { ExprKind::Block(..) => return vals, ExprKind::Match(val, a, _) => { vals.push(val); arms = a; - } + }, _ => unreachable!("dbg! macro expansion only results in block or match expressions"), } } diff --git a/clippy_lints/src/doc/doc_paragraphs_missing_punctuation.rs b/clippy_lints/src/doc/doc_paragraphs_missing_punctuation.rs index a8f7346376728..605d35f48baf2 100644 --- a/clippy_lints/src/doc/doc_paragraphs_missing_punctuation.rs +++ b/clippy_lints/src/doc/doc_paragraphs_missing_punctuation.rs @@ -53,21 +53,26 @@ fn is_missing_punctuation(doc_string: &str) -> Vec { let mut no_report_depth = 0; let mut missing_punctuation = Vec::new(); let mut current_paragraph = None; + let mut current_event_is_missing_punctuation = false; for (event, offset) in Parser::new_ext(doc_string, main_body_opts() - Options::ENABLE_SMART_PUNCTUATION).into_offset_iter() { + let last_event_was_missing_punctuation = current_event_is_missing_punctuation; + current_event_is_missing_punctuation = false; + match event { - Event::Start( - Tag::CodeBlock(..) - | Tag::FootnoteDefinition(_) - | Tag::Heading { .. } - | Tag::HtmlBlock - | Tag::List(..) - | Tag::Table(_), - ) => { + Event::Start(Tag::FootnoteDefinition(_) | Tag::Heading { .. } | Tag::HtmlBlock | Tag::Table(_)) => { no_report_depth += 1; }, + Event::Start(Tag::CodeBlock(..) | Tag::List(..)) => { + no_report_depth += 1; + if last_event_was_missing_punctuation { + // Remove the error from the previous paragraph as it is followed by a code + // block or a list. + missing_punctuation.pop(); + } + }, Event::End(TagEnd::FootnoteDefinition) => { no_report_depth -= 1; }, @@ -83,6 +88,7 @@ fn is_missing_punctuation(doc_string: &str) -> Vec { Event::End(TagEnd::Paragraph) => { if let Some(mp) = current_paragraph { missing_punctuation.push(mp); + current_event_is_missing_punctuation = true; } }, Event::Code(..) | Event::Start(Tag::Link { .. }) | Event::End(TagEnd::Link) diff --git a/clippy_lints/src/doc/doc_suspicious_footnotes.rs b/clippy_lints/src/doc/doc_suspicious_footnotes.rs index deca29a1885f0..dfa6c96378644 100644 --- a/clippy_lints/src/doc/doc_suspicious_footnotes.rs +++ b/clippy_lints/src/doc/doc_suspicious_footnotes.rs @@ -91,7 +91,7 @@ pub fn check(cx: &LateContext<'_>, doc: &str, range: Range, fragments: &F diag.span_suggestion_verbose( this_fragment.span.shrink_to_hi(), "add footnote definition", - format!("\n\n{label}: ", label = &doc[start..end],), + format!("\n\n{label}: ", label = &doc[start..end]), Applicability::HasPlaceholders, ); } else { diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index ecf7acbd7ce6e..e7a984694831b 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -1016,6 +1016,7 @@ struct CodeTags { no_run: bool, ignore: bool, compile_fail: bool, + test_harness: bool, rust: bool, } @@ -1026,6 +1027,7 @@ impl Default for CodeTags { no_run: false, ignore: false, compile_fail: false, + test_harness: false, rust: true, } @@ -1059,7 +1061,11 @@ impl CodeTags { tags.compile_fail = true; seen_rust_tags = !seen_other_tags || seen_rust_tags; }, - "test_harness" | "standalone_crate" => { + "test_harness" => { + tags.test_harness = true; + seen_rust_tags = !seen_other_tags || seen_rust_tags; + }, + "standalone_crate" => { seen_rust_tags = !seen_other_tags || seen_rust_tags; }, _ if item.starts_with("ignore-") => seen_rust_tags = true, @@ -1295,7 +1301,7 @@ fn check_doc<'a, Events: Iterator, Range for DurationSuboptimalUnits { .typeck_results() .node_type(func_ty.hir_id) .is_diag_item(cx, sym::Duration) + && matches!(cx.typeck_results().expr_ty_adjusted(arg).kind(), ty::Uint(UintTy::U64)) // We intentionally don't want to evaluate referenced constants, as we don't want to // recommend a literal value over using constants: // // let dur = Duration::from_secs(SIXTY); // ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Duration::from_mins(1)` && let Some(Constant::Int(value)) = ConstEvalCtxt::new(cx).eval_local(arg, expr.span.ctxt()) - && let value = u64::try_from(value).expect("All Duration::from_ constructors take a u64") + && let Ok(value) = u64::try_from(value) // Cannot fail // There is no need to promote e.g. 0 seconds to 0 hours && value != 0 && let Some((promoted_constructor, promoted_value)) = self.promote(cx, func_name.ident.name, value) diff --git a/clippy_lints/src/formatting.rs b/clippy_lints/src/formatting.rs index 3b9c70e23e20d..7a64d3135fa55 100644 --- a/clippy_lints/src/formatting.rs +++ b/clippy_lints/src/formatting.rs @@ -337,7 +337,7 @@ fn check_missing_else(cx: &EarlyContext<'_>, first: &Expr, second: &Expr) { else_span, format!("this looks like {looks_like} but the `else` is missing"), None, - format!("to remove this lint, add the missing `else` or add a new line before {next_thing}",), + format!("to remove this lint, add the missing `else` or add a new line before {next_thing}"), ); } } diff --git a/clippy_lints/src/functions/must_use.rs b/clippy_lints/src/functions/must_use.rs index 68532de0368f8..9ad36f7789041 100644 --- a/clippy_lints/src/functions/must_use.rs +++ b/clippy_lints/src/functions/must_use.rs @@ -13,7 +13,7 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; use clippy_utils::source::snippet_indent; use clippy_utils::ty::is_must_use_ty; use clippy_utils::visitors::for_each_expr_without_closures; -use clippy_utils::{return_ty, trait_ref_of_method}; +use clippy_utils::{is_entrypoint_fn, return_ty, trait_ref_of_method}; use rustc_hir::attrs::AttributeKind; use rustc_hir::find_attr; use rustc_span::Symbol; @@ -211,6 +211,7 @@ fn check_must_use_candidate<'tcx>( || !cx.effective_visibilities.is_exported(item_id.def_id) || is_must_use_ty(cx, return_ty(cx, item_id)) || item_span.from_expansion() + || is_entrypoint_fn(cx, item_id.def_id.to_def_id()) { return; } diff --git a/clippy_lints/src/implicit_hasher.rs b/clippy_lints/src/implicit_hasher.rs index 9dc74a157cbf2..70176c62772b7 100644 --- a/clippy_lints/src/implicit_hasher.rs +++ b/clippy_lints/src/implicit_hasher.rs @@ -92,7 +92,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher { ), ( target.span(), - format!("{}<{}, S>", target.type_name(), target.type_arguments(),), + format!("{}<{}, S>", target.type_name(), target.type_arguments()), ), ]; suggestions.extend(vis.suggestions); @@ -352,7 +352,7 @@ impl<'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'_, '_, 'tcx> { ); self.suggestions.insert( e.span, - format!("{container_name}::with_capacity_and_hasher({arg_snippet}, Default::default())",), + format!("{container_name}::with_capacity_and_hasher({arg_snippet}, Default::default())"), ); }, _ => {}, diff --git a/clippy_lints/src/indexing_slicing.rs b/clippy_lints/src/indexing_slicing.rs index a2fcdb4a54b43..e9ddd3ca8edef 100644 --- a/clippy_lints/src/indexing_slicing.rs +++ b/clippy_lints/src/indexing_slicing.rs @@ -44,7 +44,7 @@ declare_clippy_lint! { /// Checks for usage of indexing or slicing that may panic at runtime. /// /// This lint does not report on indexing or slicing operations - /// that always panic, clippy's `out_of_bound_indexing` already + /// that always panic, [out_of_bounds_indexing](#out_of_bounds_indexing) already /// handles those cases. /// /// ### Why restrict this? diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index e7b9b1cd38819..231388e7379a3 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -6,6 +6,7 @@ use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::source::{snippet, snippet_with_context}; use clippy_utils::visitors::{Descend, for_each_expr_without_closures}; use clippy_utils::{contains_return, sym}; +use rustc_ast::BinOpKind; use rustc_errors::Applicability; use rustc_hir::{ Block, Closure, Destination, Expr, ExprKind, HirId, InlineAsm, InlineAsmOperand, Node, Pat, Stmt, StmtKind, @@ -305,6 +306,9 @@ fn never_loop_expr<'tcx>( } }, ExprKind::Call(e, es) => never_loop_expr_all(cx, once(e).chain(es.iter()), local_labels, main_loop_id), + ExprKind::Binary(op, e1, _) if matches!(op.node, BinOpKind::And | BinOpKind::Or) => { + never_loop_expr(cx, e1, local_labels, main_loop_id) + }, ExprKind::Binary(_, e1, e2) | ExprKind::Assign(e1, e2, _) | ExprKind::AssignOp(_, e1, e2) diff --git a/clippy_lints/src/manual_let_else.rs b/clippy_lints/src/manual_let_else.rs index 38ee4ce104a52..cb3cc999c9361 100644 --- a/clippy_lints/src/manual_let_else.rs +++ b/clippy_lints/src/manual_let_else.rs @@ -301,7 +301,7 @@ fn replace_in_pattern( .collect::>(); let fields_string = fields.join(", "); - let dot_dot_str = if dot_dot.is_some() { " .." } else { "" }; + let dot_dot_str = if dot_dot.is_some() { ", .." } else { "" }; let (sn_pth, _) = snippet_with_context(cx, path.span(), span.ctxt(), "", app); return format!("{sn_pth} {{ {fields_string}{dot_dot_str} }}"); }, diff --git a/clippy_lints/src/matches/manual_unwrap_or.rs b/clippy_lints/src/matches/manual_unwrap_or.rs index abbc43d8e9b04..421c6064284d5 100644 --- a/clippy_lints/src/matches/manual_unwrap_or.rs +++ b/clippy_lints/src/matches/manual_unwrap_or.rs @@ -173,7 +173,7 @@ fn handle( expr.span, format!("this pattern reimplements `{ty_name}::unwrap_or`"), "replace with", - format!("{suggestion}.unwrap_or({reindented_or_body})",), + format!("{suggestion}.unwrap_or({reindented_or_body})"), app, ); } diff --git a/clippy_lints/src/matches/match_like_matches.rs b/clippy_lints/src/matches/match_like_matches.rs index c26b2dbde7fc0..347560b14eeac 100644 --- a/clippy_lints/src/matches/match_like_matches.rs +++ b/clippy_lints/src/matches/match_like_matches.rs @@ -187,7 +187,7 @@ pub(super) fn check_match<'tcx>( diag.span_suggestion_verbose( e.span, "use `matches!` directly", - format!("{}matches!({snippet}, {pat_and_guard})", if b0 { "" } else { "!" },), + format!("{}matches!({snippet}, {pat_and_guard})", if b0 { "" } else { "!" }), applicability, ); }, diff --git a/clippy_lints/src/methods/bytes_nth.rs b/clippy_lints/src/methods/bytes_nth.rs index 40d521d61c118..0a3ea3005e72b 100644 --- a/clippy_lints/src/methods/bytes_nth.rs +++ b/clippy_lints/src/methods/bytes_nth.rs @@ -34,7 +34,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx E parent.span, format!("called `.bytes().nth().unwrap()` on a `{caller_type}`"), "try", - format!("{receiver}.as_bytes()[{n}]",), + format!("{receiver}.as_bytes()[{n}]"), applicability, ); } else { diff --git a/clippy_lints/src/methods/into_iter_on_ref.rs b/clippy_lints/src/methods/into_iter_on_ref.rs index c4b116af48713..5a062732721e5 100644 --- a/clippy_lints/src/methods/into_iter_on_ref.rs +++ b/clippy_lints/src/methods/into_iter_on_ref.rs @@ -20,7 +20,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_span: Spa cx, INTO_ITER_ON_REF, method_span, - format!("this `.into_iter()` call is equivalent to `.{method_name}()` and will not consume the `{kind}`",), + format!("this `.into_iter()` call is equivalent to `.{method_name}()` and will not consume the `{kind}`"), "call directly", method_name.to_string(), Applicability::MachineApplicable, diff --git a/clippy_lints/src/methods/manual_is_variant_and.rs b/clippy_lints/src/methods/manual_is_variant_and.rs index 5ce9d364cdd84..d877b0a622918 100644 --- a/clippy_lints/src/methods/manual_is_variant_and.rs +++ b/clippy_lints/src/methods/manual_is_variant_and.rs @@ -1,9 +1,9 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::MaybeDef; -use clippy_utils::source::snippet_with_applicability; +use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::Sugg; -use clippy_utils::{get_parent_expr, sym}; +use clippy_utils::{SpanlessEq, get_parent_expr, sym}; use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; @@ -228,3 +228,65 @@ pub(super) fn check_map(cx: &LateContext<'_>, expr: &Expr<'_>) { } } } + +pub(super) fn check_or<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'tcx>, + lhs: &'tcx Expr<'tcx>, + rhs: &'tcx Expr<'tcx>, + msrv: Msrv, +) { + let (some_recv, some_arg) = if let ( + ExprKind::MethodCall(none_path, none_recv, [], _), + ExprKind::MethodCall(some_path, some_recv, [some_arg], _), + ) + | ( + ExprKind::MethodCall(some_path, some_recv, [some_arg], _), + ExprKind::MethodCall(none_path, none_recv, [], _), + ) = (lhs.kind, rhs.kind) + && none_path.ident.name == sym::is_none + && some_path.ident.name == sym::is_some_and + && cx + .typeck_results() + .expr_ty_adjusted(none_recv) + .peel_refs() + .is_diag_item(cx, sym::Option) + && cx + .typeck_results() + .expr_ty_adjusted(some_recv) + .peel_refs() + .is_diag_item(cx, sym::Option) + && SpanlessEq::new(cx).eq_expr(none_recv, some_recv) + { + (some_recv, some_arg) + } else { + return; + }; + + if !msrv.meets(cx, msrvs::IS_NONE_OR) { + return; + } + + let Ok(map_func) = MapFunc::try_from(some_arg) else { + return; + }; + + span_lint_and_then( + cx, + MANUAL_IS_VARIANT_AND, + expr.span, + "manual implementation of `Option::is_none_or`", + |diag| { + let mut app = Applicability::MachineApplicable; + let (recv_snip, _) = snippet_with_context(cx, some_recv.span, expr.span.ctxt(), "_", &mut app); + let map_func_snip = map_func.sugg(cx, false, &mut app); + + diag.span_suggestion( + expr.span, + "use", + format!("{recv_snip}.is_none_or({map_func_snip})"), + app, + ); + }, + ); +} diff --git a/clippy_lints/src/methods/manual_try_fold.rs b/clippy_lints/src/methods/manual_try_fold.rs index f2e127bedde56..5f5944d5d4230 100644 --- a/clippy_lints/src/methods/manual_try_fold.rs +++ b/clippy_lints/src/methods/manual_try_fold.rs @@ -47,7 +47,7 @@ pub(super) fn check<'tcx>( fold_span, "usage of `Iterator::fold` on a type that implements `Try`", "use `try_fold` instead", - format!("try_fold({init_snip}, {args_snip} ...)",), + format!("try_fold({init_snip}, {args_snip} ...)"), Applicability::HasPlaceholders, ); } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 376e93aa7e7d8..264405e6c3fb8 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -4965,6 +4965,16 @@ impl<'tcx> LateLintPass<'tcx> for Methods { io_other_error::check(cx, expr, func, args, self.msrv); swap_with_temporary::check(cx, expr, func, args); ip_constant::check(cx, expr, func, args); + unwrap_expect_used::check_call( + cx, + expr, + func, + args, + self.allow_unwrap_in_tests, + self.allow_expect_in_tests, + self.allow_unwrap_in_consts, + self.allow_expect_in_consts, + ); }, ExprKind::MethodCall(..) => { self.check_methods(cx, expr); @@ -4978,6 +4988,9 @@ impl<'tcx> LateLintPass<'tcx> for Methods { }; lint_binary_expr_with_method_call(cx, &mut info); }, + ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Or => { + manual_is_variant_and::check_or(cx, expr, lhs, rhs, self.msrv); + }, _ => (), } } @@ -5538,7 +5551,7 @@ impl Methods { unnecessary_sort_by::check(cx, expr, call_span, arg, true); }, (sym::split, [arg]) => { - str_split::check(cx, expr, recv, arg); + str_split::check(cx, expr, recv, call_span, arg); }, (sym::splitn | sym::rsplitn, [count_arg, pat_arg]) => { if let Some(Constant::Int(count)) = ConstEvalCtxt::new(cx).eval(count_arg) { diff --git a/clippy_lints/src/methods/option_as_ref_deref.rs b/clippy_lints/src/methods/option_as_ref_deref.rs index 1239d8927acfe..7c95d65e6b95c 100644 --- a/clippy_lints/src/methods/option_as_ref_deref.rs +++ b/clippy_lints/src/methods/option_as_ref_deref.rs @@ -58,7 +58,10 @@ pub(super) fn check( .iter() .map(|x| &x.kind) .collect::>() - && let [ty::adjustment::Adjust::Deref(ty::adjustment::DerefAdjustKind::Builtin), ty::adjustment::Adjust::Borrow(_)] = *adj + && let [ + ty::adjustment::Adjust::Deref(ty::adjustment::DerefAdjustKind::Builtin), + ty::adjustment::Adjust::Borrow(_), + ] = *adj && let method_did = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id).unwrap() && let Some(method_name) = cx.tcx.get_diagnostic_name(method_did) { diff --git a/clippy_lints/src/methods/str_split.rs b/clippy_lints/src/methods/str_split.rs index 479064a0671e5..8641f7c0abec3 100644 --- a/clippy_lints/src/methods/str_split.rs +++ b/clippy_lints/src/methods/str_split.rs @@ -1,39 +1,48 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_with_context; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::sym; use clippy_utils::visitors::is_const_evaluatable; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; +use rustc_span::Span; use super::STR_SPLIT_AT_NEWLINE; -pub(super) fn check<'a>(cx: &LateContext<'a>, expr: &'_ Expr<'_>, split_recv: &'a Expr<'_>, split_arg: &'_ Expr<'_>) { +pub(super) fn check<'a>( + cx: &LateContext<'a>, + expr: &'_ Expr<'_>, + split_recv: &'a Expr<'_>, + split_span: Span, + split_arg: &'_ Expr<'_>, +) { // We're looking for `A.trim().split(B)`, where the adjusted type of `A` is `&str` (e.g. an // expression returning `String`), and `B` is a `Pattern` that hard-codes a newline (either `"\n"` // or `"\r\n"`). There are a lot of ways to specify a pattern, and this lint only checks the most // basic ones: a `'\n'`, `"\n"`, and `"\r\n"`. - if let ExprKind::MethodCall(trim_method_name, trim_recv, [], _) = split_recv.kind + if let ExprKind::MethodCall(trim_method_name, trim_recv, [], trim_span) = split_recv.kind && trim_method_name.ident.name == sym::trim && cx.typeck_results().expr_ty_adjusted(trim_recv).peel_refs().is_str() && !is_const_evaluatable(cx, trim_recv) && let ExprKind::Lit(split_lit) = split_arg.kind - && (matches!(split_lit.node, LitKind::Char('\n')) - || matches!(split_lit.node, LitKind::Str(sym::LF | sym::CRLF, _))) + && matches!( + split_lit.node, + LitKind::Char('\n') | LitKind::Str(sym::LF | sym::CRLF, _) + ) { - let mut app = Applicability::MaybeIncorrect; - span_lint_and_sugg( + span_lint_and_then( cx, STR_SPLIT_AT_NEWLINE, expr.span, "using `str.trim().split()` with hard-coded newlines", - "use `str.lines()` instead", - format!( - "{}.lines()", - snippet_with_context(cx, trim_recv.span, expr.span.ctxt(), "..", &mut app).0 - ), - app, + |diag| { + diag.span_suggestion_verbose( + trim_span.to(split_span), // combine the call spans of the two methods + "use `str.lines()` instead", + "lines()", + Applicability::MaybeIncorrect, + ); + }, ); } } diff --git a/clippy_lints/src/methods/unit_hash.rs b/clippy_lints/src/methods/unit_hash.rs index 9defd5626eb47..fb447a99abdce 100644 --- a/clippy_lints/src/methods/unit_hash.rs +++ b/clippy_lints/src/methods/unit_hash.rs @@ -19,7 +19,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &' diag.span_suggestion( expr.span, "remove the call to `hash` or consider using", - format!("0_u8.hash({})", snippet(cx, arg.span, ".."),), + format!("0_u8.hash({})", snippet(cx, arg.span, "..")), Applicability::MaybeIncorrect, ); diag.note("the implementation of `Hash` for `()` is a no-op"); diff --git a/clippy_lints/src/methods/unwrap_expect_used.rs b/clippy_lints/src/methods/unwrap_expect_used.rs index 30db2a75df577..4effab3a5e63c 100644 --- a/clippy_lints/src/methods/unwrap_expect_used.rs +++ b/clippy_lints/src/methods/unwrap_expect_used.rs @@ -3,6 +3,7 @@ use clippy_utils::res::MaybeDef; use clippy_utils::ty::is_never_like; use clippy_utils::{is_in_test, is_inside_always_const_context, is_lint_allowed}; use rustc_hir::Expr; +use rustc_hir::def::DefKind; use rustc_lint::{LateContext, Lint}; use rustc_middle::ty; use rustc_span::sym; @@ -87,3 +88,70 @@ pub(super) fn check( }, ); } + +#[expect(clippy::too_many_arguments, clippy::fn_params_excessive_bools)] +pub(super) fn check_call( + cx: &LateContext<'_>, + expr: &Expr<'_>, + func: &Expr<'_>, + args: &[Expr<'_>], + allow_unwrap_in_consts: bool, + allow_unwrap_in_tests: bool, + allow_expect_in_consts: bool, + allow_expect_in_tests: bool, +) { + let Some(recv) = args.first() else { + return; + }; + let Some((DefKind::AssocFn, def_id)) = cx.typeck_results().type_dependent_def(func.hir_id) else { + return; + }; + + match cx.tcx.item_name(def_id) { + sym::unwrap => { + check( + cx, + expr, + recv, + false, + allow_unwrap_in_consts, + allow_unwrap_in_tests, + Variant::Unwrap, + ); + }, + sym::expect => { + check( + cx, + expr, + recv, + false, + allow_expect_in_consts, + allow_expect_in_tests, + Variant::Expect, + ); + }, + clippy_utils::sym::unwrap_err => { + check( + cx, + expr, + recv, + true, + allow_unwrap_in_consts, + allow_unwrap_in_tests, + Variant::Unwrap, + ); + }, + clippy_utils::sym::expect_err => { + check( + cx, + expr, + recv, + true, + allow_expect_in_consts, + allow_expect_in_tests, + Variant::Expect, + ); + }, + _ => (), + } +} diff --git a/clippy_lints/src/missing_asserts_for_indexing.rs b/clippy_lints/src/missing_asserts_for_indexing.rs index 87ee164a1760e..651382fb4bfe2 100644 --- a/clippy_lints/src/missing_asserts_for_indexing.rs +++ b/clippy_lints/src/missing_asserts_for_indexing.rs @@ -364,15 +364,15 @@ fn report_indexes(cx: &LateContext<'_>, map: UnindexMap> // `v.len() < 5` and `v.len() <= 5` does nothing in terms of bounds checks. // The user probably meant `v.len() > 5` LengthComparison::LengthLessThanInt | LengthComparison::LengthLessThanOrEqualInt => { - Some(format!("assert!({slice_str}.len() > {highest_index})",)) + Some(format!("assert!({slice_str}.len() > {highest_index})")) }, // `5 < v.len()` == `v.len() > 5` LengthComparison::IntLessThanLength if asserted_len < highest_index => { - Some(format!("assert!({slice_str}.len() > {highest_index})",)) + Some(format!("assert!({slice_str}.len() > {highest_index})")) }, // `5 <= v.len() == `v.len() >= 5` LengthComparison::IntLessThanOrEqualLength if asserted_len <= highest_index => { - Some(format!("assert!({slice_str}.len() > {highest_index})",)) + Some(format!("assert!({slice_str}.len() > {highest_index})")) }, // `highest_index` here is rather a length, so we need to add 1 to it LengthComparison::LengthEqualInt if asserted_len < highest_index + 1 => match macro_call { diff --git a/clippy_lints/src/missing_enforced_import_rename.rs b/clippy_lints/src/missing_enforced_import_rename.rs index 5dd38cf059c27..1f9652a2bd384 100644 --- a/clippy_lints/src/missing_enforced_import_rename.rs +++ b/clippy_lints/src/missing_enforced_import_rename.rs @@ -97,7 +97,7 @@ impl LateLintPass<'_> for ImportRename { span_without_semi, "this import should be renamed", "try", - format!("{import} as {name}",), + format!("{import} as {name}"), Applicability::MachineApplicable, ); } diff --git a/clippy_lints/src/operators/cmp_owned.rs b/clippy_lints/src/operators/cmp_owned.rs index 39097833a6c5f..db55772f4e017 100644 --- a/clippy_lints/src/operators/cmp_owned.rs +++ b/clippy_lints/src/operators/cmp_owned.rs @@ -10,10 +10,10 @@ use rustc_span::symbol::sym; use super::CMP_OWNED; -pub(super) fn check(cx: &LateContext<'_>, op: BinOpKind, lhs: &Expr<'_>, rhs: &Expr<'_>) { +pub(super) fn check(cx: &LateContext<'_>, e: &Expr<'_>, op: BinOpKind, lhs: &Expr<'_>, rhs: &Expr<'_>) { if op.is_comparison() { - check_op(cx, lhs, rhs, true); - check_op(cx, rhs, lhs, false); + check_op(cx, e, lhs, rhs, true); + check_op(cx, e, rhs, lhs, false); } } @@ -35,7 +35,11 @@ fn symmetric_partial_eq<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, other: Ty<'t }) } -fn check_op(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) { +fn check_op(cx: &LateContext<'_>, outer: &Expr<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) { + if !outer.span.eq_ctxt(expr.span) { + return; + } + let typeck = cx.typeck_results(); let (arg, arg_span) = match expr.kind { ExprKind::MethodCall(_, arg, [], _) diff --git a/clippy_lints/src/operators/mod.rs b/clippy_lints/src/operators/mod.rs index 53b8e9e5d5ae7..383b135dfa506 100644 --- a/clippy_lints/src/operators/mod.rs +++ b/clippy_lints/src/operators/mod.rs @@ -1038,7 +1038,7 @@ impl<'tcx> LateLintPass<'tcx> for Operators { float_equality_without_abs::check(cx, e, op.node, lhs, rhs); integer_division::check(cx, e, op.node, lhs, rhs); integer_division_remainder_used::check(cx, op.node, lhs, rhs, e.span); - cmp_owned::check(cx, op.node, lhs, rhs); + cmp_owned::check(cx, e, op.node, lhs, rhs); float_cmp::check(cx, e, op.node, lhs, rhs); modulo_one::check(cx, e, op.node, rhs); modulo_arithmetic::check( diff --git a/clippy_lints/src/ptr/cmp_null.rs b/clippy_lints/src/ptr/cmp_null.rs index f2d1c855eddda..5e1c62316b7d3 100644 --- a/clippy_lints/src/ptr/cmp_null.rs +++ b/clippy_lints/src/ptr/cmp_null.rs @@ -32,7 +32,7 @@ pub(super) fn check<'tcx>( expr.span, "comparing with null is better expressed by the `.is_null()` method", "try", - format!("{invert}{non_null_path_snippet}.is_null()",), + format!("{invert}{non_null_path_snippet}.is_null()"), applicability, ); true diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index e5fb3c0fa431f..5517b7f260eaa 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -474,7 +474,6 @@ fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: if_else, .. }) = higher::IfLet::hir(cx, expr) - && !is_else_clause(cx.tcx, expr) && let PatKind::TupleStruct(ref path1, [field], ddpos) = let_pat.kind && ddpos.as_opt_usize().is_none() && let PatKind::Binding(BindingMode(by_ref, _), bind_id, ident, None) = field.kind @@ -509,10 +508,15 @@ fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: ByRef::Yes(_, Mutability::Not) => ".as_ref()", ByRef::No => "", }; - let sugg = format!( + + let mut sugg = format!( "{receiver_str}{method_call_str}?{}", if requires_semi { ";" } else { "" } ); + if is_else_clause(cx.tcx, expr) { + sugg = format!("{{ {sugg} }}"); + } + span_lint_and_sugg( cx, QUESTION_MARK, diff --git a/clippy_lints/src/returns/let_and_return.rs b/clippy_lints/src/returns/let_and_return.rs index 0a00981e15bed..b19935959c4d8 100644 --- a/clippy_lints/src/returns/let_and_return.rs +++ b/clippy_lints/src/returns/let_and_return.rs @@ -27,7 +27,7 @@ pub(super) fn check_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'_>) && !initexpr.span.in_external_macro(cx.sess().source_map()) && !retexpr.span.in_external_macro(cx.sess().source_map()) && !local.span.from_expansion() - && !span_contains_non_whitespace(cx, stmt.span.between(retexpr.span), true) + && !span_contains_non_whitespace(cx, stmt.span.between(retexpr.span), false) { span_lint_hir_and_then( cx, diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index c0be724bcdeeb..509ad4e4fcb3b 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -5,6 +5,7 @@ use clippy_utils::{ SpanlessEq, get_expr_use_or_unification_node, get_parent_expr, is_lint_allowed, method_calls, peel_blocks, sym, }; use rustc_errors::Applicability; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, LangItem, Node}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -410,6 +411,23 @@ impl<'tcx> LateLintPass<'tcx> for StrToString { diag.span_suggestion(expr.span, "try", format!("{snippet}.to_owned()"), applicability); }, ); + } else if let ExprKind::Path(_) = expr.kind + && let Some(parent) = get_parent_expr(cx, expr) + && let ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args, _) = &parent.kind + && args.iter().any(|a| a.hir_id == expr.hir_id) + && let Res::Def(DefKind::AssocFn, def_id) = expr.res(cx) + && cx.tcx.is_diagnostic_item(sym::to_string_method, def_id) + { + // Detected `ToString::to_string` passed as an argument (generic: any call or method call) + span_lint_and_sugg( + cx, + STR_TO_STRING, + expr.span, + "`ToString::to_string` used as `&str` to `String` converter", + "try", + "ToOwned::to_owned".to_string(), + Applicability::MachineApplicable, + ); } } } diff --git a/clippy_lints/src/time_subtraction.rs b/clippy_lints/src/time_subtraction.rs index ca8378ba7c6a2..c241935d6c312 100644 --- a/clippy_lints/src/time_subtraction.rs +++ b/clippy_lints/src/time_subtraction.rs @@ -85,9 +85,7 @@ impl LateLintPass<'_> for UncheckedTimeSubtraction { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { let (lhs, rhs) = match expr.kind { ExprKind::Binary(op, lhs, rhs) if matches!(op.node, BinOpKind::Sub,) => (lhs, rhs), - ExprKind::MethodCall(_, lhs, [rhs], _) if cx.ty_based_def(expr).is_diag_item(cx, sym::sub) => { - (lhs, rhs) - }, + ExprKind::MethodCall(_, lhs, [rhs], _) if cx.ty_based_def(expr).is_diag_item(cx, sym::sub) => (lhs, rhs), _ => return, }; let typeck = cx.typeck_results(); diff --git a/clippy_lints/src/toplevel_ref_arg.rs b/clippy_lints/src/toplevel_ref_arg.rs index 250c277ab5e1f..bce2ede6589b6 100644 --- a/clippy_lints/src/toplevel_ref_arg.rs +++ b/clippy_lints/src/toplevel_ref_arg.rs @@ -109,7 +109,7 @@ impl<'tcx> LateLintPass<'tcx> for ToplevelRefArg { diag.span_suggestion( stmt.span, "try", - format!("let {name}{tyopt} = {initref};", name = snippet(cx, name.span, ".."),), + format!("let {name}{tyopt} = {initref};", name = snippet(cx, name.span, "..")), app, ); }, diff --git a/clippy_lints/src/zero_div_zero.rs b/clippy_lints/src/zero_div_zero.rs index bb0cab3a30757..e44c4dc9776eb 100644 --- a/clippy_lints/src/zero_div_zero.rs +++ b/clippy_lints/src/zero_div_zero.rs @@ -56,7 +56,7 @@ impl<'tcx> LateLintPass<'tcx> for ZeroDiv { expr.span, "constant division of `0.0` with `0.0` will always result in NaN", None, - format!("consider using `{float_type}::NAN` if you would like a constant representing NaN",), + format!("consider using `{float_type}::NAN` if you would like a constant representing NaN"), ); } } diff --git a/clippy_lints/src/zero_repeat_side_effects.rs b/clippy_lints/src/zero_repeat_side_effects.rs index 95085161c09c9..cb254cc156296 100644 --- a/clippy_lints/src/zero_repeat_side_effects.rs +++ b/clippy_lints/src/zero_repeat_side_effects.rs @@ -168,7 +168,7 @@ fn assign_expr_suggestion( let indent = snippet_indent(cx, outer_expr.span).unwrap_or_default(); let var_name = snippet(cx, assign_expr_span.source_callsite(), ".."); if needs_curly { - format!("{{\n {indent}{inner_expr};\n {indent}{var_name} = {vec_str}[] as {return_type}\n{indent}}}",) + format!("{{\n {indent}{inner_expr};\n {indent}{var_name} = {vec_str}[] as {return_type}\n{indent}}}") } else { format!("{inner_expr};\n{indent}{var_name} = {vec_str}[] as {return_type}") } diff --git a/clippy_utils/README.md b/clippy_utils/README.md index 204a66a435eeb..1b85dcfd848d5 100644 --- a/clippy_utils/README.md +++ b/clippy_utils/README.md @@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain: ``` -nightly-2026-01-22 +nightly-2026-02-11 ``` diff --git a/clippy_utils/src/ast_utils/mod.rs b/clippy_utils/src/ast_utils/mod.rs index 3b043f7565ef9..bae3baba12fba 100644 --- a/clippy_utils/src/ast_utils/mod.rs +++ b/clippy_utils/src/ast_utils/mod.rs @@ -795,14 +795,14 @@ pub fn eq_const_item_rhs(l: &ConstItemRhsKind, r: &ConstItemRhsKind) -> bool { use ConstItemRhsKind::*; match (l, r) { (TypeConst { rhs: Some(l) }, TypeConst { rhs: Some(r) }) => eq_anon_const(l, r), - (TypeConst { rhs: None }, TypeConst { rhs: None }) => true, - (TypeConst { rhs: Some(..) }, TypeConst { rhs: None }) => false, - (TypeConst { rhs: None }, TypeConst { rhs: Some(..) }) => false, + (TypeConst { rhs: None }, TypeConst { rhs: None }) | (Body { rhs: None }, Body { rhs: None }) => true, (Body { rhs: Some(l) }, Body { rhs: Some(r) }) => eq_expr(l, r), - (Body { rhs: None }, Body { rhs: None }) => true, - (Body { rhs: None }, Body { rhs: Some(..) }) => false, - (Body { rhs: Some(..) }, Body { rhs: None }) => false, - (TypeConst {..}, Body { .. }) | ( Body { .. }, TypeConst { .. }) => false, + (TypeConst { rhs: Some(..) }, TypeConst { rhs: None }) + | (TypeConst { rhs: None }, TypeConst { rhs: Some(..) }) + | (Body { rhs: None }, Body { rhs: Some(..) }) + | (Body { rhs: Some(..) }, Body { rhs: None }) + | (TypeConst { .. }, Body { .. }) + | (Body { .. }, TypeConst { .. }) => false, } } diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs index 7fb8616072a59..def5d968b063f 100644 --- a/clippy_utils/src/check_proc_macro.rs +++ b/clippy_utils/src/check_proc_macro.rs @@ -45,6 +45,8 @@ pub enum Pat { Sym(Symbol), /// Any decimal or hexadecimal digit depending on the location. Num, + /// An attribute. + Attr(Symbol), } /// Checks if the start and the end of the span's text matches the patterns. This will return false @@ -65,12 +67,20 @@ fn span_matches_pat(sess: &Session, span: Span, start_pat: Pat, end_pat: Pat) -> Pat::OwnedMultiStr(texts) => texts.iter().any(|s| start_str.starts_with(s)), Pat::Sym(sym) => start_str.starts_with(sym.as_str()), Pat::Num => start_str.as_bytes().first().is_some_and(u8::is_ascii_digit), + Pat::Attr(sym) => { + let start_str = start_str + .strip_prefix("#[") + .or_else(|| start_str.strip_prefix("#![")) + .unwrap_or(start_str); + start_str.trim_start().starts_with(sym.as_str()) + }, } && match end_pat { Pat::Str(text) => end_str.ends_with(text), Pat::MultiStr(texts) => texts.iter().any(|s| end_str.ends_with(s)), Pat::OwnedMultiStr(texts) => texts.iter().any(|s| end_str.ends_with(s)), Pat::Sym(sym) => end_str.ends_with(sym.as_str()), Pat::Num => end_str.as_bytes().last().is_some_and(u8::is_ascii_hexdigit), + Pat::Attr(_) => false, }) }) } @@ -350,18 +360,7 @@ fn attr_search_pat(attr: &Attribute) -> (Pat, Pat) { AttrKind::Normal(..) => { if let Some(name) = attr.name() { // NOTE: This will likely have false positives, like `allow = 1` - let ident_string = name.to_string(); - if attr.style == AttrStyle::Outer { - ( - Pat::OwnedMultiStr(vec!["#[".to_owned() + &ident_string, ident_string]), - Pat::Str(""), - ) - } else { - ( - Pat::OwnedMultiStr(vec!["#![".to_owned() + &ident_string, ident_string]), - Pat::Str(""), - ) - } + (Pat::Attr(name), Pat::Str("")) } else { (Pat::Str("#"), Pat::Str("]")) } diff --git a/clippy_utils/src/sym.rs b/clippy_utils/src/sym.rs index 5d6f2241c7c0d..e0b03ae4f7b25 100644 --- a/clippy_utils/src/sym.rs +++ b/clippy_utils/src/sym.rs @@ -147,6 +147,7 @@ generate! { exp, expect_err, expn_data, + exported_private_dependencies, extend, filter, filter_map, diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index 46456528fdf87..639492b75747a 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -31,9 +31,9 @@ use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt; use rustc_trait_selection::traits::{Obligation, ObligationCause}; #[cfg(bootstrap)] use std::assert_matches::debug_assert_matches; +use std::collections::hash_map::Entry; #[cfg(not(bootstrap))] use std::debug_assert_matches; -use std::collections::hash_map::Entry; use std::{iter, mem}; use crate::paths::{PathNS, lookup_path_str}; diff --git a/rust-toolchain.toml b/rust-toolchain.toml index c26289c237255..558f808a7a3ed 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2026-01-22" +channel = "nightly-2026-02-11" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" diff --git a/rustc_tools_util/src/lib.rs b/rustc_tools_util/src/lib.rs index 194ed84d04c28..3b7d2d4085d9c 100644 --- a/rustc_tools_util/src/lib.rs +++ b/rustc_tools_util/src/lib.rs @@ -91,7 +91,7 @@ impl std::fmt::Debug for VersionInfo { self.crate_name, self.major, self.minor, self.patch, )?; if let Some(ref commit_hash) = self.commit_hash { - write!(f, ", commit_hash: \"{}\"", commit_hash.trim(),)?; + write!(f, ", commit_hash: \"{}\"", commit_hash.trim())?; } if let Some(ref commit_date) = self.commit_date { write!(f, ", commit_date: \"{}\"", commit_date.trim())?; diff --git a/src/driver.rs b/src/driver.rs index cf06f43e8d4a3..409eb182fe33c 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -192,7 +192,7 @@ fn display_help() -> ExitCode { const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust-clippy/issues/new?template=ice.yml"; -pub fn main() -> ExitCode { +fn main() -> ExitCode { let early_dcx = EarlyDiagCtxt::new(ErrorOutputType::default()); rustc_driver::init_rustc_env_logger(&early_dcx); @@ -257,7 +257,7 @@ pub fn main() -> ExitCode { return match writeln!(&mut anstream::stdout().lock(), "{version_info}") { Ok(()) => ExitCode::SUCCESS, Err(_) => ExitCode::FAILURE, - } + }; } // Setting RUSTC_WRAPPER causes Cargo to pass 'rustc' as the first argument. diff --git a/tests/ui/allow_attributes.fixed b/tests/ui/allow_attributes.fixed index 56a98cca3404c..ecc9f08c19684 100644 --- a/tests/ui/allow_attributes.fixed +++ b/tests/ui/allow_attributes.fixed @@ -63,6 +63,11 @@ fn msrv_1_80() { let x = 1; } +#[rustfmt::skip] +#[ expect ( dead_code ) ] +//~^ allow_attributes +struct Spaced; + #[deny(clippy::allow_attributes)] fn deny_allow_attributes() -> Option { let allow = None; diff --git a/tests/ui/allow_attributes.rs b/tests/ui/allow_attributes.rs index 65a0a6b5a108c..3ab328b25affb 100644 --- a/tests/ui/allow_attributes.rs +++ b/tests/ui/allow_attributes.rs @@ -63,6 +63,11 @@ fn msrv_1_80() { let x = 1; } +#[rustfmt::skip] +#[ allow ( dead_code ) ] +//~^ allow_attributes +struct Spaced; + #[deny(clippy::allow_attributes)] fn deny_allow_attributes() -> Option { let allow = None; diff --git a/tests/ui/allow_attributes.stderr b/tests/ui/allow_attributes.stderr index dd5fb21ffeaf5..67a70aac8a860 100644 --- a/tests/ui/allow_attributes.stderr +++ b/tests/ui/allow_attributes.stderr @@ -19,5 +19,11 @@ error: #[allow] attribute found LL | #[allow(unused)] | ^^^^^ help: replace it with: `expect` -error: aborting due to 3 previous errors +error: #[allow] attribute found + --> tests/ui/allow_attributes.rs:67:4 + | +LL | #[ allow ( dead_code ) ] + | ^^^^^ help: replace it with: `expect` + +error: aborting due to 4 previous errors diff --git a/tests/ui/cmp_owned/with_suggestion.fixed b/tests/ui/cmp_owned/with_suggestion.fixed index 4c3b13b30043d..f65339605e756 100644 --- a/tests/ui/cmp_owned/with_suggestion.fixed +++ b/tests/ui/cmp_owned/with_suggestion.fixed @@ -112,3 +112,35 @@ fn issue16322(item: String) { println!("Ja!"); } } + +fn issue16458() { + macro_rules! partly_comes_from_macro { + ($i:ident: $ty:ty, $def:expr) => { + let _ = { + let res = <$ty>::default() == $def; + let _i: $ty = $def; + res + }; + }; + } + + partly_comes_from_macro! { + required_version: String, env!("HOME").to_string() + } + + macro_rules! all_comes_from_macro { + ($($i:ident: $ty:ty, $def:expr);+ $(;)*) => { + $( + let _ = { + let res = <$ty>::default() == "$def"; + //~^ cmp_owned + let _i: $ty = $def; + res + }; + )+ + }; + } + all_comes_from_macro! { + required_version: String, env!("HOME").to_string(); + } +} diff --git a/tests/ui/cmp_owned/with_suggestion.rs b/tests/ui/cmp_owned/with_suggestion.rs index a9d7509feaaf0..ed2300c80eaa9 100644 --- a/tests/ui/cmp_owned/with_suggestion.rs +++ b/tests/ui/cmp_owned/with_suggestion.rs @@ -112,3 +112,35 @@ fn issue16322(item: String) { println!("Ja!"); } } + +fn issue16458() { + macro_rules! partly_comes_from_macro { + ($i:ident: $ty:ty, $def:expr) => { + let _ = { + let res = <$ty>::default() == $def; + let _i: $ty = $def; + res + }; + }; + } + + partly_comes_from_macro! { + required_version: String, env!("HOME").to_string() + } + + macro_rules! all_comes_from_macro { + ($($i:ident: $ty:ty, $def:expr);+ $(;)*) => { + $( + let _ = { + let res = <$ty>::default() == "$def".to_string(); + //~^ cmp_owned + let _i: $ty = $def; + res + }; + )+ + }; + } + all_comes_from_macro! { + required_version: String, env!("HOME").to_string(); + } +} diff --git a/tests/ui/cmp_owned/with_suggestion.stderr b/tests/ui/cmp_owned/with_suggestion.stderr index 66544ce0c2177..38d124baa4b56 100644 --- a/tests/ui/cmp_owned/with_suggestion.stderr +++ b/tests/ui/cmp_owned/with_suggestion.stderr @@ -55,5 +55,18 @@ error: this creates an owned instance just for comparison LL | if item == t!(frohes_neu_Jahr).to_string() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t!(frohes_neu_Jahr)` -error: aborting due to 9 previous errors +error: this creates an owned instance just for comparison + --> tests/ui/cmp_owned/with_suggestion.rs:135:51 + | +LL | let res = <$ty>::default() == "$def".to_string(); + | ^^^^^^^^^^^^^^^^^^ help: try: `"$def"` +... +LL | / all_comes_from_macro! { +LL | | required_version: String, env!("HOME").to_string(); +LL | | } + | |_____- in this macro invocation + | + = note: this error originates in the macro `all_comes_from_macro` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 10 previous errors diff --git a/tests/ui/doc/doc-fixable.fixed b/tests/ui/doc/doc-fixable.fixed index 46695dc929ab7..58d8b8b33ade0 100644 --- a/tests/ui/doc/doc-fixable.fixed +++ b/tests/ui/doc/doc-fixable.fixed @@ -75,7 +75,7 @@ fn test_units() { /// IPv4 IPv6 /// InfiniBand RoCE /// ClojureScript CoffeeScript JavaScript PostScript PureScript TypeScript -/// PowerPC WebAssembly +/// PowerPC PowerShell WebAssembly /// NaN NaNs /// OAuth GraphQL /// OCaml diff --git a/tests/ui/doc/doc-fixable.rs b/tests/ui/doc/doc-fixable.rs index 4082fa5b56f4b..0b1237f716fa0 100644 --- a/tests/ui/doc/doc-fixable.rs +++ b/tests/ui/doc/doc-fixable.rs @@ -75,7 +75,7 @@ fn test_units() { /// IPv4 IPv6 /// InfiniBand RoCE /// ClojureScript CoffeeScript JavaScript PostScript PureScript TypeScript -/// PowerPC WebAssembly +/// PowerPC PowerShell WebAssembly /// NaN NaNs /// OAuth GraphQL /// OCaml diff --git a/tests/ui/doc/doc_paragraphs_missing_punctuation.fixed b/tests/ui/doc/doc_paragraphs_missing_punctuation.fixed index 95d65039440b5..faabbb381318e 100644 --- a/tests/ui/doc/doc_paragraphs_missing_punctuation.fixed +++ b/tests/ui/doc/doc_paragraphs_missing_punctuation.fixed @@ -46,13 +46,6 @@ enum Exceptions { /// | -------------- | ----- | /// | Markdown table | A-ok | MarkdownTable, - /// Here is a snippet. - //~^ doc_paragraphs_missing_punctuation - /// - /// ``` - /// // Code blocks are no issues. - /// ``` - CodeBlock, } // Check the lint can be expected on a whole enum at once. @@ -130,6 +123,24 @@ enum OrderedLists { Paren, } +/// Some elements do not have to be introduced by an independent clause. +enum NotIndependentClause { + /// Lists are allowed to be introduced by a clause that is not independent: this usually + /// requires that + /// + /// - items end with a comma or a semicolon, which is not enforced; + /// - the last item end with a period, which is also not enforced. + List, + /// For instance, the function + /// + /// ``` + /// fn answer() {} + /// ``` + /// + /// returns the Answer to the Ultimate Question of Life, the Universe, and Everything. + CodeBlock, +} + /// Doc comments with trailing blank lines are supported. //~^ doc_paragraphs_missing_punctuation /// diff --git a/tests/ui/doc/doc_paragraphs_missing_punctuation.rs b/tests/ui/doc/doc_paragraphs_missing_punctuation.rs index 35b74d7d13b9e..9821115601a65 100644 --- a/tests/ui/doc/doc_paragraphs_missing_punctuation.rs +++ b/tests/ui/doc/doc_paragraphs_missing_punctuation.rs @@ -46,13 +46,6 @@ enum Exceptions { /// | -------------- | ----- | /// | Markdown table | A-ok | MarkdownTable, - /// Here is a snippet - //~^ doc_paragraphs_missing_punctuation - /// - /// ``` - /// // Code blocks are no issues. - /// ``` - CodeBlock, } // Check the lint can be expected on a whole enum at once. @@ -130,6 +123,24 @@ enum OrderedLists { Paren, } +/// Some elements do not have to be introduced by an independent clause. +enum NotIndependentClause { + /// Lists are allowed to be introduced by a clause that is not independent: this usually + /// requires that + /// + /// - items end with a comma or a semicolon, which is not enforced; + /// - the last item end with a period, which is also not enforced. + List, + /// For instance, the function + /// + /// ``` + /// fn answer() {} + /// ``` + /// + /// returns the Answer to the Ultimate Question of Life, the Universe, and Everything. + CodeBlock, +} + /// Doc comments with trailing blank lines are supported //~^ doc_paragraphs_missing_punctuation /// diff --git a/tests/ui/doc/doc_paragraphs_missing_punctuation.stderr b/tests/ui/doc/doc_paragraphs_missing_punctuation.stderr index 49aa4e8aeb888..6645e771c6d30 100644 --- a/tests/ui/doc/doc_paragraphs_missing_punctuation.stderr +++ b/tests/ui/doc/doc_paragraphs_missing_punctuation.stderr @@ -32,82 +32,76 @@ LL | /// | ^ help: end the paragraph with some punctuation: `.` error: doc paragraphs should end with a terminal punctuation mark - --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:49:26 - | -LL | /// Here is a snippet - | ^ help: end the paragraph with some punctuation: `.` - -error: doc paragraphs should end with a terminal punctuation mark - --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:72:15 + --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:65:15 | LL | /// U+0001 | ^ help: end the paragraph with some punctuation: `.` error: doc paragraphs should end with a terminal punctuation mark - --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:79:29 + --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:72:29 | LL | //! inner attributes too | ^ help: end the paragraph with some punctuation: `.` error: doc paragraphs should end with a terminal punctuation mark - --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:90:47 + --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:83:47 | LL | /// **But sometimes it is missing a period** | ^ help: end the paragraph with some punctuation: `.` error: doc paragraphs should end with a terminal punctuation mark - --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:95:46 + --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:88:46 | LL | /// _But sometimes it is missing a period_ | ^ help: end the paragraph with some punctuation: `.` error: doc paragraphs should end with a terminal punctuation mark - --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:104:56 + --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:97:56 | LL | /// Doc comments can end with an [inline link](#anchor) | ^ help: end the paragraph with some punctuation: `.` error: doc paragraphs should end with a terminal punctuation mark - --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:108:65 + --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:101:65 | LL | /// Some doc comments contain [link reference definitions][spec] | ^ help: end the paragraph with some punctuation: `.` error: doc paragraphs should end with a terminal punctuation mark - --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:133:57 + --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:144:57 | LL | /// Doc comments with trailing blank lines are supported | ^ help: end the paragraph with some punctuation: `.` error: doc paragraphs should end with a terminal punctuation mark - --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:139:48 + --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:150:48 | LL | /// This first paragraph is missing punctuation | ^ help: end the paragraph with some punctuation: `.` error: doc paragraphs should end with a terminal punctuation mark - --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:143:34 + --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:154:34 | LL | /// And it has multiple sentences | ^ help: end the paragraph with some punctuation: `.` error: doc paragraphs should end with a terminal punctuation mark - --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:146:37 + --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:157:37 | LL | /// Same for this third and last one | ^ help: end the paragraph with some punctuation: `.` error: doc paragraphs should end with a terminal punctuation mark - --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:153:33 + --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:164:33 | LL | /// This ends with a code `span` | ^ help: end the paragraph with some punctuation: `.` error: doc paragraphs should end with a terminal punctuation mark - --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:162:27 + --> tests/ui/doc/doc_paragraphs_missing_punctuation.rs:173:27 | LL | * Block doc comments work | ^ help: end the paragraph with some punctuation: `.` -error: aborting due to 18 previous errors +error: aborting due to 17 previous errors diff --git a/tests/ui/duration_suboptimal_units.fixed b/tests/ui/duration_suboptimal_units.fixed index 98c4b6e965ba5..515ec10e572b1 100644 --- a/tests/ui/duration_suboptimal_units.fixed +++ b/tests/ui/duration_suboptimal_units.fixed @@ -89,3 +89,8 @@ mod my_duration { let dur = Duration::from_secs(60); } } + +fn issue16457() { + // Methods taking something else than `u64` are not covered + _ = Duration::from_nanos_u128(1 << 90); +} diff --git a/tests/ui/duration_suboptimal_units.rs b/tests/ui/duration_suboptimal_units.rs index c4f33a9f92e03..357c52cffb35a 100644 --- a/tests/ui/duration_suboptimal_units.rs +++ b/tests/ui/duration_suboptimal_units.rs @@ -89,3 +89,8 @@ mod my_duration { let dur = Duration::from_secs(60); } } + +fn issue16457() { + // Methods taking something else than `u64` are not covered + _ = Duration::from_nanos_u128(1 << 90); +} diff --git a/tests/ui/let_and_return.edition2021.fixed b/tests/ui/let_and_return.edition2021.fixed index e89e4476bf820..6ca0febc2b8d9 100644 --- a/tests/ui/let_and_return.edition2021.fixed +++ b/tests/ui/let_and_return.edition2021.fixed @@ -271,4 +271,12 @@ fn issue15987() -> i32 { r } +fn has_comment() -> Vec { + let v = Vec::new(); + + // TODO: stuff + + v +} + fn main() {} diff --git a/tests/ui/let_and_return.edition2024.fixed b/tests/ui/let_and_return.edition2024.fixed index d2c76673ca03c..0fce22936ae6e 100644 --- a/tests/ui/let_and_return.edition2024.fixed +++ b/tests/ui/let_and_return.edition2024.fixed @@ -271,4 +271,12 @@ fn issue15987() -> i32 { r } +fn has_comment() -> Vec { + let v = Vec::new(); + + // TODO: stuff + + v +} + fn main() {} diff --git a/tests/ui/let_and_return.rs b/tests/ui/let_and_return.rs index 1af5f8ba5c165..301f153ca8b10 100644 --- a/tests/ui/let_and_return.rs +++ b/tests/ui/let_and_return.rs @@ -271,4 +271,12 @@ fn issue15987() -> i32 { r } +fn has_comment() -> Vec { + let v = Vec::new(); + + // TODO: stuff + + v +} + fn main() {} diff --git a/tests/ui/manual_dangling_ptr.fixed b/tests/ui/manual_dangling_ptr.fixed index b6afe7898906c..c6a6379194c96 100644 --- a/tests/ui/manual_dangling_ptr.fixed +++ b/tests/ui/manual_dangling_ptr.fixed @@ -1,3 +1,4 @@ +#![feature(extern_types)] #![warn(clippy::manual_dangling_ptr)] use std::mem; @@ -42,3 +43,14 @@ fn _msrv_1_84() { //~^ manual_dangling_ptr //~| manual_dangling_ptr } + +fn issue16459() { + unsafe extern "C" { + type Extern; + } + let _ = unsafe { &mut *(1 as *mut Extern) }; + + struct Empty; + let _ = unsafe { &mut *std::ptr::dangling_mut::() }; + //~^ manual_dangling_ptr +} diff --git a/tests/ui/manual_dangling_ptr.rs b/tests/ui/manual_dangling_ptr.rs index 581ad50113e28..338003fa41fdb 100644 --- a/tests/ui/manual_dangling_ptr.rs +++ b/tests/ui/manual_dangling_ptr.rs @@ -1,3 +1,4 @@ +#![feature(extern_types)] #![warn(clippy::manual_dangling_ptr)] use std::mem; @@ -42,3 +43,14 @@ fn _msrv_1_84() { //~^ manual_dangling_ptr //~| manual_dangling_ptr } + +fn issue16459() { + unsafe extern "C" { + type Extern; + } + let _ = unsafe { &mut *(1 as *mut Extern) }; + + struct Empty; + let _ = unsafe { &mut *(1 as *mut Empty) }; + //~^ manual_dangling_ptr +} diff --git a/tests/ui/manual_dangling_ptr.stderr b/tests/ui/manual_dangling_ptr.stderr index e3bc9b16b0d93..7e1533f7a30d4 100644 --- a/tests/ui/manual_dangling_ptr.stderr +++ b/tests/ui/manual_dangling_ptr.stderr @@ -1,5 +1,5 @@ error: manual creation of a dangling pointer - --> tests/ui/manual_dangling_ptr.rs:7:24 + --> tests/ui/manual_dangling_ptr.rs:8:24 | LL | let _: *const u8 = 1 as *const _; | ^^^^^^^^^^^^^ help: use: `std::ptr::dangling()` @@ -8,58 +8,64 @@ LL | let _: *const u8 = 1 as *const _; = help: to override `-D warnings` add `#[allow(clippy::manual_dangling_ptr)]` error: manual creation of a dangling pointer - --> tests/ui/manual_dangling_ptr.rs:9:13 + --> tests/ui/manual_dangling_ptr.rs:10:13 | LL | let _ = 2 as *const u32; | ^^^^^^^^^^^^^^^ help: use: `std::ptr::dangling::()` error: manual creation of a dangling pointer - --> tests/ui/manual_dangling_ptr.rs:11:13 + --> tests/ui/manual_dangling_ptr.rs:12:13 | LL | let _ = 4 as *mut f32; | ^^^^^^^^^^^^^ help: use: `std::ptr::dangling_mut::()` error: manual creation of a dangling pointer - --> tests/ui/manual_dangling_ptr.rs:14:13 + --> tests/ui/manual_dangling_ptr.rs:15:13 | LL | let _ = mem::align_of::() as *const u8; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `std::ptr::dangling::()` error: manual creation of a dangling pointer - --> tests/ui/manual_dangling_ptr.rs:16:13 + --> tests/ui/manual_dangling_ptr.rs:17:13 | LL | let _ = mem::align_of::() as *const u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `std::ptr::dangling::()` error: manual creation of a dangling pointer - --> tests/ui/manual_dangling_ptr.rs:18:13 + --> tests/ui/manual_dangling_ptr.rs:19:13 | LL | let _ = mem::align_of::() as *const usize; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `std::ptr::dangling::()` error: manual creation of a dangling pointer - --> tests/ui/manual_dangling_ptr.rs:21:9 + --> tests/ui/manual_dangling_ptr.rs:22:9 | LL | foo(4 as *const _, 4 as *mut _); | ^^^^^^^^^^^^^ help: use: `std::ptr::dangling()` error: manual creation of a dangling pointer - --> tests/ui/manual_dangling_ptr.rs:21:24 + --> tests/ui/manual_dangling_ptr.rs:22:24 | LL | foo(4 as *const _, 4 as *mut _); | ^^^^^^^^^^^ help: use: `std::ptr::dangling_mut()` error: manual creation of a dangling pointer - --> tests/ui/manual_dangling_ptr.rs:41:9 + --> tests/ui/manual_dangling_ptr.rs:42:9 | LL | foo(4 as *const _, 4 as *mut _); | ^^^^^^^^^^^^^ help: use: `std::ptr::dangling()` error: manual creation of a dangling pointer - --> tests/ui/manual_dangling_ptr.rs:41:24 + --> tests/ui/manual_dangling_ptr.rs:42:24 | LL | foo(4 as *const _, 4 as *mut _); | ^^^^^^^^^^^ help: use: `std::ptr::dangling_mut()` -error: aborting due to 10 previous errors +error: manual creation of a dangling pointer + --> tests/ui/manual_dangling_ptr.rs:54:28 + | +LL | let _ = unsafe { &mut *(1 as *mut Empty) }; + | ^^^^^^^^^^^^^^^^^ help: use: `std::ptr::dangling_mut::()` + +error: aborting due to 11 previous errors diff --git a/tests/ui/manual_is_variant_and.fixed b/tests/ui/manual_is_variant_and.fixed index 65a9cfa6e64c2..884bef6af5f90 100644 --- a/tests/ui/manual_is_variant_and.fixed +++ b/tests/ui/manual_is_variant_and.fixed @@ -226,3 +226,22 @@ mod with_func { assert_eq!(a1, a2); } } + +fn issue16419() { + let then_fn = |s: &str| s.len() > 3; + let opt: Option<&str> = Some("test"); + let _ = opt.is_none_or(then_fn); + //~^ manual_is_variant_and + + let _ = opt.is_none_or(then_fn); + //~^ manual_is_variant_and +} + +#[clippy::msrv = "1.75.0"] +fn issue16419_msrv() { + let then_fn = |s: &str| s.len() > 3; + let opt: Option<&str> = Some("test"); + let _ = opt.is_none() || opt.is_some_and(then_fn); + + let _ = opt.is_some_and(then_fn) || opt.is_none(); +} diff --git a/tests/ui/manual_is_variant_and.rs b/tests/ui/manual_is_variant_and.rs index 85b45d654a7d0..53aca94ea3708 100644 --- a/tests/ui/manual_is_variant_and.rs +++ b/tests/ui/manual_is_variant_and.rs @@ -235,3 +235,22 @@ mod with_func { assert_eq!(a1, a2); } } + +fn issue16419() { + let then_fn = |s: &str| s.len() > 3; + let opt: Option<&str> = Some("test"); + let _ = opt.is_none() || opt.is_some_and(then_fn); + //~^ manual_is_variant_and + + let _ = opt.is_some_and(then_fn) || opt.is_none(); + //~^ manual_is_variant_and +} + +#[clippy::msrv = "1.75.0"] +fn issue16419_msrv() { + let then_fn = |s: &str| s.len() > 3; + let opt: Option<&str> = Some("test"); + let _ = opt.is_none() || opt.is_some_and(then_fn); + + let _ = opt.is_some_and(then_fn) || opt.is_none(); +} diff --git a/tests/ui/manual_is_variant_and.stderr b/tests/ui/manual_is_variant_and.stderr index da36b5a07d210..56c3b80c0aa5c 100644 --- a/tests/ui/manual_is_variant_and.stderr +++ b/tests/ui/manual_is_variant_and.stderr @@ -222,5 +222,17 @@ error: called `.map() != Ok()` LL | let a1 = b.map(iad) != Ok(false); | ^^^^^^^^^^^^^^^^^^^^^^^ help: use: `!b.is_ok_and(|x| !iad(x))` -error: aborting due to 31 previous errors +error: manual implementation of `Option::is_none_or` + --> tests/ui/manual_is_variant_and.rs:242:13 + | +LL | let _ = opt.is_none() || opt.is_some_and(then_fn); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `opt.is_none_or(then_fn)` + +error: manual implementation of `Option::is_none_or` + --> tests/ui/manual_is_variant_and.rs:245:13 + | +LL | let _ = opt.is_some_and(then_fn) || opt.is_none(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `opt.is_none_or(then_fn)` + +error: aborting due to 33 previous errors diff --git a/tests/ui/manual_let_else_match.fixed b/tests/ui/manual_let_else_match.fixed index 15f604aec2928..9cdf394e9ee47 100644 --- a/tests/ui/manual_let_else_match.fixed +++ b/tests/ui/manual_let_else_match.fixed @@ -1,4 +1,4 @@ -#![allow(unused_braces, unused_variables, dead_code)] +#![allow(unused_braces, unused_variables, dead_code, irrefutable_let_patterns)] #![allow( clippy::collapsible_else_if, clippy::let_unit_value, @@ -182,3 +182,16 @@ fn issue9939b() { let Some(Issue9939b { earthquake: erosion, hurricane: _x }) = issue else { unreachable!("can't happen") }; assert!(erosion); } + +mod issue16433 { + // https://github.com/rust-lang/rust-clippy/issues/16433 + struct A { + a: u32, + b: u32, + } + + fn foo() { + let a = A { a: 1, b: 1 }; + let A { a: first_arg, .. } = a else { return }; + } +} diff --git a/tests/ui/manual_let_else_match.rs b/tests/ui/manual_let_else_match.rs index 44a044b142bd8..b1a20551e5842 100644 --- a/tests/ui/manual_let_else_match.rs +++ b/tests/ui/manual_let_else_match.rs @@ -1,4 +1,4 @@ -#![allow(unused_braces, unused_variables, dead_code)] +#![allow(unused_braces, unused_variables, dead_code, irrefutable_let_patterns)] #![allow( clippy::collapsible_else_if, clippy::let_unit_value, @@ -250,3 +250,20 @@ fn issue9939b() { }; assert!(erosion); } + +mod issue16433 { + // https://github.com/rust-lang/rust-clippy/issues/16433 + struct A { + a: u32, + b: u32, + } + + fn foo() { + let a = A { a: 1, b: 1 }; + let first_arg = match a { + //~^ manual_let_else + A { a, .. } => a, + _ => return, + }; + } +} diff --git a/tests/ui/manual_let_else_match.stderr b/tests/ui/manual_let_else_match.stderr index ed6117ebffb7d..6bbfb0e84d952 100644 --- a/tests/ui/manual_let_else_match.stderr +++ b/tests/ui/manual_let_else_match.stderr @@ -171,5 +171,15 @@ LL | | None => unreachable!("can't happen"), LL | | }; | |______^ help: consider writing: `let Some(Issue9939b { earthquake: erosion, hurricane: _x }) = issue else { unreachable!("can't happen") };` -error: aborting due to 17 previous errors +error: this could be rewritten as `let...else` + --> tests/ui/manual_let_else_match.rs:263:9 + | +LL | / let first_arg = match a { +LL | | +LL | | A { a, .. } => a, +LL | | _ => return, +LL | | }; + | |__________^ help: consider writing: `let A { a: first_arg, .. } = a else { return };` + +error: aborting due to 18 previous errors diff --git a/tests/ui/must_use_candidates.fixed b/tests/ui/must_use_candidates.fixed index 1e8589cf39d6b..a53e6e9b85bef 100644 --- a/tests/ui/must_use_candidates.fixed +++ b/tests/ui/must_use_candidates.fixed @@ -104,6 +104,7 @@ pub extern "C" fn unmangled(i: bool) -> bool { !i } -fn main() { +pub fn main() -> std::process::ExitCode { assert_eq!(1, pure(1)); + std::process::ExitCode::SUCCESS } diff --git a/tests/ui/must_use_candidates.rs b/tests/ui/must_use_candidates.rs index 71d546718ae79..6593d6c68a134 100644 --- a/tests/ui/must_use_candidates.rs +++ b/tests/ui/must_use_candidates.rs @@ -99,6 +99,7 @@ pub extern "C" fn unmangled(i: bool) -> bool { !i } -fn main() { +pub fn main() -> std::process::ExitCode { assert_eq!(1, pure(1)); + std::process::ExitCode::SUCCESS } diff --git a/tests/ui/never_loop.rs b/tests/ui/never_loop.rs index 9769ee0c3a37d..52470c6ee81bb 100644 --- a/tests/ui/never_loop.rs +++ b/tests/ui/never_loop.rs @@ -546,3 +546,13 @@ fn issue15673() { return; } } + +#[expect(clippy::diverging_sub_expression, clippy::short_circuit_statement)] +fn issue16462() { + let mut n = 10; + loop { + println!("{n}"); + n -= 1; + n >= 0 || break; + } +} diff --git a/tests/ui/question_mark.fixed b/tests/ui/question_mark.fixed index b8072932c4ea7..102517d34c612 100644 --- a/tests/ui/question_mark.fixed +++ b/tests/ui/question_mark.fixed @@ -515,3 +515,12 @@ fn wrongly_unmangled_macros() -> Option { test_expr!(42)?; test_expr!(42) } + +fn issue16429(b: i32) -> Option { + let a = Some(5); + let _ = if b == 1 { + b + } else { a? }; + + Some(0) +} diff --git a/tests/ui/question_mark.rs b/tests/ui/question_mark.rs index b320dcd4b0bca..cfea1277fe767 100644 --- a/tests/ui/question_mark.rs +++ b/tests/ui/question_mark.rs @@ -635,3 +635,17 @@ fn wrongly_unmangled_macros() -> Option { } test_expr!(42) } + +fn issue16429(b: i32) -> Option { + let a = Some(5); + let _ = if b == 1 { + b + } else if let Some(x) = a { + //~^ question_mark + x + } else { + return None; + }; + + Some(0) +} diff --git a/tests/ui/question_mark.stderr b/tests/ui/question_mark.stderr index d645c8830adcf..c243f12de040b 100644 --- a/tests/ui/question_mark.stderr +++ b/tests/ui/question_mark.stderr @@ -350,5 +350,17 @@ LL | | return None; LL | | } | |_____^ help: replace it with: `test_expr!(42)?;` -error: aborting due to 37 previous errors +error: this block may be rewritten with the `?` operator + --> tests/ui/question_mark.rs:643:12 + | +LL | } else if let Some(x) = a { + | ____________^ +LL | | +LL | | x +LL | | } else { +LL | | return None; +LL | | }; + | |_____^ help: replace it with: `{ a? }` + +error: aborting due to 38 previous errors diff --git a/tests/ui/str_split.fixed b/tests/ui/str_split.fixed index 6aca5051c5701..c93d4d4f818d1 100644 --- a/tests/ui/str_split.fixed +++ b/tests/ui/str_split.fixed @@ -1,7 +1,5 @@ #![warn(clippy::str_split_at_newline)] -#![allow(clippy::needless_lifetimes)] -use core::str::Split; use std::ops::Deref; struct NotStr<'a> { diff --git a/tests/ui/str_split.rs b/tests/ui/str_split.rs index 11e9862da14ba..5792ce04ec33c 100644 --- a/tests/ui/str_split.rs +++ b/tests/ui/str_split.rs @@ -1,7 +1,5 @@ #![warn(clippy::str_split_at_newline)] -#![allow(clippy::needless_lifetimes)] -use core::str::Split; use std::ops::Deref; struct NotStr<'a> { diff --git a/tests/ui/str_split.stderr b/tests/ui/str_split.stderr index c4eca81004c58..1ab755008e56b 100644 --- a/tests/ui/str_split.stderr +++ b/tests/ui/str_split.stderr @@ -1,65 +1,124 @@ error: using `str.trim().split()` with hard-coded newlines - --> tests/ui/str_split.rs:60:13 + --> tests/ui/str_split.rs:58:13 | LL | let _ = s1.trim().split('\n'); - | ^^^^^^^^^^^^^^^^^^^^^ help: use `str.lines()` instead: `s1.lines()` + | ^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::str-split-at-newline` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::str_split_at_newline)]` +help: use `str.lines()` instead + | +LL - let _ = s1.trim().split('\n'); +LL + let _ = s1.lines(); + | error: using `str.trim().split()` with hard-coded newlines - --> tests/ui/str_split.rs:63:13 + --> tests/ui/str_split.rs:61:13 | LL | let _ = s1.trim().split("\n"); - | ^^^^^^^^^^^^^^^^^^^^^ help: use `str.lines()` instead: `s1.lines()` + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: use `str.lines()` instead + | +LL - let _ = s1.trim().split("\n"); +LL + let _ = s1.lines(); + | error: using `str.trim().split()` with hard-coded newlines - --> tests/ui/str_split.rs:65:13 + --> tests/ui/str_split.rs:63:13 | LL | let _ = s1.trim().split("\r\n"); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `str.lines()` instead: `s1.lines()` + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `str.lines()` instead + | +LL - let _ = s1.trim().split("\r\n"); +LL + let _ = s1.lines(); + | error: using `str.trim().split()` with hard-coded newlines - --> tests/ui/str_split.rs:69:13 + --> tests/ui/str_split.rs:67:13 | LL | let _ = s2.trim().split('\n'); - | ^^^^^^^^^^^^^^^^^^^^^ help: use `str.lines()` instead: `s2.lines()` + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: use `str.lines()` instead + | +LL - let _ = s2.trim().split('\n'); +LL + let _ = s2.lines(); + | error: using `str.trim().split()` with hard-coded newlines - --> tests/ui/str_split.rs:72:13 + --> tests/ui/str_split.rs:70:13 | LL | let _ = s2.trim().split("\n"); - | ^^^^^^^^^^^^^^^^^^^^^ help: use `str.lines()` instead: `s2.lines()` + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: use `str.lines()` instead + | +LL - let _ = s2.trim().split("\n"); +LL + let _ = s2.lines(); + | error: using `str.trim().split()` with hard-coded newlines - --> tests/ui/str_split.rs:74:13 + --> tests/ui/str_split.rs:72:13 | LL | let _ = s2.trim().split("\r\n"); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `str.lines()` instead: `s2.lines()` + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `str.lines()` instead + | +LL - let _ = s2.trim().split("\r\n"); +LL + let _ = s2.lines(); + | error: using `str.trim().split()` with hard-coded newlines - --> tests/ui/str_split.rs:79:13 + --> tests/ui/str_split.rs:77:13 | LL | let _ = s3.trim().split('\n'); - | ^^^^^^^^^^^^^^^^^^^^^ help: use `str.lines()` instead: `s3.lines()` + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: use `str.lines()` instead + | +LL - let _ = s3.trim().split('\n'); +LL + let _ = s3.lines(); + | error: using `str.trim().split()` with hard-coded newlines - --> tests/ui/str_split.rs:82:13 + --> tests/ui/str_split.rs:80:13 | LL | let _ = s3.trim().split("\n"); - | ^^^^^^^^^^^^^^^^^^^^^ help: use `str.lines()` instead: `s3.lines()` + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: use `str.lines()` instead + | +LL - let _ = s3.trim().split("\n"); +LL + let _ = s3.lines(); + | error: using `str.trim().split()` with hard-coded newlines - --> tests/ui/str_split.rs:84:13 + --> tests/ui/str_split.rs:82:13 | LL | let _ = s3.trim().split("\r\n"); - | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `str.lines()` instead: `s3.lines()` + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `str.lines()` instead + | +LL - let _ = s3.trim().split("\r\n"); +LL + let _ = s3.lines(); + | error: using `str.trim().split()` with hard-coded newlines - --> tests/ui/str_split.rs:88:13 + --> tests/ui/str_split.rs:86:13 | LL | let _ = make_str!(s1).trim().split('\n'); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `str.lines()` instead: `make_str!(s1).lines()` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `str.lines()` instead + | +LL - let _ = make_str!(s1).trim().split('\n'); +LL + let _ = make_str!(s1).lines(); + | error: aborting due to 10 previous errors diff --git a/tests/ui/str_to_string.fixed b/tests/ui/str_to_string.fixed index 8713c4f9bc863..5b76cf78f069d 100644 --- a/tests/ui/str_to_string.fixed +++ b/tests/ui/str_to_string.fixed @@ -22,3 +22,32 @@ fn issue16271(key: &[u8]) { let _value = t!(str::from_utf8(key)).to_owned(); //~^ str_to_string } + +struct GenericWrapper(T); + +impl GenericWrapper { + fn mapper U>(self, f: F) -> U { + f(self.0) + } +} + +fn issue16511(x: Option<&str>) { + let _ = x.map(ToOwned::to_owned); + //~^ str_to_string + + let _ = x.map(ToOwned::to_owned); + //~^ str_to_string + + let _ = ["a", "b"].iter().map(ToOwned::to_owned); + //~^ str_to_string + + fn mapper String>(f: F) -> String { + f("hello") + } + let _ = mapper(ToOwned::to_owned); + //~^ str_to_string + + let w = GenericWrapper("hello"); + let _ = w.mapper(ToOwned::to_owned); + //~^ str_to_string +} diff --git a/tests/ui/str_to_string.rs b/tests/ui/str_to_string.rs index b81759e1037b2..f099eb29b1b5f 100644 --- a/tests/ui/str_to_string.rs +++ b/tests/ui/str_to_string.rs @@ -22,3 +22,32 @@ fn issue16271(key: &[u8]) { let _value = t!(str::from_utf8(key)).to_string(); //~^ str_to_string } + +struct GenericWrapper(T); + +impl GenericWrapper { + fn mapper U>(self, f: F) -> U { + f(self.0) + } +} + +fn issue16511(x: Option<&str>) { + let _ = x.map(ToString::to_string); + //~^ str_to_string + + let _ = x.map(str::to_string); + //~^ str_to_string + + let _ = ["a", "b"].iter().map(ToString::to_string); + //~^ str_to_string + + fn mapper String>(f: F) -> String { + f("hello") + } + let _ = mapper(ToString::to_string); + //~^ str_to_string + + let w = GenericWrapper("hello"); + let _ = w.mapper(ToString::to_string); + //~^ str_to_string +} diff --git a/tests/ui/str_to_string.stderr b/tests/ui/str_to_string.stderr index c0a38c8ebe461..296b8e36f28c8 100644 --- a/tests/ui/str_to_string.stderr +++ b/tests/ui/str_to_string.stderr @@ -19,5 +19,35 @@ error: `to_string()` called on a `&str` LL | let _value = t!(str::from_utf8(key)).to_string(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t!(str::from_utf8(key)).to_owned()` -error: aborting due to 3 previous errors +error: `ToString::to_string` used as `&str` to `String` converter + --> tests/ui/str_to_string.rs:35:19 + | +LL | let _ = x.map(ToString::to_string); + | ^^^^^^^^^^^^^^^^^^^ help: try: `ToOwned::to_owned` + +error: `ToString::to_string` used as `&str` to `String` converter + --> tests/ui/str_to_string.rs:38:19 + | +LL | let _ = x.map(str::to_string); + | ^^^^^^^^^^^^^^ help: try: `ToOwned::to_owned` + +error: `ToString::to_string` used as `&str` to `String` converter + --> tests/ui/str_to_string.rs:41:35 + | +LL | let _ = ["a", "b"].iter().map(ToString::to_string); + | ^^^^^^^^^^^^^^^^^^^ help: try: `ToOwned::to_owned` + +error: `ToString::to_string` used as `&str` to `String` converter + --> tests/ui/str_to_string.rs:47:20 + | +LL | let _ = mapper(ToString::to_string); + | ^^^^^^^^^^^^^^^^^^^ help: try: `ToOwned::to_owned` + +error: `ToString::to_string` used as `&str` to `String` converter + --> tests/ui/str_to_string.rs:51:22 + | +LL | let _ = w.mapper(ToString::to_string); + | ^^^^^^^^^^^^^^^^^^^ help: try: `ToOwned::to_owned` + +error: aborting due to 8 previous errors diff --git a/tests/ui/test_attr_in_doctest.rs b/tests/ui/test_attr_in_doctest.rs index 7d1a09024895b..ee48cebc5e0cf 100644 --- a/tests/ui/test_attr_in_doctest.rs +++ b/tests/ui/test_attr_in_doctest.rs @@ -46,3 +46,11 @@ /// fn not_even_rust() { panic!("Ouch") } /// ``` fn test_attr_in_doctests() {} + +/// ```test_harness +/// #[test] +/// fn foo() { +/// panic!(); +/// } +/// ``` +pub fn issue16447() {} diff --git a/tests/ui/trait_duplication_in_bounds_assoc_const_eq.fixed b/tests/ui/trait_duplication_in_bounds_assoc_const_eq.fixed index 8d63fc44e7f83..96cc654fd090d 100644 --- a/tests/ui/trait_duplication_in_bounds_assoc_const_eq.fixed +++ b/tests/ui/trait_duplication_in_bounds_assoc_const_eq.fixed @@ -3,7 +3,6 @@ #![feature(min_generic_const_args)] trait AssocConstTrait { - type const ASSOC: usize; } fn assoc_const_args() diff --git a/tests/ui/trait_duplication_in_bounds_assoc_const_eq.rs b/tests/ui/trait_duplication_in_bounds_assoc_const_eq.rs index 36a83619c0f94..b81dd673bb694 100644 --- a/tests/ui/trait_duplication_in_bounds_assoc_const_eq.rs +++ b/tests/ui/trait_duplication_in_bounds_assoc_const_eq.rs @@ -3,7 +3,6 @@ #![feature(min_generic_const_args)] trait AssocConstTrait { - type const ASSOC: usize; } fn assoc_const_args() diff --git a/tests/ui/trait_duplication_in_bounds_assoc_const_eq.stderr b/tests/ui/trait_duplication_in_bounds_assoc_const_eq.stderr index accc4d7b5bb8a..4053959aff61b 100644 --- a/tests/ui/trait_duplication_in_bounds_assoc_const_eq.stderr +++ b/tests/ui/trait_duplication_in_bounds_assoc_const_eq.stderr @@ -1,5 +1,5 @@ error: these where clauses contain repeated elements - --> tests/ui/trait_duplication_in_bounds_assoc_const_eq.rs:11:8 + --> tests/ui/trait_duplication_in_bounds_assoc_const_eq.rs:10:8 | LL | T: AssocConstTrait + AssocConstTrait, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `AssocConstTrait` diff --git a/tests/ui/unconditional_recursion.rs b/tests/ui/unconditional_recursion.rs index d9f4c07dc9025..e4dd33a8eeea7 100644 --- a/tests/ui/unconditional_recursion.rs +++ b/tests/ui/unconditional_recursion.rs @@ -334,7 +334,7 @@ mod issue12154 { } // Not necessarily related to the issue but another FP from the http crate that was fixed with it: - // https://docs.rs/http/latest/src/http/header/name.rs.html#1424 + // https://github.com/hyperium/http/blob/5f0c86642f1dc86f156da82b62aceb2f4fab20e1/src/header/name.rs#L1408-L1420 // We used to simply peel refs from the LHS and RHS, so we couldn't differentiate // between `PartialEq for &T` and `PartialEq<&T> for T` impls. #[derive(PartialEq)] diff --git a/tests/ui/unwrap.rs b/tests/ui/unwrap.rs deleted file mode 100644 index 3191b396f99bc..0000000000000 --- a/tests/ui/unwrap.rs +++ /dev/null @@ -1,22 +0,0 @@ -#![warn(clippy::unwrap_used)] -#![allow(clippy::unnecessary_literal_unwrap)] - -fn unwrap_option() { - let opt = Some(0); - let _ = opt.unwrap(); - //~^ unwrap_used -} - -fn unwrap_result() { - let res: Result = Ok(0); - let _ = res.unwrap(); - //~^ unwrap_used - - let _ = res.unwrap_err(); - //~^ unwrap_used -} - -fn main() { - unwrap_option(); - unwrap_result(); -} diff --git a/tests/ui/unwrap.stderr b/tests/ui/unwrap.stderr deleted file mode 100644 index c242541a6bd73..0000000000000 --- a/tests/ui/unwrap.stderr +++ /dev/null @@ -1,31 +0,0 @@ -error: used `unwrap()` on an `Option` value - --> tests/ui/unwrap.rs:6:13 - | -LL | let _ = opt.unwrap(); - | ^^^^^^^^^^^^ - | - = note: if this value is `None`, it will panic - = help: consider using `expect()` to provide a better panic message - = note: `-D clippy::unwrap-used` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::unwrap_used)]` - -error: used `unwrap()` on a `Result` value - --> tests/ui/unwrap.rs:12:13 - | -LL | let _ = res.unwrap(); - | ^^^^^^^^^^^^ - | - = note: if this value is an `Err`, it will panic - = help: consider using `expect()` to provide a better panic message - -error: used `unwrap_err()` on a `Result` value - --> tests/ui/unwrap.rs:15:13 - | -LL | let _ = res.unwrap_err(); - | ^^^^^^^^^^^^^^^^ - | - = note: if this value is an `Ok`, it will panic - = help: consider using `expect_err()` to provide a better panic message - -error: aborting due to 3 previous errors - diff --git a/tests/ui/unwrap_expect_used.rs b/tests/ui/unwrap_expect_used.rs index b429f3a8a0bb9..207d4dd815be3 100644 --- a/tests/ui/unwrap_expect_used.rs +++ b/tests/ui/unwrap_expect_used.rs @@ -83,3 +83,15 @@ mod with_expansion { let _ = open!(file).expect_err("can open"); //~ expect_used } } + +fn issue16484() { + let opt = Some(()); + Option::unwrap(opt); //~ unwrap_used + Option::expect(opt, "error message"); //~ expect_used + + let res: Result<(), i32> = Ok(()); + Result::unwrap(res); //~ unwrap_used + Result::expect(res, "error message"); //~ expect_used + Result::unwrap_err(res); //~ unwrap_used + Result::expect_err(res, "error message"); //~ expect_used +} diff --git a/tests/ui/unwrap_expect_used.stderr b/tests/ui/unwrap_expect_used.stderr index 6fd1b84d81231..b9a2844b284c8 100644 --- a/tests/ui/unwrap_expect_used.stderr +++ b/tests/ui/unwrap_expect_used.stderr @@ -82,5 +82,53 @@ LL | let _ = open!(file).expect_err("can open"); | = note: if this value is an `Ok`, it will panic -error: aborting due to 10 previous errors +error: used `unwrap()` on an `Option` value + --> tests/ui/unwrap_expect_used.rs:89:5 + | +LL | Option::unwrap(opt); + | ^^^^^^^^^^^^^^^^^^^ + | + = note: if this value is `None`, it will panic + +error: used `expect()` on an `Option` value + --> tests/ui/unwrap_expect_used.rs:90:5 + | +LL | Option::expect(opt, "error message"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: if this value is `None`, it will panic + +error: used `unwrap()` on a `Result` value + --> tests/ui/unwrap_expect_used.rs:93:5 + | +LL | Result::unwrap(res); + | ^^^^^^^^^^^^^^^^^^^ + | + = note: if this value is an `Err`, it will panic + +error: used `expect()` on a `Result` value + --> tests/ui/unwrap_expect_used.rs:94:5 + | +LL | Result::expect(res, "error message"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: if this value is an `Err`, it will panic + +error: used `unwrap_err()` on a `Result` value + --> tests/ui/unwrap_expect_used.rs:95:5 + | +LL | Result::unwrap_err(res); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: if this value is an `Ok`, it will panic + +error: used `expect_err()` on a `Result` value + --> tests/ui/unwrap_expect_used.rs:96:5 + | +LL | Result::expect_err(res, "error message"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: if this value is an `Ok`, it will panic + +error: aborting due to 16 previous errors diff --git a/tests/ui/useless_attribute.fixed b/tests/ui/useless_attribute.fixed index e0bc23e0788c6..dbe4b6fb50f3a 100644 --- a/tests/ui/useless_attribute.fixed +++ b/tests/ui/useless_attribute.fixed @@ -42,6 +42,10 @@ mod foo { #[allow(deprecated)] pub use foo::Bar; +// don't lint on exported_private_dependencies for `use` items +#[allow(exported_private_dependencies)] +use {}; + // This should not trigger the lint. There's lint level definitions inside the external derive // that would trigger the useless_attribute lint. #[derive(DeriveSomething)] diff --git a/tests/ui/useless_attribute.rs b/tests/ui/useless_attribute.rs index 30a4c354b238a..44fb6733f054d 100644 --- a/tests/ui/useless_attribute.rs +++ b/tests/ui/useless_attribute.rs @@ -42,6 +42,10 @@ mod foo { #[allow(deprecated)] pub use foo::Bar; +// don't lint on exported_private_dependencies for `use` items +#[allow(exported_private_dependencies)] +use {}; + // This should not trigger the lint. There's lint level definitions inside the external derive // that would trigger the useless_attribute lint. #[derive(DeriveSomething)] diff --git a/triagebot.toml b/triagebot.toml index 09dec7675e7e5..eb8442ab21e2f 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -55,6 +55,11 @@ labels = ["S-waiting-on-concerns"] # Amend a review to include a link to what was changed since the review [review-changes-since] +# Adds a "View all comments" link on the issue/PR body that shows all the comments of it +# Documentation at: https://forge.rust-lang.org/triagebot/view-all-comments-link.html +[view-all-comments-link] +threshold = 20 + [assign] contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md" users_on_vacation = [ From 2655d8dd4b760ade9aab8e1dade3e78e9ccf19b6 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 16 Oct 2025 01:31:12 -0400 Subject: [PATCH 20/57] `clippy_dev`: Stores parsed lints in a map indexed by name. --- clippy_dev/src/edit_lints.rs | 221 ++++++++++++++--------------- clippy_dev/src/lib.rs | 1 + clippy_dev/src/parse.rs | 106 +++++++++----- clippy_dev/src/update_lints.rs | 134 +++++++++-------- clippy_lints/src/declared_lints.rs | 30 ++-- 5 files changed, 262 insertions(+), 230 deletions(-) diff --git a/clippy_dev/src/edit_lints.rs b/clippy_dev/src/edit_lints.rs index fb1c1458c50cd..35459a1bbd705 100644 --- a/clippy_dev/src/edit_lints.rs +++ b/clippy_dev/src/edit_lints.rs @@ -1,11 +1,13 @@ use crate::parse::cursor::{self, Capture, Cursor}; -use crate::parse::{DeprecatedLint, Lint, ParseCx, RenamedLint}; +use crate::parse::{ActiveLint, DeprecatedLint, Lint, LintData, ParseCx, RenamedLint}; use crate::update_lints::generate_lint_files; use crate::utils::{ ErrAction, FileUpdater, UpdateMode, UpdateStatus, Version, delete_dir_if_exists, delete_file_if_exists, expect_action, try_rename_dir, try_rename_file, walk_dir_no_dot_or_target, }; +use core::mem; use rustc_lexer::TokenKind; +use std::collections::hash_map::Entry; use std::ffi::OsString; use std::fs; use std::path::Path; @@ -20,74 +22,58 @@ use std::path::Path; /// /// If a file path could not read from or written to pub fn deprecate<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, name: &'env str, reason: &'env str) { - let mut lints = cx.find_lint_decls(); - let (mut deprecated_lints, renamed_lints) = cx.read_deprecated_lints(); + let mut data = cx.parse_lint_decls(); - let Some(lint_idx) = lints.iter().position(|l| l.name == name) else { + let Entry::Occupied(mut lint) = data.lints.entry(name) else { eprintln!("error: failed to find lint `{name}`"); return; }; + let Lint::Active(prev_lint) = mem::replace( + lint.get_mut(), + Lint::Deprecated(DeprecatedLint { + reason, + version: cx.str_buf.alloc_display(cx.arena, clippy_version.rust_display()), + }), + ) else { + eprintln!("error: `{name}` is already deprecated"); + return; + }; - let prefixed_name = cx.str_buf.with(|buf| { - buf.extend(["clippy::", name]); - cx.arena.alloc_str(buf) - }); - match deprecated_lints.binary_search_by(|x| x.name.cmp(prefixed_name)) { - Ok(_) => { - println!("`{name}` is already deprecated"); - return; - }, - Err(idx) => deprecated_lints.insert( - idx, - DeprecatedLint { - name: prefixed_name, - reason, - version: cx.str_buf.alloc_display(cx.arena, clippy_version.rust_display()), - }, - ), - } - - remove_lint_declaration(lint_idx, &mut lints, &mut FileUpdater::default()); - generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); + remove_lint_declaration(name, &prev_lint, &data, &mut FileUpdater::default()); + generate_lint_files(UpdateMode::Change, &data); println!("info: `{name}` has successfully been deprecated"); println!("note: you must run `cargo uitest` to update the test results"); } pub fn uplift<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_name: &'env str, new_name: &'env str) { - let mut lints = cx.find_lint_decls(); - let (deprecated_lints, mut renamed_lints) = cx.read_deprecated_lints(); + let mut data = cx.parse_lint_decls(); + + update_rename_targets( + &mut data, + cx.str_buf.with(|buf| { + buf.extend(["clippy::", old_name]); + cx.arena.alloc_str(buf) + }), + new_name, + ); - let Some(lint_idx) = lints.iter().position(|l| l.name == old_name) else { + let Entry::Occupied(mut lint) = data.lints.entry(old_name) else { eprintln!("error: failed to find lint `{old_name}`"); return; }; - - let old_name_prefixed = cx.str_buf.with(|buf| { - buf.extend(["clippy::", old_name]); - cx.arena.alloc_str(buf) - }); - for lint in &mut renamed_lints { - if lint.new_name == old_name_prefixed { - lint.new_name = new_name; - } - } - match renamed_lints.binary_search_by(|x| x.old_name.cmp(old_name_prefixed)) { - Ok(_) => { - println!("`{old_name}` is already deprecated"); - return; - }, - Err(idx) => renamed_lints.insert( - idx, - RenamedLint { - old_name: old_name_prefixed, - new_name, - version: cx.str_buf.alloc_display(cx.arena, clippy_version.rust_display()), - }, - ), - } + let Lint::Active(prev_lint) = mem::replace( + lint.get_mut(), + Lint::Renamed(RenamedLint { + new_name, + version: cx.str_buf.alloc_display(cx.arena, clippy_version.rust_display()), + }), + ) else { + eprintln!("error: `{old_name}` is already deprecated"); + return; + }; let mut updater = FileUpdater::default(); - let remove_mod = remove_lint_declaration(lint_idx, &mut lints, &mut updater); + let remove_mod = remove_lint_declaration(old_name, &prev_lint, &data, &mut updater); let mut update_fn = uplift_update_fn(old_name, new_name, remove_mod); for e in walk_dir_no_dot_or_target(".") { let e = expect_action(e, ErrAction::Read, "."); @@ -95,7 +81,7 @@ pub fn uplift<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_nam updater.update_file(e.path(), &mut update_fn); } } - generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); + generate_lint_files(UpdateMode::Change, &data); println!("info: `{old_name}` has successfully been uplifted as `{new_name}`"); println!("note: you must run `cargo uitest` to update the test results"); } @@ -117,77 +103,59 @@ pub fn uplift<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_nam /// * If `old_name` names a deprecated or renamed lint. pub fn rename<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_name: &'env str, new_name: &'env str) { let mut updater = FileUpdater::default(); - let mut lints = cx.find_lint_decls(); - let (deprecated_lints, mut renamed_lints) = cx.read_deprecated_lints(); + let mut data = cx.parse_lint_decls(); - let Ok(lint_idx) = lints.binary_search_by(|x| x.name.cmp(old_name)) else { - panic!("could not find lint `{old_name}`"); - }; - - let old_name_prefixed = cx.str_buf.with(|buf| { - buf.extend(["clippy::", old_name]); - cx.arena.alloc_str(buf) - }); let new_name_prefixed = cx.str_buf.with(|buf| { buf.extend(["clippy::", new_name]); cx.arena.alloc_str(buf) }); + update_rename_targets( + &mut data, + cx.str_buf.with(|buf| { + buf.extend(["clippy::", old_name]); + cx.arena.alloc_str(buf) + }), + new_name_prefixed, + ); - for lint in &mut renamed_lints { - if lint.new_name == old_name_prefixed { - lint.new_name = new_name_prefixed; - } - } - match renamed_lints.binary_search_by(|x| x.old_name.cmp(old_name_prefixed)) { - Ok(_) => { - println!("`{old_name}` already has a rename registered"); - return; - }, - Err(idx) => { - renamed_lints.insert( - idx, - RenamedLint { - old_name: old_name_prefixed, - new_name: new_name_prefixed, - version: cx.str_buf.alloc_display(cx.arena, clippy_version.rust_display()), - }, - ); - }, - } + let Entry::Occupied(mut lint) = data.lints.entry(old_name) else { + eprintln!("error: failed to find lint `{old_name}`"); + return; + }; + let Lint::Active(mut prev_lint) = mem::replace( + lint.get_mut(), + Lint::Renamed(RenamedLint { + new_name: new_name_prefixed, + version: cx.str_buf.alloc_display(cx.arena, clippy_version.rust_display()), + }), + ) else { + eprintln!("error: `{old_name}` is already deprecated"); + return; + }; let mut rename_mod = false; - if lints.binary_search_by(|x| x.name.cmp(new_name)).is_err() { - let lint = &mut lints[lint_idx]; - if lint.module.ends_with(old_name) - && lint + if let Entry::Vacant(e) = data.lints.entry(new_name) { + if prev_lint.module.ends_with(old_name) + && prev_lint .path .file_stem() .is_some_and(|x| x.as_encoded_bytes() == old_name.as_bytes()) { - let mut new_path = lint.path.with_file_name(new_name).into_os_string(); + let mut new_path = prev_lint.path.with_file_name(new_name).into_os_string(); new_path.push(".rs"); - if try_rename_file(lint.path.as_ref(), new_path.as_ref()) { + if try_rename_file(prev_lint.path.as_ref(), new_path.as_ref()) { rename_mod = true; } - lint.module = cx.str_buf.with(|buf| { - buf.push_str(&lint.module[..lint.module.len() - old_name.len()]); + prev_lint.module = cx.str_buf.with(|buf| { + buf.push_str(&prev_lint.module[..prev_lint.module.len() - old_name.len()]); buf.push_str(new_name); cx.arena.alloc_str(buf) }); } + e.insert(Lint::Active(prev_lint)); - rename_test_files( - old_name, - new_name, - &lints[lint_idx + 1..] - .iter() - .map(|l| l.name) - .take_while(|&n| n.starts_with(old_name)) - .collect::>(), - ); - lints[lint_idx].name = new_name; - lints.sort_by(|lhs, rhs| lhs.name.cmp(rhs.name)); + rename_test_files(old_name, new_name, &create_ignored_prefixes(old_name, &data)); } else { println!("Renamed `clippy::{old_name}` to `clippy::{new_name}`"); println!("Since `{new_name}` already exists the existing code has not been changed"); @@ -201,7 +169,7 @@ pub fn rename<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_nam updater.update_file(e.path(), &mut update_fn); } } - generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); + generate_lint_files(UpdateMode::Change, &data); println!("Renamed `clippy::{old_name}` to `clippy::{new_name}`"); println!("All code referencing the old name has been updated"); @@ -211,9 +179,14 @@ pub fn rename<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_nam /// Removes a lint's declaration and test files. Returns whether the module containing the /// lint was deleted. -fn remove_lint_declaration(lint_idx: usize, lints: &mut Vec>, updater: &mut FileUpdater) -> bool { - let lint = lints.remove(lint_idx); - let delete_mod = if lints.iter().all(|l| l.module != lint.module) { +fn remove_lint_declaration(name: &str, lint: &ActiveLint<'_>, data: &LintData<'_>, updater: &mut FileUpdater) -> bool { + let delete_mod = if data.lints.iter().all(|(_, l)| { + if let Lint::Active(l) = l { + l.module != lint.module + } else { + true + } + }) { delete_file_if_exists(lint.path.as_ref()) } else { updater.update_file(&lint.path, &mut |_, src, dst| -> UpdateStatus { @@ -231,18 +204,34 @@ fn remove_lint_declaration(lint_idx: usize, lints: &mut Vec>, updater: }); false }; - delete_test_files( - lint.name, - &lints[lint_idx..] - .iter() - .map(|l| l.name) - .take_while(|&n| n.starts_with(lint.name)) - .collect::>(), - ); + delete_test_files(name, &create_ignored_prefixes(name, data)); delete_mod } +/// Updates all renames to the old name to be renames to the new name. +/// +/// This is needed because rustc doesn't allow a lint to be renamed to a lint that has +/// also been renamed. +fn update_rename_targets<'cx>(data: &mut LintData<'cx>, old_name: &str, new_name: &'cx str) { + for lint in data.lints.values_mut() { + if let Lint::Renamed(lint) = lint + && lint.new_name == old_name + { + lint.new_name = new_name; + } + } +} + +/// Creates a list of prefixes to ignore when +fn create_ignored_prefixes<'cx>(name: &str, data: &LintData<'cx>) -> Vec<&'cx str> { + data.lints + .keys() + .copied() + .filter(|&x| x.len() > name.len() && x.starts_with(name)) + .collect() +} + fn collect_ui_test_names(lint: &str, ignored_prefixes: &[&str], dst: &mut Vec<(OsString, bool)>) { for e in fs::read_dir("tests/ui").expect("error reading `tests/ui`") { let e = e.expect("error reading `tests/ui`"); diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs index 69309403c8d02..cff51d34c30e6 100644 --- a/clippy_dev/src/lib.rs +++ b/clippy_dev/src/lib.rs @@ -19,6 +19,7 @@ #![allow(clippy::missing_panics_doc)] extern crate rustc_arena; +extern crate rustc_data_structures; #[expect(unused_extern_crates, reason = "required to link to rustc crates")] extern crate rustc_driver; extern crate rustc_lexer; diff --git a/clippy_dev/src/parse.rs b/clippy_dev/src/parse.rs index de5caf4e1ef65..566eea336f0da 100644 --- a/clippy_dev/src/parse.rs +++ b/clippy_dev/src/parse.rs @@ -5,6 +5,7 @@ use crate::utils::{ErrAction, File, Scoped, expect_action, walk_dir_no_dot_or_ta use core::fmt::{Display, Write as _}; use core::range::Range; use rustc_arena::DroplessArena; +use rustc_data_structures::fx::FxHashMap; use std::fs; use std::path::{self, Path, PathBuf}; use std::str::pattern::Pattern; @@ -81,8 +82,7 @@ impl StrBuf { } } -pub struct Lint<'cx> { - pub name: &'cx str, +pub struct ActiveLint<'cx> { pub group: &'cx str, pub module: &'cx str, pub path: PathBuf, @@ -90,22 +90,34 @@ pub struct Lint<'cx> { } pub struct DeprecatedLint<'cx> { - pub name: &'cx str, pub reason: &'cx str, pub version: &'cx str, } pub struct RenamedLint<'cx> { - pub old_name: &'cx str, pub new_name: &'cx str, pub version: &'cx str, } +pub enum Lint<'cx> { + Active(ActiveLint<'cx>), + Deprecated(DeprecatedLint<'cx>), + Renamed(RenamedLint<'cx>), +} + +pub struct LintData<'cx> { + pub lints: FxHashMap<&'cx str, Lint<'cx>>, +} + impl<'cx> ParseCxImpl<'cx> { - /// Finds all lint declarations (`declare_clippy_lint!`) + /// Finds and parses all lint declarations. #[must_use] - pub fn find_lint_decls(&mut self) -> Vec> { - let mut lints = Vec::with_capacity(1000); + pub fn parse_lint_decls(&mut self) -> LintData<'cx> { + let mut data = LintData { + #[expect(clippy::default_trait_access)] + lints: FxHashMap::with_capacity_and_hasher(1000, Default::default()), + }; + let mut contents = String::new(); for e in expect_action(fs::read_dir("."), ErrAction::Read, ".") { let e = expect_action(e, ErrAction::Read, "."); @@ -143,17 +155,18 @@ impl<'cx> ParseCxImpl<'cx> { e.path(), File::open_read_to_cleared_string(e.path(), &mut contents), module, - &mut lints, + &mut data, ); } } } - lints.sort_by(|lhs, rhs| lhs.name.cmp(rhs.name)); - lints + + self.read_deprecated_lints(&mut data); + data } /// Parse a source file looking for `declare_clippy_lint` macro invocations. - fn parse_clippy_lint_decls(&mut self, path: &Path, contents: &str, module: &'cx str, lints: &mut Vec>) { + fn parse_clippy_lint_decls(&mut self, path: &Path, contents: &str, module: &'cx str, data: &mut LintData<'cx>) { #[allow(clippy::enum_glob_use)] use cursor::Pat::*; #[rustfmt::skip] @@ -170,19 +183,24 @@ impl<'cx> ParseCxImpl<'cx> { let mut captures = [Capture::EMPTY; 2]; while let Some(start) = cursor.find_ident("declare_clippy_lint") { if cursor.match_all(DECL_TOKENS, &mut captures) && cursor.find_pat(CloseBrace) { - lints.push(Lint { - name: self.str_buf.alloc_ascii_lower(self.arena, cursor.get_text(captures[0])), - group: self.arena.alloc_str(cursor.get_text(captures[1])), - module, - path: path.into(), - declaration_range: start as usize..cursor.pos() as usize, - }); + assert!( + data.lints + .insert( + self.str_buf.alloc_ascii_lower(self.arena, cursor.get_text(captures[0])), + Lint::Active(ActiveLint { + group: self.arena.alloc_str(cursor.get_text(captures[1])), + module, + path: path.into(), + declaration_range: start as usize..cursor.pos() as usize, + }), + ) + .is_none() + ); } } } - #[must_use] - pub fn read_deprecated_lints(&mut self) -> (Vec>, Vec>) { + fn read_deprecated_lints(&mut self, data: &mut LintData<'cx>) { #[allow(clippy::enum_glob_use)] use cursor::Pat::*; #[rustfmt::skip] @@ -204,8 +222,6 @@ impl<'cx> ParseCxImpl<'cx> { ]; let path = "clippy_lints/src/deprecated_lints.rs"; - let mut deprecated = Vec::with_capacity(30); - let mut renamed = Vec::with_capacity(80); let mut contents = String::new(); File::open_read_to_cleared_string(path, &mut contents); @@ -220,11 +236,17 @@ impl<'cx> ParseCxImpl<'cx> { if cursor.find_ident("declare_with_version").is_some() && cursor.match_all(DEPRECATED_TOKENS, &mut []) { while cursor.match_all(DECL_TOKENS, &mut captures) { - deprecated.push(DeprecatedLint { - name: self.parse_str_single_line(path.as_ref(), cursor.get_text(captures[1])), - reason: self.parse_str_single_line(path.as_ref(), cursor.get_text(captures[2])), - version: self.parse_str_single_line(path.as_ref(), cursor.get_text(captures[0])), - }); + assert!( + data.lints + .insert( + self.parse_clippy_lint_name(path.as_ref(), cursor.get_text(captures[1])), + Lint::Deprecated(DeprecatedLint { + reason: self.parse_str_single_line(path.as_ref(), cursor.get_text(captures[2])), + version: self.parse_str_single_line(path.as_ref(), cursor.get_text(captures[0])), + }), + ) + .is_none() + ); } } else { panic!("error reading deprecated lints"); @@ -232,19 +254,21 @@ impl<'cx> ParseCxImpl<'cx> { if cursor.find_ident("declare_with_version").is_some() && cursor.match_all(RENAMED_TOKENS, &mut []) { while cursor.match_all(DECL_TOKENS, &mut captures) { - renamed.push(RenamedLint { - old_name: self.parse_str_single_line(path.as_ref(), cursor.get_text(captures[1])), - new_name: self.parse_str_single_line(path.as_ref(), cursor.get_text(captures[2])), - version: self.parse_str_single_line(path.as_ref(), cursor.get_text(captures[0])), - }); + assert!( + data.lints + .insert( + self.parse_clippy_lint_name(path.as_ref(), cursor.get_text(captures[1])), + Lint::Renamed(RenamedLint { + new_name: self.parse_str_single_line(path.as_ref(), cursor.get_text(captures[2])), + version: self.parse_str_single_line(path.as_ref(), cursor.get_text(captures[0])), + }), + ) + .is_none() + ); } } else { panic!("error reading renamed lints"); } - - deprecated.sort_by(|lhs, rhs| lhs.name.cmp(rhs.name)); - renamed.sort_by(|lhs, rhs| lhs.old_name.cmp(rhs.old_name)); - (deprecated, renamed) } /// Removes the line splices and surrounding quotes from a string literal @@ -282,4 +306,14 @@ impl<'cx> ParseCxImpl<'cx> { ); value } + + fn parse_clippy_lint_name(&mut self, path: &Path, s: &str) -> &'cx str { + match self.parse_str_single_line(path, s).strip_prefix("clippy::") { + Some(x) => x, + None => panic!( + "error parsing `{}`: `{s}` should be a string starting with `clippy::`", + path.display() + ), + } + } } diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index 3d0da68461144..ccc7a1d95355a 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -1,5 +1,5 @@ use crate::parse::cursor::Cursor; -use crate::parse::{DeprecatedLint, Lint, ParseCx, RenamedLint}; +use crate::parse::{Lint, LintData, ParseCx}; use crate::utils::{FileUpdater, UpdateMode, UpdateStatus, update_text_region_fn}; use itertools::Itertools; use std::collections::HashSet; @@ -22,55 +22,62 @@ const DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.ht /// /// Panics if a file path could not read from or then written to pub fn update(cx: ParseCx<'_>, update_mode: UpdateMode) { - let lints = cx.find_lint_decls(); - let (deprecated, renamed) = cx.read_deprecated_lints(); - generate_lint_files(update_mode, &lints, &deprecated, &renamed); + let data = cx.parse_lint_decls(); + generate_lint_files(update_mode, &data); } #[expect(clippy::too_many_lines)] -pub fn generate_lint_files( - update_mode: UpdateMode, - lints: &[Lint<'_>], - deprecated: &[DeprecatedLint<'_>], - renamed: &[RenamedLint<'_>], -) { +pub fn generate_lint_files(update_mode: UpdateMode, data: &LintData<'_>) { let mut updater = FileUpdater::default(); + + let mut lints: Vec<_> = data.lints.iter().map(|(&x, y)| (x, y)).collect(); + lints.sort_by_key(|&(x, _)| x); updater.update_file_checked( "cargo dev update_lints", update_mode, - "README.md", - &mut update_text_region_fn("[There are over ", " lints included in this crate!]", |dst| { - write!(dst, "{}", round_to_fifty(lints.len())).unwrap(); - }), + "CHANGELOG.md", + &mut update_text_region_fn( + "\n", + "", + |dst| { + for &(lint, _) in &lints { + writeln!(dst, "[`{lint}`]: {DOCS_LINK}#{lint}").unwrap(); + } + }, + ), ); + + let mut active = Vec::with_capacity(lints.len()); + let mut deprecated = Vec::with_capacity(lints.len() / 8); + let mut renamed = Vec::with_capacity(lints.len() / 8); + for &(name, lint) in &lints { + match lint { + Lint::Active(lint) => active.push((name, lint)), + Lint::Deprecated(lint) => deprecated.push((name, lint)), + Lint::Renamed(lint) => renamed.push((name, lint)), + } + } + active.sort_by_key(|&(_, lint)| lint.module); + + // Round to avoid updating the readme every time a lint is added/deprecated. + let lint_count = active.len() / 50 * 50; updater.update_file_checked( "cargo dev update_lints", update_mode, - "book/src/README.md", + "README.md", &mut update_text_region_fn("[There are over ", " lints included in this crate!]", |dst| { - write!(dst, "{}", round_to_fifty(lints.len())).unwrap(); + write!(dst, "{lint_count}").unwrap(); }), ); updater.update_file_checked( "cargo dev update_lints", update_mode, - "CHANGELOG.md", - &mut update_text_region_fn( - "\n", - "", - |dst| { - for lint in lints - .iter() - .map(|l| l.name) - .chain(deprecated.iter().filter_map(|l| l.name.strip_prefix("clippy::"))) - .chain(renamed.iter().filter_map(|l| l.old_name.strip_prefix("clippy::"))) - .sorted() - { - writeln!(dst, "[`{lint}`]: {DOCS_LINK}#{lint}").unwrap(); - } - }, - ), + "book/src/README.md", + &mut update_text_region_fn("[There are over ", " lints included in this crate!]", |dst| { + write!(dst, "{lint_count}").unwrap(); + }), ); + updater.update_file_checked( "cargo dev update_lints", update_mode, @@ -84,11 +91,11 @@ pub fn generate_lint_files( ); dst.push_str(&src[..cursor.pos() as usize]); dst.push_str("! { DEPRECATED(DEPRECATED_VERSION) = [\n"); - for lint in deprecated { + for &(name, data) in &deprecated { write!( dst, - " #[clippy::version = \"{}\"]\n (\"{}\", \"{}\"),\n", - lint.version, lint.name, lint.reason, + " #[clippy::version = \"{}\"]\n (\"clippy::{name}\", \"{}\"),\n", + data.version, data.reason, ) .unwrap(); } @@ -98,11 +105,11 @@ pub fn generate_lint_files( declare_with_version! { RENAMED(RENAMED_VERSION) = [\n\ ", ); - for lint in renamed { + for &(name, data) in &renamed { write!( dst, - " #[clippy::version = \"{}\"]\n (\"{}\", \"{}\"),\n", - lint.version, lint.old_name, lint.new_name, + " #[clippy::version = \"{}\"]\n (\"clippy::{name}\", \"{}\"),\n", + data.version, data.new_name, ) .unwrap(); } @@ -116,8 +123,8 @@ pub fn generate_lint_files( "tests/ui/deprecated.rs", &mut |_, src, dst| { dst.push_str(GENERATED_FILE_COMMENT); - for lint in deprecated { - writeln!(dst, "#![warn({})] //~ ERROR: lint `{}`", lint.name, lint.name).unwrap(); + for &(lint, _) in &deprecated { + writeln!(dst, "#![warn(clippy::{lint})] //~ ERROR: lint `clippy::{lint}`").unwrap(); } dst.push_str("\nfn main() {}\n"); UpdateStatus::from_changed(src != dst) @@ -131,25 +138,28 @@ pub fn generate_lint_files( let mut seen_lints = HashSet::new(); dst.push_str(GENERATED_FILE_COMMENT); dst.push_str("#![allow(clippy::duplicated_attributes)]\n"); - for lint in renamed { + for &(_, lint) in &renamed { if seen_lints.insert(lint.new_name) { writeln!(dst, "#![allow({})]", lint.new_name).unwrap(); } } seen_lints.clear(); - for lint in renamed { - if seen_lints.insert(lint.old_name) { - writeln!(dst, "#![warn({})] //~ ERROR: lint `{}`", lint.old_name, lint.old_name).unwrap(); + for &(lint, _) in &renamed { + if seen_lints.insert(lint) { + writeln!(dst, "#![warn(clippy::{lint})] //~ ERROR: lint `clippy::{lint}`").unwrap(); } } dst.push_str("\nfn main() {}\n"); UpdateStatus::from_changed(src != dst) }, ); - for (crate_name, lints) in lints.iter().into_group_map_by(|&l| { - let Some(path::Component::Normal(name)) = l.path.components().next() else { + for (crate_name, lints) in active.iter().copied().into_group_map_by(|&(_, lint)| { + let Some(path::Component::Normal(name)) = lint.path.components().next() else { // All paths should start with `{crate_name}/src` when parsed from `find_lint_decls` - panic!("internal error: can't read crate name from path `{}`", l.path.display()); + panic!( + "internal error: can't read crate name from path `{}`", + lint.path.display() + ); }; name }) { @@ -161,14 +171,12 @@ pub fn generate_lint_files( "// begin lints modules, do not remove this comment, it's used in `update_lints`\n", "// end lints modules, do not remove this comment, it's used in `update_lints`", |dst| { - for lint_mod in lints - .iter() - .filter(|l| !l.module.is_empty()) - .map(|l| l.module.split_once("::").map_or(l.module, |x| x.0)) - .sorted() - .dedup() - { - writeln!(dst, "mod {lint_mod};").unwrap(); + let mut prev = ""; + for &(_, lint) in &lints { + if lint.module != prev { + writeln!(dst, "mod {};", lint.module).unwrap(); + prev = lint.module; + } } }, ), @@ -180,11 +188,15 @@ pub fn generate_lint_files( &mut |_, src, dst| { dst.push_str(GENERATED_FILE_COMMENT); dst.push_str("pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[\n"); - for (module_path, lint_name) in lints.iter().map(|l| (&l.module, l.name.to_uppercase())).sorted() { - if module_path.is_empty() { - writeln!(dst, " crate::{lint_name}_INFO,").unwrap(); + let mut buf = String::new(); + for &(name, lint) in &lints { + buf.clear(); + buf.push_str(name); + buf.make_ascii_uppercase(); + if lint.module.is_empty() { + writeln!(dst, " crate::{buf}_INFO,").unwrap(); } else { - writeln!(dst, " crate::{module_path}::{lint_name}_INFO,").unwrap(); + writeln!(dst, " crate::{}::{buf}_INFO,", lint.module).unwrap(); } } dst.push_str("];\n"); @@ -193,7 +205,3 @@ pub fn generate_lint_files( ); } } - -fn round_to_fifty(count: usize) -> usize { - count / 50 * 50 -} diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index a04d133b0d726..2ea25d277e67a 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -100,9 +100,9 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::dereference::NEEDLESS_BORROW_INFO, crate::dereference::REF_BINDING_TO_REFERENCE_INFO, crate::derivable_impls::DERIVABLE_IMPLS_INFO, - crate::derive::DERIVED_HASH_WITH_MANUAL_EQ_INFO, crate::derive::DERIVE_ORD_XOR_PARTIAL_ORD_INFO, crate::derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ_INFO, + crate::derive::DERIVED_HASH_WITH_MANUAL_EQ_INFO, crate::derive::EXPL_IMPL_CLONE_ON_COPY_INFO, crate::derive::UNSAFE_DERIVE_DESERIALIZE_INFO, crate::disallowed_macros::DISALLOWED_MACROS_INFO, @@ -204,8 +204,8 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::if_not_else::IF_NOT_ELSE_INFO, crate::if_then_some_else_none::IF_THEN_SOME_ELSE_NONE_INFO, crate::ifs::BRANCHES_SHARING_CODE_INFO, - crate::ifs::IFS_SAME_COND_INFO, crate::ifs::IF_SAME_THEN_ELSE_INFO, + crate::ifs::IFS_SAME_COND_INFO, crate::ifs::SAME_FUNCTIONS_IN_IF_CONDITION_INFO, crate::ignored_unit_patterns::IGNORED_UNIT_PATTERNS_INFO, crate::impl_hash_with_borrow_str_and_bytes::IMPL_HASH_BORROW_WITH_STR_AND_BYTES_INFO, @@ -336,8 +336,8 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::matches::MATCH_SAME_ARMS_INFO, crate::matches::MATCH_SINGLE_BINDING_INFO, crate::matches::MATCH_STR_CASE_MISMATCH_INFO, - crate::matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS_INFO, crate::matches::MATCH_WILD_ERR_ARM_INFO, + crate::matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS_INFO, crate::matches::NEEDLESS_MATCH_INFO, crate::matches::REDUNDANT_GUARDS_INFO, crate::matches::REDUNDANT_PATTERN_MATCHING_INFO, @@ -359,9 +359,9 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::methods::CHARS_LAST_CMP_INFO, crate::methods::CHARS_NEXT_CMP_INFO, crate::methods::CLEAR_WITH_DRAIN_INFO, - crate::methods::CLONED_INSTEAD_OF_COPIED_INFO, crate::methods::CLONE_ON_COPY_INFO, crate::methods::CLONE_ON_REF_PTR_INFO, + crate::methods::CLONED_INSTEAD_OF_COPIED_INFO, crate::methods::COLLAPSIBLE_STR_REPLACE_INFO, crate::methods::CONST_IS_EMPTY_INFO, crate::methods::DOUBLE_ENDED_ITERATOR_LAST_INFO, @@ -389,7 +389,6 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::methods::IO_OTHER_ERROR_INFO, crate::methods::IP_CONSTANT_INFO, crate::methods::IS_DIGIT_ASCII_RADIX_INFO, - crate::methods::ITERATOR_STEP_BY_ZERO_INFO, crate::methods::ITER_CLONED_COLLECT_INFO, crate::methods::ITER_COUNT_INFO, crate::methods::ITER_FILTER_IS_OK_INFO, @@ -405,10 +404,11 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::methods::ITER_SKIP_NEXT_INFO, crate::methods::ITER_SKIP_ZERO_INFO, crate::methods::ITER_WITH_DRAIN_INFO, + crate::methods::ITERATOR_STEP_BY_ZERO_INFO, crate::methods::JOIN_ABSOLUTE_PATHS_INFO, crate::methods::LINES_FILTER_MAP_OK_INFO, - crate::methods::MANUAL_CONTAINS_INFO, crate::methods::MANUAL_C_STR_LITERALS_INFO, + crate::methods::MANUAL_CONTAINS_INFO, crate::methods::MANUAL_FILTER_MAP_INFO, crate::methods::MANUAL_FIND_MAP_INFO, crate::methods::MANUAL_INSPECT_INFO, @@ -437,8 +437,8 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::methods::NEEDLESS_OPTION_TAKE_INFO, crate::methods::NEEDLESS_SPLITN_INFO, crate::methods::NEW_RET_NO_SELF_INFO, - crate::methods::NONSENSICAL_OPEN_OPTIONS_INFO, crate::methods::NO_EFFECT_REPLACE_INFO, + crate::methods::NONSENSICAL_OPEN_OPTIONS_INFO, crate::methods::OBFUSCATED_IF_ELSE_INFO, crate::methods::OK_EXPECT_INFO, crate::methods::OPTION_AS_REF_CLONED_INFO, @@ -452,8 +452,8 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::methods::PTR_OFFSET_BY_LITERAL_INFO, crate::methods::PTR_OFFSET_WITH_CAST_INFO, crate::methods::RANGE_ZIP_WITH_LEN_INFO, - crate::methods::READONLY_WRITE_LOCK_INFO, crate::methods::READ_LINE_WITHOUT_TRIM_INFO, + crate::methods::READONLY_WRITE_LOCK_INFO, crate::methods::REDUNDANT_AS_STR_INFO, crate::methods::REDUNDANT_ITER_CLONED_INFO, crate::methods::REPEAT_ONCE_INFO, @@ -468,9 +468,9 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::methods::SKIP_WHILE_NEXT_INFO, crate::methods::SLICED_STRING_AS_BYTES_INFO, crate::methods::STABLE_SORT_PRIMITIVE_INFO, + crate::methods::STR_SPLIT_AT_NEWLINE_INFO, crate::methods::STRING_EXTEND_CHARS_INFO, crate::methods::STRING_LIT_CHARS_ANY_INFO, - crate::methods::STR_SPLIT_AT_NEWLINE_INFO, crate::methods::SUSPICIOUS_COMMAND_ARG_SPACE_INFO, crate::methods::SUSPICIOUS_MAP_INFO, crate::methods::SUSPICIOUS_OPEN_OPTIONS_INFO, @@ -641,8 +641,8 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::ranges::RANGE_MINUS_ONE_INFO, crate::ranges::RANGE_PLUS_ONE_INFO, crate::ranges::REVERSED_EMPTY_RANGES_INFO, - crate::raw_strings::NEEDLESS_RAW_STRINGS_INFO, crate::raw_strings::NEEDLESS_RAW_STRING_HASHES_INFO, + crate::raw_strings::NEEDLESS_RAW_STRINGS_INFO, crate::rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT_INFO, crate::read_zero_byte_vec::READ_ZERO_BYTE_VEC_INFO, crate::redundant_async_block::REDUNDANT_ASYNC_BLOCK_INFO, @@ -695,12 +695,12 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::std_instead_of_core::STD_INSTEAD_OF_CORE_INFO, crate::string_patterns::MANUAL_PATTERN_CHAR_COMPARISON_INFO, crate::string_patterns::SINGLE_CHAR_PATTERN_INFO, + crate::strings::STR_TO_STRING_INFO, crate::strings::STRING_ADD_INFO, crate::strings::STRING_ADD_ASSIGN_INFO, crate::strings::STRING_FROM_UTF8_AS_BYTES_INFO, crate::strings::STRING_LIT_AS_BYTES_INFO, crate::strings::STRING_SLICE_INFO, - crate::strings::STR_TO_STRING_INFO, crate::strings::TRIM_SPLIT_WHITESPACE_INFO, crate::strlen_on_c_strings::STRLEN_ON_C_STRINGS_INFO, crate::suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS_INFO, @@ -724,7 +724,6 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::transmute::CROSSPOINTER_TRANSMUTE_INFO, crate::transmute::EAGER_TRANSMUTE_INFO, crate::transmute::MISSING_TRANSMUTE_ANNOTATIONS_INFO, - crate::transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS_INFO, crate::transmute::TRANSMUTE_BYTES_TO_STR_INFO, crate::transmute::TRANSMUTE_INT_TO_BOOL_INFO, crate::transmute::TRANSMUTE_INT_TO_NON_ZERO_INFO, @@ -732,6 +731,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::transmute::TRANSMUTE_PTR_TO_PTR_INFO, crate::transmute::TRANSMUTE_PTR_TO_REF_INFO, crate::transmute::TRANSMUTE_UNDEFINED_REPR_INFO, + crate::transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS_INFO, crate::transmute::TRANSMUTING_NULL_INFO, crate::transmute::UNSOUND_COLLECTION_TRANSMUTE_INFO, crate::transmute::USELESS_TRANSMUTE_INFO, @@ -789,20 +789,20 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::useless_vec::USELESS_VEC_INFO, crate::vec_init_then_push::VEC_INIT_THEN_PUSH_INFO, crate::visibility::NEEDLESS_PUB_SELF_INFO, - crate::visibility::PUB_WITHOUT_SHORTHAND_INFO, crate::visibility::PUB_WITH_SHORTHAND_INFO, + crate::visibility::PUB_WITHOUT_SHORTHAND_INFO, crate::volatile_composites::VOLATILE_COMPOSITES_INFO, crate::wildcard_imports::ENUM_GLOB_USE_INFO, crate::wildcard_imports::WILDCARD_IMPORTS_INFO, - crate::write::PRINTLN_EMPTY_STRING_INFO, crate::write::PRINT_LITERAL_INFO, crate::write::PRINT_STDERR_INFO, crate::write::PRINT_STDOUT_INFO, crate::write::PRINT_WITH_NEWLINE_INFO, + crate::write::PRINTLN_EMPTY_STRING_INFO, crate::write::USE_DEBUG_INFO, - crate::write::WRITELN_EMPTY_STRING_INFO, crate::write::WRITE_LITERAL_INFO, crate::write::WRITE_WITH_NEWLINE_INFO, + crate::write::WRITELN_EMPTY_STRING_INFO, crate::zero_div_zero::ZERO_DIVIDED_BY_ZERO_INFO, crate::zero_repeat_side_effects::ZERO_REPEAT_SIDE_EFFECTS_INFO, crate::zero_sized_map_values::ZERO_SIZED_MAP_VALUES_INFO, From 2658ce8205aa916f7b69dcf0f75b4b57fcfca31d Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 20 Oct 2025 15:58:23 -0400 Subject: [PATCH 21/57] `clippy_dev`: Use types to differentiate rustc and clippy lints. --- clippy_dev/src/edit_lints.rs | 35 ++++++--------------- clippy_dev/src/parse.rs | 57 ++++++++++++++++++++++++++++++++-- clippy_dev/src/update_lints.rs | 5 +-- 3 files changed, 64 insertions(+), 33 deletions(-) diff --git a/clippy_dev/src/edit_lints.rs b/clippy_dev/src/edit_lints.rs index 35459a1bbd705..411bf530b22bf 100644 --- a/clippy_dev/src/edit_lints.rs +++ b/clippy_dev/src/edit_lints.rs @@ -1,5 +1,5 @@ use crate::parse::cursor::{self, Capture, Cursor}; -use crate::parse::{ActiveLint, DeprecatedLint, Lint, LintData, ParseCx, RenamedLint}; +use crate::parse::{ActiveLint, DeprecatedLint, Lint, LintData, LintName, ParseCx, RenamedLint}; use crate::update_lints::generate_lint_files; use crate::utils::{ ErrAction, FileUpdater, UpdateMode, UpdateStatus, Version, delete_dir_if_exists, delete_file_if_exists, @@ -48,14 +48,7 @@ pub fn deprecate<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, name pub fn uplift<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_name: &'env str, new_name: &'env str) { let mut data = cx.parse_lint_decls(); - update_rename_targets( - &mut data, - cx.str_buf.with(|buf| { - buf.extend(["clippy::", old_name]); - cx.arena.alloc_str(buf) - }), - new_name, - ); + update_rename_targets(&mut data, old_name, LintName::new_rustc(new_name)); let Entry::Occupied(mut lint) = data.lints.entry(old_name) else { eprintln!("error: failed to find lint `{old_name}`"); @@ -64,7 +57,7 @@ pub fn uplift<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_nam let Lint::Active(prev_lint) = mem::replace( lint.get_mut(), Lint::Renamed(RenamedLint { - new_name, + new_name: LintName::new_rustc(new_name), version: cx.str_buf.alloc_display(cx.arena, clippy_version.rust_display()), }), ) else { @@ -105,18 +98,7 @@ pub fn rename<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_nam let mut updater = FileUpdater::default(); let mut data = cx.parse_lint_decls(); - let new_name_prefixed = cx.str_buf.with(|buf| { - buf.extend(["clippy::", new_name]); - cx.arena.alloc_str(buf) - }); - update_rename_targets( - &mut data, - cx.str_buf.with(|buf| { - buf.extend(["clippy::", old_name]); - cx.arena.alloc_str(buf) - }), - new_name_prefixed, - ); + update_rename_targets(&mut data, old_name, LintName::new_clippy(new_name)); let Entry::Occupied(mut lint) = data.lints.entry(old_name) else { eprintln!("error: failed to find lint `{old_name}`"); @@ -125,7 +107,7 @@ pub fn rename<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_nam let Lint::Active(mut prev_lint) = mem::replace( lint.get_mut(), Lint::Renamed(RenamedLint { - new_name: new_name_prefixed, + new_name: LintName::new_clippy(new_name), version: cx.str_buf.alloc_display(cx.arena, clippy_version.rust_display()), }), ) else { @@ -157,7 +139,7 @@ pub fn rename<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_nam rename_test_files(old_name, new_name, &create_ignored_prefixes(old_name, &data)); } else { - println!("Renamed `clippy::{old_name}` to `clippy::{new_name}`"); + println!("Renamed `{old_name}` to `{new_name}`"); println!("Since `{new_name}` already exists the existing code has not been changed"); return; } @@ -171,7 +153,7 @@ pub fn rename<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_nam } generate_lint_files(UpdateMode::Change, &data); - println!("Renamed `clippy::{old_name}` to `clippy::{new_name}`"); + println!("Renamed `{old_name}` to `{new_name}`"); println!("All code referencing the old name has been updated"); println!("Make sure to inspect the results as some things may have been missed"); println!("note: `cargo uibless` still needs to be run to update the test results"); @@ -213,7 +195,8 @@ fn remove_lint_declaration(name: &str, lint: &ActiveLint<'_>, data: &LintData<'_ /// /// This is needed because rustc doesn't allow a lint to be renamed to a lint that has /// also been renamed. -fn update_rename_targets<'cx>(data: &mut LintData<'cx>, old_name: &str, new_name: &'cx str) { +fn update_rename_targets<'cx>(data: &mut LintData<'cx>, old_name: &str, new_name: LintName<'cx>) { + let old_name = LintName::new_clippy(old_name); for lint in data.lints.values_mut() { if let Lint::Renamed(lint) = lint && lint.new_name == old_name diff --git a/clippy_dev/src/parse.rs b/clippy_dev/src/parse.rs index 566eea336f0da..ffb50784a9a75 100644 --- a/clippy_dev/src/parse.rs +++ b/clippy_dev/src/parse.rs @@ -2,7 +2,7 @@ pub mod cursor; use self::cursor::{Capture, Cursor}; use crate::utils::{ErrAction, File, Scoped, expect_action, walk_dir_no_dot_or_target}; -use core::fmt::{Display, Write as _}; +use core::fmt::{self, Display, Write as _}; use core::range::Range; use rustc_arena::DroplessArena; use rustc_data_structures::fx::FxHashMap; @@ -82,6 +82,48 @@ impl StrBuf { } } +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +pub enum LintTool { + Rustc, + Clippy, +} +impl LintTool { + /// Gets the namespace prefix to use when naming a lint including the `::`. + pub fn prefix(self) -> &'static str { + match self { + Self::Rustc => "", + Self::Clippy => "clippy::", + } + } +} + +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +pub struct LintName<'cx> { + pub name: &'cx str, + pub tool: LintTool, +} +impl<'cx> LintName<'cx> { + pub fn new_rustc(name: &'cx str) -> Self { + Self { + name, + tool: LintTool::Rustc, + } + } + + pub fn new_clippy(name: &'cx str) -> Self { + Self { + name, + tool: LintTool::Clippy, + } + } +} +impl Display for LintName<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.tool.prefix())?; + f.write_str(self.name) + } +} + pub struct ActiveLint<'cx> { pub group: &'cx str, pub module: &'cx str, @@ -95,7 +137,7 @@ pub struct DeprecatedLint<'cx> { } pub struct RenamedLint<'cx> { - pub new_name: &'cx str, + pub new_name: LintName<'cx>, pub version: &'cx str, } @@ -259,7 +301,7 @@ impl<'cx> ParseCxImpl<'cx> { .insert( self.parse_clippy_lint_name(path.as_ref(), cursor.get_text(captures[1])), Lint::Renamed(RenamedLint { - new_name: self.parse_str_single_line(path.as_ref(), cursor.get_text(captures[2])), + new_name: self.parse_lint_name(path.as_ref(), cursor.get_text(captures[2])), version: self.parse_str_single_line(path.as_ref(), cursor.get_text(captures[0])), }), ) @@ -316,4 +358,13 @@ impl<'cx> ParseCxImpl<'cx> { ), } } + + fn parse_lint_name(&mut self, path: &Path, s: &str) -> LintName<'cx> { + let s = self.parse_str_single_line(path, s); + let (name, tool) = match s.strip_prefix("clippy::") { + Some(s) => (s, LintTool::Clippy), + None => (s, LintTool::Rustc), + }; + LintName { name, tool } + } } diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index ccc7a1d95355a..a4cf15058986b 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -143,11 +143,8 @@ pub fn generate_lint_files(update_mode: UpdateMode, data: &LintData<'_>) { writeln!(dst, "#![allow({})]", lint.new_name).unwrap(); } } - seen_lints.clear(); for &(lint, _) in &renamed { - if seen_lints.insert(lint) { - writeln!(dst, "#![warn(clippy::{lint})] //~ ERROR: lint `clippy::{lint}`").unwrap(); - } + writeln!(dst, "#![warn(clippy::{lint})] //~ ERROR: lint `clippy::{lint}`").unwrap(); } dst.push_str("\nfn main() {}\n"); UpdateStatus::from_changed(src != dst) From 43eb3b9684cbb01c1a99ad23d4045e7e8eaaa6f4 Mon Sep 17 00:00:00 2001 From: Greg Miller Date: Fri, 13 Feb 2026 13:49:57 -0500 Subject: [PATCH 22/57] fix: `RustcCallbacks::config()` in `clippy-driver` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `RustcCallbacks::config()` function used by `clippy-driver` was not setting `config.extra_symbols`, so when interned symbols were computed, such as `sym::CLIPPY_ARGS`, it was using some random string in the binary. The fix is to set ```rust config.extra_symbols = sym::EXTRA_SYMBOLS.into(); ``` in the config() function. This mirrors what is done in the `ClippyCallbacks::config()` function, which did not show a problem. # Steps to repro the problem (WITHOUT this PR): I'm on a mac laptop now, but I repro'd this on Linux too: ```console ❯ cargo -V cargo 1.95.0-nightly (fe2f314ae 2026-01-30) ``` 1. Create a dummy project ```console ❯ cargo new --lib /tmp/foo ``` 2. Run `clippy-driver` from this repo on `/tmp/foo/src/lib.rs`. You must specify `--cap-lints allow` in order for the `RustcCallbacks` to be used; otherwise, the `ClippyCallbacks` are used ad you won't see the problem. ```console ❯ cargo run --bin clippy-driver -- rustc --crate-name foo --edition=2024 /tmp/foo/src/lib.rs --crate-type lib --emit=dep-info --out-dir /tmp/foo --cap-lints allow ``` 3. **The problem**: The problem is the `env-dep:macos` line. It is _incorrectly_ recording that the "macos" environment varialbe is relevant for this build. On Linux it showed the string "linux". I've also seen other random strings, like "128". I believe these strings are arbitrary. The problem is that the `sym::CLIPPY_ARGS` symbol is read without the `exta_symbols` table being set. ```console ❯ cat /tmp/foo/foo.d /tmp/foo/foo.d: /tmp/foo/src/lib.rs /tmp/foo/src/lib.rs: # env-dep:macos ``` # The fix: With this fix in this PR, we can re-run the `clippy-driver` invocation in step 2 above and see the expected dep-info showing `env-dep:CLIPPY_ARGS` instead of `env-dep:macos`. ```console ❯ cargo run --bin clippy-driver -- rustc --crate-name foo --edition=2024 /tmp/foo/src/lib.rs --crate-type lib --emit=dep-info --out-dir /tmp/foo --cap-lints allow ❯ cat /tmp/foo/foo.d /tmp/foo/foo.d: /tmp/foo/src/lib.rs /tmp/foo/src/lib.rs: # env-dep:CLIPPY_ARGS ``` --- src/driver.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/driver.rs b/src/driver.rs index 409eb182fe33c..f27cb67087096 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -126,6 +126,7 @@ impl rustc_driver::Callbacks for RustcCallbacks { config.psess_created = Some(Box::new(move |psess| { track_clippy_args(psess, clippy_args_var.as_deref()); })); + config.extra_symbols = sym::EXTRA_SYMBOLS.into(); } } From 2737b26c2e5eee9229663552d0c70e2affbb8511 Mon Sep 17 00:00:00 2001 From: Jacob Adam Date: Sat, 14 Feb 2026 12:01:38 +0000 Subject: [PATCH 23/57] Fix the compile-test tests when setting Cargo's `build.build-dir` setting to a path that's distinct from `target-dir`. --- tests/compile-test.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/compile-test.rs b/tests/compile-test.rs index fa2b6cf268063..a1f3224a5da95 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -197,10 +197,6 @@ impl TestContext { defaults.set_custom("diagnostic-collector", collector); } config.with_args(&self.args); - let current_exe_path = env::current_exe().unwrap(); - let deps_path = current_exe_path.parent().unwrap(); - let profile_path = deps_path.parent().unwrap(); - config.program.args.extend( [ "--emit=metadata", @@ -224,6 +220,7 @@ impl TestContext { config.program.args.push(format!("--sysroot={sysroot}").into()); } + let profile_path = target_dir.join(env!("PROFILE")); config.program.program = profile_path.join(if cfg!(windows) { "clippy-driver.exe" } else { From 6c04e465af9a963455b93d858c528af362fc2d0d Mon Sep 17 00:00:00 2001 From: Oscar Bray Date: Fri, 13 Feb 2026 11:18:24 +0000 Subject: [PATCH 24/57] Port #[rustc_test_marker] to the attribute parser --- clippy_utils/src/lib.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 269a7f333dc58..c479ea59b884a 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -2343,11 +2343,7 @@ fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalModDefId, f: impl FnOnce(& // We could also check for the type name `test::TestDescAndFn` && let Res::Def(DefKind::Struct, _) = path.res { - let has_test_marker = tcx - .hir_attrs(item.hir_id()) - .iter() - .any(|a| a.has_name(sym::rustc_test_marker)); - if has_test_marker { + if find_attr!(tcx.hir_attrs(item.hir_id()), AttributeKind::RustcTestMarker(..)) { names.push(ident.name); } } From 904a750f440fa3dbb9397955e26ac324cbd15d71 Mon Sep 17 00:00:00 2001 From: linshuy2 Date: Fri, 13 Feb 2026 01:00:27 +0000 Subject: [PATCH 25/57] Extend `collapsible_match` to cover `if-else`s --- clippy_lints/src/collapsible_if.rs | 4 +- clippy_lints/src/matches/collapsible_match.rs | 74 ++++++++++++++++++- tests/ui/collapsible_match_fixable.fixed | 30 ++++++++ tests/ui/collapsible_match_fixable.rs | 31 ++++++++ tests/ui/collapsible_match_fixable.stderr | 50 +++++++++++++ 5 files changed, 183 insertions(+), 6 deletions(-) create mode 100644 tests/ui/collapsible_match_fixable.fixed create mode 100644 tests/ui/collapsible_match_fixable.rs create mode 100644 tests/ui/collapsible_match_fixable.stderr diff --git a/clippy_lints/src/collapsible_if.rs b/clippy_lints/src/collapsible_if.rs index be07ce1272bd8..17e11b8b281d9 100644 --- a/clippy_lints/src/collapsible_if.rs +++ b/clippy_lints/src/collapsible_if.rs @@ -307,7 +307,7 @@ fn expr_block<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> { } /// If the expression is a `||`, suggest parentheses around it. -fn parens_around(expr: &Expr<'_>) -> Vec<(Span, String)> { +pub(super) fn parens_around(expr: &Expr<'_>) -> Vec<(Span, String)> { if let ExprKind::Binary(op, _, _) = expr.peel_drop_temps().kind && op.node == BinOpKind::Or { @@ -334,7 +334,7 @@ fn span_extract_keyword(sm: &SourceMap, span: Span, keyword: &str) -> Option (Span, Span, Span) { +pub(super) fn peel_parens(sm: &SourceMap, mut span: Span) -> (Span, Span, Span) { use crate::rustc_span::Pos; let start = span.shrink_to_lo(); diff --git a/clippy_lints/src/matches/collapsible_match.rs b/clippy_lints/src/matches/collapsible_match.rs index 79f737f07eb1e..5709348c5bcde 100644 --- a/clippy_lints/src/matches/collapsible_match.rs +++ b/clippy_lints/src/matches/collapsible_match.rs @@ -1,17 +1,19 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::higher::IfLetOrMatch; +use clippy_utils::higher::{If, IfLetOrMatch}; use clippy_utils::msrvs::Msrv; use clippy_utils::res::{MaybeDef, MaybeResPath}; -use clippy_utils::source::snippet; +use clippy_utils::source::{IntoSpan, SpanRangeExt, snippet}; use clippy_utils::visitors::is_local_used; use clippy_utils::{SpanlessEq, get_ref_operators, is_unit_expr, peel_blocks_with_stmt, peel_ref_operators}; use rustc_ast::BorrowKind; -use rustc_errors::MultiSpan; +use rustc_errors::{Applicability, MultiSpan}; use rustc_hir::LangItem::OptionNone; use rustc_hir::{Arm, Expr, ExprKind, HirId, Pat, PatExpr, PatExprKind, PatKind}; use rustc_lint::LateContext; -use rustc_span::Span; use rustc_span::symbol::Ident; +use rustc_span::{BytePos, Span}; + +use crate::collapsible_if::{parens_around, peel_parens}; use super::{COLLAPSIBLE_MATCH, pat_contains_disallowed_or}; @@ -119,6 +121,70 @@ fn check_arm<'tcx>( "the outer pattern can be modified to include the inner pattern", ); }); + } else if outer_is_match // Leave if-let to the `collapsible_if` lint + && let Some(inner) = If::hir(inner_expr) + && outer_pat.span.eq_ctxt(inner.cond.span) + && match (outer_else_body, inner.r#else) { + (None, None) => true, + (None, Some(e)) | (Some(e), None) => is_unit_expr(e), + (Some(a), Some(b)) => SpanlessEq::new(cx).eq_expr(a, b), + } + { + span_lint_hir_and_then( + cx, + COLLAPSIBLE_MATCH, + inner_expr.hir_id, + inner_expr.span, + "this `if` can be collapsed into the outer `match`", + |diag| { + let outer_then_open_bracket = outer_then_body + .span + .split_at(1) + .0 + .with_leading_whitespace(cx) + .into_span(); + let outer_then_closing_bracket = { + let end = outer_then_body.span.shrink_to_hi(); + end.with_lo(end.lo() - BytePos(1)) + .with_leading_whitespace(cx) + .into_span() + }; + let outer_arrow_end = if let Some(outer_guard) = outer_guard { + outer_guard.span.shrink_to_hi() + } else { + outer_pat.span.shrink_to_hi() + }; + let (paren_start, inner_if_span, paren_end) = peel_parens(cx.tcx.sess.source_map(), inner_expr.span); + let inner_if = inner_if_span.split_at(2).0; + let mut sugg = vec![ + (inner.then.span.shrink_to_lo(), "=> ".to_string()), + (outer_arrow_end.to(outer_then_open_bracket), String::new()), + (outer_then_closing_bracket, String::new()), + ]; + + if let Some(outer_guard) = outer_guard { + sugg.extend(parens_around(outer_guard)); + sugg.push((inner_if, "&&".to_string())); + } + + if !paren_start.is_empty() { + sugg.push((paren_start, String::new())); + } + + if !paren_end.is_empty() { + sugg.push((paren_end, String::new())); + } + + sugg.extend(parens_around(inner.cond)); + + if let Some(else_inner) = inner.r#else { + let else_inner_span = inner.then.span.shrink_to_hi().to(else_inner.span); + sugg.push((else_inner_span, String::new())); + } + + diag.multipart_suggestion("collapse nested if block", sugg, Applicability::MachineApplicable); + }, + ); } } diff --git a/tests/ui/collapsible_match_fixable.fixed b/tests/ui/collapsible_match_fixable.fixed new file mode 100644 index 0000000000000..db76530aee144 --- /dev/null +++ b/tests/ui/collapsible_match_fixable.fixed @@ -0,0 +1,30 @@ +#![warn(clippy::collapsible_match)] +#![allow(clippy::single_match, clippy::redundant_guards)] + +fn issue16558() { + let opt = Some(1); + let _ = match opt { + Some(s) + if s == 1 => { s } + //~^ collapsible_match + , + _ => 1, + }; + + match opt { + Some(s) + if s == 1 => { + //~^ collapsible_match + todo!() + }, + _ => {}, + }; + + let _ = match opt { + Some(s) if s > 2 + && s == 1 => { s } + //~^ collapsible_match + , + _ => 1, + }; +} diff --git a/tests/ui/collapsible_match_fixable.rs b/tests/ui/collapsible_match_fixable.rs new file mode 100644 index 0000000000000..94bf1d6bfdfab --- /dev/null +++ b/tests/ui/collapsible_match_fixable.rs @@ -0,0 +1,31 @@ +#![warn(clippy::collapsible_match)] +#![allow(clippy::single_match, clippy::redundant_guards)] + +fn issue16558() { + let opt = Some(1); + let _ = match opt { + Some(s) => { + if s == 1 { s } else { 1 } + //~^ collapsible_match + }, + _ => 1, + }; + + match opt { + Some(s) => { + (if s == 1 { + //~^ collapsible_match + todo!() + }) + }, + _ => {}, + }; + + let _ = match opt { + Some(s) if s > 2 => { + if s == 1 { s } else { 1 } + //~^ collapsible_match + }, + _ => 1, + }; +} diff --git a/tests/ui/collapsible_match_fixable.stderr b/tests/ui/collapsible_match_fixable.stderr new file mode 100644 index 0000000000000..4d501cbd0993d --- /dev/null +++ b/tests/ui/collapsible_match_fixable.stderr @@ -0,0 +1,50 @@ +error: this `if` can be collapsed into the outer `match` + --> tests/ui/collapsible_match_fixable.rs:8:13 + | +LL | if s == 1 { s } else { 1 } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::collapsible-match` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::collapsible_match)]` +help: collapse nested if block + | +LL ~ Some(s) +LL ~ if s == 1 => { s } +LL | +LL ~ , + | + +error: this `if` can be collapsed into the outer `match` + --> tests/ui/collapsible_match_fixable.rs:16:13 + | +LL | / (if s == 1 { +LL | | +LL | | todo!() +LL | | }) + | |______________^ + | +help: collapse nested if block + | +LL ~ Some(s) +LL ~ if s == 1 => { +LL | +LL | todo!() +LL ~ }, + | + +error: this `if` can be collapsed into the outer `match` + --> tests/ui/collapsible_match_fixable.rs:26:13 + | +LL | if s == 1 { s } else { 1 } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: collapse nested if block + | +LL ~ Some(s) if s > 2 +LL ~ && s == 1 => { s } +LL | +LL ~ , + | + +error: aborting due to 3 previous errors + From 5be3049e9cf3e41a1c15b280402e353d09c37ec5 Mon Sep 17 00:00:00 2001 From: linshuy2 Date: Fri, 13 Feb 2026 01:19:00 +0000 Subject: [PATCH 26/57] Apply `collapsible_match` to Clippy itself --- clippy_dev/src/new_lint.rs | 18 ++--- clippy_lints/src/casts/unnecessary_cast.rs | 6 +- clippy_lints/src/cognitive_complexity.rs | 6 +- clippy_lints/src/doc/mod.rs | 16 +--- clippy_lints/src/double_parens.rs | 74 +++++++++---------- clippy_lints/src/infinite_iter.rs | 6 +- clippy_lints/src/loops/utils.rs | 24 +++--- clippy_lints/src/manual_option_as_slice.rs | 39 ++++------ clippy_lints/src/manual_strip.rs | 21 +++--- clippy_lints/src/matches/collapsible_match.rs | 2 +- .../src/matches/match_single_binding.rs | 6 +- clippy_lints/src/methods/mod.rs | 9 ++- clippy_lints/src/methods/or_fun_call.rs | 8 +- .../src/methods/unnecessary_filter_map.rs | 14 ++-- .../src/methods/unnecessary_to_owned.rs | 12 +-- .../src/mixed_read_write_in_expression.rs | 18 ++--- .../src/multiple_unsafe_ops_per_block.rs | 6 +- .../src/non_send_fields_in_send_ty.rs | 24 +++--- clippy_lints/src/operators/bit_mask.rs | 16 ++-- clippy_lints/src/operators/identity_op.rs | 16 ++-- clippy_lints/src/operators/manual_div_ceil.rs | 11 ++- clippy_lints/src/operators/mod.rs | 4 +- .../src/redundant_static_lifetimes.rs | 30 +++----- clippy_lints/src/std_instead_of_core.rs | 9 +-- clippy_lints/src/strings.rs | 42 +++++------ clippy_utils/src/msrvs.rs | 10 +-- clippy_utils/src/ty/mod.rs | 9 +-- tests/missing-test-files.rs | 6 +- 28 files changed, 192 insertions(+), 270 deletions(-) diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index 72f281ca4d9d7..2abe471bed2bd 100644 --- a/clippy_dev/src/new_lint.rs +++ b/clippy_dev/src/new_lint.rs @@ -526,18 +526,14 @@ fn parse_mod_file(path: &Path, contents: &str) -> (&'static str, usize) { let mut captures = [Capture::EMPTY]; while let Some(name) = cursor.find_any_ident() { match cursor.get_text(name) { - "declare_clippy_lint" => { - if cursor.match_all(&[Bang, OpenBrace], &mut []) && cursor.find_pat(CloseBrace) { - decl_end = Some(cursor.pos()); - } + "declare_clippy_lint" if cursor.match_all(&[Bang, OpenBrace], &mut []) && cursor.find_pat(CloseBrace) => { + decl_end = Some(cursor.pos()); }, - "impl" => { - if cursor.match_all(&[Lt, Lifetime, Gt, CaptureIdent], &mut captures) { - match cursor.get_text(captures[0]) { - "LateLintPass" => context = Some("LateContext"), - "EarlyLintPass" => context = Some("EarlyContext"), - _ => {}, - } + "impl" if cursor.match_all(&[Lt, Lifetime, Gt, CaptureIdent], &mut captures) => { + match cursor.get_text(captures[0]) { + "LateLintPass" => context = Some("LateContext"), + "EarlyLintPass" => context = Some("EarlyContext"), + _ => {}, } }, _ => {}, diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs index 7bfe9201d812a..7ecef955197a9 100644 --- a/clippy_lints/src/casts/unnecessary_cast.rs +++ b/clippy_lints/src/casts/unnecessary_cast.rs @@ -39,10 +39,8 @@ pub(super) fn check<'tcx>( // Ignore casts to pointers that are aliases or cfg dependant, e.g. // - p as *const std::ffi::c_char (alias) // - p as *const std::os::raw::c_char (cfg dependant) - TyKind::Path(qpath) => { - if is_ty_alias(&qpath) || is_hir_ty_cfg_dependant(cx, to_pointee.ty) { - return false; - } + TyKind::Path(qpath) if is_ty_alias(&qpath) || is_hir_ty_cfg_dependant(cx, to_pointee.ty) => { + return false; }, // Ignore `p as *const _` TyKind::Infer(()) => return false, diff --git a/clippy_lints/src/cognitive_complexity.rs b/clippy_lints/src/cognitive_complexity.rs index 595625c08bef9..950ac863d755f 100644 --- a/clippy_lints/src/cognitive_complexity.rs +++ b/clippy_lints/src/cognitive_complexity.rs @@ -81,10 +81,8 @@ impl CognitiveComplexity { } cc += arms.iter().filter(|arm| arm.guard.is_some()).count() as u64; }, - ExprKind::Ret(_) => { - if !matches!(prev_expr, Some(ExprKind::Ret(_))) { - returns += 1; - } + ExprKind::Ret(_) if !matches!(prev_expr, Some(ExprKind::Ret(_))) => { + returns += 1; }, _ => {}, } diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index e7a984694831b..0a2871f23964d 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -763,19 +763,11 @@ impl<'tcx> LateLintPass<'tcx> for Documentation { self.check_private_items, ); match item.kind { - ItemKind::Fn { sig, body, .. } => { + ItemKind::Fn { sig, body, .. } if !(is_entrypoint_fn(cx, item.owner_id.to_def_id()) - || item.span.in_external_macro(cx.tcx.sess.source_map())) - { - missing_headers::check( - cx, - item.owner_id, - sig, - headers, - Some(body), - self.check_private_items, - ); - } + || item.span.in_external_macro(cx.tcx.sess.source_map())) => + { + missing_headers::check(cx, item.owner_id, sig, headers, Some(body), self.check_private_items); }, ItemKind::Trait(_, _, unsafety, ..) => match (headers.safety, unsafety) { (false, Safety::Unsafe) => span_lint( diff --git a/clippy_lints/src/double_parens.rs b/clippy_lints/src/double_parens.rs index 351d29d874327..acc3e4936e440 100644 --- a/clippy_lints/src/double_parens.rs +++ b/clippy_lints/src/double_parens.rs @@ -46,28 +46,28 @@ impl EarlyLintPass for DoubleParens { // ((..)) // ^^^^^^ expr // ^^^^ inner - ExprKind::Paren(inner) if matches!(inner.kind, ExprKind::Paren(_) | ExprKind::Tup(_)) => { - if expr.span.eq_ctxt(inner.span) + ExprKind::Paren(inner) + if matches!(inner.kind, ExprKind::Paren(_) | ExprKind::Tup(_)) + && expr.span.eq_ctxt(inner.span) && !expr.span.in_external_macro(cx.sess().source_map()) - && check_source(cx, inner) - { - // suggest removing the outer parens + && check_source(cx, inner) => + { + // suggest removing the outer parens - let mut applicability = Applicability::MachineApplicable; - // We don't need to use `snippet_with_context` here, because: - // - if `inner`'s `ctxt` is from macro, we don't lint in the first place (see the check above) - // - otherwise, calling `snippet_with_applicability` on a not-from-macro span is fine - let sugg = snippet_with_applicability(cx.sess(), inner.span, "_", &mut applicability); - span_lint_and_sugg( - cx, - DOUBLE_PARENS, - expr.span, - "unnecessary parentheses", - "remove them", - sugg.to_string(), - applicability, - ); - } + let mut applicability = Applicability::MachineApplicable; + // We don't need to use `snippet_with_context` here, because: + // - if `inner`'s `ctxt` is from macro, we don't lint in the first place (see the check above) + // - otherwise, calling `snippet_with_applicability` on a not-from-macro span is fine + let sugg = snippet_with_applicability(cx.sess(), inner.span, "_", &mut applicability); + span_lint_and_sugg( + cx, + DOUBLE_PARENS, + expr.span, + "unnecessary parentheses", + "remove them", + sugg.to_string(), + applicability, + ); }, // func((n)) @@ -76,26 +76,24 @@ impl EarlyLintPass for DoubleParens { // ^ inner ExprKind::Call(_, args) | ExprKind::MethodCall(box MethodCall { args, .. }) if let [arg] = &**args - && let ExprKind::Paren(inner) = &arg.kind => - { - if expr.span.eq_ctxt(arg.span) + && let ExprKind::Paren(inner) = &arg.kind + && expr.span.eq_ctxt(arg.span) && !arg.span.in_external_macro(cx.sess().source_map()) - && check_source(cx, arg) - { - // suggest removing the inner parens + && check_source(cx, arg) => + { + // suggest removing the inner parens - let mut applicability = Applicability::MachineApplicable; - let sugg = snippet_with_context(cx.sess(), inner.span, arg.span.ctxt(), "_", &mut applicability).0; - span_lint_and_sugg( - cx, - DOUBLE_PARENS, - arg.span, - "unnecessary parentheses", - "remove them", - sugg.to_string(), - applicability, - ); - } + let mut applicability = Applicability::MachineApplicable; + let sugg = snippet_with_context(cx.sess(), inner.span, arg.span.ctxt(), "_", &mut applicability).0; + span_lint_and_sugg( + cx, + DOUBLE_PARENS, + arg.span, + "unnecessary parentheses", + "remove them", + sugg.to_string(), + applicability, + ); }, _ => {}, } diff --git a/clippy_lints/src/infinite_iter.rs b/clippy_lints/src/infinite_iter.rs index 8f6de9fc60bdd..8b0a4b4d78d9a 100644 --- a/clippy_lints/src/infinite_iter.rs +++ b/clippy_lints/src/infinite_iter.rs @@ -252,10 +252,8 @@ fn complete_infinite_iter(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness { } } }, - ExprKind::Binary(op, l, r) => { - if op.node.is_comparison() { - return is_infinite(cx, l).and(is_infinite(cx, r)).and(MaybeInfinite); - } + ExprKind::Binary(op, l, r) if op.node.is_comparison() => { + return is_infinite(cx, l).and(is_infinite(cx, r)).and(MaybeInfinite); }, // TODO: ExprKind::Loop + Match _ => (), } diff --git a/clippy_lints/src/loops/utils.rs b/clippy_lints/src/loops/utils.rs index 56d535c4f2625..2c37e2679d972 100644 --- a/clippy_lints/src/loops/utils.rs +++ b/clippy_lints/src/loops/utils.rs @@ -57,19 +57,17 @@ impl<'tcx> Visitor<'tcx> for IncrementVisitor<'_, 'tcx> { } match parent.kind { - ExprKind::AssignOp(op, lhs, rhs) => { - if lhs.hir_id == expr.hir_id { - *state = if op.node == AssignOpKind::AddAssign - && is_integer_const(self.cx, rhs, 1) - && *state == IncrementVisitorVarState::Initial - && self.depth == 0 - { - IncrementVisitorVarState::IncrOnce - } else { - // Assigned some other value or assigned multiple times - IncrementVisitorVarState::DontWarn - }; - } + ExprKind::AssignOp(op, lhs, rhs) if lhs.hir_id == expr.hir_id => { + *state = if op.node == AssignOpKind::AddAssign + && is_integer_const(self.cx, rhs, 1) + && *state == IncrementVisitorVarState::Initial + && self.depth == 0 + { + IncrementVisitorVarState::IncrOnce + } else { + // Assigned some other value or assigned multiple times + IncrementVisitorVarState::DontWarn + }; }, ExprKind::Assign(lhs, _, _) if lhs.hir_id == expr.hir_id => { *state = IncrementVisitorVarState::DontWarn; diff --git a/clippy_lints/src/manual_option_as_slice.rs b/clippy_lints/src/manual_option_as_slice.rs index 5cf90eecaa978..cb451d8c0da3f 100644 --- a/clippy_lints/src/manual_option_as_slice.rs +++ b/clippy_lints/src/manual_option_as_slice.rs @@ -59,12 +59,11 @@ impl LateLintPass<'_> for ManualOptionAsSlice { return; } match expr.kind { - ExprKind::Match(scrutinee, [arm1, arm2], _) => { + ExprKind::Match(scrutinee, [arm1, arm2], _) if is_none_pattern(cx, arm2.pat) && check_arms(cx, arm2, arm1) - || is_none_pattern(cx, arm1.pat) && check_arms(cx, arm1, arm2) - { - check_as_ref(cx, scrutinee, span, self.msrv); - } + || is_none_pattern(cx, arm1.pat) && check_arms(cx, arm1, arm2) => + { + check_as_ref(cx, scrutinee, span, self.msrv); }, ExprKind::If(cond, then, Some(other)) => { if let ExprKind::Let(let_expr) = cond.kind @@ -75,34 +74,24 @@ impl LateLintPass<'_> for ManualOptionAsSlice { check_as_ref(cx, let_expr.init, span, self.msrv); } }, - ExprKind::MethodCall(seg, callee, [], _) => { - if seg.ident.name == sym::unwrap_or_default { - check_map(cx, callee, span, self.msrv); - } + ExprKind::MethodCall(seg, callee, [], _) if seg.ident.name == sym::unwrap_or_default => { + check_map(cx, callee, span, self.msrv); }, ExprKind::MethodCall(seg, callee, [or], _) => match seg.ident.name { - sym::unwrap_or => { - if is_empty_slice(cx, or) { - check_map(cx, callee, span, self.msrv); - } + sym::unwrap_or if is_empty_slice(cx, or) => { + check_map(cx, callee, span, self.msrv); }, - sym::unwrap_or_else => { - if returns_empty_slice(cx, or) { - check_map(cx, callee, span, self.msrv); - } + sym::unwrap_or_else if returns_empty_slice(cx, or) => { + check_map(cx, callee, span, self.msrv); }, _ => {}, }, ExprKind::MethodCall(seg, callee, [or_else, map], _) => match seg.ident.name { - sym::map_or => { - if is_empty_slice(cx, or_else) && is_slice_from_ref(cx, map) { - check_as_ref(cx, callee, span, self.msrv); - } + sym::map_or if is_empty_slice(cx, or_else) && is_slice_from_ref(cx, map) => { + check_as_ref(cx, callee, span, self.msrv); }, - sym::map_or_else => { - if returns_empty_slice(cx, or_else) && is_slice_from_ref(cx, map) { - check_as_ref(cx, callee, span, self.msrv); - } + sym::map_or_else if returns_empty_slice(cx, or_else) && is_slice_from_ref(cx, map) => { + check_as_ref(cx, callee, span, self.msrv); }, _ => {}, }, diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index b668f391d67a2..1cceecf3b61b9 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -242,13 +242,13 @@ fn find_stripping<'tcx>( && self.cx.qpath_res(path, ex.hir_id) == self.target { match (self.strip_kind, start, end) { - (StripKind::Prefix, Some(start), None) => { - if eq_pattern_length(self.cx, self.pattern, start, self.ctxt) { - self.results.push(ex); - return; - } + (StripKind::Prefix, Some(start), None) + if eq_pattern_length(self.cx, self.pattern, start, self.ctxt) => + { + self.results.push(ex); + return; }, - (StripKind::Suffix, None, Some(end)) => { + (StripKind::Suffix, None, Some(end)) if let ExprKind::Binary( Spanned { node: BinOpKind::Sub, .. @@ -259,11 +259,10 @@ fn find_stripping<'tcx>( && let Some(left_arg) = len_arg(self.cx, left) && let ExprKind::Path(left_path) = &left_arg.kind && self.cx.qpath_res(left_path, left_arg.hir_id) == self.target - && eq_pattern_length(self.cx, self.pattern, right, self.ctxt) - { - self.results.push(ex); - return; - } + && eq_pattern_length(self.cx, self.pattern, right, self.ctxt) => + { + self.results.push(ex); + return; }, _ => {}, } diff --git a/clippy_lints/src/matches/collapsible_match.rs b/clippy_lints/src/matches/collapsible_match.rs index 5709348c5bcde..c95a72da6e29d 100644 --- a/clippy_lints/src/matches/collapsible_match.rs +++ b/clippy_lints/src/matches/collapsible_match.rs @@ -36,7 +36,7 @@ pub(super) fn check_if_let<'tcx>( check_arm(cx, false, pat, let_expr, body, None, else_expr, msrv); } -#[expect(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments, clippy::too_many_lines)] fn check_arm<'tcx>( cx: &LateContext<'tcx>, outer_is_match: bool, diff --git a/clippy_lints/src/matches/match_single_binding.rs b/clippy_lints/src/matches/match_single_binding.rs index e40e21c490f3c..3c0fad01835f6 100644 --- a/clippy_lints/src/matches/match_single_binding.rs +++ b/clippy_lints/src/matches/match_single_binding.rs @@ -386,10 +386,8 @@ fn sugg_with_curlies<'a>( | Node::Expr(Expr { kind: ExprKind::Block(..) | ExprKind::ConstBlock(..), .. - }) => { - if needs_var_binding && is_var_binding_used_later { - add_curlies(); - } + }) if needs_var_binding && is_var_binding_used_later => { + add_curlies(); }, Node::Expr(..) | Node::AnonConst(..) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 264405e6c3fb8..b0aab3eabb675 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -5165,6 +5165,7 @@ impl Methods { format_collect::check(cx, expr, m_arg, m_ident_span); }, Some((sym::take, take_self_arg, [take_arg], _, _)) => { + #[expect(clippy::collapsible_match)] if self.msrv.meets(cx, msrvs::STR_REPEAT) { manual_str_repeat::check(cx, expr, recv, take_self_arg, take_arg); } @@ -5484,7 +5485,9 @@ impl Methods { (sym::open, [_]) => { open_options::check(cx, expr, recv); }, - (sym::or_else, [arg]) => { + (sym::or_else, [arg]) => + { + #[expect(clippy::collapsible_match)] if !bind_instead_of_map::check_or_else_err(cx, expr, recv, arg) { unnecessary_lazy_eval::check(cx, expr, recv, arg, "or"); } @@ -5589,7 +5592,9 @@ impl Methods { (sym::try_into, []) if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::TryInto) => { unnecessary_fallible_conversions::check_method(cx, expr); }, - (sym::to_owned, []) => { + (sym::to_owned, []) => + { + #[expect(clippy::collapsible_match)] if !suspicious_to_owned::check(cx, expr, span) { implicit_clone::check(cx, name, expr, recv); } diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index aed4a0075c2fd..ef956697c5c44 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -84,10 +84,10 @@ pub(super) fn check<'tcx>( return ControlFlow::Break(()); } }, - hir::ExprKind::MethodCall(..) => { - if check_or_fn_call(cx, name, method_span, receiver, arg, Some(lambda), expr.span, None) { - return ControlFlow::Break(()); - } + hir::ExprKind::MethodCall(..) + if check_or_fn_call(cx, name, method_span, receiver, arg, Some(lambda), expr.span, None) => + { + return ControlFlow::Break(()); }, _ => {}, } diff --git a/clippy_lints/src/methods/unnecessary_filter_map.rs b/clippy_lints/src/methods/unnecessary_filter_map.rs index 72f1c42da2ee3..54f3fe4105198 100644 --- a/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -131,17 +131,13 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc } (true, true) }, - hir::ExprKind::MethodCall(segment, recv, [arg], _) => { + hir::ExprKind::MethodCall(segment, recv, [arg], _) if segment.ident.name == sym::then_some && cx.typeck_results().expr_ty(recv).is_bool() - && arg.res_local_id() == Some(arg_id) - { - // bool.then_some(arg_id) - (false, true) - } else { - // bool.then_some(not arg_id) - (true, true) - } + && arg.res_local_id() == Some(arg_id) => + { + // bool.then_some(arg_id) + (false, true) }, hir::ExprKind::Block(block, _) => block .expr diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index 607aaef9627bd..bc7e2cd6ec1cb 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -482,15 +482,11 @@ fn get_input_traits_and_projections<'tcx>( let mut projection_predicates = Vec::new(); for predicate in cx.tcx.param_env(callee_def_id).caller_bounds() { match predicate.kind().skip_binder() { - ClauseKind::Trait(trait_predicate) => { - if trait_predicate.trait_ref.self_ty() == input { - trait_predicates.push(trait_predicate); - } + ClauseKind::Trait(trait_predicate) if trait_predicate.trait_ref.self_ty() == input => { + trait_predicates.push(trait_predicate); }, - ClauseKind::Projection(projection_predicate) => { - if projection_predicate.projection_term.self_ty() == input { - projection_predicates.push(projection_predicate); - } + ClauseKind::Projection(projection_predicate) if projection_predicate.projection_term.self_ty() == input => { + projection_predicates.push(projection_predicate); }, _ => {}, } diff --git a/clippy_lints/src/mixed_read_write_in_expression.rs b/clippy_lints/src/mixed_read_write_in_expression.rs index ddd4271960e13..53f97abe0b449 100644 --- a/clippy_lints/src/mixed_read_write_in_expression.rs +++ b/clippy_lints/src/mixed_read_write_in_expression.rs @@ -154,18 +154,14 @@ impl<'tcx> Visitor<'tcx> for DivergenceVisitor<'_, 'tcx> { match e.kind { // fix #10776 ExprKind::Block(block, ..) => match (block.stmts, block.expr) { - (stmts, Some(e)) => { - if stmts.iter().all(|stmt| !stmt_might_diverge(stmt)) { - self.visit_expr(e); - } + (stmts, Some(e)) if stmts.iter().all(|stmt| !stmt_might_diverge(stmt)) => { + self.visit_expr(e); }, - ([first @ .., stmt], None) => { - if first.iter().all(|stmt| !stmt_might_diverge(stmt)) { - match stmt.kind { - StmtKind::Expr(e) | StmtKind::Semi(e) => self.visit_expr(e), - _ => {}, - } - } + ([first @ .., stmt], None) + if first.iter().all(|stmt| !stmt_might_diverge(stmt)) + && let StmtKind::Expr(e) | StmtKind::Semi(e) = stmt.kind => + { + self.visit_expr(e); }, _ => {}, }, diff --git a/clippy_lints/src/multiple_unsafe_ops_per_block.rs b/clippy_lints/src/multiple_unsafe_ops_per_block.rs index 42dc9f2f1fa83..ebf7ccfd5ba7c 100644 --- a/clippy_lints/src/multiple_unsafe_ops_per_block.rs +++ b/clippy_lints/src/multiple_unsafe_ops_per_block.rs @@ -165,10 +165,8 @@ impl<'tcx> Visitor<'tcx> for UnsafeExprCollector<'tcx> { return self.visit_expr(inner); }, - ExprKind::Field(e, _) => { - if self.typeck_results.expr_ty(e).is_union() { - self.insert_span(expr.span, "union field access occurs here"); - } + ExprKind::Field(e, _) if self.typeck_results.expr_ty(e).is_union() => { + self.insert_span(expr.span, "union field access occurs here"); }, ExprKind::Path(QPath::Resolved( 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 fd5562f310e48..3c4dfade34399 100644 --- a/clippy_lints/src/non_send_fields_in_send_ty.rs +++ b/clippy_lints/src/non_send_fields_in_send_ty.rs @@ -205,17 +205,13 @@ fn ty_allowed_with_raw_pointer_heuristic<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'t .iter() .all(|ty| ty_allowed_with_raw_pointer_heuristic(cx, ty, send_trait)), ty::Array(ty, _) | ty::Slice(ty) => ty_allowed_with_raw_pointer_heuristic(cx, *ty, send_trait), - ty::Adt(_, args) => { - if contains_pointer_like(cx, ty) { - // descends only if ADT contains any raw pointers - args.iter().all(|generic_arg| match generic_arg.kind() { - GenericArgKind::Type(ty) => ty_allowed_with_raw_pointer_heuristic(cx, ty, send_trait), - // Lifetimes and const generics are not solid part of ADT and ignored - GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => true, - }) - } else { - false - } + ty::Adt(_, args) if contains_pointer_like(cx, ty) => { + // descends only if ADT contains any raw pointers + args.iter().all(|generic_arg| match generic_arg.kind() { + GenericArgKind::Type(ty) => ty_allowed_with_raw_pointer_heuristic(cx, ty, send_trait), + // Lifetimes and const generics are not solid part of ADT and ignored + GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => true, + }) }, // Raw pointers are `!Send` but allowed by the heuristic ty::RawPtr(_, _) => true, @@ -231,10 +227,8 @@ fn contains_pointer_like<'tcx>(cx: &LateContext<'tcx>, target_ty: Ty<'tcx>) -> b ty::RawPtr(_, _) => { return true; }, - ty::Adt(adt_def, _) => { - if cx.tcx.is_diagnostic_item(sym::NonNull, adt_def.did()) { - return true; - } + ty::Adt(adt_def, _) if cx.tcx.is_diagnostic_item(sym::NonNull, adt_def.did()) => { + return true; }, _ => (), } diff --git a/clippy_lints/src/operators/bit_mask.rs b/clippy_lints/src/operators/bit_mask.rs index d6af0234f010b..7f6dea573de83 100644 --- a/clippy_lints/src/operators/bit_mask.rs +++ b/clippy_lints/src/operators/bit_mask.rs @@ -71,15 +71,13 @@ fn check_bit_mask( span_lint(cx, BAD_BIT_MASK, span, "&-masking with zero"); } }, - BinOpKind::BitOr => { - if mask_value | cmp_value != cmp_value { - span_lint( - cx, - BAD_BIT_MASK, - span, - format!("incompatible bit mask: `_ | {mask_value}` can never be equal to `{cmp_value}`"), - ); - } + BinOpKind::BitOr if mask_value | cmp_value != cmp_value => { + span_lint( + cx, + BAD_BIT_MASK, + span, + format!("incompatible bit mask: `_ | {mask_value}` can never be equal to `{cmp_value}`"), + ); }, _ => (), }, diff --git a/clippy_lints/src/operators/identity_op.rs b/clippy_lints/src/operators/identity_op.rs index 43c62e1e131a3..f179672b44c79 100644 --- a/clippy_lints/src/operators/identity_op.rs +++ b/clippy_lints/src/operators/identity_op.rs @@ -52,11 +52,9 @@ pub(crate) fn check<'tcx>( span_ineffective_operation(cx, expr.span, peeled_left_span, paren, left_is_coerced_to_value); } }, - BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => { - if is_redundant_op(cx, right, 0, ctxt) { - let paren = needs_parenthesis(cx, expr, left); - span_ineffective_operation(cx, expr.span, peeled_left_span, paren, left_is_coerced_to_value); - } + BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub if is_redundant_op(cx, right, 0, ctxt) => { + let paren = needs_parenthesis(cx, expr, left); + span_ineffective_operation(cx, expr.span, peeled_left_span, paren, left_is_coerced_to_value); }, BinOpKind::Mul => { if is_redundant_op(cx, left, 1, ctxt) { @@ -67,11 +65,9 @@ pub(crate) fn check<'tcx>( span_ineffective_operation(cx, expr.span, peeled_left_span, paren, left_is_coerced_to_value); } }, - BinOpKind::Div => { - if is_redundant_op(cx, right, 1, ctxt) { - let paren = needs_parenthesis(cx, expr, left); - span_ineffective_operation(cx, expr.span, peeled_left_span, paren, left_is_coerced_to_value); - } + BinOpKind::Div if is_redundant_op(cx, right, 1, ctxt) => { + let paren = needs_parenthesis(cx, expr, left); + span_ineffective_operation(cx, expr.span, peeled_left_span, paren, left_is_coerced_to_value); }, BinOpKind::BitAnd => { if is_redundant_op(cx, left, -1, ctxt) { diff --git a/clippy_lints/src/operators/manual_div_ceil.rs b/clippy_lints/src/operators/manual_div_ceil.rs index 5ed923d719bc8..6751532ecbc78 100644 --- a/clippy_lints/src/operators/manual_div_ceil.rs +++ b/clippy_lints/src/operators/manual_div_ceil.rs @@ -73,14 +73,13 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, op: BinOpKind, lhs: & build_suggestion(cx, expr, inner_lhs, rhs, &mut applicability); } }, - ExprKind::MethodCall(method, receiver, [next_multiple_of_arg], _) => { - // x.next_multiple_of(Y) / Y + ExprKind::MethodCall(method, receiver, [next_multiple_of_arg], _) if method.ident.name == sym::next_multiple_of && check_int_ty(cx.typeck_results().expr_ty(receiver)) - && check_eq_expr(cx, next_multiple_of_arg, rhs) - { - build_suggestion(cx, expr, receiver, rhs, &mut applicability); - } + && check_eq_expr(cx, next_multiple_of_arg, rhs) => + { + // x.next_multiple_of(Y) / Y + build_suggestion(cx, expr, receiver, rhs, &mut applicability); }, ExprKind::Call(callee, [receiver, next_multiple_of_arg]) => { // int_type::next_multiple_of(x, Y) / Y diff --git a/clippy_lints/src/operators/mod.rs b/clippy_lints/src/operators/mod.rs index 383b135dfa506..5b00f1b0bd13f 100644 --- a/clippy_lints/src/operators/mod.rs +++ b/clippy_lints/src/operators/mod.rs @@ -1064,7 +1064,9 @@ impl<'tcx> LateLintPass<'tcx> for Operators { assign_op_pattern::check(cx, e, lhs, rhs, self.msrv); self_assignment::check(cx, e, lhs, rhs); }, - ExprKind::Unary(op, arg) => { + ExprKind::Unary(op, arg) => + { + #[expect(clippy::collapsible_match)] if op == UnOp::Neg { self.arithmetic_context.check_negate(cx, e, arg); } diff --git a/clippy_lints/src/redundant_static_lifetimes.rs b/clippy_lints/src/redundant_static_lifetimes.rs index b4e1f70d1535b..da85125730a84 100644 --- a/clippy_lints/src/redundant_static_lifetimes.rs +++ b/clippy_lints/src/redundant_static_lifetimes.rs @@ -66,25 +66,19 @@ impl RedundantStaticLifetimes { // Match the 'static lifetime if let Some(lifetime) = *optional_lifetime { match borrow_type.ty.kind { - TyKind::Path(..) | TyKind::Slice(..) | TyKind::Array(..) | TyKind::Tup(..) => { - if lifetime.ident.name == kw::StaticLifetime { - let snip = snippet(cx, borrow_type.ty.span, ""); - let sugg = format!("&{}{snip}", borrow_type.mutbl.prefix_str()); - span_lint_and_then( - cx, - REDUNDANT_STATIC_LIFETIMES, - lifetime.ident.span, - reason, - |diag| { - diag.span_suggestion( - ty.span, - "consider removing `'static`", - sugg, - Applicability::MachineApplicable, //snippet - ); - }, + TyKind::Path(..) | TyKind::Slice(..) | TyKind::Array(..) | TyKind::Tup(..) + if lifetime.ident.name == kw::StaticLifetime => + { + let snip = snippet(cx, borrow_type.ty.span, ""); + let sugg = format!("&{}{snip}", borrow_type.mutbl.prefix_str()); + span_lint_and_then(cx, REDUNDANT_STATIC_LIFETIMES, lifetime.ident.span, reason, |diag| { + diag.span_suggestion( + ty.span, + "consider removing `'static`", + sugg, + Applicability::MachineApplicable, //snippet ); - } + }); }, _ => {}, } diff --git a/clippy_lints/src/std_instead_of_core.rs b/clippy_lints/src/std_instead_of_core.rs index 8c4a50041e67d..143be5137038e 100644 --- a/clippy_lints/src/std_instead_of_core.rs +++ b/clippy_lints/src/std_instead_of_core.rs @@ -138,14 +138,7 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports { return; }, }, - sym::alloc => { - if cx.tcx.crate_name(def_id.krate) == sym::core { - (ALLOC_INSTEAD_OF_CORE, "alloc", "core") - } else { - self.lint_if_finish(cx, first_segment.ident.span, LintPoint::Conflict); - return; - } - }, + sym::alloc if cx.tcx.crate_name(def_id.krate) == sym::core => (ALLOC_INSTEAD_OF_CORE, "alloc", "core"), _ => { self.lint_if_finish(cx, first_segment.ident.span, LintPoint::Conflict); return; diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 509ad4e4fcb3b..9d17779419fbf 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -155,36 +155,32 @@ impl<'tcx> LateLintPass<'tcx> for StringAdd { }, left, _, - ) => { - if is_string(cx, left) { - if !is_lint_allowed(cx, STRING_ADD_ASSIGN, e.hir_id) { - let parent = get_parent_expr(cx, e); - if let Some(p) = parent + ) if is_string(cx, left) => { + if !is_lint_allowed(cx, STRING_ADD_ASSIGN, e.hir_id) { + let parent = get_parent_expr(cx, e); + if let Some(p) = parent && let ExprKind::Assign(target, _, _) = p.kind // avoid duplicate matches && SpanlessEq::new(cx).eq_expr(target, left) - { - return; - } + { + return; } - span_lint( - cx, - STRING_ADD, - e.span, - "you added something to a string. Consider using `String::push_str()` instead", - ); } + span_lint( + cx, + STRING_ADD, + e.span, + "you added something to a string. Consider using `String::push_str()` instead", + ); }, - ExprKind::Assign(target, src, _) => { - if is_string(cx, target) && is_add(cx, src, target) { - span_lint( - cx, - STRING_ADD_ASSIGN, - e.span, - "you assigned the result of adding something to this string. Consider using \ + ExprKind::Assign(target, src, _) if is_string(cx, target) && is_add(cx, src, target) => { + span_lint( + cx, + STRING_ADD_ASSIGN, + e.span, + "you assigned the result of adding something to this string. Consider using \ `String::push_str()` instead", - ); - } + ); }, ExprKind::Index(target, _idx, _) => { let e_ty = cx.typeck_results().expr_ty_adjusted(target).peel_refs(); diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 18fab6035f284..5aa457b9d19c4 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -142,12 +142,10 @@ impl Msrv { match (self.0, cargo_msrv) { (None, Some(cargo_msrv)) => self.0 = Some(cargo_msrv), - (Some(clippy_msrv), Some(cargo_msrv)) => { - if clippy_msrv != cargo_msrv { - sess.dcx().warn(format!( - "the MSRV in `clippy.toml` and `Cargo.toml` differ; using `{clippy_msrv}` from `clippy.toml`" - )); - } + (Some(clippy_msrv), Some(cargo_msrv)) if clippy_msrv != cargo_msrv => { + sess.dcx().warn(format!( + "the MSRV in `clippy.toml` and `Cargo.toml` differ; using `{clippy_msrv}` from `clippy.toml`" + )); }, _ => {}, } diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index 639492b75747a..bd56a3d86b449 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -114,16 +114,15 @@ pub fn contains_ty_adt_constructor_opaque<'tcx>(cx: &LateContext<'tcx>, ty: Ty<' match predicate.kind().skip_binder() { // For `impl Trait`, it will register a predicate of `T: Trait`, so we go through // and check substitutions to find `U`. - ty::ClauseKind::Trait(trait_predicate) => { + ty::ClauseKind::Trait(trait_predicate) if trait_predicate .trait_ref .args .types() .skip(1) // Skip the implicit `Self` generic parameter - .any(|ty| contains_ty_adt_constructor_opaque_inner(cx, ty, needle, seen)) - { - return true; - } + .any(|ty| contains_ty_adt_constructor_opaque_inner(cx, ty, needle, seen)) => + { + return true; }, // For `impl Trait`, it will register a predicate of `::Assoc = U`, // so we check the term for `U`. diff --git a/tests/missing-test-files.rs b/tests/missing-test-files.rs index 9fff3132498d8..dbb25ac545fc3 100644 --- a/tests/missing-test-files.rs +++ b/tests/missing-test-files.rs @@ -53,10 +53,8 @@ fn explore_directory(dir: &Path) -> Vec { if let Some(ext) = path.extension() { match ext.to_str().unwrap() { "rs" | "toml" => current_file.clone_from(&file_prefix), - "stderr" | "stdout" => { - if file_prefix != current_file { - missing_files.push(path.to_str().unwrap().to_string()); - } + "stderr" | "stdout" if file_prefix != current_file => { + missing_files.push(path.to_str().unwrap().to_string()); }, _ => {}, } From ec1e165f7f3cae8c23b61a40b1a4da7471ba07b0 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Sat, 14 Feb 2026 17:14:51 +0000 Subject: [PATCH 27/57] Provide all lint group names to Clippy Unblocks https://github.com/rust-lang/rust-clippy/pull/14689 --- clippy_lints/src/cargo/lint_groups_priority.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/cargo/lint_groups_priority.rs b/clippy_lints/src/cargo/lint_groups_priority.rs index f937f065d6e0d..fbf5f72c53615 100644 --- a/clippy_lints/src/cargo/lint_groups_priority.rs +++ b/clippy_lints/src/cargo/lint_groups_priority.rs @@ -135,7 +135,7 @@ pub fn check(cx: &LateContext<'_>) { { let mut rustc_groups = FxHashSet::default(); let mut clippy_groups = FxHashSet::default(); - for (group, ..) in unerased_lint_store(cx.tcx.sess).get_lint_groups() { + for group in unerased_lint_store(cx.tcx.sess).get_all_group_names() { match group.split_once("::") { None => { rustc_groups.insert(group); From 32d32b68912a07b0b47705d05cc0031d8038c672 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 9 Nov 2025 12:55:51 +0100 Subject: [PATCH 28/57] adjust clippy to fix some of the issues --- clippy_utils/src/higher.rs | 8 ++--- clippy_utils/src/sym.rs | 1 + tests/ui/manual_map_option.fixed | 5 +-- tests/ui/manual_map_option.rs | 1 + tests/ui/manual_map_option.stderr | 18 ++++++++--- tests/ui/or_fun_call.fixed | 6 ++-- tests/ui/or_fun_call.rs | 6 ++-- tests/ui/unwrap_or_else_default.fixed | 3 +- tests/ui/unwrap_or_else_default.rs | 1 - tests/ui/unwrap_or_else_default.stderr | 42 +++++++++++--------------- 10 files changed, 46 insertions(+), 45 deletions(-) diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index 7d6787fec295d..9c0b136e02b5c 100644 --- a/clippy_utils/src/higher.rs +++ b/clippy_utils/src/higher.rs @@ -297,12 +297,12 @@ impl<'a> VecArgs<'a> { // `vec![elem; size]` case Some(VecArgs::Repeat(elem, size)) }, - (sym::slice_into_vec, [slice]) - if let ExprKind::Call(_, [arg]) = slice.kind - && let ExprKind::Array(args) = arg.kind => + (sym::box_assume_init_into_vec_unsafe, [write_box_via_move]) + if let ExprKind::Call(_, [_box, elems]) = write_box_via_move.kind + && let ExprKind::Array(elems) = elems.kind => { // `vec![a, b, c]` case - Some(VecArgs::Vec(args)) + Some(VecArgs::Vec(elems)) }, (sym::vec_new, []) => Some(VecArgs::Vec(&[])), _ => None, diff --git a/clippy_utils/src/sym.rs b/clippy_utils/src/sym.rs index e0b03ae4f7b25..bd99525a86fb5 100644 --- a/clippy_utils/src/sym.rs +++ b/clippy_utils/src/sym.rs @@ -91,6 +91,7 @@ generate! { author, borrow, borrow_mut, + box_assume_init_into_vec_unsafe, build_hasher, by_ref, bytes, diff --git a/tests/ui/manual_map_option.fixed b/tests/ui/manual_map_option.fixed index 3586979ab358b..e007f7d47b24d 100644 --- a/tests/ui/manual_map_option.fixed +++ b/tests/ui/manual_map_option.fixed @@ -113,10 +113,7 @@ fn main() { } // #6811 - match Some(0) { - Some(x) => Some(vec![x]), - None => None, - }; + Some(0).map(|x| vec![x]); // Don't lint, coercion let x: Option> = match Some(()) { diff --git a/tests/ui/manual_map_option.rs b/tests/ui/manual_map_option.rs index 40133748d4599..8aa51bab6f838 100644 --- a/tests/ui/manual_map_option.rs +++ b/tests/ui/manual_map_option.rs @@ -183,6 +183,7 @@ fn main() { // #6811 match Some(0) { + //~^ manual_map Some(x) => Some(vec![x]), None => None, }; diff --git a/tests/ui/manual_map_option.stderr b/tests/ui/manual_map_option.stderr index 486379c1e5f33..7d00c1757bb36 100644 --- a/tests/ui/manual_map_option.stderr +++ b/tests/ui/manual_map_option.stderr @@ -173,7 +173,17 @@ LL | | }; | |_____^ help: try: `Some((String::new(), "test")).as_ref().map(|(x, y)| (y, x))` error: manual implementation of `Option::map` - --> tests/ui/manual_map_option.rs:196:5 + --> tests/ui/manual_map_option.rs:185:5 + | +LL | / match Some(0) { +LL | | +LL | | Some(x) => Some(vec![x]), +LL | | None => None, +LL | | }; + | |_____^ help: try: `Some(0).map(|x| vec![x])` + +error: manual implementation of `Option::map` + --> tests/ui/manual_map_option.rs:197:5 | LL | / match option_env!("") { LL | | @@ -183,7 +193,7 @@ LL | | }; | |_____^ help: try: `option_env!("").map(String::from)` error: manual implementation of `Option::map` - --> tests/ui/manual_map_option.rs:217:12 + --> tests/ui/manual_map_option.rs:218:12 | LL | } else if let Some(x) = Some(0) { | ____________^ @@ -195,7 +205,7 @@ LL | | }; | |_____^ help: try: `{ Some(0).map(|x| x + 1) }` error: manual implementation of `Option::map` - --> tests/ui/manual_map_option.rs:226:12 + --> tests/ui/manual_map_option.rs:227:12 | LL | } else if let Some(x) = Some(0) { | ____________^ @@ -206,5 +216,5 @@ LL | | None LL | | }; | |_____^ help: try: `{ Some(0).map(|x| x + 1) }` -error: aborting due to 20 previous errors +error: aborting due to 21 previous errors diff --git a/tests/ui/or_fun_call.fixed b/tests/ui/or_fun_call.fixed index 314da0804a5fa..0d6f2aefa8a8b 100644 --- a/tests/ui/or_fun_call.fixed +++ b/tests/ui/or_fun_call.fixed @@ -53,7 +53,7 @@ fn or_fun_call() { with_constructor.unwrap_or_else(make); //~^ or_fun_call - let with_new = Some(vec![1]); + let with_new: Option> = Some(vec![1]); with_new.unwrap_or_default(); //~^ unwrap_or_default @@ -101,7 +101,7 @@ fn or_fun_call() { real_default.unwrap_or_default(); //~^ unwrap_or_default - let with_vec = Some(vec![1]); + let with_vec: Option> = Some(vec![1]); with_vec.unwrap_or_default(); //~^ unwrap_or_default @@ -329,7 +329,7 @@ mod lazy { } } - let with_new = Some(vec![1]); + let with_new: Option> = Some(vec![1]); with_new.unwrap_or_default(); //~^ unwrap_or_default diff --git a/tests/ui/or_fun_call.rs b/tests/ui/or_fun_call.rs index 2a19614026ec5..e1946847698be 100644 --- a/tests/ui/or_fun_call.rs +++ b/tests/ui/or_fun_call.rs @@ -53,7 +53,7 @@ fn or_fun_call() { with_constructor.unwrap_or(make()); //~^ or_fun_call - let with_new = Some(vec![1]); + let with_new: Option> = Some(vec![1]); with_new.unwrap_or(Vec::new()); //~^ unwrap_or_default @@ -101,7 +101,7 @@ fn or_fun_call() { real_default.unwrap_or(::default()); //~^ unwrap_or_default - let with_vec = Some(vec![1]); + let with_vec: Option> = Some(vec![1]); with_vec.unwrap_or(Vec::new()); //~^ unwrap_or_default @@ -329,7 +329,7 @@ mod lazy { } } - let with_new = Some(vec![1]); + let with_new: Option> = Some(vec![1]); with_new.unwrap_or_else(Vec::new); //~^ unwrap_or_default diff --git a/tests/ui/unwrap_or_else_default.fixed b/tests/ui/unwrap_or_else_default.fixed index 561cbce473deb..5cf529fe01433 100644 --- a/tests/ui/unwrap_or_else_default.fixed +++ b/tests/ui/unwrap_or_else_default.fixed @@ -43,8 +43,7 @@ fn unwrap_or_else_default() { with_enum.unwrap_or_else(Enum::A); let with_new = Some(vec![1]); - with_new.unwrap_or_default(); - //~^ unwrap_or_default + with_new.unwrap_or_else(Vec::new); let with_err: Result<_, ()> = Ok(vec![1]); with_err.unwrap_or_else(make); diff --git a/tests/ui/unwrap_or_else_default.rs b/tests/ui/unwrap_or_else_default.rs index 8389be964fe6d..e49ff13e1f2f9 100644 --- a/tests/ui/unwrap_or_else_default.rs +++ b/tests/ui/unwrap_or_else_default.rs @@ -44,7 +44,6 @@ fn unwrap_or_else_default() { let with_new = Some(vec![1]); with_new.unwrap_or_else(Vec::new); - //~^ unwrap_or_default let with_err: Result<_, ()> = Ok(vec![1]); with_err.unwrap_or_else(make); diff --git a/tests/ui/unwrap_or_else_default.stderr b/tests/ui/unwrap_or_else_default.stderr index a001f7e46add9..610f20fa62b18 100644 --- a/tests/ui/unwrap_or_else_default.stderr +++ b/tests/ui/unwrap_or_else_default.stderr @@ -1,101 +1,95 @@ error: use of `unwrap_or_else` to construct default value - --> tests/ui/unwrap_or_else_default.rs:46:14 + --> tests/ui/unwrap_or_else_default.rs:60:23 | -LL | with_new.unwrap_or_else(Vec::new); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` +LL | with_real_default.unwrap_or_else(::default); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` | = note: `-D clippy::unwrap-or-default` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unwrap_or_default)]` error: use of `unwrap_or_else` to construct default value - --> tests/ui/unwrap_or_else_default.rs:61:23 - | -LL | with_real_default.unwrap_or_else(::default); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` - -error: use of `unwrap_or_else` to construct default value - --> tests/ui/unwrap_or_else_default.rs:65:24 + --> tests/ui/unwrap_or_else_default.rs:64:24 | LL | with_default_trait.unwrap_or_else(Default::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/unwrap_or_else_default.rs:69:23 + --> tests/ui/unwrap_or_else_default.rs:68:23 | LL | with_default_type.unwrap_or_else(u64::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/unwrap_or_else_default.rs:73:23 + --> tests/ui/unwrap_or_else_default.rs:72:23 | LL | with_default_type.unwrap_or_else(Vec::new); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/unwrap_or_else_default.rs:77:18 + --> tests/ui/unwrap_or_else_default.rs:76:18 | LL | empty_string.unwrap_or_else(|| "".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/unwrap_or_else_default.rs:82:12 + --> tests/ui/unwrap_or_else_default.rs:81:12 | LL | option.unwrap_or_else(Vec::new).push(1); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/unwrap_or_else_default.rs:86:12 + --> tests/ui/unwrap_or_else_default.rs:85:12 | LL | option.unwrap_or_else(Vec::new).push(1); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/unwrap_or_else_default.rs:90:12 + --> tests/ui/unwrap_or_else_default.rs:89:12 | LL | option.unwrap_or_else(Vec::new).push(1); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/unwrap_or_else_default.rs:94:12 + --> tests/ui/unwrap_or_else_default.rs:93:12 | LL | option.unwrap_or_else(Vec::new).push(1); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/unwrap_or_else_default.rs:98:12 + --> tests/ui/unwrap_or_else_default.rs:97:12 | LL | option.unwrap_or_else(Vec::new).push(1); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/unwrap_or_else_default.rs:102:12 + --> tests/ui/unwrap_or_else_default.rs:101:12 | LL | option.unwrap_or_else(Vec::new).push(1); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/unwrap_or_else_default.rs:106:12 + --> tests/ui/unwrap_or_else_default.rs:105:12 | LL | option.unwrap_or_else(Vec::new).push(1); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/unwrap_or_else_default.rs:110:12 + --> tests/ui/unwrap_or_else_default.rs:109:12 | LL | option.unwrap_or_else(Vec::new).push(1); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `unwrap_or_else` to construct default value - --> tests/ui/unwrap_or_else_default.rs:127:12 + --> tests/ui/unwrap_or_else_default.rs:126:12 | LL | option.unwrap_or_else(Vec::new).push(1); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` error: use of `or_insert_with` to construct default value - --> tests/ui/unwrap_or_else_default.rs:145:32 + --> tests/ui/unwrap_or_else_default.rs:144:32 | LL | let _ = inner_map.entry(0).or_insert_with(Default::default); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()` -error: aborting due to 16 previous errors +error: aborting due to 15 previous errors From 175070309c3270e1ed3a0685619ff9f6323003b5 Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Wed, 15 Oct 2025 23:08:15 +0000 Subject: [PATCH 29/57] Remove ShallowInitBox. --- clippy_utils/src/qualify_min_const_fn.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index 06220f91c7458..1484b8c8bcc45 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -194,7 +194,6 @@ fn check_rvalue<'tcx>( )) } }, - Rvalue::ShallowInitBox(_, _) => Ok(()), Rvalue::UnaryOp(_, operand) => { let ty = operand.ty(body, cx.tcx); if ty.is_integral() || ty.is_bool() { From bd21958f8bcbb67a68c2effc1b0ba45dc0e7e024 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Tue, 17 Feb 2026 06:32:43 -0500 Subject: [PATCH 30/57] Remove `env` macro use from ui tests. --- tests/ui/cmp_owned/with_suggestion.fixed | 10 ++++++++-- tests/ui/cmp_owned/with_suggestion.rs | 10 ++++++++-- tests/ui/cmp_owned/with_suggestion.stderr | 4 ++-- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/tests/ui/cmp_owned/with_suggestion.fixed b/tests/ui/cmp_owned/with_suggestion.fixed index f65339605e756..471905b47df7f 100644 --- a/tests/ui/cmp_owned/with_suggestion.fixed +++ b/tests/ui/cmp_owned/with_suggestion.fixed @@ -114,6 +114,12 @@ fn issue16322(item: String) { } fn issue16458() { + macro_rules! m { + () => { + "" + }; + } + macro_rules! partly_comes_from_macro { ($i:ident: $ty:ty, $def:expr) => { let _ = { @@ -125,7 +131,7 @@ fn issue16458() { } partly_comes_from_macro! { - required_version: String, env!("HOME").to_string() + required_version: String, m!().to_string() } macro_rules! all_comes_from_macro { @@ -141,6 +147,6 @@ fn issue16458() { }; } all_comes_from_macro! { - required_version: String, env!("HOME").to_string(); + required_version: String, m!().to_string(); } } diff --git a/tests/ui/cmp_owned/with_suggestion.rs b/tests/ui/cmp_owned/with_suggestion.rs index ed2300c80eaa9..3323176d34e39 100644 --- a/tests/ui/cmp_owned/with_suggestion.rs +++ b/tests/ui/cmp_owned/with_suggestion.rs @@ -114,6 +114,12 @@ fn issue16322(item: String) { } fn issue16458() { + macro_rules! m { + () => { + "" + }; + } + macro_rules! partly_comes_from_macro { ($i:ident: $ty:ty, $def:expr) => { let _ = { @@ -125,7 +131,7 @@ fn issue16458() { } partly_comes_from_macro! { - required_version: String, env!("HOME").to_string() + required_version: String, m!().to_string() } macro_rules! all_comes_from_macro { @@ -141,6 +147,6 @@ fn issue16458() { }; } all_comes_from_macro! { - required_version: String, env!("HOME").to_string(); + required_version: String, m!().to_string(); } } diff --git a/tests/ui/cmp_owned/with_suggestion.stderr b/tests/ui/cmp_owned/with_suggestion.stderr index 38d124baa4b56..3797810e3b982 100644 --- a/tests/ui/cmp_owned/with_suggestion.stderr +++ b/tests/ui/cmp_owned/with_suggestion.stderr @@ -56,13 +56,13 @@ LL | if item == t!(frohes_neu_Jahr).to_string() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t!(frohes_neu_Jahr)` error: this creates an owned instance just for comparison - --> tests/ui/cmp_owned/with_suggestion.rs:135:51 + --> tests/ui/cmp_owned/with_suggestion.rs:141:51 | LL | let res = <$ty>::default() == "$def".to_string(); | ^^^^^^^^^^^^^^^^^^ help: try: `"$def"` ... LL | / all_comes_from_macro! { -LL | | required_version: String, env!("HOME").to_string(); +LL | | required_version: String, m!().to_string(); LL | | } | |_____- in this macro invocation | From b8e86e69efba07012da8b8dd21a73c144a2d4ba2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 12 Aug 2025 22:27:45 +0000 Subject: [PATCH 31/57] Unify wording of resolve error Remove "failed to resolve" and use the same format we use in other resolution errors "cannot find `name`". ``` error[E0433]: cannot find `nonexistent` in `existent` --> $DIR/custom_attr_multisegment_error.rs:5:13 | LL | #[existent::nonexistent] | ^^^^^^^^^^^ could not find `nonexistent` in `existent` ``` --- tests/ui/crashes/unreachable-array-or-slice.rs | 2 +- tests/ui/crashes/unreachable-array-or-slice.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ui/crashes/unreachable-array-or-slice.rs b/tests/ui/crashes/unreachable-array-or-slice.rs index bc2e9aa92ce1e..b4f5bdb315f25 100644 --- a/tests/ui/crashes/unreachable-array-or-slice.rs +++ b/tests/ui/crashes/unreachable-array-or-slice.rs @@ -2,7 +2,7 @@ struct Foo(isize, isize, isize, isize); pub fn main() { let Self::anything_here_kills_it(a, b, ..) = Foo(5, 5, 5, 5); - //~^ ERROR: failed to resolve + //~^ ERROR: cannot find `Self` in this scope match [5, 5, 5, 5] { [..] => {}, } diff --git a/tests/ui/crashes/unreachable-array-or-slice.stderr b/tests/ui/crashes/unreachable-array-or-slice.stderr index 9e0d3b934b80f..6d7926ec9ad1b 100644 --- a/tests/ui/crashes/unreachable-array-or-slice.stderr +++ b/tests/ui/crashes/unreachable-array-or-slice.stderr @@ -1,4 +1,4 @@ -error[E0433]: failed to resolve: `Self` is only available in impls, traits, and type definitions +error[E0433]: cannot find `Self` in this scope --> tests/ui/crashes/unreachable-array-or-slice.rs:4:9 | LL | let Self::anything_here_kills_it(a, b, ..) = Foo(5, 5, 5, 5); From f72e055625674d8fb2a7da4f6d484738db122ffd Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Fri, 6 Feb 2026 14:26:36 -0500 Subject: [PATCH 32/57] Add unnecessary_trailing_comma lint Suggest removing an unnecessary trailing comma before the closing parenthesis in single-line format-like macro invocations (e.g. println!, format!, write!). The lint currently only runs on format-like macros because it relies on format-argument parsing; arbitrary user macros are not supported to avoid incorrect suggestions. - Lint is in the `style` group (allow-by-default) - Single-line only: multi-line macro invocations are not linted - Machine-applicable fix: removes the trailing comma --- CHANGELOG.md | 6 ++ clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/format_args.rs | 56 +++++++++- tests/ui/println_empty_string.fixed | 2 +- tests/ui/println_empty_string.rs | 2 +- tests/ui/unnecessary_trailing_comma.fixed | 86 +++++++++++++++ tests/ui/unnecessary_trailing_comma.rs | 86 +++++++++++++++ tests/ui/unnecessary_trailing_comma.stderr | 119 +++++++++++++++++++++ 8 files changed, 355 insertions(+), 3 deletions(-) create mode 100644 tests/ui/unnecessary_trailing_comma.fixed create mode 100644 tests/ui/unnecessary_trailing_comma.rs create mode 100644 tests/ui/unnecessary_trailing_comma.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 795eba1dfeaf0..1e8688819a886 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,11 @@ document. [92b4b68...master](https://github.com/rust-lang/rust-clippy/compare/92b4b68...master) +### New Lints + +* Added [`unnecessary_trailing_comma`] to `style` (single-line format-like macros only) + [#13965](https://github.com/rust-lang/rust-clippy/issues/13965) + ## Rust 1.93 Current stable, released 2026-01-22 @@ -7136,6 +7141,7 @@ Released 2018-09-13 [`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by [`unnecessary_struct_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_struct_initialization [`unnecessary_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_to_owned +[`unnecessary_trailing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_trailing_comma [`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap [`unnecessary_wraps`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wraps [`unneeded_field_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_field_pattern diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index a04d133b0d726..18974e8210a6d 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -173,6 +173,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::format_args::TO_STRING_IN_FORMAT_ARGS_INFO, crate::format_args::UNINLINED_FORMAT_ARGS_INFO, crate::format_args::UNNECESSARY_DEBUG_FORMATTING_INFO, + crate::format_args::UNNECESSARY_TRAILING_COMMA_INFO, crate::format_args::UNUSED_FORMAT_SPECS_INFO, crate::format_impl::PRINT_IN_FORMAT_IMPL_INFO, crate::format_impl::RECURSIVE_FORMAT_IMPL_INFO, diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index 5fb1a0b80f1a4..3eb358917a0ed 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -28,7 +28,7 @@ use rustc_middle::ty::adjustment::{Adjust, Adjustment, DerefAdjustKind}; use rustc_middle::ty::{self, GenericArg, List, TraitRef, Ty, TyCtxt, Upcast}; use rustc_session::impl_lint_pass; use rustc_span::edition::Edition::Edition2021; -use rustc_span::{Span, Symbol, sym}; +use rustc_span::{BytePos, Pos, Span, Symbol, sym}; use rustc_trait_selection::infer::TyCtxtInferExt; use rustc_trait_selection::traits::{Obligation, ObligationCause, Selection, SelectionContext}; @@ -229,6 +229,35 @@ declare_clippy_lint! { "formatting a pointer" } +declare_clippy_lint! { + /// ### What it does + /// Suggests removing an unnecessary trailing comma before the closing parenthesis in + /// single-line macro invocations. + /// + /// ### Why is this bad? + /// The trailing comma is redundant and removing it is more consistent with how + /// `rustfmt` formats regular function calls. + /// + /// ### Known limitations + /// This lint currently only runs on format-like macros (e.g. `format!`, `println!`, + /// `write!`) because it relies on format-argument parsing; applying it to arbitrary + /// user macros could cause incorrect suggestions. It may be extended to other + /// macros in the future. Only single-line macro invocations are linted. + /// + /// ### Example + /// ```no_run + /// println!("Foo={}", 1,); + /// ``` + /// Use instead: + /// ```no_run + /// println!("Foo={}", 1); + /// ``` + #[clippy::version = "1.95.0"] + pub UNNECESSARY_TRAILING_COMMA, + pedantic, + "unnecessary trailing comma before closing parenthesis" +} + impl_lint_pass!(FormatArgs<'_> => [ FORMAT_IN_FORMAT_ARGS, TO_STRING_IN_FORMAT_ARGS, @@ -236,6 +265,7 @@ impl_lint_pass!(FormatArgs<'_> => [ UNNECESSARY_DEBUG_FORMATTING, UNUSED_FORMAT_SPECS, POINTER_FORMAT, + UNNECESSARY_TRAILING_COMMA, ]); #[expect(clippy::struct_field_names)] @@ -280,6 +310,7 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs<'tcx> { has_pointer_format: &mut self.has_pointer_format, }; + linter.check_trailing_comma(); linter.check_templates(); if self.msrv.meets(cx, msrvs::FORMAT_ARGS_CAPTURE) { @@ -302,6 +333,29 @@ struct FormatArgsExpr<'a, 'tcx> { } impl<'tcx> FormatArgsExpr<'_, 'tcx> { + /// Check if there is a comma after the last format macro arg. + fn check_trailing_comma(&self) { + let span = self.macro_call.span; + if let Some(src) = span.get_source_text(self.cx) + && let Some(src) = src.strip_suffix([')', ']', '}']) + && let src = src.trim_end_matches(|c: char| c.is_whitespace() && c != '\n') + && let Some(src) = src.strip_suffix(',') + && let src = src.trim_end_matches(|c: char| c.is_whitespace() && c != '\n') + && !src.ends_with('\n') + { + span_lint_and_sugg( + self.cx, + UNNECESSARY_TRAILING_COMMA, + span.with_lo(span.lo() + BytePos::from_usize(src.len())) + .with_hi(span.hi() - BytePos(1)), + "unnecessary trailing comma", + "remove the trailing comma", + String::new(), + Applicability::MachineApplicable, + ); + } + } + fn check_templates(&mut self) { for piece in &self.format_args.template { if let FormatArgsPiece::Placeholder(placeholder) = piece diff --git a/tests/ui/println_empty_string.fixed b/tests/ui/println_empty_string.fixed index 6b1039ee8020c..2c2901bc715a6 100644 --- a/tests/ui/println_empty_string.fixed +++ b/tests/ui/println_empty_string.fixed @@ -1,4 +1,4 @@ -#![allow(clippy::match_single_binding)] +#![allow(clippy::match_single_binding, clippy::unnecessary_trailing_comma)] fn main() { println!(); diff --git a/tests/ui/println_empty_string.rs b/tests/ui/println_empty_string.rs index db3b8e1a0eac4..bc2971f54f2cd 100644 --- a/tests/ui/println_empty_string.rs +++ b/tests/ui/println_empty_string.rs @@ -1,4 +1,4 @@ -#![allow(clippy::match_single_binding)] +#![allow(clippy::match_single_binding, clippy::unnecessary_trailing_comma)] fn main() { println!(); diff --git a/tests/ui/unnecessary_trailing_comma.fixed b/tests/ui/unnecessary_trailing_comma.fixed new file mode 100644 index 0000000000000..499d9ee1ca235 --- /dev/null +++ b/tests/ui/unnecessary_trailing_comma.fixed @@ -0,0 +1,86 @@ +// run-rustfix +#![warn(clippy::unnecessary_trailing_comma)] + +fn main() {} + +// fmt breaks - https://github.com/rust-lang/rustfmt/issues/6797 +#[rustfmt::skip] +fn simple() { + println!["Foo(,)"]; + println!("Foo"); //~ unnecessary_trailing_comma + println!{"Foo"}; //~ unnecessary_trailing_comma + println!["Foo"]; //~ unnecessary_trailing_comma + println!("Foo={}", 1); //~ unnecessary_trailing_comma + println!(concat!("b", "o", "o")); //~ unnecessary_trailing_comma + println!("Foo(,)"); //~ unnecessary_trailing_comma + println!("Foo[,]"); //~ unnecessary_trailing_comma + println!["Foo(,)"]; //~ unnecessary_trailing_comma + println!["Foo[,]"]; //~ unnecessary_trailing_comma + println!["Foo{{,}}"]; //~ unnecessary_trailing_comma + println!{"Foo{{,}}"}; //~ unnecessary_trailing_comma + println!{"Foo(,)"}; //~ unnecessary_trailing_comma + println!{"Foo[,]"}; //~ unnecessary_trailing_comma + println!["Foo(,"]; //~ unnecessary_trailing_comma + println!["Foo[,"]; //~ unnecessary_trailing_comma + println!["Foo{{,}}"]; //~ unnecessary_trailing_comma + println!{"Foo{{,}}"}; //~ unnecessary_trailing_comma + println!{"Foo(,"}; //~ unnecessary_trailing_comma + println!{"Foo[,"}; //~ unnecessary_trailing_comma + + // This should eventually work, but requires more work + println!(concat!("Foo", "=", "{}"), 1,); + println!("No params", /*"a,){ */); + println!("No params" /* "a,){*/, /*"a,){ */); + + // No trailing comma - no lint + println!("{}", 1); + println!(concat!("b", "o", "o")); + println!(concat!("Foo", "=", "{}"), 1); + + println!("Foo" ); + println!{"Foo" }; + println!["Foo" ]; + println!("Foo={}", 1); + println!(concat!("b", "o", "o")); + println!("Foo(,)"); + println!("Foo[,]"); + println!["Foo(,)"]; + println!["Foo[,]"]; + println!["Foo{{,}}"]; + println!{"Foo{{,}}"}; + println!{"Foo(,)"}; + println!{"Foo[,]"}; + println!["Foo(,"]; + println!["Foo[,"]; + println!["Foo{{,}}"]; + println!{"Foo{{,}}"}; + println!{"Foo(,"}; + println!{"Foo[,"}; + + // Multi-line macro - must NOT lint (single-line only) + println!( + "very long string to prevent fmt from making it into a single line: {}", + 1, + ); + + print!("{}" + , 1 + ,); +} + +// The macro invocation itself should never be fixed +// The call to println! on the other hand might be ok to suggest in the future + +macro_rules! from_macro { + (0,) => { + println!("Foo",); + }; + (1,) => { + println!("Foo={}", 1,); + }; +} + +fn from_macro() { + from_macro!(0,); + from_macro!(1,); +} diff --git a/tests/ui/unnecessary_trailing_comma.rs b/tests/ui/unnecessary_trailing_comma.rs new file mode 100644 index 0000000000000..15dea27b887b6 --- /dev/null +++ b/tests/ui/unnecessary_trailing_comma.rs @@ -0,0 +1,86 @@ +// run-rustfix +#![warn(clippy::unnecessary_trailing_comma)] + +fn main() {} + +// fmt breaks - https://github.com/rust-lang/rustfmt/issues/6797 +#[rustfmt::skip] +fn simple() { + println!["Foo(,)"]; + println!("Foo" , ); //~ unnecessary_trailing_comma + println!{"Foo" , }; //~ unnecessary_trailing_comma + println!["Foo" , ]; //~ unnecessary_trailing_comma + println!("Foo={}", 1 , ); //~ unnecessary_trailing_comma + println!(concat!("b", "o", "o") , ); //~ unnecessary_trailing_comma + println!("Foo(,)",); //~ unnecessary_trailing_comma + println!("Foo[,]" , ); //~ unnecessary_trailing_comma + println!["Foo(,)", ]; //~ unnecessary_trailing_comma + println!["Foo[,]", ]; //~ unnecessary_trailing_comma + println!["Foo{{,}}", ]; //~ unnecessary_trailing_comma + println!{"Foo{{,}}", }; //~ unnecessary_trailing_comma + println!{"Foo(,)", }; //~ unnecessary_trailing_comma + println!{"Foo[,]", }; //~ unnecessary_trailing_comma + println!["Foo(,", ]; //~ unnecessary_trailing_comma + println!["Foo[,", ]; //~ unnecessary_trailing_comma + println!["Foo{{,}}", ]; //~ unnecessary_trailing_comma + println!{"Foo{{,}}", }; //~ unnecessary_trailing_comma + println!{"Foo(,", }; //~ unnecessary_trailing_comma + println!{"Foo[,", }; //~ unnecessary_trailing_comma + + // This should eventually work, but requires more work + println!(concat!("Foo", "=", "{}"), 1,); + println!("No params", /*"a,){ */); + println!("No params" /* "a,){*/, /*"a,){ */); + + // No trailing comma - no lint + println!("{}", 1); + println!(concat!("b", "o", "o")); + println!(concat!("Foo", "=", "{}"), 1); + + println!("Foo" ); + println!{"Foo" }; + println!["Foo" ]; + println!("Foo={}", 1); + println!(concat!("b", "o", "o")); + println!("Foo(,)"); + println!("Foo[,]"); + println!["Foo(,)"]; + println!["Foo[,]"]; + println!["Foo{{,}}"]; + println!{"Foo{{,}}"}; + println!{"Foo(,)"}; + println!{"Foo[,]"}; + println!["Foo(,"]; + println!["Foo[,"]; + println!["Foo{{,}}"]; + println!{"Foo{{,}}"}; + println!{"Foo(,"}; + println!{"Foo[,"}; + + // Multi-line macro - must NOT lint (single-line only) + println!( + "very long string to prevent fmt from making it into a single line: {}", + 1, + ); + + print!("{}" + , 1 + ,); +} + +// The macro invocation itself should never be fixed +// The call to println! on the other hand might be ok to suggest in the future + +macro_rules! from_macro { + (0,) => { + println!("Foo",); + }; + (1,) => { + println!("Foo={}", 1,); + }; +} + +fn from_macro() { + from_macro!(0,); + from_macro!(1,); +} diff --git a/tests/ui/unnecessary_trailing_comma.stderr b/tests/ui/unnecessary_trailing_comma.stderr new file mode 100644 index 0000000000000..06fd5b1861a55 --- /dev/null +++ b/tests/ui/unnecessary_trailing_comma.stderr @@ -0,0 +1,119 @@ +error: unnecessary trailing comma + --> tests/ui/unnecessary_trailing_comma.rs:10:19 + | +LL | println!("Foo" , ); + | ^^^ help: remove the trailing comma + | + = note: `-D clippy::unnecessary-trailing-comma` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_trailing_comma)]` + +error: unnecessary trailing comma + --> tests/ui/unnecessary_trailing_comma.rs:11:19 + | +LL | println!{"Foo" , }; + | ^^^ help: remove the trailing comma + +error: unnecessary trailing comma + --> tests/ui/unnecessary_trailing_comma.rs:12:19 + | +LL | println!["Foo" , ]; + | ^^^ help: remove the trailing comma + +error: unnecessary trailing comma + --> tests/ui/unnecessary_trailing_comma.rs:13:27 + | +LL | println!("Foo={}", 1 , ); + | ^^^^^ help: remove the trailing comma + +error: unnecessary trailing comma + --> tests/ui/unnecessary_trailing_comma.rs:14:36 + | +LL | println!(concat!("b", "o", "o") , ); + | ^^^^ help: remove the trailing comma + +error: unnecessary trailing comma + --> tests/ui/unnecessary_trailing_comma.rs:15:22 + | +LL | println!("Foo(,)",); + | ^ help: remove the trailing comma + +error: unnecessary trailing comma + --> tests/ui/unnecessary_trailing_comma.rs:16:22 + | +LL | println!("Foo[,]" , ); + | ^^^ help: remove the trailing comma + +error: unnecessary trailing comma + --> tests/ui/unnecessary_trailing_comma.rs:17:22 + | +LL | println!["Foo(,)", ]; + | ^^ help: remove the trailing comma + +error: unnecessary trailing comma + --> tests/ui/unnecessary_trailing_comma.rs:18:22 + | +LL | println!["Foo[,]", ]; + | ^^ help: remove the trailing comma + +error: unnecessary trailing comma + --> tests/ui/unnecessary_trailing_comma.rs:19:24 + | +LL | println!["Foo{{,}}", ]; + | ^^ help: remove the trailing comma + +error: unnecessary trailing comma + --> tests/ui/unnecessary_trailing_comma.rs:20:24 + | +LL | println!{"Foo{{,}}", }; + | ^^ help: remove the trailing comma + +error: unnecessary trailing comma + --> tests/ui/unnecessary_trailing_comma.rs:21:22 + | +LL | println!{"Foo(,)", }; + | ^^ help: remove the trailing comma + +error: unnecessary trailing comma + --> tests/ui/unnecessary_trailing_comma.rs:22:22 + | +LL | println!{"Foo[,]", }; + | ^^ help: remove the trailing comma + +error: unnecessary trailing comma + --> tests/ui/unnecessary_trailing_comma.rs:23:21 + | +LL | println!["Foo(,", ]; + | ^^ help: remove the trailing comma + +error: unnecessary trailing comma + --> tests/ui/unnecessary_trailing_comma.rs:24:21 + | +LL | println!["Foo[,", ]; + | ^^ help: remove the trailing comma + +error: unnecessary trailing comma + --> tests/ui/unnecessary_trailing_comma.rs:25:24 + | +LL | println!["Foo{{,}}", ]; + | ^^ help: remove the trailing comma + +error: unnecessary trailing comma + --> tests/ui/unnecessary_trailing_comma.rs:26:24 + | +LL | println!{"Foo{{,}}", }; + | ^^ help: remove the trailing comma + +error: unnecessary trailing comma + --> tests/ui/unnecessary_trailing_comma.rs:27:21 + | +LL | println!{"Foo(,", }; + | ^^ help: remove the trailing comma + +error: unnecessary trailing comma + --> tests/ui/unnecessary_trailing_comma.rs:28:21 + | +LL | println!{"Foo[,", }; + | ^^ help: remove the trailing comma + +error: aborting due to 19 previous errors + From 1994230db1b142789786ec38165ec07b5e38b2e9 Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Sat, 14 Feb 2026 22:28:06 -0500 Subject: [PATCH 33/57] fix(str_to_string): false positive non-str types --- clippy_lints/src/strings.rs | 4 +++- tests/ui/str_to_string.fixed | 25 ++++++++++++-------- tests/ui/str_to_string.rs | 25 ++++++++++++-------- tests/ui/str_to_string.stderr | 44 +++++++++++++++-------------------- 4 files changed, 52 insertions(+), 46 deletions(-) diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 509ad4e4fcb3b..d14a56115cbdf 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -417,6 +417,8 @@ impl<'tcx> LateLintPass<'tcx> for StrToString { && args.iter().any(|a| a.hir_id == expr.hir_id) && let Res::Def(DefKind::AssocFn, def_id) = expr.res(cx) && cx.tcx.is_diagnostic_item(sym::to_string_method, def_id) + && let Some(args) = cx.typeck_results().node_args_opt(expr.hir_id) + && args.type_at(0).is_str() { // Detected `ToString::to_string` passed as an argument (generic: any call or method call) span_lint_and_sugg( @@ -425,7 +427,7 @@ impl<'tcx> LateLintPass<'tcx> for StrToString { expr.span, "`ToString::to_string` used as `&str` to `String` converter", "try", - "ToOwned::to_owned".to_string(), + "str::to_owned".to_string(), Applicability::MachineApplicable, ); } diff --git a/tests/ui/str_to_string.fixed b/tests/ui/str_to_string.fixed index 5b76cf78f069d..a8950ab70e566 100644 --- a/tests/ui/str_to_string.fixed +++ b/tests/ui/str_to_string.fixed @@ -1,10 +1,10 @@ #![warn(clippy::str_to_string)] fn main() { - let hello = "hello world".to_owned(); + let hello: String = "hello world".to_owned(); //~^ str_to_string - let msg = &hello[..]; + let msg: &str = &hello[..]; msg.to_owned(); //~^ str_to_string } @@ -19,7 +19,7 @@ fn issue16271(key: &[u8]) { }; } - let _value = t!(str::from_utf8(key)).to_owned(); + let _value: String = t!(str::from_utf8(key)).to_owned(); //~^ str_to_string } @@ -32,22 +32,27 @@ impl GenericWrapper { } fn issue16511(x: Option<&str>) { - let _ = x.map(ToOwned::to_owned); + let _: Option = x.map(str::to_owned); //~^ str_to_string - let _ = x.map(ToOwned::to_owned); + let _: Option = x.map(str::to_owned); //~^ str_to_string - let _ = ["a", "b"].iter().map(ToOwned::to_owned); - //~^ str_to_string + // This should not trigger the lint because ToOwned::to_owned would produce &str, not String. + let _: Vec = ["a", "b"].iter().map(ToString::to_string).collect(); fn mapper String>(f: F) -> String { f("hello") } - let _ = mapper(ToOwned::to_owned); + let _: String = mapper(str::to_owned); //~^ str_to_string - let w = GenericWrapper("hello"); - let _ = w.mapper(ToOwned::to_owned); + let w: GenericWrapper<&str> = GenericWrapper("hello"); + let _: String = w.mapper(str::to_owned); //~^ str_to_string } + +// No lint: non-str types should not trigger str_to_string. See #16569 +fn no_lint_non_str() { + let _: Vec = [1, 2].iter().map(i32::to_string).collect(); +} diff --git a/tests/ui/str_to_string.rs b/tests/ui/str_to_string.rs index f099eb29b1b5f..5d78893f53f6c 100644 --- a/tests/ui/str_to_string.rs +++ b/tests/ui/str_to_string.rs @@ -1,10 +1,10 @@ #![warn(clippy::str_to_string)] fn main() { - let hello = "hello world".to_string(); + let hello: String = "hello world".to_string(); //~^ str_to_string - let msg = &hello[..]; + let msg: &str = &hello[..]; msg.to_string(); //~^ str_to_string } @@ -19,7 +19,7 @@ fn issue16271(key: &[u8]) { }; } - let _value = t!(str::from_utf8(key)).to_string(); + let _value: String = t!(str::from_utf8(key)).to_string(); //~^ str_to_string } @@ -32,22 +32,27 @@ impl GenericWrapper { } fn issue16511(x: Option<&str>) { - let _ = x.map(ToString::to_string); + let _: Option = x.map(ToString::to_string); //~^ str_to_string - let _ = x.map(str::to_string); + let _: Option = x.map(str::to_string); //~^ str_to_string - let _ = ["a", "b"].iter().map(ToString::to_string); - //~^ str_to_string + // This should not trigger the lint because ToOwned::to_owned would produce &str, not String. + let _: Vec = ["a", "b"].iter().map(ToString::to_string).collect(); fn mapper String>(f: F) -> String { f("hello") } - let _ = mapper(ToString::to_string); + let _: String = mapper(ToString::to_string); //~^ str_to_string - let w = GenericWrapper("hello"); - let _ = w.mapper(ToString::to_string); + let w: GenericWrapper<&str> = GenericWrapper("hello"); + let _: String = w.mapper(ToString::to_string); //~^ str_to_string } + +// No lint: non-str types should not trigger str_to_string. See #16569 +fn no_lint_non_str() { + let _: Vec = [1, 2].iter().map(i32::to_string).collect(); +} diff --git a/tests/ui/str_to_string.stderr b/tests/ui/str_to_string.stderr index 296b8e36f28c8..cd4db83e6370c 100644 --- a/tests/ui/str_to_string.stderr +++ b/tests/ui/str_to_string.stderr @@ -1,8 +1,8 @@ error: `to_string()` called on a `&str` - --> tests/ui/str_to_string.rs:4:17 + --> tests/ui/str_to_string.rs:4:25 | -LL | let hello = "hello world".to_string(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"hello world".to_owned()` +LL | let hello: String = "hello world".to_string(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"hello world".to_owned()` | = note: `-D clippy::str-to-string` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::str_to_string)]` @@ -14,40 +14,34 @@ LL | msg.to_string(); | ^^^^^^^^^^^^^^^ help: try: `msg.to_owned()` error: `to_string()` called on a `&str` - --> tests/ui/str_to_string.rs:22:18 + --> tests/ui/str_to_string.rs:22:26 | -LL | let _value = t!(str::from_utf8(key)).to_string(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t!(str::from_utf8(key)).to_owned()` +LL | let _value: String = t!(str::from_utf8(key)).to_string(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t!(str::from_utf8(key)).to_owned()` error: `ToString::to_string` used as `&str` to `String` converter - --> tests/ui/str_to_string.rs:35:19 + --> tests/ui/str_to_string.rs:35:35 | -LL | let _ = x.map(ToString::to_string); - | ^^^^^^^^^^^^^^^^^^^ help: try: `ToOwned::to_owned` +LL | let _: Option = x.map(ToString::to_string); + | ^^^^^^^^^^^^^^^^^^^ help: try: `str::to_owned` error: `ToString::to_string` used as `&str` to `String` converter - --> tests/ui/str_to_string.rs:38:19 + --> tests/ui/str_to_string.rs:38:35 | -LL | let _ = x.map(str::to_string); - | ^^^^^^^^^^^^^^ help: try: `ToOwned::to_owned` +LL | let _: Option = x.map(str::to_string); + | ^^^^^^^^^^^^^^ help: try: `str::to_owned` error: `ToString::to_string` used as `&str` to `String` converter - --> tests/ui/str_to_string.rs:41:35 + --> tests/ui/str_to_string.rs:47:28 | -LL | let _ = ["a", "b"].iter().map(ToString::to_string); - | ^^^^^^^^^^^^^^^^^^^ help: try: `ToOwned::to_owned` +LL | let _: String = mapper(ToString::to_string); + | ^^^^^^^^^^^^^^^^^^^ help: try: `str::to_owned` error: `ToString::to_string` used as `&str` to `String` converter - --> tests/ui/str_to_string.rs:47:20 + --> tests/ui/str_to_string.rs:51:30 | -LL | let _ = mapper(ToString::to_string); - | ^^^^^^^^^^^^^^^^^^^ help: try: `ToOwned::to_owned` +LL | let _: String = w.mapper(ToString::to_string); + | ^^^^^^^^^^^^^^^^^^^ help: try: `str::to_owned` -error: `ToString::to_string` used as `&str` to `String` converter - --> tests/ui/str_to_string.rs:51:22 - | -LL | let _ = w.mapper(ToString::to_string); - | ^^^^^^^^^^^^^^^^^^^ help: try: `ToOwned::to_owned` - -error: aborting due to 8 previous errors +error: aborting due to 7 previous errors From b0c4cbaa69f9282a29439309f143c6d36b00cdc3 Mon Sep 17 00:00:00 2001 From: "Daniel Szoke (via Pi Coding Agent)" Date: Wed, 18 Feb 2026 14:54:28 +0000 Subject: [PATCH 34/57] Fix stale metadata output comment in compile-test DiagnosticCollector currently writes util/gh-pages/index.html, but the doc comment still said util/gh-pages/lints.json. The drift came from the switch to HTML generation in b522e7a94 ("Generate lint list in HTML directly instead of JS"), which changed the output file from lints.json to index.html but left this comment unchanged. --- tests/compile-test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/compile-test.rs b/tests/compile-test.rs index a1f3224a5da95..6bdfb17481a39 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -466,7 +466,7 @@ enum DiagnosticOrMessage { } /// Collects applicabilities from the diagnostics produced for each UI test, producing the -/// `util/gh-pages/lints.json` file used by +/// `util/gh-pages/index.html` file used by #[derive(Debug, Clone)] struct DiagnosticCollector { sender: Sender>, From 4916d9538151589027f8fb243de94a1af2774f2a Mon Sep 17 00:00:00 2001 From: Taym Haddadi Date: Thu, 19 Feb 2026 09:24:28 +0100 Subject: [PATCH 35/57] Fix unnecessary_min_or_max for usize Signed-off-by: Taym Haddadi --- .../src/methods/unnecessary_min_or_max.rs | 12 ++++- tests/ui/unnecessary_min_or_max.fixed | 26 ++++++++++ tests/ui/unnecessary_min_or_max.rs | 26 ++++++++++ tests/ui/unnecessary_min_or_max.stderr | 50 ++++++++++++++++++- 4 files changed, 112 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/methods/unnecessary_min_or_max.rs b/clippy_lints/src/methods/unnecessary_min_or_max.rs index bf91a469e7f01..ea1dff6369400 100644 --- a/clippy_lints/src/methods/unnecessary_min_or_max.rs +++ b/clippy_lints/src/methods/unnecessary_min_or_max.rs @@ -88,7 +88,17 @@ fn detect_extrema<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option< match (cv.int_value(cx.tcx, ty)?, ty.kind()) { (FullInt::S(i), &ty::Int(ity)) if i == i128::MIN >> (128 - ity.bit_width()?) => Some(Extrema::Minimum), (FullInt::S(i), &ty::Int(ity)) if i == i128::MAX >> (128 - ity.bit_width()?) => Some(Extrema::Maximum), - (FullInt::U(i), &ty::Uint(uty)) if i == u128::MAX >> (128 - uty.bit_width()?) => Some(Extrema::Maximum), + (FullInt::U(i), &ty::Uint(uty)) + if { + let bits = match uty { + ty::UintTy::Usize => u32::try_from(cx.tcx.data_layout.pointer_size().bits()).ok()?, + _ => u32::try_from(uty.bit_width()?).ok()?, + }; + i == u128::MAX >> (128 - bits) + } => + { + Some(Extrema::Maximum) + }, (FullInt::U(0), &ty::Uint(_)) => Some(Extrema::Minimum), _ => None, } diff --git a/tests/ui/unnecessary_min_or_max.fixed b/tests/ui/unnecessary_min_or_max.fixed index 2650cae5baf46..e2e1700e28b99 100644 --- a/tests/ui/unnecessary_min_or_max.fixed +++ b/tests/ui/unnecessary_min_or_max.fixed @@ -77,6 +77,32 @@ fn main() { let _ = (X + 1).min(12); let _ = 12.min(X - 1); let _ = 12.max(X - 1); + + let n: usize = 42; + + let _ = 0; + //~^ unnecessary_min_or_max + + let _ = 0usize; + //~^ unnecessary_min_or_max + + let _ = (0usize); + //~^ unnecessary_min_or_max + + let _ = usize::MIN; + //~^ unnecessary_min_or_max + + let _ = usize::MAX; + //~^ unnecessary_min_or_max + + let _ = (usize::MAX); + //~^ unnecessary_min_or_max + + let _ = !0usize; + //~^ unnecessary_min_or_max + + let _ = n; + //~^ unnecessary_min_or_max } fn random_u32() -> u32 { // random number generator diff --git a/tests/ui/unnecessary_min_or_max.rs b/tests/ui/unnecessary_min_or_max.rs index 2f3c480b3d150..dd71ee2080305 100644 --- a/tests/ui/unnecessary_min_or_max.rs +++ b/tests/ui/unnecessary_min_or_max.rs @@ -77,6 +77,32 @@ fn main() { let _ = (X + 1).min(12); let _ = 12.min(X - 1); let _ = 12.max(X - 1); + + let n: usize = 42; + + let _ = n.min(0); + //~^ unnecessary_min_or_max + + let _ = n.min(0usize); + //~^ unnecessary_min_or_max + + let _ = (0usize).min(n); + //~^ unnecessary_min_or_max + + let _ = n.min(usize::MIN); + //~^ unnecessary_min_or_max + + let _ = n.max(usize::MAX); + //~^ unnecessary_min_or_max + + let _ = (usize::MAX).max(n); + //~^ unnecessary_min_or_max + + let _ = n.max(!0usize); + //~^ unnecessary_min_or_max + + let _ = n.max(0); + //~^ unnecessary_min_or_max } fn random_u32() -> u32 { // random number generator diff --git a/tests/ui/unnecessary_min_or_max.stderr b/tests/ui/unnecessary_min_or_max.stderr index dfe6910dfa5cb..b391d21d3bf43 100644 --- a/tests/ui/unnecessary_min_or_max.stderr +++ b/tests/ui/unnecessary_min_or_max.stderr @@ -103,5 +103,53 @@ error: `x` is never smaller than `i32::MIN - 0` and has therefore no effect LL | let _ = x.min(i32::MIN - 0); | ^^^^^^^^^^^^^^^^^^^ help: try: `i32::MIN - 0` -error: aborting due to 17 previous errors +error: `n` is never smaller than `0` and has therefore no effect + --> tests/ui/unnecessary_min_or_max.rs:83:13 + | +LL | let _ = n.min(0); + | ^^^^^^^^ help: try: `0` + +error: `n` is never smaller than `0usize` and has therefore no effect + --> tests/ui/unnecessary_min_or_max.rs:86:13 + | +LL | let _ = n.min(0usize); + | ^^^^^^^^^^^^^ help: try: `0usize` + +error: `(0usize)` is never greater than `n` and has therefore no effect + --> tests/ui/unnecessary_min_or_max.rs:89:13 + | +LL | let _ = (0usize).min(n); + | ^^^^^^^^^^^^^^^ help: try: `(0usize)` + +error: `n` is never smaller than `usize::MIN` and has therefore no effect + --> tests/ui/unnecessary_min_or_max.rs:92:13 + | +LL | let _ = n.min(usize::MIN); + | ^^^^^^^^^^^^^^^^^ help: try: `usize::MIN` + +error: `n` is never greater than `usize::MAX` and has therefore no effect + --> tests/ui/unnecessary_min_or_max.rs:95:13 + | +LL | let _ = n.max(usize::MAX); + | ^^^^^^^^^^^^^^^^^ help: try: `usize::MAX` + +error: `(usize::MAX)` is never smaller than `n` and has therefore no effect + --> tests/ui/unnecessary_min_or_max.rs:98:13 + | +LL | let _ = (usize::MAX).max(n); + | ^^^^^^^^^^^^^^^^^^^ help: try: `(usize::MAX)` + +error: `n` is never greater than `!0usize` and has therefore no effect + --> tests/ui/unnecessary_min_or_max.rs:101:13 + | +LL | let _ = n.max(!0usize); + | ^^^^^^^^^^^^^^ help: try: `!0usize` + +error: `n` is never smaller than `0` and has therefore no effect + --> tests/ui/unnecessary_min_or_max.rs:104:13 + | +LL | let _ = n.max(0); + | ^^^^^^^^ help: try: `n` + +error: aborting due to 25 previous errors From 3b2b250db53d23e4b77d464d4ae1ad625c7cabd2 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Thu, 19 Feb 2026 11:48:27 +0100 Subject: [PATCH 36/57] DRY: make all `span_lint_*` use `span_lint{_hir,}_and_then` This removes code duplication. Also, this limits the number of places to update when rustc lint code interface is modified to two places. --- clippy_utils/src/diagnostics.rs | 41 +++++++-------------------------- 1 file changed, 8 insertions(+), 33 deletions(-) diff --git a/clippy_utils/src/diagnostics.rs b/clippy_utils/src/diagnostics.rs index 4ba9af58f90a1..81b06ea0c539b 100644 --- a/clippy_utils/src/diagnostics.rs +++ b/clippy_utils/src/diagnostics.rs @@ -104,14 +104,7 @@ fn validate_diag(diag: &Diag<'_, impl EmissionGuarantee>) { /// ``` #[track_caller] pub fn span_lint(cx: &T, lint: &'static Lint, sp: impl Into, msg: impl Into) { - #[expect(clippy::disallowed_methods)] - cx.span_lint(lint, sp, |diag| { - diag.primary_message(msg); - docs_link(diag, lint); - - #[cfg(debug_assertions)] - validate_diag(diag); - }); + span_lint_and_then(cx, lint, sp, msg, |_| {}); } /// Same as [`span_lint`] but with an extra `help` message. @@ -157,18 +150,12 @@ pub fn span_lint_and_help( help_span: Option, help: impl Into, ) { - #[expect(clippy::disallowed_methods)] - cx.span_lint(lint, span, |diag| { - diag.primary_message(msg); + span_lint_and_then(cx, lint, span, msg, |diag| { if let Some(help_span) = help_span { diag.span_help(help_span, help.into()); } else { diag.help(help.into()); } - docs_link(diag, lint); - - #[cfg(debug_assertions)] - validate_diag(diag); }); } @@ -218,18 +205,12 @@ pub fn span_lint_and_note( note_span: Option, note: impl Into, ) { - #[expect(clippy::disallowed_methods)] - cx.span_lint(lint, span, |diag| { - diag.primary_message(msg); + span_lint_and_then(cx, lint, span, msg, |diag| { if let Some(note_span) = note_span { diag.span_note(note_span, note.into()); } else { diag.note(note.into()); } - docs_link(diag, lint); - - #[cfg(debug_assertions)] - validate_diag(diag); }); } @@ -296,14 +277,7 @@ where /// the `#[allow]` will work. #[track_caller] pub fn span_lint_hir(cx: &LateContext<'_>, lint: &'static Lint, hir_id: HirId, sp: Span, msg: impl Into) { - #[expect(clippy::disallowed_methods)] - cx.tcx.node_span_lint(lint, hir_id, sp, |diag| { - diag.primary_message(msg); - docs_link(diag, lint); - - #[cfg(debug_assertions)] - validate_diag(diag); - }); + span_lint_hir_and_then(cx, lint, hir_id, sp, msg, |_| {}); } /// Like [`span_lint_and_then`], but emits the lint at the node identified by the given `HirId`. @@ -383,7 +357,6 @@ pub fn span_lint_hir_and_then( /// | /// = note: `-D fold-any` implied by `-D warnings` /// ``` -#[cfg_attr(not(debug_assertions), expect(clippy::collapsible_span_lint_calls))] #[track_caller] pub fn span_lint_and_sugg( cx: &T, @@ -397,7 +370,9 @@ pub fn span_lint_and_sugg( span_lint_and_then(cx, lint, sp, msg.into(), |diag| { diag.span_suggestion(sp, help.into(), sugg, applicability); - #[cfg(debug_assertions)] - validate_diag(diag); + // This dummy construct is here to prevent the internal `clippy::collapsible_span_lint_calls` + // lint from triggering. We don't want to allow/expect it as internal lints might or might + // not be activated when linting, and we don't want an unknown lint warning either. + std::hint::black_box(()); }); } From 650809e8a2b5a1f96e1dda03305df10b9650485f Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Thu, 19 Feb 2026 15:39:12 +0100 Subject: [PATCH 37/57] Bump nightly version -> 2026-02-19 --- clippy_utils/README.md | 2 +- rust-toolchain.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_utils/README.md b/clippy_utils/README.md index 1b85dcfd848d5..87048ceff8914 100644 --- a/clippy_utils/README.md +++ b/clippy_utils/README.md @@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain: ``` -nightly-2026-02-11 +nightly-2026-02-19 ``` diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 558f808a7a3ed..e9b06bcbdaefa 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2026-02-11" +channel = "nightly-2026-02-19" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" From e3d2a3153e1fb72775494e1818c990612693796b Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 19 Feb 2026 15:15:58 -0800 Subject: [PATCH 38/57] `result-map-unit-fn`: use "a" before `Result` Rather than "an" --- clippy_lints/src/map_unit_fn.rs | 16 +++++---- tests/ui/result_map_unit_fn_fixable.stderr | 36 ++++++++++---------- tests/ui/result_map_unit_fn_unfixable.stderr | 12 +++---- 3 files changed, 33 insertions(+), 31 deletions(-) diff --git a/clippy_lints/src/map_unit_fn.rs b/clippy_lints/src/map_unit_fn.rs index b07d4fe81f8a9..6a02bb38d17b5 100644 --- a/clippy_lints/src/map_unit_fn.rs +++ b/clippy_lints/src/map_unit_fn.rs @@ -193,8 +193,10 @@ fn let_binding_name(cx: &LateContext<'_>, var_arg: &hir::Expr<'_>) -> String { } #[must_use] -fn suggestion_msg(function_type: &str, map_type: &str) -> String { - format!("called `map(f)` on an `{map_type}` value where `f` is a {function_type} that returns the unit type `()`") +fn suggestion_msg(function_type: &str, article: &str, map_type: &str) -> String { + format!( + "called `map(f)` on {article} `{map_type}` value where `f` is a {function_type} that returns the unit type `()`" + ) } fn lint_map_unit_fn( @@ -205,10 +207,10 @@ fn lint_map_unit_fn( ) { let var_arg = &map_args.0; - let (map_type, variant, lint) = if cx.typeck_results().expr_ty(var_arg).is_diag_item(cx, sym::Option) { - ("Option", "Some", OPTION_MAP_UNIT_FN) + let (article, map_type, variant, lint) = if cx.typeck_results().expr_ty(var_arg).is_diag_item(cx, sym::Option) { + ("an", "Option", "Some", OPTION_MAP_UNIT_FN) } else if cx.typeck_results().expr_ty(var_arg).is_diag_item(cx, sym::Result) { - ("Result", "Ok", RESULT_MAP_UNIT_FN) + ("a", "Result", "Ok", RESULT_MAP_UNIT_FN) } else { return; }; @@ -219,7 +221,7 @@ fn lint_map_unit_fn( if is_unit_function(cx, fn_arg) { let mut applicability = Applicability::MachineApplicable; - let msg = suggestion_msg("function", map_type); + let msg = suggestion_msg("function", article, map_type); let suggestion = format!( "if let {0}({binding}) = {1} {{ {2}({binding}) }}", variant, @@ -232,7 +234,7 @@ fn lint_map_unit_fn( diag.span_suggestion_verbose(stmt.span, SUGG_MSG, suggestion, applicability); }); } else if let Some((binding, closure_expr)) = unit_closure(cx, fn_arg) { - let msg = suggestion_msg("closure", map_type); + let msg = suggestion_msg("closure", article, map_type); span_lint_and_then(cx, lint, expr.span, msg, |diag| { if let Some((reduced_expr_span, is_unsafe)) = reduce_unit_expression(cx, closure_expr) { diff --git a/tests/ui/result_map_unit_fn_fixable.stderr b/tests/ui/result_map_unit_fn_fixable.stderr index d8f6239764d13..1af70ce5a1f23 100644 --- a/tests/ui/result_map_unit_fn_fixable.stderr +++ b/tests/ui/result_map_unit_fn_fixable.stderr @@ -1,4 +1,4 @@ -error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type `()` +error: called `map(f)` on a `Result` value where `f` is a function that returns the unit type `()` --> tests/ui/result_map_unit_fn_fixable.rs:32:5 | LL | x.field.map(do_nothing); @@ -12,7 +12,7 @@ LL - x.field.map(do_nothing); LL + if let Ok(x_field) = x.field { do_nothing(x_field) } | -error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type `()` +error: called `map(f)` on a `Result` value where `f` is a function that returns the unit type `()` --> tests/ui/result_map_unit_fn_fixable.rs:35:5 | LL | x.field.map(do_nothing); @@ -24,7 +24,7 @@ LL - x.field.map(do_nothing); LL + if let Ok(x_field) = x.field { do_nothing(x_field) } | -error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type `()` +error: called `map(f)` on a `Result` value where `f` is a function that returns the unit type `()` --> tests/ui/result_map_unit_fn_fixable.rs:38:5 | LL | x.field.map(diverge); @@ -36,7 +36,7 @@ LL - x.field.map(diverge); LL + if let Ok(x_field) = x.field { diverge(x_field) } | -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` +error: called `map(f)` on a `Result` value where `f` is a closure that returns the unit type `()` --> tests/ui/result_map_unit_fn_fixable.rs:45:5 | LL | x.field.map(|value| x.do_result_nothing(value + captured)); @@ -48,7 +48,7 @@ LL - x.field.map(|value| x.do_result_nothing(value + captured)); LL + if let Ok(value) = x.field { x.do_result_nothing(value + captured) } | -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` +error: called `map(f)` on a `Result` value where `f` is a closure that returns the unit type `()` --> tests/ui/result_map_unit_fn_fixable.rs:48:5 | LL | x.field.map(|value| { x.do_result_plus_one(value + captured); }); @@ -60,7 +60,7 @@ LL - x.field.map(|value| { x.do_result_plus_one(value + captured); }); LL + if let Ok(value) = x.field { x.do_result_plus_one(value + captured); } | -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` +error: called `map(f)` on a `Result` value where `f` is a closure that returns the unit type `()` --> tests/ui/result_map_unit_fn_fixable.rs:52:5 | LL | x.field.map(|value| do_nothing(value + captured)); @@ -72,7 +72,7 @@ LL - x.field.map(|value| do_nothing(value + captured)); LL + if let Ok(value) = x.field { do_nothing(value + captured) } | -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` +error: called `map(f)` on a `Result` value where `f` is a closure that returns the unit type `()` --> tests/ui/result_map_unit_fn_fixable.rs:55:5 | LL | x.field.map(|value| { do_nothing(value + captured) }); @@ -84,7 +84,7 @@ LL - x.field.map(|value| { do_nothing(value + captured) }); LL + if let Ok(value) = x.field { do_nothing(value + captured) } | -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` +error: called `map(f)` on a `Result` value where `f` is a closure that returns the unit type `()` --> tests/ui/result_map_unit_fn_fixable.rs:58:5 | LL | x.field.map(|value| { do_nothing(value + captured); }); @@ -96,7 +96,7 @@ LL - x.field.map(|value| { do_nothing(value + captured); }); LL + if let Ok(value) = x.field { do_nothing(value + captured); } | -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` +error: called `map(f)` on a `Result` value where `f` is a closure that returns the unit type `()` --> tests/ui/result_map_unit_fn_fixable.rs:61:5 | LL | x.field.map(|value| { { do_nothing(value + captured); } }); @@ -108,7 +108,7 @@ LL - x.field.map(|value| { { do_nothing(value + captured); } }); LL + if let Ok(value) = x.field { do_nothing(value + captured); } | -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` +error: called `map(f)` on a `Result` value where `f` is a closure that returns the unit type `()` --> tests/ui/result_map_unit_fn_fixable.rs:65:5 | LL | x.field.map(|value| diverge(value + captured)); @@ -120,7 +120,7 @@ LL - x.field.map(|value| diverge(value + captured)); LL + if let Ok(value) = x.field { diverge(value + captured) } | -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` +error: called `map(f)` on a `Result` value where `f` is a closure that returns the unit type `()` --> tests/ui/result_map_unit_fn_fixable.rs:68:5 | LL | x.field.map(|value| { diverge(value + captured) }); @@ -132,7 +132,7 @@ LL - x.field.map(|value| { diverge(value + captured) }); LL + if let Ok(value) = x.field { diverge(value + captured) } | -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` +error: called `map(f)` on a `Result` value where `f` is a closure that returns the unit type `()` --> tests/ui/result_map_unit_fn_fixable.rs:71:5 | LL | x.field.map(|value| { diverge(value + captured); }); @@ -144,7 +144,7 @@ LL - x.field.map(|value| { diverge(value + captured); }); LL + if let Ok(value) = x.field { diverge(value + captured); } | -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` +error: called `map(f)` on a `Result` value where `f` is a closure that returns the unit type `()` --> tests/ui/result_map_unit_fn_fixable.rs:74:5 | LL | x.field.map(|value| { { diverge(value + captured); } }); @@ -156,7 +156,7 @@ LL - x.field.map(|value| { { diverge(value + captured); } }); LL + if let Ok(value) = x.field { diverge(value + captured); } | -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` +error: called `map(f)` on a `Result` value where `f` is a closure that returns the unit type `()` --> tests/ui/result_map_unit_fn_fixable.rs:80:5 | LL | x.field.map(|value| { let y = plus_one(value + captured); }); @@ -168,7 +168,7 @@ LL - x.field.map(|value| { let y = plus_one(value + captured); }); LL + if let Ok(value) = x.field { let y = plus_one(value + captured); } | -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` +error: called `map(f)` on a `Result` value where `f` is a closure that returns the unit type `()` --> tests/ui/result_map_unit_fn_fixable.rs:83:5 | LL | x.field.map(|value| { plus_one(value + captured); }); @@ -180,7 +180,7 @@ LL - x.field.map(|value| { plus_one(value + captured); }); LL + if let Ok(value) = x.field { plus_one(value + captured); } | -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` +error: called `map(f)` on a `Result` value where `f` is a closure that returns the unit type `()` --> tests/ui/result_map_unit_fn_fixable.rs:86:5 | LL | x.field.map(|value| { { plus_one(value + captured); } }); @@ -192,7 +192,7 @@ LL - x.field.map(|value| { { plus_one(value + captured); } }); LL + if let Ok(value) = x.field { plus_one(value + captured); } | -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` +error: called `map(f)` on a `Result` value where `f` is a closure that returns the unit type `()` --> tests/ui/result_map_unit_fn_fixable.rs:90:5 | LL | x.field.map(|ref value| { do_nothing(value + captured) }); @@ -204,7 +204,7 @@ LL - x.field.map(|ref value| { do_nothing(value + captured) }); LL + if let Ok(ref value) = x.field { do_nothing(value + captured) } | -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` +error: called `map(f)` on a `Result` value where `f` is a closure that returns the unit type `()` --> tests/ui/result_map_unit_fn_fixable.rs:93:5 | LL | x.field.map(|value| println!("{value:?}")); diff --git a/tests/ui/result_map_unit_fn_unfixable.stderr b/tests/ui/result_map_unit_fn_unfixable.stderr index 9f80ec1bbbd07..c7bca9e9e02b3 100644 --- a/tests/ui/result_map_unit_fn_unfixable.stderr +++ b/tests/ui/result_map_unit_fn_unfixable.stderr @@ -1,4 +1,4 @@ -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` +error: called `map(f)` on a `Result` value where `f` is a closure that returns the unit type `()` --> tests/ui/result_map_unit_fn_unfixable.rs:24:5 | LL | x.field.map(|value| { do_nothing(value); do_nothing(value) }); @@ -12,7 +12,7 @@ LL - x.field.map(|value| { do_nothing(value); do_nothing(value) }); LL + if let Ok(value) = x.field { ... } | -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` +error: called `map(f)` on a `Result` value where `f` is a closure that returns the unit type `()` --> tests/ui/result_map_unit_fn_unfixable.rs:29:5 | LL | x.field.map(|value| if value > 0 { do_nothing(value); do_nothing(value) }); @@ -24,7 +24,7 @@ LL - x.field.map(|value| if value > 0 { do_nothing(value); do_nothing(value) LL + if let Ok(value) = x.field { ... } | -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` +error: called `map(f)` on a `Result` value where `f` is a closure that returns the unit type `()` --> tests/ui/result_map_unit_fn_unfixable.rs:35:5 | LL | / x.field.map(|value| { @@ -46,7 +46,7 @@ LL - }); LL + if let Ok(value) = x.field { ... } | -error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()` +error: called `map(f)` on a `Result` value where `f` is a closure that returns the unit type `()` --> tests/ui/result_map_unit_fn_unfixable.rs:41:5 | LL | x.field.map(|value| { do_nothing(value); do_nothing(value); }); @@ -58,7 +58,7 @@ LL - x.field.map(|value| { do_nothing(value); do_nothing(value); }); LL + if let Ok(value) = x.field { ... } | -error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type `()` +error: called `map(f)` on a `Result` value where `f` is a function that returns the unit type `()` --> tests/ui/result_map_unit_fn_unfixable.rs:47:5 | LL | "12".parse::().map(diverge); @@ -70,7 +70,7 @@ LL - "12".parse::().map(diverge); LL + if let Ok(a) = "12".parse::() { diverge(a) } | -error: called `map(f)` on an `Result` value where `f` is a function that returns the unit type `()` +error: called `map(f)` on a `Result` value where `f` is a function that returns the unit type `()` --> tests/ui/result_map_unit_fn_unfixable.rs:55:5 | LL | y.map(do_nothing); From d65fe0bb7cfb1077a575bd5335e00c6621daa2d9 Mon Sep 17 00:00:00 2001 From: lapla Date: Fri, 20 Feb 2026 11:36:50 +0900 Subject: [PATCH 39/57] `useless_conversion`: also fire inside compiler desugarings --- clippy_lints/src/useless_conversion.rs | 6 ++-- tests/ui/useless_conversion.fixed | 31 ++++++++++++++++++ tests/ui/useless_conversion.rs | 31 ++++++++++++++++++ tests/ui/useless_conversion.stderr | 44 +++++++++++++++++++++++++- 4 files changed, 109 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index 4ce132e9e3abd..e287a3de86bd7 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -157,7 +157,9 @@ fn into_iter_deep_call<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>) - impl<'tcx> LateLintPass<'tcx> for UselessConversion { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { if e.span.from_expansion() { - self.expn_depth += 1; + if e.span.desugaring_kind().is_none() { + self.expn_depth += 1; + } return; } @@ -437,7 +439,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { if Some(&e.hir_id) == self.try_desugar_arm.last() { self.try_desugar_arm.pop(); } - if e.span.from_expansion() { + if e.span.from_expansion() && e.span.desugaring_kind().is_none() { self.expn_depth -= 1; } } diff --git a/tests/ui/useless_conversion.fixed b/tests/ui/useless_conversion.fixed index 4832e922fa8e5..cbdb10351eebe 100644 --- a/tests/ui/useless_conversion.fixed +++ b/tests/ui/useless_conversion.fixed @@ -463,3 +463,34 @@ fn issue16165() { for _ in mac!(iter [1, 2]) {} //~^ useless_conversion } + +fn takes_into_iter_usize(_: impl IntoIterator) {} +fn takes_into_iter_usize_result(_: impl IntoIterator) -> Result<(), ()> { + Ok(()) +} + +async fn issue16590() { + let a: Vec = vec![]; + let b: Vec = vec![]; + + takes_into_iter_usize(b); + //~^ useless_conversion +} + +fn in_for_loop() { + let a: Vec = vec![1, 2, 3]; + let b: Vec = vec![4, 5, 6]; + + for _ in &a { + takes_into_iter_usize(b.clone()); + //~^ useless_conversion + } +} + +fn after_question_mark() -> Result<(), ()> { + let b: Vec = vec![4, 5, 6]; + + takes_into_iter_usize_result(b.clone())?; + //~^ useless_conversion + Ok(()) +} diff --git a/tests/ui/useless_conversion.rs b/tests/ui/useless_conversion.rs index 6ef1f93a5606b..c97261e0c049c 100644 --- a/tests/ui/useless_conversion.rs +++ b/tests/ui/useless_conversion.rs @@ -463,3 +463,34 @@ fn issue16165() { for _ in mac!(iter [1, 2]).into_iter() {} //~^ useless_conversion } + +fn takes_into_iter_usize(_: impl IntoIterator) {} +fn takes_into_iter_usize_result(_: impl IntoIterator) -> Result<(), ()> { + Ok(()) +} + +async fn issue16590() { + let a: Vec = vec![]; + let b: Vec = vec![]; + + takes_into_iter_usize(b.into_iter()); + //~^ useless_conversion +} + +fn in_for_loop() { + let a: Vec = vec![1, 2, 3]; + let b: Vec = vec![4, 5, 6]; + + for _ in &a { + takes_into_iter_usize(b.clone().into_iter()); + //~^ useless_conversion + } +} + +fn after_question_mark() -> Result<(), ()> { + let b: Vec = vec![4, 5, 6]; + + takes_into_iter_usize_result(b.clone().into_iter())?; + //~^ useless_conversion + Ok(()) +} diff --git a/tests/ui/useless_conversion.stderr b/tests/ui/useless_conversion.stderr index d28b7a5cbfb68..8ea7d0d5d3689 100644 --- a/tests/ui/useless_conversion.stderr +++ b/tests/ui/useless_conversion.stderr @@ -412,5 +412,47 @@ error: useless conversion to the same type: `std::slice::Iter<'_, i32>` LL | for _ in mac!(iter [1, 2]).into_iter() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `mac!(iter [1, 2])` -error: aborting due to 45 previous errors +error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` + --> tests/ui/useless_conversion.rs:476:27 + | +LL | takes_into_iter_usize(b.into_iter()); + | ^------------ + | | + | help: consider removing the `.into_iter()` + | +note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` + --> tests/ui/useless_conversion.rs:467:34 + | +LL | fn takes_into_iter_usize(_: impl IntoIterator) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` + --> tests/ui/useless_conversion.rs:485:31 + | +LL | takes_into_iter_usize(b.clone().into_iter()); + | ^^^^^^^^^------------ + | | + | help: consider removing the `.into_iter()` + | +note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` + --> tests/ui/useless_conversion.rs:467:34 + | +LL | fn takes_into_iter_usize(_: impl IntoIterator) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` + --> tests/ui/useless_conversion.rs:493:34 + | +LL | takes_into_iter_usize_result(b.clone().into_iter())?; + | ^^^^^^^^^------------ + | | + | help: consider removing the `.into_iter()` + | +note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` + --> tests/ui/useless_conversion.rs:468:41 + | +LL | fn takes_into_iter_usize_result(_: impl IntoIterator) -> Result<(), ()> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 48 previous errors From 5991fd9f76351d0359685f7b76ee9698655a0c63 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Sat, 21 Feb 2026 11:02:54 +0100 Subject: [PATCH 40/57] Add brackets around unsafe or labeled block used in `else` --- clippy_lints/src/manual_let_else.rs | 9 +++++-- tests/ui/manual_let_else.rs | 18 ++++++++++++++ tests/ui/manual_let_else.stderr | 38 ++++++++++++++++++++++++++++- 3 files changed, 62 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/manual_let_else.rs b/clippy_lints/src/manual_let_else.rs index cb3cc999c9361..8b92c3b8cbeb5 100644 --- a/clippy_lints/src/manual_let_else.rs +++ b/clippy_lints/src/manual_let_else.rs @@ -9,7 +9,9 @@ use rustc_ast::BindingMode; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_hir::def::{CtorOf, DefKind, Res}; -use rustc_hir::{Arm, Expr, ExprKind, MatchSource, Pat, PatExpr, PatExprKind, PatKind, QPath, Stmt, StmtKind}; +use rustc_hir::{ + Arm, BlockCheckMode, Expr, ExprKind, MatchSource, Pat, PatExpr, PatExprKind, PatKind, QPath, Stmt, StmtKind, +}; use rustc_lint::{LateContext, LintContext}; use rustc_span::Span; use rustc_span::symbol::{Symbol, sym}; @@ -177,7 +179,10 @@ fn emit_manual_let_else( let (sn_expr, _) = snippet_with_context(cx, expr.span, span.ctxt(), "", &mut app); let (sn_else, else_is_mac_call) = snippet_with_context(cx, else_body.span, span.ctxt(), "", &mut app); - let else_bl = if matches!(else_body.kind, ExprKind::Block(..)) && !else_is_mac_call { + let else_bl = if let ExprKind::Block(block, None) = else_body.kind + && matches!(block.rules, BlockCheckMode::DefaultBlock) + && !else_is_mac_call + { sn_else.into_owned() } else { format!("{{ {sn_else} }}") diff --git a/tests/ui/manual_let_else.rs b/tests/ui/manual_let_else.rs index 4523edec3c764..8271671645efd 100644 --- a/tests/ui/manual_let_else.rs +++ b/tests/ui/manual_let_else.rs @@ -571,3 +571,21 @@ mod issue15914 { }; } } + +fn issue16602(i: Result) { + //~v manual_let_else + _ = match i { + Ok(i) => i, + Err(_) => unsafe { + core::hint::unreachable_unchecked(); + }, + }; + + //~v manual_let_else + _ = match i { + Ok(i) => i, + Err(_) => 'useless_label: { + panic!(); + }, + }; +} diff --git a/tests/ui/manual_let_else.stderr b/tests/ui/manual_let_else.stderr index f4b1644c44baa..8e988cf189b54 100644 --- a/tests/ui/manual_let_else.stderr +++ b/tests/ui/manual_let_else.stderr @@ -585,5 +585,41 @@ LL + return; LL + }; | -error: aborting due to 37 previous errors +error: this could be rewritten as `let...else` + --> tests/ui/manual_let_else.rs:577:5 + | +LL | / _ = match i { +LL | | Ok(i) => i, +LL | | Err(_) => unsafe { +LL | | core::hint::unreachable_unchecked(); +LL | | }, +LL | | }; + | |_____^ + | +help: consider writing + | +LL ~ let Ok(_) = i else { unsafe { +LL + core::hint::unreachable_unchecked(); +LL ~ } };; + | + +error: this could be rewritten as `let...else` + --> tests/ui/manual_let_else.rs:585:5 + | +LL | / _ = match i { +LL | | Ok(i) => i, +LL | | Err(_) => 'useless_label: { +LL | | panic!(); +LL | | }, +LL | | }; + | |_____^ + | +help: consider writing + | +LL ~ let Ok(_) = i else { 'useless_label: { +LL + panic!(); +LL ~ } };; + | + +error: aborting due to 39 previous errors From 92fb0f78994bfc0bd5240ebb575448ecc210a87d Mon Sep 17 00:00:00 2001 From: mikhailofff Date: Sat, 21 Feb 2026 18:37:47 +0400 Subject: [PATCH 41/57] make unnecessary_fold commutative --- clippy_lints/src/methods/unnecessary_fold.rs | 33 +++++- tests/ui/unnecessary_fold.fixed | 30 ++++-- tests/ui/unnecessary_fold.rs | 30 ++++-- tests/ui/unnecessary_fold.stderr | 100 ++++++++++++++----- 4 files changed, 146 insertions(+), 47 deletions(-) diff --git a/clippy_lints/src/methods/unnecessary_fold.rs b/clippy_lints/src/methods/unnecessary_fold.rs index c3f031edff2e2..367d98ece1951 100644 --- a/clippy_lints/src/methods/unnecessary_fold.rs +++ b/clippy_lints/src/methods/unnecessary_fold.rs @@ -89,6 +89,28 @@ impl Replacement { } } +fn get_triggered_expr_span( + left_expr: &hir::Expr<'_>, + right_expr: &hir::Expr<'_>, + first_arg_id: hir::HirId, + second_arg_id: hir::HirId, + replacement: Replacement, +) -> Option { + if left_expr.res_local_id() == Some(first_arg_id) + && (replacement.has_args || right_expr.res_local_id() == Some(second_arg_id)) + { + right_expr.span.into() + } + // https://github.com/rust-lang/rust-clippy/issues/16581 + else if right_expr.res_local_id() == Some(first_arg_id) + && (replacement.has_args || left_expr.res_local_id() == Some(second_arg_id)) + { + left_expr.span.into() + } else { + None + } +} + fn check_fold_with_op( cx: &LateContext<'_>, expr: &hir::Expr<'_>, @@ -111,8 +133,13 @@ fn check_fold_with_op( && let PatKind::Binding(_, first_arg_id, ..) = strip_pat_refs(param_a.pat).kind && let PatKind::Binding(_, second_arg_id, second_arg_ident, _) = strip_pat_refs(param_b.pat).kind - && left_expr.res_local_id() == Some(first_arg_id) - && (replacement.has_args || right_expr.res_local_id() == Some(second_arg_id)) + && let Some(triggered_expr_span) = get_triggered_expr_span( + left_expr, + right_expr, + first_arg_id, + second_arg_id, + replacement + ) { let span = fold_span.with_hi(expr.span.hi()); span_lint_and_then( @@ -125,7 +152,7 @@ fn check_fold_with_op( let turbofish = replacement.maybe_turbofish(cx.typeck_results().expr_ty_adjusted(right_expr).peel_refs()); let (r_snippet, _) = - snippet_with_context(cx, right_expr.span, expr.span.ctxt(), "EXPR", &mut applicability); + snippet_with_context(cx, triggered_expr_span, expr.span.ctxt(), "EXPR", &mut applicability); let sugg = if replacement.has_args { format!( "{method}{turbofish}(|{second_arg_ident}| {r_snippet})", diff --git a/tests/ui/unnecessary_fold.fixed b/tests/ui/unnecessary_fold.fixed index d51359349cb9e..a8370ed03b746 100644 --- a/tests/ui/unnecessary_fold.fixed +++ b/tests/ui/unnecessary_fold.fixed @@ -88,16 +88,6 @@ fn unnecessary_fold_should_ignore() { let _: i32 = (0..3).fold(0, FakeAdd::add); let _: i32 = (0..3).fold(1, FakeMul::mul); - // We only match against an accumulator on the left - // hand side. We could lint for .sum and .product when - // it's on the right, but don't for now (and this wouldn't - // be valid if we extended the lint to cover arbitrary numeric - // types). - let _ = (0..3).fold(false, |acc, x| x > 2 || acc); - let _ = (0..3).fold(true, |acc, x| x > 2 && acc); - let _ = (0..3).fold(0, |acc, x| x + acc); - let _ = (0..3).fold(1, |acc, x| x * acc); - let _ = [(0..2), (0..3)].iter().fold(0, |a, b| a + b.len()); let _ = [(0..2), (0..3)].iter().fold(1, |a, b| a * b.len()); } @@ -178,6 +168,26 @@ fn issue10000() { } } +fn issue16581() { + let _ = (2..=3).product::(); + //~^ unnecessary_fold + let _ = (1..=3).sum::(); + //~^ unnecessary_fold + let _ = (2..=3).product::(); + //~^ unnecessary_fold + let _ = (1..=3).sum::(); + //~^ unnecessary_fold + + let _ = (0..3).any(|x| x > 2); + //~^ unnecessary_fold + let _ = (0..3).all(|x| x > 2); + //~^ unnecessary_fold + let _ = (0..3).sum::(); + //~^ unnecessary_fold + let _ = (0..3).product::(); + //~^ unnecessary_fold +} + fn wrongly_unmangled_macros() { macro_rules! test_expr { ($e:expr) => { diff --git a/tests/ui/unnecessary_fold.rs b/tests/ui/unnecessary_fold.rs index c6eb7157ab129..f495da6e720ba 100644 --- a/tests/ui/unnecessary_fold.rs +++ b/tests/ui/unnecessary_fold.rs @@ -88,16 +88,6 @@ fn unnecessary_fold_should_ignore() { let _: i32 = (0..3).fold(0, FakeAdd::add); let _: i32 = (0..3).fold(1, FakeMul::mul); - // We only match against an accumulator on the left - // hand side. We could lint for .sum and .product when - // it's on the right, but don't for now (and this wouldn't - // be valid if we extended the lint to cover arbitrary numeric - // types). - let _ = (0..3).fold(false, |acc, x| x > 2 || acc); - let _ = (0..3).fold(true, |acc, x| x > 2 && acc); - let _ = (0..3).fold(0, |acc, x| x + acc); - let _ = (0..3).fold(1, |acc, x| x * acc); - let _ = [(0..2), (0..3)].iter().fold(0, |a, b| a + b.len()); let _ = [(0..2), (0..3)].iter().fold(1, |a, b| a * b.len()); } @@ -178,6 +168,26 @@ fn issue10000() { } } +fn issue16581() { + let _ = (2..=3).fold(1, |a, b| a * b); + //~^ unnecessary_fold + let _ = (1..=3).fold(0, |a, b| a + b); + //~^ unnecessary_fold + let _ = (2..=3).fold(1, |b, a| a * b); + //~^ unnecessary_fold + let _ = (1..=3).fold(0, |b, a| a + b); + //~^ unnecessary_fold + + let _ = (0..3).fold(false, |acc, x| x > 2 || acc); + //~^ unnecessary_fold + let _ = (0..3).fold(true, |acc, x| x > 2 && acc); + //~^ unnecessary_fold + let _ = (0..3).fold(0, |acc, x| x + acc); + //~^ unnecessary_fold + let _ = (0..3).fold(1, |acc, x| x * acc); + //~^ unnecessary_fold +} + fn wrongly_unmangled_macros() { macro_rules! test_expr { ($e:expr) => { diff --git a/tests/ui/unnecessary_fold.stderr b/tests/ui/unnecessary_fold.stderr index 560427a681a98..266ced07eb827 100644 --- a/tests/ui/unnecessary_fold.stderr +++ b/tests/ui/unnecessary_fold.stderr @@ -70,7 +70,7 @@ LL | let _: bool = (0..3).map(|x| 2 * x).fold(false, |acc, x| acc || x > 2); = note: the `any` method is short circuiting and may change the program semantics if the iterator has side effects error: this `.fold` can be written more succinctly using another method - --> tests/ui/unnecessary_fold.rs:110:10 + --> tests/ui/unnecessary_fold.rs:100:10 | LL | .fold(false, |acc, x| acc || x > 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `any(|x| x > 2)` @@ -78,138 +78,190 @@ LL | .fold(false, |acc, x| acc || x > 2); = note: the `any` method is short circuiting and may change the program semantics if the iterator has side effects error: this `.fold` can be written more succinctly using another method - --> tests/ui/unnecessary_fold.rs:123:33 + --> tests/ui/unnecessary_fold.rs:113:33 | LL | assert_eq!(map.values().fold(0, |x, y| x + y), 0); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `sum::()` error: this `.fold` can be written more succinctly using another method - --> tests/ui/unnecessary_fold.rs:127:30 + --> tests/ui/unnecessary_fold.rs:117:30 | LL | let _ = map.values().fold(0, |x, y| x + y); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `sum::()` error: this `.fold` can be written more succinctly using another method - --> tests/ui/unnecessary_fold.rs:129:30 + --> tests/ui/unnecessary_fold.rs:119:30 | LL | let _ = map.values().fold(0, Add::add); | ^^^^^^^^^^^^^^^^^ help: try: `sum::()` error: this `.fold` can be written more succinctly using another method - --> tests/ui/unnecessary_fold.rs:131:30 + --> tests/ui/unnecessary_fold.rs:121:30 | LL | let _ = map.values().fold(1, |x, y| x * y); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `product::()` error: this `.fold` can be written more succinctly using another method - --> tests/ui/unnecessary_fold.rs:133:30 + --> tests/ui/unnecessary_fold.rs:123:30 | LL | let _ = map.values().fold(1, Mul::mul); | ^^^^^^^^^^^^^^^^^ help: try: `product::()` error: this `.fold` can be written more succinctly using another method - --> tests/ui/unnecessary_fold.rs:135:35 + --> tests/ui/unnecessary_fold.rs:125:35 | LL | let _: i32 = map.values().fold(0, |x, y| x + y); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `sum()` error: this `.fold` can be written more succinctly using another method - --> tests/ui/unnecessary_fold.rs:137:35 + --> tests/ui/unnecessary_fold.rs:127:35 | LL | let _: i32 = map.values().fold(0, Add::add); | ^^^^^^^^^^^^^^^^^ help: try: `sum()` error: this `.fold` can be written more succinctly using another method - --> tests/ui/unnecessary_fold.rs:139:35 + --> tests/ui/unnecessary_fold.rs:129:35 | LL | let _: i32 = map.values().fold(1, |x, y| x * y); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `product()` error: this `.fold` can be written more succinctly using another method - --> tests/ui/unnecessary_fold.rs:141:35 + --> tests/ui/unnecessary_fold.rs:131:35 | LL | let _: i32 = map.values().fold(1, Mul::mul); | ^^^^^^^^^^^^^^^^^ help: try: `product()` error: this `.fold` can be written more succinctly using another method - --> tests/ui/unnecessary_fold.rs:143:31 + --> tests/ui/unnecessary_fold.rs:133:31 | LL | anything(map.values().fold(0, |x, y| x + y)); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `sum::()` error: this `.fold` can be written more succinctly using another method - --> tests/ui/unnecessary_fold.rs:145:31 + --> tests/ui/unnecessary_fold.rs:135:31 | LL | anything(map.values().fold(0, Add::add)); | ^^^^^^^^^^^^^^^^^ help: try: `sum::()` error: this `.fold` can be written more succinctly using another method - --> tests/ui/unnecessary_fold.rs:147:31 + --> tests/ui/unnecessary_fold.rs:137:31 | LL | anything(map.values().fold(1, |x, y| x * y)); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `product::()` error: this `.fold` can be written more succinctly using another method - --> tests/ui/unnecessary_fold.rs:149:31 + --> tests/ui/unnecessary_fold.rs:139:31 | LL | anything(map.values().fold(1, Mul::mul)); | ^^^^^^^^^^^^^^^^^ help: try: `product::()` error: this `.fold` can be written more succinctly using another method - --> tests/ui/unnecessary_fold.rs:151:26 + --> tests/ui/unnecessary_fold.rs:141:26 | LL | num(map.values().fold(0, |x, y| x + y)); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `sum()` error: this `.fold` can be written more succinctly using another method - --> tests/ui/unnecessary_fold.rs:153:26 + --> tests/ui/unnecessary_fold.rs:143:26 | LL | num(map.values().fold(0, Add::add)); | ^^^^^^^^^^^^^^^^^ help: try: `sum()` error: this `.fold` can be written more succinctly using another method - --> tests/ui/unnecessary_fold.rs:155:26 + --> tests/ui/unnecessary_fold.rs:145:26 | LL | num(map.values().fold(1, |x, y| x * y)); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `product()` error: this `.fold` can be written more succinctly using another method - --> tests/ui/unnecessary_fold.rs:157:26 + --> tests/ui/unnecessary_fold.rs:147:26 | LL | num(map.values().fold(1, Mul::mul)); | ^^^^^^^^^^^^^^^^^ help: try: `product()` error: this `.fold` can be written more succinctly using another method - --> tests/ui/unnecessary_fold.rs:164:16 + --> tests/ui/unnecessary_fold.rs:154:16 | LL | (0..3).fold(0, |acc, x| acc + x) | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `sum()` error: this `.fold` can be written more succinctly using another method - --> tests/ui/unnecessary_fold.rs:168:16 + --> tests/ui/unnecessary_fold.rs:158:16 | LL | (0..3).fold(1, |acc, x| acc * x) | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `product()` error: this `.fold` can be written more succinctly using another method - --> tests/ui/unnecessary_fold.rs:172:16 + --> tests/ui/unnecessary_fold.rs:162:16 | LL | (0..3).fold(0, |acc, x| acc + x) | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `sum::()` error: this `.fold` can be written more succinctly using another method - --> tests/ui/unnecessary_fold.rs:176:16 + --> tests/ui/unnecessary_fold.rs:166:16 | LL | (0..3).fold(1, |acc, x| acc * x) | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `product::()` error: this `.fold` can be written more succinctly using another method - --> tests/ui/unnecessary_fold.rs:188:20 + --> tests/ui/unnecessary_fold.rs:172:21 + | +LL | let _ = (2..=3).fold(1, |a, b| a * b); + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `product::()` + +error: this `.fold` can be written more succinctly using another method + --> tests/ui/unnecessary_fold.rs:174:21 + | +LL | let _ = (1..=3).fold(0, |a, b| a + b); + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `sum::()` + +error: this `.fold` can be written more succinctly using another method + --> tests/ui/unnecessary_fold.rs:176:21 + | +LL | let _ = (2..=3).fold(1, |b, a| a * b); + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `product::()` + +error: this `.fold` can be written more succinctly using another method + --> tests/ui/unnecessary_fold.rs:178:21 + | +LL | let _ = (1..=3).fold(0, |b, a| a + b); + | ^^^^^^^^^^^^^^^^^^^^^ help: try: `sum::()` + +error: this `.fold` can be written more succinctly using another method + --> tests/ui/unnecessary_fold.rs:181:20 + | +LL | let _ = (0..3).fold(false, |acc, x| x > 2 || acc); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `any(|x| x > 2)` + | + = note: the `any` method is short circuiting and may change the program semantics if the iterator has side effects + +error: this `.fold` can be written more succinctly using another method + --> tests/ui/unnecessary_fold.rs:183:20 + | +LL | let _ = (0..3).fold(true, |acc, x| x > 2 && acc); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `all(|x| x > 2)` + | + = note: the `all` method is short circuiting and may change the program semantics if the iterator has side effects + +error: this `.fold` can be written more succinctly using another method + --> tests/ui/unnecessary_fold.rs:185:20 + | +LL | let _ = (0..3).fold(0, |acc, x| x + acc); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `sum::()` + +error: this `.fold` can be written more succinctly using another method + --> tests/ui/unnecessary_fold.rs:187:20 + | +LL | let _ = (0..3).fold(1, |acc, x| x * acc); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `product::()` + +error: this `.fold` can be written more succinctly using another method + --> tests/ui/unnecessary_fold.rs:198:20 | LL | let _ = (0..3).fold(false, |acc: bool, x| acc || test_expr!(x)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `any(|x| test_expr!(x))` | = note: the `any` method is short circuiting and may change the program semantics if the iterator has side effects -error: aborting due to 33 previous errors +error: aborting due to 41 previous errors From 5137bb3ceb67f14e33e34a5198c7f9db7bbbd836 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Wed, 7 Jan 2026 19:31:52 +0100 Subject: [PATCH 42/57] Handle `Result` and `ControlFlow` as `T` wrt `#[must_use]` There is a proposal to change the behaviour of rustc's `must_use` lint to consider `Result` and `ControlFlow` as `T` when `U` is uninhabited. See . This might make the user adding extra `#[must_use]` attributes to functions returning `Result` or `ControlFlow`, which would trigger the `double_must_use` lint in Clippy without the current change. --- clippy_lints/src/functions/must_use.rs | 8 +++++++ clippy_utils/src/ty/mod.rs | 14 +++++++++++-- tests/ui/double_must_use.rs | 28 +++++++++++++++++++++++++ tests/ui/double_must_use.stderr | 26 ++++++++++++++++++----- tests/ui/drop_non_drop.rs | 9 ++++++++ tests/ui/drop_non_drop.stderr | 22 ++++++++++++++----- tests/ui/let_underscore_must_use.rs | 10 +++++++++ tests/ui/let_underscore_must_use.stderr | 10 ++++++++- tests/ui/must_use_candidates.fixed | 12 +++++++++++ tests/ui/must_use_candidates.rs | 10 +++++++++ tests/ui/must_use_candidates.stderr | 28 ++++++++++++++++++++++++- 11 files changed, 163 insertions(+), 14 deletions(-) diff --git a/clippy_lints/src/functions/must_use.rs b/clippy_lints/src/functions/must_use.rs index 68532de0368f8..2eefe4e099295 100644 --- a/clippy_lints/src/functions/must_use.rs +++ b/clippy_lints/src/functions/must_use.rs @@ -1,3 +1,4 @@ +use clippy_utils::res::MaybeDef as _; use hir::FnSig; use rustc_errors::Applicability; use rustc_hir::def::Res; @@ -222,6 +223,13 @@ fn check_must_use_candidate<'tcx>( format!("#[must_use] \n{indent}"), Applicability::MachineApplicable, ); + if let Some(msg) = match return_ty(cx, item_id).opt_diag_name(cx) { + Some(sym::ControlFlow) => Some("`ControlFlow` as `C` when `B` is uninhabited"), + Some(sym::Result) => Some("`Result` as `T` when `E` is uninhabited"), + _ => None, + } { + diag.note(format!("a future version of Rust will treat {msg} wrt `#[must_use]`")); + } }); } diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index a90d64e972c1f..34d6f983daae9 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -305,10 +305,20 @@ pub fn has_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { } } -// Returns whether the type has #[must_use] attribute +// Returns whether the `ty` has `#[must_use]` attribute. If `ty` is a `Result`/`ControlFlow` +// whose `Err`/`Break` payload is an uninhabited type, the `Ok`/`Continue` payload type +// will be used instead. See . pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { match ty.kind() { - ty::Adt(adt, _) => find_attr!(cx.tcx.get_all_attrs(adt.did()), AttributeKind::MustUse { .. }), + ty::Adt(adt, args) => match cx.tcx.get_diagnostic_name(adt.did()) { + Some(sym::Result) if args.type_at(1).is_privately_uninhabited(cx.tcx, cx.typing_env()) => { + is_must_use_ty(cx, args.type_at(0)) + }, + Some(sym::ControlFlow) if args.type_at(0).is_privately_uninhabited(cx.tcx, cx.typing_env()) => { + is_must_use_ty(cx, args.type_at(1)) + }, + _ => find_attr!(cx.tcx.get_all_attrs(adt.did()), AttributeKind::MustUse { .. }), + }, ty::Foreign(did) => find_attr!(cx.tcx.get_all_attrs(*did), AttributeKind::MustUse { .. }), ty::Slice(ty) | ty::Array(ty, _) | ty::RawPtr(ty, _) | ty::Ref(_, ty, _) => { // for the Array case we don't need to care for the len == 0 case diff --git a/tests/ui/double_must_use.rs b/tests/ui/double_must_use.rs index 3d4aaa9baa49c..71995d9466d9d 100644 --- a/tests/ui/double_must_use.rs +++ b/tests/ui/double_must_use.rs @@ -1,5 +1,8 @@ #![warn(clippy::double_must_use)] #![allow(clippy::result_unit_err)] +#![feature(never_type)] + +use std::ops::ControlFlow; #[must_use] pub fn must_use_result() -> Result<(), ()> { @@ -40,6 +43,31 @@ async fn async_must_use_result() -> Result<(), ()> { Ok(()) } +#[must_use] +pub fn must_use_result_with_uninhabited() -> Result<(), !> { + unimplemented!(); +} + +#[must_use] +pub struct T; + +#[must_use] +pub fn must_use_result_with_uninhabited_2() -> Result { + //~^ double_must_use + unimplemented!(); +} + +#[must_use] +pub fn must_use_controlflow_with_uninhabited() -> ControlFlow { + unimplemented!(); +} + +#[must_use] +pub fn must_use_controlflow_with_uninhabited_2() -> ControlFlow { + //~^ double_must_use + unimplemented!(); +} + fn main() { must_use_result(); must_use_tuple(); diff --git a/tests/ui/double_must_use.stderr b/tests/ui/double_must_use.stderr index 555dd8902cac4..50b1640c47f5f 100644 --- a/tests/ui/double_must_use.stderr +++ b/tests/ui/double_must_use.stderr @@ -1,5 +1,5 @@ error: this function has a `#[must_use]` attribute with no message, but returns a type already marked as `#[must_use]` - --> tests/ui/double_must_use.rs:5:1 + --> tests/ui/double_must_use.rs:8:1 | LL | pub fn must_use_result() -> Result<(), ()> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | pub fn must_use_result() -> Result<(), ()> { = help: to override `-D warnings` add `#[allow(clippy::double_must_use)]` error: this function has a `#[must_use]` attribute with no message, but returns a type already marked as `#[must_use]` - --> tests/ui/double_must_use.rs:12:1 + --> tests/ui/double_must_use.rs:15:1 | LL | pub fn must_use_tuple() -> (Result<(), ()>, u8) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | pub fn must_use_tuple() -> (Result<(), ()>, u8) { = help: either add some descriptive message or remove the attribute error: this function has a `#[must_use]` attribute with no message, but returns a type already marked as `#[must_use]` - --> tests/ui/double_must_use.rs:19:1 + --> tests/ui/double_must_use.rs:22:1 | LL | pub fn must_use_array() -> [Result<(), ()>; 1] { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -25,12 +25,28 @@ LL | pub fn must_use_array() -> [Result<(), ()>; 1] { = help: either add some descriptive message or remove the attribute error: this function has a `#[must_use]` attribute with no message, but returns a type already marked as `#[must_use]` - --> tests/ui/double_must_use.rs:37:1 + --> tests/ui/double_must_use.rs:40:1 | LL | async fn async_must_use_result() -> Result<(), ()> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: either add some descriptive message or remove the attribute -error: aborting due to 4 previous errors +error: this function has a `#[must_use]` attribute with no message, but returns a type already marked as `#[must_use]` + --> tests/ui/double_must_use.rs:55:1 + | +LL | pub fn must_use_result_with_uninhabited_2() -> Result { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: either add some descriptive message or remove the attribute + +error: this function has a `#[must_use]` attribute with no message, but returns a type already marked as `#[must_use]` + --> tests/ui/double_must_use.rs:66:1 + | +LL | pub fn must_use_controlflow_with_uninhabited_2() -> ControlFlow { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: either add some descriptive message or remove the attribute + +error: aborting due to 6 previous errors diff --git a/tests/ui/drop_non_drop.rs b/tests/ui/drop_non_drop.rs index 0345e8670ab83..e6433de5163d8 100644 --- a/tests/ui/drop_non_drop.rs +++ b/tests/ui/drop_non_drop.rs @@ -6,6 +6,11 @@ fn make_result(t: T) -> Result { Ok(t) } +// The return type should behave as `T` as the `Err` variant is uninhabited +fn make_result_uninhabited_err(t: T) -> Result { + Ok(t) +} + #[must_use] fn must_use(t: T) -> T { t @@ -41,4 +46,8 @@ fn main() { // Don't lint drop(Baz(Bar)); + + // Lint + drop(make_result_uninhabited_err(Foo)); + //~^ drop_non_drop } diff --git a/tests/ui/drop_non_drop.stderr b/tests/ui/drop_non_drop.stderr index b431c62c92c57..567a820990c60 100644 --- a/tests/ui/drop_non_drop.stderr +++ b/tests/ui/drop_non_drop.stderr @@ -1,11 +1,11 @@ error: call to `std::mem::drop` with a value that does not implement `Drop`. Dropping such a type only extends its contained lifetimes - --> tests/ui/drop_non_drop.rs:22:5 + --> tests/ui/drop_non_drop.rs:27:5 | LL | drop(Foo); | ^^^^^^^^^ | note: argument has type `main::Foo` - --> tests/ui/drop_non_drop.rs:22:10 + --> tests/ui/drop_non_drop.rs:27:10 | LL | drop(Foo); | ^^^ @@ -13,16 +13,28 @@ LL | drop(Foo); = help: to override `-D warnings` add `#[allow(clippy::drop_non_drop)]` error: call to `std::mem::drop` with a value that does not implement `Drop`. Dropping such a type only extends its contained lifetimes - --> tests/ui/drop_non_drop.rs:39:5 + --> tests/ui/drop_non_drop.rs:44:5 | LL | drop(Baz(Foo)); | ^^^^^^^^^^^^^^ | note: argument has type `main::Baz` - --> tests/ui/drop_non_drop.rs:39:10 + --> tests/ui/drop_non_drop.rs:44:10 | LL | drop(Baz(Foo)); | ^^^^^^^^ -error: aborting due to 2 previous errors +error: call to `std::mem::drop` with a value that does not implement `Drop`. Dropping such a type only extends its contained lifetimes + --> tests/ui/drop_non_drop.rs:51:5 + | +LL | drop(make_result_uninhabited_err(Foo)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: argument has type `std::result::Result` + --> tests/ui/drop_non_drop.rs:51:10 + | +LL | drop(make_result_uninhabited_err(Foo)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors diff --git a/tests/ui/let_underscore_must_use.rs b/tests/ui/let_underscore_must_use.rs index 5cf31ec63c66f..1ef43c343fb8d 100644 --- a/tests/ui/let_underscore_must_use.rs +++ b/tests/ui/let_underscore_must_use.rs @@ -109,4 +109,14 @@ fn main() { #[allow(clippy::let_underscore_must_use)] let _ = a; + + // No lint because this type should behave as `()` + let _ = Result::<_, std::convert::Infallible>::Ok(()); + + #[must_use] + struct T; + + // Lint because this type should behave as `T` + let _ = Result::<_, std::convert::Infallible>::Ok(T); + //~^ let_underscore_must_use } diff --git a/tests/ui/let_underscore_must_use.stderr b/tests/ui/let_underscore_must_use.stderr index 130ea11646fd1..23e929b5bf895 100644 --- a/tests/ui/let_underscore_must_use.stderr +++ b/tests/ui/let_underscore_must_use.stderr @@ -96,5 +96,13 @@ LL | let _ = a; | = help: consider explicitly using expression value -error: aborting due to 12 previous errors +error: non-binding `let` on an expression with `#[must_use]` type + --> tests/ui/let_underscore_must_use.rs:120:5 + | +LL | let _ = Result::<_, std::convert::Infallible>::Ok(T); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider explicitly using expression value + +error: aborting due to 13 previous errors diff --git a/tests/ui/must_use_candidates.fixed b/tests/ui/must_use_candidates.fixed index 1e8589cf39d6b..c277d92d7c7bc 100644 --- a/tests/ui/must_use_candidates.fixed +++ b/tests/ui/must_use_candidates.fixed @@ -107,3 +107,15 @@ pub extern "C" fn unmangled(i: bool) -> bool { fn main() { assert_eq!(1, pure(1)); } + +//~v must_use_candidate +#[must_use] +pub fn result_uninhabited() -> Result { + todo!() +} + +//~v must_use_candidate +#[must_use] +pub fn controlflow_uninhabited() -> std::ops::ControlFlow { + todo!() +} diff --git a/tests/ui/must_use_candidates.rs b/tests/ui/must_use_candidates.rs index 71d546718ae79..224506dacfe49 100644 --- a/tests/ui/must_use_candidates.rs +++ b/tests/ui/must_use_candidates.rs @@ -102,3 +102,13 @@ pub extern "C" fn unmangled(i: bool) -> bool { fn main() { assert_eq!(1, pure(1)); } + +//~v must_use_candidate +pub fn result_uninhabited() -> Result { + todo!() +} + +//~v must_use_candidate +pub fn controlflow_uninhabited() -> std::ops::ControlFlow { + todo!() +} diff --git a/tests/ui/must_use_candidates.stderr b/tests/ui/must_use_candidates.stderr index 5ddbd02606291..f2ec9b265c450 100644 --- a/tests/ui/must_use_candidates.stderr +++ b/tests/ui/must_use_candidates.stderr @@ -60,5 +60,31 @@ LL + #[must_use] LL | pub fn arcd(_x: Arc) -> bool { | -error: aborting due to 5 previous errors +error: this function could have a `#[must_use]` attribute + --> tests/ui/must_use_candidates.rs:108:8 + | +LL | pub fn result_uninhabited() -> Result { + | ^^^^^^^^^^^^^^^^^^ + | + = note: a future version of Rust will treat `Result` as `T` when `E` is uninhabited wrt `#[must_use]` +help: add the attribute + | +LL + #[must_use] +LL | pub fn result_uninhabited() -> Result { + | + +error: this function could have a `#[must_use]` attribute + --> tests/ui/must_use_candidates.rs:113:8 + | +LL | pub fn controlflow_uninhabited() -> std::ops::ControlFlow { + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: a future version of Rust will treat `ControlFlow` as `C` when `B` is uninhabited wrt `#[must_use]` +help: add the attribute + | +LL + #[must_use] +LL | pub fn controlflow_uninhabited() -> std::ops::ControlFlow { + | + +error: aborting due to 7 previous errors From b84fc434130952242214081225550ce2cae4b8f4 Mon Sep 17 00:00:00 2001 From: kawkoi Date: Sat, 21 Feb 2026 21:52:58 +0530 Subject: [PATCH 43/57] allow unwrap types --- CHANGELOG.md | 1 + book/src/lint_configuration.md | 17 ++++++ clippy_config/src/conf.rs | 9 +++ clippy_lints/src/methods/mod.rs | 7 +++ .../src/methods/unwrap_expect_used.rs | 55 +++++++++++++++++++ .../toml_unknown_key/conf_unknown_key.stderr | 3 + tests/ui-toml/unwrap_used_allowed/clippy.toml | 1 + .../unwrap_used_allowed.rs | 20 +++++++ .../unwrap_used_allowed.stderr | 22 ++++++++ 9 files changed, 135 insertions(+) create mode 100644 tests/ui-toml/unwrap_used_allowed/clippy.toml create mode 100644 tests/ui-toml/unwrap_used_allowed/unwrap_used_allowed.rs create mode 100644 tests/ui-toml/unwrap_used_allowed/unwrap_used_allowed.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index ec2e317dd746f..3175b36dc4793 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7234,6 +7234,7 @@ Released 2018-09-13 [`allow-renamed-params-for`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-renamed-params-for [`allow-unwrap-in-consts`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-unwrap-in-consts [`allow-unwrap-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-unwrap-in-tests +[`allow-unwrap-types`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-unwrap-types [`allow-useless-vec-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-useless-vec-in-tests [`allowed-dotfiles`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-dotfiles [`allowed-duplicate-crates`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-duplicate-crates diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index 6cd3e4d195a18..c87f8e9a68de1 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -214,6 +214,23 @@ Whether `unwrap` should be allowed in test functions or `#[cfg(test)]` * [`unwrap_used`](https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used) +## `allow-unwrap-types` +List of types to allow `unwrap()` and `expect()` on. + +#### Example + +```toml +allow-unwrap-types = [ "std::sync::LockResult" ] +``` + +**Default Value:** `[]` + +--- +**Affected lints:** +* [`expect_used`](https://rust-lang.github.io/rust-clippy/master/index.html#expect_used) +* [`unwrap_used`](https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used) + + ## `allow-useless-vec-in-tests` Whether `useless_vec` should ignore test functions or `#[cfg(test)]` diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 0c12c3cdcbc63..41099f94b0448 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -413,6 +413,15 @@ define_Conf! { /// Whether `unwrap` should be allowed in test functions or `#[cfg(test)]` #[lints(unwrap_used)] allow_unwrap_in_tests: bool = false, + /// List of types to allow `unwrap()` and `expect()` on. + /// + /// #### Example + /// + /// ```toml + /// allow-unwrap-types = [ "std::sync::LockResult" ] + /// ``` + #[lints(expect_used, unwrap_used)] + allow_unwrap_types: Vec = Vec::new(), /// Whether `useless_vec` should ignore test functions or `#[cfg(test)]` #[lints(useless_vec)] allow_useless_vec_in_tests: bool = false, diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index ff534abe53537..c1b71a61ccb2d 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -4760,6 +4760,7 @@ pub struct Methods { allow_unwrap_in_consts: bool, allowed_dotfiles: FxHashSet<&'static str>, format_args: FormatArgsStorage, + allow_unwrap_types: Vec, } impl Methods { @@ -4776,6 +4777,7 @@ impl Methods { allow_unwrap_in_consts: conf.allow_unwrap_in_consts, allowed_dotfiles, format_args, + allow_unwrap_types: conf.allow_unwrap_types.clone(), } } } @@ -4974,6 +4976,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { self.allow_expect_in_tests, self.allow_unwrap_in_consts, self.allow_expect_in_consts, + &self.allow_unwrap_types, ); }, ExprKind::MethodCall(..) => { @@ -5722,6 +5725,7 @@ impl Methods { false, self.allow_expect_in_consts, self.allow_expect_in_tests, + &self.allow_unwrap_types, unwrap_expect_used::Variant::Expect, ); expect_fun_call::check(cx, &self.format_args, expr, method_span, recv, arg); @@ -5734,6 +5738,7 @@ impl Methods { true, self.allow_expect_in_consts, self.allow_expect_in_tests, + &self.allow_unwrap_types, unwrap_expect_used::Variant::Expect, ); }, @@ -5754,6 +5759,7 @@ impl Methods { false, self.allow_unwrap_in_consts, self.allow_unwrap_in_tests, + &self.allow_unwrap_types, unwrap_expect_used::Variant::Unwrap, ); }, @@ -5765,6 +5771,7 @@ impl Methods { true, self.allow_unwrap_in_consts, self.allow_unwrap_in_tests, + &self.allow_unwrap_types, unwrap_expect_used::Variant::Unwrap, ); }, diff --git a/clippy_lints/src/methods/unwrap_expect_used.rs b/clippy_lints/src/methods/unwrap_expect_used.rs index 4effab3a5e63c..6d8777cbc23f8 100644 --- a/clippy_lints/src/methods/unwrap_expect_used.rs +++ b/clippy_lints/src/methods/unwrap_expect_used.rs @@ -36,6 +36,7 @@ impl Variant { /// Lint usage of `unwrap` or `unwrap_err` for `Result` and `unwrap()` for `Option` (and their /// `expect` counterparts). +#[allow(clippy::too_many_arguments)] pub(super) fn check( cx: &LateContext<'_>, expr: &Expr<'_>, @@ -43,6 +44,7 @@ pub(super) fn check( is_err: bool, allow_unwrap_in_consts: bool, allow_unwrap_in_tests: bool, + allow_unwrap_types: &[String], variant: Variant, ) { let ty = cx.typeck_results().expr_ty(recv).peel_refs(); @@ -64,6 +66,54 @@ pub(super) fn check( let method_suffix = if is_err { "_err" } else { "" }; + let ty_name = ty.to_string(); + if allow_unwrap_types + .iter() + .any(|allowed_type| ty_name.starts_with(allowed_type) || ty_name == *allowed_type) + { + return; + } + + for s in allow_unwrap_types { + let def_ids = clippy_utils::paths::lookup_path_str(cx.tcx, clippy_utils::paths::PathNS::Type, s); + for def_id in def_ids { + if let ty::Adt(adt, _) = ty.kind() + && adt.did() == def_id + { + return; + } + if cx.tcx.def_kind(def_id) == DefKind::TyAlias { + let alias_ty = cx.tcx.type_of(def_id).instantiate_identity(); + if let (ty::Adt(adt, substs), ty::Adt(alias_adt, alias_substs)) = (ty.kind(), alias_ty.kind()) + && adt.did() == alias_adt.did() + { + let mut all_match = true; + for (arg, alias_arg) in substs.iter().zip(alias_substs.iter()) { + if let (Some(arg_ty), Some(alias_arg_ty)) = (arg.as_type(), alias_arg.as_type()) { + if matches!(alias_arg_ty.kind(), ty::Param(_)) { + continue; + } + if let (ty::Adt(arg_adt, _), ty::Adt(alias_arg_adt, _)) = + (arg_ty.peel_refs().kind(), alias_arg_ty.peel_refs().kind()) + { + if arg_adt.did() != alias_arg_adt.did() { + all_match = false; + break; + } + } else if arg_ty != alias_arg_ty { + all_match = false; + break; + } + } + } + if all_match { + return; + } + } + } + } + } + if allow_unwrap_in_tests && is_in_test(cx.tcx, expr.hir_id) { return; } @@ -99,6 +149,7 @@ pub(super) fn check_call( allow_unwrap_in_tests: bool, allow_expect_in_consts: bool, allow_expect_in_tests: bool, + allow_unwrap_types: &[String], ) { let Some(recv) = args.first() else { return; @@ -116,6 +167,7 @@ pub(super) fn check_call( false, allow_unwrap_in_consts, allow_unwrap_in_tests, + allow_unwrap_types, Variant::Unwrap, ); }, @@ -127,6 +179,7 @@ pub(super) fn check_call( false, allow_expect_in_consts, allow_expect_in_tests, + allow_unwrap_types, Variant::Expect, ); }, @@ -138,6 +191,7 @@ pub(super) fn check_call( true, allow_unwrap_in_consts, allow_unwrap_in_tests, + allow_unwrap_types, Variant::Unwrap, ); }, @@ -149,6 +203,7 @@ pub(super) fn check_call( true, allow_expect_in_consts, allow_expect_in_tests, + allow_unwrap_types, Variant::Expect, ); }, diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index eea1f4f00e11d..6bb3db8db67f0 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -18,6 +18,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect allow-renamed-params-for allow-unwrap-in-consts allow-unwrap-in-tests + allow-unwrap-types allow-useless-vec-in-tests allowed-dotfiles allowed-duplicate-crates @@ -118,6 +119,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect allow-renamed-params-for allow-unwrap-in-consts allow-unwrap-in-tests + allow-unwrap-types allow-useless-vec-in-tests allowed-dotfiles allowed-duplicate-crates @@ -218,6 +220,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni allow-renamed-params-for allow-unwrap-in-consts allow-unwrap-in-tests + allow-unwrap-types allow-useless-vec-in-tests allowed-dotfiles allowed-duplicate-crates diff --git a/tests/ui-toml/unwrap_used_allowed/clippy.toml b/tests/ui-toml/unwrap_used_allowed/clippy.toml new file mode 100644 index 0000000000000..cceb303c126fa --- /dev/null +++ b/tests/ui-toml/unwrap_used_allowed/clippy.toml @@ -0,0 +1 @@ +allow-unwrap-types = ["std::sync::LockResult"] \ No newline at end of file diff --git a/tests/ui-toml/unwrap_used_allowed/unwrap_used_allowed.rs b/tests/ui-toml/unwrap_used_allowed/unwrap_used_allowed.rs new file mode 100644 index 0000000000000..7905674e21e7f --- /dev/null +++ b/tests/ui-toml/unwrap_used_allowed/unwrap_used_allowed.rs @@ -0,0 +1,20 @@ +#![warn(clippy::unwrap_used)] +#![allow(clippy::unnecessary_literal_unwrap)] +#![allow(unused_variables)] +use std::sync::Mutex; + +fn main() { + let m = Mutex::new(0); + // This is allowed because `LockResult` is configured! + let _guard = m.lock().unwrap(); + + let optional: Option = Some(1); + // This is not allowed! + let _opt = optional.unwrap(); + //~^ ERROR: used `unwrap()` on an `Option` value + + let result: Result = Ok(1); + // This is not allowed! + let _res = result.unwrap(); + //~^ ERROR: used `unwrap()` on a `Result` value +} diff --git a/tests/ui-toml/unwrap_used_allowed/unwrap_used_allowed.stderr b/tests/ui-toml/unwrap_used_allowed/unwrap_used_allowed.stderr new file mode 100644 index 0000000000000..1df89bf2eb92a --- /dev/null +++ b/tests/ui-toml/unwrap_used_allowed/unwrap_used_allowed.stderr @@ -0,0 +1,22 @@ +error: used `unwrap()` on an `Option` value + --> tests/ui-toml/unwrap_used_allowed/unwrap_used_allowed.rs:13:16 + | +LL | let _opt = optional.unwrap(); + | ^^^^^^^^^^^^^^^^^ + | + = note: if this value is `None`, it will panic + = help: consider using `expect()` to provide a better panic message + = note: `-D clippy::unwrap-used` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unwrap_used)]` + +error: used `unwrap()` on a `Result` value + --> tests/ui-toml/unwrap_used_allowed/unwrap_used_allowed.rs:18:16 + | +LL | let _res = result.unwrap(); + | ^^^^^^^^^^^^^^^ + | + = note: if this value is an `Err`, it will panic + = help: consider using `expect()` to provide a better panic message + +error: aborting due to 2 previous errors + From ef1ee716663575e52ff6c3af1b7f0060b15d7455 Mon Sep 17 00:00:00 2001 From: linshuy2 Date: Fri, 6 Feb 2026 22:30:39 +0000 Subject: [PATCH 44/57] Enhance `unchecked_time_subtraction` to better handle `Duration` literals --- clippy_lints/src/time_subtraction.rs | 72 ++++++++++++++++++- clippy_utils/src/sym.rs | 3 + tests/ui/unchecked_time_subtraction.fixed | 16 +++-- tests/ui/unchecked_time_subtraction.rs | 14 +++- tests/ui/unchecked_time_subtraction.stderr | 24 +++---- .../unchecked_time_subtraction_unfixable.rs | 9 +++ ...nchecked_time_subtraction_unfixable.stderr | 26 ++++++- 7 files changed, 138 insertions(+), 26 deletions(-) diff --git a/clippy_lints/src/time_subtraction.rs b/clippy_lints/src/time_subtraction.rs index c241935d6c312..457a8f48f7e2b 100644 --- a/clippy_lints/src/time_subtraction.rs +++ b/clippy_lints/src/time_subtraction.rs @@ -1,14 +1,18 @@ +use std::time::Duration; + use clippy_config::Conf; -use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::consts::{ConstEvalCtxt, Constant}; +use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::sugg::Sugg; +use clippy_utils::sym; use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_hir::{BinOpKind, Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::Ty; use rustc_session::impl_lint_pass; -use rustc_span::sym; +use rustc_span::SyntaxContext; declare_clippy_lint! { /// ### What it does @@ -111,6 +115,27 @@ impl LateLintPass<'_> for UncheckedTimeSubtraction { && !expr.span.from_expansion() && self.msrv.meets(cx, msrvs::TRY_FROM) { + let const_eval = ConstEvalCtxt::new(cx); + let ctxt = expr.span.ctxt(); + if let Some(lhs) = const_eval_duration(&const_eval, lhs, ctxt) + && let Some(rhs) = const_eval_duration(&const_eval, rhs, ctxt) + { + if lhs >= rhs { + // If the duration subtraction can be proven to not underflow, then we don't lint + return; + } + + span_lint_and_note( + cx, + UNCHECKED_TIME_SUBTRACTION, + expr.span, + "unchecked subtraction of two `Duration` that will underflow", + None, + "if this is intentional, consider allowing the lint", + ); + return; + } + print_unchecked_duration_subtraction_sugg(cx, lhs, rhs, expr); } } @@ -189,3 +214,44 @@ fn print_unchecked_duration_subtraction_sugg( }, ); } + +fn const_eval_duration(const_eval: &ConstEvalCtxt<'_>, expr: &Expr<'_>, ctxt: SyntaxContext) -> Option { + if let ExprKind::Call(func, args) = expr.kind + && let ExprKind::Path(QPath::TypeRelative(_, func_name)) = func.kind + { + macro_rules! try_parse_duration { + (($( $name:ident : $var:ident ( $ty:ty ) ),+ $(,)?) -> $ctor:ident ( $($args:tt)* )) => {{ + let [$( $name ),+] = args else { return None }; + $( + let Some(Constant::$var(v)) = const_eval.eval_local($name, ctxt) else { return None }; + let $name = <$ty>::try_from(v).ok()?; + )+ + Some(Duration::$ctor($($args)*)) + }}; + } + + return match func_name.ident.name { + sym::new => try_parse_duration! { (secs: Int(u64), nanos: Int(u32)) -> new(secs, nanos) }, + sym::from_nanos => try_parse_duration! { (nanos: Int(u64)) -> from_nanos(nanos) }, + sym::from_nanos_u128 => try_parse_duration! { (nanos: Int(u128)) -> from_nanos_u128(nanos) }, + sym::from_micros => try_parse_duration! { (micros: Int(u64)) -> from_micros(micros) }, + sym::from_millis => try_parse_duration! { (millis: Int(u64)) -> from_millis(millis) }, + sym::from_secs => try_parse_duration! { (secs: Int(u64)) -> from_secs(secs) }, + sym::from_secs_f32 => try_parse_duration! { (secs: F32(f32)) -> from_secs_f32(secs) }, + sym::from_secs_f64 => try_parse_duration! { (secs: F64(f64)) -> from_secs_f64(secs) }, + sym::from_mins => try_parse_duration! { (mins: Int(u64)) -> from_mins(mins) }, + sym::from_hours => { + try_parse_duration! { (hours: Int(u64)) -> from_hours(hours) } + }, + sym::from_days => { + try_parse_duration! { (days: Int(u64)) -> from_hours(days * 24) } + }, + sym::from_weeks => { + try_parse_duration! { (weeks: Int(u64)) -> from_hours(weeks * 24 * 7) } + }, + _ => None, + }; + } + + None +} diff --git a/clippy_utils/src/sym.rs b/clippy_utils/src/sym.rs index bd99525a86fb5..9c274efa72be7 100644 --- a/clippy_utils/src/sym.rs +++ b/clippy_utils/src/sym.rs @@ -173,11 +173,14 @@ generate! { from_millis, from_mins, from_nanos, + from_nanos_u128, from_ne_bytes, from_ptr, from_raw, from_raw_parts, from_secs, + from_secs_f32, + from_secs_f64, from_str_radix, from_weeks, fs, diff --git a/tests/ui/unchecked_time_subtraction.fixed b/tests/ui/unchecked_time_subtraction.fixed index 830b737f18e7a..5fe51dd5ba7ca 100644 --- a/tests/ui/unchecked_time_subtraction.fixed +++ b/tests/ui/unchecked_time_subtraction.fixed @@ -25,8 +25,7 @@ fn main() { let _ = dur1.checked_sub(dur2).unwrap(); //~^ unchecked_time_subtraction - let _ = Duration::from_secs(10).checked_sub(Duration::from_secs(5)).unwrap(); - //~^ unchecked_time_subtraction + let _ = Duration::from_secs(10) - Duration::from_secs(5); let _ = second.checked_sub(dur1).unwrap(); //~^ unchecked_time_subtraction @@ -55,8 +54,17 @@ fn issue16234() { }; } - duration!(0).checked_sub(duration!(1)).unwrap(); + let d = duration!(0); + d.checked_sub(duration!(1)).unwrap(); //~^ unchecked_time_subtraction - let _ = duration!(0).checked_sub(duration!(1)).unwrap(); + let _ = d.checked_sub(duration!(1)).unwrap(); //~^ unchecked_time_subtraction } + +fn issue16499() { + let _ = Duration::from_millis(2) - Duration::from_millis(1); + let _ = Duration::new(10000, 0) - Duration::from_secs(1); + let _ = Duration::from_nanos_u128(1000) - Duration::from_nanos(100); + let _ = Duration::from_secs_f32(1.5) - Duration::from_secs_f64(0.5); + let _ = Duration::from_mins(70) - Duration::from_hours(1); +} diff --git a/tests/ui/unchecked_time_subtraction.rs b/tests/ui/unchecked_time_subtraction.rs index e41860157c41e..466d96a9a6064 100644 --- a/tests/ui/unchecked_time_subtraction.rs +++ b/tests/ui/unchecked_time_subtraction.rs @@ -26,7 +26,6 @@ fn main() { //~^ unchecked_time_subtraction let _ = Duration::from_secs(10) - Duration::from_secs(5); - //~^ unchecked_time_subtraction let _ = second - dur1; //~^ unchecked_time_subtraction @@ -55,8 +54,17 @@ fn issue16234() { }; } - duration!(0).sub(duration!(1)); + let d = duration!(0); + d.sub(duration!(1)); //~^ unchecked_time_subtraction - let _ = duration!(0) - duration!(1); + let _ = d - duration!(1); //~^ unchecked_time_subtraction } + +fn issue16499() { + let _ = Duration::from_millis(2) - Duration::from_millis(1); + let _ = Duration::new(10000, 0) - Duration::from_secs(1); + let _ = Duration::from_nanos_u128(1000) - Duration::from_nanos(100); + let _ = Duration::from_secs_f32(1.5) - Duration::from_secs_f64(0.5); + let _ = Duration::from_mins(70) - Duration::from_hours(1); +} diff --git a/tests/ui/unchecked_time_subtraction.stderr b/tests/ui/unchecked_time_subtraction.stderr index fa4bd1db81aee..b71e2c474f2ea 100644 --- a/tests/ui/unchecked_time_subtraction.stderr +++ b/tests/ui/unchecked_time_subtraction.stderr @@ -32,31 +32,25 @@ LL | let _ = dur1 - dur2; | ^^^^^^^^^^^ help: try: `dur1.checked_sub(dur2).unwrap()` error: unchecked subtraction of a `Duration` - --> tests/ui/unchecked_time_subtraction.rs:28:13 - | -LL | let _ = Duration::from_secs(10) - Duration::from_secs(5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Duration::from_secs(10).checked_sub(Duration::from_secs(5)).unwrap()` - -error: unchecked subtraction of a `Duration` - --> tests/ui/unchecked_time_subtraction.rs:31:13 + --> tests/ui/unchecked_time_subtraction.rs:30:13 | LL | let _ = second - dur1; | ^^^^^^^^^^^^^ help: try: `second.checked_sub(dur1).unwrap()` error: unchecked subtraction of a `Duration` - --> tests/ui/unchecked_time_subtraction.rs:35:13 + --> tests/ui/unchecked_time_subtraction.rs:34:13 | LL | let _ = 2 * dur1 - dur2; | ^^^^^^^^^^^^^^^ help: try: `(2 * dur1).checked_sub(dur2).unwrap()` error: unchecked subtraction of a `Duration` - --> tests/ui/unchecked_time_subtraction.rs:42:5 + --> tests/ui/unchecked_time_subtraction.rs:41:5 | LL | Duration::ZERO.sub(Duration::MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Duration::ZERO.checked_sub(Duration::MAX).unwrap()` error: unchecked subtraction of a `Duration` - --> tests/ui/unchecked_time_subtraction.rs:45:13 + --> tests/ui/unchecked_time_subtraction.rs:44:13 | LL | let _ = Duration::ZERO - Duration::MAX; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Duration::ZERO.checked_sub(Duration::MAX).unwrap()` @@ -64,14 +58,14 @@ LL | let _ = Duration::ZERO - Duration::MAX; error: unchecked subtraction of a `Duration` --> tests/ui/unchecked_time_subtraction.rs:58:5 | -LL | duration!(0).sub(duration!(1)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `duration!(0).checked_sub(duration!(1)).unwrap()` +LL | d.sub(duration!(1)); + | ^^^^^^^^^^^^^^^^^^^ help: try: `d.checked_sub(duration!(1)).unwrap()` error: unchecked subtraction of a `Duration` --> tests/ui/unchecked_time_subtraction.rs:60:13 | -LL | let _ = duration!(0) - duration!(1); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `duration!(0).checked_sub(duration!(1)).unwrap()` +LL | let _ = d - duration!(1); + | ^^^^^^^^^^^^^^^^ help: try: `d.checked_sub(duration!(1)).unwrap()` -error: aborting due to 12 previous errors +error: aborting due to 11 previous errors diff --git a/tests/ui/unchecked_time_subtraction_unfixable.rs b/tests/ui/unchecked_time_subtraction_unfixable.rs index 4b6a5ca156209..6c3777eec915a 100644 --- a/tests/ui/unchecked_time_subtraction_unfixable.rs +++ b/tests/ui/unchecked_time_subtraction_unfixable.rs @@ -20,3 +20,12 @@ fn main() { //~^ unchecked_time_subtraction //~| unchecked_time_subtraction } + +fn issue16499() { + let _ = Duration::from_millis(1) - Duration::from_millis(2); + //~^ unchecked_time_subtraction + let _ = Duration::from_millis(1) - Duration::from_mins(2); + //~^ unchecked_time_subtraction + let _ = Duration::from_nanos(1) - Duration::from_secs(1); + //~^ unchecked_time_subtraction +} diff --git a/tests/ui/unchecked_time_subtraction_unfixable.stderr b/tests/ui/unchecked_time_subtraction_unfixable.stderr index 017e5b1c7c11e..a48676bf5c168 100644 --- a/tests/ui/unchecked_time_subtraction_unfixable.stderr +++ b/tests/ui/unchecked_time_subtraction_unfixable.stderr @@ -25,5 +25,29 @@ error: unchecked subtraction of a `Duration` LL | let _ = instant1 - dur2 - dur3; | ^^^^^^^^^^^^^^^ help: try: `instant1.checked_sub(dur2).unwrap()` -error: aborting due to 4 previous errors +error: unchecked subtraction of two `Duration` that will underflow + --> tests/ui/unchecked_time_subtraction_unfixable.rs:25:13 + | +LL | let _ = Duration::from_millis(1) - Duration::from_millis(2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: if this is intentional, consider allowing the lint + +error: unchecked subtraction of two `Duration` that will underflow + --> tests/ui/unchecked_time_subtraction_unfixable.rs:27:13 + | +LL | let _ = Duration::from_millis(1) - Duration::from_mins(2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: if this is intentional, consider allowing the lint + +error: unchecked subtraction of two `Duration` that will underflow + --> tests/ui/unchecked_time_subtraction_unfixable.rs:29:13 + | +LL | let _ = Duration::from_nanos(1) - Duration::from_secs(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: if this is intentional, consider allowing the lint + +error: aborting due to 7 previous errors From 2f6e9e4d5549e484cde811267fe4381cbf12ea18 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Sat, 21 Feb 2026 10:52:00 +0100 Subject: [PATCH 45/57] Add `Sugg::strip_paren()` --- clippy_utils/src/sugg.rs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index 3ade38bea8ed6..641c6684a0bd1 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -370,6 +370,20 @@ impl<'a> Sugg<'a> { } } + /// Strip enclosing parentheses if present. This method must be called when + /// it is known that removing those will not change the meaning. For example, + /// if `self` is known to represent a reference and the suggestion will be + /// used as the argument of a function call, it is safe to remove the enclosing + /// parentheses. It would not be safe to do so for an expression that might + /// represent a tuple. + #[must_use] + pub fn strip_paren(self) -> Self { + match self { + Sugg::NonParen(s) | Sugg::MaybeParen(s) => Sugg::NonParen(strip_enclosing_paren(s)), + sugg => sugg, + } + } + pub fn into_string(self) -> String { match self { Sugg::NonParen(p) | Sugg::MaybeParen(p) => p.into_owned(), @@ -430,6 +444,22 @@ pub fn has_enclosing_paren(sugg: impl AsRef) -> bool { } } +/// Strip enclosing parentheses from a snippet if present. +fn strip_enclosing_paren(snippet: Cow<'_, str>) -> Cow<'_, str> { + if has_enclosing_paren(&snippet) { + match snippet { + Cow::Borrowed(s) => Cow::Borrowed(&s[1..s.len() - 1]), + Cow::Owned(mut s) => { + s.pop(); + s.remove(0); + Cow::Owned(s) + }, + } + } else { + snippet + } +} + /// Copied from the rust standard library, and then edited macro_rules! forward_binop_impls_to_ref { (impl $imp:ident, $method:ident for $t:ty, type Output = $o:ty) => { From b0a25d208fb62b135c6609987a11e9d0675414b5 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 23 Sep 2025 15:58:18 +0200 Subject: [PATCH 46/57] feat(clone_on_ref_ptr): don't add a `&` to the receiver if it's a reference --- clippy_lints/src/methods/clone_on_ref_ptr.rs | 20 +++++++--- tests/ui/clone_on_ref_ptr.fixed | 29 +++++++++++--- tests/ui/clone_on_ref_ptr.rs | 27 +++++++++++-- tests/ui/clone_on_ref_ptr.stderr | 42 ++++++++++++++++++-- 4 files changed, 100 insertions(+), 18 deletions(-) diff --git a/clippy_lints/src/methods/clone_on_ref_ptr.rs b/clippy_lints/src/methods/clone_on_ref_ptr.rs index 238e1fe988b39..de32b0476d205 100644 --- a/clippy_lints/src/methods/clone_on_ref_ptr.rs +++ b/clippy_lints/src/methods/clone_on_ref_ptr.rs @@ -1,5 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::snippet_with_context; +use clippy_utils::sugg::Sugg; +use clippy_utils::ty::peel_and_count_ty_refs; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -9,9 +10,10 @@ use rustc_span::symbol::sym; use super::CLONE_ON_REF_PTR; pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::Expr<'_>) { - let obj_ty = cx.typeck_results().expr_ty(receiver).peel_refs(); + let receiver_ty = cx.typeck_results().expr_ty(receiver); + let (receiver_ty_peeled, n_refs, _) = peel_and_count_ty_refs(receiver_ty); - if let ty::Adt(adt, subst) = obj_ty.kind() + if let ty::Adt(adt, subst) = receiver_ty_peeled.kind() && let Some(name) = cx.tcx.get_diagnostic_name(adt.did()) { let caller_type = match name { @@ -29,20 +31,26 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir:: |diag| { // Sometimes unnecessary ::<_> after Rc/Arc/Weak let mut app = Applicability::Unspecified; - let snippet = snippet_with_context(cx, receiver.span, expr.span.ctxt(), "..", &mut app).0; + let mut sugg = Sugg::hir_with_context(cx, receiver, expr.span.ctxt(), "..", &mut app); + if n_refs == 0 { + sugg = sugg.addr(); + } + // References on argument position don't need to preserve parentheses + // even if they were present in the original expression. + sugg = sugg.strip_paren(); let generic = subst.type_at(0); if generic.is_suggestable(cx.tcx, true) { diag.span_suggestion( expr.span, "try", - format!("{caller_type}::<{generic}>::clone(&{snippet})"), + format!("{caller_type}::<{generic}>::clone({sugg})"), app, ); } else { diag.span_suggestion( expr.span, "try", - format!("{caller_type}::::clone(&{snippet})"), + format!("{caller_type}::::clone({sugg})"), Applicability::HasPlaceholders, ); } diff --git a/tests/ui/clone_on_ref_ptr.fixed b/tests/ui/clone_on_ref_ptr.fixed index ede9d171517e7..86ae237f09af3 100644 --- a/tests/ui/clone_on_ref_ptr.fixed +++ b/tests/ui/clone_on_ref_ptr.fixed @@ -50,10 +50,6 @@ mod issue2076 { } } -#[allow( - clippy::needless_borrow, - reason = "the suggestion creates `Weak::clone(&rec)`, but `rec` is already a reference" -)] mod issue15009 { use std::rc::{Rc, Weak}; use std::sync::atomic::{AtomicU32, Ordering}; @@ -62,7 +58,7 @@ mod issue15009 { let counter = AtomicU32::new(0); let counter_ref = &counter; let factorial = Rc::new_cyclic(move |rec| { - let rec = std::rc::Weak::::clone(&rec) as Weak u32>; + let rec = std::rc::Weak::::clone(rec) as Weak u32>; //~^ clone_on_ref_ptr move |x| { // can capture env @@ -79,3 +75,26 @@ mod issue15009 { println!("{}", counter.load(Ordering::Relaxed)); // 14 } } + +fn issue15741(mut rc: Rc, ref_rc: &Rc, refmut_rc: &mut Rc) { + std::rc::Rc::::clone(&rc); + //~^ clone_on_ref_ptr + std::rc::Rc::::clone(ref_rc); + //~^ clone_on_ref_ptr + std::rc::Rc::::clone(refmut_rc); + //~^ clone_on_ref_ptr + + // The following cases already cause warn-by-default lints to fire, and the suggestion just makes + // another set of warn-by-default lints to fire, so this is probably fine + + #[allow(clippy::needless_borrow, clippy::unnecessary_mut_passed)] // before the suggestion + #[allow(clippy::double_parens)] // after the suggestion + { + std::rc::Rc::::clone(&(rc)); + //~^ clone_on_ref_ptr + std::rc::Rc::::clone(&rc); + //~^ clone_on_ref_ptr + std::rc::Rc::::clone(&mut rc); + //~^ clone_on_ref_ptr + }; +} diff --git a/tests/ui/clone_on_ref_ptr.rs b/tests/ui/clone_on_ref_ptr.rs index 5999b4069d0f2..cc6d2b6115753 100644 --- a/tests/ui/clone_on_ref_ptr.rs +++ b/tests/ui/clone_on_ref_ptr.rs @@ -50,10 +50,6 @@ mod issue2076 { } } -#[allow( - clippy::needless_borrow, - reason = "the suggestion creates `Weak::clone(&rec)`, but `rec` is already a reference" -)] mod issue15009 { use std::rc::{Rc, Weak}; use std::sync::atomic::{AtomicU32, Ordering}; @@ -79,3 +75,26 @@ mod issue15009 { println!("{}", counter.load(Ordering::Relaxed)); // 14 } } + +fn issue15741(mut rc: Rc, ref_rc: &Rc, refmut_rc: &mut Rc) { + rc.clone(); + //~^ clone_on_ref_ptr + ref_rc.clone(); + //~^ clone_on_ref_ptr + refmut_rc.clone(); + //~^ clone_on_ref_ptr + + // The following cases already cause warn-by-default lints to fire, and the suggestion just makes + // another set of warn-by-default lints to fire, so this is probably fine + + #[allow(clippy::needless_borrow, clippy::unnecessary_mut_passed)] // before the suggestion + #[allow(clippy::double_parens)] // after the suggestion + { + (rc).clone(); + //~^ clone_on_ref_ptr + (&rc).clone(); + //~^ clone_on_ref_ptr + (&mut rc).clone(); + //~^ clone_on_ref_ptr + }; +} diff --git a/tests/ui/clone_on_ref_ptr.stderr b/tests/ui/clone_on_ref_ptr.stderr index b8ddc3058c012..6a151e9ee101d 100644 --- a/tests/ui/clone_on_ref_ptr.stderr +++ b/tests/ui/clone_on_ref_ptr.stderr @@ -38,10 +38,46 @@ LL | Some(try_opt!(Some(rc)).clone()) | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::rc::Rc::::clone(&try_opt!(Some(rc)))` error: using `.clone()` on a ref-counted pointer - --> tests/ui/clone_on_ref_ptr.rs:65:23 + --> tests/ui/clone_on_ref_ptr.rs:61:23 | LL | let rec = rec.clone() as Weak u32>; - | ^^^^^^^^^^^ help: try: `std::rc::Weak::::clone(&rec)` + | ^^^^^^^^^^^ help: try: `std::rc::Weak::::clone(rec)` -error: aborting due to 7 previous errors +error: using `.clone()` on a ref-counted pointer + --> tests/ui/clone_on_ref_ptr.rs:80:5 + | +LL | rc.clone(); + | ^^^^^^^^^^ help: try: `std::rc::Rc::::clone(&rc)` + +error: using `.clone()` on a ref-counted pointer + --> tests/ui/clone_on_ref_ptr.rs:82:5 + | +LL | ref_rc.clone(); + | ^^^^^^^^^^^^^^ help: try: `std::rc::Rc::::clone(ref_rc)` + +error: using `.clone()` on a ref-counted pointer + --> tests/ui/clone_on_ref_ptr.rs:84:5 + | +LL | refmut_rc.clone(); + | ^^^^^^^^^^^^^^^^^ help: try: `std::rc::Rc::::clone(refmut_rc)` + +error: using `.clone()` on a ref-counted pointer + --> tests/ui/clone_on_ref_ptr.rs:93:9 + | +LL | (rc).clone(); + | ^^^^^^^^^^^^ help: try: `std::rc::Rc::::clone(&(rc))` + +error: using `.clone()` on a ref-counted pointer + --> tests/ui/clone_on_ref_ptr.rs:95:9 + | +LL | (&rc).clone(); + | ^^^^^^^^^^^^^ help: try: `std::rc::Rc::::clone(&rc)` + +error: using `.clone()` on a ref-counted pointer + --> tests/ui/clone_on_ref_ptr.rs:97:9 + | +LL | (&mut rc).clone(); + | ^^^^^^^^^^^^^^^^^ help: try: `std::rc::Rc::::clone(&mut rc)` + +error: aborting due to 13 previous errors From 394afbe88f30dc56347ab56b24e47aa7386e42b9 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Sun, 22 Feb 2026 11:32:23 +0100 Subject: [PATCH 47/57] Remove `no-rustfix` marker for tests with no lint suggestions --- tests/ui-toml/expect_used/expect_used.rs | 1 - tests/ui-toml/expect_used/expect_used.stderr | 8 +- tests/ui/crashes/ice-10912.rs | 2 +- tests/ui/four_forward_slashes_bare_cr.rs | 1 - tests/ui/four_forward_slashes_bare_cr.stderr | 2 +- tests/ui/len_without_is_empty_expect.rs | 1 - tests/ui/len_without_is_empty_expect.stderr | 2 +- tests/ui/let_underscore_future.rs | 2 +- .../mixed_attributes_style/mod_declaration.rs | 2 +- tests/ui/should_impl_trait/method_list_2.rs | 2 +- tests/ui/std_instead_of_core_unfixable.rs | 2 - tests/ui/std_instead_of_core_unfixable.stderr | 6 +- tests/ui/string_add.rs | 2 +- tests/ui/unconditional_recursion.rs | 2 - tests/ui/unconditional_recursion.stderr | 90 +++++++++---------- 15 files changed, 59 insertions(+), 66 deletions(-) diff --git a/tests/ui-toml/expect_used/expect_used.rs b/tests/ui-toml/expect_used/expect_used.rs index 9955c9b6baa1d..594524a796313 100644 --- a/tests/ui-toml/expect_used/expect_used.rs +++ b/tests/ui-toml/expect_used/expect_used.rs @@ -1,5 +1,4 @@ //@compile-flags: --test -//@no-rustfix #![warn(clippy::expect_used)] #![allow(clippy::unnecessary_literal_unwrap)] diff --git a/tests/ui-toml/expect_used/expect_used.stderr b/tests/ui-toml/expect_used/expect_used.stderr index 3bb471e6dfcc8..2be9540179e71 100644 --- a/tests/ui-toml/expect_used/expect_used.stderr +++ b/tests/ui-toml/expect_used/expect_used.stderr @@ -1,5 +1,5 @@ error: used `expect()` on an `Option` value - --> tests/ui-toml/expect_used/expect_used.rs:8:13 + --> tests/ui-toml/expect_used/expect_used.rs:7:13 | LL | let _ = opt.expect(""); | ^^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | let _ = opt.expect(""); = help: to override `-D warnings` add `#[allow(clippy::expect_used)]` error: used `expect()` on a `Result` value - --> tests/ui-toml/expect_used/expect_used.rs:14:13 + --> tests/ui-toml/expect_used/expect_used.rs:13:13 | LL | let _ = res.expect(""); | ^^^^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | let _ = res.expect(""); = note: if this value is an `Err`, it will panic error: used `expect()` on an `Option` value - --> tests/ui-toml/expect_used/expect_used.rs:23:28 + --> tests/ui-toml/expect_used/expect_used.rs:22:28 | LL | const UNWRAPPED: i32 = SOME.expect("Not three?"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL | const UNWRAPPED: i32 = SOME.expect("Not three?"); = note: if this value is `None`, it will panic error: used `expect()` on an `Option` value - --> tests/ui-toml/expect_used/expect_used.rs:26:9 + --> tests/ui-toml/expect_used/expect_used.rs:25:9 | LL | SOME.expect("Still not three?"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/crashes/ice-10912.rs b/tests/ui/crashes/ice-10912.rs index 1d689e1d0082d..57403d98e5d0a 100644 --- a/tests/ui/crashes/ice-10912.rs +++ b/tests/ui/crashes/ice-10912.rs @@ -1,5 +1,5 @@ #![warn(clippy::unreadable_literal)] -//@no-rustfix + fn f2() -> impl Sized { && 3.14159265358979323846E } //~^ ERROR: expected at least one digit in exponent diff --git a/tests/ui/four_forward_slashes_bare_cr.rs b/tests/ui/four_forward_slashes_bare_cr.rs index 19123cd206e37..9f3388512a059 100644 --- a/tests/ui/four_forward_slashes_bare_cr.rs +++ b/tests/ui/four_forward_slashes_bare_cr.rs @@ -1,4 +1,3 @@ -//@no-rustfix #![warn(clippy::four_forward_slashes)] //~v four_forward_slashes diff --git a/tests/ui/four_forward_slashes_bare_cr.stderr b/tests/ui/four_forward_slashes_bare_cr.stderr index 64e70b97db9ad..266a5fa72c54a 100644 --- a/tests/ui/four_forward_slashes_bare_cr.stderr +++ b/tests/ui/four_forward_slashes_bare_cr.stderr @@ -1,5 +1,5 @@ error: this item has comments with 4 forward slashes (`////`). These look like doc comments, but they aren't - --> tests/ui/four_forward_slashes_bare_cr.rs:5:1 + --> tests/ui/four_forward_slashes_bare_cr.rs:4:1 | LL | / //// nondoc comment with bare CR: '␍' LL | | fn main() {} diff --git a/tests/ui/len_without_is_empty_expect.rs b/tests/ui/len_without_is_empty_expect.rs index 9d1245e2d02ad..4c427904a82b0 100644 --- a/tests/ui/len_without_is_empty_expect.rs +++ b/tests/ui/len_without_is_empty_expect.rs @@ -1,4 +1,3 @@ -//@no-rustfix #![allow(clippy::len_without_is_empty)] // Check that the lint expectation is fulfilled even if the lint is allowed at the type level. diff --git a/tests/ui/len_without_is_empty_expect.stderr b/tests/ui/len_without_is_empty_expect.stderr index e96870f054e43..f8fecf5a69950 100644 --- a/tests/ui/len_without_is_empty_expect.stderr +++ b/tests/ui/len_without_is_empty_expect.stderr @@ -1,5 +1,5 @@ error: this lint expectation is unfulfilled - --> tests/ui/len_without_is_empty_expect.rs:18:14 + --> tests/ui/len_without_is_empty_expect.rs:17:14 | LL | #[expect(clippy::len_without_is_empty)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/let_underscore_future.rs b/tests/ui/let_underscore_future.rs index 6347c792280e9..eb1a985a91172 100644 --- a/tests/ui/let_underscore_future.rs +++ b/tests/ui/let_underscore_future.rs @@ -1,5 +1,5 @@ use std::future::Future; -//@no-rustfix + async fn some_async_fn() {} fn sync_side_effects() {} diff --git a/tests/ui/mixed_attributes_style/mod_declaration.rs b/tests/ui/mixed_attributes_style/mod_declaration.rs index 8cef6a80048a5..43b49c6d8b025 100644 --- a/tests/ui/mixed_attributes_style/mod_declaration.rs +++ b/tests/ui/mixed_attributes_style/mod_declaration.rs @@ -1,5 +1,5 @@ //@error-in-other-file: item has both inner and outer attributes -//@no-rustfix + #[path = "auxiliary/submodule.rs"] // don't lint. /// This doc comment should not lint, it could be used to add context to the original module doc mod submodule; diff --git a/tests/ui/should_impl_trait/method_list_2.rs b/tests/ui/should_impl_trait/method_list_2.rs index 4dfbe7e0f9f4d..4c6d7df236ef6 100644 --- a/tests/ui/should_impl_trait/method_list_2.rs +++ b/tests/ui/should_impl_trait/method_list_2.rs @@ -12,7 +12,7 @@ clippy::missing_panics_doc, clippy::return_self_not_must_use )] -//@no-rustfix + use std::ops::Mul; use std::rc::{self, Rc}; use std::sync::{self, Arc}; diff --git a/tests/ui/std_instead_of_core_unfixable.rs b/tests/ui/std_instead_of_core_unfixable.rs index 957f472a45448..5e71159ac6c5d 100644 --- a/tests/ui/std_instead_of_core_unfixable.rs +++ b/tests/ui/std_instead_of_core_unfixable.rs @@ -1,5 +1,3 @@ -//@no-rustfix - #![warn(clippy::std_instead_of_core)] #![warn(clippy::std_instead_of_alloc)] #![allow(unused_imports)] diff --git a/tests/ui/std_instead_of_core_unfixable.stderr b/tests/ui/std_instead_of_core_unfixable.stderr index 0cdec56c99274..147b46022126b 100644 --- a/tests/ui/std_instead_of_core_unfixable.stderr +++ b/tests/ui/std_instead_of_core_unfixable.stderr @@ -1,5 +1,5 @@ error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core_unfixable.rs:9:43 + --> tests/ui/std_instead_of_core_unfixable.rs:7:43 | LL | use std::{collections::HashMap, hash::Hash}; | ^^^^ @@ -9,7 +9,7 @@ LL | use std::{collections::HashMap, hash::Hash}; = help: to override `-D warnings` add `#[allow(clippy::std_instead_of_core)]` error: used import from `std` instead of `core` - --> tests/ui/std_instead_of_core_unfixable.rs:15:22 + --> tests/ui/std_instead_of_core_unfixable.rs:13:22 | LL | use std::{error::Error, vec::Vec, fs::File}; | ^^^^^ @@ -17,7 +17,7 @@ LL | use std::{error::Error, vec::Vec, fs::File}; = help: consider importing the item from `core` error: used import from `std` instead of `alloc` - --> tests/ui/std_instead_of_core_unfixable.rs:15:34 + --> tests/ui/std_instead_of_core_unfixable.rs:13:34 | LL | use std::{error::Error, vec::Vec, fs::File}; | ^^^ diff --git a/tests/ui/string_add.rs b/tests/ui/string_add.rs index 5c7d13ebcd58b..50627a1921a4f 100644 --- a/tests/ui/string_add.rs +++ b/tests/ui/string_add.rs @@ -1,5 +1,5 @@ //@aux-build:proc_macros.rs -//@no-rustfix + extern crate proc_macros; use proc_macros::external; diff --git a/tests/ui/unconditional_recursion.rs b/tests/ui/unconditional_recursion.rs index e4dd33a8eeea7..20b4198d63926 100644 --- a/tests/ui/unconditional_recursion.rs +++ b/tests/ui/unconditional_recursion.rs @@ -1,5 +1,3 @@ -//@no-rustfix - #![warn(clippy::unconditional_recursion)] #![allow( clippy::partialeq_ne_impl, diff --git a/tests/ui/unconditional_recursion.stderr b/tests/ui/unconditional_recursion.stderr index c6eaa6a0775c1..5edccf9593da9 100644 --- a/tests/ui/unconditional_recursion.stderr +++ b/tests/ui/unconditional_recursion.stderr @@ -1,5 +1,5 @@ error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:51:5 + --> tests/ui/unconditional_recursion.rs:49:5 | LL | fn ne(&self, other: &Self) -> bool { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing @@ -12,7 +12,7 @@ LL | self.ne(other) = help: to override `-D warnings` add `#[allow(unconditional_recursion)]` error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:56:5 + --> tests/ui/unconditional_recursion.rs:54:5 | LL | fn eq(&self, other: &Self) -> bool { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing @@ -23,7 +23,7 @@ LL | self.eq(other) = help: a `loop` may express intention better if this is on purpose error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:235:5 + --> tests/ui/unconditional_recursion.rs:233:5 | LL | fn to_string(&self) -> String { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing @@ -34,7 +34,7 @@ LL | self.to_string() = help: a `loop` may express intention better if this is on purpose error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:245:5 + --> tests/ui/unconditional_recursion.rs:243:5 | LL | fn to_string(&self) -> String { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing @@ -45,7 +45,7 @@ LL | x.to_string() = help: a `loop` may express intention better if this is on purpose error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:256:5 + --> tests/ui/unconditional_recursion.rs:254:5 | LL | fn to_string(&self) -> String { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing @@ -56,7 +56,7 @@ LL | (self as &Self).to_string() = help: a `loop` may express intention better if this is on purpose error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:17:5 + --> tests/ui/unconditional_recursion.rs:15:5 | LL | / fn ne(&self, other: &Self) -> bool { LL | | @@ -66,7 +66,7 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:20:9 + --> tests/ui/unconditional_recursion.rs:18:9 | LL | self != other | ^^^^^^^^^^^^^ @@ -74,7 +74,7 @@ LL | self != other = help: to override `-D warnings` add `#[allow(clippy::unconditional_recursion)]` error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:22:5 + --> tests/ui/unconditional_recursion.rs:20:5 | LL | / fn eq(&self, other: &Self) -> bool { LL | | @@ -84,13 +84,13 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:25:9 + --> tests/ui/unconditional_recursion.rs:23:9 | LL | self == other | ^^^^^^^^^^^^^ error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:35:5 + --> tests/ui/unconditional_recursion.rs:33:5 | LL | / fn ne(&self, other: &Self) -> bool { LL | | @@ -99,13 +99,13 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:37:9 + --> tests/ui/unconditional_recursion.rs:35:9 | LL | self != &Foo2::B // no error here | ^^^^^^^^^^^^^^^^ error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:39:5 + --> tests/ui/unconditional_recursion.rs:37:5 | LL | / fn eq(&self, other: &Self) -> bool { LL | | @@ -114,13 +114,13 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:41:9 + --> tests/ui/unconditional_recursion.rs:39:9 | LL | self == &Foo2::B // no error here | ^^^^^^^^^^^^^^^^ error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:51:5 + --> tests/ui/unconditional_recursion.rs:49:5 | LL | / fn ne(&self, other: &Self) -> bool { LL | | @@ -130,13 +130,13 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:54:9 + --> tests/ui/unconditional_recursion.rs:52:9 | LL | self.ne(other) | ^^^^^^^^^^^^^^ error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:56:5 + --> tests/ui/unconditional_recursion.rs:54:5 | LL | / fn eq(&self, other: &Self) -> bool { ... | @@ -144,13 +144,13 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:60:9 + --> tests/ui/unconditional_recursion.rs:58:9 | LL | self.eq(other) | ^^^^^^^^^^^^^^ error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:102:5 + --> tests/ui/unconditional_recursion.rs:100:5 | LL | / fn ne(&self, other: &Self) -> bool { LL | | @@ -160,13 +160,13 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:105:9 + --> tests/ui/unconditional_recursion.rs:103:9 | LL | other != self | ^^^^^^^^^^^^^ error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:107:5 + --> tests/ui/unconditional_recursion.rs:105:5 | LL | / fn eq(&self, other: &Self) -> bool { LL | | @@ -176,13 +176,13 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:110:9 + --> tests/ui/unconditional_recursion.rs:108:9 | LL | other == self | ^^^^^^^^^^^^^ error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:118:5 + --> tests/ui/unconditional_recursion.rs:116:5 | LL | / fn ne(&self, other: &Self) -> bool { LL | | @@ -193,13 +193,13 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:121:9 + --> tests/ui/unconditional_recursion.rs:119:9 | LL | other != other | ^^^^^^^^^^^^^^ error: equal expressions as operands to `!=` - --> tests/ui/unconditional_recursion.rs:121:9 + --> tests/ui/unconditional_recursion.rs:119:9 | LL | other != other | ^^^^^^^^^^^^^^ @@ -207,7 +207,7 @@ LL | other != other = note: `#[deny(clippy::eq_op)]` on by default error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:124:5 + --> tests/ui/unconditional_recursion.rs:122:5 | LL | / fn eq(&self, other: &Self) -> bool { LL | | @@ -218,19 +218,19 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:127:9 + --> tests/ui/unconditional_recursion.rs:125:9 | LL | other == other | ^^^^^^^^^^^^^^ error: equal expressions as operands to `==` - --> tests/ui/unconditional_recursion.rs:127:9 + --> tests/ui/unconditional_recursion.rs:125:9 | LL | other == other | ^^^^^^^^^^^^^^ error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:135:5 + --> tests/ui/unconditional_recursion.rs:133:5 | LL | / fn ne(&self, _other: &Self) -> bool { LL | | @@ -241,19 +241,19 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:138:9 + --> tests/ui/unconditional_recursion.rs:136:9 | LL | self != self | ^^^^^^^^^^^^ error: equal expressions as operands to `!=` - --> tests/ui/unconditional_recursion.rs:138:9 + --> tests/ui/unconditional_recursion.rs:136:9 | LL | self != self | ^^^^^^^^^^^^ error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:141:5 + --> tests/ui/unconditional_recursion.rs:139:5 | LL | / fn eq(&self, _other: &Self) -> bool { LL | | @@ -264,19 +264,19 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:144:9 + --> tests/ui/unconditional_recursion.rs:142:9 | LL | self == self | ^^^^^^^^^^^^ error: equal expressions as operands to `==` - --> tests/ui/unconditional_recursion.rs:144:9 + --> tests/ui/unconditional_recursion.rs:142:9 | LL | self == self | ^^^^^^^^^^^^ error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:171:13 + --> tests/ui/unconditional_recursion.rs:169:13 | LL | / fn eq(&self, other: &Self) -> bool { LL | | @@ -289,7 +289,7 @@ LL | impl_partial_eq!(S5); | -------------------- in this macro invocation | note: recursive call site - --> tests/ui/unconditional_recursion.rs:174:17 + --> tests/ui/unconditional_recursion.rs:172:17 | LL | self == other | ^^^^^^^^^^^^^ @@ -299,7 +299,7 @@ LL | impl_partial_eq!(S5); = note: this error originates in the macro `impl_partial_eq` (in Nightly builds, run with -Z macro-backtrace for more info) error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:201:5 + --> tests/ui/unconditional_recursion.rs:199:5 | LL | / fn eq(&self, other: &Self) -> bool { LL | | @@ -311,13 +311,13 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:206:9 + --> tests/ui/unconditional_recursion.rs:204:9 | LL | mine == theirs | ^^^^^^^^^^^^^^ error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:271:5 + --> tests/ui/unconditional_recursion.rs:269:5 | LL | / fn new() -> Self { LL | | @@ -327,13 +327,13 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:274:9 + --> tests/ui/unconditional_recursion.rs:272:9 | LL | Self::default() | ^^^^^^^^^^^^^^^ error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:311:5 + --> tests/ui/unconditional_recursion.rs:309:5 | LL | / fn eq(&self, other: &Self) -> bool { LL | | @@ -345,13 +345,13 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:316:9 + --> tests/ui/unconditional_recursion.rs:314:9 | LL | mine.eq(theirs) | ^^^^^^^^^^^^^^^ error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:383:5 + --> tests/ui/unconditional_recursion.rs:381:5 | LL | / fn from(f: BadFromTy1<'a>) -> Self { LL | | @@ -360,13 +360,13 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:385:9 + --> tests/ui/unconditional_recursion.rs:383:9 | LL | f.into() | ^^^^^^^^ error: function cannot return without recursing - --> tests/ui/unconditional_recursion.rs:393:5 + --> tests/ui/unconditional_recursion.rs:391:5 | LL | / fn from(f: BadFromTy2<'a>) -> Self { LL | | @@ -375,7 +375,7 @@ LL | | } | |_____^ | note: recursive call site - --> tests/ui/unconditional_recursion.rs:395:9 + --> tests/ui/unconditional_recursion.rs:393:9 | LL | Into::into(f) | ^^^^^^^^^^^^^ From b12ef56efd242f9a9a2f947cb3b689c8c5cb786f Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Sun, 22 Feb 2026 11:37:44 +0100 Subject: [PATCH 48/57] Remove `no-rustfix` marker for tests with valid suggestions --- tests/ui/crashes/ice-6250.fixed | 19 + tests/ui/crashes/ice-6250.rs | 3 +- tests/ui/crashes/ice-6250.stderr | 4 +- tests/ui/crashes/ice-9041.fixed | 12 + tests/ui/crashes/ice-9041.rs | 4 +- tests/ui/crashes/ice-9041.stderr | 2 +- .../ui/manual_memcpy/with_loop_counters.fixed | 55 +++ tests/ui/manual_memcpy/with_loop_counters.rs | 3 +- .../manual_memcpy/with_loop_counters.stderr | 22 +- .../manual_memcpy/without_loop_counters.fixed | 175 +++++++++ .../ui/manual_memcpy/without_loop_counters.rs | 4 +- tests/ui/map_flatten.fixed | 69 ++++ tests/ui/map_flatten.rs | 4 +- tests/ui/needless_collect_indirect.fixed | 344 ++++++++++++++++++ tests/ui/needless_collect_indirect.rs | 10 +- tests/ui/needless_collect_indirect.stderr | 32 +- tests/ui/track-diagnostics-clippy.fixed | 22 ++ tests/ui/track-diagnostics-clippy.rs | 1 - tests/ui/transmute_ref_to_ref.fixed | 38 ++ tests/ui/transmute_ref_to_ref.rs | 4 +- tests/ui/transmute_ref_to_ref.stderr | 12 +- tests/ui/transmute_ref_to_ref_no_std.fixed | 30 ++ tests/ui/transmute_ref_to_ref_no_std.rs | 4 +- tests/ui/transmute_ref_to_ref_no_std.stderr | 8 +- 24 files changed, 825 insertions(+), 56 deletions(-) create mode 100644 tests/ui/crashes/ice-6250.fixed create mode 100644 tests/ui/crashes/ice-9041.fixed create mode 100644 tests/ui/manual_memcpy/with_loop_counters.fixed create mode 100644 tests/ui/manual_memcpy/without_loop_counters.fixed create mode 100644 tests/ui/map_flatten.fixed create mode 100644 tests/ui/needless_collect_indirect.fixed create mode 100644 tests/ui/track-diagnostics-clippy.fixed create mode 100644 tests/ui/transmute_ref_to_ref.fixed create mode 100644 tests/ui/transmute_ref_to_ref_no_std.fixed diff --git a/tests/ui/crashes/ice-6250.fixed b/tests/ui/crashes/ice-6250.fixed new file mode 100644 index 0000000000000..71b3c90d56b0e --- /dev/null +++ b/tests/ui/crashes/ice-6250.fixed @@ -0,0 +1,19 @@ +// originally from glacier/fixed/77218.rs +// ice while adjusting... +#![expect(clippy::useless_vec)] + +pub struct Cache { + data: Vec, +} + +pub fn list_data(cache: &Cache, key: usize) { + for reference in vec![1, 2, 3] { + if + /* let */ + let Some(reference) = cache.data.get(key) { + //~^ ERROR: mismatched types + //~| ERROR: mismatched types + unimplemented!() + } + } +} diff --git a/tests/ui/crashes/ice-6250.rs b/tests/ui/crashes/ice-6250.rs index 65cdce7931429..8c6100e2054df 100644 --- a/tests/ui/crashes/ice-6250.rs +++ b/tests/ui/crashes/ice-6250.rs @@ -1,6 +1,7 @@ // originally from glacier/fixed/77218.rs // ice while adjusting... -//@no-rustfix +#![expect(clippy::useless_vec)] + pub struct Cache { data: Vec, } diff --git a/tests/ui/crashes/ice-6250.stderr b/tests/ui/crashes/ice-6250.stderr index c126547611f36..83a7ef9fd2cf0 100644 --- a/tests/ui/crashes/ice-6250.stderr +++ b/tests/ui/crashes/ice-6250.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> tests/ui/crashes/ice-6250.rs:12:14 + --> tests/ui/crashes/ice-6250.rs:13:14 | LL | for reference in vec![1, 2, 3] { | --------- expected due to the type of this binding @@ -8,7 +8,7 @@ LL | Some(reference) = cache.data.get(key) { | ^^^^^^^^^ expected integer, found `&i32` error[E0308]: mismatched types - --> tests/ui/crashes/ice-6250.rs:12:9 + --> tests/ui/crashes/ice-6250.rs:13:9 | LL | Some(reference) = cache.data.get(key) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found `()` diff --git a/tests/ui/crashes/ice-9041.fixed b/tests/ui/crashes/ice-9041.fixed new file mode 100644 index 0000000000000..32399e4255d1d --- /dev/null +++ b/tests/ui/crashes/ice-9041.fixed @@ -0,0 +1,12 @@ +#![warn(clippy::search_is_some)] +#![allow(clippy::needless_borrow)] + +pub struct Thing; + +pub fn has_thing(things: &[Thing]) -> bool { + let is_thing_ready = |_peer: &Thing| -> bool { todo!() }; + things.iter().any(|p| is_thing_ready(&p)) + //~^ search_is_some +} + +fn main() {} diff --git a/tests/ui/crashes/ice-9041.rs b/tests/ui/crashes/ice-9041.rs index fae3233ba2f51..55e88fa6da5af 100644 --- a/tests/ui/crashes/ice-9041.rs +++ b/tests/ui/crashes/ice-9041.rs @@ -1,6 +1,8 @@ #![warn(clippy::search_is_some)] +#![allow(clippy::needless_borrow)] + pub struct Thing; -//@no-rustfix + pub fn has_thing(things: &[Thing]) -> bool { let is_thing_ready = |_peer: &Thing| -> bool { todo!() }; things.iter().find(|p| is_thing_ready(p)).is_some() diff --git a/tests/ui/crashes/ice-9041.stderr b/tests/ui/crashes/ice-9041.stderr index 256c8b8330344..98bdff9bbfac4 100644 --- a/tests/ui/crashes/ice-9041.stderr +++ b/tests/ui/crashes/ice-9041.stderr @@ -1,5 +1,5 @@ error: called `is_some()` after searching an `Iterator` with `find` - --> tests/ui/crashes/ice-9041.rs:6:19 + --> tests/ui/crashes/ice-9041.rs:8:19 | LL | things.iter().find(|p| is_thing_ready(p)).is_some() | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `any(|p| is_thing_ready(&p))` diff --git a/tests/ui/manual_memcpy/with_loop_counters.fixed b/tests/ui/manual_memcpy/with_loop_counters.fixed new file mode 100644 index 0000000000000..8fd8b9cd0ffde --- /dev/null +++ b/tests/ui/manual_memcpy/with_loop_counters.fixed @@ -0,0 +1,55 @@ +#![warn(clippy::needless_range_loop, clippy::manual_memcpy)] +#![allow(clippy::redundant_slicing, clippy::identity_op)] + +pub fn manual_copy_with_counters(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) { + let mut count = 0; + dst[3..src.len()].copy_from_slice(&src[..(src.len() - 3)]); + + let mut count = 0; + dst[..(src.len() - 3)].copy_from_slice(&src[3..]); + + let mut count = 3; + dst[3..(src.len() + 3)].copy_from_slice(&src[..]); + + let mut count = 3; + dst[..src.len()].copy_from_slice(&src[3..(src.len() + 3)]); + + let mut count = 0; + dst[3..(3 + src.len())].copy_from_slice(&src[..(3 + src.len() - 3)]); + + let mut count = 3; + dst[5..src.len()].copy_from_slice(&src[(3 - 2)..((src.len() - 2) + 3 - 5)]); + + let mut count = 2; + dst.copy_from_slice(&src[2..(dst.len() + 2)]); + + let mut count = 5; + dst[3..10].copy_from_slice(&src[5..(10 + 5 - 3)]); + + let mut count = 3; + let mut count2 = 30; + dst[3..(src.len() + 3)].copy_from_slice(&src[..]); + dst2[30..(src.len() + 30)].copy_from_slice(&src[..]); + + // make sure parentheses are added properly to bitwise operators, which have lower precedence than + // arithmetic ones + let mut count = 0 << 1; + dst[(0 << 1)..((1 << 1) + (0 << 1))].copy_from_slice(&src[2..((1 << 1) + 2)]); + + // make sure incrementing expressions without semicolons at the end of loops are handled correctly. + let mut count = 0; + dst[3..src.len()].copy_from_slice(&src[..(src.len() - 3)]); + + // make sure ones where the increment is not at the end of the loop. + // As a possible enhancement, one could adjust the offset in the suggestion according to + // the position. For example, if the increment is at the top of the loop; + // treating the loop counter as if it were initialized 1 greater than the original value. + let mut count = 0; + #[allow(clippy::needless_range_loop)] + for i in 0..src.len() { + count += 1; + dst[i] = src[count]; + } +} + +fn main() {} diff --git a/tests/ui/manual_memcpy/with_loop_counters.rs b/tests/ui/manual_memcpy/with_loop_counters.rs index c83a26cab21ab..9296cefef4f0a 100644 --- a/tests/ui/manual_memcpy/with_loop_counters.rs +++ b/tests/ui/manual_memcpy/with_loop_counters.rs @@ -1,5 +1,6 @@ #![warn(clippy::needless_range_loop, clippy::manual_memcpy)] -//@no-rustfix +#![allow(clippy::redundant_slicing, clippy::identity_op)] + pub fn manual_copy_with_counters(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) { let mut count = 0; for i in 3..src.len() { diff --git a/tests/ui/manual_memcpy/with_loop_counters.stderr b/tests/ui/manual_memcpy/with_loop_counters.stderr index 70da8309f3987..fafa855bad798 100644 --- a/tests/ui/manual_memcpy/with_loop_counters.stderr +++ b/tests/ui/manual_memcpy/with_loop_counters.stderr @@ -1,5 +1,5 @@ error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/with_loop_counters.rs:5:5 + --> tests/ui/manual_memcpy/with_loop_counters.rs:6:5 | LL | / for i in 3..src.len() { LL | | @@ -13,7 +13,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::manual_memcpy)]` error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/with_loop_counters.rs:13:5 + --> tests/ui/manual_memcpy/with_loop_counters.rs:14:5 | LL | / for i in 3..src.len() { LL | | @@ -24,7 +24,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst[..(src.len() - 3)].copy_from_slice(&src[3..]);` error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/with_loop_counters.rs:21:5 + --> tests/ui/manual_memcpy/with_loop_counters.rs:22:5 | LL | / for i in 0..src.len() { LL | | @@ -35,7 +35,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst[3..(src.len() + 3)].copy_from_slice(&src[..]);` error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/with_loop_counters.rs:29:5 + --> tests/ui/manual_memcpy/with_loop_counters.rs:30:5 | LL | / for i in 0..src.len() { LL | | @@ -46,7 +46,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst[..src.len()].copy_from_slice(&src[3..(src.len() + 3)]);` error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/with_loop_counters.rs:37:5 + --> tests/ui/manual_memcpy/with_loop_counters.rs:38:5 | LL | / for i in 3..(3 + src.len()) { LL | | @@ -57,7 +57,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst[3..(3 + src.len())].copy_from_slice(&src[..(3 + src.len() - 3)]);` error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/with_loop_counters.rs:45:5 + --> tests/ui/manual_memcpy/with_loop_counters.rs:46:5 | LL | / for i in 5..src.len() { LL | | @@ -68,7 +68,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst[5..src.len()].copy_from_slice(&src[(3 - 2)..((src.len() - 2) + 3 - 5)]);` error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/with_loop_counters.rs:53:5 + --> tests/ui/manual_memcpy/with_loop_counters.rs:54:5 | LL | / for i in 0..dst.len() { LL | | @@ -79,7 +79,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst.copy_from_slice(&src[2..(dst.len() + 2)]);` error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/with_loop_counters.rs:61:5 + --> tests/ui/manual_memcpy/with_loop_counters.rs:62:5 | LL | / for i in 3..10 { LL | | @@ -90,7 +90,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst[3..10].copy_from_slice(&src[5..(10 + 5 - 3)]);` error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/with_loop_counters.rs:70:5 + --> tests/ui/manual_memcpy/with_loop_counters.rs:71:5 | LL | / for i in 0..src.len() { LL | | @@ -108,7 +108,7 @@ LL + dst2[30..(src.len() + 30)].copy_from_slice(&src[..]); | error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/with_loop_counters.rs:82:5 + --> tests/ui/manual_memcpy/with_loop_counters.rs:83:5 | LL | / for i in 0..1 << 1 { LL | | @@ -119,7 +119,7 @@ LL | | } | |_____^ help: try replacing the loop by: `dst[(0 << 1)..((1 << 1) + (0 << 1))].copy_from_slice(&src[2..((1 << 1) + 2)]);` error: it looks like you're manually copying between slices - --> tests/ui/manual_memcpy/with_loop_counters.rs:91:5 + --> tests/ui/manual_memcpy/with_loop_counters.rs:92:5 | LL | / for i in 3..src.len() { LL | | diff --git a/tests/ui/manual_memcpy/without_loop_counters.fixed b/tests/ui/manual_memcpy/without_loop_counters.fixed new file mode 100644 index 0000000000000..8502c740b7126 --- /dev/null +++ b/tests/ui/manual_memcpy/without_loop_counters.fixed @@ -0,0 +1,175 @@ +#![warn(clippy::manual_memcpy)] +#![allow( + clippy::assigning_clones, + clippy::useless_vec, + clippy::needless_range_loop, + clippy::manual_slice_fill, + clippy::redundant_slicing +)] + +const LOOP_OFFSET: usize = 5000; + +pub fn manual_copy(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) { + // plain manual memcpy + dst[..src.len()].copy_from_slice(&src[..]); + + // dst offset memcpy + dst[10..(src.len() + 10)].copy_from_slice(&src[..]); + + // src offset memcpy + dst[..src.len()].copy_from_slice(&src[10..(src.len() + 10)]); + + // src offset memcpy + dst[11..src.len()].copy_from_slice(&src[(11 - 10)..(src.len() - 10)]); + + // overwrite entire dst + dst.copy_from_slice(&src[..dst.len()]); + + // manual copy with branch - can't easily convert to memcpy! + for i in 0..src.len() { + dst[i] = src[i]; + if dst[i] > 5 { + break; + } + } + + // multiple copies - suggest two memcpy statements + dst[10..256].copy_from_slice(&src[(10 - 5)..(256 - 5)]); + dst2[(10 + 500)..(256 + 500)].copy_from_slice(&src[10..256]); + + // this is a reversal - the copy lint shouldn't be triggered + for i in 10..LOOP_OFFSET { + dst[i + LOOP_OFFSET] = src[LOOP_OFFSET - i]; + } + + let some_var = 5; + // Offset in variable + dst[(10 + LOOP_OFFSET)..(LOOP_OFFSET + LOOP_OFFSET)].copy_from_slice(&src[(10 - some_var)..(LOOP_OFFSET - some_var)]); + + // Non continuous copy - don't trigger lint + for i in 0..10 { + dst[i + i] = src[i]; + } + + let src_vec = vec![1, 2, 3, 4, 5]; + let mut dst_vec = vec![0, 0, 0, 0, 0]; + + // make sure vectors are supported + dst_vec[..src_vec.len()].copy_from_slice(&src_vec[..]); + + // lint should not trigger when either + // source or destination type is not + // slice-like, like DummyStruct + struct DummyStruct(i32); + + impl ::std::ops::Index for DummyStruct { + type Output = i32; + + fn index(&self, _: usize) -> &i32 { + &self.0 + } + } + + let src = DummyStruct(5); + let mut dst_vec = vec![0; 10]; + + for i in 0..10 { + dst_vec[i] = src[i]; + } + + // Simplify suggestion (issue #3004) + let src = [0, 1, 2, 3, 4]; + let mut dst = [0, 0, 0, 0, 0, 0]; + let from = 1; + + dst[from..(from + src.len())].copy_from_slice(&src[..(from + src.len() - from)]); + + dst[from..(from + 3)].copy_from_slice(&src[..(from + 3 - from)]); + + #[allow(clippy::identity_op)] + dst[..5].copy_from_slice(&src); + + #[allow(clippy::reversed_empty_ranges)] + dst[..0].copy_from_slice(&src[..0]); + + // `RangeTo` `for` loop - don't trigger lint + for i in 0.. { + dst[i] = src[i]; + } + + // VecDeque - ideally this would work, but would require something like `range_as_slices` + let mut dst = std::collections::VecDeque::from_iter([0; 5]); + let src = std::collections::VecDeque::from_iter([0, 1, 2, 3, 4]); + for i in 0..dst.len() { + dst[i] = src[i]; + } + let src = vec![0, 1, 2, 3, 4]; + for i in 0..dst.len() { + dst[i] = src[i]; + } + + // Range is equal to array length + let src = [0, 1, 2, 3, 4]; + let mut dst = [0; 4]; + dst.copy_from_slice(&src[..4]); + + let mut dst = [0; 6]; + dst[..5].copy_from_slice(&src); + + let mut dst = [0; 5]; + dst.copy_from_slice(&src); + + // Don't trigger lint for following multi-dimensional arrays + let src = [[0; 5]; 5]; + for i in 0..4 { + dst[i] = src[i + 1][i]; + } + for i in 0..5 { + dst[i] = src[i][i]; + } + for i in 0..5 { + dst[i] = src[i][3]; + } + + let src = [0; 5]; + let mut dst = [[0; 5]; 5]; + for i in 0..5 { + dst[i][i] = src[i]; + } + + let src = [[[0; 5]; 5]; 5]; + let mut dst = [0; 5]; + for i in 0..5 { + dst[i] = src[i][i][i]; + } + for i in 0..5 { + dst[i] = src[i][i][0]; + } + for i in 0..5 { + dst[i] = src[i][0][i]; + } + for i in 0..5 { + dst[i] = src[0][i][i]; + } + for i in 0..5 { + dst[i] = src[0][i][1]; + } + for i in 0..5 { + dst[i] = src[i][0][1]; + } + + // Trigger lint + let src = [[0; 5]; 5]; + let mut dst = [0; 5]; + dst.copy_from_slice(&src[0]); + + let src = [[[0; 5]; 5]; 5]; + dst.copy_from_slice(&src[0][1]); +} + +#[warn(clippy::needless_range_loop, clippy::manual_memcpy)] +pub fn manual_clone(src: &[String], dst: &mut [String]) { + dst[..src.len()].clone_from_slice(&src[..]); +} + +fn main() {} diff --git a/tests/ui/manual_memcpy/without_loop_counters.rs b/tests/ui/manual_memcpy/without_loop_counters.rs index a3b8763812d72..fc55102fca453 100644 --- a/tests/ui/manual_memcpy/without_loop_counters.rs +++ b/tests/ui/manual_memcpy/without_loop_counters.rs @@ -3,10 +3,10 @@ clippy::assigning_clones, clippy::useless_vec, clippy::needless_range_loop, - clippy::manual_slice_fill + clippy::manual_slice_fill, + clippy::redundant_slicing )] -//@no-rustfix const LOOP_OFFSET: usize = 5000; pub fn manual_copy(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) { diff --git a/tests/ui/map_flatten.fixed b/tests/ui/map_flatten.fixed new file mode 100644 index 0000000000000..168db8dd62912 --- /dev/null +++ b/tests/ui/map_flatten.fixed @@ -0,0 +1,69 @@ +#![warn(clippy::map_flatten)] +#![allow(clippy::unnecessary_filter_map)] + +// issue #8506, multi-line +#[rustfmt::skip] +fn long_span() { + let _: Option = Some(1) + .and_then(|x| { + //~^ map_flatten + + + if x <= 5 { + Some(x) + } else { + None + } + }); + + let _: Result = Ok(1) + .and_then(|x| { + //~^ map_flatten + + if x == 1 { + Ok(x) + } else { + Err(0) + } + }); + + let result: Result = Ok(2); + fn do_something() { } + let _: Result = result + .and_then(|res| { + //~^ map_flatten + + if res > 0 { + do_something(); + Ok(res) + } else { + Err(0) + } + }); + + let _: Vec<_> = vec![5_i8; 6] + .into_iter() + .filter_map(|some_value| { + //~^ map_flatten + + if some_value > 3 { + Some(some_value) + } else { + None + } + }) + .collect(); +} + +#[allow(clippy::useless_vec)] +fn no_suggestion_if_comments_present() { + let vec = vec![vec![1, 2, 3]]; + let _ = vec + .iter() + // a lovely comment explaining the code in very detail + .flat_map(|x| x.iter()); +} + +fn main() { + long_span(); +} diff --git a/tests/ui/map_flatten.rs b/tests/ui/map_flatten.rs index 0970da8039a48..df4edd8f16eed 100644 --- a/tests/ui/map_flatten.rs +++ b/tests/ui/map_flatten.rs @@ -1,6 +1,6 @@ #![warn(clippy::map_flatten)] +#![allow(clippy::unnecessary_filter_map)] -//@no-rustfix // issue #8506, multi-line #[rustfmt::skip] fn long_span() { @@ -43,7 +43,7 @@ fn long_span() { } }) .flatten(); - + let _: Vec<_> = vec![5_i8; 6] .into_iter() .map(|some_value| { diff --git a/tests/ui/needless_collect_indirect.fixed b/tests/ui/needless_collect_indirect.fixed new file mode 100644 index 0000000000000..22b9bb6f6a20a --- /dev/null +++ b/tests/ui/needless_collect_indirect.fixed @@ -0,0 +1,344 @@ +#![allow( + clippy::uninlined_format_args, + clippy::useless_vec, + clippy::needless_ifs, + clippy::iter_next_slice, + clippy::iter_count +)] +#![warn(clippy::needless_collect)] + +use std::collections::{BinaryHeap, HashMap, HashSet, LinkedList, VecDeque}; + +fn main() { + let sample = [1; 5]; + + //~^ needless_collect + + sample.iter().map(|x| (x, x + 1)).collect::>(); + + //~^ needless_collect + + sample.iter().count(); + + //~^ needless_collect + + sample.iter().next().is_none(); + + //~^ needless_collect + + sample.iter().any(|x| x == &5); + let indirect_negative = sample.iter().collect::>(); + indirect_negative.len(); + indirect_negative + .into_iter() + .map(|x| (*x, *x + 1)) + .collect::>(); + + // #6202 + let a = "a".to_string(); + let sample = vec![a.clone(), "b".to_string(), "c".to_string()]; + + //~^ needless_collect + + sample.into_iter().any(|x| x == a); + + // Fix #5991 + let vec_a = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + let vec_b = vec_a.iter().collect::>(); + if vec_b.len() > 3 {} + let other_vec = vec![1, 3, 12, 4, 16, 2]; + let we_got_the_same_numbers = other_vec.iter().filter(|item| vec_b.contains(item)).collect::>(); + + // Fix #6297 + let sample = [1; 5]; + let multiple_indirect = sample.iter().collect::>(); + let sample2 = vec![2, 3]; + if multiple_indirect.is_empty() { + // do something + } else { + let found = sample2 + .iter() + .filter(|i| multiple_indirect.iter().any(|s| **s % **i == 0)) + .collect::>(); + } +} + +mod issue7110 { + // #7110 - lint for type annotation cases + use super::*; + + fn lint_vec(string: &str) -> usize { + + //~^ needless_collect + + string.split('/').count() + } + fn lint_vec_deque() -> usize { + let sample = [1; 5]; + + //~^ needless_collect + + sample.iter().count() + } + fn lint_linked_list() -> usize { + let sample = [1; 5]; + + //~^ needless_collect + + sample.iter().count() + } + fn lint_binary_heap() -> usize { + let sample = [1; 5]; + + //~^ needless_collect + + sample.iter().count() + } + fn dont_lint(string: &str) -> usize { + let buffer: Vec<&str> = string.split('/').collect(); + for buff in &buffer { + println!("{}", buff); + } + buffer.len() + } +} + +mod issue7975 { + use super::*; + + fn direct_mapping_with_used_mutable_reference() -> Vec<()> { + let test_vec: Vec<()> = vec![]; + let mut vec_2: Vec<()> = vec![]; + let mut_ref = &mut vec_2; + let collected_vec: Vec<_> = test_vec.into_iter().map(|_| mut_ref.push(())).collect(); + collected_vec.into_iter().map(|_| mut_ref.push(())).collect() + } + + fn indirectly_mapping_with_used_mutable_reference() -> Vec<()> { + let test_vec: Vec<()> = vec![]; + let mut vec_2: Vec<()> = vec![]; + let mut_ref = &mut vec_2; + let collected_vec: Vec<_> = test_vec.into_iter().map(|_| mut_ref.push(())).collect(); + let iter = collected_vec.into_iter(); + iter.map(|_| mut_ref.push(())).collect() + } + + fn indirect_collect_after_indirect_mapping_with_used_mutable_reference() -> Vec<()> { + let test_vec: Vec<()> = vec![]; + let mut vec_2: Vec<()> = vec![]; + let mut_ref = &mut vec_2; + let collected_vec: Vec<_> = test_vec.into_iter().map(|_| mut_ref.push(())).collect(); + let iter = collected_vec.into_iter(); + let mapped_iter = iter.map(|_| mut_ref.push(())); + mapped_iter.collect() + } +} + +fn allow_test() { + #[allow(clippy::needless_collect)] + let v = [1].iter().collect::>(); + v.into_iter().collect::>(); +} + +mod issue_8553 { + fn test_for() { + let vec = vec![1, 2]; + let w: Vec = vec.iter().map(|i| i * i).collect(); + + for i in 0..2 { + // Do not lint, because this method call is in the loop + w.contains(&i); + } + + for i in 0..2 { + + //~^ needless_collect + + let z: Vec = vec.iter().map(|k| k * k).collect(); + // Do lint + vec.iter().map(|k| k * k).any(|x| x == i); + for j in 0..2 { + // Do not lint, because this method call is in the loop + z.contains(&j); + } + } + + // Do not lint, because this variable is used. + w.contains(&0); + } + + fn test_while() { + let vec = vec![1, 2]; + let x: Vec = vec.iter().map(|i| i * i).collect(); + let mut n = 0; + while n > 1 { + // Do not lint, because this method call is in the loop + x.contains(&n); + n += 1; + } + + while n > 2 { + + //~^ needless_collect + + let z: Vec = vec.iter().map(|k| k * k).collect(); + // Do lint + vec.iter().map(|k| k * k).any(|x| x == n); + n += 1; + while n > 4 { + // Do not lint, because this method call is in the loop + z.contains(&n); + n += 1; + } + } + } + + fn test_loop() { + let vec = vec![1, 2]; + let x: Vec = vec.iter().map(|i| i * i).collect(); + let mut n = 0; + loop { + if n < 1 { + // Do not lint, because this method call is in the loop + x.contains(&n); + n += 1; + } else { + break; + } + } + + loop { + if n < 2 { + + //~^ needless_collect + + let z: Vec = vec.iter().map(|k| k * k).collect(); + // Do lint + vec.iter().map(|k| k * k).any(|x| x == n); + n += 1; + loop { + if n < 4 { + // Do not lint, because this method call is in the loop + z.contains(&n); + n += 1; + } else { + break; + } + } + } else { + break; + } + } + } + + fn test_while_let() { + let vec = vec![1, 2]; + let x: Vec = vec.iter().map(|i| i * i).collect(); + let optional = Some(0); + let mut n = 0; + while let Some(value) = optional { + if n < 1 { + // Do not lint, because this method call is in the loop + x.contains(&n); + n += 1; + } else { + break; + } + } + + while let Some(value) = optional { + + //~^ needless_collect + + let z: Vec = vec.iter().map(|k| k * k).collect(); + if n < 2 { + // Do lint + vec.iter().map(|k| k * k).any(|x| x == n); + n += 1; + } else { + break; + } + + while let Some(value) = optional { + if n < 4 { + // Do not lint, because this method call is in the loop + z.contains(&n); + n += 1; + } else { + break; + } + } + } + } + + fn test_if_cond() { + let vec = vec![1, 2]; + let v: Vec = vec.iter().map(|i| i * i).collect(); + + //~^ needless_collect + + // Do lint + for _ in 0..v.iter().count() { + todo!(); + } + } + + fn test_if_cond_false_case() { + let vec = vec![1, 2]; + let v: Vec = vec.iter().map(|i| i * i).collect(); + let w = v.iter().collect::>(); + // Do not lint, because w is used. + for _ in 0..w.len() { + todo!(); + } + + w.len(); + } + + fn test_while_cond() { + let mut vec = vec![1, 2]; + let mut v: Vec = vec.iter().map(|i| i * i).collect(); + + //~^ needless_collect + + // Do lint + while 1 == v.iter().count() { + todo!(); + } + } + + fn test_while_cond_false_case() { + let mut vec = vec![1, 2]; + let mut v: Vec = vec.iter().map(|i| i * i).collect(); + let mut w = v.iter().collect::>(); + // Do not lint, because w is used. + while 1 == w.len() { + todo!(); + } + + w.len(); + } + + fn test_while_let_cond() { + let mut vec = vec![1, 2]; + let mut v: Vec = vec.iter().map(|i| i * i).collect(); + + //~^ needless_collect + + // Do lint + while let Some(i) = Some(v.iter().count()) { + todo!(); + } + } + + fn test_while_let_cond_false_case() { + let mut vec = vec![1, 2]; + let mut v: Vec = vec.iter().map(|i| i * i).collect(); + let mut w = v.iter().collect::>(); + // Do not lint, because w is used. + while let Some(i) = Some(w.len()) { + todo!(); + } + w.len(); + } +} diff --git a/tests/ui/needless_collect_indirect.rs b/tests/ui/needless_collect_indirect.rs index 69764becfe666..20b7ae91a6b88 100644 --- a/tests/ui/needless_collect_indirect.rs +++ b/tests/ui/needless_collect_indirect.rs @@ -1,6 +1,12 @@ -#![allow(clippy::uninlined_format_args, clippy::useless_vec, clippy::needless_ifs)] +#![allow( + clippy::uninlined_format_args, + clippy::useless_vec, + clippy::needless_ifs, + clippy::iter_next_slice, + clippy::iter_count +)] #![warn(clippy::needless_collect)] -//@no-rustfix + use std::collections::{BinaryHeap, HashMap, HashSet, LinkedList, VecDeque}; fn main() { diff --git a/tests/ui/needless_collect_indirect.stderr b/tests/ui/needless_collect_indirect.stderr index 24523c9f97b04..d34f1c37558dc 100644 --- a/tests/ui/needless_collect_indirect.stderr +++ b/tests/ui/needless_collect_indirect.stderr @@ -1,5 +1,5 @@ error: avoid using `collect()` when not needed - --> tests/ui/needless_collect_indirect.rs:8:39 + --> tests/ui/needless_collect_indirect.rs:14:39 | LL | let indirect_iter = sample.iter().collect::>(); | ^^^^^^^ @@ -18,7 +18,7 @@ LL ~ sample.iter().map(|x| (x, x + 1)).collect::>(); | error: avoid using `collect()` when not needed - --> tests/ui/needless_collect_indirect.rs:12:38 + --> tests/ui/needless_collect_indirect.rs:18:38 | LL | let indirect_len = sample.iter().collect::>(); | ^^^^^^^ @@ -35,7 +35,7 @@ LL ~ sample.iter().count(); | error: avoid using `collect()` when not needed - --> tests/ui/needless_collect_indirect.rs:16:40 + --> tests/ui/needless_collect_indirect.rs:22:40 | LL | let indirect_empty = sample.iter().collect::>(); | ^^^^^^^ @@ -52,7 +52,7 @@ LL ~ sample.iter().next().is_none(); | error: avoid using `collect()` when not needed - --> tests/ui/needless_collect_indirect.rs:20:43 + --> tests/ui/needless_collect_indirect.rs:26:43 | LL | let indirect_contains = sample.iter().collect::>(); | ^^^^^^^ @@ -69,7 +69,7 @@ LL ~ sample.iter().any(|x| x == &5); | error: avoid using `collect()` when not needed - --> tests/ui/needless_collect_indirect.rs:34:48 + --> tests/ui/needless_collect_indirect.rs:40:48 | LL | let non_copy_contains = sample.into_iter().collect::>(); | ^^^^^^^ @@ -86,7 +86,7 @@ LL ~ sample.into_iter().any(|x| x == a); | error: avoid using `collect()` when not needed - --> tests/ui/needless_collect_indirect.rs:65:51 + --> tests/ui/needless_collect_indirect.rs:71:51 | LL | let buffer: Vec<&str> = string.split('/').collect(); | ^^^^^^^ @@ -103,7 +103,7 @@ LL ~ string.split('/').count() | error: avoid using `collect()` when not needed - --> tests/ui/needless_collect_indirect.rs:72:55 + --> tests/ui/needless_collect_indirect.rs:78:55 | LL | let indirect_len: VecDeque<_> = sample.iter().collect(); | ^^^^^^^ @@ -120,7 +120,7 @@ LL ~ sample.iter().count() | error: avoid using `collect()` when not needed - --> tests/ui/needless_collect_indirect.rs:79:57 + --> tests/ui/needless_collect_indirect.rs:85:57 | LL | let indirect_len: LinkedList<_> = sample.iter().collect(); | ^^^^^^^ @@ -137,7 +137,7 @@ LL ~ sample.iter().count() | error: avoid using `collect()` when not needed - --> tests/ui/needless_collect_indirect.rs:86:57 + --> tests/ui/needless_collect_indirect.rs:92:57 | LL | let indirect_len: BinaryHeap<_> = sample.iter().collect(); | ^^^^^^^ @@ -154,7 +154,7 @@ LL ~ sample.iter().count() | error: avoid using `collect()` when not needed - --> tests/ui/needless_collect_indirect.rs:148:59 + --> tests/ui/needless_collect_indirect.rs:154:59 | LL | let y: Vec = vec.iter().map(|k| k * k).collect(); | ^^^^^^^ @@ -172,7 +172,7 @@ LL ~ vec.iter().map(|k| k * k).any(|x| x == i); | error: avoid using `collect()` when not needed - --> tests/ui/needless_collect_indirect.rs:175:59 + --> tests/ui/needless_collect_indirect.rs:181:59 | LL | let y: Vec = vec.iter().map(|k| k * k).collect(); | ^^^^^^^ @@ -190,7 +190,7 @@ LL ~ vec.iter().map(|k| k * k).any(|x| x == n); | error: avoid using `collect()` when not needed - --> tests/ui/needless_collect_indirect.rs:206:63 + --> tests/ui/needless_collect_indirect.rs:212:63 | LL | let y: Vec = vec.iter().map(|k| k * k).collect(); | ^^^^^^^ @@ -208,7 +208,7 @@ LL ~ vec.iter().map(|k| k * k).any(|x| x == n); | error: avoid using `collect()` when not needed - --> tests/ui/needless_collect_indirect.rs:244:59 + --> tests/ui/needless_collect_indirect.rs:250:59 | LL | let y: Vec = vec.iter().map(|k| k * k).collect(); | ^^^^^^^ @@ -226,7 +226,7 @@ LL ~ vec.iter().map(|k| k * k).any(|x| x == n); | error: avoid using `collect()` when not needed - --> tests/ui/needless_collect_indirect.rs:271:26 + --> tests/ui/needless_collect_indirect.rs:277:26 | LL | let w = v.iter().collect::>(); | ^^^^^^^ @@ -244,7 +244,7 @@ LL ~ for _ in 0..v.iter().count() { | error: avoid using `collect()` when not needed - --> tests/ui/needless_collect_indirect.rs:295:30 + --> tests/ui/needless_collect_indirect.rs:301:30 | LL | let mut w = v.iter().collect::>(); | ^^^^^^^ @@ -262,7 +262,7 @@ LL ~ while 1 == v.iter().count() { | error: avoid using `collect()` when not needed - --> tests/ui/needless_collect_indirect.rs:319:30 + --> tests/ui/needless_collect_indirect.rs:325:30 | LL | let mut w = v.iter().collect::>(); | ^^^^^^^ diff --git a/tests/ui/track-diagnostics-clippy.fixed b/tests/ui/track-diagnostics-clippy.fixed new file mode 100644 index 0000000000000..f25ffa7e9afdf --- /dev/null +++ b/tests/ui/track-diagnostics-clippy.fixed @@ -0,0 +1,22 @@ +//@compile-flags: -Z track-diagnostics + +// Normalize the emitted location so this doesn't need +// updating everytime someone adds or removes a line. +//@normalize-stderr-test: ".rs:\d+:\d+" -> ".rs:LL:CC" +//@normalize-stderr-test: "src/tools/clippy/" -> "" + +#![warn(clippy::let_and_return, clippy::unnecessary_cast)] + +fn main() { + // Check the provenance of a lint sent through `LintContext::span_lint()` + let a = 3u32; + let b = a; + //~^ unnecessary_cast + + // Check the provenance of a lint sent through `TyCtxt::node_span_lint()` + let c = { + + 42 + //~^ let_and_return + }; +} diff --git a/tests/ui/track-diagnostics-clippy.rs b/tests/ui/track-diagnostics-clippy.rs index 3bae23f198495..8fc1d996c0029 100644 --- a/tests/ui/track-diagnostics-clippy.rs +++ b/tests/ui/track-diagnostics-clippy.rs @@ -1,5 +1,4 @@ //@compile-flags: -Z track-diagnostics -//@no-rustfix // Normalize the emitted location so this doesn't need // updating everytime someone adds or removes a line. diff --git a/tests/ui/transmute_ref_to_ref.fixed b/tests/ui/transmute_ref_to_ref.fixed new file mode 100644 index 0000000000000..0ec59ea810aed --- /dev/null +++ b/tests/ui/transmute_ref_to_ref.fixed @@ -0,0 +1,38 @@ +#![deny(clippy::transmute_ptr_to_ptr)] +#![allow(dead_code, clippy::missing_transmute_annotations, clippy::cast_slice_different_sizes)] + +fn main() { + unsafe { + let single_u64: &[u64] = &[0xDEAD_BEEF_DEAD_BEEF]; + let bools: &[bool] = unsafe { &*(single_u64 as *const [u64] as *const [bool]) }; + //~^ transmute_ptr_to_ptr + + let a: &[u32] = &[0x12345678, 0x90ABCDEF, 0xFEDCBA09, 0x87654321]; + let b: &[u8] = unsafe { &*(a as *const [u32] as *const [u8]) }; + //~^ transmute_ptr_to_ptr + + let bytes = &[1u8, 2u8, 3u8, 4u8] as &[u8]; + let alt_slice: &[u32] = unsafe { &*(bytes as *const [u8] as *const [u32]) }; + //~^ transmute_ptr_to_ptr + } +} + +fn issue16104(make_ptr: fn() -> *const u32) { + macro_rules! call { + ($x:expr) => { + $x() + }; + } + macro_rules! take_ref { + ($x:expr) => { + &$x + }; + } + + unsafe { + let _: *const f32 = call!(make_ptr).cast::(); + //~^ transmute_ptr_to_ptr + let _: &f32 = &*(take_ref!(1u32) as *const u32 as *const f32); + //~^ transmute_ptr_to_ptr + } +} diff --git a/tests/ui/transmute_ref_to_ref.rs b/tests/ui/transmute_ref_to_ref.rs index ed8fb80832918..78f3d1fe0bb24 100644 --- a/tests/ui/transmute_ref_to_ref.rs +++ b/tests/ui/transmute_ref_to_ref.rs @@ -1,7 +1,5 @@ -//@no-rustfix - #![deny(clippy::transmute_ptr_to_ptr)] -#![allow(dead_code, clippy::missing_transmute_annotations)] +#![allow(dead_code, clippy::missing_transmute_annotations, clippy::cast_slice_different_sizes)] fn main() { unsafe { diff --git a/tests/ui/transmute_ref_to_ref.stderr b/tests/ui/transmute_ref_to_ref.stderr index 1b845ef859d88..13ee64574f7b9 100644 --- a/tests/ui/transmute_ref_to_ref.stderr +++ b/tests/ui/transmute_ref_to_ref.stderr @@ -1,29 +1,29 @@ error: transmute from a reference to a reference - --> tests/ui/transmute_ref_to_ref.rs:9:39 + --> tests/ui/transmute_ref_to_ref.rs:7:39 | LL | let bools: &[bool] = unsafe { std::mem::transmute(single_u64) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(single_u64 as *const [u64] as *const [bool])` | note: the lint level is defined here - --> tests/ui/transmute_ref_to_ref.rs:3:9 + --> tests/ui/transmute_ref_to_ref.rs:1:9 | LL | #![deny(clippy::transmute_ptr_to_ptr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a reference to a reference - --> tests/ui/transmute_ref_to_ref.rs:13:33 + --> tests/ui/transmute_ref_to_ref.rs:11:33 | LL | let b: &[u8] = unsafe { std::mem::transmute(a) }; | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(a as *const [u32] as *const [u8])` error: transmute from a reference to a reference - --> tests/ui/transmute_ref_to_ref.rs:17:42 + --> tests/ui/transmute_ref_to_ref.rs:15:42 | LL | let alt_slice: &[u32] = unsafe { std::mem::transmute(bytes) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(bytes as *const [u8] as *const [u32])` error: transmute from a pointer to a pointer - --> tests/ui/transmute_ref_to_ref.rs:35:29 + --> tests/ui/transmute_ref_to_ref.rs:33:29 | LL | let _: *const f32 = std::mem::transmute(call!(make_ptr)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -35,7 +35,7 @@ LL + let _: *const f32 = call!(make_ptr).cast::(); | error: transmute from a reference to a reference - --> tests/ui/transmute_ref_to_ref.rs:37:23 + --> tests/ui/transmute_ref_to_ref.rs:35:23 | LL | let _: &f32 = std::mem::transmute(take_ref!(1u32)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(take_ref!(1u32) as *const u32 as *const f32)` diff --git a/tests/ui/transmute_ref_to_ref_no_std.fixed b/tests/ui/transmute_ref_to_ref_no_std.fixed new file mode 100644 index 0000000000000..3a1c75f168546 --- /dev/null +++ b/tests/ui/transmute_ref_to_ref_no_std.fixed @@ -0,0 +1,30 @@ +#![deny(clippy::transmute_ptr_to_ptr)] +#![allow(dead_code, clippy::missing_transmute_annotations, clippy::cast_slice_different_sizes)] +#![feature(lang_items)] +#![no_std] + +use core::panic::PanicInfo; + +#[lang = "eh_personality"] +extern "C" fn eh_personality() {} + +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + loop {} +} + +fn main() { + unsafe { + let single_u64: &[u64] = &[0xDEAD_BEEF_DEAD_BEEF]; + let bools: &[bool] = unsafe { &*(single_u64 as *const [u64] as *const [bool]) }; + //~^ transmute_ptr_to_ptr + + let a: &[u32] = &[0x12345678, 0x90ABCDEF, 0xFEDCBA09, 0x87654321]; + let b: &[u8] = unsafe { &*(a as *const [u32] as *const [u8]) }; + //~^ transmute_ptr_to_ptr + + let bytes = &[1u8, 2u8, 3u8, 4u8] as &[u8]; + let alt_slice: &[u32] = unsafe { &*(bytes as *const [u8] as *const [u32]) }; + //~^ transmute_ptr_to_ptr + } +} diff --git a/tests/ui/transmute_ref_to_ref_no_std.rs b/tests/ui/transmute_ref_to_ref_no_std.rs index faa776f2afcec..7ed885bbbb07e 100644 --- a/tests/ui/transmute_ref_to_ref_no_std.rs +++ b/tests/ui/transmute_ref_to_ref_no_std.rs @@ -1,7 +1,5 @@ -//@no-rustfix - #![deny(clippy::transmute_ptr_to_ptr)] -#![allow(dead_code, clippy::missing_transmute_annotations)] +#![allow(dead_code, clippy::missing_transmute_annotations, clippy::cast_slice_different_sizes)] #![feature(lang_items)] #![no_std] diff --git a/tests/ui/transmute_ref_to_ref_no_std.stderr b/tests/ui/transmute_ref_to_ref_no_std.stderr index 9aa9ed928a4d5..136a9b21fb9e9 100644 --- a/tests/ui/transmute_ref_to_ref_no_std.stderr +++ b/tests/ui/transmute_ref_to_ref_no_std.stderr @@ -1,23 +1,23 @@ error: transmute from a reference to a reference - --> tests/ui/transmute_ref_to_ref_no_std.rs:21:39 + --> tests/ui/transmute_ref_to_ref_no_std.rs:19:39 | LL | let bools: &[bool] = unsafe { core::mem::transmute(single_u64) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(single_u64 as *const [u64] as *const [bool])` | note: the lint level is defined here - --> tests/ui/transmute_ref_to_ref_no_std.rs:3:9 + --> tests/ui/transmute_ref_to_ref_no_std.rs:1:9 | LL | #![deny(clippy::transmute_ptr_to_ptr)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a reference to a reference - --> tests/ui/transmute_ref_to_ref_no_std.rs:25:33 + --> tests/ui/transmute_ref_to_ref_no_std.rs:23:33 | LL | let b: &[u8] = unsafe { core::mem::transmute(a) }; | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(a as *const [u32] as *const [u8])` error: transmute from a reference to a reference - --> tests/ui/transmute_ref_to_ref_no_std.rs:29:42 + --> tests/ui/transmute_ref_to_ref_no_std.rs:27:42 | LL | let alt_slice: &[u32] = unsafe { core::mem::transmute(bytes) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(bytes as *const [u8] as *const [u32])` From a95d0c51d8c4aa9193abb46f72cd7c9783a62cec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Gonz=C3=A1lez?= Date: Sun, 22 Feb 2026 13:53:12 +0100 Subject: [PATCH 49/57] book: describe Clippy walk up directory tree behavior when searching config Reading the source code at https://github.com/rust-lang/rust-clippy/blob/b3188b8a9f0f2df2ba58bfe9001fe4fe711cff74/clippy_config/src/conf.rs#L928-L974 clearly shows that Clippy also searches parent directories when looking for a configuration file. However, this behavior is not documented anywhere I could see in the Clippy book, despite being useful to end users. For example, end users can leverage this to share a single configuration file across all member packages of a Cargo workspace. This change adds documentation for this behavior to the Clippy book. --- book/src/configuration.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/book/src/configuration.md b/book/src/configuration.md index b130544318980..b270c11ab397c 100644 --- a/book/src/configuration.md +++ b/book/src/configuration.md @@ -2,13 +2,17 @@ > **Note:** The configuration file is unstable and may be deprecated in the future. -Some lints can be configured in a TOML file named `clippy.toml` or `.clippy.toml`, which is searched for in: +Some lints can be configured in a TOML file named `clippy.toml` or `.clippy.toml`, which is searched for starting in the +first defined directory according to the following priority order: 1. The directory specified by the `CLIPPY_CONF_DIR` environment variable, or 2. The directory specified by the [CARGO_MANIFEST_DIR](https://doc.rust-lang.org/cargo/reference/environment-variables.html) environment variable, or 3. The current directory. +If the chosen directory does not contain a configuration file, Clippy will walk up the directory tree, searching each +parent directory until it finds one or reaches the filesystem root. + It contains a basic `variable = value` mapping e.g. ```toml From 34d0b12346b374951bd2d3990c9ff61a3bc3dc1e Mon Sep 17 00:00:00 2001 From: mikhailofff Date: Sun, 22 Feb 2026 16:41:34 +0400 Subject: [PATCH 50/57] correct `join_absolute_paths` for a double backslash --- clippy_lints/src/methods/join_absolute_paths.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/methods/join_absolute_paths.rs b/clippy_lints/src/methods/join_absolute_paths.rs index 905a58afa795e..9f2321ff30921 100644 --- a/clippy_lints/src/methods/join_absolute_paths.rs +++ b/clippy_lints/src/methods/join_absolute_paths.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::expr_or_init; use clippy_utils::res::MaybeDef; use clippy_utils::source::snippet; -use rustc_ast::ast::LitKind; +use rustc_ast::ast::{LitKind, StrStyle}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; @@ -15,7 +15,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, recv: &'tcx Expr<'tcx>, join_a let ty = cx.typeck_results().expr_ty(recv).peel_refs(); if matches!(ty.opt_diag_name(cx), Some(sym::Path | sym::PathBuf)) && let ExprKind::Lit(spanned) = expr_or_init(cx, join_arg).kind - && let LitKind::Str(symbol, _) = spanned.node + && let LitKind::Str(symbol, style) = spanned.node && let sym_str = symbol.as_str() && sym_str.starts_with(['/', '\\']) { @@ -29,8 +29,10 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, recv: &'tcx Expr<'tcx>, join_a let no_separator = if sym_str.starts_with('/') { arg_str.replacen('/', "", 1) - } else { + } else if let StrStyle::Raw(_) = style { arg_str.replacen('\\', "", 1) + } else { + arg_str.replacen("\\\\", "", 1) }; diag.note("joining a path starting with separator will replace the path instead") From f2a134b403c614e9529869d9d519602ae2a3ed58 Mon Sep 17 00:00:00 2001 From: mikhailofff Date: Sun, 22 Feb 2026 16:43:54 +0400 Subject: [PATCH 51/57] remove `no-rustfix` marker for tests --- tests/ui/join_absolute_paths.1.fixed | 36 ++++++++++++++ tests/ui/join_absolute_paths.2.fixed | 36 ++++++++++++++ tests/ui/join_absolute_paths.rs | 18 ++++--- tests/ui/join_absolute_paths.stderr | 72 +++++++++++++++++++++------- 4 files changed, 138 insertions(+), 24 deletions(-) create mode 100644 tests/ui/join_absolute_paths.1.fixed create mode 100644 tests/ui/join_absolute_paths.2.fixed diff --git a/tests/ui/join_absolute_paths.1.fixed b/tests/ui/join_absolute_paths.1.fixed new file mode 100644 index 0000000000000..ff524b529b187 --- /dev/null +++ b/tests/ui/join_absolute_paths.1.fixed @@ -0,0 +1,36 @@ +#![allow(clippy::needless_raw_string_hashes)] +#![warn(clippy::join_absolute_paths)] + +use std::path::{Path, PathBuf}; + +fn main() { + let path = Path::new("/bin"); + path.join("sh"); + //~^ join_absolute_paths + + let path = PathBuf::from("/bin"); + path.join("sh"); + //~^ join_absolute_paths + + let path = PathBuf::from("/bin"); + path.join(r#"sh"#); + //~^ join_absolute_paths + + let path = Path::new("C:\\Users"); + path.join("user"); + //~^ join_absolute_paths + + let path = PathBuf::from("C:\\Users"); + path.join("user"); + //~^ join_absolute_paths + + let path = PathBuf::from("C:\\Users"); + path.join(r#"user"#); + //~^ join_absolute_paths + + let path: &[&str] = &["/bin"]; + path.join("/sh"); + + let path = Path::new("/bin"); + path.join("sh"); +} diff --git a/tests/ui/join_absolute_paths.2.fixed b/tests/ui/join_absolute_paths.2.fixed new file mode 100644 index 0000000000000..372647ecde646 --- /dev/null +++ b/tests/ui/join_absolute_paths.2.fixed @@ -0,0 +1,36 @@ +#![allow(clippy::needless_raw_string_hashes)] +#![warn(clippy::join_absolute_paths)] + +use std::path::{Path, PathBuf}; + +fn main() { + let path = Path::new("/bin"); + PathBuf::from("/sh"); + //~^ join_absolute_paths + + let path = PathBuf::from("/bin"); + PathBuf::from("/sh"); + //~^ join_absolute_paths + + let path = PathBuf::from("/bin"); + PathBuf::from(r#"/sh"#); + //~^ join_absolute_paths + + let path = Path::new("C:\\Users"); + PathBuf::from("\\user"); + //~^ join_absolute_paths + + let path = PathBuf::from("C:\\Users"); + PathBuf::from("\\user"); + //~^ join_absolute_paths + + let path = PathBuf::from("C:\\Users"); + PathBuf::from(r#"\user"#); + //~^ join_absolute_paths + + let path: &[&str] = &["/bin"]; + path.join("/sh"); + + let path = Path::new("/bin"); + path.join("sh"); +} diff --git a/tests/ui/join_absolute_paths.rs b/tests/ui/join_absolute_paths.rs index 144c9147c2a1b..dffc4a5f10ed2 100644 --- a/tests/ui/join_absolute_paths.rs +++ b/tests/ui/join_absolute_paths.rs @@ -1,5 +1,3 @@ -//@no-rustfix - #![allow(clippy::needless_raw_string_hashes)] #![warn(clippy::join_absolute_paths)] @@ -10,10 +8,6 @@ fn main() { path.join("/sh"); //~^ join_absolute_paths - let path = Path::new("C:\\Users"); - path.join("\\user"); - //~^ join_absolute_paths - let path = PathBuf::from("/bin"); path.join("/sh"); //~^ join_absolute_paths @@ -22,6 +16,18 @@ fn main() { path.join(r#"/sh"#); //~^ join_absolute_paths + let path = Path::new("C:\\Users"); + path.join("\\user"); + //~^ join_absolute_paths + + let path = PathBuf::from("C:\\Users"); + path.join("\\user"); + //~^ join_absolute_paths + + let path = PathBuf::from("C:\\Users"); + path.join(r#"\user"#); + //~^ join_absolute_paths + let path: &[&str] = &["/bin"]; path.join("/sh"); diff --git a/tests/ui/join_absolute_paths.stderr b/tests/ui/join_absolute_paths.stderr index 300946bf3b5fc..c44292276d277 100644 --- a/tests/ui/join_absolute_paths.stderr +++ b/tests/ui/join_absolute_paths.stderr @@ -1,5 +1,5 @@ error: argument to `Path::join` starts with a path separator - --> tests/ui/join_absolute_paths.rs:10:15 + --> tests/ui/join_absolute_paths.rs:8:15 | LL | path.join("/sh"); | ^^^^^ @@ -19,7 +19,43 @@ LL + PathBuf::from("/sh"); | error: argument to `Path::join` starts with a path separator - --> tests/ui/join_absolute_paths.rs:14:15 + --> tests/ui/join_absolute_paths.rs:12:15 + | +LL | path.join("/sh"); + | ^^^^^ + | + = note: joining a path starting with separator will replace the path instead +help: if this is unintentional, try removing the starting separator + | +LL - path.join("/sh"); +LL + path.join("sh"); + | +help: if this is intentional, consider using `Path::new` + | +LL - path.join("/sh"); +LL + PathBuf::from("/sh"); + | + +error: argument to `Path::join` starts with a path separator + --> tests/ui/join_absolute_paths.rs:16:15 + | +LL | path.join(r#"/sh"#); + | ^^^^^^^^ + | + = note: joining a path starting with separator will replace the path instead +help: if this is unintentional, try removing the starting separator + | +LL - path.join(r#"/sh"#); +LL + path.join(r#"sh"#); + | +help: if this is intentional, consider using `Path::new` + | +LL - path.join(r#"/sh"#); +LL + PathBuf::from(r#"/sh"#); + | + +error: argument to `Path::join` starts with a path separator + --> tests/ui/join_absolute_paths.rs:20:15 | LL | path.join("\\user"); | ^^^^^^^^ @@ -28,7 +64,7 @@ LL | path.join("\\user"); help: if this is unintentional, try removing the starting separator | LL - path.join("\\user"); -LL + path.join("\user"); +LL + path.join("user"); | help: if this is intentional, consider using `Path::new` | @@ -37,40 +73,40 @@ LL + PathBuf::from("\\user"); | error: argument to `Path::join` starts with a path separator - --> tests/ui/join_absolute_paths.rs:18:15 + --> tests/ui/join_absolute_paths.rs:24:15 | -LL | path.join("/sh"); - | ^^^^^ +LL | path.join("\\user"); + | ^^^^^^^^ | = note: joining a path starting with separator will replace the path instead help: if this is unintentional, try removing the starting separator | -LL - path.join("/sh"); -LL + path.join("sh"); +LL - path.join("\\user"); +LL + path.join("user"); | help: if this is intentional, consider using `Path::new` | -LL - path.join("/sh"); -LL + PathBuf::from("/sh"); +LL - path.join("\\user"); +LL + PathBuf::from("\\user"); | error: argument to `Path::join` starts with a path separator - --> tests/ui/join_absolute_paths.rs:22:15 + --> tests/ui/join_absolute_paths.rs:28:15 | -LL | path.join(r#"/sh"#); - | ^^^^^^^^ +LL | path.join(r#"\user"#); + | ^^^^^^^^^^ | = note: joining a path starting with separator will replace the path instead help: if this is unintentional, try removing the starting separator | -LL - path.join(r#"/sh"#); -LL + path.join(r#"sh"#); +LL - path.join(r#"\user"#); +LL + path.join(r#"user"#); | help: if this is intentional, consider using `Path::new` | -LL - path.join(r#"/sh"#); -LL + PathBuf::from(r#"/sh"#); +LL - path.join(r#"\user"#); +LL + PathBuf::from(r#"\user"#); | -error: aborting due to 4 previous errors +error: aborting due to 6 previous errors From 6fe95cb73770a18fc62ae0e2a546bd06b9813246 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Sun, 22 Feb 2026 23:24:25 -0800 Subject: [PATCH 52/57] changelog_update.md: update name of rust primary branch Use `main` rather than `master` in the text explaining the link - the link was updated previously, but not the description. Also internally update the link name for consistency. --- book/src/development/infrastructure/changelog_update.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/book/src/development/infrastructure/changelog_update.md b/book/src/development/infrastructure/changelog_update.md index 9eb7e7cc9c9eb..4652d71a9ea33 100644 --- a/book/src/development/infrastructure/changelog_update.md +++ b/book/src/development/infrastructure/changelog_update.md @@ -28,8 +28,8 @@ bullet points might be helpful: check out the Clippy commit of the current Rust `beta` branch. [Link][rust_beta_tools] * When writing the release notes for the **upcoming beta release**, you need to - check out the Clippy commit of the current Rust `master`. - [Link][rust_master_tools] + check out the Clippy commit of the current Rust `main`. + [Link][rust_main_tools] * When writing the (forgotten) release notes for a **past stable release**, you need to check out the Rust release tag of the stable release. [Link][rust_stable_tools] @@ -112,7 +112,7 @@ written for. If not, update the version to the changelog version. [changelog]: https://github.com/rust-lang/rust-clippy/blob/master/CHANGELOG.md [forge]: https://forge.rust-lang.org/ -[rust_master_tools]: https://github.com/rust-lang/rust/tree/HEAD/src/tools/clippy +[rust_main_tools]: https://github.com/rust-lang/rust/tree/HEAD/src/tools/clippy [rust_beta_tools]: https://github.com/rust-lang/rust/tree/beta/src/tools/clippy [rust_stable_tools]: https://github.com/rust-lang/rust/releases [`beta-accepted`]: https://github.com/rust-lang/rust-clippy/issues?q=label%3Abeta-accepted+ From f56dfe47257f43035eda3e443ac48e915da47343 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 19 Feb 2026 15:02:55 +0100 Subject: [PATCH 53/57] test: don't call test functions in `main` seems to be a remnant of some older testing scheme that is completely unnecessary now --- .../await_holding_invalid_type.rs | 7 +- tests/ui-toml/expect_used/expect_used.rs | 3 - tests/ui-toml/expect_used/expect_used.stderr | 4 +- .../lint_decimal_readability/test.fixed | 2 - .../ui-toml/lint_decimal_readability/test.rs | 2 - .../lint_decimal_readability/test.stderr | 4 +- .../index_refutable_slice.fixed | 5 +- .../index_refutable_slice.rs | 5 +- .../min_rust_version/min_rust_version.fixed | 10 +- .../min_rust_version/min_rust_version.rs | 10 +- tests/ui-toml/mut_key/mut_key.rs | 5 +- tests/ui/attrs.rs | 13 +- tests/ui/crashes/returns.rs | 5 +- tests/ui/debug_assert_with_mut_call.rs | 10 +- tests/ui/doc/doc-fixable.fixed | 4 - tests/ui/doc/doc-fixable.rs | 4 - tests/ui/doc/doc-fixable.stderr | 40 +++--- tests/ui/doc_link_with_quotes.rs | 4 +- tests/ui/doc_link_with_quotes.stderr | 4 +- tests/ui/double_must_use.rs | 6 +- tests/ui/duplicate_underscore_argument.rs | 5 +- tests/ui/erasing_op.rs | 4 +- tests/ui/expect.rs | 6 +- tests/ui/floating_point_abs.fixed | 16 +-- tests/ui/floating_point_abs.rs | 16 +-- tests/ui/infinite_iter.rs | 5 +- tests/ui/infinite_iter.stderr | 2 +- tests/ui/ip_constant_from_external.rs | 4 +- tests/ui/iter_on_empty_collections.fixed | 6 +- tests/ui/iter_on_empty_collections.rs | 6 +- tests/ui/iter_on_single_items.fixed | 6 +- tests/ui/iter_on_single_items.rs | 6 +- tests/ui/iter_with_drain.fixed | 7 +- tests/ui/iter_with_drain.rs | 7 +- tests/ui/let_if_seq.rs | 5 - tests/ui/let_if_seq.stderr | 12 +- tests/ui/manual_is_variant_and.fixed | 5 +- tests/ui/manual_is_variant_and.rs | 5 +- tests/ui/manual_is_variant_and.stderr | 40 +++--- tests/ui/manual_option_as_slice.fixed | 5 +- tests/ui/manual_option_as_slice.rs | 5 +- tests/ui/manual_retain.fixed | 17 +-- tests/ui/manual_retain.rs | 17 +-- tests/ui/manual_retain.stderr | 76 ++++++------ tests/ui/manual_take.fixed | 2 - tests/ui/manual_take.rs | 2 - tests/ui/manual_take.stderr | 4 +- tests/ui/map_flatten.fixed | 4 +- tests/ui/map_flatten.rs | 4 +- tests/ui/map_unwrap_or.rs | 5 +- tests/ui/map_unwrap_or.stderr | 8 +- tests/ui/map_unwrap_or_fixable.fixed | 5 +- tests/ui/map_unwrap_or_fixable.rs | 5 +- tests/ui/map_unwrap_or_fixable.stderr | 18 +-- tests/ui/mem_replace.fixed | 6 +- tests/ui/mem_replace.rs | 6 +- tests/ui/mem_replace.stderr | 18 +-- tests/ui/mem_replace_no_std.fixed | 6 +- tests/ui/mem_replace_no_std.rs | 6 +- tests/ui/mem_replace_no_std.stderr | 6 +- tests/ui/methods.rs | 7 +- tests/ui/methods_unfixable.rs | 4 +- tests/ui/methods_unfixable.stderr | 4 +- tests/ui/must_use_unit.fixed | 16 +-- tests/ui/must_use_unit.rs | 16 +-- tests/ui/needless_doc_main.rs | 5 +- tests/ui/never_loop.rs | 17 +-- tests/ui/never_loop.stderr | 22 ++-- tests/ui/panicking_macros.rs | 13 +- tests/ui/panicking_overflow_checks.rs | 4 +- tests/ui/regex.rs | 6 +- tests/ui/set_contains_or_insert.rs | 8 +- tests/ui/set_contains_or_insert.stderr | 2 +- tests/ui/skip_while_next.rs | 4 +- tests/ui/slow_vector_initialization.fixed | 8 +- tests/ui/slow_vector_initialization.rs | 8 +- tests/ui/slow_vector_initialization.stderr | 26 ++-- tests/ui/transmute_null_to_fn.rs | 5 +- tests/ui/transmuting_null.rs | 8 +- tests/ui/try_err.fixed | 23 ++-- tests/ui/try_err.rs | 23 ++-- tests/ui/try_err.stderr | 10 +- tests/ui/unicode.fixed | 5 +- tests/ui/unicode.rs | 5 +- tests/ui/uninlined_format_args.fixed | 4 +- tests/ui/uninlined_format_args.rs | 4 +- tests/ui/uninlined_format_args.stderr | 10 +- tests/ui/unit_arg.rs | 5 +- tests/ui/unit_arg.stderr | 6 +- tests/ui/unit_arg_fixable.fixed | 4 +- tests/ui/unit_arg_fixable.rs | 4 +- tests/ui/unit_arg_fixable.stderr | 12 +- tests/ui/unnecessary_literal_unwrap.fixed | 10 +- tests/ui/unnecessary_literal_unwrap.rs | 10 +- .../unnecessary_literal_unwrap_unfixable.rs | 14 +-- tests/ui/unnecessary_sort_by.fixed | 6 +- tests/ui/unnecessary_sort_by.rs | 6 +- tests/ui/unnecessary_sort_by.stderr | 20 +-- tests/ui/unused_async.rs | 5 +- tests/ui/unused_peekable.rs | 5 +- tests/ui/unused_peekable.stderr | 16 +-- tests/ui/useless_asref.fixed | 5 +- tests/ui/useless_asref.rs | 5 +- tests/ui/useless_attribute.fixed | 4 +- tests/ui/useless_attribute.rs | 4 +- tests/ui/useless_conversion.fixed | 15 --- tests/ui/useless_conversion.rs | 15 --- tests/ui/useless_conversion.stderr | 116 +++++++++--------- tests/ui/while_immutable_condition.rs | 14 +-- 109 files changed, 342 insertions(+), 761 deletions(-) diff --git a/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs b/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs index 4a179cd929f07..673a2d1676527 100644 --- a/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs +++ b/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs @@ -39,9 +39,4 @@ fn block_bad() -> impl std::future::Future { } } -fn main() { - good(); - bad(); - bad_reason(); - block_bad(); -} +fn main() {} diff --git a/tests/ui-toml/expect_used/expect_used.rs b/tests/ui-toml/expect_used/expect_used.rs index 594524a796313..143b2cd280353 100644 --- a/tests/ui-toml/expect_used/expect_used.rs +++ b/tests/ui-toml/expect_used/expect_used.rs @@ -15,9 +15,6 @@ fn expect_result() { } fn main() { - expect_option(); - expect_result(); - const SOME: Option = Some(3); const UNWRAPPED: i32 = SOME.expect("Not three?"); //~^ expect_used diff --git a/tests/ui-toml/expect_used/expect_used.stderr b/tests/ui-toml/expect_used/expect_used.stderr index 2be9540179e71..b794c2fc42fc8 100644 --- a/tests/ui-toml/expect_used/expect_used.stderr +++ b/tests/ui-toml/expect_used/expect_used.stderr @@ -17,7 +17,7 @@ LL | let _ = res.expect(""); = note: if this value is an `Err`, it will panic error: used `expect()` on an `Option` value - --> tests/ui-toml/expect_used/expect_used.rs:22:28 + --> tests/ui-toml/expect_used/expect_used.rs:19:28 | LL | const UNWRAPPED: i32 = SOME.expect("Not three?"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL | const UNWRAPPED: i32 = SOME.expect("Not three?"); = note: if this value is `None`, it will panic error: used `expect()` on an `Option` value - --> tests/ui-toml/expect_used/expect_used.rs:25:9 + --> tests/ui-toml/expect_used/expect_used.rs:22:9 | LL | SOME.expect("Still not three?"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui-toml/lint_decimal_readability/test.fixed b/tests/ui-toml/lint_decimal_readability/test.fixed index 2d76824128bfe..47a61122c9bc2 100644 --- a/tests/ui-toml/lint_decimal_readability/test.fixed +++ b/tests/ui-toml/lint_decimal_readability/test.fixed @@ -7,8 +7,6 @@ fn allow_inconsistent_digit_grouping() { } fn main() { - allow_inconsistent_digit_grouping(); - let _pass1 = 100_200_300.100_200_300; let _pass2 = 1.123456789; let _pass3 = 1.0; diff --git a/tests/ui-toml/lint_decimal_readability/test.rs b/tests/ui-toml/lint_decimal_readability/test.rs index b42e31085217f..eca273274d0d9 100644 --- a/tests/ui-toml/lint_decimal_readability/test.rs +++ b/tests/ui-toml/lint_decimal_readability/test.rs @@ -7,8 +7,6 @@ fn allow_inconsistent_digit_grouping() { } fn main() { - allow_inconsistent_digit_grouping(); - let _pass1 = 100_200_300.100_200_300; let _pass2 = 1.123456789; let _pass3 = 1.0; diff --git a/tests/ui-toml/lint_decimal_readability/test.stderr b/tests/ui-toml/lint_decimal_readability/test.stderr index c4e164c67ed89..e010e1e4d2fd4 100644 --- a/tests/ui-toml/lint_decimal_readability/test.stderr +++ b/tests/ui-toml/lint_decimal_readability/test.stderr @@ -1,5 +1,5 @@ error: digits grouped inconsistently by underscores - --> tests/ui-toml/lint_decimal_readability/test.rs:19:18 + --> tests/ui-toml/lint_decimal_readability/test.rs:17:18 | LL | let _fail1 = 100_200_300.123456789; | ^^^^^^^^^^^^^^^^^^^^^ help: consider: `100_200_300.123_456_789` @@ -8,7 +8,7 @@ LL | let _fail1 = 100_200_300.123456789; = help: to override `-D warnings` add `#[allow(clippy::inconsistent_digit_grouping)]` error: long literal lacking separators - --> tests/ui-toml/lint_decimal_readability/test.rs:23:18 + --> tests/ui-toml/lint_decimal_readability/test.rs:21:18 | LL | let _fail2 = 100200300.300200100; | ^^^^^^^^^^^^^^^^^^^ help: consider: `100_200_300.300_200_100` diff --git a/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.fixed b/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.fixed index 36540bf1dcf73..92c356b9b277d 100644 --- a/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.fixed +++ b/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.fixed @@ -18,7 +18,4 @@ fn above_limit() { } } -fn main() { - below_limit(); - above_limit(); -} +fn main() {} diff --git a/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs b/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs index da76bb20fd961..3bf743c256641 100644 --- a/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs +++ b/tests/ui-toml/max_suggested_slice_pattern_length/index_refutable_slice.rs @@ -18,7 +18,4 @@ fn above_limit() { } } -fn main() { - below_limit(); - above_limit(); -} +fn main() {} diff --git a/tests/ui-toml/min_rust_version/min_rust_version.fixed b/tests/ui-toml/min_rust_version/min_rust_version.fixed index 685f77b7efe82..d73da96543d72 100644 --- a/tests/ui-toml/min_rust_version/min_rust_version.fixed +++ b/tests/ui-toml/min_rust_version/min_rust_version.fixed @@ -88,12 +88,4 @@ fn manual_bits() { size_of_val(&0u32) * 8; } -fn main() { - option_as_ref_deref(); - match_like_matches(); - match_same_arms(); - match_same_arms2(); - manual_strip_msrv(); - check_index_refutable_slice(); - borrow_as_ptr(); -} +fn main() {} diff --git a/tests/ui-toml/min_rust_version/min_rust_version.rs b/tests/ui-toml/min_rust_version/min_rust_version.rs index 0bf073af89032..78cfba1189fbf 100644 --- a/tests/ui-toml/min_rust_version/min_rust_version.rs +++ b/tests/ui-toml/min_rust_version/min_rust_version.rs @@ -88,12 +88,4 @@ fn manual_bits() { size_of_val(&0u32) * 8; } -fn main() { - option_as_ref_deref(); - match_like_matches(); - match_same_arms(); - match_same_arms2(); - manual_strip_msrv(); - check_index_refutable_slice(); - borrow_as_ptr(); -} +fn main() {} diff --git a/tests/ui-toml/mut_key/mut_key.rs b/tests/ui-toml/mut_key/mut_key.rs index b23c2340b7d63..aca6c6f841138 100644 --- a/tests/ui-toml/mut_key/mut_key.rs +++ b/tests/ui-toml/mut_key/mut_key.rs @@ -56,7 +56,4 @@ fn should_not_take_this_arg(_v: HashSet>) {} fn indirect(_: HashMap) {} -fn main() { - should_not_take_this_arg(HashSet::new()); - indirect(HashMap::new()); -} +fn main() {} diff --git a/tests/ui/attrs.rs b/tests/ui/attrs.rs index f60c71837d5e9..128fb6f5e38d2 100644 --- a/tests/ui/attrs.rs +++ b/tests/ui/attrs.rs @@ -37,15 +37,4 @@ pub const YET_ANOTHER_CONST: u8 = 0; #[deprecated(since = "TBD")] pub const GONNA_DEPRECATE_THIS_LATER: u8 = 0; -fn main() { - test_attr_lint(); - if false { - false_positive_expr() - } - if false { - false_positive_stmt() - } - if false { - empty_and_false_positive_stmt() - } -} +fn main() {} diff --git a/tests/ui/crashes/returns.rs b/tests/ui/crashes/returns.rs index ccab9522753eb..a28266a2d2f1f 100644 --- a/tests/ui/crashes/returns.rs +++ b/tests/ui/crashes/returns.rs @@ -18,7 +18,4 @@ fn cfg_let_and_return() -> i32 { x } -fn main() { - cfg_return(); - cfg_let_and_return(); -} +fn main() {} diff --git a/tests/ui/debug_assert_with_mut_call.rs b/tests/ui/debug_assert_with_mut_call.rs index 74d59b7593fa7..199f217a02b96 100644 --- a/tests/ui/debug_assert_with_mut_call.rs +++ b/tests/ui/debug_assert_with_mut_call.rs @@ -179,12 +179,4 @@ async fn debug_await() { }.await); } -fn main() { - func_non_mutable(); - func_mutable(); - method_non_mutable(); - method_mutable(); - - misc(); - debug_await(); -} +fn main() {} diff --git a/tests/ui/doc/doc-fixable.fixed b/tests/ui/doc/doc-fixable.fixed index 58d8b8b33ade0..570d8090f8e3e 100644 --- a/tests/ui/doc/doc-fixable.fixed +++ b/tests/ui/doc/doc-fixable.fixed @@ -109,10 +109,6 @@ fn test_allowed() { /// `be_sure_we_got_to_the_end_of_it` //~^ doc_markdown fn main() { - foo_bar(); - multiline_codeblock(); - test_emphasis(); - test_units(); } /// ## `CamelCaseThing` diff --git a/tests/ui/doc/doc-fixable.rs b/tests/ui/doc/doc-fixable.rs index 0b1237f716fa0..dd80bc4b25eeb 100644 --- a/tests/ui/doc/doc-fixable.rs +++ b/tests/ui/doc/doc-fixable.rs @@ -109,10 +109,6 @@ fn test_allowed() { /// be_sure_we_got_to_the_end_of_it //~^ doc_markdown fn main() { - foo_bar(); - multiline_codeblock(); - test_emphasis(); - test_units(); } /// ## CamelCaseThing diff --git a/tests/ui/doc/doc-fixable.stderr b/tests/ui/doc/doc-fixable.stderr index 2a94a8f316587..a04d06f6db1ab 100644 --- a/tests/ui/doc/doc-fixable.stderr +++ b/tests/ui/doc/doc-fixable.stderr @@ -169,7 +169,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:118:8 + --> tests/ui/doc/doc-fixable.rs:114:8 | LL | /// ## CamelCaseThing | ^^^^^^^^^^^^^^ @@ -181,7 +181,7 @@ LL + /// ## `CamelCaseThing` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:122:7 + --> tests/ui/doc/doc-fixable.rs:118:7 | LL | /// # CamelCaseThing | ^^^^^^^^^^^^^^ @@ -193,7 +193,7 @@ LL + /// # `CamelCaseThing` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:125:22 + --> tests/ui/doc/doc-fixable.rs:121:22 | LL | /// Not a title #897 CamelCaseThing | ^^^^^^^^^^^^^^ @@ -205,7 +205,7 @@ LL + /// Not a title #897 `CamelCaseThing` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:127:5 + --> tests/ui/doc/doc-fixable.rs:123:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -217,7 +217,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:135:5 + --> tests/ui/doc/doc-fixable.rs:131:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -229,7 +229,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:149:5 + --> tests/ui/doc/doc-fixable.rs:145:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -241,7 +241,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:161:43 + --> tests/ui/doc/doc-fixable.rs:157:43 | LL | /** E.g., serialization of an empty list: FooBar | ^^^^^^ @@ -253,7 +253,7 @@ LL + /** E.g., serialization of an empty list: `FooBar` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:166:5 + --> tests/ui/doc/doc-fixable.rs:162:5 | LL | And BarQuz too. | ^^^^^^ @@ -265,7 +265,7 @@ LL + And `BarQuz` too. | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:167:1 + --> tests/ui/doc/doc-fixable.rs:163:1 | LL | be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -277,7 +277,7 @@ LL + `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:175:43 + --> tests/ui/doc/doc-fixable.rs:171:43 | LL | /** E.g., serialization of an empty list: FooBar | ^^^^^^ @@ -289,7 +289,7 @@ LL + /** E.g., serialization of an empty list: `FooBar` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:180:5 + --> tests/ui/doc/doc-fixable.rs:176:5 | LL | And BarQuz too. | ^^^^^^ @@ -301,7 +301,7 @@ LL + And `BarQuz` too. | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:181:1 + --> tests/ui/doc/doc-fixable.rs:177:1 | LL | be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -313,7 +313,7 @@ LL + `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:195:5 + --> tests/ui/doc/doc-fixable.rs:191:5 | LL | /// be_sure_we_got_to_the_end_of_it | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -325,7 +325,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:215:22 + --> tests/ui/doc/doc-fixable.rs:211:22 | LL | /// An iterator over mycrate::Collection's values. | ^^^^^^^^^^^^^^^^^^^ @@ -337,7 +337,7 @@ LL + /// An iterator over `mycrate::Collection`'s values. | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:240:34 + --> tests/ui/doc/doc-fixable.rs:236:34 | LL | /// Foo \[bar\] \[baz\] \[qux\]. DocMarkdownLint | ^^^^^^^^^^^^^^^ @@ -349,7 +349,7 @@ LL + /// Foo \[bar\] \[baz\] \[qux\]. `DocMarkdownLint` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:264:22 + --> tests/ui/doc/doc-fixable.rs:260:22 | LL | /// There is no try (do() or do_not()). | ^^^^ @@ -361,7 +361,7 @@ LL + /// There is no try (`do()` or do_not()). | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:264:30 + --> tests/ui/doc/doc-fixable.rs:260:30 | LL | /// There is no try (do() or do_not()). | ^^^^^^^^ @@ -373,7 +373,7 @@ LL + /// There is no try (do() or `do_not()`). | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:269:5 + --> tests/ui/doc/doc-fixable.rs:265:5 | LL | /// ABes | ^^^^ @@ -385,7 +385,7 @@ LL + /// `ABes` | error: item in documentation is missing backticks - --> tests/ui/doc/doc-fixable.rs:276:9 + --> tests/ui/doc/doc-fixable.rs:272:9 | LL | /// foo() | ^^^^^ @@ -397,7 +397,7 @@ LL + /// `foo()` | error: you should put bare URLs between `<`/`>` or make a proper Markdown link - --> tests/ui/doc/doc-fixable.rs:281:5 + --> tests/ui/doc/doc-fixable.rs:277:5 | LL | /// https://github.com/rust-lang/rust-clippy/pull/12836 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `` diff --git a/tests/ui/doc_link_with_quotes.rs b/tests/ui/doc_link_with_quotes.rs index 0c0e273da6db4..82ec8fc2e081c 100644 --- a/tests/ui/doc_link_with_quotes.rs +++ b/tests/ui/doc_link_with_quotes.rs @@ -1,8 +1,6 @@ #![warn(clippy::doc_link_with_quotes)] -fn main() { - foo() -} +fn main() {} /// Calls ['bar'] uselessly //~^ doc_link_with_quotes diff --git a/tests/ui/doc_link_with_quotes.stderr b/tests/ui/doc_link_with_quotes.stderr index 47c60390310c1..729fc483f5621 100644 --- a/tests/ui/doc_link_with_quotes.stderr +++ b/tests/ui/doc_link_with_quotes.stderr @@ -1,5 +1,5 @@ error: possible intra-doc link using quotes instead of backticks - --> tests/ui/doc_link_with_quotes.rs:7:12 + --> tests/ui/doc_link_with_quotes.rs:5:12 | LL | /// Calls ['bar'] uselessly | ^^^^^ @@ -8,7 +8,7 @@ LL | /// Calls ['bar'] uselessly = help: to override `-D warnings` add `#[allow(clippy::doc_link_with_quotes)]` error: possible intra-doc link using quotes instead of backticks - --> tests/ui/doc_link_with_quotes.rs:13:12 + --> tests/ui/doc_link_with_quotes.rs:11:12 | LL | /// Calls ["bar"] uselessly | ^^^^^ diff --git a/tests/ui/double_must_use.rs b/tests/ui/double_must_use.rs index 71995d9466d9d..c8b5828889fc8 100644 --- a/tests/ui/double_must_use.rs +++ b/tests/ui/double_must_use.rs @@ -68,8 +68,4 @@ pub fn must_use_controlflow_with_uninhabited_2() -> ControlFlow f64 { if a.a > 0.0 { a.a } else { -a.b } } -fn main() { - fake_abs1(5.0); - fake_abs2(5.0); - fake_abs3(A { a: 5.0, b: 5.0 }); - fake_abs4(5.0); - fake_abs5(A { a: 5.0, b: 5.0 }); - fake_nabs1(5.0); - fake_nabs2(5.0); - fake_nabs3(A { a: 5.0, b: 5.0 }); - not_fake_abs1(5.0); - not_fake_abs2(5.0); - not_fake_abs3(5.0, 5.0); - not_fake_abs4(A { a: 5.0, b: 5.0 }); - not_fake_abs5(A { a: 5.0, b: 5.0 }); -} +fn main() {} diff --git a/tests/ui/floating_point_abs.rs b/tests/ui/floating_point_abs.rs index a27279b0662f5..d5c792f6eb9c9 100644 --- a/tests/ui/floating_point_abs.rs +++ b/tests/ui/floating_point_abs.rs @@ -73,18 +73,4 @@ fn not_fake_abs5(a: A) -> f64 { if a.a > 0.0 { a.a } else { -a.b } } -fn main() { - fake_abs1(5.0); - fake_abs2(5.0); - fake_abs3(A { a: 5.0, b: 5.0 }); - fake_abs4(5.0); - fake_abs5(A { a: 5.0, b: 5.0 }); - fake_nabs1(5.0); - fake_nabs2(5.0); - fake_nabs3(A { a: 5.0, b: 5.0 }); - not_fake_abs1(5.0); - not_fake_abs2(5.0); - not_fake_abs3(5.0, 5.0); - not_fake_abs4(A { a: 5.0, b: 5.0 }); - not_fake_abs5(A { a: 5.0, b: 5.0 }); -} +fn main() {} diff --git a/tests/ui/infinite_iter.rs b/tests/ui/infinite_iter.rs index 4e1668ed04fb7..f09b2a4b75918 100644 --- a/tests/ui/infinite_iter.rs +++ b/tests/ui/infinite_iter.rs @@ -87,10 +87,7 @@ fn potential_infinite_iters() { repeat(42).take_while(|x| *x == 42).next(); } -fn main() { - infinite_iters(); - potential_infinite_iters(); -} +fn main() {} mod finite_collect { use std::collections::HashSet; diff --git a/tests/ui/infinite_iter.stderr b/tests/ui/infinite_iter.stderr index 3db97313b621d..3017940c82cb1 100644 --- a/tests/ui/infinite_iter.stderr +++ b/tests/ui/infinite_iter.stderr @@ -100,7 +100,7 @@ LL | (0..).all(|x| x == 24); | ^^^^^^^^^^^^^^^^^^^^^^ error: infinite iteration detected - --> tests/ui/infinite_iter.rs:107:31 + --> tests/ui/infinite_iter.rs:104:31 | LL | let _: HashSet = (0..).collect(); | ^^^^^^^^^^^^^^^ diff --git a/tests/ui/ip_constant_from_external.rs b/tests/ui/ip_constant_from_external.rs index 7fd27022f1274..dae15623018a3 100644 --- a/tests/ui/ip_constant_from_external.rs +++ b/tests/ui/ip_constant_from_external.rs @@ -7,6 +7,4 @@ fn external_constant_test() { // lint in external file `localhost.txt` } -fn main() { - external_constant_test(); -} +fn main() {} diff --git a/tests/ui/iter_on_empty_collections.fixed b/tests/ui/iter_on_empty_collections.fixed index 0c2100034e184..f4c2bc43a6793 100644 --- a/tests/ui/iter_on_empty_collections.fixed +++ b/tests/ui/iter_on_empty_collections.fixed @@ -84,8 +84,4 @@ mod custom_option { } } -fn main() { - array(); - custom_option::custom_option(); - in_macros!(); -} +fn main() {} diff --git a/tests/ui/iter_on_empty_collections.rs b/tests/ui/iter_on_empty_collections.rs index 0fb7a32d3691f..f5852d97c93c8 100644 --- a/tests/ui/iter_on_empty_collections.rs +++ b/tests/ui/iter_on_empty_collections.rs @@ -84,8 +84,4 @@ mod custom_option { } } -fn main() { - array(); - custom_option::custom_option(); - in_macros!(); -} +fn main() {} diff --git a/tests/ui/iter_on_single_items.fixed b/tests/ui/iter_on_single_items.fixed index 044037aac2e34..c5f85122aa76c 100644 --- a/tests/ui/iter_on_single_items.fixed +++ b/tests/ui/iter_on_single_items.fixed @@ -61,11 +61,7 @@ mod custom_option { } } -fn main() { - array(); - custom_option::custom_option(); - in_macros!(); -} +fn main() {} mod issue14981 { use std::option::IntoIter; diff --git a/tests/ui/iter_on_single_items.rs b/tests/ui/iter_on_single_items.rs index c925d0e480fac..ffeb76d627b0b 100644 --- a/tests/ui/iter_on_single_items.rs +++ b/tests/ui/iter_on_single_items.rs @@ -61,11 +61,7 @@ mod custom_option { } } -fn main() { - array(); - custom_option::custom_option(); - in_macros!(); -} +fn main() {} mod issue14981 { use std::option::IntoIter; diff --git a/tests/ui/iter_with_drain.fixed b/tests/ui/iter_with_drain.fixed index b0661ffb981b1..9e0a03f580c0b 100644 --- a/tests/ui/iter_with_drain.fixed +++ b/tests/ui/iter_with_drain.fixed @@ -62,9 +62,4 @@ fn should_not_help_0(bomb: &mut Bomb) { let _: Vec = bomb.fire.drain(..).collect(); } -fn main() { - full(); - closed(); - should_not_help(); - should_not_help_0(&mut Bomb::default()); -} +fn main() {} diff --git a/tests/ui/iter_with_drain.rs b/tests/ui/iter_with_drain.rs index 746b0f9a5eda3..6546c93071af3 100644 --- a/tests/ui/iter_with_drain.rs +++ b/tests/ui/iter_with_drain.rs @@ -62,9 +62,4 @@ fn should_not_help_0(bomb: &mut Bomb) { let _: Vec = bomb.fire.drain(..).collect(); } -fn main() { - full(); - closed(); - should_not_help(); - should_not_help_0(&mut Bomb::default()); -} +fn main() {} diff --git a/tests/ui/let_if_seq.rs b/tests/ui/let_if_seq.rs index 69d6319fa8bf2..ebd9cfe4b8d5c 100644 --- a/tests/ui/let_if_seq.rs +++ b/tests/ui/let_if_seq.rs @@ -69,11 +69,6 @@ fn allow_works() -> i32 { } fn main() { - early_return(); - issue975(); - issue985(); - issue985_alt(); - let mut foo = 0; //~^ useless_let_if_seq diff --git a/tests/ui/let_if_seq.stderr b/tests/ui/let_if_seq.stderr index b86bca6b384bc..417d9287ad9c7 100644 --- a/tests/ui/let_if_seq.stderr +++ b/tests/ui/let_if_seq.stderr @@ -1,5 +1,5 @@ error: `if _ { .. } else { .. }` is an expression - --> tests/ui/let_if_seq.rs:77:5 + --> tests/ui/let_if_seq.rs:72:5 | LL | / let mut foo = 0; LL | | @@ -14,7 +14,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::useless_let_if_seq)]` error: `if _ { .. } else { .. }` is an expression - --> tests/ui/let_if_seq.rs:84:5 + --> tests/ui/let_if_seq.rs:79:5 | LL | / let mut bar = 0; LL | | @@ -28,7 +28,7 @@ LL | | } = note: you might not need `mut` at all error: `if _ { .. } else { .. }` is an expression - --> tests/ui/let_if_seq.rs:94:5 + --> tests/ui/let_if_seq.rs:89:5 | LL | / let quz; LL | | @@ -40,7 +40,7 @@ LL | | } | |_____^ help: it is more idiomatic to write: `let quz = if f() { 42 } else { 0 };` error: `if _ { .. } else { .. }` is an expression - --> tests/ui/let_if_seq.rs:125:5 + --> tests/ui/let_if_seq.rs:120:5 | LL | / let mut baz = 0; LL | | @@ -53,7 +53,7 @@ LL | | } = note: you might not need `mut` at all error: `if _ { .. } else { .. }` is an expression - --> tests/ui/let_if_seq.rs:144:5 + --> tests/ui/let_if_seq.rs:139:5 | LL | / let foo; LL | | @@ -65,7 +65,7 @@ LL | | } | |_____^ help: it is more idiomatic to write: `let foo = if bar() { 42 } else { 0 };` error: `if _ { .. } else { .. }` is an expression - --> tests/ui/let_if_seq.rs:163:5 + --> tests/ui/let_if_seq.rs:158:5 | LL | / let foo; LL | | diff --git a/tests/ui/manual_is_variant_and.fixed b/tests/ui/manual_is_variant_and.fixed index 96ffd9680d229..e0ac240edbe5e 100644 --- a/tests/ui/manual_is_variant_and.fixed +++ b/tests/ui/manual_is_variant_and.fixed @@ -113,10 +113,7 @@ fn result_methods() { let _ = opt_map!(res2, |x| x == 'a').unwrap_or_default(); // should not lint } -fn main() { - option_methods(); - result_methods(); -} +fn main() {} fn issue15202() { let xs = [None, Some(b'_'), Some(b'1')]; diff --git a/tests/ui/manual_is_variant_and.rs b/tests/ui/manual_is_variant_and.rs index 5f33f38fb51e6..98069cab4557e 100644 --- a/tests/ui/manual_is_variant_and.rs +++ b/tests/ui/manual_is_variant_and.rs @@ -122,10 +122,7 @@ fn result_methods() { let _ = opt_map!(res2, |x| x == 'a').unwrap_or_default(); // should not lint } -fn main() { - option_methods(); - result_methods(); -} +fn main() {} fn issue15202() { let xs = [None, Some(b'_'), Some(b'1')]; diff --git a/tests/ui/manual_is_variant_and.stderr b/tests/ui/manual_is_variant_and.stderr index cf5371378c9f6..88aa3bb4eed83 100644 --- a/tests/ui/manual_is_variant_and.stderr +++ b/tests/ui/manual_is_variant_and.stderr @@ -127,121 +127,121 @@ LL | let _ = res2.map(char::is_alphanumeric).unwrap_or_default(); // should | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `is_ok_and(char::is_alphanumeric)` error: called `.map() != Some()` - --> tests/ui/manual_is_variant_and.rs:133:18 + --> tests/ui/manual_is_variant_and.rs:130:18 | LL | let a1 = x.map(|b| b.is_ascii_digit()) != Some(true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.is_none_or(|b| !b.is_ascii_digit())` error: called `.map() != Some()` - --> tests/ui/manual_is_variant_and.rs:140:18 + --> tests/ui/manual_is_variant_and.rs:137:18 | LL | let a1 = x.map(|b| b.is_ascii_digit()) != Some(false); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.is_none_or(|b| b.is_ascii_digit())` error: called `.map() == Some()` - --> tests/ui/manual_is_variant_and.rs:147:18 + --> tests/ui/manual_is_variant_and.rs:144:18 | LL | let a1 = x.map(|b| b.is_ascii_digit()) == Some(true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.is_some_and(|b| b.is_ascii_digit())` error: called `.map() == Some()` - --> tests/ui/manual_is_variant_and.rs:154:18 + --> tests/ui/manual_is_variant_and.rs:151:18 | LL | let a1 = x.map(|b| b.is_ascii_digit()) == Some(false); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.is_some_and(|b| !b.is_ascii_digit())` error: called `.map() != Ok()` - --> tests/ui/manual_is_variant_and.rs:162:18 + --> tests/ui/manual_is_variant_and.rs:159:18 | LL | let a1 = x.map(|b| b.is_ascii_digit()) != Ok(true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `!x.is_ok_and(|b| b.is_ascii_digit())` error: called `.map() != Ok()` - --> tests/ui/manual_is_variant_and.rs:169:18 + --> tests/ui/manual_is_variant_and.rs:166:18 | LL | let a1 = x.map(|b| b.is_ascii_digit()) != Ok(false); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `!x.is_ok_and(|b| !b.is_ascii_digit())` error: called `.map() == Ok()` - --> tests/ui/manual_is_variant_and.rs:176:18 + --> tests/ui/manual_is_variant_and.rs:173:18 | LL | let a1 = x.map(|b| b.is_ascii_digit()) == Ok(true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.is_ok_and(|b| b.is_ascii_digit())` error: called `.map() == Ok()` - --> tests/ui/manual_is_variant_and.rs:183:18 + --> tests/ui/manual_is_variant_and.rs:180:18 | LL | let a1 = x.map(|b| b.is_ascii_digit()) == Ok(false); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.is_ok_and(|b| !b.is_ascii_digit())` error: called `.map() == Some()` - --> tests/ui/manual_is_variant_and.rs:196:18 + --> tests/ui/manual_is_variant_and.rs:193:18 | LL | let a1 = b.map(iad) == Some(true); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `b.is_some_and(iad)` error: called `.map() == Some()` - --> tests/ui/manual_is_variant_and.rs:201:18 + --> tests/ui/manual_is_variant_and.rs:198:18 | LL | let a1 = b.map(iad) == Some(false); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `b.is_some_and(|x| !iad(x))` error: called `.map() != Some()` - --> tests/ui/manual_is_variant_and.rs:206:18 + --> tests/ui/manual_is_variant_and.rs:203:18 | LL | let a1 = b.map(iad) != Some(true); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `b.is_none_or(|x| !iad(x))` error: called `.map() != Some()` - --> tests/ui/manual_is_variant_and.rs:211:18 + --> tests/ui/manual_is_variant_and.rs:208:18 | LL | let a1 = b.map(iad) != Some(false); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `b.is_none_or(iad)` error: called `.map() == Ok()` - --> tests/ui/manual_is_variant_and.rs:218:18 + --> tests/ui/manual_is_variant_and.rs:215:18 | LL | let a1 = b.map(iad) == Ok(true); | ^^^^^^^^^^^^^^^^^^^^^^ help: use: `b.is_ok_and(iad)` error: called `.map() == Ok()` - --> tests/ui/manual_is_variant_and.rs:223:18 + --> tests/ui/manual_is_variant_and.rs:220:18 | LL | let a1 = b.map(iad) == Ok(false); | ^^^^^^^^^^^^^^^^^^^^^^^ help: use: `b.is_ok_and(|x| !iad(x))` error: called `.map() != Ok()` - --> tests/ui/manual_is_variant_and.rs:228:18 + --> tests/ui/manual_is_variant_and.rs:225:18 | LL | let a1 = b.map(iad) != Ok(true); | ^^^^^^^^^^^^^^^^^^^^^^ help: use: `!b.is_ok_and(iad)` error: called `.map() != Ok()` - --> tests/ui/manual_is_variant_and.rs:233:18 + --> tests/ui/manual_is_variant_and.rs:230:18 | LL | let a1 = b.map(iad) != Ok(false); | ^^^^^^^^^^^^^^^^^^^^^^^ help: use: `!b.is_ok_and(|x| !iad(x))` error: manual implementation of `Option::is_none_or` - --> tests/ui/manual_is_variant_and.rs:243:13 + --> tests/ui/manual_is_variant_and.rs:240:13 | LL | let _ = opt.is_none() || opt.is_some_and(then_fn); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `opt.is_none_or(then_fn)` error: manual implementation of `Option::is_none_or` - --> tests/ui/manual_is_variant_and.rs:246:13 + --> tests/ui/manual_is_variant_and.rs:243:13 | LL | let _ = opt.is_some_and(then_fn) || opt.is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `opt.is_none_or(then_fn)` error: manual implementation of `Option::is_some_and` - --> tests/ui/manual_is_variant_and.rs:262:5 + --> tests/ui/manual_is_variant_and.rs:259:5 | LL | opt.filter(|x| condition(x)).is_some(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `opt.as_ref().is_some_and(|x| condition(x))` error: manual implementation of `Option::is_none_or` - --> tests/ui/manual_is_variant_and.rs:264:5 + --> tests/ui/manual_is_variant_and.rs:261:5 | LL | opt.filter(|x| condition(x)).is_none(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `opt.as_ref().is_none_or(|x| !condition(x))` diff --git a/tests/ui/manual_option_as_slice.fixed b/tests/ui/manual_option_as_slice.fixed index 48337d7654dea..3f8674ce0e264 100644 --- a/tests/ui/manual_option_as_slice.fixed +++ b/tests/ui/manual_option_as_slice.fixed @@ -56,7 +56,4 @@ fn check_msrv(x: Option) { _ = x.as_ref().map_or(&[][..], std::slice::from_ref); } -fn main() { - check(Some(1)); - check_msrv(Some(175)); -} +fn main() {} diff --git a/tests/ui/manual_option_as_slice.rs b/tests/ui/manual_option_as_slice.rs index e1a97a6b27115..1dd6caea4879d 100644 --- a/tests/ui/manual_option_as_slice.rs +++ b/tests/ui/manual_option_as_slice.rs @@ -66,7 +66,4 @@ fn check_msrv(x: Option) { _ = x.as_ref().map_or(&[][..], std::slice::from_ref); } -fn main() { - check(Some(1)); - check_msrv(Some(175)); -} +fn main() {} diff --git a/tests/ui/manual_retain.fixed b/tests/ui/manual_retain.fixed index 016f520e216c0..ea97c488a9ad9 100644 --- a/tests/ui/manual_retain.fixed +++ b/tests/ui/manual_retain.fixed @@ -2,22 +2,7 @@ #![allow(unused, clippy::needless_borrowed_reference, clippy::redundant_clone)] use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, VecDeque}; -fn main() { - binary_heap_retain(); - btree_set_retain(); - btree_map_retain(); - hash_set_retain(); - hash_map_retain(); - string_retain(); - vec_deque_retain(); - vec_retain(); - _msrv_153(); - _msrv_126(); - _msrv_118(); - - issue_10393(); - issue_12081(); -} +fn main() {} fn binary_heap_retain() { let mut binary_heap = BinaryHeap::from([1, 2, 3]); diff --git a/tests/ui/manual_retain.rs b/tests/ui/manual_retain.rs index 62f9b7b0595d0..5cee4cefa038a 100644 --- a/tests/ui/manual_retain.rs +++ b/tests/ui/manual_retain.rs @@ -2,22 +2,7 @@ #![allow(unused, clippy::needless_borrowed_reference, clippy::redundant_clone)] use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, VecDeque}; -fn main() { - binary_heap_retain(); - btree_set_retain(); - btree_map_retain(); - hash_set_retain(); - hash_map_retain(); - string_retain(); - vec_deque_retain(); - vec_retain(); - _msrv_153(); - _msrv_126(); - _msrv_118(); - - issue_10393(); - issue_12081(); -} +fn main() {} fn binary_heap_retain() { let mut binary_heap = BinaryHeap::from([1, 2, 3]); diff --git a/tests/ui/manual_retain.stderr b/tests/ui/manual_retain.stderr index e7d3e34b5d7d4..fc562d32d79fb 100644 --- a/tests/ui/manual_retain.stderr +++ b/tests/ui/manual_retain.stderr @@ -1,5 +1,5 @@ error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:25:5 + --> tests/ui/manual_retain.rs:10:5 | LL | binary_heap = binary_heap.into_iter().filter(|x| x % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `binary_heap.retain(|x| x % 2 == 0)` @@ -8,43 +8,43 @@ LL | binary_heap = binary_heap.into_iter().filter(|x| x % 2 == 0).collect(); = help: to override `-D warnings` add `#[allow(clippy::manual_retain)]` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:27:5 + --> tests/ui/manual_retain.rs:12:5 | LL | binary_heap = binary_heap.iter().filter(|&x| x % 2 == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `binary_heap.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:29:5 + --> tests/ui/manual_retain.rs:14:5 | LL | binary_heap = binary_heap.iter().filter(|&x| x % 2 == 0).cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `binary_heap.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:34:5 + --> tests/ui/manual_retain.rs:19:5 | LL | tuples = tuples.iter().filter(|&&(ref x, ref y)| *x == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|&(ref x, ref y)| *x == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:36:5 + --> tests/ui/manual_retain.rs:21:5 | LL | tuples = tuples.iter().filter(|(x, y)| *x == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(x, y)| *x == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:67:5 + --> tests/ui/manual_retain.rs:52:5 | LL | btree_map = btree_map.into_iter().filter(|(k, _)| k % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_map.retain(|k, _| k % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:69:5 + --> tests/ui/manual_retain.rs:54:5 | LL | btree_map = btree_map.into_iter().filter(|(_, v)| v % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_map.retain(|_, &mut v| v % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:71:5 + --> tests/ui/manual_retain.rs:56:5 | LL | / btree_map = btree_map LL | | @@ -54,49 +54,49 @@ LL | | .collect(); | |__________________^ help: consider calling `.retain()` instead: `btree_map.retain(|k, &mut v| (k % 2 == 0) && (v % 2 == 0))` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:97:5 + --> tests/ui/manual_retain.rs:82:5 | LL | btree_set = btree_set.iter().filter(|&x| x % 2 == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_set.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:99:5 + --> tests/ui/manual_retain.rs:84:5 | LL | btree_set = btree_set.iter().filter(|&x| x % 2 == 0).cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_set.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:101:5 + --> tests/ui/manual_retain.rs:86:5 | LL | btree_set = btree_set.into_iter().filter(|x| x % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_set.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:106:5 + --> tests/ui/manual_retain.rs:91:5 | LL | tuples = tuples.iter().filter(|&&(ref x, ref y)| *x == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|&(ref x, ref y)| *x == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:108:5 + --> tests/ui/manual_retain.rs:93:5 | LL | tuples = tuples.iter().filter(|(x, y)| *x == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(x, y)| *x == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:139:5 + --> tests/ui/manual_retain.rs:124:5 | LL | hash_map = hash_map.into_iter().filter(|(k, _)| k % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_map.retain(|k, _| k % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:141:5 + --> tests/ui/manual_retain.rs:126:5 | LL | hash_map = hash_map.into_iter().filter(|(_, v)| v % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_map.retain(|_, &mut v| v % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:143:5 + --> tests/ui/manual_retain.rs:128:5 | LL | / hash_map = hash_map LL | | @@ -106,133 +106,133 @@ LL | | .collect(); | |__________________^ help: consider calling `.retain()` instead: `hash_map.retain(|k, &mut v| (k % 2 == 0) && (v % 2 == 0))` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:168:5 + --> tests/ui/manual_retain.rs:153:5 | LL | hash_set = hash_set.into_iter().filter(|x| x % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_set.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:170:5 + --> tests/ui/manual_retain.rs:155:5 | LL | hash_set = hash_set.iter().filter(|&x| x % 2 == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_set.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:172:5 + --> tests/ui/manual_retain.rs:157:5 | LL | hash_set = hash_set.iter().filter(|&x| x % 2 == 0).cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_set.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:177:5 + --> tests/ui/manual_retain.rs:162:5 | LL | tuples = tuples.iter().filter(|&&(ref x, ref y)| *x == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|&(ref x, ref y)| *x == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:179:5 + --> tests/ui/manual_retain.rs:164:5 | LL | tuples = tuples.iter().filter(|(x, y)| *x == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(x, y)| *x == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:209:5 + --> tests/ui/manual_retain.rs:194:5 | LL | s = s.chars().filter(|&c| c != 'o').to_owned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `s.retain(|c| c != 'o')` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:222:5 + --> tests/ui/manual_retain.rs:207:5 | LL | vec = vec.iter().filter(|&x| x % 2 == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:224:5 + --> tests/ui/manual_retain.rs:209:5 | LL | vec = vec.iter().filter(|&x| x % 2 == 0).cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:226:5 + --> tests/ui/manual_retain.rs:211:5 | LL | vec = vec.into_iter().filter(|x| x % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:231:5 + --> tests/ui/manual_retain.rs:216:5 | LL | tuples = tuples.iter().filter(|&&(ref x, ref y)| *x == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|&(ref x, ref y)| *x == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:233:5 + --> tests/ui/manual_retain.rs:218:5 | LL | tuples = tuples.iter().filter(|(x, y)| *x == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(x, y)| *x == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:256:5 + --> tests/ui/manual_retain.rs:241:5 | LL | vec_deque = vec_deque.iter().filter(|&x| x % 2 == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec_deque.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:258:5 + --> tests/ui/manual_retain.rs:243:5 | LL | vec_deque = vec_deque.iter().filter(|&x| x % 2 == 0).cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec_deque.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:260:5 + --> tests/ui/manual_retain.rs:245:5 | LL | vec_deque = vec_deque.into_iter().filter(|x| x % 2 == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec_deque.retain(|x| x % 2 == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:318:5 + --> tests/ui/manual_retain.rs:303:5 | LL | vec = vec.into_iter().filter(|(x, y)| *x == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|(x, y)| *x == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:323:5 + --> tests/ui/manual_retain.rs:308:5 | LL | tuples = tuples.into_iter().filter(|(_, n)| *n > 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(_, n)| *n > 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:341:5 + --> tests/ui/manual_retain.rs:326:5 | LL | vec = vec.iter().filter(|&&x| x == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|&x| x == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:343:5 + --> tests/ui/manual_retain.rs:328:5 | LL | vec = vec.iter().filter(|&&x| x == 0).cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|&x| x == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:345:5 + --> tests/ui/manual_retain.rs:330:5 | LL | vec = vec.into_iter().filter(|&x| x == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|&x| x == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:349:5 + --> tests/ui/manual_retain.rs:334:5 | LL | vec = vec.iter().filter(|&x| *x == 0).copied().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| *x == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:351:5 + --> tests/ui/manual_retain.rs:336:5 | LL | vec = vec.iter().filter(|&x| *x == 0).cloned().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| *x == 0)` error: this expression can be written more simply using `.retain()` - --> tests/ui/manual_retain.rs:353:5 + --> tests/ui/manual_retain.rs:338:5 | LL | vec = vec.into_iter().filter(|x| *x == 0).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| *x == 0)` diff --git a/tests/ui/manual_take.fixed b/tests/ui/manual_take.fixed index 5891bd49c81c1..b8f7ca86dfea2 100644 --- a/tests/ui/manual_take.fixed +++ b/tests/ui/manual_take.fixed @@ -1,8 +1,6 @@ #![warn(clippy::manual_take)] fn main() { - msrv_1_39(); - msrv_1_40(); let mut x = true; let mut y = false; diff --git a/tests/ui/manual_take.rs b/tests/ui/manual_take.rs index 89903fcb2416c..39637a8f2e176 100644 --- a/tests/ui/manual_take.rs +++ b/tests/ui/manual_take.rs @@ -1,8 +1,6 @@ #![warn(clippy::manual_take)] fn main() { - msrv_1_39(); - msrv_1_40(); let mut x = true; let mut y = false; diff --git a/tests/ui/manual_take.stderr b/tests/ui/manual_take.stderr index 69ba7778a2758..207711a5a183e 100644 --- a/tests/ui/manual_take.stderr +++ b/tests/ui/manual_take.stderr @@ -1,5 +1,5 @@ error: manual implementation of `mem::take` - --> tests/ui/manual_take.rs:9:25 + --> tests/ui/manual_take.rs:7:25 | LL | let _lint_negated = if x { | _________________________^ @@ -26,7 +26,7 @@ LL + let _lint_negated = !std::mem::take(&mut x); | error: manual implementation of `mem::take` - --> tests/ui/manual_take.rs:62:5 + --> tests/ui/manual_take.rs:60:5 | LL | / if x { LL | | diff --git a/tests/ui/map_flatten.fixed b/tests/ui/map_flatten.fixed index 168db8dd62912..12fca6706e412 100644 --- a/tests/ui/map_flatten.fixed +++ b/tests/ui/map_flatten.fixed @@ -64,6 +64,4 @@ fn no_suggestion_if_comments_present() { .flat_map(|x| x.iter()); } -fn main() { - long_span(); -} +fn main() {} diff --git a/tests/ui/map_flatten.rs b/tests/ui/map_flatten.rs index df4edd8f16eed..3f1b7a268822d 100644 --- a/tests/ui/map_flatten.rs +++ b/tests/ui/map_flatten.rs @@ -71,6 +71,4 @@ fn no_suggestion_if_comments_present() { .flatten(); } -fn main() { - long_span(); -} +fn main() {} diff --git a/tests/ui/map_unwrap_or.rs b/tests/ui/map_unwrap_or.rs index dccacd7df8af9..37470a50cfbef 100644 --- a/tests/ui/map_unwrap_or.rs +++ b/tests/ui/map_unwrap_or.rs @@ -92,10 +92,7 @@ fn result_methods() { let _ = opt_map!(res, |x| x + 1).unwrap_or_else(|_e| 0); // should not lint } -fn main() { - option_methods(); - result_methods(); -} +fn main() {} #[clippy::msrv = "1.40"] fn msrv_1_40() { diff --git a/tests/ui/map_unwrap_or.stderr b/tests/ui/map_unwrap_or.stderr index bd9e0eeb0bdac..a90da4a97e0c8 100644 --- a/tests/ui/map_unwrap_or.stderr +++ b/tests/ui/map_unwrap_or.stderr @@ -203,13 +203,13 @@ LL ~ }, |x| x + 1); | error: called `map().unwrap_or_else()` on a `Result` value - --> tests/ui/map_unwrap_or.rs:111:13 + --> tests/ui/map_unwrap_or.rs:108:13 | LL | let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `res.map_or_else(|_e| 0, |x| x + 1)` error: called `map().unwrap_or()` on an `Option` value - --> tests/ui/map_unwrap_or.rs:119:13 + --> tests/ui/map_unwrap_or.rs:116:13 | LL | let _ = opt.map(|x| x > 5).unwrap_or(false); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -221,7 +221,7 @@ LL + let _ = opt.map_or(false, |x| x > 5); | error: called `map().unwrap_or(false)` on an `Option` value - --> tests/ui/map_unwrap_or.rs:127:13 + --> tests/ui/map_unwrap_or.rs:124:13 | LL | let _ = opt.map(|x| x > 5).unwrap_or(false); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -233,7 +233,7 @@ LL + let _ = opt.is_some_and(|x| x > 5); | error: called `map().unwrap_or()` on an `Option` value - --> tests/ui/map_unwrap_or.rs:164:5 + --> tests/ui/map_unwrap_or.rs:161:5 | LL | x.map(|y| y.0).unwrap_or(&[]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/map_unwrap_or_fixable.fixed b/tests/ui/map_unwrap_or_fixable.fixed index dca2536132d79..efdb7cf276692 100644 --- a/tests/ui/map_unwrap_or_fixable.fixed +++ b/tests/ui/map_unwrap_or_fixable.fixed @@ -52,10 +52,7 @@ fn result_methods() { let _ = opt_map!(res, |x| x + 1).unwrap_or_else(|_e| 0); // should not lint } -fn main() { - option_methods(); - result_methods(); -} +fn main() {} fn issue15714() { let o: Option = Some(3); diff --git a/tests/ui/map_unwrap_or_fixable.rs b/tests/ui/map_unwrap_or_fixable.rs index c60cb082ae3c0..5c3f13c2844be 100644 --- a/tests/ui/map_unwrap_or_fixable.rs +++ b/tests/ui/map_unwrap_or_fixable.rs @@ -58,10 +58,7 @@ fn result_methods() { let _ = opt_map!(res, |x| x + 1).unwrap_or_else(|_e| 0); // should not lint } -fn main() { - option_methods(); - result_methods(); -} +fn main() {} fn issue15714() { let o: Option = Some(3); diff --git a/tests/ui/map_unwrap_or_fixable.stderr b/tests/ui/map_unwrap_or_fixable.stderr index 33a865d6769ca..90bb625d7a2ae 100644 --- a/tests/ui/map_unwrap_or_fixable.stderr +++ b/tests/ui/map_unwrap_or_fixable.stderr @@ -20,7 +20,7 @@ LL | | .unwrap_or_else(|_e| 0); | |_______________________________^ help: try: `res.map_or_else(|_e| 0, |x| x + 1)` error: called `map().unwrap_or()` on an `Option` value - --> tests/ui/map_unwrap_or_fixable.rs:69:20 + --> tests/ui/map_unwrap_or_fixable.rs:66:20 | LL | println!("{}", o.map(|y| y + 1).unwrap_or(3)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -32,13 +32,13 @@ LL + println!("{}", o.map_or(3, |y| y + 1)); | error: called `map().unwrap_or_else()` on an `Option` value - --> tests/ui/map_unwrap_or_fixable.rs:71:20 + --> tests/ui/map_unwrap_or_fixable.rs:68:20 | LL | println!("{}", o.map(|y| y + 1).unwrap_or_else(|| 3)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `o.map_or_else(|| 3, |y| y + 1)` error: called `map().unwrap_or()` on a `Result` value - --> tests/ui/map_unwrap_or_fixable.rs:73:20 + --> tests/ui/map_unwrap_or_fixable.rs:70:20 | LL | println!("{}", r.map(|y| y + 1).unwrap_or(3)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -50,13 +50,13 @@ LL + println!("{}", r.map_or(3, |y| y + 1)); | error: called `map().unwrap_or_else()` on a `Result` value - --> tests/ui/map_unwrap_or_fixable.rs:75:20 + --> tests/ui/map_unwrap_or_fixable.rs:72:20 | LL | println!("{}", r.map(|y| y + 1).unwrap_or_else(|()| 3)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `r.map_or_else(|()| 3, |y| y + 1)` error: called `map().unwrap_or(false)` on a `Result` value - --> tests/ui/map_unwrap_or_fixable.rs:78:20 + --> tests/ui/map_unwrap_or_fixable.rs:75:20 | LL | println!("{}", r.map(|y| y == 1).unwrap_or(false)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -68,7 +68,7 @@ LL + println!("{}", r.is_ok_and(|y| y == 1)); | error: called `map().unwrap_or()` on an `Option` value - --> tests/ui/map_unwrap_or_fixable.rs:84:20 + --> tests/ui/map_unwrap_or_fixable.rs:81:20 | LL | println!("{}", x.map(|y| y + 1).unwrap_or(3)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -80,7 +80,7 @@ LL + println!("{}", x.map_or(3, |y| y + 1)); | error: called `map().unwrap_or()` on a `Result` value - --> tests/ui/map_unwrap_or_fixable.rs:88:20 + --> tests/ui/map_unwrap_or_fixable.rs:85:20 | LL | println!("{}", x.map(|y| y + 1).unwrap_or(3)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -92,13 +92,13 @@ LL + println!("{}", x.map_or(3, |y| y + 1)); | error: called `map().unwrap_or_else()` on an `Option` value - --> tests/ui/map_unwrap_or_fixable.rs:92:20 + --> tests/ui/map_unwrap_or_fixable.rs:89:20 | LL | println!("{}", x.map(|y| y + 1).unwrap_or_else(|| 3)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `x.map_or_else(|| 3, |y| y + 1)` error: called `map().unwrap_or_else()` on a `Result` value - --> tests/ui/map_unwrap_or_fixable.rs:96:20 + --> tests/ui/map_unwrap_or_fixable.rs:93:20 | LL | println!("{}", x.map(|y| y + 1).unwrap_or_else(|_| 3)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `x.map_or_else(|_| 3, |y| y + 1)` diff --git a/tests/ui/mem_replace.fixed b/tests/ui/mem_replace.fixed index 94ad1aad3eb75..26aa7aa1b2703 100644 --- a/tests/ui/mem_replace.fixed +++ b/tests/ui/mem_replace.fixed @@ -101,11 +101,7 @@ fn dont_lint_not_used() { std::mem::replace(&mut s, String::default()); } -fn main() { - replace_option_with_none(); - replace_with_default(); - dont_lint_primitive(); -} +fn main() {} #[clippy::msrv = "1.39"] fn msrv_1_39() { diff --git a/tests/ui/mem_replace.rs b/tests/ui/mem_replace.rs index ac79660f0f1ee..cd675f5735a19 100644 --- a/tests/ui/mem_replace.rs +++ b/tests/ui/mem_replace.rs @@ -101,11 +101,7 @@ fn dont_lint_not_used() { std::mem::replace(&mut s, String::default()); } -fn main() { - replace_option_with_none(); - replace_with_default(); - dont_lint_primitive(); -} +fn main() {} #[clippy::msrv = "1.39"] fn msrv_1_39() { diff --git a/tests/ui/mem_replace.stderr b/tests/ui/mem_replace.stderr index 104c985400282..bc374930cf0f2 100644 --- a/tests/ui/mem_replace.stderr +++ b/tests/ui/mem_replace.stderr @@ -131,37 +131,37 @@ LL | let _ = std::mem::replace(&mut slice, &[]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut slice)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> tests/ui/mem_replace.rs:119:13 + --> tests/ui/mem_replace.rs:115:13 | LL | let _ = std::mem::replace(&mut s, String::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut s)` error: replacing an `Option` with `None` - --> tests/ui/mem_replace.rs:150:13 + --> tests/ui/mem_replace.rs:146:13 | LL | let _ = std::mem::replace(&mut f.0, None); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `f.0.take()` error: replacing an `Option` with `None` - --> tests/ui/mem_replace.rs:152:13 + --> tests/ui/mem_replace.rs:148:13 | LL | let _ = std::mem::replace(&mut *f, None); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `(*f).take()` error: replacing an `Option` with `None` - --> tests/ui/mem_replace.rs:154:13 + --> tests/ui/mem_replace.rs:150:13 | LL | let _ = std::mem::replace(&mut b.opt, None); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `b.opt.take()` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> tests/ui/mem_replace.rs:157:13 + --> tests/ui/mem_replace.rs:153:13 | LL | let _ = std::mem::replace(&mut b.val, String::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut b.val)` error: replacing an `Option` with `Some(..)` - --> tests/ui/mem_replace.rs:164:20 + --> tests/ui/mem_replace.rs:160:20 | LL | let replaced = mem::replace(&mut an_option, Some(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::replace()` instead: `an_option.replace(1)` @@ -170,19 +170,19 @@ LL | let replaced = mem::replace(&mut an_option, Some(1)); = help: to override `-D warnings` add `#[allow(clippy::mem_replace_option_with_some)]` error: replacing an `Option` with `Some(..)` - --> tests/ui/mem_replace.rs:168:20 + --> tests/ui/mem_replace.rs:164:20 | LL | let replaced = mem::replace(an_option, Some(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::replace()` instead: `an_option.replace(1)` error: replacing an `Option` with `Some(..)` - --> tests/ui/mem_replace.rs:173:20 + --> tests/ui/mem_replace.rs:169:20 | LL | let replaced = mem::replace(if b { &mut opt1 } else { &mut opt2 }, Some(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::replace()` instead: `(if b { &mut opt1 } else { &mut opt2 }).replace(1)` error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` - --> tests/ui/mem_replace.rs:185:20 + --> tests/ui/mem_replace.rs:181:20 | LL | let replaced = std::mem::replace(dbg!(&mut text), String::default()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(dbg!(&mut text))` diff --git a/tests/ui/mem_replace_no_std.fixed b/tests/ui/mem_replace_no_std.fixed index 669450e59d8b0..4e2d413af6cdf 100644 --- a/tests/ui/mem_replace_no_std.fixed +++ b/tests/ui/mem_replace_no_std.fixed @@ -47,11 +47,7 @@ fn dont_lint_primitive() { let _ = mem::replace(&mut pint, 0); } -fn main() { - replace_option_with_none(); - replace_with_default(); - dont_lint_primitive(); -} +fn main() {} fn issue9824() { struct Foo<'a>(Option<&'a str>); diff --git a/tests/ui/mem_replace_no_std.rs b/tests/ui/mem_replace_no_std.rs index 2cdf1df5196ac..c0892304aba86 100644 --- a/tests/ui/mem_replace_no_std.rs +++ b/tests/ui/mem_replace_no_std.rs @@ -47,11 +47,7 @@ fn dont_lint_primitive() { let _ = mem::replace(&mut pint, 0); } -fn main() { - replace_option_with_none(); - replace_with_default(); - dont_lint_primitive(); -} +fn main() {} fn issue9824() { struct Foo<'a>(Option<&'a str>); diff --git a/tests/ui/mem_replace_no_std.stderr b/tests/ui/mem_replace_no_std.stderr index 926a8288fd31b..34e81a9f07504 100644 --- a/tests/ui/mem_replace_no_std.stderr +++ b/tests/ui/mem_replace_no_std.stderr @@ -29,19 +29,19 @@ LL | let _ = mem::replace(&mut slice, &[]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `core::mem::take(&mut slice)` error: replacing an `Option` with `None` - --> tests/ui/mem_replace_no_std.rs:80:13 + --> tests/ui/mem_replace_no_std.rs:76:13 | LL | let _ = mem::replace(&mut f.0, None); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `f.0.take()` error: replacing an `Option` with `None` - --> tests/ui/mem_replace_no_std.rs:82:13 + --> tests/ui/mem_replace_no_std.rs:78:13 | LL | let _ = mem::replace(&mut *f, None); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `(*f).take()` error: replacing an `Option` with `None` - --> tests/ui/mem_replace_no_std.rs:84:13 + --> tests/ui/mem_replace_no_std.rs:80:13 | LL | let _ = mem::replace(&mut b.opt, None); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `b.opt.take()` diff --git a/tests/ui/methods.rs b/tests/ui/methods.rs index 9595888b99f83..f73ec563dceb1 100644 --- a/tests/ui/methods.rs +++ b/tests/ui/methods.rs @@ -145,7 +145,7 @@ fn filter_next_back() { *x < 0 } ).next_back(); - + // Check that we don't lint if the caller is not an `Iterator`. let foo = IteratorFalsePositives { foo: 0 }; let _ = foo.filter().next_back(); @@ -154,7 +154,4 @@ fn filter_next_back() { let _ = foo.filter(42).next_back(); } -fn main() { - filter_next(); - filter_next_back(); -} +fn main() {} diff --git a/tests/ui/methods_unfixable.rs b/tests/ui/methods_unfixable.rs index c19a769f79745..46a5d95eb1b72 100644 --- a/tests/ui/methods_unfixable.rs +++ b/tests/ui/methods_unfixable.rs @@ -1,8 +1,6 @@ #![warn(clippy::filter_next)] //@no-rustfix -fn main() { - issue10029(); -} +fn main() {} pub fn issue10029() { let iter = (0..10); diff --git a/tests/ui/methods_unfixable.stderr b/tests/ui/methods_unfixable.stderr index 76691860f819d..137112ae41795 100644 --- a/tests/ui/methods_unfixable.stderr +++ b/tests/ui/methods_unfixable.stderr @@ -1,11 +1,11 @@ error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead - --> tests/ui/methods_unfixable.rs:9:13 + --> tests/ui/methods_unfixable.rs:7:13 | LL | let _ = iter.filter(|_| true).next(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `iter.find(|_| true)` | help: you will also need to make `iter` mutable, because `find` takes `&mut self` - --> tests/ui/methods_unfixable.rs:8:9 + --> tests/ui/methods_unfixable.rs:6:9 | LL | let iter = (0..10); | ^^^^ diff --git a/tests/ui/must_use_unit.fixed b/tests/ui/must_use_unit.fixed index 683754a98c828..b9871991b16f0 100644 --- a/tests/ui/must_use_unit.fixed +++ b/tests/ui/must_use_unit.fixed @@ -15,14 +15,10 @@ pub fn must_use_unit() -> () {} pub fn must_use_with_note() {} //~^ must_use_unit -fn main() { - must_use_default(); - must_use_unit(); - must_use_with_note(); +// We should not lint in external macros +external!( + #[must_use] + fn foo() {} +); - // We should not lint in external macros - external!( - #[must_use] - fn foo() {} - ); -} +fn main() {} diff --git a/tests/ui/must_use_unit.rs b/tests/ui/must_use_unit.rs index 519b8fa368219..a5865681c8a8e 100644 --- a/tests/ui/must_use_unit.rs +++ b/tests/ui/must_use_unit.rs @@ -18,14 +18,10 @@ pub fn must_use_unit() -> () {} pub fn must_use_with_note() {} //~^ must_use_unit -fn main() { - must_use_default(); - must_use_unit(); - must_use_with_note(); +// We should not lint in external macros +external!( + #[must_use] + fn foo() {} +); - // We should not lint in external macros - external!( - #[must_use] - fn foo() {} - ); -} +fn main() {} diff --git a/tests/ui/needless_doc_main.rs b/tests/ui/needless_doc_main.rs index afecd4b47f5eb..803b0bbb0aeff 100644 --- a/tests/ui/needless_doc_main.rs +++ b/tests/ui/needless_doc_main.rs @@ -134,7 +134,4 @@ fn no_false_positives() {} /// ``` fn issue_6022() {} -fn main() { - bad_doctests(); - no_false_positives(); -} +fn main() {} diff --git a/tests/ui/never_loop.rs b/tests/ui/never_loop.rs index 52470c6ee81bb..7b2d66450f171 100644 --- a/tests/ui/never_loop.rs +++ b/tests/ui/never_loop.rs @@ -447,22 +447,7 @@ fn loop_label() { } } -fn main() { - test1(); - test2(); - test3(); - test4(); - test5(); - test6(); - test7(); - test8(); - test9(); - test10(); - test11(|| 0); - test12(true, false); - test13(); - test14(); -} +fn main() {} fn issue15059() { 'a: for _ in 0..1 { diff --git a/tests/ui/never_loop.stderr b/tests/ui/never_loop.stderr index 49392d971ee32..815758107884c 100644 --- a/tests/ui/never_loop.stderr +++ b/tests/ui/never_loop.stderr @@ -248,7 +248,7 @@ LL | | } | |_________^ error: this loop never actually loops - --> tests/ui/never_loop.rs:468:5 + --> tests/ui/never_loop.rs:453:5 | LL | / 'a: for _ in 0..1 { LL | | @@ -264,7 +264,7 @@ LL ~ | error: this loop never actually loops - --> tests/ui/never_loop.rs:474:5 + --> tests/ui/never_loop.rs:459:5 | LL | / 'a: for i in 0..1 { LL | | @@ -288,7 +288,7 @@ LL ~ | error: this loop never actually loops - --> tests/ui/never_loop.rs:489:5 + --> tests/ui/never_loop.rs:474:5 | LL | / for v in 0..10 { LL | | @@ -311,7 +311,7 @@ LL ~ | error: this loop never actually loops - --> tests/ui/never_loop.rs:500:5 + --> tests/ui/never_loop.rs:485:5 | LL | / 'bar: for _ in 0..100 { LL | | @@ -321,7 +321,7 @@ LL | | } | |_____^ | help: this code is unreachable. Consider moving the reachable parts out - --> tests/ui/never_loop.rs:502:9 + --> tests/ui/never_loop.rs:487:9 | LL | / loop { LL | | @@ -336,7 +336,7 @@ LL + if let Some(_) = (0..100).next() { | error: this loop never actually loops - --> tests/ui/never_loop.rs:502:9 + --> tests/ui/never_loop.rs:487:9 | LL | / loop { LL | | @@ -346,7 +346,7 @@ LL | | } | |_________^ error: this loop never actually loops - --> tests/ui/never_loop.rs:509:5 + --> tests/ui/never_loop.rs:494:5 | LL | / 'foo: for _ in 0..100 { LL | | @@ -356,7 +356,7 @@ LL | | } | |_____^ | help: this code is unreachable. Consider moving the reachable parts out - --> tests/ui/never_loop.rs:511:9 + --> tests/ui/never_loop.rs:496:9 | LL | / loop { LL | | @@ -372,7 +372,7 @@ LL + if let Some(_) = (0..100).next() { | error: this loop never actually loops - --> tests/ui/never_loop.rs:511:9 + --> tests/ui/never_loop.rs:496:9 | LL | / loop { LL | | @@ -383,7 +383,7 @@ LL | | } | |_________^ error: this loop never actually loops - --> tests/ui/never_loop.rs:514:13 + --> tests/ui/never_loop.rs:499:13 | LL | / loop { LL | | @@ -393,7 +393,7 @@ LL | | } | |_____________^ error: this loop never actually loops - --> tests/ui/never_loop.rs:537:5 + --> tests/ui/never_loop.rs:522:5 | LL | / loop { LL | | unsafe { diff --git a/tests/ui/panicking_macros.rs b/tests/ui/panicking_macros.rs index b044be7d54ac5..a9728d4708e6e 100644 --- a/tests/ui/panicking_macros.rs +++ b/tests/ui/panicking_macros.rs @@ -126,15 +126,4 @@ fn debug_assert_msg() { debug_assert_ne!(true, false, "test"); } -fn main() { - panic(); - panic_const(); - todo(); - unimplemented(); - unreachable(); - core_versions(); - assert(); - assert_msg(); - debug_assert(); - debug_assert_msg(); -} +fn main() {} diff --git a/tests/ui/panicking_overflow_checks.rs b/tests/ui/panicking_overflow_checks.rs index 61dfca8b37286..ecae3130fd505 100644 --- a/tests/ui/panicking_overflow_checks.rs +++ b/tests/ui/panicking_overflow_checks.rs @@ -28,6 +28,4 @@ fn test(a: u32, b: u32, c: u32) { if i - j < i {} } -fn main() { - test(1, 2, 3) -} +fn main() {} diff --git a/tests/ui/regex.rs b/tests/ui/regex.rs index c63d549b75534..699256f22c01c 100644 --- a/tests/ui/regex.rs +++ b/tests/ui/regex.rs @@ -152,8 +152,4 @@ fn regex_creation_in_loops() { } } -fn main() { - syntax_error(); - trivial_regex(); - regex_creation_in_loops(); -} +fn main() {} diff --git a/tests/ui/set_contains_or_insert.rs b/tests/ui/set_contains_or_insert.rs index ac1d74f8afa4c..b9480cd9172a5 100644 --- a/tests/ui/set_contains_or_insert.rs +++ b/tests/ui/set_contains_or_insert.rs @@ -157,13 +157,7 @@ fn simply_true() -> bool { true } -// This is placed last in order to be able to add new tests without changing line numbers -fn main() { - should_warn_hashset(); - should_warn_btreeset(); - should_not_warn_hashset(); - should_not_warn_btreeset(); -} +fn main() {} fn issue15990(s: &mut HashSet, v: usize) { if !s.contains(&v) { diff --git a/tests/ui/set_contains_or_insert.stderr b/tests/ui/set_contains_or_insert.stderr index 3b06b63182abb..85331514be7a1 100644 --- a/tests/ui/set_contains_or_insert.stderr +++ b/tests/ui/set_contains_or_insert.stderr @@ -128,7 +128,7 @@ LL | borrow_set.insert(value); | ^^^^^^^^^^^^^ error: usage of `HashSet::insert` after `HashSet::contains` - --> tests/ui/set_contains_or_insert.rs:182:11 + --> tests/ui/set_contains_or_insert.rs:176:11 | LL | if !s.contains(&v) { | ^^^^^^^^^^^^ diff --git a/tests/ui/skip_while_next.rs b/tests/ui/skip_while_next.rs index 96f4604ad42dd..6dbb6a2ebb9af 100644 --- a/tests/ui/skip_while_next.rs +++ b/tests/ui/skip_while_next.rs @@ -26,6 +26,4 @@ fn skip_while_next() { let _ = foo.skip_while().next(); } -fn main() { - skip_while_next(); -} +fn main() {} diff --git a/tests/ui/slow_vector_initialization.fixed b/tests/ui/slow_vector_initialization.fixed index a8570366e646d..a13be693e4ec2 100644 --- a/tests/ui/slow_vector_initialization.fixed +++ b/tests/ui/slow_vector_initialization.fixed @@ -1,12 +1,8 @@ #![allow(clippy::useless_vec, clippy::manual_repeat_n)] use std::iter::repeat; -fn main() { - resize_vector(); - extend_vector(); - mixed_extend_resize_vector(); - from_empty_vec(); -} + +fn main() {} fn extend_vector() { // Extend with constant expression diff --git a/tests/ui/slow_vector_initialization.rs b/tests/ui/slow_vector_initialization.rs index 52f2a52fbbb86..eda96d0a0f82a 100644 --- a/tests/ui/slow_vector_initialization.rs +++ b/tests/ui/slow_vector_initialization.rs @@ -1,12 +1,8 @@ #![allow(clippy::useless_vec, clippy::manual_repeat_n)] use std::iter::repeat; -fn main() { - resize_vector(); - extend_vector(); - mixed_extend_resize_vector(); - from_empty_vec(); -} + +fn main() {} fn extend_vector() { // Extend with constant expression diff --git a/tests/ui/slow_vector_initialization.stderr b/tests/ui/slow_vector_initialization.stderr index e83eef8264724..320f2cacf61b3 100644 --- a/tests/ui/slow_vector_initialization.stderr +++ b/tests/ui/slow_vector_initialization.stderr @@ -1,5 +1,5 @@ error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:14:20 + --> tests/ui/slow_vector_initialization.rs:10:20 | LL | let mut vec1 = Vec::with_capacity(len); | ____________________^ @@ -11,7 +11,7 @@ LL | | vec1.extend(repeat(0).take(len)); = help: to override `-D warnings` add `#[allow(clippy::slow_vector_initialization)]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:20:20 + --> tests/ui/slow_vector_initialization.rs:16:20 | LL | let mut vec2 = Vec::with_capacity(len - 10); | ____________________^ @@ -20,7 +20,7 @@ LL | | vec2.extend(repeat(0).take(len - 10)); | |_________________________________________^ help: consider replacing this with: `vec![0; len - 10]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:29:20 + --> tests/ui/slow_vector_initialization.rs:25:20 | LL | let mut vec4 = Vec::with_capacity(len); | ____________________^ @@ -29,7 +29,7 @@ LL | | vec4.extend(repeat(0).take(vec4.capacity())); | |________________________________________________^ help: consider replacing this with: `vec![0; len]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:41:27 + --> tests/ui/slow_vector_initialization.rs:37:27 | LL | let mut resized_vec = Vec::with_capacity(30); | ___________________________^ @@ -38,7 +38,7 @@ LL | | resized_vec.resize(30, 0); | |_____________________________^ help: consider replacing this with: `vec![0; 30]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:46:26 + --> tests/ui/slow_vector_initialization.rs:42:26 | LL | let mut extend_vec = Vec::with_capacity(30); | __________________________^ @@ -47,7 +47,7 @@ LL | | extend_vec.extend(repeat(0).take(30)); | |_________________________________________^ help: consider replacing this with: `vec![0; 30]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:55:20 + --> tests/ui/slow_vector_initialization.rs:51:20 | LL | let mut vec1 = Vec::with_capacity(len); | ____________________^ @@ -56,7 +56,7 @@ LL | | vec1.resize(len, 0); | |_______________________^ help: consider replacing this with: `vec![0; len]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:65:20 + --> tests/ui/slow_vector_initialization.rs:61:20 | LL | let mut vec3 = Vec::with_capacity(len - 10); | ____________________^ @@ -65,7 +65,7 @@ LL | | vec3.resize(len - 10, 0); | |____________________________^ help: consider replacing this with: `vec![0; len - 10]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:70:20 + --> tests/ui/slow_vector_initialization.rs:66:20 | LL | let mut vec4 = Vec::with_capacity(len); | ____________________^ @@ -74,7 +74,7 @@ LL | | vec4.resize(vec4.capacity(), 0); | |___________________________________^ help: consider replacing this with: `vec![0; len]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:76:12 + --> tests/ui/slow_vector_initialization.rs:72:12 | LL | vec1 = Vec::with_capacity(10); | ____________^ @@ -83,7 +83,7 @@ LL | | vec1.resize(10, 0); | |______________________^ help: consider replacing this with: `vec![0; 10]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:85:20 + --> tests/ui/slow_vector_initialization.rs:81:20 | LL | let mut vec1 = Vec::new(); | ____________________^ @@ -92,7 +92,7 @@ LL | | vec1.resize(len, 0); | |_______________________^ help: consider replacing this with: `vec![0; len]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:91:20 + --> tests/ui/slow_vector_initialization.rs:87:20 | LL | let mut vec3 = Vec::new(); | ____________________^ @@ -101,7 +101,7 @@ LL | | vec3.resize(len - 10, 0); | |____________________________^ help: consider replacing this with: `vec![0; len - 10]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:97:12 + --> tests/ui/slow_vector_initialization.rs:93:12 | LL | vec1 = Vec::new(); | ____________^ @@ -110,7 +110,7 @@ LL | | vec1.resize(10, 0); | |______________________^ help: consider replacing this with: `vec![0; 10]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:102:12 + --> tests/ui/slow_vector_initialization.rs:98:12 | LL | vec1 = vec![]; | ____________^ diff --git a/tests/ui/transmute_null_to_fn.rs b/tests/ui/transmute_null_to_fn.rs index 4712374af934f..9ce097d124ab8 100644 --- a/tests/ui/transmute_null_to_fn.rs +++ b/tests/ui/transmute_null_to_fn.rs @@ -41,7 +41,4 @@ fn issue_11485() { } } -fn main() { - one_liners(); - transmute_const(); -} +fn main() {} diff --git a/tests/ui/transmuting_null.rs b/tests/ui/transmuting_null.rs index efa4c5cfdc2d7..c2de798ab3082 100644 --- a/tests/ui/transmuting_null.rs +++ b/tests/ui/transmuting_null.rs @@ -57,10 +57,4 @@ fn transmute_pointer_creators() { } } -fn main() { - one_liners(); - transmute_const(); - transmute_const_int(); - transumute_single_expr_blocks(); - transmute_pointer_creators(); -} +fn main() {} diff --git a/tests/ui/try_err.fixed b/tests/ui/try_err.fixed index ea509f9a2da87..bec366f4fd604 100644 --- a/tests/ui/try_err.fixed +++ b/tests/ui/try_err.fixed @@ -107,24 +107,17 @@ fn calling_macro() -> Result { Ok(5) } -fn main() { - basic_test().unwrap(); - into_test().unwrap(); - negative_test().unwrap(); - closure_matches_test().unwrap(); - closure_into_test().unwrap(); - calling_macro().unwrap(); - - // We don't want to lint in external macros - external! { - pub fn try_err_fn() -> Result { - let err: i32 = 1; - // To avoid warnings during rustfix - if true { Err(err)? } else { Ok(2) } - } +// We don't want to lint in external macros +external! { + pub fn try_err_fn() -> Result { + let err: i32 = 1; + // To avoid warnings during rustfix + if true { Err(err)? } else { Ok(2) } } } +fn main() {} + #[inline_macros] pub fn macro_inside(fail: bool) -> Result { if fail { diff --git a/tests/ui/try_err.rs b/tests/ui/try_err.rs index 295f7bd7089b9..9c10acd2ce0ad 100644 --- a/tests/ui/try_err.rs +++ b/tests/ui/try_err.rs @@ -107,24 +107,17 @@ fn calling_macro() -> Result { Ok(5) } -fn main() { - basic_test().unwrap(); - into_test().unwrap(); - negative_test().unwrap(); - closure_matches_test().unwrap(); - closure_into_test().unwrap(); - calling_macro().unwrap(); - - // We don't want to lint in external macros - external! { - pub fn try_err_fn() -> Result { - let err: i32 = 1; - // To avoid warnings during rustfix - if true { Err(err)? } else { Ok(2) } - } +// We don't want to lint in external macros +external! { + pub fn try_err_fn() -> Result { + let err: i32 = 1; + // To avoid warnings during rustfix + if true { Err(err)? } else { Ok(2) } } } +fn main() {} + #[inline_macros] pub fn macro_inside(fail: bool) -> Result { if fail { diff --git a/tests/ui/try_err.stderr b/tests/ui/try_err.stderr index 50c2b092d6c87..3ca51e9d88915 100644 --- a/tests/ui/try_err.stderr +++ b/tests/ui/try_err.stderr @@ -45,31 +45,31 @@ LL | Err(_) => Err(inline!(1))?, = note: this error originates in the macro `__inline_mac_fn_calling_macro` (in Nightly builds, run with -Z macro-backtrace for more info) error: returning an `Err(_)` with the `?` operator - --> tests/ui/try_err.rs:131:9 + --> tests/ui/try_err.rs:124:9 | LL | Err(inline!(inline!(String::from("aasdfasdfasdfa"))))?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `return Err(inline!(inline!(String::from("aasdfasdfasdfa"))))` error: returning an `Err(_)` with the `?` operator - --> tests/ui/try_err.rs:139:9 + --> tests/ui/try_err.rs:132:9 | LL | Err(io::ErrorKind::WriteZero)? | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `return Poll::Ready(Err(io::ErrorKind::WriteZero.into()))` error: returning an `Err(_)` with the `?` operator - --> tests/ui/try_err.rs:142:9 + --> tests/ui/try_err.rs:135:9 | LL | Err(io::Error::new(io::ErrorKind::InvalidInput, "error"))? | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `return Poll::Ready(Err(io::Error::new(io::ErrorKind::InvalidInput, "error")))` error: returning an `Err(_)` with the `?` operator - --> tests/ui/try_err.rs:151:9 + --> tests/ui/try_err.rs:144:9 | LL | Err(io::ErrorKind::NotFound)? | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `return Poll::Ready(Some(Err(io::ErrorKind::NotFound.into())))` error: returning an `Err(_)` with the `?` operator - --> tests/ui/try_err.rs:161:16 + --> tests/ui/try_err.rs:154:16 | LL | return Err(42)?; | ^^^^^^^^ help: try: `Err(42)` diff --git a/tests/ui/unicode.fixed b/tests/ui/unicode.fixed index 9234b440d9648..8fd25d1b48f5f 100644 --- a/tests/ui/unicode.fixed +++ b/tests/ui/unicode.fixed @@ -60,7 +60,4 @@ mod non_ascii_literal { } } -fn main() { - zero(); - canon(); -} +fn main() {} diff --git a/tests/ui/unicode.rs b/tests/ui/unicode.rs index ada8bac8e0492..6447a704a3566 100644 --- a/tests/ui/unicode.rs +++ b/tests/ui/unicode.rs @@ -60,7 +60,4 @@ mod non_ascii_literal { } } -fn main() { - zero(); - canon(); -} +fn main() {} diff --git a/tests/ui/uninlined_format_args.fixed b/tests/ui/uninlined_format_args.fixed index 7b609d9163a9c..6e999a48595ca 100644 --- a/tests/ui/uninlined_format_args.fixed +++ b/tests/ui/uninlined_format_args.fixed @@ -238,9 +238,7 @@ fn tester(fn_arg: i32) { } } -fn main() { - tester(42); -} +fn main() {} #[clippy::msrv = "1.57"] fn _under_msrv() { diff --git a/tests/ui/uninlined_format_args.rs b/tests/ui/uninlined_format_args.rs index 14e0b6caabc0e..58bbe41b97bfe 100644 --- a/tests/ui/uninlined_format_args.rs +++ b/tests/ui/uninlined_format_args.rs @@ -243,9 +243,7 @@ fn tester(fn_arg: i32) { } } -fn main() { - tester(42); -} +fn main() {} #[clippy::msrv = "1.57"] fn _under_msrv() { diff --git a/tests/ui/uninlined_format_args.stderr b/tests/ui/uninlined_format_args.stderr index 7e62a095eadf7..45994fdc9588f 100644 --- a/tests/ui/uninlined_format_args.stderr +++ b/tests/ui/uninlined_format_args.stderr @@ -836,7 +836,7 @@ LL + panic!("p3 {local_i32}"); | error: variables can be used directly in the `format!` string - --> tests/ui/uninlined_format_args.rs:259:5 + --> tests/ui/uninlined_format_args.rs:257:5 | LL | println!("expand='{}'", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -848,7 +848,7 @@ LL + println!("expand='{local_i32}'"); | error: variables can be used directly in the `format!` string - --> tests/ui/uninlined_format_args.rs:362:5 + --> tests/ui/uninlined_format_args.rs:360:5 | LL | usr_println!(true, "val='{}'", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -860,7 +860,7 @@ LL + usr_println!(true, "val='{local_i32}'"); | error: variables can be used directly in the `format!` string - --> tests/ui/uninlined_format_args.rs:364:5 + --> tests/ui/uninlined_format_args.rs:362:5 | LL | usr_println!(true, "{}", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -872,7 +872,7 @@ LL + usr_println!(true, "{local_i32}"); | error: variables can be used directly in the `format!` string - --> tests/ui/uninlined_format_args.rs:366:5 + --> tests/ui/uninlined_format_args.rs:364:5 | LL | usr_println!(true, "{:#010x}", local_i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -884,7 +884,7 @@ LL + usr_println!(true, "{local_i32:#010x}"); | error: variables can be used directly in the `format!` string - --> tests/ui/uninlined_format_args.rs:368:5 + --> tests/ui/uninlined_format_args.rs:366:5 | LL | usr_println!(true, "{:.1}", local_f64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/unit_arg.rs b/tests/ui/unit_arg.rs index 4208efad6774d..3cba0735b3623 100644 --- a/tests/ui/unit_arg.rs +++ b/tests/ui/unit_arg.rs @@ -147,10 +147,7 @@ fn proc_macro() { with_span!(span taking_multiple_units(unsafe { (); }, 'x: loop { break 'x (); })); } -fn main() { - bad(); - ok(); -} +fn main() {} fn issue14857() { let fn_take_unit = |_: ()| {}; diff --git a/tests/ui/unit_arg.stderr b/tests/ui/unit_arg.stderr index eb7c56c45b595..ed13108631574 100644 --- a/tests/ui/unit_arg.stderr +++ b/tests/ui/unit_arg.stderr @@ -200,19 +200,19 @@ LL + Some(()) | error: passing a unit value to a function - --> tests/ui/unit_arg.rs:171:5 + --> tests/ui/unit_arg.rs:168:5 | LL | fn_take_unit(mac!(def)); | ^^^^^^^^^^^^^^^^^^^^^^^ error: passing a unit value to a function - --> tests/ui/unit_arg.rs:173:5 + --> tests/ui/unit_arg.rs:170:5 | LL | fn_take_unit(mac!(func Default::default)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: passing a unit value to a function - --> tests/ui/unit_arg.rs:175:5 + --> tests/ui/unit_arg.rs:172:5 | LL | fn_take_unit(mac!(nonempty_block Default::default())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/unit_arg_fixable.fixed b/tests/ui/unit_arg_fixable.fixed index 03353a14081b2..3bbc62ca0bca7 100644 --- a/tests/ui/unit_arg_fixable.fixed +++ b/tests/ui/unit_arg_fixable.fixed @@ -29,9 +29,7 @@ fn bad() { fn taking_two_units(a: (), b: ()) {} fn taking_three_units(a: (), b: (), c: ()) {} -fn main() { - bad(); -} +fn main() {} fn issue14857() { let fn_take_unit = |_: ()| {}; diff --git a/tests/ui/unit_arg_fixable.rs b/tests/ui/unit_arg_fixable.rs index 03fd96efdf901..12d6cbcf61d92 100644 --- a/tests/ui/unit_arg_fixable.rs +++ b/tests/ui/unit_arg_fixable.rs @@ -26,9 +26,7 @@ fn bad() { fn taking_two_units(a: (), b: ()) {} fn taking_three_units(a: (), b: (), c: ()) {} -fn main() { - bad(); -} +fn main() {} fn issue14857() { let fn_take_unit = |_: ()| {}; diff --git a/tests/ui/unit_arg_fixable.stderr b/tests/ui/unit_arg_fixable.stderr index ccd5aa8322f9a..b1229c5a4e3e1 100644 --- a/tests/ui/unit_arg_fixable.stderr +++ b/tests/ui/unit_arg_fixable.stderr @@ -43,7 +43,7 @@ LL ~ taking_three_units((), (), ()); | error: passing a unit value to a function - --> tests/ui/unit_arg_fixable.rs:35:5 + --> tests/ui/unit_arg_fixable.rs:33:5 | LL | fn_take_unit(Default::default()); | ^^^^^^^^^^^^^------------------^ @@ -51,7 +51,7 @@ LL | fn_take_unit(Default::default()); | help: use a unit literal instead: `()` error: passing a unit value to a function - --> tests/ui/unit_arg_fixable.rs:49:5 + --> tests/ui/unit_arg_fixable.rs:47:5 | LL | fn_take_unit(another_mac!()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -63,7 +63,7 @@ LL ~ fn_take_unit(()); | error: passing a unit value to a function - --> tests/ui/unit_arg_fixable.rs:51:5 + --> tests/ui/unit_arg_fixable.rs:49:5 | LL | fn_take_unit(another_mac!(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -75,7 +75,7 @@ LL ~ fn_take_unit(()); | error: passing a unit value to a function - --> tests/ui/unit_arg_fixable.rs:60:5 + --> tests/ui/unit_arg_fixable.rs:58:5 | LL | fn_take_unit(mac!(nondef Default::default())); | ^^^^^^^^^^^^^^^^^^^^^^^^^------------------^^ @@ -83,7 +83,7 @@ LL | fn_take_unit(mac!(nondef Default::default())); | help: use a unit literal instead: `()` error: passing a unit value to a function - --> tests/ui/unit_arg_fixable.rs:62:5 + --> tests/ui/unit_arg_fixable.rs:60:5 | LL | fn_take_unit(mac!(empty_block)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -95,7 +95,7 @@ LL ~ fn_take_unit(()); | error: passing a unit value to a function - --> tests/ui/unit_arg_fixable.rs:69:5 + --> tests/ui/unit_arg_fixable.rs:67:5 | LL | fn_take_unit(def()); | ^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/unnecessary_literal_unwrap.fixed b/tests/ui/unnecessary_literal_unwrap.fixed index 877f4ad41c7ef..05ffdeb1dcc59 100644 --- a/tests/ui/unnecessary_literal_unwrap.fixed +++ b/tests/ui/unnecessary_literal_unwrap.fixed @@ -152,12 +152,4 @@ fn unwrap_unchecked() { //~^ unnecessary_literal_unwrap } -fn main() { - unwrap_option_some(); - unwrap_option_none(); - unwrap_result_ok(); - unwrap_result_err(); - unwrap_methods_option(); - unwrap_methods_result(); - unwrap_unchecked(); -} +fn main() {} diff --git a/tests/ui/unnecessary_literal_unwrap.rs b/tests/ui/unnecessary_literal_unwrap.rs index c0a35ae78a71d..5efefb24530b9 100644 --- a/tests/ui/unnecessary_literal_unwrap.rs +++ b/tests/ui/unnecessary_literal_unwrap.rs @@ -152,12 +152,4 @@ fn unwrap_unchecked() { //~^ unnecessary_literal_unwrap } -fn main() { - unwrap_option_some(); - unwrap_option_none(); - unwrap_result_ok(); - unwrap_result_err(); - unwrap_methods_option(); - unwrap_methods_result(); - unwrap_unchecked(); -} +fn main() {} diff --git a/tests/ui/unnecessary_literal_unwrap_unfixable.rs b/tests/ui/unnecessary_literal_unwrap_unfixable.rs index b6cba4e6a5685..2680d0a6697be 100644 --- a/tests/ui/unnecessary_literal_unwrap_unfixable.rs +++ b/tests/ui/unnecessary_literal_unwrap_unfixable.rs @@ -191,16 +191,4 @@ fn unwrap_methods_result_context() { //~^ unnecessary_literal_unwrap } -fn main() { - unwrap_option_some(); - unwrap_option_some_context(); - unwrap_option_none(); - unwrap_result_ok(); - unwrap_result_ok_context(); - unwrap_result_err(); - unwrap_result_err_context(); - unwrap_methods_option(); - unwrap_methods_option_context(); - unwrap_methods_result(); - unwrap_methods_result_context(); -} +fn main() {} diff --git a/tests/ui/unnecessary_sort_by.fixed b/tests/ui/unnecessary_sort_by.fixed index 6870470e74c5a..317140eacc785 100644 --- a/tests/ui/unnecessary_sort_by.fixed +++ b/tests/ui/unnecessary_sort_by.fixed @@ -106,11 +106,7 @@ mod issue_6001 { } } -fn main() { - unnecessary_sort_by(); - issue_5754::test(); - issue_6001::test(); -} +fn main() {} fn issue16405() { let mut v: Vec<(i32, &str)> = vec![(1, "foo"), (2, "bar")]; diff --git a/tests/ui/unnecessary_sort_by.rs b/tests/ui/unnecessary_sort_by.rs index d95306176817d..a31cf29679993 100644 --- a/tests/ui/unnecessary_sort_by.rs +++ b/tests/ui/unnecessary_sort_by.rs @@ -106,11 +106,7 @@ mod issue_6001 { } } -fn main() { - unnecessary_sort_by(); - issue_5754::test(); - issue_6001::test(); -} +fn main() {} fn issue16405() { let mut v: Vec<(i32, &str)> = vec![(1, "foo"), (2, "bar")]; diff --git a/tests/ui/unnecessary_sort_by.stderr b/tests/ui/unnecessary_sort_by.stderr index cc545d604ff3b..56d4831eb70aa 100644 --- a/tests/ui/unnecessary_sort_by.stderr +++ b/tests/ui/unnecessary_sort_by.stderr @@ -145,7 +145,7 @@ LL + args.sort_unstable_by_key(|b| std::cmp::Reverse(b.name())); | error: consider using `sort_by_key` - --> tests/ui/unnecessary_sort_by.rs:118:5 + --> tests/ui/unnecessary_sort_by.rs:114:5 | LL | v.sort_by(|a, b| a.0.cmp(&b.0)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -157,7 +157,7 @@ LL + v.sort_by_key(|a| a.0); | error: consider using `sort_by_key` - --> tests/ui/unnecessary_sort_by.rs:136:5 + --> tests/ui/unnecessary_sort_by.rs:132:5 | LL | items.sort_by(|item1, item2| item1.key.cmp(&item2.key)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -169,7 +169,7 @@ LL + items.sort_by_key(|item1| item1.key); | error: consider using `sort_by_key` - --> tests/ui/unnecessary_sort_by.rs:141:5 + --> tests/ui/unnecessary_sort_by.rs:137:5 | LL | items.sort_by(|item1, item2| item1.value.clone().cmp(&item2.value.clone())); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -181,7 +181,7 @@ LL + items.sort_by_key(|item1| item1.value.clone()); | error: consider using `sort_by_key` - --> tests/ui/unnecessary_sort_by.rs:147:5 + --> tests/ui/unnecessary_sort_by.rs:143:5 | LL | v.sort_by(|(_, s1), (_, s2)| s1.cmp(s2)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -193,7 +193,7 @@ LL + v.sort_by_key(|(_, s1)| *s1); | error: consider using `sort_by_key` - --> tests/ui/unnecessary_sort_by.rs:154:5 + --> tests/ui/unnecessary_sort_by.rs:150:5 | LL | v.sort_by(|Foo { bar: b1 }, Foo { bar: b2 }| b1.cmp(b2)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -205,7 +205,7 @@ LL + v.sort_by_key(|Foo { bar: b1 }| *b1); | error: consider using `sort_by_key` - --> tests/ui/unnecessary_sort_by.rs:159:5 + --> tests/ui/unnecessary_sort_by.rs:155:5 | LL | v.sort_by(|Baz(b1), Baz(b2)| b1.cmp(b2)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -217,7 +217,7 @@ LL + v.sort_by_key(|Baz(b1)| *b1); | error: consider using `sort_by_key` - --> tests/ui/unnecessary_sort_by.rs:162:5 + --> tests/ui/unnecessary_sort_by.rs:158:5 | LL | v.sort_by(|&Baz(b1), &Baz(b2)| b1.cmp(&b2)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -229,7 +229,7 @@ LL + v.sort_by_key(|&Baz(b1)| b1); | error: consider using `sort_by_key` - --> tests/ui/unnecessary_sort_by.rs:166:5 + --> tests/ui/unnecessary_sort_by.rs:162:5 | LL | v.sort_by(|&&b1, &&b2| b1.cmp(&b2)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -241,7 +241,7 @@ LL + v.sort_by_key(|&&b1| b1); | error: consider using `sort_by_key` - --> tests/ui/unnecessary_sort_by.rs:170:5 + --> tests/ui/unnecessary_sort_by.rs:166:5 | LL | v.sort_by(|[a1, b1], [a2, b2]| a1.cmp(a2)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -253,7 +253,7 @@ LL + v.sort_by_key(|[a1, b1]| *a1); | error: consider using `sort_by_key` - --> tests/ui/unnecessary_sort_by.rs:173:5 + --> tests/ui/unnecessary_sort_by.rs:169:5 | LL | v.sort_by(|[a1, b1], [a2, b2]| (a1 - b1).cmp(&(a2 - b2))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/unused_async.rs b/tests/ui/unused_async.rs index 7a0be825a2de9..3f9244ab49707 100644 --- a/tests/ui/unused_async.rs +++ b/tests/ui/unused_async.rs @@ -115,10 +115,7 @@ macro_rules! async_trait_impl { } async_trait_impl!(); -fn main() { - foo(); - bar(); -} +fn main() {} mod issue14704 { use std::sync::Arc; diff --git a/tests/ui/unused_peekable.rs b/tests/ui/unused_peekable.rs index e7fe297764eb3..29e830fefb87d 100644 --- a/tests/ui/unused_peekable.rs +++ b/tests/ui/unused_peekable.rs @@ -3,10 +3,7 @@ use std::iter::{Empty, Peekable}; -fn main() { - invalid(); - valid(); -} +fn main() {} #[allow(clippy::unused_unit)] fn invalid() { diff --git a/tests/ui/unused_peekable.stderr b/tests/ui/unused_peekable.stderr index 9330d8c58001d..376c896a1c57c 100644 --- a/tests/ui/unused_peekable.stderr +++ b/tests/ui/unused_peekable.stderr @@ -1,5 +1,5 @@ error: `peek` never called on `Peekable` iterator - --> tests/ui/unused_peekable.rs:13:9 + --> tests/ui/unused_peekable.rs:10:9 | LL | let peekable = std::iter::empty::().peekable(); | ^^^^^^^^ @@ -9,7 +9,7 @@ LL | let peekable = std::iter::empty::().peekable(); = help: to override `-D warnings` add `#[allow(clippy::unused_peekable)]` error: `peek` never called on `Peekable` iterator - --> tests/ui/unused_peekable.rs:18:9 + --> tests/ui/unused_peekable.rs:15:9 | LL | let new_local = old_local; | ^^^^^^^^^ @@ -17,7 +17,7 @@ LL | let new_local = old_local; = help: consider removing the call to `peekable` error: `peek` never called on `Peekable` iterator - --> tests/ui/unused_peekable.rs:23:9 + --> tests/ui/unused_peekable.rs:20:9 | LL | let by_mut_ref = &mut by_mut_ref_test; | ^^^^^^^^^^ @@ -25,7 +25,7 @@ LL | let by_mut_ref = &mut by_mut_ref_test; = help: consider removing the call to `peekable` error: `peek` never called on `Peekable` iterator - --> tests/ui/unused_peekable.rs:31:9 + --> tests/ui/unused_peekable.rs:28:9 | LL | let peekable_from_fn = returns_peekable(); | ^^^^^^^^^^^^^^^^ @@ -33,7 +33,7 @@ LL | let peekable_from_fn = returns_peekable(); = help: consider removing the call to `peekable` error: `peek` never called on `Peekable` iterator - --> tests/ui/unused_peekable.rs:35:13 + --> tests/ui/unused_peekable.rs:32:13 | LL | let mut peekable_using_iterator_method = std::iter::empty::().peekable(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -41,7 +41,7 @@ LL | let mut peekable_using_iterator_method = std::iter::empty::().peek = help: consider removing the call to `peekable` error: `peek` never called on `Peekable` iterator - --> tests/ui/unused_peekable.rs:42:9 + --> tests/ui/unused_peekable.rs:39:9 | LL | let passed_along_ref = std::iter::empty::().peekable(); | ^^^^^^^^^^^^^^^^ @@ -49,7 +49,7 @@ LL | let passed_along_ref = std::iter::empty::().peekable(); = help: consider removing the call to `peekable` error: `peek` never called on `Peekable` iterator - --> tests/ui/unused_peekable.rs:49:9 + --> tests/ui/unused_peekable.rs:46:9 | LL | let _by_ref = by_ref_test.by_ref(); | ^^^^^^^ @@ -57,7 +57,7 @@ LL | let _by_ref = by_ref_test.by_ref(); = help: consider removing the call to `peekable` error: `peek` never called on `Peekable` iterator - --> tests/ui/unused_peekable.rs:52:13 + --> tests/ui/unused_peekable.rs:49:13 | LL | let mut peekable_in_for_loop = std::iter::empty::().peekable(); | ^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/useless_asref.fixed b/tests/ui/useless_asref.fixed index 54a7c0a8c0802..5131ae113d184 100644 --- a/tests/ui/useless_asref.fixed +++ b/tests/ui/useless_asref.fixed @@ -293,7 +293,4 @@ fn issue16098(exts: Vec<&str>) { //~^ useless_asref } -fn main() { - not_ok(); - ok(); -} +fn main() {} diff --git a/tests/ui/useless_asref.rs b/tests/ui/useless_asref.rs index 1b9ce1d462335..e1c2a1d81d04f 100644 --- a/tests/ui/useless_asref.rs +++ b/tests/ui/useless_asref.rs @@ -293,7 +293,4 @@ fn issue16098(exts: Vec<&str>) { //~^ useless_asref } -fn main() { - not_ok(); - ok(); -} +fn main() {} diff --git a/tests/ui/useless_attribute.fixed b/tests/ui/useless_attribute.fixed index dbe4b6fb50f3a..ff40f51c02054 100644 --- a/tests/ui/useless_attribute.fixed +++ b/tests/ui/useless_attribute.fixed @@ -96,9 +96,7 @@ mod module { #[allow(unused_braces)] use module::{Struct}; -fn main() { - test_indented_attr(); -} +fn main() {} // Regression test for https://github.com/rust-lang/rust-clippy/issues/4467 #[allow(dead_code)] diff --git a/tests/ui/useless_attribute.rs b/tests/ui/useless_attribute.rs index 44fb6733f054d..426c720b4429e 100644 --- a/tests/ui/useless_attribute.rs +++ b/tests/ui/useless_attribute.rs @@ -96,9 +96,7 @@ mod module { #[allow(unused_braces)] use module::{Struct}; -fn main() { - test_indented_attr(); -} +fn main() {} // Regression test for https://github.com/rust-lang/rust-clippy/issues/4467 #[allow(dead_code)] diff --git a/tests/ui/useless_conversion.fixed b/tests/ui/useless_conversion.fixed index cbdb10351eebe..d0297ef6bcdc0 100644 --- a/tests/ui/useless_conversion.fixed +++ b/tests/ui/useless_conversion.fixed @@ -117,21 +117,6 @@ fn dont_lint_into_iter_on_static_copy_iter() { } fn main() { - test_generic(10i32); - test_generic2::(10i32); - test_questionmark().unwrap(); - test_issue_3913().unwrap(); - - dont_lint_on_type_alias(); - dont_lint_into_iter_on_immutable_local_implementing_iterator_in_expr(); - lint_into_iter_on_mutable_local_implementing_iterator_in_expr(); - lint_into_iter_on_expr_implementing_iterator(); - lint_into_iter_on_expr_implementing_iterator_2(); - lint_into_iter_on_const_implementing_iterator(); - lint_into_iter_on_const_implementing_iterator_2(); - dont_lint_into_iter_on_copy_iter(); - dont_lint_into_iter_on_static_copy_iter(); - { // triggers the IntoIterator trait fn consume(_: impl IntoIterator) {} diff --git a/tests/ui/useless_conversion.rs b/tests/ui/useless_conversion.rs index c97261e0c049c..20a0f6d72f9a8 100644 --- a/tests/ui/useless_conversion.rs +++ b/tests/ui/useless_conversion.rs @@ -117,21 +117,6 @@ fn dont_lint_into_iter_on_static_copy_iter() { } fn main() { - test_generic(10i32); - test_generic2::(10i32); - test_questionmark().unwrap(); - test_issue_3913().unwrap(); - - dont_lint_on_type_alias(); - dont_lint_into_iter_on_immutable_local_implementing_iterator_in_expr(); - lint_into_iter_on_mutable_local_implementing_iterator_in_expr(); - lint_into_iter_on_expr_implementing_iterator(); - lint_into_iter_on_expr_implementing_iterator_2(); - lint_into_iter_on_const_implementing_iterator(); - lint_into_iter_on_const_implementing_iterator_2(); - dont_lint_into_iter_on_copy_iter(); - dont_lint_into_iter_on_static_copy_iter(); - { // triggers the IntoIterator trait fn consume(_: impl IntoIterator) {} diff --git a/tests/ui/useless_conversion.stderr b/tests/ui/useless_conversion.stderr index 8ea7d0d5d3689..5c157886ca709 100644 --- a/tests/ui/useless_conversion.stderr +++ b/tests/ui/useless_conversion.stderr @@ -53,13 +53,13 @@ LL | let mut n = NUMBERS.into_iter(); | ^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `NUMBERS` error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:141:17 + --> tests/ui/useless_conversion.rs:126:17 | LL | consume(items.into_iter()); | ^^^^^^^^^^^^^^^^^ | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:137:28 + --> tests/ui/useless_conversion.rs:122:28 | LL | fn consume(_: impl IntoIterator) {} | ^^^^^^^^^^^^ @@ -70,73 +70,73 @@ LL + consume(*items); | error: useless conversion to the same type: `std::string::String` - --> tests/ui/useless_conversion.rs:154:21 + --> tests/ui/useless_conversion.rs:139:21 | LL | let _: String = "foo".to_string().into(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `"foo".to_string()` error: useless conversion to the same type: `std::string::String` - --> tests/ui/useless_conversion.rs:156:21 + --> tests/ui/useless_conversion.rs:141:21 | LL | let _: String = From::from("foo".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `From::from()`: `"foo".to_string()` error: useless conversion to the same type: `std::string::String` - --> tests/ui/useless_conversion.rs:158:13 + --> tests/ui/useless_conversion.rs:143:13 | LL | let _ = String::from("foo".to_string()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `"foo".to_string()` error: useless conversion to the same type: `std::string::String` - --> tests/ui/useless_conversion.rs:160:13 + --> tests/ui/useless_conversion.rs:145:13 | LL | let _ = String::from(format!("A: {:04}", 123)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `String::from()`: `format!("A: {:04}", 123)` error: useless conversion to the same type: `std::str::Lines<'_>` - --> tests/ui/useless_conversion.rs:162:13 + --> tests/ui/useless_conversion.rs:147:13 | LL | let _ = "".lines().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `"".lines()` error: useless conversion to the same type: `std::vec::IntoIter` - --> tests/ui/useless_conversion.rs:164:13 + --> tests/ui/useless_conversion.rs:149:13 | LL | let _ = vec![1, 2, 3].into_iter().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `vec![1, 2, 3].into_iter()` error: useless conversion to the same type: `std::string::String` - --> tests/ui/useless_conversion.rs:166:21 + --> tests/ui/useless_conversion.rs:151:21 | LL | let _: String = format!("Hello {}", "world").into(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into()`: `format!("Hello {}", "world")` error: useless conversion to the same type: `i32` - --> tests/ui/useless_conversion.rs:172:13 + --> tests/ui/useless_conversion.rs:157:13 | LL | let _ = i32::from(a + b) * 3; | ^^^^^^^^^^^^^^^^ help: consider removing `i32::from()`: `(a + b)` error: useless conversion to the same type: `Foo<'a'>` - --> tests/ui/useless_conversion.rs:179:23 + --> tests/ui/useless_conversion.rs:164:23 | LL | let _: Foo<'a'> = s2.into(); | ^^^^^^^^^ help: consider removing `.into()`: `s2` error: useless conversion to the same type: `Foo<'a'>` - --> tests/ui/useless_conversion.rs:182:13 + --> tests/ui/useless_conversion.rs:167:13 | LL | let _ = Foo::<'a'>::from(s3); | ^^^^^^^^^^^^^^^^^^^^ help: consider removing `Foo::<'a'>::from()`: `s3` error: useless conversion to the same type: `std::vec::IntoIter>` - --> tests/ui/useless_conversion.rs:185:13 + --> tests/ui/useless_conversion.rs:170:13 | LL | let _ = vec![s4, s4, s4].into_iter().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `vec![s4, s4, s4].into_iter()` error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:218:7 + --> tests/ui/useless_conversion.rs:203:7 | LL | b(vec![1, 2].into_iter()); | ^^^^^^^^^^------------ @@ -144,13 +144,13 @@ LL | b(vec![1, 2].into_iter()); | help: consider removing the `.into_iter()` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:208:13 + --> tests/ui/useless_conversion.rs:193:13 | LL | fn b>(_: T) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:220:7 + --> tests/ui/useless_conversion.rs:205:7 | LL | c(vec![1, 2].into_iter()); | ^^^^^^^^^^------------ @@ -158,13 +158,13 @@ LL | c(vec![1, 2].into_iter()); | help: consider removing the `.into_iter()` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:209:18 + --> tests/ui/useless_conversion.rs:194:18 | LL | fn c(_: impl IntoIterator) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:222:7 + --> tests/ui/useless_conversion.rs:207:7 | LL | d(vec![1, 2].into_iter()); | ^^^^^^^^^^------------ @@ -172,13 +172,13 @@ LL | d(vec![1, 2].into_iter()); | help: consider removing the `.into_iter()` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:212:12 + --> tests/ui/useless_conversion.rs:197:12 | LL | T: IntoIterator, | ^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:226:7 + --> tests/ui/useless_conversion.rs:211:7 | LL | b(vec![1, 2].into_iter().into_iter()); | ^^^^^^^^^^------------------------ @@ -186,13 +186,13 @@ LL | b(vec![1, 2].into_iter().into_iter()); | help: consider removing the `.into_iter()`s | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:208:13 + --> tests/ui/useless_conversion.rs:193:13 | LL | fn b>(_: T) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:228:7 + --> tests/ui/useless_conversion.rs:213:7 | LL | b(vec![1, 2].into_iter().into_iter().into_iter()); | ^^^^^^^^^^------------------------------------ @@ -200,13 +200,13 @@ LL | b(vec![1, 2].into_iter().into_iter().into_iter()); | help: consider removing the `.into_iter()`s | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:208:13 + --> tests/ui/useless_conversion.rs:193:13 | LL | fn b>(_: T) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:275:24 + --> tests/ui/useless_conversion.rs:260:24 | LL | foo2::([1, 2, 3].into_iter()); | ^^^^^^^^^------------ @@ -214,13 +214,13 @@ LL | foo2::([1, 2, 3].into_iter()); | help: consider removing the `.into_iter()` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:254:12 + --> tests/ui/useless_conversion.rs:239:12 | LL | I: IntoIterator + Helper, | ^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:284:14 + --> tests/ui/useless_conversion.rs:269:14 | LL | foo3([1, 2, 3].into_iter()); | ^^^^^^^^^------------ @@ -228,13 +228,13 @@ LL | foo3([1, 2, 3].into_iter()); | help: consider removing the `.into_iter()` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:263:12 + --> tests/ui/useless_conversion.rs:248:12 | LL | I: IntoIterator, | ^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:294:16 + --> tests/ui/useless_conversion.rs:279:16 | LL | S1.foo([1, 2].into_iter()); | ^^^^^^------------ @@ -242,13 +242,13 @@ LL | S1.foo([1, 2].into_iter()); | help: consider removing the `.into_iter()` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:291:27 + --> tests/ui/useless_conversion.rs:276:27 | LL | pub fn foo(&self, _: I) {} | ^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:314:44 + --> tests/ui/useless_conversion.rs:299:44 | LL | v0.into_iter().interleave_shortest(v1.into_iter()); | ^^------------ @@ -256,67 +256,67 @@ LL | v0.into_iter().interleave_shortest(v1.into_iter()); | help: consider removing the `.into_iter()` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:301:20 + --> tests/ui/useless_conversion.rs:286:20 | LL | J: IntoIterator, | ^^^^^^^^^^^^ error: useless conversion to the same type: `()` - --> tests/ui/useless_conversion.rs:342:58 + --> tests/ui/useless_conversion.rs:327:58 | LL | let _: Result<(), std::io::Error> = test_issue_3913().map(Into::into); | ^^^^^^^^^^^^^^^^ help: consider removing error: useless conversion to the same type: `std::io::Error` - --> tests/ui/useless_conversion.rs:345:58 + --> tests/ui/useless_conversion.rs:330:58 | LL | let _: Result<(), std::io::Error> = test_issue_3913().map_err(Into::into); | ^^^^^^^^^^^^^^^^^^^^ help: consider removing error: useless conversion to the same type: `()` - --> tests/ui/useless_conversion.rs:348:58 + --> tests/ui/useless_conversion.rs:333:58 | LL | let _: Result<(), std::io::Error> = test_issue_3913().map(From::from); | ^^^^^^^^^^^^^^^^ help: consider removing error: useless conversion to the same type: `std::io::Error` - --> tests/ui/useless_conversion.rs:351:58 + --> tests/ui/useless_conversion.rs:336:58 | LL | let _: Result<(), std::io::Error> = test_issue_3913().map_err(From::from); | ^^^^^^^^^^^^^^^^^^^^ help: consider removing error: useless conversion to the same type: `()` - --> tests/ui/useless_conversion.rs:355:31 + --> tests/ui/useless_conversion.rs:340:31 | LL | let _: ControlFlow<()> = c.map_break(Into::into); | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing error: useless conversion to the same type: `()` - --> tests/ui/useless_conversion.rs:359:31 + --> tests/ui/useless_conversion.rs:344:31 | LL | let _: ControlFlow<()> = c.map_continue(Into::into); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing error: useless conversion to the same type: `u32` - --> tests/ui/useless_conversion.rs:373:41 + --> tests/ui/useless_conversion.rs:358:41 | LL | let _: Vec = [1u32].into_iter().map(Into::into).collect(); | ^^^^^^^^^^^^^^^^ help: consider removing error: useless conversion to the same type: `T` - --> tests/ui/useless_conversion.rs:384:18 + --> tests/ui/useless_conversion.rs:369:18 | LL | x.into_iter().map(Into::into).collect() | ^^^^^^^^^^^^^^^^ help: consider removing error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:400:29 + --> tests/ui/useless_conversion.rs:385:29 | LL | takes_into_iter(self.my_field.into_iter()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:389:32 + --> tests/ui/useless_conversion.rs:374:32 | LL | fn takes_into_iter(_: impl IntoIterator) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -327,13 +327,13 @@ LL + takes_into_iter(&self.my_field); | error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:408:29 + --> tests/ui/useless_conversion.rs:393:29 | LL | takes_into_iter(self.my_field.into_iter()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:389:32 + --> tests/ui/useless_conversion.rs:374:32 | LL | fn takes_into_iter(_: impl IntoIterator) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -344,13 +344,13 @@ LL + takes_into_iter(&mut self.my_field); | error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:417:29 + --> tests/ui/useless_conversion.rs:402:29 | LL | takes_into_iter(self.my_field.into_iter()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:389:32 + --> tests/ui/useless_conversion.rs:374:32 | LL | fn takes_into_iter(_: impl IntoIterator) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -361,13 +361,13 @@ LL + takes_into_iter(*self.my_field); | error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:426:29 + --> tests/ui/useless_conversion.rs:411:29 | LL | takes_into_iter(self.my_field.into_iter()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:389:32 + --> tests/ui/useless_conversion.rs:374:32 | LL | fn takes_into_iter(_: impl IntoIterator) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -378,13 +378,13 @@ LL + takes_into_iter(&*self.my_field); | error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:435:29 + --> tests/ui/useless_conversion.rs:420:29 | LL | takes_into_iter(self.my_field.into_iter()); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:389:32 + --> tests/ui/useless_conversion.rs:374:32 | LL | fn takes_into_iter(_: impl IntoIterator) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -395,25 +395,25 @@ LL + takes_into_iter(&mut *self.my_field); | error: useless conversion to the same type: `std::ops::Range` - --> tests/ui/useless_conversion.rs:450:5 + --> tests/ui/useless_conversion.rs:435:5 | LL | R.into_iter().for_each(|_x| {}); | ^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `R` error: useless conversion to the same type: `std::ops::Range` - --> tests/ui/useless_conversion.rs:452:13 + --> tests/ui/useless_conversion.rs:437:13 | LL | let _ = R.into_iter().map(|_x| 0); | ^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `R` error: useless conversion to the same type: `std::slice::Iter<'_, i32>` - --> tests/ui/useless_conversion.rs:463:14 + --> tests/ui/useless_conversion.rs:448:14 | LL | for _ in mac!(iter [1, 2]).into_iter() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `.into_iter()`: `mac!(iter [1, 2])` error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:476:27 + --> tests/ui/useless_conversion.rs:461:27 | LL | takes_into_iter_usize(b.into_iter()); | ^------------ @@ -421,13 +421,13 @@ LL | takes_into_iter_usize(b.into_iter()); | help: consider removing the `.into_iter()` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:467:34 + --> tests/ui/useless_conversion.rs:452:34 | LL | fn takes_into_iter_usize(_: impl IntoIterator) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:485:31 + --> tests/ui/useless_conversion.rs:470:31 | LL | takes_into_iter_usize(b.clone().into_iter()); | ^^^^^^^^^------------ @@ -435,13 +435,13 @@ LL | takes_into_iter_usize(b.clone().into_iter()); | help: consider removing the `.into_iter()` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:467:34 + --> tests/ui/useless_conversion.rs:452:34 | LL | fn takes_into_iter_usize(_: impl IntoIterator) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` - --> tests/ui/useless_conversion.rs:493:34 + --> tests/ui/useless_conversion.rs:478:34 | LL | takes_into_iter_usize_result(b.clone().into_iter())?; | ^^^^^^^^^------------ @@ -449,7 +449,7 @@ LL | takes_into_iter_usize_result(b.clone().into_iter())?; | help: consider removing the `.into_iter()` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` - --> tests/ui/useless_conversion.rs:468:41 + --> tests/ui/useless_conversion.rs:453:41 | LL | fn takes_into_iter_usize_result(_: impl IntoIterator) -> Result<(), ()> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/while_immutable_condition.rs b/tests/ui/while_immutable_condition.rs index 5c18cd41ff792..569a0c006aba2 100644 --- a/tests/ui/while_immutable_condition.rs +++ b/tests/ui/while_immutable_condition.rs @@ -226,16 +226,4 @@ fn immutable_condition_false_positive(mut n: u64) -> u32 { count } -fn main() { - immutable_condition(); - unused_var(); - used_immutable(); - internally_mutable(); - immutable_condition_false_positive(5); - - let mut c = Counter { count: 0 }; - c.inc_n(5); - c.print_n(2); - - while_loop_with_break_and_return(); -} +fn main() {} From 12abbc0676941d441931e6ea6f0d499346208491 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Mon, 23 Feb 2026 01:13:46 +0100 Subject: [PATCH 54/57] Do not suggest removing reborrow of a captured upvar --- clippy_lints/src/borrow_deref_ref.rs | 15 ++++++++++++++- clippy_utils/src/lib.rs | 21 +++++++++++++++++++++ tests/ui/borrow_deref_ref.fixed | 24 ++++++++++++++++++++++++ tests/ui/borrow_deref_ref.rs | 24 ++++++++++++++++++++++++ 4 files changed, 83 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/borrow_deref_ref.rs b/clippy_lints/src/borrow_deref_ref.rs index 70c9c45a60c89..947b99696bb1f 100644 --- a/clippy_lints/src/borrow_deref_ref.rs +++ b/clippy_lints/src/borrow_deref_ref.rs @@ -2,7 +2,10 @@ use crate::reference::DEREF_ADDROF; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::implements_trait; -use clippy_utils::{get_parent_expr, is_expr_temporary_value, is_from_proc_macro, is_lint_allowed, is_mutable}; +use clippy_utils::{ + get_enclosing_closure, get_parent_expr, is_expr_temporary_value, is_from_proc_macro, is_lint_allowed, is_mutable, + is_upvar_in_closure, path_to_local_with_projections, +}; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, Node, UnOp}; use rustc_lint::{LateContext, LateLintPass}; @@ -81,6 +84,16 @@ impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef { && (is_expr_temporary_value(cx, deref_target) || !potentially_bound_to_mutable_ref(cx, e)) && let Some(deref_text) = deref_target.span.get_source_text(cx) { + // `&*x` can be needed to shorten the borrow of `x`. Replacing it with `x` can be + // incorrect when `x` is a closure-captured upvar (e.g. a closure returning another + // closure that captures `x`). + if let Some(closure) = get_enclosing_closure(cx, e.hir_id) + && let Some(local_id) = path_to_local_with_projections(deref_target) + && is_upvar_in_closure(cx, closure, local_id) + { + return; + } + span_lint_and_then( cx, BORROW_DEREF_REF, diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 490174c20fd51..cc6d2b10be1fb 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1144,6 +1144,27 @@ pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Optio }) } +/// Returns the [`Closure`] enclosing `hir_id`, if any. +pub fn get_enclosing_closure<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Closure<'tcx>> { + cx.tcx.hir_parent_iter(hir_id).find_map(|(_, node)| { + if let Node::Expr(expr) = node + && let ExprKind::Closure(closure) = expr.kind + { + Some(closure) + } else { + None + } + }) +} + +/// Checks whether a local identified by `local_id` is captured as an upvar by the given `closure`. +pub fn is_upvar_in_closure(cx: &LateContext<'_>, closure: &Closure<'_>, local_id: HirId) -> bool { + cx.typeck_results() + .closure_min_captures + .get(&closure.def_id) + .is_some_and(|x| x.contains_key(&local_id)) +} + /// Gets the loop or closure enclosing the given expression, if any. pub fn get_enclosing_loop_or_multi_call_closure<'tcx>( cx: &LateContext<'tcx>, diff --git a/tests/ui/borrow_deref_ref.fixed b/tests/ui/borrow_deref_ref.fixed index 6d06fcc3037aa..60334922a08a7 100644 --- a/tests/ui/borrow_deref_ref.fixed +++ b/tests/ui/borrow_deref_ref.fixed @@ -171,3 +171,27 @@ fn issue_14934() { //~^ borrow_deref_ref } } + +mod issue16556 { + use std::pin::Pin; + + async fn async_main() { + for_each_city(|city| { + Box::pin(async { + // Do not lint, as it would not compile without reborrowing + let city = &*city; + println!("{city}") + }) + }) + .await; + } + + async fn for_each_city(mut f: F) + where + F: for<'c> FnMut(&'c str) -> Pin + Send + 'c>>, + { + for x in ["New York", "London", "Tokyo"] { + f(x).await; + } + } +} diff --git a/tests/ui/borrow_deref_ref.rs b/tests/ui/borrow_deref_ref.rs index b43f4c93bf2b0..99fd1ae0acbc2 100644 --- a/tests/ui/borrow_deref_ref.rs +++ b/tests/ui/borrow_deref_ref.rs @@ -171,3 +171,27 @@ fn issue_14934() { //~^ borrow_deref_ref } } + +mod issue16556 { + use std::pin::Pin; + + async fn async_main() { + for_each_city(|city| { + Box::pin(async { + // Do not lint, as it would not compile without reborrowing + let city = &*city; + println!("{city}") + }) + }) + .await; + } + + async fn for_each_city(mut f: F) + where + F: for<'c> FnMut(&'c str) -> Pin + Send + 'c>>, + { + for x in ["New York", "London", "Tokyo"] { + f(x).await; + } + } +} From c234b12cfa232e1e3365edaded6adbbe56290665 Mon Sep 17 00:00:00 2001 From: linshuy2 Date: Mon, 23 Feb 2026 05:13:07 +0000 Subject: [PATCH 55/57] fix: `explicit_counter_loop` FN when loop counter starts at non-zero --- .../src/loops/explicit_counter_loop.rs | 85 +++++++++++-------- tests/ui/explicit_counter_loop.rs | 20 ++++- tests/ui/explicit_counter_loop.stderr | 20 ++++- 3 files changed, 86 insertions(+), 39 deletions(-) diff --git a/clippy_lints/src/loops/explicit_counter_loop.rs b/clippy_lints/src/loops/explicit_counter_loop.rs index 9aa4d2f0adc2c..bfd4d4bcd5644 100644 --- a/clippy_lints/src/loops/explicit_counter_loop.rs +++ b/clippy_lints/src/loops/explicit_counter_loop.rs @@ -1,17 +1,19 @@ +use std::borrow::Cow; + use super::{EXPLICIT_COUNTER_LOOP, IncrementVisitor, InitializeVisitor, make_iterator_snippet}; -use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{get_enclosing_block, is_integer_const}; -use rustc_ast::Label; +use clippy_utils::sugg::{EMPTY, Sugg}; +use clippy_utils::{get_enclosing_block, is_integer_const, is_integer_literal_untyped}; +use rustc_ast::{Label, RangeLimits}; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_block, walk_expr}; use rustc_hir::{Expr, Pat}; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty, UintTy}; -// To trigger the EXPLICIT_COUNTER_LOOP lint, a variable must be -// incremented exactly once in the loop body, and initialized to zero -// at the start of the loop. +// To trigger the EXPLICIT_COUNTER_LOOP lint, a variable must be incremented exactly once in the +// loop body. pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, @@ -31,34 +33,11 @@ pub(super) fn check<'tcx>( let mut initialize_visitor = InitializeVisitor::new(cx, expr, id); walk_block(&mut initialize_visitor, block); - if let Some((name, ty, initializer)) = initialize_visitor.get_result() - && is_integer_const(cx, initializer, 0) - { + if let Some((name, ty, initializer)) = initialize_visitor.get_result() { + let is_zero = is_integer_const(cx, initializer, 0); let mut applicability = Applicability::MaybeIncorrect; let span = expr.span.with_hi(arg.span.hi()); let loop_label = label.map_or(String::new(), |l| format!("{}: ", l.ident.name)); - let int_name = match ty.map(Ty::kind) { - // usize or inferred - Some(ty::Uint(UintTy::Usize)) | None => { - span_lint_and_sugg( - cx, - EXPLICIT_COUNTER_LOOP, - span, - format!("the variable `{name}` is used as a loop counter"), - "consider using", - format!( - "{loop_label}for ({name}, {}) in {}.enumerate()", - snippet_with_applicability(cx, pat.span, "item", &mut applicability), - make_iterator_snippet(cx, arg, &mut applicability), - ), - applicability, - ); - return; - }, - Some(ty::Int(int_ty)) => int_ty.name_str(), - Some(ty::Uint(uint_ty)) => uint_ty.name_str(), - _ => return, - }; span_lint_and_then( cx, @@ -66,20 +45,52 @@ pub(super) fn check<'tcx>( span, format!("the variable `{name}` is used as a loop counter"), |diag| { + let pat_snippet = snippet_with_applicability(cx, pat.span, "item", &mut applicability); + let iter_snippet = make_iterator_snippet(cx, arg, &mut applicability); + let int_name = match ty.map(Ty::kind) { + Some(ty::Uint(UintTy::Usize)) | None => { + if is_zero { + diag.span_suggestion( + span, + "consider using", + format!( + "{loop_label}for ({name}, {pat_snippet}) in {iter_snippet}.enumerate()", + ), + applicability, + ); + return; + } + None + }, + Some(ty::Int(int_ty)) => Some(int_ty.name_str()), + Some(ty::Uint(uint_ty)) => Some(uint_ty.name_str()), + _ => None, + } + .filter(|_| is_integer_literal_untyped(initializer)); + + let initializer = Sugg::hir_from_snippet(cx, initializer, |span| { + let snippet = snippet_with_applicability(cx, span, "..", &mut applicability); + if let Some(int_name) = int_name { + return Cow::Owned(format!("{snippet}_{int_name}")); + } + snippet + }); + diag.span_suggestion( span, "consider using", format!( - "{loop_label}for ({name}, {}) in (0_{int_name}..).zip({})", - snippet_with_applicability(cx, pat.span, "item", &mut applicability), - make_iterator_snippet(cx, arg, &mut applicability), + "{loop_label}for ({name}, {pat_snippet}) in ({}).zip({iter_snippet})", + initializer.range(&EMPTY, RangeLimits::HalfOpen) ), applicability, ); - diag.note(format!( - "`{name}` is of type `{int_name}`, making it ineligible for `Iterator::enumerate`" - )); + if is_zero && let Some(int_name) = int_name { + diag.note(format!( + "`{name}` is of type `{int_name}`, making it ineligible for `Iterator::enumerate`" + )); + } }, ); } diff --git a/tests/ui/explicit_counter_loop.rs b/tests/ui/explicit_counter_loop.rs index ec4cecf377667..ebb261a0d97a7 100644 --- a/tests/ui/explicit_counter_loop.rs +++ b/tests/ui/explicit_counter_loop.rs @@ -33,10 +33,10 @@ fn main() { } let vec = [1, 2, 3, 4]; - // Potential false positives let mut _index = 0; _index = 1; for _v in &vec { + //~^ explicit_counter_loop _index += 1 } @@ -299,3 +299,21 @@ mod issue_13123 { } } } + +fn issue16612(v: Vec, s: i64) { + use std::hint::black_box; + + let mut i = 1; + for item in &v { + //~^ explicit_counter_loop + black_box((i, *item)); + i += 1; + } + + let mut j = s + 1; + for item in &v { + //~^ explicit_counter_loop + black_box((j, *item)); + j += 1; + } +} diff --git a/tests/ui/explicit_counter_loop.stderr b/tests/ui/explicit_counter_loop.stderr index a73516558c20c..7a83df05ec0a8 100644 --- a/tests/ui/explicit_counter_loop.stderr +++ b/tests/ui/explicit_counter_loop.stderr @@ -25,6 +25,12 @@ error: the variable `_index` is used as a loop counter LL | for _v in vec { | ^^^^^^^^^^^^^ help: consider using: `for (_index, _v) in vec.into_iter().enumerate()` +error: the variable `_index` is used as a loop counter + --> tests/ui/explicit_counter_loop.rs:38:5 + | +LL | for _v in &vec { + | ^^^^^^^^^^^^^^ help: consider using: `for (_index, _v) in (1..).zip(vec.iter())` + error: the variable `count` is used as a loop counter --> tests/ui/explicit_counter_loop.rs:118:9 | @@ -63,5 +69,17 @@ error: the variable `_index` is used as a loop counter LL | 'label: for v in vec { | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `'label: for (_index, v) in vec.into_iter().enumerate()` -error: aborting due to 10 previous errors +error: the variable `i` is used as a loop counter + --> tests/ui/explicit_counter_loop.rs:307:5 + | +LL | for item in &v { + | ^^^^^^^^^^^^^^ help: consider using: `for (i, item) in (1..).zip(v.iter())` + +error: the variable `j` is used as a loop counter + --> tests/ui/explicit_counter_loop.rs:314:5 + | +LL | for item in &v { + | ^^^^^^^^^^^^^^ help: consider using: `for (j, item) in (s + 1..).zip(v.iter())` + +error: aborting due to 13 previous errors From 5e242b66b978dbc31b0a13cc2cfbbc69ce3f6113 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Mon, 23 Feb 2026 20:05:19 +0100 Subject: [PATCH 56/57] Revert "Fix the compile-test tests when setting Cargo's `build.build-dir` setting to a path that's distinct from `target-dir`." This reverts commit 2737b26c2e5eee9229663552d0c70e2affbb8511 as it prevents Clippy tests from passing when running inside the compiler repository. --- tests/compile-test.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 6bdfb17481a39..a92bb8f5ca734 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -197,6 +197,10 @@ impl TestContext { defaults.set_custom("diagnostic-collector", collector); } config.with_args(&self.args); + let current_exe_path = env::current_exe().unwrap(); + let deps_path = current_exe_path.parent().unwrap(); + let profile_path = deps_path.parent().unwrap(); + config.program.args.extend( [ "--emit=metadata", @@ -220,7 +224,6 @@ impl TestContext { config.program.args.push(format!("--sysroot={sysroot}").into()); } - let profile_path = target_dir.join(env!("PROFILE")); config.program.program = profile_path.join(if cfg!(windows) { "clippy-driver.exe" } else { From f597b0fffeb6cd1678b5aa41ef26cac77d1cba35 Mon Sep 17 00:00:00 2001 From: Philipp Krones Date: Tue, 24 Feb 2026 15:11:25 +0100 Subject: [PATCH 57/57] allow clippy::collapsible_match in cg_gcc --- compiler/rustc_codegen_gcc/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index 7fd9c28c43f2b..afb07f57ab7a7 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -19,6 +19,7 @@ #![warn(unused_lifetimes)] #![deny(clippy::pattern_type_mismatch)] #![expect(clippy::uninlined_format_args)] +#![allow(clippy::collapsible_match)] // The rustc crates we need extern crate rustc_abi;