1- use clippy_utils :: desugar_await ;
1+ use clippy_config :: Conf ;
22use clippy_utils:: diagnostics:: span_lint_and_then;
3+ use clippy_utils:: msrvs:: Msrv ;
4+ use clippy_utils:: { desugar_await, msrvs} ;
35use hir:: def:: { DefKind , Res } ;
46use hir:: { BlockCheckMode , ExprKind , QPath , UnOp } ;
57use rustc_ast:: { BorrowKind , Mutability } ;
68use rustc_hir as hir;
79use rustc_hir:: intravisit:: { Visitor , walk_body, walk_expr} ;
810use rustc_lint:: { LateContext , LateLintPass } ;
911use rustc_middle:: hir:: nested_filter;
10- use rustc_middle:: ty:: { self , TyCtxt , TypeckResults } ;
11- use rustc_session:: declare_lint_pass ;
12+ use rustc_middle:: ty:: { self , TypeckResults } ;
13+ use rustc_session:: impl_lint_pass ;
1214use rustc_span:: { DesugaringKind , Span } ;
1315
1416declare_clippy_lint ! {
@@ -55,19 +57,23 @@ declare_clippy_lint! {
5557 /// unsafe { char::from_u32_unchecked(int_value) }
5658 /// }
5759 /// ```
58- ///
59- /// ### Note
60- ///
61- /// Taking a raw pointer to a union field is always safe and will
62- /// not be considered unsafe by this lint, even when linting code written
63- /// with a specified Rust version of 1.91 or earlier (which required
64- /// using an `unsafe` block).
6560 #[ clippy:: version = "1.69.0" ]
6661 pub MULTIPLE_UNSAFE_OPS_PER_BLOCK ,
6762 restriction,
6863 "more than one unsafe operation per `unsafe` block"
6964}
70- 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+ }
7177
7278impl < ' tcx > LateLintPass < ' tcx > for MultipleUnsafeOpsPerBlock {
7379 fn check_block ( & mut self , cx : & LateContext < ' tcx > , block : & ' tcx hir:: Block < ' _ > ) {
@@ -77,7 +83,7 @@ impl<'tcx> LateLintPass<'tcx> for MultipleUnsafeOpsPerBlock {
7783 {
7884 return ;
7985 }
80- let unsafe_ops = UnsafeExprCollector :: collect_unsafe_exprs ( cx, block) ;
86+ let unsafe_ops = UnsafeExprCollector :: collect_unsafe_exprs ( cx, block, self . msrv ) ;
8187 if unsafe_ops. len ( ) > 1 {
8288 span_lint_and_then (
8389 cx,
@@ -97,28 +103,52 @@ impl<'tcx> LateLintPass<'tcx> for MultipleUnsafeOpsPerBlock {
97103 }
98104}
99105
100- struct UnsafeExprCollector < ' tcx > {
101- tcx : TyCtxt < ' tcx > ,
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+
117+ struct UnsafeExprCollector < ' cx , ' tcx > {
118+ cx : & ' cx LateContext < ' tcx > ,
102119 typeck_results : & ' tcx TypeckResults < ' tcx > ,
120+ msrv : Msrv ,
103121 unsafe_ops : Vec < ( & ' static str , Span ) > ,
122+ under_raw_ptr : UnderRawPtr ,
104123}
105124
106- impl < ' tcx > UnsafeExprCollector < ' tcx > {
107- fn collect_unsafe_exprs ( cx : & LateContext < ' tcx > , block : & ' tcx hir:: Block < ' tcx > ) -> Vec < ( & ' static str , Span ) > {
125+ impl < ' cx , ' tcx > UnsafeExprCollector < ' cx , ' tcx > {
126+ fn collect_unsafe_exprs (
127+ cx : & ' cx LateContext < ' tcx > ,
128+ block : & ' tcx hir:: Block < ' tcx > ,
129+ msrv : Msrv ,
130+ ) -> Vec < ( & ' static str , Span ) > {
108131 let mut collector = Self {
109- tcx : cx . tcx ,
132+ cx ,
110133 typeck_results : cx. typeck_results ( ) ,
134+ msrv,
111135 unsafe_ops : vec ! [ ] ,
136+ under_raw_ptr : UnderRawPtr :: No ,
112137 } ;
113138 collector. visit_block ( block) ;
114139 collector. unsafe_ops
115140 }
116141}
117142
118- impl < ' tcx > Visitor < ' tcx > for UnsafeExprCollector < ' tcx > {
143+ impl < ' tcx > Visitor < ' tcx > for UnsafeExprCollector < ' _ , ' tcx > {
119144 type NestedFilter = nested_filter:: OnlyBodies ;
120145
121146 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+
122152 match expr. kind {
123153 // The `await` itself will desugar to two unsafe calls, but we should ignore those.
124154 // Instead, check the expression that is `await`ed
@@ -128,18 +158,22 @@ impl<'tcx> Visitor<'tcx> for UnsafeExprCollector<'tcx> {
128158
129159 ExprKind :: InlineAsm ( _) => self . unsafe_ops . push ( ( "inline assembly used here" , expr. span ) ) ,
130160
131- ExprKind :: AddrOf ( BorrowKind :: Raw , _, mut inner) => {
132- while let ExprKind :: Field ( prefix, _) = inner. kind
133- && self . typeck_results . expr_adjustments ( prefix) . is_empty ( )
134- {
135- inner = prefix;
136- }
137- return self . visit_expr ( inner) ;
161+ ExprKind :: AddrOf ( BorrowKind :: Raw , _, _) => {
162+ self . under_raw_ptr = UnderRawPtr :: Yes ;
138163 } ,
139164
140- ExprKind :: Field ( e, _) => {
165+ ExprKind :: Field ( e, _) if self . typeck_results . expr_adjustments ( e) . is_empty ( ) => {
166+ // Restore `self.under_raw_pointer` and determine safety of taking a raw pointer to
167+ // a union field if this is not known already.
168+ self . under_raw_ptr = if matches ! ( under_raw_ptr, UnderRawPtr :: Yes ) {
169+ UnderRawPtr :: WithSafeMsrv ( self . msrv . meets ( self . cx , msrvs:: SAFE_RAW_PTR_TO_UNION_FIELD ) )
170+ } else {
171+ under_raw_ptr
172+ } ;
141173 if self . typeck_results . expr_ty ( e) . is_union ( ) {
142- self . unsafe_ops . push ( ( "union field access occurs here" , expr. span ) ) ;
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+ }
143177 }
144178 } ,
145179
@@ -167,7 +201,7 @@ impl<'tcx> Visitor<'tcx> for UnsafeExprCollector<'tcx> {
167201
168202 ExprKind :: Call ( path_expr, _) => {
169203 let opt_sig = match * self . typeck_results . expr_ty_adjusted ( path_expr) . kind ( ) {
170- ty:: FnDef ( id, _) => Some ( self . tcx . fn_sig ( id) . skip_binder ( ) ) ,
204+ ty:: FnDef ( id, _) => Some ( self . cx . tcx . fn_sig ( id) . skip_binder ( ) ) ,
171205 ty:: FnPtr ( sig_tys, hdr) => Some ( sig_tys. with ( hdr) ) ,
172206 _ => None ,
173207 } ;
@@ -180,7 +214,7 @@ impl<'tcx> Visitor<'tcx> for UnsafeExprCollector<'tcx> {
180214 let opt_sig = self
181215 . typeck_results
182216 . type_dependent_def_id ( expr. hir_id )
183- . map ( |def_id| self . tcx . fn_sig ( def_id) ) ;
217+ . map ( |def_id| self . cx . tcx . fn_sig ( def_id) ) ;
184218 if opt_sig. is_some_and ( |sig| sig. skip_binder ( ) . safety ( ) . is_unsafe ( ) ) {
185219 self . unsafe_ops . push ( ( "unsafe method call occurs here" , expr. span ) ) ;
186220 }
@@ -217,12 +251,12 @@ impl<'tcx> Visitor<'tcx> for UnsafeExprCollector<'tcx> {
217251
218252 fn visit_body ( & mut self , body : & hir:: Body < ' tcx > ) {
219253 let saved_typeck_results = self . typeck_results ;
220- self . typeck_results = self . tcx . typeck_body ( body. id ( ) ) ;
254+ self . typeck_results = self . cx . tcx . typeck_body ( body. id ( ) ) ;
221255 walk_body ( self , body) ;
222256 self . typeck_results = saved_typeck_results;
223257 }
224258
225259 fn maybe_tcx ( & mut self ) -> Self :: MaybeTyCtxt {
226- self . tcx
260+ self . cx . tcx
227261 }
228262}
0 commit comments