@@ -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 }
@@ -483,36 +498,29 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
483498 }
484499}
485500
486- #[ derive( Clone , Copy ) ]
501+ #[ derive( Clone ) ]
487502enum SafetyContext {
488503 Safe ,
489504 BuiltinUnsafeBlock ,
490505 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+ } ,
492512}
493513
494514#[ 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 ,
501518}
502519
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 > ,
516524}
517525
518526#[ derive( Clone , Copy , PartialEq ) ]
@@ -805,27 +813,37 @@ pub fn thir_check_unsafety(tcx: TyCtxt<'_>, def: LocalDefId) {
805813 }
806814
807815 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| {
809817 if fn_sig. header . unsafety == hir:: Unsafety :: Unsafe {
810- BodyUnsafety :: Unsafe ( fn_sig . span )
818+ SafetyContext :: UnsafeFn
811819 } else {
812- BodyUnsafety :: Safe
820+ SafetyContext :: Safe
813821 }
814822 } ) ;
815823 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 ( ) ;
818825 let mut visitor = UnsafetyVisitor {
819826 tcx,
820827 thir,
821828 safety_context,
822829 hir_context : hir_id,
823- body_unsafety,
824830 body_target_features,
825831 assignment_info : None ,
826832 in_union_destructure : false ,
827833 param_env : tcx. param_env ( def) ,
828834 inside_adt : false ,
835+ warnings : & mut warnings,
829836 } ;
830837 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+ }
831849}
0 commit comments