Skip to content

Commit 7ee77b5

Browse files
committed
Add support for ptr::write for the invalid_reference_casting lint
1 parent b4d09f3 commit 7ee77b5

File tree

5 files changed

+106
-22
lines changed

5 files changed

+106
-22
lines changed

compiler/rustc_lint/src/reference_casting.rs

+47-17
Original file line numberDiff line numberDiff line change
@@ -56,20 +56,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidReferenceCasting {
5656
}
5757

5858
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
59-
// &mut <expr>
60-
let inner = if let ExprKind::AddrOf(_, Mutability::Mut, expr) = expr.kind {
61-
expr
62-
// <expr> = ...
63-
} else if let ExprKind::Assign(expr, _, _) = expr.kind {
64-
expr
65-
// <expr> += ...
66-
} else if let ExprKind::AssignOp(_, expr, _) = expr.kind {
67-
expr
68-
} else {
69-
return;
70-
};
71-
72-
let ExprKind::Unary(UnOp::Deref, e) = &inner.kind else {
59+
let Some((is_assignment, e)) = is_operation_we_care_about(cx, expr) else {
7360
return;
7461
};
7562

@@ -86,15 +73,58 @@ impl<'tcx> LateLintPass<'tcx> for InvalidReferenceCasting {
8673
cx.emit_spanned_lint(
8774
INVALID_REFERENCE_CASTING,
8875
expr.span,
89-
if matches!(expr.kind, ExprKind::AddrOf(..)) {
90-
InvalidReferenceCastingDiag::BorrowAsMut { orig_cast }
91-
} else {
76+
if is_assignment {
9277
InvalidReferenceCastingDiag::AssignToRef { orig_cast }
78+
} else {
79+
InvalidReferenceCastingDiag::BorrowAsMut { orig_cast }
9380
},
9481
);
9582
}
9683
}
9784

85+
fn is_operation_we_care_about<'tcx>(
86+
cx: &LateContext<'tcx>,
87+
e: &'tcx Expr<'tcx>,
88+
) -> Option<(bool, &'tcx Expr<'tcx>)> {
89+
fn deref_assign_or_addr_of<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<(bool, &'tcx Expr<'tcx>)> {
90+
// &mut <expr>
91+
let inner = if let ExprKind::AddrOf(_, Mutability::Mut, expr) = expr.kind {
92+
expr
93+
// <expr> = ...
94+
} else if let ExprKind::Assign(expr, _, _) = expr.kind {
95+
expr
96+
// <expr> += ...
97+
} else if let ExprKind::AssignOp(_, expr, _) = expr.kind {
98+
expr
99+
} else {
100+
return None;
101+
};
102+
103+
if let ExprKind::Unary(UnOp::Deref, e) = &inner.kind {
104+
Some((!matches!(expr.kind, ExprKind::AddrOf(..)), e))
105+
} else {
106+
None
107+
}
108+
}
109+
110+
fn ptr_write<'tcx>(
111+
cx: &LateContext<'tcx>,
112+
e: &'tcx Expr<'tcx>,
113+
) -> Option<(bool, &'tcx Expr<'tcx>)> {
114+
if let ExprKind::Call(path, [arg_ptr, _arg_val]) = e.kind
115+
&& let ExprKind::Path(ref qpath) = path.kind
116+
&& let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
117+
&& matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::ptr_write | sym::ptr_write_volatile | sym::ptr_write_unaligned))
118+
{
119+
Some((true, arg_ptr))
120+
} else {
121+
None
122+
}
123+
}
124+
125+
deref_assign_or_addr_of(e).or_else(|| ptr_write(cx, e))
126+
}
127+
98128
fn is_cast_from_const_to_mut<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
99129
let e = e.peel_blocks();
100130

