about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_lint/messages.ftl5
-rw-r--r--compiler/rustc_lint/src/lints.rs30
-rw-r--r--compiler/rustc_lint/src/types.rs102
-rw-r--r--compiler/rustc_span/src/symbol.rs2
-rw-r--r--library/core/src/num/f32.rs1
-rw-r--r--library/core/src/num/f64.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/declared_lints.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/cmp_nan.rs30
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/mod.rs28
-rw-r--r--src/tools/clippy/clippy_lints/src/renamed_lints.rs1
-rw-r--r--src/tools/clippy/tests/ui/cmp_nan.rs34
-rw-r--r--src/tools/clippy/tests/ui/cmp_nan.stderr148
-rw-r--r--src/tools/clippy/tests/ui/crashes/mut_mut_macro.rs5
-rw-r--r--src/tools/clippy/tests/ui/rename.fixed2
-rw-r--r--src/tools/clippy/tests/ui/rename.rs2
-rw-r--r--src/tools/clippy/tests/ui/rename.stderr110
-rw-r--r--src/tools/clippy/tests/ui/unknown_clippy_lints.fixed2
-rw-r--r--src/tools/clippy/tests/ui/unknown_clippy_lints.rs2
-rw-r--r--src/tools/clippy/tests/ui/unknown_clippy_lints.stderr6
-rw-r--r--tests/ui/issues/issue-50811.rs1
-rw-r--r--tests/ui/lint/invalid-nan-comparison-suggestion.fixed36
-rw-r--r--tests/ui/lint/invalid-nan-comparison-suggestion.rs39
-rw-r--r--tests/ui/lint/invalid-nan-comparison-suggestion.stderr114
-rw-r--r--tests/ui/lint/invalid-nan-comparison.rs51
-rw-r--r--tests/ui/lint/invalid-nan-comparison.stderr159
25 files changed, 604 insertions, 308 deletions
diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl
index 51eb8210d46..5e13ee7b8a4 100644
--- a/compiler/rustc_lint/messages.ftl
+++ b/compiler/rustc_lint/messages.ftl
@@ -314,6 +314,11 @@ lint_invalid_from_utf8_checked = calls to `{$method}` with a invalid literal alw
 lint_invalid_from_utf8_unchecked = calls to `{$method}` with a invalid literal are undefined behavior
     .label = the literal was valid UTF-8 up to the {$valid_up_to} bytes
 
+lint_invalid_nan_comparisons_eq_ne = incorrect NaN comparison, NaN cannot be directly compared to itself
+    .suggestion = use `f32::is_nan()` or `f64::is_nan()` instead
+
+lint_invalid_nan_comparisons_lt_le_gt_ge = incorrect NaN comparison, NaN is not orderable
+
 lint_lintpass_by_hand = implementing `LintPass` by hand
     .help = try using `declare_lint_pass!` or `impl_lint_pass!` instead
 
diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs
index 03dbffcc2c4..e990c771bdf 100644
--- a/compiler/rustc_lint/src/lints.rs
+++ b/compiler/rustc_lint/src/lints.rs
@@ -1434,6 +1434,36 @@ pub struct OverflowingLiteral<'a> {
 #[diag(lint_unused_comparisons)]
 pub struct UnusedComparisons;
 
+#[derive(LintDiagnostic)]
+pub enum InvalidNanComparisons {
+    #[diag(lint_invalid_nan_comparisons_eq_ne)]
+    EqNe {
+        #[subdiagnostic]
+        suggestion: InvalidNanComparisonsSuggestion,
+    },
+    #[diag(lint_invalid_nan_comparisons_lt_le_gt_ge)]
+    LtLeGtGe,
+}
+
+#[derive(Subdiagnostic)]
+pub enum InvalidNanComparisonsSuggestion {
+    #[multipart_suggestion(
+        lint_suggestion,
+        style = "verbose",
+        applicability = "machine-applicable"
+    )]
+    Spanful {
+        #[suggestion_part(code = "!")]
+        neg: Option<Span>,
+        #[suggestion_part(code = ".is_nan()")]
+        float: Span,
+        #[suggestion_part(code = "")]
+        nan_plus_binop: Span,
+    },
+    #[help(lint_suggestion)]
+    Spanless,
+}
+
 pub struct ImproperCTypes<'a> {
     pub ty: Ty<'a>,
     pub desc: &'a str,
diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs
index 4bf4fda8292..264a59c5585 100644
--- a/compiler/rustc_lint/src/types.rs
+++ b/compiler/rustc_lint/src/types.rs
@@ -2,10 +2,10 @@ use crate::{
     fluent_generated as fluent,
     lints::{
         AtomicOrderingFence, AtomicOrderingLoad, AtomicOrderingStore, ImproperCTypes,
-        InvalidAtomicOrderingDiag, OnlyCastu8ToChar, OverflowingBinHex, OverflowingBinHexSign,
-        OverflowingBinHexSub, OverflowingInt, OverflowingIntHelp, OverflowingLiteral,
-        OverflowingUInt, RangeEndpointOutOfRange, UnusedComparisons, UseInclusiveRange,
-        VariantSizeDifferencesDiag,
+        InvalidAtomicOrderingDiag, InvalidNanComparisons, InvalidNanComparisonsSuggestion,
+        OnlyCastu8ToChar, OverflowingBinHex, OverflowingBinHexSign, OverflowingBinHexSub,
+        OverflowingInt, OverflowingIntHelp, OverflowingLiteral, OverflowingUInt,
+        RangeEndpointOutOfRange, UnusedComparisons, UseInclusiveRange, VariantSizeDifferencesDiag,
     },
 };
 use crate::{LateContext, LateLintPass, LintContext};
@@ -113,13 +113,35 @@ declare_lint! {
     "detects enums with widely varying variant sizes"
 }
 
+declare_lint! {
+    /// The `invalid_nan_comparisons` lint checks comparison with `f32::NAN` or `f64::NAN`
+    /// as one of the operand.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// let a = 2.3f32;
+    /// if a == f32::NAN {}
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// NaN does not compare meaningfully to anything – not
+    /// even itself – so those comparisons are always false.
+    INVALID_NAN_COMPARISONS,
+    Warn,
+    "detects invalid floating point NaN comparisons"
+}
+
 #[derive(Copy, Clone)]
 pub struct TypeLimits {
     /// Id of the last visited negated expression
     negated_expr_id: Option<hir::HirId>,
 }
 
-impl_lint_pass!(TypeLimits => [UNUSED_COMPARISONS, OVERFLOWING_LITERALS]);
+impl_lint_pass!(TypeLimits => [UNUSED_COMPARISONS, OVERFLOWING_LITERALS, INVALID_NAN_COMPARISONS]);
 
 impl TypeLimits {
     pub fn new() -> TypeLimits {
@@ -486,6 +508,68 @@ fn lint_literal<'tcx>(
     }
 }
 
