33// from live codes are live, and everything else is dead.
44
55use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
6+ use rustc_errors:: pluralize;
67use rustc_hir as hir;
78use rustc_hir:: def:: { CtorOf , DefKind , Res } ;
89use rustc_hir:: def_id:: { DefId , LocalDefId } ;
@@ -15,6 +16,7 @@ use rustc_middle::middle::privacy;
1516use rustc_middle:: ty:: { self , DefIdTree , TyCtxt } ;
1617use rustc_session:: lint;
1718use rustc_span:: symbol:: { sym, Symbol } ;
19+ use rustc_span:: Span ;
1820use std:: mem;
1921
2022// Any local node that may call something in its body block should be
@@ -47,6 +49,10 @@ struct MarkSymbolVisitor<'tcx> {
4749 ignore_variant_stack : Vec < DefId > ,
4850 // maps from tuple struct constructors to tuple struct items
4951 struct_constructors : FxHashMap < LocalDefId , LocalDefId > ,
52+ // maps from ADTs to ignored derived traits (e.g. Debug and Clone)
53+ // and the span of their respective impl (i.e., part of the derive
54+ // macro)
55+ ignored_derived_traits : FxHashMap < DefId , Vec < ( Span , DefId ) > > ,
5056}
5157
5258impl < ' tcx > MarkSymbolVisitor < ' tcx > {
@@ -242,14 +248,24 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
242248 /// Automatically generated items marked with `rustc_trivial_field_reads`
243249 /// will be ignored for the purposes of dead code analysis (see PR #85200
244250 /// for discussion).
245- fn should_ignore_item ( & self , def_id : DefId ) -> bool {
251+ fn should_ignore_item ( & mut self , def_id : DefId ) -> bool {
246252 if let Some ( impl_of) = self . tcx . impl_of_method ( def_id) {
247253 if !self . tcx . has_attr ( impl_of, sym:: automatically_derived) {
248254 return false ;
249255 }
250256
251257 if let Some ( trait_of) = self . tcx . trait_id_of_impl ( impl_of) {
252258 if self . tcx . has_attr ( trait_of, sym:: rustc_trivial_field_reads) {
259+ let trait_ref = self . tcx . impl_trait_ref ( impl_of) . unwrap ( ) ;
260+ if let ty:: Adt ( adt_def, _) = trait_ref. self_ty ( ) . kind ( ) {
261+ let impl_span = self . tcx . def_span ( impl_of) ;
262+ if let Some ( v) = self . ignored_derived_traits . get_mut ( & adt_def. did ) {
263+ v. push ( ( impl_span, trait_of) ) ;
264+ } else {
265+ self . ignored_derived_traits
266+ . insert ( adt_def. did , vec ! [ ( impl_span, trait_of) ] ) ;
267+ }
268+ }
253269 return true ;
254270 }
255271 }
@@ -571,7 +587,7 @@ fn create_and_seed_worklist<'tcx>(
571587fn find_live < ' tcx > (
572588 tcx : TyCtxt < ' tcx > ,
573589 access_levels : & privacy:: AccessLevels ,
574- ) -> FxHashSet < LocalDefId > {
590+ ) -> ( FxHashSet < LocalDefId > , FxHashMap < DefId , Vec < ( Span , DefId ) > > ) {
575591 let ( worklist, struct_constructors) = create_and_seed_worklist ( tcx, access_levels) ;
576592 let mut symbol_visitor = MarkSymbolVisitor {
577593 worklist,
@@ -584,14 +600,16 @@ fn find_live<'tcx>(
584600 pub_visibility : false ,
585601 ignore_variant_stack : vec ! [ ] ,
586602 struct_constructors,
603+ ignored_derived_traits : FxHashMap :: default ( ) ,
587604 } ;
588605 symbol_visitor. mark_live_symbols ( ) ;
589- symbol_visitor. live_symbols
606+ ( symbol_visitor. live_symbols , symbol_visitor . ignored_derived_traits )
590607}
591608
592609struct DeadVisitor < ' tcx > {
593610 tcx : TyCtxt < ' tcx > ,
594611 live_symbols : FxHashSet < LocalDefId > ,
612+ ignored_derived_traits : FxHashMap < DefId , Vec < ( Span , DefId ) > > ,
595613}
596614
597615impl < ' tcx > DeadVisitor < ' tcx > {
@@ -660,7 +678,37 @@ impl<'tcx> DeadVisitor<'tcx> {
660678 self . tcx . struct_span_lint_hir ( lint:: builtin:: DEAD_CODE , id, span, |lint| {
661679 let def_id = self . tcx . hir ( ) . local_def_id ( id) ;
662680 let descr = self . tcx . def_kind ( def_id) . descr ( def_id. to_def_id ( ) ) ;
663- lint. build ( & format ! ( "{} is never {}: `{}`" , descr, participle, name) ) . emit ( )
681+ let mut err = lint. build ( & format ! ( "{} is never {}: `{}`" , descr, participle, name) ) ;
682+ let hir = self . tcx . hir ( ) ;
683+ if let Some ( encl_scope) = hir. get_enclosing_scope ( id) {
684+ if let Some ( encl_def_id) = hir. opt_local_def_id ( encl_scope) {
685+ if let Some ( ign_traits) =
686+ self . ignored_derived_traits . get ( & encl_def_id. to_def_id ( ) )
687+ {
688+ let traits_str = ign_traits
689+ . iter ( )
690+ . map ( |( _, t) | format ! ( "`{}`" , self . tcx. item_name( * t) ) )
691+ . collect :: < Vec < _ > > ( )
692+ . join ( " and " ) ;
693+ let plural_s = pluralize ! ( ign_traits. len( ) ) ;
694+ let article = if ign_traits. len ( ) > 1 { "" } else { "a " } ;
695+ let is_are = if ign_traits. len ( ) > 1 { "these are" } else { "this is" } ;
696+ let msg = format ! (
697+ "`{}` has {}derived impl{} for the trait{} {}, but {} \
698+ intentionally ignored during dead code analysis",
699+ self . tcx. item_name( encl_def_id. to_def_id( ) ) ,
700+ article,
701+ plural_s,
702+ plural_s,
703+ traits_str,
704+ is_are
705+ ) ;
706+ let multispan = ign_traits. iter ( ) . map ( |( s, _) | * s) . collect :: < Vec < _ > > ( ) ;
707+ err. span_note ( multispan, & msg) ;
708+ }
709+ }
710+ }
711+ err. emit ( ) ;
664712 } ) ;
665713 }
666714 }
@@ -790,7 +838,7 @@ impl<'tcx> Visitor<'tcx> for DeadVisitor<'tcx> {
790838
791839pub fn check_crate ( tcx : TyCtxt < ' _ > ) {
792840 let access_levels = & tcx. privacy_access_levels ( ( ) ) ;
793- let live_symbols = find_live ( tcx, access_levels) ;
794- let mut visitor = DeadVisitor { tcx, live_symbols } ;
841+ let ( live_symbols, ignored_derived_traits ) = find_live ( tcx, access_levels) ;
842+ let mut visitor = DeadVisitor { tcx, live_symbols, ignored_derived_traits } ;
795843 tcx. hir ( ) . walk_toplevel_module ( & mut visitor) ;
796844}
0 commit comments