about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJason Newcomb <jsnewcomb@pm.me>2022-06-01 02:06:12 -0400
committerJason Newcomb <jsnewcomb@pm.me>2022-06-28 12:51:30 -0400
commitc0b0ee5bdcbf9005cdd7f48e0ad350b42387e1c6 (patch)
treeedef4f3565f01eada156d73d12e8ff46f6a6fb9a
parenta8df16ae1df9084dc8e20e835f3de981e32d110c (diff)
downloadrust-c0b0ee5bdcbf9005cdd7f48e0ad350b42387e1c6.tar.gz
rust-c0b0ee5bdcbf9005cdd7f48e0ad350b42387e1c6.zip
Move some lints from `Misc` to `Operators`
-rw-r--r--clippy_lints/src/lib.register_all.rs6
-rw-r--r--clippy_lints/src/lib.register_correctness.rs4
-rw-r--r--clippy_lints/src/lib.register_lints.rs10
-rw-r--r--clippy_lints/src/lib.register_pedantic.rs2
-rw-r--r--clippy_lints/src/lib.register_perf.rs2
-rw-r--r--clippy_lints/src/lib.register_restriction.rs2
-rw-r--r--clippy_lints/src/misc.rs483
-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/float_cmp.rs139
-rw-r--r--clippy_lints/src/operators/mod.rs163
-rw-r--r--clippy_lints/src/operators/modulo_one.rs26
12 files changed, 524 insertions, 490 deletions
diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs
index fb08d7d112d..06f8a6325ff 100644
--- a/clippy_lints/src/lib.register_all.rs
+++ b/clippy_lints/src/lib.register_all.rs
@@ -212,9 +212,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(methods::WRONG_SELF_CONVENTION),
     LintId::of(methods::ZST_OFFSET),
     LintId::of(minmax::MIN_MAX),
-    LintId::of(misc::CMP_NAN),
-    LintId::of(misc::CMP_OWNED),
-    LintId::of(misc::MODULO_ONE),
     LintId::of(misc::SHORT_CIRCUIT_STATEMENT),
     LintId::of(misc::TOPLEVEL_REF_ARG),
     LintId::of(misc::ZERO_PTR),
@@ -251,6 +248,8 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(operators::ABSURD_EXTREME_COMPARISONS),
     LintId::of(operators::ASSIGN_OP_PATTERN),
     LintId::of(operators::BAD_BIT_MASK),
+    LintId::of(operators::CMP_NAN),
+    LintId::of(operators::CMP_OWNED),
     LintId::of(operators::DOUBLE_COMPARISONS),
     LintId::of(operators::DURATION_SUBSEC),
     LintId::of(operators::EQ_OP),
@@ -259,6 +258,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(operators::IDENTITY_OP),
     LintId::of(operators::INEFFECTIVE_BIT_MASK),
     LintId::of(operators::MISREFACTORED_ASSIGN_OP),
+    LintId::of(operators::MODULO_ONE),
     LintId::of(operators::OP_REF),
     LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP),
     LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL),
diff --git a/clippy_lints/src/lib.register_correctness.rs b/clippy_lints/src/lib.register_correctness.rs
index b8362d5a72d..7133b6cdd88 100644
--- a/clippy_lints/src/lib.register_correctness.rs
+++ b/clippy_lints/src/lib.register_correctness.rs
@@ -42,15 +42,15 @@ store.register_group(true, "clippy::correctness", Some("clippy_correctness"), ve
     LintId::of(methods::UNINIT_ASSUMED_INIT),
     LintId::of(methods::ZST_OFFSET),
     LintId::of(minmax::MIN_MAX),
-    LintId::of(misc::CMP_NAN),
-    LintId::of(misc::MODULO_ONE),
     LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
     LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
     LintId::of(operators::ABSURD_EXTREME_COMPARISONS),
     LintId::of(operators::BAD_BIT_MASK),
+    LintId::of(operators::CMP_NAN),
     LintId::of(operators::EQ_OP),
     LintId::of(operators::ERASING_OP),
     LintId::of(operators::INEFFECTIVE_BIT_MASK),
+    LintId::of(operators::MODULO_ONE),
     LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP),
     LintId::of(ptr::INVALID_NULL_PTR_USAGE),
     LintId::of(ptr::MUT_FROM_REF),
diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs
index 7acfdf56840..139b55e965f 100644
--- a/clippy_lints/src/lib.register_lints.rs
+++ b/clippy_lints/src/lib.register_lints.rs
@@ -356,11 +356,6 @@ store.register_lints(&[
     methods::WRONG_SELF_CONVENTION,
     methods::ZST_OFFSET,
     minmax::MIN_MAX,
-    misc::CMP_NAN,
-    misc::CMP_OWNED,
-    misc::FLOAT_CMP,
-    misc::FLOAT_CMP_CONST,
-    misc::MODULO_ONE,
     misc::SHORT_CIRCUIT_STATEMENT,
     misc::TOPLEVEL_REF_ARG,
     misc::USED_UNDERSCORE_BINDING,
@@ -424,17 +419,22 @@ store.register_lints(&[
     operators::ABSURD_EXTREME_COMPARISONS,
     operators::ASSIGN_OP_PATTERN,
     operators::BAD_BIT_MASK,
+    operators::CMP_NAN,
+    operators::CMP_OWNED,
     operators::DOUBLE_COMPARISONS,
     operators::DURATION_SUBSEC,
     operators::EQ_OP,
     operators::ERASING_OP,
     operators::FLOAT_ARITHMETIC,
+    operators::FLOAT_CMP,
+    operators::FLOAT_CMP_CONST,
     operators::FLOAT_EQUALITY_WITHOUT_ABS,
     operators::IDENTITY_OP,
     operators::INEFFECTIVE_BIT_MASK,
     operators::INTEGER_ARITHMETIC,
     operators::INTEGER_DIVISION,
     operators::MISREFACTORED_ASSIGN_OP,
+    operators::MODULO_ONE,
     operators::OP_REF,
     operators::VERBOSE_BIT_MASK,
     option_env_unwrap::OPTION_ENV_UNWRAP,
diff --git a/clippy_lints/src/lib.register_pedantic.rs b/clippy_lints/src/lib.register_pedantic.rs
index b1603afa25f..0c02c2bef56 100644
--- a/clippy_lints/src/lib.register_pedantic.rs
+++ b/clippy_lints/src/lib.register_pedantic.rs
@@ -64,7 +64,6 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
     LintId::of(methods::INEFFICIENT_TO_STRING),
     LintId::of(methods::MAP_UNWRAP_OR),
     LintId::of(methods::UNNECESSARY_JOIN),
-    LintId::of(misc::FLOAT_CMP),
     LintId::of(misc::USED_UNDERSCORE_BINDING),
     LintId::of(mismatching_type_param_order::MISMATCHING_TYPE_PARAM_ORDER),
     LintId::of(mut_mut::MUT_MUT),
@@ -75,6 +74,7 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
     LintId::of(no_effect::NO_EFFECT_UNDERSCORE_BINDING),
     LintId::of(non_expressive_names::MANY_SINGLE_CHAR_NAMES),
     LintId::of(non_expressive_names::SIMILAR_NAMES),
+    LintId::of(operators::FLOAT_CMP),
     LintId::of(operators::VERBOSE_BIT_MASK),
     LintId::of(pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE),
     LintId::of(pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF),
diff --git a/clippy_lints/src/lib.register_perf.rs b/clippy_lints/src/lib.register_perf.rs
index 2233b118849..6bf519c24e8 100644
--- a/clippy_lints/src/lib.register_perf.rs
+++ b/clippy_lints/src/lib.register_perf.rs
@@ -22,7 +22,7 @@ store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![
     LintId::of(methods::OR_FUN_CALL),
     LintId::of(methods::SINGLE_CHAR_PATTERN),
     LintId::of(methods::UNNECESSARY_TO_OWNED),
-    LintId::of(misc::CMP_OWNED),
+    LintId::of(operators::CMP_OWNED),
     LintId::of(redundant_clone::REDUNDANT_CLONE),
     LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
     LintId::of(types::BOX_COLLECTION),
diff --git a/clippy_lints/src/lib.register_restriction.rs b/clippy_lints/src/lib.register_restriction.rs
index 53d968c4710..e66dc67f3dc 100644
--- a/clippy_lints/src/lib.register_restriction.rs
+++ b/clippy_lints/src/lib.register_restriction.rs
@@ -38,7 +38,6 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve
     LintId::of(methods::FILETYPE_IS_FILE),
     LintId::of(methods::GET_UNWRAP),
     LintId::of(methods::UNWRAP_USED),
-    LintId::of(misc::FLOAT_CMP_CONST),
     LintId::of(misc_early::SEPARATED_LITERAL_SUFFIX),
     LintId::of(misc_early::UNNEEDED_FIELD_PATTERN),
     LintId::of(misc_early::UNSEPARATED_LITERAL_SUFFIX),
@@ -50,6 +49,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve
     LintId::of(module_style::SELF_NAMED_MODULE_FILES),
     LintId::of(modulo_arithmetic::MODULO_ARITHMETIC),
     LintId::of(operators::FLOAT_ARITHMETIC),
+    LintId::of(operators::FLOAT_CMP_CONST),
     LintId::of(operators::INTEGER_ARITHMETIC),
     LintId::of(operators::INTEGER_DIVISION),
     LintId::of(panic_in_result_fn::PANIC_IN_RESULT_FN),
diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs
index 01bf871198a..df2430ced6b 100644
--- a/clippy_lints/src/misc.rs
+++ b/clippy_lints/src/misc.rs
@@ -1,28 +1,21 @@
-use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_hir_and_then};
 use clippy_utils::source::{snippet, snippet_opt};
-use clippy_utils::ty::{implements_trait, is_copy};
 use if_chain::if_chain;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::FnKind;
 use rustc_hir::{
     self as hir, def, BinOpKind, BindingAnnotation, Body, Expr, ExprKind, FnDecl, HirId, Mutability, PatKind, Stmt,
-    StmtKind, TyKind, UnOp,
+    StmtKind, TyKind,
 };
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::lint::in_external_macro;
-use rustc_middle::ty::{self, Ty};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::hygiene::DesugaringKind;
 use rustc_span::source_map::{ExpnKind, Span};
-use rustc_span::symbol::sym;
 
-use clippy_utils::consts::{constant, Constant};
 use clippy_utils::sugg::Sugg;
-use clippy_utils::{
-    get_item_name, get_parent_expr, in_constant, is_integer_const, iter_input_pats, last_path_segment,
-    match_any_def_paths, path_def_id, paths, unsext, SpanlessEq,
-};
+use clippy_utils::{get_parent_expr, in_constant, iter_input_pats, last_path_segment, SpanlessEq};
 
 declare_clippy_lint! {
     /// ### What it does
@@ -58,122 +51,6 @@ declare_clippy_lint! {
     style,
     "an entire binding declared as `ref`, in a function argument or a `let` statement"
 }
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for comparisons to NaN.
-    ///
-    /// ### Why is this bad?
-    /// NaN does not compare meaningfully to anything – not
-    /// even itself – so those comparisons are simply wrong.
-    ///
-    /// ### Example
-    /// ```rust
-    /// # let x = 1.0;
-    /// if x == f32::NAN { }
-    /// ```
-    ///
-    /// Use instead:
-    /// ```rust
-    /// # let x = 1.0f32;
-    /// if x.is_nan() { }
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub CMP_NAN,
-    correctness,
-    "comparisons to `NAN`, which will always return false, probably not intended"
-}
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for (in-)equality comparisons on floating-point
-    /// values (apart from zero), except in functions called `*eq*` (which probably
-    /// implement equality for a type involving floats).
-    ///
-    /// ### Why is this bad?
-    /// Floating point calculations are usually imprecise, so
-    /// asking if two values are *exactly* equal is asking for trouble. For a good
-    /// guide on what to do, see [the floating point
-    /// guide](http://www.floating-point-gui.de/errors/comparison).
-    ///
-    /// ### Example
-    /// ```rust
-    /// let x = 1.2331f64;
-    /// let y = 1.2332f64;
-    ///
-    /// if y == 1.23f64 { }
-    /// if y != x {} // where both are floats
-    /// ```
-    ///
-    /// Use instead:
-    /// ```rust
-    /// # let x = 1.2331f64;
-    /// # let y = 1.2332f64;
-    /// let error_margin = f64::EPSILON; // Use an epsilon for comparison
-    /// // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead.
-    /// // let error_margin = std::f64::EPSILON;
-    /// if (y - 1.23f64).abs() < error_margin { }
-    /// if (y - x).abs() > error_margin { }
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub FLOAT_CMP,
-    pedantic,
-    "using `==` or `!=` on float values instead of comparing difference with an epsilon"
-}
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for conversions to owned values just for the sake
-    /// of a comparison.
-    ///
-    /// ### Why is this bad?
-    /// The comparison can operate on a reference, so creating
-    /// an owned value effectively throws it away directly afterwards, which is
-    /// needlessly consuming code and heap space.
-    ///
-    /// ### Example
-    /// ```rust
-    /// # let x = "foo";
-    /// # let y = String::from("foo");
-    /// if x.to_owned() == y {}
-    /// ```
-    ///
-    /// Use instead:
-    /// ```rust
-    /// # let x = "foo";
-    /// # let y = String::from("foo");
-    /// if x == y {}
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub CMP_OWNED,
-    perf,
-    "creating owned instances for comparing with others, e.g., `x == \"foo\".to_string()`"
-}
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for getting the remainder of a division by one or minus
-    /// one.
-    ///
-    /// ### Why is this bad?
-    /// The result for a divisor of one can only ever be zero; for
-    /// minus one it can cause panic/overflow (if the left operand is the minimal value of
-    /// the respective integer type) or results in zero. No one will write such code
-    /// deliberately, unless trying to win an Underhanded Rust Contest. Even for that
-    /// contest, it's probably a bad idea. Use something more underhanded.
-    ///
-    /// ### Example
-    /// ```rust
-    /// # let x = 1;
-    /// let a = x % 1;
-    /// let a = x % -1;
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub MODULO_ONE,
-    correctness,
-    "taking a number modulo +/-1, which can either panic/overflow or always returns 0"
-}
-
 declare_clippy_lint! {
     /// ### What it does
     /// Checks for the use of bindings with a single leading
@@ -244,51 +121,11 @@ declare_clippy_lint! {
     "using `0 as *{const, mut} T`"
 }
 
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for (in-)equality comparisons on floating-point
-    /// value and constant, except in functions called `*eq*` (which probably
-    /// implement equality for a type involving floats).
-    ///
-    /// ### Why is this bad?
-    /// Floating point calculations are usually imprecise, so
-    /// asking if two values are *exactly* equal is asking for trouble. For a good
-    /// guide on what to do, see [the floating point
-    /// guide](http://www.floating-point-gui.de/errors/comparison).
-    ///
-    /// ### Example
-    /// ```rust
-    /// let x: f64 = 1.0;
-    /// const ONE: f64 = 1.00;
-    ///
-    /// if x == ONE { } // where both are floats
-    /// ```
-    ///
-    /// Use instead:
-    /// ```rust
-    /// # let x: f64 = 1.0;
-    /// # const ONE: f64 = 1.00;
-    /// let error_margin = f64::EPSILON; // Use an epsilon for comparison
-    /// // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead.
-    /// // let error_margin = std::f64::EPSILON;
-    /// if (x - ONE).abs() < error_margin { }
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub FLOAT_CMP_CONST,
-    restriction,
-    "using `==` or `!=` on float constants instead of comparing difference with an epsilon"
-}
-
 declare_lint_pass!(MiscLints => [
     TOPLEVEL_REF_ARG,
-    CMP_NAN,
-    FLOAT_CMP,
-    CMP_OWNED,
-    MODULO_ONE,
     USED_UNDERSCORE_BINDING,
     SHORT_CIRCUIT_STATEMENT,
     ZERO_PTR,
-    FLOAT_CMP_CONST
 ]);
 
 impl<'tcx> LateLintPass<'tcx> for MiscLints {
@@ -398,16 +235,9 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints {
     }
 
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        match expr.kind {
-            ExprKind::Cast(e, ty) => {
-                check_cast(cx, expr.span, e, ty);
-                return;
-            },
-            ExprKind::Binary(ref cmp, left, right) => {
-                check_binary(cx, expr, cmp, left, right);
-                return;
-            },
-            _ => {},
+        if let ExprKind::Cast(e, ty) = expr.kind {
+            check_cast(cx, expr.span, e, ty);
+            return;
         }
         if in_attributes_expansion(expr) || expr.span.is_desugaring(DesugaringKind::Await) {
             // Don't lint things expanded by #[derive(...)], etc or `await` desugaring
@@ -455,236 +285,6 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints {
     }
 }
 
-fn get_lint_and_message(
-    is_comparing_constants: bool,
-    is_comparing_arrays: bool,
-) -> (&'static rustc_lint::Lint, &'static str) {
-    if is_comparing_constants {
-        (
-            FLOAT_CMP_CONST,
-            if is_comparing_arrays {
-                "strict comparison of `f32` or `f64` constant arrays"
-            } else {
-                "strict comparison of `f32` or `f64` constant"
-            },
-        )
-    } else {
-        (
-            FLOAT_CMP,
-            if is_comparing_arrays {
-                "strict comparison of `f32` or `f64` arrays"
-            } else {
-                "strict comparison of `f32` or `f64`"
-            },
-        )
-    }
-}
-
-fn check_nan(cx: &LateContext<'_>, expr: &Expr<'_>, cmp_expr: &Expr<'_>) {
-    if_chain! {
-        if !in_constant(cx, cmp_expr.hir_id);
-        if let Some((value, _)) = constant(cx, cx.typeck_results(), expr);
-        if match value {
-            Constant::F32(num) => num.is_nan(),
-            Constant::F64(num) => num.is_nan(),
-            _ => false,
-        };
-        then {
-            span_lint(
-                cx,
-                CMP_NAN,
-                cmp_expr.span,
-                "doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead",
-            );
-        }
-    }
-}
-
-fn is_named_constant<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
-    if let Some((_, res)) = constant(cx, cx.typeck_results(), expr) {
-        res
-    } else {
-        false
-    }
-}
-
-fn is_allowed<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
-    match constant(cx, cx.typeck_results(), expr) {
-        Some((Constant::F32(f), _)) => f == 0.0 || f.is_infinite(),
-        Some((Constant::F64(f), _)) => f == 0.0 || f.is_infinite(),
-        Some((Constant::Vec(vec), _)) => vec.iter().all(|f| match f {
-            Constant::F32(f) => *f == 0.0 || (*f).is_infinite(),
-            Constant::F64(f) => *f == 0.0 || (*f).is_infinite(),
-            _ => false,
-        }),
-        _ => false,
-    }
-}
-
-// Return true if `expr` is the result of `signum()` invoked on a float value.
-fn is_signum(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
-    // The negation of a signum is still a signum
-    if let ExprKind::Unary(UnOp::Neg, child_expr) = expr.kind {
-        return is_signum(cx, child_expr);
-    }
-
-    if_chain! {
-        if let ExprKind::MethodCall(method_name, [ref self_arg, ..], _) = expr.kind;
-        if sym!(signum) == method_name.ident.name;
-        // Check that the receiver of the signum() is a float (expressions[0] is the receiver of
-        // the method call)
-        then {
-            return is_float(cx, self_arg);
-        }
-    }
-    false
-}
-
-fn is_float(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
-    let value = &cx.typeck_results().expr_ty(expr).peel_refs().kind();
-
-    if let ty::Array(arr_ty, _) = value {
-        return matches!(arr_ty.kind(), ty::Float(_));
-    };
-
-    matches!(value, ty::Float(_))
-}
-
-fn is_array(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
-    matches!(&cx.typeck_results().expr_ty(expr).peel_refs().kind(), ty::Array(_, _))
-}
-
-#[expect(clippy::too_many_lines)]
-fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) {
-    #[derive(Default)]
-    struct EqImpl {
-        ty_eq_other: bool,
-        other_eq_ty: bool,
-    }
-
-    impl EqImpl {
-        fn is_implemented(&self) -> bool {
-            self.ty_eq_other || self.other_eq_ty
-        }
-    }
-
-    fn symmetric_partial_eq<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, other: Ty<'tcx>) -> Option<EqImpl> {
-        cx.tcx.lang_items().eq_trait().map(|def_id| EqImpl {
-            ty_eq_other: implements_trait(cx, ty, def_id, &[other.into()]),
-            other_eq_ty: implements_trait(cx, other, def_id, &[ty.into()]),
-        })
-    }
-
-    let typeck = cx.typeck_results();
-    let (arg, arg_span) = match expr.kind {
-        ExprKind::MethodCall(.., [arg], _)
-            if typeck
-                .type_dependent_def_id(expr.hir_id)
-                .and_then(|id| cx.tcx.trait_of_item(id))
-                .map_or(false, |id| {
-                    matches!(cx.tcx.get_diagnostic_name(id), Some(sym::ToString | sym::ToOwned))
-                }) =>
-        {
-            (arg, arg.span)
-        },
-        ExprKind::Call(path, [arg])
-            if path_def_id(cx, path)
-                .and_then(|id| match_any_def_paths(cx, id, &[&paths::FROM_STR_METHOD, &paths::FROM_FROM]))
-                .map_or(false, |idx| match idx {
-                    0 => true,
-                    1 => !is_copy(cx, typeck.expr_ty(expr)),
-                    _ => false,
-                }) =>
-        {
-            (arg, arg.span)
-        },
-        _ => return,
-    };
-
-    let arg_ty = typeck.expr_ty(arg);
-    let other_ty = typeck.expr_ty(other);
-
-    let without_deref = symmetric_partial_eq(cx, arg_ty, other_ty).unwrap_or_default();
-    let with_deref = arg_ty
-        .builtin_deref(true)
-        .and_then(|tam| symmetric_partial_eq(cx, tam.ty, other_ty))
-        .unwrap_or_default();
-
-    if !with_deref.is_implemented() && !without_deref.is_implemented() {
-        return;
-    }
-
-    let other_gets_derefed = matches!(other.kind, ExprKind::Unary(UnOp::Deref, _));
-
-    let lint_span = if other_gets_derefed {
-        expr.span.to(other.span)
-    } else {
-        expr.span
-    };
-
-    span_lint_and_then(
-        cx,
-        CMP_OWNED,
-        lint_span,
-        "this creates an owned instance just for comparison",
-        |diag| {
-            // This also catches `PartialEq` implementations that call `to_owned`.
-            if other_gets_derefed {
-                diag.span_label(lint_span, "try implementing the comparison without allocating");
-                return;
-            }
-
-            let arg_snip = snippet(cx, arg_span, "..");
-            let expr_snip;
-            let eq_impl;
-            if with_deref.is_implemented() {
-                expr_snip = format!("*{}", arg_snip);
-                eq_impl = with_deref;
-            } else {
-                expr_snip = arg_snip.to_string();
-                eq_impl = without_deref;
-            };
-
-            let span;
-            let hint;
-            if (eq_impl.ty_eq_other && left) || (eq_impl.other_eq_ty && !left) {
-                span = expr.span;
-                hint = expr_snip;
-            } else {
-                span = expr.span.to(other.span);
-
-                let cmp_span = if other.span < expr.span {
-                    other.span.between(expr.span)
-                } else {
-                    expr.span.between(other.span)
-                };
-                if eq_impl.ty_eq_other {
-                    hint = format!(
-                        "{}{}{}",
-                        expr_snip,
-                        snippet(cx, cmp_span, ".."),
-                        snippet(cx, other.span, "..")
-                    );
-                } else {
-                    hint = format!(
-                        "{}{}{}",
-                        snippet(cx, other.span, ".."),
-                        snippet(cx, cmp_span, ".."),
-                        expr_snip
-                    );
-                }
-            }
-
-            diag.span_suggestion(
-                span,
-                "try",
-                hint,
-                Applicability::MachineApplicable, // snippet
-            );
-        },
-    );
-}
-
 /// Heuristic to see if an expression is used. Should be compatible with
 /// `unused_variables`'s idea
 /// of what it means for an expression to be "used".
