@@ -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
}
@@ -74,7 +76,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
74
76
TerminatorKind :: Resume |
75
77
TerminatorKind :: Return |
76
78
TerminatorKind :: Unreachable => {
77
- // safe (at least as emitted during MIR construction)
79
+ // safe (at least as emitted during MIR construction)
78
80
}
79
81
80
82
TerminatorKind :: Call { ref func, .. } => {
@@ -116,12 +118,19 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
116
118
rvalue : & Rvalue < ' tcx > ,
117
119
location : Location )
118
120
{
119
- if let & Rvalue :: Aggregate (
120
- box AggregateKind :: Closure ( def_id, _) ,
121
- _
122
- ) = rvalue {
123
- let unsafety_violations = self . tcx . unsafety_violations ( def_id) ;
124
- self . register_violations ( & unsafety_violations) ;
121
+ if let & Rvalue :: Aggregate ( box ref aggregate, _) = rvalue {
122
+ match aggregate {
123
+ & AggregateKind :: Array ( ..) |
124
+ & AggregateKind :: Tuple |
125
+ & AggregateKind :: Adt ( ..) => { }
126
+ & AggregateKind :: Closure ( def_id, _) |
127
+ & AggregateKind :: Generator ( def_id, _, _) => {
128
+ let UnsafetyCheckResult {
129
+ violations, unsafe_blocks
130
+ } = self . tcx . unsafety_check_result ( def_id) ;
131
+ self . register_violations ( & violations, & unsafe_blocks) ;
132
+ }
133
+ }
125
134
}
126
135
self . super_rvalue ( rvalue, location) ;
127
136
}
@@ -188,7 +197,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
188
197
source_info,
189
198
description : "use of extern static" ,
190
199
lint_node_id : Some ( lint_root)
191
- } ] ) ;
200
+ } ] , & [ ] ) ;
192
201
}
193
202
}
194
203
}
@@ -221,41 +230,49 @@ impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
221
230
let source_info = self . source_info ;
222
231
self . register_violations ( & [ UnsafetyViolation {
223
232
source_info, description, lint_node_id : None
224
- } ] ) ;
233
+ } ] , & [ ] ) ;
225
234
}
226
235
227
- fn register_violations ( & mut self , violations : & [ UnsafetyViolation ] ) {
228
- match self . visibility_scope_info [ self . source_info . scope ] . safety {
236
+ fn register_violations ( & mut self ,
237
+ violations : & [ UnsafetyViolation ] ,
238
+ unsafe_blocks : & [ ( ast:: NodeId , bool ) ] ) {
239
+ let within_unsafe = match self . visibility_scope_info [ self . source_info . scope ] . safety {
229
240
Safety :: Safe => {
230
241
for violation in violations {
231
242
if !self . violations . contains ( violation) {
232
243
self . violations . push ( violation. clone ( ) )
233
244
}
234
245
}
246
+
247
+ false
235
248
}
236
- Safety :: BuiltinUnsafe | Safety :: FnUnsafe => { }
249
+ Safety :: BuiltinUnsafe | Safety :: FnUnsafe => true ,
237
250
Safety :: ExplicitUnsafe ( node_id) => {
238
251
if !violations. is_empty ( ) {
239
252
self . used_unsafe . insert ( node_id) ;
240
253
}
254
+ true
241
255
}
242
- }
256
+ } ;
257
+ self . inherited_blocks . extend ( unsafe_blocks. iter ( ) . map ( |& ( node_id, is_used) | {
258
+ ( node_id, is_used && !within_unsafe)
259
+ } ) ) ;
243
260
}
244
261
}
245
262
246
263
pub ( crate ) fn provide ( providers : & mut Providers ) {
247
264
* providers = Providers {
248
- unsafety_violations ,
265
+ unsafety_check_result ,
249
266
..* providers
250
267
} ;
251
268
}
252
269
253
- struct UnusedUnsafeVisitor < ' a , ' tcx : ' a > {
254
- tcx : TyCtxt < ' a , ' tcx , ' tcx > ,
255
- used_unsafe : FxHashSet < ast:: NodeId >
270
+ struct UnusedUnsafeVisitor < ' a > {
271
+ used_unsafe : & ' a FxHashSet < ast :: NodeId > ,
272
+ unsafe_blocks : & ' a mut Vec < ( ast:: NodeId , bool ) > ,
256
273
}
257
274
258
- impl < ' a , ' tcx > hir:: intravisit:: Visitor < ' tcx > for UnusedUnsafeVisitor < ' a , ' tcx > {
275
+ impl < ' a , ' tcx > hir:: intravisit:: Visitor < ' tcx > for UnusedUnsafeVisitor < ' a > {
259
276
fn nested_visit_map < ' this > ( & ' this mut self ) ->
260
277
hir:: intravisit:: NestedVisitorMap < ' this , ' tcx >
261
278
{
@@ -266,50 +283,15 @@ impl<'a, 'tcx> hir::intravisit::Visitor<'tcx> for UnusedUnsafeVisitor<'a, 'tcx>
266
283
hir:: intravisit:: walk_block ( self , block) ;
267
284
268
285
if let hir:: UnsafeBlock ( hir:: UserProvided ) = block. rules {
269
- if !self . used_unsafe . contains ( & block. id ) {
270
- self . report_unused_unsafe ( block) ;
271
- }
272
- }
273
- }
274
- }
275
-
276
- impl < ' a , ' tcx > UnusedUnsafeVisitor < ' a , ' tcx > {
277
- /// Return the NodeId for an enclosing scope that is also `unsafe`
278
- fn is_enclosed ( & self , id : ast:: NodeId ) -> Option < ( String , ast:: NodeId ) > {
279
- let parent_id = self . tcx . hir . get_parent_node ( id) ;
280
- if parent_id != id {
281
- if self . used_unsafe . contains ( & parent_id) {
282
- Some ( ( "block" . to_string ( ) , parent_id) )
283
- } else if let Some ( hir:: map:: NodeItem ( & hir:: Item {
284
- node : hir:: ItemFn ( _, hir:: Unsafety :: Unsafe , _, _, _, _) ,
285
- ..
286
- } ) ) = self . tcx . hir . find ( parent_id) {
287
- Some ( ( "fn" . to_string ( ) , parent_id) )
288
- } else {
289
- self . is_enclosed ( parent_id)
290
- }
291
- } else {
292
- None
293
- }
294
- }
295
-
296
- fn report_unused_unsafe ( & self , block : & ' tcx hir:: Block ) {
297
- let mut db = self . tcx . struct_span_lint_node ( UNUSED_UNSAFE ,
298
- block. id ,
299
- block. span ,
300
- "unnecessary `unsafe` block" ) ;
301
- db. span_label ( block. span , "unnecessary `unsafe` block" ) ;
302
- if let Some ( ( kind, id) ) = self . is_enclosed ( block. id ) {
303
- db. span_note ( self . tcx . hir . span ( id) ,
304
- & format ! ( "because it's nested under this `unsafe` {}" , kind) ) ;
286
+ self . unsafe_blocks . push ( ( block. id , self . used_unsafe . contains ( & block. id ) ) ) ;
305
287
}
306
- db. emit ( ) ;
307
288
}
308
289
}
309
290
310
291
fn check_unused_unsafe < ' a , ' tcx > ( tcx : TyCtxt < ' a , ' tcx , ' tcx > ,
311
292
def_id : DefId ,
312
- used_unsafe : FxHashSet < ast:: NodeId > )
293
+ used_unsafe : & FxHashSet < ast:: NodeId > ,
294
+ unsafe_blocks : & ' a mut Vec < ( ast:: NodeId , bool ) > )
313
295
{
314
296
let body_id =
315
297
tcx. hir . as_local_node_id ( def_id) . and_then ( |node_id| {
@@ -327,25 +309,27 @@ fn check_unused_unsafe<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
327
309
debug ! ( "check_unused_unsafe({:?}, body={:?}, used_unsafe={:?})" ,
328
310
def_id, body, used_unsafe) ;
329
311
330
- hir:: intravisit:: Visitor :: visit_body (
331
- & mut UnusedUnsafeVisitor { tcx, used_unsafe } ,
332
- body) ;
312
+ let mut visitor = UnusedUnsafeVisitor { used_unsafe, unsafe_blocks } ;
313
+ hir:: intravisit:: Visitor :: visit_body ( & mut visitor, body) ;
333
314
}
334
315
335
- fn unsafety_violations < ' a , ' tcx > ( tcx : TyCtxt < ' a , ' tcx , ' tcx > , def_id : DefId ) ->
336
- Rc < [ UnsafetyViolation ] >
316
+ fn unsafety_check_result < ' a , ' tcx > ( tcx : TyCtxt < ' a , ' tcx , ' tcx > , def_id : DefId )
317
+ -> UnsafetyCheckResult
337
318
{
338
319
debug ! ( "unsafety_violations({:?})" , def_id) ;
339
320
340
321
// NB: this borrow is valid because all the consumers of
341
- // `mir_const ` force this.
342
- let mir = & tcx. mir_const ( def_id) . borrow ( ) ;
322
+ // `mir_built ` force this.
323
+ let mir = & tcx. mir_built ( def_id) . borrow ( ) ;
343
324
344
325
let visibility_scope_info = match mir. visibility_scope_info {
345
326
ClearOnDecode :: Set ( ref data) => data,
346
327
ClearOnDecode :: Clear => {
347
328
debug ! ( "unsafety_violations: {:?} - remote, skipping" , def_id) ;
348
- return Rc :: new ( [ ] )
329
+ return UnsafetyCheckResult {
330
+ violations : Rc :: new ( [ ] ) ,
331
+ unsafe_blocks : Rc :: new ( [ ] )
332
+ }
349
333
}
350
334
} ;
351
335
@@ -354,8 +338,43 @@ fn unsafety_violations<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) ->
354
338
mir, visibility_scope_info, tcx, param_env) ;
355
339
checker. visit_mir ( mir) ;
356
340
357
- check_unused_unsafe ( tcx, def_id, checker. used_unsafe ) ;
358
- checker. violations . into ( )
341
+ check_unused_unsafe ( tcx, def_id, & checker. used_unsafe , & mut checker. inherited_blocks ) ;
342
+ UnsafetyCheckResult {
343
+ violations : checker. violations . into ( ) ,
344
+ unsafe_blocks : checker. inherited_blocks . into ( )
345
+ }
346
+ }
347
+
348
+ /// Return the NodeId for an enclosing scope that is also `unsafe`
349
+ fn is_enclosed ( tcx : TyCtxt ,
350
+ used_unsafe : & FxHashSet < ast:: NodeId > ,
351
+ id : ast:: NodeId ) -> Option < ( String , ast:: NodeId ) > {
352
+ let parent_id = tcx. hir . get_parent_node ( id) ;
353
+ if parent_id != id {
354
+ if used_unsafe. contains ( & parent_id) {
355
+ Some ( ( "block" . to_string ( ) , parent_id) )
356
+ } else if let Some ( hir:: map:: NodeItem ( & hir:: Item {
357
+ node : hir:: ItemFn ( _, hir:: Unsafety :: Unsafe , _, _, _, _) ,
358
+ ..
359
+ } ) ) = tcx. hir . find ( parent_id) {
360
+ Some ( ( "fn" . to_string ( ) , parent_id) )
361
+ } else {
362
+ is_enclosed ( tcx, used_unsafe, parent_id)
363
+ }
364
+ } else {
365
+ None
366
+ }
367
+ }
368
+
369
+ fn report_unused_unsafe ( tcx : TyCtxt , used_unsafe : & FxHashSet < ast:: NodeId > , id : ast:: NodeId ) {
370
+ let span = tcx. hir . span ( id) ;
371
+ let mut db = tcx. struct_span_lint_node ( UNUSED_UNSAFE , id, span, "unnecessary `unsafe` block" ) ;
372
+ db. span_label ( span, "unnecessary `unsafe` block" ) ;
373
+ if let Some ( ( kind, id) ) = is_enclosed ( tcx, used_unsafe, id) {
374
+ db. span_note ( tcx. hir . span ( id) ,
375
+ & format ! ( "because it's nested under this `unsafe` {}" , kind) ) ;
376
+ }
377
+ db. emit ( ) ;
359
378
}
360
379
361
380
pub fn check_unsafety < ' a , ' tcx > ( tcx : TyCtxt < ' a , ' tcx , ' tcx > , def_id : DefId ) {
@@ -366,9 +385,14 @@ pub fn check_unsafety<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) {
366
385
_ => { }
367
386
} ;
368
387
388
+ let UnsafetyCheckResult {
389
+ violations,
390
+ unsafe_blocks
391
+ } = tcx. unsafety_check_result ( def_id) ;
392
+
369
393
for & UnsafetyViolation {
370
394
source_info, description, lint_node_id
371
- } in & * tcx . unsafety_violations ( def_id ) {
395
+ } in violations . iter ( ) {
372
396
// Report an error.
373
397
if let Some ( lint_node_id) = lint_node_id {
374
398
tcx. lint_node ( SAFE_EXTERN_STATICS ,
@@ -384,4 +408,15 @@ pub fn check_unsafety<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) {
384
408
. emit ( ) ;
385
409
}
386
410
}
411
+
412
+ let mut unsafe_blocks: Vec < _ > = unsafe_blocks. into_iter ( ) . collect ( ) ;
413
+ unsafe_blocks. sort ( ) ;
414
+ let used_unsafe: FxHashSet < _ > = unsafe_blocks. iter ( )
415
+ . flat_map ( |& & ( id, used) | if used { Some ( id) } else { None } )
416
+ . collect ( ) ;
417
+ for & ( block_id, is_used) in unsafe_blocks {
418
+ if !is_used {
419
+ report_unused_unsafe ( tcx, & used_unsafe, block_id) ;
420
+ }
421
+ }
387
422
}
0 commit comments