about summary refs log tree commit diff
path: root/clippy_lints
diff options
context:
space:
mode:
authorPhilipp Krones <hello@philkrones.com>2024-05-02 17:26:44 +0200
committerPhilipp Krones <hello@philkrones.com>2024-05-02 17:26:44 +0200
commit80c6f8ff7bf3d16f3d05171c7fe770c9164f0140 (patch)
treebe6b26c5acd97ef7a8292e67ba0423b0db9369ca /clippy_lints
parentfc15bc169e50801a7ba167042cd00c697cbd8baf (diff)
downloadrust-80c6f8ff7bf3d16f3d05171c7fe770c9164f0140.tar.gz
rust-80c6f8ff7bf3d16f3d05171c7fe770c9164f0140.zip
Merge commit '20b085d500dfba5afe0869707bf357af3afe20be' into clippy-subtree-update
Diffstat (limited to 'clippy_lints')
-rw-r--r--clippy_lints/Cargo.toml2
-rw-r--r--clippy_lints/src/assigning_clones.rs4
-rw-r--r--clippy_lints/src/box_default.rs4
-rw-r--r--clippy_lints/src/cargo/mod.rs2
-rw-r--r--clippy_lints/src/casts/cast_possible_truncation.rs7
-rw-r--r--clippy_lints/src/casts/mod.rs2
-rw-r--r--clippy_lints/src/collection_is_never_read.rs27
-rw-r--r--clippy_lints/src/copies.rs44
-rw-r--r--clippy_lints/src/dbg_macro.rs10
-rw-r--r--clippy_lints/src/dereference.rs4
-rw-r--r--clippy_lints/src/format_args.rs6
-rw-r--r--clippy_lints/src/implied_bounds_in_impls.rs2
-rw-r--r--clippy_lints/src/index_refutable_slice.rs9
-rw-r--r--clippy_lints/src/large_stack_arrays.rs65
-rw-r--r--clippy_lints/src/lib.rs2
-rw-r--r--clippy_lints/src/loops/unused_enumerate_index.rs7
-rw-r--r--clippy_lints/src/manual_assert.rs5
-rw-r--r--clippy_lints/src/manual_is_ascii_check.rs101
-rw-r--r--clippy_lints/src/matches/collapsible_match.rs23
-rw-r--r--clippy_lints/src/matches/mod.rs23
-rw-r--r--clippy_lints/src/matches/redundant_guards.rs27
-rw-r--r--clippy_lints/src/matches/single_match.rs2
-rw-r--r--clippy_lints/src/methods/filter_map.rs5
-rw-r--r--clippy_lints/src/methods/iter_overeager_cloned.rs3
-rw-r--r--clippy_lints/src/methods/mod.rs17
-rw-r--r--clippy_lints/src/methods/readonly_write_lock.rs7
-rw-r--r--clippy_lints/src/methods/single_char_insert_string.rs2
-rw-r--r--clippy_lints/src/methods/single_char_pattern.rs6
-rw-r--r--clippy_lints/src/methods/single_char_push_string.rs2
-rw-r--r--clippy_lints/src/methods/unused_enumerate_index.rs9
-rw-r--r--clippy_lints/src/methods/utils.rs8
-rw-r--r--clippy_lints/src/multiple_bound_locations.rs2
-rw-r--r--clippy_lints/src/mut_key.rs67
-rw-r--r--clippy_lints/src/needless_for_each.rs16
-rw-r--r--clippy_lints/src/needless_late_init.rs3
-rw-r--r--clippy_lints/src/needless_pass_by_ref_mut.rs26
-rw-r--r--clippy_lints/src/needless_pass_by_value.rs4
-rw-r--r--clippy_lints/src/non_canonical_impls.rs46
-rw-r--r--clippy_lints/src/non_copy_const.rs99
-rw-r--r--clippy_lints/src/operators/arithmetic_side_effects.rs53
-rw-r--r--clippy_lints/src/ptr.rs9
-rw-r--r--clippy_lints/src/question_mark.rs4
-rw-r--r--clippy_lints/src/repeat_vec_with_capacity.rs5
-rw-r--r--clippy_lints/src/size_of_ref.rs2
-rw-r--r--clippy_lints/src/slow_vector_initialization.rs6
-rw-r--r--clippy_lints/src/thread_local_initializer_can_be_made_const.rs36
-rw-r--r--clippy_lints/src/to_string_trait_impl.rs2
-rw-r--r--clippy_lints/src/trait_bounds.rs1
-rw-r--r--clippy_lints/src/types/mod.rs12
-rw-r--r--clippy_lints/src/utils/author.rs4
-rw-r--r--clippy_lints/src/vec.rs6
-rw-r--r--clippy_lints/src/vec_init_then_push.rs4
52 files changed, 484 insertions, 360 deletions
diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml
index 1d954607eee..5e3a119337c 100644
--- a/clippy_lints/Cargo.toml
+++ b/clippy_lints/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "clippy_lints"
-version = "0.1.79"
+version = "0.1.80"
 description = "A bunch of helpful lints to avoid common pitfalls in Rust"
 repository = "https://github.com/rust-lang/rust-clippy"
 readme = "README.md"
