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 } ;
@@ -8,7 +10,7 @@ use rustc_hir::intravisit::{Visitor, walk_body, walk_expr};
810use rustc_lint:: { LateContext , LateLintPass } ;
911use rustc_middle:: hir:: nested_filter;
1012use rustc_middle:: ty:: { self , TypeckResults } ;
11- use rustc_session:: declare_lint_pass ;
13+ use rustc_session:: impl_lint_pass ;
1214use rustc_span:: { DesugaringKind , Span } ;
1315
1416declare_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
6578impl < ' 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+
93117struct 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
99125impl < ' 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
0 commit comments