@@ -740,74 +340,3 @@ fn check_cast(cx: &LateContext<'_>, span: Span, e: &Expr<'_>, ty: &hir::Ty<'_>)
         }
     }
 }
-
-fn check_binary<'a>(
-    cx: &LateContext<'a>,
-    expr: &Expr<'_>,
-    cmp: &rustc_span::source_map::Spanned<rustc_hir::BinOpKind>,
-    left: &'a Expr<'_>,
-    right: &'a Expr<'_>,
-) {
-    let op = cmp.node;
-    if op.is_comparison() {
-        check_nan(cx, left, expr);
-        check_nan(cx, right, expr);
-        check_to_owned(cx, left, right, true);
-        check_to_owned(cx, right, left, false);
-    }
-    if (op == BinOpKind::Eq || op == BinOpKind::Ne) && (is_float(cx, left) || is_float(cx, right)) {
-        if is_allowed(cx, left) || is_allowed(cx, right) {
-            return;
-        }
-
-        // Allow comparing the results of signum()
-        if is_signum(cx, left) && is_signum(cx, right) {
-            return;
-        }
-
-        if let Some(name) = get_item_name(cx, expr) {
-            let name = name.as_str();
-            if name == "eq" || name == "ne" || name == "is_nan" || name.starts_with("eq_") || name.ends_with("_eq") {
-                return;
-            }
-        }
-        let is_comparing_arrays = is_array(cx, left) || is_array(cx, right);
-        let (lint, msg) = get_lint_and_message(
-            is_named_constant(cx, left) || is_named_constant(cx, right),
-            is_comparing_arrays,
-        );
-        span_lint_and_then(cx, lint, expr.span, msg, |diag| {
-            let lhs = Sugg::hir(cx, left, "..");
-            let rhs = Sugg::hir(cx, right, "..");
-
-            if !is_comparing_arrays {
-                diag.span_suggestion(
-                    expr.span,
-                    "consider comparing them within some margin of error",
-                    format!(
-                        "({}).abs() {} error_margin",
-                        lhs - rhs,
-                        if op == BinOpKind::Eq { '<' } else { '>' }
-                    ),
-                    Applicability::HasPlaceholders, // snippet
-                );
-            }
-            diag.note("`f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`");
-        });
-    } else if op == BinOpKind::Rem {
-        if is_integer_const(cx, right, 1) {
-            span_lint(cx, MODULO_ONE, expr.span, "any number modulo 1 will be 0");
-        }
-
-        if let ty::Int(ity) = cx.typeck_results().expr_ty(right).kind() {
-            if is_integer_const(cx, right, unsext(cx.tcx, -1, *ity)) {
-                span_lint(
-                    cx,
-                    MODULO_ONE,
-                    expr.span,
-                    "any number modulo -1 will panic/overflow or result in 0",
-                );
-            }
-        };
-    }
-}
diff --git a/clippy_lints/src/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/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/mod.rs b/clippy_lints/src/operators/mod.rs
index ea6a0083bc8..e4aff267fef 100644
--- a/clippy_lints/src/operators/mod.rs
+++ b/clippy_lints/src/operators/mod.rs
@@ -5,14 +5,18 @@ 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_one;
 mod numeric_arithmetic;
 mod op_ref;
 mod verbose_bit_mask;
@@ -464,6 +468,156 @@ declare_clippy_lint! {
     "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"
+}
+
 pub struct Operators {
     arithmetic_context: numeric_arithmetic::Context,
     verbose_bit_mask_threshold: u64,
@@ -485,6 +639,11 @@ impl_lint_pass!(Operators => [
     FLOAT_EQUALITY_WITHOUT_ABS,
     IDENTITY_OP,
     INTEGER_DIVISION,
+    CMP_NAN,
+    CMP_OWNED,
+    FLOAT_CMP,
+    FLOAT_CMP_CONST,
+    MODULO_ONE,
 ]);
 impl Operators {
     pub fn new(verbose_bit_mask_threshold: u64) -> Self {
@@ -515,6 +674,10 @@ impl<'tcx> LateLintPass<'tcx> for Operators {
                 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);
             },
             ExprKind::AssignOp(op, lhs, rhs) => {
                 self.arithmetic_context.check_binary(cx, e, op.node, lhs, rhs);
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",
+                );
+            }
+        };
+    }
+}