From 47df71722923474014f26510d59968f063d8ecdd Mon Sep 17 00:00:00 2001 From: PizzaIter Date: Mon, 2 Oct 2017 17:23:24 +0200 Subject: [PATCH] Add lints `transmute_int_to_*` --- clippy_lints/src/lib.rs | 3 + clippy_lints/src/transmute.rs | 110 +++++++++++++++++++++++++++++++++- tests/ui/transmute.rs | 17 ++++++ tests/ui/transmute.stderr | 36 +++++++++++ 4 files changed, 165 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 38d7847f42b8..933ec1a8e70c 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -544,6 +544,9 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) { transmute::TRANSMUTE_PTR_TO_REF, transmute::USELESS_TRANSMUTE, transmute::WRONG_TRANSMUTE, + transmute::TRANSMUTE_INT_TO_CHAR, + transmute::TRANSMUTE_INT_TO_BOOL, + transmute::TRANSMUTE_INT_TO_FLOAT, types::ABSURD_EXTREME_COMPARISONS, types::BORROWED_BOX, types::BOX_VEC, diff --git a/clippy_lints/src/transmute.rs b/clippy_lints/src/transmute.rs index 76110ecb152b..a97c24166b48 100644 --- a/clippy_lints/src/transmute.rs +++ b/clippy_lints/src/transmute.rs @@ -1,6 +1,8 @@ use rustc::lint::*; use rustc::ty::{self, Ty}; use rustc::hir::*; +use std::borrow::Cow; +use syntax::ast; use utils::{last_path_segment, match_def_path, paths, snippet, span_lint, span_lint_and_then}; use utils::{sugg, opt_def_id}; @@ -76,11 +78,73 @@ declare_lint! { "transmutes from a pointer to a reference type" } +/// **What it does:** Checks for transmutes from an integer to a `char`. +/// +/// **Why is this bad?** Not every integer is a unicode scalar value. +/// +/// **Known problems:** None. +/// +/// **Example:** +/// ```rust +/// let _: char = std::mem::transmute(x); // where x: u32 +/// // should be: +/// let _: Option = std::char::from_u32(x); +/// ``` +declare_lint! { + pub TRANSMUTE_INT_TO_CHAR, + Warn, + "transmutes from an integer to a `char`" +} + +/// **What it does:** Checks for transmutes from an integer to a `bool`. +/// +/// **Why is this bad?** This might result in an invalid in-memory representation of a `bool`. +/// +/// **Known problems:** None. +/// +/// **Example:** +/// ```rust +/// let _: bool = std::mem::transmute(x); // where x: u8 +/// // should be: +/// let _: bool = x != 0; +/// ``` +declare_lint! { + pub TRANSMUTE_INT_TO_BOOL, + Warn, + "transmutes from an integer to a `bool`" +} + +/// **What it does:** Checks for transmutes from an integer to a float. +/// +/// **Why is this bad?** This might result in an invalid in-memory representation of a float. +/// +/// **Known problems:** None. +/// +/// **Example:** +/// ```rust +/// let _: f32 = std::mem::transmute(x); // where x: u32 +/// // should be: +/// let _: f32 = f32::from_bits(x); +/// ``` +declare_lint! { + pub TRANSMUTE_INT_TO_FLOAT, + Warn, + "transmutes from an integer to a float" +} + pub struct Transmute; impl LintPass for Transmute { fn get_lints(&self) -> LintArray { - lint_array!(CROSSPOINTER_TRANSMUTE, TRANSMUTE_PTR_TO_REF, USELESS_TRANSMUTE, WRONG_TRANSMUTE) + lint_array!( + CROSSPOINTER_TRANSMUTE, + TRANSMUTE_PTR_TO_REF, + USELESS_TRANSMUTE, + WRONG_TRANSMUTE, + TRANSMUTE_INT_TO_CHAR, + TRANSMUTE_INT_TO_BOOL, + TRANSMUTE_INT_TO_FLOAT + ) } } @@ -177,6 +241,50 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Transmute { db.span_suggestion(e.span, "try", sugg::make_unop(deref, arg).to_string()); }, ), + (&ty::TyInt(ast::IntTy::I32), &ty::TyChar) | + (&ty::TyUint(ast::UintTy::U32), &ty::TyChar) => span_lint_and_then( + cx, + TRANSMUTE_INT_TO_CHAR, + e.span, + &format!("transmute from a `{}` to a `char`", from_ty), + |db| { + let arg = sugg::Sugg::hir(cx, &args[0], ".."); + let arg = if let ty::TyInt(_) = from_ty.sty { + arg.as_ty(ty::TyUint(ast::UintTy::U32)) + } else { + arg + }; + db.span_suggestion(e.span, "consider using", format!("std::char::from_u32({})", arg.to_string())); + } + ), + (&ty::TyInt(ast::IntTy::I8), &ty::TyBool) | + (&ty::TyUint(ast::UintTy::U8), &ty::TyBool) => span_lint_and_then( + cx, + TRANSMUTE_INT_TO_BOOL, + e.span, + &format!("transmute from a `{}` to a `bool`", from_ty), + |db| { + let arg = sugg::Sugg::hir(cx, &args[0], ".."); + let zero = sugg::Sugg::NonParen(Cow::from("0")); + db.span_suggestion(e.span, "consider using", sugg::make_binop(ast::BinOpKind::Ne, &arg, &zero).to_string()); + } + ), + (&ty::TyInt(_), &ty::TyFloat(_)) | + (&ty::TyUint(_), &ty::TyFloat(_)) => span_lint_and_then( + cx, + TRANSMUTE_INT_TO_FLOAT, + e.span, + &format!("transmute from a `{}` to a `{}`", from_ty, to_ty), + |db| { + let arg = sugg::Sugg::hir(cx, &args[0], ".."); + let arg = if let ty::TyInt(int_ty) = from_ty.sty { + arg.as_ty(format!("u{}", int_ty.bit_width().map_or_else(|| "size".to_string(), |v| v.to_string()))) + } else { + arg + }; + db.span_suggestion(e.span, "consider using", format!("{}::from_bits({})", to_ty, arg.to_string())); + } + ), _ => return, }; } diff --git a/tests/ui/transmute.rs b/tests/ui/transmute.rs index 31cd8304eba7..81582b5a15fa 100644 --- a/tests/ui/transmute.rs +++ b/tests/ui/transmute.rs @@ -118,4 +118,21 @@ fn crosspointer() { } } +#[warn(transmute_int_to_char)] +fn int_to_char() { + let _: char = unsafe { std::mem::transmute(0_u32) }; + let _: char = unsafe { std::mem::transmute(0_i32) }; +} + +#[warn(transmute_int_to_bool)] +fn int_to_bool() { + let _: bool = unsafe { std::mem::transmute(0_u8) }; +} + +#[warn(transmute_int_to_float)] +fn int_to_float() { + let _: f32 = unsafe { std::mem::transmute(0_u32) }; + let _: f32 = unsafe { std::mem::transmute(0_i32) }; +} + fn main() { } diff --git a/tests/ui/transmute.stderr b/tests/ui/transmute.stderr index 0c5aff11b0ce..c81ec5260be2 100644 --- a/tests/ui/transmute.stderr +++ b/tests/ui/transmute.stderr @@ -154,3 +154,39 @@ error: transmute from a type (`Usize`) to a pointer to that type (`*mut Usize`) 117 | let _: *mut Usize = core::intrinsics::transmute(my_int()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +error: transmute from a `u32` to a `char` + --> $DIR/transmute.rs:123:28 + | +123 | let _: char = unsafe { std::mem::transmute(0_u32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_u32)` + | + = note: `-D transmute-int-to-char` implied by `-D warnings` + +error: transmute from a `i32` to a `char` + --> $DIR/transmute.rs:124:28 + | +124 | let _: char = unsafe { std::mem::transmute(0_i32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_i32 as u32)` + +error: transmute from a `u8` to a `bool` + --> $DIR/transmute.rs:129:28 + | +129 | let _: bool = unsafe { std::mem::transmute(0_u8) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `0_u8 != 0` + | + = note: `-D transmute-int-to-bool` implied by `-D warnings` + +error: transmute from a `u32` to a `f32` + --> $DIR/transmute.rs:134:27 + | +134 | let _: f32 = unsafe { std::mem::transmute(0_u32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_u32)` + | + = note: `-D transmute-int-to-float` implied by `-D warnings` + +error: transmute from a `i32` to a `f32` + --> $DIR/transmute.rs:135:27 + | +135 | let _: f32 = unsafe { std::mem::transmute(0_i32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_i32 as u32)` +