Fix wrongly unmangled macros for transmute_ptr_to_ptr and transmute_bytes_to_str (#16105)

Closes rust-lang/rust-clippy#16104

changelog: [`transmute_ptr_to_ptr`] fix wrongly unmangled macros
[`transmute_bytes_to_str`] fix wrongly unmangled macros
This commit is contained in:
Samuel Tardieu 2025-11-22 07:50:52 +00:00 committed by GitHub
commit 45168a79cd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 111 additions and 58 deletions

View file

@ -17,6 +17,8 @@ pub(super) fn check<'tcx>(
arg: &'tcx Expr<'_>,
msrv: Msrv,
) -> bool {
let mut applicability = Applicability::MachineApplicable;
let arg_sugg = sugg::Sugg::hir_with_context(cx, arg, e.span.ctxt(), "..", &mut applicability);
match (from_ty.kind(), to_ty.kind()) {
(ty::RawPtr(from_pointee_ty, from_mutbl), ty::RawPtr(to_pointee_ty, to_mutbl)) => {
span_lint_and_then(
@ -25,40 +27,38 @@ pub(super) fn check<'tcx>(
e.span,
"transmute from a pointer to a pointer",
|diag| {
if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) {
if from_mutbl == to_mutbl
&& to_pointee_ty.is_sized(cx.tcx, cx.typing_env())
&& msrv.meets(cx, msrvs::POINTER_CAST)
{
diag.span_suggestion_verbose(
e.span,
"use `pointer::cast` instead",
format!("{}.cast::<{to_pointee_ty}>()", arg.maybe_paren()),
Applicability::MaybeIncorrect,
);
} else if from_pointee_ty == to_pointee_ty
&& let Some(method) = match (from_mutbl, to_mutbl) {
(ty::Mutability::Not, ty::Mutability::Mut) => Some("cast_mut"),
(ty::Mutability::Mut, ty::Mutability::Not) => Some("cast_const"),
_ => None,
}
&& !from_pointee_ty.has_erased_regions()
&& msrv.meets(cx, msrvs::POINTER_CAST_CONSTNESS)
{
diag.span_suggestion_verbose(
e.span,
format!("use `pointer::{method}` instead"),
format!("{}.{method}()", arg.maybe_paren()),
Applicability::MaybeIncorrect,
);
} else {
diag.span_suggestion_verbose(
e.span,
"use an `as` cast instead",
arg.as_ty(to_ty),
Applicability::MaybeIncorrect,
);
if from_mutbl == to_mutbl
&& to_pointee_ty.is_sized(cx.tcx, cx.typing_env())
&& msrv.meets(cx, msrvs::POINTER_CAST)
{
diag.span_suggestion_verbose(
e.span,
"use `pointer::cast` instead",
format!("{}.cast::<{to_pointee_ty}>()", arg_sugg.maybe_paren()),
Applicability::MaybeIncorrect,
);
} else if from_pointee_ty == to_pointee_ty
&& let Some(method) = match (from_mutbl, to_mutbl) {
(ty::Mutability::Not, ty::Mutability::Mut) => Some("cast_mut"),
(ty::Mutability::Mut, ty::Mutability::Not) => Some("cast_const"),
_ => None,
}
&& !from_pointee_ty.has_erased_regions()
&& msrv.meets(cx, msrvs::POINTER_CAST_CONSTNESS)
{
diag.span_suggestion_verbose(
e.span,
format!("use `pointer::{method}` instead"),
format!("{}.{method}()", arg_sugg.maybe_paren()),
Applicability::MaybeIncorrect,
);
} else {
diag.span_suggestion_verbose(
e.span,
"use an `as` cast instead",
arg_sugg.as_ty(to_ty),
Applicability::MaybeIncorrect,
);
}
},
);

View file

@ -1,6 +1,5 @@
use super::{TRANSMUTE_BYTES_TO_STR, TRANSMUTE_PTR_TO_PTR};
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::source::snippet;
use clippy_utils::{std_or_core, sugg};
use rustc_errors::Applicability;
use rustc_hir::{Expr, Mutability};
@ -17,8 +16,7 @@ pub(super) fn check<'tcx>(
arg: &'tcx Expr<'_>,
const_context: bool,
) -> bool {
let mut triggered = false;
let arg_sugg = || sugg::Sugg::hir_with_context(cx, arg, e.span.ctxt(), "..", &mut Applicability::Unspecified);
if let (ty::Ref(_, ty_from, from_mutbl), ty::Ref(_, ty_to, to_mutbl)) = (*from_ty.kind(), *to_ty.kind()) {
if let ty::Slice(slice_ty) = *ty_from.kind()
&& ty_to.is_str()
@ -29,8 +27,6 @@ pub(super) fn check<'tcx>(
let postfix = if from_mutbl == Mutability::Mut { "_mut" } else { "" };
let snippet = snippet(cx, arg.span, "..");
span_lint_and_sugg(
cx,
TRANSMUTE_BYTES_TO_STR,
@ -38,15 +34,17 @@ pub(super) fn check<'tcx>(
format!("transmute from a `{from_ty}` to a `{to_ty}`"),
"consider using",
if const_context {
format!("{top_crate}::str::from_utf8_unchecked{postfix}({snippet})")
format!("{top_crate}::str::from_utf8_unchecked{postfix}({})", arg_sugg())
} else {
format!("{top_crate}::str::from_utf8{postfix}({snippet}).unwrap()")
format!("{top_crate}::str::from_utf8{postfix}({}).unwrap()", arg_sugg())
},
Applicability::MaybeIncorrect,
);
triggered = true;
} else if (cx.tcx.erase_and_anonymize_regions(from_ty) != cx.tcx.erase_and_anonymize_regions(to_ty))
&& !const_context
return true;
}
if (cx.tcx.erase_and_anonymize_regions(from_ty) != cx.tcx.erase_and_anonymize_regions(to_ty)) && !const_context
{
span_lint_and_then(
cx,
@ -54,23 +52,21 @@ pub(super) fn check<'tcx>(
e.span,
"transmute from a reference to a reference",
|diag| {
if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) {
let sugg_paren = arg
.as_ty(Ty::new_ptr(cx.tcx, ty_from, from_mutbl))
.as_ty(Ty::new_ptr(cx.tcx, ty_to, to_mutbl));
let sugg = if to_mutbl == Mutability::Mut {
sugg_paren.mut_addr_deref()
} else {
sugg_paren.addr_deref()
};
diag.span_suggestion(e.span, "try", sugg, Applicability::Unspecified);
}
let sugg_paren = arg_sugg()
.as_ty(Ty::new_ptr(cx.tcx, ty_from, from_mutbl))
.as_ty(Ty::new_ptr(cx.tcx, ty_to, to_mutbl));
let sugg = if to_mutbl == Mutability::Mut {
sugg_paren.mut_addr_deref()
} else {
sugg_paren.addr_deref()
};
diag.span_suggestion(e.span, "try", sugg, Applicability::Unspecified);
},
);
triggered = true;
return true;
}
}
triggered
false
}

