Skip to content

Commit

Permalink
Add lint to detect transmutes from float to integer
Browse files Browse the repository at this point in the history
Add lint that detects transmutation from a float to an integer
and suggests usage of `{f32, f64}.to_bits()` instead.
  • Loading branch information
krishna-veerareddy committed Dec 8, 2019
1 parent 61b19a1 commit c77fc06
Show file tree
Hide file tree
Showing 7 changed files with 136 additions and 11 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down
2 changes: 2 additions & 0 deletions clippy_lints/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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),
]);
}
Expand Down
67 changes: 67 additions & 0 deletions clippy_lints/src/transmute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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,
]);

Expand Down Expand Up @@ -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(int_ty.name_str().to_string())
} 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)) {
Expand Down
9 changes: 8 additions & 1 deletion src/lintlist/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down
10 changes: 10 additions & 0 deletions tests/ui/transmute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) };
Expand Down
56 changes: 47 additions & 9 deletions tests/ui/transmute.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -190,57 +190,95 @@ 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()`
|
= 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`
|
= 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<f32> = std::mem::transmute(&GenericParam { t: 1u32 });
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(&GenericParam { t: 1u32 } as *const GenericParam<u32> as *const GenericParam<f32>)`

error: aborting due to 38 previous errors
error: aborting due to 44 previous errors

0 comments on commit c77fc06

Please sign in to comment.