diff --git a/clippy_lints/src/transmute/mod.rs b/clippy_lints/src/transmute/mod.rs index 691d759d7..c0d290b5a 100644 --- a/clippy_lints/src/transmute/mod.rs +++ b/clippy_lints/src/transmute/mod.rs @@ -479,7 +479,10 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { // - char conversions (https://github.com/rust-lang/rust/issues/89259) let const_context = in_constant(cx, e.hir_id); - let from_ty = cx.typeck_results().expr_ty_adjusted(arg); + let (from_ty, from_ty_adjusted) = match cx.typeck_results().expr_adjustments(arg) { + [] => (cx.typeck_results().expr_ty(arg), false), + [.., a] => (a.target, true), + }; // Adjustments for `to_ty` happen after the call to `transmute`, so don't use them. let to_ty = cx.typeck_results().expr_ty(e); @@ -506,7 +509,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { ); if !linted { - transmutes_expressible_as_ptr_casts::check(cx, e, from_ty, to_ty, arg); + transmutes_expressible_as_ptr_casts::check(cx, e, from_ty, from_ty_adjusted, to_ty, arg); } } } diff --git a/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs b/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs index b79d4e915..8530b4324 100644 --- a/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs +++ b/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs @@ -1,11 +1,11 @@ -use super::utils::can_be_expressed_as_pointer_cast; +use super::utils::check_cast; use super::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS; -use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::sugg; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::sugg::Sugg; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; -use rustc_middle::ty::Ty; +use rustc_middle::ty::{cast::CastKind, Ty}; /// Checks for `transmutes_expressible_as_ptr_casts` lint. /// Returns `true` if it's triggered, otherwise returns `false`. @@ -13,24 +13,40 @@ pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty<'tcx>, + from_ty_adjusted: bool, to_ty: Ty<'tcx>, arg: &'tcx Expr<'_>, ) -> bool { - if can_be_expressed_as_pointer_cast(cx, e, from_ty, to_ty) { - span_lint_and_then( - cx, - TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS, - e.span, - &format!("transmute from `{from_ty}` to `{to_ty}` which could be expressed as a pointer cast instead"), - |diag| { - if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) { - let sugg = arg.as_ty(to_ty.to_string()).to_string(); - diag.span_suggestion(e.span, "try", sugg, Applicability::MachineApplicable); - } - }, - ); - true - } else { - false - } + use CastKind::{AddrPtrCast, ArrayPtrCast, FnPtrAddrCast, FnPtrPtrCast, PtrAddrCast, PtrPtrCast}; + let mut app = Applicability::MachineApplicable; + let sugg = match check_cast(cx, e, from_ty, to_ty) { + Some(PtrPtrCast | AddrPtrCast | ArrayPtrCast | FnPtrPtrCast | FnPtrAddrCast) => { + Sugg::hir_with_context(cx, arg, e.span.ctxt(), "..", &mut app) + .as_ty(to_ty.to_string()) + .to_string() + }, + Some(PtrAddrCast) if !from_ty_adjusted => Sugg::hir_with_context(cx, arg, e.span.ctxt(), "..", &mut app) + .as_ty(to_ty.to_string()) + .to_string(), + + // The only adjustments here would be ref-to-ptr and unsize coercions. The result of an unsize coercions can't + // be transmuted to a usize. For ref-to-ptr coercions, borrows need to be cast to a pointer before being cast to + // a usize. + Some(PtrAddrCast) => format!( + "{} as {to_ty}", + Sugg::hir_with_context(cx, arg, e.span.ctxt(), "..", &mut app).as_ty(from_ty) + ), + _ => return false, + }; + + span_lint_and_sugg( + cx, + TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS, + e.span, + &format!("transmute from `{from_ty}` to `{to_ty}` which could be expressed as a pointer cast instead"), + "try", + sugg, + app, + ); + true } diff --git a/clippy_lints/src/transmute/utils.rs b/clippy_lints/src/transmute/utils.rs index 49d863ec0..c93f047f5 100644 --- a/clippy_lints/src/transmute/utils.rs +++ b/clippy_lints/src/transmute/utils.rs @@ -20,28 +20,16 @@ pub(super) fn is_layout_incompatible<'tcx>(cx: &LateContext<'tcx>, from: Ty<'tcx } } -/// Check if the type conversion can be expressed as a pointer cast, instead of -/// a transmute. In certain cases, including some invalid casts from array -/// references to pointers, this may cause additional errors to be emitted and/or -/// ICE error messages. This function will panic if that occurs. -pub(super) fn can_be_expressed_as_pointer_cast<'tcx>( - cx: &LateContext<'tcx>, - e: &'tcx Expr<'_>, - from_ty: Ty<'tcx>, - to_ty: Ty<'tcx>, -) -> bool { - use CastKind::{AddrPtrCast, ArrayPtrCast, FnPtrAddrCast, FnPtrPtrCast, PtrAddrCast, PtrPtrCast}; - matches!( - check_cast(cx, e, from_ty, to_ty), - Some(PtrPtrCast | PtrAddrCast | AddrPtrCast | ArrayPtrCast | FnPtrPtrCast | FnPtrAddrCast) - ) -} - /// If a cast from `from_ty` to `to_ty` is valid, returns an Ok containing the kind of /// the cast. In certain cases, including some invalid casts from array references /// to pointers, this may cause additional errors to be emitted and/or ICE error /// messages. This function will panic if that occurs. -fn check_cast<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty<'tcx>, to_ty: Ty<'tcx>) -> Option { +pub(super) fn check_cast<'tcx>( + cx: &LateContext<'tcx>, + e: &'tcx Expr<'_>, + from_ty: Ty<'tcx>, + to_ty: Ty<'tcx>, +) -> Option { let hir_id = e.hir_id; let local_def_id = hir_id.owner.def_id; diff --git a/tests/ui/transmutes_expressible_as_ptr_casts.fixed b/tests/ui/transmutes_expressible_as_ptr_casts.fixed index 7263abac1..55307506e 100644 --- a/tests/ui/transmutes_expressible_as_ptr_casts.fixed +++ b/tests/ui/transmutes_expressible_as_ptr_casts.fixed @@ -51,6 +51,8 @@ fn main() { // e is a function pointer type and U is an integer; fptr-addr-cast let _usize_from_fn_ptr_transmute = unsafe { foo as usize }; let _usize_from_fn_ptr = foo as *const usize; + + let _usize_from_ref = unsafe { &1u32 as *const u32 as usize }; } // If a ref-to-ptr cast of this form where the pointer type points to a type other diff --git a/tests/ui/transmutes_expressible_as_ptr_casts.rs b/tests/ui/transmutes_expressible_as_ptr_casts.rs index d8e4421d4..e7360f3f9 100644 --- a/tests/ui/transmutes_expressible_as_ptr_casts.rs +++ b/tests/ui/transmutes_expressible_as_ptr_casts.rs @@ -51,6 +51,8 @@ fn main() { // e is a function pointer type and U is an integer; fptr-addr-cast let _usize_from_fn_ptr_transmute = unsafe { transmute:: u8, usize>(foo) }; let _usize_from_fn_ptr = foo as *const usize; + + let _usize_from_ref = unsafe { transmute::<*const u32, usize>(&1u32) }; } // If a ref-to-ptr cast of this form where the pointer type points to a type other diff --git a/tests/ui/transmutes_expressible_as_ptr_casts.stderr b/tests/ui/transmutes_expressible_as_ptr_casts.stderr index de9418c8d..e862fcb67 100644 --- a/tests/ui/transmutes_expressible_as_ptr_casts.stderr +++ b/tests/ui/transmutes_expressible_as_ptr_casts.stderr @@ -46,11 +46,17 @@ error: transmute from `fn(usize) -> u8` to `usize` which could be expressed as a LL | let _usize_from_fn_ptr_transmute = unsafe { transmute:: u8, usize>(foo) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `foo as usize` +error: transmute from `*const u32` to `usize` which could be expressed as a pointer cast instead + --> $DIR/transmutes_expressible_as_ptr_casts.rs:55:36 + | +LL | let _usize_from_ref = unsafe { transmute::<*const u32, usize>(&1u32) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&1u32 as *const u32 as usize` + error: transmute from a reference to a pointer - --> $DIR/transmutes_expressible_as_ptr_casts.rs:64:14 + --> $DIR/transmutes_expressible_as_ptr_casts.rs:66:14 | LL | unsafe { transmute::<&[i32; 1], *const u8>(in_param) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `in_param as *const [i32; 1] as *const u8` -error: aborting due to 8 previous errors +error: aborting due to 9 previous errors