Skip to content

Commit 16eaf79

Browse files
authored
feat(transmute_ptr_to_ref): Handle a pointer wrapped in a struct (#15948)
Fixes rust-lang/rust-clippy#1966. Now the program checks for transmutting from a struct containing a single raw pointer to a reference. ```Rust struct Foo(*const i32); fn foo(foo: Foo) -> &i32 { unsafe { transmute(foo) } } ``` changelog: [`transmute_ptr_to_ref`]: now checks for a pointer wrapped in a struct
2 parents 5357257 + e9ceae4 commit 16eaf79

File tree

9 files changed

+346
-53
lines changed

9 files changed

+346
-53
lines changed

clippy_lints/src/transmute/mod.rs

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,11 @@ mod wrong_transmute;
1818
use clippy_config::Conf;
1919
use clippy_utils::is_in_const_context;
2020
use clippy_utils::msrvs::Msrv;
21+
use clippy_utils::sugg::Sugg;
22+
use rustc_errors::Applicability;
2123
use rustc_hir::{Expr, ExprKind, QPath};
2224
use rustc_lint::{LateContext, LateLintPass};
25+
use rustc_middle::ty::{self, Ty};
2326
use rustc_session::impl_lint_pass;
2427
use rustc_span::symbol::sym;
2528

@@ -490,6 +493,32 @@ impl Transmute {
490493
pub fn new(conf: &'static Conf) -> Self {
491494
Self { msrv: conf.msrv }
492495
}
496+
497+
/// When transmuting, a struct containing a single field works like the field.
498+
/// This function extracts the field type and the expression to get the field.
499+
fn extract_struct_field<'tcx>(
500+
cx: &LateContext<'tcx>,
501+
e: &'tcx Expr<'_>,
502+
outer_type: Ty<'tcx>,
503+
outer: &'tcx Expr<'tcx>,
504+
) -> (Ty<'tcx>, Sugg<'tcx>) {
505+
let mut applicability = Applicability::MachineApplicable;
506+
let outer_sugg = Sugg::hir_with_context(cx, outer, e.span.ctxt(), "..", &mut applicability);
507+
if let ty::Adt(struct_def, struct_args) = *outer_type.kind()
508+
&& struct_def.is_struct()
509+
&& let mut fields = struct_def.all_fields()
510+
&& let Some(first) = fields.next()
511+
&& fields.next().is_none()
512+
&& first.vis.is_accessible_from(cx.tcx.parent_module(outer.hir_id), cx.tcx)
513+
{
514+
(
515+
first.ty(cx.tcx, struct_args),
516+
Sugg::NonParen(format!("{}.{}", outer_sugg.maybe_paren(), first.name).into()),
517+
)
518+
} else {
519+
(outer_type, outer_sugg)
520+
}
521+
}
493522
}
494523
impl<'tcx> LateLintPass<'tcx> for Transmute {
495524
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
@@ -516,14 +545,17 @@ impl<'tcx> LateLintPass<'tcx> for Transmute {
516545
return;
517546
}
518547

548+
// A struct having a single pointer can be treated like a pointer.
549+
let (from_field_ty, from_field_expr) = Self::extract_struct_field(cx, e, from_ty, arg);
550+
519551
let linted = wrong_transmute::check(cx, e, from_ty, to_ty)
520552
| crosspointer_transmute::check(cx, e, from_ty, to_ty)
521553
| transmuting_null::check(cx, e, arg, to_ty)
522554
| transmute_null_to_fn::check(cx, e, arg, to_ty)
523-
| transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, self.msrv)
555+
| transmute_ptr_to_ref::check(cx, e, from_field_ty, to_ty, from_field_expr.clone(), path, self.msrv)
524556
| missing_transmute_annotations::check(cx, path, arg, from_ty, to_ty, e.hir_id)
525557
| transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context)
526-
| transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, arg, self.msrv)
558+
| transmute_ptr_to_ptr::check(cx, e, from_field_ty, to_ty, from_field_expr, self.msrv)
527559
| transmute_int_to_bool::check(cx, e, from_ty, to_ty, arg)
528560
| transmute_int_to_non_zero::check(cx, e, from_ty, to_ty, arg)
529561
| (unsound_collection_transmute::check(cx, e, from_ty, to_ty)

clippy_lints/src/transmute/transmute_ptr_to_ptr.rs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,9 @@ pub(super) fn check<'tcx>(
1414
e: &'tcx Expr<'_>,
1515
from_ty: Ty<'tcx>,
1616
to_ty: Ty<'tcx>,
17-
arg: &'tcx Expr<'_>,
17+
arg: sugg::Sugg<'_>,
1818
msrv: Msrv,
1919
) -> bool {
20-
let mut applicability = Applicability::MachineApplicable;
21-
let arg_sugg = sugg::Sugg::hir_with_context(cx, arg, e.span.ctxt(), "..", &mut applicability);
2220
match (from_ty.kind(), to_ty.kind()) {
2321
(ty::RawPtr(from_pointee_ty, from_mutbl), ty::RawPtr(to_pointee_ty, to_mutbl)) => {
2422
span_lint_and_then(
@@ -34,7 +32,7 @@ pub(super) fn check<'tcx>(
3432
diag.span_suggestion_verbose(
3533
e.span,
3634
"use `pointer::cast` instead",
37-
format!("{}.cast::<{to_pointee_ty}>()", arg_sugg.maybe_paren()),
35+
format!("{}.cast::<{to_pointee_ty}>()", arg.maybe_paren()),
3836
Applicability::MaybeIncorrect,
3937
);
4038
} else if from_pointee_ty == to_pointee_ty
@@ -49,14 +47,14 @@ pub(super) fn check<'tcx>(
4947
diag.span_suggestion_verbose(
5048
e.span,
5149
format!("use `pointer::{method}` instead"),
52-
format!("{}.{method}()", arg_sugg.maybe_paren()),
50+
format!("{}.{method}()", arg.maybe_paren()),
5351
Applicability::MaybeIncorrect,
5452
);
5553
} else {
5654
diag.span_suggestion_verbose(
5755
e.span,
5856
"use an `as` cast instead",
59-
arg_sugg.as_ty(to_ty),
57+
arg.as_ty(to_ty),
6058
Applicability::MaybeIncorrect,
6159
);
6260
}

clippy_lints/src/transmute/transmute_ptr_to_ref.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ pub(super) fn check<'tcx>(
1515
e: &'tcx Expr<'_>,
1616
from_ty: Ty<'tcx>,
1717
to_ty: Ty<'tcx>,
18-
arg: &'tcx Expr<'_>,
18+
arg: sugg::Sugg<'_>,
1919
path: &'tcx Path<'_>,
2020
msrv: Msrv,
2121
) -> bool {
@@ -27,7 +27,6 @@ pub(super) fn check<'tcx>(
2727
e.span,
2828
format!("transmute from a pointer type (`{from_ty}`) to a reference type (`{to_ty}`)"),
2929
|diag| {
30-
let arg = sugg::Sugg::hir(cx, arg, "..");
3130
let (deref, cast) = match mutbl {
3231
Mutability::Mut => ("&mut *", "*mut"),
3332
Mutability::Not => ("&*", "*const"),

tests/ui/transmute_ptr_to_ptr.fixed

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,13 @@ struct GenericParam<T> {
2424
t: T,
2525
}
2626

27+
#[derive(Clone, Copy)]
28+
struct PtrNamed {
29+
ptr: *const u32,
30+
}
31+
#[derive(Clone, Copy)]
32+
struct Ptr(*const u32);
33+
2734
fn transmute_ptr_to_ptr() {
2835
let ptr = &1u32 as *const u32;
2936
let mut_ptr = &mut 1u32 as *mut u32;
@@ -68,6 +75,18 @@ fn transmute_ptr_to_ptr() {
6875
let _: &GenericParam<&LifetimeParam<'static>> = unsafe { transmute(&GenericParam { t: &lp }) };
6976
}
7077

78+
fn issue1966() {
79+
let ptr = &1u32 as *const u32;
80+
unsafe {
81+
let _: *const f32 = Ptr(ptr).0.cast::<f32>();
82+
//~^ transmute_ptr_to_ptr
83+
let _: *const f32 = PtrNamed { ptr }.ptr.cast::<f32>();
84+
//~^ transmute_ptr_to_ptr
85+
let _: *mut u32 = Ptr(ptr).0.cast_mut();
86+
//~^ transmute_ptr_to_ptr
87+
}
88+
}
89+
7190
fn lifetime_to_static(v: *mut &()) -> *const &'static () {
7291
unsafe { v as *const &() }
7392
//~^ transmute_ptr_to_ptr
@@ -81,11 +100,15 @@ const _: &() = {
81100
unsafe { transmute::<&'static Zst, &'static ()>(zst) }
82101
};
83102

103+
#[derive(Clone, Copy)]
104+
struct Ptr8(*const u8);
84105
#[clippy::msrv = "1.37"]
85106
fn msrv_1_37(ptr: *const u8) {
86107
unsafe {
87108
let _: *const i8 = ptr as *const i8;
88109
//~^ transmute_ptr_to_ptr
110+
let _: *const i8 = Ptr8(ptr).0 as *const i8;
111+
//~^ transmute_ptr_to_ptr
89112
}
90113
}
91114

tests/ui/transmute_ptr_to_ptr.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,13 @@ struct GenericParam<T> {
2424
t: T,
2525
}
2626

27+
#[derive(Clone, Copy)]
28+
struct PtrNamed {
29+
ptr: *const u32,
30+
}
31+
#[derive(Clone, Copy)]
32+
struct Ptr(*const u32);
33+
2734
fn transmute_ptr_to_ptr() {
2835
let ptr = &1u32 as *const u32;
2936
let mut_ptr = &mut 1u32 as *mut u32;
@@ -68,6 +75,18 @@ fn transmute_ptr_to_ptr() {
6875
let _: &GenericParam<&LifetimeParam<'static>> = unsafe { transmute(&GenericParam { t: &lp }) };
6976
}
7077

78+
fn issue1966() {
79+
let ptr = &1u32 as *const u32;
80+
unsafe {
81+
let _: *const f32 = transmute(Ptr(ptr));
82+
//~^ transmute_ptr_to_ptr
83+
let _: *const f32 = transmute(PtrNamed { ptr });
84+
//~^ transmute_ptr_to_ptr
85+
let _: *mut u32 = transmute(Ptr(ptr));
86+
//~^ transmute_ptr_to_ptr
87+
}
88+
}
89+
7190
fn lifetime_to_static(v: *mut &()) -> *const &'static () {
7291
unsafe { transmute(v) }
7392
//~^ transmute_ptr_to_ptr
@@ -81,11 +100,15 @@ const _: &() = {
81100
unsafe { transmute::<&'static Zst, &'static ()>(zst) }
82101
};
83102

103+
#[derive(Clone, Copy)]
104+
struct Ptr8(*const u8);
84105
#[clippy::msrv = "1.37"]
85106
fn msrv_1_37(ptr: *const u8) {
86107
unsafe {
87108
let _: *const i8 = transmute(ptr);
88109
//~^ transmute_ptr_to_ptr
110+
let _: *const i8 = transmute(Ptr8(ptr));
111+
//~^ transmute_ptr_to_ptr
89112
}
90113
}
91114

0 commit comments

Comments
 (0)