@@ -6,14 +6,15 @@ use rustc_hir::hir_id::HirId;
6
6
use rustc_hir:: intravisit;
7
7
use rustc_hir:: Node ;
8
8
use rustc_middle:: mir:: visit:: { MutatingUseContext , PlaceContext , Visitor } ;
9
- use rustc_middle:: mir:: * ;
10
9
use rustc_middle:: ty:: query:: Providers ;
11
10
use rustc_middle:: ty:: { self , TyCtxt } ;
11
+ use rustc_middle:: { lint, mir:: * } ;
12
12
use rustc_session:: lint:: builtin:: { UNSAFE_OP_IN_UNSAFE_FN , UNUSED_UNSAFE } ;
13
13
use rustc_session:: lint:: Level ;
14
14
15
- use std:: iter ;
15
+ use std:: collections :: hash_map ;
16
16
use std:: ops:: Bound ;
17
+ use std:: { cmp, iter} ;
17
18
18
19
pub struct UnsafetyChecker < ' a , ' tcx > {
19
20
body : & ' a Body < ' tcx > ,
@@ -25,9 +26,9 @@ pub struct UnsafetyChecker<'a, 'tcx> {
25
26
26
27
/// Used `unsafe` blocks in this function. This is used for the "unused_unsafe" lint.
27
28
///
28
- /// The keys are the used `unsafe` blocks, the boolean flag indicates whether
29
+ /// The keys are the used `unsafe` blocks, the UnusedUnsafeKind indicates whether
29
30
/// or not any of the usages happen at a place that doesn't allow `unsafe_op_in_unsafe_fn`.
30
- used_unsafe_blocks : FxHashMap < HirId , bool > ,
31
+ used_unsafe_blocks : FxHashMap < HirId , UsedUnsafeBlockData > ,
31
32
}
32
33
33
34
impl < ' a , ' tcx > UnsafetyChecker < ' a , ' tcx > {
@@ -262,8 +263,21 @@ impl<'tcx> UnsafetyChecker<'_, 'tcx> {
262
263
fn register_violations < ' a > (
263
264
& mut self ,
264
265
violations : impl ExactSizeIterator < Item = & ' a UnsafetyViolation > ,
265
- new_used_unsafe_blocks : impl Iterator < Item = ( & ' a HirId , & ' a bool ) > ,
266
+ new_used_unsafe_blocks : impl Iterator < Item = ( & ' a HirId , & ' a UsedUnsafeBlockData ) > ,
266
267
) {
268
+ use UsedUnsafeBlockData :: { AllAllowedInUnsafeFn , SomeDisallowedInUnsafeFn } ;
269
+
270
+ let update_entry = |this : & mut Self , hir_id, new_usage| {
271
+ match this. used_unsafe_blocks . entry ( hir_id) {
272
+ hash_map:: Entry :: Occupied ( mut entry) => {
273
+ let entry = entry. get_mut ( ) ;
274
+ * entry = cmp:: min ( * entry, new_usage) ;
275
+ }
276
+ hash_map:: Entry :: Vacant ( entry) => {
277
+ entry. insert ( new_usage) ;
278
+ }
279
+ } ;
280
+ } ;
267
281
let safety = self . body . source_scopes [ self . source_info . scope ]
268
282
. local_data
269
283
. as_ref ( )
@@ -290,23 +304,21 @@ impl<'tcx> UnsafetyChecker<'_, 'tcx> {
290
304
}
291
305
} ) ,
292
306
Safety :: BuiltinUnsafe => { }
293
- Safety :: ExplicitUnsafe ( hir_id) => {
294
- let used = violations. len ( ) != 0 ;
295
- let disallowed_in_unsafe_fn = { violations } . any ( |violation| {
296
- self . tcx . lint_level_at_node ( UNSAFE_OP_IN_UNSAFE_FN , violation. lint_root ) . 0
297
- != Level :: Allow
298
- } ) ;
299
- // mark unsafe block as used if there are any unsafe operations inside
300
- if used {
301
- * self . used_unsafe_blocks . entry ( hir_id) . or_insert ( false ) |=
302
- disallowed_in_unsafe_fn;
303
- }
304
- }
307
+ Safety :: ExplicitUnsafe ( hir_id) => violations. for_each ( |violation| {
308
+ update_entry (
309
+ self ,
310
+ hir_id,
311
+ match self . tcx . lint_level_at_node ( UNSAFE_OP_IN_UNSAFE_FN , violation. lint_root ) . 0
312
+ {
313
+ Level :: Allow => AllAllowedInUnsafeFn ( violation. lint_root ) ,
314
+ _ => SomeDisallowedInUnsafeFn ,
315
+ } ,
316
+ )
317
+ } ) ,
305
318
} ;
306
319
307
- new_used_unsafe_blocks. for_each ( |( & hir_id, & disallowed_in_unsafe_fn) | {
308
- * self . used_unsafe_blocks . entry ( hir_id) . or_insert ( false ) |= disallowed_in_unsafe_fn;
309
- } ) ;
320
+ new_used_unsafe_blocks
321
+ . for_each ( |( & hir_id, & usage_data) | update_entry ( self , hir_id, usage_data) ) ;
310
322
}
311
323
fn check_mut_borrowing_layout_constrained_field (
312
324
& mut self ,
@@ -402,31 +414,36 @@ enum Context {
402
414
403
415
struct UnusedUnsafeVisitor < ' a , ' tcx > {
404
416
tcx : TyCtxt < ' tcx > ,
405
- used_unsafe_blocks : & ' a FxHashMap < HirId , bool > ,
417
+ used_unsafe_blocks : & ' a FxHashMap < HirId , UsedUnsafeBlockData > ,
406
418
context : Context ,
407
419
unused_unsafe : & ' a mut Vec < ( HirId , UnusedUnsafe ) > ,
408
420
}
409
421
410
422
impl < ' tcx > intravisit:: Visitor < ' tcx > for UnusedUnsafeVisitor < ' _ , ' tcx > {
411
423
fn visit_block ( & mut self , block : & ' tcx hir:: Block < ' tcx > ) {
424
+ use UsedUnsafeBlockData :: { AllAllowedInUnsafeFn , SomeDisallowedInUnsafeFn } ;
425
+
412
426
if let hir:: BlockCheckMode :: UnsafeBlock ( hir:: UnsafeSource :: UserProvided ) = block. rules {
413
427
let used = match self . tcx . lint_level_at_node ( UNUSED_UNSAFE , block. hir_id ) {
414
- ( Level :: Allow , _) => Some ( true ) ,
428
+ ( Level :: Allow , _) => Some ( SomeDisallowedInUnsafeFn ) ,
415
429
_ => self . used_unsafe_blocks . get ( & block. hir_id ) . copied ( ) ,
416
430
} ;
417
431
// only pushes if the contained `match` doesn't `return` early
418
432
self . unused_unsafe . push ( (
419
433
block. hir_id ,
420
434
match ( self . context , used) {
421
435
( _, None ) => UnusedUnsafe :: Unused ,
422
- ( Context :: Safe , Some ( _) ) | ( Context :: UnsafeFn ( _) , Some ( true ) ) => {
436
+ ( Context :: Safe , Some ( _) )
437
+ | ( Context :: UnsafeFn ( _) , Some ( SomeDisallowedInUnsafeFn ) ) => {
423
438
let previous_context = self . context ;
424
439
self . context = Context :: UnsafeBlock ( block. hir_id ) ;
425
440
intravisit:: walk_block ( self , block) ;
426
441
self . context = previous_context;
427
442
return ;
428
443
}
429
- ( Context :: UnsafeFn ( hir_id) , Some ( false ) ) => UnusedUnsafe :: InUnsafeFn ( hir_id) ,
444
+ ( Context :: UnsafeFn ( hir_id) , Some ( AllAllowedInUnsafeFn ( lint_root) ) ) => {
445
+ UnusedUnsafe :: InUnsafeFn ( hir_id, lint_root)
446
+ }
430
447
( Context :: UnsafeBlock ( hir_id) , Some ( _) ) => UnusedUnsafe :: InUnsafeBlock ( hir_id) ,
431
448
} ,
432
449
) ) ;
@@ -451,7 +468,7 @@ impl<'tcx> intravisit::Visitor<'tcx> for UnusedUnsafeVisitor<'_, 'tcx> {
451
468
fn check_unused_unsafe (
452
469
tcx : TyCtxt < ' _ > ,
453
470
def_id : LocalDefId ,
454
- used_unsafe_blocks : & FxHashMap < HirId , bool > ,
471
+ used_unsafe_blocks : & FxHashMap < HirId , UsedUnsafeBlockData > ,
455
472
) -> Vec < ( HirId , UnusedUnsafe ) > {
456
473
let hir_id = tcx. hir ( ) . local_def_id_to_hir_id ( def_id) ;
457
474
let body_id = tcx. hir ( ) . maybe_body_owned_by ( hir_id) ;
@@ -527,13 +544,18 @@ fn report_unused_unsafe(tcx: TyCtxt<'_>, kind: UnusedUnsafe, id: HirId) {
527
544
format ! ( "because it's nested under this `unsafe` block" ) ,
528
545
) ;
529
546
}
530
- UnusedUnsafe :: InUnsafeFn ( id) => {
547
+ UnusedUnsafe :: InUnsafeFn ( id, usage_lint_root ) => {
531
548
db. span_label (
532
549
tcx. sess . source_map ( ) . guess_head_span ( tcx. hir ( ) . span ( id) ) ,
533
550
format ! ( "because it's nested under this `unsafe` fn" ) ,
534
551
) ;
552
+ db. note ( "this `unsafe` block does contain unsafe operations, but those are already allowed in an `unsafe` fn" ) ;
553
+ let ( level, source) = tcx. lint_level_at_node ( UNSAFE_OP_IN_UNSAFE_FN , usage_lint_root) ;
554
+ assert_eq ! ( level, Level :: Allow ) ;
555
+ lint:: explain_lint_level_source ( tcx. sess , UNSAFE_OP_IN_UNSAFE_FN , Level :: Allow , source, & mut db) ;
535
556
}
536
557
}
558
+
537
559
db. emit ( ) ;
538
560
} ) ;
539
561
}
0 commit comments