Skip to content

Commit 3a8c121

Browse files
committed
Raw access to an union field was unsafe before Rust 1.92
1 parent a728e87 commit 3a8c121

File tree

7 files changed

+109
-53
lines changed

7 files changed

+109
-53
lines changed

book/src/lint_configuration.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -892,6 +892,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio
892892
* [`mem_replace_option_with_some`](https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_option_with_some)
893893
* [`mem_replace_with_default`](https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default)
894894
* [`missing_const_for_fn`](https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn)
895+
* [`multiple_unsafe_ops_per_block`](https://rust-lang.github.io/rust-clippy/master/index.html#multiple_unsafe_ops_per_block)
895896
* [`needless_borrow`](https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow)
896897
* [`non_std_lazy_statics`](https://rust-lang.github.io/rust-clippy/master/index.html#non_std_lazy_statics)
897898
* [`option_as_ref_deref`](https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref)

clippy_config/src/conf.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -781,6 +781,7 @@ define_Conf! {
781781
mem_replace_option_with_some,
782782
mem_replace_with_default,
783783
missing_const_for_fn,
784+
multiple_unsafe_ops_per_block,
784785
needless_borrow,
785786
non_std_lazy_statics,
786787
option_as_ref_deref,

clippy_lints/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -725,7 +725,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co
725725
store.register_late_pass(move |_| Box::new(semicolon_block::SemicolonBlock::new(conf)));
726726
store.register_late_pass(|_| Box::new(permissions_set_readonly_false::PermissionsSetReadonlyFalse));
727727
store.register_late_pass(|_| Box::new(size_of_ref::SizeOfRef));
728-
store.register_late_pass(|_| Box::new(multiple_unsafe_ops_per_block::MultipleUnsafeOpsPerBlock));
728+
store.register_late_pass(move |_| Box::new(multiple_unsafe_ops_per_block::MultipleUnsafeOpsPerBlock::new(conf)));
729729
store.register_late_pass(move |_| Box::new(extra_unused_type_parameters::ExtraUnusedTypeParameters::new(conf)));
730730
store.register_late_pass(|_| Box::new(no_mangle_with_rust_abi::NoMangleWithRustAbi));
731731
store.register_late_pass(|_| Box::new(collection_is_never_read::CollectionIsNeverRead));

clippy_lints/src/multiple_unsafe_ops_per_block.rs

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
use clippy_utils::desugar_await;
1+
use clippy_config::Conf;
22
use clippy_utils::diagnostics::span_lint_and_then;
3+
use clippy_utils::msrvs::Msrv;
34
use clippy_utils::visitors::{Descend, Visitable, for_each_expr};
5+
use clippy_utils::{desugar_await, msrvs};
46
use core::ops::ControlFlow::Continue;
57
use hir::def::{DefKind, Res};
68
use hir::{BlockCheckMode, ExprKind, QPath, UnOp};
@@ -9,7 +11,7 @@ use rustc_data_structures::fx::FxHashSet;
911
use rustc_hir as hir;
1012
use rustc_lint::{LateContext, LateLintPass};
1113
use rustc_middle::ty;
12-
use rustc_session::declare_lint_pass;
14+
use rustc_session::impl_lint_pass;
1315
use rustc_span::{DesugaringKind, Span};
1416

1517
declare_clippy_lint! {
@@ -61,7 +63,18 @@ declare_clippy_lint! {
6163
restriction,
6264
"more than one unsafe operation per `unsafe` block"
6365
}
64-
declare_lint_pass!(MultipleUnsafeOpsPerBlock => [MULTIPLE_UNSAFE_OPS_PER_BLOCK]);
66+
67+
pub struct MultipleUnsafeOpsPerBlock {
68+
msrv: Msrv,
69+
}
70+
71+
impl_lint_pass!(MultipleUnsafeOpsPerBlock => [MULTIPLE_UNSAFE_OPS_PER_BLOCK]);
72+
73+
impl MultipleUnsafeOpsPerBlock {
74+
pub fn new(conf: &Conf) -> Self {
75+
Self { msrv: conf.msrv }
76+
}
77+
}
6578

6679
impl<'tcx> LateLintPass<'tcx> for MultipleUnsafeOpsPerBlock {
6780
fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) {
@@ -72,7 +85,7 @@ impl<'tcx> LateLintPass<'tcx> for MultipleUnsafeOpsPerBlock {
7285
return;
7386
}
7487
let mut unsafe_ops = vec![];
75-
collect_unsafe_exprs(cx, block, &mut unsafe_ops);
88+
collect_unsafe_exprs(cx, block, &mut unsafe_ops, self.msrv);
7689
if unsafe_ops.len() > 1 {
7790
span_lint_and_then(
7891
cx,
@@ -96,14 +109,15 @@ fn collect_unsafe_exprs<'tcx>(
96109
cx: &LateContext<'tcx>,
97110
node: impl Visitable<'tcx>,
98111
unsafe_ops: &mut Vec<(&'static str, Span)>,
112+
msrv: Msrv,
99113
) {
100114
let mut ignored_field = FxHashSet::default();
101115
for_each_expr(cx, node, |expr| {
102116
match expr.kind {
103117
// The `await` itself will desugar to two unsafe calls, but we should ignore those.
104118
// Instead, check the expression that is `await`ed
105119
_ if let Some(e) = desugar_await(expr) => {
106-
collect_unsafe_exprs(cx, e, unsafe_ops);
120+
collect_unsafe_exprs(cx, e, unsafe_ops, msrv);
107121
return Continue(Descend::No);
108122
},
109123

@@ -113,7 +127,15 @@ fn collect_unsafe_exprs<'tcx>(
113127
// still be checked, it should not trigger if the place is a union field. The prefix of the field
114128
// access should still be checked, so it is necessary to descend.
115129
ExprKind::AddrOf(BorrowKind::Raw, _, mut inner_expr) => {
130+
// Checking current MSRV might be expensive, do it only if necessary
131+
let mut msrv_checked = false;
116132
while let ExprKind::Field(e, _) = inner_expr.kind {
133+
if !msrv_checked {
134+
if !msrv.meets(cx, msrvs::SAFE_RAW_PTR_TO_UNION_FIELD) {
135+
break;
136+
}
137+
msrv_checked = true;
138+
}
117139
ignored_field.insert(inner_expr.hir_id);
118140
inner_expr = e;
119141
}
@@ -186,7 +208,7 @@ fn collect_unsafe_exprs<'tcx>(
186208
))
187209
) {
188210
unsafe_ops.push(("modification of a mutable static occurs here", expr.span));
189-
collect_unsafe_exprs(cx, rhs, unsafe_ops);
211+
collect_unsafe_exprs(cx, rhs, unsafe_ops, msrv);
190212
return Continue(Descend::No);
191213
}
192214
},

clippy_utils/src/msrvs.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ macro_rules! msrv_aliases {
2323

2424
// names may refer to stabilized feature flags or library items
2525
msrv_aliases! {
26+
1,92,0 { SAFE_RAW_PTR_TO_UNION_FIELD }
2627
1,88,0 { LET_CHAINS }
2728
1,87,0 { OS_STR_DISPLAY, INT_MIDPOINT, CONST_CHAR_IS_DIGIT, UNSIGNED_IS_MULTIPLE_OF, INTEGER_SIGN_CAST }
2829
1,85,0 { UINT_FLOAT_MIDPOINT, CONST_SIZE_OF_VAL }

tests/ui/multiple_unsafe_ops_per_block.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//@needs-asm-support
22
//@aux-build:proc_macros.rs
3+
#![feature(stmt_expr_attributes)]
34
#![expect(
45
dropping_copy_types,
56
clippy::unnecessary_operation,
@@ -218,12 +219,21 @@ fn issue16076() {
218219

219220
let u = U { i: 0 };
220221

221-
// Taking a raw pointer to a place is safe
222+
// Taking a raw pointer to a place is safe since Rust 1.92
223+
#[clippy::msrv = "1.92"]
222224
unsafe {
223225
_ = &raw const u.i;
224226
_ = &raw const u.i;
225227
}
226228

229+
// However it was not the case before Rust 1.92
230+
#[clippy::msrv = "1.91"]
231+
unsafe {
232+
//~^ multiple_unsafe_ops_per_block
233+
_ = &raw const u.i;
234+
_ = &raw const u.i;
235+
}
236+
227237
// Taking a reference to a union field is not safe
228238
unsafe {
229239
//~^ multiple_unsafe_ops_per_block

0 commit comments

Comments
 (0)