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
@@ -505,6 +506,13 @@ fn find_live<'tcx>(
505506 symbol_visitor. live_symbols
506507}
507508
509+ #[ derive( Debug , Clone , Copy , PartialEq , Eq , Hash ) ]
510+ enum ExtraNote {
511+ /// Use this to provide some examples in the diagnostic of potential other purposes for a value
512+ /// or field that is dead code
513+ OtherPurposeExamples ,
514+ }
515+
508516struct DeadVisitor < ' tcx > {
509517 tcx : TyCtxt < ' tcx > ,
510518 live_symbols : FxHashSet < hir:: HirId > ,
@@ -570,14 +578,42 @@ impl DeadVisitor<'tcx> {
570578 & mut self ,
571579 id : hir:: HirId ,
572580 span : rustc_span:: Span ,
573- name : Symbol ,
581+ name : Ident ,
574582 participle : & str ,
583+ extra_note : Option < ExtraNote > ,
575584 ) {
576585 if !name. as_str ( ) . starts_with ( '_' ) {
577586 self . tcx . struct_span_lint_hir ( lint:: builtin:: DEAD_CODE , id, span, |lint| {
578587 let def_id = self . tcx . hir ( ) . local_def_id ( id) ;
579588 let descr = self . tcx . def_kind ( def_id) . descr ( def_id. to_def_id ( ) ) ;
580- lint. build ( & format ! ( "{} is never {}: `{}`" , descr, participle, name) ) . emit ( )
589+
590+ let prefixed = vec ! [ ( name. span, format!( "_{}" , name) ) ] ;
591+
592+ let mut diag =
593+ lint. build ( & format ! ( "{} is never {}: `{}`" , descr, participle, name) ) ;
594+
595+ diag. multipart_suggestion (
596+ "if this is intentional, prefix it with an underscore" ,
597+ prefixed,
598+ Applicability :: MachineApplicable ,
599+ ) ;
600+
601+ let mut note = format ! (
602+ "the leading underscore signals that this {} serves some other \
603+ purpose even if it isn't used in a way that we can detect.",
604+ descr,
605+ ) ;
606+ if matches ! ( extra_note, Some ( ExtraNote :: OtherPurposeExamples ) ) {
607+ note += " (e.g. for its effect when dropped or in foreign code)" ;
608+ }
609+
610+ diag. note ( & note) ;
611+
612+ // Force the note we added to the front, before any other subdiagnostics
613+ // added in lint.build(...)
614+ diag. children . rotate_right ( 1 ) ;
615+
616+ diag. emit ( )
581617 } ) ;
582618 }
583619 }
@@ -623,7 +659,7 @@ impl Visitor<'tcx> for DeadVisitor<'tcx> {
623659 hir:: ItemKind :: Struct ( ..) => "constructed" , // Issue #52325
624660 _ => "used" ,
625661 } ;
626- self . warn_dead_code ( item. hir_id ( ) , span, item. ident . name , participle) ;
662+ self . warn_dead_code ( item. hir_id ( ) , span, item. ident , participle, None ) ;
627663 } else {
628664 // Only continue if we didn't warn
629665 intravisit:: walk_item ( self , item) ;
@@ -637,22 +673,28 @@ impl Visitor<'tcx> for DeadVisitor<'tcx> {
637673 id : hir:: HirId ,
638674 ) {
639675 if self . should_warn_about_variant ( & variant) {
640- self . warn_dead_code ( variant. id , variant. span , variant. ident . name , "constructed" ) ;
676+ self . warn_dead_code ( variant. id , variant. span , variant. ident , "constructed" , None ) ;
641677 } else {
642678 intravisit:: walk_variant ( self , variant, g, id) ;
643679 }
644680 }
645681
646682 fn visit_foreign_item ( & mut self , fi : & ' tcx hir:: ForeignItem < ' tcx > ) {
647683 if self . should_warn_about_foreign_item ( fi) {
648- self . warn_dead_code ( fi. hir_id ( ) , fi. span , fi. ident . name , "used" ) ;
684+ self . warn_dead_code ( fi. hir_id ( ) , fi. span , fi. ident , "used" , None ) ;
649685 }
650686 intravisit:: walk_foreign_item ( self , fi) ;
651687 }
652688
653689 fn visit_field_def ( & mut self , field : & ' tcx hir:: FieldDef < ' tcx > ) {
654690 if self . should_warn_about_field ( & field) {
655- self . warn_dead_code ( field. hir_id , field. span , field. ident . name , "read" ) ;
691+ self . warn_dead_code (
692+ field. hir_id ,
693+ field. span ,
694+ field. ident ,
695+ "read" ,
696+ Some ( ExtraNote :: OtherPurposeExamples ) ,
697+ ) ;
656698 }
657699 intravisit:: walk_field_def ( self , field) ;
658700 }
@@ -664,8 +706,9 @@ impl Visitor<'tcx> for DeadVisitor<'tcx> {
664706 self . warn_dead_code (
665707 impl_item. hir_id ( ) ,
666708 impl_item. span ,
667- impl_item. ident . name ,
709+ impl_item. ident ,
668710 "used" ,
711+ None ,
669712 ) ;
670713 }
671714 self . visit_nested_body ( body_id)
@@ -683,7 +726,7 @@ impl Visitor<'tcx> for DeadVisitor<'tcx> {
683726 } else {
684727 impl_item. ident . span
685728 } ;
686- self . warn_dead_code ( impl_item. hir_id ( ) , span, impl_item. ident . name , "used" ) ;
729+ self . warn_dead_code ( impl_item. hir_id ( ) , span, impl_item. ident , "used" , None ) ;
687730 }
688731 self . visit_nested_body ( body_id)
689732 }
0 commit comments