compiler/rustc_span/src/symbol.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1179,6 +1179,9 @@ symbols! {
11791179
ptr_offset_from,
11801180
ptr_offset_from_unsigned,
11811181
ptr_unique,
1182+
ptr_write,
1183+
ptr_write_unaligned,
1184+
ptr_write_volatile,
11821185
pub_macro_rules,
11831186
pub_restricted,
11841187
public,

library/core/src/ptr/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1357,6 +1357,7 @@ pub const unsafe fn read_unaligned<T>(src: *const T) -> T {
13571357
#[inline]
13581358
#[stable(feature = "rust1", since = "1.0.0")]
13591359
#[rustc_const_unstable(feature = "const_ptr_write", issue = "86302")]
1360+
#[rustc_diagnostic_item = "ptr_write"]
13601361
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
13611362
pub const unsafe fn write<T>(dst: *mut T, src: T) {
13621363
// Semantically, it would be fine for this to be implemented as a
@@ -1459,6 +1460,7 @@ pub const unsafe fn write<T>(dst: *mut T, src: T) {
14591460
#[inline]
14601461
#[stable(feature = "ptr_unaligned", since = "1.17.0")]
14611462
#[rustc_const_unstable(feature = "const_ptr_write", issue = "86302")]
1463+
#[rustc_diagnostic_item = "ptr_write_unaligned"]
14621464
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
14631465
pub const unsafe fn write_unaligned<T>(dst: *mut T, src: T) {
14641466
// SAFETY: the caller must guarantee that `dst` is valid for writes.
@@ -1607,6 +1609,7 @@ pub unsafe fn read_volatile<T>(src: *const T) -> T {
16071609
/// ```
16081610
#[inline]
16091611
#[stable(feature = "volatile", since = "1.9.0")]
1612+
#[rustc_diagnostic_item = "ptr_write_volatile"]
16101613
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
16111614
pub unsafe fn write_volatile<T>(dst: *mut T, src: T) {
16121615
// SAFETY: the caller must uphold the safety contract for `volatile_store`.

tests/ui/lint/reference_casting.rs

+11
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,11 @@ unsafe fn assign_to_ref() {
7171
//~^ ERROR assigning to `&T` is undefined behavior
7272
*std::mem::transmute::<_, *mut i32>(num) += 1;
7373
//~^ ERROR assigning to `&T` is undefined behavior
74+
std::ptr::write(
75+
//~^ ERROR assigning to `&T` is undefined behavior
76+
std::mem::transmute::<*const i32, *mut i32>(num),
77+
-1i32,
78+
);
7479

7580
let value = num as *const i32 as *mut i32;
7681
*value = 1;
@@ -79,6 +84,12 @@ unsafe fn assign_to_ref() {
7984
//~^ ERROR assigning to `&T` is undefined behavior
8085
*(num as *const _ as usize as *mut i32) = 2;
8186
//~^ ERROR assigning to `&T` is undefined behavior
87+
std::ptr::write(value, 2);
88+
//~^ ERROR assigning to `&T` is undefined behavior
89+
std::ptr::write_unaligned(value, 2);
90+
//~^ ERROR assigning to `&T` is undefined behavior
91+
std::ptr::write_volatile(value, 2);
92+
//~^ ERROR assigning to `&T` is undefined behavior
8293

8394
unsafe fn generic_assign_to_ref<T>(this: &T, a: T) {
8495
*(this as *const _ as *mut _) = a;

tests/ui/lint/reference_casting.stderr

+42-5
Original file line numberDiff line numberDiff line change
@@ -131,30 +131,67 @@ LL | *std::mem::transmute::<_, *mut i32>(num) += 1;
131131
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
132132

133133
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
134-
--> $DIR/reference_casting.rs:76:5
134+
--> $DIR/reference_casting.rs:74:5
135+
|
136+
LL | / std::ptr::write(
137+
LL | |
138+
LL | | std::mem::transmute::<*const i32, *mut i32>(num),
139+
LL | | -1i32,
140+
LL | | );
141+
| |_____^
142+
143+
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
144+
--> $DIR/reference_casting.rs:81:5
135145
|
136146
LL | let value = num as *const i32 as *mut i32;
137147
| ----------------------------- casting happend here
138148
LL | *value = 1;
139149
| ^^^^^^^^^^
140150

141151
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
142-
--> $DIR/reference_casting.rs:78:5
152+
--> $DIR/reference_casting.rs:83:5
143153
|
144154
LL | *(num as *const i32).cast::<i32>().cast_mut() = 2;
145155
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
146156

147157
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
148-
--> $DIR/reference_casting.rs:80:5
158+
--> $DIR/reference_casting.rs:85:5
149159
|
150160
LL | *(num as *const _ as usize as *mut i32) = 2;
151161
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
152162

153163
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
154-
--> $DIR/reference_casting.rs:84:9
164+
--> $DIR/reference_casting.rs:87:5
165+
|
166+
LL | let value = num as *const i32 as *mut i32;
167+
| ----------------------------- casting happend here
168+
...
169+
LL | std::ptr::write(value, 2);
170+
| ^^^^^^^^^^^^^^^^^^^^^^^^^
171+
172+
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
173+
--> $DIR/reference_casting.rs:89:5
174+
|
175+
LL | let value = num as *const i32 as *mut i32;
176+
| ----------------------------- casting happend here
177+
...
178+
LL | std::ptr::write_unaligned(value, 2);
179+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
180+
181+
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
182+
--> $DIR/reference_casting.rs:91:5
183+
|
184+
LL | let value = num as *const i32 as *mut i32;
185+
| ----------------------------- casting happend here
186+
...
187+
LL | std::ptr::write_volatile(value, 2);
188+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
189+
190+
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
191+
--> $DIR/reference_casting.rs:95:9
155192
|
156193
LL | *(this as *const _ as *mut _) = a;
157194
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
158195

159-
error: aborting due to 25 previous errors
196+
error: aborting due to 29 previous errors
160197

0 commit comments

Comments
 (0)