add cast-nan-to-int lint

This commit is contained in:
Andre Bogus 2022-10-08 23:33:23 +02:00
parent 854015c33c
commit e4c80f2bba
10 changed files with 143 additions and 0 deletions

View file

@ -3773,6 +3773,7 @@ Released 2018-09-13
[`cast_enum_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_enum_constructor [`cast_enum_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_enum_constructor
[`cast_enum_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_enum_truncation [`cast_enum_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_enum_truncation
[`cast_lossless`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_lossless [`cast_lossless`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_lossless
[`cast_nan_to_int`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_nan_to_int
[`cast_possible_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_possible_truncation [`cast_possible_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_possible_truncation
[`cast_possible_wrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_possible_wrap [`cast_possible_wrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_possible_wrap
[`cast_precision_loss`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_precision_loss [`cast_precision_loss`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_precision_loss

View file

@ -0,0 +1,28 @@
use super::CAST_NAN_TO_INT;
use clippy_utils::consts::{constant, Constant};
use clippy_utils::diagnostics::span_lint_and_note;
use rustc_hir::Expr;
use rustc_lint::LateContext;
use rustc_middle::ty::Ty;
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, from_ty: Ty<'_>, to_ty: Ty<'_>) {
if from_ty.is_floating_point() && to_ty.is_integral() && is_known_nan(cx, cast_expr) {
span_lint_and_note(
cx,
CAST_NAN_TO_INT,
expr.span,
&format!("casting a known NaN to {to_ty}"),
None,
"this always evaluates to 0",
);
}
}
fn is_known_nan(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
match constant(cx, cx.typeck_results(), e) {
Some((Constant::F64(n), _)) => n.is_nan(),
Some((Constant::F32(n), _)) => n.is_nan(),
_ => false,
}
}

View file

@ -4,6 +4,7 @@ mod borrow_as_ptr;
mod cast_abs_to_unsigned; mod cast_abs_to_unsigned;
mod cast_enum_constructor; mod cast_enum_constructor;
mod cast_lossless; mod cast_lossless;
mod cast_nan_to_int;
mod cast_possible_truncation; mod cast_possible_truncation;
mod cast_possible_wrap; mod cast_possible_wrap;
mod cast_precision_loss; mod cast_precision_loss;
@ -570,6 +571,7 @@ declare_clippy_lint! {
pedantic, pedantic,
"borrowing just to cast to a raw pointer" "borrowing just to cast to a raw pointer"
} }
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
/// Checks for a raw slice being cast to a slice pointer /// Checks for a raw slice being cast to a slice pointer
@ -623,6 +625,28 @@ declare_clippy_lint! {
"casting the result of the `&self`-taking `as_ptr` to a mutabe pointer" "casting the result of the `&self`-taking `as_ptr` to a mutabe pointer"
} }
declare_clippy_lint! {
/// ### What it does
/// Checks for a known NaN float being cast to an integer
///
/// ### Why is this bad?
/// NaNs are cast into zero, so one could simply use this and make the
/// code more readable. The lint could also hint at a programmer error.
///
/// ### Example
/// ```rust,ignore
/// let _: (0.0_f32 / 0.0) as u64;
/// ```
/// Use instead:
/// ```rust,ignore
/// let _: = 0_u64;
/// ```
#[clippy::version = "1.64.0"]
pub CAST_NAN_TO_INT,
suspicious,
"casting a known floating-point NaN into an integer"
}
pub struct Casts { pub struct Casts {
msrv: Option<RustcVersion>, msrv: Option<RustcVersion>,
} }
@ -656,6 +680,7 @@ impl_lint_pass!(Casts => [
BORROW_AS_PTR, BORROW_AS_PTR,
CAST_SLICE_FROM_RAW_PARTS, CAST_SLICE_FROM_RAW_PARTS,
AS_PTR_CAST_MUT, AS_PTR_CAST_MUT,
CAST_NAN_TO_INT,
]); ]);
impl<'tcx> LateLintPass<'tcx> for Casts { impl<'tcx> LateLintPass<'tcx> for Casts {
@ -693,6 +718,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
cast_precision_loss::check(cx, expr, cast_from, cast_to); cast_precision_loss::check(cx, expr, cast_from, cast_to);
cast_sign_loss::check(cx, expr, cast_expr, cast_from, cast_to); cast_sign_loss::check(cx, expr, cast_expr, cast_from, cast_to);
cast_abs_to_unsigned::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv); cast_abs_to_unsigned::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv);
cast_nan_to_int::check(cx, expr, cast_expr, cast_from, cast_to);
} }
cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv); cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv);
cast_enum_constructor::check(cx, expr, cast_expr, cast_from); cast_enum_constructor::check(cx, expr, cast_expr, cast_from);

View file

