Skip to content

Commit 2c73f92

Browse files
committed
Still lint raw pointer to union field if MSRV < 1.92
1 parent 49a0c44 commit 2c73f92

File tree

6 files changed

+136
-60
lines changed

6 files changed

+136
-60
lines changed

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: 54 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
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;
4+
use clippy_utils::{desugar_await, msrvs};
35
use hir::def::{DefKind, Res};
46
use hir::{BlockCheckMode, ExprKind, QPath, UnOp};
57
use rustc_ast::{BorrowKind, Mutability};
@@ -8,7 +10,7 @@ use rustc_hir::intravisit::{Visitor, walk_body, walk_expr};
810
use rustc_lint::{LateContext, LateLintPass};
911
use rustc_middle::hir::nested_filter;
1012
use rustc_middle::ty::{self, TypeckResults};
11-
use rustc_session::declare_lint_pass;
13+
use rustc_session::impl_lint_pass;
1214
use rustc_span::{DesugaringKind, Span};
1315

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

6578
impl<'tcx> LateLintPass<'tcx> for MultipleUnsafeOpsPerBlock {
6679
fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) {
@@ -70,7 +83,7 @@ impl<'tcx> LateLintPass<'tcx> for MultipleUnsafeOpsPerBlock {
7083
{
7184
return;
7285
}
73-
let unsafe_ops = UnsafeExprCollector::collect_unsafe_exprs(cx, block);
86+
let unsafe_ops = UnsafeExprCollector::collect_unsafe_exprs(cx, block, self.msrv);
7487
if unsafe_ops.len() > 1 {
7588
span_lint_and_then(
7689
cx,
@@ -90,18 +103,37 @@ impl<'tcx> LateLintPass<'tcx> for MultipleUnsafeOpsPerBlock {
90103
}
91104
}
92105

106+
#[derive(Clone, Copy)]
107+
enum UnderRawPtr {
108+
/// The expression is not located under a raw pointer
109+
No,
110+
/// The expression is located under a raw pointer, MSRV yet unknown
111+
Yes,
112+
/// The expression is located under a raw pointer and MSRV has been determined.
113+
/// `true` means that taking a raw pointer to a union field is a safe operation.
114+
WithSafeMsrv(bool),
115+
}
116+
93117
struct UnsafeExprCollector<'cx, 'tcx> {
94118
cx: &'cx LateContext<'tcx>,
95119
typeck_results: &'tcx TypeckResults<'tcx>,
120+
msrv: Msrv,
96121
unsafe_ops: Vec<(&'static str, Span)>,
122+
under_raw_ptr: UnderRawPtr,
97123
}
98124

99125
impl<'cx, 'tcx> UnsafeExprCollector<'cx, 'tcx> {
100-
fn collect_unsafe_exprs(cx: &'cx LateContext<'tcx>, block: &'tcx hir::Block<'tcx>) -> Vec<(&'static str, Span)> {
126+
fn collect_unsafe_exprs(
127+
cx: &'cx LateContext<'tcx>,
128+
block: &'tcx hir::Block<'tcx>,
129+
msrv: Msrv,
130+
) -> Vec<(&'static str, Span)> {
101131
let mut collector = Self {
102132
cx,
103133
typeck_results: cx.typeck_results(),
134+
msrv,
104135
unsafe_ops: vec![],
136+
under_raw_ptr: UnderRawPtr::No,
105137
};
106138
collector.visit_block(block);
107139
collector.unsafe_ops
@@ -112,6 +144,11 @@ impl<'tcx> Visitor<'tcx> for UnsafeExprCollector<'_, 'tcx> {
112144
type NestedFilter = nested_filter::OnlyBodies;
113145

114146
fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
147+
// `self.under_raw_ptr` is preventively reset, while the current value is
148+
// preserved in `under_raw_ptr`.
149+
let under_raw_ptr = self.under_raw_ptr;
150+
self.under_raw_ptr = UnderRawPtr::No;
151+
115152
match expr.kind {
116153
// The `await` itself will desugar to two unsafe calls, but we should ignore those.
117154
// Instead, check the expression that is `await`ed
@@ -121,16 +158,22 @@ impl<'tcx> Visitor<'tcx> for UnsafeExprCollector<'_, 'tcx> {
121158

122159
ExprKind::InlineAsm(_) => self.unsafe_ops.push(("inline assembly used here", expr.span)),
123160

124-
ExprKind::AddrOf(BorrowKind::Raw, _, mut inner) => {
125-
while let ExprKind::Field(prefix, _) = inner.kind {
126-
inner = prefix;
127-
}
128-
return self.visit_expr(inner);
161+
ExprKind::AddrOf(BorrowKind::Raw, _, _) => {
162+
self.under_raw_ptr = UnderRawPtr::Yes;
129163
},
130164

131165
ExprKind::Field(e, _) => {
132166
if self.typeck_results.expr_ty(e).is_union() {
133-
self.unsafe_ops.push(("union field access occurs here", expr.span));
167+
// Restore `self.under_raw_pointer` and determine safety of taking a raw pointer to
168+
// a union field if this is not known already.
169+
self.under_raw_ptr = if matches!(under_raw_ptr, UnderRawPtr::Yes) {
170+
UnderRawPtr::WithSafeMsrv(self.msrv.meets(self.cx, msrvs::SAFE_RAW_PTR_TO_UNION_FIELD))
171+
} else {
172+
under_raw_ptr
173+
};
174+
if matches!(self.under_raw_ptr, UnderRawPtr::No | UnderRawPtr::WithSafeMsrv(false)) {
175+
self.unsafe_ops.push(("union field access occurs here", expr.span));
176+
}
134177
}
135178
},
136179

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: 10 additions & 0 deletions
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,
@@ -219,11 +220,20 @@ fn issue16076() {
219220
let u = U { i: 0 };
220221

221222
// 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)