Skip to content

Commit 6bc229b

Browse files
committed
manual_float_methods
1 parent 2c40b99 commit 6bc229b

File tree

8 files changed

+239
-0
lines changed

8 files changed

+239
-0
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -4941,6 +4941,8 @@ Released 2018-09-13
49414941
[`manual_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_flatten
49424942
[`manual_instant_elapsed`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_instant_elapsed
49434943
[`manual_is_ascii_check`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check
4944+
[`manual_is_finite`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_finite
4945+
[`manual_is_infinite`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_infinite
49444946
[`manual_let_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else
49454947
[`manual_main_separator_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_main_separator_str
49464948
[`manual_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_map

clippy_lints/src/declared_lints.rs

+2
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,8 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
273273
crate::manual_async_fn::MANUAL_ASYNC_FN_INFO,
274274
crate::manual_bits::MANUAL_BITS_INFO,
275275
crate::manual_clamp::MANUAL_CLAMP_INFO,
276+
crate::manual_float_methods::MANUAL_IS_FINITE_INFO,
277+
crate::manual_float_methods::MANUAL_IS_INFINITE_INFO,
276278
crate::manual_is_ascii_check::MANUAL_IS_ASCII_CHECK_INFO,
277279
crate::manual_let_else::MANUAL_LET_ELSE_INFO,
278280
crate::manual_main_separator_str::MANUAL_MAIN_SEPARATOR_STR_INFO,

clippy_lints/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ mod manual_assert;
184184
mod manual_async_fn;
185185
mod manual_bits;
186186
mod manual_clamp;
187+
mod manual_float_methods;
187188
mod manual_is_ascii_check;
188189
mod manual_let_else;
189190
mod manual_main_separator_str;
@@ -1072,6 +1073,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
10721073
});
10731074
store.register_late_pass(|_| Box::new(manual_range_patterns::ManualRangePatterns));
10741075
store.register_early_pass(|| Box::new(visibility::Visibility));
1076+
store.register_late_pass(|_| Box::new(manual_float_methods::ManualFloatMethods));
10751077
// add lints here, do not remove this comment, it's used in `new_lint`
10761078
}
10771079

