about summary refs log tree commit diff
path: root/clippy_lints/src/manual_float_methods.rs
diff options
context:
space:
mode:
Diffstat (limited to 'clippy_lints/src/manual_float_methods.rs')
-rw-r--r--clippy_lints/src/manual_float_methods.rs82
1 files changed, 67 insertions, 15 deletions
diff --git a/clippy_lints/src/manual_float_methods.rs b/clippy_lints/src/manual_float_methods.rs
index abf1eb788dd..0f488627b4e 100644
--- a/clippy_lints/src/manual_float_methods.rs
+++ b/clippy_lints/src/manual_float_methods.rs
@@ -1,9 +1,9 @@
 use clippy_utils::{
-    consts::constant, diagnostics::span_lint_and_sugg, is_from_proc_macro, path_to_local, source::snippet_opt,
+    consts::constant, diagnostics::span_lint_and_then, is_from_proc_macro, path_to_local, source::snippet_opt,
 };
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_lint::{LateContext, LateLintPass, Lint, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 
@@ -53,12 +53,37 @@ declare_clippy_lint! {
 }
 declare_lint_pass!(ManualFloatMethods => [MANUAL_IS_INFINITE, MANUAL_IS_FINITE]);
 
+#[derive(Clone, Copy)]
+enum Variant {
+    ManualIsInfinite,
+    ManualIsFinite,
+}
+
+impl Variant {
+    pub fn lint(self) -> &'static Lint {
+        match self {
+            Self::ManualIsInfinite => MANUAL_IS_INFINITE,
+            Self::ManualIsFinite => MANUAL_IS_FINITE,
+        }
+    }
+
+    pub fn msg(self) -> &'static str {
+        match self {
+            Self::ManualIsInfinite => "manually checking if a float is infinite",
+            Self::ManualIsFinite => "manually checking if a float is finite",
+        }
+    }
+}
+
 impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
         if !in_external_macro(cx.sess(), expr.span)
+            && (!cx.param_env.is_const() || cx.tcx.features().active(sym!(const_float_classify)))
             && let ExprKind::Binary(kind, lhs, rhs) = expr.kind
             && let ExprKind::Binary(lhs_kind, lhs_lhs, lhs_rhs) = lhs.kind
             && let ExprKind::Binary(rhs_kind, rhs_lhs, rhs_rhs) = rhs.kind
+            // Checking all possible scenarios using a function would be a hopeless task, as we have
+            // 16 possible alignments of constants/operands. For now, let's use `partition`.
             && let (operands, consts) = [lhs_lhs, lhs_rhs, rhs_lhs, rhs_rhs]
                 .into_iter()
                 .partition::<Vec<&Expr<'_>>, _>(|i| path_to_local(i).is_some())
@@ -74,24 +99,51 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods {
             && let Some(local_snippet) = snippet_opt(cx, first.span)
             && !is_from_proc_macro(cx, expr)
         {
-            let (msg, lint, sugg_fn) = match (kind.node, lhs_kind.node, rhs_kind.node) {
-                (BinOpKind::Or, BinOpKind::Eq, BinOpKind::Eq) => {
-                    ("manually checking if a float is infinite", MANUAL_IS_INFINITE, "is_infinite")
-                },
-                (BinOpKind::And, BinOpKind::Ne, BinOpKind::Ne) => {
-                    ("manually checking if a float is finite", MANUAL_IS_FINITE, "is_finite")
-                },
+            let variant = match (kind.node, lhs_kind.node, rhs_kind.node) {
+                (BinOpKind::Or, BinOpKind::Eq, BinOpKind::Eq) => Variant::ManualIsInfinite,
+                (BinOpKind::And, BinOpKind::Ne, BinOpKind::Ne) => Variant::ManualIsFinite,
                 _ => return,
             };
 
-            span_lint_and_sugg(
+            span_lint_and_then(
                 cx,
-                lint,
+                variant.lint(),
                 expr.span,
-                msg,
-                "try",
-                format!("{local_snippet}.{sugg_fn}()"),
-                Applicability::MachineApplicable,
+                variant.msg(),
+                |diag| {
+                    match variant {
+                        Variant::ManualIsInfinite => {
+                            diag.span_suggestion(
+                                expr.span,
+                                "use the dedicated method instead",
+                                format!("{local_snippet}.is_infinite()"),
+                                Applicability::MachineApplicable,
+                            );
+                        },
+                        Variant::ManualIsFinite => {
+                            // TODO: There's probably some better way to do this, i.e., create
+                            // multiple suggestions with notes between each of them
+                            diag.span_suggestion_verbose(
+                                expr.span,
+                                "use the dedicated method instead",
+                                format!("{local_snippet}.is_finite()"),
+                                Applicability::MaybeIncorrect,
+                            );
+                            diag.span_suggestion_verbose(
+                                expr.span,
+                                "this will alter how it handles NaN; if that is a problem, use instead",
+                                format!("{local_snippet}.is_finite() || {local_snippet}.is_nan()"),
+                                Applicability::MaybeIncorrect,
+                            );
+                            diag.span_suggestion_verbose(
+                                expr.span,
+                                "or, for conciseness",
+                                format!("!{local_snippet}.is_infinite()"),
+                                Applicability::MaybeIncorrect,
+                            );
+                        }
+                    }
+                }
             );
         }
     }