Suggest .cast/.cast_const/.cast_mut in transmute_ptr_as_ptr

This commit is contained in:
Alex Macleod 2024-07-22 17:20:23 +00:00
parent 1807580a49
commit afe811ad30
7 changed files with 274 additions and 80 deletions

View file

@ -617,7 +617,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute {
| missing_transmute_annotations::check(cx, path, from_ty, to_ty, e.hir_id)
| 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)
| transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, arg, &self.msrv)
| transmute_int_to_bool::check(cx, e, from_ty, to_ty, arg)
| transmute_int_to_float::check(cx, e, from_ty, to_ty, arg, const_context)
| transmute_int_to_non_zero::check(cx, e, from_ty, to_ty, arg)

View file

@ -1,4 +1,5 @@
use super::TRANSMUTE_PTR_TO_PTR;
use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::sugg;
use rustc_errors::Applicability;
@ -14,9 +15,10 @@ pub(super) fn check<'tcx>(
from_ty: Ty<'tcx>,
to_ty: Ty<'tcx>,
arg: &'tcx Expr<'_>,
msrv: &Msrv,
) -> bool {
match (&from_ty.kind(), &to_ty.kind()) {
(ty::RawPtr(_, _), ty::RawPtr(to_ty, to_mutbl)) => {
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(
cx,
TRANSMUTE_PTR_TO_PTR,
@ -24,8 +26,38 @@ pub(super) fn check<'tcx>(
"transmute from a pointer to a pointer",
|diag| {
if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) {
let sugg = arg.as_ty(Ty::new_ptr(cx.tcx, *to_ty, *to_mutbl));
diag.span_suggestion(e.span, "try", sugg, Applicability::Unspecified);
if from_mutbl == to_mutbl
&& to_pointee_ty.is_sized(cx.tcx, cx.param_env)
&& msrv.meets(msrvs::POINTER_CAST)
{
diag.span_suggestion_verbose(
e.span,
"use `pointer::cast` instead",
format!("{}.cast::<{to_pointee_ty}>()", arg.maybe_par()),
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,
}
&& msrv.meets(msrvs::POINTER_CAST_CONSTNESS)
{
diag.span_suggestion_verbose(
e.span,
format!("use `pointer::{method}` instead"),
format!("{}.{method}()", arg.maybe_par()),
Applicability::MaybeIncorrect,
);
} else {
diag.span_suggestion_verbose(
e.span,
"use an `as` cast instead",
arg.as_ty(to_ty),
Applicability::MaybeIncorrect,
);
}
}
},
);

View file

@ -1,17 +1,19 @@
#![warn(clippy::transmute_ptr_to_ptr)]
#![allow(clippy::borrow_as_ptr, clippy::missing_transmute_annotations)]
use std::mem::transmute;
// Make sure we can modify lifetimes, which is one of the recommended uses
// of transmute
// Make sure we can do static lifetime transmutes
unsafe fn transmute_lifetime_to_static<'a, T>(t: &'a T) -> &'static T {
std::mem::transmute::<&'a T, &'static T>(t)
transmute::<&'a T, &'static T>(t)
}
// Make sure we can do non-static lifetime transmutes
unsafe fn transmute_lifetime<'a, 'b, T>(t: &'a T, u: &'b T) -> &'b T {
std::mem::transmute::<&'a T, &'b T>(t)
transmute::<&'a T, &'b T>(t)
}
struct LifetimeParam<'a> {
@ -27,39 +29,35 @@ fn transmute_ptr_to_ptr() {
let mut_ptr = &mut 1u32 as *mut u32;
unsafe {
// pointer-to-pointer transmutes; bad
let _: *const f32 = ptr as *const f32;
//~^ ERROR: transmute from a pointer to a pointer
//~| NOTE: `-D clippy::transmute-ptr-to-ptr` implied by `-D warnings`
let _: *mut f32 = mut_ptr as *mut f32;
//~^ ERROR: transmute from a pointer to a pointer
let _: *const f32 = ptr.cast::<f32>();
//~^ transmute_ptr_to_ptr
let _: *mut f32 = mut_ptr.cast::<f32>();
//~^ transmute_ptr_to_ptr
// ref-ref transmutes; bad
let _: &f32 = &*(&1u32 as *const u32 as *const f32);
//~^ ERROR: transmute from a reference to a reference
//~^ transmute_ptr_to_ptr
let _: &f32 = &*(&1f64 as *const f64 as *const f32);
//~^ ERROR: transmute from a reference to a reference
//~^ transmute_ptr_to_ptr
//:^ this test is here because both f32 and f64 are the same TypeVariant, but they are not
// the same type
let _: &mut f32 = &mut *(&mut 1u32 as *mut u32 as *mut f32);
//~^ ERROR: transmute from a reference to a reference
//~^ transmute_ptr_to_ptr
let _: &GenericParam<f32> = &*(&GenericParam { t: 1u32 } as *const GenericParam<u32> as *const GenericParam<f32>);
//~^ ERROR: transmute from a reference to a reference
//~^ transmute_ptr_to_ptr
let u64_ref: &u64 = &0u64;
let u8_ref: &u8 = unsafe { &*(u64_ref as *const u64 as *const u8) };
//~^ ERROR: transmute from a reference to a reference
let u8_ref: &u8 = &*(u64_ref as *const u64 as *const u8);
//~^ transmute_ptr_to_ptr
let _: *const u32 = mut_ptr.cast_const();
//~^ transmute_ptr_to_ptr
let _: *mut u32 = ptr.cast_mut();
//~^ transmute_ptr_to_ptr
}
// these are recommendations for solving the above; if these lint we need to update
// those suggestions
let _ = ptr as *const f32;
let _ = mut_ptr as *mut f32;
let _ = unsafe { &*(&1u32 as *const u32 as *const f32) };
let _ = unsafe { &mut *(&mut 1u32 as *mut u32 as *mut f32) };
// transmute internal lifetimes, should not lint
let s = "hello world".to_owned();
let lp = LifetimeParam { s: &s };
let _: &LifetimeParam<'static> = unsafe { std::mem::transmute(&lp) };
let _: &GenericParam<&LifetimeParam<'static>> = unsafe { std::mem::transmute(&GenericParam { t: &lp }) };
let _: &LifetimeParam<'static> = unsafe { transmute(&lp) };
let _: &GenericParam<&LifetimeParam<'static>> = unsafe { transmute(&GenericParam { t: &lp }) };
}
// dereferencing raw pointers in const contexts, should not lint as it's unstable (issue 5959)
@ -67,7 +65,37 @@ const _: &() = {
struct Zst;
let zst = &Zst;
unsafe { std::mem::transmute::<&'static Zst, &'static ()>(zst) }
unsafe { transmute::<&'static Zst, &'static ()>(zst) }
};
#[clippy::msrv = "1.37"]
fn msrv_1_37(ptr: *const u8) {
unsafe {
let _: *const i8 = ptr as *const i8;
}
}
#[clippy::msrv = "1.38"]
fn msrv_1_38(ptr: *const u8) {
unsafe {
let _: *const i8 = ptr.cast::<i8>();
}
}
#[clippy::msrv = "1.64"]
fn msrv_1_64(ptr: *const u8, mut_ptr: *mut u8) {
unsafe {
let _: *mut u8 = ptr as *mut u8;
let _: *const u8 = mut_ptr as *const u8;
}
}
#[clippy::msrv = "1.65"]
fn msrv_1_65(ptr: *const u8, mut_ptr: *mut u8) {
unsafe {
let _: *mut u8 = ptr.cast_mut();
let _: *const u8 = mut_ptr.cast_const();
}
}
fn main() {}

