diff options
| author | Catherine <114838443+Centri3@users.noreply.github.com> | 2023-06-30 11:31:08 -0500 |
|---|---|---|
| committer | Catherine <114838443+Centri3@users.noreply.github.com> | 2023-07-06 20:27:21 -0500 |
| commit | f12edfdb53b13008dcff9eba2a3fd9a19370d2ee (patch) | |
| tree | d021e96ce7d4b54a29acadb55bf07a549fc8e229 /clippy_lints/src | |
| parent | dd8e44c5a22ab646821252604420c5bb82c36aa9 (diff) | |
`manual_float_methods`
Diffstat (limited to 'clippy_lints/src')
| -rw-r--r-- | clippy_lints/src/declared_lints.rs | 2 | ||||
| -rw-r--r-- | clippy_lints/src/lib.rs | 2 | ||||
| -rw-r--r-- | clippy_lints/src/manual_float_methods.rs | 106 |
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 +} |
