diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 235a998f7531..a22df037b464 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -646,7 +646,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(borrow_deref_ref::BorrowDerefRef)); store.register_late_pass(|| Box::new(no_effect::NoEffect)); store.register_late_pass(|| Box::new(temporary_assignment::TemporaryAssignment)); - store.register_late_pass(|| Box::new(transmute::Transmute)); + store.register_late_pass(move || Box::new(transmute::Transmute::new(msrv))); let cognitive_complexity_threshold = conf.cognitive_complexity_threshold; store.register_late_pass(move || { Box::new(cognitive_complexity::CognitiveComplexity::new( diff --git a/clippy_lints/src/transmute/mod.rs b/clippy_lints/src/transmute/mod.rs index cbe1406728bc..5f3e98144f42 100644 --- a/clippy_lints/src/transmute/mod.rs +++ b/clippy_lints/src/transmute/mod.rs @@ -16,9 +16,10 @@ mod wrong_transmute; use clippy_utils::in_constant; use if_chain::if_chain; -use rustc_hir::{Expr, ExprKind}; +use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::sym; declare_clippy_lint! { @@ -385,7 +386,10 @@ declare_clippy_lint! { "transmute to or from a type with an undefined representation" } -declare_lint_pass!(Transmute => [ +pub struct Transmute { + msrv: Option, +} +impl_lint_pass!(Transmute => [ CROSSPOINTER_TRANSMUTE, TRANSMUTE_PTR_TO_REF, TRANSMUTE_PTR_TO_PTR, @@ -401,13 +405,18 @@ declare_lint_pass!(Transmute => [ TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS, TRANSMUTE_UNDEFINED_REPR, ]); - +impl Transmute { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } +} impl<'tcx> LateLintPass<'tcx> for Transmute { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { if_chain! { if let ExprKind::Call(path_expr, [arg]) = e.kind; - if let ExprKind::Path(ref qpath) = path_expr.kind; - if let Some(def_id) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id(); + if let ExprKind::Path(QPath::Resolved(None, path)) = path_expr.kind; + if let Some(def_id) = path.res.opt_def_id(); if cx.tcx.is_diagnostic_item(sym::transmute, def_id); then { // Avoid suggesting non-const operations in const contexts: @@ -427,7 +436,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { let linted = wrong_transmute::check(cx, e, from_ty, to_ty) | crosspointer_transmute::check(cx, e, from_ty, to_ty) - | transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, qpath) + | transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, self.msrv) | transmute_int_to_char::check(cx, e, from_ty, to_ty, arg, const_context) | transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context) | transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, arg) @@ -446,4 +455,6 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { } } } + + extract_msrv_attr!(LateContext); } diff --git a/clippy_lints/src/transmute/transmute_ptr_to_ref.rs b/clippy_lints/src/transmute/transmute_ptr_to_ref.rs index f3653199b375..ef1b55a9991a 100644 --- a/clippy_lints/src/transmute/transmute_ptr_to_ref.rs +++ b/clippy_lints/src/transmute/transmute_ptr_to_ref.rs @@ -1,11 +1,12 @@ -use super::utils::get_type_snippet; use super::TRANSMUTE_PTR_TO_REF; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::sugg; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::{meets_msrv, msrvs, sugg}; use rustc_errors::Applicability; -use rustc_hir::{Expr, Mutability, QPath}; +use rustc_hir::{self as hir, Expr, GenericArg, Mutability, Path, TyKind}; use rustc_lint::LateContext; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, Ty, TypeFoldable}; +use rustc_semver::RustcVersion; /// Checks for `transmute_ptr_to_ref` lint. /// Returns `true` if it's triggered, otherwise returns `false`. @@ -15,7 +16,8 @@ pub(super) fn check<'tcx>( from_ty: Ty<'tcx>, to_ty: Ty<'tcx>, arg: &'tcx Expr<'_>, - qpath: &'tcx QPath<'_>, + path: &'tcx Path<'_>, + msrv: Option, ) -> bool { match (&from_ty.kind(), &to_ty.kind()) { (ty::RawPtr(from_ptr_ty), ty::Ref(_, to_ref_ty, mutbl)) => { @@ -34,19 +36,34 @@ pub(super) fn check<'tcx>( } else { ("&*", "*const") }; + let mut app = Applicability::MachineApplicable; - let arg = if from_ptr_ty.ty == *to_ref_ty { - arg + let sugg = if let Some(ty) = get_explicit_type(path) { + let ty_snip = snippet_with_applicability(cx, ty.span, "..", &mut app); + if meets_msrv(msrv, msrvs::PTR_CAST) { + format!("{}{}.cast::<{}>()", deref, arg.maybe_par(), ty_snip) + } else if from_ptr_ty.has_erased_regions() { + sugg::make_unop(deref, arg.as_ty(format!("{} () as {} {}", cast, cast, ty_snip))) + .to_string() + } else { + sugg::make_unop(deref, arg.as_ty(format!("{} {}", cast, ty_snip))).to_string() + } + } else if from_ptr_ty.ty == *to_ref_ty { + if from_ptr_ty.has_erased_regions() { + if meets_msrv(msrv, msrvs::PTR_CAST) { + format!("{}{}.cast::<{}>()", deref, arg.maybe_par(), to_ref_ty) + } else { + sugg::make_unop(deref, arg.as_ty(format!("{} () as {} {}", cast, cast, to_ref_ty))) + .to_string() + } + } else { + sugg::make_unop(deref, arg).to_string() + } } else { - arg.as_ty(&format!("{} {}", cast, get_type_snippet(cx, qpath, *to_ref_ty))) + sugg::make_unop(deref, arg.as_ty(format!("{} {}", cast, to_ref_ty))).to_string() }; - diag.span_suggestion( - e.span, - "try", - sugg::make_unop(deref, arg).to_string(), - Applicability::Unspecified, - ); + diag.span_suggestion(e.span, "try", sugg, app); }, ); true @@ -54,3 +71,14 @@ pub(super) fn check<'tcx>( _ => false, } } + +/// Gets the type `Bar` in `…::transmute`. +fn get_explicit_type<'tcx>(path: &'tcx Path<'tcx>) -> Option<&'tcx hir::Ty<'tcx>> { + if let GenericArg::Type(ty) = path.segments.last()?.args?.args.get(1)? + && let TyKind::Rptr(_, ty) = &ty.kind + { + Some(ty.ty) + } else { + None + } +} diff --git a/clippy_lints/src/transmute/utils.rs b/clippy_lints/src/transmute/utils.rs index 0cbf5ccefa6d..74927570b40e 100644 --- a/clippy_lints/src/transmute/utils.rs +++ b/clippy_lints/src/transmute/utils.rs @@ -1,35 +1,9 @@ -use clippy_utils::last_path_segment; -use clippy_utils::source::snippet; -use if_chain::if_chain; -use rustc_hir::{Expr, GenericArg, QPath, TyKind}; +use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty::{cast::CastKind, Ty}; use rustc_span::DUMMY_SP; use rustc_typeck::check::{cast::CastCheck, FnCtxt, Inherited}; -/// Gets the snippet of `Bar` in `…::transmute`. If that snippet is -/// not available , use -/// the type's `ToString` implementation. In weird cases it could lead to types -/// with invalid `'_` -/// lifetime, but it should be rare. -pub(super) fn get_type_snippet(cx: &LateContext<'_>, path: &QPath<'_>, to_ref_ty: Ty<'_>) -> String { - let seg = last_path_segment(path); - if_chain! { - if let Some(params) = seg.args; - if !params.parenthesized; - if let Some(to_ty) = params.args.iter().filter_map(|arg| match arg { - GenericArg::Type(ty) => Some(ty), - _ => None, - }).nth(1); - if let TyKind::Rptr(_, ref to_ty) = to_ty.kind; - then { - return snippet(cx, to_ty.ty.span, &to_ref_ty.to_string()).to_string(); - } - } - - to_ref_ty.to_string() -} - // check if the component types of the transmuted collection and the result have different ABI, // size or alignment pub(super) fn is_layout_incompatible<'tcx>(cx: &LateContext<'tcx>, from: Ty<'tcx>, to: Ty<'tcx>) -> bool { diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index b9ec2c19fdd3..8c8fc15747c1 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -25,7 +25,7 @@ msrv_aliases! { 1,40,0 { MEM_TAKE, NON_EXHAUSTIVE, OPTION_AS_DEREF } 1,38,0 { POINTER_CAST } 1,37,0 { TYPE_ALIAS_ENUM_VARIANTS } - 1,36,0 { ITERATOR_COPIED } + 1,36,0 { ITERATOR_COPIED, PTR_CAST } 1,35,0 { OPTION_COPIED, RANGE_CONTAINS } 1,34,0 { TRY_FROM } 1,30,0 { ITERATOR_FIND_MAP, TOOL_ATTRIBUTES } diff --git a/tests/ui/transmute_ptr_to_ref.fixed b/tests/ui/transmute_ptr_to_ref.fixed new file mode 100644 index 000000000000..b981094013ab --- /dev/null +++ b/tests/ui/transmute_ptr_to_ref.fixed @@ -0,0 +1,78 @@ +// run-rustfix + +#![feature(custom_inner_attributes)] +#![warn(clippy::transmute_ptr_to_ref)] +#![allow(clippy::match_single_binding)] + +unsafe fn _ptr_to_ref(p: *const T, m: *mut T, o: *const U, om: *mut U) { + let _: &T = &*p; + let _: &T = &*p; + + let _: &mut T = &mut *m; + let _: &mut T = &mut *m; + + let _: &T = &*m; + let _: &T = &*m; + + let _: &mut T = &mut *(p as *mut T); + let _ = &mut *(p as *mut T); + + let _: &T = &*(o as *const T); + let _: &T = &*(o as *const T); + + let _: &mut T = &mut *(om as *mut T); + let _: &mut T = &mut *(om as *mut T); + + let _: &T = &*(om as *const T); + let _: &T = &*(om as *const T); +} + +fn _issue1231() { + struct Foo<'a, T> { + bar: &'a T, + } + + let raw = 42 as *const i32; + let _: &Foo = unsafe { &*raw.cast::>() }; + + let _: &Foo<&u8> = unsafe { &*raw.cast::>() }; + + type Bar<'a> = &'a u8; + let raw = 42 as *const i32; + unsafe { &*(raw as *const u8) }; +} + +unsafe fn _issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &'b u32 { + match 0 { + 0 => &*x.cast::<&u32>(), + 1 => &*y.cast::<&u32>(), + 2 => &*x.cast::<&'b u32>(), + _ => &*y.cast::<&'b u32>(), + } +} + +unsafe fn _meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { + #![clippy::msrv = "1.36"] + let a = 0u32; + let a = &a as *const u32; + let _: &u32 = &*a; + let _: &u32 = &*a.cast::(); + match 0 { + 0 => &*x.cast::<&u32>(), + _ => &*x.cast::<&'b u32>(), + } +} + +unsafe fn _under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { + #![clippy::msrv = "1.35"] + let a = 0u32; + let a = &a as *const u32; + let _: &u32 = &*a; + let _: &u32 = &*(a as *const u32); + match 0 { + 0 => &*(x as *const () as *const &u32), + _ => &*(x as *const () as *const &'b u32), + } +} + +fn main() {} diff --git a/tests/ui/transmute_ptr_to_ref.rs b/tests/ui/transmute_ptr_to_ref.rs index ba35c6adc4de..1a3c88b09389 100644 --- a/tests/ui/transmute_ptr_to_ref.rs +++ b/tests/ui/transmute_ptr_to_ref.rs @@ -1,4 +1,8 @@ +// run-rustfix + +#![feature(custom_inner_attributes)] #![warn(clippy::transmute_ptr_to_ref)] +#![allow(clippy::match_single_binding)] unsafe fn _ptr_to_ref(p: *const T, m: *mut T, o: *const U, om: *mut U) { let _: &T = std::mem::transmute(p); @@ -23,7 +27,7 @@ unsafe fn _ptr_to_ref(p: *const T, m: *mut T, o: *const U, om: *mut U) { let _: &T = &*(om as *const T); } -fn issue1231() { +fn _issue1231() { struct Foo<'a, T> { bar: &'a T, } @@ -38,4 +42,37 @@ fn issue1231() { unsafe { std::mem::transmute::<_, Bar>(raw) }; } +unsafe fn _issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &'b u32 { + match 0 { + 0 => std::mem::transmute(x), + 1 => std::mem::transmute(y), + 2 => std::mem::transmute::<_, &&'b u32>(x), + _ => std::mem::transmute::<_, &&'b u32>(y), + } +} + +unsafe fn _meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { + #![clippy::msrv = "1.36"] + let a = 0u32; + let a = &a as *const u32; + let _: &u32 = std::mem::transmute(a); + let _: &u32 = std::mem::transmute::<_, &u32>(a); + match 0 { + 0 => std::mem::transmute(x), + _ => std::mem::transmute::<_, &&'b u32>(x), + } +} + +unsafe fn _under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 { + #![clippy::msrv = "1.35"] + let a = 0u32; + let a = &a as *const u32; + let _: &u32 = std::mem::transmute(a); + let _: &u32 = std::mem::transmute::<_, &u32>(a); + match 0 { + 0 => std::mem::transmute(x), + _ => std::mem::transmute::<_, &&'b u32>(x), + } +} + fn main() {} diff --git a/tests/ui/transmute_ptr_to_ref.stderr b/tests/ui/transmute_ptr_to_ref.stderr index df0598a58cd3..2993e5e7b0c9 100644 --- a/tests/ui/transmute_ptr_to_ref.stderr +++ b/tests/ui/transmute_ptr_to_ref.stderr @@ -1,5 +1,5 @@ error: transmute from a pointer type (`*const T`) to a reference type (`&T`) - --> $DIR/transmute_ptr_to_ref.rs:4:17 + --> $DIR/transmute_ptr_to_ref.rs:8:17 | LL | let _: &T = std::mem::transmute(p); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*p` @@ -7,58 +7,130 @@ LL | let _: &T = std::mem::transmute(p); = note: `-D clippy::transmute-ptr-to-ref` implied by `-D warnings` error: transmute from a pointer type (`*mut T`) to a reference type (`&mut T`) - --> $DIR/transmute_ptr_to_ref.rs:7:21 + --> $DIR/transmute_ptr_to_ref.rs:11:21 | LL | let _: &mut T = std::mem::transmute(m); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *m` error: transmute from a pointer type (`*mut T`) to a reference type (`&T`) - --> $DIR/transmute_ptr_to_ref.rs:10:17 + --> $DIR/transmute_ptr_to_ref.rs:14:17 | LL | let _: &T = std::mem::transmute(m); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*m` error: transmute from a pointer type (`*mut T`) to a reference type (`&mut T`) - --> $DIR/transmute_ptr_to_ref.rs:13:21 + --> $DIR/transmute_ptr_to_ref.rs:17:21 | LL | let _: &mut T = std::mem::transmute(p as *mut T); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(p as *mut T)` error: transmute from a pointer type (`*const U`) to a reference type (`&T`) - --> $DIR/transmute_ptr_to_ref.rs:16:17 + --> $DIR/transmute_ptr_to_ref.rs:20:17 | LL | let _: &T = std::mem::transmute(o); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(o as *const T)` error: transmute from a pointer type (`*mut U`) to a reference type (`&mut T`) - --> $DIR/transmute_ptr_to_ref.rs:19:21 + --> $DIR/transmute_ptr_to_ref.rs:23:21 | LL | let _: &mut T = std::mem::transmute(om); | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(om as *mut T)` error: transmute from a pointer type (`*mut U`) to a reference type (`&T`) - --> $DIR/transmute_ptr_to_ref.rs:22:17 + --> $DIR/transmute_ptr_to_ref.rs:26:17 | LL | let _: &T = std::mem::transmute(om); | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(om as *const T)` -error: transmute from a pointer type (`*const i32`) to a reference type (`&issue1231::Foo`) - --> $DIR/transmute_ptr_to_ref.rs:32:32 +error: transmute from a pointer type (`*const i32`) to a reference type (`&_issue1231::Foo`) + --> $DIR/transmute_ptr_to_ref.rs:36:32 | LL | let _: &Foo = unsafe { std::mem::transmute::<_, &Foo<_>>(raw) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(raw as *const Foo<_>)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*raw.cast::>()` -error: transmute from a pointer type (`*const i32`) to a reference type (`&issue1231::Foo<&u8>`) - --> $DIR/transmute_ptr_to_ref.rs:34:33 +error: transmute from a pointer type (`*const i32`) to a reference type (`&_issue1231::Foo<&u8>`) + --> $DIR/transmute_ptr_to_ref.rs:38:33 | LL | let _: &Foo<&u8> = unsafe { std::mem::transmute::<_, &Foo<&_>>(raw) }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(raw as *const Foo<&_>)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*raw.cast::>()` error: transmute from a pointer type (`*const i32`) to a reference type (`&u8`) - --> $DIR/transmute_ptr_to_ref.rs:38:14 + --> $DIR/transmute_ptr_to_ref.rs:42:14 | LL | unsafe { std::mem::transmute::<_, Bar>(raw) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(raw as *const u8)` -error: aborting due to 10 previous errors +error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) + --> $DIR/transmute_ptr_to_ref.rs:47:14 + | +LL | 0 => std::mem::transmute(x), + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&u32>()` + +error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) + --> $DIR/transmute_ptr_to_ref.rs:48:14 + | +LL | 1 => std::mem::transmute(y), + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*y.cast::<&u32>()` + +error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) + --> $DIR/transmute_ptr_to_ref.rs:49:14 + | +LL | 2 => std::mem::transmute::<_, &&'b u32>(x), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&'b u32>()` + +error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) + --> $DIR/transmute_ptr_to_ref.rs:50:14 + | +LL | _ => std::mem::transmute::<_, &&'b u32>(y), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*y.cast::<&'b u32>()` + +error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`) + --> $DIR/transmute_ptr_to_ref.rs:58:19 + | +LL | let _: &u32 = std::mem::transmute(a); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*a` + +error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`) + --> $DIR/transmute_ptr_to_ref.rs:59:19 + | +LL | let _: &u32 = std::mem::transmute::<_, &u32>(a); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*a.cast::()` + +error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) + --> $DIR/transmute_ptr_to_ref.rs:61:14 + | +LL | 0 => std::mem::transmute(x), + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&u32>()` + +error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) + --> $DIR/transmute_ptr_to_ref.rs:62:14 + | +LL | _ => std::mem::transmute::<_, &&'b u32>(x), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&'b u32>()` + +error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`) + --> $DIR/transmute_ptr_to_ref.rs:70:19 + | +LL | let _: &u32 = std::mem::transmute(a); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*a` + +error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`) + --> $DIR/transmute_ptr_to_ref.rs:71:19 + | +LL | let _: &u32 = std::mem::transmute::<_, &u32>(a); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(a as *const u32)` + +error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) + --> $DIR/transmute_ptr_to_ref.rs:73:14 + | +LL | 0 => std::mem::transmute(x), + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(x as *const () as *const &u32)` + +error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`) + --> $DIR/transmute_ptr_to_ref.rs:74:14 + | +LL | _ => std::mem::transmute::<_, &&'b u32>(x), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(x as *const () as *const &'b u32)` + +error: aborting due to 22 previous errors