diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 3920b7e6c688..a4e18c7eec3c 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -603,6 +603,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) { transmute::TRANSMUTE_PTR_TO_REF, transmute::USELESS_TRANSMUTE, transmute::WRONG_TRANSMUTE, + transmute::MISALIGNED_TRANSMUTE, 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 1028381e20a1..090f9397472d 100644 --- a/clippy_lints/src/transmute.rs +++ b/clippy_lints/src/transmute.rs @@ -3,7 +3,8 @@ 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::{last_path_segment, match_def_path, paths, snippet, span_lint, span_lint_and_then, + alignment}; use utils::{opt_def_id, sugg}; /// **What it does:** Checks for transmutes that can't ever be correct on any @@ -168,6 +169,23 @@ declare_lint! { "transmutes from an integer to a float" } +/// **What it does:** Checks for transmutes to a potentially less-aligned type. +/// +/// **Why is this bad?** This might result in undefined behavior. +/// +/// **Known problems:** None. +/// +/// **Example:** +/// ```rust +/// // u32 is 32-bit aligned; u8 is 8-bit aligned +/// let _: u32 = unsafe { std::mem::transmute([0u8; 4]) }; +/// ``` +declare_lint! { + pub MISALIGNED_TRANSMUTE, + Warn, + "transmutes to a potentially less-aligned type" +} + pub struct Transmute; impl LintPass for Transmute { @@ -180,7 +198,8 @@ impl LintPass for Transmute { TRANSMUTE_INT_TO_CHAR, TRANSMUTE_BYTES_TO_STR, TRANSMUTE_INT_TO_BOOL, - TRANSMUTE_INT_TO_FLOAT + TRANSMUTE_INT_TO_FLOAT, + MISALIGNED_TRANSMUTE ) } } @@ -201,6 +220,18 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Transmute { e.span, &format!("transmute from a type (`{}`) to itself", from_ty), ), + _ if alignment(cx, from_ty).map(|a| a.abi()) + < alignment(cx, to_ty).map(|a| a.abi()) + => span_lint( + cx, + MISALIGNED_TRANSMUTE, + e.span, + &format!( + "transmute from `{}` to a less-aligned type (`{}`)", + from_ty, + to_ty, + ) + ), (&ty::TyRef(_, rty), &ty::TyRawPtr(ptr_ty)) => span_lint_and_then( cx, USELESS_TRANSMUTE, diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index 4019321d7116..b7f1e7b24545 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -9,7 +9,7 @@ use rustc::lint::{LateContext, Level, Lint, LintContext}; use rustc::session::Session; use rustc::traits; use rustc::ty::{self, Ty, TyCtxt}; -use rustc::ty::layout::LayoutOf; +use rustc::ty::layout::{LayoutOf, Align}; use rustc_errors; use std::borrow::Cow; use std::env; @@ -1056,3 +1056,8 @@ pub fn get_arg_name(pat: &Pat) -> Option { _ => None, } } + +/// Returns alignment for a type, or None if alignment is undefined +pub fn alignment<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty<'tcx>) -> Option { + (cx.tcx, cx.param_env).layout_of(ty).ok().map(|layout| layout.align) +} diff --git a/tests/ui/transmute.rs b/tests/ui/transmute.rs index b04297f01fb2..48b89c33ab91 100644 --- a/tests/ui/transmute.rs +++ b/tests/ui/transmute.rs @@ -140,4 +140,11 @@ fn bytes_to_str(b: &[u8], mb: &mut [u8]) { let _: &mut str = unsafe { std::mem::transmute(mb) }; } +#[warn(misaligned_transmute)] +fn misaligned_transmute() { + let _: u32 = unsafe { std::mem::transmute([0u8; 4]) }; // err + let _: u32 = unsafe { std::mem::transmute(0f32) }; // ok (alignment-wise) + let _: [u8; 4] = unsafe { std::mem::transmute(0u32) }; // ok (alignment-wise) +} + fn main() { } diff --git a/tests/ui/transmute.stderr b/tests/ui/transmute.stderr index f3ac9a101ae1..74bbf95d5258 100644 --- a/tests/ui/transmute.stderr +++ b/tests/ui/transmute.stderr @@ -204,5 +204,13 @@ error: transmute from a `&mut [u8]` to a `&mut str` 140 | let _: &mut str = unsafe { std::mem::transmute(mb) }; | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_mut(mb).unwrap()` -error: aborting due to 32 previous errors +error: transmute from `[u8; 4]` to a less-aligned type (`u32`) + --> $DIR/transmute.rs:145:27 + | +145 | let _: u32 = unsafe { std::mem::transmute([0u8; 4]) }; // err + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D misaligned-transmute` implied by `-D warnings` + +error: aborting due to 33 previous errors