@@ -34,6 +34,7 @@ pub struct UnsafetyChecker<'a, 'tcx: 'a> {
34
34
tcx : TyCtxt < ' a , ' tcx , ' tcx > ,
35
35
param_env : ty:: ParamEnv < ' tcx > ,
36
36
used_unsafe : FxHashSet < ast:: NodeId > ,
37
+ inherited_blocks : Vec < ( ast:: NodeId , bool ) > ,
37
38
}
38
39
39
40
impl < ' a , ' gcx , ' tcx > UnsafetyChecker < ' a , ' tcx > {
@@ -52,6 +53,7 @@ impl<'a, 'gcx, 'tcx> UnsafetyChecker<'a, 'tcx> {
52
53
tcx,
53
54
param_env,
54
55
used_unsafe : FxHashSet ( ) ,
56
+ inherited_blocks : vec ! [ ] ,
55
57
}
56
58
}
57
59
}
@@ -124,8 +126,11 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
124
126
& AggregateKind :: Adt ( ..) => { }
125
127
& AggregateKind :: Closure ( def_id, _) |
126
128
& 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) ;
129
134
}
130
135
}
131
136
}
@@ -194,7 +199,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
194
199
source_info,
195
200
description : "use of extern static" ,
196
201
lint_node_id : Some ( lint_root)
197
- } ] ) ;
202
+ } ] , & [ ] ) ;
198
203
}
199
204
}
200
205
}
@@ -227,41 +232,49 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
227
232
let source_info = self . source_info ;
228
233
self . register_violations ( & [ UnsafetyViolation {
229
234
source_info, description, lint_node_id : None
230
- } ] ) ;
235
+ } ] , & [ ] ) ;
231
236
}
232
237
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 {
235
242
Safety :: Safe => {
236
243
for violation in violations {
237
244
if !self . violations . contains ( violation) {
238
245
self . violations . push ( violation. clone ( ) )
239
246
}
240
247
}
248
+
249
+ false
241
250
}
242
- Safety :: BuiltinUnsafe | Safety :: FnUnsafe => { }
251
+ Safety :: BuiltinUnsafe | Safety :: FnUnsafe => true ,
243
252
Safety :: ExplicitUnsafe ( node_id) => {
244
253
if !violations. is_empty ( ) {
245
254
self . used_unsafe . insert ( node_id) ;
246
255
}
256
+ true
247
257
}
248
- }
258
+ } ;
259
+ self . inherited_blocks . extend ( unsafe_blocks. iter ( ) . map ( |& ( node_id, is_used) | {
260
+ ( node_id, is_used && !within_unsafe)
261
+ } ) ) ;
249
262
}
250
263
}
251
264
252
265
pub ( crate ) fn provide ( providers : & mut Providers ) {
253
266
* providers = Providers {
254
- unsafety_violations ,
267
+ unsafety_check_result ,
255
268
..* providers
256
269
} ;
257
270
}
258
271
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 ) > ,
262
275
}
263
276
264
- impl < ' a , ' tcx > hir:: intravisit:: Visitor < ' tcx > for UnusedUnsafeVisitor < ' a , ' tcx > {
277
+ impl < ' a , ' tcx > hir:: intravisit:: Visitor < ' tcx > for UnusedUnsafeVisitor < ' a > {
265
278
fn nested_visit_map < ' this > ( & ' this mut self ) ->
266
279
hir:: intravisit:: NestedVisitorMap < ' this , ' tcx >
267
280
{
@@ -272,50 +285,15 @@ impl<'a, 'tcx> hir::intravisit::Visitor<'tcx> for UnusedUnsafeVisitor<'a, 'tcx>
272
285
hir:: intravisit:: walk_block ( self , block) ;
273
286
274
287
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 ) ) ) ;
299
289
}
300
290
}
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
- }
314
291
}
315
292
316
293
fn check_unused_unsafe < ' a , ' tcx > ( tcx : TyCtxt < ' a , ' tcx , ' tcx > ,
317
294
def_id : DefId ,
318
- used_unsafe : FxHashSet < ast:: NodeId > )
295
+ used_unsafe : & FxHashSet < ast:: NodeId > ,
296
+ unsafe_blocks : & ' a mut Vec < ( ast:: NodeId , bool ) > )
319
297
{
320
298
let body_id =
321
299
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>,
333
311
debug ! ( "check_unused_unsafe({:?}, body={:?}, used_unsafe={:?})" ,
334
312
def_id, body, used_unsafe) ;
335
313
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) ;
339
316
}
340
317
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
343
320
{
344
321
debug ! ( "unsafety_violations({:?})" , def_id) ;
345
322
@@ -351,7 +328,10 @@ fn unsafety_violations<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) ->
351
328
ClearOnDecode :: Set ( ref data) => data,
352
329
ClearOnDecode :: Clear => {
353
330
debug ! ( "unsafety_violations: {:?} - remote, skipping" , def_id) ;
354
- return Rc :: new ( [ ] )
331
+ return UnsafetyCheckResult {
332
+ violations : Rc :: new ( [ ] ) ,
333
+ unsafe_blocks : Rc :: new ( [ ] )
334
+ }
355
335
}
356
336
} ;
357
337
@@ -360,8 +340,43 @@ fn unsafety_violations<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) ->
360
340
mir, visibility_scope_info, tcx, param_env) ;
361
341
checker. visit_mir ( mir) ;
362
342
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 ( ) ;
365
380
}
366
381
367
382
pub 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) {
372
387
_ => { }
373
388
} ;
374
389
390
+ let UnsafetyCheckResult {
391
+ violations,
392
+ unsafe_blocks
393
+ } = tcx. unsafety_check_result ( def_id) ;
394
+
375
395
for & UnsafetyViolation {
376
396
source_info, description, lint_node_id
377
- } in & * tcx . unsafety_violations ( def_id ) {
397
+ } in violations . iter ( ) {
378
398
// Report an error.
379
399
if let Some ( lint_node_id) = lint_node_id {
380
400
tcx. lint_node ( SAFE_EXTERN_STATICS ,
@@ -390,4 +410,15 @@ pub fn check_unsafety<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) {
390
410
. emit ( ) ;
391
411
}
392
412
}
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
+ }
393
424
}
0 commit comments