about summary refs log tree commit diff
path: root/clippy_lints/src
diff options
context:
space:
mode:
authorPhilipp Krones <hello@philkrones.com>2022-06-30 10:50:09 +0200
committerPhilipp Krones <hello@philkrones.com>2022-06-30 10:50:09 +0200
commit09f5df5087c1d045db3bbf1b886702ec343a2f61 (patch)
tree5f7eecd245841b64223f85e99b269c9dd3b55f18 /clippy_lints/src
parentee37029afa62e93ab5ec8b1f2a23f8dbfd2a453f (diff)
Merge commit '0cb0f7636851f9fcc57085cf80197a2ef6db098f' into clippyup
Diffstat (limited to 'clippy_lints/src')
-rw-r--r--clippy_lints/src/assign_ops.rs235
-rw-r--r--clippy_lints/src/attrs.rs20
-rw-r--r--clippy_lints/src/booleans.rs8
-rw-r--r--clippy_lints/src/default_instead_of_iter_empty.rs68
-rw-r--r--clippy_lints/src/deprecated_lints.rs15
-rw-r--r--clippy_lints/src/dereference.rs697
-rw-r--r--clippy_lints/src/double_comparison.rs96
-rw-r--r--clippy_lints/src/duration_subsec.rs75
-rw-r--r--clippy_lints/src/enum_variants.rs7
-rw-r--r--clippy_lints/src/eq_op.rs319
-rw-r--r--clippy_lints/src/erasing_op.rs77
-rw-r--r--clippy_lints/src/escape.rs5
-rw-r--r--clippy_lints/src/float_equality_without_abs.rs116
-rw-r--r--clippy_lints/src/implicit_return.rs39
-rw-r--r--clippy_lints/src/integer_division.rs61
-rw-r--r--clippy_lints/src/large_const_arrays.rs2
-rw-r--r--clippy_lints/src/let_underscore.rs7
-rw-r--r--clippy_lints/src/lib.register_all.rs39
-rw-r--r--clippy_lints/src/lib.register_complexity.rs9
-rw-r--r--clippy_lints/src/lib.register_correctness.rs16
-rw-r--r--clippy_lints/src/lib.register_internal.rs1
-rw-r--r--clippy_lints/src/lib.register_lints.rs57
-rw-r--r--clippy_lints/src/lib.register_pedantic.rs6
-rw-r--r--clippy_lints/src/lib.register_perf.rs3
-rw-r--r--clippy_lints/src/lib.register_restriction.rs10
-rw-r--r--clippy_lints/src/lib.register_style.rs7
-rw-r--r--clippy_lints/src/lib.register_suspicious.rs4
-rw-r--r--clippy_lints/src/lib.rs95
-rw-r--r--clippy_lints/src/lifetimes.rs4
-rw-r--r--clippy_lints/src/loops/manual_find.rs158
-rw-r--r--clippy_lints/src/loops/mod.rs43
-rw-r--r--clippy_lints/src/loops/while_let_loop.rs106
-rw-r--r--clippy_lints/src/macro_use.rs42
-rw-r--r--clippy_lints/src/manual_non_exhaustive.rs9
-rw-r--r--clippy_lints/src/manual_rem_euclid.rs123
-rw-r--r--clippy_lints/src/manual_retain.rs228
-rw-r--r--clippy_lints/src/matches/match_same_arms.rs2
-rw-r--r--clippy_lints/src/matches/match_single_binding.rs4
-rw-r--r--clippy_lints/src/matches/match_str_case_mismatch.rs2
-rw-r--r--clippy_lints/src/matches/mod.rs4
-rw-r--r--clippy_lints/src/matches/redundant_pattern_match.rs88
-rw-r--r--clippy_lints/src/matches/significant_drop_in_scrutinee.rs162
-rw-r--r--clippy_lints/src/matches/single_match.rs85
-rw-r--r--clippy_lints/src/methods/map_flatten.rs7
-rw-r--r--clippy_lints/src/misc.rs483
-rw-r--r--clippy_lints/src/needless_bitwise_bool.rs85
-rw-r--r--clippy_lints/src/neg_multiply.rs9
-rw-r--r--clippy_lints/src/non_copy_const.rs13
-rw-r--r--clippy_lints/src/non_expressive_names.rs12
-rw-r--r--clippy_lints/src/numeric_arithmetic.rs170
-rw-r--r--clippy_lints/src/operators/absurd_extreme_comparisons.rs (renamed from clippy_lints/src/absurd_extreme_comparisons.rs)103
-rw-r--r--clippy_lints/src/operators/assign_op_pattern.rs101
-rw-r--r--clippy_lints/src/operators/bit_mask.rs (renamed from clippy_lints/src/bit_mask.rs)168
-rw-r--r--clippy_lints/src/operators/cmp_nan.rs30
-rw-r--r--clippy_lints/src/operators/cmp_owned.rs147
-rw-r--r--clippy_lints/src/operators/double_comparison.rs54
-rw-r--r--clippy_lints/src/operators/duration_subsec.rs44
-rw-r--r--clippy_lints/src/operators/eq_op.rs45
-rw-r--r--clippy_lints/src/operators/erasing_op.rs53
-rw-r--r--clippy_lints/src/operators/float_cmp.rs139
-rw-r--r--clippy_lints/src/operators/float_equality_without_abs.rs71
-rw-r--r--clippy_lints/src/operators/identity_op.rs (renamed from clippy_lints/src/identity_op.rs)87
-rw-r--r--clippy_lints/src/operators/integer_division.rs27
-rw-r--r--clippy_lints/src/operators/misrefactored_assign_op.rs84
-rw-r--r--clippy_lints/src/operators/mod.rs849
-rw-r--r--clippy_lints/src/operators/modulo_arithmetic.rs (renamed from clippy_lints/src/modulo_arithmetic.rs)74
-rw-r--r--clippy_lints/src/operators/modulo_one.rs26
-rw-r--r--clippy_lints/src/operators/needless_bitwise_bool.rs36
-rw-r--r--clippy_lints/src/operators/numeric_arithmetic.rs127
-rw-r--r--clippy_lints/src/operators/op_ref.rs218
-rw-r--r--clippy_lints/src/operators/ptr_eq.rs65
-rw-r--r--clippy_lints/src/operators/self_assignment.rs20
-rw-r--r--clippy_lints/src/operators/verbose_bit_mask.rs44
-rw-r--r--clippy_lints/src/pass_by_ref_or_value.rs104
-rw-r--r--clippy_lints/src/ptr.rs50
-rw-r--r--clippy_lints/src/ptr_eq.rs97
-rw-r--r--clippy_lints/src/redundant_static_lifetimes.rs2
-rw-r--r--clippy_lints/src/returns.rs71
-rw-r--r--clippy_lints/src/self_assignment.rs56
-rw-r--r--clippy_lints/src/slow_vector_initialization.rs39
-rw-r--r--clippy_lints/src/strings.rs6
-rw-r--r--clippy_lints/src/transmute/mod.rs25
-rw-r--r--clippy_lints/src/transmute/transmute_ptr_to_ref.rs56
-rw-r--r--clippy_lints/src/transmute/utils.rs28
-rw-r--r--clippy_lints/src/unused_async.rs28
-rw-r--r--clippy_lints/src/unwrap.rs8
-rw-r--r--clippy_lints/src/utils/internal_lints.rs107
-rw-r--r--clippy_lints/src/utils/internal_lints/metadata_collector.rs11
-rw-r--r--clippy_lints/src/vec_resize_to_zero.rs5
89 files changed, 4282 insertions, 2956 deletions
diff --git a/clippy_lints/src/assign_ops.rs b/clippy_lints/src/assign_ops.rs
deleted file mode 100644
index f81da2d4223..00000000000
--- a/clippy_lints/src/assign_ops.rs
+++ /dev/null
@@ -1,235 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::source::snippet_opt;
-use clippy_utils::ty::implements_trait;
-use clippy_utils::{binop_traits, sugg};
-use clippy_utils::{eq_expr_value, trait_ref_of_method};
-use if_chain::if_chain;
-use rustc_errors::Applicability;
-use rustc_hir as hir;
-use rustc_hir::intravisit::{walk_expr, Visitor};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for `a = a op b` or `a = b commutative_op a`
-    /// patterns.
-    ///
-    /// ### Why is this bad?
-    /// These can be written as the shorter `a op= b`.
-    ///
-    /// ### Known problems
-    /// While forbidden by the spec, `OpAssign` traits may have
-    /// implementations that differ from the regular `Op` impl.
-    ///
-    /// ### Example
-    /// ```rust
-    /// let mut a = 5;
-    /// let b = 0;
-    /// // ...
-    ///
-    /// a = a + b;
-    /// ```
-    ///
-    /// Use instead:
-    /// ```rust
-    /// let mut a = 5;
-    /// let b = 0;
-    /// // ...
-    ///
-    /// a += b;
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub ASSIGN_OP_PATTERN,
-    style,
-    "assigning the result of an operation on a variable to that same variable"
-}
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for `a op= a op b` or `a op= b op a` patterns.
-    ///
-    /// ### Why is this bad?
-    /// Most likely these are bugs where one meant to write `a
-    /// op= b`.
-    ///
-    /// ### Known problems
-    /// Clippy cannot know for sure if `a op= a op b` should have
-    /// been `a = a op a op b` or `a = a op b`/`a op= b`. Therefore, it suggests both.
-    /// If `a op= a op b` is really the correct behavior it should be
-    /// written as `a = a op a op b` as it's less confusing.
-    ///
-    /// ### Example
-    /// ```rust
-    /// let mut a = 5;
-    /// let b = 2;
-    /// // ...
-    /// a += a + b;
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub MISREFACTORED_ASSIGN_OP,
-    suspicious,
-    "having a variable on both sides of an assign op"
-}
-
-declare_lint_pass!(AssignOps => [ASSIGN_OP_PATTERN, MISREFACTORED_ASSIGN_OP]);
-
-impl<'tcx> LateLintPass<'tcx> for AssignOps {
-    #[allow(clippy::too_many_lines)]
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
-        match &expr.kind {
-            hir::ExprKind::AssignOp(op, lhs, rhs) => {
-                if let hir::ExprKind::Binary(binop, l, r) = &rhs.kind {
-                    if op.node != binop.node {
-                        return;
-                    }
-                    // lhs op= l op r
-                    if eq_expr_value(cx, lhs, l) {
-                        lint_misrefactored_assign_op(cx, expr, *op, rhs, lhs, r);
-                    }
-                    // lhs op= l commutative_op r
-                    if is_commutative(op.node) && eq_expr_value(cx, lhs, r) {
-                        lint_misrefactored_assign_op(cx, expr, *op, rhs, lhs, l);
-                    }
-                }
-            },
-            hir::ExprKind::Assign(assignee, e, _) => {
-                if let hir::ExprKind::Binary(op, l, r) = &e.kind {
-                    let lint = |assignee: &hir::Expr<'_>, rhs: &hir::Expr<'_>| {
-                        let ty = cx.typeck_results().expr_ty(assignee);
-                        let rty = cx.typeck_results().expr_ty(rhs);
-                        if_chain! {
-                            if let Some((_, lang_item)) = binop_traits(op.node);
-                            if let Ok(trait_id) = cx.tcx.lang_items().require(lang_item);
-                            let parent_fn = cx.tcx.hir().get_parent_item(e.hir_id);
-                            if trait_ref_of_method(cx, parent_fn)
-                                .map_or(true, |t| t.path.res.def_id() != trait_id);
-                            if implements_trait(cx, ty, trait_id, &[rty.into()]);
-                            then {
-                                span_lint_and_then(
-                                    cx,
-                                    ASSIGN_OP_PATTERN,
-                                    expr.span,
-                                    "manual implementation of an assign operation",
-                                    |diag| {
-                                        if let (Some(snip_a), Some(snip_r)) =
-                                            (snippet_opt(cx, assignee.span), snippet_opt(cx, rhs.span))
-                                        {
-                                            diag.span_suggestion(
-                                                expr.span,
-                                                "replace it with",
-                                                format!("{} {}= {}", snip_a, op.node.as_str(), snip_r),
-                                                Applicability::MachineApplicable,
-                                            );
-                                        }
-                                    },
-                                );
-                            }
-                        }
-                    };
-
-                    let mut visitor = ExprVisitor {
-                        assignee,
-                        counter: 0,
-                        cx,
-                    };
-
-                    walk_expr(&mut visitor, e);
-
-                    if visitor.counter == 1 {
-                        // a = a op b
-                        if eq_expr_value(cx, assignee, l) {
-                            lint(assignee, r);
-                        }
-                        // a = b commutative_op a
-                        // Limited to primitive type as these ops are know to be commutative
-                        if eq_expr_value(cx, assignee, r) && cx.typeck_results().expr_ty(assignee).is_primitive_ty() {
-                            match op.node {
-                                hir::BinOpKind::Add
-                                | hir::BinOpKind::Mul
-                                | hir::BinOpKind::And
-                                | hir::BinOpKind::Or
-                                | hir::BinOpKind::BitXor
-                                | hir::BinOpKind::BitAnd
-                                | hir::BinOpKind::BitOr => {
-                                    lint(assignee, l);
-                                },
-                                _ => {},
-                            }
-                        }
-                    }
-                }
-            },
-            _ => {},
-        }
-    }
-}
-
-fn lint_misrefactored_assign_op(
-    cx: &LateContext<'_>,
-    expr: &hir::Expr<'_>,
-    op: hir::BinOp,
-    rhs: &hir::Expr<'_>,
-    assignee: &hir::Expr<'_>,
-    rhs_other: &hir::Expr<'_>,
-) {
-    span_lint_and_then(
-        cx,
-        MISREFACTORED_ASSIGN_OP,
-        expr.span,
-        "variable appears on both sides of an assignment operation",
-        |diag| {
-            if let (Some(snip_a), Some(snip_r)) = (snippet_opt(cx, assignee.span), snippet_opt(cx, rhs_other.span)) {
-                let a = &sugg::Sugg::hir(cx, assignee, "..");
-                let r = &sugg::Sugg::hir(cx, rhs, "..");
-                let long = format!("{} = {}", snip_a, sugg::make_binop(op.node.into(), a, r));
-                diag.span_suggestion(
-                    expr.span,
-                    &format!(
-                        "did you mean `{} = {} {} {}` or `{}`? Consider replacing it with",
-                        snip_a,
-                        snip_a,
-                        op.node.as_str(),
-                        snip_r,
-                        long
-                    ),
-                    format!("{} {}= {}", snip_a, op.node.as_str(), snip_r),
-                    Applicability::MaybeIncorrect,
-                );
-                diag.span_suggestion(
-                    expr.span,
-                    "or",
-                    long,
-                    Applicability::MaybeIncorrect, // snippet
-                );
-            }
-        },
-    );
-}
-
-#[must_use]
-fn is_commutative(op: hir::BinOpKind) -> bool {
-    use rustc_hir::BinOpKind::{
-        Add, And, BitAnd, BitOr, BitXor, Div, Eq, Ge, Gt, Le, Lt, Mul, Ne, Or, Rem, Shl, Shr, Sub,
-    };
-    match op {
-        Add | Mul | And | Or | BitXor | BitAnd | BitOr | Eq | Ne => true,
-        Sub | Div | Rem | Shl | Shr | Lt | Le | Ge | Gt => false,
-    }
-}
-
-struct ExprVisitor<'a, 'tcx> {
-    assignee: &'a hir::Expr<'a>,
-    counter: u8,
-    cx: &'a LateContext<'tcx>,
-}
-
-impl<'a, 'tcx> Visitor<'tcx> for ExprVisitor<'a, 'tcx> {
-    fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
-        if eq_expr_value(self.cx, self.assignee, expr) {
-            self.counter += 1;
-        }
-
-        walk_expr(self, expr);
-    }
-}
diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs
index ed12ad9c367..4bcbeacf9fe 100644
--- a/clippy_lints/src/attrs.rs
+++ b/clippy_lints/src/attrs.rs
@@ -78,10 +78,17 @@ declare_clippy_lint! {
     /// Checks for `extern crate` and `use` items annotated with
     /// lint attributes.
     ///
-    /// This lint permits `#[allow(unused_imports)]`, `#[allow(deprecated)]`,
-    /// `#[allow(unreachable_pub)]`, `#[allow(clippy::wildcard_imports)]` and
-    /// `#[allow(clippy::enum_glob_use)]` on `use` items and `#[allow(unused_imports)]` on
-    /// `extern crate` items with a `#[macro_use]` attribute.
+    /// This lint permits lint attributes for lints emitted on the items themself.
+    /// For `use` items these lints are:
+    /// * deprecated
+    /// * unreachable_pub
+    /// * unused_imports
+    /// * clippy::enum_glob_use
+    /// * clippy::macro_use_imports
+    /// * clippy::wildcard_imports
+    ///
+    /// For `extern crate` items these lints are:
+    /// * `unused_imports` on items with `#[macro_use]`
     ///
     /// ### Why is this bad?
     /// Lint attributes have no effect on crate imports. Most
@@ -347,7 +354,10 @@ impl<'tcx> LateLintPass<'tcx> for Attributes {
                                             || extract_clippy_lint(lint).map_or(false, |s| {
                                                 matches!(
                                                     s.as_str(),
-                                                    "wildcard_imports" | "enum_glob_use" | "redundant_pub_crate",
+                                                    "wildcard_imports"
+                                                        | "enum_glob_use"
+                                                        | "redundant_pub_crate"
+                                                        | "macro_use_imports",
                                                 )
                                             })
                                         {
diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs
index e4e122ba6eb..526ee2f891a 100644
--- a/clippy_lints/src/booleans.rs
+++ b/clippy_lints/src/booleans.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
 use clippy_utils::source::snippet_opt;
 use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
 use clippy_utils::{eq_expr_value, get_trait_def_id, paths};
@@ -394,9 +394,10 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> {
                         continue 'simplified;
                     }
                     if stats.terminals[i] != 0 && simplified_stats.terminals[i] == 0 {
-                        span_lint_and_then(
+                        span_lint_hir_and_then(
                             self.cx,
                             LOGIC_BUG,
+                            e.hir_id,
                             e.span,
                             "this boolean expression contains a logic bug",
                             |diag| {
@@ -429,9 +430,10 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> {
                 }
             }
             let nonminimal_bool_lint = |suggestions: Vec<_>| {
-                span_lint_and_then(
+                span_lint_hir_and_then(
                     self.cx,
                     NONMINIMAL_BOOL,
+                    e.hir_id,
                     e.span,
                     "this boolean expression can be simplified",
                     |diag| {
diff --git a/clippy_lints/src/default_instead_of_iter_empty.rs b/clippy_lints/src/default_instead_of_iter_empty.rs
new file mode 100644
index 00000000000..3c996d3d2ae
--- /dev/null
+++ b/clippy_lints/src/default_instead_of_iter_empty.rs
@@ -0,0 +1,68 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::last_path_segment;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::{match_def_path, paths};
+use rustc_errors::Applicability;
+use rustc_hir::{def, Expr, ExprKind, GenericArg, QPath, TyKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// It checks for `std::iter::Empty::default()` and suggests replacing it with
+    /// `std::iter::empty()`.
+    /// ### Why is this bad?
+    /// `std::iter::empty()` is the more idiomatic way.
+    /// ### Example
+    /// ```rust
+    /// let _ = std::iter::Empty::<usize>::default();
+    /// let iter: std::iter::Empty<usize> = std::iter::Empty::default();
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// let _ = std::iter::empty::<usize>();
+    /// let iter: std::iter::Empty<usize> = std::iter::empty();
+    /// ```
+    #[clippy::version = "1.63.0"]
+    pub DEFAULT_INSTEAD_OF_ITER_EMPTY,
+    style,
+    "check `std::iter::Empty::default()` and replace with `std::iter::empty()`"
+}
+declare_lint_pass!(DefaultIterEmpty => [DEFAULT_INSTEAD_OF_ITER_EMPTY]);
+
+impl<'tcx> LateLintPass<'tcx> for DefaultIterEmpty {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+        if let ExprKind::Call(iter_expr, []) = &expr.kind
+            && let ExprKind::Path(QPath::TypeRelative(ty, _)) = &iter_expr.kind
+            && let TyKind::Path(ty_path) = &ty.kind
+            && let QPath::Resolved(None, path) = ty_path
+            && let def::Res::Def(_, def_id) = &path.res
+            && match_def_path(cx, *def_id, &paths::ITER_EMPTY)
+        {
+            let mut applicability = Applicability::MachineApplicable;
+            let sugg = make_sugg(cx, ty_path, &mut applicability);
+            span_lint_and_sugg(
+                cx,
+                DEFAULT_INSTEAD_OF_ITER_EMPTY,
+                expr.span,
+                "`std::iter::empty()` is the more idiomatic way",
+                "try",
+                sugg,
+                applicability,
+            );
+        }
+    }
+}
+
+fn make_sugg(cx: &LateContext<'_>, ty_path: &rustc_hir::QPath<'_>, applicability: &mut Applicability) -> String {
+    if let Some(last) = last_path_segment(ty_path).args
+        && let Some(iter_ty) = last.args.iter().find_map(|arg| match arg {
+            GenericArg::Type(ty) => Some(ty),
+            _ => None,
+        })
+    {
+        format!("std::iter::empty::<{}>()", snippet_with_applicability(cx, iter_ty.span, "..", applicability))
+    } else {
+        "std::iter::empty()".to_owned()
+    }
+}
diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs
index 5d5ea0f49c8..9aa5af3190f 100644
--- a/clippy_lints/src/deprecated_lints.rs
+++ b/clippy_lints/src/deprecated_lints.rs
@@ -1,16 +1,21 @@
-// NOTE: if you add a deprecated lint in this file, please add a corresponding test in
-// tests/ui/deprecated.rs
+// NOTE: Entries should be created with `cargo dev deprecate`
 
 /// This struct fakes the `Lint` declaration that is usually created by `declare_lint!`. This
 /// enables the simple extraction of the metadata without changing the current deprecation
 /// declaration.
-pub struct ClippyDeprecatedLint;
+pub struct ClippyDeprecatedLint {
+    #[allow(dead_code)]
+    pub desc: &'static str,
+}
 
+#[macro_export]
 macro_rules! declare_deprecated_lint {
-    { $(#[$attr:meta])* pub $name: ident, $_reason: expr} => {
+    { $(#[$attr:meta])* pub $name: ident, $reason: literal} => {
         $(#[$attr])*
         #[allow(dead_code)]
-        pub static $name: ClippyDeprecatedLint = ClippyDeprecatedLint {};
+        pub static $name: ClippyDeprecatedLint = ClippyDeprecatedLint {
+            desc: $reason
+        };
     }
 }
 
diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs
index b47441eff37..59dcc1ebf19 100644
--- a/clippy_lints/src/dereference.rs
+++ b/clippy_lints/src/dereference.rs
@@ -1,20 +1,24 @@
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
 use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
 use clippy_utils::sugg::has_enclosing_paren;
-use clippy_utils::ty::peel_mid_ty_refs;
-use clippy_utils::{get_parent_expr, get_parent_node, is_lint_allowed, path_to_local};
+use clippy_utils::ty::{expr_sig, peel_mid_ty_refs, variant_of_res};
+use clippy_utils::{get_parent_expr, is_lint_allowed, path_to_local, walk_to_expr_usage};
 use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX};
 use rustc_data_structures::fx::FxIndexMap;
 use rustc_errors::Applicability;
+use rustc_hir::intravisit::{walk_ty, Visitor};
 use rustc_hir::{
-    BindingAnnotation, Body, BodyId, BorrowKind, Destination, Expr, ExprKind, HirId, MatchSource, Mutability, Node,
-    Pat, PatKind, UnOp,
+    self as hir, BindingAnnotation, Body, BodyId, BorrowKind, Expr, ExprKind, GenericArg, HirId, ImplItem,
+    ImplItemKind, Item, ItemKind, Local, MatchSource, Mutability, Node, Pat, PatKind, Path, QPath, TraitItem,
+    TraitItemKind, TyKind, UnOp,
 };
+use rustc_infer::infer::TyCtxtInferExt;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
-use rustc_middle::ty::{self, Ty, TyCtxt, TypeckResults};
+use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeckResults};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::{symbol::sym, Span};
+use rustc_span::{symbol::sym, Span, Symbol};
+use rustc_trait_selection::infer::InferCtxtExt;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -104,10 +108,34 @@ declare_clippy_lint! {
     "`ref` binding to a reference"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for dereferencing expressions which would be covered by auto-deref.
+    ///
+    /// ### Why is this bad?
+    /// This unnecessarily complicates the code.
+    ///
+    /// ### Example
+    /// ```rust
+    /// let x = String::new();
+    /// let y: &str = &*x;
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// let x = String::new();
+    /// let y: &str = &x;
+    /// ```
+    #[clippy::version = "1.60.0"]
+    pub EXPLICIT_AUTO_DEREF,
+    complexity,
+    "dereferencing when the compiler would automatically dereference"
+}
+
 impl_lint_pass!(Dereferencing => [
     EXPLICIT_DEREF_METHODS,
     NEEDLESS_BORROW,
     REF_BINDING_TO_REFERENCE,
+    EXPLICIT_AUTO_DEREF,
 ]);
 
 #[derive(Default)]
@@ -136,6 +164,12 @@ struct StateData {
     /// Span of the top level expression
     span: Span,
     hir_id: HirId,
+    position: Position,
+}
+
+struct DerefedBorrow {
+    count: usize,
+    msg: &'static str,
 }
 
 enum State {
@@ -147,11 +181,19 @@ enum State {
         /// The required mutability
         target_mut: Mutability,
     },
-    DerefedBorrow {
-        count: usize,
-        required_precedence: i8,
-        msg: &'static str,
+    DerefedBorrow(DerefedBorrow),
+    ExplicitDeref {
+        // Span and id of the top-level deref expression if the parent expression is a borrow.
+        deref_span_id: Option<(Span, HirId)>,
+    },
+    ExplicitDerefField {
+        name: Symbol,
     },
+    Reborrow {
+        deref_span: Span,
+        deref_hir_id: HirId,
+    },
+    Borrow,
 }
 
 // A reference operation considered by this lint pass
@@ -207,13 +249,28 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
 
         match (self.state.take(), kind) {
             (None, kind) => {
-                let parent = get_parent_node(cx.tcx, expr.hir_id);
                 let expr_ty = typeck.expr_ty(expr);
+                let (position, adjustments) = walk_parents(cx, expr);
 
                 match kind {
+                    RefOp::Deref => {
+                        if let Position::FieldAccess(name) = position
+                            && !ty_contains_field(typeck.expr_ty(sub_expr), name)
+                        {
+                            self.state = Some((
+                                State::ExplicitDerefField { name },
+                                StateData { span: expr.span, hir_id: expr.hir_id, position },
+                            ));
+                        } else if position.is_deref_stable() {
+                            self.state = Some((
+                                State::ExplicitDeref { deref_span_id: None },
+                                StateData { span: expr.span, hir_id: expr.hir_id, position },
+                            ));
+                        }
+                    }
                     RefOp::Method(target_mut)
                         if !is_lint_allowed(cx, EXPLICIT_DEREF_METHODS, expr.hir_id)
-                            && is_linted_explicit_deref_position(parent, expr.hir_id, expr.span) =>
+                            && position.lint_explicit_deref() =>
                     {
                         self.state = Some((
                             State::DerefMethod {
@@ -228,12 +285,13 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
                             StateData {
                                 span: expr.span,
                                 hir_id: expr.hir_id,
+                                position
                             },
                         ));
                     },
                     RefOp::AddrOf => {
                         // Find the number of times the borrow is auto-derefed.
-                        let mut iter = find_adjustments(cx.tcx, typeck, expr).iter();
+                        let mut iter = adjustments.iter();
                         let mut deref_count = 0usize;
                         let next_adjust = loop {
                             match iter.next() {
@@ -274,40 +332,43 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
                             "this expression creates a reference which is immediately dereferenced by the compiler";
                         let borrow_msg = "this expression borrows a value the compiler would automatically borrow";
 
-                        let (required_refs, required_precedence, msg) = if is_auto_borrow_position(parent, expr.hir_id)
-                        {
-                            (1, PREC_POSTFIX, if deref_count == 1 { borrow_msg } else { deref_msg })
+                        let (required_refs, msg) = if position.can_auto_borrow() {
+                            (1, if deref_count == 1 { borrow_msg } else { deref_msg })
                         } else if let Some(&Adjust::Borrow(AutoBorrow::Ref(_, mutability))) =
                             next_adjust.map(|a| &a.kind)
                         {
-                            if matches!(mutability, AutoBorrowMutability::Mut { .. })
-                                && !is_auto_reborrow_position(parent)
+                            if matches!(mutability, AutoBorrowMutability::Mut { .. }) && !position.is_reborrow_stable()
                             {
-                                (3, 0, deref_msg)
+                                (3, deref_msg)
                             } else {
-                                (2, 0, deref_msg)
+                                (2, deref_msg)
                             }
                         } else {
-                            (2, 0, deref_msg)
+                            (2, deref_msg)
                         };
 
                         if deref_count >= required_refs {
                             self.state = Some((
-                                State::DerefedBorrow {
+                                State::DerefedBorrow(DerefedBorrow {
                                     // One of the required refs is for the current borrow expression, the remaining ones
                                     // can't be removed without breaking the code. See earlier comment.
                                     count: deref_count - required_refs,
-                                    required_precedence,
                                     msg,
-                                },
+                                }),
+                                StateData { span: expr.span, hir_id: expr.hir_id, position },
+                            ));
+                        } else if position.is_deref_stable() {
+                            self.state = Some((
+                                State::Borrow,
                                 StateData {
                                     span: expr.span,
                                     hir_id: expr.hir_id,
+                                    position
                                 },
                             ));
                         }
                     },
-                    _ => (),
+                    RefOp::Method(..) => (),
                 }
             },
             (
@@ -334,26 +395,90 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
                     data,
                 ));
             },
+            (Some((State::DerefedBorrow(state), data)), RefOp::AddrOf) if state.count != 0 => {
+                self.state = Some((
+                    State::DerefedBorrow(DerefedBorrow {
+                        count: state.count - 1,
+                        ..state
+                    }),
+                    data,
+                ));
+            },
+            (Some((State::DerefedBorrow(state), data)), RefOp::AddrOf) => {
+                let position = data.position;
+                report(cx, expr, State::DerefedBorrow(state), data);
+                if position.is_deref_stable() {
+                    self.state = Some((
+                        State::Borrow,
+                        StateData {
+                            span: expr.span,
+                            hir_id: expr.hir_id,
+                            position,
+                        },
+                    ));
+                }
+            },
+            (Some((State::DerefedBorrow(state), data)), RefOp::Deref) => {
+                let position = data.position;
+                report(cx, expr, State::DerefedBorrow(state), data);
+                if let Position::FieldAccess(name) = position
+                    && !ty_contains_field(typeck.expr_ty(sub_expr), name)
+                {
+                    self.state = Some((
+                        State::ExplicitDerefField { name },
+                        StateData { span: expr.span, hir_id: expr.hir_id, position },
+                    ));
+                } else if position.is_deref_stable() {
+                    self.state = Some((
+                        State::ExplicitDeref { deref_span_id: None },
+                        StateData { span: expr.span, hir_id: expr.hir_id, position },
+                    ));
+                }
+            },
+
+            (Some((State::Borrow, data)), RefOp::Deref) => {
+                if typeck.expr_ty(sub_expr).is_ref() {
+                    self.state = Some((
+                        State::Reborrow {
+                            deref_span: expr.span,
+                            deref_hir_id: expr.hir_id,
+                        },
+                        data,
+                    ));
+                } else {
+                    self.state = Some((
+                        State::ExplicitDeref {
+                            deref_span_id: Some((expr.span, expr.hir_id)),
+                        },
+                        data,
+                    ));
+                }
+            },
             (
                 Some((
-                    State::DerefedBorrow {
-                        count,
-                        required_precedence,
-                        msg,
+                    State::Reborrow {
+                        deref_span,
+                        deref_hir_id,
                     },
                     data,
                 )),
-                RefOp::AddrOf,
-            ) if count != 0 => {
+                RefOp::Deref,
+            ) => {
                 self.state = Some((
-                    State::DerefedBorrow {
-                        count: count - 1,
-                        required_precedence,
-                        msg,
+                    State::ExplicitDeref {
+                        deref_span_id: Some((deref_span, deref_hir_id)),
                     },
                     data,
                 ));
             },
+            (state @ Some((State::ExplicitDeref { .. }, _)), RefOp::Deref) => {
+                self.state = state;
+            },
+            (Some((State::ExplicitDerefField { name }, data)), RefOp::Deref)
+                if !ty_contains_field(typeck.expr_ty(sub_expr), name) =>
+            {
+                self.state = Some((State::ExplicitDerefField { name }, data));
+            },
 
             (Some((state, data)), _) => report(cx, expr, state, data),
         }
@@ -473,131 +598,362 @@ fn deref_method_same_type<'tcx>(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool {
     }
 }
 
-// Checks whether the parent node is a suitable context for switching from a deref method to the
-// deref operator.
-fn is_linted_explicit_deref_position(parent: Option<Node<'_>>, child_id: HirId, child_span: Span) -> bool {
-    let parent = match parent {
-        Some(Node::Expr(e)) if e.span.ctxt() == child_span.ctxt() => e,
-        _ => return true,
-    };
-    match parent.kind {
-        // Leave deref calls in the middle of a method chain.
-        // e.g. x.deref().foo()
-        ExprKind::MethodCall(_, [self_arg, ..], _) if self_arg.hir_id == child_id => false,
-
-        // Leave deref calls resulting in a called function
-        // e.g. (x.deref())()
-        ExprKind::Call(func_expr, _) if func_expr.hir_id == child_id => false,
-
-        // Makes an ugly suggestion
-        // e.g. *x.deref() => *&*x
-        ExprKind::Unary(UnOp::Deref, _)
-        // Postfix expressions would require parens
-        | ExprKind::Match(_, _, MatchSource::TryDesugar | MatchSource::AwaitDesugar)
-        | ExprKind::Field(..)
-        | ExprKind::Index(..)
-        | ExprKind::Err => false,
-
-        ExprKind::Box(..)
-        | ExprKind::ConstBlock(..)
-        | ExprKind::Array(_)
-        | ExprKind::Call(..)
-        | ExprKind::MethodCall(..)
-        | ExprKind::Tup(..)
-        | ExprKind::Binary(..)
-        | ExprKind::Unary(..)
-        | ExprKind::Lit(..)
-        | ExprKind::Cast(..)
-        | ExprKind::Type(..)
-        | ExprKind::DropTemps(..)
-        | ExprKind::If(..)
-        | ExprKind::Loop(..)
-        | ExprKind::Match(..)
-        | ExprKind::Let(..)
-        | ExprKind::Closure{..}
-        | ExprKind::Block(..)
-        | ExprKind::Assign(..)
-        | ExprKind::AssignOp(..)
-        | ExprKind::Path(..)
-        | ExprKind::AddrOf(..)
-        | ExprKind::Break(..)
-        | ExprKind::Continue(..)
-        | ExprKind::Ret(..)
-        | ExprKind::InlineAsm(..)
-        | ExprKind::Struct(..)
-        | ExprKind::Repeat(..)
-        | ExprKind::Yield(..) => true,
-    }
+/// The position of an expression relative to it's parent.
+#[derive(Clone, Copy)]
+enum Position {
+    MethodReceiver,
+    /// The method is defined on a reference type. e.g. `impl Foo for &T`
+    MethodReceiverRefImpl,
+    Callee,
+    FieldAccess(Symbol),
+    Postfix,
+    Deref,
+    /// Any other location which will trigger auto-deref to a specific time.
+    DerefStable(i8),
+    /// Any other location which will trigger auto-reborrowing.
+    ReborrowStable(i8),
+    Other(i8),
 }
+impl Position {
+    fn is_deref_stable(self) -> bool {
+        matches!(self, Self::DerefStable(_))
+    }
 
-/// Checks if the given expression is in a position which can be auto-reborrowed.
-/// Note: This is only correct assuming auto-deref is already occurring.
-fn is_auto_reborrow_position(parent: Option<Node<'_>>) -> bool {
-    match parent {
-        Some(Node::Expr(parent)) => matches!(parent.kind, ExprKind::MethodCall(..) | ExprKind::Call(..)),
-        Some(Node::Local(_)) => true,
-        _ => false,
+    fn is_reborrow_stable(self) -> bool {
+        matches!(self, Self::DerefStable(_) | Self::ReborrowStable(_))
     }
-}
 
-/// Checks if the given expression is a position which can auto-borrow.
-fn is_auto_borrow_position(parent: Option<Node<'_>>, child_id: HirId) -> bool {
-    if let Some(Node::Expr(parent)) = parent {
-        match parent.kind {
-            // ExprKind::MethodCall(_, [self_arg, ..], _) => self_arg.hir_id == child_id,
-            ExprKind::Field(..) => true,
-            ExprKind::Call(f, _) => f.hir_id == child_id,
-            _ => false,
-        }
-    } else {
-        false
+    fn can_auto_borrow(self) -> bool {
+        matches!(self, Self::MethodReceiver | Self::FieldAccess(_) | Self::Callee)
     }
-}
 
-/// Adjustments are sometimes made in the parent block rather than the expression itself.
-fn find_adjustments<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    typeck: &'tcx TypeckResults<'tcx>,
-    expr: &'tcx Expr<'tcx>,
-) -> &'tcx [Adjustment<'tcx>] {
-    let map = tcx.hir();
-    let mut iter = map.parent_iter(expr.hir_id);
-    let mut prev = expr;
+    fn lint_explicit_deref(self) -> bool {
+        matches!(self, Self::Other(_) | Self::DerefStable(_) | Self::ReborrowStable(_))
+    }
 
-    loop {
-        match typeck.expr_adjustments(prev) {
-            [] => (),
-            a => break a,
-        };
+    fn precedence(self) -> i8 {
+        match self {
+            Self::MethodReceiver
+            | Self::MethodReceiverRefImpl
+            | Self::Callee
+            | Self::FieldAccess(_)
+            | Self::Postfix => PREC_POSTFIX,
+            Self::Deref => PREC_PREFIX,
+            Self::DerefStable(p) | Self::ReborrowStable(p) | Self::Other(p) => p,
+        }
+    }
+}
 
-        match iter.next().map(|(_, x)| x) {
-            Some(Node::Block(_)) => {
-                if let Some((_, Node::Expr(e))) = iter.next() {
-                    prev = e;
+/// Walks up the parent expressions attempting to determine both how stable the auto-deref result
+/// is, and which adjustments will be applied to it. Note this will not consider auto-borrow
+/// locations as those follow different rules.
+#[allow(clippy::too_many_lines)]
+fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &'tcx [Adjustment<'tcx>]) {
+    let mut adjustments = [].as_slice();
+    let mut precedence = 0i8;
+    let ctxt = e.span.ctxt();
+    let position = walk_to_expr_usage(cx, e, &mut |parent, child_id| {
+        // LocalTableInContext returns the wrong lifetime, so go use `expr_adjustments` instead.
+        if adjustments.is_empty() && let Node::Expr(e) = cx.tcx.hir().get(child_id) {
+            adjustments = cx.typeck_results().expr_adjustments(e);
+        }
+        match parent {
+            Node::Local(Local { ty: Some(ty), span, .. }) if span.ctxt() == ctxt => {
+                Some(binding_ty_auto_deref_stability(ty, precedence))
+            },
+            Node::Item(&Item {
+                kind: ItemKind::Static(..) | ItemKind::Const(..),
+                def_id,
+                span,
+                ..
+            })
+            | Node::TraitItem(&TraitItem {
+                kind: TraitItemKind::Const(..),
+                def_id,
+                span,
+                ..
+            })
+            | Node::ImplItem(&ImplItem {
+                kind: ImplItemKind::Const(..),
+                def_id,
+                span,
+                ..
+            }) if span.ctxt() == ctxt => {
+                let ty = cx.tcx.type_of(def_id);
+                Some(if ty.is_ref() {
+                    Position::DerefStable(precedence)
                 } else {
-                    // This shouldn't happen. Blocks are always contained in an expression.
-                    break &[];
-                }
+                    Position::Other(precedence)
+                })
             },
-            Some(Node::Expr(&Expr {
-                kind: ExprKind::Break(Destination { target_id: Ok(id), .. }, _),
+
+            Node::Item(&Item {
+                kind: ItemKind::Fn(..),
+                def_id,
+                span,
+                ..
+            })
+            | Node::TraitItem(&TraitItem {
+                kind: TraitItemKind::Fn(..),
+                def_id,
+                span,
+                ..
+            })
+            | Node::ImplItem(&ImplItem {
+                kind: ImplItemKind::Fn(..),
+                def_id,
+                span,
                 ..
-            })) => {
-                if let Some(Node::Expr(e)) = map.find(id) {
-                    prev = e;
-                    iter = map.parent_iter(id);
+            }) if span.ctxt() == ctxt => {
+                let output = cx.tcx.fn_sig(def_id.to_def_id()).skip_binder().output();
+                Some(if !output.is_ref() {
+                    Position::Other(precedence)
+                } else if output.has_placeholders() || output.has_opaque_types() {
+                    Position::ReborrowStable(precedence)
+                } else {
+                    Position::DerefStable(precedence)
+                })
+            },
+
+            Node::Expr(parent) if parent.span.ctxt() == ctxt => match parent.kind {
+                ExprKind::Ret(_) => {
+                    let output = cx
+                        .tcx
+                        .fn_sig(cx.tcx.hir().body_owner_def_id(cx.enclosing_body.unwrap()))
+                        .skip_binder()
+                        .output();
+                    Some(if !output.is_ref() {
+                        Position::Other(precedence)
+                    } else if output.has_placeholders() || output.has_opaque_types() {
+                        Position::ReborrowStable(precedence)
+                    } else {
+                        Position::DerefStable(precedence)
+                    })
+                },
+                ExprKind::Call(func, _) if func.hir_id == child_id => (child_id == e.hir_id).then(|| Position::Callee),
+                ExprKind::Call(func, args) => args
+                    .iter()
+                    .position(|arg| arg.hir_id == child_id)
+                    .zip(expr_sig(cx, func))
+                    .and_then(|(i, sig)| sig.input_with_hir(i))
+                    .map(|(hir_ty, ty)| match hir_ty {
+                        // Type inference for closures can depend on how they're called. Only go by the explicit
+                        // types here.
+                        Some(ty) => binding_ty_auto_deref_stability(ty, precedence),
+                        None => param_auto_deref_stability(ty.skip_binder(), precedence),
+                    }),
+                ExprKind::MethodCall(_, args, _) => {
+                    let id = cx.typeck_results().type_dependent_def_id(parent.hir_id).unwrap();
+                    args.iter().position(|arg| arg.hir_id == child_id).map(|i| {
+                        if i == 0 {
+                            // Check for calls to trait methods where the trait is implemented on a reference.
+                            // Two cases need to be handled:
+                            // * `self` methods on `&T` will never have auto-borrow
+                            // * `&self` methods on `&T` can have auto-borrow, but `&self` methods on `T` will take
+                            //   priority.
+                            if e.hir_id != child_id {
+                                Position::ReborrowStable(precedence)
+                            } else if let Some(trait_id) = cx.tcx.trait_of_item(id)
+                                && let arg_ty = cx.tcx.erase_regions(cx.typeck_results().expr_ty_adjusted(e))
+                                && let ty::Ref(_, sub_ty, _) = *arg_ty.kind()
+                                && let subs = cx.typeck_results().node_substs_opt(child_id).unwrap_or_else(
+                                    || cx.tcx.mk_substs([].iter())
+                                ) && let impl_ty = if cx.tcx.fn_sig(id).skip_binder().inputs()[0].is_ref() {
+                                    // Trait methods taking `&self`
+                                    sub_ty
+                                } else {
+                                    // Trait methods taking `self`
+                                    arg_ty
+                                } && impl_ty.is_ref()
+                                && cx.tcx.infer_ctxt().enter(|infcx|
+                                    infcx
+                                        .type_implements_trait(trait_id, impl_ty, subs, cx.param_env)
+                                        .must_apply_modulo_regions()
+                                )
+                            {
+                                Position::MethodReceiverRefImpl
+                            } else {
+                                Position::MethodReceiver
+                            }
+                        } else {
+                            param_auto_deref_stability(cx.tcx.fn_sig(id).skip_binder().inputs()[i], precedence)
+                        }
+                    })
+                },
+                ExprKind::Struct(path, fields, _) => {
+                    let variant = variant_of_res(cx, cx.qpath_res(path, parent.hir_id));
+                    fields
+                        .iter()
+                        .find(|f| f.expr.hir_id == child_id)
+                        .zip(variant)
+                        .and_then(|(field, variant)| variant.fields.iter().find(|f| f.name == field.ident.name))
+                        .map(|field| param_auto_deref_stability(cx.tcx.type_of(field.did), precedence))
+                },
+                ExprKind::Field(child, name) if child.hir_id == e.hir_id => Some(Position::FieldAccess(name.name)),
+                ExprKind::Unary(UnOp::Deref, child) if child.hir_id == e.hir_id => Some(Position::Deref),
+                ExprKind::Match(child, _, MatchSource::TryDesugar | MatchSource::AwaitDesugar)
+                | ExprKind::Index(child, _)
+                    if child.hir_id == e.hir_id =>
+                {
+                    Some(Position::Postfix)
+                },
+                _ if child_id == e.hir_id => {
+                    precedence = parent.precedence().order();
+                    None
+                },
+                _ => None,
+            },
+            _ => None,
+        }
+    })
+    .unwrap_or(Position::Other(precedence));
+    (position, adjustments)
+}
+
+// Checks the stability of auto-deref when assigned to a binding with the given explicit type.
+//
+// e.g.
+// let x = Box::new(Box::new(0u32));
+// let y1: &Box<_> = x.deref();
+// let y2: &Box<_> = &x;
+//
+// Here `y1` and `y2` would resolve to different types, so the type `&Box<_>` is not stable when
+// switching to auto-dereferencing.
+fn binding_ty_auto_deref_stability(ty: &hir::Ty<'_>, precedence: i8) -> Position {
+    let TyKind::Rptr(_, ty) = &ty.kind else {
+        return Position::Other(precedence);
+    };
+    let mut ty = ty;
+
+    loop {
+        break match ty.ty.kind {
+            TyKind::Rptr(_, ref ref_ty) => {
+                ty = ref_ty;
+                continue;
+            },
+            TyKind::Path(
+                QPath::TypeRelative(_, path)
+                | QPath::Resolved(
+                    _,
+                    Path {
+                        segments: [.., path], ..
+                    },
+                ),
+            ) => {
+                if let Some(args) = path.args
+                    && args.args.iter().any(|arg| match arg {
+                        GenericArg::Infer(_) => true,
+                        GenericArg::Type(ty) => ty_contains_infer(ty),
+                        _ => false,
+                    })
+                {
+                    Position::ReborrowStable(precedence)
                 } else {
-                    // This shouldn't happen. The destination should exist.
-                    break &[];
+                    Position::DerefStable(precedence)
                 }
             },
-            _ => break &[],
+            TyKind::Slice(_)
+            | TyKind::Array(..)
+            | TyKind::BareFn(_)
+            | TyKind::Never
+            | TyKind::Tup(_)
+            | TyKind::Ptr(_)
+            | TyKind::TraitObject(..)
+            | TyKind::Path(_) => Position::DerefStable(precedence),
+            TyKind::OpaqueDef(..)
+            | TyKind::Infer
+            | TyKind::Typeof(..)
+            | TyKind::Err => Position::ReborrowStable(precedence),
+        };
+    }
+}
+
+// Checks whether a type is inferred at some point.
+// e.g. `_`, `Box<_>`, `[_]`
+fn ty_contains_infer(ty: &hir::Ty<'_>) -> bool {
+    struct V(bool);
+    impl Visitor<'_> for V {
+        fn visit_ty(&mut self, ty: &hir::Ty<'_>) {
+            if self.0
+                || matches!(
+                    ty.kind,
+                    TyKind::OpaqueDef(..) | TyKind::Infer | TyKind::Typeof(_) | TyKind::Err
+                )
+            {
+                self.0 = true;
+            } else {
+                walk_ty(self, ty);
+            }
         }
+
+        fn visit_generic_arg(&mut self, arg: &GenericArg<'_>) {
+            if self.0 || matches!(arg, GenericArg::Infer(_)) {
+                self.0 = true;
+            } else if let GenericArg::Type(ty) = arg {
+                self.visit_ty(ty);
+            }
+        }
+    }
+    let mut v = V(false);
+    v.visit_ty(ty);
+    v.0
+}
+
+// Checks whether a type is stable when switching to auto dereferencing,
+fn param_auto_deref_stability(ty: Ty<'_>, precedence: i8) -> Position {
+    let ty::Ref(_, mut ty, _) = *ty.kind() else {
+        return Position::Other(precedence);
+    };
+
+    loop {
+        break match *ty.kind() {
+            ty::Ref(_, ref_ty, _) => {
+                ty = ref_ty;
+                continue;
+            },
+            ty::Infer(_)
+            | ty::Error(_)
+            | ty::Param(_)
+            | ty::Bound(..)
+            | ty::Opaque(..)
+            | ty::Placeholder(_)
+            | ty::Dynamic(..) => Position::ReborrowStable(precedence),
+            ty::Adt(..) if ty.has_placeholders() || ty.has_param_types_or_consts() => {
+                Position::ReborrowStable(precedence)
+            },
+            ty::Adt(..)
+            | ty::Bool
+            | ty::Char
+            | ty::Int(_)
+            | ty::Uint(_)
+            | ty::Float(_)
+            | ty::Foreign(_)
+            | ty::Str
+            | ty::Array(..)
+            | ty::Slice(..)
+            | ty::RawPtr(..)
+            | ty::FnDef(..)
+            | ty::FnPtr(_)
+            | ty::Closure(..)
+            | ty::Generator(..)
+            | ty::GeneratorWitness(..)
+            | ty::Never
+            | ty::Tuple(_)
+            | ty::Projection(_) => Position::DerefStable(precedence),
+        };
+    }
+}
+
+fn ty_contains_field(ty: Ty<'_>, name: Symbol) -> bool {
+    if let ty::Adt(adt, _) = *ty.kind() {
+        adt.is_struct() && adt.all_fields().any(|f| f.name == name)
+    } else {
+        false
     }
 }
 
-#[expect(clippy::needless_pass_by_value)]
-fn report<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, state: State, data: StateData) {
+#[expect(clippy::needless_pass_by_value, clippy::too_many_lines)]
+fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data: StateData) {
     match state {
         State::DerefMethod {
             ty_changed_count,
@@ -647,15 +1003,14 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, state: State, data: S
                 app,
             );
         },
-        State::DerefedBorrow {
-            required_precedence,
-            msg,
-            ..
-        } => {
+        State::DerefedBorrow(state) => {
             let mut app = Applicability::MachineApplicable;
-            let snip = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app).0;
-            span_lint_hir_and_then(cx, NEEDLESS_BORROW, data.hir_id, data.span, msg, |diag| {
-                let sugg = if required_precedence > expr.precedence().order() && !has_enclosing_paren(&snip) {
+            let (snip, snip_is_macro) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app);
+            span_lint_hir_and_then(cx, NEEDLESS_BORROW, data.hir_id, data.span, state.msg, |diag| {
+                let sugg = if !snip_is_macro
+                    && expr.precedence().order() < data.position.precedence()
+                    && !has_enclosing_paren(&snip)
+                {
                     format!("({})", snip)
                 } else {
                     snip.into()
@@ -663,6 +1018,48 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, state: State, data: S
                 diag.span_suggestion(data.span, "change this to", sugg, app);
             });
         },
+        State::ExplicitDeref { deref_span_id } => {
+            let (span, hir_id, precedence) = if let Some((span, hir_id)) = deref_span_id
+                && !cx.typeck_results().expr_ty(expr).is_ref()
+            {
+                (span, hir_id, PREC_PREFIX)
+            } else {
+                (data.span, data.hir_id, data.position.precedence())
+            };
+            span_lint_hir_and_then(
+                cx,
+                EXPLICIT_AUTO_DEREF,
+                hir_id,
+                span,
+                "deref which would be done by auto-deref",
+                |diag| {
+                    let mut app = Applicability::MachineApplicable;
+                    let (snip, snip_is_macro) = snippet_with_context(cx, expr.span, span.ctxt(), "..", &mut app);
+                    let sugg =
+                        if !snip_is_macro && expr.precedence().order() < precedence && !has_enclosing_paren(&snip) {
+                            format!("({})", snip)
+                        } else {
+                            snip.into()
+                        };
+                    diag.span_suggestion(span, "try this", sugg, app);
+                },
+            );
+        },
+        State::ExplicitDerefField { .. } => {
+            span_lint_hir_and_then(
+                cx,
+                EXPLICIT_AUTO_DEREF,
+                data.hir_id,
+                data.span,
+                "deref which would be done by auto-deref",
+                |diag| {
+                    let mut app = Applicability::MachineApplicable;
+                    let snip = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app).0;
+                    diag.span_suggestion(data.span, "try this", snip.into_owned(), app);
+                },
+            );
+        },
+        State::Borrow | State::Reborrow { .. } => (),
     }
 }
 
diff --git a/clippy_lints/src/double_comparison.rs b/clippy_lints/src/double_comparison.rs
deleted file mode 100644
index ee0440e52ff..00000000000
--- a/clippy_lints/src/double_comparison.rs
+++ /dev/null
@@ -1,96 +0,0 @@
-//! Lint on unnecessary double comparisons. Some examples:
-
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::eq_expr_value;
-use clippy_utils::source::snippet_with_applicability;
-use rustc_errors::Applicability;
-use rustc_hir::{BinOpKind, Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::source_map::Span;
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for double comparisons that could be simplified to a single expression.
-    ///
-    ///
-    /// ### Why is this bad?
-    /// Readability.
-    ///
-    /// ### Example
-    /// ```rust
-    /// # let x = 1;
-    /// # let y = 2;
-    /// if x == y || x < y {}
-    /// ```
-    ///
-    /// Use instead:
-    ///
-    /// ```rust
-    /// # let x = 1;
-    /// # let y = 2;
-    /// if x <= y {}
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub DOUBLE_COMPARISONS,
-    complexity,
-    "unnecessary double comparisons that can be simplified"
-}
-
-declare_lint_pass!(DoubleComparisons => [DOUBLE_COMPARISONS]);
-
-impl<'tcx> DoubleComparisons {
-    #[expect(clippy::similar_names)]
-    fn check_binop(cx: &LateContext<'tcx>, op: BinOpKind, lhs: &'tcx Expr<'_>, rhs: &'tcx Expr<'_>, span: Span) {
-        let (lkind, llhs, lrhs, rkind, rlhs, rrhs) = match (&lhs.kind, &rhs.kind) {
-            (ExprKind::Binary(lb, llhs, lrhs), ExprKind::Binary(rb, rlhs, rrhs)) => {
-                (lb.node, llhs, lrhs, rb.node, rlhs, rrhs)
-            },
-            _ => return,
-        };
-        if !(eq_expr_value(cx, llhs, rlhs) && eq_expr_value(cx, lrhs, rrhs)) {
-            return;
-        }
-        macro_rules! lint_double_comparison {
-            ($op:tt) => {{
-                let mut applicability = Applicability::MachineApplicable;
-                let lhs_str = snippet_with_applicability(cx, llhs.span, "", &mut applicability);
-                let rhs_str = snippet_with_applicability(cx, lrhs.span, "", &mut applicability);
-                let sugg = format!("{} {} {}", lhs_str, stringify!($op), rhs_str);
-                span_lint_and_sugg(
-                    cx,
-                    DOUBLE_COMPARISONS,
-                    span,
-                    "this binary expression can be simplified",
-                    "try",
-                    sugg,
-                    applicability,
-                );
-            }};
-        }
-        #[rustfmt::skip]
-        match (op, lkind, rkind) {
-            (BinOpKind::Or, BinOpKind::Eq, BinOpKind::Lt) | (BinOpKind::Or, BinOpKind::Lt, BinOpKind::Eq) => {
-                lint_double_comparison!(<=);
-            },
-            (BinOpKind::Or, BinOpKind::Eq, BinOpKind::Gt) | (BinOpKind::Or, BinOpKind::Gt, BinOpKind::Eq) => {
-                lint_double_comparison!(>=);
-            },
-            (BinOpKind::Or, BinOpKind::Lt, BinOpKind::Gt) | (BinOpKind::Or, BinOpKind::Gt, BinOpKind::Lt) => {
-                lint_double_comparison!(!=);
-            },
-            (BinOpKind::And, BinOpKind::Le, BinOpKind::Ge) | (BinOpKind::And, BinOpKind::Ge, BinOpKind::Le) => {
-                lint_double_comparison!(==);
-            },
-            _ => (),
-        };
-    }
-}
-
-impl<'tcx> LateLintPass<'tcx> for DoubleComparisons {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if let ExprKind::Binary(ref kind, lhs, rhs) = expr.kind {
-            Self::check_binop(cx, kind.node, lhs, rhs, expr.span);
-        }
-    }
-}
diff --git a/clippy_lints/src/duration_subsec.rs b/clippy_lints/src/duration_subsec.rs
deleted file mode 100644
index d85ace3a279..00000000000
--- a/clippy_lints/src/duration_subsec.rs
+++ /dev/null
@@ -1,75 +0,0 @@
-use clippy_utils::consts::{constant, Constant};
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::ty::is_type_diagnostic_item;
-use if_chain::if_chain;
-use rustc_errors::Applicability;
-use rustc_hir::{BinOpKind, Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::source_map::Spanned;
-use rustc_span::sym;
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for calculation of subsecond microseconds or milliseconds
-    /// from other `Duration` methods.
-    ///
-    /// ### Why is this bad?
-    /// It's more concise to call `Duration::subsec_micros()` or
-    /// `Duration::subsec_millis()` than to calculate them.
-    ///
-    /// ### Example
-    /// ```rust
-    /// # use std::time::Duration;
-    /// # let duration = Duration::new(5, 0);
-    /// let micros = duration.subsec_nanos() / 1_000;
-    /// let millis = duration.subsec_nanos() / 1_000_000;
-    /// ```
-    ///
-    /// Use instead:
-    /// ```rust
-    /// # use std::time::Duration;
-    /// # let duration = Duration::new(5, 0);
-    /// let micros = duration.subsec_micros();
-    /// let millis = duration.subsec_millis();
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub DURATION_SUBSEC,
-    complexity,
-    "checks for calculation of subsecond microseconds or milliseconds"
-}
-
-declare_lint_pass!(DurationSubsec => [DURATION_SUBSEC]);
-
-impl<'tcx> LateLintPass<'tcx> for DurationSubsec {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if_chain! {
-            if let ExprKind::Binary(Spanned { node: BinOpKind::Div, .. }, left, right) = expr.kind;
-            if let ExprKind::MethodCall(method_path, args, _) = left.kind;
-            if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]).peel_refs(), sym::Duration);
-            if let Some((Constant::Int(divisor), _)) = constant(cx, cx.typeck_results(), right);
-            then {
-                let suggested_fn = match (method_path.ident.as_str(), divisor) {
-                    ("subsec_micros", 1_000) | ("subsec_nanos", 1_000_000) => "subsec_millis",
-                    ("subsec_nanos", 1_000) => "subsec_micros",
-                    _ => return,
-                };
-                let mut applicability = Applicability::MachineApplicable;
-                span_lint_and_sugg(
-                    cx,
-                    DURATION_SUBSEC,
-                    expr.span,
-                    &format!("calling `{}()` is more concise than this calculation", suggested_fn),
-                    "try",
-                    format!(
-                        "{}.{}()",
-                        snippet_with_applicability(cx, args[0].span, "_", &mut applicability),
-                        suggested_fn
-                    ),
-                    applicability,
-                );
-            }
-        }
-    }
-}
diff --git a/clippy_lints/src/enum_variants.rs b/clippy_lints/src/enum_variants.rs
index 23b75104570..cd36f9fcd72 100644
--- a/clippy_lints/src/enum_variants.rs
+++ b/clippy_lints/src/enum_variants.rs
@@ -190,7 +190,7 @@ fn check_variant(cx: &LateContext<'_>, threshold: u64, def: &EnumDef<'_>, item_n
             .map(|e| *e.0)
             .collect();
     }
-    let (what, value) = match (pre.is_empty(), post.is_empty()) {
+    let (what, value) = match (have_no_extra_prefix(&pre), post.is_empty()) {
         (true, true) => return,
         (false, _) => ("pre", pre.join("")),
         (true, false) => {
@@ -213,6 +213,11 @@ fn check_variant(cx: &LateContext<'_>, threshold: u64, def: &EnumDef<'_>, item_n
 }
 
 #[must_use]
+fn have_no_extra_prefix(prefixes: &[&str]) -> bool {
+    prefixes.iter().all(|p| p == &"" || p == &"_")
+}
+
+#[must_use]
 fn to_camel_case(item_name: &str) -> String {
     let mut s = String::new();
     let mut up = true;
diff --git a/clippy_lints/src/eq_op.rs b/clippy_lints/src/eq_op.rs
deleted file mode 100644
index 2f4c90d07cf..00000000000
--- a/clippy_lints/src/eq_op.rs
+++ /dev/null
@@ -1,319 +0,0 @@
-use clippy_utils::diagnostics::{multispan_sugg, span_lint, span_lint_and_then};
-use clippy_utils::get_enclosing_block;
-use clippy_utils::macros::{find_assert_eq_args, first_node_macro_backtrace};
-use clippy_utils::source::snippet;
-use clippy_utils::ty::{implements_trait, is_copy};
-use clippy_utils::{ast_utils::is_useless_with_eq_exprs, eq_expr_value, is_in_test_function};
-use if_chain::if_chain;
-use rustc_errors::Applicability;
-use rustc_hir::{def::Res, def_id::DefId, BinOpKind, BorrowKind, Expr, ExprKind, GenericArg, ItemKind, QPath, TyKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty::{self, Ty};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for equal operands to comparison, logical and
-    /// bitwise, difference and division binary operators (`==`, `>`, etc., `&&`,
-    /// `||`, `&`, `|`, `^`, `-` and `/`).
-    ///
-    /// ### Why is this bad?
-    /// This is usually just a typo or a copy and paste error.
-    ///
-    /// ### Known problems
-    /// False negatives: We had some false positives regarding
-    /// calls (notably [racer](https://github.com/phildawes/racer) had one instance
-    /// of `x.pop() && x.pop()`), so we removed matching any function or method
-    /// calls. We may introduce a list of known pure functions in the future.
-    ///
-    /// ### Example
-    /// ```rust
-    /// # let x = 1;
-    /// if x + 1 == x + 1 {}
-    ///
-    /// // or
-    ///
-    /// # let a = 3;
-    /// # let b = 4;
-    /// assert_eq!(a, a);
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub EQ_OP,
-    correctness,
-    "equal operands on both sides of a comparison or bitwise combination (e.g., `x == x`)"
-}
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for arguments to `==` which have their address
-    /// taken to satisfy a bound
-    /// and suggests to dereference the other argument instead
-    ///
-    /// ### Why is this bad?
-    /// It is more idiomatic to dereference the other argument.
-    ///
-    /// ### Example
-    /// ```rust,ignore
-    /// &x == y
-    /// ```
-    ///
-    /// Use instead:
-    /// ```rust,ignore
-    /// x == *y
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub OP_REF,
-    style,
-    "taking a reference to satisfy the type constraints on `==`"
-}
-
-declare_lint_pass!(EqOp => [EQ_OP, OP_REF]);
-
-impl<'tcx> LateLintPass<'tcx> for EqOp {
-    #[expect(clippy::similar_names, clippy::too_many_lines)]
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
-        if_chain! {
-            if let Some((macro_call, macro_name)) = first_node_macro_backtrace(cx, e).find_map(|macro_call| {
-                let name = cx.tcx.item_name(macro_call.def_id);
-                matches!(name.as_str(), "assert_eq" | "assert_ne" | "debug_assert_eq" | "debug_assert_ne")
-                    .then(|| (macro_call, name))
-            });
-            if let Some((lhs, rhs, _)) = find_assert_eq_args(cx, e, macro_call.expn);
-            if eq_expr_value(cx, lhs, rhs);
-            if macro_call.is_local();
-            if !is_in_test_function(cx.tcx, e.hir_id);
-            then {
-                span_lint(
-                    cx,
-                    EQ_OP,
-                    lhs.span.to(rhs.span),
-                    &format!("identical args used in this `{}!` macro call", macro_name),
-                );
-            }
-        }
-        if let ExprKind::Binary(op, left, right) = e.kind {
-            if e.span.from_expansion() {
-                return;
-            }
-            let macro_with_not_op = |expr_kind: &ExprKind<'_>| {
-                if let ExprKind::Unary(_, expr) = *expr_kind {
-                    expr.span.from_expansion()
-                } else {
-                    false
-                }
-            };
-            if macro_with_not_op(&left.kind) || macro_with_not_op(&right.kind) {
-                return;
-            }
-            if is_useless_with_eq_exprs(op.node.into())
-                && eq_expr_value(cx, left, right)
-                && !is_in_test_function(cx.tcx, e.hir_id)
-            {
-                span_lint(
-                    cx,
-                    EQ_OP,
-                    e.span,
-                    &format!("equal expressions as operands to `{}`", op.node.as_str()),
-                );
-                return;
-            }
-            let (trait_id, requires_ref) = match op.node {
-                BinOpKind::Add => (cx.tcx.lang_items().add_trait(), false),
-                BinOpKind::Sub => (cx.tcx.lang_items().sub_trait(), false),
-                BinOpKind::Mul => (cx.tcx.lang_items().mul_trait(), false),
-                BinOpKind::Div => (cx.tcx.lang_items().div_trait(), false),
-                BinOpKind::Rem => (cx.tcx.lang_items().rem_trait(), false),
-                // don't lint short circuiting ops
-                BinOpKind::And | BinOpKind::Or => return,
-                BinOpKind::BitXor => (cx.tcx.lang_items().bitxor_trait(), false),
-                BinOpKind::BitAnd => (cx.tcx.lang_items().bitand_trait(), false),
-                BinOpKind::BitOr => (cx.tcx.lang_items().bitor_trait(), false),
-                BinOpKind::Shl => (cx.tcx.lang_items().shl_trait(), false),
-                BinOpKind::Shr => (cx.tcx.lang_items().shr_trait(), false),
-                BinOpKind::Ne | BinOpKind::Eq => (cx.tcx.lang_items().eq_trait(), true),
-                BinOpKind::Lt | BinOpKind::Le | BinOpKind::Ge | BinOpKind::Gt => {
-                    (cx.tcx.lang_items().partial_ord_trait(), true)
-                },
-            };
-            if let Some(trait_id) = trait_id {
-                match (&left.kind, &right.kind) {
-                    // do not suggest to dereference literals
-                    (&ExprKind::Lit(..), _) | (_, &ExprKind::Lit(..)) => {},
-                    // &foo == &bar
-                    (&ExprKind::AddrOf(BorrowKind::Ref, _, l), &ExprKind::AddrOf(BorrowKind::Ref, _, r)) => {
-                        let lty = cx.typeck_results().expr_ty(l);
-                        let rty = cx.typeck_results().expr_ty(r);
-                        let lcpy = is_copy(cx, lty);
-                        let rcpy = is_copy(cx, rty);
-                        if let Some((self_ty, other_ty)) = in_impl(cx, e, trait_id) {
-                            if (are_equal(cx, rty, self_ty) && are_equal(cx, lty, other_ty))
-                                || (are_equal(cx, rty, other_ty) && are_equal(cx, lty, self_ty))
-                            {
-                                return; // Don't lint
-                            }
-                        }
-                        // either operator autorefs or both args are copyable
-                        if (requires_ref || (lcpy && rcpy)) && implements_trait(cx, lty, trait_id, &[rty.into()]) {
-                            span_lint_and_then(
-                                cx,
-                                OP_REF,
-                                e.span,
-                                "needlessly taken reference of both operands",
-                                |diag| {
-                                    let lsnip = snippet(cx, l.span, "...").to_string();
-                                    let rsnip = snippet(cx, r.span, "...").to_string();
-                                    multispan_sugg(
-                                        diag,
-                                        "use the values directly",
-                                        vec![(left.span, lsnip), (right.span, rsnip)],
-                                    );
-                                },
-                            );
-                        } else if lcpy
-                            && !rcpy
-                            && implements_trait(cx, lty, trait_id, &[cx.typeck_results().expr_ty(right).into()])
-                        {
-                            span_lint_and_then(
-                                cx,
-                                OP_REF,
-                                e.span,
-                                "needlessly taken reference of left operand",
-                                |diag| {
-                                    let lsnip = snippet(cx, l.span, "...").to_string();
-                                    diag.span_suggestion(
-                                        left.span,
-                                        "use the left value directly",
-                                        lsnip,
-                                        Applicability::MaybeIncorrect, // FIXME #2597
-                                    );
-                                },
-                            );
-                        } else if !lcpy
-                            && rcpy
-                            && implements_trait(cx, cx.typeck_results().expr_ty(left), trait_id, &[rty.into()])
-                        {
-                            span_lint_and_then(
-                                cx,
-                                OP_REF,
-                                e.span,
-                                "needlessly taken reference of right operand",
-                                |diag| {
-                                    let rsnip = snippet(cx, r.span, "...").to_string();
-                                    diag.span_suggestion(
-                                        right.span,
-                                        "use the right value directly",
-                                        rsnip,
-                                        Applicability::MaybeIncorrect, // FIXME #2597
-                                    );
-                                },
-                            );
-                        }
-                    },
-                    // &foo == bar
-                    (&ExprKind::AddrOf(BorrowKind::Ref, _, l), _) => {
-                        let lty = cx.typeck_results().expr_ty(l);
-                        if let Some((self_ty, other_ty)) = in_impl(cx, e, trait_id) {
-                            let rty = cx.typeck_results().expr_ty(right);
-                            if (are_equal(cx, rty, self_ty) && are_equal(cx, lty, other_ty))
-                                || (are_equal(cx, rty, other_ty) && are_equal(cx, lty, self_ty))
-                            {
-                                return; // Don't lint
-                            }
-                        }
-                        let lcpy = is_copy(cx, lty);
-                        if (requires_ref || lcpy)
-                            && implements_trait(cx, lty, trait_id, &[cx.typeck_results().expr_ty(right).into()])
-                        {
-                            span_lint_and_then(
-                                cx,
-                                OP_REF,
-                                e.span,
-                                "needlessly taken reference of left operand",
-                                |diag| {
-                                    let lsnip = snippet(cx, l.span, "...").to_string();
-                                    diag.span_suggestion(
-                                        left.span,
-                                        "use the left value directly",
-                                        lsnip,
-                                        Applicability::MaybeIncorrect, // FIXME #2597
-                                    );
-                                },
-                            );
-                        }
-                    },
-                    // foo == &bar
-                    (_, &ExprKind::AddrOf(BorrowKind::Ref, _, r)) => {
-                        let rty = cx.typeck_results().expr_ty(r);
-                        if let Some((self_ty, other_ty)) = in_impl(cx, e, trait_id) {
-                            let lty = cx.typeck_results().expr_ty(left);
-                            if (are_equal(cx, rty, self_ty) && are_equal(cx, lty, other_ty))
-                                || (are_equal(cx, rty, other_ty) && are_equal(cx, lty, self_ty))
-                            {
-                                return; // Don't lint
-                            }
-                        }
-                        let rcpy = is_copy(cx, rty);
-                        if (requires_ref || rcpy)
-                            && implements_trait(cx, cx.typeck_results().expr_ty(left), trait_id, &[rty.into()])
-                        {
-                            span_lint_and_then(cx, OP_REF, e.span, "taken reference of right operand", |diag| {
-                                let rsnip = snippet(cx, r.span, "...").to_string();
-                                diag.span_suggestion(
-                                    right.span,
-                                    "use the right value directly",
-                                    rsnip,
-                                    Applicability::MaybeIncorrect, // FIXME #2597
-                                );
-                            });
-                        }
-                    },
-                    _ => {},
-                }
-            }
-        }
-    }
-}
-
-fn in_impl<'tcx>(
-    cx: &LateContext<'tcx>,
-    e: &'tcx Expr<'_>,
-    bin_op: DefId,
-) -> Option<(&'tcx rustc_hir::Ty<'tcx>, &'tcx rustc_hir::Ty<'tcx>)> {
-    if_chain! {
-        if let Some(block) = get_enclosing_block(cx, e.hir_id);
-        if let Some(impl_def_id) = cx.tcx.impl_of_method(block.hir_id.owner.to_def_id());
-        let item = cx.tcx.hir().expect_item(impl_def_id.expect_local());
-        if let ItemKind::Impl(item) = &item.kind;
-        if let Some(of_trait) = &item.of_trait;
-        if let Some(seg) = of_trait.path.segments.last();
-        if let Some(Res::Def(_, trait_id)) = seg.res;
-        if trait_id == bin_op;
-        if let Some(generic_args) = seg.args;
-        if let Some(GenericArg::Type(other_ty)) = generic_args.args.last();
-
-        then {
-            Some((item.self_ty, other_ty))
-        }
-        else {
-            None
-        }
-    }
-}
-
-fn are_equal<'tcx>(cx: &LateContext<'tcx>, middle_ty: Ty<'_>, hir_ty: &rustc_hir::Ty<'_>) -> bool {
-    if_chain! {
-        if let ty::Adt(adt_def, _) = middle_ty.kind();
-        if let Some(local_did) = adt_def.did().as_local();
-        let item = cx.tcx.hir().expect_item(local_did);
-        let middle_ty_id = item.def_id.to_def_id();
-        if let TyKind::Path(QPath::Resolved(_, path)) = hir_ty.kind;
-        if let Res::Def(_, hir_ty_id) = path.res;
-
-        then {
-            hir_ty_id == middle_ty_id
-        }
-        else {
-            false
-        }
-    }
-}
diff --git a/clippy_lints/src/erasing_op.rs b/clippy_lints/src/erasing_op.rs
deleted file mode 100644
index c1a84973c42..00000000000
--- a/clippy_lints/src/erasing_op.rs
+++ /dev/null
@@ -1,77 +0,0 @@
-use clippy_utils::consts::{constant_simple, Constant};
-use clippy_utils::diagnostics::span_lint;
-use clippy_utils::ty::same_type_and_consts;
-
-use rustc_hir::{BinOpKind, Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty::TypeckResults;
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for erasing operations, e.g., `x * 0`.
-    ///
-    /// ### Why is this bad?
-    /// The whole expression can be replaced by zero.
-    /// This is most likely not the intended outcome and should probably be
-    /// corrected
-    ///
-    /// ### Example
-    /// ```rust
-    /// let x = 1;
-    /// 0 / x;
-    /// 0 * x;
-    /// x & 0;
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub ERASING_OP,
-    correctness,
-    "using erasing operations, e.g., `x * 0` or `y & 0`"
-}
-
-declare_lint_pass!(ErasingOp => [ERASING_OP]);
-
-impl<'tcx> LateLintPass<'tcx> for ErasingOp {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
-        if e.span.from_expansion() {
-            return;
-        }
-        if let ExprKind::Binary(ref cmp, left, right) = e.kind {
-            let tck = cx.typeck_results();
-            match cmp.node {
-                BinOpKind::Mul | BinOpKind::BitAnd => {
-                    check(cx, tck, left, right, e);
-                    check(cx, tck, right, left, e);
-                },
-                BinOpKind::Div => check(cx, tck, left, right, e),
-                _ => (),
-            }
-        }
-    }
-}
-
-fn different_types(tck: &TypeckResults<'_>, input: &Expr<'_>, output: &Expr<'_>) -> bool {
-    let input_ty = tck.expr_ty(input).peel_refs();
-    let output_ty = tck.expr_ty(output).peel_refs();
-    !same_type_and_consts(input_ty, output_ty)
-}
-
-fn check<'tcx>(
-    cx: &LateContext<'tcx>,
-    tck: &TypeckResults<'tcx>,
-    op: &Expr<'tcx>,
-    other: &Expr<'tcx>,
-    parent: &Expr<'tcx>,
-) {
-    if constant_simple(cx, tck, op) == Some(Constant::Int(0)) {
-        if different_types(tck, other, parent) {
-            return;
-        }
-        span_lint(
-            cx,
-            ERASING_OP,
-            parent.span,
-            "this operation will always return zero. This is likely not the intended outcome",
-        );
-    }
-}
diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs
index 9d21dd71e0e..7a65b849a66 100644
--- a/clippy_lints/src/escape.rs
+++ b/clippy_lints/src/escape.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::span_lint;
+use clippy_utils::diagnostics::span_lint_hir;
 use clippy_utils::ty::contains_ty;
 use rustc_hir::intravisit;
 use rustc_hir::{self, AssocItemKind, Body, FnDecl, HirId, HirIdSet, Impl, ItemKind, Node};
@@ -118,9 +118,10 @@ impl<'tcx> LateLintPass<'tcx> for BoxedLocal {
         });
 
         for node in v.set {
-            span_lint(
+            span_lint_hir(
                 cx,
                 BOXED_LOCAL,
+                node,
                 cx.tcx.hir().span(node),
                 "local variable doesn't need to be boxed here",
             );
diff --git a/clippy_lints/src/float_equality_without_abs.rs b/clippy_lints/src/float_equality_without_abs.rs
deleted file mode 100644
index 98aee7592ae..00000000000
--- a/clippy_lints/src/float_equality_without_abs.rs
+++ /dev/null
@@ -1,116 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::{match_def_path, paths, sugg};
-use if_chain::if_chain;
-use rustc_ast::util::parser::AssocOp;
-use rustc_errors::Applicability;
-use rustc_hir::def::{DefKind, Res};
-use rustc_hir::{BinOpKind, Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty;
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::source_map::Spanned;
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for statements of the form `(a - b) < f32::EPSILON` or
-    /// `(a - b) < f64::EPSILON`. Notes the missing `.abs()`.
-    ///
-    /// ### Why is this bad?
-    /// The code without `.abs()` is more likely to have a bug.
-    ///
-    /// ### Known problems
-    /// If the user can ensure that b is larger than a, the `.abs()` is
-    /// technically unnecessary. However, it will make the code more robust and doesn't have any
-    /// large performance implications. If the abs call was deliberately left out for performance
-    /// reasons, it is probably better to state this explicitly in the code, which then can be done
-    /// with an allow.
-    ///
-    /// ### Example
-    /// ```rust
-    /// pub fn is_roughly_equal(a: f32, b: f32) -> bool {
-    ///     (a - b) < f32::EPSILON
-    /// }
-    /// ```
-    /// Use instead:
-    /// ```rust
-    /// pub fn is_roughly_equal(a: f32, b: f32) -> bool {
-    ///     (a - b).abs() < f32::EPSILON
-    /// }
-    /// ```
-    #[clippy::version = "1.48.0"]
-    pub FLOAT_EQUALITY_WITHOUT_ABS,
-    suspicious,
-    "float equality check without `.abs()`"
-}
-
-declare_lint_pass!(FloatEqualityWithoutAbs => [FLOAT_EQUALITY_WITHOUT_ABS]);
-
-impl<'tcx> LateLintPass<'tcx> for FloatEqualityWithoutAbs {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        let lhs;
-        let rhs;
-
-        // check if expr is a binary expression with a lt or gt operator
-        if let ExprKind::Binary(op, left, right) = expr.kind {
-            match op.node {
-                BinOpKind::Lt => {
-                    lhs = left;
-                    rhs = right;
-                },
-                BinOpKind::Gt => {
-                    lhs = right;
-                    rhs = left;
-                },
-                _ => return,
-            };
-        } else {
-            return;
-        }
-
-        if_chain! {
-
-            // left hand side is a subtraction
-            if let ExprKind::Binary(
-                Spanned {
-                    node: BinOpKind::Sub,
-                    ..
-                },
-                val_l,
-                val_r,
-            ) = lhs.kind;
-
-            // right hand side matches either f32::EPSILON or f64::EPSILON
-            if let ExprKind::Path(ref epsilon_path) = rhs.kind;
-            if let Res::Def(DefKind::AssocConst, def_id) = cx.qpath_res(epsilon_path, rhs.hir_id);
-            if match_def_path(cx, def_id, &paths::F32_EPSILON) || match_def_path(cx, def_id, &paths::F64_EPSILON);
-
-            // values of the subtractions on the left hand side are of the type float
-            let t_val_l = cx.typeck_results().expr_ty(val_l);
-            let t_val_r = cx.typeck_results().expr_ty(val_r);
-            if let ty::Float(_) = t_val_l.kind();
-            if let ty::Float(_) = t_val_r.kind();
-
-            then {
-                let sug_l = sugg::Sugg::hir(cx, val_l, "..");
-                let sug_r = sugg::Sugg::hir(cx, val_r, "..");
-                // format the suggestion
-                let suggestion = format!("{}.abs()", sugg::make_assoc(AssocOp::Subtract, &sug_l, &sug_r).maybe_par());
-                // spans the lint
-                span_lint_and_then(
-                    cx,
-                    FLOAT_EQUALITY_WITHOUT_ABS,
-                    expr.span,
-                    "float equality check without `.abs()`",
-                    | diag | {
-                        diag.span_suggestion(
-                            lhs.span,
-                            "add `.abs()`",
-                            suggestion,
-                            Applicability::MaybeIncorrect,
-                        );
-                    }
-                );
-            }
-        }
-    }
-}
diff --git a/clippy_lints/src/implicit_return.rs b/clippy_lints/src/implicit_return.rs
index 647947d5d30..a6610ade37e 100644
--- a/clippy_lints/src/implicit_return.rs
+++ b/clippy_lints/src/implicit_return.rs
@@ -1,5 +1,5 @@
 use clippy_utils::{
-    diagnostics::span_lint_and_sugg,
+    diagnostics::span_lint_hir_and_then,
     get_async_fn_body, is_async_fn,
     source::{snippet_with_applicability, snippet_with_context, walk_span_to_context},
     visitors::expr_visitor_no_bodies,
@@ -43,31 +43,38 @@ declare_clippy_lint! {
 
 declare_lint_pass!(ImplicitReturn => [IMPLICIT_RETURN]);
 
-fn lint_return(cx: &LateContext<'_>, span: Span) {
+fn lint_return(cx: &LateContext<'_>, emission_place: HirId, span: Span) {
     let mut app = Applicability::MachineApplicable;
     let snip = snippet_with_applicability(cx, span, "..", &mut app);
-    span_lint_and_sugg(
+    span_lint_hir_and_then(
         cx,
         IMPLICIT_RETURN,
+        emission_place,
         span,
         "missing `return` statement",
-        "add `return` as shown",
-        format!("return {}", snip),
-        app,
+        |diag| {
+            diag.span_suggestion(span, "add `return` as shown", format!("return {}", snip), app);
+        },
     );
 }
 
-fn lint_break(cx: &LateContext<'_>, break_span: Span, expr_span: Span) {
+fn lint_break(cx: &LateContext<'_>, emission_place: HirId, break_span: Span, expr_span: Span) {
     let mut app = Applicability::MachineApplicable;
     let snip = snippet_with_context(cx, expr_span, break_span.ctxt(), "..", &mut app).0;
-    span_lint_and_sugg(
+    span_lint_hir_and_then(
         cx,
         IMPLICIT_RETURN,
+        emission_place,
         break_span,
         "missing `return` statement",
-        "change `break` to `return` as shown",
-        format!("return {}", snip),
-        app,
+        |diag| {
+            diag.span_suggestion(
+                break_span,
+                "change `break` to `return` as shown",
+                format!("return {}", snip),
+                app,
+            );
+        },
     );
 }
 
@@ -152,7 +159,7 @@ fn lint_implicit_returns(
                             // At this point sub_expr can be `None` in async functions which either diverge, or return
                             // the unit type.
                             if let Some(sub_expr) = sub_expr {
-                                lint_break(cx, e.span, sub_expr.span);
+                                lint_break(cx, e.hir_id, e.span, sub_expr.span);
                             }
                         } else {
                             // the break expression is from a macro call, add a return to the loop
@@ -166,10 +173,10 @@ fn lint_implicit_returns(
             if add_return {
                 #[expect(clippy::option_if_let_else)]
                 if let Some(span) = call_site_span {
-                    lint_return(cx, span);
+                    lint_return(cx, expr.hir_id, span);
                     LintLocation::Parent
                 } else {
-                    lint_return(cx, expr.span);
+                    lint_return(cx, expr.hir_id, expr.span);
                     LintLocation::Inner
                 }
             } else {
@@ -198,10 +205,10 @@ fn lint_implicit_returns(
         {
             #[expect(clippy::option_if_let_else)]
             if let Some(span) = call_site_span {
-                lint_return(cx, span);
+                lint_return(cx, expr.hir_id, span);
                 LintLocation::Parent
             } else {
-                lint_return(cx, expr.span);
+                lint_return(cx, expr.hir_id, expr.span);
                 LintLocation::Inner
             }
         },
diff --git a/clippy_lints/src/integer_division.rs b/clippy_lints/src/integer_division.rs
deleted file mode 100644
index 3effba56826..00000000000
--- a/clippy_lints/src/integer_division.rs
+++ /dev/null
@@ -1,61 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_help;
-use if_chain::if_chain;
-use rustc_hir as hir;
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for division of integers
-    ///
-    /// ### Why is this bad?
-    /// When outside of some very specific algorithms,
-    /// integer division is very often a mistake because it discards the
-    /// remainder.
-    ///
-    /// ### Example
-    /// ```rust
-    /// let x = 3 / 2;
-    /// println!("{}", x);
-    /// ```
-    ///
-    /// Use instead:
-    /// ```rust
-    /// let x = 3f32 / 2f32;
-    /// println!("{}", x);
-    /// ```
-    #[clippy::version = "1.37.0"]
-    pub INTEGER_DIVISION,
-    restriction,
-    "integer division may cause loss of precision"
-}
-
-declare_lint_pass!(IntegerDivision => [INTEGER_DIVISION]);
-
-impl<'tcx> LateLintPass<'tcx> for IntegerDivision {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
-        if is_integer_division(cx, expr) {
-            span_lint_and_help(
-                cx,
-                INTEGER_DIVISION,
-                expr.span,
-                "integer division",
-                None,
-                "division of integers may cause loss of precision. consider using floats",
-            );
-        }
-    }
-}
-
-fn is_integer_division<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) -> bool {
-    if_chain! {
-        if let hir::ExprKind::Binary(binop, left, right) = &expr.kind;
-        if binop.node == hir::BinOpKind::Div;
-        then {
-            let (left_ty, right_ty) = (cx.typeck_results().expr_ty(left), cx.typeck_results().expr_ty(right));
-            return left_ty.is_integral() && right_ty.is_integral();
-        }
-    }
-
-    false
-}
diff --git a/clippy_lints/src/large_const_arrays.rs b/clippy_lints/src/large_const_arrays.rs
index 289755bfec6..984c5cd4e37 100644
--- a/clippy_lints/src/large_const_arrays.rs
+++ b/clippy_lints/src/large_const_arrays.rs
@@ -24,7 +24,7 @@ declare_clippy_lint! {
     /// ```
     ///
     /// Use instead:
-    /// ```rust.ignore
+    /// ```rust,ignore
     /// pub static a = [0u32; 1_000_000];
     /// ```
     #[clippy::version = "1.44.0"]
diff --git a/clippy_lints/src/let_underscore.rs b/clippy_lints/src/let_underscore.rs
index 26c540e2223..176787497eb 100644
--- a/clippy_lints/src/let_underscore.rs
+++ b/clippy_lints/src/let_underscore.rs
@@ -99,12 +99,13 @@ declare_clippy_lint! {
 
 declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK, LET_UNDERSCORE_DROP]);
 
-const SYNC_GUARD_PATHS: [&[&str]; 5] = [
+const SYNC_GUARD_PATHS: [&[&str]; 6] = [
     &paths::MUTEX_GUARD,
     &paths::RWLOCK_READ_GUARD,
     &paths::RWLOCK_WRITE_GUARD,
-    &paths::PARKING_LOT_RAWMUTEX,
-    &paths::PARKING_LOT_RAWRWLOCK,
+    &paths::PARKING_LOT_MUTEX_GUARD,
+    &paths::PARKING_LOT_RWLOCK_READ_GUARD,
+    &paths::PARKING_LOT_RWLOCK_WRITE_GUARD,
 ];
 
 impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs
index 8a2cfbff953..563ad891603 100644
--- a/clippy_lints/src/lib.register_all.rs
+++ b/clippy_lints/src/lib.register_all.rs
@@ -3,12 +3,9 @@
 // Manual edits will be overwritten.
 
 store.register_group(true, "clippy::all", Some("clippy_all"), vec![
-    LintId::of(absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS),
     LintId::of(almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE),
     LintId::of(approx_const::APPROX_CONSTANT),
     LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS),
-    LintId::of(assign_ops::ASSIGN_OP_PATTERN),
-    LintId::of(assign_ops::MISREFACTORED_ASSIGN_OP),
     LintId::of(async_yields_async::ASYNC_YIELDS_ASYNC),
     LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
     LintId::of(attrs::DEPRECATED_CFG_ATTR),
@@ -18,8 +15,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE),
     LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK),
     LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF),
-    LintId::of(bit_mask::BAD_BIT_MASK),
-    LintId::of(bit_mask::INEFFECTIVE_BIT_MASK),
     LintId::of(blacklisted_name::BLACKLISTED_NAME),
     LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
     LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON),
@@ -43,6 +38,8 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(copies::IF_SAME_THEN_ELSE),
     LintId::of(crate_in_macro_def::CRATE_IN_MACRO_DEF),
     LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT),
+    LintId::of(default_instead_of_iter_empty::DEFAULT_INSTEAD_OF_ITER_EMPTY),
+    LintId::of(dereference::EXPLICIT_AUTO_DEREF),
     LintId::of(dereference::NEEDLESS_BORROW),
     LintId::of(derivable_impls::DERIVABLE_IMPLS),
     LintId::of(derive::DERIVE_HASH_XOR_EQ),
@@ -52,7 +49,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(disallowed_types::DISALLOWED_TYPES),
     LintId::of(doc::MISSING_SAFETY_DOC),
     LintId::of(doc::NEEDLESS_DOCTEST_MAIN),
-    LintId::of(double_comparison::DOUBLE_COMPARISONS),
     LintId::of(double_parens::DOUBLE_PARENS),
     LintId::of(drop_forget_ref::DROP_COPY),
     LintId::of(drop_forget_ref::DROP_NON_DROP),
@@ -62,18 +58,13 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(drop_forget_ref::FORGET_REF),
     LintId::of(drop_forget_ref::UNDROPPED_MANUALLY_DROPS),
     LintId::of(duplicate_mod::DUPLICATE_MOD),
-    LintId::of(duration_subsec::DURATION_SUBSEC),
     LintId::of(entry::MAP_ENTRY),
     LintId::of(enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT),
     LintId::of(enum_variants::ENUM_VARIANT_NAMES),
     LintId::of(enum_variants::MODULE_INCEPTION),
-    LintId::of(eq_op::EQ_OP),
-    LintId::of(eq_op::OP_REF),
-    LintId::of(erasing_op::ERASING_OP),
     LintId::of(escape::BOXED_LOCAL),
     LintId::of(eta_reduction::REDUNDANT_CLOSURE),
     LintId::of(explicit_write::EXPLICIT_WRITE),
-    LintId::of(float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS),
     LintId::of(float_literal::EXCESSIVE_PRECISION),
     LintId::of(format::USELESS_FORMAT),
     LintId::of(format_args::FORMAT_IN_FORMAT_ARGS),
@@ -93,7 +84,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(functions::RESULT_UNIT_ERR),
     LintId::of(functions::TOO_MANY_ARGUMENTS),
     LintId::of(get_first::GET_FIRST),
-    LintId::of(identity_op::IDENTITY_OP),
     LintId::of(if_let_mutex::IF_LET_MUTEX),
     LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING),
     LintId::of(infinite_iter::INFINITE_ITER),
@@ -118,6 +108,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(loops::FOR_KV_MAP),
     LintId::of(loops::FOR_LOOPS_OVER_FALLIBLES),
     LintId::of(loops::ITER_NEXT_LOOP),
+    LintId::of(loops::MANUAL_FIND),
     LintId::of(loops::MANUAL_FLATTEN),
     LintId::of(loops::MANUAL_MEMCPY),
     LintId::of(loops::MISSING_SPIN_LOOP),
@@ -134,6 +125,8 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(manual_async_fn::MANUAL_ASYNC_FN),
     LintId::of(manual_bits::MANUAL_BITS),
     LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
+    LintId::of(manual_rem_euclid::MANUAL_REM_EUCLID),
+    LintId::of(manual_retain::MANUAL_RETAIN),
     LintId::of(manual_strip::MANUAL_STRIP),
     LintId::of(map_clone::MAP_CLONE),
     LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN),
@@ -220,9 +213,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(methods::WRONG_SELF_CONVENTION),
     LintId::of(methods::ZST_OFFSET),
     LintId::of(minmax::MIN_MAX),
-    LintId::of(misc::CMP_NAN),
-    LintId::of(misc::CMP_OWNED),
-    LintId::of(misc::MODULO_ONE),
     LintId::of(misc::SHORT_CIRCUIT_STATEMENT),
     LintId::of(misc::TOPLEVEL_REF_ARG),
     LintId::of(misc::ZERO_PTR),
@@ -256,6 +246,23 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
     LintId::of(octal_escapes::OCTAL_ESCAPES),
     LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
+    LintId::of(operators::ABSURD_EXTREME_COMPARISONS),
+    LintId::of(operators::ASSIGN_OP_PATTERN),
+    LintId::of(operators::BAD_BIT_MASK),
+    LintId::of(operators::CMP_NAN),
+    LintId::of(operators::CMP_OWNED),
+    LintId::of(operators::DOUBLE_COMPARISONS),
+    LintId::of(operators::DURATION_SUBSEC),
+    LintId::of(operators::EQ_OP),
+    LintId::of(operators::ERASING_OP),
+    LintId::of(operators::FLOAT_EQUALITY_WITHOUT_ABS),
+    LintId::of(operators::IDENTITY_OP),
+    LintId::of(operators::INEFFECTIVE_BIT_MASK),
+    LintId::of(operators::MISREFACTORED_ASSIGN_OP),
+    LintId::of(operators::MODULO_ONE),
+    LintId::of(operators::OP_REF),
+    LintId::of(operators::PTR_EQ),
+    LintId::of(operators::SELF_ASSIGNMENT),
     LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP),
     LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL),
     LintId::of(partialeq_ne_impl::PARTIALEQ_NE_IMPL),
@@ -264,7 +271,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(ptr::INVALID_NULL_PTR_USAGE),
     LintId::of(ptr::MUT_FROM_REF),
     LintId::of(ptr::PTR_ARG),
-    LintId::of(ptr_eq::PTR_EQ),
     LintId::of(ptr_offset_with_cast::PTR_OFFSET_WITH_CAST),
     LintId::of(question_mark::QUESTION_MARK),
     LintId::of(ranges::MANUAL_RANGE_CONTAINS),
@@ -282,7 +288,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(repeat_once::REPEAT_ONCE),
     LintId::of(returns::LET_AND_RETURN),
     LintId::of(returns::NEEDLESS_RETURN),
-    LintId::of(self_assignment::SELF_ASSIGNMENT),
     LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS),
     LintId::of(serde_api::SERDE_API_MISUSE),
     LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
diff --git a/clippy_lints/src/lib.register_complexity.rs b/clippy_lints/src/lib.register_complexity.rs
index 4f1c3673f85..3784d3c68dc 100644
--- a/clippy_lints/src/lib.register_complexity.rs
+++ b/clippy_lints/src/lib.register_complexity.rs
@@ -9,21 +9,21 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec!
     LintId::of(bytes_count_to_len::BYTES_COUNT_TO_LEN),
     LintId::of(casts::CHAR_LIT_AS_U8),
     LintId::of(casts::UNNECESSARY_CAST),
+    LintId::of(dereference::EXPLICIT_AUTO_DEREF),
     LintId::of(derivable_impls::DERIVABLE_IMPLS),
-    LintId::of(double_comparison::DOUBLE_COMPARISONS),
     LintId::of(double_parens::DOUBLE_PARENS),
-    LintId::of(duration_subsec::DURATION_SUBSEC),
     LintId::of(explicit_write::EXPLICIT_WRITE),
     LintId::of(format::USELESS_FORMAT),
     LintId::of(functions::TOO_MANY_ARGUMENTS),
-    LintId::of(identity_op::IDENTITY_OP),
     LintId::of(int_plus_one::INT_PLUS_ONE),
     LintId::of(lifetimes::EXTRA_UNUSED_LIFETIMES),
     LintId::of(lifetimes::NEEDLESS_LIFETIMES),
     LintId::of(loops::EXPLICIT_COUNTER_LOOP),
+    LintId::of(loops::MANUAL_FIND),
     LintId::of(loops::MANUAL_FLATTEN),
     LintId::of(loops::SINGLE_ELEMENT_LOOP),
     LintId::of(loops::WHILE_LET_LOOP),
+    LintId::of(manual_rem_euclid::MANUAL_REM_EUCLID),
     LintId::of(manual_strip::MANUAL_STRIP),
     LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN),
     LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN),
@@ -69,6 +69,9 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec!
     LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD),
     LintId::of(no_effect::NO_EFFECT),
     LintId::of(no_effect::UNNECESSARY_OPERATION),
+    LintId::of(operators::DOUBLE_COMPARISONS),
+    LintId::of(operators::DURATION_SUBSEC),
+    LintId::of(operators::IDENTITY_OP),
     LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL),
     LintId::of(partialeq_ne_impl::PARTIALEQ_NE_IMPL),
     LintId::of(precedence::PRECEDENCE),
diff --git a/clippy_lints/src/lib.register_correctness.rs b/clippy_lints/src/lib.register_correctness.rs
index 92a3a0aabf1..7d5e65cb27a 100644
--- a/clippy_lints/src/lib.register_correctness.rs
+++ b/clippy_lints/src/lib.register_correctness.rs
@@ -3,14 +3,11 @@
 // Manual edits will be overwritten.
 
 store.register_group(true, "clippy::correctness", Some("clippy_correctness"), vec![
-    LintId::of(absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS),
     LintId::of(approx_const::APPROX_CONSTANT),
     LintId::of(async_yields_async::ASYNC_YIELDS_ASYNC),
     LintId::of(attrs::DEPRECATED_SEMVER),
     LintId::of(attrs::MISMATCHED_TARGET_OS),
     LintId::of(attrs::USELESS_ATTRIBUTE),
-    LintId::of(bit_mask::BAD_BIT_MASK),
-    LintId::of(bit_mask::INEFFECTIVE_BIT_MASK),
     LintId::of(booleans::LOGIC_BUG),
     LintId::of(casts::CAST_REF_TO_MUT),
     LintId::of(casts::CAST_SLICE_DIFFERENT_SIZES),
@@ -24,8 +21,6 @@ store.register_group(true, "clippy::correctness", Some("clippy_correctness"), ve
     LintId::of(drop_forget_ref::FORGET_REF),
     LintId::of(drop_forget_ref::UNDROPPED_MANUALLY_DROPS),
     LintId::of(enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT),
-    LintId::of(eq_op::EQ_OP),
-    LintId::of(erasing_op::ERASING_OP),
     LintId::of(format_impl::RECURSIVE_FORMAT_IMPL),
     LintId::of(formatting::POSSIBLE_MISSING_COMMA),
     LintId::of(functions::NOT_UNSAFE_PTR_ARG_DEREF),
@@ -47,17 +42,22 @@ store.register_group(true, "clippy::correctness", Some("clippy_correctness"), ve
     LintId::of(methods::UNINIT_ASSUMED_INIT),
     LintId::of(methods::ZST_OFFSET),
     LintId::of(minmax::MIN_MAX),
-    LintId::of(misc::CMP_NAN),
-    LintId::of(misc::MODULO_ONE),
     LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
     LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
+    LintId::of(operators::ABSURD_EXTREME_COMPARISONS),
+    LintId::of(operators::BAD_BIT_MASK),
+    LintId::of(operators::CMP_NAN),
+    LintId::of(operators::EQ_OP),
+    LintId::of(operators::ERASING_OP),
+    LintId::of(operators::INEFFECTIVE_BIT_MASK),
+    LintId::of(operators::MODULO_ONE),
+    LintId::of(operators::SELF_ASSIGNMENT),
     LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP),
     LintId::of(ptr::INVALID_NULL_PTR_USAGE),
     LintId::of(ptr::MUT_FROM_REF),
     LintId::of(ranges::REVERSED_EMPTY_RANGES),
     LintId::of(read_zero_byte_vec::READ_ZERO_BYTE_VEC),
     LintId::of(regex::INVALID_REGEX),
-    LintId::of(self_assignment::SELF_ASSIGNMENT),
     LintId::of(serde_api::SERDE_API_MISUSE),
     LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT),
     LintId::of(swap::ALMOST_SWAPPED),
diff --git a/clippy_lints/src/lib.register_internal.rs b/clippy_lints/src/lib.register_internal.rs
index 4778f4fdfa7..be63646a12f 100644
--- a/clippy_lints/src/lib.register_internal.rs
+++ b/clippy_lints/src/lib.register_internal.rs
@@ -6,6 +6,7 @@ store.register_group(true, "clippy::internal", Some("clippy_internal"), vec![
     LintId::of(utils::internal_lints::CLIPPY_LINTS_INTERNAL),
     LintId::of(utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS),
     LintId::of(utils::internal_lints::COMPILER_LINT_FUNCTIONS),
+    LintId::of(utils::internal_lints::DEFAULT_DEPRECATION_REASON),
     LintId::of(utils::internal_lints::DEFAULT_LINT),
     LintId::of(utils::internal_lints::IF_CHAIN_STYLE),
     LintId::of(utils::internal_lints::INTERNING_DEFINED_SYMBOL),
diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs
index 8ad984c68b8..d3c75f8b519 100644
--- a/clippy_lints/src/lib.register_lints.rs
+++ b/clippy_lints/src/lib.register_lints.rs
@@ -10,6 +10,8 @@ store.register_lints(&[
     #[cfg(feature = "internal")]
     utils::internal_lints::COMPILER_LINT_FUNCTIONS,
     #[cfg(feature = "internal")]
+    utils::internal_lints::DEFAULT_DEPRECATION_REASON,
+    #[cfg(feature = "internal")]
     utils::internal_lints::DEFAULT_LINT,
     #[cfg(feature = "internal")]
     utils::internal_lints::IF_CHAIN_STYLE,
@@ -33,7 +35,6 @@ store.register_lints(&[
     utils::internal_lints::PRODUCE_ICE,
     #[cfg(feature = "internal")]
     utils::internal_lints::UNNECESSARY_SYMBOL_STR,
-    absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS,
     almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE,
     approx_const::APPROX_CONSTANT,
     as_conversions::AS_CONVERSIONS,
@@ -41,8 +42,6 @@ store.register_lints(&[
     asm_syntax::INLINE_ASM_X86_ATT_SYNTAX,
     asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX,
     assertions_on_constants::ASSERTIONS_ON_CONSTANTS,
-    assign_ops::ASSIGN_OP_PATTERN,
-    assign_ops::MISREFACTORED_ASSIGN_OP,
     async_yields_async::ASYNC_YIELDS_ASYNC,
     attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON,
     attrs::BLANKET_CLIPPY_RESTRICTION_LINTS,
@@ -55,9 +54,6 @@ store.register_lints(&[
     await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE,
     await_holding_invalid::AWAIT_HOLDING_LOCK,
     await_holding_invalid::AWAIT_HOLDING_REFCELL_REF,
-    bit_mask::BAD_BIT_MASK,
-    bit_mask::INEFFECTIVE_BIT_MASK,
-    bit_mask::VERBOSE_BIT_MASK,
     blacklisted_name::BLACKLISTED_NAME,
     blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS,
     bool_assert_comparison::BOOL_ASSERT_COMPARISON,
@@ -105,8 +101,10 @@ store.register_lints(&[
     dbg_macro::DBG_MACRO,
     default::DEFAULT_TRAIT_ACCESS,
     default::FIELD_REASSIGN_WITH_DEFAULT,
+    default_instead_of_iter_empty::DEFAULT_INSTEAD_OF_ITER_EMPTY,
     default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK,
     default_union_representation::DEFAULT_UNION_REPRESENTATION,
+    dereference::EXPLICIT_AUTO_DEREF,
     dereference::EXPLICIT_DEREF_METHODS,
     dereference::NEEDLESS_BORROW,
     dereference::REF_BINDING_TO_REFERENCE,
@@ -125,7 +123,6 @@ store.register_lints(&[
     doc::MISSING_SAFETY_DOC,
     doc::NEEDLESS_DOCTEST_MAIN,
     doc_link_with_quotes::DOC_LINK_WITH_QUOTES,
-    double_comparison::DOUBLE_COMPARISONS,
     double_parens::DOUBLE_PARENS,
     drop_forget_ref::DROP_COPY,
     drop_forget_ref::DROP_NON_DROP,
@@ -135,7 +132,6 @@ store.register_lints(&[
     drop_forget_ref::FORGET_REF,
     drop_forget_ref::UNDROPPED_MANUALLY_DROPS,
     duplicate_mod::DUPLICATE_MOD,
-    duration_subsec::DURATION_SUBSEC,
     else_if_without_else::ELSE_IF_WITHOUT_ELSE,
     empty_drop::EMPTY_DROP,
     empty_enum::EMPTY_ENUM,
@@ -145,10 +141,7 @@ store.register_lints(&[
     enum_variants::ENUM_VARIANT_NAMES,
     enum_variants::MODULE_INCEPTION,
     enum_variants::MODULE_NAME_REPETITIONS,
-    eq_op::EQ_OP,
-    eq_op::OP_REF,
     equatable_if_let::EQUATABLE_IF_LET,
-    erasing_op::ERASING_OP,
     escape::BOXED_LOCAL,
     eta_reduction::REDUNDANT_CLOSURE,
     eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS,
@@ -159,7 +152,6 @@ store.register_lints(&[
     exit::EXIT,
     explicit_write::EXPLICIT_WRITE,
     fallible_impl_from::FALLIBLE_IMPL_FROM,
-    float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS,
     float_literal::EXCESSIVE_PRECISION,
     float_literal::LOSSY_FLOAT_LITERAL,
     floating_point_arithmetic::IMPRECISE_FLOPS,
@@ -185,7 +177,6 @@ store.register_lints(&[
     functions::TOO_MANY_LINES,
     future_not_send::FUTURE_NOT_SEND,
     get_first::GET_FIRST,
-    identity_op::IDENTITY_OP,
     if_let_mutex::IF_LET_MUTEX,
     if_not_else::IF_NOT_ELSE,
     if_then_some_else_none::IF_THEN_SOME_ELSE_NONE,
@@ -204,7 +195,6 @@ store.register_lints(&[
     init_numbered_fields::INIT_NUMBERED_FIELDS,
     inline_fn_without_body::INLINE_FN_WITHOUT_BODY,
     int_plus_one::INT_PLUS_ONE,
-    integer_division::INTEGER_DIVISION,
     invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS,
     items_after_statements::ITEMS_AFTER_STATEMENTS,
     iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR,
@@ -234,6 +224,7 @@ store.register_lints(&[
     loops::FOR_KV_MAP,
     loops::FOR_LOOPS_OVER_FALLIBLES,
     loops::ITER_NEXT_LOOP,
+    loops::MANUAL_FIND,
     loops::MANUAL_FLATTEN,
     loops::MANUAL_MEMCPY,
     loops::MISSING_SPIN_LOOP,
@@ -253,6 +244,8 @@ store.register_lints(&[
     manual_bits::MANUAL_BITS,
     manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE,
     manual_ok_or::MANUAL_OK_OR,
+    manual_rem_euclid::MANUAL_REM_EUCLID,
+    manual_retain::MANUAL_RETAIN,
     manual_strip::MANUAL_STRIP,
     map_clone::MAP_CLONE,
     map_err_ignore::MAP_ERR_IGNORE,
@@ -364,11 +357,6 @@ store.register_lints(&[
     methods::WRONG_SELF_CONVENTION,
     methods::ZST_OFFSET,
     minmax::MIN_MAX,
-    misc::CMP_NAN,
-    misc::CMP_OWNED,
-    misc::FLOAT_CMP,
-    misc::FLOAT_CMP_CONST,
-    misc::MODULO_ONE,
     misc::SHORT_CIRCUIT_STATEMENT,
     misc::TOPLEVEL_REF_ARG,
     misc::USED_UNDERSCORE_BINDING,
@@ -392,7 +380,6 @@ store.register_lints(&[
     mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION,
     module_style::MOD_MODULE_FILES,
     module_style::SELF_NAMED_MODULE_FILES,
-    modulo_arithmetic::MODULO_ARITHMETIC,
     mut_key::MUTABLE_KEY_TYPE,
     mut_mut::MUT_MUT,
     mut_mutex_lock::MUT_MUTEX_LOCK,
@@ -401,7 +388,6 @@ store.register_lints(&[
     mutex_atomic::MUTEX_ATOMIC,
     mutex_atomic::MUTEX_INTEGER,
     needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE,
-    needless_bitwise_bool::NEEDLESS_BITWISE_BOOL,
     needless_bool::BOOL_COMPARISON,
     needless_bool::NEEDLESS_BOOL,
     needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE,
@@ -426,11 +412,34 @@ store.register_lints(&[
     non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS,
     non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY,
     nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES,
-    numeric_arithmetic::FLOAT_ARITHMETIC,
-    numeric_arithmetic::INTEGER_ARITHMETIC,
     octal_escapes::OCTAL_ESCAPES,
     only_used_in_recursion::ONLY_USED_IN_RECURSION,
     open_options::NONSENSICAL_OPEN_OPTIONS,
+    operators::ABSURD_EXTREME_COMPARISONS,
+    operators::ASSIGN_OP_PATTERN,
+    operators::BAD_BIT_MASK,
+    operators::CMP_NAN,
+    operators::CMP_OWNED,
+    operators::DOUBLE_COMPARISONS,
+    operators::DURATION_SUBSEC,
+    operators::EQ_OP,
+    operators::ERASING_OP,
+    operators::FLOAT_ARITHMETIC,
+    operators::FLOAT_CMP,
+    operators::FLOAT_CMP_CONST,
+    operators::FLOAT_EQUALITY_WITHOUT_ABS,
+    operators::IDENTITY_OP,
+    operators::INEFFECTIVE_BIT_MASK,
+    operators::INTEGER_ARITHMETIC,
+    operators::INTEGER_DIVISION,
+    operators::MISREFACTORED_ASSIGN_OP,
+    operators::MODULO_ARITHMETIC,
+    operators::MODULO_ONE,
+    operators::NEEDLESS_BITWISE_BOOL,
+    operators::OP_REF,
+    operators::PTR_EQ,
+    operators::SELF_ASSIGNMENT,
+    operators::VERBOSE_BIT_MASK,
     option_env_unwrap::OPTION_ENV_UNWRAP,
     option_if_let_else::OPTION_IF_LET_ELSE,
     overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL,
@@ -449,7 +458,6 @@ store.register_lints(&[
     ptr::INVALID_NULL_PTR_USAGE,
     ptr::MUT_FROM_REF,
     ptr::PTR_ARG,
-    ptr_eq::PTR_EQ,
     ptr_offset_with_cast::PTR_OFFSET_WITH_CAST,
     pub_use::PUB_USE,
     question_mark::QUESTION_MARK,
@@ -477,7 +485,6 @@ store.register_lints(&[
     returns::LET_AND_RETURN,
     returns::NEEDLESS_RETURN,
     same_name_method::SAME_NAME_METHOD,
-    self_assignment::SELF_ASSIGNMENT,
     self_named_constructors::SELF_NAMED_CONSTRUCTORS,
     semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED,
     serde_api::SERDE_API_MISUSE,
diff --git a/clippy_lints/src/lib.register_pedantic.rs b/clippy_lints/src/lib.register_pedantic.rs
index 48de92ae945..a1b54665814 100644
--- a/clippy_lints/src/lib.register_pedantic.rs
+++ b/clippy_lints/src/lib.register_pedantic.rs
@@ -4,7 +4,6 @@
 
 store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
     LintId::of(attrs::INLINE_ALWAYS),
-    LintId::of(bit_mask::VERBOSE_BIT_MASK),
     LintId::of(borrow_as_ptr::BORROW_AS_PTR),
     LintId::of(bytecount::NAIVE_BYTECOUNT),
     LintId::of(case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS),
@@ -65,17 +64,18 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
     LintId::of(methods::INEFFICIENT_TO_STRING),
     LintId::of(methods::MAP_UNWRAP_OR),
     LintId::of(methods::UNNECESSARY_JOIN),
-    LintId::of(misc::FLOAT_CMP),
     LintId::of(misc::USED_UNDERSCORE_BINDING),
     LintId::of(mismatching_type_param_order::MISMATCHING_TYPE_PARAM_ORDER),
     LintId::of(mut_mut::MUT_MUT),
-    LintId::of(needless_bitwise_bool::NEEDLESS_BITWISE_BOOL),
     LintId::of(needless_continue::NEEDLESS_CONTINUE),
     LintId::of(needless_for_each::NEEDLESS_FOR_EACH),
     LintId::of(needless_pass_by_value::NEEDLESS_PASS_BY_VALUE),
     LintId::of(no_effect::NO_EFFECT_UNDERSCORE_BINDING),
     LintId::of(non_expressive_names::MANY_SINGLE_CHAR_NAMES),
     LintId::of(non_expressive_names::SIMILAR_NAMES),
+    LintId::of(operators::FLOAT_CMP),
+    LintId::of(operators::NEEDLESS_BITWISE_BOOL),
+    LintId::of(operators::VERBOSE_BIT_MASK),
     LintId::of(pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE),
     LintId::of(pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF),
     LintId::of(ranges::RANGE_MINUS_ONE),
diff --git a/clippy_lints/src/lib.register_perf.rs b/clippy_lints/src/lib.register_perf.rs
index 82431863e6c..6bf519c24e8 100644
--- a/clippy_lints/src/lib.register_perf.rs
+++ b/clippy_lints/src/lib.register_perf.rs
@@ -13,6 +13,7 @@ store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![
     LintId::of(loops::MANUAL_MEMCPY),
     LintId::of(loops::MISSING_SPIN_LOOP),
     LintId::of(loops::NEEDLESS_COLLECT),
+    LintId::of(manual_retain::MANUAL_RETAIN),
     LintId::of(methods::EXPECT_FUN_CALL),
     LintId::of(methods::EXTEND_WITH_DRAIN),
     LintId::of(methods::ITER_NTH),
@@ -21,7 +22,7 @@ store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![
     LintId::of(methods::OR_FUN_CALL),
     LintId::of(methods::SINGLE_CHAR_PATTERN),
     LintId::of(methods::UNNECESSARY_TO_OWNED),
-    LintId::of(misc::CMP_OWNED),
+    LintId::of(operators::CMP_OWNED),
     LintId::of(redundant_clone::REDUNDANT_CLONE),
     LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
     LintId::of(types::BOX_COLLECTION),
diff --git a/clippy_lints/src/lib.register_restriction.rs b/clippy_lints/src/lib.register_restriction.rs
index 3695012f552..970e9db4772 100644
--- a/clippy_lints/src/lib.register_restriction.rs
+++ b/clippy_lints/src/lib.register_restriction.rs
@@ -25,7 +25,6 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve
     LintId::of(implicit_return::IMPLICIT_RETURN),
     LintId::of(indexing_slicing::INDEXING_SLICING),
     LintId::of(inherent_impl::MULTIPLE_INHERENT_IMPL),
-    LintId::of(integer_division::INTEGER_DIVISION),
     LintId::of(large_include_file::LARGE_INCLUDE_FILE),
     LintId::of(let_underscore::LET_UNDERSCORE_MUST_USE),
     LintId::of(literal_representation::DECIMAL_LITERAL_REPRESENTATION),
@@ -39,7 +38,6 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve
     LintId::of(methods::FILETYPE_IS_FILE),
     LintId::of(methods::GET_UNWRAP),
     LintId::of(methods::UNWRAP_USED),
-    LintId::of(misc::FLOAT_CMP_CONST),
     LintId::of(misc_early::SEPARATED_LITERAL_SUFFIX),
     LintId::of(misc_early::UNNEEDED_FIELD_PATTERN),
     LintId::of(misc_early::UNSEPARATED_LITERAL_SUFFIX),
@@ -49,9 +47,11 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve
     LintId::of(mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION),
     LintId::of(module_style::MOD_MODULE_FILES),
     LintId::of(module_style::SELF_NAMED_MODULE_FILES),
-    LintId::of(modulo_arithmetic::MODULO_ARITHMETIC),
-    LintId::of(numeric_arithmetic::FLOAT_ARITHMETIC),
-    LintId::of(numeric_arithmetic::INTEGER_ARITHMETIC),
+    LintId::of(operators::FLOAT_ARITHMETIC),
+    LintId::of(operators::FLOAT_CMP_CONST),
+    LintId::of(operators::INTEGER_ARITHMETIC),
+    LintId::of(operators::INTEGER_DIVISION),
+    LintId::of(operators::MODULO_ARITHMETIC),
     LintId::of(panic_in_result_fn::PANIC_IN_RESULT_FN),
     LintId::of(panic_unimplemented::PANIC),
     LintId::of(panic_unimplemented::TODO),
diff --git a/clippy_lints/src/lib.register_style.rs b/clippy_lints/src/lib.register_style.rs
index b6992ae0ad2..15a1bc569af 100644
--- a/clippy_lints/src/lib.register_style.rs
+++ b/clippy_lints/src/lib.register_style.rs
@@ -4,7 +4,6 @@
 
 store.register_group(true, "clippy::style", Some("clippy_style"), vec![
     LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS),
-    LintId::of(assign_ops::ASSIGN_OP_PATTERN),
     LintId::of(blacklisted_name::BLACKLISTED_NAME),
     LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
     LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON),
@@ -14,6 +13,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
     LintId::of(collapsible_if::COLLAPSIBLE_IF),
     LintId::of(comparison_chain::COMPARISON_CHAIN),
     LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT),
+    LintId::of(default_instead_of_iter_empty::DEFAULT_INSTEAD_OF_ITER_EMPTY),
     LintId::of(dereference::NEEDLESS_BORROW),
     LintId::of(derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ),
     LintId::of(disallowed_methods::DISALLOWED_METHODS),
@@ -22,7 +22,6 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
     LintId::of(doc::NEEDLESS_DOCTEST_MAIN),
     LintId::of(enum_variants::ENUM_VARIANT_NAMES),
     LintId::of(enum_variants::MODULE_INCEPTION),
-    LintId::of(eq_op::OP_REF),
     LintId::of(eta_reduction::REDUNDANT_CLOSURE),
     LintId::of(float_literal::EXCESSIVE_PRECISION),
     LintId::of(from_over_into::FROM_OVER_INTO),
@@ -97,9 +96,11 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
     LintId::of(non_copy_const::BORROW_INTERIOR_MUTABLE_CONST),
     LintId::of(non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST),
     LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
+    LintId::of(operators::ASSIGN_OP_PATTERN),
+    LintId::of(operators::OP_REF),
+    LintId::of(operators::PTR_EQ),
     LintId::of(ptr::CMP_NULL),
     LintId::of(ptr::PTR_ARG),
-    LintId::of(ptr_eq::PTR_EQ),
     LintId::of(question_mark::QUESTION_MARK),
     LintId::of(ranges::MANUAL_RANGE_CONTAINS),
     LintId::of(redundant_field_names::REDUNDANT_FIELD_NAMES),
diff --git a/clippy_lints/src/lib.register_suspicious.rs b/clippy_lints/src/lib.register_suspicious.rs
index 7b13713c36e..f7558f87098 100644
--- a/clippy_lints/src/lib.register_suspicious.rs
+++ b/clippy_lints/src/lib.register_suspicious.rs
@@ -4,7 +4,6 @@
 
 store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec![
     LintId::of(almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE),
-    LintId::of(assign_ops::MISREFACTORED_ASSIGN_OP),
     LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
     LintId::of(await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE),
     LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK),
@@ -16,7 +15,6 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec!
     LintId::of(drop_forget_ref::DROP_NON_DROP),
     LintId::of(drop_forget_ref::FORGET_NON_DROP),
     LintId::of(duplicate_mod::DUPLICATE_MOD),
-    LintId::of(float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS),
     LintId::of(format_impl::PRINT_IN_FORMAT_IMPL),
     LintId::of(formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING),
     LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING),
@@ -29,6 +27,8 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec!
     LintId::of(methods::SUSPICIOUS_MAP),
     LintId::of(mut_key::MUTABLE_KEY_TYPE),
     LintId::of(octal_escapes::OCTAL_ESCAPES),
+    LintId::of(operators::FLOAT_EQUALITY_WITHOUT_ABS),
+    LintId::of(operators::MISREFACTORED_ASSIGN_OP),
     LintId::of(rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT),
     LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
     LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index 84898eae05a..172fdf8c852 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -7,6 +7,7 @@
 #![feature(let_chains)]
 #![feature(let_else)]
 #![feature(lint_reasons)]
+#![feature(never_type)]
 #![feature(once_cell)]
 #![feature(rustc_private)]
 #![feature(stmt_expr_attributes)]
@@ -52,6 +53,7 @@ extern crate clippy_utils;
 use clippy_utils::parse_msrv;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_lint::LintId;
+use rustc_semver::RustcVersion;
 use rustc_session::Session;
 
 /// Macro used to declare a Clippy lint.
@@ -159,25 +161,22 @@ macro_rules! declare_clippy_lint {
 }
 
 #[cfg(feature = "internal")]
-mod deprecated_lints;
+pub mod deprecated_lints;
 #[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))]
 mod utils;
 
 mod renamed_lints;
 
 // begin lints modules, do not remove this comment, it’s used in `update_lints`
-mod absurd_extreme_comparisons;
 mod almost_complete_letter_range;
 mod approx_const;
 mod as_conversions;
 mod as_underscore;
 mod asm_syntax;
 mod assertions_on_constants;
-mod assign_ops;
 mod async_yields_async;
 mod attrs;
 mod await_holding_invalid;
-mod bit_mask;
 mod blacklisted_name;
 mod blocks_in_if_conditions;
 mod bool_assert_comparison;
@@ -199,6 +198,7 @@ mod crate_in_macro_def;
 mod create_dir;
 mod dbg_macro;
 mod default;
+mod default_instead_of_iter_empty;
 mod default_numeric_fallback;
 mod default_union_representation;
 mod dereference;
@@ -209,11 +209,9 @@ mod disallowed_script_idents;
 mod disallowed_types;
 mod doc;
 mod doc_link_with_quotes;
-mod double_comparison;
 mod double_parens;
 mod drop_forget_ref;
 mod duplicate_mod;
-mod duration_subsec;
 mod else_if_without_else;
 mod empty_drop;
 mod empty_enum;
@@ -221,9 +219,7 @@ mod empty_structs_with_brackets;
 mod entry;
 mod enum_clike;
 mod enum_variants;
-mod eq_op;
 mod equatable_if_let;
-mod erasing_op;
 mod escape;
 mod eta_reduction;
 mod excessive_bools;
@@ -231,7 +227,6 @@ mod exhaustive_items;
 mod exit;
 mod explicit_write;
 mod fallible_impl_from;
-mod float_equality_without_abs;
 mod float_literal;
 mod floating_point_arithmetic;
 mod format;
@@ -244,7 +239,6 @@ mod from_str_radix_10;
 mod functions;
 mod future_not_send;
 mod get_first;
-mod identity_op;
 mod if_let_mutex;
 mod if_not_else;
 mod if_then_some_else_none;
@@ -260,7 +254,6 @@ mod inherent_to_string;
 mod init_numbered_fields;
 mod inline_fn_without_body;
 mod int_plus_one;
-mod integer_division;
 mod invalid_upcast_comparisons;
 mod items_after_statements;
 mod iter_not_returning_iterator;
@@ -281,6 +274,8 @@ mod manual_async_fn;
 mod manual_bits;
 mod manual_non_exhaustive;
 mod manual_ok_or;
+mod manual_rem_euclid;
+mod manual_retain;
 mod manual_strip;
 mod map_clone;
 mod map_err_ignore;
@@ -300,7 +295,6 @@ mod missing_enforced_import_rename;
 mod missing_inline;
 mod mixed_read_write_in_expression;
 mod module_style;
-mod modulo_arithmetic;
 mod mut_key;
 mod mut_mut;
 mod mut_mutex_lock;
@@ -308,7 +302,6 @@ mod mut_reference;
 mod mutable_debug_assertion;
 mod mutex_atomic;
 mod needless_arbitrary_self_type;
-mod needless_bitwise_bool;
 mod needless_bool;
 mod needless_borrowed_ref;
 mod needless_continue;
@@ -327,10 +320,10 @@ mod non_expressive_names;
 mod non_octal_unix_permissions;
 mod non_send_fields_in_send_ty;
 mod nonstandard_macro_braces;
-mod numeric_arithmetic;
 mod octal_escapes;
 mod only_used_in_recursion;
 mod open_options;
+mod operators;
 mod option_env_unwrap;
 mod option_if_let_else;
 mod overflow_check_conditional;
@@ -342,7 +335,6 @@ mod path_buf_push_overwrite;
 mod pattern_type_mismatch;
 mod precedence;
 mod ptr;
-mod ptr_eq;
 mod ptr_offset_with_cast;
 mod pub_use;
 mod question_mark;
@@ -363,7 +355,6 @@ mod repeat_once;
 mod return_self_not_must_use;
 mod returns;
 mod same_name_method;
-mod self_assignment;
 mod self_named_constructors;
 mod semicolon_if_nothing_returned;
 mod serde_api;
@@ -448,6 +439,39 @@ pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore, sess: &Se
     store.register_pre_expansion_pass(move || Box::new(attrs::EarlyAttributes { msrv }));
 }
 
+fn read_msrv(conf: &Conf, sess: &Session) -> Option<RustcVersion> {
+    let cargo_msrv = std::env::var("CARGO_PKG_RUST_VERSION")
+        .ok()
+        .and_then(|v| parse_msrv(&v, None, None));
+    let clippy_msrv = conf.msrv.as_ref().and_then(|s| {
+        parse_msrv(s, None, None).or_else(|| {
+            sess.err(&format!(
+                "error reading Clippy's configuration file. `{}` is not a valid Rust version",
+                s
+            ));
+            None
+        })
+    });
+
+    if let Some(cargo_msrv) = cargo_msrv {
+        if let Some(clippy_msrv) = clippy_msrv {
+            // if both files have an msrv, let's compare them and emit a warning if they differ
+            if clippy_msrv != cargo_msrv {
+                sess.warn(&format!(
+                    "the MSRV in `clippy.toml` and `Cargo.toml` differ; using `{}` from `clippy.toml`",
+                    clippy_msrv
+                ));
+            }
+
+            Some(clippy_msrv)
+        } else {
+            Some(cargo_msrv)
+        }
+    } else {
+        clippy_msrv
+    }
+}
+
 #[doc(hidden)]
 pub fn read_conf(sess: &Session) -> Conf {
     let file_name = match utils::conf::lookup_conf_file() {
@@ -463,12 +487,11 @@ pub fn read_conf(sess: &Session) -> Conf {
     let TryConf { conf, errors } = utils::conf::read(&file_name);
     // all conf errors are non-fatal, we just use the default conf in case of error
     for error in errors {
-        sess.struct_err(&format!(
+        sess.err(&format!(
             "error reading Clippy's configuration file `{}`: {}",
             file_name.display(),
             format_error(error)
-        ))
-        .emit();
+        ));
     }
 
     conf
@@ -543,21 +566,14 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         ))
     });
     store.register_late_pass(|| Box::new(booleans::NonminimalBool));
-    store.register_late_pass(|| Box::new(needless_bitwise_bool::NeedlessBitwiseBool));
-    store.register_late_pass(|| Box::new(eq_op::EqOp));
     store.register_late_pass(|| Box::new(enum_clike::UnportableVariant));
     store.register_late_pass(|| Box::new(float_literal::FloatLiteral));
-    let verbose_bit_mask_threshold = conf.verbose_bit_mask_threshold;
-    store.register_late_pass(move || Box::new(bit_mask::BitMask::new(verbose_bit_mask_threshold)));
     store.register_late_pass(|| Box::new(ptr::Ptr));
-    store.register_late_pass(|| Box::new(ptr_eq::PtrEq));
     store.register_late_pass(|| Box::new(needless_bool::NeedlessBool));
     store.register_late_pass(|| Box::new(needless_bool::BoolComparison));
     store.register_late_pass(|| Box::new(needless_for_each::NeedlessForEach));
     store.register_late_pass(|| Box::new(misc::MiscLints));
     store.register_late_pass(|| Box::new(eta_reduction::EtaReduction));
-    store.register_late_pass(|| Box::new(identity_op::IdentityOp));
-    store.register_late_pass(|| Box::new(erasing_op::ErasingOp));
     store.register_late_pass(|| Box::new(mut_mut::MutMut));
     store.register_late_pass(|| Box::new(mut_reference::UnnecessaryMutPassed));
     store.register_late_pass(|| Box::new(len_zero::LenZero));
@@ -575,16 +591,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| Box::new(non_octal_unix_permissions::NonOctalUnixPermissions));
     store.register_early_pass(|| Box::new(unnecessary_self_imports::UnnecessarySelfImports));
 
-    let msrv = conf.msrv.as_ref().and_then(|s| {
-        parse_msrv(s, None, None).or_else(|| {
-            sess.err(&format!(
-                "error reading Clippy's configuration file. `{}` is not a valid Rust version",
-                s
-            ));
-            None
-        })
-    });
-
+    let msrv = read_msrv(conf, sess);
     let avoid_breaking_exported_api = conf.avoid_breaking_exported_api;
     let allow_expect_in_tests = conf.allow_expect_in_tests;
     let allow_unwrap_in_tests = conf.allow_unwrap_in_tests;
@@ -639,7 +646,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| Box::new(borrow_deref_ref::BorrowDerefRef));
     store.register_late_pass(|| Box::new(no_effect::NoEffect));
     store.register_late_pass(|| Box::new(temporary_assignment::TemporaryAssignment));
-    store.register_late_pass(|| Box::new(transmute::Transmute));
+    store.register_late_pass(move || Box::new(transmute::Transmute::new(msrv)));
     let cognitive_complexity_threshold = conf.cognitive_complexity_threshold;
     store.register_late_pass(move || {
         Box::new(cognitive_complexity::CognitiveComplexity::new(
@@ -655,7 +662,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| Box::new(derivable_impls::DerivableImpls));
     store.register_late_pass(|| Box::new(drop_forget_ref::DropForgetRef));
     store.register_late_pass(|| Box::new(empty_enum::EmptyEnum));
-    store.register_late_pass(|| Box::new(absurd_extreme_comparisons::AbsurdExtremeComparisons));
     store.register_late_pass(|| Box::new(invalid_upcast_comparisons::InvalidUpcastComparisons));
     store.register_late_pass(|| Box::new(regex::Regex));
     store.register_late_pass(|| Box::new(copies::CopyAndPaste));
@@ -678,8 +684,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(move || Box::new(doc::DocMarkdown::new(doc_valid_idents.clone())));
     store.register_late_pass(|| Box::new(neg_multiply::NegMultiply));
     store.register_late_pass(|| Box::new(mem_forget::MemForget));
-    store.register_late_pass(|| Box::new(numeric_arithmetic::NumericArithmetic::default()));
-    store.register_late_pass(|| Box::new(assign_ops::AssignOps));
     store.register_late_pass(|| Box::new(let_if_seq::LetIfSeq));
     store.register_late_pass(|| Box::new(mixed_read_write_in_expression::EvalOrderDependence));
     store.register_late_pass(|| Box::new(missing_doc::MissingDoc::new()));
@@ -706,7 +710,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| Box::new(useless_conversion::UselessConversion::default()));
     store.register_late_pass(|| Box::new(implicit_hasher::ImplicitHasher));
     store.register_late_pass(|| Box::new(fallible_impl_from::FallibleImplFrom));
-    store.register_late_pass(|| Box::new(double_comparison::DoubleComparisons));
     store.register_late_pass(|| Box::new(question_mark::QuestionMark));
     store.register_early_pass(|| Box::new(suspicious_operation_groupings::SuspiciousOperationGroupings));
     store.register_late_pass(|| Box::new(suspicious_trait_impl::SuspiciousImpl));
@@ -714,7 +717,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| Box::new(inherent_impl::MultipleInherentImpl));
     store.register_late_pass(|| Box::new(neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd));
     store.register_late_pass(|| Box::new(unwrap::Unwrap));
-    store.register_late_pass(|| Box::new(duration_subsec::DurationSubsec));
     store.register_late_pass(|| Box::new(indexing_slicing::IndexingSlicing));
     store.register_late_pass(|| Box::new(non_copy_const::NonCopyConst));
     store.register_late_pass(|| Box::new(ptr_offset_with_cast::PtrOffsetWithCast));
@@ -725,13 +727,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| Box::new(assertions_on_constants::AssertionsOnConstants));
     store.register_late_pass(|| Box::new(transmuting_null::TransmutingNull));
     store.register_late_pass(|| Box::new(path_buf_push_overwrite::PathBufPushOverwrite));
-    store.register_late_pass(|| Box::new(integer_division::IntegerDivision));
     store.register_late_pass(|| Box::new(inherent_to_string::InherentToString));
     let max_trait_bounds = conf.max_trait_bounds;
     store.register_late_pass(move || Box::new(trait_bounds::TraitBounds::new(max_trait_bounds)));
     store.register_late_pass(|| Box::new(comparison_chain::ComparisonChain));
     store.register_late_pass(|| Box::new(mut_key::MutableKeyType));
-    store.register_late_pass(|| Box::new(modulo_arithmetic::ModuloArithmetic));
     store.register_early_pass(|| Box::new(reference::DerefAddrOf));
     store.register_early_pass(|| Box::new(double_parens::DoubleParens));
     store.register_late_pass(|| Box::new(format_impl::FormatImpl::new()));
@@ -828,9 +828,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| Box::new(stable_sort_primitive::StableSortPrimitive));
     store.register_late_pass(|| Box::new(repeat_once::RepeatOnce));
     store.register_late_pass(|| Box::new(unwrap_in_result::UnwrapInResult));
-    store.register_late_pass(|| Box::new(self_assignment::SelfAssignment));
     store.register_late_pass(|| Box::new(manual_ok_or::ManualOkOr));
-    store.register_late_pass(|| Box::new(float_equality_without_abs::FloatEqualityWithoutAbs));
     store.register_late_pass(|| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned));
     store.register_late_pass(|| Box::new(async_yields_async::AsyncYieldsAsync));
     let disallowed_methods = conf.disallowed_methods.clone();
@@ -910,6 +908,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| Box::new(mismatching_type_param_order::TypeParamMismatch));
     store.register_late_pass(|| Box::new(as_underscore::AsUnderscore));
     store.register_late_pass(|| Box::new(read_zero_byte_vec::ReadZeroByteVec));
+    store.register_late_pass(|| Box::new(default_instead_of_iter_empty::DefaultIterEmpty));
+    store.register_late_pass(move || Box::new(manual_rem_euclid::ManualRemEuclid::new(msrv)));
+    store.register_late_pass(move || Box::new(manual_retain::ManualRetain::new(msrv)));
+    let verbose_bit_mask_threshold = conf.verbose_bit_mask_threshold;
+    store.register_late_pass(move || Box::new(operators::Operators::new(verbose_bit_mask_threshold)));
     // add lints here, do not remove this comment, it's used in `new_lint`
 }
 
diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs
index 93f5663312f..5c0bd57ac50 100644
--- a/clippy_lints/src/lifetimes.rs
+++ b/clippy_lints/src/lifetimes.rs
@@ -92,7 +92,9 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes {
         if let ItemKind::Fn(ref sig, generics, id) = item.kind {
             check_fn_inner(cx, sig.decl, Some(id), None, generics, item.span, true);
         } else if let ItemKind::Impl(impl_) = item.kind {
-            report_extra_impl_lifetimes(cx, impl_);
+            if !item.span.from_expansion() {
+                report_extra_impl_lifetimes(cx, impl_);
+            }
         }
     }
 
diff --git a/clippy_lints/src/loops/manual_find.rs b/clippy_lints/src/loops/manual_find.rs
new file mode 100644
index 00000000000..33736d6d4e6
--- /dev/null
+++ b/clippy_lints/src/loops/manual_find.rs
@@ -0,0 +1,158 @@
+use super::utils::make_iterator_snippet;
+use super::MANUAL_FIND;
+use clippy_utils::{
+    diagnostics::span_lint_and_then, higher, is_lang_ctor, path_res, peel_blocks_with_stmt,
+    source::snippet_with_applicability, ty::implements_trait,
+};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::{
+    def::Res, lang_items::LangItem, BindingAnnotation, Block, Expr, ExprKind, HirId, Node, Pat, PatKind, Stmt, StmtKind,
+};
+use rustc_lint::LateContext;
+use rustc_span::source_map::Span;
+
+pub(super) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    pat: &'tcx Pat<'_>,
+    arg: &'tcx Expr<'_>,
+    body: &'tcx Expr<'_>,
+    span: Span,
+    expr: &'tcx Expr<'_>,
+) {
+    let inner_expr = peel_blocks_with_stmt(body);
+    // Check for the specific case that the result is returned and optimize suggestion for that (more
+    // cases can be added later)
+    if_chain! {
+        if let Some(higher::If { cond, then, r#else: None, }) = higher::If::hir(inner_expr);
+        if let Some(binding_id) = get_binding(pat);
+        if let ExprKind::Block(block, _) = then.kind;
+        if let [stmt] = block.stmts;
+        if let StmtKind::Semi(semi) = stmt.kind;
+        if let ExprKind::Ret(Some(ret_value)) = semi.kind;
+        if let ExprKind::Call(Expr { kind: ExprKind::Path(ctor), .. }, [inner_ret]) = ret_value.kind;
+        if is_lang_ctor(cx, ctor, LangItem::OptionSome);
+        if path_res(cx, inner_ret) == Res::Local(binding_id);
+        if let Some((last_stmt, last_ret)) = last_stmt_and_ret(cx, expr);
+        then {
+            let mut applicability = Applicability::MachineApplicable;
+            let mut snippet = make_iterator_snippet(cx, arg, &mut applicability);
+            // Checks if `pat` is a single reference to a binding (`&x`)
+            let is_ref_to_binding =
+                matches!(pat.kind, PatKind::Ref(inner, _) if matches!(inner.kind, PatKind::Binding(..)));
+            // If `pat` is not a binding or a reference to a binding (`x` or `&x`)
+            // we need to map it to the binding returned by the function (i.e. `.map(|(x, _)| x)`)
+            if !(matches!(pat.kind, PatKind::Binding(..)) || is_ref_to_binding) {
+                snippet.push_str(
+                    &format!(
+                        ".map(|{}| {})",
+                        snippet_with_applicability(cx, pat.span, "..", &mut applicability),
+                        snippet_with_applicability(cx, inner_ret.span, "..", &mut applicability),
+                    )[..],
+                );
+            }
+            let ty = cx.typeck_results().expr_ty(inner_ret);
+            if cx.tcx.lang_items().copy_trait().map_or(false, |id| implements_trait(cx, ty, id, &[])) {
+                snippet.push_str(
+                    &format!(
+                        ".find(|{}{}| {})",
+                        "&".repeat(1 + usize::from(is_ref_to_binding)),
+                        snippet_with_applicability(cx, inner_ret.span, "..", &mut applicability),
+                        snippet_with_applicability(cx, cond.span, "..", &mut applicability),
+                    )[..],
+                );
+                if is_ref_to_binding {
+                    snippet.push_str(".copied()");
+                }
+            } else {
+                applicability = Applicability::MaybeIncorrect;
+                snippet.push_str(
+                    &format!(
+                        ".find(|{}| {})",
+                        snippet_with_applicability(cx, inner_ret.span, "..", &mut applicability),
+                        snippet_with_applicability(cx, cond.span, "..", &mut applicability),
+                    )[..],
+                );
+            }
+            // Extends to `last_stmt` to include semicolon in case of `return None;`
+            let lint_span = span.to(last_stmt.span).to(last_ret.span);
+            span_lint_and_then(
+                cx,
+                MANUAL_FIND,
+                lint_span,
+                "manual implementation of `Iterator::find`",
+                |diag| {
+                    if applicability == Applicability::MaybeIncorrect {
+                        diag.note("you may need to dereference some variables");
+                    }
+                    diag.span_suggestion(
+                        lint_span,
+                        "replace with an iterator",
+                        snippet,
+                        applicability,
+                    );
+                },
+            );
+        }
+    }
+}
+
+fn get_binding(pat: &Pat<'_>) -> Option<HirId> {
+    let mut hir_id = None;
+    let mut count = 0;
+    pat.each_binding(|annotation, id, _, _| {
+        count += 1;
+        if count > 1 {
+            hir_id = None;
+            return;
+        }
+        if let BindingAnnotation::Unannotated = annotation {
+            hir_id = Some(id);
+        }
+    });
+    hir_id
+}
+
+// Returns the last statement and last return if function fits format for lint
+fn last_stmt_and_ret<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'_>,
+) -> Option<(&'tcx Stmt<'tcx>, &'tcx Expr<'tcx>)> {
+    // Returns last non-return statement and the last return
+    fn extract<'tcx>(block: &Block<'tcx>) -> Option<(&'tcx Stmt<'tcx>, &'tcx Expr<'tcx>)> {
+        if let [.., last_stmt] = block.stmts {
+            if let Some(ret) = block.expr {
+                return Some((last_stmt, ret));
+            }
+            if_chain! {
+                if let [.., snd_last, _] = block.stmts;
+                if let StmtKind::Semi(last_expr) = last_stmt.kind;
+                if let ExprKind::Ret(Some(ret)) = last_expr.kind;
+                then {
+                    return Some((snd_last, ret));
+                }
+            }
+        }
+        None
+    }
+    let mut parent_iter = cx.tcx.hir().parent_iter(expr.hir_id);
+    if_chain! {
+        // This should be the loop
+        if let Some((node_hir, Node::Stmt(..))) = parent_iter.next();
+        // This should be the funciton body
+        if let Some((_, Node::Block(block))) = parent_iter.next();
+        if let Some((last_stmt, last_ret)) = extract(block);
+        if last_stmt.hir_id == node_hir;
+        if let ExprKind::Path(path) = &last_ret.kind;
+        if is_lang_ctor(cx, path, LangItem::OptionNone);
+        if let Some((_, Node::Expr(_block))) = parent_iter.next();
+        // This includes the function header
+        if let Some((_, func)) = parent_iter.next();
+        if func.fn_kind().is_some();
+        then {
+            Some((block.stmts.last().unwrap(), last_ret))
+        } else {
+            None
+        }
+    }
+}
diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs
index 391de922e1e..ed270bd490d 100644
--- a/clippy_lints/src/loops/mod.rs
+++ b/clippy_lints/src/loops/mod.rs
@@ -5,6 +5,7 @@ mod explicit_iter_loop;
 mod for_kv_map;
 mod for_loops_over_fallibles;
 mod iter_next_loop;
+mod manual_find;
 mod manual_flatten;
 mod manual_memcpy;
 mod missing_spin_loop;
@@ -346,7 +347,14 @@ declare_clippy_lint! {
     ///
     /// ### Example
     /// ```ignore
-    /// while let Some(val) = iter() {
+    /// while let Some(val) = iter.next() {
+    ///     ..
+    /// }
+    /// ```
+    ///
+    /// Use instead:
+    /// ```ignore
+    /// for val in &mut iter {
     ///     ..
     /// }
     /// ```
@@ -602,6 +610,37 @@ declare_clippy_lint! {
     "An empty busy waiting loop"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Check for manual implementations of Iterator::find
+    ///
+    /// ### Why is this bad?
+    /// It doesn't affect performance, but using `find` is shorter and easier to read.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// fn example(arr: Vec<i32>) -> Option<i32> {
+    ///     for el in arr {
+    ///         if el == 1 {
+    ///             return Some(el);
+    ///         }
+    ///     }
+    ///     None
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// fn example(arr: Vec<i32>) -> Option<i32> {
+    ///     arr.into_iter().find(|&el| el == 1)
+    /// }
+    /// ```
+    #[clippy::version = "1.61.0"]
+    pub MANUAL_FIND,
+    complexity,
+    "manual implementation of `Iterator::find`"
+}
+
 declare_lint_pass!(Loops => [
     MANUAL_MEMCPY,
     MANUAL_FLATTEN,
@@ -622,6 +661,7 @@ declare_lint_pass!(Loops => [
     SAME_ITEM_PUSH,
     SINGLE_ELEMENT_LOOP,
     MISSING_SPIN_LOOP,
+    MANUAL_FIND,
 ]);
 
 impl<'tcx> LateLintPass<'tcx> for Loops {
@@ -696,6 +736,7 @@ fn check_for_loop<'tcx>(
     single_element_loop::check(cx, pat, arg, body, expr);
     same_item_push::check(cx, pat, arg, body, expr);
     manual_flatten::check(cx, pat, arg, body, span);
+    manual_find::check(cx, pat, arg, body, span, expr);
 }
 
 fn check_for_loop_arg(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>) {
diff --git a/clippy_lints/src/loops/while_let_loop.rs b/clippy_lints/src/loops/while_let_loop.rs
index 8f57df0be6b..45af6be2653 100644
--- a/clippy_lints/src/loops/while_let_loop.rs
+++ b/clippy_lints/src/loops/while_let_loop.rs
@@ -2,71 +2,60 @@ use super::WHILE_LET_LOOP;
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::higher;
 use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::needs_ordered_drop;
+use clippy_utils::visitors::any_temporaries_need_ordered_drop;
 use rustc_errors::Applicability;
-use rustc_hir::{Block, Expr, ExprKind, MatchSource, Pat, StmtKind};
-use rustc_lint::{LateContext, LintContext};
-use rustc_middle::lint::in_external_macro;
+use rustc_hir::{Block, Expr, ExprKind, Local, MatchSource, Pat, StmtKind};
+use rustc_lint::LateContext;
 
 pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &'tcx Block<'_>) {
-    // extract the expression from the first statement (if any) in a block
-    let inner_stmt_expr = extract_expr_from_first_stmt(loop_block);
-    // or extract the first expression (if any) from the block
-    if let Some(inner) = inner_stmt_expr.or_else(|| extract_first_expr(loop_block)) {
-        if let Some(higher::IfLet {
-            let_pat,
-            let_expr,
-            if_else: Some(if_else),
-            ..
-        }) = higher::IfLet::hir(cx, inner)
-        {
-            if is_simple_break_expr(if_else) {
-                could_be_while_let(cx, expr, let_pat, let_expr);
+    let (init, has_trailing_exprs) = match (loop_block.stmts, loop_block.expr) {
+        ([stmt, stmts @ ..], expr) => {
+            if let StmtKind::Local(&Local { init: Some(e), .. }) | StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind {
+                (e, !stmts.is_empty() || expr.is_some())
+            } else {
+                return;
             }
-        }
-
-        if let ExprKind::Match(matchexpr, arms, MatchSource::Normal) = inner.kind {
-            if arms.len() == 2
-                && arms[0].guard.is_none()
-                && arms[1].guard.is_none()
-                && is_simple_break_expr(arms[1].body)
-            {
-                could_be_while_let(cx, expr, arms[0].pat, matchexpr);
-            }
-        }
-    }
-}
+        },
+        ([], Some(e)) => (e, false),
+        _ => return,
+    };
 
-/// If a block begins with a statement (possibly a `let` binding) and has an
-/// expression, return it.
-fn extract_expr_from_first_stmt<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> {
-    if let Some(first_stmt) = block.stmts.get(0) {
-        if let StmtKind::Local(local) = first_stmt.kind {
-            return local.init;
-        }
+    if let Some(if_let) = higher::IfLet::hir(cx, init)
+        && let Some(else_expr) = if_let.if_else
+        && is_simple_break_expr(else_expr)
+    {
+        could_be_while_let(cx, expr, if_let.let_pat, if_let.let_expr, has_trailing_exprs);
+    } else if let ExprKind::Match(scrutinee, [arm1, arm2], MatchSource::Normal) = init.kind
+        && arm1.guard.is_none()
+        && arm2.guard.is_none()
+        && is_simple_break_expr(arm2.body)
+    {
+        could_be_while_let(cx, expr, arm1.pat, scrutinee, has_trailing_exprs);
     }
-    None
 }
 
-/// If a block begins with an expression (with or without semicolon), return it.
-fn extract_first_expr<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> {
-    match block.expr {
-        Some(expr) if block.stmts.is_empty() => Some(expr),
-        None if !block.stmts.is_empty() => match block.stmts[0].kind {
-            StmtKind::Expr(expr) | StmtKind::Semi(expr) => Some(expr),
-            StmtKind::Local(..) | StmtKind::Item(..) => None,
-        },
-        _ => None,
-    }
+/// Returns `true` if expr contains a single break expression without a label or eub-expression.
+fn is_simple_break_expr(e: &Expr<'_>) -> bool {
+    matches!(peel_blocks(e).kind, ExprKind::Break(dest, None) if dest.label.is_none())
 }
 
-/// Returns `true` if expr contains a single break expr without destination label
-/// and
-/// passed expression. The expression may be within a block.
-fn is_simple_break_expr(expr: &Expr<'_>) -> bool {
-    match expr.kind {
-        ExprKind::Break(dest, ref passed_expr) if dest.label.is_none() && passed_expr.is_none() => true,
-        ExprKind::Block(b, _) => extract_first_expr(b).map_or(false, is_simple_break_expr),
-        _ => false,
+/// Removes any blocks containing only a single expression.
+fn peel_blocks<'tcx>(e: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> {
+    if let ExprKind::Block(b, _) = e.kind {
+        match (b.stmts, b.expr) {
+            ([s], None) => {
+                if let StmtKind::Expr(e) | StmtKind::Semi(e) = s.kind {
+                    peel_blocks(e)
+                } else {
+                    e
+                }
+            },
+            ([], Some(e)) => peel_blocks(e),
+            _ => e,
+        }
+    } else {
+        e
     }
 }
 
@@ -75,8 +64,13 @@ fn could_be_while_let<'tcx>(
     expr: &'tcx Expr<'_>,
     let_pat: &'tcx Pat<'_>,
     let_expr: &'tcx Expr<'_>,
+    has_trailing_exprs: bool,
 ) {
-    if in_external_macro(cx.sess(), expr.span) {
+    if has_trailing_exprs
+        && (needs_ordered_drop(cx, cx.typeck_results().expr_ty(let_expr))
+            || any_temporaries_need_ordered_drop(cx, let_expr))
+    {
+        // Switching to a `while let` loop will extend the lifetime of some values.
         return;
     }
 
diff --git a/clippy_lints/src/macro_use.rs b/clippy_lints/src/macro_use.rs
index 753469c603d..d573a1b4fbb 100644
--- a/clippy_lints/src/macro_use.rs
+++ b/clippy_lints/src/macro_use.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::diagnostics::span_lint_hir_and_then;
 use clippy_utils::source::snippet;
 use hir::def::{DefKind, Res};
 use if_chain::if_chain;
@@ -50,8 +50,9 @@ impl MacroRefData {
 #[derive(Default)]
 #[expect(clippy::module_name_repetitions)]
 pub struct MacroUseImports {
-    /// the actual import path used and the span of the attribute above it.
-    imports: Vec<(String, Span)>,
+    /// the actual import path used and the span of the attribute above it. The value is
+    /// the location, where the lint should be emitted.
+    imports: Vec<(String, Span, hir::HirId)>,
     /// the span of the macro reference, kept to ensure only one reference is used per macro call.
     collected: FxHashSet<Span>,
     mac_refs: Vec<MacroRefData>,
@@ -90,7 +91,8 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports {
         if_chain! {
             if cx.sess().opts.edition >= Edition::Edition2018;
             if let hir::ItemKind::Use(path, _kind) = &item.kind;
-            let attrs = cx.tcx.hir().attrs(item.hir_id());
+            let hir_id = item.hir_id();
+            let attrs = cx.tcx.hir().attrs(hir_id);
             if let Some(mac_attr) = attrs.iter().find(|attr| attr.has_name(sym::macro_use));
             if let Res::Def(DefKind::Mod, id) = path.res;
             if !id.is_local();
@@ -99,7 +101,7 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports {
                     if let Res::Def(DefKind::Macro(_mac_type), mac_id) = kid.res {
                         let span = mac_attr.span;
                         let def_path = cx.tcx.def_path_str(mac_id);
-                        self.imports.push((def_path, span));
+                        self.imports.push((def_path, span, hir_id));
                     }
                 }
             } else {
@@ -137,7 +139,7 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports {
     fn check_crate_post(&mut self, cx: &LateContext<'_>) {
         let mut used = FxHashMap::default();
         let mut check_dup = vec![];
-        for (import, span) in &self.imports {
+        for (import, span, hir_id) in &self.imports {
             let found_idx = self.mac_refs.iter().position(|mac| import.ends_with(&mac.name));
 
             if let Some(idx) = found_idx {
@@ -150,7 +152,7 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports {
                     [] | [_] => return,
                     [root, item] => {
                         if !check_dup.contains(&(*item).to_string()) {
-                            used.entry(((*root).to_string(), span))
+                            used.entry(((*root).to_string(), span, hir_id))
                                 .or_insert_with(Vec::new)
                                 .push((*item).to_string());
                             check_dup.push((*item).to_string());
@@ -168,13 +170,13 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports {
                                     }
                                 })
                                 .collect::<Vec<_>>();
-                            used.entry(((*root).to_string(), span))
+                            used.entry(((*root).to_string(), span, hir_id))
                                 .or_insert_with(Vec::new)
                                 .push(filtered.join("::"));
                             check_dup.extend(filtered);
                         } else {
                             let rest = rest.to_vec();
-                            used.entry(((*root).to_string(), span))
+                            used.entry(((*root).to_string(), span, hir_id))
                                 .or_insert_with(Vec::new)
                                 .push(rest.join("::"));
                             check_dup.extend(rest.iter().map(ToString::to_string));
@@ -185,27 +187,33 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports {
         }
 
         let mut suggestions = vec![];
-        for ((root, span), path) in used {
+        for ((root, span, hir_id), path) in used {
             if path.len() == 1 {
-                suggestions.push((span, format!("{}::{}", root, path[0])));
+                suggestions.push((span, format!("{}::{}", root, path[0]), hir_id));
             } else {
-                suggestions.push((span, format!("{}::{{{}}}", root, path.join(", "))));
+                suggestions.push((span, format!("{}::{{{}}}", root, path.join(", ")), hir_id));
             }
         }
 
         // If mac_refs is not empty we have encountered an import we could not handle
         // such as `std::prelude::v1::foo` or some other macro that expands to an import.
         if self.mac_refs.is_empty() {
-            for (span, import) in suggestions {
+            for (span, import, hir_id) in suggestions {
                 let help = format!("use {};", import);
-                span_lint_and_sugg(
+                span_lint_hir_and_then(
                     cx,
                     MACRO_USE_IMPORTS,
+                    *hir_id,
                     *span,
                     "`macro_use` attributes are no longer needed in the Rust 2018 edition",
-                    "remove the attribute and import the macro directly, try",
-                    help,
-                    Applicability::MaybeIncorrect,
+                    |diag| {
+                        diag.span_suggestion(
+                            *span,
+                            "remove the attribute and import the macro directly, try",
+                            help,
+                            Applicability::MaybeIncorrect,
+                        );
+                    },
                 );
             }
         }
diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs
index 14f5faafd7c..4278e98dc91 100644
--- a/clippy_lints/src/manual_non_exhaustive.rs
+++ b/clippy_lints/src/manual_non_exhaustive.rs
@@ -1,6 +1,6 @@
-use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
 use clippy_utils::source::snippet_opt;
-use clippy_utils::{is_doc_hidden, is_lint_allowed, meets_msrv, msrvs};
+use clippy_utils::{is_doc_hidden, meets_msrv, msrvs};
 use rustc_ast::ast::{self, VisibilityKind};
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Applicability;
@@ -190,12 +190,13 @@ impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum {
                 !self
                     .constructed_enum_variants
                     .contains(&(enum_id.to_def_id(), variant_id.to_def_id()))
-                    && !is_lint_allowed(cx, MANUAL_NON_EXHAUSTIVE, cx.tcx.hir().local_def_id_to_hir_id(enum_id))
             })
         {
-            span_lint_and_then(
+            let hir_id = cx.tcx.hir().local_def_id_to_hir_id(enum_id);
+            span_lint_hir_and_then(
                 cx,
                 MANUAL_NON_EXHAUSTIVE,
+                hir_id,
                 enum_span,
                 "this seems like a manual implementation of the non-exhaustive pattern",
                 |diag| {
diff --git a/clippy_lints/src/manual_rem_euclid.rs b/clippy_lints/src/manual_rem_euclid.rs
new file mode 100644
index 00000000000..b5698965fc3
--- /dev/null
+++ b/clippy_lints/src/manual_rem_euclid.rs
@@ -0,0 +1,123 @@
+use clippy_utils::consts::{constant_full_int, FullInt};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::{in_constant, meets_msrv, msrvs, path_to_local};
+use rustc_errors::Applicability;
+use rustc_hir::{BinOpKind, Expr, ExprKind, Node, TyKind};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::lint::in_external_macro;
+use rustc_semver::RustcVersion;
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for an expression like `((x % 4) + 4) % 4` which is a common manual reimplementation
+    /// of `x.rem_euclid(4)`.
+    ///
+    /// ### Why is this bad?
+    /// It's simpler and more readable.
+    ///
+    /// ### Example
+    /// ```rust
+    /// let x: i32 = 24;
+    /// let rem = ((x % 4) + 4) % 4;
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// let x: i32 = 24;
+    /// let rem = x.rem_euclid(4);
+    /// ```
+    #[clippy::version = "1.63.0"]
+    pub MANUAL_REM_EUCLID,
+    complexity,
+    "manually reimplementing `rem_euclid`"
+}
+
+pub struct ManualRemEuclid {
+    msrv: Option<RustcVersion>,
+}
+
+impl ManualRemEuclid {
+    #[must_use]
+    pub fn new(msrv: Option<RustcVersion>) -> Self {
+        Self { msrv }
+    }
+}
+
+impl_lint_pass!(ManualRemEuclid => [MANUAL_REM_EUCLID]);
+
+impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+        if !meets_msrv(self.msrv, msrvs::REM_EUCLID) {
+            return;
+        }
+
+        if in_constant(cx, expr.hir_id) && !meets_msrv(self.msrv, msrvs::REM_EUCLID_CONST) {
+            return;
+        }
+
+        if in_external_macro(cx.sess(), expr.span) {
+            return;
+        }
+
+        if let ExprKind::Binary(op1, expr1, right) = expr.kind
+            && op1.node == BinOpKind::Rem
+            && let Some(const1) = check_for_unsigned_int_constant(cx, right)
+            && let ExprKind::Binary(op2, left, right) = expr1.kind
+            && op2.node == BinOpKind::Add
+            && let Some((const2, expr2)) = check_for_either_unsigned_int_constant(cx, left, right)
+            && let ExprKind::Binary(op3, expr3, right) = expr2.kind
+            && op3.node == BinOpKind::Rem
+            && let Some(const3) = check_for_unsigned_int_constant(cx, right)
+            // Also ensures the const is nonzero since zero can't be a divisor
+            && const1 == const2 && const2 == const3
+            && let Some(hir_id) = path_to_local(expr3)
+            && let Some(Node::Binding(_)) = cx.tcx.hir().find(hir_id) {
+                // Apply only to params or locals with annotated types
+                match cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) {
+                    Some(Node::Param(..)) => (),
+                    Some(Node::Local(local)) => {
+                        let Some(ty) = local.ty else { return };
+                        if matches!(ty.kind, TyKind::Infer) {
+                            return;
+                        }
+                    }
+                    _ => return,
+                };
+
+                let mut app = Applicability::MachineApplicable;
+                let rem_of = snippet_with_applicability(cx, expr3.span, "_", &mut app);
+                span_lint_and_sugg(
+                    cx,
+                    MANUAL_REM_EUCLID,
+                    expr.span,
+                    "manual `rem_euclid` implementation",
+                    "consider using",
+                    format!("{rem_of}.rem_euclid({const1})"),
+                    app,
+                );
+        }
+    }
+
+    extract_msrv_attr!(LateContext);
+}
+
+// Checks if either the left or right expressions can be an unsigned int constant and returns that
+// constant along with the other expression unchanged if so
+fn check_for_either_unsigned_int_constant<'a>(
+    cx: &'a LateContext<'_>,
+    left: &'a Expr<'_>,
+    right: &'a Expr<'_>,
+) -> Option<(u128, &'a Expr<'a>)> {
+    check_for_unsigned_int_constant(cx, left)
+        .map(|int_const| (int_const, right))
+        .or_else(|| check_for_unsigned_int_constant(cx, right).map(|int_const| (int_const, left)))
+}
+
+fn check_for_unsigned_int_constant<'a>(cx: &'a LateContext<'_>, expr: &'a Expr<'_>) -> Option<u128> {
+    let Some(int_const) = constant_full_int(cx, cx.typeck_results(), expr) else { return None };
+    match int_const {
+        FullInt::S(s) => s.try_into().ok(),
+        FullInt::U(u) => Some(u),
+    }
+}
diff --git a/clippy_lints/src/manual_retain.rs b/clippy_lints/src/manual_retain.rs
new file mode 100644
index 00000000000..c35e1e021ef
--- /dev/null
+++ b/clippy_lints/src/manual_retain.rs
@@ -0,0 +1,228 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{get_parent_expr, match_def_path, paths, SpanlessEq};
+use clippy_utils::{meets_msrv, msrvs};
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_hir::def_id::DefId;
+use rustc_hir::ExprKind::Assign;
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_semver::RustcVersion;
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::symbol::sym;
+
+const ACCEPTABLE_METHODS: [&[&str]; 4] = [
+    &paths::HASHSET_ITER,
+    &paths::BTREESET_ITER,
+    &paths::SLICE_INTO,
+    &paths::VEC_DEQUE_ITER,
+];
+const ACCEPTABLE_TYPES: [(rustc_span::Symbol, Option<RustcVersion>); 6] = [
+    (sym::BTreeSet, Some(msrvs::BTREE_SET_RETAIN)),
+    (sym::BTreeMap, Some(msrvs::BTREE_MAP_RETAIN)),
+    (sym::HashSet, Some(msrvs::HASH_SET_RETAIN)),
+    (sym::HashMap, Some(msrvs::HASH_MAP_RETAIN)),
+    (sym::Vec, None),
+    (sym::VecDeque, None),
+];
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for code to be replaced by `.retain()`.
+    /// ### Why is this bad?
+    /// `.retain()` is simpler and avoids needless allocation.
+    /// ### Example
+    /// ```rust
+    /// let mut vec = vec![0, 1, 2];
+    /// vec = vec.iter().filter(|&x| x % 2 == 0).copied().collect();
+    /// vec = vec.into_iter().filter(|x| x % 2 == 0).collect();
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// let mut vec = vec![0, 1, 2];
+    /// vec.retain(|x| x % 2 == 0);
+    /// ```
+    #[clippy::version = "1.63.0"]
+    pub MANUAL_RETAIN,
+    perf,
+    "`retain()` is simpler and the same functionalitys"
+}
+
+pub struct ManualRetain {
+    msrv: Option<RustcVersion>,
+}
+
+impl ManualRetain {
+    #[must_use]
+    pub fn new(msrv: Option<RustcVersion>) -> Self {
+        Self { msrv }
+    }
+}
+
+impl_lint_pass!(ManualRetain => [MANUAL_RETAIN]);
+
+impl<'tcx> LateLintPass<'tcx> for ManualRetain {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
+        if let Some(parent_expr) = get_parent_expr(cx, expr)
+            && let Assign(left_expr, collect_expr, _) = &parent_expr.kind
+            && let hir::ExprKind::MethodCall(seg, _, _) = &collect_expr.kind
+            && seg.args.is_none()
+            && let hir::ExprKind::MethodCall(_, [target_expr], _) = &collect_expr.kind
+            && let Some(collect_def_id) = cx.typeck_results().type_dependent_def_id(collect_expr.hir_id)
+            && match_def_path(cx, collect_def_id, &paths::CORE_ITER_COLLECT) {
+            check_into_iter(cx, parent_expr, left_expr, target_expr, self.msrv);
+            check_iter(cx, parent_expr, left_expr, target_expr, self.msrv);
+            check_to_owned(cx, parent_expr, left_expr, target_expr, self.msrv);
+        }
+    }
+
+    extract_msrv_attr!(LateContext);
+}
+
+fn check_into_iter(
+    cx: &LateContext<'_>,
+    parent_expr: &hir::Expr<'_>,
+    left_expr: &hir::Expr<'_>,
+    target_expr: &hir::Expr<'_>,
+    msrv: Option<RustcVersion>,
+) {
+    if let hir::ExprKind::MethodCall(_, [into_iter_expr, _], _) = &target_expr.kind
+        && let Some(filter_def_id) = cx.typeck_results().type_dependent_def_id(target_expr.hir_id)
+        && match_def_path(cx, filter_def_id, &paths::CORE_ITER_FILTER)
+        && let hir::ExprKind::MethodCall(_, [struct_expr], _) = &into_iter_expr.kind
+        && let Some(into_iter_def_id) = cx.typeck_results().type_dependent_def_id(into_iter_expr.hir_id)
+        && match_def_path(cx, into_iter_def_id, &paths::CORE_ITER_INTO_ITER)
+        && match_acceptable_type(cx, left_expr, msrv)
+        && SpanlessEq::new(cx).eq_expr(left_expr, struct_expr) {
+        suggest(cx, parent_expr, left_expr, target_expr);
+    }
+}
+
+fn check_iter(
+    cx: &LateContext<'_>,
+    parent_expr: &hir::Expr<'_>,
+    left_expr: &hir::Expr<'_>,
+    target_expr: &hir::Expr<'_>,
+    msrv: Option<RustcVersion>,
+) {
+    if let hir::ExprKind::MethodCall(_, [filter_expr], _) = &target_expr.kind
+        && let Some(copied_def_id) = cx.typeck_results().type_dependent_def_id(target_expr.hir_id)
+        && (match_def_path(cx, copied_def_id, &paths::CORE_ITER_COPIED)
+            || match_def_path(cx, copied_def_id, &paths::CORE_ITER_CLONED))
+        && let hir::ExprKind::MethodCall(_, [iter_expr, _], _) = &filter_expr.kind
+        && let Some(filter_def_id) = cx.typeck_results().type_dependent_def_id(filter_expr.hir_id)
+        && match_def_path(cx, filter_def_id, &paths::CORE_ITER_FILTER)
+        && let hir::ExprKind::MethodCall(_, [struct_expr], _) = &iter_expr.kind
+        && let Some(iter_expr_def_id) = cx.typeck_results().type_dependent_def_id(iter_expr.hir_id)
+        && match_acceptable_def_path(cx, iter_expr_def_id)
+        && match_acceptable_type(cx, left_expr, msrv)
+        && SpanlessEq::new(cx).eq_expr(left_expr, struct_expr) {
+        suggest(cx, parent_expr, left_expr, filter_expr);
+    }
+}
+
+fn check_to_owned(
+    cx: &LateContext<'_>,
+    parent_expr: &hir::Expr<'_>,
+    left_expr: &hir::Expr<'_>,
+    target_expr: &hir::Expr<'_>,
+    msrv: Option<RustcVersion>,
+) {
+    if meets_msrv(msrv,  msrvs::STRING_RETAIN)
+        && let hir::ExprKind::MethodCall(_, [filter_expr], _) = &target_expr.kind
+        && let Some(to_owned_def_id) = cx.typeck_results().type_dependent_def_id(target_expr.hir_id)
+        && match_def_path(cx, to_owned_def_id, &paths::TO_OWNED_METHOD)
+        && let hir::ExprKind::MethodCall(_, [chars_expr, _], _) = &filter_expr.kind
+        && let Some(filter_def_id) = cx.typeck_results().type_dependent_def_id(filter_expr.hir_id)
+        && match_def_path(cx, filter_def_id, &paths::CORE_ITER_FILTER)
+        && let hir::ExprKind::MethodCall(_, [str_expr], _) = &chars_expr.kind
+        && let Some(chars_expr_def_id) = cx.typeck_results().type_dependent_def_id(chars_expr.hir_id)
+        && match_def_path(cx, chars_expr_def_id, &paths::STR_CHARS)
+        && let ty = cx.typeck_results().expr_ty(str_expr).peel_refs()
+        && is_type_diagnostic_item(cx, ty, sym::String)
+        && SpanlessEq::new(cx).eq_expr(left_expr, str_expr) {
+        suggest(cx, parent_expr, left_expr, filter_expr);
+    }
+}
+
+fn suggest(cx: &LateContext<'_>, parent_expr: &hir::Expr<'_>, left_expr: &hir::Expr<'_>, filter_expr: &hir::Expr<'_>) {
+    if let hir::ExprKind::MethodCall(_, [_, closure], _) = filter_expr.kind
+        && let hir::ExprKind::Closure{ body, ..} = closure.kind
+        && let filter_body = cx.tcx.hir().body(body)
+        && let [filter_params] = filter_body.params
+        && let Some(sugg) = match filter_params.pat.kind {
+            hir::PatKind::Binding(_, _, filter_param_ident, None) => {
+                Some(format!("{}.retain(|{}| {})", snippet(cx, left_expr.span, ".."), filter_param_ident, snippet(cx, filter_body.value.span, "..")))
+            },
+            hir::PatKind::Tuple([key_pat, value_pat], _) => {
+                make_sugg(cx, key_pat, value_pat, left_expr, filter_body)
+            },
+            hir::PatKind::Ref(pat, _) => {
+                match pat.kind {
+                    hir::PatKind::Binding(_, _, filter_param_ident, None) => {
+                        Some(format!("{}.retain(|{}| {})", snippet(cx, left_expr.span, ".."), filter_param_ident, snippet(cx, filter_body.value.span, "..")))
+                    },
+                    _ => None
+                }
+            },
+            _ => None
+        } {
+        span_lint_and_sugg(
+            cx,
+            MANUAL_RETAIN,
+            parent_expr.span,
+            "this expression can be written more simply using `.retain()`",
+            "consider calling `.retain()` instead",
+            sugg,
+            Applicability::MachineApplicable
+        );
+    }
+}
+
+fn make_sugg(
+    cx: &LateContext<'_>,
+    key_pat: &rustc_hir::Pat<'_>,
+    value_pat: &rustc_hir::Pat<'_>,
+    left_expr: &hir::Expr<'_>,
+    filter_body: &hir::Body<'_>,
+) -> Option<String> {
+    match (&key_pat.kind, &value_pat.kind) {
+        (hir::PatKind::Binding(_, _, key_param_ident, None), hir::PatKind::Binding(_, _, value_param_ident, None)) => {
+            Some(format!(
+                "{}.retain(|{}, &mut {}| {})",
+                snippet(cx, left_expr.span, ".."),
+                key_param_ident,
+                value_param_ident,
+                snippet(cx, filter_body.value.span, "..")
+            ))
+        },
+        (hir::PatKind::Binding(_, _, key_param_ident, None), hir::PatKind::Wild) => Some(format!(
+            "{}.retain(|{}, _| {})",
+            snippet(cx, left_expr.span, ".."),
+            key_param_ident,
+            snippet(cx, filter_body.value.span, "..")
+        )),
+        (hir::PatKind::Wild, hir::PatKind::Binding(_, _, value_param_ident, None)) => Some(format!(
+            "{}.retain(|_, &mut {}| {})",
+            snippet(cx, left_expr.span, ".."),
+            value_param_ident,
+            snippet(cx, filter_body.value.span, "..")
+        )),
+        _ => None,
+    }
+}
+
+fn match_acceptable_def_path(cx: &LateContext<'_>, collect_def_id: DefId) -> bool {
+    ACCEPTABLE_METHODS
+        .iter()
+        .any(|&method| match_def_path(cx, collect_def_id, method))
+}
+
+fn match_acceptable_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>, msrv: Option<RustcVersion>) -> bool {
+    let expr_ty = cx.typeck_results().expr_ty(expr).peel_refs();
+    ACCEPTABLE_TYPES.iter().any(|(ty, acceptable_msrv)| {
+        is_type_diagnostic_item(cx, expr_ty, *ty)
+            && acceptable_msrv.map_or(true, |acceptable_msrv| meets_msrv(msrv, acceptable_msrv))
+    })
+}
diff --git a/clippy_lints/src/matches/match_same_arms.rs b/clippy_lints/src/matches/match_same_arms.rs
index 16fefea5520..15513de7d86 100644
--- a/clippy_lints/src/matches/match_same_arms.rs
+++ b/clippy_lints/src/matches/match_same_arms.rs
@@ -285,7 +285,7 @@ impl<'a> NormalizedPat<'a> {
                 // TODO: Handle negative integers. They're currently treated as a wild match.
                 ExprKind::Lit(lit) => match lit.node {
                     LitKind::Str(sym, _) => Self::LitStr(sym),
-                    LitKind::ByteStr(ref bytes) => Self::LitBytes(&**bytes),
+                    LitKind::ByteStr(ref bytes) => Self::LitBytes(bytes),
                     LitKind::Byte(val) => Self::LitInt(val.into()),
                     LitKind::Char(val) => Self::LitInt(val.into()),
                     LitKind::Int(val, _) => Self::LitInt(val),
diff --git a/clippy_lints/src/matches/match_single_binding.rs b/clippy_lints/src/matches/match_single_binding.rs
index 9df2db45dcf..5ae4a65acaf 100644
--- a/clippy_lints/src/matches/match_single_binding.rs
+++ b/clippy_lints/src/matches/match_single_binding.rs
@@ -55,7 +55,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e
                         cx,
                         (ex, expr),
                         (bind_names, matched_vars),
-                        &*snippet_body,
+                        &snippet_body,
                         &mut applicability,
                         Some(span),
                     );
@@ -88,7 +88,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e
                         cx,
                         (ex, expr),
                         (bind_names, matched_vars),
-                        &*snippet_body,
+                        &snippet_body,
                         &mut applicability,
                         None,
                     );
diff --git a/clippy_lints/src/matches/match_str_case_mismatch.rs b/clippy_lints/src/matches/match_str_case_mismatch.rs
index 8302ce426e5..fa3b8d1fcea 100644
--- a/clippy_lints/src/matches/match_str_case_mismatch.rs
+++ b/clippy_lints/src/matches/match_str_case_mismatch.rs
@@ -118,7 +118,7 @@ fn lint(cx: &LateContext<'_>, case_method: &CaseMethod, bad_case_span: Span, bad
         MATCH_STR_CASE_MISMATCH,
         bad_case_span,
         "this `match` arm has a differing case than its expression",
-        &*format!("consider changing the case of this arm to respect `{}`", method_str),
+        &format!("consider changing the case of this arm to respect `{}`", method_str),
         format!("\"{}\"", suggestion),
         Applicability::MachineApplicable,
     );
diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs
index 3e765173fb9..b2a873ef582 100644
--- a/clippy_lints/src/matches/mod.rs
+++ b/clippy_lints/src/matches/mod.rs
@@ -791,7 +791,7 @@ declare_clippy_lint! {
     /// the match block and thus will not unlock.
     ///
     /// ### Example
-    /// ```rust.ignore
+    /// ```rust,ignore
     /// # use std::sync::Mutex;
     ///
     /// # struct State {}
@@ -963,7 +963,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
                 return;
             }
             if matches!(source, MatchSource::Normal | MatchSource::ForLoopDesugar) {
-                significant_drop_in_scrutinee::check(cx, expr, ex, source);
+                significant_drop_in_scrutinee::check(cx, expr, ex, arms, source);
             }
 
             collapsible_match::check_match(cx, arms);
diff --git a/clippy_lints/src/matches/redundant_pattern_match.rs b/clippy_lints/src/matches/redundant_pattern_match.rs
index 0ea3f3b673b..8499e050af2 100644
--- a/clippy_lints/src/matches/redundant_pattern_match.rs
+++ b/clippy_lints/src/matches/redundant_pattern_match.rs
@@ -3,16 +3,13 @@ use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::snippet;
 use clippy_utils::sugg::Sugg;
 use clippy_utils::ty::needs_ordered_drop;
-use clippy_utils::{higher, match_def_path};
-use clippy_utils::{is_lang_ctor, is_trait_method, paths};
+use clippy_utils::visitors::any_temporaries_need_ordered_drop;
+use clippy_utils::{higher, is_lang_ctor, is_trait_method, match_def_path, paths};
 use if_chain::if_chain;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
 use rustc_hir::LangItem::{OptionNone, PollPending};
-use rustc_hir::{
-    intravisit::{walk_expr, Visitor},
-    Arm, Block, Expr, ExprKind, Node, Pat, PatKind, QPath, UnOp,
-};
+use rustc_hir::{Arm, Expr, ExprKind, Node, Pat, PatKind, QPath, UnOp};
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, subst::GenericArgKind, DefIdTree, Ty};
 use rustc_span::sym;
@@ -47,79 +44,6 @@ fn try_get_generic_ty(ty: Ty<'_>, index: usize) -> Option<Ty<'_>> {
     }
 }
 
-// Checks if there are any temporaries created in the given expression for which drop order
-// matters.
-fn temporaries_need_ordered_drop<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
-    struct V<'a, 'tcx> {
-        cx: &'a LateContext<'tcx>,
-        res: bool,
-    }
-    impl<'a, 'tcx> Visitor<'tcx> for V<'a, 'tcx> {
-        fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
-            match expr.kind {
-                // Taking the reference of a value leaves a temporary
-                // e.g. In `&String::new()` the string is a temporary value.
-                // Remaining fields are temporary values
-                // e.g. In `(String::new(), 0).1` the string is a temporary value.
-                ExprKind::AddrOf(_, _, expr) | ExprKind::Field(expr, _) => {
-                    if !matches!(expr.kind, ExprKind::Path(_)) {
-                        if needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(expr)) {
-                            self.res = true;
-                        } else {
-                            self.visit_expr(expr);
-                        }
-                    }
-                },
-                // the base type is always taken by reference.
-                // e.g. In `(vec![0])[0]` the vector is a temporary value.
-                ExprKind::Index(base, index) => {
-                    if !matches!(base.kind, ExprKind::Path(_)) {
-                        if needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(base)) {
-                            self.res = true;
-                        } else {
-                            self.visit_expr(base);
-                        }
-                    }
-                    self.visit_expr(index);
-                },
-                // Method calls can take self by reference.
-                // e.g. In `String::new().len()` the string is a temporary value.
-                ExprKind::MethodCall(_, [self_arg, args @ ..], _) => {
-                    if !matches!(self_arg.kind, ExprKind::Path(_)) {
-                        let self_by_ref = self
-                            .cx
-                            .typeck_results()
-                            .type_dependent_def_id(expr.hir_id)
-                            .map_or(false, |id| self.cx.tcx.fn_sig(id).skip_binder().inputs()[0].is_ref());
-                        if self_by_ref && needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(self_arg)) {
-                            self.res = true;
-                        } else {
-                            self.visit_expr(self_arg);
-                        }
-                    }
-                    args.iter().for_each(|arg| self.visit_expr(arg));
-                },
-                // Either explicitly drops values, or changes control flow.
-                ExprKind::DropTemps(_)
-                | ExprKind::Ret(_)
-                | ExprKind::Break(..)
-                | ExprKind::Yield(..)
-                | ExprKind::Block(Block { expr: None, .. }, _)
-                | ExprKind::Loop(..) => (),
-
-                // Only consider the final expression.
-                ExprKind::Block(Block { expr: Some(expr), .. }, _) => self.visit_expr(expr),
-
-                _ => walk_expr(self, expr),
-            }
-        }
-    }
-
-    let mut v = V { cx, res: false };
-    v.visit_expr(expr);
-    v.res
-}
-
 fn find_sugg_for_if_let<'tcx>(
     cx: &LateContext<'tcx>,
     expr: &'tcx Expr<'_>,
@@ -191,7 +115,7 @@ fn find_sugg_for_if_let<'tcx>(
     // scrutinee would be, so they have to be considered as well.
     // e.g. in `if let Some(x) = foo.lock().unwrap().baz.as_ref() { .. }` the lock will be held
     // for the duration if body.
-    let needs_drop = needs_ordered_drop(cx, check_ty) || temporaries_need_ordered_drop(cx, let_expr);
+    let needs_drop = needs_ordered_drop(cx, check_ty) || any_temporaries_need_ordered_drop(cx, let_expr);
 
     // check that `while_let_on_iterator` lint does not trigger
     if_chain! {
@@ -362,9 +286,9 @@ fn find_good_method_for_match<'a>(
         .qpath_res(path_right, arms[1].pat.hir_id)
         .opt_def_id()?;
     let body_node_pair = if match_def_path(cx, left_id, expected_left) && match_def_path(cx, right_id, expected_right) {
-        (&(*arms[0].body).kind, &(*arms[1].body).kind)
+        (&arms[0].body.kind, &arms[1].body.kind)
     } else if match_def_path(cx, right_id, expected_left) && match_def_path(cx, right_id, expected_right) {
-        (&(*arms[1].body).kind, &(*arms[0].body).kind)
+        (&arms[1].body.kind, &arms[0].body.kind)
     } else {
         return None;
     };
diff --git a/clippy_lints/src/matches/significant_drop_in_scrutinee.rs b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs
index dcaf6f865de..0704a5af525 100644
--- a/clippy_lints/src/matches/significant_drop_in_scrutinee.rs
+++ b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs
@@ -1,10 +1,10 @@
 use crate::FxHashSet;
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::get_attr;
 use clippy_utils::source::{indent_of, snippet};
+use clippy_utils::{get_attr, is_lint_allowed};
 use rustc_errors::{Applicability, Diagnostic};
 use rustc_hir::intravisit::{walk_expr, Visitor};
-use rustc_hir::{Expr, ExprKind, MatchSource};
+use rustc_hir::{Arm, Expr, ExprKind, MatchSource};
 use rustc_lint::{LateContext, LintContext};
 use rustc_middle::ty::subst::GenericArgKind;
 use rustc_middle::ty::{Ty, TypeAndMut};
@@ -16,12 +16,23 @@ pub(super) fn check<'tcx>(
     cx: &LateContext<'tcx>,
     expr: &'tcx Expr<'tcx>,
     scrutinee: &'tcx Expr<'_>,
+    arms: &'tcx [Arm<'_>],
     source: MatchSource,
 ) {
+    if is_lint_allowed(cx, SIGNIFICANT_DROP_IN_SCRUTINEE, expr.hir_id) {
+        return;
+    }
+
     if let Some((suggestions, message)) = has_significant_drop_in_scrutinee(cx, scrutinee, source) {
         for found in suggestions {
             span_lint_and_then(cx, SIGNIFICANT_DROP_IN_SCRUTINEE, found.found_span, message, |diag| {
                 set_diagnostic(diag, cx, expr, found);
+                let s = Span::new(expr.span.hi(), expr.span.hi(), expr.span.ctxt(), None);
+                diag.span_label(s, "temporary lives until here");
+                for span in has_significant_drop_in_arms(cx, arms) {
+                    diag.span_label(span, "another value with significant `Drop` created here");
+                }
+                diag.note("this might lead to deadlocks or other unexpected behavior");
             });
         }
     }
@@ -80,22 +91,77 @@ fn has_significant_drop_in_scrutinee<'tcx, 'a>(
     let mut helper = SigDropHelper::new(cx);
     helper.find_sig_drop(scrutinee).map(|drops| {
         let message = if source == MatchSource::Normal {
-            "temporary with significant drop in match scrutinee"
+            "temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression"
         } else {
-            "temporary with significant drop in for loop"
+            "temporary with significant `Drop` in `for` loop condition will live until the end of the `for` expression"
         };
         (drops, message)
     })
 }
 
+struct SigDropChecker<'a, 'tcx> {
+    seen_types: FxHashSet<Ty<'tcx>>,
+    cx: &'a LateContext<'tcx>,
+}
+
+impl<'a, 'tcx> SigDropChecker<'a, 'tcx> {
+    fn new(cx: &'a LateContext<'tcx>) -> SigDropChecker<'a, 'tcx> {
+        SigDropChecker {
+            seen_types: FxHashSet::default(),
+            cx,
+        }
+    }
+
+    fn get_type(&self, ex: &'tcx Expr<'_>) -> Ty<'tcx> {
+        self.cx.typeck_results().expr_ty(ex)
+    }
+
+    fn has_seen_type(&mut self, ty: Ty<'tcx>) -> bool {
+        !self.seen_types.insert(ty)
+    }
+
+    fn has_sig_drop_attr(&mut self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
+        if let Some(adt) = ty.ty_adt_def() {
+            if get_attr(cx.sess(), cx.tcx.get_attrs_unchecked(adt.did()), "has_significant_drop").count() > 0 {
+                return true;
+            }
+        }
+
+        match ty.kind() {
+            rustc_middle::ty::Adt(a, b) => {
+                for f in a.all_fields() {
+                    let ty = f.ty(cx.tcx, b);
+                    if !self.has_seen_type(ty) && self.has_sig_drop_attr(cx, ty) {
+                        return true;
+                    }
+                }
+
+                for generic_arg in b.iter() {
+                    if let GenericArgKind::Type(ty) = generic_arg.unpack() {
+                        if self.has_sig_drop_attr(cx, ty) {
+                            return true;
+                        }
+                    }
+                }
+                false
+            },
+            rustc_middle::ty::Array(ty, _)
+            | rustc_middle::ty::RawPtr(TypeAndMut { ty, .. })
+            | rustc_middle::ty::Ref(_, ty, _)
+            | rustc_middle::ty::Slice(ty) => self.has_sig_drop_attr(cx, *ty),
+            _ => false,
+        }
+    }
+}
+
 struct SigDropHelper<'a, 'tcx> {
     cx: &'a LateContext<'tcx>,
     is_chain_end: bool,
-    seen_types: FxHashSet<Ty<'tcx>>,
     has_significant_drop: bool,
     current_sig_drop: Option<FoundSigDrop>,
     sig_drop_spans: Option<Vec<FoundSigDrop>>,
     special_handling_for_binary_op: bool,
+    sig_drop_checker: SigDropChecker<'a, 'tcx>,
 }
 
 #[expect(clippy::enum_variant_names)]
@@ -118,11 +184,11 @@ impl<'a, 'tcx> SigDropHelper<'a, 'tcx> {
         SigDropHelper {
             cx,
             is_chain_end: true,
-            seen_types: FxHashSet::default(),
             has_significant_drop: false,
             current_sig_drop: None,
             sig_drop_spans: None,
             special_handling_for_binary_op: false,
+            sig_drop_checker: SigDropChecker::new(cx),
         }
     }
 
@@ -163,7 +229,7 @@ impl<'a, 'tcx> SigDropHelper<'a, 'tcx> {
         if self.current_sig_drop.is_some() {
             return;
         }
-        let ty = self.get_type(expr);
+        let ty = self.sig_drop_checker.get_type(expr);
         if ty.is_ref() {
             // We checked that the type was ref, so builtin_deref will return Some TypeAndMut,
             // but let's avoid any chance of an ICE
@@ -187,14 +253,6 @@ impl<'a, 'tcx> SigDropHelper<'a, 'tcx> {
         }
     }
 
-    fn get_type(&self, ex: &'tcx Expr<'_>) -> Ty<'tcx> {
-        self.cx.typeck_results().expr_ty(ex)
-    }
-
-    fn has_seen_type(&mut self, ty: Ty<'tcx>) -> bool {
-        !self.seen_types.insert(ty)
-    }
-
     fn visit_exprs_for_binary_ops(
         &mut self,
         left: &'tcx Expr<'_>,
@@ -214,44 +272,15 @@ impl<'a, 'tcx> SigDropHelper<'a, 'tcx> {
 
         self.special_handling_for_binary_op = false;
     }
-
-    fn has_sig_drop_attr(&mut self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
-        if let Some(adt) = ty.ty_adt_def() {
-            if get_attr(cx.sess(), cx.tcx.get_attrs_unchecked(adt.did()), "has_significant_drop").count() > 0 {
-                return true;
-            }
-        }
-
-        match ty.kind() {
-            rustc_middle::ty::Adt(a, b) => {
-                for f in a.all_fields() {
-                    let ty = f.ty(cx.tcx, b);
-                    if !self.has_seen_type(ty) && self.has_sig_drop_attr(cx, ty) {
-                        return true;
-                    }
-                }
-
-                for generic_arg in b.iter() {
-                    if let GenericArgKind::Type(ty) = generic_arg.unpack() {
-                        if self.has_sig_drop_attr(cx, ty) {
-                            return true;
-                        }
-                    }
-                }
-                false
-            },
-            rustc_middle::ty::Array(ty, _)
-            | rustc_middle::ty::RawPtr(TypeAndMut { ty, .. })
-            | rustc_middle::ty::Ref(_, ty, _)
-            | rustc_middle::ty::Slice(ty) => self.has_sig_drop_attr(cx, *ty),
-            _ => false,
-        }
-    }
 }
 
 impl<'a, 'tcx> Visitor<'tcx> for SigDropHelper<'a, 'tcx> {
     fn visit_expr(&mut self, ex: &'tcx Expr<'_>) {
-        if !self.is_chain_end && self.has_sig_drop_attr(self.cx, self.get_type(ex)) {
+        if !self.is_chain_end
+            && self
+                .sig_drop_checker
+                .has_sig_drop_attr(self.cx, self.sig_drop_checker.get_type(ex))
+        {
             self.has_significant_drop = true;
             return;
         }
@@ -330,3 +359,38 @@ impl<'a, 'tcx> Visitor<'tcx> for SigDropHelper<'a, 'tcx> {
         }
     }
 }
+
+struct ArmSigDropHelper<'a, 'tcx> {
+    sig_drop_checker: SigDropChecker<'a, 'tcx>,
+    found_sig_drop_spans: FxHashSet<Span>,
+}
+
+impl<'a, 'tcx> ArmSigDropHelper<'a, 'tcx> {
+    fn new(cx: &'a LateContext<'tcx>) -> ArmSigDropHelper<'a, 'tcx> {
+        ArmSigDropHelper {
+            sig_drop_checker: SigDropChecker::new(cx),
+            found_sig_drop_spans: FxHashSet::<Span>::default(),
+        }
+    }
+}
+
+fn has_significant_drop_in_arms<'tcx, 'a>(cx: &'a LateContext<'tcx>, arms: &'tcx [Arm<'_>]) -> FxHashSet<Span> {
+    let mut helper = ArmSigDropHelper::new(cx);
+    for arm in arms {
+        helper.visit_expr(arm.body);
+    }
+    helper.found_sig_drop_spans
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for ArmSigDropHelper<'a, 'tcx> {
+    fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
+        if self
+            .sig_drop_checker
+            .has_sig_drop_attr(self.sig_drop_checker.cx, self.sig_drop_checker.get_type(ex))
+        {
+            self.found_sig_drop_spans.insert(ex.span);
+            return;
+        }
+        walk_expr(self, ex);
+    }
+}
diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs
index 0c4cb45d147..92091a0c339 100644
--- a/clippy_lints/src/matches/single_match.rs
+++ b/clippy_lints/src/matches/single_match.rs
@@ -140,70 +140,45 @@ fn check_opt_like<'a>(
     ty: Ty<'a>,
     els: Option<&Expr<'_>>,
 ) {
-    // list of candidate `Enum`s we know will never get any more members
-    let candidates = &[
-        (&paths::COW, "Borrowed"),
-        (&paths::COW, "Cow::Borrowed"),
-        (&paths::COW, "Cow::Owned"),
-        (&paths::COW, "Owned"),
-        (&paths::OPTION, "None"),
-        (&paths::RESULT, "Err"),
-        (&paths::RESULT, "Ok"),
-    ];
-
-    // We want to suggest to exclude an arm that contains only wildcards or forms the exhaustive
-    // match with the second branch, without enum variants in matches.
-    if !contains_only_wilds(arms[1].pat) && !form_exhaustive_matches(arms[0].pat, arms[1].pat) {
-        return;
+    // We don't want to lint if the second arm contains an enum which could
+    // have more variants in the future.
+    if form_exhaustive_matches(cx, ty, arms[0].pat, arms[1].pat) {
+        report_single_pattern(cx, ex, arms, expr, els);
     }
+}
 
+/// Returns `true` if all of the types in the pattern are enums which we know
+/// won't be expanded in the future
+fn pat_in_candidate_enum<'a>(cx: &LateContext<'a>, ty: Ty<'a>, pat: &Pat<'_>) -> bool {
     let mut paths_and_types = Vec::new();
-    if !collect_pat_paths(&mut paths_and_types, cx, arms[1].pat, ty) {
-        return;
-    }
+    collect_pat_paths(&mut paths_and_types, cx, pat, ty);
+    paths_and_types.iter().all(|ty| in_candidate_enum(cx, *ty))
+}
 
-    let in_candidate_enum = |path_info: &(String, Ty<'_>)| -> bool {
-        let (path, ty) = path_info;
-        for &(ty_path, pat_path) in candidates {
-            if path == pat_path && match_type(cx, *ty, ty_path) {
-                return true;
-            }
+/// Returns `true` if the given type is an enum we know won't be expanded in the future
+fn in_candidate_enum<'a>(cx: &LateContext<'a>, ty: Ty<'_>) -> bool {
+    // list of candidate `Enum`s we know will never get any more members
+    let candidates = [&paths::COW, &paths::OPTION, &paths::RESULT];
+
+    for candidate_ty in candidates {
+        if match_type(cx, ty, candidate_ty) {
+            return true;
         }
-        false
-    };
-    if paths_and_types.iter().all(in_candidate_enum) {
-        report_single_pattern(cx, ex, arms, expr, els);
     }
+    false
 }
 
-/// Collects paths and their types from the given patterns. Returns true if the given pattern could
-/// be simplified, false otherwise.
-fn collect_pat_paths<'a>(acc: &mut Vec<(String, Ty<'a>)>, cx: &LateContext<'a>, pat: &Pat<'_>, ty: Ty<'a>) -> bool {
+/// Collects types from the given pattern
+fn collect_pat_paths<'a>(acc: &mut Vec<Ty<'a>>, cx: &LateContext<'a>, pat: &Pat<'_>, ty: Ty<'a>) {
     match pat.kind {
-        PatKind::Wild => true,
-        PatKind::Tuple(inner, _) => inner.iter().all(|p| {
+        PatKind::Tuple(inner, _) => inner.iter().for_each(|p| {
             let p_ty = cx.typeck_results().pat_ty(p);
-            collect_pat_paths(acc, cx, p, p_ty)
+            collect_pat_paths(acc, cx, p, p_ty);
         }),
-        PatKind::TupleStruct(ref path, ..) => {
-            let path = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| {
-                s.print_qpath(path, false);
-            });
-            acc.push((path, ty));
-            true
-        },
-        PatKind::Binding(BindingAnnotation::Unannotated, .., ident, None) => {
-            acc.push((ident.to_string(), ty));
-            true
+        PatKind::TupleStruct(..) | PatKind::Binding(BindingAnnotation::Unannotated, .., None) | PatKind::Path(_) => {
+            acc.push(ty);
         },
-        PatKind::Path(ref path) => {
-            let path = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| {
-                s.print_qpath(path, false);
-            });
-            acc.push((path, ty));
-            true
-        },
-        _ => false,
+        _ => {},
     }
 }
 
@@ -218,7 +193,7 @@ fn contains_only_wilds(pat: &Pat<'_>) -> bool {
 
 /// Returns true if the given patterns forms only exhaustive matches that don't contain enum
 /// patterns without a wildcard.
-fn form_exhaustive_matches(left: &Pat<'_>, right: &Pat<'_>) -> bool {
+fn form_exhaustive_matches<'a>(cx: &LateContext<'a>, ty: Ty<'a>, left: &Pat<'_>, right: &Pat<'_>) -> bool {
     match (&left.kind, &right.kind) {
         (PatKind::Wild, _) | (_, PatKind::Wild) => true,
         (PatKind::Tuple(left_in, left_pos), PatKind::Tuple(right_in, right_pos)) => {
@@ -264,6 +239,10 @@ fn form_exhaustive_matches(left: &Pat<'_>, right: &Pat<'_>) -> bool {
             }
             true
         },
+        (PatKind::TupleStruct(..), PatKind::Path(_)) => pat_in_candidate_enum(cx, ty, right),
+        (PatKind::TupleStruct(..), PatKind::TupleStruct(_, inner, _)) => {
+            pat_in_candidate_enum(cx, ty, right) && inner.iter().all(contains_only_wilds)
+        },
         _ => false,
     }
 }
diff --git a/clippy_lints/src/methods/map_flatten.rs b/clippy_lints/src/methods/map_flatten.rs
index 8ae84dbb3dc..13853dec99d 100644
--- a/clippy_lints/src/methods/map_flatten.rs
+++ b/clippy_lints/src/methods/map_flatten.rs
@@ -14,14 +14,17 @@ use super::MAP_FLATTEN;
 pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, map_arg: &Expr<'_>, map_span: Span) {
     if let Some((caller_ty_name, method_to_use)) = try_get_caller_ty_name_and_method_name(cx, expr, recv, map_arg) {
         let mut applicability = Applicability::MachineApplicable;
-        
+
         let closure_snippet = snippet_with_applicability(cx, map_arg.span, "..", &mut applicability);
         span_lint_and_sugg(
             cx,
             MAP_FLATTEN,
             expr.span.with_lo(map_span.lo()),
             &format!("called `map(..).flatten()` on `{}`", caller_ty_name),
-            &format!("try replacing `map` with `{}` and remove the `.flatten()`", method_to_use),
+            &format!(
+                "try replacing `map` with `{}` and remove the `.flatten()`",
+                method_to_use
+            ),
             format!("{}({})", method_to_use, closure_snippet),
             applicability,
         );
diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs
index 01bf871198a..df2430ced6b 100644
--- a/clippy_lints/src/misc.rs
+++ b/clippy_lints/src/misc.rs
@@ -1,28 +1,21 @@
-use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_hir_and_then};
 use clippy_utils::source::{snippet, snippet_opt};
-use clippy_utils::ty::{implements_trait, is_copy};
 use if_chain::if_chain;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::FnKind;
 use rustc_hir::{
     self as hir, def, BinOpKind, BindingAnnotation, Body, Expr, ExprKind, FnDecl, HirId, Mutability, PatKind, Stmt,
-    StmtKind, TyKind, UnOp,
+    StmtKind, TyKind,
 };
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::lint::in_external_macro;
-use rustc_middle::ty::{self, Ty};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::hygiene::DesugaringKind;
 use rustc_span::source_map::{ExpnKind, Span};
-use rustc_span::symbol::sym;
 
-use clippy_utils::consts::{constant, Constant};
 use clippy_utils::sugg::Sugg;
-use clippy_utils::{
-    get_item_name, get_parent_expr, in_constant, is_integer_const, iter_input_pats, last_path_segment,
-    match_any_def_paths, path_def_id, paths, unsext, SpanlessEq,
-};
+use clippy_utils::{get_parent_expr, in_constant, iter_input_pats, last_path_segment, SpanlessEq};
 
 declare_clippy_lint! {
     /// ### What it does
@@ -58,122 +51,6 @@ declare_clippy_lint! {
     style,
     "an entire binding declared as `ref`, in a function argument or a `let` statement"
 }
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for comparisons to NaN.
-    ///
-    /// ### Why is this bad?
-    /// NaN does not compare meaningfully to anything – not
-    /// even itself – so those comparisons are simply wrong.
-    ///
-    /// ### Example
-    /// ```rust
-    /// # let x = 1.0;
-    /// if x == f32::NAN { }
-    /// ```
-    ///
-    /// Use instead:
-    /// ```rust
-    /// # let x = 1.0f32;
-    /// if x.is_nan() { }
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub CMP_NAN,
-    correctness,
-    "comparisons to `NAN`, which will always return false, probably not intended"
-}
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for (in-)equality comparisons on floating-point
-    /// values (apart from zero), except in functions called `*eq*` (which probably
-    /// implement equality for a type involving floats).
-    ///
-    /// ### Why is this bad?
-    /// Floating point calculations are usually imprecise, so
-    /// asking if two values are *exactly* equal is asking for trouble. For a good
-    /// guide on what to do, see [the floating point
-    /// guide](http://www.floating-point-gui.de/errors/comparison).
-    ///
-    /// ### Example
-    /// ```rust
-    /// let x = 1.2331f64;
-    /// let y = 1.2332f64;
-    ///
-    /// if y == 1.23f64 { }
-    /// if y != x {} // where both are floats
-    /// ```
-    ///
-    /// Use instead:
-    /// ```rust
-    /// # let x = 1.2331f64;
-    /// # let y = 1.2332f64;
-    /// let error_margin = f64::EPSILON; // Use an epsilon for comparison
-    /// // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead.
-    /// // let error_margin = std::f64::EPSILON;
-    /// if (y - 1.23f64).abs() < error_margin { }
-    /// if (y - x).abs() > error_margin { }
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub FLOAT_CMP,
-    pedantic,
-    "using `==` or `!=` on float values instead of comparing difference with an epsilon"
-}
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for conversions to owned values just for the sake
-    /// of a comparison.
-    ///
-    /// ### Why is this bad?
-    /// The comparison can operate on a reference, so creating
-    /// an owned value effectively throws it away directly afterwards, which is
-    /// needlessly consuming code and heap space.
-    ///
-    /// ### Example
-    /// ```rust
-    /// # let x = "foo";
-    /// # let y = String::from("foo");
-    /// if x.to_owned() == y {}
-    /// ```
-    ///
-    /// Use instead:
-    /// ```rust
-    /// # let x = "foo";
-    /// # let y = String::from("foo");
-    /// if x == y {}
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub CMP_OWNED,
-    perf,
-    "creating owned instances for comparing with others, e.g., `x == \"foo\".to_string()`"
-}
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for getting the remainder of a division by one or minus
-    /// one.
-    ///
-    /// ### Why is this bad?
-    /// The result for a divisor of one can only ever be zero; for
-    /// minus one it can cause panic/overflow (if the left operand is the minimal value of
-    /// the respective integer type) or results in zero. No one will write such code
-    /// deliberately, unless trying to win an Underhanded Rust Contest. Even for that
-    /// contest, it's probably a bad idea. Use something more underhanded.
-    ///
-    /// ### Example
-    /// ```rust
-    /// # let x = 1;
-    /// let a = x % 1;
-    /// let a = x % -1;
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub MODULO_ONE,
-    correctness,
-    "taking a number modulo +/-1, which can either panic/overflow or always returns 0"
-}
-
 declare_clippy_lint! {
     /// ### What it does
     /// Checks for the use of bindings with a single leading
@@ -244,51 +121,11 @@ declare_clippy_lint! {
     "using `0 as *{const, mut} T`"
 }
 
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for (in-)equality comparisons on floating-point
-    /// value and constant, except in functions called `*eq*` (which probably
-    /// implement equality for a type involving floats).
-    ///
-    /// ### Why is this bad?
-    /// Floating point calculations are usually imprecise, so
-    /// asking if two values are *exactly* equal is asking for trouble. For a good
-    /// guide on what to do, see [the floating point
-    /// guide](http://www.floating-point-gui.de/errors/comparison).
-    ///
-    /// ### Example
-    /// ```rust
-    /// let x: f64 = 1.0;
-    /// const ONE: f64 = 1.00;
-    ///
-    /// if x == ONE { } // where both are floats
-    /// ```
-    ///
-    /// Use instead:
-    /// ```rust
-    /// # let x: f64 = 1.0;
-    /// # const ONE: f64 = 1.00;
-    /// let error_margin = f64::EPSILON; // Use an epsilon for comparison
-    /// // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead.
-    /// // let error_margin = std::f64::EPSILON;
-    /// if (x - ONE).abs() < error_margin { }
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub FLOAT_CMP_CONST,
-    restriction,
-    "using `==` or `!=` on float constants instead of comparing difference with an epsilon"
-}
-
 declare_lint_pass!(MiscLints => [
     TOPLEVEL_REF_ARG,
-    CMP_NAN,
-    FLOAT_CMP,
-    CMP_OWNED,
-    MODULO_ONE,
     USED_UNDERSCORE_BINDING,
     SHORT_CIRCUIT_STATEMENT,
     ZERO_PTR,
-    FLOAT_CMP_CONST
 ]);
 
 impl<'tcx> LateLintPass<'tcx> for MiscLints {
@@ -398,16 +235,9 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints {
     }
 
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        match expr.kind {
-            ExprKind::Cast(e, ty) => {
-                check_cast(cx, expr.span, e, ty);
-                return;
-            },
-            ExprKind::Binary(ref cmp, left, right) => {
-                check_binary(cx, expr, cmp, left, right);
-                return;
-            },
-            _ => {},
+        if let ExprKind::Cast(e, ty) = expr.kind {
+            check_cast(cx, expr.span, e, ty);
+            return;
         }
         if in_attributes_expansion(expr) || expr.span.is_desugaring(DesugaringKind::Await) {
             // Don't lint things expanded by #[derive(...)], etc or `await` desugaring
@@ -455,236 +285,6 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints {
     }
 }
 
-fn get_lint_and_message(
-    is_comparing_constants: bool,
-    is_comparing_arrays: bool,
-) -> (&'static rustc_lint::Lint, &'static str) {
-    if is_comparing_constants {
-        (
-            FLOAT_CMP_CONST,
-            if is_comparing_arrays {
-                "strict comparison of `f32` or `f64` constant arrays"
-            } else {
-                "strict comparison of `f32` or `f64` constant"
-            },
-        )
-    } else {
-        (
-            FLOAT_CMP,
-            if is_comparing_arrays {
-                "strict comparison of `f32` or `f64` arrays"
-            } else {
-                "strict comparison of `f32` or `f64`"
-            },
-        )
-    }
-}
-
-fn check_nan(cx: &LateContext<'_>, expr: &Expr<'_>, cmp_expr: &Expr<'_>) {
-    if_chain! {
-        if !in_constant(cx, cmp_expr.hir_id);
-        if let Some((value, _)) = constant(cx, cx.typeck_results(), expr);
-        if match value {
-            Constant::F32(num) => num.is_nan(),
-            Constant::F64(num) => num.is_nan(),
-            _ => false,
-        };
-        then {
-            span_lint(
-                cx,
-                CMP_NAN,
-                cmp_expr.span,
-                "doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead",
-            );
-        }
-    }
-}
-
-fn is_named_constant<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
-    if let Some((_, res)) = constant(cx, cx.typeck_results(), expr) {
-        res
-    } else {
-        false
-    }
-}
-
-fn is_allowed<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
-    match constant(cx, cx.typeck_results(), expr) {
-        Some((Constant::F32(f), _)) => f == 0.0 || f.is_infinite(),
-        Some((Constant::F64(f), _)) => f == 0.0 || f.is_infinite(),
-        Some((Constant::Vec(vec), _)) => vec.iter().all(|f| match f {
-            Constant::F32(f) => *f == 0.0 || (*f).is_infinite(),
-            Constant::F64(f) => *f == 0.0 || (*f).is_infinite(),
-            _ => false,
-        }),
-        _ => false,
-    }
-}
-
-// Return true if `expr` is the result of `signum()` invoked on a float value.
-fn is_signum(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
-    // The negation of a signum is still a signum
-    if let ExprKind::Unary(UnOp::Neg, child_expr) = expr.kind {
-        return is_signum(cx, child_expr);
-    }
-
-    if_chain! {
-        if let ExprKind::MethodCall(method_name, [ref self_arg, ..], _) = expr.kind;
-        if sym!(signum) == method_name.ident.name;
-        // Check that the receiver of the signum() is a float (expressions[0] is the receiver of
-        // the method call)
-        then {
-            return is_float(cx, self_arg);
-        }
-    }
-    false
-}
-
-fn is_float(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
-    let value = &cx.typeck_results().expr_ty(expr).peel_refs().kind();
-
-    if let ty::Array(arr_ty, _) = value {
-        return matches!(arr_ty.kind(), ty::Float(_));
-    };
-
-    matches!(value, ty::Float(_))
-}
-
-fn is_array(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
-    matches!(&cx.typeck_results().expr_ty(expr).peel_refs().kind(), ty::Array(_, _))
-}
-
-#[expect(clippy::too_many_lines)]
-fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) {
-    #[derive(Default)]
-    struct EqImpl {
-        ty_eq_other: bool,
-        other_eq_ty: bool,
-    }
-
-    impl EqImpl {
-        fn is_implemented(&self) -> bool {
-            self.ty_eq_other || self.other_eq_ty
-        }
-    }
-
-    fn symmetric_partial_eq<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, other: Ty<'tcx>) -> Option<EqImpl> {
-        cx.tcx.lang_items().eq_trait().map(|def_id| EqImpl {
-            ty_eq_other: implements_trait(cx, ty, def_id, &[other.into()]),
-            other_eq_ty: implements_trait(cx, other, def_id, &[ty.into()]),
-        })
-    }
-
-    let typeck = cx.typeck_results();
-    let (arg, arg_span) = match expr.kind {
-        ExprKind::MethodCall(.., [arg], _)
-            if typeck
-                .type_dependent_def_id(expr.hir_id)
-                .and_then(|id| cx.tcx.trait_of_item(id))
-                .map_or(false, |id| {
-                    matches!(cx.tcx.get_diagnostic_name(id), Some(sym::ToString | sym::ToOwned))
-                }) =>
-        {
-            (arg, arg.span)
-        },
-        ExprKind::Call(path, [arg])
-            if path_def_id(cx, path)
-                .and_then(|id| match_any_def_paths(cx, id, &[&paths::FROM_STR_METHOD, &paths::FROM_FROM]))
-                .map_or(false, |idx| match idx {
-                    0 => true,
-                    1 => !is_copy(cx, typeck.expr_ty(expr)),
-                    _ => false,
-                }) =>
-        {
-            (arg, arg.span)
-        },
-        _ => return,
-    };
-
-    let arg_ty = typeck.expr_ty(arg);
-    let other_ty = typeck.expr_ty(other);
-
-    let without_deref = symmetric_partial_eq(cx, arg_ty, other_ty).unwrap_or_default();
-    let with_deref = arg_ty
-        .builtin_deref(true)
-        .and_then(|tam| symmetric_partial_eq(cx, tam.ty, other_ty))
-        .unwrap_or_default();
-
-    if !with_deref.is_implemented() && !without_deref.is_implemented() {
-        return;
-    }
-
-    let other_gets_derefed = matches!(other.kind, ExprKind::Unary(UnOp::Deref, _));
-
-    let lint_span = if other_gets_derefed {
-        expr.span.to(other.span)
-    } else {
-        expr.span
-    };
-
-    span_lint_and_then(
-        cx,
-        CMP_OWNED,
-        lint_span,
-        "this creates an owned instance just for comparison",
-        |diag| {
-            // This also catches `PartialEq` implementations that call `to_owned`.
-            if other_gets_derefed {
-                diag.span_label(lint_span, "try implementing the comparison without allocating");
-                return;
-            }
-
-            let arg_snip = snippet(cx, arg_span, "..");
-            let expr_snip;
-            let eq_impl;
-            if with_deref.is_implemented() {
-                expr_snip = format!("*{}", arg_snip);
-                eq_impl = with_deref;
-            } else {
-                expr_snip = arg_snip.to_string();
-                eq_impl = without_deref;
-            };
-
-            let span;
-            let hint;
-            if (eq_impl.ty_eq_other && left) || (eq_impl.other_eq_ty && !left) {
-                span = expr.span;
-                hint = expr_snip;
-            } else {
-                span = expr.span.to(other.span);
-
-                let cmp_span = if other.span < expr.span {
-                    other.span.between(expr.span)
-                } else {
-                    expr.span.between(other.span)
-                };
-                if eq_impl.ty_eq_other {
-                    hint = format!(
-                        "{}{}{}",
-                        expr_snip,
-                        snippet(cx, cmp_span, ".."),
-                        snippet(cx, other.span, "..")
-                    );
-                } else {
-                    hint = format!(
-                        "{}{}{}",
-                        snippet(cx, other.span, ".."),
-                        snippet(cx, cmp_span, ".."),
-                        expr_snip
-                    );
-                }
-            }
-
-            diag.span_suggestion(
-                span,
-                "try",
-                hint,
-                Applicability::MachineApplicable, // snippet
-            );
-        },
-    );
-}
-
 /// Heuristic to see if an expression is used. Should be compatible with
 /// `unused_variables`'s idea
 /// of what it means for an expression to be "used".
@@ -740,74 +340,3 @@ fn check_cast(cx: &LateContext<'_>, span: Span, e: &Expr<'_>, ty: &hir::Ty<'_>)
         }
     }
 }
-
-fn check_binary<'a>(
-    cx: &LateContext<'a>,
-    expr: &Expr<'_>,
-    cmp: &rustc_span::source_map::Spanned<rustc_hir::BinOpKind>,
-    left: &'a Expr<'_>,
-    right: &'a Expr<'_>,
-) {
-    let op = cmp.node;
-    if op.is_comparison() {
-        check_nan(cx, left, expr);
-        check_nan(cx, right, expr);
-        check_to_owned(cx, left, right, true);
-        check_to_owned(cx, right, left, false);
-    }
-    if (op == BinOpKind::Eq || op == BinOpKind::Ne) && (is_float(cx, left) || is_float(cx, right)) {
-        if is_allowed(cx, left) || is_allowed(cx, right) {
-            return;
-        }
-
-        // Allow comparing the results of signum()
-        if is_signum(cx, left) && is_signum(cx, right) {
-            return;
-        }
-
-        if let Some(name) = get_item_name(cx, expr) {
-            let name = name.as_str();
-            if name == "eq" || name == "ne" || name == "is_nan" || name.starts_with("eq_") || name.ends_with("_eq") {
-                return;
-            }
-        }
-        let is_comparing_arrays = is_array(cx, left) || is_array(cx, right);
-        let (lint, msg) = get_lint_and_message(
-            is_named_constant(cx, left) || is_named_constant(cx, right),
-            is_comparing_arrays,
-        );
-        span_lint_and_then(cx, lint, expr.span, msg, |diag| {
-            let lhs = Sugg::hir(cx, left, "..");
-            let rhs = Sugg::hir(cx, right, "..");
-
-            if !is_comparing_arrays {
-                diag.span_suggestion(
-                    expr.span,
-                    "consider comparing them within some margin of error",
-                    format!(
-                        "({}).abs() {} error_margin",
-                        lhs - rhs,
-                        if op == BinOpKind::Eq { '<' } else { '>' }
-                    ),
-                    Applicability::HasPlaceholders, // snippet
-                );
-            }
-            diag.note("`f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`");
-        });
-    } else if op == BinOpKind::Rem {
-        if is_integer_const(cx, right, 1) {
-            span_lint(cx, MODULO_ONE, expr.span, "any number modulo 1 will be 0");
-        }
-
-        if let ty::Int(ity) = cx.typeck_results().expr_ty(right).kind() {
-            if is_integer_const(cx, right, unsext(cx.tcx, -1, *ity)) {
-                span_lint(
-                    cx,
-                    MODULO_ONE,
-                    expr.span,
-                    "any number modulo -1 will panic/overflow or result in 0",
-                );
-            }
-        };
-    }
-}
diff --git a/clippy_lints/src/needless_bitwise_bool.rs b/clippy_lints/src/needless_bitwise_bool.rs
deleted file mode 100644
index 623d22bc9bd..00000000000
--- a/clippy_lints/src/needless_bitwise_bool.rs
+++ /dev/null
@@ -1,85 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::source::snippet_opt;
-use if_chain::if_chain;
-use rustc_errors::Applicability;
-use rustc_hir::{BinOpKind, Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty;
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for uses of bitwise and/or operators between booleans, where performance may be improved by using
-    /// a lazy and.
-    ///
-    /// ### Why is this bad?
-    /// The bitwise operators do not support short-circuiting, so it may hinder code performance.
-    /// Additionally, boolean logic "masked" as bitwise logic is not caught by lints like `unnecessary_fold`
-    ///
-    /// ### Known problems
-    /// This lint evaluates only when the right side is determined to have no side effects. At this time, that
-    /// determination is quite conservative.
-    ///
-    /// ### Example
-    /// ```rust
-    /// let (x,y) = (true, false);
-    /// if x & !y {} // where both x and y are booleans
-    /// ```
-    /// Use instead:
-    /// ```rust
-    /// let (x,y) = (true, false);
-    /// if x && !y {}
-    /// ```
-    #[clippy::version = "1.54.0"]
-    pub NEEDLESS_BITWISE_BOOL,
-    pedantic,
-    "Boolean expressions that use bitwise rather than lazy operators"
-}
-
-declare_lint_pass!(NeedlessBitwiseBool => [NEEDLESS_BITWISE_BOOL]);
-
-fn is_bitwise_operation(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
-    let ty = cx.typeck_results().expr_ty(expr);
-    if_chain! {
-        if !expr.span.from_expansion();
-        if let (&ExprKind::Binary(ref op, _, right), &ty::Bool) = (&expr.kind, &ty.kind());
-        if op.node == BinOpKind::BitAnd || op.node == BinOpKind::BitOr;
-        if let ExprKind::Call(..) | ExprKind::MethodCall(..) | ExprKind::Binary(..) | ExprKind::Unary(..) = right.kind;
-        if !right.can_have_side_effects();
-        then {
-            return true;
-        }
-    }
-    false
-}
-
-fn suggestion_snippet(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
-    if let ExprKind::Binary(ref op, left, right) = expr.kind {
-        if let (Some(l_snippet), Some(r_snippet)) = (snippet_opt(cx, left.span), snippet_opt(cx, right.span)) {
-            let op_snippet = match op.node {
-                BinOpKind::BitAnd => "&&",
-                _ => "||",
-            };
-            return Some(format!("{} {} {}", l_snippet, op_snippet, r_snippet));
-        }
-    }
-    None
-}
-
-impl LateLintPass<'_> for NeedlessBitwiseBool {
-    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
-        if is_bitwise_operation(cx, expr) {
-            span_lint_and_then(
-                cx,
-                NEEDLESS_BITWISE_BOOL,
-                expr.span,
-                "use of bitwise operator instead of lazy operator between booleans",
-                |diag| {
-                    if let Some(sugg) = suggestion_snippet(cx, expr) {
-                        diag.span_suggestion(expr.span, "try", sugg, Applicability::MachineApplicable);
-                    }
-                },
-            );
-        }
-    }
-}
diff --git a/clippy_lints/src/neg_multiply.rs b/clippy_lints/src/neg_multiply.rs
index ce6bb38b7c0..b087cfb36b1 100644
--- a/clippy_lints/src/neg_multiply.rs
+++ b/clippy_lints/src/neg_multiply.rs
@@ -1,7 +1,9 @@
 use clippy_utils::consts::{self, Constant};
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::sugg::has_enclosing_paren;
 use if_chain::if_chain;
+use rustc_ast::util::parser::PREC_PREFIX;
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp};
 use rustc_lint::{LateContext, LateLintPass};
@@ -58,7 +60,12 @@ fn check_mul(cx: &LateContext<'_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) {
 
         then {
             let mut applicability = Applicability::MachineApplicable;
-            let suggestion = format!("-{}", snippet_with_applicability(cx, exp.span, "..", &mut applicability));
+            let snip = snippet_with_applicability(cx, exp.span, "..", &mut applicability);
+            let suggestion = if exp.precedence().order() < PREC_PREFIX && !has_enclosing_paren(&snip) {
+                format!("-({})", snip)
+            } else {
+                format!("-{}", snip)
+            };
             span_lint_and_sugg(
                     cx,
                     NEG_MULTIPLY,
diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs
index 1727275a4e0..a1ef32ae608 100644
--- a/clippy_lints/src/non_copy_const.rs
+++ b/clippy_lints/src/non_copy_const.rs
@@ -6,6 +6,7 @@ use std::ptr;
 
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::in_constant;
+use clippy_utils::macros::macro_backtrace;
 use if_chain::if_chain;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::DefId;
@@ -18,7 +19,7 @@ use rustc_middle::mir::interpret::{ConstValue, ErrorHandled};
 use rustc_middle::ty::adjustment::Adjust;
 use rustc_middle::ty::{self, Ty};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::{InnerSpan, Span, DUMMY_SP};
+use rustc_span::{sym, InnerSpan, Span, DUMMY_SP};
 use rustc_typeck::hir_ty_to_ty;
 
 // FIXME: this is a correctness problem but there's no suitable
@@ -250,8 +251,14 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst {
     fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx Item<'_>) {
         if let ItemKind::Const(hir_ty, body_id) = it.kind {
             let ty = hir_ty_to_ty(cx.tcx, hir_ty);
-
-            if is_unfrozen(cx, ty) && is_value_unfrozen_poly(cx, body_id, ty) {
+            if !macro_backtrace(it.span).last().map_or(false, |macro_call| {
+                matches!(
+                    cx.tcx.get_diagnostic_name(macro_call.def_id),
+                    Some(sym::thread_local_macro)
+                )
+            }) && is_unfrozen(cx, ty)
+                && is_value_unfrozen_poly(cx, body_id, ty)
+            {
                 lint(cx, Source::Item { item: it.span });
             }
         }
diff --git a/clippy_lints/src/non_expressive_names.rs b/clippy_lints/src/non_expressive_names.rs
index 7f6b535c7b1..b96af06b8d7 100644
--- a/clippy_lints/src/non_expressive_names.rs
+++ b/clippy_lints/src/non_expressive_names.rs
@@ -159,12 +159,10 @@ impl<'a, 'tcx, 'b> Visitor<'tcx> for SimilarNamesNameVisitor<'a, 'tcx, 'b> {
 
 #[must_use]
 fn get_exemptions(interned_name: &str) -> Option<&'static [&'static str]> {
-    for &list in ALLOWED_TO_BE_SIMILAR {
-        if allowed_to_be_similar(interned_name, list) {
-            return Some(list);
-        }
-    }
-    None
+    ALLOWED_TO_BE_SIMILAR
+        .iter()
+        .find(|&&list| allowed_to_be_similar(interned_name, list))
+        .copied()
 }
 
 #[must_use]
@@ -328,7 +326,7 @@ impl<'a, 'tcx> Visitor<'tcx> for SimilarNamesLocalVisitor<'a, 'tcx> {
         // add the pattern after the expression because the bindings aren't available
         // yet in the init
         // expression
-        SimilarNamesNameVisitor(self).visit_pat(&*local.pat);
+        SimilarNamesNameVisitor(self).visit_pat(&local.pat);
     }
     fn visit_block(&mut self, blk: &'tcx Block) {
         self.single_char_names.push(vec![]);
diff --git a/clippy_lints/src/numeric_arithmetic.rs b/clippy_lints/src/numeric_arithmetic.rs
deleted file mode 100644
index 5c4de338149..00000000000
--- a/clippy_lints/src/numeric_arithmetic.rs
+++ /dev/null
@@ -1,170 +0,0 @@
-use clippy_utils::consts::constant_simple;
-use clippy_utils::diagnostics::span_lint;
-use rustc_hir as hir;
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::source_map::Span;
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for integer arithmetic operations which could overflow or panic.
-    ///
-    /// Specifically, checks for any operators (`+`, `-`, `*`, `<<`, etc) which are capable
-    /// of overflowing according to the [Rust
-    /// Reference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#overflow),
-    /// or which can panic (`/`, `%`). No bounds analysis or sophisticated reasoning is
-    /// attempted.
-    ///
-    /// ### Why is this bad?
-    /// Integer overflow will trigger a panic in debug builds or will wrap in
-    /// release mode. Division by zero will cause a panic in either mode. In some applications one
-    /// wants explicitly checked, wrapping or saturating arithmetic.
-    ///
-    /// ### Example
-    /// ```rust
-    /// # let a = 0;
-    /// a + 1;
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub INTEGER_ARITHMETIC,
-    restriction,
-    "any integer arithmetic expression which could overflow or panic"
-}
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for float arithmetic.
-    ///
-    /// ### Why is this bad?
-    /// For some embedded systems or kernel development, it
-    /// can be useful to rule out floating-point numbers.
-    ///
-    /// ### Example
-    /// ```rust
-    /// # let a = 0.0;
-    /// a + 1.0;
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub FLOAT_ARITHMETIC,
-    restriction,
-    "any floating-point arithmetic statement"
-}
-
-#[derive(Copy, Clone, Default)]
-pub struct NumericArithmetic {
-    expr_span: Option<Span>,
-    /// This field is used to check whether expressions are constants, such as in enum discriminants
-    /// and consts
-    const_span: Option<Span>,
-}
-
-impl_lint_pass!(NumericArithmetic => [INTEGER_ARITHMETIC, FLOAT_ARITHMETIC]);
-
-impl<'tcx> LateLintPass<'tcx> for NumericArithmetic {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
-        if self.expr_span.is_some() {
-            return;
-        }
-
-        if let Some(span) = self.const_span {
-            if span.contains(expr.span) {
-                return;
-            }
-        }
-        match &expr.kind {
-            hir::ExprKind::Binary(op, l, r) | hir::ExprKind::AssignOp(op, l, r) => {
-                match op.node {
-                    hir::BinOpKind::And
-                    | hir::BinOpKind::Or
-                    | hir::BinOpKind::BitAnd
-                    | hir::BinOpKind::BitOr
-                    | hir::BinOpKind::BitXor
-                    | hir::BinOpKind::Eq
-                    | hir::BinOpKind::Lt
-                    | hir::BinOpKind::Le
-                    | hir::BinOpKind::Ne
-                    | hir::BinOpKind::Ge
-                    | hir::BinOpKind::Gt => return,
-                    _ => (),
-                }
-
-                let (l_ty, r_ty) = (cx.typeck_results().expr_ty(l), cx.typeck_results().expr_ty(r));
-                if l_ty.peel_refs().is_integral() && r_ty.peel_refs().is_integral() {
-                    match op.node {
-                        hir::BinOpKind::Div | hir::BinOpKind::Rem => match &r.kind {
-                            hir::ExprKind::Lit(_lit) => (),
-                            hir::ExprKind::Unary(hir::UnOp::Neg, expr) => {
-                                if let hir::ExprKind::Lit(lit) = &expr.kind {
-                                    if let rustc_ast::ast::LitKind::Int(1, _) = lit.node {
-                                        span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
-                                        self.expr_span = Some(expr.span);
-                                    }
-                                }
-                            },
-                            _ => {
-                                span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
-                                self.expr_span = Some(expr.span);
-                            },
-                        },
-                        _ => {
-                            span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
-                            self.expr_span = Some(expr.span);
-                        },
-                    }
-                } else if r_ty.peel_refs().is_floating_point() && r_ty.peel_refs().is_floating_point() {
-                    span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected");
-                    self.expr_span = Some(expr.span);
-                }
-            },
-            hir::ExprKind::Unary(hir::UnOp::Neg, arg) => {
-                let ty = cx.typeck_results().expr_ty(arg);
-                if constant_simple(cx, cx.typeck_results(), expr).is_none() {
-                    if ty.is_integral() {
-                        span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
-                        self.expr_span = Some(expr.span);
-                    } else if ty.is_floating_point() {
-                        span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected");
-                        self.expr_span = Some(expr.span);
-                    }
-                }
-            },
-            _ => (),
-        }
-    }
-
-    fn check_expr_post(&mut self, _: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
-        if Some(expr.span) == self.expr_span {
-            self.expr_span = None;
-        }
-    }
-
-    fn check_body(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>) {
-        let body_owner = cx.tcx.hir().body_owner_def_id(body.id());
-
-        match cx.tcx.hir().body_owner_kind(body_owner) {
-            hir::BodyOwnerKind::Static(_) | hir::BodyOwnerKind::Const => {
-                let body_span = cx.tcx.def_span(body_owner);
-
-                if let Some(span) = self.const_span {
-                    if span.contains(body_span) {
-                        return;
-                    }
-                }
-                self.const_span = Some(body_span);
-            },
-            hir::BodyOwnerKind::Fn | hir::BodyOwnerKind::Closure => (),
-        }
-    }
-
-    fn check_body_post(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>) {
-        let body_owner = cx.tcx.hir().body_owner(body.id());
-        let body_span = cx.tcx.hir().span(body_owner);
-
-        if let Some(span) = self.const_span {
-            if span.contains(body_span) {
-                return;
-            }
-        }
-        self.const_span = None;
-    }
-}
diff --git a/clippy_lints/src/absurd_extreme_comparisons.rs b/clippy_lints/src/operators/absurd_extreme_comparisons.rs
index 7665aa8380b..1ec4240afef 100644
--- a/clippy_lints/src/absurd_extreme_comparisons.rs
+++ b/clippy_lints/src/operators/absurd_extreme_comparisons.rs
@@ -1,7 +1,6 @@
 use rustc_hir::{BinOpKind, Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass};
+use rustc_lint::LateContext;
 use rustc_middle::ty;
-use rustc_session::{declare_lint_pass, declare_tool_lint};
 
 use clippy_utils::comparisons::{normalize_comparison, Rel};
 use clippy_utils::consts::{constant, Constant};
@@ -10,73 +9,41 @@ use clippy_utils::source::snippet;
 use clippy_utils::ty::is_isize_or_usize;
 use clippy_utils::{clip, int_bits, unsext};
 
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for comparisons where one side of the relation is
-    /// either the minimum or maximum value for its type and warns if it involves a
-    /// case that is always true or always false. Only integer and boolean types are
-    /// checked.
-    ///
-    /// ### Why is this bad?
-    /// An expression like `min <= x` may misleadingly imply
-    /// that it is possible for `x` to be less than the minimum. Expressions like
-    /// `max < x` are probably mistakes.
-    ///
-    /// ### Known problems
-    /// For `usize` the size of the current compile target will
-    /// be assumed (e.g., 64 bits on 64 bit systems). This means code that uses such
-    /// a comparison to detect target pointer width will trigger this lint. One can
-    /// use `mem::sizeof` and compare its value or conditional compilation
-    /// attributes
-    /// like `#[cfg(target_pointer_width = "64")] ..` instead.
-    ///
-    /// ### Example
-    /// ```rust
-    /// let vec: Vec<isize> = Vec::new();
-    /// if vec.len() <= 0 {}
-    /// if 100 > i32::MAX {}
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub ABSURD_EXTREME_COMPARISONS,
-    correctness,
-    "a comparison with a maximum or minimum value that is always true or false"
-}
+use super::ABSURD_EXTREME_COMPARISONS;
 
-declare_lint_pass!(AbsurdExtremeComparisons => [ABSURD_EXTREME_COMPARISONS]);
-
-impl<'tcx> LateLintPass<'tcx> for AbsurdExtremeComparisons {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if let ExprKind::Binary(ref cmp, lhs, rhs) = expr.kind {
-            if let Some((culprit, result)) = detect_absurd_comparison(cx, cmp.node, lhs, rhs) {
-                if !expr.span.from_expansion() {
-                    let msg = "this comparison involving the minimum or maximum element for this \
-                               type contains a case that is always true or always false";
-
-                    let conclusion = match result {
-                        AbsurdComparisonResult::AlwaysFalse => "this comparison is always false".to_owned(),
-                        AbsurdComparisonResult::AlwaysTrue => "this comparison is always true".to_owned(),
-                        AbsurdComparisonResult::InequalityImpossible => format!(
-                            "the case where the two sides are not equal never occurs, consider using `{} == {}` \
-                             instead",
-                            snippet(cx, lhs.span, "lhs"),
-                            snippet(cx, rhs.span, "rhs")
-                        ),
-                    };
-
-                    let help = format!(
-                        "because `{}` is the {} value for this type, {}",
-                        snippet(cx, culprit.expr.span, "x"),
-                        match culprit.which {
-                            ExtremeType::Minimum => "minimum",
-                            ExtremeType::Maximum => "maximum",
-                        },
-                        conclusion
-                    );
-
-                    span_lint_and_help(cx, ABSURD_EXTREME_COMPARISONS, expr.span, msg, None, &help);
-                }
-            }
-        }
+pub(super) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'_>,
+    op: BinOpKind,
+    lhs: &'tcx Expr<'_>,
+    rhs: &'tcx Expr<'_>,
+) {
+    if let Some((culprit, result)) = detect_absurd_comparison(cx, op, lhs, rhs) {
+        let msg = "this comparison involving the minimum or maximum element for this \
+                           type contains a case that is always true or always false";
+
+        let conclusion = match result {
+            AbsurdComparisonResult::AlwaysFalse => "this comparison is always false".to_owned(),
+            AbsurdComparisonResult::AlwaysTrue => "this comparison is always true".to_owned(),
+            AbsurdComparisonResult::InequalityImpossible => format!(
+                "the case where the two sides are not equal never occurs, consider using `{} == {}` \
+                         instead",
+                snippet(cx, lhs.span, "lhs"),
+                snippet(cx, rhs.span, "rhs")
+            ),
+        };
+
+        let help = format!(
+            "because `{}` is the {} value for this type, {}",
+            snippet(cx, culprit.expr.span, "x"),
+            match culprit.which {
+                ExtremeType::Minimum => "minimum",
+                ExtremeType::Maximum => "maximum",
+            },
+            conclusion
+        );
+
+        span_lint_and_help(cx, ABSURD_EXTREME_COMPARISONS, expr.span, msg, None, &help);
     }
 }
 
diff --git a/clippy_lints/src/operators/assign_op_pattern.rs b/clippy_lints/src/operators/assign_op_pattern.rs
new file mode 100644
index 00000000000..979e0a66707
--- /dev/null
+++ b/clippy_lints/src/operators/assign_op_pattern.rs
@@ -0,0 +1,101 @@
+use clippy_utils::binop_traits;
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::snippet_opt;
+use clippy_utils::ty::implements_trait;
+use clippy_utils::{eq_expr_value, trait_ref_of_method};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_hir::intravisit::{walk_expr, Visitor};
+use rustc_lint::LateContext;
+
+use super::ASSIGN_OP_PATTERN;
+
+pub(super) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx hir::Expr<'_>,
+    assignee: &'tcx hir::Expr<'_>,
+    e: &'tcx hir::Expr<'_>,
+) {
+    if let hir::ExprKind::Binary(op, l, r) = &e.kind {
+        let lint = |assignee: &hir::Expr<'_>, rhs: &hir::Expr<'_>| {
+            let ty = cx.typeck_results().expr_ty(assignee);
+            let rty = cx.typeck_results().expr_ty(rhs);
+            if_chain! {
+                if let Some((_, lang_item)) = binop_traits(op.node);
+                if let Ok(trait_id) = cx.tcx.lang_items().require(lang_item);
+                let parent_fn = cx.tcx.hir().get_parent_item(e.hir_id);
+                if trait_ref_of_method(cx, parent_fn)
+                    .map_or(true, |t| t.path.res.def_id() != trait_id);
+                if implements_trait(cx, ty, trait_id, &[rty.into()]);
+                then {
+                    span_lint_and_then(
+                        cx,
+                        ASSIGN_OP_PATTERN,
+                        expr.span,
+                        "manual implementation of an assign operation",
+                        |diag| {
+                            if let (Some(snip_a), Some(snip_r)) =
+                                (snippet_opt(cx, assignee.span), snippet_opt(cx, rhs.span))
+                            {
+                                diag.span_suggestion(
+                                    expr.span,
+                                    "replace it with",
+                                    format!("{} {}= {}", snip_a, op.node.as_str(), snip_r),
+                                    Applicability::MachineApplicable,
+                                );
+                            }
+                        },
+                    );
+                }
+            }
+        };
+
+        let mut visitor = ExprVisitor {
+            assignee,
+            counter: 0,
+            cx,
+        };
+
+        walk_expr(&mut visitor, e);
+
+        if visitor.counter == 1 {
+            // a = a op b
+            if eq_expr_value(cx, assignee, l) {
+                lint(assignee, r);
+            }
+            // a = b commutative_op a
+            // Limited to primitive type as these ops are know to be commutative
+            if eq_expr_value(cx, assignee, r) && cx.typeck_results().expr_ty(assignee).is_primitive_ty() {
+                match op.node {
+                    hir::BinOpKind::Add
+                    | hir::BinOpKind::Mul
+                    | hir::BinOpKind::And
+                    | hir::BinOpKind::Or
+                    | hir::BinOpKind::BitXor
+                    | hir::BinOpKind::BitAnd
+                    | hir::BinOpKind::BitOr => {
+                        lint(assignee, l);
+                    },
+                    _ => {},
+                }
+            }
+        }
+    }
+}
+
+struct ExprVisitor<'a, 'tcx> {
+    assignee: &'a hir::Expr<'a>,
+    counter: u8,
+    cx: &'a LateContext<'tcx>,
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for ExprVisitor<'a, 'tcx> {
+    fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
+        if eq_expr_value(self.cx, self.assignee, expr) {
+            self.counter += 1;
+        }
+
+        walk_expr(self, expr);
+    }
+}
diff --git a/clippy_lints/src/bit_mask.rs b/clippy_lints/src/operators/bit_mask.rs
index dc7e400fdc2..74387fbc87b 100644
--- a/clippy_lints/src/bit_mask.rs
+++ b/clippy_lints/src/operators/bit_mask.rs
@@ -1,161 +1,23 @@
 use clippy_utils::consts::{constant, Constant};
-use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
-use clippy_utils::sugg::Sugg;
-use rustc_ast::ast::LitKind;
-use rustc_errors::Applicability;
+use clippy_utils::diagnostics::span_lint;
 use rustc_hir::{BinOpKind, Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_lint::LateContext;
 use rustc_span::source_map::Span;
 
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for incompatible bit masks in comparisons.
-    ///
-    /// The formula for detecting if an expression of the type `_ <bit_op> m
-    /// <cmp_op> c` (where `<bit_op>` is one of {`&`, `|`} and `<cmp_op>` is one of
-    /// {`!=`, `>=`, `>`, `!=`, `>=`, `>`}) can be determined from the following
-    /// table:
-    ///
-    /// |Comparison  |Bit Op|Example      |is always|Formula               |
-    /// |------------|------|-------------|---------|----------------------|
-    /// |`==` or `!=`| `&`  |`x & 2 == 3` |`false`  |`c & m != c`          |
-    /// |`<`  or `>=`| `&`  |`x & 2 < 3`  |`true`   |`m < c`               |
-    /// |`>`  or `<=`| `&`  |`x & 1 > 1`  |`false`  |`m <= c`              |
-    /// |`==` or `!=`| `\|` |`x \| 1 == 0`|`false`  |`c \| m != c`         |
-    /// |`<`  or `>=`| `\|` |`x \| 1 < 1` |`false`  |`m >= c`              |
-    /// |`<=` or `>` | `\|` |`x \| 1 > 0` |`true`   |`m > c`               |
-    ///
-    /// ### Why is this bad?
-    /// If the bits that the comparison cares about are always
-    /// set to zero or one by the bit mask, the comparison is constant `true` or
-    /// `false` (depending on mask, compared value, and operators).
-    ///
-    /// So the code is actively misleading, and the only reason someone would write
-    /// this intentionally is to win an underhanded Rust contest or create a
-    /// test-case for this lint.
-    ///
-    /// ### Example
-    /// ```rust
-    /// # let x = 1;
-    /// if (x & 1 == 2) { }
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub BAD_BIT_MASK,
-    correctness,
-    "expressions of the form `_ & mask == select` that will only ever return `true` or `false`"
-}
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for bit masks in comparisons which can be removed
-    /// without changing the outcome. The basic structure can be seen in the
-    /// following table:
-    ///
-    /// |Comparison| Bit Op   |Example     |equals |
-    /// |----------|----------|------------|-------|
-    /// |`>` / `<=`|`\|` / `^`|`x \| 2 > 3`|`x > 3`|
-    /// |`<` / `>=`|`\|` / `^`|`x ^ 1 < 4` |`x < 4`|
-    ///
-    /// ### Why is this bad?
-    /// Not equally evil as [`bad_bit_mask`](#bad_bit_mask),
-    /// but still a bit misleading, because the bit mask is ineffective.
-    ///
-    /// ### Known problems
-    /// False negatives: This lint will only match instances
-    /// where we have figured out the math (which is for a power-of-two compared
-    /// value). This means things like `x | 1 >= 7` (which would be better written
-    /// as `x >= 6`) will not be reported (but bit masks like this are fairly
-    /// uncommon).
-    ///
-    /// ### Example
-    /// ```rust
-    /// # let x = 1;
-    /// if (x | 1 > 3) {  }
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub INEFFECTIVE_BIT_MASK,
-    correctness,
-    "expressions where a bit mask will be rendered useless by a comparison, e.g., `(x | 1) > 2`"
-}
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for bit masks that can be replaced by a call
-    /// to `trailing_zeros`
-    ///
-    /// ### Why is this bad?
-    /// `x.trailing_zeros() > 4` is much clearer than `x & 15
-    /// == 0`
-    ///
-    /// ### Known problems
-    /// llvm generates better code for `x & 15 == 0` on x86
-    ///
-    /// ### Example
-    /// ```rust
-    /// # let x = 1;
-    /// if x & 0b1111 == 0 { }
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub VERBOSE_BIT_MASK,
-    pedantic,
-    "expressions where a bit mask is less readable than the corresponding method call"
-}
+use super::{BAD_BIT_MASK, INEFFECTIVE_BIT_MASK};
 
-#[derive(Copy, Clone)]
-pub struct BitMask {
-    verbose_bit_mask_threshold: u64,
-}
-
-impl BitMask {
-    #[must_use]
-    pub fn new(verbose_bit_mask_threshold: u64) -> Self {
-        Self {
-            verbose_bit_mask_threshold,
-        }
-    }
-}
-
-impl_lint_pass!(BitMask => [BAD_BIT_MASK, INEFFECTIVE_BIT_MASK, VERBOSE_BIT_MASK]);
-
-impl<'tcx> LateLintPass<'tcx> for BitMask {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
-        if let ExprKind::Binary(cmp, left, right) = &e.kind {
-            if cmp.node.is_comparison() {
-                if let Some(cmp_opt) = fetch_int_literal(cx, right) {
-                    check_compare(cx, left, cmp.node, cmp_opt, e.span);
-                } else if let Some(cmp_val) = fetch_int_literal(cx, left) {
-                    check_compare(cx, right, invert_cmp(cmp.node), cmp_val, e.span);
-                }
-            }
-        }
-
-        if let ExprKind::Binary(op, left, right) = &e.kind
-            && BinOpKind::Eq == op.node
-            && let ExprKind::Binary(op1, left1, right1) = &left.kind
-            && BinOpKind::BitAnd == op1.node
-            && let ExprKind::Lit(lit) = &right1.kind
-            && let LitKind::Int(n, _) = lit.node
-            && let ExprKind::Lit(lit1) = &right.kind
-            && let LitKind::Int(0, _) = lit1.node
-            && n.leading_zeros() == n.count_zeros()
-            && n > u128::from(self.verbose_bit_mask_threshold)
-        {
-            span_lint_and_then(
-                cx,
-                VERBOSE_BIT_MASK,
-                e.span,
-                "bit mask could be simplified with a call to `trailing_zeros`",
-                |diag| {
-                    let sugg = Sugg::hir(cx, left1, "...").maybe_par();
-                    diag.span_suggestion(
-                        e.span,
-                        "try",
-                        format!("{}.trailing_zeros() >= {}", sugg, n.count_ones()),
-                        Applicability::MaybeIncorrect,
-                    );
-                },
-            );
+pub(super) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    e: &'tcx Expr<'_>,
+    op: BinOpKind,
+    left: &'tcx Expr<'_>,
+    right: &'tcx Expr<'_>,
+) {
+    if op.is_comparison() {
+        if let Some(cmp_opt) = fetch_int_literal(cx, right) {
+            check_compare(cx, left, op, cmp_opt, e.span);
+        } else if let Some(cmp_val) = fetch_int_literal(cx, left) {
+            check_compare(cx, right, invert_cmp(op), cmp_val, e.span);
         }
     }
 }
diff --git a/clippy_lints/src/operators/cmp_nan.rs b/clippy_lints/src/operators/cmp_nan.rs
new file mode 100644
index 00000000000..786ae1552ad
--- /dev/null
+++ b/clippy_lints/src/operators/cmp_nan.rs
@@ -0,0 +1,30 @@
+use clippy_utils::consts::{constant, Constant};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::in_constant;
+use rustc_hir::{BinOpKind, Expr};
+use rustc_lint::LateContext;
+
+use super::CMP_NAN;
+
+pub(super) fn check(cx: &LateContext<'_>, e: &Expr<'_>, op: BinOpKind, lhs: &Expr<'_>, rhs: &Expr<'_>) {
+    if op.is_comparison() && !in_constant(cx, e.hir_id) && (is_nan(cx, lhs) || is_nan(cx, rhs)) {
+        span_lint(
+            cx,
+            CMP_NAN,
+            e.span,
+            "doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead",
+        );
+    }
+}
+
+fn is_nan(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
+    if let Some((value, _)) = constant(cx, cx.typeck_results(), e) {
+        match value {
+            Constant::F32(num) => num.is_nan(),
+            Constant::F64(num) => num.is_nan(),
+            _ => false,
+        }
+    } else {
+        false
+    }
+}
diff --git a/clippy_lints/src/operators/cmp_owned.rs b/clippy_lints/src/operators/cmp_owned.rs
new file mode 100644
index 00000000000..e1f9b5906f6
--- /dev/null
+++ b/clippy_lints/src/operators/cmp_owned.rs
@@ -0,0 +1,147 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::snippet;
+use clippy_utils::ty::{implements_trait, is_copy};
+use clippy_utils::{match_any_def_paths, path_def_id, paths};
+use rustc_errors::Applicability;
+use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp};
+use rustc_lint::LateContext;
+use rustc_middle::ty::Ty;
+use rustc_span::symbol::sym;
+
+use super::CMP_OWNED;
+
+pub(super) fn check(cx: &LateContext<'_>, op: BinOpKind, lhs: &Expr<'_>, rhs: &Expr<'_>) {
+    if op.is_comparison() {
+        check_op(cx, lhs, rhs, true);
+        check_op(cx, rhs, lhs, false);
+    }
+}
+
+#[derive(Default)]
+struct EqImpl {
+    ty_eq_other: bool,
+    other_eq_ty: bool,
+}
+impl EqImpl {
+    fn is_implemented(&self) -> bool {
+        self.ty_eq_other || self.other_eq_ty
+    }
+}
+
+fn symmetric_partial_eq<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, other: Ty<'tcx>) -> Option<EqImpl> {
+    cx.tcx.lang_items().eq_trait().map(|def_id| EqImpl {
+        ty_eq_other: implements_trait(cx, ty, def_id, &[other.into()]),
+        other_eq_ty: implements_trait(cx, other, def_id, &[ty.into()]),
+    })
+}
+
+fn check_op(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) {
+    let typeck = cx.typeck_results();
+    let (arg, arg_span) = match expr.kind {
+        ExprKind::MethodCall(.., [arg], _)
+            if typeck
+                .type_dependent_def_id(expr.hir_id)
+                .and_then(|id| cx.tcx.trait_of_item(id))
+                .map_or(false, |id| {
+                    matches!(cx.tcx.get_diagnostic_name(id), Some(sym::ToString | sym::ToOwned))
+                }) =>
+        {
+            (arg, arg.span)
+        },
+        ExprKind::Call(path, [arg])
+            if path_def_id(cx, path)
+                .and_then(|id| match_any_def_paths(cx, id, &[&paths::FROM_STR_METHOD, &paths::FROM_FROM]))
+                .map_or(false, |idx| match idx {
+                    0 => true,
+                    1 => !is_copy(cx, typeck.expr_ty(expr)),
+                    _ => false,
+                }) =>
+        {
+            (arg, arg.span)
+        },
+        _ => return,
+    };
+
+    let arg_ty = typeck.expr_ty(arg);
+    let other_ty = typeck.expr_ty(other);
+
+    let without_deref = symmetric_partial_eq(cx, arg_ty, other_ty).unwrap_or_default();
+    let with_deref = arg_ty
+        .builtin_deref(true)
+        .and_then(|tam| symmetric_partial_eq(cx, tam.ty, other_ty))
+        .unwrap_or_default();
+
+    if !with_deref.is_implemented() && !without_deref.is_implemented() {
+        return;
+    }
+
+    let other_gets_derefed = matches!(other.kind, ExprKind::Unary(UnOp::Deref, _));
+
+    let lint_span = if other_gets_derefed {
+        expr.span.to(other.span)
+    } else {
+        expr.span
+    };
+
+    span_lint_and_then(
+        cx,
+        CMP_OWNED,
+        lint_span,
+        "this creates an owned instance just for comparison",
+        |diag| {
+            // This also catches `PartialEq` implementations that call `to_owned`.
+            if other_gets_derefed {
+                diag.span_label(lint_span, "try implementing the comparison without allocating");
+                return;
+            }
+
+            let arg_snip = snippet(cx, arg_span, "..");
+            let expr_snip;
+            let eq_impl;
+            if with_deref.is_implemented() {
+                expr_snip = format!("*{}", arg_snip);
+                eq_impl = with_deref;
+            } else {
+                expr_snip = arg_snip.to_string();
+                eq_impl = without_deref;
+            };
+
+            let span;
+            let hint;
+            if (eq_impl.ty_eq_other && left) || (eq_impl.other_eq_ty && !left) {
+                span = expr.span;
+                hint = expr_snip;
+            } else {
+                span = expr.span.to(other.span);
+
+                let cmp_span = if other.span < expr.span {
+                    other.span.between(expr.span)
+                } else {
+                    expr.span.between(other.span)
+                };
+                if eq_impl.ty_eq_other {
+                    hint = format!(
+                        "{}{}{}",
+                        expr_snip,
+                        snippet(cx, cmp_span, ".."),
+                        snippet(cx, other.span, "..")
+                    );
+                } else {
+                    hint = format!(
+                        "{}{}{}",
+                        snippet(cx, other.span, ".."),
+                        snippet(cx, cmp_span, ".."),
+                        expr_snip
+                    );
+                }
+            }
+
+            diag.span_suggestion(
+                span,
+                "try",
+                hint,
+                Applicability::MachineApplicable, // snippet
+            );
+        },
+    );
+}
diff --git a/clippy_lints/src/operators/double_comparison.rs b/clippy_lints/src/operators/double_comparison.rs
new file mode 100644
index 00000000000..56a86d0ffa2
--- /dev/null
+++ b/clippy_lints/src/operators/double_comparison.rs
@@ -0,0 +1,54 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::eq_expr_value;
+use clippy_utils::source::snippet_with_applicability;
+use rustc_errors::Applicability;
+use rustc_hir::{BinOpKind, Expr, ExprKind};
+use rustc_lint::LateContext;
+use rustc_span::source_map::Span;
+
+use super::DOUBLE_COMPARISONS;
+
+#[expect(clippy::similar_names)]
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, op: BinOpKind, lhs: &'tcx Expr<'_>, rhs: &'tcx Expr<'_>, span: Span) {
+    let (lkind, llhs, lrhs, rkind, rlhs, rrhs) = match (&lhs.kind, &rhs.kind) {
+        (ExprKind::Binary(lb, llhs, lrhs), ExprKind::Binary(rb, rlhs, rrhs)) => {
+            (lb.node, llhs, lrhs, rb.node, rlhs, rrhs)
+        },
+        _ => return,
+    };
+    if !(eq_expr_value(cx, llhs, rlhs) && eq_expr_value(cx, lrhs, rrhs)) {
+        return;
+    }
+    macro_rules! lint_double_comparison {
+        ($op:tt) => {{
+            let mut applicability = Applicability::MachineApplicable;
+            let lhs_str = snippet_with_applicability(cx, llhs.span, "", &mut applicability);
+            let rhs_str = snippet_with_applicability(cx, lrhs.span, "", &mut applicability);
+            let sugg = format!("{} {} {}", lhs_str, stringify!($op), rhs_str);
+            span_lint_and_sugg(
+                cx,
+                DOUBLE_COMPARISONS,
+                span,
+                "this binary expression can be simplified",
+                "try",
+                sugg,
+                applicability,
+            );
+        }};
+    }
+    match (op, lkind, rkind) {
+        (BinOpKind::Or, BinOpKind::Eq, BinOpKind::Lt) | (BinOpKind::Or, BinOpKind::Lt, BinOpKind::Eq) => {
+            lint_double_comparison!(<=);
+        },
+        (BinOpKind::Or, BinOpKind::Eq, BinOpKind::Gt) | (BinOpKind::Or, BinOpKind::Gt, BinOpKind::Eq) => {
+            lint_double_comparison!(>=);
+        },
+        (BinOpKind::Or, BinOpKind::Lt, BinOpKind::Gt) | (BinOpKind::Or, BinOpKind::Gt, BinOpKind::Lt) => {
+            lint_double_comparison!(!=);
+        },
+        (BinOpKind::And, BinOpKind::Le, BinOpKind::Ge) | (BinOpKind::And, BinOpKind::Ge, BinOpKind::Le) => {
+            lint_double_comparison!(==);
+        },
+        _ => (),
+    };
+}
diff --git a/clippy_lints/src/operators/duration_subsec.rs b/clippy_lints/src/operators/duration_subsec.rs
new file mode 100644
index 00000000000..0d067d1e196
--- /dev/null
+++ b/clippy_lints/src/operators/duration_subsec.rs
@@ -0,0 +1,44 @@
+use clippy_utils::consts::{constant, Constant};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::is_type_diagnostic_item;
+use rustc_errors::Applicability;
+use rustc_hir::{BinOpKind, Expr, ExprKind};
+use rustc_lint::LateContext;
+use rustc_span::sym;
+
+use super::DURATION_SUBSEC;
+
+pub(crate) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'_>,
+    op: BinOpKind,
+    left: &'tcx Expr<'_>,
+    right: &'tcx Expr<'_>,
+) {
+    if op == BinOpKind::Div
+        && let ExprKind::MethodCall(method_path, [self_arg], _) = left.kind
+        && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_arg).peel_refs(), sym::Duration)
+        && let Some((Constant::Int(divisor), _)) = constant(cx, cx.typeck_results(), right)
+    {
+        let suggested_fn = match (method_path.ident.as_str(), divisor) {
+            ("subsec_micros", 1_000) | ("subsec_nanos", 1_000_000) => "subsec_millis",
+            ("subsec_nanos", 1_000) => "subsec_micros",
+            _ => return,
+        };
+        let mut applicability = Applicability::MachineApplicable;
+        span_lint_and_sugg(
+            cx,
+            DURATION_SUBSEC,
+            expr.span,
+            &format!("calling `{}()` is more concise than this calculation", suggested_fn),
+            "try",
+            format!(
+                "{}.{}()",
+                snippet_with_applicability(cx, self_arg.span, "_", &mut applicability),
+                suggested_fn
+            ),
+            applicability,
+        );
+    }
+}
diff --git a/clippy_lints/src/operators/eq_op.rs b/clippy_lints/src/operators/eq_op.rs
new file mode 100644
index 00000000000..44cf0bb0612
--- /dev/null
+++ b/clippy_lints/src/operators/eq_op.rs
@@ -0,0 +1,45 @@
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::macros::{find_assert_eq_args, first_node_macro_backtrace};
+use clippy_utils::{ast_utils::is_useless_with_eq_exprs, eq_expr_value, is_in_test_function};
+use rustc_hir::{BinOpKind, Expr};
+use rustc_lint::LateContext;
+
+use super::EQ_OP;
+
+pub(crate) fn check_assert<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
+    if let Some((macro_call, macro_name))
+        = first_node_macro_backtrace(cx, e).find_map(|macro_call| {
+            let name = cx.tcx.item_name(macro_call.def_id);
+            matches!(name.as_str(), "assert_eq" | "assert_ne" | "debug_assert_eq" | "debug_assert_ne")
+                .then(|| (macro_call, name))
+        })
+        && let Some((lhs, rhs, _)) = find_assert_eq_args(cx, e, macro_call.expn)
+        && eq_expr_value(cx, lhs, rhs)
+        && macro_call.is_local()
+        && !is_in_test_function(cx.tcx, e.hir_id)
+    {
+        span_lint(
+            cx,
+            EQ_OP,
+            lhs.span.to(rhs.span),
+            &format!("identical args used in this `{}!` macro call", macro_name),
+        );
+    }
+}
+
+pub(crate) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    e: &'tcx Expr<'_>,
+    op: BinOpKind,
+    left: &'tcx Expr<'_>,
+    right: &'tcx Expr<'_>,
+) {
+    if is_useless_with_eq_exprs(op.into()) && eq_expr_value(cx, left, right) && !is_in_test_function(cx.tcx, e.hir_id) {
+        span_lint(
+            cx,
+            EQ_OP,
+            e.span,
+            &format!("equal expressions as operands to `{}`", op.as_str()),
+        );
+    }
+}
diff --git a/clippy_lints/src/operators/erasing_op.rs b/clippy_lints/src/operators/erasing_op.rs
new file mode 100644
index 00000000000..066e08f3bd4
--- /dev/null
+++ b/clippy_lints/src/operators/erasing_op.rs
@@ -0,0 +1,53 @@
+use clippy_utils::consts::{constant_simple, Constant};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::ty::same_type_and_consts;
+
+use rustc_hir::{BinOpKind, Expr};
+use rustc_lint::LateContext;
+use rustc_middle::ty::TypeckResults;
+
+use super::ERASING_OP;
+
+pub(super) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    e: &'tcx Expr<'_>,
+    op: BinOpKind,
+    left: &'tcx Expr<'_>,
+    right: &'tcx Expr<'_>,
+) {
+    let tck = cx.typeck_results();
+    match op {
+        BinOpKind::Mul | BinOpKind::BitAnd => {
+            check_op(cx, tck, left, right, e);
+            check_op(cx, tck, right, left, e);
+        },
+        BinOpKind::Div => check_op(cx, tck, left, right, e),
+        _ => (),
+    }
+}
+
+fn different_types(tck: &TypeckResults<'_>, input: &Expr<'_>, output: &Expr<'_>) -> bool {
+    let input_ty = tck.expr_ty(input).peel_refs();
+    let output_ty = tck.expr_ty(output).peel_refs();
+    !same_type_and_consts(input_ty, output_ty)
+}
+
+fn check_op<'tcx>(
+    cx: &LateContext<'tcx>,
+    tck: &TypeckResults<'tcx>,
+    op: &Expr<'tcx>,
+    other: &Expr<'tcx>,
+    parent: &Expr<'tcx>,
+) {
+    if constant_simple(cx, tck, op) == Some(Constant::Int(0)) {
+        if different_types(tck, other, parent) {
+            return;
+        }
+        span_lint(
+            cx,
+            ERASING_OP,
+            parent.span,
+            "this operation will always return zero. This is likely not the intended outcome",
+        );
+    }
+}
diff --git a/clippy_lints/src/operators/float_cmp.rs b/clippy_lints/src/operators/float_cmp.rs
new file mode 100644
index 00000000000..0ef793443ff
--- /dev/null
+++ b/clippy_lints/src/operators/float_cmp.rs
@@ -0,0 +1,139 @@
+use clippy_utils::consts::{constant, Constant};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::get_item_name;
+use clippy_utils::sugg::Sugg;
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp};
+use rustc_lint::LateContext;
+use rustc_middle::ty;
+
+use super::{FLOAT_CMP, FLOAT_CMP_CONST};
+
+pub(crate) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'_>,
+    op: BinOpKind,
+    left: &'tcx Expr<'_>,
+    right: &'tcx Expr<'_>,
+) {
+    if (op == BinOpKind::Eq || op == BinOpKind::Ne) && (is_float(cx, left) || is_float(cx, right)) {
+        if is_allowed(cx, left) || is_allowed(cx, right) {
+            return;
+        }
+
+        // Allow comparing the results of signum()
+        if is_signum(cx, left) && is_signum(cx, right) {
+            return;
+        }
+
+        if let Some(name) = get_item_name(cx, expr) {
+            let name = name.as_str();
+            if name == "eq" || name == "ne" || name == "is_nan" || name.starts_with("eq_") || name.ends_with("_eq") {
+                return;
+            }
+        }
+        let is_comparing_arrays = is_array(cx, left) || is_array(cx, right);
+        let (lint, msg) = get_lint_and_message(
+            is_named_constant(cx, left) || is_named_constant(cx, right),
+            is_comparing_arrays,
+        );
+        span_lint_and_then(cx, lint, expr.span, msg, |diag| {
+            let lhs = Sugg::hir(cx, left, "..");
+            let rhs = Sugg::hir(cx, right, "..");
+
+            if !is_comparing_arrays {
+                diag.span_suggestion(
+                    expr.span,
+                    "consider comparing them within some margin of error",
+                    format!(
+                        "({}).abs() {} error_margin",
+                        lhs - rhs,
+                        if op == BinOpKind::Eq { '<' } else { '>' }
+                    ),
+                    Applicability::HasPlaceholders, // snippet
+                );
+            }
+            diag.note("`f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`");
+        });
+    }
+}
+
+fn get_lint_and_message(
+    is_comparing_constants: bool,
+    is_comparing_arrays: bool,
+) -> (&'static rustc_lint::Lint, &'static str) {
+    if is_comparing_constants {
+        (
+            FLOAT_CMP_CONST,
+            if is_comparing_arrays {
+                "strict comparison of `f32` or `f64` constant arrays"
+            } else {
+                "strict comparison of `f32` or `f64` constant"
+            },
+        )
+    } else {
+        (
+            FLOAT_CMP,
+            if is_comparing_arrays {
+                "strict comparison of `f32` or `f64` arrays"
+            } else {
+                "strict comparison of `f32` or `f64`"
+            },
+        )
+    }
+}
+
+fn is_named_constant<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
+    if let Some((_, res)) = constant(cx, cx.typeck_results(), expr) {
+        res
+    } else {
+        false
+    }
+}
+
+fn is_allowed<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
+    match constant(cx, cx.typeck_results(), expr) {
+        Some((Constant::F32(f), _)) => f == 0.0 || f.is_infinite(),
+        Some((Constant::F64(f), _)) => f == 0.0 || f.is_infinite(),
+        Some((Constant::Vec(vec), _)) => vec.iter().all(|f| match f {
+            Constant::F32(f) => *f == 0.0 || (*f).is_infinite(),
+            Constant::F64(f) => *f == 0.0 || (*f).is_infinite(),
+            _ => false,
+        }),
+        _ => false,
+    }
+}
+
+// Return true if `expr` is the result of `signum()` invoked on a float value.
+fn is_signum(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+    // The negation of a signum is still a signum
+    if let ExprKind::Unary(UnOp::Neg, child_expr) = expr.kind {
+        return is_signum(cx, child_expr);
+    }
+
+    if_chain! {
+        if let ExprKind::MethodCall(method_name, [ref self_arg, ..], _) = expr.kind;
+        if sym!(signum) == method_name.ident.name;
+        // Check that the receiver of the signum() is a float (expressions[0] is the receiver of
+        // the method call)
+        then {
+            return is_float(cx, self_arg);
+        }
+    }
+    false
+}
+
+fn is_float(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+    let value = &cx.typeck_results().expr_ty(expr).peel_refs().kind();
+
+    if let ty::Array(arr_ty, _) = value {
+        return matches!(arr_ty.kind(), ty::Float(_));
+    };
+
+    matches!(value, ty::Float(_))
+}
+
+fn is_array(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+    matches!(&cx.typeck_results().expr_ty(expr).peel_refs().kind(), ty::Array(_, _))
+}
diff --git a/clippy_lints/src/operators/float_equality_without_abs.rs b/clippy_lints/src/operators/float_equality_without_abs.rs
new file mode 100644
index 00000000000..a0a8b6aabd9
--- /dev/null
+++ b/clippy_lints/src/operators/float_equality_without_abs.rs
@@ -0,0 +1,71 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::{match_def_path, paths, sugg};
+use if_chain::if_chain;
+use rustc_ast::util::parser::AssocOp;
+use rustc_errors::Applicability;
+use rustc_hir::def::{DefKind, Res};
+use rustc_hir::{BinOpKind, Expr, ExprKind};
+use rustc_lint::LateContext;
+use rustc_middle::ty;
+use rustc_span::source_map::Spanned;
+
+use super::FLOAT_EQUALITY_WITHOUT_ABS;
+
+pub(crate) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'_>,
+    op: BinOpKind,
+    lhs: &'tcx Expr<'_>,
+    rhs: &'tcx Expr<'_>,
+) {
+    let (lhs, rhs) = match op {
+        BinOpKind::Lt => (lhs, rhs),
+        BinOpKind::Gt => (rhs, lhs),
+        _ => return,
+    };
+
+    if_chain! {
+        // left hand side is a subtraction
+        if let ExprKind::Binary(
+            Spanned {
+                node: BinOpKind::Sub,
+                ..
+            },
+            val_l,
+            val_r,
+        ) = lhs.kind;
+
+        // right hand side matches either f32::EPSILON or f64::EPSILON
+        if let ExprKind::Path(ref epsilon_path) = rhs.kind;
+        if let Res::Def(DefKind::AssocConst, def_id) = cx.qpath_res(epsilon_path, rhs.hir_id);
+        if match_def_path(cx, def_id, &paths::F32_EPSILON) || match_def_path(cx, def_id, &paths::F64_EPSILON);
+
+        // values of the subtractions on the left hand side are of the type float
+        let t_val_l = cx.typeck_results().expr_ty(val_l);
+        let t_val_r = cx.typeck_results().expr_ty(val_r);
+        if let ty::Float(_) = t_val_l.kind();
+        if let ty::Float(_) = t_val_r.kind();
+
+        then {
+            let sug_l = sugg::Sugg::hir(cx, val_l, "..");
+            let sug_r = sugg::Sugg::hir(cx, val_r, "..");
+            // format the suggestion
+            let suggestion = format!("{}.abs()", sugg::make_assoc(AssocOp::Subtract, &sug_l, &sug_r).maybe_par());
+            // spans the lint
+            span_lint_and_then(
+                cx,
+                FLOAT_EQUALITY_WITHOUT_ABS,
+                expr.span,
+                "float equality check without `.abs()`",
+                | diag | {
+                    diag.span_suggestion(
+                        lhs.span,
+                        "add `.abs()`",
+                        suggestion,
+                        Applicability::MaybeIncorrect,
+                    );
+                }
+            );
+        }
+    }
+}
diff --git a/clippy_lints/src/identity_op.rs b/clippy_lints/src/operators/identity_op.rs
index 419ea5a6811..b48d6c4e2e2 100644
--- a/clippy_lints/src/identity_op.rs
+++ b/clippy_lints/src/operators/identity_op.rs
@@ -3,61 +3,40 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::{clip, unsext};
 use rustc_errors::Applicability;
-use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, Node};
-use rustc_lint::{LateContext, LateLintPass};
+use rustc_hir::{BinOpKind, Expr, ExprKind, Node};
+use rustc_lint::LateContext;
 use rustc_middle::ty;
-use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::source_map::Span;
 
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for identity operations, e.g., `x + 0`.
-    ///
-    /// ### Why is this bad?
-    /// This code can be removed without changing the
-    /// meaning. So it just obscures what's going on. Delete it mercilessly.
-    ///
-    /// ### Example
-    /// ```rust
-    /// # let x = 1;
-    /// x / 1 + 0 * 1 - 0 | 0;
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub IDENTITY_OP,
-    complexity,
-    "using identity operations, e.g., `x + 0` or `y / 1`"
-}
-
-declare_lint_pass!(IdentityOp => [IDENTITY_OP]);
+use super::IDENTITY_OP;
 
-impl<'tcx> LateLintPass<'tcx> for IdentityOp {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if expr.span.from_expansion() {
-            return;
-        }
-        if let ExprKind::Binary(cmp, left, right) = &expr.kind {
-            if !is_allowed(cx, *cmp, left, right) {
-                match cmp.node {
-                    BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => {
-                        check(cx, left, 0, expr.span, right.span, needs_parenthesis(cx, expr, right));
-                        check(cx, right, 0, expr.span, left.span, Parens::Unneeded);
-                    },
-                    BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => {
-                        check(cx, right, 0, expr.span, left.span, Parens::Unneeded);
-                    },
-                    BinOpKind::Mul => {
-                        check(cx, left, 1, expr.span, right.span, needs_parenthesis(cx, expr, right));
-                        check(cx, right, 1, expr.span, left.span, Parens::Unneeded);
-                    },
-                    BinOpKind::Div => check(cx, right, 1, expr.span, left.span, Parens::Unneeded),
-                    BinOpKind::BitAnd => {
-                        check(cx, left, -1, expr.span, right.span, needs_parenthesis(cx, expr, right));
-                        check(cx, right, -1, expr.span, left.span, Parens::Unneeded);
-                    },
-                    BinOpKind::Rem => check_remainder(cx, left, right, expr.span, left.span),
-                    _ => (),
-                }
-            }
+pub(crate) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'_>,
+    op: BinOpKind,
+    left: &'tcx Expr<'_>,
+    right: &'tcx Expr<'_>,
+) {
+    if !is_allowed(cx, op, left, right) {
+        match op {
+            BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => {
+                check_op(cx, left, 0, expr.span, right.span, needs_parenthesis(cx, expr, right));
+                check_op(cx, right, 0, expr.span, left.span, Parens::Unneeded);
+            },
+            BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => {
+                check_op(cx, right, 0, expr.span, left.span, Parens::Unneeded);
+            },
+            BinOpKind::Mul => {
+                check_op(cx, left, 1, expr.span, right.span, needs_parenthesis(cx, expr, right));
+                check_op(cx, right, 1, expr.span, left.span, Parens::Unneeded);
+            },
+            BinOpKind::Div => check_op(cx, right, 1, expr.span, left.span, Parens::Unneeded),
+            BinOpKind::BitAnd => {
+                check_op(cx, left, -1, expr.span, right.span, needs_parenthesis(cx, expr, right));
+                check_op(cx, right, -1, expr.span, left.span, Parens::Unneeded);
+            },
+            BinOpKind::Rem => check_remainder(cx, left, right, expr.span, left.span),
+            _ => (),
         }
     }
 }
@@ -108,12 +87,12 @@ fn needs_parenthesis(cx: &LateContext<'_>, binary: &Expr<'_>, right: &Expr<'_>)
     Parens::Needed
 }
 
-fn is_allowed(cx: &LateContext<'_>, cmp: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> bool {
+fn is_allowed(cx: &LateContext<'_>, cmp: BinOpKind, left: &Expr<'_>, right: &Expr<'_>) -> bool {
     // This lint applies to integers
     !cx.typeck_results().expr_ty(left).peel_refs().is_integral()
         || !cx.typeck_results().expr_ty(right).peel_refs().is_integral()
         // `1 << 0` is a common pattern in bit manipulation code
-        || (cmp.node == BinOpKind::Shl
+        || (cmp == BinOpKind::Shl
             && constant_simple(cx, cx.typeck_results(), right) == Some(Constant::Int(0))
             && constant_simple(cx, cx.typeck_results(), left) == Some(Constant::Int(1)))
 }
@@ -130,7 +109,7 @@ fn check_remainder(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>, span
     }
 }
 
-fn check(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span, parens: Parens) {
+fn check_op(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span, parens: Parens) {
     if let Some(Constant::Int(v)) = constant_simple(cx, cx.typeck_results(), e).map(Constant::peel_refs) {
         let check = match *cx.typeck_results().expr_ty(e).peel_refs().kind() {
             ty::Int(ity) => unsext(cx.tcx, -1_i128, ity),
diff --git a/clippy_lints/src/operators/integer_division.rs b/clippy_lints/src/operators/integer_division.rs
new file mode 100644
index 00000000000..631d10f4a72
--- /dev/null
+++ b/clippy_lints/src/operators/integer_division.rs
@@ -0,0 +1,27 @@
+use clippy_utils::diagnostics::span_lint_and_help;
+use rustc_hir as hir;
+use rustc_lint::LateContext;
+
+use super::INTEGER_DIVISION;
+
+pub(crate) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx hir::Expr<'_>,
+    op: hir::BinOpKind,
+    left: &'tcx hir::Expr<'_>,
+    right: &'tcx hir::Expr<'_>,
+) {
+    if op == hir::BinOpKind::Div
+        && cx.typeck_results().expr_ty(left).is_integral()
+        && cx.typeck_results().expr_ty(right).is_integral()
+    {
+        span_lint_and_help(
+            cx,
+            INTEGER_DIVISION,
+            expr.span,
+            "integer division",
+            None,
+            "division of integers may cause loss of precision. consider using floats",
+        );
+    }
+}
diff --git a/clippy_lints/src/operators/misrefactored_assign_op.rs b/clippy_lints/src/operators/misrefactored_assign_op.rs
new file mode 100644
index 00000000000..0024384d927
--- /dev/null
+++ b/clippy_lints/src/operators/misrefactored_assign_op.rs
@@ -0,0 +1,84 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::eq_expr_value;
+use clippy_utils::source::snippet_opt;
+use clippy_utils::sugg;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_lint::LateContext;
+
+use super::MISREFACTORED_ASSIGN_OP;
+
+pub(super) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx hir::Expr<'_>,
+    op: hir::BinOpKind,
+    lhs: &'tcx hir::Expr<'_>,
+    rhs: &'tcx hir::Expr<'_>,
+) {
+    if let hir::ExprKind::Binary(binop, l, r) = &rhs.kind {
+        if op != binop.node {
+            return;
+        }
+        // lhs op= l op r
+        if eq_expr_value(cx, lhs, l) {
+            lint_misrefactored_assign_op(cx, expr, op, rhs, lhs, r);
+        }
+        // lhs op= l commutative_op r
+        if is_commutative(op) && eq_expr_value(cx, lhs, r) {
+            lint_misrefactored_assign_op(cx, expr, op, rhs, lhs, l);
+        }
+    }
+}
+
+fn lint_misrefactored_assign_op(
+    cx: &LateContext<'_>,
+    expr: &hir::Expr<'_>,
+    op: hir::BinOpKind,
+    rhs: &hir::Expr<'_>,
+    assignee: &hir::Expr<'_>,
+    rhs_other: &hir::Expr<'_>,
+) {
+    span_lint_and_then(
+        cx,
+        MISREFACTORED_ASSIGN_OP,
+        expr.span,
+        "variable appears on both sides of an assignment operation",
+        |diag| {
+            if let (Some(snip_a), Some(snip_r)) = (snippet_opt(cx, assignee.span), snippet_opt(cx, rhs_other.span)) {
+                let a = &sugg::Sugg::hir(cx, assignee, "..");
+                let r = &sugg::Sugg::hir(cx, rhs, "..");
+                let long = format!("{} = {}", snip_a, sugg::make_binop(op.into(), a, r));
+                diag.span_suggestion(
+                    expr.span,
+                    &format!(
+                        "did you mean `{} = {} {} {}` or `{}`? Consider replacing it with",
+                        snip_a,
+                        snip_a,
+                        op.as_str(),
+                        snip_r,
+                        long
+                    ),
+                    format!("{} {}= {}", snip_a, op.as_str(), snip_r),
+                    Applicability::MaybeIncorrect,
+                );
+                diag.span_suggestion(
+                    expr.span,
+                    "or",
+                    long,
+                    Applicability::MaybeIncorrect, // snippet
+                );
+            }
+        },
+    );
+}
+
+#[must_use]
+fn is_commutative(op: hir::BinOpKind) -> bool {
+    use rustc_hir::BinOpKind::{
+        Add, And, BitAnd, BitOr, BitXor, Div, Eq, Ge, Gt, Le, Lt, Mul, Ne, Or, Rem, Shl, Shr, Sub,
+    };
+    match op {
+        Add | Mul | And | Or | BitXor | BitAnd | BitOr | Eq | Ne => true,
+        Sub | Div | Rem | Shl | Shr | Lt | Le | Ge | Gt => false,
+    }
+}
diff --git a/clippy_lints/src/operators/mod.rs b/clippy_lints/src/operators/mod.rs
new file mode 100644
index 00000000000..35fe405bcf1
--- /dev/null
+++ b/clippy_lints/src/operators/mod.rs
@@ -0,0 +1,849 @@
+use rustc_hir::{Body, Expr, ExprKind, UnOp};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+
+mod absurd_extreme_comparisons;
+mod assign_op_pattern;
+mod bit_mask;
+mod cmp_nan;
+mod cmp_owned;
+mod double_comparison;
+mod duration_subsec;
+mod eq_op;
+mod erasing_op;
+mod float_cmp;
+mod float_equality_without_abs;
+mod identity_op;
+mod integer_division;
+mod misrefactored_assign_op;
+mod modulo_arithmetic;
+mod modulo_one;
+mod needless_bitwise_bool;
+mod numeric_arithmetic;
+mod op_ref;
+mod ptr_eq;
+mod self_assignment;
+mod verbose_bit_mask;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for comparisons where one side of the relation is
+    /// either the minimum or maximum value for its type and warns if it involves a
+    /// case that is always true or always false. Only integer and boolean types are
+    /// checked.
+    ///
+    /// ### Why is this bad?
+    /// An expression like `min <= x` may misleadingly imply
+    /// that it is possible for `x` to be less than the minimum. Expressions like
+    /// `max < x` are probably mistakes.
+    ///
+    /// ### Known problems
+    /// For `usize` the size of the current compile target will
+    /// be assumed (e.g., 64 bits on 64 bit systems). This means code that uses such
+    /// a comparison to detect target pointer width will trigger this lint. One can
+    /// use `mem::sizeof` and compare its value or conditional compilation
+    /// attributes
+    /// like `#[cfg(target_pointer_width = "64")] ..` instead.
+    ///
+    /// ### Example
+    /// ```rust
+    /// let vec: Vec<isize> = Vec::new();
+    /// if vec.len() <= 0 {}
+    /// if 100 > i32::MAX {}
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub ABSURD_EXTREME_COMPARISONS,
+    correctness,
+    "a comparison with a maximum or minimum value that is always true or false"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for integer arithmetic operations which could overflow or panic.
+    ///
+    /// Specifically, checks for any operators (`+`, `-`, `*`, `<<`, etc) which are capable
+    /// of overflowing according to the [Rust
+    /// Reference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#overflow),
+    /// or which can panic (`/`, `%`). No bounds analysis or sophisticated reasoning is
+    /// attempted.
+    ///
+    /// ### Why is this bad?
+    /// Integer overflow will trigger a panic in debug builds or will wrap in
+    /// release mode. Division by zero will cause a panic in either mode. In some applications one
+    /// wants explicitly checked, wrapping or saturating arithmetic.
+    ///
+    /// ### Example
+    /// ```rust
+    /// # let a = 0;
+    /// a + 1;
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub INTEGER_ARITHMETIC,
+    restriction,
+    "any integer arithmetic expression which could overflow or panic"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for float arithmetic.
+    ///
+    /// ### Why is this bad?
+    /// For some embedded systems or kernel development, it
+    /// can be useful to rule out floating-point numbers.
+    ///
+    /// ### Example
+    /// ```rust
+    /// # let a = 0.0;
+    /// a + 1.0;
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub FLOAT_ARITHMETIC,
+    restriction,
+    "any floating-point arithmetic statement"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for `a = a op b` or `a = b commutative_op a`
+    /// patterns.
+    ///
+    /// ### Why is this bad?
+    /// These can be written as the shorter `a op= b`.
+    ///
+    /// ### Known problems
+    /// While forbidden by the spec, `OpAssign` traits may have
+    /// implementations that differ from the regular `Op` impl.
+    ///
+    /// ### Example
+    /// ```rust
+    /// let mut a = 5;
+    /// let b = 0;
+    /// // ...
+    ///
+    /// a = a + b;
+    /// ```
+    ///
+    /// Use instead:
+    /// ```rust
+    /// let mut a = 5;
+    /// let b = 0;
+    /// // ...
+    ///
+    /// a += b;
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub ASSIGN_OP_PATTERN,
+    style,
+    "assigning the result of an operation on a variable to that same variable"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for `a op= a op b` or `a op= b op a` patterns.
+    ///
+    /// ### Why is this bad?
+    /// Most likely these are bugs where one meant to write `a
+    /// op= b`.
+    ///
+    /// ### Known problems
+    /// Clippy cannot know for sure if `a op= a op b` should have
+    /// been `a = a op a op b` or `a = a op b`/`a op= b`. Therefore, it suggests both.
+    /// If `a op= a op b` is really the correct behavior it should be
+    /// written as `a = a op a op b` as it's less confusing.
+    ///
+    /// ### Example
+    /// ```rust
+    /// let mut a = 5;
+    /// let b = 2;
+    /// // ...
+    /// a += a + b;
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub MISREFACTORED_ASSIGN_OP,
+    suspicious,
+    "having a variable on both sides of an assign op"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for incompatible bit masks in comparisons.
+    ///
+    /// The formula for detecting if an expression of the type `_ <bit_op> m
+    /// <cmp_op> c` (where `<bit_op>` is one of {`&`, `|`} and `<cmp_op>` is one of
+    /// {`!=`, `>=`, `>`, `!=`, `>=`, `>`}) can be determined from the following
+    /// table:
+    ///
+    /// |Comparison  |Bit Op|Example      |is always|Formula               |
+    /// |------------|------|-------------|---------|----------------------|
+    /// |`==` or `!=`| `&`  |`x & 2 == 3` |`false`  |`c & m != c`          |
+    /// |`<`  or `>=`| `&`  |`x & 2 < 3`  |`true`   |`m < c`               |
+    /// |`>`  or `<=`| `&`  |`x & 1 > 1`  |`false`  |`m <= c`              |
+    /// |`==` or `!=`| `\|` |`x \| 1 == 0`|`false`  |`c \| m != c`         |
+    /// |`<`  or `>=`| `\|` |`x \| 1 < 1` |`false`  |`m >= c`              |
+    /// |`<=` or `>` | `\|` |`x \| 1 > 0` |`true`   |`m > c`               |
+    ///
+    /// ### Why is this bad?
+    /// If the bits that the comparison cares about are always
+    /// set to zero or one by the bit mask, the comparison is constant `true` or
+    /// `false` (depending on mask, compared value, and operators).
+    ///
+    /// So the code is actively misleading, and the only reason someone would write
+    /// this intentionally is to win an underhanded Rust contest or create a
+    /// test-case for this lint.
+    ///
+    /// ### Example
+    /// ```rust
+    /// # let x = 1;
+    /// if (x & 1 == 2) { }
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub BAD_BIT_MASK,
+    correctness,
+    "expressions of the form `_ & mask == select` that will only ever return `true` or `false`"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for bit masks in comparisons which can be removed
+    /// without changing the outcome. The basic structure can be seen in the
+    /// following table:
+    ///
+    /// |Comparison| Bit Op   |Example     |equals |
+    /// |----------|----------|------------|-------|
+    /// |`>` / `<=`|`\|` / `^`|`x \| 2 > 3`|`x > 3`|
+    /// |`<` / `>=`|`\|` / `^`|`x ^ 1 < 4` |`x < 4`|
+    ///
+    /// ### Why is this bad?
+    /// Not equally evil as [`bad_bit_mask`](#bad_bit_mask),
+    /// but still a bit misleading, because the bit mask is ineffective.
+    ///
+    /// ### Known problems
+    /// False negatives: This lint will only match instances
+    /// where we have figured out the math (which is for a power-of-two compared
+    /// value). This means things like `x | 1 >= 7` (which would be better written
+    /// as `x >= 6`) will not be reported (but bit masks like this are fairly
+    /// uncommon).
+    ///
+    /// ### Example
+    /// ```rust
+    /// # let x = 1;
+    /// if (x | 1 > 3) {  }
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub INEFFECTIVE_BIT_MASK,
+    correctness,
+    "expressions where a bit mask will be rendered useless by a comparison, e.g., `(x | 1) > 2`"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for bit masks that can be replaced by a call
+    /// to `trailing_zeros`
+    ///
+    /// ### Why is this bad?
+    /// `x.trailing_zeros() > 4` is much clearer than `x & 15
+    /// == 0`
+    ///
+    /// ### Known problems
+    /// llvm generates better code for `x & 15 == 0` on x86
+    ///
+    /// ### Example
+    /// ```rust
+    /// # let x = 1;
+    /// if x & 0b1111 == 0 { }
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub VERBOSE_BIT_MASK,
+    pedantic,
+    "expressions where a bit mask is less readable than the corresponding method call"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for double comparisons that could be simplified to a single expression.
+    ///
+    ///
+    /// ### Why is this bad?
+    /// Readability.
+    ///
+    /// ### Example
+    /// ```rust
+    /// # let x = 1;
+    /// # let y = 2;
+    /// if x == y || x < y {}
+    /// ```
+    ///
+    /// Use instead:
+    ///
+    /// ```rust
+    /// # let x = 1;
+    /// # let y = 2;
+    /// if x <= y {}
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub DOUBLE_COMPARISONS,
+    complexity,
+    "unnecessary double comparisons that can be simplified"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for calculation of subsecond microseconds or milliseconds
+    /// from other `Duration` methods.
+    ///
+    /// ### Why is this bad?
+    /// It's more concise to call `Duration::subsec_micros()` or
+    /// `Duration::subsec_millis()` than to calculate them.
+    ///
+    /// ### Example
+    /// ```rust
+    /// # use std::time::Duration;
+    /// # let duration = Duration::new(5, 0);
+    /// let micros = duration.subsec_nanos() / 1_000;
+    /// let millis = duration.subsec_nanos() / 1_000_000;
+    /// ```
+    ///
+    /// Use instead:
+    /// ```rust
+    /// # use std::time::Duration;
+    /// # let duration = Duration::new(5, 0);
+    /// let micros = duration.subsec_micros();
+    /// let millis = duration.subsec_millis();
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub DURATION_SUBSEC,
+    complexity,
+    "checks for calculation of subsecond microseconds or milliseconds"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for equal operands to comparison, logical and
+    /// bitwise, difference and division binary operators (`==`, `>`, etc., `&&`,
+    /// `||`, `&`, `|`, `^`, `-` and `/`).
+    ///
+    /// ### Why is this bad?
+    /// This is usually just a typo or a copy and paste error.
+    ///
+    /// ### Known problems
+    /// False negatives: We had some false positives regarding
+    /// calls (notably [racer](https://github.com/phildawes/racer) had one instance
+    /// of `x.pop() && x.pop()`), so we removed matching any function or method
+    /// calls. We may introduce a list of known pure functions in the future.
+    ///
+    /// ### Example
+    /// ```rust
+    /// # let x = 1;
+    /// if x + 1 == x + 1 {}
+    ///
+    /// // or
+    ///
+    /// # let a = 3;
+    /// # let b = 4;
+    /// assert_eq!(a, a);
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub EQ_OP,
+    correctness,
+    "equal operands on both sides of a comparison or bitwise combination (e.g., `x == x`)"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for arguments to `==` which have their address
+    /// taken to satisfy a bound
+    /// and suggests to dereference the other argument instead
+    ///
+    /// ### Why is this bad?
+    /// It is more idiomatic to dereference the other argument.
+    ///
+    /// ### Example
+    /// ```rust,ignore
+    /// &x == y
+    /// ```
+    ///
+    /// Use instead:
+    /// ```rust,ignore
+    /// x == *y
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub OP_REF,
+    style,
+    "taking a reference to satisfy the type constraints on `==`"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for erasing operations, e.g., `x * 0`.
+    ///
+    /// ### Why is this bad?
+    /// The whole expression can be replaced by zero.
+    /// This is most likely not the intended outcome and should probably be
+    /// corrected
+    ///
+    /// ### Example
+    /// ```rust
+    /// let x = 1;
+    /// 0 / x;
+    /// 0 * x;
+    /// x & 0;
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub ERASING_OP,
+    correctness,
+    "using erasing operations, e.g., `x * 0` or `y & 0`"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for statements of the form `(a - b) < f32::EPSILON` or
+    /// `(a - b) < f64::EPSILON`. Notes the missing `.abs()`.
+    ///
+    /// ### Why is this bad?
+    /// The code without `.abs()` is more likely to have a bug.
+    ///
+    /// ### Known problems
+    /// If the user can ensure that b is larger than a, the `.abs()` is
+    /// technically unnecessary. However, it will make the code more robust and doesn't have any
+    /// large performance implications. If the abs call was deliberately left out for performance
+    /// reasons, it is probably better to state this explicitly in the code, which then can be done
+    /// with an allow.
+    ///
+    /// ### Example
+    /// ```rust
+    /// pub fn is_roughly_equal(a: f32, b: f32) -> bool {
+    ///     (a - b) < f32::EPSILON
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// pub fn is_roughly_equal(a: f32, b: f32) -> bool {
+    ///     (a - b).abs() < f32::EPSILON
+    /// }
+    /// ```
+    #[clippy::version = "1.48.0"]
+    pub FLOAT_EQUALITY_WITHOUT_ABS,
+    suspicious,
+    "float equality check without `.abs()`"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for identity operations, e.g., `x + 0`.
+    ///
+    /// ### Why is this bad?
+    /// This code can be removed without changing the
+    /// meaning. So it just obscures what's going on. Delete it mercilessly.
+    ///
+    /// ### Example
+    /// ```rust
+    /// # let x = 1;
+    /// x / 1 + 0 * 1 - 0 | 0;
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub IDENTITY_OP,
+    complexity,
+    "using identity operations, e.g., `x + 0` or `y / 1`"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for division of integers
+    ///
+    /// ### Why is this bad?
+    /// When outside of some very specific algorithms,
+    /// integer division is very often a mistake because it discards the
+    /// remainder.
+    ///
+    /// ### Example
+    /// ```rust
+    /// let x = 3 / 2;
+    /// println!("{}", x);
+    /// ```
+    ///
+    /// Use instead:
+    /// ```rust
+    /// let x = 3f32 / 2f32;
+    /// println!("{}", x);
+    /// ```
+    #[clippy::version = "1.37.0"]
+    pub INTEGER_DIVISION,
+    restriction,
+    "integer division may cause loss of precision"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for comparisons to NaN.
+    ///
+    /// ### Why is this bad?
+    /// NaN does not compare meaningfully to anything – not
+    /// even itself – so those comparisons are simply wrong.
+    ///
+    /// ### Example
+    /// ```rust
+    /// # let x = 1.0;
+    /// if x == f32::NAN { }
+    /// ```
+    ///
+    /// Use instead:
+    /// ```rust
+    /// # let x = 1.0f32;
+    /// if x.is_nan() { }
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub CMP_NAN,
+    correctness,
+    "comparisons to `NAN`, which will always return false, probably not intended"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for conversions to owned values just for the sake
+    /// of a comparison.
+    ///
+    /// ### Why is this bad?
+    /// The comparison can operate on a reference, so creating
+    /// an owned value effectively throws it away directly afterwards, which is
+    /// needlessly consuming code and heap space.
+    ///
+    /// ### Example
+    /// ```rust
+    /// # let x = "foo";
+    /// # let y = String::from("foo");
+    /// if x.to_owned() == y {}
+    /// ```
+    ///
+    /// Use instead:
+    /// ```rust
+    /// # let x = "foo";
+    /// # let y = String::from("foo");
+    /// if x == y {}
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub CMP_OWNED,
+    perf,
+    "creating owned instances for comparing with others, e.g., `x == \"foo\".to_string()`"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for (in-)equality comparisons on floating-point
+    /// values (apart from zero), except in functions called `*eq*` (which probably
+    /// implement equality for a type involving floats).
+    ///
+    /// ### Why is this bad?
+    /// Floating point calculations are usually imprecise, so
+    /// asking if two values are *exactly* equal is asking for trouble. For a good
+    /// guide on what to do, see [the floating point
+    /// guide](http://www.floating-point-gui.de/errors/comparison).
+    ///
+    /// ### Example
+    /// ```rust
+    /// let x = 1.2331f64;
+    /// let y = 1.2332f64;
+    ///
+    /// if y == 1.23f64 { }
+    /// if y != x {} // where both are floats
+    /// ```
+    ///
+    /// Use instead:
+    /// ```rust
+    /// # let x = 1.2331f64;
+    /// # let y = 1.2332f64;
+    /// let error_margin = f64::EPSILON; // Use an epsilon for comparison
+    /// // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead.
+    /// // let error_margin = std::f64::EPSILON;
+    /// if (y - 1.23f64).abs() < error_margin { }
+    /// if (y - x).abs() > error_margin { }
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub FLOAT_CMP,
+    pedantic,
+    "using `==` or `!=` on float values instead of comparing difference with an epsilon"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for (in-)equality comparisons on floating-point
+    /// value and constant, except in functions called `*eq*` (which probably
+    /// implement equality for a type involving floats).
+    ///
+    /// ### Why is this bad?
+    /// Floating point calculations are usually imprecise, so
+    /// asking if two values are *exactly* equal is asking for trouble. For a good
+    /// guide on what to do, see [the floating point
+    /// guide](http://www.floating-point-gui.de/errors/comparison).
+    ///
+    /// ### Example
+    /// ```rust
+    /// let x: f64 = 1.0;
+    /// const ONE: f64 = 1.00;
+    ///
+    /// if x == ONE { } // where both are floats
+    /// ```
+    ///
+    /// Use instead:
+    /// ```rust
+    /// # let x: f64 = 1.0;
+    /// # const ONE: f64 = 1.00;
+    /// let error_margin = f64::EPSILON; // Use an epsilon for comparison
+    /// // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead.
+    /// // let error_margin = std::f64::EPSILON;
+    /// if (x - ONE).abs() < error_margin { }
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub FLOAT_CMP_CONST,
+    restriction,
+    "using `==` or `!=` on float constants instead of comparing difference with an epsilon"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for getting the remainder of a division by one or minus
+    /// one.
+    ///
+    /// ### Why is this bad?
+    /// The result for a divisor of one can only ever be zero; for
+    /// minus one it can cause panic/overflow (if the left operand is the minimal value of
+    /// the respective integer type) or results in zero. No one will write such code
+    /// deliberately, unless trying to win an Underhanded Rust Contest. Even for that
+    /// contest, it's probably a bad idea. Use something more underhanded.
+    ///
+    /// ### Example
+    /// ```rust
+    /// # let x = 1;
+    /// let a = x % 1;
+    /// let a = x % -1;
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub MODULO_ONE,
+    correctness,
+    "taking a number modulo +/-1, which can either panic/overflow or always returns 0"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for modulo arithmetic.
+    ///
+    /// ### Why is this bad?
+    /// The results of modulo (%) operation might differ
+    /// depending on the language, when negative numbers are involved.
+    /// If you interop with different languages it might be beneficial
+    /// to double check all places that use modulo arithmetic.
+    ///
+    /// For example, in Rust `17 % -3 = 2`, but in Python `17 % -3 = -1`.
+    ///
+    /// ### Example
+    /// ```rust
+    /// let x = -17 % 3;
+    /// ```
+    #[clippy::version = "1.42.0"]
+    pub MODULO_ARITHMETIC,
+    restriction,
+    "any modulo arithmetic statement"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for uses of bitwise and/or operators between booleans, where performance may be improved by using
+    /// a lazy and.
+    ///
+    /// ### Why is this bad?
+    /// The bitwise operators do not support short-circuiting, so it may hinder code performance.
+    /// Additionally, boolean logic "masked" as bitwise logic is not caught by lints like `unnecessary_fold`
+    ///
+    /// ### Known problems
+    /// This lint evaluates only when the right side is determined to have no side effects. At this time, that
+    /// determination is quite conservative.
+    ///
+    /// ### Example
+    /// ```rust
+    /// let (x,y) = (true, false);
+    /// if x & !y {} // where both x and y are booleans
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// let (x,y) = (true, false);
+    /// if x && !y {}
+    /// ```
+    #[clippy::version = "1.54.0"]
+    pub NEEDLESS_BITWISE_BOOL,
+    pedantic,
+    "Boolean expressions that use bitwise rather than lazy operators"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Use `std::ptr::eq` when applicable
+    ///
+    /// ### Why is this bad?
+    /// `ptr::eq` can be used to compare `&T` references
+    /// (which coerce to `*const T` implicitly) by their address rather than
+    /// comparing the values they point to.
+    ///
+    /// ### Example
+    /// ```rust
+    /// let a = &[1, 2, 3];
+    /// let b = &[1, 2, 3];
+    ///
+    /// assert!(a as *const _ as usize == b as *const _ as usize);
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// let a = &[1, 2, 3];
+    /// let b = &[1, 2, 3];
+    ///
+    /// assert!(std::ptr::eq(a, b));
+    /// ```
+    #[clippy::version = "1.49.0"]
+    pub PTR_EQ,
+    style,
+    "use `std::ptr::eq` when comparing raw pointers"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for explicit self-assignments.
+    ///
+    /// ### Why is this bad?
+    /// Self-assignments are redundant and unlikely to be
+    /// intentional.
+    ///
+    /// ### Known problems
+    /// If expression contains any deref coercions or
+    /// indexing operations they are assumed not to have any side effects.
+    ///
+    /// ### Example
+    /// ```rust
+    /// struct Event {
+    ///     x: i32,
+    /// }
+    ///
+    /// fn copy_position(a: &mut Event, b: &Event) {
+    ///     a.x = a.x;
+    /// }
+    /// ```
+    ///
+    /// Should be:
+    /// ```rust
+    /// struct Event {
+    ///     x: i32,
+    /// }
+    ///
+    /// fn copy_position(a: &mut Event, b: &Event) {
+    ///     a.x = b.x;
+    /// }
+    /// ```
+    #[clippy::version = "1.48.0"]
+    pub SELF_ASSIGNMENT,
+    correctness,
+    "explicit self-assignment"
+}
+
+pub struct Operators {
+    arithmetic_context: numeric_arithmetic::Context,
+    verbose_bit_mask_threshold: u64,
+}
+impl_lint_pass!(Operators => [
+    ABSURD_EXTREME_COMPARISONS,
+    INTEGER_ARITHMETIC,
+    FLOAT_ARITHMETIC,
+    ASSIGN_OP_PATTERN,
+    MISREFACTORED_ASSIGN_OP,
+    BAD_BIT_MASK,
+    INEFFECTIVE_BIT_MASK,
+    VERBOSE_BIT_MASK,
+    DOUBLE_COMPARISONS,
+    DURATION_SUBSEC,
+    EQ_OP,
+    OP_REF,
+    ERASING_OP,
+    FLOAT_EQUALITY_WITHOUT_ABS,
+    IDENTITY_OP,
+    INTEGER_DIVISION,
+    CMP_NAN,
+    CMP_OWNED,
+    FLOAT_CMP,
+    FLOAT_CMP_CONST,
+    MODULO_ONE,
+    MODULO_ARITHMETIC,
+    NEEDLESS_BITWISE_BOOL,
+    PTR_EQ,
+    SELF_ASSIGNMENT,
+]);
+impl Operators {
+    pub fn new(verbose_bit_mask_threshold: u64) -> Self {
+        Self {
+            arithmetic_context: numeric_arithmetic::Context::default(),
+            verbose_bit_mask_threshold,
+        }
+    }
+}
+impl<'tcx> LateLintPass<'tcx> for Operators {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
+        eq_op::check_assert(cx, e);
+        match e.kind {
+            ExprKind::Binary(op, lhs, rhs) => {
+                if !e.span.from_expansion() {
+                    absurd_extreme_comparisons::check(cx, e, op.node, lhs, rhs);
+                    if !(macro_with_not_op(lhs) || macro_with_not_op(rhs)) {
+                        eq_op::check(cx, e, op.node, lhs, rhs);
+                        op_ref::check(cx, e, op.node, lhs, rhs);
+                    }
+                    erasing_op::check(cx, e, op.node, lhs, rhs);
+                    identity_op::check(cx, e, op.node, lhs, rhs);
+                    needless_bitwise_bool::check(cx, e, op.node, lhs, rhs);
+                    ptr_eq::check(cx, e, op.node, lhs, rhs);
+                }
+                self.arithmetic_context.check_binary(cx, e, op.node, lhs, rhs);
+                bit_mask::check(cx, e, op.node, lhs, rhs);
+                verbose_bit_mask::check(cx, e, op.node, lhs, rhs, self.verbose_bit_mask_threshold);
+                double_comparison::check(cx, op.node, lhs, rhs, e.span);
+                duration_subsec::check(cx, e, op.node, lhs, rhs);
+                float_equality_without_abs::check(cx, e, op.node, lhs, rhs);
+                integer_division::check(cx, e, op.node, lhs, rhs);
+                cmp_nan::check(cx, e, op.node, lhs, rhs);
+                cmp_owned::check(cx, op.node, lhs, rhs);
+                float_cmp::check(cx, e, op.node, lhs, rhs);
+                modulo_one::check(cx, e, op.node, rhs);
+                modulo_arithmetic::check(cx, e, op.node, lhs, rhs);
+            },
+            ExprKind::AssignOp(op, lhs, rhs) => {
+                self.arithmetic_context.check_binary(cx, e, op.node, lhs, rhs);
+                misrefactored_assign_op::check(cx, e, op.node, lhs, rhs);
+                modulo_arithmetic::check(cx, e, op.node, lhs, rhs);
+            },
+            ExprKind::Assign(lhs, rhs, _) => {
+                assign_op_pattern::check(cx, e, lhs, rhs);
+                self_assignment::check(cx, e, lhs, rhs);
+            },
+            ExprKind::Unary(op, arg) => {
+                if op == UnOp::Neg {
+                    self.arithmetic_context.check_negate(cx, e, arg);
+                }
+            },
+            _ => (),
+        }
+    }
+
+    fn check_expr_post(&mut self, _: &LateContext<'_>, e: &Expr<'_>) {
+        self.arithmetic_context.expr_post(e.hir_id);
+    }
+
+    fn check_body(&mut self, cx: &LateContext<'tcx>, b: &'tcx Body<'_>) {
+        self.arithmetic_context.enter_body(cx, b);
+    }
+
+    fn check_body_post(&mut self, cx: &LateContext<'tcx>, b: &'tcx Body<'_>) {
+        self.arithmetic_context.body_post(cx, b);
+    }
+}
+
+fn macro_with_not_op(e: &Expr<'_>) -> bool {
+    if let ExprKind::Unary(_, e) = e.kind {
+        e.span.from_expansion()
+    } else {
+        false
+    }
+}
diff --git a/clippy_lints/src/modulo_arithmetic.rs b/clippy_lints/src/operators/modulo_arithmetic.rs
index 195b2e5c2ee..af4e74947f4 100644
--- a/clippy_lints/src/modulo_arithmetic.rs
+++ b/clippy_lints/src/operators/modulo_arithmetic.rs
@@ -2,35 +2,35 @@ use clippy_utils::consts::{constant, Constant};
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::sext;
 use if_chain::if_chain;
-use rustc_hir::{BinOpKind, Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass};
+use rustc_hir::{BinOpKind, Expr};
+use rustc_lint::LateContext;
 use rustc_middle::ty::{self, Ty};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
 use std::fmt::Display;
 
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for modulo arithmetic.
-    ///
-    /// ### Why is this bad?
-    /// The results of modulo (%) operation might differ
-    /// depending on the language, when negative numbers are involved.
-    /// If you interop with different languages it might be beneficial
-    /// to double check all places that use modulo arithmetic.
-    ///
-    /// For example, in Rust `17 % -3 = 2`, but in Python `17 % -3 = -1`.
-    ///
-    /// ### Example
-    /// ```rust
-    /// let x = -17 % 3;
-    /// ```
-    #[clippy::version = "1.42.0"]
-    pub MODULO_ARITHMETIC,
-    restriction,
-    "any modulo arithmetic statement"
-}
+use super::MODULO_ARITHMETIC;
 
-declare_lint_pass!(ModuloArithmetic => [MODULO_ARITHMETIC]);
+pub(super) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    e: &'tcx Expr<'_>,
+    op: BinOpKind,
+    lhs: &'tcx Expr<'_>,
+    rhs: &'tcx Expr<'_>,
+) {
+    if op == BinOpKind::Rem {
+        let lhs_operand = analyze_operand(lhs, cx, e);
+        let rhs_operand = analyze_operand(rhs, cx, e);
+        if_chain! {
+            if let Some(lhs_operand) = lhs_operand;
+            if let Some(rhs_operand) = rhs_operand;
+            then {
+                check_const_operands(cx, e, &lhs_operand, &rhs_operand);
+            }
+            else {
+                check_non_const_operands(cx, e, lhs);
+            }
+        }
+    };
+}
 
 struct OperandInfo {
     string_representation: Option<String>,
@@ -124,27 +124,3 @@ fn check_non_const_operands<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>,
         );
     }
 }
-
-impl<'tcx> LateLintPass<'tcx> for ModuloArithmetic {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        match &expr.kind {
-            ExprKind::Binary(op, lhs, rhs) | ExprKind::AssignOp(op, lhs, rhs) => {
-                if op.node == BinOpKind::Rem {
-                    let lhs_operand = analyze_operand(lhs, cx, expr);
-                    let rhs_operand = analyze_operand(rhs, cx, expr);
-                    if_chain! {
-                        if let Some(lhs_operand) = lhs_operand;
-                        if let Some(rhs_operand) = rhs_operand;
-                        then {
-                            check_const_operands(cx, expr, &lhs_operand, &rhs_operand);
-                        }
-                        else {
-                            check_non_const_operands(cx, expr, lhs);
-                        }
-                    }
-                };
-            },
-            _ => {},
-        }
-    }
-}
diff --git a/clippy_lints/src/operators/modulo_one.rs b/clippy_lints/src/operators/modulo_one.rs
new file mode 100644
index 00000000000..54eea14833f
--- /dev/null
+++ b/clippy_lints/src/operators/modulo_one.rs
@@ -0,0 +1,26 @@
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::{is_integer_const, unsext};
+use rustc_hir::{BinOpKind, Expr};
+use rustc_lint::LateContext;
+use rustc_middle::ty;
+
+use super::MODULO_ONE;
+
+pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, op: BinOpKind, right: &Expr<'_>) {
+    if op == BinOpKind::Rem {
+        if is_integer_const(cx, right, 1) {
+            span_lint(cx, MODULO_ONE, expr.span, "any number modulo 1 will be 0");
+        }
+
+        if let ty::Int(ity) = cx.typeck_results().expr_ty(right).kind() {
+            if is_integer_const(cx, right, unsext(cx.tcx, -1, *ity)) {
+                span_lint(
+                    cx,
+                    MODULO_ONE,
+                    expr.span,
+                    "any number modulo -1 will panic/overflow or result in 0",
+                );
+            }
+        };
+    }
+}
diff --git a/clippy_lints/src/operators/needless_bitwise_bool.rs b/clippy_lints/src/operators/needless_bitwise_bool.rs
new file mode 100644
index 00000000000..e902235a014
--- /dev/null
+++ b/clippy_lints/src/operators/needless_bitwise_bool.rs
@@ -0,0 +1,36 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::snippet_opt;
+use rustc_errors::Applicability;
+use rustc_hir::{BinOpKind, Expr, ExprKind};
+use rustc_lint::LateContext;
+
+use super::NEEDLESS_BITWISE_BOOL;
+
+pub(super) fn check(cx: &LateContext<'_>, e: &Expr<'_>, op: BinOpKind, lhs: &Expr<'_>, rhs: &Expr<'_>) {
+    let op_str = match op {
+        BinOpKind::BitAnd => "&&",
+        BinOpKind::BitOr => "||",
+        _ => return,
+    };
+    if matches!(
+        rhs.kind,
+        ExprKind::Call(..) | ExprKind::MethodCall(..) | ExprKind::Binary(..) | ExprKind::Unary(..)
+    ) && cx.typeck_results().expr_ty(e).is_bool()
+        && !rhs.can_have_side_effects()
+    {
+        span_lint_and_then(
+            cx,
+            NEEDLESS_BITWISE_BOOL,
+            e.span,
+            "use of bitwise operator instead of lazy operator between booleans",
+            |diag| {
+                if let Some(lhs_snip) = snippet_opt(cx, lhs.span)
+                    && let Some(rhs_snip) = snippet_opt(cx, rhs.span)
+                {
+                    let sugg = format!("{} {} {}", lhs_snip, op_str, rhs_snip);
+                    diag.span_suggestion(e.span, "try", sugg, Applicability::MachineApplicable);
+                }
+            },
+        );
+    }
+}
diff --git a/clippy_lints/src/operators/numeric_arithmetic.rs b/clippy_lints/src/operators/numeric_arithmetic.rs
new file mode 100644
index 00000000000..82f454d02f7
--- /dev/null
+++ b/clippy_lints/src/operators/numeric_arithmetic.rs
@@ -0,0 +1,127 @@
+use clippy_utils::consts::constant_simple;
+use clippy_utils::diagnostics::span_lint;
+use rustc_hir as hir;
+use rustc_lint::LateContext;
+use rustc_span::source_map::Span;
+
+use super::{FLOAT_ARITHMETIC, INTEGER_ARITHMETIC};
+
+#[derive(Default)]
+pub struct Context {
+    expr_id: Option<hir::HirId>,
+    /// This field is used to check whether expressions are constants, such as in enum discriminants
+    /// and consts
+    const_span: Option<Span>,
+}
+impl Context {
+    fn skip_expr(&mut self, e: &hir::Expr<'_>) -> bool {
+        self.expr_id.is_some() || self.const_span.map_or(false, |span| span.contains(e.span))
+    }
+
+    pub fn check_binary<'tcx>(
+        &mut self,
+        cx: &LateContext<'tcx>,
+        expr: &'tcx hir::Expr<'_>,
+        op: hir::BinOpKind,
+        l: &'tcx hir::Expr<'_>,
+        r: &'tcx hir::Expr<'_>,
+    ) {
+        if self.skip_expr(expr) {
+            return;
+        }
+        match op {
+            hir::BinOpKind::And
+            | hir::BinOpKind::Or
+            | hir::BinOpKind::BitAnd
+            | hir::BinOpKind::BitOr
+            | hir::BinOpKind::BitXor
+            | hir::BinOpKind::Eq
+            | hir::BinOpKind::Lt
+            | hir::BinOpKind::Le
+            | hir::BinOpKind::Ne
+            | hir::BinOpKind::Ge
+            | hir::BinOpKind::Gt => return,
+            _ => (),
+        }
+
+        let (l_ty, r_ty) = (cx.typeck_results().expr_ty(l), cx.typeck_results().expr_ty(r));
+        if l_ty.peel_refs().is_integral() && r_ty.peel_refs().is_integral() {
+            match op {
+                hir::BinOpKind::Div | hir::BinOpKind::Rem => match &r.kind {
+                    hir::ExprKind::Lit(_lit) => (),
+                    hir::ExprKind::Unary(hir::UnOp::Neg, expr) => {
+                        if let hir::ExprKind::Lit(lit) = &expr.kind {
+                            if let rustc_ast::ast::LitKind::Int(1, _) = lit.node {
+                                span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
+                                self.expr_id = Some(expr.hir_id);
+                            }
+                        }
+                    },
+                    _ => {
+                        span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
+                        self.expr_id = Some(expr.hir_id);
+                    },
+                },
+                _ => {
+                    span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
+                    self.expr_id = Some(expr.hir_id);
+                },
+            }
+        } else if r_ty.peel_refs().is_floating_point() && r_ty.peel_refs().is_floating_point() {
+            span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected");
+            self.expr_id = Some(expr.hir_id);
+        }
+    }
+
+    pub fn check_negate<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, arg: &'tcx hir::Expr<'_>) {
+        if self.skip_expr(expr) {
+            return;
+        }
+        let ty = cx.typeck_results().expr_ty(arg);
+        if constant_simple(cx, cx.typeck_results(), expr).is_none() {
+            if ty.is_integral() {
+                span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
+                self.expr_id = Some(expr.hir_id);
+            } else if ty.is_floating_point() {
+                span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected");
+                self.expr_id = Some(expr.hir_id);
+            }
+        }
+    }
+
+    pub fn expr_post(&mut self, id: hir::HirId) {
+        if Some(id) == self.expr_id {
+            self.expr_id = None;
+        }
+    }
+
+    pub fn enter_body(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>) {
+        let body_owner = cx.tcx.hir().body_owner_def_id(body.id());
+
+        match cx.tcx.hir().body_owner_kind(body_owner) {
+            hir::BodyOwnerKind::Static(_) | hir::BodyOwnerKind::Const => {
+                let body_span = cx.tcx.def_span(body_owner);
+
+                if let Some(span) = self.const_span {
+                    if span.contains(body_span) {
+                        return;
+                    }
+                }
+                self.const_span = Some(body_span);
+            },
+            hir::BodyOwnerKind::Fn | hir::BodyOwnerKind::Closure => (),
+        }
+    }
+
+    pub fn body_post(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>) {
+        let body_owner = cx.tcx.hir().body_owner(body.id());
+        let body_span = cx.tcx.hir().span(body_owner);
+
+        if let Some(span) = self.const_span {
+            if span.contains(body_span) {
+                return;
+            }
+        }
+        self.const_span = None;
+    }
+}
diff --git a/clippy_lints/src/operators/op_ref.rs b/clippy_lints/src/operators/op_ref.rs
new file mode 100644
index 00000000000..1805672e372
--- /dev/null
+++ b/clippy_lints/src/operators/op_ref.rs
@@ -0,0 +1,218 @@
+use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
+use clippy_utils::get_enclosing_block;
+use clippy_utils::source::snippet;
+use clippy_utils::ty::{implements_trait, is_copy};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::{def::Res, def_id::DefId, BinOpKind, BorrowKind, Expr, ExprKind, GenericArg, ItemKind, QPath, TyKind};
+use rustc_lint::LateContext;
+use rustc_middle::ty::{self, Ty};
+
+use super::OP_REF;
+
+#[expect(clippy::similar_names, clippy::too_many_lines)]
+pub(crate) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    e: &'tcx Expr<'_>,
+    op: BinOpKind,
+    left: &'tcx Expr<'_>,
+    right: &'tcx Expr<'_>,
+) {
+    let (trait_id, requires_ref) = match op {
+        BinOpKind::Add => (cx.tcx.lang_items().add_trait(), false),
+        BinOpKind::Sub => (cx.tcx.lang_items().sub_trait(), false),
+        BinOpKind::Mul => (cx.tcx.lang_items().mul_trait(), false),
+        BinOpKind::Div => (cx.tcx.lang_items().div_trait(), false),
+        BinOpKind::Rem => (cx.tcx.lang_items().rem_trait(), false),
+        // don't lint short circuiting ops
+        BinOpKind::And | BinOpKind::Or => return,
+        BinOpKind::BitXor => (cx.tcx.lang_items().bitxor_trait(), false),
+        BinOpKind::BitAnd => (cx.tcx.lang_items().bitand_trait(), false),
+        BinOpKind::BitOr => (cx.tcx.lang_items().bitor_trait(), false),
+        BinOpKind::Shl => (cx.tcx.lang_items().shl_trait(), false),
+        BinOpKind::Shr => (cx.tcx.lang_items().shr_trait(), false),
+        BinOpKind::Ne | BinOpKind::Eq => (cx.tcx.lang_items().eq_trait(), true),
+        BinOpKind::Lt | BinOpKind::Le | BinOpKind::Ge | BinOpKind::Gt => {
+            (cx.tcx.lang_items().partial_ord_trait(), true)
+        },
+    };
+    if let Some(trait_id) = trait_id {
+        match (&left.kind, &right.kind) {
+            // do not suggest to dereference literals
+            (&ExprKind::Lit(..), _) | (_, &ExprKind::Lit(..)) => {},
+            // &foo == &bar
+            (&ExprKind::AddrOf(BorrowKind::Ref, _, l), &ExprKind::AddrOf(BorrowKind::Ref, _, r)) => {
+                let lty = cx.typeck_results().expr_ty(l);
+                let rty = cx.typeck_results().expr_ty(r);
+                let lcpy = is_copy(cx, lty);
+                let rcpy = is_copy(cx, rty);
+                if let Some((self_ty, other_ty)) = in_impl(cx, e, trait_id) {
+                    if (are_equal(cx, rty, self_ty) && are_equal(cx, lty, other_ty))
+                        || (are_equal(cx, rty, other_ty) && are_equal(cx, lty, self_ty))
+                    {
+                        return; // Don't lint
+                    }
+                }
+                // either operator autorefs or both args are copyable
+                if (requires_ref || (lcpy && rcpy)) && implements_trait(cx, lty, trait_id, &[rty.into()]) {
+                    span_lint_and_then(
+                        cx,
+                        OP_REF,
+                        e.span,
+                        "needlessly taken reference of both operands",
+                        |diag| {
+                            let lsnip = snippet(cx, l.span, "...").to_string();
+                            let rsnip = snippet(cx, r.span, "...").to_string();
+                            multispan_sugg(
+                                diag,
+                                "use the values directly",
+                                vec![(left.span, lsnip), (right.span, rsnip)],
+                            );
+                        },
+                    );
+                } else if lcpy
+                    && !rcpy
+                    && implements_trait(cx, lty, trait_id, &[cx.typeck_results().expr_ty(right).into()])
+                {
+                    span_lint_and_then(
+                        cx,
+                        OP_REF,
+                        e.span,
+                        "needlessly taken reference of left operand",
+                        |diag| {
+                            let lsnip = snippet(cx, l.span, "...").to_string();
+                            diag.span_suggestion(
+                                left.span,
+                                "use the left value directly",
+                                lsnip,
+                                Applicability::MaybeIncorrect, // FIXME #2597
+                            );
+                        },
+                    );
+                } else if !lcpy
+                    && rcpy
+                    && implements_trait(cx, cx.typeck_results().expr_ty(left), trait_id, &[rty.into()])
+                {
+                    span_lint_and_then(
+                        cx,
+                        OP_REF,
+                        e.span,
+                        "needlessly taken reference of right operand",
+                        |diag| {
+                            let rsnip = snippet(cx, r.span, "...").to_string();
+                            diag.span_suggestion(
+                                right.span,
+                                "use the right value directly",
+                                rsnip,
+                                Applicability::MaybeIncorrect, // FIXME #2597
+                            );
+                        },
+                    );
+                }
+            },
+            // &foo == bar
+            (&ExprKind::AddrOf(BorrowKind::Ref, _, l), _) => {
+                let lty = cx.typeck_results().expr_ty(l);
+                if let Some((self_ty, other_ty)) = in_impl(cx, e, trait_id) {
+                    let rty = cx.typeck_results().expr_ty(right);
+                    if (are_equal(cx, rty, self_ty) && are_equal(cx, lty, other_ty))
+                        || (are_equal(cx, rty, other_ty) && are_equal(cx, lty, self_ty))
+                    {
+                        return; // Don't lint
+                    }
+                }
+                let lcpy = is_copy(cx, lty);
+                if (requires_ref || lcpy)
+                    && implements_trait(cx, lty, trait_id, &[cx.typeck_results().expr_ty(right).into()])
+                {
+                    span_lint_and_then(
+                        cx,
+                        OP_REF,
+                        e.span,
+                        "needlessly taken reference of left operand",
+                        |diag| {
+                            let lsnip = snippet(cx, l.span, "...").to_string();
+                            diag.span_suggestion(
+                                left.span,
+                                "use the left value directly",
+                                lsnip,
+                                Applicability::MaybeIncorrect, // FIXME #2597
+                            );
+                        },
+                    );
+                }
+            },
+            // foo == &bar
+            (_, &ExprKind::AddrOf(BorrowKind::Ref, _, r)) => {
+                let rty = cx.typeck_results().expr_ty(r);
+                if let Some((self_ty, other_ty)) = in_impl(cx, e, trait_id) {
+                    let lty = cx.typeck_results().expr_ty(left);
+                    if (are_equal(cx, rty, self_ty) && are_equal(cx, lty, other_ty))
+                        || (are_equal(cx, rty, other_ty) && are_equal(cx, lty, self_ty))
+                    {
+                        return; // Don't lint
+                    }
+                }
+                let rcpy = is_copy(cx, rty);
+                if (requires_ref || rcpy)
+                    && implements_trait(cx, cx.typeck_results().expr_ty(left), trait_id, &[rty.into()])
+                {
+                    span_lint_and_then(cx, OP_REF, e.span, "taken reference of right operand", |diag| {
+                        let rsnip = snippet(cx, r.span, "...").to_string();
+                        diag.span_suggestion(
+                            right.span,
+                            "use the right value directly",
+                            rsnip,
+                            Applicability::MaybeIncorrect, // FIXME #2597
+                        );
+                    });
+                }
+            },
+            _ => {},
+        }
+    }
+}
+
+fn in_impl<'tcx>(
+    cx: &LateContext<'tcx>,
+    e: &'tcx Expr<'_>,
+    bin_op: DefId,
+) -> Option<(&'tcx rustc_hir::Ty<'tcx>, &'tcx rustc_hir::Ty<'tcx>)> {
+    if_chain! {
+        if let Some(block) = get_enclosing_block(cx, e.hir_id);
+        if let Some(impl_def_id) = cx.tcx.impl_of_method(block.hir_id.owner.to_def_id());
+        let item = cx.tcx.hir().expect_item(impl_def_id.expect_local());
+        if let ItemKind::Impl(item) = &item.kind;
+        if let Some(of_trait) = &item.of_trait;
+        if let Some(seg) = of_trait.path.segments.last();
+        if let Some(Res::Def(_, trait_id)) = seg.res;
+        if trait_id == bin_op;
+        if let Some(generic_args) = seg.args;
+        if let Some(GenericArg::Type(other_ty)) = generic_args.args.last();
+
+        then {
+            Some((item.self_ty, other_ty))
+        }
+        else {
+            None
+        }
+    }
+}
+
+fn are_equal<'tcx>(cx: &LateContext<'tcx>, middle_ty: Ty<'_>, hir_ty: &rustc_hir::Ty<'_>) -> bool {
+    if_chain! {
+        if let ty::Adt(adt_def, _) = middle_ty.kind();
+        if let Some(local_did) = adt_def.did().as_local();
+        let item = cx.tcx.hir().expect_item(local_did);
+        let middle_ty_id = item.def_id.to_def_id();
+        if let TyKind::Path(QPath::Resolved(_, path)) = hir_ty.kind;
+        if let Res::Def(_, hir_ty_id) = path.res;
+
+        then {
+            hir_ty_id == middle_ty_id
+        }
+        else {
+            false
+        }
+    }
+}
diff --git a/clippy_lints/src/operators/ptr_eq.rs b/clippy_lints/src/operators/ptr_eq.rs
new file mode 100644
index 00000000000..1aefc2741c2
--- /dev/null
+++ b/clippy_lints/src/operators/ptr_eq.rs
@@ -0,0 +1,65 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_opt;
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::{BinOpKind, Expr, ExprKind};
+use rustc_lint::LateContext;
+
+use super::PTR_EQ;
+
+static LINT_MSG: &str = "use `std::ptr::eq` when comparing raw pointers";
+
+pub(super) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'_>,
+    op: BinOpKind,
+    left: &'tcx Expr<'_>,
+    right: &'tcx Expr<'_>,
+) {
+    if BinOpKind::Eq == op {
+        let (left, right) = match (expr_as_cast_to_usize(cx, left), expr_as_cast_to_usize(cx, right)) {
+            (Some(lhs), Some(rhs)) => (lhs, rhs),
+            _ => (left, right),
+        };
+
+        if_chain! {
+            if let Some(left_var) = expr_as_cast_to_raw_pointer(cx, left);
+            if let Some(right_var) = expr_as_cast_to_raw_pointer(cx, right);
+            if let Some(left_snip) = snippet_opt(cx, left_var.span);
+            if let Some(right_snip) = snippet_opt(cx, right_var.span);
+            then {
+                span_lint_and_sugg(
+                    cx,
+                    PTR_EQ,
+                    expr.span,
+                    LINT_MSG,
+                    "try",
+                    format!("std::ptr::eq({}, {})", left_snip, right_snip),
+                    Applicability::MachineApplicable,
+                    );
+            }
+        }
+    }
+}
+
+// If the given expression is a cast to a usize, return the lhs of the cast
+// E.g., `foo as *const _ as usize` returns `foo as *const _`.
+fn expr_as_cast_to_usize<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
+    if cx.typeck_results().expr_ty(cast_expr) == cx.tcx.types.usize {
+        if let ExprKind::Cast(expr, _) = cast_expr.kind {
+            return Some(expr);
+        }
+    }
+    None
+}
+
+// If the given expression is a cast to a `*const` pointer, return the lhs of the cast
+// E.g., `foo as *const _` returns `foo`.
+fn expr_as_cast_to_raw_pointer<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
+    if cx.typeck_results().expr_ty(cast_expr).is_unsafe_ptr() {
+        if let ExprKind::Cast(expr, _) = cast_expr.kind {
+            return Some(expr);
+        }
+    }
+    None
+}
diff --git a/clippy_lints/src/operators/self_assignment.rs b/clippy_lints/src/operators/self_assignment.rs
new file mode 100644
index 00000000000..9d6bec05bf0
--- /dev/null
+++ b/clippy_lints/src/operators/self_assignment.rs
@@ -0,0 +1,20 @@
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::eq_expr_value;
+use clippy_utils::source::snippet;
+use rustc_hir::Expr;
+use rustc_lint::LateContext;
+
+use super::SELF_ASSIGNMENT;
+
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, lhs: &'tcx Expr<'_>, rhs: &'tcx Expr<'_>) {
+    if eq_expr_value(cx, lhs, rhs) {
+        let lhs = snippet(cx, lhs.span, "<lhs>");
+        let rhs = snippet(cx, rhs.span, "<rhs>");
+        span_lint(
+            cx,
+            SELF_ASSIGNMENT,
+            e.span,
+            &format!("self-assignment of `{}` to `{}`", rhs, lhs),
+        );
+    }
+}
diff --git a/clippy_lints/src/operators/verbose_bit_mask.rs b/clippy_lints/src/operators/verbose_bit_mask.rs
new file mode 100644
index 00000000000..ff85fd55429
--- /dev/null
+++ b/clippy_lints/src/operators/verbose_bit_mask.rs
@@ -0,0 +1,44 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::sugg::Sugg;
+use rustc_ast::ast::LitKind;
+use rustc_errors::Applicability;
+use rustc_hir::{BinOpKind, Expr, ExprKind};
+use rustc_lint::LateContext;
+
+use super::VERBOSE_BIT_MASK;
+
+pub(super) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    e: &'tcx Expr<'_>,
+    op: BinOpKind,
+    left: &'tcx Expr<'_>,
+    right: &'tcx Expr<'_>,
+    threshold: u64,
+) {
+    if BinOpKind::Eq == op
+        && let ExprKind::Binary(op1, left1, right1) = &left.kind
+        && BinOpKind::BitAnd == op1.node
+        && let ExprKind::Lit(lit) = &right1.kind
+        && let LitKind::Int(n, _) = lit.node
+        && let ExprKind::Lit(lit1) = &right.kind
+        && let LitKind::Int(0, _) = lit1.node
+        && n.leading_zeros() == n.count_zeros()
+        && n > u128::from(threshold)
+    {
+        span_lint_and_then(
+            cx,
+            VERBOSE_BIT_MASK,
+            e.span,
+            "bit mask could be simplified with a call to `trailing_zeros`",
+            |diag| {
+                let sugg = Sugg::hir(cx, left1, "...").maybe_par();
+                diag.span_suggestion(
+                    e.span,
+                    "try",
+                    format!("{}.trailing_zeros() >= {}", sugg, n.count_ones()),
+                    Applicability::MaybeIncorrect,
+                );
+            },
+        );
+    }
+}
diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs
index 05ab62786f4..5fa4fd74853 100644
--- a/clippy_lints/src/pass_by_ref_or_value.rs
+++ b/clippy_lints/src/pass_by_ref_or_value.rs
@@ -3,17 +3,20 @@ use std::iter;
 
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet;
-use clippy_utils::ty::is_copy;
+use clippy_utils::ty::{for_each_top_level_late_bound_region, is_copy};
 use clippy_utils::{is_self, is_self_ty};
+use core::ops::ControlFlow;
 use if_chain::if_chain;
 use rustc_ast::attr;
+use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_hir::intravisit::FnKind;
 use rustc_hir::{BindingAnnotation, Body, FnDecl, HirId, Impl, ItemKind, MutTy, Mutability, Node, PatKind};
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty;
+use rustc_middle::ty::adjustment::{Adjust, PointerCast};
 use rustc_middle::ty::layout::LayoutOf;
+use rustc_middle::ty::{self, RegionKind};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::def_id::LocalDefId;
 use rustc_span::{sym, Span};
@@ -141,50 +144,76 @@ impl<'tcx> PassByRefOrValue {
         }
 
         let fn_sig = cx.tcx.fn_sig(def_id);
-        let fn_sig = cx.tcx.erase_late_bound_regions(fn_sig);
-
         let fn_body = cx.enclosing_body.map(|id| cx.tcx.hir().body(id));
 
-        for (index, (input, &ty)) in iter::zip(decl.inputs, fn_sig.inputs()).enumerate() {
+        // Gather all the lifetimes found in the output type which may affect whether
+        // `TRIVIALLY_COPY_PASS_BY_REF` should be linted.
+        let mut output_regions = FxHashSet::default();
+        for_each_top_level_late_bound_region(fn_sig.skip_binder().output(), |region| -> ControlFlow<!> {
+            output_regions.insert(region);
+            ControlFlow::Continue(())
+        });
+
+        for (index, (input, ty)) in iter::zip(
+            decl.inputs,
+            fn_sig.skip_binder().inputs().iter().map(|&ty| fn_sig.rebind(ty)),
+        )
+        .enumerate()
+        {
             // All spans generated from a proc-macro invocation are the same...
             match span {
-                Some(s) if s == input.span => return,
+                Some(s) if s == input.span => continue,
                 _ => (),
             }
 
-            match ty.kind() {
-                ty::Ref(input_lt, ty, Mutability::Not) => {
-                    // Use lifetimes to determine if we're returning a reference to the
-                    // argument. In that case we can't switch to pass-by-value as the
-                    // argument will not live long enough.
-                    let output_lts = match *fn_sig.output().kind() {
-                        ty::Ref(output_lt, _, _) => vec![output_lt],
-                        ty::Adt(_, substs) => substs.regions().collect(),
-                        _ => vec![],
-                    };
+            match *ty.skip_binder().kind() {
+                ty::Ref(lt, ty, Mutability::Not) => {
+                    match lt.kind() {
+                        RegionKind::ReLateBound(index, region)
+                            if index.as_u32() == 0 && output_regions.contains(&region) =>
+                        {
+                            continue;
+                        },
+                        // Early bound regions on functions are either from the containing item, are bounded by another
+                        // lifetime, or are used as a bound for a type or lifetime.
+                        RegionKind::ReEarlyBound(..) => continue,
+                        _ => (),
+                    }
 
-                    if_chain! {
-                        if !output_lts.contains(input_lt);
-                        if is_copy(cx, *ty);
-                        if let Some(size) = cx.layout_of(*ty).ok().map(|l| l.size.bytes());
-                        if size <= self.ref_min_size;
-                        if let hir::TyKind::Rptr(_, MutTy { ty: decl_ty, .. }) = input.kind;
-                        then {
-                            let value_type = if fn_body.and_then(|body| body.params.get(index)).map_or(false, is_self) {
-                                "self".into()
-                            } else {
-                                snippet(cx, decl_ty.span, "_").into()
-                            };
-                            span_lint_and_sugg(
-                                cx,
-                                TRIVIALLY_COPY_PASS_BY_REF,
-                                input.span,
-                                &format!("this argument ({} byte) is passed by reference, but would be more efficient if passed by value (limit: {} byte)", size, self.ref_min_size),
-                                "consider passing by value instead",
-                                value_type,
-                                Applicability::Unspecified,
-                            );
+                    let ty = cx.tcx.erase_late_bound_regions(fn_sig.rebind(ty));
+                    if is_copy(cx, ty)
+                        && let Some(size) = cx.layout_of(ty).ok().map(|l| l.size.bytes())
+                        && size <= self.ref_min_size
+                        && let hir::TyKind::Rptr(_, MutTy { ty: decl_ty, .. }) = input.kind
+                    {
+                        if let Some(typeck) = cx.maybe_typeck_results() {
+                            // Don't lint if an unsafe pointer is created.
+                            // TODO: Limit the check only to unsafe pointers to the argument (or part of the argument)
+                            //       which escape the current function.
+                            if typeck.node_types().iter().any(|(_, &ty)| ty.is_unsafe_ptr())
+                                || typeck
+                                    .adjustments()
+                                    .iter()
+                                    .flat_map(|(_, a)| a)
+                                    .any(|a| matches!(a.kind, Adjust::Pointer(PointerCast::UnsafeFnPointer)))
+                            {
+                                continue;
+                            }
                         }
+                        let value_type = if fn_body.and_then(|body| body.params.get(index)).map_or(false, is_self) {
+                            "self".into()
+                        } else {
+                            snippet(cx, decl_ty.span, "_").into()
+                        };
+                        span_lint_and_sugg(
+                            cx,
+                            TRIVIALLY_COPY_PASS_BY_REF,
+                            input.span,
+                            &format!("this argument ({} byte) is passed by reference, but would be more efficient if passed by value (limit: {} byte)", size, self.ref_min_size),
+                            "consider passing by value instead",
+                            value_type,
+                            Applicability::Unspecified,
+                        );
                     }
                 },
 
@@ -196,6 +225,7 @@ impl<'tcx> PassByRefOrValue {
                             _ => continue,
                         }
                     }
+                    let ty = cx.tcx.erase_late_bound_regions(ty);
 
                     if_chain! {
                         if is_copy(cx, ty);
diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs
index b06eba13d2f..25b73918c0a 100644
--- a/clippy_lints/src/ptr.rs
+++ b/clippy_lints/src/ptr.rs
@@ -1,6 +1,6 @@
 //! Checks for usage of  `&Vec[_]` and `&String`.
 
-use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then};
 use clippy_utils::source::snippet_opt;
 use clippy_utils::ty::expr_sig;
 use clippy_utils::visitors::contains_unsafe_block;
@@ -166,15 +166,14 @@ impl<'tcx> LateLintPass<'tcx> for Ptr {
             )
             .filter(|arg| arg.mutability() == Mutability::Not)
             {
-                span_lint_and_sugg(
-                    cx,
-                    PTR_ARG,
-                    arg.span,
-                    &arg.build_msg(),
-                    "change this to",
-                    format!("{}{}", arg.ref_prefix, arg.deref_ty.display(cx)),
-                    Applicability::Unspecified,
-                );
+                span_lint_hir_and_then(cx, PTR_ARG, arg.emission_id, arg.span, &arg.build_msg(), |diag| {
+                    diag.span_suggestion(
+                        arg.span,
+                        "change this to",
+                        format!("{}{}", arg.ref_prefix, arg.deref_ty.display(cx)),
+                        Applicability::Unspecified,
+                    );
+                });
             }
         }
     }
@@ -221,7 +220,7 @@ impl<'tcx> LateLintPass<'tcx> for Ptr {
         let results = check_ptr_arg_usage(cx, body, &lint_args);
 
         for (result, args) in results.iter().zip(lint_args.iter()).filter(|(r, _)| !r.skip) {
-            span_lint_and_then(cx, PTR_ARG, args.span, &args.build_msg(), |diag| {
+            span_lint_hir_and_then(cx, PTR_ARG, args.emission_id, args.span, &args.build_msg(), |diag| {
                 diag.multipart_suggestion(
                     "change this to",
                     iter::once((args.span, format!("{}{}", args.ref_prefix, args.deref_ty.display(cx))))
@@ -315,6 +314,7 @@ struct PtrArgReplacement {
 
 struct PtrArg<'tcx> {
     idx: usize,
+    emission_id: hir::HirId,
     span: Span,
     ty_did: DefId,
     ty_name: Symbol,
@@ -419,10 +419,8 @@ fn check_fn_args<'cx, 'tcx: 'cx>(
                 if let [.., name] = path.segments;
                 if cx.tcx.item_name(adt.did()) == name.ident.name;
 
-                if !is_lint_allowed(cx, PTR_ARG, hir_ty.hir_id);
-                if params.get(i).map_or(true, |p| !is_lint_allowed(cx, PTR_ARG, p.hir_id));
-
                 then {
+                    let emission_id = params.get(i).map_or(hir_ty.hir_id, |param| param.hir_id);
                     let (method_renames, deref_ty) = match cx.tcx.get_diagnostic_name(adt.did()) {
                         Some(sym::Vec) => (
                             [("clone", ".to_owned()")].as_slice(),
@@ -455,14 +453,20 @@ fn check_fn_args<'cx, 'tcx: 'cx>(
                                 })
                                 .and_then(|arg| snippet_opt(cx, arg.span))
                                 .unwrap_or_else(|| substs.type_at(1).to_string());
-                            span_lint_and_sugg(
+                            span_lint_hir_and_then(
                                 cx,
                                 PTR_ARG,
+                                emission_id,
                                 hir_ty.span,
                                 "using a reference to `Cow` is not recommended",
-                                "change this to",
-                                format!("&{}{}", mutability.prefix_str(), ty_name),
-                                Applicability::Unspecified,
+                                |diag| {
+                                    diag.span_suggestion(
+                                        hir_ty.span,
+                                        "change this to",
+                                        format!("&{}{}", mutability.prefix_str(), ty_name),
+                                        Applicability::Unspecified,
+                                    );
+                                }
                             );
                             return None;
                         },
@@ -470,6 +474,7 @@ fn check_fn_args<'cx, 'tcx: 'cx>(
                     };
                     return Some(PtrArg {
                         idx: i,
+                        emission_id,
                         span: hir_ty.span,
                         ty_did: adt.did(),
                         ty_name: name.ident.name,
@@ -574,14 +579,13 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args:
                 Some((Node::Expr(e), child_id)) => match e.kind {
                     ExprKind::Call(f, expr_args) => {
                         let i = expr_args.iter().position(|arg| arg.hir_id == child_id).unwrap_or(0);
-                        if expr_sig(self.cx, f)
-                            .map(|sig| sig.input(i).skip_binder().peel_refs())
-                            .map_or(true, |ty| match *ty.kind() {
+                        if expr_sig(self.cx, f).and_then(|sig| sig.input(i)).map_or(true, |ty| {
+                            match *ty.skip_binder().peel_refs().kind() {
                                 ty::Param(_) => true,
                                 ty::Adt(def, _) => def.did() == args.ty_did,
                                 _ => false,
-                            })
-                        {
+                            }
+                        }) {
                             // Passed to a function taking the non-dereferenced type.
                             set_skip_flag();
                         }
diff --git a/clippy_lints/src/ptr_eq.rs b/clippy_lints/src/ptr_eq.rs
deleted file mode 100644
index 2bec93ac606..00000000000
--- a/clippy_lints/src/ptr_eq.rs
+++ /dev/null
@@ -1,97 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_opt;
-use if_chain::if_chain;
-use rustc_errors::Applicability;
-use rustc_hir::{BinOpKind, Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Use `std::ptr::eq` when applicable
-    ///
-    /// ### Why is this bad?
-    /// `ptr::eq` can be used to compare `&T` references
-    /// (which coerce to `*const T` implicitly) by their address rather than
-    /// comparing the values they point to.
-    ///
-    /// ### Example
-    /// ```rust
-    /// let a = &[1, 2, 3];
-    /// let b = &[1, 2, 3];
-    ///
-    /// assert!(a as *const _ as usize == b as *const _ as usize);
-    /// ```
-    /// Use instead:
-    /// ```rust
-    /// let a = &[1, 2, 3];
-    /// let b = &[1, 2, 3];
-    ///
-    /// assert!(std::ptr::eq(a, b));
-    /// ```
-    #[clippy::version = "1.49.0"]
-    pub PTR_EQ,
-    style,
-    "use `std::ptr::eq` when comparing raw pointers"
-}
-
-declare_lint_pass!(PtrEq => [PTR_EQ]);
-
-static LINT_MSG: &str = "use `std::ptr::eq` when comparing raw pointers";
-
-impl<'tcx> LateLintPass<'tcx> for PtrEq {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if expr.span.from_expansion() {
-            return;
-        }
-
-        if let ExprKind::Binary(ref op, left, right) = expr.kind {
-            if BinOpKind::Eq == op.node {
-                let (left, right) = match (expr_as_cast_to_usize(cx, left), expr_as_cast_to_usize(cx, right)) {
-                    (Some(lhs), Some(rhs)) => (lhs, rhs),
-                    _ => (left, right),
-                };
-
-                if_chain! {
-                    if let Some(left_var) = expr_as_cast_to_raw_pointer(cx, left);
-                    if let Some(right_var) = expr_as_cast_to_raw_pointer(cx, right);
-                    if let Some(left_snip) = snippet_opt(cx, left_var.span);
-                    if let Some(right_snip) = snippet_opt(cx, right_var.span);
-                    then {
-                        span_lint_and_sugg(
-                            cx,
-                            PTR_EQ,
-                            expr.span,
-                            LINT_MSG,
-                            "try",
-                            format!("std::ptr::eq({}, {})", left_snip, right_snip),
-                            Applicability::MachineApplicable,
-                            );
-                    }
-                }
-            }
-        }
-    }
-}
-
-// If the given expression is a cast to a usize, return the lhs of the cast
-// E.g., `foo as *const _ as usize` returns `foo as *const _`.
-fn expr_as_cast_to_usize<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
-    if cx.typeck_results().expr_ty(cast_expr) == cx.tcx.types.usize {
-        if let ExprKind::Cast(expr, _) = cast_expr.kind {
-            return Some(expr);
-        }
-    }
-    None
-}
-
-// If the given expression is a cast to a `*const` pointer, return the lhs of the cast
-// E.g., `foo as *const _` returns `foo`.
-fn expr_as_cast_to_raw_pointer<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
-    if cx.typeck_results().expr_ty(cast_expr).is_unsafe_ptr() {
-        if let ExprKind::Cast(expr, _) = cast_expr.kind {
-            return Some(expr);
-        }
-    }
-    None
-}
diff --git a/clippy_lints/src/redundant_static_lifetimes.rs b/clippy_lints/src/redundant_static_lifetimes.rs
index 0825f00f421..f8801f769e8 100644
--- a/clippy_lints/src/redundant_static_lifetimes.rs
+++ b/clippy_lints/src/redundant_static_lifetimes.rs
@@ -87,7 +87,7 @@ impl RedundantStaticLifetimes {
                         _ => {},
                     }
                 }
-                self.visit_type(&*borrow_type.ty, cx, reason);
+                self.visit_type(&borrow_type.ty, cx, reason);
             },
             _ => {},
         }
diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs
index e525eba53e2..5ae04947b82 100644
--- a/clippy_lints/src/returns.rs
+++ b/clippy_lints/src/returns.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::diagnostics::span_lint_hir_and_then;
 use clippy_utils::source::{snippet_opt, snippet_with_context};
 use clippy_utils::{fn_def_id, path_to_local_id};
 use if_chain::if_chain;
@@ -94,9 +94,10 @@ impl<'tcx> LateLintPass<'tcx> for Return {
             if !in_external_macro(cx.sess(), retexpr.span);
             if !local.span.from_expansion();
             then {
-                span_lint_and_then(
+                span_lint_hir_and_then(
                     cx,
                     LET_AND_RETURN,
+                    retexpr.hir_id,
                     retexpr.span,
                     "returning the result of a `let` binding from a block",
                     |err| {
@@ -185,6 +186,7 @@ fn check_final_expr<'tcx>(
                 if !borrows {
                     emit_return_lint(
                         cx,
+                        inner.map_or(expr.hir_id, |inner| inner.hir_id),
                         span.expect("`else return` is not possible"),
                         inner.as_ref().map(|i| i.span),
                         replacement,
@@ -220,50 +222,81 @@ fn check_final_expr<'tcx>(
     }
 }
 
-fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, inner_span: Option<Span>, replacement: RetReplacement) {
+fn emit_return_lint(
+    cx: &LateContext<'_>,
+    emission_place: HirId,
+    ret_span: Span,
+    inner_span: Option<Span>,
+    replacement: RetReplacement,
+) {
     if ret_span.from_expansion() {
         return;
     }
     match inner_span {
         Some(inner_span) => {
             let mut applicability = Applicability::MachineApplicable;
-            span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded `return` statement", |diag| {
-                let (snippet, _) = snippet_with_context(cx, inner_span, ret_span.ctxt(), "..", &mut applicability);
-                diag.span_suggestion(ret_span, "remove `return`", snippet, applicability);
-            });
+            span_lint_hir_and_then(
+                cx,
+                NEEDLESS_RETURN,
+                emission_place,
+                ret_span,
+                "unneeded `return` statement",
+                |diag| {
+                    let (snippet, _) = snippet_with_context(cx, inner_span, ret_span.ctxt(), "..", &mut applicability);
+                    diag.span_suggestion(ret_span, "remove `return`", snippet, applicability);
+                },
+            );
         },
         None => match replacement {
             RetReplacement::Empty => {
-                span_lint_and_sugg(
+                span_lint_hir_and_then(
                     cx,
                     NEEDLESS_RETURN,
+                    emission_place,
                     ret_span,
                     "unneeded `return` statement",
-                    "remove `return`",
-                    String::new(),
-                    Applicability::MachineApplicable,
+                    |diag| {
+                        diag.span_suggestion(
+                            ret_span,
+                            "remove `return`",
+                            String::new(),
+                            Applicability::MachineApplicable,
+                        );
+                    },
                 );
             },
             RetReplacement::Block => {
-                span_lint_and_sugg(
+                span_lint_hir_and_then(
                     cx,
                     NEEDLESS_RETURN,
+                    emission_place,
                     ret_span,
                     "unneeded `return` statement",
-                    "replace `return` with an empty block",
-                    "{}".to_string(),
-                    Applicability::MachineApplicable,
+                    |diag| {
+                        diag.span_suggestion(
+                            ret_span,
+                            "replace `return` with an empty block",
+                            "{}".to_string(),
+                            Applicability::MachineApplicable,
+                        );
+                    },
                 );
             },
             RetReplacement::Unit => {
-                span_lint_and_sugg(
+                span_lint_hir_and_then(
                     cx,
                     NEEDLESS_RETURN,
+                    emission_place,
                     ret_span,
                     "unneeded `return` statement",
-                    "replace `return` with a unit value",
-                    "()".to_string(),
-                    Applicability::MachineApplicable,
+                    |diag| {
+                        diag.span_suggestion(
+                            ret_span,
+                            "replace `return` with a unit value",
+                            "()".to_string(),
+                            Applicability::MachineApplicable,
+                        );
+                    },
                 );
             },
         },
diff --git a/clippy_lints/src/self_assignment.rs b/clippy_lints/src/self_assignment.rs
deleted file mode 100644
index b14f0518bdb..00000000000
--- a/clippy_lints/src/self_assignment.rs
+++ /dev/null
@@ -1,56 +0,0 @@
-use clippy_utils::diagnostics::span_lint;
-use clippy_utils::eq_expr_value;
-use clippy_utils::source::snippet;
-use rustc_hir::{Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for explicit self-assignments.
-    ///
-    /// ### Why is this bad?
-    /// Self-assignments are redundant and unlikely to be
-    /// intentional.
-    ///
-    /// ### Known problems
-    /// If expression contains any deref coercions or
-    /// indexing operations they are assumed not to have any side effects.
-    ///
-    /// ### Example
-    /// ```rust
-    /// struct Event {
-    ///     id: usize,
-    ///     x: i32,
-    ///     y: i32,
-    /// }
-    ///
-    /// fn copy_position(a: &mut Event, b: &Event) {
-    ///     a.x = b.x;
-    ///     a.y = a.y;
-    /// }
-    /// ```
-    #[clippy::version = "1.48.0"]
-    pub SELF_ASSIGNMENT,
-    correctness,
-    "explicit self-assignment"
-}
-
-declare_lint_pass!(SelfAssignment => [SELF_ASSIGNMENT]);
-
-impl<'tcx> LateLintPass<'tcx> for SelfAssignment {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if let ExprKind::Assign(lhs, rhs, _) = &expr.kind {
-            if eq_expr_value(cx, lhs, rhs) {
-                let lhs = snippet(cx, lhs.span, "<lhs>");
-                let rhs = snippet(cx, rhs.span, "<rhs>");
-                span_lint(
-                    cx,
-                    SELF_ASSIGNMENT,
-                    expr.span,
-                    &format!("self-assignment of `{}` to `{}`", rhs, lhs),
-                );
-            }
-        }
-    }
-}
diff --git a/clippy_lints/src/slow_vector_initialization.rs b/clippy_lints/src/slow_vector_initialization.rs
index 975a0a06e38..2c8aa17e80d 100644
--- a/clippy_lints/src/slow_vector_initialization.rs
+++ b/clippy_lints/src/slow_vector_initialization.rs
@@ -26,6 +26,9 @@ declare_clippy_lint! {
     /// let mut vec1 = Vec::with_capacity(len);
     /// vec1.resize(len, 0);
     ///
+    /// let mut vec1 = Vec::with_capacity(len);
+    /// vec1.resize(vec1.capacity(), 0);
+    ///
     /// let mut vec2 = Vec::with_capacity(len);
     /// vec2.extend(repeat(0).take(len));
     /// ```
@@ -211,23 +214,20 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> {
 
     /// Checks if the given expression is resizing a vector with 0
     fn search_slow_resize_filling(&mut self, expr: &'tcx Expr<'_>) {
-        if_chain! {
-            if self.initialization_found;
-            if let ExprKind::MethodCall(path, [self_arg, len_arg, fill_arg], _) = expr.kind;
-            if path_to_local_id(self_arg, self.vec_alloc.local_id);
-            if path.ident.name == sym!(resize);
-
+        if self.initialization_found
+            && let ExprKind::MethodCall(path, [self_arg, len_arg, fill_arg], _) = expr.kind
+            && path_to_local_id(self_arg, self.vec_alloc.local_id)
+            && path.ident.name == sym!(resize)
             // Check that is filled with 0
-            if let ExprKind::Lit(ref lit) = fill_arg.kind;
-            if let LitKind::Int(0, _) = lit.node;
-
-            // Check that len expression is equals to `with_capacity` expression
-            if SpanlessEq::new(self.cx).eq_expr(len_arg, self.vec_alloc.len_expr);
-
-            then {
-                self.slow_expression = Some(InitializationType::Resize(expr));
+            && let ExprKind::Lit(ref lit) = fill_arg.kind
+            && let LitKind::Int(0, _) = lit.node {
+                // Check that len expression is equals to `with_capacity` expression
+                if SpanlessEq::new(self.cx).eq_expr(len_arg, self.vec_alloc.len_expr) {
+                    self.slow_expression = Some(InitializationType::Resize(expr));
+                } else if let ExprKind::MethodCall(path, _, _) = len_arg.kind && path.ident.as_str() == "capacity" {
+                    self.slow_expression = Some(InitializationType::Resize(expr));
+                }
             }
-        }
     }
 
     /// Returns `true` if give expression is `repeat(0).take(...)`
@@ -240,12 +240,15 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> {
             if let Some(repeat_expr) = take_args.get(0);
             if self.is_repeat_zero(repeat_expr);
 
-            // Check that len expression is equals to `with_capacity` expression
             if let Some(len_arg) = take_args.get(1);
-            if SpanlessEq::new(self.cx).eq_expr(len_arg, self.vec_alloc.len_expr);
 
             then {
-                return true;
+                // Check that len expression is equals to `with_capacity` expression
+                if SpanlessEq::new(self.cx).eq_expr(len_arg, self.vec_alloc.len_expr) {
+                    return true;
+                } else if let ExprKind::MethodCall(path, _, _) = len_arg.kind && path.ident.as_str() == "capacity" {
+                    return true;
+                }
             }
         }
 
diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs
index 71f3e6b6a6e..eb704a07451 100644
--- a/clippy_lints/src/strings.rs
+++ b/clippy_lints/src/strings.rs
@@ -60,6 +60,12 @@ declare_clippy_lint! {
     /// let x = "Hello".to_owned();
     /// x + ", World";
     /// ```
+    ///
+    /// Use instead:
+    /// ```rust
+    /// let mut x = "Hello".to_owned();
+    /// x.push_str(", World");
+    /// ```
     #[clippy::version = "pre 1.29.0"]
     pub STRING_ADD,
     restriction,
diff --git a/clippy_lints/src/transmute/mod.rs b/clippy_lints/src/transmute/mod.rs
index cbe1406728b..5f3e98144f4 100644
--- a/clippy_lints/src/transmute/mod.rs
+++ b/clippy_lints/src/transmute/mod.rs
@@ -16,9 +16,10 @@ mod wrong_transmute;
 
 use clippy_utils::in_constant;
 use if_chain::if_chain;
-use rustc_hir::{Expr, ExprKind};
+use rustc_hir::{Expr, ExprKind, QPath};
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_semver::RustcVersion;
+use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::symbol::sym;
 
 declare_clippy_lint! {
@@ -385,7 +386,10 @@ declare_clippy_lint! {
     "transmute to or from a type with an undefined representation"
 }
 
-declare_lint_pass!(Transmute => [
+pub struct Transmute {
+    msrv: Option<RustcVersion>,
+}
+impl_lint_pass!(Transmute => [
     CROSSPOINTER_TRANSMUTE,
     TRANSMUTE_PTR_TO_REF,
     TRANSMUTE_PTR_TO_PTR,
@@ -401,13 +405,18 @@ declare_lint_pass!(Transmute => [
     TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
     TRANSMUTE_UNDEFINED_REPR,
 ]);
-
+impl Transmute {
+    #[must_use]
+    pub fn new(msrv: Option<RustcVersion>) -> Self {
+        Self { msrv }
+    }
+}
 impl<'tcx> LateLintPass<'tcx> for Transmute {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
         if_chain! {
             if let ExprKind::Call(path_expr, [arg]) = e.kind;
-            if let ExprKind::Path(ref qpath) = path_expr.kind;
-            if let Some(def_id) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id();
+            if let ExprKind::Path(QPath::Resolved(None, path)) = path_expr.kind;
+            if let Some(def_id) = path.res.opt_def_id();
             if cx.tcx.is_diagnostic_item(sym::transmute, def_id);
             then {
                 // Avoid suggesting non-const operations in const contexts:
@@ -427,7 +436,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute {
 
                 let linted = wrong_transmute::check(cx, e, from_ty, to_ty)
                     | crosspointer_transmute::check(cx, e, from_ty, to_ty)
-                    | transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, qpath)
+                    | transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, self.msrv)
                     | transmute_int_to_char::check(cx, e, from_ty, to_ty, arg, const_context)
                     | transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context)
                     | transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, arg)
@@ -446,4 +455,6 @@ impl<'tcx> LateLintPass<'tcx> for Transmute {
             }
         }
     }
+
+    extract_msrv_attr!(LateContext);
 }
diff --git a/clippy_lints/src/transmute/transmute_ptr_to_ref.rs b/clippy_lints/src/transmute/transmute_ptr_to_ref.rs
index f3653199b37..3ed5d5c6950 100644
--- a/clippy_lints/src/transmute/transmute_ptr_to_ref.rs
+++ b/clippy_lints/src/transmute/transmute_ptr_to_ref.rs
@@ -1,11 +1,12 @@
-use super::utils::get_type_snippet;
 use super::TRANSMUTE_PTR_TO_REF;
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::{meets_msrv, msrvs, sugg};
 use rustc_errors::Applicability;
-use rustc_hir::{Expr, Mutability, QPath};
+use rustc_hir::{self as hir, Expr, GenericArg, Mutability, Path, TyKind};
 use rustc_lint::LateContext;
-use rustc_middle::ty::{self, Ty};
+use rustc_middle::ty::{self, Ty, TypeFoldable};
+use rustc_semver::RustcVersion;
 
 /// Checks for `transmute_ptr_to_ref` lint.
 /// Returns `true` if it's triggered, otherwise returns `false`.
@@ -15,7 +16,8 @@ pub(super) fn check<'tcx>(
     from_ty: Ty<'tcx>,
     to_ty: Ty<'tcx>,
     arg: &'tcx Expr<'_>,
-    qpath: &'tcx QPath<'_>,
+    path: &'tcx Path<'_>,
+    msrv: Option<RustcVersion>,
 ) -> bool {
     match (&from_ty.kind(), &to_ty.kind()) {
         (ty::RawPtr(from_ptr_ty), ty::Ref(_, to_ref_ty, mutbl)) => {
@@ -34,19 +36,34 @@ pub(super) fn check<'tcx>(
                     } else {
                         ("&*", "*const")
                     };
+                    let mut app = Applicability::MachineApplicable;
 
-                    let arg = if from_ptr_ty.ty == *to_ref_ty {
-                        arg
+                    let sugg = if let Some(ty) = get_explicit_type(path) {
+                        let ty_snip = snippet_with_applicability(cx, ty.span, "..", &mut app);
+                        if meets_msrv(msrv, msrvs::POINTER_CAST) {
+                            format!("{}{}.cast::<{}>()", deref, arg.maybe_par(), ty_snip)
+                        } else if from_ptr_ty.has_erased_regions() {
+                            sugg::make_unop(deref, arg.as_ty(format!("{} () as {} {}", cast, cast, ty_snip)))
+                                .to_string()
+                        } else {
+                            sugg::make_unop(deref, arg.as_ty(format!("{} {}", cast, ty_snip))).to_string()
+                        }
+                    } else if from_ptr_ty.ty == *to_ref_ty {
+                        if from_ptr_ty.has_erased_regions() {
+                            if meets_msrv(msrv, msrvs::POINTER_CAST) {
+                                format!("{}{}.cast::<{}>()", deref, arg.maybe_par(), to_ref_ty)
+                            } else {
+                                sugg::make_unop(deref, arg.as_ty(format!("{} () as {} {}", cast, cast, to_ref_ty)))
+                                    .to_string()
+                            }
+                        } else {
+                            sugg::make_unop(deref, arg).to_string()
+                        }
                     } else {
-                        arg.as_ty(&format!("{} {}", cast, get_type_snippet(cx, qpath, *to_ref_ty)))
+                        sugg::make_unop(deref, arg.as_ty(format!("{} {}", cast, to_ref_ty))).to_string()
                     };
 
-                    diag.span_suggestion(
-                        e.span,
-                        "try",
-                        sugg::make_unop(deref, arg).to_string(),
-                        Applicability::Unspecified,
-                    );
+                    diag.span_suggestion(e.span, "try", sugg, app);
                 },
             );
             true
@@ -54,3 +71,14 @@ pub(super) fn check<'tcx>(
         _ => false,
     }
 }
+
+/// Gets the type `Bar` in `…::transmute<Foo, &Bar>`.
+fn get_explicit_type<'tcx>(path: &'tcx Path<'tcx>) -> Option<&'tcx hir::Ty<'tcx>> {
+    if let GenericArg::Type(ty) = path.segments.last()?.args?.args.get(1)?
+        && let TyKind::Rptr(_, ty) = &ty.kind
+    {
+        Some(ty.ty)
+    } else {
+        None
+    }
+}
diff --git a/clippy_lints/src/transmute/utils.rs b/clippy_lints/src/transmute/utils.rs
index 0cbf5ccefa6..74927570b40 100644
--- a/clippy_lints/src/transmute/utils.rs
+++ b/clippy_lints/src/transmute/utils.rs
@@ -1,35 +1,9 @@
-use clippy_utils::last_path_segment;
-use clippy_utils::source::snippet;
-use if_chain::if_chain;
-use rustc_hir::{Expr, GenericArg, QPath, TyKind};
+use rustc_hir::Expr;
 use rustc_lint::LateContext;
 use rustc_middle::ty::{cast::CastKind, Ty};
 use rustc_span::DUMMY_SP;
 use rustc_typeck::check::{cast::CastCheck, FnCtxt, Inherited};
 
-/// Gets the snippet of `Bar` in `…::transmute<Foo, &Bar>`. If that snippet is
-/// not available , use
-/// the type's `ToString` implementation. In weird cases it could lead to types
-/// with invalid `'_`
-/// lifetime, but it should be rare.
-pub(super) fn get_type_snippet(cx: &LateContext<'_>, path: &QPath<'_>, to_ref_ty: Ty<'_>) -> String {
-    let seg = last_path_segment(path);
-    if_chain! {
-        if let Some(params) = seg.args;
-        if !params.parenthesized;
-        if let Some(to_ty) = params.args.iter().filter_map(|arg| match arg {
-            GenericArg::Type(ty) => Some(ty),
-            _ => None,
-        }).nth(1);
-        if let TyKind::Rptr(_, ref to_ty) = to_ty.kind;
-        then {
-            return snippet(cx, to_ty.ty.span, &to_ref_ty.to_string()).to_string();
-        }
-    }
-
-    to_ref_ty.to_string()
-}
-
 // check if the component types of the transmuted collection and the result have different ABI,
 // size or alignment
 pub(super) fn is_layout_incompatible<'tcx>(cx: &LateContext<'tcx>, from: Ty<'tcx>, to: Ty<'tcx>) -> bool {
diff --git a/clippy_lints/src/unused_async.rs b/clippy_lints/src/unused_async.rs
index c8ec4442ab1..a832dfcccaf 100644
--- a/clippy_lints/src/unused_async.rs
+++ b/clippy_lints/src/unused_async.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_help;
 use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, Visitor};
-use rustc_hir::{Body, Expr, ExprKind, FnDecl, FnHeader, HirId, IsAsync, YieldSource};
+use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId, IsAsync, YieldSource};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::hir::nested_filter;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -68,20 +68,18 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync {
         span: Span,
         hir_id: HirId,
     ) {
-        if let FnKind::ItemFn(_, _, FnHeader { asyncness, .. }) = &fn_kind {
-            if matches!(asyncness, IsAsync::Async) {
-                let mut visitor = AsyncFnVisitor { cx, found_await: false };
-                walk_fn(&mut visitor, fn_kind, fn_decl, body.id(), span, hir_id);
-                if !visitor.found_await {
-                    span_lint_and_help(
-                        cx,
-                        UNUSED_ASYNC,
-                        span,
-                        "unused `async` for function with no await statements",
-                        None,
-                        "consider removing the `async` from this function",
-                    );
-                }
+        if !span.from_expansion() && fn_kind.asyncness() == IsAsync::Async {
+            let mut visitor = AsyncFnVisitor { cx, found_await: false };
+            walk_fn(&mut visitor, fn_kind, fn_decl, body.id(), span, hir_id);
+            if !visitor.found_await {
+                span_lint_and_help(
+                    cx,
+                    UNUSED_ASYNC,
+                    span,
+                    "unused `async` for function with no await statements",
+                    None,
+                    "consider removing the `async` from this function",
+                );
             }
         }
     }
diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs
index 9b9e25326f9..d3f9e5abfd7 100644
--- a/clippy_lints/src/unwrap.rs
+++ b/clippy_lints/src/unwrap.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::diagnostics::span_lint_hir_and_then;
 use clippy_utils::higher;
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::{path_to_local, usage::is_potentially_mutated};
@@ -251,9 +251,10 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> {
                             unwrappable.kind.error_variant_pattern()
                         };
 
-                        span_lint_and_then(
+                        span_lint_hir_and_then(
                             self.cx,
                             UNNECESSARY_UNWRAP,
+                            expr.hir_id,
                             expr.span,
                             &format!(
                                 "called `{}` on `{}` after checking its variant with `{}`",
@@ -283,9 +284,10 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> {
                             },
                         );
                     } else {
-                        span_lint_and_then(
+                        span_lint_hir_and_then(
                             self.cx,
                             PANICKING_UNWRAP,
+                            expr.hir_id,
                             expr.span,
                             &format!("this call to `{}()` will always panic",
                             method_name.ident.name),
diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs
index b885e5132f1..a94f0357977 100644
--- a/clippy_lints/src/utils/internal_lints.rs
+++ b/clippy_lints/src/utils/internal_lints.rs
@@ -1,3 +1,4 @@
+use crate::utils::internal_lints::metadata_collector::is_deprecated_lint;
 use clippy_utils::consts::{constant_simple, Constant};
 use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::macros::root_macro_call_first_node;
@@ -338,6 +339,46 @@ declare_clippy_lint! {
     "checking if all necessary steps were taken when adding a MSRV to a lint"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for cases of an auto-generated deprecated lint without an updated reason,
+    /// i.e. `"default deprecation note"`.
+    ///
+    /// ### Why is this bad?
+    /// Indicates that the documentation is incomplete.
+    ///
+    /// ### Example
+    /// ```rust,ignore
+    /// declare_deprecated_lint! {
+    ///     /// ### What it does
+    ///     /// Nothing. This lint has been deprecated.
+    ///     ///
+    ///     /// ### Deprecation reason
+    ///     /// TODO
+    ///     #[clippy::version = "1.63.0"]
+    ///     pub COOL_LINT,
+    ///     "default deprecation note"
+    /// }
+    /// ```
+    ///
+    /// Use instead:
+    /// ```rust,ignore
+    /// declare_deprecated_lint! {
+    ///     /// ### What it does
+    ///     /// Nothing. This lint has been deprecated.
+    ///     ///
+    ///     /// ### Deprecation reason
+    ///     /// This lint has been replaced by `cooler_lint`
+    ///     #[clippy::version = "1.63.0"]
+    ///     pub COOL_LINT,
+    ///     "this lint has been replaced by `cooler_lint`"
+    /// }
+    /// ```
+    pub DEFAULT_DEPRECATION_REASON,
+    internal,
+    "found 'default deprecation note' in a deprecated lint declaration"
+}
+
 declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]);
 
 impl EarlyLintPass for ClippyLintsInternal {
@@ -375,42 +416,67 @@ pub struct LintWithoutLintPass {
     registered_lints: FxHashSet<Symbol>,
 }
 
-impl_lint_pass!(LintWithoutLintPass => [DEFAULT_LINT, LINT_WITHOUT_LINT_PASS, INVALID_CLIPPY_VERSION_ATTRIBUTE, MISSING_CLIPPY_VERSION_ATTRIBUTE]);
+impl_lint_pass!(LintWithoutLintPass => [DEFAULT_LINT, LINT_WITHOUT_LINT_PASS, INVALID_CLIPPY_VERSION_ATTRIBUTE, MISSING_CLIPPY_VERSION_ATTRIBUTE, DEFAULT_DEPRECATION_REASON]);
 
 impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
-        if is_lint_allowed(cx, DEFAULT_LINT, item.hir_id()) {
+        if is_lint_allowed(cx, DEFAULT_LINT, item.hir_id())
+            || is_lint_allowed(cx, DEFAULT_DEPRECATION_REASON, item.hir_id())
+        {
             return;
         }
 
         if let hir::ItemKind::Static(ty, Mutability::Not, body_id) = item.kind {
-            if is_lint_ref_type(cx, ty) {
+            let is_lint_ref_ty = is_lint_ref_type(cx, ty);
+            if is_deprecated_lint(cx, ty) || is_lint_ref_ty {
                 check_invalid_clippy_version_attribute(cx, item);
 
                 let expr = &cx.tcx.hir().body(body_id).value;
-                if_chain! {
-                    if let ExprKind::AddrOf(_, _, inner_exp) = expr.kind;
-                    if let ExprKind::Struct(_, fields, _) = inner_exp.kind;
-                    let field = fields
-                        .iter()
-                        .find(|f| f.ident.as_str() == "desc")
-                        .expect("lints must have a description field");
-                    if let ExprKind::Lit(Spanned {
-                        node: LitKind::Str(ref sym, _),
-                        ..
-                    }) = field.expr.kind;
-                    if sym.as_str() == "default lint description";
-
-                    then {
+                let fields;
+                if is_lint_ref_ty {
+                    if let ExprKind::AddrOf(_, _, inner_exp) = expr.kind
+                        && let ExprKind::Struct(_, struct_fields, _) = inner_exp.kind {
+                            fields = struct_fields;
+                    } else {
+                        return;
+                    }
+                } else if let ExprKind::Struct(_, struct_fields, _) = expr.kind {
+                    fields = struct_fields;
+                } else {
+                    return;
+                }
+
+                let field = fields
+                    .iter()
+                    .find(|f| f.ident.as_str() == "desc")
+                    .expect("lints must have a description field");
+
+                if let ExprKind::Lit(Spanned {
+                    node: LitKind::Str(ref sym, _),
+                    ..
+                }) = field.expr.kind
+                {
+                    let sym_str = sym.as_str();
+                    if is_lint_ref_ty {
+                        if sym_str == "default lint description" {
+                            span_lint(
+                                cx,
+                                DEFAULT_LINT,
+                                item.span,
+                                &format!("the lint `{}` has the default lint description", item.ident.name),
+                            );
+                        }
+
+                        self.declared_lints.insert(item.ident.name, item.span);
+                    } else if sym_str == "default deprecation note" {
                         span_lint(
                             cx,
-                            DEFAULT_LINT,
+                            DEFAULT_DEPRECATION_REASON,
                             item.span,
-                            &format!("the lint `{}` has the default lint description", item.ident.name),
+                            &format!("the lint `{}` has the default deprecation reason", item.ident.name),
                         );
                     }
                 }
-                self.declared_lints.insert(item.ident.name, item.span);
             }
         } else if let Some(macro_call) = root_macro_call_first_node(cx, item) {
             if !matches!(
@@ -668,6 +734,7 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls {
             let body = cx.tcx.hir().body(*body);
             let only_expr = peel_blocks_with_stmt(&body.value);
             if let ExprKind::MethodCall(ps, span_call_args, _) = &only_expr.kind;
+            if let ExprKind::Path(..) = span_call_args[0].kind;
             then {
                 let and_then_snippets = get_and_then_snippets(cx, and_then_args);
                 let mut sle = SpanlessEq::new(cx).deny_side_effects();
diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs
index 2564099f4db..6518e0a6ea0 100644
--- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs
+++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs
@@ -104,7 +104,7 @@ macro_rules! RENAME_VALUE_TEMPLATE {
     };
 }
 
-const LINT_EMISSION_FUNCTIONS: [&[&str]; 8] = [
+const LINT_EMISSION_FUNCTIONS: [&[&str]; 7] = [
     &["clippy_utils", "diagnostics", "span_lint"],
     &["clippy_utils", "diagnostics", "span_lint_and_help"],
     &["clippy_utils", "diagnostics", "span_lint_and_note"],
@@ -190,7 +190,12 @@ impl MetadataCollector {
             lints: BinaryHeap::<LintMetadata>::default(),
             applicability_info: FxHashMap::<String, ApplicabilityInfo>::default(),
             config: collect_configs(),
-            clippy_project_root: clippy_dev::clippy_project_root(),
+            clippy_project_root: std::env::current_dir()
+                .expect("failed to get current dir")
+                .ancestors()
+                .nth(1)
+                .expect("failed to get project root")
+                .to_path_buf(),
         }
     }
 
@@ -841,7 +846,7 @@ fn get_lint_level_from_group(lint_group: &str) -> Option<&'static str> {
         .find_map(|(group_name, group_level)| (*group_name == lint_group).then(|| *group_level))
 }
 
-fn is_deprecated_lint(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
+pub(super) fn is_deprecated_lint(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
     if let hir::TyKind::Path(ref path) = ty.kind {
         if let hir::def::Res::Def(DefKind::Struct, def_id) = cx.qpath_res(path, ty.hir_id) {
             return match_def_path(cx, def_id, &DEPRECATED_LINT_TYPE);
diff --git a/clippy_lints/src/vec_resize_to_zero.rs b/clippy_lints/src/vec_resize_to_zero.rs
index 4d86abd0fa1..0fee3e812d2 100644
--- a/clippy_lints/src/vec_resize_to_zero.rs
+++ b/clippy_lints/src/vec_resize_to_zero.rs
@@ -20,6 +20,11 @@ declare_clippy_lint! {
     /// ```rust
     /// vec!(1, 2, 3, 4, 5).resize(0, 5)
     /// ```
+    ///
+    /// Use instead:
+    /// ```rust
+    /// vec!(1, 2, 3, 4, 5).clear()
+    /// ```
     #[clippy::version = "1.46.0"]
     pub VEC_RESIZE_TO_ZERO,
     correctness,