Skip to content

Commit 5ac2656

Browse files
author
ceptontech
committed
feat(transmute_ptr_to_ref): Handle a pointer wrapped in a struct
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
1 parent 65c339e commit 5ac2656

File tree

5 files changed

+230
-29
lines changed

5 files changed

+230
-29
lines changed

clippy_lints/src/transmute/mod.rs

Lines changed: 33 additions & 1 deletion
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,11 +545,14 @@ 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)
526558
| transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, arg, self.msrv)

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_ref.fixed

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,52 @@ fn issue1231() {
5555
//~^ transmute_ptr_to_ref
5656
}
5757

58+
#[derive(Clone, Copy)]
59+
struct PtrRefNamed<'a> {
60+
ptr: *const &'a u32,
61+
}
62+
#[derive(Clone, Copy)]
63+
struct PtrRef<'a>(*const &'a u32);
64+
#[derive(Clone, Copy)]
65+
struct PtrSliceRef<'a>(*const [&'a str]);
66+
#[derive(Clone, Copy)]
67+
struct PtrSlice(*const [i32]);
68+
#[derive(Clone, Copy)]
69+
struct Ptr(*const u32);
70+
impl std::ops::Add for Ptr {
71+
type Output = Self;
72+
fn add(self, _: Self) -> Self {
73+
self
74+
}
75+
}
76+
mod ptr_mod {
77+
#[derive(Clone, Copy)]
78+
pub struct Ptr(*const u32);
79+
}
80+
fn issue1966(u: PtrSlice, v: PtrSliceRef, w: Ptr, x: PtrRefNamed, y: PtrRef, z: ptr_mod::Ptr) {
81+
unsafe {
82+
let _: &i32 = &*(w.0 as *const i32);
83+
//~^ transmute_ptr_to_ref
84+
let _: &u32 = &*w.0;
85+
//~^ transmute_ptr_to_ref
86+
let _: &&u32 = &*x.ptr.cast::<&u32>();
87+
//~^ transmute_ptr_to_ref
88+
// The field is not accessible. The program should not generate code
89+
// that accesses the field.
90+
let _: &u32 = std::mem::transmute(z);
91+
let _ = &*w.0.cast::<u32>();
92+
//~^ transmute_ptr_to_ref
93+
let _: &[&str] = &*(v.0 as *const [&str]);
94+
//~^ transmute_ptr_to_ref
95+
let _ = &*(u.0 as *const [i32]);
96+
//~^ transmute_ptr_to_ref
97+
let _: &&u32 = &*y.0.cast::<&u32>();
98+
//~^ transmute_ptr_to_ref
99+
let _: &u32 = &*(w + w).0;
100+
//~^ transmute_ptr_to_ref
101+
}
102+
}
103+
58104
fn issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &'b u32 {
59105
unsafe {
60106
match 0 {
@@ -89,18 +135,24 @@ fn meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 {
89135
}
90136

91137
#[clippy::msrv = "1.37"]
92-
fn under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 {
138+
fn under_msrv<'a, 'b, 'c>(x: *const &'a u32, y: PtrRef) -> &'c &'b u32 {
93139
unsafe {
94140
let a = 0u32;
95141
let a = &a as *const u32;
96142
let _: &u32 = &*a;
97143
//~^ transmute_ptr_to_ref
98144
let _: &u32 = &*(a as *const u32);
99145
//~^ transmute_ptr_to_ref
146+
let _ = &*(Ptr(a).0 as *const u32);
147+
//~^ transmute_ptr_to_ref
100148
match 0 {
101149
0 => &*(x as *const () as *const &u32),
102150
//~^ transmute_ptr_to_ref
103-
_ => &*(x as *const () as *const &'b u32),
151+
1 => &*(x as *const () as *const &'b u32),
152+
//~^ transmute_ptr_to_ref
153+
2 => &*(y.0 as *const () as *const &u32),
154+
//~^ transmute_ptr_to_ref
155+
_ => &*(y.0 as *const () as *const &'b u32),
104156
//~^ transmute_ptr_to_ref
105157
}
106158
}

tests/ui/transmute_ptr_to_ref.rs

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,52 @@ fn issue1231() {
5555
//~^ transmute_ptr_to_ref
5656
}
5757

58+
#[derive(Clone, Copy)]
59+
struct PtrRefNamed<'a> {
60+
ptr: *const &'a u32,
61+
}
62+
#[derive(Clone, Copy)]
63+
struct PtrRef<'a>(*const &'a u32);
64+
#[derive(Clone, Copy)]
65+
struct PtrSliceRef<'a>(*const [&'a str]);
66+
#[derive(Clone, Copy)]
67+
struct PtrSlice(*const [i32]);
68+
#[derive(Clone, Copy)]
69+
struct Ptr(*const u32);
70+
impl std::ops::Add for Ptr {
71+
type Output = Self;
72+
fn add(self, _: Self) -> Self {
73+
self
74+
}
75+
}
76+
mod ptr_mod {
77+
#[derive(Clone, Copy)]
78+
pub struct Ptr(*const u32);
79+
}
80+
fn issue1966(u: PtrSlice, v: PtrSliceRef, w: Ptr, x: PtrRefNamed, y: PtrRef, z: ptr_mod::Ptr) {
81+
unsafe {
82+
let _: &i32 = std::mem::transmute(w);
83+
//~^ transmute_ptr_to_ref
84+
let _: &u32 = std::mem::transmute(w);
85+
//~^ transmute_ptr_to_ref
86+
let _: &&u32 = core::mem::transmute(x);
87+
//~^ transmute_ptr_to_ref
88+
// The field is not accessible. The program should not generate code
89+
// that accesses the field.
90+
let _: &u32 = std::mem::transmute(z);
91+
let _ = std::mem::transmute::<_, &u32>(w);
92+
//~^ transmute_ptr_to_ref
93+
let _: &[&str] = core::mem::transmute(v);
94+
//~^ transmute_ptr_to_ref
95+
let _ = std::mem::transmute::<_, &[i32]>(u);
96+
//~^ transmute_ptr_to_ref
97+
let _: &&u32 = std::mem::transmute(y);
98+
//~^ transmute_ptr_to_ref
99+
let _: &u32 = std::mem::transmute(w + w);
100+
//~^ transmute_ptr_to_ref
101+
}
102+
}
103+
58104
fn issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &'b u32 {
59105
unsafe {
60106
match 0 {
@@ -89,18 +135,24 @@ fn meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 {
89135
}
90136

91137
#[clippy::msrv = "1.37"]
92-
fn under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 {
138+
fn under_msrv<'a, 'b, 'c>(x: *const &'a u32, y: PtrRef) -> &'c &'b u32 {
93139
unsafe {
94140
let a = 0u32;
95141
let a = &a as *const u32;
96142
let _: &u32 = std::mem::transmute(a);
97143
//~^ transmute_ptr_to_ref
98144
let _: &u32 = std::mem::transmute::<_, &u32>(a);
99145
//~^ transmute_ptr_to_ref
146+
let _ = std::mem::transmute::<_, &u32>(Ptr(a));
147+
//~^ transmute_ptr_to_ref
100148
match 0 {
101149
0 => std::mem::transmute(x),
102150
//~^ transmute_ptr_to_ref
103-
_ => std::mem::transmute::<_, &&'b u32>(x),
151+
1 => std::mem::transmute::<_, &&'b u32>(x),
152+
//~^ transmute_ptr_to_ref
153+
2 => std::mem::transmute(y),
154+
//~^ transmute_ptr_to_ref
155+
_ => std::mem::transmute::<_, &&'b u32>(y),
104156
//~^ transmute_ptr_to_ref
105157
}
106158
}

0 commit comments

Comments
 (0)