View file

@ -1,17 +1,19 @@
#![warn(clippy::transmute_ptr_to_ptr)]
#![allow(clippy::borrow_as_ptr, clippy::missing_transmute_annotations)]
use std::mem::transmute;
// Make sure we can modify lifetimes, which is one of the recommended uses
// of transmute
// Make sure we can do static lifetime transmutes
unsafe fn transmute_lifetime_to_static<'a, T>(t: &'a T) -> &'static T {
std::mem::transmute::<&'a T, &'static T>(t)
transmute::<&'a T, &'static T>(t)
}
// Make sure we can do non-static lifetime transmutes
unsafe fn transmute_lifetime<'a, 'b, T>(t: &'a T, u: &'b T) -> &'b T {
std::mem::transmute::<&'a T, &'b T>(t)
transmute::<&'a T, &'b T>(t)
}
struct LifetimeParam<'a> {
@ -27,39 +29,35 @@ fn transmute_ptr_to_ptr() {
let mut_ptr = &mut 1u32 as *mut u32;
unsafe {
// pointer-to-pointer transmutes; bad
let _: *const f32 = std::mem::transmute(ptr);
//~^ ERROR: transmute from a pointer to a pointer
//~| NOTE: `-D clippy::transmute-ptr-to-ptr` implied by `-D warnings`
let _: *mut f32 = std::mem::transmute(mut_ptr);
//~^ ERROR: transmute from a pointer to a pointer
let _: *const f32 = transmute(ptr);
//~^ transmute_ptr_to_ptr
let _: *mut f32 = transmute(mut_ptr);
//~^ transmute_ptr_to_ptr
// ref-ref transmutes; bad
let _: &f32 = std::mem::transmute(&1u32);
//~^ ERROR: transmute from a reference to a reference
let _: &f32 = std::mem::transmute(&1f64);
//~^ ERROR: transmute from a reference to a reference
let _: &f32 = transmute(&1u32);
//~^ transmute_ptr_to_ptr
let _: &f32 = transmute(&1f64);
//~^ transmute_ptr_to_ptr
//:^ this test is here because both f32 and f64 are the same TypeVariant, but they are not
// the same type
let _: &mut f32 = std::mem::transmute(&mut 1u32);
//~^ ERROR: transmute from a reference to a reference
let _: &GenericParam<f32> = std::mem::transmute(&GenericParam { t: 1u32 });
//~^ ERROR: transmute from a reference to a reference
let _: &mut f32 = transmute(&mut 1u32);
//~^ transmute_ptr_to_ptr
let _: &GenericParam<f32> = transmute(&GenericParam { t: 1u32 });
//~^ transmute_ptr_to_ptr
let u64_ref: &u64 = &0u64;
let u8_ref: &u8 = unsafe { std::mem::transmute(u64_ref) };
//~^ ERROR: transmute from a reference to a reference
let u8_ref: &u8 = transmute(u64_ref);
//~^ transmute_ptr_to_ptr
let _: *const u32 = transmute(mut_ptr);
//~^ transmute_ptr_to_ptr
let _: *mut u32 = transmute(ptr);
//~^ transmute_ptr_to_ptr
}
// these are recommendations for solving the above; if these lint we need to update
// those suggestions
let _ = ptr as *const f32;
let _ = mut_ptr as *mut f32;
let _ = unsafe { &*(&1u32 as *const u32 as *const f32) };
let _ = unsafe { &mut *(&mut 1u32 as *mut u32 as *mut f32) };
// transmute internal lifetimes, should not lint
let s = "hello world".to_owned();
let lp = LifetimeParam { s: &s };
let _: &LifetimeParam<'static> = unsafe { std::mem::transmute(&lp) };
let _: &GenericParam<&LifetimeParam<'static>> = unsafe { std::mem::transmute(&GenericParam { t: &lp }) };
let _: &LifetimeParam<'static> = unsafe { transmute(&lp) };
let _: &GenericParam<&LifetimeParam<'static>> = unsafe { transmute(&GenericParam { t: &lp }) };
}
// dereferencing raw pointers in const contexts, should not lint as it's unstable (issue 5959)
@ -67,7 +65,37 @@ const _: &() = {
struct Zst;
let zst = &Zst;
unsafe { std::mem::transmute::<&'static Zst, &'static ()>(zst) }
unsafe { transmute::<&'static Zst, &'static ()>(zst) }
};
#[clippy::msrv = "1.37"]
fn msrv_1_37(ptr: *const u8) {
unsafe {
let _: *const i8 = transmute(ptr);
}
}
#[clippy::msrv = "1.38"]
fn msrv_1_38(ptr: *const u8) {
unsafe {
let _: *const i8 = transmute(ptr);
}
}
#[clippy::msrv = "1.64"]
fn msrv_1_64(ptr: *const u8, mut_ptr: *mut u8) {
unsafe {
let _: *mut u8 = transmute(ptr);
let _: *const u8 = transmute(mut_ptr);
}
}
#[clippy::msrv = "1.65"]
fn msrv_1_65(ptr: *const u8, mut_ptr: *mut u8) {
unsafe {
let _: *mut u8 = transmute(ptr);
let _: *const u8 = transmute(mut_ptr);
}
}
fn main() {}

