@@ -34,6 +34,7 @@ pub struct UnsafetyChecker<'a, 'tcx: 'a> {
3434 tcx : TyCtxt < ' a , ' tcx , ' tcx > ,
3535 param_env : ty:: ParamEnv < ' tcx > ,
3636 used_unsafe : FxHashSet < ast:: NodeId > ,
37+ inherited_blocks : Vec < ( ast:: NodeId , bool ) > ,
3738}
3839
3940impl < ' a , ' gcx , ' tcx > UnsafetyChecker < ' a , ' tcx > {
@@ -52,6 +53,7 @@ impl<'a, 'gcx, 'tcx> UnsafetyChecker<'a, 'tcx> {
5253 tcx,
5354 param_env,
5455 used_unsafe : FxHashSet ( ) ,
56+ inherited_blocks : vec ! [ ] ,
5557 }
5658 }
5759}
@@ -124,8 +126,11 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
124126 & AggregateKind :: Adt ( ..) => { }
125127 & AggregateKind :: Closure ( def_id, _) |
126128 & AggregateKind :: Generator ( def_id, _, _) => {
127- let unsafety_violations = self . tcx . unsafety_violations ( def_id) ;
128- self . register_violations ( & unsafety_violations) ;
129+ let UnsafetyCheckResult {
130+ violations, unsafe_blocks
131+ } = self . tcx . unsafety_check_result ( def_id) ;
132+ self . inherited_blocks . extend ( unsafe_blocks. iter ( ) . cloned ( ) ) ;
133+ self . register_violations ( & violations, & unsafe_blocks) ;
129134 }
130135 }
131136 }
@@ -194,7 +199,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
194199 source_info,
195200 description : "use of extern static" ,
196201 lint_node_id : Some ( lint_root)
197- } ] ) ;
202+ } ] , & [ ] ) ;
198203 }
199204 }
200205 }
@@ -227,41 +232,49 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
227232 let source_info = self . source_info ;
228233 self . register_violations ( & [ UnsafetyViolation {
229234 source_info, description, lint_node_id : None
230- } ] ) ;
235+ } ] , & [ ] ) ;
231236 }
232237
233- fn register_violations ( & mut self , violations : & [ UnsafetyViolation ] ) {
234- match self . visibility_scope_info [ self . source_info . scope ] . safety {
238+ fn register_violations ( & mut self ,
239+ violations : & [ UnsafetyViolation ] ,
240+ unsafe_blocks : & [ ( ast:: NodeId , bool ) ] ) {
241+ let within_unsafe = match self . visibility_scope_info [ self . source_info . scope ] . safety {
235242 Safety :: Safe => {
236243 for violation in violations {
237244 if !self . violations . contains ( violation) {
238245 self . violations . push ( violation. clone ( ) )
239246 }
240247 }
248+
249+ false
241250 }
242- Safety :: BuiltinUnsafe | Safety :: FnUnsafe => { }
251+ Safety :: BuiltinUnsafe | Safety :: FnUnsafe => true ,
243252 Safety :: ExplicitUnsafe ( node_id) => {
244253 if !violations. is_empty ( ) {
245254 self . used_unsafe . insert ( node_id) ;
246255 }
256+ true
247257 }
248- }
258+ } ;
259+ self . inherited_blocks . extend ( unsafe_blocks. iter ( ) . map ( |& ( node_id, is_used) | {
260+ ( node_id, is_used && !within_unsafe)
261+ } ) ) ;
249262 }
250263}
251264
252265pub ( crate ) fn provide ( providers : & mut Providers ) {
253266 * providers = Providers {
254- unsafety_violations ,
267+ unsafety_check_result ,
255268 ..* providers
256269 } ;
257270}
258271
259- struct UnusedUnsafeVisitor < ' a , ' tcx : ' a > {
260- tcx : TyCtxt < ' a , ' tcx , ' tcx > ,
261- used_unsafe : FxHashSet < ast:: NodeId >
272+ struct UnusedUnsafeVisitor < ' a > {
273+ used_unsafe : & ' a FxHashSet < ast :: NodeId > ,
274+ unsafe_blocks : & ' a mut Vec < ( ast:: NodeId , bool ) > ,
262275}
263276
264- impl < ' a , ' tcx > hir:: intravisit:: Visitor < ' tcx > for UnusedUnsafeVisitor < ' a , ' tcx > {
277+ impl < ' a , ' tcx > hir:: intravisit:: Visitor < ' tcx > for UnusedUnsafeVisitor < ' a > {
265278 fn nested_visit_map < ' this > ( & ' this mut self ) ->
266279 hir:: intravisit:: NestedVisitorMap < ' this , ' tcx >
267280 {
@@ -272,50 +285,15 @@ impl<'a, 'tcx> hir::intravisit::Visitor<'tcx> for UnusedUnsafeVisitor<'a, 'tcx>
272285 hir:: intravisit:: walk_block ( self , block) ;
273286
274287 if let hir:: UnsafeBlock ( hir:: UserProvided ) = block. rules {
275- if !self . used_unsafe . contains ( & block. id ) {
276- self . report_unused_unsafe ( block) ;
277- }
278- }
279- }
280- }
281-
282- impl < ' a , ' tcx > UnusedUnsafeVisitor < ' a , ' tcx > {
283- /// Return the NodeId for an enclosing scope that is also `unsafe`
284- fn is_enclosed ( & self , id : ast:: NodeId ) -> Option < ( String , ast:: NodeId ) > {
285- let parent_id = self . tcx . hir . get_parent_node ( id) ;
286- if parent_id != id {
287- if self . used_unsafe . contains ( & parent_id) {
288- Some ( ( "block" . to_string ( ) , parent_id) )
289- } else if let Some ( hir:: map:: NodeItem ( & hir:: Item {
290- node : hir:: ItemFn ( _, hir:: Unsafety :: Unsafe , _, _, _, _) ,
291- ..
292- } ) ) = self . tcx . hir . find ( parent_id) {
293- Some ( ( "fn" . to_string ( ) , parent_id) )
294- } else {
295- self . is_enclosed ( parent_id)
296- }
297- } else {
298- None
288+ self . unsafe_blocks . push ( ( block. id , self . used_unsafe . contains ( & block. id ) ) ) ;
299289 }
300290 }
301-
302- fn report_unused_unsafe ( & self , block : & ' tcx hir:: Block ) {
303- let mut db = self . tcx . struct_span_lint_node ( UNUSED_UNSAFE ,
304- block. id ,
305- block. span ,
306- "unnecessary `unsafe` block" ) ;
307- db. span_label ( block. span , "unnecessary `unsafe` block" ) ;
308- if let Some ( ( kind, id) ) = self . is_enclosed ( block. id ) {
309- db. span_note ( self . tcx . hir . span ( id) ,
310- & format ! ( "because it's nested under this `unsafe` {}" , kind) ) ;
311- }
312- db. emit ( ) ;
313- }
314291}
315292
316293fn check_unused_unsafe < ' a , ' tcx > ( tcx : TyCtxt < ' a , ' tcx , ' tcx > ,
317294 def_id : DefId ,
318- used_unsafe : FxHashSet < ast:: NodeId > )
295+ used_unsafe : & FxHashSet < ast:: NodeId > ,
296+ unsafe_blocks : & ' a mut Vec < ( ast:: NodeId , bool ) > )
319297{
320298 let body_id =
321299 tcx. hir . as_local_node_id ( def_id) . and_then ( |node_id| {
@@ -333,13 +311,12 @@ fn check_unused_unsafe<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
333311 debug ! ( "check_unused_unsafe({:?}, body={:?}, used_unsafe={:?})" ,
334312 def_id, body, used_unsafe) ;
335313
336- hir:: intravisit:: Visitor :: visit_body (
337- & mut UnusedUnsafeVisitor { tcx, used_unsafe } ,
338- body) ;
314+ let mut visitor = UnusedUnsafeVisitor { used_unsafe, unsafe_blocks } ;
315+ hir:: intravisit:: Visitor :: visit_body ( & mut visitor, body) ;
339316}
340317
341- fn unsafety_violations < ' a , ' tcx > ( tcx : TyCtxt < ' a , ' tcx , ' tcx > , def_id : DefId ) ->
342- Rc < [ UnsafetyViolation ] >
318+ fn unsafety_check_result < ' a , ' tcx > ( tcx : TyCtxt < ' a , ' tcx , ' tcx > , def_id : DefId )
319+ -> UnsafetyCheckResult
343320{
344321 debug ! ( "unsafety_violations({:?})" , def_id) ;
345322
@@ -351,7 +328,10 @@ fn unsafety_violations<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) ->
351328 ClearOnDecode :: Set ( ref data) => data,
352329 ClearOnDecode :: Clear => {
353330 debug ! ( "unsafety_violations: {:?} - remote, skipping" , def_id) ;
354- return Rc :: new ( [ ] )
331+ return UnsafetyCheckResult {
332+ violations : Rc :: new ( [ ] ) ,
333+ unsafe_blocks : Rc :: new ( [ ] )
334+ }
355335 }
356336 } ;
357337
@@ -360,8 +340,43 @@ fn unsafety_violations<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) ->
360340 mir, visibility_scope_info, tcx, param_env) ;
361341 checker. visit_mir ( mir) ;
362342
363- check_unused_unsafe ( tcx, def_id, checker. used_unsafe ) ;
364- checker. violations . into ( )
343+ check_unused_unsafe ( tcx, def_id, & checker. used_unsafe , & mut checker. inherited_blocks ) ;
344+ UnsafetyCheckResult {
345+ violations : checker. violations . into ( ) ,
346+ unsafe_blocks : checker. inherited_blocks . into ( )
347+ }
348+ }
349+
350+ /// Return the NodeId for an enclosing scope that is also `unsafe`
351+ fn is_enclosed ( tcx : TyCtxt ,
352+ used_unsafe : & FxHashSet < ast:: NodeId > ,
353+ id : ast:: NodeId ) -> Option < ( String , ast:: NodeId ) > {
354+ let parent_id = tcx. hir . get_parent_node ( id) ;
355+ if parent_id != id {
356+ if used_unsafe. contains ( & parent_id) {
357+ Some ( ( "block" . to_string ( ) , parent_id) )
358+ } else if let Some ( hir:: map:: NodeItem ( & hir:: Item {
359+ node : hir:: ItemFn ( _, hir:: Unsafety :: Unsafe , _, _, _, _) ,
360+ ..
361+ } ) ) = tcx. hir . find ( parent_id) {
362+ Some ( ( "fn" . to_string ( ) , parent_id) )
363+ } else {
364+ is_enclosed ( tcx, used_unsafe, parent_id)
365+ }
366+ } else {
367+ None
368+ }
369+ }
370+
371+ fn report_unused_unsafe ( tcx : TyCtxt , used_unsafe : & FxHashSet < ast:: NodeId > , id : ast:: NodeId ) {
372+ let span = tcx. hir . span ( id) ;
373+ let mut db = tcx. struct_span_lint_node ( UNUSED_UNSAFE , id, span, "unnecessary `unsafe` block" ) ;
374+ db. span_label ( span, "unnecessary `unsafe` block" ) ;
375+ if let Some ( ( kind, id) ) = is_enclosed ( tcx, used_unsafe, id) {
376+ db. span_note ( tcx. hir . span ( id) ,
377+ & format ! ( "because it's nested under this `unsafe` {}" , kind) ) ;
378+ }
379+ db. emit ( ) ;
365380}
366381
367382pub fn check_unsafety < ' a , ' tcx > ( tcx : TyCtxt < ' a , ' tcx , ' tcx > , def_id : DefId ) {
@@ -372,9 +387,14 @@ pub fn check_unsafety<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) {
372387 _ => { }
373388 } ;
374389
390+ let UnsafetyCheckResult {
391+ violations,
392+ unsafe_blocks
393+ } = tcx. unsafety_check_result ( def_id) ;
394+
375395 for & UnsafetyViolation {
376396 source_info, description, lint_node_id
377- } in & * tcx . unsafety_violations ( def_id ) {
397+ } in violations . iter ( ) {
378398 // Report an error.
379399 if let Some ( lint_node_id) = lint_node_id {
380400 tcx. lint_node ( SAFE_EXTERN_STATICS ,
@@ -390,4 +410,15 @@ pub fn check_unsafety<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) {
390410 . emit ( ) ;
391411 }
392412 }
413+
414+ let mut unsafe_blocks: Vec < _ > = unsafe_blocks. into_iter ( ) . collect ( ) ;
415+ unsafe_blocks. sort ( ) ;
416+ let used_unsafe: FxHashSet < _ > = unsafe_blocks. iter ( )
417+ . flat_map ( |& & ( id, used) | if used { Some ( id) } else { None } )
418+ . collect ( ) ;
419+ for & ( block_id, is_used) in unsafe_blocks {
420+ if !is_used {
421+ report_unused_unsafe ( tcx, & used_unsafe, block_id) ;
422+ }
423+ }
393424}
0 commit comments