diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index 19e71d6e40d5..8d555240e707 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -2121,6 +2121,72 @@ impl LintPass for InvalidNoMangleItems { } } +#[derive(Clone, Copy)] +pub struct MutableTransmutes; + +declare_lint! { + MUTABLE_TRANSMUTES, + Deny, + "mutating transmuted &mut T from &T may cause undefined behavior" +} + +impl LintPass for MutableTransmutes { + fn get_lints(&self) -> LintArray { + lint_array!(MUTABLE_TRANSMUTES) + } + + fn check_expr(&mut self, cx: &Context, expr: &ast::Expr) { + use syntax::ast::DefId; + use syntax::abi::RustIntrinsic; + let msg = "mutating transmuted &mut T from &T may cause undefined behavior,\ + consider instead using an UnsafeCell"; + match get_transmute_from_to(cx, expr) { + Some((&ty::ty_rptr(_, from_mt), &ty::ty_rptr(_, to_mt))) => { + if to_mt.mutbl == ast::Mutability::MutMutable + && from_mt.mutbl == ast::Mutability::MutImmutable { + cx.span_lint(MUTABLE_TRANSMUTES, expr.span, msg); + } + } + _ => () + } + + fn get_transmute_from_to<'a, 'tcx>(cx: &Context<'a, 'tcx>, expr: &ast::Expr) + -> Option<(&'tcx ty::sty<'tcx>, &'tcx ty::sty<'tcx>)> { + match expr.node { + ast::ExprPath(..) => (), + _ => return None + } + if let DefFn(did, _) = ty::resolve_expr(cx.tcx, expr) { + if !def_id_is_transmute(cx, did) { + return None; + } + let typ = ty::node_id_to_type(cx.tcx, expr.id); + match typ.sty { + ty::ty_bare_fn(_, ref bare_fn) if bare_fn.abi == RustIntrinsic => { + if let ty::FnConverging(to) = bare_fn.sig.0.output { + let from = bare_fn.sig.0.inputs[0]; + return Some((&from.sty, &to.sty)); + } + }, + _ => () + } + } + None + } + + fn def_id_is_transmute(cx: &Context, def_id: DefId) -> bool { + match ty::lookup_item_type(cx.tcx, def_id).ty.sty { + ty::ty_bare_fn(_, ref bfty) if bfty.abi == RustIntrinsic => (), + _ => return false + } + ty::with_path(cx.tcx, def_id, |path| match path.last() { + Some(ref last) => last.name().as_str() == "transmute", + _ => false + }) + } + } +} + /// Forbids using the `#[feature(...)]` attribute #[derive(Copy, Clone)] pub struct UnstableFeatures; diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index 970f9c634a2c..df834c36e5b2 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -109,6 +109,7 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) { InvalidNoMangleItems, PluginAsLibrary, DropWithReprExtern, + MutableTransmutes, ); add_builtin_with_new!(sess, diff --git a/src/test/compile-fail/transmute-imut-to-mut.rs b/src/test/compile-fail/transmute-imut-to-mut.rs new file mode 100644 index 000000000000..2e076337f53e --- /dev/null +++ b/src/test/compile-fail/transmute-imut-to-mut.rs @@ -0,0 +1,20 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Tests that transmuting from &T to &mut T is Undefined Behavior. + +use std::mem::transmute; + +fn main() { + let _a: &mut u8 = unsafe { transmute(&1u8) }; + //~^ ERROR mutating transmuted &mut T from &T may cause undefined behavior +} + +