1 use clippy_utils
::consts
::{constant, Constant}
;
2 use clippy_utils
::diagnostics
::span_lint_and_then
;
3 use clippy_utils
::source
::snippet_opt
;
4 use clippy_utils
::{is_from_proc_macro, path_to_local}
;
5 use rustc_errors
::Applicability
;
6 use rustc_hir
::{BinOpKind, Constness, Expr, ExprKind}
;
7 use rustc_lint
::{LateContext, LateLintPass, Lint, LintContext}
;
8 use rustc_middle
::lint
::in_external_macro
;
9 use rustc_session
::declare_lint_pass
;
11 declare_clippy_lint
! {
13 /// Checks for manual `is_infinite` reimplementations
14 /// (i.e., `x == <float>::INFINITY || x == <float>::NEG_INFINITY`).
16 /// ### Why is this bad?
17 /// The method `is_infinite` is shorter and more readable.
22 /// if x == f32::INFINITY || x == f32::NEG_INFINITY {}
27 /// if x.is_infinite() {}
29 #[clippy::version = "1.73.0"]
30 pub MANUAL_IS_INFINITE
,
32 "use dedicated method to check if a float is infinite"
34 declare_clippy_lint
! {
36 /// Checks for manual `is_finite` reimplementations
37 /// (i.e., `x != <float>::INFINITY && x != <float>::NEG_INFINITY`).
39 /// ### Why is this bad?
40 /// The method `is_finite` is shorter and more readable.
45 /// if x != f32::INFINITY && x != f32::NEG_INFINITY {}
46 /// if x.abs() < f32::INFINITY {}
51 /// if x.is_finite() {}
52 /// if x.is_finite() {}
54 #[clippy::version = "1.73.0"]
57 "use dedicated method to check if a float is finite"
59 declare_lint_pass
!(ManualFloatMethods
=> [MANUAL_IS_INFINITE
, MANUAL_IS_FINITE
]);
61 #[derive(Clone, Copy)]
68 pub fn lint(self) -> &'
static Lint
{
70 Self::ManualIsInfinite
=> MANUAL_IS_INFINITE
,
71 Self::ManualIsFinite
=> MANUAL_IS_FINITE
,
75 pub fn msg(self) -> &'
static str {
77 Self::ManualIsInfinite
=> "manually checking if a float is infinite",
78 Self::ManualIsFinite
=> "manually checking if a float is finite",
83 impl<'tcx
> LateLintPass
<'tcx
> for ManualFloatMethods
{
84 fn check_expr(&mut self, cx
: &LateContext
<'tcx
>, expr
: &'tcx Expr
<'tcx
>) {
85 if !in_external_macro(cx
.sess(), expr
.span
)
87 matches
!(cx
.tcx
.constness(cx
.tcx
.hir().enclosing_body_owner(expr
.hir_id
)), Constness
::NotConst
)
88 || cx
.tcx
.features().declared(sym
!(const_float_classify
))
89 ) && let ExprKind
::Binary(kind
, lhs
, rhs
) = expr
.kind
90 && let ExprKind
::Binary(lhs_kind
, lhs_lhs
, lhs_rhs
) = lhs
.kind
91 && let ExprKind
::Binary(rhs_kind
, rhs_lhs
, rhs_rhs
) = rhs
.kind
92 // Checking all possible scenarios using a function would be a hopeless task, as we have
93 // 16 possible alignments of constants/operands. For now, let's use `partition`.
94 && let (operands
, constants
) = [lhs_lhs
, lhs_rhs
, rhs_lhs
, rhs_rhs
]
96 .partition
::<Vec
<&Expr
<'_
>>, _
>(|i
| path_to_local(i
).is_some())
97 && let [first
, second
] = &*operands
98 && let Some([const_1
, const_2
]) = constants
100 .map(|i
| constant(cx
, cx
.typeck_results(), i
))
101 .collect
::<Option
<Vec
<_
>>>()
103 && path_to_local(first
).is_some_and(|f
| path_to_local(second
).is_some_and(|s
| f
== s
))
104 // The actual infinity check, we also allow `NEG_INFINITY` before` INFINITY` just in
105 // case somebody does that for some reason
106 && (is_infinity(const_1
) && is_neg_infinity(const_2
)
107 || is_neg_infinity(const_1
) && is_infinity(const_2
))
108 && let Some(local_snippet
) = snippet_opt(cx
, first
.span
)
110 let variant
= match (kind
.node
, lhs_kind
.node
, rhs_kind
.node
) {
111 (BinOpKind
::Or
, BinOpKind
::Eq
, BinOpKind
::Eq
) => Variant
::ManualIsInfinite
,
112 (BinOpKind
::And
, BinOpKind
::Ne
, BinOpKind
::Ne
) => Variant
::ManualIsFinite
,
115 if is_from_proc_macro(cx
, expr
) {
119 span_lint_and_then(cx
, variant
.lint(), expr
.span
, variant
.msg(), |diag
| {
121 Variant
::ManualIsInfinite
=> {
122 diag
.span_suggestion(
124 "use the dedicated method instead",
125 format
!("{local_snippet}.is_infinite()"),
126 Applicability
::MachineApplicable
,
129 Variant
::ManualIsFinite
=> {
130 // TODO: There's probably some better way to do this, i.e., create
131 // multiple suggestions with notes between each of them
132 diag
.span_suggestion_verbose(
134 "use the dedicated method instead",
135 format
!("{local_snippet}.is_finite()"),
136 Applicability
::MaybeIncorrect
,
138 .span_suggestion_verbose(
140 "this will alter how it handles NaN; if that is a problem, use instead",
141 format
!("{local_snippet}.is_finite() || {local_snippet}.is_nan()"),
142 Applicability
::MaybeIncorrect
,
144 .span_suggestion_verbose(
146 "or, for conciseness",
147 format
!("!{local_snippet}.is_infinite()"),
148 Applicability
::MaybeIncorrect
,
157 fn is_infinity(constant
: &Constant
<'_
>) -> bool
{
159 Constant
::F32(float
) => *float
== f32::INFINITY
,
160 Constant
::F64(float
) => *float
== f64::INFINITY
,
165 fn is_neg_infinity(constant
: &Constant
<'_
>) -> bool
{
167 Constant
::F32(float
) => *float
== f32::NEG_INFINITY
,
168 Constant
::F64(float
) => *float
== f64::NEG_INFINITY
,