Skip to content

Commit 5928b61

Browse files
committed
add cast-nan-to-int lint
1 parent 292e313 commit 5928b61

10 files changed

+144
-1
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3772,6 +3772,7 @@ Released 2018-09-13
37723772
[`cast_enum_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_enum_constructor
37733773
[`cast_enum_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_enum_truncation
37743774
[`cast_lossless`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_lossless
3775+
[`cast_nan_to_int`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_nan_to_int
37753776
[`cast_possible_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_possible_truncation
37763777
[`cast_possible_wrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_possible_wrap
37773778
[`cast_precision_loss`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_precision_loss
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
use super::CAST_NAN_TO_INT;
2+
3+
use clippy_utils::consts::{constant, Constant};
4+
use clippy_utils::diagnostics::span_lint_and_note;
5+
use rustc_hir::Expr;
6+
use rustc_lint::LateContext;
7+
use rustc_middle::ty::Ty;
8+
9+
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, from_ty: Ty<'_>, to_ty: Ty<'_>) {
10+
if from_ty.is_floating_point() && to_ty.is_integral() && is_known_nan(cx, cast_expr) {
11+
span_lint_and_note(
12+
cx,
13+
CAST_NAN_TO_INT,
14+
expr.span,
15+
&format!("casting a known NaN to {to_ty}"),
16+
None,
17+
"this always evaluates to 0",
18+
);
19+
}
20+
}
21+
22+
fn is_known_nan(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
23+
match constant(cx, cx.typeck_results(), e) {
24+
Some((Constant::F64(n), _)) => n.is_nan(),
25+
Some((Constant::F32(n), _)) => n.is_nan(),
26+
_ => false,
27+
}
28+
}

clippy_lints/src/casts/mod.rs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ mod borrow_as_ptr;
33
mod cast_abs_to_unsigned;
44
mod cast_enum_constructor;
55
mod cast_lossless;
6+
mod cast_nan_to_int;
67
mod cast_possible_truncation;
78
mod cast_possible_wrap;
89
mod cast_precision_loss;
@@ -569,6 +570,7 @@ declare_clippy_lint! {
569570
pedantic,
570571
"borrowing just to cast to a raw pointer"
571572
}
573+
572574
declare_clippy_lint! {
573575
/// ### What it does
574576
/// Checks for a raw slice being cast to a slice pointer
@@ -596,6 +598,28 @@ declare_clippy_lint! {
596598
"casting a slice created from a pointer and length to a slice pointer"
597599
}
598600

601+
declare_clippy_lint! {
602+
/// ### What it does
603+
/// Checks for a known NaN float being cast to an integer
604+
///
605+
/// ### Why is this bad?
606+
/// NaNs are cast into zero, so one could simply use this and make the
607+
/// code more readable. The lint could also hint at a programmer error.
608+
///
609+
/// ### Example
610+
/// ```rust,ignore
611+
/// let _: (0.0_f32 / 0.0) as u64;
612+
/// ```
613+
/// Use instead:
614+
/// ```rust,ignore
615+
/// let _: = 0_u64;
616+
/// ```
617+
#[clippy::version = "1.64.0"]
618+
pub CAST_NAN_TO_INT,
619+
suspicious,
620+
"casting a known floating-point NaN into an integer"
621+
}
622+
599623
pub struct Casts {
600624
msrv: Option<RustcVersion>,
601625
}
@@ -627,7 +651,8 @@ impl_lint_pass!(Casts => [
627651
CAST_ABS_TO_UNSIGNED,
628652
AS_UNDERSCORE,
629653
BORROW_AS_PTR,
630-
CAST_SLICE_FROM_RAW_PARTS
654+
CAST_SLICE_FROM_RAW_PARTS,
655+
CAST_NAN_TO_INT,
631656
]);
632657

633658
impl<'tcx> LateLintPass<'tcx> for Casts {
@@ -664,6 +689,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
664689
cast_precision_loss::check(cx, expr, cast_from, cast_to);
665690
cast_sign_loss::check(cx, expr, cast_expr, cast_from, cast_to);
666691
cast_abs_to_unsigned::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv);
692+
cast_nan_to_int::check(cx, expr, cast_expr, cast_from, cast_to);
667693
}
668694
cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv);
669695
cast_enum_constructor::check(cx, expr, cast_expr, cast_from);

clippy_lints/src/lib.register_all.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
2525
LintId::of(casts::CAST_ABS_TO_UNSIGNED),
2626
LintId::of(casts::CAST_ENUM_CONSTRUCTOR),
2727
LintId::of(casts::CAST_ENUM_TRUNCATION),
28+
LintId::of(casts::CAST_NAN_TO_INT),
2829
LintId::of(casts::CAST_REF_TO_MUT),
2930
LintId::of(casts::CAST_SLICE_DIFFERENT_SIZES),
3031
LintId::of(casts::CAST_SLICE_FROM_RAW_PARTS),

clippy_lints/src/lib.register_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ store.register_lints(&[
7272
casts::CAST_ENUM_CONSTRUCTOR,
7373
casts::CAST_ENUM_TRUNCATION,
7474
casts::CAST_LOSSLESS,
75+
casts::CAST_NAN_TO_INT,
7576
casts::CAST_POSSIBLE_TRUNCATION,
7677
casts::CAST_POSSIBLE_WRAP,
7778
casts::CAST_PRECISION_LOSS,

clippy_lints/src/lib.register_suspicious.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec!
1111
LintId::of(casts::CAST_ABS_TO_UNSIGNED),
1212
LintId::of(casts::CAST_ENUM_CONSTRUCTOR),
1313
LintId::of(casts::CAST_ENUM_TRUNCATION),
14+
LintId::of(casts::CAST_NAN_TO_INT),
1415
LintId::of(casts::CAST_SLICE_FROM_RAW_PARTS),
1516
LintId::of(crate_in_macro_def::CRATE_IN_MACRO_DEF),
1617
LintId::of(drop_forget_ref::DROP_NON_DROP),

src/docs.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ docs! {
6060
"cast_enum_constructor",
6161
"cast_enum_truncation",
6262
"cast_lossless",
63+
"cast_nan_to_int",
6364
"cast_possible_truncation",
6465
"cast_possible_wrap",
6566
"cast_precision_loss",

src/docs/cast_nan_to_int.txt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
### What it does
2+
Checks for a known NaN float being cast to an integer
3+
4+
### Why is this bad?
5+
NaNs are cast into zero, so one could simply use this and make the
6+
code more readable. The lint could also hint at a programmer error.
7+
8+
### Example
9+
```
10+
let _: (0.0_f32 / 0.0) as u64;
11+
```
12+
Use instead:
13+
```
14+
let _: = 0_u64;
15+
```

tests/ui/cast_nan_to_int.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#![warn(clippy::cast_nan_to_int)]
2+
#![allow(clippy::eq_op)]
3+
4+
fn main() {
5+
let _ = (0.0_f32 / -0.0) as usize;
6+
let _ = (f64::INFINITY * -0.0) as usize;
7+
let _ = (0.0 * f32::INFINITY) as usize;
8+
9+
let _ = (f64::INFINITY + f64::NEG_INFINITY) as usize;
10+
let _ = (f32::INFINITY - f32::INFINITY) as usize;
11+
let _ = (f32::INFINITY / f32::NEG_INFINITY) as usize;
12+
13+
// those won't be linted:
14+
let _ = (1.0_f32 / 0.0) as usize;
15+
let _ = (f32::INFINITY * f32::NEG_INFINITY) as usize;
16+
let _ = (f32::INFINITY - f32::NEG_INFINITY) as usize;
17+
let _ = (f64::INFINITY - 0.0) as usize;
18+
}

tests/ui/cast_nan_to_int.stderr

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
error: casting a known NaN to usize
2+
--> $DIR/cast_nan_to_int.rs:5:13
3+
|
4+
LL | let _ = (0.0_f32 / -0.0) as usize;
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: this always evaluates to 0
8+
= note: `-D clippy::cast-nan-to-int` implied by `-D warnings`
9+
10+
error: casting a known NaN to usize
11+
--> $DIR/cast_nan_to_int.rs:6:13
12+
|
13+
LL | let _ = (f64::INFINITY * -0.0) as usize;
14+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
15+
|
16+
= note: this always evaluates to 0
17+
18+
error: casting a known NaN to usize
19+
--> $DIR/cast_nan_to_int.rs:7:13
20+
|
21+
LL | let _ = (0.0 * f32::INFINITY) as usize;
22+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
23+
|
24+
= note: this always evaluates to 0
25+
26+
error: casting a known NaN to usize
27+
--> $DIR/cast_nan_to_int.rs:9:13
28+
|
29+
LL | let _ = (f64::INFINITY + f64::NEG_INFINITY) as usize;
30+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
31+
|
32+
= note: this always evaluates to 0
33+
34+
error: casting a known NaN to usize
35+
--> $DIR/cast_nan_to_int.rs:10:13
36+
|
37+
LL | let _ = (f32::INFINITY - f32::INFINITY) as usize;
38+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
39+
|
40+
= note: this always evaluates to 0
41+
42+
error: casting a known NaN to usize
43+
--> $DIR/cast_nan_to_int.rs:11:13
44+
|
45+
LL | let _ = (f32::INFINITY / f32::NEG_INFINITY) as usize;
46+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
47+
|
48+
= note: this always evaluates to 0
49+
50+
error: aborting due to 6 previous errors
51+

0 commit comments

Comments
 (0)