Skip to content

Commit a548e5a

Browse files
committed
Taking a raw pointer on a union field is a safe operation
1 parent d599529 commit a548e5a

File tree

3 files changed

+84
-3
lines changed

3 files changed

+84
-3
lines changed

clippy_lints/src/multiple_unsafe_ops_per_block.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ use clippy_utils::visitors::{Descend, Visitable, for_each_expr};
44
use core::ops::ControlFlow::Continue;
55
use hir::def::{DefKind, Res};
66
use hir::{BlockCheckMode, ExprKind, QPath, UnOp};
7-
use rustc_ast::Mutability;
7+
use rustc_ast::{BorrowKind, Mutability};
8+
use rustc_data_structures::fx::FxHashSet;
89
use rustc_hir as hir;
910
use rustc_lint::{LateContext, LateLintPass};
1011
use rustc_middle::ty;
@@ -96,6 +97,7 @@ fn collect_unsafe_exprs<'tcx>(
9697
node: impl Visitable<'tcx>,
9798
unsafe_ops: &mut Vec<(&'static str, Span)>,
9899
) {
100+
let mut ignored_union_field = FxHashSet::default();
99101
for_each_expr(cx, node, |expr| {
100102
match expr.kind {
101103
// The `await` itself will desugar to two unsafe calls, but we should ignore those.
@@ -107,8 +109,15 @@ fn collect_unsafe_exprs<'tcx>(
107109

108110
ExprKind::InlineAsm(_) => unsafe_ops.push(("inline assembly used here", expr.span)),
109111

112+
// Taking a raw reference on a place is a safe operation. While the inner expression should
113+
// still be checked, it should not trigger if the place is a union field. The prefix of the field
114+
// access should still be checked, so it is necessary to descend.
115+
ExprKind::AddrOf(BorrowKind::Raw, _, inner_expr) if matches!(inner_expr.kind, ExprKind::Field(..)) => {
116+
ignored_union_field.insert(inner_expr.hir_id);
117+
},
118+
110119
ExprKind::Field(e, _) => {
111-
if cx.typeck_results().expr_ty(e).is_union() {
120+
if cx.typeck_results().expr_ty(e).is_union() && !ignored_union_field.contains(&expr.hir_id) {
112121
unsafe_ops.push(("union field access occurs here", expr.span));
113122
}
114123
},

tests/ui/multiple_unsafe_ops_per_block.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,4 +209,34 @@ async fn issue13879() {
209209
}
210210
}
211211

212+
fn issue16076() {
213+
union U {
214+
i: u32,
215+
f: f32,
216+
}
217+
218+
let u = U { i: 0 };
219+
220+
// Taking a raw pointer to a place is safe
221+
unsafe {
222+
_ = &raw const u.i;
223+
_ = &raw const u.i;
224+
}
225+
226+
// Taking a reference to a union field is not safe
227+
unsafe {
228+
//~^ multiple_unsafe_ops_per_block
229+
_ = &u.i;
230+
_ = &u.i;
231+
}
232+
233+
// Check that we still check and lint the prefix of the raw pointer to a field access
234+
#[expect(clippy::deref_addrof)]
235+
unsafe {
236+
//~^ multiple_unsafe_ops_per_block
237+
_ = &raw const (*&raw const u).i;
238+
_ = &raw const (*&raw const u).i;
239+
}
240+
}
241+
212242
fn main() {}

tests/ui/multiple_unsafe_ops_per_block.stderr

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,5 +255,47 @@ note: unsafe function call occurs here
255255
LL | Some(foo_unchecked()).unwrap_unchecked().await;
256256
| ^^^^^^^^^^^^^^^
257257

258-
error: aborting due to 11 previous errors
258+
error: this `unsafe` block contains 2 unsafe operations, expected only one
259+
--> tests/ui/multiple_unsafe_ops_per_block.rs:227:5
260+
|
261+
LL | / unsafe {
262+
LL | |
263+
LL | | _ = &u.i;
264+
LL | | _ = &u.i;
265+
LL | | }
266+
| |_____^
267+
|
268+
note: union field access occurs here
269+
--> tests/ui/multiple_unsafe_ops_per_block.rs:229:14
270+
|
271+
LL | _ = &u.i;
272+
| ^^^
273+
note: union field access occurs here
274+
--> tests/ui/multiple_unsafe_ops_per_block.rs:230:14
275+
|
276+
LL | _ = &u.i;
277+
| ^^^
278+
279+
error: this `unsafe` block contains 2 unsafe operations, expected only one
280+
--> tests/ui/multiple_unsafe_ops_per_block.rs:235:5
281+
|
282+
LL | / unsafe {
283+
LL | |
284+
LL | | _ = &raw const (*&raw const u).i;
285+
LL | | _ = &raw const (*&raw const u).i;
286+
LL | | }
287+
| |_____^
288+
|
289+
note: raw pointer dereference occurs here
290+
--> tests/ui/multiple_unsafe_ops_per_block.rs:237:24
291+
|
292+
LL | _ = &raw const (*&raw const u).i;
293+
| ^^^^^^^^^^^^^^^
294+
note: raw pointer dereference occurs here
295+
--> tests/ui/multiple_unsafe_ops_per_block.rs:238:24
296+
|
297+
LL | _ = &raw const (*&raw const u).i;
298+
| ^^^^^^^^^^^^^^^
299+
300+
error: aborting due to 13 previous errors
259301

0 commit comments

Comments
 (0)