+fn lint_nan<'tcx>(
+    cx: &LateContext<'tcx>,
+    e: &'tcx hir::Expr<'tcx>,
+    binop: hir::BinOp,
+    l: &'tcx hir::Expr<'tcx>,
+    r: &'tcx hir::Expr<'tcx>,
+) {
+    fn is_nan(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
+        let expr = expr.peel_blocks().peel_borrows();
+        match expr.kind {
+            ExprKind::Path(qpath) => {
+                let Some(def_id) = cx.typeck_results().qpath_res(&qpath, expr.hir_id).opt_def_id() else { return false; };
+
+                matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::f32_nan | sym::f64_nan))
+            }
+            _ => false,
+        }
+    }
+
+    fn eq_ne(
+        e: &hir::Expr<'_>,
+        l: &hir::Expr<'_>,
+        r: &hir::Expr<'_>,
+        f: impl FnOnce(Span, Span) -> InvalidNanComparisonsSuggestion,
+    ) -> InvalidNanComparisons {
+        let suggestion =
+            if let Some(l_span) = l.span.find_ancestor_inside(e.span) &&
+                let Some(r_span) = r.span.find_ancestor_inside(e.span) {
+                f(l_span, r_span)
+            } else {
+                InvalidNanComparisonsSuggestion::Spanless
+            };
+
+        InvalidNanComparisons::EqNe { suggestion }
+    }
+
+    let lint = match binop.node {
+        hir::BinOpKind::Eq | hir::BinOpKind::Ne if is_nan(cx, l) => {
+            eq_ne(e, l, r, |l_span, r_span| InvalidNanComparisonsSuggestion::Spanful {
+                nan_plus_binop: l_span.until(r_span),
+                float: r_span.shrink_to_hi(),
+                neg: (binop.node == hir::BinOpKind::Ne).then(|| r_span.shrink_to_lo()),
+            })
+        }
+        hir::BinOpKind::Eq | hir::BinOpKind::Ne if is_nan(cx, r) => {
+            eq_ne(e, l, r, |l_span, r_span| InvalidNanComparisonsSuggestion::Spanful {
+                nan_plus_binop: l_span.shrink_to_hi().to(r_span),
+                float: l_span.shrink_to_hi(),
+                neg: (binop.node == hir::BinOpKind::Ne).then(|| l_span.shrink_to_lo()),
+            })
+        }
+        hir::BinOpKind::Lt | hir::BinOpKind::Le | hir::BinOpKind::Gt | hir::BinOpKind::Ge
+            if is_nan(cx, l) || is_nan(cx, r) =>
+        {
+            InvalidNanComparisons::LtLeGtGe
+        }
+        _ => return,
+    };
+
+    cx.emit_spanned_lint(INVALID_NAN_COMPARISONS, e.span, lint);
+}
+
 impl<'tcx> LateLintPass<'tcx> for TypeLimits {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx hir::Expr<'tcx>) {
         match e.kind {
@@ -496,8 +580,12 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits {
                 }
             }
             hir::ExprKind::Binary(binop, ref l, ref r) => {
-                if is_comparison(binop) && !check_limits(cx, binop, &l, &r) {
-                    cx.emit_spanned_lint(UNUSED_COMPARISONS, e.span, UnusedComparisons);
+                if is_comparison(binop) {
+                    if !check_limits(cx, binop, &l, &r) {
+                        cx.emit_spanned_lint(UNUSED_COMPARISONS, e.span, UnusedComparisons);
+                    } else {
+                        lint_nan(cx, e, binop, l, r);
+                    }
                 }
             }
             hir::ExprKind::Lit(ref lit) => lint_literal(cx, self, e, lit),
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 2f002e42427..c5ce2575fff 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -701,7 +701,9 @@ symbols! {
         f,
         f16c_target_feature,
         f32,
+        f32_nan,
         f64,
+        f64_nan,
         fabsf32,
         fabsf64,
         fadd_fast,
diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs
index 4a035ad61e1..d050d21c8c5 100644
--- a/library/core/src/num/f32.rs
+++ b/library/core/src/num/f32.rs
@@ -403,6 +403,7 @@ impl f32 {
     /// and the stability of its representation over Rust versions
     /// and target platforms isn't guaranteed.
     #[stable(feature = "assoc_int_consts", since = "1.43.0")]
+    #[rustc_diagnostic_item = "f32_nan"]
     pub const NAN: f32 = 0.0_f32 / 0.0_f32;
     /// Infinity (∞).
     #[stable(feature = "assoc_int_consts", since = "1.43.0")]
diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs
index 3aafc435f1e..d9a738191f7 100644
--- a/library/core/src/num/f64.rs
+++ b/library/core/src/num/f64.rs
@@ -401,6 +401,7 @@ impl f64 {
     /// This constant isn't guaranteed to equal to any specific NaN bitpattern,
     /// and the stability of its representation over Rust versions
     /// and target platforms isn't guaranteed.
+    #[rustc_diagnostic_item = "f64_nan"]
     #[stable(feature = "assoc_int_consts", since = "1.43.0")]
     pub const NAN: f64 = 0.0_f64 / 0.0_f64;
     /// Infinity (∞).
diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs
index 6270d261313..f1d1355123a 100644
--- a/src/tools/clippy/clippy_lints/src/declared_lints.rs
+++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs
@@ -476,7 +476,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::operators::ARITHMETIC_SIDE_EFFECTS_INFO,
     crate::operators::ASSIGN_OP_PATTERN_INFO,
     crate::operators::BAD_BIT_MASK_INFO,
-    crate::operators::CMP_NAN_INFO,
     crate::operators::CMP_OWNED_INFO,
     crate::operators::DOUBLE_COMPARISONS_INFO,
     crate::operators::DURATION_SUBSEC_INFO,
diff --git a/src/tools/clippy/clippy_lints/src/operators/cmp_nan.rs b/src/tools/clippy/clippy_lints/src/operators/cmp_nan.rs
deleted file mode 100644
index e18064b7061..00000000000
--- a/src/tools/clippy/clippy_lints/src/operators/cmp_nan.rs
+++ /dev/null
@@ -1,30 +0,0 @@
-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/src/tools/clippy/clippy_lints/src/operators/mod.rs b/src/tools/clippy/clippy_lints/src/operators/mod.rs
index d63a836e73d..2cf15adda01 100644
--- a/src/tools/clippy/clippy_lints/src/operators/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/mod.rs
@@ -1,7 +1,6 @@
 mod absurd_extreme_comparisons;
 mod assign_op_pattern;
 mod bit_mask;
-mod cmp_nan;
 mod cmp_owned;
 mod double_comparison;
 mod duration_subsec;
@@ -487,31 +486,6 @@ declare_clippy_lint! {
 
 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.
     ///
@@ -775,7 +749,6 @@ impl_lint_pass!(Operators => [
     FLOAT_EQUALITY_WITHOUT_ABS,
     IDENTITY_OP,
     INTEGER_DIVISION,
-    CMP_NAN,
     CMP_OWNED,
     FLOAT_CMP,
     FLOAT_CMP_CONST,
@@ -816,7 +789,6 @@ 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);
diff --git a/src/tools/clippy/clippy_lints/src/renamed_lints.rs b/src/tools/clippy/clippy_lints/src/renamed_lints.rs
index f76fa76f076..cbcd11debfd 100644
--- a/src/tools/clippy/clippy_lints/src/renamed_lints.rs
+++ b/src/tools/clippy/clippy_lints/src/renamed_lints.rs
@@ -33,6 +33,7 @@ pub static RENAMED_LINTS: &[(&str, &str)] = &[
     ("clippy::zero_width_space", "clippy::invisible_characters"),
     ("clippy::cast_ref_to_mut", "cast_ref_to_mut"),
     ("clippy::clone_double_ref", "suspicious_double_ref_op"),
+    ("clippy::cmp_nan", "invalid_nan_comparisons"),
     ("clippy::drop_bounds", "drop_bounds"),
     ("clippy::drop_copy", "dropping_copy_types"),
     ("clippy::drop_ref", "dropping_references"),
diff --git a/src/tools/clippy/tests/ui/cmp_nan.rs b/src/tools/clippy/tests/ui/cmp_nan.rs
deleted file mode 100644
index 64ca52b010a..00000000000
--- a/src/tools/clippy/tests/ui/cmp_nan.rs
+++ /dev/null
@@ -1,34 +0,0 @@
-const NAN_F32: f32 = f32::NAN;
-const NAN_F64: f64 = f64::NAN;
-
-#[warn(clippy::cmp_nan)]
-#[allow(clippy::float_cmp, clippy::no_effect, clippy::unnecessary_operation)]
-fn main() {
-    let x = 5f32;
-    x == f32::NAN;
-    x != f32::NAN;
-    x < f32::NAN;
-    x > f32::NAN;
-    x <= f32::NAN;
-    x >= f32::NAN;
-    x == NAN_F32;
-    x != NAN_F32;
-    x < NAN_F32;
-    x > NAN_F32;
-    x <= NAN_F32;
-    x >= NAN_F32;
-
-    let y = 0f64;
-    y == f64::NAN;
-    y != f64::NAN;
-    y < f64::NAN;
-    y > f64::NAN;
-    y <= f64::NAN;
-    y >= f64::NAN;
-    y == NAN_F64;
-    y != NAN_F64;
-    y < NAN_F64;
-    y > NAN_F64;
-    y <= NAN_F64;
-    y >= NAN_F64;
-}
diff --git a/src/tools/clippy/tests/ui/cmp_nan.stderr b/src/tools/clippy/tests/ui/cmp_nan.stderr
deleted file mode 100644
index 867516661a5..00000000000
--- a/src/tools/clippy/tests/ui/cmp_nan.stderr
+++ /dev/null
@@ -1,148 +0,0 @@
-error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
-  --> $DIR/cmp_nan.rs:8:5
-   |
-LL |     x == f32::NAN;
-   |     ^^^^^^^^^^^^^
-   |
-   = note: `-D clippy::cmp-nan` implied by `-D warnings`
-
-error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
-  --> $DIR/cmp_nan.rs:9:5
-   |
-LL |     x != f32::NAN;
-   |     ^^^^^^^^^^^^^
-
-error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
-  --> $DIR/cmp_nan.rs:10:5
-   |
-LL |     x < f32::NAN;
-   |     ^^^^^^^^^^^^
-
-error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
-  --> $DIR/cmp_nan.rs:11:5
-   |
-LL |     x > f32::NAN;
-   |     ^^^^^^^^^^^^
-
-error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
-  --> $DIR/cmp_nan.rs:12:5
-   |
-LL |     x <= f32::NAN;
-   |     ^^^^^^^^^^^^^
-
-error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
-  --> $DIR/cmp_nan.rs:13:5
-   |
-LL |     x >= f32::NAN;
-   |     ^^^^^^^^^^^^^
-
-error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
-  --> $DIR/cmp_nan.rs:14:5
-   |
-LL |     x == NAN_F32;
-   |     ^^^^^^^^^^^^
-
-error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
-  --> $DIR/cmp_nan.rs:15:5
-   |
-LL |     x != NAN_F32;
-   |     ^^^^^^^^^^^^
-
-error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
-  --> $DIR/cmp_nan.rs:16:5
-   |
-LL |     x < NAN_F32;
-   |     ^^^^^^^^^^^
-
-error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
-  --> $DIR/cmp_nan.rs:17:5
-   |
-LL |     x > NAN_F32;
-   |     ^^^^^^^^^^^
-
-error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
-  --> $DIR/cmp_nan.rs:18:5
-   |
-LL |     x <= NAN_F32;
-   |     ^^^^^^^^^^^^
-
-error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
-  --> $DIR/cmp_nan.rs:19:5
-   |
-LL |     x >= NAN_F32;
-   |     ^^^^^^^^^^^^
-
-error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
-  --> $DIR/cmp_nan.rs:22:5
-   |
-LL |     y == f64::NAN;
-   |     ^^^^^^^^^^^^^
-
-error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
-  --> $DIR/cmp_nan.rs:23:5
-   |
-LL |     y != f64::NAN;
-   |     ^^^^^^^^^^^^^
-
-error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
-  --> $DIR/cmp_nan.rs:24:5
-   |
-LL |     y < f64::NAN;
-   |     ^^^^^^^^^^^^
-
-error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
-  --> $DIR/cmp_nan.rs:25:5
-   |
-LL |     y > f64::NAN;
-   |     ^^^^^^^^^^^^
-
-error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
-  --> $DIR/cmp_nan.rs:26:5
-   |
-LL |     y <= f64::NAN;
-   |     ^^^^^^^^^^^^^
-
-error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
-  --> $DIR/cmp_nan.rs:27:5
-   |
-LL |     y >= f64::NAN;
-   |     ^^^^^^^^^^^^^
-
-error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
-  --> $DIR/cmp_nan.rs:28:5
-   |
-LL |     y == NAN_F64;
-   |     ^^^^^^^^^^^^
-
-error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
-  --> $DIR/cmp_nan.rs:29:5
-   |
-LL |     y != NAN_F64;
-   |     ^^^^^^^^^^^^
-
-error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
-  --> $DIR/cmp_nan.rs:30:5
-   |
-LL |     y < NAN_F64;
-   |     ^^^^^^^^^^^
-
-error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
-  --> $DIR/cmp_nan.rs:31:5
-   |
-LL |     y > NAN_F64;
-   |     ^^^^^^^^^^^
-
-error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
-  --> $DIR/cmp_nan.rs:32:5
-   |
-LL |     y <= NAN_F64;
-   |     ^^^^^^^^^^^^
-
-error: doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead
-  --> $DIR/cmp_nan.rs:33:5
-   |
-LL |     y >= NAN_F64;
-   |     ^^^^^^^^^^^^
-
-error: aborting due to 24 previous errors
-
diff --git a/src/tools/clippy/tests/ui/crashes/mut_mut_macro.rs b/src/tools/clippy/tests/ui/crashes/mut_mut_macro.rs
index a238e7896fc..92821b6ecbb 100644
--- a/src/tools/clippy/tests/ui/crashes/mut_mut_macro.rs
+++ b/src/tools/clippy/tests/ui/crashes/mut_mut_macro.rs
@@ -1,4 +1,4 @@
-#![deny(clippy::mut_mut, clippy::zero_ptr, clippy::cmp_nan)]
+#![deny(clippy::mut_mut, clippy::zero_ptr)]
 #![allow(dead_code)]
 
 // FIXME: compiletest + extern crates doesn't work together. To make this test work, it would need
@@ -8,13 +8,12 @@
 //     extern crate lazy_static;
 //     use std::collections::HashMap;
 
-/// ensure that we don't suggest `is_nan` and `is_null` inside constants
+/// ensure that we don't suggest `is_null` inside constants
 /// FIXME: once const fn is stable, suggest these functions again in constants
 
 const BAA: *const i32 = 0 as *const i32;
 static mut BAR: *const i32 = BAA;
 static mut FOO: *const i32 = 0 as *const i32;
-static mut BUH: bool = 42.0 < f32::NAN;
 
 #[allow(unused_variables, unused_mut)]
 fn main() {
diff --git a/src/tools/clippy/tests/ui/rename.fixed b/src/tools/clippy/tests/ui/rename.fixed
index 954145fc4e6..f1b25dc094e 100644
--- a/src/tools/clippy/tests/ui/rename.fixed
+++ b/src/tools/clippy/tests/ui/rename.fixed
@@ -41,6 +41,7 @@
 #![allow(invalid_atomic_ordering)]
 #![allow(invalid_value)]
 #![allow(invalid_from_utf8_unchecked)]
+#![allow(invalid_nan_comparisons)]
 #![allow(let_underscore_drop)]
 #![allow(enum_intrinsics_non_enums)]
 #![allow(non_fmt_panics)]
@@ -55,6 +56,7 @@
 #![warn(clippy::blocks_in_if_conditions)]
 #![warn(clippy::blocks_in_if_conditions)]
 #![warn(clippy::box_collection)]
+#![warn(invalid_nan_comparisons)]
 #![warn(clippy::redundant_static_lifetimes)]
 #![warn(clippy::cognitive_complexity)]
 #![warn(clippy::derived_hash_with_manual_eq)]
diff --git a/src/tools/clippy/tests/ui/rename.rs b/src/tools/clippy/tests/ui/rename.rs
index 067a3109afb..4af511c344c 100644
--- a/src/tools/clippy/tests/ui/rename.rs
+++ b/src/tools/clippy/tests/ui/rename.rs
@@ -41,6 +41,7 @@
 #![allow(invalid_atomic_ordering)]
 #![allow(invalid_value)]
 #![allow(invalid_from_utf8_unchecked)]
+#![allow(invalid_nan_comparisons)]
 #![allow(let_underscore_drop)]
 #![allow(enum_intrinsics_non_enums)]
 #![allow(non_fmt_panics)]
@@ -55,6 +56,7 @@
 #![warn(clippy::block_in_if_condition_expr)]
 #![warn(clippy::block_in_if_condition_stmt)]
 #![warn(clippy::box_vec)]
+#![warn(clippy::cmp_nan)]
 #![warn(clippy::const_static_lifetime)]
 #![warn(clippy::cyclomatic_complexity)]
 #![warn(clippy::derive_hash_xor_eq)]
diff --git a/src/tools/clippy/tests/ui/rename.stderr b/src/tools/clippy/tests/ui/rename.stderr
index 1819d108c57..156a8f96b50 100644
--- a/src/tools/clippy/tests/ui/rename.stderr
+++ b/src/tools/clippy/tests/ui/rename.stderr
@@ -1,5 +1,5 @@
 error: lint `clippy::almost_complete_letter_range` has been renamed to `clippy::almost_complete_range`
-  --> $DIR/rename.rs:53:9
+  --> $DIR/rename.rs:54:9
    |
 LL | #![warn(clippy::almost_complete_letter_range)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::almost_complete_range`
@@ -7,304 +7,310 @@ LL | #![warn(clippy::almost_complete_letter_range)]
    = note: `-D renamed-and-removed-lints` implied by `-D warnings`
 
 error: lint `clippy::blacklisted_name` has been renamed to `clippy::disallowed_names`
-  --> $DIR/rename.rs:54:9
+  --> $DIR/rename.rs:55:9
    |
 LL | #![warn(clippy::blacklisted_name)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_names`
 
 error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_if_conditions`
-  --> $DIR/rename.rs:55:9
+  --> $DIR/rename.rs:56:9
    |
 LL | #![warn(clippy::block_in_if_condition_expr)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions`
 
 error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_if_conditions`
-  --> $DIR/rename.rs:56:9
+  --> $DIR/rename.rs:57:9
    |
 LL | #![warn(clippy::block_in_if_condition_stmt)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions`
 
 error: lint `clippy::box_vec` has been renamed to `clippy::box_collection`
-  --> $DIR/rename.rs:57:9
+  --> $DIR/rename.rs:58:9
    |
 LL | #![warn(clippy::box_vec)]
    |         ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection`
 
+error: lint `clippy::cmp_nan` has been renamed to `invalid_nan_comparisons`
+  --> $DIR/rename.rs:59:9
+   |
+LL | #![warn(clippy::cmp_nan)]
+   |         ^^^^^^^^^^^^^^^ help: use the new name: `invalid_nan_comparisons`
+
 error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes`
-  --> $DIR/rename.rs:58:9
+  --> $DIR/rename.rs:60:9
    |
 LL | #![warn(clippy::const_static_lifetime)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes`
 
 error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity`
-  --> $DIR/rename.rs:59:9
+  --> $DIR/rename.rs:61:9
    |
 LL | #![warn(clippy::cyclomatic_complexity)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity`
 
 error: lint `clippy::derive_hash_xor_eq` has been renamed to `clippy::derived_hash_with_manual_eq`
-  --> $DIR/rename.rs:60:9
+  --> $DIR/rename.rs:62:9
    |
 LL | #![warn(clippy::derive_hash_xor_eq)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::derived_hash_with_manual_eq`
 
 error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods`
-  --> $DIR/rename.rs:61:9
+  --> $DIR/rename.rs:63:9
    |
 LL | #![warn(clippy::disallowed_method)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods`
 
 error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types`
-  --> $DIR/rename.rs:62:9
+  --> $DIR/rename.rs:64:9
    |
 LL | #![warn(clippy::disallowed_type)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types`
 
 error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression`
-  --> $DIR/rename.rs:63:9
+  --> $DIR/rename.rs:65:9
    |
 LL | #![warn(clippy::eval_order_dependence)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression`
 
 error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion`
-  --> $DIR/rename.rs:64:9
+  --> $DIR/rename.rs:66:9
    |
 LL | #![warn(clippy::identity_conversion)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion`
 
 error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok`
-  --> $DIR/rename.rs:65:9
+  --> $DIR/rename.rs:67:9
    |
 LL | #![warn(clippy::if_let_some_result)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok`
 
 error: lint `clippy::integer_arithmetic` has been renamed to `clippy::arithmetic_side_effects`
-  --> $DIR/rename.rs:66:9
+  --> $DIR/rename.rs:68:9
    |
 LL | #![warn(clippy::integer_arithmetic)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::arithmetic_side_effects`
 
 error: lint `clippy::logic_bug` has been renamed to `clippy::overly_complex_bool_expr`
-  --> $DIR/rename.rs:67:9
+  --> $DIR/rename.rs:69:9
    |
 LL | #![warn(clippy::logic_bug)]
    |         ^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::overly_complex_bool_expr`
 
 error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default`
-  --> $DIR/rename.rs:68:9
+  --> $DIR/rename.rs:70:9
    |
 LL | #![warn(clippy::new_without_default_derive)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default`
 
 error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map`
-  --> $DIR/rename.rs:69:9
+  --> $DIR/rename.rs:71:9
    |
 LL | #![warn(clippy::option_and_then_some)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map`
 
 error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used`
-  --> $DIR/rename.rs:70:9
+  --> $DIR/rename.rs:72:9
    |
 LL | #![warn(clippy::option_expect_used)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used`
 
 error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or`
-  --> $DIR/rename.rs:71:9
+  --> $DIR/rename.rs:73:9
    |
 LL | #![warn(clippy::option_map_unwrap_or)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
 
 error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or`
-  --> $DIR/rename.rs:72:9
+  --> $DIR/rename.rs:74:9
    |
 LL | #![warn(clippy::option_map_unwrap_or_else)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
 
 error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used`
-  --> $DIR/rename.rs:73:9
+  --> $DIR/rename.rs:75:9
    |
 LL | #![warn(clippy::option_unwrap_used)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used`
 
 error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow`
-  --> $DIR/rename.rs:74:9
+  --> $DIR/rename.rs:76:9
    |
 LL | #![warn(clippy::ref_in_deref)]
    |         ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow`
 
 error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used`
-  --> $DIR/rename.rs:75:9
+  --> $DIR/rename.rs:77:9
    |
 LL | #![warn(clippy::result_expect_used)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used`
 
 error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or`
-  --> $DIR/rename.rs:76:9
+  --> $DIR/rename.rs:78:9
    |
 LL | #![warn(clippy::result_map_unwrap_or_else)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
 
 error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used`
-  --> $DIR/rename.rs:77:9
+  --> $DIR/rename.rs:79:9
    |
 LL | #![warn(clippy::result_unwrap_used)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used`
 
 error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str`
-  --> $DIR/rename.rs:78:9
+  --> $DIR/rename.rs:80:9
    |
 LL | #![warn(clippy::single_char_push_str)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str`
 
 error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions`
-  --> $DIR/rename.rs:79:9
+  --> $DIR/rename.rs:81:9
    |
 LL | #![warn(clippy::stutter)]
    |         ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions`
 
 error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl`
-  --> $DIR/rename.rs:80:9
+  --> $DIR/rename.rs:82:9
    |
 LL | #![warn(clippy::to_string_in_display)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl`
 
 error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters`
-  --> $DIR/rename.rs:81:9
+  --> $DIR/rename.rs:83:9
    |
 LL | #![warn(clippy::zero_width_space)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters`
 
 error: lint `clippy::cast_ref_to_mut` has been renamed to `cast_ref_to_mut`
-  --> $DIR/rename.rs:82:9
+  --> $DIR/rename.rs:84:9
    |
 LL | #![warn(clippy::cast_ref_to_mut)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `cast_ref_to_mut`
 
 error: lint `clippy::clone_double_ref` has been renamed to `suspicious_double_ref_op`
-  --> $DIR/rename.rs:83:9
+  --> $DIR/rename.rs:85:9
    |
 LL | #![warn(clippy::clone_double_ref)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `suspicious_double_ref_op`
 
 error: lint `clippy::drop_bounds` has been renamed to `drop_bounds`
-  --> $DIR/rename.rs:84:9
+  --> $DIR/rename.rs:86:9
    |
 LL | #![warn(clippy::drop_bounds)]
    |         ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds`
 
 error: lint `clippy::drop_copy` has been renamed to `dropping_copy_types`
-  --> $DIR/rename.rs:85:9
+  --> $DIR/rename.rs:87:9
    |
 LL | #![warn(clippy::drop_copy)]
    |         ^^^^^^^^^^^^^^^^^ help: use the new name: `dropping_copy_types`
 
 error: lint `clippy::drop_ref` has been renamed to `dropping_references`
-  --> $DIR/rename.rs:86:9
+  --> $DIR/rename.rs:88:9
    |
 LL | #![warn(clippy::drop_ref)]
    |         ^^^^^^^^^^^^^^^^ help: use the new name: `dropping_references`
 
 error: lint `clippy::for_loop_over_option` has been renamed to `for_loops_over_fallibles`
-  --> $DIR/rename.rs:87:9
+  --> $DIR/rename.rs:89:9
    |
 LL | #![warn(clippy::for_loop_over_option)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles`
 
 error: lint `clippy::for_loop_over_result` has been renamed to `for_loops_over_fallibles`
-  --> $DIR/rename.rs:88:9
+  --> $DIR/rename.rs:90:9
    |
 LL | #![warn(clippy::for_loop_over_result)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles`
 
 error: lint `clippy::for_loops_over_fallibles` has been renamed to `for_loops_over_fallibles`
-  --> $DIR/rename.rs:89:9
+  --> $DIR/rename.rs:91:9
    |
 LL | #![warn(clippy::for_loops_over_fallibles)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles`
 
 error: lint `clippy::forget_copy` has been renamed to `forgetting_copy_types`
-  --> $DIR/rename.rs:90:9
+  --> $DIR/rename.rs:92:9
    |
 LL | #![warn(clippy::forget_copy)]
    |         ^^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_copy_types`
 
 error: lint `clippy::forget_ref` has been renamed to `forgetting_references`
-  --> $DIR/rename.rs:91:9
+  --> $DIR/rename.rs:93:9
    |
 LL | #![warn(clippy::forget_ref)]
    |         ^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_references`
 
 error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter`
-  --> $DIR/rename.rs:92:9
+  --> $DIR/rename.rs:94:9
    |
 LL | #![warn(clippy::into_iter_on_array)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter`
 
 error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering`
-  --> $DIR/rename.rs:93:9
+  --> $DIR/rename.rs:95:9
    |
 LL | #![warn(clippy::invalid_atomic_ordering)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering`
 
 error: lint `clippy::invalid_ref` has been renamed to `invalid_value`
-  --> $DIR/rename.rs:94:9
+  --> $DIR/rename.rs:96:9
    |
 LL | #![warn(clippy::invalid_ref)]
    |         ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value`
 
 error: lint `clippy::invalid_utf8_in_unchecked` has been renamed to `invalid_from_utf8_unchecked`
-  --> $DIR/rename.rs:95:9
+  --> $DIR/rename.rs:97:9
    |
 LL | #![warn(clippy::invalid_utf8_in_unchecked)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_from_utf8_unchecked`
 
 error: lint `clippy::let_underscore_drop` has been renamed to `let_underscore_drop`
-  --> $DIR/rename.rs:96:9
+  --> $DIR/rename.rs:98:9
    |
 LL | #![warn(clippy::let_underscore_drop)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `let_underscore_drop`
 
 error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums`
-  --> $DIR/rename.rs:97:9
+  --> $DIR/rename.rs:99:9
    |
 LL | #![warn(clippy::mem_discriminant_non_enum)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums`
 
 error: lint `clippy::panic_params` has been renamed to `non_fmt_panics`
-  --> $DIR/rename.rs:98:9
+  --> $DIR/rename.rs:100:9
    |
 LL | #![warn(clippy::panic_params)]
    |         ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics`
 
 error: lint `clippy::positional_named_format_parameters` has been renamed to `named_arguments_used_positionally`
-  --> $DIR/rename.rs:99:9
+  --> $DIR/rename.rs:101:9
    |
 LL | #![warn(clippy::positional_named_format_parameters)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally`
 
 error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr`
-  --> $DIR/rename.rs:100:9
+  --> $DIR/rename.rs:102:9
    |
 LL | #![warn(clippy::temporary_cstring_as_ptr)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr`
 
 error: lint `clippy::undropped_manually_drops` has been renamed to `undropped_manually_drops`
-  --> $DIR/rename.rs:101:9
+  --> $DIR/rename.rs:103:9
    |
 LL | #![warn(clippy::undropped_manually_drops)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `undropped_manually_drops`
 
 error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints`
-  --> $DIR/rename.rs:102:9
+  --> $DIR/rename.rs:104:9
    |
 LL | #![warn(clippy::unknown_clippy_lints)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints`
 
 error: lint `clippy::unused_label` has been renamed to `unused_labels`
-  --> $DIR/rename.rs:103:9
+  --> $DIR/rename.rs:105:9
    |
 LL | #![warn(clippy::unused_label)]
    |         ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels`
 
-error: aborting due to 51 previous errors
+error: aborting due to 52 previous errors
 
diff --git a/src/tools/clippy/tests/ui/unknown_clippy_lints.fixed b/src/tools/clippy/tests/ui/unknown_clippy_lints.fixed
index 49c0e4dc7eb..debc7e152e7 100644
--- a/src/tools/clippy/tests/ui/unknown_clippy_lints.fixed
+++ b/src/tools/clippy/tests/ui/unknown_clippy_lints.fixed
@@ -3,7 +3,7 @@
 #![warn(clippy::pedantic)]
 // Should suggest lowercase
 #![allow(clippy::all)]
-#![warn(clippy::cmp_nan)]
+#![warn(clippy::cmp_owned)]
 
 // Should suggest similar clippy lint name
 #[warn(clippy::if_not_else)]
diff --git a/src/tools/clippy/tests/ui/unknown_clippy_lints.rs b/src/tools/clippy/tests/ui/unknown_clippy_lints.rs
index b60042923ea..16140fd1079 100644
--- a/src/tools/clippy/tests/ui/unknown_clippy_lints.rs
+++ b/src/tools/clippy/tests/ui/unknown_clippy_lints.rs
@@ -3,7 +3,7 @@
 #![warn(clippy::pedantic)]
 // Should suggest lowercase
 #![allow(clippy::All)]
-#![warn(clippy::CMP_NAN)]
+#![warn(clippy::CMP_OWNED)]
 
 // Should suggest similar clippy lint name
 #[warn(clippy::if_not_els)]
diff --git a/src/tools/clippy/tests/ui/unknown_clippy_lints.stderr b/src/tools/clippy/tests/ui/unknown_clippy_lints.stderr
index 584c428932f..880673eef3e 100644
--- a/src/tools/clippy/tests/ui/unknown_clippy_lints.stderr
+++ b/src/tools/clippy/tests/ui/unknown_clippy_lints.stderr
@@ -6,11 +6,11 @@ LL | #![allow(clippy::All)]
    |
    = note: `-D unknown-lints` implied by `-D warnings`
 
-error: unknown lint: `clippy::CMP_NAN`
+error: unknown lint: `clippy::CMP_OWNED`
   --> $DIR/unknown_clippy_lints.rs:6:9
    |
-LL | #![warn(clippy::CMP_NAN)]
-   |         ^^^^^^^^^^^^^^^ help: did you mean: `clippy::cmp_nan`
+LL | #![warn(clippy::CMP_OWNED)]
+   |         ^^^^^^^^^^^^^^^^^ help: did you mean: `clippy::cmp_owned`
 
 error: unknown lint: `clippy::if_not_els`
   --> $DIR/unknown_clippy_lints.rs:9:8
diff --git a/tests/ui/issues/issue-50811.rs b/tests/ui/issues/issue-50811.rs
index 683c856049f..2a20e50fa45 100644
--- a/tests/ui/issues/issue-50811.rs
+++ b/tests/ui/issues/issue-50811.rs
@@ -1,5 +1,6 @@
 // run-pass
 #![feature(test)]
+#![allow(invalid_nan_comparisons)]
 
 extern crate test;
 
diff --git a/tests/ui/lint/invalid-nan-comparison-suggestion.fixed b/tests/ui/lint/invalid-nan-comparison-suggestion.fixed
new file mode 100644
index 00000000000..feafc6c1b8c
--- /dev/null
+++ b/tests/ui/lint/invalid-nan-comparison-suggestion.fixed
@@ -0,0 +1,36 @@
+// check-pass
+// run-rustfix
+
+fn main() {
+    let x = 5f32;
+    let _ = x.is_nan();
+    //~^ WARN incorrect NaN comparison
+    let _ = !x.is_nan();
+    //~^ WARN incorrect NaN comparison
+
+    let x = 5f64;
+    let _ = x.is_nan();
+    //~^ WARN incorrect NaN comparison
+    let _ = !x.is_nan();
+    //~^ WARN incorrect NaN comparison
+
+    let b = &2.3f32;
+    if !b.is_nan() {}
+    //~^ WARN incorrect NaN comparison
+
+    let b = &2.3f32;
+    if !b.is_nan() {}
+    //~^ WARN incorrect NaN comparison
+
+    let _ =
+        !b.is_nan();
+
+    #[allow(unused_macros)]
+    macro_rules! nan { () => { f32::NAN }; }
+    macro_rules! number { () => { 5f32 }; }
+
+    let _ = number!().is_nan();
+    //~^ WARN incorrect NaN comparison
+    let _ = !number!().is_nan();
+    //~^ WARN incorrect NaN comparison
+}
diff --git a/tests/ui/lint/invalid-nan-comparison-suggestion.rs b/tests/ui/lint/invalid-nan-comparison-suggestion.rs
new file mode 100644
index 00000000000..ad5eb66e5f1
--- /dev/null
+++ b/tests/ui/lint/invalid-nan-comparison-suggestion.rs
@@ -0,0 +1,39 @@
+// check-pass
+// run-rustfix
+
+fn main() {
+    let x = 5f32;
+    let _ = x == f32::NAN;
+    //~^ WARN incorrect NaN comparison
+    let _ = x != f32::NAN;
+    //~^ WARN incorrect NaN comparison
+
+    let x = 5f64;
+    let _ = x == f64::NAN;
+    //~^ WARN incorrect NaN comparison
+    let _ = x != f64::NAN;
+    //~^ WARN incorrect NaN comparison
+
+    let b = &2.3f32;
+    if b != &f32::NAN {}
+    //~^ WARN incorrect NaN comparison
+
+    let b = &2.3f32;
+    if b != { &f32::NAN } {}
+    //~^ WARN incorrect NaN comparison
+
+    let _ =
+        b != {
+    //~^ WARN incorrect NaN comparison
+            &f32::NAN
+        };
+
+    #[allow(unused_macros)]
+    macro_rules! nan { () => { f32::NAN }; }
+    macro_rules! number { () => { 5f32 }; }
+
+    let _ = nan!() == number!();
+    //~^ WARN incorrect NaN comparison
+    let _ = number!() != nan!();
+    //~^ WARN incorrect NaN comparison
+}
diff --git a/tests/ui/lint/invalid-nan-comparison-suggestion.stderr b/tests/ui/lint/invalid-nan-comparison-suggestion.stderr
new file mode 100644
index 00000000000..c310341de07
--- /dev/null
+++ b/tests/ui/lint/invalid-nan-comparison-suggestion.stderr
@@ -0,0 +1,114 @@
+warning: incorrect NaN comparison, NaN cannot be directly compared to itself
+  --> $DIR/invalid-nan-comparison-suggestion.rs:6:13
+   |
+LL |     let _ = x == f32::NAN;
+   |             ^^^^^^^^^^^^^
+   |
+   = note: `#[warn(invalid_nan_comparisons)]` on by default
+help: use `f32::is_nan()` or `f64::is_nan()` instead
+   |
+LL -     let _ = x == f32::NAN;
+LL +     let _ = x.is_nan();
+   |
+
+warning: incorrect NaN comparison, NaN cannot be directly compared to itself
+  --> $DIR/invalid-nan-comparison-suggestion.rs:8:13
+   |
+LL |     let _ = x != f32::NAN;
+   |             ^^^^^^^^^^^^^
+   |
+help: use `f32::is_nan()` or `f64::is_nan()` instead
+   |
+LL -     let _ = x != f32::NAN;
+LL +     let _ = !x.is_nan();
+   |
+
+warning: incorrect NaN comparison, NaN cannot be directly compared to itself
+  --> $DIR/invalid-nan-comparison-suggestion.rs:12:13
+   |
+LL |     let _ = x == f64::NAN;
+   |             ^^^^^^^^^^^^^
+   |
+help: use `f32::is_nan()` or `f64::is_nan()` instead
+   |
+LL -     let _ = x == f64::NAN;
+LL +     let _ = x.is_nan();
+   |
+
+warning: incorrect NaN comparison, NaN cannot be directly compared to itself
+  --> $DIR/invalid-nan-comparison-suggestion.rs:14:13
+   |
+LL |     let _ = x != f64::NAN;
+   |             ^^^^^^^^^^^^^
+   |
+help: use `f32::is_nan()` or `f64::is_nan()` instead
+   |
+LL -     let _ = x != f64::NAN;
+LL +     let _ = !x.is_nan();
+   |
+
+warning: incorrect NaN comparison, NaN cannot be directly compared to itself
+  --> $DIR/invalid-nan-comparison-suggestion.rs:18:8
+   |
+LL |     if b != &f32::NAN {}
+   |        ^^^^^^^^^^^^^^
+   |
+help: use `f32::is_nan()` or `f64::is_nan()` instead
+   |
+LL -     if b != &f32::NAN {}
+LL +     if !b.is_nan() {}
+   |
+
+warning: incorrect NaN comparison, NaN cannot be directly compared to itself
+  --> $DIR/invalid-nan-comparison-suggestion.rs:22:8
+   |
+LL |     if b != { &f32::NAN } {}
+   |        ^^^^^^^^^^^^^^^^^^
+   |
+help: use `f32::is_nan()` or `f64::is_nan()` instead
+   |
+LL -     if b != { &f32::NAN } {}
+LL +     if !b.is_nan() {}
+   |
+
+warning: incorrect NaN comparison, NaN cannot be directly compared to itself
+  --> $DIR/invalid-nan-comparison-suggestion.rs:26:9
+   |
+LL | /         b != {
+LL | |
+LL | |             &f32::NAN
+LL | |         };
+   | |_________^
+   |
+help: use `f32::is_nan()` or `f64::is_nan()` instead
+   |
+LL -         b != {
+LL +         !b.is_nan();
+   |
+
+warning: incorrect NaN comparison, NaN cannot be directly compared to itself
+  --> $DIR/invalid-nan-comparison-suggestion.rs:35:13
+   |
+LL |     let _ = nan!() == number!();
+   |             ^^^^^^^^^^^^^^^^^^^
+   |
+help: use `f32::is_nan()` or `f64::is_nan()` instead
+   |
+LL -     let _ = nan!() == number!();
+LL +     let _ = number!().is_nan();
+   |
+
+warning: incorrect NaN comparison, NaN cannot be directly compared to itself
+  --> $DIR/invalid-nan-comparison-suggestion.rs:37:13
+   |
+LL |     let _ = number!() != nan!();
+   |             ^^^^^^^^^^^^^^^^^^^
+   |
+help: use `f32::is_nan()` or `f64::is_nan()` instead
+   |
+LL -     let _ = number!() != nan!();
+LL +     let _ = !number!().is_nan();
+   |
+
+warning: 9 warnings emitted
+
diff --git a/tests/ui/lint/invalid-nan-comparison.rs b/tests/ui/lint/invalid-nan-comparison.rs
new file mode 100644
index 00000000000..d7e793ca583
--- /dev/null
+++ b/tests/ui/lint/invalid-nan-comparison.rs
@@ -0,0 +1,51 @@
+// check-pass
+
+fn main() {
+    f32();
+    f64();
+}
+
+const TEST: bool = 5f32 == f32::NAN;
+//~^ WARN incorrect NaN comparison
+
+fn f32() {
+    macro_rules! number { () => { 5f32 }; }
+    let x = number!();
+    x == f32::NAN;
+    //~^ WARN incorrect NaN comparison
+    x != f32::NAN;
+    //~^ WARN incorrect NaN comparison
+    x < f32::NAN;
+    //~^ WARN incorrect NaN comparison
+    x > f32::NAN;
+    //~^ WARN incorrect NaN comparison
+    x <= f32::NAN;
+    //~^ WARN incorrect NaN comparison
+    x >= f32::NAN;
+    //~^ WARN incorrect NaN comparison
+    number!() == f32::NAN;
+    //~^ WARN incorrect NaN comparison
+    f32::NAN != number!();
+    //~^ WARN incorrect NaN comparison
+}
+
+fn f64() {
+    macro_rules! number { () => { 5f64 }; }
+    let x = number!();
+    x == f64::NAN;
+    //~^ WARN incorrect NaN comparison
+    x != f64::NAN;
+    //~^ WARN incorrect NaN comparison
+    x < f64::NAN;
+    //~^ WARN incorrect NaN comparison
+    x > f64::NAN;
+    //~^ WARN incorrect NaN comparison
+    x <= f64::NAN;
+    //~^ WARN incorrect NaN comparison
+    x >= f64::NAN;
+    //~^ WARN incorrect NaN comparison
+    number!() == f64::NAN;
+    //~^ WARN incorrect NaN comparison
+    f64::NAN != number!();
+    //~^ WARN incorrect NaN comparison
+}
diff --git a/tests/ui/lint/invalid-nan-comparison.stderr b/tests/ui/lint/invalid-nan-comparison.stderr
new file mode 100644
index 00000000000..054c06d38b3
--- /dev/null
+++ b/tests/ui/lint/invalid-nan-comparison.stderr
@@ -0,0 +1,159 @@
+warning: incorrect NaN comparison, NaN cannot be directly compared to itself
+  --> $DIR/invalid-nan-comparison.rs:8:20
+   |
+LL | const TEST: bool = 5f32 == f32::NAN;
+   |                    ^^^^^^^^^^^^^^^^
+   |
+   = note: `#[warn(invalid_nan_comparisons)]` on by default
+help: use `f32::is_nan()` or `f64::is_nan()` instead
+   |
+LL - const TEST: bool = 5f32 == f32::NAN;
+LL + const TEST: bool = 5f32.is_nan();
+   |
+
+warning: incorrect NaN comparison, NaN cannot be directly compared to itself
+  --> $DIR/invalid-nan-comparison.rs:14:5
+   |
+LL |     x == f32::NAN;
+   |     ^^^^^^^^^^^^^
+   |
+help: use `f32::is_nan()` or `f64::is_nan()` instead
+   |
+LL -     x == f32::NAN;
+LL +     x.is_nan();
+   |
+
+warning: incorrect NaN comparison, NaN cannot be directly compared to itself
+  --> $DIR/invalid-nan-comparison.rs:16:5
+   |
+LL |     x != f32::NAN;
+   |     ^^^^^^^^^^^^^
+   |
+help: use `f32::is_nan()` or `f64::is_nan()` instead
+   |
+LL -     x != f32::NAN;
+LL +     !x.is_nan();
+   |
+
+warning: incorrect NaN comparison, NaN is not orderable
+  --> $DIR/invalid-nan-comparison.rs:18:5
+   |
+LL |     x < f32::NAN;
+   |     ^^^^^^^^^^^^
+
+warning: incorrect NaN comparison, NaN is not orderable
+  --> $DIR/invalid-nan-comparison.rs:20:5
+   |
+LL |     x > f32::NAN;
+   |     ^^^^^^^^^^^^
+
+warning: incorrect NaN comparison, NaN is not orderable
+  --> $DIR/invalid-nan-comparison.rs:22:5
+   |
+LL |     x <= f32::NAN;
+   |     ^^^^^^^^^^^^^
+
+warning: incorrect NaN comparison, NaN is not orderable
+  --> $DIR/invalid-nan-comparison.rs:24:5
+   |
+LL |     x >= f32::NAN;
+   |     ^^^^^^^^^^^^^
+
+warning: incorrect NaN comparison, NaN cannot be directly compared to itself
+  --> $DIR/invalid-nan-comparison.rs:26:5
+   |
+LL |     number!() == f32::NAN;
+   |     ^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `f32::is_nan()` or `f64::is_nan()` instead
+   |
+LL -     number!() == f32::NAN;
+LL +     number!().is_nan();
+   |
+
+warning: incorrect NaN comparison, NaN cannot be directly compared to itself
+  --> $DIR/invalid-nan-comparison.rs:28:5
+   |
+LL |     f32::NAN != number!();
+   |     ^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `f32::is_nan()` or `f64::is_nan()` instead
+   |
+LL -     f32::NAN != number!();
+LL +     !number!().is_nan();
+   |
+
+warning: incorrect NaN comparison, NaN cannot be directly compared to itself
+  --> $DIR/invalid-nan-comparison.rs:35:5
+   |
+LL |     x == f64::NAN;
+   |     ^^^^^^^^^^^^^
+   |
+help: use `f32::is_nan()` or `f64::is_nan()` instead
+   |
+LL -     x == f64::NAN;
+LL +     x.is_nan();
+   |
+
+warning: incorrect NaN comparison, NaN cannot be directly compared to itself
+  --> $DIR/invalid-nan-comparison.rs:37:5
+   |
+LL |     x != f64::NAN;
+   |     ^^^^^^^^^^^^^
+   |
+help: use `f32::is_nan()` or `f64::is_nan()` instead
+   |
+LL -     x != f64::NAN;
+LL +     !x.is_nan();
+   |
+
+warning: incorrect NaN comparison, NaN is not orderable
+  --> $DIR/invalid-nan-comparison.rs:39:5
+   |
+LL |     x < f64::NAN;
+   |     ^^^^^^^^^^^^
+
+warning: incorrect NaN comparison, NaN is not orderable
+  --> $DIR/invalid-nan-comparison.rs:41:5
+   |
+LL |     x > f64::NAN;
+   |     ^^^^^^^^^^^^
+
+warning: incorrect NaN comparison, NaN is not orderable
+  --> $DIR/invalid-nan-comparison.rs:43:5
+   |
+LL |     x <= f64::NAN;
+   |     ^^^^^^^^^^^^^
+
+warning: incorrect NaN comparison, NaN is not orderable
+  --> $DIR/invalid-nan-comparison.rs:45:5
+   |
+LL |     x >= f64::NAN;
+   |     ^^^^^^^^^^^^^
+
+warning: incorrect NaN comparison, NaN cannot be directly compared to itself
+  --> $DIR/invalid-nan-comparison.rs:47:5
+   |
+LL |     number!() == f64::NAN;
+   |     ^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `f32::is_nan()` or `f64::is_nan()` instead
+   |
+LL -     number!() == f64::NAN;
+LL +     number!().is_nan();
+   |
+
+warning: incorrect NaN comparison, NaN cannot be directly compared to itself
+  --> $DIR/invalid-nan-comparison.rs:49:5
+   |
+LL |     f64::NAN != number!();
+   |     ^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `f32::is_nan()` or `f64::is_nan()` instead
+   |
+LL -     f64::NAN != number!();
+LL +     !number!().is_nan();
+   |
+
+warning: 17 warnings emitted
+