+106
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
use clippy_utils::{
2+
consts::constant, diagnostics::span_lint_and_sugg, is_from_proc_macro, path_to_local, source::snippet_opt,
3+
};
4+
use rustc_errors::Applicability;
5+
use rustc_hir::{BinOpKind, Expr, ExprKind};
6+
use rustc_lint::{LateContext, LateLintPass, LintContext};
7+
use rustc_middle::lint::in_external_macro;
8+
use rustc_session::{declare_lint_pass, declare_tool_lint};
9+
10+
declare_clippy_lint! {
11+
/// ### What it does
12+
/// Checks for `x == <float>::INFINITY || x == <float>::NEG_INFINITY`.
13+
///
14+
/// ### Why is this bad?
15+
/// This should use the dedicated method instead, `is_infinite`.
16+
///
17+
/// ### Example
18+
/// ```rust
19+
/// # let x = 1.0f32;
20+
/// if x == f32::INFINITY || x == f32::NEG_INFINITY {}
21+
/// ```
22+
/// Use instead:
23+
/// ```rust
24+
/// # let x = 1.0f32;
25+
/// if x.is_infinite() {}
26+
/// ```
27+
#[clippy::version = "1.72.0"]
28+
pub MANUAL_IS_INFINITE,
29+
style,
30+
"use dedicated method to check if a float is infinite"
31+
}
32+
declare_clippy_lint! {
33+
/// ### What it does
34+
/// Checks for `x != <float>::INFINITY && x != <float>::NEG_INFINITY`.
35+
///
36+
/// ### Why is this bad?
37+
/// This should use the dedicated method instead, `is_finite`.
38+
///
39+
/// ### Example
40+
/// ```rust
41+
/// # let x = 1.0f32;
42+
/// if x != f32::INFINITY && x != f32::NEG_INFINITY {}
43+
/// ```
44+
/// Use instead:
45+
/// ```rust
46+
/// # let x = 1.0f32;
47+
/// if x.is_finite() {}
48+
/// ```
49+
#[clippy::version = "1.72.0"]
50+
pub MANUAL_IS_FINITE,
51+
style,
52+
"use dedicated method to check if a float is finite"
53+
}
54+
declare_lint_pass!(ManualFloatMethods => [MANUAL_IS_INFINITE, MANUAL_IS_FINITE]);
55+
56+
impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods {
57+
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
58+
if !in_external_macro(cx.sess(), expr.span)
59+
&& let ExprKind::Binary(kind, lhs, rhs) = expr.kind
60+
&& let ExprKind::Binary(lhs_kind, lhs_lhs, lhs_rhs) = lhs.kind
61+
&& let ExprKind::Binary(rhs_kind, rhs_lhs, rhs_rhs) = rhs.kind
62+
&& let (operands, consts) = [lhs_lhs, lhs_rhs, rhs_lhs, rhs_rhs]
63+
.into_iter()
64+
.partition::<Vec<&Expr<'_>>, _>(|i| path_to_local(i).is_some())
65+
&& let [first, second] = &*operands
66+
&& let Some([const_1, const_2]) = consts
67+
.into_iter()
68+
.map(|i| constant(cx, cx.typeck_results(), i).and_then(|c| c.to_bits()))
69+
.collect::<Option<Vec<_>>>()
70+
.as_deref()
71+
&& path_to_local(first).is_some_and(|f| path_to_local(second).is_some_and(|s| f == s))
72+
&& (is_infinity(*const_1) && is_neg_infinity(*const_2)
73+
|| is_neg_infinity(*const_1) && is_infinity(*const_2))
74+
&& let Some(local_snippet) = snippet_opt(cx, first.span)
75+
&& !is_from_proc_macro(cx, expr)
76+
{
77+
let (msg, lint, sugg_fn) = match (kind.node, lhs_kind.node, rhs_kind.node) {
78+
(BinOpKind::Or, BinOpKind::Eq, BinOpKind::Eq) => {
79+
("manually checking if a float is infinite", MANUAL_IS_INFINITE, "is_infinite")
80+
},
81+
(BinOpKind::And, BinOpKind::Ne, BinOpKind::Ne) => {
82+
("manually checking if a float is finite", MANUAL_IS_FINITE, "is_finite")
83+
},
84+
_ => return,
85+
};
86+
87+
span_lint_and_sugg(
88+
cx,
89+
lint,
90+
expr.span,
91+
msg,
92+
"try",
93+
format!("{local_snippet}.{sugg_fn}()"),
94+
Applicability::MachineApplicable,
95+
);
96+
}
97+
}
98+
}
99+
100+
fn is_infinity(bits: u128) -> bool {
101+
bits == 0x7f80_0000 || bits == 0x7ff0_0000_0000_0000
102+
}
103+
104+
fn is_neg_infinity(bits: u128) -> bool {
105+
bits == 0xff80_0000 || bits == 0xfff0_0000_0000_0000
106+
}

clippy_utils/src/consts.rs

+11
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,17 @@ impl<'tcx> Constant<'tcx> {
190190
}
191191
}
192192

