about summary refs log tree commit diff
path: root/clippy_lints/src/operators
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/operators
parentee37029afa62e93ab5ec8b1f2a23f8dbfd2a453f (diff)
Merge commit '0cb0f7636851f9fcc57085cf80197a2ef6db098f' into clippyup
Diffstat (limited to 'clippy_lints/src/operators')
-rw-r--r--clippy_lints/src/operators/absurd_extreme_comparisons.rs142
-rw-r--r--clippy_lints/src/operators/assign_op_pattern.rs101
-rw-r--r--clippy_lints/src/operators/bit_mask.rs197
-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.rs148
-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.rs126
-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
23 files changed, 2793 insertions, 0 deletions
diff --git a/clippy_lints/src/operators/absurd_extreme_comparisons.rs b/clippy_lints/src/operators/absurd_extreme_comparisons.rs
new file mode 100644
index 00000000000..1ec4240afef
--- /dev/null
+++ b/clippy_lints/src/operators/absurd_extreme_comparisons.rs
@@ -0,0 +1,142 @@
+use rustc_hir::{BinOpKind, Expr, ExprKind};
+use rustc_lint::LateContext;
+use rustc_middle::ty;
+
+use clippy_utils::comparisons::{normalize_comparison, Rel};
+use clippy_utils::consts::{constant, Constant};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::source::snippet;
+use clippy_utils::ty::is_isize_or_usize;
+use clippy_utils::{clip, int_bits, unsext};
+
+use super::ABSURD_EXTREME_COMPARISONS;
+
+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);
+    }
+}
+
+enum ExtremeType {
+    Minimum,
+    Maximum,
+}
+
+struct ExtremeExpr<'a> {
+    which: ExtremeType,
+    expr: &'a Expr<'a>,
+}
+
+enum AbsurdComparisonResult {
+    AlwaysFalse,
+    AlwaysTrue,
+    InequalityImpossible,
+}
+
+fn is_cast_between_fixed_and_target<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
+    if let ExprKind::Cast(cast_exp, _) = expr.kind {
+        let precast_ty = cx.typeck_results().expr_ty(cast_exp);
+        let cast_ty = cx.typeck_results().expr_ty(expr);
+
+        return is_isize_or_usize(precast_ty) != is_isize_or_usize(cast_ty);
+    }
+
+    false
+}
+
+fn detect_absurd_comparison<'tcx>(
+    cx: &LateContext<'tcx>,
+    op: BinOpKind,
+    lhs: &'tcx Expr<'_>,
+    rhs: &'tcx Expr<'_>,
+) -> Option<(ExtremeExpr<'tcx>, AbsurdComparisonResult)> {
+    use AbsurdComparisonResult::{AlwaysFalse, AlwaysTrue, InequalityImpossible};
+    use ExtremeType::{Maximum, Minimum};
+    // absurd comparison only makes sense on primitive types
+    // primitive types don't implement comparison operators with each other
+    if cx.typeck_results().expr_ty(lhs) != cx.typeck_results().expr_ty(rhs) {
+        return None;
+    }
+
+    // comparisons between fix sized types and target sized types are considered unanalyzable
+    if is_cast_between_fixed_and_target(cx, lhs) || is_cast_between_fixed_and_target(cx, rhs) {
+        return None;
+    }
+
+    let (rel, normalized_lhs, normalized_rhs) = normalize_comparison(op, lhs, rhs)?;
+
+    let lx = detect_extreme_expr(cx, normalized_lhs);
+    let rx = detect_extreme_expr(cx, normalized_rhs);
+
+    Some(match rel {
+        Rel::Lt => {
+            match (lx, rx) {
+                (Some(l @ ExtremeExpr { which: Maximum, .. }), _) => (l, AlwaysFalse), // max < x
+                (_, Some(r @ ExtremeExpr { which: Minimum, .. })) => (r, AlwaysFalse), // x < min
+                _ => return None,
+            }
+        },
+        Rel::Le => {
+            match (lx, rx) {
+                (Some(l @ ExtremeExpr { which: Minimum, .. }), _) => (l, AlwaysTrue), // min <= x
+                (Some(l @ ExtremeExpr { which: Maximum, .. }), _) => (l, InequalityImpossible), // max <= x
+                (_, Some(r @ ExtremeExpr { which: Minimum, .. })) => (r, InequalityImpossible), // x <= min
+                (_, Some(r @ ExtremeExpr { which: Maximum, .. })) => (r, AlwaysTrue), // x <= max
+                _ => return None,
+            }
+        },
+        Rel::Ne | Rel::Eq => return None,
+    })
+}
+
+fn detect_extreme_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<ExtremeExpr<'tcx>> {
+    let ty = cx.typeck_results().expr_ty(expr);
+
+    let cv = constant(cx, cx.typeck_results(), expr)?.0;
+
+    let which = match (ty.kind(), cv) {
+        (&ty::Bool, Constant::Bool(false)) | (&ty::Uint(_), Constant::Int(0)) => ExtremeType::Minimum,
+        (&ty::Int(ity), Constant::Int(i)) if i == unsext(cx.tcx, i128::MIN >> (128 - int_bits(cx.tcx, ity)), ity) => {
+            ExtremeType::Minimum
+        },
+
+        (&ty::Bool, Constant::Bool(true)) => ExtremeType::Maximum,
+        (&ty::Int(ity), Constant::Int(i)) if i == unsext(cx.tcx, i128::MAX >> (128 - int_bits(cx.tcx, ity)), ity) => {
+            ExtremeType::Maximum
+        },
+        (&ty::Uint(uty), Constant::Int(i)) if clip(cx.tcx, u128::MAX, uty) == i => ExtremeType::Maximum,
+
+        _ => return None,
+    };
+    Some(ExtremeExpr { which, expr })
+}
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/operators/bit_mask.rs b/clippy_lints/src/operators/bit_mask.rs
new file mode 100644
index 00000000000..74387fbc87b
--- /dev/null
+++ b/clippy_lints/src/operators/bit_mask.rs
@@ -0,0 +1,197 @@
+use clippy_utils::consts::{constant, Constant};
+use clippy_utils::diagnostics::span_lint;
+use rustc_hir::{BinOpKind, Expr, ExprKind};
+use rustc_lint::LateContext;
+use rustc_span::source_map::Span;
+
+use super::{BAD_BIT_MASK, INEFFECTIVE_BIT_MASK};
+
+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);
+        }
+    }
+}
+
+#[must_use]
+fn invert_cmp(cmp: BinOpKind) -> BinOpKind {
+    match cmp {
+        BinOpKind::Eq => BinOpKind::Eq,
+        BinOpKind::Ne => BinOpKind::Ne,
+        BinOpKind::Lt => BinOpKind::Gt,
+        BinOpKind::Gt => BinOpKind::Lt,
+        BinOpKind::Le => BinOpKind::Ge,
+        BinOpKind::Ge => BinOpKind::Le,
+        _ => BinOpKind::Or, // Dummy
+    }
+}
+
+fn check_compare(cx: &LateContext<'_>, bit_op: &Expr<'_>, cmp_op: BinOpKind, cmp_value: u128, span: Span) {
+    if let ExprKind::Binary(op, left, right) = &bit_op.kind {
+        if op.node != BinOpKind::BitAnd && op.node != BinOpKind::BitOr {
+            return;
+        }
+        fetch_int_literal(cx, right)
+            .or_else(|| fetch_int_literal(cx, left))
+            .map_or((), |mask| check_bit_mask(cx, op.node, cmp_op, mask, cmp_value, span));
+    }
+}
+
+#[allow(clippy::too_many_lines)]
+fn check_bit_mask(
+    cx: &LateContext<'_>,
+    bit_op: BinOpKind,
+    cmp_op: BinOpKind,
+    mask_value: u128,
+    cmp_value: u128,
+    span: Span,
+) {
+    match cmp_op {
+        BinOpKind::Eq | BinOpKind::Ne => match bit_op {
+            BinOpKind::BitAnd => {
+                if mask_value & cmp_value != cmp_value {
+                    if cmp_value != 0 {
+                        span_lint(
+                            cx,
+                            BAD_BIT_MASK,
+                            span,
+                            &format!(
+                                "incompatible bit mask: `_ & {}` can never be equal to `{}`",
+                                mask_value, cmp_value
+                            ),
+                        );
+                    }
+                } else if mask_value == 0 {
+                    span_lint(cx, BAD_BIT_MASK, span, "&-masking with zero");
+                }
+            },
+            BinOpKind::BitOr => {
+                if mask_value | cmp_value != cmp_value {
+                    span_lint(
+                        cx,
+                        BAD_BIT_MASK,
+                        span,
+                        &format!(
+                            "incompatible bit mask: `_ | {}` can never be equal to `{}`",
+                            mask_value, cmp_value
+                        ),
+                    );
+                }
+            },
+            _ => (),
+        },
+        BinOpKind::Lt | BinOpKind::Ge => match bit_op {
+            BinOpKind::BitAnd => {
+                if mask_value < cmp_value {
+                    span_lint(
+                        cx,
+                        BAD_BIT_MASK,
+                        span,
+                        &format!(
+                            "incompatible bit mask: `_ & {}` will always be lower than `{}`",
+                            mask_value, cmp_value
+                        ),
+                    );
+                } else if mask_value == 0 {
+                    span_lint(cx, BAD_BIT_MASK, span, "&-masking with zero");
+                }
+            },
+            BinOpKind::BitOr => {
+                if mask_value >= cmp_value {
+                    span_lint(
+                        cx,
+                        BAD_BIT_MASK,
+                        span,
+                        &format!(
+                            "incompatible bit mask: `_ | {}` will never be lower than `{}`",
+                            mask_value, cmp_value
+                        ),
+                    );
+                } else {
+                    check_ineffective_lt(cx, span, mask_value, cmp_value, "|");
+                }
+            },
+            BinOpKind::BitXor => check_ineffective_lt(cx, span, mask_value, cmp_value, "^"),
+            _ => (),
+        },
+        BinOpKind::Le | BinOpKind::Gt => match bit_op {
+            BinOpKind::BitAnd => {
+                if mask_value <= cmp_value {
+                    span_lint(
+                        cx,
+                        BAD_BIT_MASK,
+                        span,
+                        &format!(
+                            "incompatible bit mask: `_ & {}` will never be higher than `{}`",
+                            mask_value, cmp_value
+                        ),
+                    );
+                } else if mask_value == 0 {
+                    span_lint(cx, BAD_BIT_MASK, span, "&-masking with zero");
+                }
+            },
+            BinOpKind::BitOr => {
+                if mask_value > cmp_value {
+                    span_lint(
+                        cx,
+                        BAD_BIT_MASK,
+                        span,
+                        &format!(
+                            "incompatible bit mask: `_ | {}` will always be higher than `{}`",
+                            mask_value, cmp_value
+                        ),
+                    );
+                } else {
+                    check_ineffective_gt(cx, span, mask_value, cmp_value, "|");
+                }
+            },
+            BinOpKind::BitXor => check_ineffective_gt(cx, span, mask_value, cmp_value, "^"),
+            _ => (),
+        },
+        _ => (),
+    }
+}
+
+fn check_ineffective_lt(cx: &LateContext<'_>, span: Span, m: u128, c: u128, op: &str) {
+    if c.is_power_of_two() && m < c {
+        span_lint(
+            cx,
+            INEFFECTIVE_BIT_MASK,
+            span,
+            &format!(
+                "ineffective bit mask: `x {} {}` compared to `{}`, is the same as x compared directly",
+                op, m, c
+            ),
+        );
+    }
+}
+
+fn check_ineffective_gt(cx: &LateContext<'_>, span: Span, m: u128, c: u128, op: &str) {
+    if (c + 1).is_power_of_two() && m <= c {
+        span_lint(
+            cx,
+            INEFFECTIVE_BIT_MASK,
+            span,
+            &format!(
+                "ineffective bit mask: `x {} {}` compared to `{}`, is the same as x compared directly",
+                op, m, c
+            ),
+        );
+    }
+}
+
+fn fetch_int_literal(cx: &LateContext<'_>, lit: &Expr<'_>) -> Option<u128> {
+    match constant(cx, cx.typeck_results(), lit)?.0 {
+        Constant::Int(n) => Some(n),
+        _ => None,
+    }
+}
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/operators/identity_op.rs b/clippy_lints/src/operators/identity_op.rs
new file mode 100644
index 00000000000..b48d6c4e2e2
--- /dev/null
+++ b/clippy_lints/src/operators/identity_op.rs
@@ -0,0 +1,148 @@
+use clippy_utils::consts::{constant_full_int, constant_simple, Constant, FullInt};
+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::{BinOpKind, Expr, ExprKind, Node};
+use rustc_lint::LateContext;
+use rustc_middle::ty;
+use rustc_span::source_map::Span;
+
+use super::IDENTITY_OP;
+
+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),
+            _ => (),
+        }
+    }
+}
+
+#[derive(Copy, Clone)]
+enum Parens {
+    Needed,
+    Unneeded,
+}
+
+/// Checks if `left op right` needs parenthesis when reduced to `right`
+/// e.g. `0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }` cannot be reduced
+/// to `if b { 1 } else { 2 } + if b { 3 } else { 4 }` where the `if` could be
+/// interpreted as a statement
+///
+/// See #8724
+fn needs_parenthesis(cx: &LateContext<'_>, binary: &Expr<'_>, right: &Expr<'_>) -> Parens {
+    match right.kind {
+        ExprKind::Binary(_, lhs, _) | ExprKind::Cast(lhs, _) => {
+            // ensure we're checking against the leftmost expression of `right`
+            //
+            //     ~~~ `lhs`
+            // 0 + {4} * 2
+            //     ~~~~~~~ `right`
+            return needs_parenthesis(cx, binary, lhs);
+        },
+        ExprKind::If(..) | ExprKind::Match(..) | ExprKind::Block(..) | ExprKind::Loop(..) => {},
+        _ => return Parens::Unneeded,
+    }
+
+    let mut prev_id = binary.hir_id;
+    for (_, node) in cx.tcx.hir().parent_iter(binary.hir_id) {
+        if let Node::Expr(expr) = node
+            && let ExprKind::Binary(_, lhs, _) | ExprKind::Cast(lhs, _) = expr.kind
+            && lhs.hir_id == prev_id
+        {
+            // keep going until we find a node that encompasses left of `binary`
+            prev_id = expr.hir_id;
+            continue;
+        }
+
+        match node {
+            Node::Block(_) | Node::Stmt(_) => break,
+            _ => return Parens::Unneeded,
+        };
+    }
+
+    Parens::Needed
+}
+
+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 == BinOpKind::Shl
+            && constant_simple(cx, cx.typeck_results(), right) == Some(Constant::Int(0))
+            && constant_simple(cx, cx.typeck_results(), left) == Some(Constant::Int(1)))
+}
+
+fn check_remainder(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>, span: Span, arg: Span) {
+    let lhs_const = constant_full_int(cx, cx.typeck_results(), left);
+    let rhs_const = constant_full_int(cx, cx.typeck_results(), right);
+    if match (lhs_const, rhs_const) {
+        (Some(FullInt::S(lv)), Some(FullInt::S(rv))) => lv.abs() < rv.abs(),
+        (Some(FullInt::U(lv)), Some(FullInt::U(rv))) => lv < rv,
+        _ => return,
+    } {
+        span_ineffective_operation(cx, span, arg, Parens::Unneeded);
+    }
+}
+
+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),
+            ty::Uint(uty) => clip(cx.tcx, !0, uty),
+            _ => return,
+        };
+        if match m {
+            0 => v == 0,
+            -1 => v == check,
+            1 => v == 1,
+            _ => unreachable!(),
+        } {
+            span_ineffective_operation(cx, span, arg, parens);
+        }
+    }
+}
+
+fn span_ineffective_operation(cx: &LateContext<'_>, span: Span, arg: Span, parens: Parens) {
+    let mut applicability = Applicability::MachineApplicable;
+    let expr_snippet = snippet_with_applicability(cx, arg, "..", &mut applicability);
+
+    let suggestion = match parens {
+        Parens::Needed => format!("({expr_snippet})"),
+        Parens::Unneeded => expr_snippet.into_owned(),
+    };
+
+    span_lint_and_sugg(
+        cx,
+        IDENTITY_OP,
+        span,
+        "this operation has no effect",
+        "consider reducing it to",
+        suggestion,
+        applicability,
+    );
+}
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/operators/modulo_arithmetic.rs b/clippy_lints/src/operators/modulo_arithmetic.rs
new file mode 100644
index 00000000000..af4e74947f4
--- /dev/null
+++ b/clippy_lints/src/operators/modulo_arithmetic.rs
@@ -0,0 +1,126 @@
+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};
+use rustc_lint::LateContext;
+use rustc_middle::ty::{self, Ty};
+use std::fmt::Display;
+
+use super::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>,
+    is_negative: bool,
+    is_integral: bool,
+}
+
+fn analyze_operand(operand: &Expr<'_>, cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<OperandInfo> {
+    match constant(cx, cx.typeck_results(), operand) {
+        Some((Constant::Int(v), _)) => match *cx.typeck_results().expr_ty(expr).kind() {
+            ty::Int(ity) => {
+                let value = sext(cx.tcx, v, ity);
+                return Some(OperandInfo {
+                    string_representation: Some(value.to_string()),
+                    is_negative: value < 0,
+                    is_integral: true,
+                });
+            },
+            ty::Uint(_) => {
+                return Some(OperandInfo {
+                    string_representation: None,
+                    is_negative: false,
+                    is_integral: true,
+                });
+            },
+            _ => {},
+        },
+        Some((Constant::F32(f), _)) => {
+            return Some(floating_point_operand_info(&f));
+        },
+        Some((Constant::F64(f), _)) => {
+            return Some(floating_point_operand_info(&f));
+        },
+        _ => {},
+    }
+    None
+}
+
+fn floating_point_operand_info<T: Display + PartialOrd + From<f32>>(f: &T) -> OperandInfo {
+    OperandInfo {
+        string_representation: Some(format!("{:.3}", *f)),
+        is_negative: *f < 0.0.into(),
+        is_integral: false,
+    }
+}
+
+fn might_have_negative_value(t: Ty<'_>) -> bool {
+    t.is_signed() || t.is_floating_point()
+}
+
+fn check_const_operands<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'_>,
+    lhs_operand: &OperandInfo,
+    rhs_operand: &OperandInfo,
+) {
+    if lhs_operand.is_negative ^ rhs_operand.is_negative {
+        span_lint_and_then(
+            cx,
+            MODULO_ARITHMETIC,
+            expr.span,
+            &format!(
+                "you are using modulo operator on constants with different signs: `{} % {}`",
+                lhs_operand.string_representation.as_ref().unwrap(),
+                rhs_operand.string_representation.as_ref().unwrap()
+            ),
+            |diag| {
+                diag.note("double check for expected result especially when interoperating with different languages");
+                if lhs_operand.is_integral {
+                    diag.note("or consider using `rem_euclid` or similar function");
+                }
+            },
+        );
+    }
+}
+
+fn check_non_const_operands<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, operand: &Expr<'_>) {
+    let operand_type = cx.typeck_results().expr_ty(operand);
+    if might_have_negative_value(operand_type) {
+        span_lint_and_then(
+            cx,
+            MODULO_ARITHMETIC,
+            expr.span,
+            "you are using modulo operator on types that might have different signs",
+            |diag| {
+                diag.note("double check for expected result especially when interoperating with different languages");
+                if operand_type.is_integral() {
+                    diag.note("or consider using `rem_euclid` or similar function");
+                }
+            },
+        );
+    }
+}
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,
+                );
+            },
+        );
+    }
+}