@ -25,6 +25,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
LintId::of(casts::CAST_ABS_TO_UNSIGNED), LintId::of(casts::CAST_ABS_TO_UNSIGNED),
LintId::of(casts::CAST_ENUM_CONSTRUCTOR), LintId::of(casts::CAST_ENUM_CONSTRUCTOR),
LintId::of(casts::CAST_ENUM_TRUNCATION), LintId::of(casts::CAST_ENUM_TRUNCATION),
LintId::of(casts::CAST_NAN_TO_INT),
LintId::of(casts::CAST_REF_TO_MUT), LintId::of(casts::CAST_REF_TO_MUT),
LintId::of(casts::CAST_SLICE_DIFFERENT_SIZES), LintId::of(casts::CAST_SLICE_DIFFERENT_SIZES),
LintId::of(casts::CAST_SLICE_FROM_RAW_PARTS), LintId::of(casts::CAST_SLICE_FROM_RAW_PARTS),

View file

@ -73,6 +73,7 @@ store.register_lints(&[
casts::CAST_ENUM_CONSTRUCTOR, casts::CAST_ENUM_CONSTRUCTOR,
casts::CAST_ENUM_TRUNCATION, casts::CAST_ENUM_TRUNCATION,
casts::CAST_LOSSLESS, casts::CAST_LOSSLESS,
casts::CAST_NAN_TO_INT,
casts::CAST_POSSIBLE_TRUNCATION, casts::CAST_POSSIBLE_TRUNCATION,
casts::CAST_POSSIBLE_WRAP, casts::CAST_POSSIBLE_WRAP,
casts::CAST_PRECISION_LOSS, casts::CAST_PRECISION_LOSS,

View file

@ -11,6 +11,7 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec!
LintId::of(casts::CAST_ABS_TO_UNSIGNED), LintId::of(casts::CAST_ABS_TO_UNSIGNED),
LintId::of(casts::CAST_ENUM_CONSTRUCTOR), LintId::of(casts::CAST_ENUM_CONSTRUCTOR),
LintId::of(casts::CAST_ENUM_TRUNCATION), LintId::of(casts::CAST_ENUM_TRUNCATION),
LintId::of(casts::CAST_NAN_TO_INT),
LintId::of(casts::CAST_SLICE_FROM_RAW_PARTS), LintId::of(casts::CAST_SLICE_FROM_RAW_PARTS),
LintId::of(crate_in_macro_def::CRATE_IN_MACRO_DEF), LintId::of(crate_in_macro_def::CRATE_IN_MACRO_DEF),
LintId::of(drop_forget_ref::DROP_NON_DROP), LintId::of(drop_forget_ref::DROP_NON_DROP),

View file

@ -61,6 +61,7 @@ docs! {
"cast_enum_constructor", "cast_enum_constructor",
"cast_enum_truncation", "cast_enum_truncation",
"cast_lossless", "cast_lossless",
"cast_nan_to_int",
"cast_possible_truncation", "cast_possible_truncation",
"cast_possible_wrap", "cast_possible_wrap",
"cast_precision_loss", "cast_precision_loss",

View file

@ -0,0 +1,15 @@
### What it does
Checks for a known NaN float being cast to an integer
### Why is this bad?
NaNs are cast into zero, so one could simply use this and make the
code more readable. The lint could also hint at a programmer error.
### Example
```
let _: (0.0_f32 / 0.0) as u64;
```
Use instead:
```
let _: = 0_u64;
```

View file

@ -0,0 +1,18 @@
#![warn(clippy::cast_nan_to_int)]
#![allow(clippy::eq_op)]
fn main() {
let _ = (0.0_f32 / -0.0) as usize;
let _ = (f64::INFINITY * -0.0) as usize;
let _ = (0.0 * f32::INFINITY) as usize;
let _ = (f64::INFINITY + f64::NEG_INFINITY) as usize;
let _ = (f32::INFINITY - f32::INFINITY) as usize;
let _ = (f32::INFINITY / f32::NEG_INFINITY) as usize;
// those won't be linted:
let _ = (1.0_f32 / 0.0) as usize;
let _ = (f32::INFINITY * f32::NEG_INFINITY) as usize;
let _ = (f32::INFINITY - f32::NEG_INFINITY) as usize;
let _ = (f64::INFINITY - 0.0) as usize;
}

View file

@ -0,0 +1,51 @@
error: casting a known NaN to usize
--> $DIR/cast_nan_to_int.rs:5:13
|
LL | let _ = (0.0_f32 / -0.0) as usize;
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this always evaluates to 0
= note: `-D clippy::cast-nan-to-int` implied by `-D warnings`
error: casting a known NaN to usize
--> $DIR/cast_nan_to_int.rs:6:13
|
LL | let _ = (f64::INFINITY * -0.0) as usize;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this always evaluates to 0
error: casting a known NaN to usize
--> $DIR/cast_nan_to_int.rs:7:13
|
LL | let _ = (0.0 * f32::INFINITY) as usize;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this always evaluates to 0
error: casting a known NaN to usize
--> $DIR/cast_nan_to_int.rs:9:13
|
LL | let _ = (f64::INFINITY + f64::NEG_INFINITY) as usize;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this always evaluates to 0
error: casting a known NaN to usize
--> $DIR/cast_nan_to_int.rs:10:13
|
LL | let _ = (f32::INFINITY - f32::INFINITY) as usize;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this always evaluates to 0
error: casting a known NaN to usize
--> $DIR/cast_nan_to_int.rs:11:13
|
LL | let _ = (f32::INFINITY / f32::NEG_INFINITY) as usize;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this always evaluates to 0
error: aborting due to 6 previous errors