193+
/// Returns the bit representation if `self` is a bool, integer, or float.
194+
pub fn to_bits(&self) -> Option<u128> {
195+
match self {
196+
Constant::Int(int) => Some(*int),
197+
Constant::F32(float) => Some(u128::from(float.to_bits())),
198+
Constant::F64(float) => Some(u128::from(float.to_bits())),
199+
Constant::Bool(bool) => Some(u128::from(*bool)),
200+
_ => None,
201+
}
202+
}
203+
193204
/// Returns the integer value or `None` if `self` or `val_type` is not integer type.
194205
pub fn int_value(&self, cx: &LateContext<'_>, val_type: Ty<'_>) -> Option<FullInt> {
195206
if let Constant::Int(const_int) = *self {

tests/ui/manual_float_methods.fixed

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//@run-rustfix
2+
//@aux-build:proc_macros.rs:proc-macro
3+
#![allow(clippy::needless_if, unused)]
4+
#![warn(clippy::manual_is_infinite, clippy::manual_is_finite)]
5+
6+
#[macro_use]
7+
extern crate proc_macros;
8+
9+
const INFINITE: f32 = f32::INFINITY;
10+
const NEG_INFINITE: f32 = f32::NEG_INFINITY;
11+
12+
fn main() {
13+
let x = 1.0f32;
14+
if x.is_infinite() {}
15+
if x.is_finite() {}
16+
if x.is_infinite() {}
17+
if x.is_finite() {}
18+
let x = 1.0f64;
19+
if x.is_infinite() {}
20+
if x.is_finite() {}
21+
// Don't lint
22+
if x.is_infinite() {}
23+
if x.is_finite() {}
24+
// If they're doing it this way, they probably know what they're doing
25+
if x.abs() < f64::INFINITY {}
26+
external! {
27+
let x = 1.0;
28+
if x == f32::INFINITY || x == f32::NEG_INFINITY {}
29+
if x != f32::INFINITY && x != f32::NEG_INFINITY {}
30+
}
31+
with_span! {
32+
span
33+
let x = 1.0;
34+
if x == f32::INFINITY || x == f32::NEG_INFINITY {}
35+
if x != f32::INFINITY && x != f32::NEG_INFINITY {}
36+
}
37+
}

tests/ui/manual_float_methods.rs

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//@run-rustfix
2+
//@aux-build:proc_macros.rs:proc-macro
3+
#![allow(clippy::needless_if, unused)]
4+
#![warn(clippy::manual_is_infinite, clippy::manual_is_finite)]
5+
6+
#[macro_use]
7+
extern crate proc_macros;
8+
9+
const INFINITE: f32 = f32::INFINITY;
10+
const NEG_INFINITE: f32 = f32::NEG_INFINITY;
11+
12+
fn main() {
13+
let x = 1.0f32;
14+
if x == f32::INFINITY || x == f32::NEG_INFINITY {}
15+
if x != f32::INFINITY && x != f32::NEG_INFINITY {}
16+
if x == INFINITE || x == NEG_INFINITE {}
17+
if x != INFINITE && x != NEG_INFINITE {}
18+
let x = 1.0f64;
19+
if x == f64::INFINITY || x == f64::NEG_INFINITY {}
20+
if x != f64::INFINITY && x != f64::NEG_INFINITY {}
21+
// Don't lint
22+
if x.is_infinite() {}
23+
if x.is_finite() {}
24+
// If they're doing it this way, they probably know what they're doing
25+
if x.abs() < f64::INFINITY {}
26+
external! {
27+
let x = 1.0;
28+
if x == f32::INFINITY || x == f32::NEG_INFINITY {}
29+
if x != f32::INFINITY && x != f32::NEG_INFINITY {}
30+
}
31+
with_span! {
32+
span
33+
let x = 1.0;
34+
if x == f32::INFINITY || x == f32::NEG_INFINITY {}
35+
if x != f32::INFINITY && x != f32::NEG_INFINITY {}
36+
}
37+
}

tests/ui/manual_float_methods.stderr

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
error: manually checking if a float is infinite
2+
--> $DIR/manual_float_methods.rs:14:8
3+
|
4+
LL | if x == f32::INFINITY || x == f32::NEG_INFINITY {}
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `x.is_infinite()`
6+
|
7+
= note: `-D clippy::manual-is-infinite` implied by `-D warnings`
8+
9+
error: manually checking if a float is finite
10+
--> $DIR/manual_float_methods.rs:15:8
11+
|
12+
LL | if x != f32::INFINITY && x != f32::NEG_INFINITY {}
13+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `x.is_finite()`
14+
|
15+
= note: `-D clippy::manual-is-finite` implied by `-D warnings`
16+
17+
error: manually checking if a float is infinite
18+
--> $DIR/manual_float_methods.rs:16:8
19+
|
20+
LL | if x == INFINITE || x == NEG_INFINITE {}
21+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `x.is_infinite()`
22+
23+
error: manually checking if a float is finite
24+
--> $DIR/manual_float_methods.rs:17:8
25+
|
26+
LL | if x != INFINITE && x != NEG_INFINITE {}
27+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `x.is_finite()`
28+
29+
error: manually checking if a float is infinite
30+
--> $DIR/manual_float_methods.rs:19:8
31+
|
32+
LL | if x == f64::INFINITY || x == f64::NEG_INFINITY {}
33+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `x.is_infinite()`
34+
35+
error: manually checking if a float is finite
36+
--> $DIR/manual_float_methods.rs:20:8
37+
|
38+
LL | if x != f64::INFINITY && x != f64::NEG_INFINITY {}
39+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `x.is_finite()`
40+
41+
error: aborting due to 6 previous errors
42+

0 commit comments

Comments
 (0)