Skip to content

Commit

Permalink
Suggest .cast/.cast_const/.cast_mut in transmute_ptr_as_ptr
Browse files Browse the repository at this point in the history
  • Loading branch information
Alexendoo committed Jul 22, 2024
1 parent 1807580 commit 27afc5d
Show file tree
Hide file tree
Showing 7 changed files with 142 additions and 80 deletions.
2 changes: 1 addition & 1 deletion clippy_lints/src/transmute/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
34 changes: 30 additions & 4 deletions clippy_lints/src/transmute/transmute_ptr_to_ptr.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -14,18 +15,43 @@ 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,
e.span,
"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 && msrv.meets(msrvs::POINTER_CAST_CONSTNESS) {
let method = if from_mutbl.is_mut() { "cast_const" } else { "cast_mut" };
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,
);
}
}
},
);
Expand Down
44 changes: 21 additions & 23 deletions tests/ui/transmute_ptr_to_ptr.fixed
Original file line number Diff line number Diff line change
@@ -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> {
Expand All @@ -27,47 +29,43 @@ 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)
const _: &() = {
struct Zst;
let zst = &Zst;

unsafe { std::mem::transmute::<&'static Zst, &'static ()>(zst) }
unsafe { transmute::<&'static Zst, &'static ()>(zst) }
};

fn main() {}
52 changes: 25 additions & 27 deletions tests/ui/transmute_ptr_to_ptr.rs
Original file line number Diff line number Diff line change
@@ -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> {
Expand All @@ -27,47 +29,43 @@ 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)
const _: &() = {
struct Zst;
let zst = &Zst;

unsafe { std::mem::transmute::<&'static Zst, &'static ()>(zst) }
unsafe { transmute::<&'static Zst, &'static ()>(zst) }
};

fn main() {}
75 changes: 53 additions & 22 deletions tests/ui/transmute_ptr_to_ptr.stderr
Original file line number Diff line number Diff line change
@@ -1,47 +1,78 @@
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 = transmute(mut_ptr);
| ^^^^^^^^^^^^^^^^^^
|
LL | let _: *mut f32 = std::mem::transmute(mut_ptr);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `mut_ptr as *mut f32`
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 = transmute(u64_ref);
| ^^^^^^^^^^^^^^^^^^ help: try: `&*(u64_ref as *const u64 as *const u8)`

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 u8_ref: &u8 = unsafe { std::mem::transmute(u64_ref) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(u64_ref as *const u64 as *const u8)`
LL | let _: *mut u32 = ptr.cast_mut();
| ~~~~~~~~~~~~~~

error: aborting due to 7 previous errors
error: aborting due to 9 previous errors

2 changes: 1 addition & 1 deletion tests/ui/transmutes_expressible_as_ptr_casts.fixed
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down
13 changes: 11 additions & 2 deletions tests/ui/transmutes_expressible_as_ptr_casts.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 27afc5d

Please sign in to comment.