diff --git a/README.md b/README.md index d712d3e6750..5d490645d89 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code. -[There are over 600 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) +[There are over 650 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html). You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the lint level by category. diff --git a/book/src/README.md b/book/src/README.md index 3b627096268..486ea3df704 100644 --- a/book/src/README.md +++ b/book/src/README.md @@ -6,7 +6,7 @@ A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code. -[There are over 600 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) +[There are over 650 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html). You can choose how diff --git a/clippy_lints/src/manual_float_methods.rs b/clippy_lints/src/manual_float_methods.rs index 0f488627b4e..09b39634333 100644 --- a/clippy_lints/src/manual_float_methods.rs +++ b/clippy_lints/src/manual_float_methods.rs @@ -1,5 +1,8 @@ use clippy_utils::{ - consts::constant, diagnostics::span_lint_and_then, is_from_proc_macro, path_to_local, source::snippet_opt, + consts::{constant, 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}; @@ -9,10 +12,11 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// ### What it does - /// Checks for `x == ::INFINITY || x == ::NEG_INFINITY`. + /// Checks for manual `is_infinite` reimplementations + /// (i.e., `x == ::INFINITY || x == ::NEG_INFINITY`). /// /// ### Why is this bad? - /// This should use the dedicated method instead, `is_infinite`. + /// The method `is_infinite` is shorter and more readable. /// /// ### Example /// ```rust @@ -31,20 +35,23 @@ declare_clippy_lint! { } declare_clippy_lint! { /// ### What it does - /// Checks for `x != ::INFINITY && x != ::NEG_INFINITY`. + /// Checks for manual `is_finite` reimplementations + /// (i.e., `x != ::INFINITY && x != ::NEG_INFINITY`). /// /// ### Why is this bad? - /// This should use the dedicated method instead, `is_finite`. + /// The method `is_finite` is shorter and more readable. /// /// ### Example /// ```rust /// # let x = 1.0f32; /// if x != f32::INFINITY && x != f32::NEG_INFINITY {} + /// if x.abs() < f32::INFINITY {} /// ``` /// Use instead: /// ```rust /// # let x = 1.0f32; /// if x.is_finite() {} + /// if x.is_finite() {} /// ``` #[clippy::version = "1.72.0"] pub MANUAL_IS_FINITE, @@ -84,20 +91,22 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods { && 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] + && let (operands, constants) = [lhs_lhs, lhs_rhs, rhs_lhs, rhs_rhs] .into_iter() .partition::>, _>(|i| path_to_local(i).is_some()) && let [first, second] = &*operands - && let Some([const_1, const_2]) = consts + && let Some([const_1, const_2]) = constants .into_iter() - .map(|i| constant(cx, cx.typeck_results(), i).and_then(|c| c.to_bits())) + .map(|i| constant(cx, cx.typeck_results(), i)) .collect::>>() .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) + // The actual infinity check, we also allow `NEG_INFINITY` before` INFINITY` just in + // case somebody does that for some reason + && (is_infinity(const_1) && is_neg_infinity(const_2) + || is_neg_infinity(const_1) && is_infinity(const_2)) && !is_from_proc_macro(cx, expr) + && let Some(local_snippet) = snippet_opt(cx, first.span) { let variant = match (kind.node, lhs_kind.node, rhs_kind.node) { (BinOpKind::Or, BinOpKind::Eq, BinOpKind::Eq) => Variant::ManualIsInfinite, @@ -128,31 +137,39 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods { "use the dedicated method instead", format!("{local_snippet}.is_finite()"), Applicability::MaybeIncorrect, - ); - diag.span_suggestion_verbose( + ) + .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( + ) + .span_suggestion_verbose( expr.span, "or, for conciseness", format!("!{local_snippet}.is_infinite()"), Applicability::MaybeIncorrect, ); - } + }, } - } + }, ); } } } -fn is_infinity(bits: u128) -> bool { - bits == 0x7f80_0000 || bits == 0x7ff0_0000_0000_0000 +fn is_infinity(constant: &Constant<'_>) -> bool { + match constant { + Constant::F32(float) => *float == f32::INFINITY, + Constant::F64(float) => *float == f64::INFINITY, + _ => false, + } } -fn is_neg_infinity(bits: u128) -> bool { - bits == 0xff80_0000 || bits == 0xfff0_0000_0000_0000 +fn is_neg_infinity(constant: &Constant<'_>) -> bool { + match constant { + Constant::F32(float) => *float == f32::NEG_INFINITY, + Constant::F64(float) => *float == f64::NEG_INFINITY, + _ => false, + } } diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index b9f8eefeb8e..87d85d742ce 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -190,17 +190,6 @@ impl<'tcx> Constant<'tcx> { } } - /// Returns the bit representation if `self` is a bool, integer, or float. - pub fn to_bits(&self) -> Option { - match self { - Constant::Int(int) => Some(*int), - Constant::F32(float) => Some(u128::from(float.to_bits())), - Constant::F64(float) => Some(u128::from(float.to_bits())), - Constant::Bool(bool) => Some(u128::from(*bool)), - _ => None, - } - } - /// Returns the integer value or `None` if `self` or `val_type` is not integer type. pub fn int_value(&self, cx: &LateContext<'_>, val_type: Ty<'_>) -> Option { if let Constant::Int(const_int) = *self { diff --git a/tests/ui/manual_float_methods.rs b/tests/ui/manual_float_methods.rs index b772202c003..af9076cfb71 100644 --- a/tests/ui/manual_float_methods.rs +++ b/tests/ui/manual_float_methods.rs @@ -1,6 +1,7 @@ //@aux-build:proc_macros.rs:proc-macro #![allow(clippy::needless_if, unused)] #![warn(clippy::manual_is_infinite, clippy::manual_is_finite)] +#![feature(inline_const)] #[macro_use] extern crate proc_macros; @@ -8,6 +9,14 @@ extern crate proc_macros; const INFINITE: f32 = f32::INFINITY; const NEG_INFINITE: f32 = f32::NEG_INFINITY; +fn fn_test() -> f64 { + f64::NEG_INFINITY +} + +fn fn_test_not_inf() -> f64 { + 112.0 +} + fn main() { let x = 1.0f32; if x == f32::INFINITY || x == f32::NEG_INFINITY {} @@ -20,8 +29,18 @@ fn main() { // Don't lint if x.is_infinite() {} if x.is_finite() {} - // If they're doing it this way, they probably know what they're doing if x.abs() < f64::INFINITY {} + if f64::INFINITY > x.abs() {} + if f64::abs(x) < f64::INFINITY {} + if f64::INFINITY > f64::abs(x) {} + // Is not evaluated by `clippy_utils::constant` + if x != f64::INFINITY && x != fn_test() {} + // Not -inf + if x != f64::INFINITY && x != fn_test_not_inf() {} + const X: f64 = 1.0f64; + // Will be linted if `const_float_classify` is enabled + if const { X == f64::INFINITY || X == f64::NEG_INFINITY } {} + if const { X != f64::INFINITY && X != f64::NEG_INFINITY } {} external! { let x = 1.0; if x == f32::INFINITY || x == f32::NEG_INFINITY {} diff --git a/tests/ui/manual_float_methods.stderr b/tests/ui/manual_float_methods.stderr index 7f4a4d2e95b..a56118b316a 100644 --- a/tests/ui/manual_float_methods.stderr +++ b/tests/ui/manual_float_methods.stderr @@ -1,5 +1,5 @@ error: manually checking if a float is infinite - --> $DIR/manual_float_methods.rs:13:8 + --> $DIR/manual_float_methods.rs:22:8 | LL | if x == f32::INFINITY || x == f32::NEG_INFINITY {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the dedicated method instead: `x.is_infinite()` @@ -7,7 +7,7 @@ LL | if x == f32::INFINITY || x == f32::NEG_INFINITY {} = note: `-D clippy::manual-is-infinite` implied by `-D warnings` error: manually checking if a float is finite - --> $DIR/manual_float_methods.rs:14:8 + --> $DIR/manual_float_methods.rs:23:8 | LL | if x != f32::INFINITY && x != f32::NEG_INFINITY {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -27,13 +27,13 @@ LL | if !x.is_infinite() {} | ~~~~~~~~~~~~~~~~ error: manually checking if a float is infinite - --> $DIR/manual_float_methods.rs:15:8 + --> $DIR/manual_float_methods.rs:24:8 | LL | if x == INFINITE || x == NEG_INFINITE {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the dedicated method instead: `x.is_infinite()` error: manually checking if a float is finite - --> $DIR/manual_float_methods.rs:16:8 + --> $DIR/manual_float_methods.rs:25:8 | LL | if x != INFINITE && x != NEG_INFINITE {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -52,13 +52,13 @@ LL | if !x.is_infinite() {} | ~~~~~~~~~~~~~~~~ error: manually checking if a float is infinite - --> $DIR/manual_float_methods.rs:18:8 + --> $DIR/manual_float_methods.rs:27:8 | LL | if x == f64::INFINITY || x == f64::NEG_INFINITY {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the dedicated method instead: `x.is_infinite()` error: manually checking if a float is finite - --> $DIR/manual_float_methods.rs:19:8 + --> $DIR/manual_float_methods.rs:28:8 | LL | if x != f64::INFINITY && x != f64::NEG_INFINITY {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^