View file

@ -1,47 +1,144 @@
error: transmute from a pointer to a pointer
--> tests/ui/transmute_ptr_to_ptr.rs:30:29
--> tests/ui/transmute_ptr_to_ptr.rs:32:29
|
LL | let _: *const f32 = std::mem::transmute(ptr);
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr as *const f32`
LL | let _: *const f32 = transmute(ptr);
| ^^^^^^^^^^^^^^
|
= note: `-D clippy::transmute-ptr-to-ptr` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::transmute_ptr_to_ptr)]`
help: use `pointer::cast` instead
|
LL | let _: *const f32 = ptr.cast::<f32>();
| ~~~~~~~~~~~~~~~~~
error: transmute from a pointer to a pointer
--> tests/ui/transmute_ptr_to_ptr.rs:33:27
--> tests/ui/transmute_ptr_to_ptr.rs:34:27
|
LL | let _: *mut f32 = std::mem::transmute(mut_ptr);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `mut_ptr as *mut f32`
LL | let _: *mut f32 = transmute(mut_ptr);
| ^^^^^^^^^^^^^^^^^^
|
help: use `pointer::cast` instead
|
LL | let _: *mut f32 = mut_ptr.cast::<f32>();
| ~~~~~~~~~~~~~~~~~~~~~
error: transmute from a reference to a reference
--> tests/ui/transmute_ptr_to_ptr.rs:36:23
--> tests/ui/transmute_ptr_to_ptr.rs:37:23
|
LL | let _: &f32 = std::mem::transmute(&1u32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(&1u32 as *const u32 as *const f32)`
LL | let _: &f32 = transmute(&1u32);
| ^^^^^^^^^^^^^^^^ help: try: `&*(&1u32 as *const u32 as *const f32)`
error: transmute from a reference to a reference
--> tests/ui/transmute_ptr_to_ptr.rs:38:23
--> tests/ui/transmute_ptr_to_ptr.rs:39:23
|
LL | let _: &f32 = std::mem::transmute(&1f64);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(&1f64 as *const f64 as *const f32)`
LL | let _: &f32 = transmute(&1f64);
| ^^^^^^^^^^^^^^^^ help: try: `&*(&1f64 as *const f64 as *const f32)`
error: transmute from a reference to a reference
--> tests/ui/transmute_ptr_to_ptr.rs:42:27
--> tests/ui/transmute_ptr_to_ptr.rs:43:27
|
LL | let _: &mut f32 = std::mem::transmute(&mut 1u32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(&mut 1u32 as *mut u32 as *mut f32)`
LL | let _: &mut f32 = transmute(&mut 1u32);
| ^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(&mut 1u32 as *mut u32 as *mut f32)`
error: transmute from a reference to a reference
--> tests/ui/transmute_ptr_to_ptr.rs:44:37
--> tests/ui/transmute_ptr_to_ptr.rs:45:37
|
LL | let _: &GenericParam<f32> = std::mem::transmute(&GenericParam { t: 1u32 });
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(&GenericParam { t: 1u32 } as *const GenericParam<u32> as *const GenericParam<f32>)`
LL | let _: &GenericParam<f32> = transmute(&GenericParam { t: 1u32 });
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(&GenericParam { t: 1u32 } as *const GenericParam<u32> as *const GenericParam<f32>)`
error: transmute from a reference to a reference
--> tests/ui/transmute_ptr_to_ptr.rs:47:36
--> tests/ui/transmute_ptr_to_ptr.rs:48:27
|
LL | let u8_ref: &u8 = unsafe { std::mem::transmute(u64_ref) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(u64_ref as *const u64 as *const u8)`
LL | let u8_ref: &u8 = transmute(u64_ref);
| ^^^^^^^^^^^^^^^^^^ help: try: `&*(u64_ref as *const u64 as *const u8)`
error: aborting due to 7 previous errors
error: transmute from a pointer to a pointer
--> tests/ui/transmute_ptr_to_ptr.rs:50:29
|
LL | let _: *const u32 = transmute(mut_ptr);
| ^^^^^^^^^^^^^^^^^^
|
help: use `pointer::cast_const` instead
|
LL | let _: *const u32 = mut_ptr.cast_const();
| ~~~~~~~~~~~~~~~~~~~~
error: transmute from a pointer to a pointer
--> tests/ui/transmute_ptr_to_ptr.rs:52:27
|
LL | let _: *mut u32 = transmute(ptr);
| ^^^^^^^^^^^^^^
|
help: use `pointer::cast_mut` instead
|
LL | let _: *mut u32 = ptr.cast_mut();
| ~~~~~~~~~~~~~~
error: transmute from a pointer to a pointer
--> tests/ui/transmute_ptr_to_ptr.rs:74:28
|
LL | let _: *const i8 = transmute(ptr);
| ^^^^^^^^^^^^^^
|
help: use an `as` cast instead
|
LL | let _: *const i8 = ptr as *const i8;
| ~~~~~~~~~~~~~~~~
error: transmute from a pointer to a pointer
--> tests/ui/transmute_ptr_to_ptr.rs:81:28
|
LL | let _: *const i8 = transmute(ptr);
| ^^^^^^^^^^^^^^
|
help: use `pointer::cast` instead
|
LL | let _: *const i8 = ptr.cast::<i8>();
| ~~~~~~~~~~~~~~~~
error: transmute from a pointer to a pointer
--> tests/ui/transmute_ptr_to_ptr.rs:88:26
|
LL | let _: *mut u8 = transmute(ptr);
| ^^^^^^^^^^^^^^
|
help: use an `as` cast instead
|
LL | let _: *mut u8 = ptr as *mut u8;
| ~~~~~~~~~~~~~~
error: transmute from a pointer to a pointer
--> tests/ui/transmute_ptr_to_ptr.rs:89:28
|
LL | let _: *const u8 = transmute(mut_ptr);
| ^^^^^^^^^^^^^^^^^^
|
help: use an `as` cast instead
|
LL | let _: *const u8 = mut_ptr as *const u8;
| ~~~~~~~~~~~~~~~~~~~~
error: transmute from a pointer to a pointer
--> tests/ui/transmute_ptr_to_ptr.rs:96:26
|
LL | let _: *mut u8 = transmute(ptr);
| ^^^^^^^^^^^^^^
|
help: use `pointer::cast_mut` instead
|
LL | let _: *mut u8 = ptr.cast_mut();
| ~~~~~~~~~~~~~~
error: transmute from a pointer to a pointer
--> tests/ui/transmute_ptr_to_ptr.rs:97:28
|
LL | let _: *const u8 = transmute(mut_ptr);
| ^^^^^^^^^^^^^^^^^^
|
help: use `pointer::cast_const` instead
|
LL | let _: *const u8 = mut_ptr.cast_const();
| ~~~~~~~~~~~~~~~~~~~~
error: aborting due to 15 previous errors

View file

@ -18,7 +18,7 @@ fn main() {
let ptr_i32 = usize::MAX as *const i32;
// e has type *T, U is *U_0, and either U_0: Sized ...
let _ptr_i8_transmute = unsafe { ptr_i32 as *const i8 };
let _ptr_i8_transmute = unsafe { ptr_i32.cast::<i8>() };
let _ptr_i8 = ptr_i32 as *const i8;
let slice_ptr = &[0, 1, 2, 3] as *const [i32];

View file

@ -11,16 +11,25 @@ error: transmute from a pointer to a pointer
--> tests/ui/transmutes_expressible_as_ptr_casts.rs:21:38
|
LL | let _ptr_i8_transmute = unsafe { transmute::<*const i32, *const i8>(ptr_i32) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr_i32 as *const i8`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::transmute-ptr-to-ptr` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::transmute_ptr_to_ptr)]`
help: use `pointer::cast` instead
|
LL | let _ptr_i8_transmute = unsafe { ptr_i32.cast::<i8>() };
| ~~~~~~~~~~~~~~~~~~~~
error: transmute from a pointer to a pointer
--> tests/ui/transmutes_expressible_as_ptr_casts.rs:27:46
|
LL | let _ptr_to_unsized_transmute = unsafe { transmute::<*const [i32], *const [u32]>(slice_ptr) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `slice_ptr as *const [u32]`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: use an `as` cast instead
|
LL | let _ptr_to_unsized_transmute = unsafe { slice_ptr as *const [u32] };
| ~~~~~~~~~~~~~~~~~~~~~~~~~
error: transmute from `*const i32` to `usize` which could be expressed as a pointer cast instead
--> tests/ui/transmutes_expressible_as_ptr_casts.rs:33:50