@@ -13,6 +13,7 @@ use rustc_span::def_id::{DefId, LocalDefId};
1313use rustc_span:: symbol:: Symbol ;
1414use rustc_span:: Span ;
1515
16+ use std:: mem;
1617use std:: ops:: Bound ;
1718
1819struct UnsafetyVisitor < ' a , ' tcx > {
@@ -24,7 +25,6 @@ struct UnsafetyVisitor<'a, 'tcx> {
2425 /// The current "safety context". This notably tracks whether we are in an
2526 /// `unsafe` block, and whether it has been used.
2627 safety_context : SafetyContext ,
27- body_unsafety : BodyUnsafety ,
2828 /// The `#[target_feature]` attributes of the body. Used for checking
2929 /// calls to functions with `#[target_feature]` (RFC 2396).
3030 body_target_features : & ' tcx [ Symbol ] ,
@@ -34,43 +34,50 @@ struct UnsafetyVisitor<'a, 'tcx> {
3434 in_union_destructure : bool ,
3535 param_env : ParamEnv < ' tcx > ,
3636 inside_adt : bool ,
37+ warnings : & ' a mut Vec < UnusedUnsafeWarning > ,
3738}
3839
3940impl < ' tcx > UnsafetyVisitor < ' _ , ' tcx > {
4041 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
4549 {
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 ) ;
5752
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+ }
5970
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+ }
7280 }
73- self . safety_context = prev_context;
7481 }
7582 }
7683
@@ -102,18 +109,12 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
102109 }
103110
104111 fn warn_unused_unsafe (
105- & self ,
112+ & mut self ,
106113 hir_id : hir:: HirId ,
107114 block_span : Span ,
108115 enclosing_unsafe : Option < UnusedUnsafeEnclosing > ,
109116 ) {
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 } ) ;
117118 }
118119
119120 /// Whether the `unsafe_op_in_unsafe_fn` lint is `allow`ed at the current HIR node.
@@ -128,7 +129,14 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
128129 self . tcx . ensure_with_value ( ) . mir_built ( def) ;
129130 let inner_thir = & inner_thir. steal ( ) ;
130131 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+ } ;
132140 inner_visitor. visit_expr ( & inner_thir[ expr] ) ;
133141 // Unsafe blocks can be used in the inner body, make sure to take it into account
134142 self . safety_context = inner_visitor. safety_context ;
@@ -195,8 +203,15 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
195203 } ) ;
196204 }
197205 BlockSafety :: ExplicitUnsafe ( hir_id) => {
206+ let used =
207+ matches ! ( self . tcx. lint_level_at_node( UNUSED_UNSAFE , hir_id) , ( Level :: Allow , _) ) ;
198208 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+ } ,
200215 |this| visit:: walk_block ( this, block) ,
201216 ) ;
202217 }
@@ -481,36 +496,29 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
481496 }
482497}
483498
484- #[ derive( Clone , Copy ) ]
499+ #[ derive( Clone ) ]
485500enum SafetyContext {
486501 Safe ,
487502 BuiltinUnsafeBlock ,
488503 UnsafeFn ,
489- UnsafeBlock { span : Span , hir_id : hir:: HirId , used : bool } ,
504+ UnsafeBlock {
505+ span : Span ,
506+ hir_id : hir:: HirId ,
507+ used : bool ,
508+ nested_used_blocks : Vec < NestedUsedBlock > ,
509+ } ,
490510}
491511
492512#[ derive( Clone , Copy ) ]
493- enum BodyUnsafety {
494- /// The body is not unsafe.
495- Safe ,
496- /// The body is an unsafe function. The span points to
497- /// the signature of the function.
498- Unsafe ( Span ) ,
513+ struct NestedUsedBlock {
514+ hir_id : hir:: HirId ,
515+ span : Span ,
499516}
500517
501- impl BodyUnsafety {
502- /// Returns whether the body is unsafe.
503- fn is_unsafe ( & self ) -> bool {
504- matches ! ( self , BodyUnsafety :: Unsafe ( _) )
505- }
506-
507- /// If the body is unsafe, returns the `Span` of its signature.
508- fn unsafe_fn_sig_span ( self ) -> Option < Span > {
509- match self {
510- BodyUnsafety :: Unsafe ( span) => Some ( span) ,
511- BodyUnsafety :: Safe => None ,
512- }
513- }
518+ struct UnusedUnsafeWarning {
519+ hir_id : hir:: HirId ,
520+ block_span : Span ,
521+ enclosing_unsafe : Option < UnusedUnsafeEnclosing > ,
514522}
515523
516524#[ derive( Clone , Copy , PartialEq ) ]
@@ -803,27 +811,37 @@ pub fn thir_check_unsafety(tcx: TyCtxt<'_>, def: LocalDefId) {
803811 }
804812
805813 let hir_id = tcx. hir ( ) . local_def_id_to_hir_id ( def) ;
806- let body_unsafety = tcx. hir ( ) . fn_sig_by_hir_id ( hir_id) . map_or ( BodyUnsafety :: Safe , |fn_sig| {
814+ let safety_context = tcx. hir ( ) . fn_sig_by_hir_id ( hir_id) . map_or ( SafetyContext :: Safe , |fn_sig| {
807815 if fn_sig. header . unsafety == hir:: Unsafety :: Unsafe {
808- BodyUnsafety :: Unsafe ( fn_sig . span )
816+ SafetyContext :: UnsafeFn
809817 } else {
810- BodyUnsafety :: Safe
818+ SafetyContext :: Safe
811819 }
812820 } ) ;
813821 let body_target_features = & tcx. body_codegen_attrs ( def. to_def_id ( ) ) . target_features ;
814- let safety_context =
815- if body_unsafety. is_unsafe ( ) { SafetyContext :: UnsafeFn } else { SafetyContext :: Safe } ;
822+ let mut warnings = Vec :: new ( ) ;
816823 let mut visitor = UnsafetyVisitor {
817824 tcx,
818825 thir,
819826 safety_context,
820827 hir_context : hir_id,
821- body_unsafety,
822828 body_target_features,
823829 assignment_info : None ,
824830 in_union_destructure : false ,
825831 param_env : tcx. param_env ( def) ,
826832 inside_adt : false ,
833+ warnings : & mut warnings,
827834 } ;
828835 visitor. visit_expr ( & thir[ expr] ) ;
836+
837+ warnings. sort_by_key ( |w| w. block_span ) ;
838+ for UnusedUnsafeWarning { hir_id, block_span, enclosing_unsafe } in warnings {
839+ let block_span = tcx. sess . source_map ( ) . guess_head_span ( block_span) ;
840+ tcx. emit_spanned_lint (
841+ UNUSED_UNSAFE ,
842+ hir_id,
843+ block_span,
844+ UnusedUnsafe { span : block_span, enclosing : enclosing_unsafe } ,
845+ ) ;
846+ }
829847}
0 commit comments