@@ -13,6 +13,7 @@ use rustc_span::def_id::{DefId, LocalDefId};
13
13
use rustc_span:: symbol:: Symbol ;
14
14
use rustc_span:: Span ;
15
15
16
+ use std:: mem;
16
17
use std:: ops:: Bound ;
17
18
18
19
struct UnsafetyVisitor < ' a , ' tcx > {
@@ -24,7 +25,6 @@ struct UnsafetyVisitor<'a, 'tcx> {
24
25
/// The current "safety context". This notably tracks whether we are in an
25
26
/// `unsafe` block, and whether it has been used.
26
27
safety_context : SafetyContext ,
27
- body_unsafety : BodyUnsafety ,
28
28
/// The `#[target_feature]` attributes of the body. Used for checking
29
29
/// calls to functions with `#[target_feature]` (RFC 2396).
30
30
body_target_features : & ' tcx [ Symbol ] ,
@@ -34,43 +34,50 @@ struct UnsafetyVisitor<'a, 'tcx> {
34
34
in_union_destructure : bool ,
35
35
param_env : ParamEnv < ' tcx > ,
36
36
inside_adt : bool ,
37
+ warnings : & ' a mut Vec < UnusedUnsafeWarning > ,
37
38
}
38
39
39
40
impl < ' tcx > UnsafetyVisitor < ' _ , ' tcx > {
40
41
fn in_safety_context ( & mut self , safety_context : SafetyContext , f : impl FnOnce ( & mut Self ) ) {
41
- if let (
42
- SafetyContext :: UnsafeBlock { span : enclosing_span, .. } ,
43
- SafetyContext :: UnsafeBlock { span : block_span, hir_id, .. } ,
44
- ) = ( self . safety_context , safety_context)
42
+ let prev_context = mem:: replace ( & mut self . safety_context , safety_context) ;
43
+
44
+ f ( self ) ;
45
+
46
+ let safety_context = mem:: replace ( & mut self . safety_context , prev_context) ;
47
+ if let SafetyContext :: UnsafeBlock { used, span, hir_id, nested_used_blocks } =
48
+ safety_context
45
49
{
46
- self . warn_unused_unsafe (
47
- hir_id,
48
- block_span,
49
- Some ( UnusedUnsafeEnclosing :: Block {
50
- span : self . tcx . sess . source_map ( ) . guess_head_span ( enclosing_span) ,
51
- } ) ,
52
- ) ;
53
- f ( self ) ;
54
- } else {
55
- let prev_context = self . safety_context ;
56
- self . safety_context = safety_context;
50
+ if !used {
51
+ self . warn_unused_unsafe ( hir_id, span, None ) ;
57
52
58
- f ( self ) ;
53
+ if let SafetyContext :: UnsafeBlock {
54
+ nested_used_blocks : ref mut prev_nested_used_blocks,
55
+ ..
56
+ } = self . safety_context
57
+ {
58
+ prev_nested_used_blocks. extend ( nested_used_blocks) ;
59
+ }
60
+ } else {
61
+ for block in nested_used_blocks {
62
+ self . warn_unused_unsafe (
63
+ block. hir_id ,
64
+ block. span ,
65
+ Some ( UnusedUnsafeEnclosing :: Block {
66
+ span : self . tcx . sess . source_map ( ) . guess_head_span ( span) ,
67
+ } ) ,
68
+ ) ;
69
+ }
59
70
60
- if let SafetyContext :: UnsafeBlock { used : false , span, hir_id } = self . safety_context {
61
- self . warn_unused_unsafe (
62
- hir_id,
63
- span,
64
- if self . unsafe_op_in_unsafe_fn_allowed ( ) {
65
- self . body_unsafety
66
- . unsafe_fn_sig_span ( )
67
- . map ( |span| UnusedUnsafeEnclosing :: Function { span } )
68
- } else {
69
- None
70
- } ,
71
- ) ;
71
+ match self . safety_context {
72
+ SafetyContext :: UnsafeBlock {
73
+ nested_used_blocks : ref mut prev_nested_used_blocks,
74
+ ..
75
+ } => {
76
+ prev_nested_used_blocks. push ( NestedUsedBlock { hir_id, span } ) ;
77
+ }
78
+ _ => ( ) ,
79
+ }
72
80
}
73
- self . safety_context = prev_context;
74
81
}
75
82
}
76
83
@@ -102,18 +109,12 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
102
109
}
103
110
104
111
fn warn_unused_unsafe (
105
- & self ,
112
+ & mut self ,
106
113
hir_id : hir:: HirId ,
107
114
block_span : Span ,
108
115
enclosing_unsafe : Option < UnusedUnsafeEnclosing > ,
109
116
) {
110
- let block_span = self . tcx . sess . source_map ( ) . guess_head_span ( block_span) ;
111
- self . tcx . emit_spanned_lint (
112
- UNUSED_UNSAFE ,
113
- hir_id,
114
- block_span,
115
- UnusedUnsafe { span : block_span, enclosing : enclosing_unsafe } ,
116
- ) ;
117
+ self . warnings . push ( UnusedUnsafeWarning { hir_id, block_span, enclosing_unsafe } ) ;
117
118
}
118
119
119
120
/// Whether the `unsafe_op_in_unsafe_fn` lint is `allow`ed at the current HIR node.
@@ -128,7 +129,14 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
128
129
self . tcx . ensure_with_value ( ) . mir_built ( def) ;
129
130
let inner_thir = & inner_thir. steal ( ) ;
130
131
let hir_context = self . tcx . hir ( ) . local_def_id_to_hir_id ( def) ;
131
- let mut inner_visitor = UnsafetyVisitor { thir : inner_thir, hir_context, ..* self } ;
132
+ let safety_context = mem:: replace ( & mut self . safety_context , SafetyContext :: Safe ) ;
133
+ let mut inner_visitor = UnsafetyVisitor {
134
+ thir : inner_thir,
135
+ hir_context,
136
+ safety_context,
137
+ warnings : self . warnings ,
138
+ ..* self
139
+ } ;
132
140
inner_visitor. visit_expr ( & inner_thir[ expr] ) ;
133
141
// Unsafe blocks can be used in the inner body, make sure to take it into account
134
142
self . safety_context = inner_visitor. safety_context ;
@@ -195,8 +203,15 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
195
203
} ) ;
196
204
}
197
205
BlockSafety :: ExplicitUnsafe ( hir_id) => {
206
+ let used =
207
+ matches ! ( self . tcx. lint_level_at_node( UNUSED_UNSAFE , hir_id) , ( Level :: Allow , _) ) ;
198
208
self . in_safety_context (
199
- SafetyContext :: UnsafeBlock { span : block. span , hir_id, used : false } ,
209
+ SafetyContext :: UnsafeBlock {
210
+ span : block. span ,
211
+ hir_id,
212
+ used,
213
+ nested_used_blocks : Vec :: new ( ) ,
214
+ } ,
200
215
|this| visit:: walk_block ( this, block) ,
201
216
) ;
202
217
}
@@ -483,36 +498,29 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
483
498
}
484
499
}
485
500
486
- #[ derive( Clone , Copy ) ]
501
+ #[ derive( Clone ) ]
487
502
enum SafetyContext {
488
503
Safe ,
489
504
BuiltinUnsafeBlock ,
490
505
UnsafeFn ,
491
- UnsafeBlock { span : Span , hir_id : hir:: HirId , used : bool } ,
506
+ UnsafeBlock {
507
+ span : Span ,
508
+ hir_id : hir:: HirId ,
509
+ used : bool ,
510
+ nested_used_blocks : Vec < NestedUsedBlock > ,
511
+ } ,
492
512
}
493
513
494
514
#[ derive( Clone , Copy ) ]
495
- enum BodyUnsafety {
496
- /// The body is not unsafe.
497
- Safe ,
498
- /// The body is an unsafe function. The span points to
499
- /// the signature of the function.
500
- Unsafe ( Span ) ,
515
+ struct NestedUsedBlock {
516
+ hir_id : hir:: HirId ,
517
+ span : Span ,
501
518
}
502
519
503
- impl BodyUnsafety {
504
- /// Returns whether the body is unsafe.
505
- fn is_unsafe ( & self ) -> bool {
506
- matches ! ( self , BodyUnsafety :: Unsafe ( _) )
507
- }
508
-
509
- /// If the body is unsafe, returns the `Span` of its signature.
510
- fn unsafe_fn_sig_span ( self ) -> Option < Span > {
511
- match self {
512
- BodyUnsafety :: Unsafe ( span) => Some ( span) ,
513
- BodyUnsafety :: Safe => None ,
514
- }
515
- }
520
+ struct UnusedUnsafeWarning {
521
+ hir_id : hir:: HirId ,
522
+ block_span : Span ,
523
+ enclosing_unsafe : Option < UnusedUnsafeEnclosing > ,
516
524
}
517
525
518
526
#[ derive( Clone , Copy , PartialEq ) ]
@@ -805,27 +813,37 @@ pub fn thir_check_unsafety(tcx: TyCtxt<'_>, def: LocalDefId) {
805
813
}
806
814
807
815
let hir_id = tcx. hir ( ) . local_def_id_to_hir_id ( def) ;
808
- let body_unsafety = tcx. hir ( ) . fn_sig_by_hir_id ( hir_id) . map_or ( BodyUnsafety :: Safe , |fn_sig| {
816
+ let safety_context = tcx. hir ( ) . fn_sig_by_hir_id ( hir_id) . map_or ( SafetyContext :: Safe , |fn_sig| {
809
817
if fn_sig. header . unsafety == hir:: Unsafety :: Unsafe {
810
- BodyUnsafety :: Unsafe ( fn_sig . span )
818
+ SafetyContext :: UnsafeFn
811
819
} else {
812
- BodyUnsafety :: Safe
820
+ SafetyContext :: Safe
813
821
}
814
822
} ) ;
815
823
let body_target_features = & tcx. body_codegen_attrs ( def. to_def_id ( ) ) . target_features ;
816
- let safety_context =
817
- if body_unsafety. is_unsafe ( ) { SafetyContext :: UnsafeFn } else { SafetyContext :: Safe } ;
824
+ let mut warnings = Vec :: new ( ) ;
818
825
let mut visitor = UnsafetyVisitor {
819
826
tcx,
820
827
thir,
821
828
safety_context,
822
829
hir_context : hir_id,
823
- body_unsafety,
824
830
body_target_features,
825
831
assignment_info : None ,
826
832
in_union_destructure : false ,
827
833
param_env : tcx. param_env ( def) ,
828
834
inside_adt : false ,
835
+ warnings : & mut warnings,
829
836
} ;
830
837
visitor. visit_expr ( & thir[ expr] ) ;
838
+
839
+ warnings. sort_by_key ( |w| w. block_span ) ;
840
+ for UnusedUnsafeWarning { hir_id, block_span, enclosing_unsafe } in warnings {
841
+ let block_span = tcx. sess . source_map ( ) . guess_head_span ( block_span) ;
842
+ tcx. emit_spanned_lint (
843
+ UNUSED_UNSAFE ,
844
+ hir_id,
845
+ block_span,
846
+ UnusedUnsafe { span : block_span, enclosing : enclosing_unsafe } ,
847
+ ) ;
848
+ }
831
849
}
0 commit comments