From fcf9b7fff6f3f226b88b618a3d0ed85b3e2d0ffd Mon Sep 17 00:00:00 2001 From: Krishna Veera Reddy Date: Sat, 7 Dec 2019 16:12:41 -0800 Subject: [PATCH] Add lint to detect transmutes from float to integer Add lint that detects transmutation from a float to an integer and suggests usage of `{f32, f64}.to_bits()` instead. --- CHANGELOG.md | 1 + README.md | 2 +- clippy_lints/src/lib.rs | 2 ++ clippy_lints/src/transmute.rs | 67 +++++++++++++++++++++++++++++++++++ src/lintlist/mod.rs | 9 ++++- tests/ui/transmute.rs | 10 ++++++ tests/ui/transmute.stderr | 56 ++++++++++++++++++++++++----- 7 files changed, 136 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a9448a57f7fd..962f9067a4e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1214,6 +1214,7 @@ Released 2018-09-13 [`too_many_lines`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines [`toplevel_ref_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#toplevel_ref_arg [`transmute_bytes_to_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_bytes_to_str +[`transmute_float_to_int`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_float_to_int [`transmute_int_to_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_bool [`transmute_int_to_char`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_char [`transmute_int_to_float`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_float diff --git a/README.md b/README.md index 97a7c97b49a2..6133fa4c3a57 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code. -[There are 339 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) +[There are 340 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you: diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index d14f946c8eb8..736ff30c81a7 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -735,6 +735,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf &trait_bounds::TYPE_REPETITION_IN_BOUNDS, &transmute::CROSSPOINTER_TRANSMUTE, &transmute::TRANSMUTE_BYTES_TO_STR, + &transmute::TRANSMUTE_FLOAT_TO_INT, &transmute::TRANSMUTE_INT_TO_BOOL, &transmute::TRANSMUTE_INT_TO_CHAR, &transmute::TRANSMUTE_INT_TO_FLOAT, @@ -1586,6 +1587,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf LintId::of(&mutex_atomic::MUTEX_INTEGER), LintId::of(&needless_borrow::NEEDLESS_BORROW), LintId::of(&path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE), + LintId::of(&transmute::TRANSMUTE_FLOAT_TO_INT), LintId::of(&use_self::USE_SELF), ]); } diff --git a/clippy_lints/src/transmute.rs b/clippy_lints/src/transmute.rs index 12de3023dada..080d9ef9dfe5 100644 --- a/clippy_lints/src/transmute.rs +++ b/clippy_lints/src/transmute.rs @@ -190,6 +190,28 @@ declare_clippy_lint! { "transmutes from an integer to a float" } +declare_clippy_lint! { + /// **What it does:** Checks for transmutes from a float to an integer. + /// + /// **Why is this bad?** Transmutes are dangerous and error-prone, whereas `to_bits` is intuitive + /// and safe. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// unsafe { + /// let _: u32 = std::mem::transmute(1f32); + /// } + /// + /// // should be: + /// let _: u32 = 1f32.to_bits(); + /// ``` + pub TRANSMUTE_FLOAT_TO_INT, + nursery, + "transmutes from a float to an integer" +} + declare_clippy_lint! { /// **What it does:** Checks for transmutes from a pointer to a pointer, or /// from a reference to a reference. @@ -254,6 +276,7 @@ declare_lint_pass!(Transmute => [ TRANSMUTE_BYTES_TO_STR, TRANSMUTE_INT_TO_BOOL, TRANSMUTE_INT_TO_FLOAT, + TRANSMUTE_FLOAT_TO_INT, UNSOUND_COLLECTION_TRANSMUTE, ]); @@ -520,6 +543,50 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Transmute { ); }, ), + (&ty::Float(float_ty), &ty::Int(_)) | (&ty::Float(float_ty), &ty::Uint(_)) => span_lint_and_then( + cx, + TRANSMUTE_FLOAT_TO_INT, + e.span, + &format!("transmute from a `{}` to a `{}`", from_ty, to_ty), + |db| { + let mut expr = &args[0]; + let mut arg = sugg::Sugg::hir(cx, expr, ".."); + + if let ExprKind::Unary(UnOp::UnNeg, inner_expr) = &expr.kind { + expr = &inner_expr; + } + + if_chain! { + // if the expression is a float literal and it is unsuffixed then + // add a suffix so the suggestion is valid and unambiguous + let op = format!("{}{}", arg, float_ty.name_str()).into(); + if let ExprKind::Lit(lit) = &expr.kind; + if let ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) = lit.node; + then { + match arg { + sugg::Sugg::MaybeParen(_) => arg = sugg::Sugg::MaybeParen(op), + _ => arg = sugg::Sugg::NonParen(op) + } + } + } + + arg = sugg::Sugg::NonParen(format!("{}.to_bits()", arg.maybe_par()).into()); + + // cast the result of `to_bits` if `to_ty` is signed + arg = if let ty::Int(int_ty) = to_ty.kind { + arg.as_ty(format!("{}", int_ty.name_str())) + } else { + arg + }; + + db.span_suggestion( + e.span, + "consider using", + arg.to_string(), + Applicability::Unspecified, + ); + }, + ), (&ty::Adt(ref from_adt, ref from_substs), &ty::Adt(ref to_adt, ref to_substs)) => { if from_adt.did != to_adt.did || !COLLECTIONS.iter().any(|path| match_def_path(cx, to_adt.did, path)) { diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 1f1c24b2c303..f4ebf6cbd918 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -6,7 +6,7 @@ pub use lint::Lint; pub use lint::LINT_LEVELS; // begin lint list, do not remove this comment, it’s used in `update_lints` -pub const ALL_LINTS: [Lint; 339] = [ +pub const ALL_LINTS: [Lint; 340] = [ Lint { name: "absurd_extreme_comparisons", group: "correctness", @@ -1953,6 +1953,13 @@ pub const ALL_LINTS: [Lint; 339] = [ deprecation: None, module: "transmute", }, + Lint { + name: "transmute_float_to_int", + group: "nursery", + desc: "transmutes from a float to an integer", + deprecation: None, + module: "transmute", + }, Lint { name: "transmute_int_to_bool", group: "complexity", diff --git a/tests/ui/transmute.rs b/tests/ui/transmute.rs index 4f0c2f5a895c..75d16f66a87f 100644 --- a/tests/ui/transmute.rs +++ b/tests/ui/transmute.rs @@ -126,6 +126,16 @@ fn int_to_float() { let _: f32 = unsafe { std::mem::transmute(0_i32) }; } +#[warn(clippy::transmute_float_to_int)] +fn float_to_int() { + let _: u32 = unsafe { std::mem::transmute(1f32) }; + let _: i32 = unsafe { std::mem::transmute(1f32) }; + let _: u64 = unsafe { std::mem::transmute(1f64) }; + let _: i64 = unsafe { std::mem::transmute(1f64) }; + let _: u64 = unsafe { std::mem::transmute(1.0) }; + let _: u64 = unsafe { std::mem::transmute(-1.0) }; +} + fn bytes_to_str(b: &[u8], mb: &mut [u8]) { let _: &str = unsafe { std::mem::transmute(b) }; let _: &mut str = unsafe { std::mem::transmute(mb) }; diff --git a/tests/ui/transmute.stderr b/tests/ui/transmute.stderr index f73d72f20bb3..241e850a2089 100644 --- a/tests/ui/transmute.stderr +++ b/tests/ui/transmute.stderr @@ -190,8 +190,46 @@ error: transmute from a `i32` to a `f32` LL | let _: f32 = unsafe { std::mem::transmute(0_i32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_i32 as u32)` +error: transmute from a `f32` to a `u32` + --> $DIR/transmute.rs:131:27 + | +LL | let _: u32 = unsafe { std::mem::transmute(1f32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f32.to_bits()` + | + = note: `-D clippy::transmute-float-to-int` implied by `-D warnings` + +error: transmute from a `f32` to a `i32` + --> $DIR/transmute.rs:132:27 + | +LL | let _: i32 = unsafe { std::mem::transmute(1f32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f32.to_bits() as i32` + +error: transmute from a `f64` to a `u64` + --> $DIR/transmute.rs:133:27 + | +LL | let _: u64 = unsafe { std::mem::transmute(1f64) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f64.to_bits()` + +error: transmute from a `f64` to a `i64` + --> $DIR/transmute.rs:134:27 + | +LL | let _: i64 = unsafe { std::mem::transmute(1f64) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1f64.to_bits() as i64` + +error: transmute from a `f64` to a `u64` + --> $DIR/transmute.rs:135:27 + | +LL | let _: u64 = unsafe { std::mem::transmute(1.0) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1.0f64.to_bits()` + +error: transmute from a `f64` to a `u64` + --> $DIR/transmute.rs:136:27 + | +LL | let _: u64 = unsafe { std::mem::transmute(-1.0) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(-1.0f64).to_bits()` + error: transmute from a `&[u8]` to a `&str` - --> $DIR/transmute.rs:130:28 + --> $DIR/transmute.rs:140:28 | LL | let _: &str = unsafe { std::mem::transmute(b) }; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8(b).unwrap()` @@ -199,13 +237,13 @@ LL | let _: &str = unsafe { std::mem::transmute(b) }; = note: `-D clippy::transmute-bytes-to-str` implied by `-D warnings` error: transmute from a `&mut [u8]` to a `&mut str` - --> $DIR/transmute.rs:131:32 + --> $DIR/transmute.rs:141:32 | LL | let _: &mut str = unsafe { std::mem::transmute(mb) }; | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_mut(mb).unwrap()` error: transmute from a pointer to a pointer - --> $DIR/transmute.rs:163:29 + --> $DIR/transmute.rs:173:29 | LL | let _: *const f32 = std::mem::transmute(ptr); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr as *const f32` @@ -213,34 +251,34 @@ LL | let _: *const f32 = std::mem::transmute(ptr); = note: `-D clippy::transmute-ptr-to-ptr` implied by `-D warnings` error: transmute from a pointer to a pointer - --> $DIR/transmute.rs:164:27 + --> $DIR/transmute.rs:174:27 | LL | let _: *mut f32 = std::mem::transmute(mut_ptr); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `mut_ptr as *mut f32` error: transmute from a reference to a reference - --> $DIR/transmute.rs:166:23 + --> $DIR/transmute.rs:176:23 | LL | let _: &f32 = std::mem::transmute(&1u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(&1u32 as *const u32 as *const f32)` error: transmute from a reference to a reference - --> $DIR/transmute.rs:167:23 + --> $DIR/transmute.rs:177:23 | LL | let _: &f64 = std::mem::transmute(&1f32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(&1f32 as *const f32 as *const f64)` error: transmute from a reference to a reference - --> $DIR/transmute.rs:170:27 + --> $DIR/transmute.rs:180:27 | LL | let _: &mut f32 = std::mem::transmute(&mut 1u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(&mut 1u32 as *mut u32 as *mut f32)` error: transmute from a reference to a reference - --> $DIR/transmute.rs:171:37 + --> $DIR/transmute.rs:181:37 | LL | let _: &GenericParam = std::mem::transmute(&GenericParam { t: 1u32 }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(&GenericParam { t: 1u32 } as *const GenericParam as *const GenericParam)` -error: aborting due to 38 previous errors +error: aborting due to 44 previous errors