33// from live codes are live, and everything else is dead.
44
55use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
6+ use rustc_errors:: Applicability ;
67use rustc_hir as hir;
78use rustc_hir:: def:: { CtorOf , DefKind , Res } ;
89use rustc_hir:: def_id:: { DefId , LOCAL_CRATE } ;
@@ -15,7 +16,7 @@ use rustc_middle::middle::privacy;
1516use rustc_middle:: ty:: { self , DefIdTree , TyCtxt } ;
1617use rustc_session:: lint;
1718
18- use rustc_span:: symbol:: { sym, Symbol } ;
19+ use rustc_span:: symbol:: { sym, Ident , Symbol } ;
1920
2021// Any local node that may call something in its body block should be
2122// explored. For example, if it's a live Node::Item that is a
@@ -507,6 +508,13 @@ fn find_live<'tcx>(
507508 symbol_visitor. live_symbols
508509}
509510
511+ #[ derive( Debug , Clone , Copy , PartialEq , Eq , Hash ) ]
512+ enum ExtraNote {
513+ /// Use this to provide some examples in the diagnostic of potential other purposes for a value
514+ /// or field that is dead code
515+ OtherPurposeExamples ,
516+ }
517+
510518struct DeadVisitor < ' tcx > {
511519 tcx : TyCtxt < ' tcx > ,
512520 live_symbols : FxHashSet < hir:: HirId > ,
@@ -572,14 +580,42 @@ impl DeadVisitor<'tcx> {
572580 & mut self ,
573581 id : hir:: HirId ,
574582 span : rustc_span:: Span ,
575- name : Symbol ,
583+ name : Ident ,
576584 participle : & str ,
585+ extra_note : Option < ExtraNote > ,
577586 ) {
578587 if !name. as_str ( ) . starts_with ( '_' ) {
579588 self . tcx . struct_span_lint_hir ( lint:: builtin:: DEAD_CODE , id, span, |lint| {
580589 let def_id = self . tcx . hir ( ) . local_def_id ( id) ;
581590 let descr = self . tcx . def_kind ( def_id) . descr ( def_id. to_def_id ( ) ) ;
582- lint. build ( & format ! ( "{} is never {}: `{}`" , descr, participle, name) ) . emit ( )
591+
592+ let prefixed = vec ! [ ( name. span, format!( "_{}" , name) ) ] ;
593+
594+ let mut diag =
595+ lint. build ( & format ! ( "{} is never {}: `{}`" , descr, participle, name) ) ;
596+
597+ diag. multipart_suggestion (
598+ "if this is intentional, prefix it with an underscore" ,
599+ prefixed,
600+ Applicability :: MachineApplicable ,
601+ ) ;
602+
603+ let mut note = format ! (
604+ "the leading underscore signals that this {} serves some other \
605+ purpose even if it isn't used in a way that we can detect.",
606+ descr,
607+ ) ;
608+ if matches ! ( extra_note, Some ( ExtraNote :: OtherPurposeExamples ) ) {
609+ note += " (e.g. for its effect when dropped or in foreign code)" ;
610+ }
611+
612+ diag. note ( & note) ;
613+
614+ // Force the note we added to the front, before any other subdiagnostics
615+ // added in lint.build(...)
616+ diag. children . rotate_right ( 1 ) ;
617+
618+ diag. emit ( )
583619 } ) ;
584620 }
585621 }
@@ -625,7 +661,7 @@ impl Visitor<'tcx> for DeadVisitor<'tcx> {
625661 hir:: ItemKind :: Struct ( ..) => "constructed" , // Issue #52325
626662 _ => "used" ,
627663 } ;
628- self . warn_dead_code ( item. hir_id ( ) , span, item. ident . name , participle) ;
664+ self . warn_dead_code ( item. hir_id ( ) , span, item. ident , participle, None ) ;
629665 } else {
630666 // Only continue if we didn't warn
631667 intravisit:: walk_item ( self , item) ;
@@ -639,22 +675,28 @@ impl Visitor<'tcx> for DeadVisitor<'tcx> {
639675 id : hir:: HirId ,
640676 ) {
641677 if self . should_warn_about_variant ( & variant) {
642- self . warn_dead_code ( variant. id , variant. span , variant. ident . name , "constructed" ) ;
678+ self . warn_dead_code ( variant. id , variant. span , variant. ident , "constructed" , None ) ;
643679 } else {
644680 intravisit:: walk_variant ( self , variant, g, id) ;
645681 }
646682 }
647683
648684 fn visit_foreign_item ( & mut self , fi : & ' tcx hir:: ForeignItem < ' tcx > ) {
649685 if self . should_warn_about_foreign_item ( fi) {
650- self . warn_dead_code ( fi. hir_id ( ) , fi. span , fi. ident . name , "used" ) ;
686+ self . warn_dead_code ( fi. hir_id ( ) , fi. span , fi. ident , "used" , None ) ;
651687 }
652688 intravisit:: walk_foreign_item ( self , fi) ;
653689 }
654690
655691 fn visit_field_def ( & mut self , field : & ' tcx hir:: FieldDef < ' tcx > ) {
656692 if self . should_warn_about_field ( & field) {
657- self . warn_dead_code ( field. hir_id , field. span , field. ident . name , "read" ) ;
693+ self . warn_dead_code (
694+ field. hir_id ,
695+ field. span ,
696+ field. ident ,
697+ "read" ,
698+ Some ( ExtraNote :: OtherPurposeExamples ) ,
699+ ) ;
658700 }
659701 intravisit:: walk_field_def ( self , field) ;
660702 }
@@ -666,8 +708,9 @@ impl Visitor<'tcx> for DeadVisitor<'tcx> {
666708 self . warn_dead_code (
667709 impl_item. hir_id ( ) ,
668710 impl_item. span ,
669- impl_item. ident . name ,
711+ impl_item. ident ,
670712 "used" ,
713+ None ,
671714 ) ;
672715 }
673716 self . visit_nested_body ( body_id)
@@ -685,7 +728,7 @@ impl Visitor<'tcx> for DeadVisitor<'tcx> {
685728 } else {
686729 impl_item. ident . span
687730 } ;
688- self . warn_dead_code ( impl_item. hir_id ( ) , span, impl_item. ident . name , "used" ) ;
731+ self . warn_dead_code ( impl_item. hir_id ( ) , span, impl_item. ident , "used" , None ) ;
689732 }
690733 self . visit_nested_body ( body_id)
691734 }
0 commit comments