From 7fa27d938774891cfca3c488db0edc0eb926c12f Mon Sep 17 00:00:00 2001 From: sinkuu Date: Sun, 29 Oct 2017 10:27:45 +0900 Subject: [PATCH] Lint `transmute::<&[u8], &str>` --- clippy_lints/src/transmute.rs | 55 +++++++++++++++++++++++++++++++++-- tests/ui/transmute.rs | 5 ++++ tests/ui/transmute.stderr | 18 ++++++++++-- 3 files changed, 74 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/transmute.rs b/clippy_lints/src/transmute.rs index fde6abe48af0..cc8c6d284451 100644 --- a/clippy_lints/src/transmute.rs +++ b/clippy_lints/src/transmute.rs @@ -88,7 +88,7 @@ declare_lint! { /// ```rust /// let _: char = std::mem::transmute(x); // where x: u32 /// // should be: -/// let _: Option = std::char::from_u32(x); +/// let _ = std::char::from_u32(x).unwrap(); /// ``` declare_lint! { pub TRANSMUTE_INT_TO_CHAR, @@ -96,6 +96,24 @@ declare_lint! { "transmutes from an integer to a `char`" } +/// **What it does:** Checks for transmutes from a `&[u8] to a `&str`. +/// +/// **Why is this bad?** Not every byte slice is a valid UTF-8 string. +/// +/// **Known problems:** None. +/// +/// **Example:** +/// ```rust +/// let _: &str = std::mem::transmute(b); // where b: &[u8] +/// // should be: +/// let _ = std::str::from_utf8(b).unwrap(); +/// ``` +declare_lint! { + pub TRANSMUTE_BYTES_TO_STR, + Warn, + "transmutes from a `&[u8]` to a `&str`" +} + /// **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`. @@ -142,6 +160,7 @@ impl LintPass for Transmute { USELESS_TRANSMUTE, WRONG_TRANSMUTE, TRANSMUTE_INT_TO_CHAR, + TRANSMUTE_BYTES_TO_STR, TRANSMUTE_INT_TO_BOOL, TRANSMUTE_INT_TO_FLOAT ) @@ -254,9 +273,41 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Transmute { } else { arg }; - db.span_suggestion(e.span, "consider using", format!("std::char::from_u32({})", arg.to_string())); + db.span_suggestion(e.span, "consider using", format!("std::char::from_u32({}).unwrap()", arg.to_string())); } ), + (&ty::TyRef(_, ref ref_from), &ty::TyRef(_, ref ref_to)) => { + if_chain! { + if let (&ty::TySlice(slice_ty), &ty::TyStr) = (&ref_from.ty.sty, &ref_to.ty.sty); + if let ty::TyUint(ast::UintTy::U8) = slice_ty.sty; + if ref_from.mutbl == ref_to.mutbl; + then { + let postfix = if ref_from.mutbl == Mutability::MutMutable { + "_mut" + } else { + "" + }; + + span_lint_and_then( + cx, + TRANSMUTE_BYTES_TO_STR, + e.span, + &format!("transmute from a `{}` to a `{}`", from_ty, to_ty), + |db| { + db.span_suggestion( + e.span, + "consider using", + format!( + "std::str::from_utf8{}({}).unwrap()", + postfix, + snippet(cx, args[0].span, ".."), + ), + ); + } + ) + } + } + }, (&ty::TyInt(ast::IntTy::I8), &ty::TyBool) | (&ty::TyUint(ast::UintTy::U8), &ty::TyBool) => span_lint_and_then( cx, diff --git a/tests/ui/transmute.rs b/tests/ui/transmute.rs index 81582b5a15fa..b04297f01fb2 100644 --- a/tests/ui/transmute.rs +++ b/tests/ui/transmute.rs @@ -135,4 +135,9 @@ fn int_to_float() { let _: f32 = unsafe { std::mem::transmute(0_i32) }; } +fn bytes_to_str(b: &[u8], mb: &mut [u8]) { + let _: &str = unsafe { std::mem::transmute(b) }; + let _: &mut str = unsafe { std::mem::transmute(mb) }; +} + fn main() { } diff --git a/tests/ui/transmute.stderr b/tests/ui/transmute.stderr index c81ec5260be2..6504f55845dd 100644 --- a/tests/ui/transmute.stderr +++ b/tests/ui/transmute.stderr @@ -158,7 +158,7 @@ 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)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_u32).unwrap()` | = note: `-D transmute-int-to-char` implied by `-D warnings` @@ -166,7 +166,7 @@ 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)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_i32 as u32).unwrap()` error: transmute from a `u8` to a `bool` --> $DIR/transmute.rs:129:28 @@ -190,3 +190,17 @@ error: transmute from a `i32` to a `f32` 135 | let _: f32 = unsafe { std::mem::transmute(0_i32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_i32 as u32)` +error: transmute from a `&[u8]` to a `&str` + --> $DIR/transmute.rs:139:28 + | +139 | let _: &str = unsafe { std::mem::transmute(b) }; + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8(b).unwrap()` + | + = note: `-D transmute-bytes-to-str` implied by `-D warnings` + +error: transmute from a `&mut [u8]` to a `&mut str` + --> $DIR/transmute.rs:140:32 + | +140 | let _: &mut str = unsafe { std::mem::transmute(mb) }; + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_mut(mb).unwrap()` +