about summary refs log tree commit diff
path: root/clippy_lints/src
diff options
context:
space:
mode:
authorCatherine <114838443+Centri3@users.noreply.github.com>2023-06-30 11:31:08 -0500
committerCatherine <114838443+Centri3@users.noreply.github.com>2023-07-06 20:27:21 -0500
commitf12edfdb53b13008dcff9eba2a3fd9a19370d2ee (patch)
treed021e96ce7d4b54a29acadb55bf07a549fc8e229 /clippy_lints/src
parentdd8e44c5a22ab646821252604420c5bb82c36aa9 (diff)
`manual_float_methods`
Diffstat (limited to 'clippy_lints/src')
-rw-r--r--clippy_lints/src/declared_lints.rs2
-rw-r--r--clippy_lints/src/lib.rs2
-rw-r--r--clippy_lints/src/manual_float_methods.rs106
3 files changed, 110 insertions, 0 deletions
diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs
index ca97db04079..f8b8b94dd29 100644
--- a/clippy_lints/src/declared_lints.rs
+++ b/clippy_lints/src/declared_lints.rs
@@ -273,6 +273,8 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::manual_async_fn::MANUAL_ASYNC_FN_INFO,
     crate::manual_bits::MANUAL_BITS_INFO,
     crate::manual_clamp::MANUAL_CLAMP_INFO,
+    crate::manual_float_methods::MANUAL_IS_FINITE_INFO,
+    crate::manual_float_methods::MANUAL_IS_INFINITE_INFO,
     crate::manual_is_ascii_check::MANUAL_IS_ASCII_CHECK_INFO,
     crate::manual_let_else::MANUAL_LET_ELSE_INFO,
     crate::manual_main_separator_str::MANUAL_MAIN_SEPARATOR_STR_INFO,
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index 00d46025caa..9abcbe01176 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -184,6 +184,7 @@ mod manual_assert;
 mod manual_async_fn;
 mod manual_bits;
 mod manual_clamp;
+mod manual_float_methods;
 mod manual_is_ascii_check;
 mod manual_let_else;
 mod manual_main_separator_str;
@@ -1073,6 +1074,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|_| Box::new(manual_range_patterns::ManualRangePatterns));
     store.register_early_pass(|| Box::new(visibility::Visibility));
     store.register_late_pass(move |_| Box::new(tuple_array_conversions::TupleArrayConversions { msrv: msrv() }));
+    store.register_late_pass(|_| Box::new(manual_float_methods::ManualFloatMethods));
     // add lints here, do not remove this comment, it's used in `new_lint`
 }
 
diff --git a/clippy_lints/src/manual_float_methods.rs b/clippy_lints/src/manual_float_methods.rs
new file mode 100644
index 00000000000..abf1eb788dd
--- /dev/null
+++ b/clippy_lints/src/manual_float_methods.rs
@@ -0,0 +1,106 @@
+use clippy_utils::{
+    consts::constant, diagnostics::span_lint_and_sugg, 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_middle::lint::in_external_macro;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for `x == <float>::INFINITY || x == <float>::NEG_INFINITY`.
+    ///
+    /// ### Why is this bad?
+    /// This should use the dedicated method instead, `is_infinite`.
+    ///
+    /// ### Example
+    /// ```rust
+    /// # let x = 1.0f32;
+    /// if x == f32::INFINITY || x == f32::NEG_INFINITY {}
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// # let x = 1.0f32;
+    /// if x.is_infinite() {}
+    /// ```
+    #[clippy::version = "1.72.0"]
+    pub MANUAL_IS_INFINITE,
+    style,
+    "use dedicated method to check if a float is infinite"
+}
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for `x != <float>::INFINITY && x != <float>::NEG_INFINITY`.
+    ///
+    /// ### Why is this bad?
+    /// This should use the dedicated method instead, `is_finite`.
+    ///
+    /// ### Example
+    /// ```rust
+    /// # let x = 1.0f32;
+    /// if x != f32::INFINITY && x != f32::NEG_INFINITY {}
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// # let x = 1.0f32;
+    /// if x.is_finite() {}
+    /// ```
+    #[clippy::version = "1.72.0"]
+    pub MANUAL_IS_FINITE,
+    style,
+    "use dedicated method to check if a float is finite"
+}
+declare_lint_pass!(ManualFloatMethods => [MANUAL_IS_INFINITE, MANUAL_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)
+            && 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
+            && let (operands, consts) = [lhs_lhs, lhs_rhs, rhs_lhs, rhs_rhs]
+                .into_iter()
+                .partition::<Vec<&Expr<'_>>, _>(|i| path_to_local(i).is_some())
+            && let [first, second] = &*operands
+            && let Some([const_1, const_2]) = consts
+                .into_iter()
+                .map(|i| constant(cx, cx.typeck_results(), i).and_then(|c| c.to_bits()))
+                .collect::<Option<Vec<_>>>()
+                .as_deref()
+            && path_to_local(first).is_some_and(|f| path_to_local(second).is_some_and(|s| f == s))
+            && (is_infinity(*const_1) && is_neg_infinity(*const_2)
+                || is_neg_infinity(*const_1) && is_infinity(*const_2))
+            && 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")
+                },
+                _ => return,
+            };
+
+            span_lint_and_sugg(
+                cx,
+                lint,
+                expr.span,
+                msg,
+                "try",
+                format!("{local_snippet}.{sugg_fn}()"),
+                Applicability::MachineApplicable,
+            );
+        }
+    }
+}
+
+fn is_infinity(bits: u128) -> bool {
+    bits == 0x7f80_0000 || bits == 0x7ff0_0000_0000_0000
+}
+
+fn is_neg_infinity(bits: u128) -> bool {
+    bits == 0xff80_0000 || bits == 0xfff0_0000_0000_0000
+}