diff --git a/clippy_lints/src/assigning_clones.rs b/clippy_lints/src/assigning_clones.rs
index dc7f44af2b7..f0dafb1ae0d 100644
--- a/clippy_lints/src/assigning_clones.rs
+++ b/clippy_lints/src/assigning_clones.rs
@@ -45,7 +45,7 @@ declare_clippy_lint! {
     ///     a.clone_from(&b);
     /// }
     /// ```
-    #[clippy::version = "1.77.0"]
+    #[clippy::version = "1.78.0"]
     pub ASSIGNING_CLONES,
     perf,
     "assigning the result of cloning may be inefficient"
@@ -153,7 +153,7 @@ fn extract_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<
 fn is_ok_to_suggest<'tcx>(cx: &LateContext<'tcx>, lhs: &Expr<'tcx>, call: &CallCandidate<'tcx>, msrv: &Msrv) -> bool {
     // For calls to .to_owned we suggest using .clone_into(), which was only stablilized in 1.63.
     // If the current MSRV is below that, don't suggest the lint.
-    if !msrv.meets(msrvs::ASSIGNING_CLONES) && matches!(call.target, TargetTrait::ToOwned) {
+    if !msrv.meets(msrvs::CLONE_INTO) && matches!(call.target, TargetTrait::ToOwned) {
         return false;
     }
 
diff --git a/clippy_lints/src/box_default.rs b/clippy_lints/src/box_default.rs
index 4062212f408..8459f051d3d 100644
--- a/clippy_lints/src/box_default.rs
+++ b/clippy_lints/src/box_default.rs
@@ -121,9 +121,9 @@ fn given_type(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
             if let Some(index) = args.iter().position(|arg| arg.hir_id == expr.hir_id)
                 && let Some(sig) = expr_sig(cx, path)
                 && let Some(input) = sig.input(index)
-                && !cx.typeck_results().expr_ty_adjusted(expr).boxed_ty().is_trait()
+                && let Some(input_ty) = input.no_bound_vars()
             {
-                input.no_bound_vars().is_some()
+                input_ty == cx.typeck_results().expr_ty_adjusted(expr)
             } else {
                 false
             }
diff --git a/clippy_lints/src/cargo/mod.rs b/clippy_lints/src/cargo/mod.rs
index ca7fa4e5a41..593bc6c81ee 100644
--- a/clippy_lints/src/cargo/mod.rs
+++ b/clippy_lints/src/cargo/mod.rs
@@ -197,7 +197,7 @@ declare_clippy_lint! {
     /// pedantic = { level = "warn", priority = -1 }
     /// similar_names = "allow"
     /// ```
-    #[clippy::version = "1.76.0"]
+    #[clippy::version = "1.78.0"]
     pub LINT_GROUPS_PRIORITY,
     correctness,
     "a lint group in `Cargo.toml` at the same priority as a lint"
diff --git a/clippy_lints/src/casts/cast_possible_truncation.rs b/clippy_lints/src/casts/cast_possible_truncation.rs
index dbfa8e1ee91..7c5acd1a678 100644
--- a/clippy_lints/src/casts/cast_possible_truncation.rs
+++ b/clippy_lints/src/casts/cast_possible_truncation.rs
@@ -40,9 +40,14 @@ fn apply_reductions(cx: &LateContext<'_>, nbits: u64, expr: &Expr<'_>, signed: b
                     get_constant_bits(cx, right).map_or(0, |b| b.saturating_sub(1))
                 })
             },
-            BinOpKind::Rem | BinOpKind::BitAnd => get_constant_bits(cx, right)
+            BinOpKind::Rem => get_constant_bits(cx, right)
                 .unwrap_or(u64::MAX)
                 .min(apply_reductions(cx, nbits, left, signed)),
+            BinOpKind::BitAnd => get_constant_bits(cx, right)
+                .unwrap_or(u64::MAX)
+                .min(get_constant_bits(cx, left).unwrap_or(u64::MAX))
+                .min(apply_reductions(cx, nbits, right, signed))
+                .min(apply_reductions(cx, nbits, left, signed)),
             BinOpKind::Shr => apply_reductions(cx, nbits, left, signed)
                 .saturating_sub(constant_int(cx, right).map_or(0, |s| u64::try_from(s).unwrap_or_default())),
             _ => nbits,
diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs
index d14898a8196..bd2c96f01f6 100644
--- a/clippy_lints/src/casts/mod.rs
+++ b/clippy_lints/src/casts/mod.rs
@@ -708,7 +708,7 @@ declare_clippy_lint! {
     /// let a_ref = &1;
     /// let a_ptr = std::ptr::from_ref(a_ref);
     /// ```
-    #[clippy::version = "1.77.0"]
+    #[clippy::version = "1.78.0"]
     pub REF_AS_PTR,
     pedantic,
     "using `as` to cast a reference to pointer"
diff --git a/clippy_lints/src/collection_is_never_read.rs b/clippy_lints/src/collection_is_never_read.rs
index 6942ca53640..70856b80881 100644
--- a/clippy_lints/src/collection_is_never_read.rs
+++ b/clippy_lints/src/collection_is_never_read.rs
@@ -1,9 +1,9 @@
 use clippy_utils::diagnostics::span_lint;
 use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
-use clippy_utils::visitors::for_each_expr_with_closures;
+use clippy_utils::visitors::{for_each_expr_with_closures, Visitable};
 use clippy_utils::{get_enclosing_block, path_to_local_id};
 use core::ops::ControlFlow;
-use rustc_hir::{Block, ExprKind, HirId, LangItem, LetStmt, Node, PatKind};
+use rustc_hir::{Body, ExprKind, HirId, LangItem, LetStmt, Node, PatKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
 use rustc_span::symbol::sym;
@@ -77,7 +77,7 @@ fn match_acceptable_type(cx: &LateContext<'_>, local: &LetStmt<'_>, collections:
         || is_type_lang_item(cx, ty, LangItem::String)
 }
 
-fn has_no_read_access<'tcx>(cx: &LateContext<'tcx>, id: HirId, block: &'tcx Block<'tcx>) -> bool {
+fn has_no_read_access<'tcx, T: Visitable<'tcx>>(cx: &LateContext<'tcx>, id: HirId, block: T) -> bool {
     let mut has_access = false;
     let mut has_read_access = false;
 
@@ -109,11 +109,30 @@ fn has_no_read_access<'tcx>(cx: &LateContext<'tcx>, id: HirId, block: &'tcx Bloc
         // traits (identified as local, based on the orphan rule), pessimistically assume that they might
         // have side effects, so consider them a read.
         if let Node::Expr(parent) = cx.tcx.parent_hir_node(expr.hir_id)
-            && let ExprKind::MethodCall(_, receiver, _, _) = parent.kind
+            && let ExprKind::MethodCall(_, receiver, args, _) = parent.kind
             && path_to_local_id(receiver, id)
             && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(parent.hir_id)
             && !method_def_id.is_local()
         {
+            // If this "official" method takes closures,
+            // it has read access if one of the closures has read access.
+            //
+            // items.retain(|item| send_item(item).is_ok());
+            let is_read_in_closure_arg = args.iter().any(|arg| {
+                if let ExprKind::Closure(closure) = arg.kind
+                    // To keep things simple, we only check the first param to see if its read.
+                    && let Body { params: [param, ..], value } = cx.tcx.hir().body(closure.body)
+                {
+                    !has_no_read_access(cx, param.hir_id, *value)
+                } else {
+                    false
+                }
+            });
+            if is_read_in_closure_arg {
+                has_read_access = true;
+                return ControlFlow::Break(());
+            }
+
             // The method call is a statement, so the return value is not used. That's not a read access:
             //
             // id.foo(args);
diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs
index acdcb54be27..ccf1d9d6f8c 100644
--- a/clippy_lints/src/copies.rs
+++ b/clippy_lints/src/copies.rs
@@ -1,15 +1,14 @@
 use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then};
 use clippy_utils::source::{first_line_of_span, indent_of, reindent_multiline, snippet, snippet_opt};
-use clippy_utils::ty::{is_interior_mut_ty, needs_ordered_drop};
+use clippy_utils::ty::{needs_ordered_drop, InteriorMut};
 use clippy_utils::visitors::for_each_expr;
 use clippy_utils::{
-    capture_local_usage, def_path_def_ids, eq_expr_value, find_binding_init, get_enclosing_block, hash_expr, hash_stmt,
-    if_sequence, is_else_clause, is_lint_allowed, path_to_local, search_same, ContainsName, HirEqInterExpr, SpanlessEq,
+    capture_local_usage, eq_expr_value, find_binding_init, get_enclosing_block, hash_expr, hash_stmt, if_sequence,
+    is_else_clause, is_lint_allowed, path_to_local, search_same, ContainsName, HirEqInterExpr, SpanlessEq,
 };
 use core::iter;
 use core::ops::ControlFlow;
 use rustc_errors::Applicability;
-use rustc_hir::def_id::DefIdSet;
 use rustc_hir::{intravisit, BinOpKind, Block, Expr, ExprKind, HirId, HirIdSet, Stmt, StmtKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::impl_lint_pass;
@@ -159,40 +158,36 @@ declare_clippy_lint! {
     "`if` statement with shared code in all blocks"
 }
 
-pub struct CopyAndPaste {
+pub struct CopyAndPaste<'tcx> {
     ignore_interior_mutability: Vec<String>,
-    ignored_ty_ids: DefIdSet,
+    interior_mut: InteriorMut<'tcx>,
 }
 
-impl CopyAndPaste {
+impl CopyAndPaste<'_> {
     pub fn new(ignore_interior_mutability: Vec<String>) -> Self {
         Self {
             ignore_interior_mutability,
-            ignored_ty_ids: DefIdSet::new(),
+            interior_mut: InteriorMut::default(),
         }
     }
 }
 
-impl_lint_pass!(CopyAndPaste => [
+impl_lint_pass!(CopyAndPaste<'_> => [
     IFS_SAME_COND,
     SAME_FUNCTIONS_IN_IF_CONDITION,
     IF_SAME_THEN_ELSE,
     BRANCHES_SHARING_CODE
 ]);
 
-impl<'tcx> LateLintPass<'tcx> for CopyAndPaste {
+impl<'tcx> LateLintPass<'tcx> for CopyAndPaste<'tcx> {
     fn check_crate(&mut self, cx: &LateContext<'tcx>) {
-        for ignored_ty in &self.ignore_interior_mutability {
-            let path: Vec<&str> = ignored_ty.split("::").collect();
-            for id in def_path_def_ids(cx, path.as_slice()) {
-                self.ignored_ty_ids.insert(id);
-            }
-        }
+        self.interior_mut = InteriorMut::new(cx, &self.ignore_interior_mutability);
     }
+
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         if !expr.span.from_expansion() && matches!(expr.kind, ExprKind::If(..)) && !is_else_clause(cx.tcx, expr) {
             let (conds, blocks) = if_sequence(expr);
-            lint_same_cond(cx, &conds, &self.ignored_ty_ids);
+            lint_same_cond(cx, &conds, &mut self.interior_mut);
             lint_same_fns_in_if_cond(cx, &conds);
             let all_same =
                 !is_lint_allowed(cx, IF_SAME_THEN_ELSE, expr.hir_id) && lint_if_same_then_else(cx, &conds, &blocks);
@@ -570,13 +565,14 @@ fn check_for_warn_of_moved_symbol(cx: &LateContext<'_>, symbols: &[(HirId, Symbo
     })
 }
 
-fn method_caller_is_mutable(cx: &LateContext<'_>, caller_expr: &Expr<'_>, ignored_ty_ids: &DefIdSet) -> bool {
+fn method_caller_is_mutable<'tcx>(
+    cx: &LateContext<'tcx>,
+    caller_expr: &Expr<'_>,
+    interior_mut: &mut InteriorMut<'tcx>,
+) -> bool {
     let caller_ty = cx.typeck_results().expr_ty(caller_expr);
-    // Check if given type has inner mutability and was not set to ignored by the configuration
-    let is_inner_mut_ty = is_interior_mut_ty(cx, caller_ty)
-        && !matches!(caller_ty.ty_adt_def(), Some(adt) if ignored_ty_ids.contains(&adt.did()));
 
-    is_inner_mut_ty
+    interior_mut.is_interior_mut_ty(cx, caller_ty)
         || caller_ty.is_mutable_ptr()
         // `find_binding_init` will return the binding iff its not mutable
         || path_to_local(caller_expr)
@@ -585,7 +581,7 @@ fn method_caller_is_mutable(cx: &LateContext<'_>, caller_expr: &Expr<'_>, ignore
 }
 
 /// Implementation of `IFS_SAME_COND`.
-fn lint_same_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>], ignored_ty_ids: &DefIdSet) {
+fn lint_same_cond<'tcx>(cx: &LateContext<'tcx>, conds: &[&Expr<'_>], interior_mut: &mut InteriorMut<'tcx>) {
     for (i, j) in search_same(
         conds,
         |e| hash_expr(cx, e),
@@ -593,7 +589,7 @@ fn lint_same_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>], ignored_ty_ids: &De
             // Ignore eq_expr side effects iff one of the expression kind is a method call
             // and the caller is not a mutable, including inner mutable type.
             if let ExprKind::MethodCall(_, caller, _, _) = lhs.kind {
-                if method_caller_is_mutable(cx, caller, ignored_ty_ids) {
+                if method_caller_is_mutable(cx, caller, interior_mut) {
                     false
                 } else {
                     SpanlessEq::new(cx).eq_expr(lhs, rhs)
diff --git a/clippy_lints/src/dbg_macro.rs b/clippy_lints/src/dbg_macro.rs
index e2296767431..db593726604 100644
--- a/clippy_lints/src/dbg_macro.rs
+++ b/clippy_lints/src/dbg_macro.rs
@@ -1,10 +1,10 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_in_test;
 use clippy_utils::macros::{macro_backtrace, MacroCall};
 use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{is_in_cfg_test, is_in_test_function};
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind, HirId, Node};
+use rustc_hir::{Expr, ExprKind, Node};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_session::impl_lint_pass;
@@ -63,7 +63,7 @@ impl LateLintPass<'_> for DbgMacro {
             !in_external_macro(cx.sess(), macro_call.span) &&
             self.checked_dbg_call_site.insert(macro_call.span) &&
             // allows `dbg!` in test code if allow-dbg-in-test is set to true in clippy.toml
-            !(self.allow_dbg_in_tests && is_in_test(cx, expr.hir_id))
+            !(self.allow_dbg_in_tests && is_in_test(cx.tcx, expr.hir_id))
         {
             let mut applicability = Applicability::MachineApplicable;
 
@@ -129,10 +129,6 @@ impl LateLintPass<'_> for DbgMacro {
     }
 }
 
-fn is_in_test(cx: &LateContext<'_>, hir_id: HirId) -> bool {
-    is_in_test_function(cx.tcx, hir_id) || is_in_cfg_test(cx.tcx, hir_id)
-}
-
 fn first_dbg_macro_in_expansion(cx: &LateContext<'_>, span: Span) -> Option<MacroCall> {
     macro_backtrace(span).find(|mc| cx.tcx.is_diagnostic_item(sym::dbg_macro, mc.def_id))
 }
diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs
index 89e2b344968..b936b28470b 100644
--- a/clippy_lints/src/dereference.rs
+++ b/clippy_lints/src/dereference.rs
@@ -9,8 +9,8 @@ use rustc_data_structures::fx::FxIndexMap;
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::{walk_ty, Visitor};
 use rustc_hir::{
-    self as hir, BindingMode, Body, BodyId, BorrowKind, Expr, ExprKind, HirId, MatchSource, Mutability, Node,
-    Pat, PatKind, Path, QPath, TyKind, UnOp,
+    self as hir, BindingMode, Body, BodyId, BorrowKind, Expr, ExprKind, HirId, MatchSource, Mutability, Node, Pat,
+    PatKind, Path, QPath, TyKind, UnOp,
 };
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs
index 80db617c639..003a9995c15 100644
--- a/clippy_lints/src/format_args.rs
+++ b/clippy_lints/src/format_args.rs
@@ -4,7 +4,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::is_diag_trait_item;
 use clippy_utils::macros::{
     find_format_arg_expr, find_format_args, format_arg_removal_span, format_placeholder_format_span, is_assert_macro,
-    is_format_macro, is_panic, root_macro_call, root_macro_call_first_node, FormatParamUsage, MacroCall,
+    is_format_macro, is_panic, matching_root_macro_call, root_macro_call_first_node, FormatParamUsage, MacroCall,
 };
 use clippy_utils::source::snippet_opt;
 use clippy_utils::ty::{implements_trait, is_type_lang_item};
@@ -271,9 +271,7 @@ impl<'a, 'tcx> FormatArgsExpr<'a, 'tcx> {
                     let mut suggest_format = |spec| {
                         let message = format!("for the {spec} to apply consider using `format!()`");
 
-                        if let Some(mac_call) = root_macro_call(arg_span)
-                            && self.cx.tcx.is_diagnostic_item(sym::format_args_macro, mac_call.def_id)
-                        {
+                        if let Some(mac_call) = matching_root_macro_call(self.cx, arg_span, sym::format_args_macro) {
                             diag.span_suggestion(
                                 self.cx.sess().source_map().span_until_char(mac_call.span, '!'),
                                 message,
diff --git a/clippy_lints/src/implied_bounds_in_impls.rs b/clippy_lints/src/implied_bounds_in_impls.rs
index 7b97fc15caa..3bf8d618955 100644
--- a/clippy_lints/src/implied_bounds_in_impls.rs
+++ b/clippy_lints/src/implied_bounds_in_impls.rs
@@ -24,7 +24,7 @@ declare_clippy_lint! {
     ///
     /// ### Limitations
     /// This lint does not check for implied bounds transitively. Meaning that
-    /// it does't check for implied bounds from supertraits of supertraits
+    /// it doesn't check for implied bounds from supertraits of supertraits
     /// (e.g. `trait A {} trait B: A {} trait C: B {}`, then having an `fn() -> impl A + C`)
     ///
     /// ### Example
diff --git a/clippy_lints/src/index_refutable_slice.rs b/clippy_lints/src/index_refutable_slice.rs
index a4c3b06046e..128461ce7bc 100644
--- a/clippy_lints/src/index_refutable_slice.rs
+++ b/clippy_lints/src/index_refutable_slice.rs
@@ -93,12 +93,9 @@ fn find_slice_values(cx: &LateContext<'_>, pat: &hir::Pat<'_>) -> FxIndexMap<Hir
     let mut slices: FxIndexMap<HirId, SliceLintInformation> = FxIndexMap::default();
     pat.walk_always(|pat| {
         // We'll just ignore mut and ref mut for simplicity sake right now
-        if let hir::PatKind::Binding(
-            hir::BindingMode(by_ref, hir::Mutability::Not),
-            value_hir_id,
-            ident,
-            sub_pat,
-        ) = pat.kind && by_ref != hir::ByRef::Yes(hir::Mutability::Mut)
+        if let hir::PatKind::Binding(hir::BindingMode(by_ref, hir::Mutability::Not), value_hir_id, ident, sub_pat) =
+            pat.kind
+            && by_ref != hir::ByRef::Yes(hir::Mutability::Mut)
         {
             // This block catches bindings with sub patterns. It would be hard to build a correct suggestion
             // for them and it's likely that the user knows what they are doing in such a case.
diff --git a/clippy_lints/src/large_stack_arrays.rs b/clippy_lints/src/large_stack_arrays.rs
index afcb6745947..208d1bb6e68 100644
--- a/clippy_lints/src/large_stack_arrays.rs
+++ b/clippy_lints/src/large_stack_arrays.rs
@@ -1,10 +1,13 @@
-use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::is_from_proc_macro;
+use clippy_utils::macros::macro_backtrace;
 use clippy_utils::source::snippet;
-use rustc_hir::{Expr, ExprKind, Item, ItemKind, Node};
+use rustc_hir::{ArrayLen, Expr, ExprKind, Item, ItemKind, Node};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::layout::LayoutOf;
 use rustc_middle::ty::{self, ConstKind};
 use rustc_session::impl_lint_pass;
+use rustc_span::{sym, Span};
 
 declare_clippy_lint! {
     /// ### What it does
@@ -25,20 +28,41 @@ declare_clippy_lint! {
 
 pub struct LargeStackArrays {
     maximum_allowed_size: u128,
+    prev_vec_macro_callsite: Option<Span>,
 }
 
 impl LargeStackArrays {
     #[must_use]
     pub fn new(maximum_allowed_size: u128) -> Self {
-        Self { maximum_allowed_size }
+        Self {
+            maximum_allowed_size,
+            prev_vec_macro_callsite: None,
+        }
+    }
+
+    /// Check if the given span of an expr is already in a `vec!` call.
+    fn is_from_vec_macro(&mut self, cx: &LateContext<'_>, span: Span) -> bool {
+        // First, we check if this is span is within the last encountered `vec!` macro's root callsite.
+        self.prev_vec_macro_callsite
+            .is_some_and(|vec_mac| vec_mac.contains(span))
+            || {
+                // Then, we try backtracking the macro expansions, to see if there's a `vec!` macro,
+                // and update the `prev_vec_macro_callsite`.
+                let res = macro_backtrace(span).any(|mac| cx.tcx.is_diagnostic_item(sym::vec_macro, mac.def_id));
+                if res {
+                    self.prev_vec_macro_callsite = Some(span.source_callsite());
+                }
+                res
+            }
     }
 }
 
 impl_lint_pass!(LargeStackArrays => [LARGE_STACK_ARRAYS]);
 
 impl<'tcx> LateLintPass<'tcx> for LargeStackArrays {
-    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
         if let ExprKind::Repeat(_, _) | ExprKind::Array(_) = expr.kind
+            && !self.is_from_vec_macro(cx, expr.span)
             && let ty::Array(element_type, cst) = cx.typeck_results().expr_ty(expr).kind()
             && let ConstKind::Value(ty::ValTree::Leaf(element_count)) = cst.kind()
             && let Ok(element_count) = element_count.try_to_target_usize(cx.tcx)
@@ -54,7 +78,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeStackArrays {
             })
             && self.maximum_allowed_size < u128::from(element_count) * u128::from(element_size)
         {
-            span_lint_and_help(
+            span_lint_and_then(
                 cx,
                 LARGE_STACK_ARRAYS,
                 expr.span,
@@ -62,12 +86,33 @@ impl<'tcx> LateLintPass<'tcx> for LargeStackArrays {
                     "allocating a local array larger than {} bytes",
                     self.maximum_allowed_size
                 ),
-                None,
-                format!(
-                    "consider allocating on the heap with `vec!{}.into_boxed_slice()`",
-                    snippet(cx, expr.span, "[...]")
-                ),
+                |diag| {
+                    if !might_be_expanded(cx, expr) {
+                        diag.help(format!(
+                            "consider allocating on the heap with `vec!{}.into_boxed_slice()`",
+                            snippet(cx, expr.span, "[...]")
+                        ));
+                    }
+                },
             );
         }
     }
 }
+
+/// Only giving help messages if the expr does not contains macro expanded codes.
+fn might_be_expanded<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> bool {
+    /// Check if the span of `ArrayLen` of a repeat expression is within the expr's span,
+    /// if not, meaning this repeat expr is definitely from some proc-macro.
+    ///
+    /// This is a fail-safe to a case where even the `is_from_proc_macro` is unable to determain the
+    /// correct result.
+    fn repeat_expr_might_be_expanded<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> bool {
+        let ExprKind::Repeat(_, ArrayLen::Body(anon_const)) = expr.kind else {
+            return false;
+        };
+        let len_span = cx.tcx.def_span(anon_const.def_id);
+        !expr.span.contains(len_span)
+    }
+
+    expr.span.from_expansion() || is_from_proc_macro(cx, expr) || repeat_expr_might_be_expanded(cx, expr)
+}
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index e2aac58bf97..2c44c3881aa 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -535,6 +535,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
         allow_print_in_tests,
         allow_private_module_inception,
         allow_unwrap_in_tests,
+        allow_useless_vec_in_tests,
         ref allowed_dotfiles,
         ref allowed_idents_below_min_chars,
         ref allowed_scripts,
@@ -754,6 +755,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
             too_large_for_stack,
             msrv: msrv(),
             span_to_lint_map: BTreeMap::new(),
+            allow_in_test: allow_useless_vec_in_tests,
         })
     });
     store.register_late_pass(|_| Box::new(panic_unimplemented::PanicUnimplemented));
diff --git a/clippy_lints/src/loops/unused_enumerate_index.rs b/clippy_lints/src/loops/unused_enumerate_index.rs
index 31f0f1cfeba..40ccfec02be 100644
--- a/clippy_lints/src/loops/unused_enumerate_index.rs
+++ b/clippy_lints/src/loops/unused_enumerate_index.rs
@@ -1,11 +1,12 @@
 use super::UNUSED_ENUMERATE_INDEX;
 use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
 use clippy_utils::source::snippet;
-use clippy_utils::{match_def_path, pat_is_wild, sugg};
+use clippy_utils::{pat_is_wild, sugg};
 use rustc_hir::def::DefKind;
 use rustc_hir::{Expr, ExprKind, Pat, PatKind};
 use rustc_lint::LateContext;
 use rustc_middle::ty;
+use rustc_span::sym;
 
 /// Checks for the `UNUSED_ENUMERATE_INDEX` lint.
 ///
@@ -16,9 +17,9 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>, arg: &Expr<'_
         && let ty = cx.typeck_results().expr_ty(arg)
         && pat_is_wild(cx, &index.kind, body)
         && let ty::Adt(base, _) = *ty.kind()
-        && match_def_path(cx, base.did(), &clippy_utils::paths::CORE_ITER_ENUMERATE_STRUCT)
+        && cx.tcx.is_diagnostic_item(sym::Enumerate, base.did())
         && let Some((DefKind::AssocFn, call_id)) = cx.typeck_results().type_dependent_def(arg.hir_id)
-        && match_def_path(cx, call_id, &clippy_utils::paths::CORE_ITER_ENUMERATE_METHOD)
+        && cx.tcx.is_diagnostic_item(sym::enumerate_method, call_id)
     {
         span_lint_and_then(
             cx,
diff --git a/clippy_lints/src/manual_assert.rs b/clippy_lints/src/manual_assert.rs
index 76edbe8b755..d76b94eba23 100644
--- a/clippy_lints/src/manual_assert.rs
+++ b/clippy_lints/src/manual_assert.rs
@@ -1,12 +1,11 @@
 use crate::rustc_lint::LintContext;
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::macros::root_macro_call;
+use clippy_utils::macros::{is_panic, root_macro_call};
 use clippy_utils::{is_else_clause, is_parent_stmt, peel_blocks_with_stmt, span_extract_comment, sugg};
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, UnOp};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
-use rustc_span::sym;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -42,7 +41,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualAssert {
             && !expr.span.from_expansion()
             && let then = peel_blocks_with_stmt(then)
             && let Some(macro_call) = root_macro_call(then.span)
-            && cx.tcx.item_name(macro_call.def_id) == sym::panic
+            && is_panic(cx, macro_call.def_id)
             && !cx.tcx.sess.source_map().is_multiline(cond.span)
             && let Ok(panic_snippet) = cx.sess().source_map().span_to_snippet(macro_call.span)
             && let Some(panic_snippet) = panic_snippet.strip_suffix(')')
diff --git a/clippy_lints/src/manual_is_ascii_check.rs b/clippy_lints/src/manual_is_ascii_check.rs
index 338a299740a..6f6ba1852a6 100644
--- a/clippy_lints/src/manual_is_ascii_check.rs
+++ b/clippy_lints/src/manual_is_ascii_check.rs
@@ -1,15 +1,15 @@
 use clippy_config::msrvs::{self, Msrv};
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::macros::root_macro_call;
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::macros::matching_root_macro_call;
 use clippy_utils::sugg::Sugg;
-use clippy_utils::{higher, in_constant};
+use clippy_utils::{higher, in_constant, path_to_local, peel_ref_operators};
 use rustc_ast::ast::RangeLimits;
 use rustc_ast::LitKind::{Byte, Char};
 use rustc_errors::Applicability;
-use rustc_hir::{BorrowKind, Expr, ExprKind, PatKind, RangeEnd};
+use rustc_hir::{Expr, ExprKind, Node, Param, PatKind, RangeEnd};
 use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty;
 use rustc_session::impl_lint_pass;
-use rustc_span::def_id::DefId;
 use rustc_span::{sym, Span};
 
 declare_clippy_lint! {
@@ -97,12 +97,10 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck {
             return;
         }
 
-        if let Some(macro_call) = root_macro_call(expr.span)
-            && is_matches_macro(cx, macro_call.def_id)
-        {
+        if let Some(macro_call) = matching_root_macro_call(cx, expr.span, sym::matches_macro) {
             if let ExprKind::Match(recv, [arm, ..], _) = expr.kind {
                 let range = check_pat(&arm.pat.kind);
-                check_is_ascii(cx, macro_call.span, recv, &range);
+                check_is_ascii(cx, macro_call.span, recv, &range, None);
             }
         } else if let ExprKind::MethodCall(path, receiver, [arg], ..) = expr.kind
             && path.ident.name == sym!(contains)
@@ -111,42 +109,67 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck {
                 end: Some(end),
                 limits: RangeLimits::Closed,
             }) = higher::Range::hir(receiver)
+            && !matches!(cx.typeck_results().expr_ty(arg).peel_refs().kind(), ty::Param(_))
         {
+            let arg = peel_ref_operators(cx, arg);
+            let ty_sugg = get_ty_sugg(cx, arg, start);
             let range = check_range(start, end);
-            if let ExprKind::AddrOf(BorrowKind::Ref, _, e) = arg.kind {
-                check_is_ascii(cx, expr.span, e, &range);
-            } else {
-                check_is_ascii(cx, expr.span, arg, &range);
-            }
+            check_is_ascii(cx, expr.span, arg, &range, ty_sugg);
         }
     }
 
     extract_msrv_attr!(LateContext);
 }
 
-fn check_is_ascii(cx: &LateContext<'_>, span: Span, recv: &Expr<'_>, range: &CharRange) {
-    if let Some(sugg) = match range {
-        CharRange::UpperChar => Some("is_ascii_uppercase"),
-        CharRange::LowerChar => Some("is_ascii_lowercase"),
-        CharRange::FullChar => Some("is_ascii_alphabetic"),
-        CharRange::Digit => Some("is_ascii_digit"),
-        CharRange::HexDigit => Some("is_ascii_hexdigit"),
-        CharRange::Otherwise | CharRange::LowerHexLetter | CharRange::UpperHexLetter => None,
-    } {
-        let default_snip = "..";
-        let mut app = Applicability::MachineApplicable;
-        let recv = Sugg::hir_with_context(cx, recv, span.ctxt(), default_snip, &mut app).maybe_par();
+fn get_ty_sugg(cx: &LateContext<'_>, arg: &Expr<'_>, bound_expr: &Expr<'_>) -> Option<(Span, &'static str)> {
+    if let ExprKind::Lit(lit) = bound_expr.kind
+        && let local_hid = path_to_local(arg)?
+        && let Node::Param(Param { ty_span, span, .. }) = cx.tcx.parent_hir_node(local_hid)
+        // `ty_span` and `span` are the same for inferred type, thus a type suggestion must be given
+        && ty_span == span
+    {
+        let ty_str = match lit.node {
+            Char(_) => "char",
+            Byte(_) => "u8",
+            _ => return None,
+        };
+        return Some((*ty_span, ty_str));
+    }
+    None
+}
 
-        span_lint_and_sugg(
-            cx,
-            MANUAL_IS_ASCII_CHECK,
-            span,
-            "manual check for common ascii range",
-            "try",
-            format!("{recv}.{sugg}()"),
-            app,
-        );
+fn check_is_ascii(
+    cx: &LateContext<'_>,
+    span: Span,
+    recv: &Expr<'_>,
+    range: &CharRange,
+    ty_sugg: Option<(Span, &'_ str)>,
+) {
+    let sugg = match range {
+        CharRange::UpperChar => "is_ascii_uppercase",
+        CharRange::LowerChar => "is_ascii_lowercase",
+        CharRange::FullChar => "is_ascii_alphabetic",
+        CharRange::Digit => "is_ascii_digit",
+        CharRange::HexDigit => "is_ascii_hexdigit",
+        CharRange::Otherwise | CharRange::LowerHexLetter | CharRange::UpperHexLetter => return,
+    };
+    let default_snip = "..";
+    let mut app = Applicability::MachineApplicable;
+    let recv = Sugg::hir_with_context(cx, recv, span.ctxt(), default_snip, &mut app).maybe_par();
+    let mut suggestion = vec![(span, format!("{recv}.{sugg}()"))];
+    if let Some((ty_span, ty_str)) = ty_sugg {
+        suggestion.push((ty_span, format!("{recv}: {ty_str}")));
     }
+
+    span_lint_and_then(
+        cx,
+        MANUAL_IS_ASCII_CHECK,
+        span,
+        "manual check for common ascii range",
+        |diag| {
+            diag.multipart_suggestion("try", suggestion, app);
+        },
+    );
 }
 
 fn check_pat(pat_kind: &PatKind<'_>) -> CharRange {
@@ -187,11 +210,3 @@ fn check_range(start: &Expr<'_>, end: &Expr<'_>) -> CharRange {
         CharRange::Otherwise
     }
 }
-
-fn is_matches_macro(cx: &LateContext<'_>, macro_def_id: DefId) -> bool {
-    if let Some(name) = cx.tcx.get_diagnostic_name(macro_def_id) {
-        return sym::matches_macro == name;
-    }
-
-    false
-}
diff --git a/clippy_lints/src/matches/collapsible_match.rs b/clippy_lints/src/matches/collapsible_match.rs
index 6746920edc5..90cfdecc199 100644
--- a/clippy_lints/src/matches/collapsible_match.rs
+++ b/clippy_lints/src/matches/collapsible_match.rs
@@ -1,3 +1,4 @@
+use clippy_config::msrvs::Msrv;
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::higher::IfLetOrMatch;
 use clippy_utils::source::snippet;
@@ -11,12 +12,12 @@ use rustc_hir::{Arm, Expr, HirId, Pat, PatKind};
 use rustc_lint::LateContext;
 use rustc_span::Span;
 
-use super::COLLAPSIBLE_MATCH;
+use super::{pat_contains_disallowed_or, COLLAPSIBLE_MATCH};
 
-pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) {
+pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], msrv: &Msrv) {
     if let Some(els_arm) = arms.iter().rfind(|arm| arm_is_wild_like(cx, arm)) {
         for arm in arms {
-            check_arm(cx, true, arm.pat, arm.body, arm.guard, Some(els_arm.body));
+            check_arm(cx, true, arm.pat, arm.body, arm.guard, Some(els_arm.body), msrv);
         }
     }
 }
@@ -26,8 +27,9 @@ pub(super) fn check_if_let<'tcx>(
     pat: &'tcx Pat<'_>,
     body: &'tcx Expr<'_>,
     else_expr: Option<&'tcx Expr<'_>>,
+    msrv: &Msrv,
 ) {
-    check_arm(cx, false, pat, body, None, else_expr);
+    check_arm(cx, false, pat, body, None, else_expr, msrv);
 }
 
 fn check_arm<'tcx>(
@@ -37,6 +39,7 @@ fn check_arm<'tcx>(
     outer_then_body: &'tcx Expr<'tcx>,
     outer_guard: Option<&'tcx Expr<'tcx>>,
     outer_else_body: Option<&'tcx Expr<'tcx>>,
+    msrv: &Msrv,
 ) {
     let inner_expr = peel_blocks_with_stmt(outer_then_body);
     if let Some(inner) = IfLetOrMatch::parse(cx, inner_expr)
@@ -57,7 +60,7 @@ fn check_arm<'tcx>(
         // match expression must be a local binding
         // match <local> { .. }
         && let Some(binding_id) = path_to_local(peel_ref_operators(cx, inner_scrutinee))
-        && !pat_contains_or(inner_then_pat)
+        && !pat_contains_disallowed_or(inner_then_pat, msrv)
         // the binding must come from the pattern of the containing match arm
         // ..<local>.. => match <local> { .. }
         && let (Some(binding_span), is_innermost_parent_pat_struct)
@@ -142,13 +145,3 @@ fn find_pat_binding_and_is_innermost_parent_pat_struct(pat: &Pat<'_>, hir_id: Hi
     });
     (span, is_innermost_parent_pat_struct)
 }
-
-fn pat_contains_or(pat: &Pat<'_>) -> bool {
-    let mut result = false;
-    pat.walk(|p| {
-        let is_or = matches!(p.kind, PatKind::Or(_));
-        result |= is_or;
-        !is_or
-    });
-    result
-}
diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs
index fae2c4e4af9..ee9f48d71ad 100644
--- a/clippy_lints/src/matches/mod.rs
+++ b/clippy_lints/src/matches/mod.rs
@@ -27,7 +27,7 @@ mod wild_in_or_pats;
 use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::source::walk_span_to_context;
 use clippy_utils::{higher, in_constant, is_direct_expn_of, is_span_match, span_contains_cfg};
-use rustc_hir::{Arm, Expr, ExprKind, LetStmt, MatchSource, Pat};
+use rustc_hir::{Arm, Expr, ExprKind, LetStmt, MatchSource, Pat, PatKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_session::impl_lint_pass;
@@ -1040,7 +1040,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
                 significant_drop_in_scrutinee::check(cx, expr, ex, arms, source);
             }
 
-            collapsible_match::check_match(cx, arms);
+            collapsible_match::check_match(cx, arms, &self.msrv);
             if !from_expansion {
                 // These don't depend on a relationship between multiple arms
                 match_wild_err_arm::check(cx, ex, arms);
@@ -1066,7 +1066,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
                     needless_match::check_match(cx, ex, arms, expr);
                     match_on_vec_items::check(cx, ex);
                     match_str_case_mismatch::check(cx, ex, arms);
-                    redundant_guards::check(cx, arms);
+                    redundant_guards::check(cx, arms, &self.msrv);
 
                     if !in_constant(cx, expr.hir_id) {
                         manual_unwrap_or::check(cx, expr, ex, arms);
@@ -1083,7 +1083,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
                 match_ref_pats::check(cx, ex, arms.iter().map(|el| el.pat), expr);
             }
         } else if let Some(if_let) = higher::IfLet::hir(cx, expr) {
-            collapsible_match::check_if_let(cx, if_let.let_pat, if_let.if_then, if_let.if_else);
+            collapsible_match::check_if_let(cx, if_let.let_pat, if_let.if_then, if_let.if_else, &self.msrv);
             if !from_expansion {
                 if let Some(else_expr) = if_let.if_else {
                     if self.msrv.meets(msrvs::MATCHES_MACRO) {
@@ -1195,3 +1195,18 @@ fn contains_cfg_arm(cx: &LateContext<'_>, e: &Expr<'_>, scrutinee: &Expr<'_>, ar
         Err(()) => true,
     }
 }
+
+/// Checks if `pat` contains OR patterns that cannot be nested due to a too low MSRV.
+fn pat_contains_disallowed_or(pat: &Pat<'_>, msrv: &Msrv) -> bool {
+    if msrv.meets(msrvs::OR_PATTERNS) {
+        return false;
+    }
+
+    let mut result = false;
+    pat.walk(|p| {
+        let is_or = matches!(p.kind, PatKind::Or(_));
+        result |= is_or;
+        !is_or
+    });
+    result
+}
diff --git a/clippy_lints/src/matches/redundant_guards.rs b/clippy_lints/src/matches/redundant_guards.rs
index 50cbccc3968..a75cf37945f 100644
--- a/clippy_lints/src/matches/redundant_guards.rs
+++ b/clippy_lints/src/matches/redundant_guards.rs
@@ -1,40 +1,32 @@
+use clippy_config::msrvs::Msrv;
 use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::macros::matching_root_macro_call;
 use clippy_utils::source::snippet;
 use clippy_utils::visitors::{for_each_expr, is_local_used};
 use clippy_utils::{in_constant, path_to_local};
 use rustc_ast::{BorrowKind, LitKind};
 use rustc_errors::Applicability;
 use rustc_hir::def::{DefKind, Res};
-use rustc_hir::{Arm, BinOpKind, Expr, ExprKind, MatchSource, Node, Pat, PatKind, UnOp};
+use rustc_hir::{Arm, BinOpKind, Expr, ExprKind, MatchSource, Node, PatKind, UnOp};
 use rustc_lint::LateContext;
 use rustc_span::symbol::Ident;
-use rustc_span::{Span, Symbol};
+use rustc_span::{sym, Span, Symbol};
 use std::borrow::Cow;
 use std::ops::ControlFlow;
 
-use super::REDUNDANT_GUARDS;
+use super::{pat_contains_disallowed_or, REDUNDANT_GUARDS};
 
-pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>]) {
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>], msrv: &Msrv) {
     for outer_arm in arms {
         let Some(guard) = outer_arm.guard else {
             continue;
         };
 
         // `Some(x) if matches!(x, y)`
-        if let ExprKind::Match(
-            scrutinee,
-            [
-                arm,
-                Arm {
-                    pat: Pat {
-                        kind: PatKind::Wild, ..
-                    },
-                    ..
-                },
-            ],
-            MatchSource::Normal,
-        ) = guard.kind
+        if let ExprKind::Match(scrutinee, [arm, _], MatchSource::Normal) = guard.kind
+            && matching_root_macro_call(cx, guard.span, sym::matches_macro).is_some()
             && let Some(binding) = get_pat_binding(cx, scrutinee, outer_arm)
+            && !pat_contains_disallowed_or(arm.pat, msrv)
         {
             let pat_span = match (arm.pat.kind, binding.byref_ident) {
                 (PatKind::Ref(pat, _), Some(_)) => pat.span,
@@ -53,6 +45,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>]) {
         // `Some(x) if let Some(2) = x`
         else if let ExprKind::Let(let_expr) = guard.kind
             && let Some(binding) = get_pat_binding(cx, let_expr.init, outer_arm)
+            && !pat_contains_disallowed_or(let_expr.pat, msrv)
         {
             let pat_span = match (let_expr.pat.kind, binding.byref_ident) {
                 (PatKind::Ref(pat, _), Some(_)) => pat.span,
diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs
index 37f72528140..69791414f72 100644
--- a/clippy_lints/src/matches/single_match.rs
+++ b/clippy_lints/src/matches/single_match.rs
@@ -75,7 +75,7 @@ fn report_single_pattern(
 ) {
     let lint = if els.is_some() { SINGLE_MATCH_ELSE } else { SINGLE_MATCH };
     let ctxt = expr.span.ctxt();
-    let mut app = Applicability::HasPlaceholders;
+    let mut app = Applicability::MachineApplicable;
     let els_str = els.map_or(String::new(), |els| {
         format!(" else {}", expr_block(cx, els, ctxt, "..", Some(expr.span), &mut app))
     });
diff --git a/clippy_lints/src/methods/filter_map.rs b/clippy_lints/src/methods/filter_map.rs
index 581e3b308c3..02a11257007 100644
--- a/clippy_lints/src/methods/filter_map.rs
+++ b/clippy_lints/src/methods/filter_map.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
-use clippy_utils::macros::{is_panic, root_macro_call};
+use clippy_utils::macros::{is_panic, matching_root_macro_call, root_macro_call};
 use clippy_utils::source::{indent_of, reindent_multiline, snippet};
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::{higher, is_trait_method, path_to_local_id, peel_blocks, SpanlessEq};
@@ -247,8 +247,7 @@ impl<'tcx> OffendingFilterExpr<'tcx> {
             } else {
                 None
             }
-        } else if let Some(macro_call) = root_macro_call(expr.span)
-            && cx.tcx.get_diagnostic_name(macro_call.def_id) == Some(sym::matches_macro)
+        } else if matching_root_macro_call(cx, expr.span, sym::matches_macro).is_some()
             // we know for a fact that the wildcard pattern is the second arm
             && let ExprKind::Match(scrutinee, [arm, _], _) = expr.kind
             && path_to_local_id(scrutinee, filter_param_id)
diff --git a/clippy_lints/src/methods/iter_overeager_cloned.rs b/clippy_lints/src/methods/iter_overeager_cloned.rs
index 6d70989546a..03b4680c522 100644
--- a/clippy_lints/src/methods/iter_overeager_cloned.rs
+++ b/clippy_lints/src/methods/iter_overeager_cloned.rs
@@ -89,8 +89,7 @@ pub(super) fn check<'tcx>(
                 }
 
                 match it.kind {
-                    PatKind::Binding(BindingMode(_, Mutability::Mut), _, _, _)
-                    | PatKind::Ref(_, Mutability::Mut) => {
+                    PatKind::Binding(BindingMode(_, Mutability::Mut), _, _, _) | PatKind::Ref(_, Mutability::Mut) => {
                         to_be_discarded = true;
                         false
                     },
diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs
index 0939c028564..63545d6c503 100644
--- a/clippy_lints/src/methods/mod.rs
+++ b/clippy_lints/src/methods/mod.rs
@@ -1145,11 +1145,16 @@ declare_clippy_lint! {
     /// `str` as an argument, e.g., `_.split("x")`.
     ///
     /// ### Why is this bad?
-    /// Performing these methods using a `char` is faster than
-    /// using a `str`.
+    /// While this can make a perf difference on some systems,
+    /// benchmarks have proven inconclusive. But at least using a
+    /// char literal makes it clear that we are looking at a single
+    /// character.
     ///
     /// ### Known problems
-    /// Does not catch multi-byte unicode characters.
+    /// Does not catch multi-byte unicode characters. This is by
+    /// design, on many machines, splitting by a non-ascii char is
+    /// actually slower. Please do your own measurements instead of
+    /// relying solely on the results of this lint.
     ///
     /// ### Example
     /// ```rust,ignore
@@ -1162,7 +1167,7 @@ declare_clippy_lint! {
     /// ```
     #[clippy::version = "pre 1.29.0"]
     pub SINGLE_CHAR_PATTERN,
-    perf,
+    pedantic,
     "using a single-character str where a char could be used, e.g., `_.split(\"x\")`"
 }
 
@@ -3988,7 +3993,7 @@ declare_clippy_lint! {
     /// let x: Result<u32, ()> = Ok(0);
     /// let y = x.unwrap_or_else(|err| handle_error(err));
     /// ```
-    #[clippy::version = "1.77.0"]
+    #[clippy::version = "1.78.0"]
     pub UNNECESSARY_RESULT_MAP_OR_ELSE,
     suspicious,
     "making no use of the \"map closure\" when calling `.map_or_else(|err| handle_error(err), |n| n)`"
@@ -4022,7 +4027,7 @@ declare_clippy_lint! {
     /// needs_cstr(c"Hello");
     /// unsafe { libc::puts(c"World".as_ptr()) }
     /// ```
-    #[clippy::version = "1.76.0"]
+    #[clippy::version = "1.78.0"]
     pub MANUAL_C_STR_LITERALS,
     pedantic,
     r#"creating a `CStr` through functions when `c""` literals can be used"#
diff --git a/clippy_lints/src/methods/readonly_write_lock.rs b/clippy_lints/src/methods/readonly_write_lock.rs
index 9b0180d9369..774aaec1afd 100644
--- a/clippy_lints/src/methods/readonly_write_lock.rs
+++ b/clippy_lints/src/methods/readonly_write_lock.rs
@@ -4,7 +4,7 @@ use clippy_utils::mir::{enclosing_mir, visit_local_usage};
 use clippy_utils::source::snippet;
 use clippy_utils::ty::is_type_diagnostic_item;
 use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind, Node};
+use rustc_hir::{Expr, ExprKind, Node, PatKind};
 use rustc_lint::LateContext;
 use rustc_middle::mir::{Location, START_BLOCK};
 use rustc_span::sym;
@@ -25,6 +25,11 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, receiver
         && is_unwrap_call(cx, unwrap_call_expr)
         && let parent = cx.tcx.parent_hir_node(unwrap_call_expr.hir_id)
         && let Node::LetStmt(local) = parent
+        && let PatKind::Binding(.., ident, _) = local.pat.kind
+        // if the binding is prefixed with `_`, it typically means
+        // that this guard only exists to protect a section of code
+        // rather than the contained data
+        && !ident.as_str().starts_with('_')
         && let Some(mir) = enclosing_mir(cx.tcx, expr.hir_id)
         && let Some((local, _)) = mir
             .local_decls
diff --git a/clippy_lints/src/methods/single_char_insert_string.rs b/clippy_lints/src/methods/single_char_insert_string.rs
index 44a7ad394fa..20ec2b74d81 100644
--- a/clippy_lints/src/methods/single_char_insert_string.rs
+++ b/clippy_lints/src/methods/single_char_insert_string.rs
@@ -10,7 +10,7 @@ use super::SINGLE_CHAR_ADD_STR;
 /// lint for length-1 `str`s as argument for `insert_str`
 pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
     let mut applicability = Applicability::MachineApplicable;
-    if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[1], &mut applicability) {
+    if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[1], &mut applicability, false) {
         let base_string_snippet =
             snippet_with_applicability(cx, receiver.span.source_callsite(), "_", &mut applicability);
         let pos_arg = snippet_with_applicability(cx, args[0].span, "..", &mut applicability);
diff --git a/clippy_lints/src/methods/single_char_pattern.rs b/clippy_lints/src/methods/single_char_pattern.rs
index 363b1f2b812..982a7901c45 100644
--- a/clippy_lints/src/methods/single_char_pattern.rs
+++ b/clippy_lints/src/methods/single_char_pattern.rs
@@ -8,7 +8,7 @@ use rustc_span::symbol::Symbol;
 
 use super::SINGLE_CHAR_PATTERN;
 
-const PATTERN_METHODS: [(&str, usize); 24] = [
+const PATTERN_METHODS: [(&str, usize); 22] = [
     ("contains", 0),
     ("starts_with", 0),
     ("ends_with", 0),
@@ -27,8 +27,6 @@ const PATTERN_METHODS: [(&str, usize); 24] = [
     ("rmatches", 0),
     ("match_indices", 0),
     ("rmatch_indices", 0),
-    ("strip_prefix", 0),
-    ("strip_suffix", 0),
     ("trim_start_matches", 0),
     ("trim_end_matches", 0),
     ("replace", 0),
@@ -50,7 +48,7 @@ pub(super) fn check(
             && args.len() > pos
             && let arg = &args[pos]
             && let mut applicability = Applicability::MachineApplicable
-            && let Some(hint) = get_hint_if_single_char_arg(cx, arg, &mut applicability)
+            && let Some(hint) = get_hint_if_single_char_arg(cx, arg, &mut applicability, true)
         {
             span_lint_and_sugg(
                 cx,
diff --git a/clippy_lints/src/methods/single_char_push_string.rs b/clippy_lints/src/methods/single_char_push_string.rs
index 0698bd6a0c5..97c13825bc1 100644
--- a/clippy_lints/src/methods/single_char_push_string.rs
+++ b/clippy_lints/src/methods/single_char_push_string.rs
@@ -10,7 +10,7 @@ use super::SINGLE_CHAR_ADD_STR;
 /// lint for length-1 `str`s as argument for `push_str`
 pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
     let mut applicability = Applicability::MachineApplicable;
-    if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[0], &mut applicability) {
+    if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[0], &mut applicability, false) {
         let base_string_snippet =
             snippet_with_applicability(cx, receiver.span.source_callsite(), "..", &mut applicability);
         let sugg = format!("{base_string_snippet}.push({extension_string})");
diff --git a/clippy_lints/src/methods/unused_enumerate_index.rs b/clippy_lints/src/methods/unused_enumerate_index.rs
index e5cc898612e..8b8a965b9f0 100644
--- a/clippy_lints/src/methods/unused_enumerate_index.rs
+++ b/clippy_lints/src/methods/unused_enumerate_index.rs
@@ -1,7 +1,6 @@
 use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_hir_and_then};
-use clippy_utils::paths::{CORE_ITER_ENUMERATE_METHOD, CORE_ITER_ENUMERATE_STRUCT};
 use clippy_utils::source::{snippet, snippet_opt};
-use clippy_utils::{expr_or_init, is_trait_method, match_def_path, pat_is_wild};
+use clippy_utils::{expr_or_init, is_trait_method, pat_is_wild};
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, FnDecl, PatKind, TyKind};
 use rustc_lint::LateContext;
@@ -42,7 +41,7 @@ pub(super) fn check(cx: &LateContext<'_>, call_expr: &Expr<'_>, recv: &Expr<'_>,
     let recv_ty = cx.typeck_results().expr_ty(recv);
     if let Some(recv_ty_defid) = recv_ty.ty_adt_def().map(AdtDef::did)
         // If we call a method on a `std::iter::Enumerate` instance
-        && match_def_path(cx, recv_ty_defid, &CORE_ITER_ENUMERATE_STRUCT)
+        && cx.tcx.is_diagnostic_item(sym::Enumerate, recv_ty_defid)
         // If we are calling a method of the `Iterator` trait
         && is_trait_method(cx, call_expr, sym::Iterator)
         // And the map argument is a closure
@@ -75,10 +74,10 @@ pub(super) fn check(cx: &LateContext<'_>, call_expr: &Expr<'_>, recv: &Expr<'_>,
         && let ExprKind::MethodCall(_, enumerate_recv, _, enumerate_span) = recv_init_expr.kind
         && let Some(enumerate_defid) = cx.typeck_results().type_dependent_def_id(recv_init_expr.hir_id)
         // Make sure the method call is `std::iter::Iterator::enumerate`.
-        && match_def_path(cx, enumerate_defid, &CORE_ITER_ENUMERATE_METHOD)
+        && cx.tcx.is_diagnostic_item(sym::enumerate_method, enumerate_defid)
     {
         // Check if the tuple type was explicit. It may be the type system _needs_ the type of the element
-        // that would be explicited in the closure.
+        // that would be explicitly in the closure.
         let new_closure_param = match find_elem_explicit_type_span(closure.fn_decl) {
             // We have an explicit type. Get its snippet, that of the binding name, and do `binding: ty`.
             // Fallback to `..` if we fail getting either snippet.
diff --git a/clippy_lints/src/methods/utils.rs b/clippy_lints/src/methods/utils.rs
index ef00c812d51..c50f24f824a 100644
--- a/clippy_lints/src/methods/utils.rs
+++ b/clippy_lints/src/methods/utils.rs
@@ -52,11 +52,17 @@ pub(super) fn get_hint_if_single_char_arg(
     cx: &LateContext<'_>,
     arg: &Expr<'_>,
     applicability: &mut Applicability,
+    ascii_only: bool,
 ) -> Option<String> {
     if let ExprKind::Lit(lit) = &arg.kind
         && let ast::LitKind::Str(r, style) = lit.node
         && let string = r.as_str()
-        && string.chars().count() == 1
+        && let len = if ascii_only {
+            string.len()
+        } else {
+            string.chars().count()
+        }
+        && len == 1
     {
         let snip = snippet_with_applicability(cx, arg.span, string, applicability);
         let ch = if let ast::StrStyle::Raw(nhash) = style {
diff --git a/clippy_lints/src/multiple_bound_locations.rs b/clippy_lints/src/multiple_bound_locations.rs
index 191b32408ef..d608f3bf7b4 100644
--- a/clippy_lints/src/multiple_bound_locations.rs
+++ b/clippy_lints/src/multiple_bound_locations.rs
@@ -29,7 +29,7 @@ declare_clippy_lint! {
     ///     F: Sized + std::fmt::Debug,
     /// {}
     /// ```
-    #[clippy::version = "1.77.0"]
+    #[clippy::version = "1.78.0"]
     pub MULTIPLE_BOUND_LOCATIONS,
     suspicious,
     "defining generic bounds in multiple locations"
diff --git a/clippy_lints/src/mut_key.rs b/clippy_lints/src/mut_key.rs
index 8c2f43c97f4..2eb534da092 100644
--- a/clippy_lints/src/mut_key.rs
+++ b/clippy_lints/src/mut_key.rs
@@ -1,7 +1,6 @@
 use clippy_utils::diagnostics::span_lint;
-use clippy_utils::ty::is_interior_mut_ty;
-use clippy_utils::{def_path_def_ids, trait_ref_of_method};
-use rustc_data_structures::fx::FxHashSet;
+use clippy_utils::trait_ref_of_method;
+use clippy_utils::ty::InteriorMut;
 use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::{self, Ty};
@@ -23,27 +22,15 @@ declare_clippy_lint! {
     /// ### Known problems
     ///
     /// #### False Positives
-    /// It's correct to use a struct that contains interior mutability as a key, when its
+    /// It's correct to use a struct that contains interior mutability as a key when its
     /// implementation of `Hash` or `Ord` doesn't access any of the interior mutable types.
     /// However, this lint is unable to recognize this, so it will often cause false positives in
-    /// theses cases.  The `bytes` crate is a great example of this.
+    /// these cases.
     ///
     /// #### False Negatives
-    /// For custom `struct`s/`enum`s, this lint is unable to check for interior mutability behind
-    /// indirection.  For example, `struct BadKey<'a>(&'a Cell<usize>)` will be seen as immutable
-    /// and cause a false negative if its implementation of `Hash`/`Ord` accesses the `Cell`.
-    ///
-    /// This lint does check a few cases for indirection.  Firstly, using some standard library
-    /// types (`Option`, `Result`, `Box`, `Rc`, `Arc`, `Vec`, `VecDeque`, `BTreeMap` and
-    /// `BTreeSet`) directly as keys (e.g. in `HashMap<Box<Cell<usize>>, ()>`) **will** trigger the
-    /// lint, because the impls of `Hash`/`Ord` for these types directly call `Hash`/`Ord` on their
-    /// contained type.
-    ///
-    /// Secondly, the implementations of `Hash` and `Ord` for raw pointers (`*const T` or `*mut T`)
-    /// apply only to the **address** of the contained value.  Therefore, interior mutability
-    /// behind raw pointers (e.g. in `HashSet<*mut Cell<usize>>`) can't impact the value of `Hash`
-    /// or `Ord`, and therefore will not trigger this link.  For more info, see issue
-    /// [#6745](https://github.com/rust-lang/rust-clippy/issues/6745).
+    /// This lint does not follow raw pointers (`*const T` or `*mut T`) as `Hash` and `Ord`
+    /// apply only to the **address** of the contained value. This can cause false negatives for
+    /// custom collections that use raw pointers internally.
     ///
     /// ### Example
     /// ```no_run
@@ -51,13 +38,12 @@ declare_clippy_lint! {
     /// use std::collections::HashSet;
     /// use std::hash::{Hash, Hasher};
     /// use std::sync::atomic::AtomicUsize;
-    ///# #[allow(unused)]
     ///
     /// struct Bad(AtomicUsize);
     /// impl PartialEq for Bad {
     ///     fn eq(&self, rhs: &Self) -> bool {
     ///          ..
-    /// ; unimplemented!();
+    /// # ; true
     ///     }
     /// }
     ///
@@ -66,7 +52,7 @@ declare_clippy_lint! {
     /// impl Hash for Bad {
     ///     fn hash<H: Hasher>(&self, h: &mut H) {
     ///         ..
-    /// ; unimplemented!();
+    /// # ;
     ///     }
     /// }
     ///
@@ -80,25 +66,16 @@ declare_clippy_lint! {
     "Check for mutable `Map`/`Set` key type"
 }
 
-#[derive(Clone)]
-pub struct MutableKeyType {
+pub struct MutableKeyType<'tcx> {
     ignore_interior_mutability: Vec<String>,
-    ignore_mut_def_ids: FxHashSet<hir::def_id::DefId>,
+    interior_mut: InteriorMut<'tcx>,
 }
 
-impl_lint_pass!(MutableKeyType => [ MUTABLE_KEY_TYPE ]);
+impl_lint_pass!(MutableKeyType<'_> => [ MUTABLE_KEY_TYPE ]);
 
-impl<'tcx> LateLintPass<'tcx> for MutableKeyType {
+impl<'tcx> LateLintPass<'tcx> for MutableKeyType<'tcx> {
     fn check_crate(&mut self, cx: &LateContext<'tcx>) {
-        self.ignore_mut_def_ids.clear();
-        let mut path = Vec::new();
-        for ty in &self.ignore_interior_mutability {
-            path.extend(ty.split("::"));
-            for id in def_path_def_ids(cx, &path[..]) {
-                self.ignore_mut_def_ids.insert(id);
-            }
-            path.clear();
-        }
+        self.interior_mut = InteriorMut::without_pointers(cx, &self.ignore_interior_mutability);
     }
 
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
@@ -121,7 +98,7 @@ impl<'tcx> LateLintPass<'tcx> for MutableKeyType {
         }
     }
 
-    fn check_local(&mut self, cx: &LateContext<'_>, local: &hir::LetStmt<'_>) {
+    fn check_local(&mut self, cx: &LateContext<'tcx>, local: &hir::LetStmt<'tcx>) {
         if let hir::PatKind::Wild = local.pat.kind {
             return;
         }
@@ -129,15 +106,15 @@ impl<'tcx> LateLintPass<'tcx> for MutableKeyType {
     }
 }
 
-impl MutableKeyType {
+impl<'tcx> MutableKeyType<'tcx> {
     pub fn new(ignore_interior_mutability: Vec<String>) -> Self {
         Self {
             ignore_interior_mutability,
-            ignore_mut_def_ids: FxHashSet::default(),
+            interior_mut: InteriorMut::default(),
         }
     }
 
-    fn check_sig(&self, cx: &LateContext<'_>, fn_def_id: LocalDefId, decl: &hir::FnDecl<'_>) {
+    fn check_sig(&mut self, cx: &LateContext<'tcx>, fn_def_id: LocalDefId, decl: &hir::FnDecl<'tcx>) {
         let fn_sig = cx.tcx.fn_sig(fn_def_id).instantiate_identity();
         for (hir_ty, ty) in iter::zip(decl.inputs, fn_sig.inputs().skip_binder()) {
             self.check_ty_(cx, hir_ty.span, *ty);
@@ -151,7 +128,7 @@ impl MutableKeyType {
 
     // We want to lint 1. sets or maps with 2. not immutable key types and 3. no unerased
     // generics (because the compiler cannot ensure immutability for unknown types).
-    fn check_ty_<'tcx>(&self, cx: &LateContext<'tcx>, span: Span, ty: Ty<'tcx>) {
+    fn check_ty_(&mut self, cx: &LateContext<'tcx>, span: Span, ty: Ty<'tcx>) {
         let ty = ty.peel_refs();
         if let ty::Adt(def, args) = ty.kind() {
             let is_keyed_type = [sym::HashMap, sym::BTreeMap, sym::HashSet, sym::BTreeSet]
@@ -162,11 +139,7 @@ impl MutableKeyType {
             }
 
             let subst_ty = args.type_at(0);
-            // Determines if a type contains interior mutability which would affect its implementation of
-            // [`Hash`] or [`Ord`].
-            if is_interior_mut_ty(cx, subst_ty)
-                && !matches!(subst_ty.ty_adt_def(), Some(adt) if self.ignore_mut_def_ids.contains(&adt.did()))
-            {
+            if self.interior_mut.is_interior_mut_ty(cx, subst_ty) {
                 span_lint(cx, MUTABLE_KEY_TYPE, span, "mutable key type");
             }
         }
diff --git a/clippy_lints/src/needless_for_each.rs b/clippy_lints/src/needless_for_each.rs
index 84a07df1bb0..630018238f4 100644
--- a/clippy_lints/src/needless_for_each.rs
+++ b/clippy_lints/src/needless_for_each.rs
@@ -1,6 +1,6 @@
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::{walk_expr, Visitor};
-use rustc_hir::{Closure, Expr, ExprKind, Stmt, StmtKind};
+use rustc_hir::{Block, BlockCheckMode, Closure, Expr, ExprKind, Stmt, StmtKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
 use rustc_span::{sym, Span, Symbol};
@@ -35,6 +35,16 @@ declare_clippy_lint! {
     ///     println!("{}", elem);
     /// }
     /// ```
+    ///
+    /// ### Known Problems
+    /// When doing things such as:
+    /// ```ignore
+    /// let v = vec![0, 1, 2];
+    /// v.iter().for_each(|elem| unsafe {
+    ///     libc::printf(c"%d\n".as_ptr(), elem);
+    /// });
+    /// ```
+    /// This lint will not trigger.
     #[clippy::version = "1.53.0"]
     pub NEEDLESS_FOR_EACH,
     pedantic,
@@ -68,7 +78,9 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessForEach {
             // e.g. `v.iter().for_each(f)` is simpler and clearer than using `for` loop.
             && let ExprKind::Closure(&Closure { body, .. }) = for_each_arg.kind
             && let body = cx.tcx.hir().body(body)
-            && let ExprKind::Block(..) = body.value.kind
+            // Skip the lint if the body is not safe, so as not to suggest `for … in … unsafe {}`
+            // and suggesting `for … in … { unsafe { } }` is a little ugly.
+            && let ExprKind::Block(Block { rules: BlockCheckMode::DefaultBlock, .. }, ..) = body.value.kind
         {
             let mut ret_collector = RetCollector::default();
             ret_collector.visit_expr(body.value);
diff --git a/clippy_lints/src/needless_late_init.rs b/clippy_lints/src/needless_late_init.rs
index 0c0b1a73351..6605d1fa51a 100644
--- a/clippy_lints/src/needless_late_init.rs
+++ b/clippy_lints/src/needless_late_init.rs
@@ -6,8 +6,7 @@ use clippy_utils::visitors::{for_each_expr, for_each_expr_with_closures, is_loca
 use core::ops::ControlFlow;
 use rustc_errors::{Applicability, MultiSpan};
 use rustc_hir::{
-    BindingMode, Block, Expr, ExprKind, HirId, LetStmt, LocalSource, MatchSource, Node, Pat, PatKind, Stmt,
-    StmtKind,
+    BindingMode, Block, Expr, ExprKind, HirId, LetStmt, LocalSource, MatchSource, Node, Pat, PatKind, Stmt, StmtKind,
 };
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
diff --git a/clippy_lints/src/needless_pass_by_ref_mut.rs b/clippy_lints/src/needless_pass_by_ref_mut.rs
index 30d3e86dc4e..9e47c3ad0b7 100644
--- a/clippy_lints/src/needless_pass_by_ref_mut.rs
+++ b/clippy_lints/src/needless_pass_by_ref_mut.rs
@@ -83,7 +83,9 @@ fn should_skip<'tcx>(
     }
 
     if is_self(arg) {
-        return true;
+        // Interestingly enough, `self` arguments make `is_from_proc_macro` return `true`, hence why
+        // we return early here.
+        return false;
     }
 
     if let PatKind::Binding(.., name, _) = arg.pat.kind {
@@ -185,7 +187,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> {
         }
         // Collect variables mutably used and spans which will need dereferencings from the
         // function body.
-        let MutablyUsedVariablesCtxt { mutably_used_vars, .. } = {
+        let mutably_used_vars = {
             let mut ctx = MutablyUsedVariablesCtxt {
                 mutably_used_vars: HirIdSet::default(),
                 prev_bind: None,
@@ -217,7 +219,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> {
                     check_closures(&mut ctx, cx, &infcx, &mut checked_closures, async_closures);
                 }
             }
-            ctx
+            ctx.generate_mutably_used_ids_from_aliases()
         };
         for ((&input, &_), arg) in it {
             // Only take `&mut` arguments.
@@ -309,12 +311,22 @@ struct MutablyUsedVariablesCtxt<'tcx> {
 }
 
 impl<'tcx> MutablyUsedVariablesCtxt<'tcx> {
-    fn add_mutably_used_var(&mut self, mut used_id: HirId) {
-        while let Some(id) = self.aliases.get(&used_id) {
+    fn add_mutably_used_var(&mut self, used_id: HirId) {
+        self.mutably_used_vars.insert(used_id);
+    }
+
+    // Because the alias may come after the mutable use of a variable, we need to fill the map at
+    // the end.
+    fn generate_mutably_used_ids_from_aliases(mut self) -> HirIdSet {
+        let all_ids = self.mutably_used_vars.iter().copied().collect::<Vec<_>>();
+        for mut used_id in all_ids {
+            while let Some(id) = self.aliases.get(&used_id) {
+                self.mutably_used_vars.insert(used_id);
+                used_id = *id;
+            }
             self.mutably_used_vars.insert(used_id);
-            used_id = *id;
         }
-        self.mutably_used_vars.insert(used_id);
+        self.mutably_used_vars
     }
 
     fn would_be_alias_cycle(&self, alias: HirId, mut target: HirId) -> bool {
diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs
index 53bcde68087..39d374d0d27 100644
--- a/clippy_lints/src/needless_pass_by_value.rs
+++ b/clippy_lints/src/needless_pass_by_value.rs
@@ -9,8 +9,8 @@ use rustc_ast::ast::Attribute;
 use rustc_errors::{Applicability, Diag};
 use rustc_hir::intravisit::FnKind;
 use rustc_hir::{
-    BindingMode, Body, FnDecl, GenericArg, HirId, HirIdSet, Impl, ItemKind, LangItem, Mutability, Node, PatKind,
-    QPath, TyKind,
+    BindingMode, Body, FnDecl, GenericArg, HirId, HirIdSet, Impl, ItemKind, LangItem, Mutability, Node, PatKind, QPath,
+    TyKind,
 };
 use rustc_hir_typeck::expr_use_visitor as euv;
 use rustc_infer::infer::TyCtxtInferExt;
diff --git a/clippy_lints/src/non_canonical_impls.rs b/clippy_lints/src/non_canonical_impls.rs
index fd3985a5daf..932d6fe54d6 100644
--- a/clippy_lints/src/non_canonical_impls.rs
+++ b/clippy_lints/src/non_canonical_impls.rs
@@ -182,17 +182,17 @@ impl LateLintPass<'_> for NonCanonicalImpls {
 
             if block.stmts.is_empty()
                 && let Some(expr) = block.expr
-                && let ExprKind::Call(
-                        Expr {
-                            kind: ExprKind::Path(some_path),
-                            hir_id: some_hir_id,
-                            ..
-                        },
-                        [cmp_expr],
-                    ) = expr.kind
-                && is_res_lang_ctor(cx, cx.qpath_res(some_path, *some_hir_id), LangItem::OptionSome)
-                // Fix #11178, allow `Self::cmp(self, ..)` too
-                && self_cmp_call(cx, cmp_expr, impl_item.owner_id.def_id, &mut needs_fully_qualified)
+                && expr_is_cmp(cx, &expr.kind, impl_item, &mut needs_fully_qualified)
+            {
+            }
+            // Fix #12683, allow [`needless_return`] here
+            else if block.expr.is_none()
+                && let Some(stmt) = block.stmts.first()
+                && let rustc_hir::StmtKind::Semi(Expr {
+                    kind: ExprKind::Ret(Some(Expr { kind: ret_kind, .. })),
+                    ..
+                }) = stmt.kind
+                && expr_is_cmp(cx, ret_kind, impl_item, &mut needs_fully_qualified)
             {
             } else {
                 // If `Self` and `Rhs` are not the same type, bail. This makes creating a valid
@@ -245,6 +245,30 @@ impl LateLintPass<'_> for NonCanonicalImpls {
     }
 }
 
+/// Return true if `expr_kind` is a `cmp` call.
+fn expr_is_cmp<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr_kind: &'tcx ExprKind<'tcx>,
+    impl_item: &ImplItem<'_>,
+    needs_fully_qualified: &mut bool,
+) -> bool {
+    if let ExprKind::Call(
+        Expr {
+            kind: ExprKind::Path(some_path),
+            hir_id: some_hir_id,
+            ..
+        },
+        [cmp_expr],
+    ) = expr_kind
+    {
+        is_res_lang_ctor(cx, cx.qpath_res(some_path, *some_hir_id), LangItem::OptionSome)
+            // Fix #11178, allow `Self::cmp(self, ..)` too
+            && self_cmp_call(cx, cmp_expr, impl_item.owner_id.def_id, needs_fully_qualified)
+    } else {
+        false
+    }
+}
+
 /// Returns whether this is any of `self.cmp(..)`, `Self::cmp(self, ..)` or `Ord::cmp(self, ..)`.
 fn self_cmp_call<'tcx>(
     cx: &LateContext<'tcx>,
diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs
index ff10a841aef..76d9cee18aa 100644
--- a/clippy_lints/src/non_copy_const.rs
+++ b/clippy_lints/src/non_copy_const.rs
@@ -5,9 +5,9 @@
 use std::ptr;
 
 use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::in_constant;
 use clippy_utils::macros::macro_backtrace;
-use clippy_utils::{def_path_def_ids, in_constant};
-use rustc_data_structures::fx::FxHashSet;
+use clippy_utils::ty::InteriorMut;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::DefId;
 use rustc_hir::{
@@ -52,8 +52,8 @@ declare_clippy_lint! {
     /// There're other enums plus associated constants cases that the lint cannot handle.
     ///
     /// Types that have underlying or potential interior mutability trigger the lint whether
-    /// the interior mutable field is used or not. See issues
-    /// [#5812](https://github.com/rust-lang/rust-clippy/issues/5812) and
+    /// the interior mutable field is used or not. See issue
+    /// [#5812](https://github.com/rust-lang/rust-clippy/issues/5812)
     ///
     /// ### Example
     /// ```no_run
@@ -170,42 +170,22 @@ fn lint(cx: &LateContext<'_>, source: Source) {
     });
 }
 
-#[derive(Clone)]
-pub struct NonCopyConst {
+pub struct NonCopyConst<'tcx> {
     ignore_interior_mutability: Vec<String>,
-    ignore_mut_def_ids: FxHashSet<DefId>,
+    interior_mut: InteriorMut<'tcx>,
 }
 
-impl_lint_pass!(NonCopyConst => [DECLARE_INTERIOR_MUTABLE_CONST, BORROW_INTERIOR_MUTABLE_CONST]);
+impl_lint_pass!(NonCopyConst<'_> => [DECLARE_INTERIOR_MUTABLE_CONST, BORROW_INTERIOR_MUTABLE_CONST]);
 
-impl NonCopyConst {
+impl<'tcx> NonCopyConst<'tcx> {
     pub fn new(ignore_interior_mutability: Vec<String>) -> Self {
         Self {
             ignore_interior_mutability,
-            ignore_mut_def_ids: FxHashSet::default(),
+            interior_mut: InteriorMut::default(),
         }
     }
 
-    fn is_ty_ignored(&self, ty: Ty<'_>) -> bool {
-        matches!(ty.ty_adt_def(), Some(adt) if self.ignore_mut_def_ids.contains(&adt.did()))
-    }
-
-    fn is_unfrozen<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
-        // Ignore types whose layout is unknown since `is_freeze` reports every generic types as `!Freeze`,
-        // making it indistinguishable from `UnsafeCell`. i.e. it isn't a tool to prove a type is
-        // 'unfrozen'. However, this code causes a false negative in which
-        // a type contains a layout-unknown type, but also an unsafe cell like `const CELL: Cell<T>`.
-        // Yet, it's better than `ty.has_type_flags(TypeFlags::HAS_TY_PARAM | TypeFlags::HAS_PROJECTION)`
-        // since it works when a pointer indirection involves (`Cell<*const T>`).
-        // Making up a `ParamEnv` where every generic params and assoc types are `Freeze`is another option;
-        // but I'm not sure whether it's a decent way, if possible.
-        cx.tcx.layout_of(cx.param_env.and(ty)).is_ok() && !ty.is_freeze(cx.tcx, cx.param_env)
-    }
-
-    fn is_value_unfrozen_raw_inner<'tcx>(&self, cx: &LateContext<'tcx>, val: ty::ValTree<'tcx>, ty: Ty<'tcx>) -> bool {
-        if self.is_ty_ignored(ty) {
-            return false;
-        }
+    fn is_value_unfrozen_raw_inner(cx: &LateContext<'tcx>, val: ty::ValTree<'tcx>, ty: Ty<'tcx>) -> bool {
         match *ty.kind() {
             // the fact that we have to dig into every structs to search enums
             // leads us to the point checking `UnsafeCell` directly is the only option.
@@ -216,8 +196,7 @@ impl NonCopyConst {
             ty::Array(ty, _) => val
                 .unwrap_branch()
                 .iter()
-                .any(|field| self.is_value_unfrozen_raw_inner(cx, *field, ty)),
-            ty::Adt(def, _) if def.is_union() => false,
+                .any(|field| Self::is_value_unfrozen_raw_inner(cx, *field, ty)),
             ty::Adt(def, args) if def.is_enum() => {
                 let (&variant_index, fields) = val.unwrap_branch().split_first().unwrap();
                 let variant_index = VariantIdx::from_u32(variant_index.unwrap_leaf().try_to_u32().ok().unwrap());
@@ -230,24 +209,23 @@ impl NonCopyConst {
                             .iter()
                             .map(|field| field.ty(cx.tcx, args)),
                     )
-                    .any(|(field, ty)| self.is_value_unfrozen_raw_inner(cx, field, ty))
+                    .any(|(field, ty)| Self::is_value_unfrozen_raw_inner(cx, field, ty))
             },
             ty::Adt(def, args) => val
                 .unwrap_branch()
                 .iter()
                 .zip(def.non_enum_variant().fields.iter().map(|field| field.ty(cx.tcx, args)))
-                .any(|(field, ty)| self.is_value_unfrozen_raw_inner(cx, *field, ty)),
+                .any(|(field, ty)| Self::is_value_unfrozen_raw_inner(cx, *field, ty)),
             ty::Tuple(tys) => val
                 .unwrap_branch()
                 .iter()
                 .zip(tys)
-                .any(|(field, ty)| self.is_value_unfrozen_raw_inner(cx, *field, ty)),
+                .any(|(field, ty)| Self::is_value_unfrozen_raw_inner(cx, *field, ty)),
             _ => false,
         }
     }
 
-    fn is_value_unfrozen_raw<'tcx>(
-        &self,
+    fn is_value_unfrozen_raw(
         cx: &LateContext<'tcx>,
         result: Result<Option<ty::ValTree<'tcx>>, ErrorHandled>,
         ty: Ty<'tcx>,
@@ -277,11 +255,11 @@ impl NonCopyConst {
                 // I chose this way because unfrozen enums as assoc consts are rare (or, hopefully, none).
                 matches!(err, ErrorHandled::TooGeneric(..))
             },
-            |val| val.map_or(true, |val| self.is_value_unfrozen_raw_inner(cx, val, ty)),
+            |val| val.map_or(true, |val| Self::is_value_unfrozen_raw_inner(cx, val, ty)),
         )
     }
 
-    fn is_value_unfrozen_poly<'tcx>(&self, cx: &LateContext<'tcx>, body_id: BodyId, ty: Ty<'tcx>) -> bool {
+    fn is_value_unfrozen_poly(cx: &LateContext<'tcx>, body_id: BodyId, ty: Ty<'tcx>) -> bool {
         let def_id = body_id.hir_id.owner.to_def_id();
         let args = ty::GenericArgs::identity_for_item(cx.tcx, def_id);
         let instance = ty::Instance::new(def_id, args);
@@ -291,17 +269,17 @@ impl NonCopyConst {
         };
         let param_env = cx.tcx.param_env(def_id).with_reveal_all_normalized(cx.tcx);
         let result = cx.tcx.const_eval_global_id_for_typeck(param_env, cid, DUMMY_SP);
-        self.is_value_unfrozen_raw(cx, result, ty)
+        Self::is_value_unfrozen_raw(cx, result, ty)
     }
 
-    fn is_value_unfrozen_expr<'tcx>(&self, cx: &LateContext<'tcx>, hir_id: HirId, def_id: DefId, ty: Ty<'tcx>) -> bool {
+    fn is_value_unfrozen_expr(cx: &LateContext<'tcx>, hir_id: HirId, def_id: DefId, ty: Ty<'tcx>) -> bool {
         let args = cx.typeck_results().node_args(hir_id);
 
         let result = Self::const_eval_resolve(cx.tcx, cx.param_env, ty::UnevaluatedConst::new(def_id, args), DUMMY_SP);
-        self.is_value_unfrozen_raw(cx, result, ty)
+        Self::is_value_unfrozen_raw(cx, result, ty)
     }
 
-    pub fn const_eval_resolve<'tcx>(
+    pub fn const_eval_resolve(
         tcx: TyCtxt<'tcx>,
         param_env: ty::ParamEnv<'tcx>,
         ct: ty::UnevaluatedConst<'tcx>,
@@ -321,26 +299,17 @@ impl NonCopyConst {
     }
 }
 
-impl<'tcx> LateLintPass<'tcx> for NonCopyConst {
+impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> {
     fn check_crate(&mut self, cx: &LateContext<'tcx>) {
-        self.ignore_mut_def_ids.clear();
-        let mut path = Vec::new();
-        for ty in &self.ignore_interior_mutability {
-            path.extend(ty.split("::"));
-            for id in def_path_def_ids(cx, &path[..]) {
-                self.ignore_mut_def_ids.insert(id);
-            }
-            path.clear();
-        }
+        self.interior_mut = InteriorMut::new(cx, &self.ignore_interior_mutability);
     }
 
     fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx Item<'_>) {
         if let ItemKind::Const(.., body_id) = it.kind {
             let ty = cx.tcx.type_of(it.owner_id).instantiate_identity();
             if !ignored_macro(cx, it)
-                && !self.is_ty_ignored(ty)
-                && Self::is_unfrozen(cx, ty)
-                && self.is_value_unfrozen_poly(cx, body_id, ty)
+                && self.interior_mut.is_interior_mut_ty(cx, ty)
+                && Self::is_value_unfrozen_poly(cx, body_id, ty)
             {
                 lint(cx, Source::Item { item: it.span });
             }
@@ -354,7 +323,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst {
             // Normalize assoc types because ones originated from generic params
             // bounded other traits could have their bound.
             let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty);
-            if !self.is_ty_ignored(ty) && Self::is_unfrozen(cx, normalized)
+            if self.interior_mut.is_interior_mut_ty(cx, normalized)
                 // When there's no default value, lint it only according to its type;
                 // in other words, lint consts whose value *could* be unfrozen, not definitely is.
                 // This feels inconsistent with how the lint treats generic types,
@@ -367,7 +336,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst {
                 // i.e. having an enum doesn't necessary mean a type has a frozen variant.
                 // And, implementing it isn't a trivial task; it'll probably end up
                 // re-implementing the trait predicate evaluation specific to `Freeze`.
-                && body_id_opt.map_or(true, |body_id| self.is_value_unfrozen_poly(cx, body_id, normalized))
+                && body_id_opt.map_or(true, |body_id| Self::is_value_unfrozen_poly(cx, body_id, normalized))
             {
                 lint(cx, Source::Assoc { item: trait_item.span });
             }
@@ -409,8 +378,8 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst {
                             // e.g. `layout_of(...).is_err() || has_frozen_variant(...);`
                         && let ty = cx.tcx.type_of(impl_item.owner_id).instantiate_identity()
                         && let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty)
-                        && !self.is_ty_ignored(ty) && Self::is_unfrozen(cx, normalized)
-                        && self.is_value_unfrozen_poly(cx, *body_id, normalized)
+                        && self.interior_mut.is_interior_mut_ty(cx, normalized)
+                        && Self::is_value_unfrozen_poly(cx, *body_id, normalized)
                     {
                         lint(cx, Source::Assoc { item: impl_item.span });
                     }
@@ -420,9 +389,8 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst {
                     // Normalize assoc types originated from generic params.
                     let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty);
 
-                    if !self.is_ty_ignored(ty)
-                        && Self::is_unfrozen(cx, ty)
-                        && self.is_value_unfrozen_poly(cx, *body_id, normalized)
+                    if self.interior_mut.is_interior_mut_ty(cx, normalized)
+                        && Self::is_value_unfrozen_poly(cx, *body_id, normalized)
                     {
                         lint(cx, Source::Assoc { item: impl_item.span });
                     }
@@ -517,9 +485,8 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst {
                 cx.typeck_results().expr_ty(dereferenced_expr)
             };
 
-            if !self.is_ty_ignored(ty)
-                && Self::is_unfrozen(cx, ty)
-                && self.is_value_unfrozen_expr(cx, expr.hir_id, item_def_id, ty)
+            if self.interior_mut.is_interior_mut_ty(cx, ty)
+                && Self::is_value_unfrozen_expr(cx, expr.hir_id, item_def_id, ty)
             {
                 lint(cx, Source::Expr { expr: expr.span });
             }
diff --git a/clippy_lints/src/operators/arithmetic_side_effects.rs b/clippy_lints/src/operators/arithmetic_side_effects.rs
index 96ea063aa74..7d6f26cde3e 100644
--- a/clippy_lints/src/operators/arithmetic_side_effects.rs
+++ b/clippy_lints/src/operators/arithmetic_side_effects.rs
@@ -7,14 +7,13 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::{self, Ty};
 use rustc_session::impl_lint_pass;
-use rustc_span::source_map::Spanned;
 use rustc_span::symbol::sym;
 use rustc_span::{Span, Symbol};
 use {rustc_ast as ast, rustc_hir as hir};
 
 const HARD_CODED_ALLOWED_BINARY: &[[&str; 2]] = &[["f32", "f32"], ["f64", "f64"], ["std::string::String", "str"]];
 const HARD_CODED_ALLOWED_UNARY: &[&str] = &["f32", "f64", "std::num::Saturating", "std::num::Wrapping"];
-const INTEGER_METHODS: &[Symbol] = &[
+const DISALLOWED_INT_METHODS: &[Symbol] = &[
     sym::saturating_div,
     sym::wrapping_div,
     sym::wrapping_rem,
@@ -27,8 +26,8 @@ pub struct ArithmeticSideEffects {
     allowed_unary: FxHashSet<String>,
     // Used to check whether expressions are constants, such as in enum discriminants and consts
     const_span: Option<Span>,
+    disallowed_int_methods: FxHashSet<Symbol>,
     expr_span: Option<Span>,
-    integer_methods: FxHashSet<Symbol>,
 }
 
 impl_lint_pass!(ArithmeticSideEffects => [ARITHMETIC_SIDE_EFFECTS]);
@@ -53,8 +52,8 @@ impl ArithmeticSideEffects {
             allowed_binary,
             allowed_unary,
             const_span: None,
+            disallowed_int_methods: DISALLOWED_INT_METHODS.iter().copied().collect(),
             expr_span: None,
-            integer_methods: INTEGER_METHODS.iter().copied().collect(),
         }
     }
 
@@ -91,10 +90,10 @@ impl ArithmeticSideEffects {
     fn has_specific_allowed_type_and_operation<'tcx>(
         cx: &LateContext<'tcx>,
         lhs_ty: Ty<'tcx>,
-        op: &Spanned<hir::BinOpKind>,
+        op: hir::BinOpKind,
         rhs_ty: Ty<'tcx>,
     ) -> bool {
-        let is_div_or_rem = matches!(op.node, hir::BinOpKind::Div | hir::BinOpKind::Rem);
+        let is_div_or_rem = matches!(op, hir::BinOpKind::Div | hir::BinOpKind::Rem);
         let is_non_zero_u = |cx: &LateContext<'tcx>, ty: Ty<'tcx>| {
             let tcx = cx.tcx;
 
@@ -166,13 +165,35 @@ impl ArithmeticSideEffects {
         None
     }
 
+    /// Methods like `add_assign` are send to their `BinOps` references.
+    fn manage_sugar_methods<'tcx>(
+        &mut self,
+        cx: &LateContext<'tcx>,
+        expr: &'tcx hir::Expr<'_>,
+        lhs: &'tcx hir::Expr<'_>,
+        ps: &hir::PathSegment<'_>,
+        rhs: &'tcx hir::Expr<'_>,
+    ) {
+        if ps.ident.name == sym::add || ps.ident.name == sym::add_assign {
+            self.manage_bin_ops(cx, expr, hir::BinOpKind::Add, lhs, rhs);
+        } else if ps.ident.name == sym::div || ps.ident.name == sym::div_assign {
+            self.manage_bin_ops(cx, expr, hir::BinOpKind::Div, lhs, rhs);
+        } else if ps.ident.name == sym::mul || ps.ident.name == sym::mul_assign {
+            self.manage_bin_ops(cx, expr, hir::BinOpKind::Mul, lhs, rhs);
+        } else if ps.ident.name == sym::rem || ps.ident.name == sym::rem_assign {
+            self.manage_bin_ops(cx, expr, hir::BinOpKind::Rem, lhs, rhs);
+        } else if ps.ident.name == sym::sub || ps.ident.name == sym::sub_assign {
+            self.manage_bin_ops(cx, expr, hir::BinOpKind::Sub, lhs, rhs);
+        }
+    }
+
     /// Manages when the lint should be triggered. Operations in constant environments, hard coded
-    /// types, custom allowed types and non-constant operations that won't overflow are ignored.
+    /// types, custom allowed types and non-constant operations that don't overflow are ignored.
     fn manage_bin_ops<'tcx>(
         &mut self,
         cx: &LateContext<'tcx>,
         expr: &'tcx hir::Expr<'_>,
-        op: &Spanned<hir::BinOpKind>,
+        op: hir::BinOpKind,
         lhs: &'tcx hir::Expr<'_>,
         rhs: &'tcx hir::Expr<'_>,
     ) {
@@ -180,7 +201,7 @@ impl ArithmeticSideEffects {
             return;
         }
         if !matches!(
-            op.node,
+            op,
             hir::BinOpKind::Add
                 | hir::BinOpKind::Div
                 | hir::BinOpKind::Mul
@@ -204,7 +225,7 @@ impl ArithmeticSideEffects {
             return;
         }
         let has_valid_op = if Self::is_integral(lhs_ty) && Self::is_integral(rhs_ty) {
-            if let hir::BinOpKind::Shl | hir::BinOpKind::Shr = op.node {
+            if let hir::BinOpKind::Shl | hir::BinOpKind::Shr = op {
                 // At least for integers, shifts are already handled by the CTFE
                 return;
             }
@@ -213,7 +234,7 @@ impl ArithmeticSideEffects {
                 Self::literal_integer(cx, actual_rhs),
             ) {
                 (None, None) => false,
-                (None, Some(n)) => match (&op.node, n) {
+                (None, Some(n)) => match (&op, n) {
                     // Division and module are always valid if applied to non-zero integers
                     (hir::BinOpKind::Div | hir::BinOpKind::Rem, local_n) if local_n != 0 => true,
                     // Adding or subtracting zeros is always a no-op
@@ -223,7 +244,7 @@ impl ArithmeticSideEffects {
                     => true,
                     _ => false,
                 },
-                (Some(n), None) => match (&op.node, n) {
+                (Some(n), None) => match (&op, n) {
                     // Adding or subtracting zeros is always a no-op
                     (hir::BinOpKind::Add | hir::BinOpKind::Sub, 0)
                     // Multiplication by 1 or 0 will never overflow
@@ -249,6 +270,7 @@ impl ArithmeticSideEffects {
         &mut self,
         args: &'tcx [hir::Expr<'_>],
         cx: &LateContext<'tcx>,
+        expr: &'tcx hir::Expr<'_>,
         ps: &'tcx hir::PathSegment<'_>,
         receiver: &'tcx hir::Expr<'_>,
     ) {
@@ -262,7 +284,8 @@ impl ArithmeticSideEffects {
         if !Self::is_integral(instance_ty) {
             return;
         }
-        if !self.integer_methods.contains(&ps.ident.name) {
+        self.manage_sugar_methods(cx, expr, receiver, ps, arg);
+        if !self.disallowed_int_methods.contains(&ps.ident.name) {
             return;
         }
         let (actual_arg, _) = peel_hir_expr_refs(arg);
@@ -310,10 +333,10 @@ impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects {
         }
         match &expr.kind {
             hir::ExprKind::AssignOp(op, lhs, rhs) | hir::ExprKind::Binary(op, lhs, rhs) => {
-                self.manage_bin_ops(cx, expr, op, lhs, rhs);
+                self.manage_bin_ops(cx, expr, op.node, lhs, rhs);
             },
             hir::ExprKind::MethodCall(ps, receiver, args, _) => {
-                self.manage_method_call(args, cx, ps, receiver);
+                self.manage_method_call(args, cx, expr, ps, receiver);
             },
             hir::ExprKind::Unary(un_op, un_expr) => {
                 self.manage_unary_ops(cx, expr, un_expr, *un_op);
diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs
index cc61ef9184c..2534e3c8468 100644
--- a/clippy_lints/src/ptr.rs
+++ b/clippy_lints/src/ptr.rs
@@ -11,9 +11,8 @@ use rustc_hir::def_id::DefId;
 use rustc_hir::hir_id::{HirId, HirIdMap};
 use rustc_hir::intravisit::{walk_expr, Visitor};
 use rustc_hir::{
-    self as hir, AnonConst, BinOpKind, BindingMode, Body, Expr, ExprKind, FnRetTy, FnSig, GenericArg,
-    ImplItemKind, ItemKind, Lifetime, Mutability, Node, Param, PatKind, QPath, TraitFn, TraitItem, TraitItemKind,
-    TyKind, Unsafety,
+    self as hir, AnonConst, BinOpKind, BindingMode, Body, Expr, ExprKind, FnRetTy, FnSig, GenericArg, ImplItemKind,
+    ItemKind, Lifetime, Mutability, Node, Param, PatKind, QPath, TraitFn, TraitItem, TraitItemKind, TyKind, Unsafety,
 };
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_infer::traits::{Obligation, ObligationCause};
@@ -687,9 +686,7 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args:
             .filter_map(|(i, arg)| {
                 let param = &body.params[arg.idx];
                 match param.pat.kind {
-                    PatKind::Binding(BindingMode::NONE, id, _, None)
-                        if !is_lint_allowed(cx, PTR_ARG, param.hir_id) =>
-                    {
+                    PatKind::Binding(BindingMode::NONE, id, _, None) if !is_lint_allowed(cx, PTR_ARG, param.hir_id) => {
                         Some((id, i))
                     },
                     _ => {
diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs
index 4ad967589a5..1f1ce147ca2 100644
--- a/clippy_lints/src/question_mark.rs
+++ b/clippy_lints/src/question_mark.rs
@@ -14,8 +14,8 @@ use rustc_errors::Applicability;
 use rustc_hir::def::Res;
 use rustc_hir::LangItem::{self, OptionNone, OptionSome, ResultErr, ResultOk};
 use rustc_hir::{
-    BindingMode, Block, Body, ByRef, Expr, ExprKind, LetStmt, Mutability, Node, PatKind, PathSegment, QPath,
-    Stmt, StmtKind,
+    BindingMode, Block, Body, ByRef, Expr, ExprKind, LetStmt, Mutability, Node, PatKind, PathSegment, QPath, Stmt,
+    StmtKind,
 };
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::Ty;
diff --git a/clippy_lints/src/repeat_vec_with_capacity.rs b/clippy_lints/src/repeat_vec_with_capacity.rs
index a358881bf80..792d8fc88f0 100644
--- a/clippy_lints/src/repeat_vec_with_capacity.rs
+++ b/clippy_lints/src/repeat_vec_with_capacity.rs
@@ -1,7 +1,7 @@
 use clippy_utils::consts::{constant, Constant};
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::higher::VecArgs;
-use clippy_utils::macros::root_macro_call;
+use clippy_utils::macros::matching_root_macro_call;
 use clippy_utils::source::snippet;
 use clippy_utils::{expr_or_init, fn_def_id, match_def_path, paths};
 use rustc_errors::Applicability;
@@ -65,8 +65,7 @@ fn emit_lint(cx: &LateContext<'_>, span: Span, kind: &str, note: &'static str, s
 
 /// Checks `vec![Vec::with_capacity(x); n]`
 fn check_vec_macro(cx: &LateContext<'_>, expr: &Expr<'_>) {
-    if let Some(mac_call) = root_macro_call(expr.span)
-        && cx.tcx.is_diagnostic_item(sym::vec_macro, mac_call.def_id)
+    if matching_root_macro_call(cx, expr.span, sym::vec_macro).is_some()
         && let Some(VecArgs::Repeat(repeat_expr, len_expr)) = VecArgs::hir(cx, expr)
         && fn_def_id(cx, repeat_expr).is_some_and(|did| match_def_path(cx, did, &paths::VEC_WITH_CAPACITY))
         && !len_expr.span.from_expansion()
diff --git a/clippy_lints/src/size_of_ref.rs b/clippy_lints/src/size_of_ref.rs
index 14ca7a3f004..8d7f12af86e 100644
--- a/clippy_lints/src/size_of_ref.rs
+++ b/clippy_lints/src/size_of_ref.rs
@@ -28,7 +28,7 @@ declare_clippy_lint! {
     ///     fn size(&self) -> usize {
     ///         // Note that `&self` as an argument is a `&&Foo`: Because `self`
     ///         // is already a reference, `&self` is a double-reference.
-    ///         // The return value of `size_of_val()` therefor is the
+    ///         // The return value of `size_of_val()` therefore is the
     ///         // size of the reference-type, not the size of `self`.
     ///         std::mem::size_of_val(&self)
     ///     }
diff --git a/clippy_lints/src/slow_vector_initialization.rs b/clippy_lints/src/slow_vector_initialization.rs
index 28c254537ab..b0e25c02265 100644
--- a/clippy_lints/src/slow_vector_initialization.rs
+++ b/clippy_lints/src/slow_vector_initialization.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::macros::root_macro_call;
+use clippy_utils::macros::matching_root_macro_call;
 use clippy_utils::sugg::Sugg;
 use clippy_utils::{
     get_enclosing_block, is_expr_path_def_path, is_integer_literal, is_path_diagnostic_item, path_to_local,
@@ -145,9 +145,7 @@ impl SlowVectorInit {
         // Generally don't warn if the vec initializer comes from an expansion, except for the vec! macro.
         // This lets us still warn on `vec![]`, while ignoring other kinds of macros that may output an
         // empty vec
-        if expr.span.from_expansion()
-            && root_macro_call(expr.span).map(|m| m.def_id) != cx.tcx.get_diagnostic_item(sym::vec_macro)
-        {
+        if expr.span.from_expansion() && matching_root_macro_call(cx, expr.span, sym::vec_macro).is_none() {
             return None;
         }
 
diff --git a/clippy_lints/src/thread_local_initializer_can_be_made_const.rs b/clippy_lints/src/thread_local_initializer_can_be_made_const.rs
index c1e24674e3e..4af3ee74d0e 100644
--- a/clippy_lints/src/thread_local_initializer_can_be_made_const.rs
+++ b/clippy_lints/src/thread_local_initializer_can_be_made_const.rs
@@ -1,13 +1,14 @@
 use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::macros::macro_backtrace;
 use clippy_utils::qualify_min_const_fn::is_min_const_fn;
 use clippy_utils::source::snippet;
 use clippy_utils::{fn_has_unsatisfiable_preds, peel_blocks};
 use rustc_errors::Applicability;
-use rustc_hir::{intravisit, ExprKind};
+use rustc_hir::{intravisit, Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::impl_lint_pass;
-use rustc_span::sym::thread_local_macro;
+use rustc_span::sym::{self, thread_local_macro};
 
 declare_clippy_lint! {
     /// ### What it does
@@ -69,6 +70,26 @@ fn is_thread_local_initializer(
     )
 }
 
+fn is_unreachable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+    if let Some(macro_call) = macro_backtrace(expr.span).next()
+        && let Some(diag_name) = cx.tcx.get_diagnostic_name(macro_call.def_id)
+    {
+        return (matches!(
+            diag_name,
+            sym::core_panic_macro
+                | sym::std_panic_macro
+                | sym::core_panic_2015_macro
+                | sym::std_panic_2015_macro
+                | sym::core_panic_2021_macro
+        ) && !cx.tcx.hir().is_inside_const_context(expr.hir_id))
+            || matches!(
+                diag_name,
+                sym::unimplemented_macro | sym::todo_macro | sym::unreachable_macro | sym::unreachable_2015_macro
+            );
+    }
+    false
+}
+
 #[inline]
 fn initializer_can_be_made_const(cx: &LateContext<'_>, defid: rustc_span::def_id::DefId, msrv: &Msrv) -> bool {
     // Building MIR for `fn`s with unsatisfiable preds results in ICE.
@@ -102,12 +123,17 @@ impl<'tcx> LateLintPass<'tcx> for ThreadLocalInitializerCanBeMadeConst {
             // for details on this issue, see:
             // https://github.com/rust-lang/rust-clippy/pull/12276
             && !cx.tcx.is_const_fn(defid)
-            && initializer_can_be_made_const(cx, defid, &self.msrv)
-            // we know that the function is const-qualifiable, so now
-            // we need only to get the initializer expression to span-lint it.
             && let ExprKind::Block(block, _) = body.value.kind
             && let Some(unpeeled) = block.expr
             && let ret_expr = peel_blocks(unpeeled)
+            // A common pattern around threadlocal! is to make the value unreachable
+            // to force an initialization before usage
+            // https://github.com/rust-lang/rust-clippy/issues/12637
+            // we ensure that this is reachable before we check in mir
+            && !is_unreachable(cx, ret_expr)
+            && initializer_can_be_made_const(cx, defid, &self.msrv)
+            // we know that the function is const-qualifiable, so now
+            // we need only to get the initializer expression to span-lint it.
             && let initializer_snippet = snippet(cx, ret_expr.span, "thread_local! { ... }")
             && initializer_snippet != "thread_local! { ... }"
         {
diff --git a/clippy_lints/src/to_string_trait_impl.rs b/clippy_lints/src/to_string_trait_impl.rs
index 59ae185c9de..0361836cdec 100644
--- a/clippy_lints/src/to_string_trait_impl.rs
+++ b/clippy_lints/src/to_string_trait_impl.rs
@@ -38,7 +38,7 @@ declare_clippy_lint! {
     ///   }
     /// }
     /// ```
-    #[clippy::version = "1.77.0"]
+    #[clippy::version = "1.78.0"]
     pub TO_STRING_TRAIT_IMPL,
     style,
     "check for direct implementations of `ToString`"
diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs
index 9468d367a92..c05cd9ed593 100644
--- a/clippy_lints/src/trait_bounds.rs
+++ b/clippy_lints/src/trait_bounds.rs
@@ -237,6 +237,7 @@ impl TraitBounds {
         }
     }
 
+    #[allow(clippy::mutable_key_type)]
     fn check_type_repetition<'tcx>(&self, cx: &LateContext<'tcx>, gen: &'tcx Generics<'_>) {
         struct SpanlessTy<'cx, 'tcx> {
             ty: &'tcx Ty<'tcx>,
diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs
index 0802cb2b7c7..5e45ab211ef 100644
--- a/clippy_lints/src/types/mod.rs
+++ b/clippy_lints/src/types/mod.rs
@@ -12,8 +12,8 @@ mod vec_box;
 use rustc_hir as hir;
 use rustc_hir::intravisit::FnKind;
 use rustc_hir::{
-    Body, FnDecl, FnRetTy, GenericArg, ImplItem, ImplItemKind, Item, ItemKind, LetStmt, MutTy, QPath, TraitItem,
-    TraitItemKind, TyKind,
+    Body, FnDecl, FnRetTy, GenericArg, ImplItem, ImplItemKind, Item, ItemKind, LetStmt, MutTy, QPath, TraitFn,
+    TraitItem, TraitItemKind, TyKind,
 };
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::impl_lint_pass;
@@ -420,7 +420,13 @@ impl<'tcx> LateLintPass<'tcx> for Types {
             TraitItemKind::Const(ty, _) | TraitItemKind::Type(_, Some(ty)) => {
                 self.check_ty(cx, ty, context);
             },
-            TraitItemKind::Fn(ref sig, _) => self.check_fn_decl(cx, sig.decl, context),
+            TraitItemKind::Fn(ref sig, trait_method) => {
+                // Check only methods without body
+                // Methods with body are covered by check_fn.
+                if let TraitFn::Required(_) = trait_method {
+                    self.check_fn_decl(cx, sig.decl, context);
+                }
+            },
             TraitItemKind::Type(..) => (),
         }
     }
diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs
index 7f0769452c7..4448c9ae3df 100644
--- a/clippy_lints/src/utils/author.rs
+++ b/clippy_lints/src/utils/author.rs
@@ -7,8 +7,8 @@ use rustc_ast::LitIntType;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_hir as hir;
 use rustc_hir::{
-    ArrayLen, BindingMode, CaptureBy, Closure, ClosureKind, CoroutineKind, ExprKind, FnRetTy, HirId, Lit,
-    PatKind, QPath, StmtKind, TyKind,
+    ArrayLen, BindingMode, CaptureBy, Closure, ClosureKind, CoroutineKind, ExprKind, FnRetTy, HirId, Lit, PatKind,
+    QPath, StmtKind, TyKind,
 };
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_session::declare_lint_pass;
diff --git a/clippy_lints/src/vec.rs b/clippy_lints/src/vec.rs
index 27ead55bf39..9edf7579d48 100644
--- a/clippy_lints/src/vec.rs
+++ b/clippy_lints/src/vec.rs
@@ -7,7 +7,7 @@ use clippy_utils::diagnostics::span_lint_hir_and_then;
 use clippy_utils::source::snippet_opt;
 use clippy_utils::ty::is_copy;
 use clippy_utils::visitors::for_each_local_use_after_expr;
-use clippy_utils::{get_parent_expr, higher, is_trait_method};
+use clippy_utils::{get_parent_expr, higher, is_in_test, is_trait_method};
 use rustc_errors::Applicability;
 use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, LetStmt, Mutability, Node, Pat, PatKind};
 use rustc_lint::{LateContext, LateLintPass};
@@ -22,6 +22,7 @@ pub struct UselessVec {
     pub too_large_for_stack: u64,
     pub msrv: Msrv,
     pub span_to_lint_map: BTreeMap<Span, Option<(HirId, SuggestedType, String, Applicability)>>,
+    pub allow_in_test: bool,
 }
 
 declare_clippy_lint! {
@@ -57,6 +58,9 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec {
         let Some(vec_args) = higher::VecArgs::hir(cx, expr.peel_borrows()) else {
             return;
         };
+        if self.allow_in_test && is_in_test(cx.tcx, expr.hir_id) {
+            return;
+        };
         // the parent callsite of this `vec!` expression, or span to the borrowed one such as `&vec!`
         let callsite = expr.span.parent_callsite().unwrap_or(expr.span);
 
diff --git a/clippy_lints/src/vec_init_then_push.rs b/clippy_lints/src/vec_init_then_push.rs
index c46f0298cc8..a599415a2dd 100644
--- a/clippy_lints/src/vec_init_then_push.rs
+++ b/clippy_lints/src/vec_init_then_push.rs
@@ -6,9 +6,7 @@ use clippy_utils::{get_parent_expr, path_to_local_id};
 use core::ops::ControlFlow;
 use rustc_errors::Applicability;
 use rustc_hir::def::Res;
-use rustc_hir::{
-    BindingMode, Block, Expr, ExprKind, HirId, LetStmt, Mutability, PatKind, QPath, Stmt, StmtKind, UnOp,
-};
+use rustc_hir::{BindingMode, Block, Expr, ExprKind, HirId, LetStmt, Mutability, PatKind, QPath, Stmt, StmtKind, UnOp};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_session::impl_lint_pass;