View file

@ -128,4 +128,17 @@ fn bytes_to_str(mb: &mut [u8]) {
//~^ transmute_bytes_to_str
}
fn issue16104() {
let b = vec![1_u8, 2_u8];
macro_rules! take_ref {
($x:expr) => {
$x.as_slice()
};
}
unsafe {
let _: &str = std::mem::transmute(take_ref!(b));
//~^ transmute_bytes_to_str
}
}
fn main() {}

View file

@ -106,5 +106,11 @@ error: transmute from a `&[u8]` to a `&str`
LL | const _: &str = unsafe { std::mem::transmute(B) };
| ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_unchecked(B)`
error: aborting due to 16 previous errors
error: transmute from a `&[u8]` to a `&str`
--> tests/ui/transmute.rs:139:23
|
LL | let _: &str = std::mem::transmute(take_ref!(b));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8(take_ref!(b)).unwrap()`
error: aborting due to 17 previous errors

View file

@ -18,3 +18,23 @@ fn main() {
//~^ transmute_ptr_to_ptr
}
}
fn issue16104(make_ptr: fn() -> *const u32) {
macro_rules! call {
($x:expr) => {
$x()
};
}
macro_rules! take_ref {
($x:expr) => {
&$x
};
}
unsafe {
let _: *const f32 = std::mem::transmute(call!(make_ptr));
//~^ transmute_ptr_to_ptr
let _: &f32 = std::mem::transmute(take_ref!(1u32));
//~^ transmute_ptr_to_ptr
}
}

View file

@ -22,5 +22,23 @@ error: transmute from a reference to a reference
LL | let alt_slice: &[u32] = unsafe { std::mem::transmute(bytes) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(bytes as *const [u8] as *const [u32])`
error: aborting due to 3 previous errors
error: transmute from a pointer to a pointer
--> tests/ui/transmute_ref_to_ref.rs:35:29
|
LL | let _: *const f32 = std::mem::transmute(call!(make_ptr));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: use `pointer::cast` instead
|
LL - let _: *const f32 = std::mem::transmute(call!(make_ptr));
LL + let _: *const f32 = call!(make_ptr).cast::<f32>();
|
error: transmute from a reference to a reference
--> tests/ui/transmute_ref_to_ref.rs:37:23
|
LL | let _: &f32 = std::mem::transmute(take_ref!(1u32));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(take_ref!(1u32) as *const u32 as *const f32)`
error: aborting due to 5 previous errors