3
3
// from live codes are live, and everything else is dead.
4
4
5
5
use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
6
+ use rustc_errors:: pluralize;
6
7
use rustc_hir as hir;
7
8
use rustc_hir:: def:: { CtorOf , DefKind , Res } ;
8
9
use rustc_hir:: def_id:: { DefId , LocalDefId } ;
@@ -47,6 +48,8 @@ struct MarkSymbolVisitor<'tcx> {
47
48
ignore_variant_stack : Vec < DefId > ,
48
49
// maps from tuple struct constructors to tuple struct items
49
50
struct_constructors : FxHashMap < LocalDefId , LocalDefId > ,
51
+ // maps from ADTs to ignored derived traits (e.g. Debug and Clone)
52
+ ignored_derived_traits : FxHashMap < DefId , Vec < DefId > > ,
50
53
}
51
54
52
55
impl < ' tcx > MarkSymbolVisitor < ' tcx > {
@@ -242,14 +245,22 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
242
245
/// Automatically generated items marked with `rustc_trivial_field_reads`
243
246
/// will be ignored for the purposes of dead code analysis (see PR #85200
244
247
/// for discussion).
245
- fn should_ignore_item ( & self , def_id : DefId ) -> bool {
248
+ fn should_ignore_item ( & mut self , def_id : DefId ) -> bool {
246
249
if let Some ( impl_of) = self . tcx . impl_of_method ( def_id) {
247
250
if !self . tcx . has_attr ( impl_of, sym:: automatically_derived) {
248
251
return false ;
249
252
}
250
253
251
254
if let Some ( trait_of) = self . tcx . trait_id_of_impl ( impl_of) {
252
255
if self . tcx . has_attr ( trait_of, sym:: rustc_trivial_field_reads) {
256
+ let trait_ref = self . tcx . impl_trait_ref ( impl_of) . unwrap ( ) ;
257
+ if let ty:: Adt ( adt_def, _) = trait_ref. self_ty ( ) . kind ( ) {
258
+ if let Some ( v) = self . ignored_derived_traits . get_mut ( & adt_def. did ) {
259
+ v. push ( trait_of) ;
260
+ } else {
261
+ self . ignored_derived_traits . insert ( adt_def. did , vec ! [ trait_of] ) ;
262
+ }
263
+ }
253
264
return true ;
254
265
}
255
266
}
@@ -577,7 +588,7 @@ fn create_and_seed_worklist<'tcx>(
577
588
fn find_live < ' tcx > (
578
589
tcx : TyCtxt < ' tcx > ,
579
590
access_levels : & privacy:: AccessLevels ,
580
- ) -> FxHashSet < LocalDefId > {
591
+ ) -> ( FxHashSet < LocalDefId > , FxHashMap < DefId , Vec < DefId > > ) {
581
592
let ( worklist, struct_constructors) = create_and_seed_worklist ( tcx, access_levels) ;
582
593
let mut symbol_visitor = MarkSymbolVisitor {
583
594
worklist,
@@ -590,14 +601,16 @@ fn find_live<'tcx>(
590
601
pub_visibility : false ,
591
602
ignore_variant_stack : vec ! [ ] ,
592
603
struct_constructors,
604
+ ignored_derived_traits : FxHashMap :: default ( ) ,
593
605
} ;
594
606
symbol_visitor. mark_live_symbols ( ) ;
595
- symbol_visitor. live_symbols
607
+ ( symbol_visitor. live_symbols , symbol_visitor . ignored_derived_traits )
596
608
}
597
609
598
610
struct DeadVisitor < ' tcx > {
599
611
tcx : TyCtxt < ' tcx > ,
600
612
live_symbols : FxHashSet < LocalDefId > ,
613
+ ignored_derived_traits : FxHashMap < DefId , Vec < DefId > > ,
601
614
}
602
615
603
616
impl < ' tcx > DeadVisitor < ' tcx > {
@@ -666,7 +679,34 @@ impl<'tcx> DeadVisitor<'tcx> {
666
679
self . tcx . struct_span_lint_hir ( lint:: builtin:: DEAD_CODE , id, span, |lint| {
667
680
let def_id = self . tcx . hir ( ) . local_def_id ( id) ;
668
681
let descr = self . tcx . def_kind ( def_id) . descr ( def_id. to_def_id ( ) ) ;
669
- lint. build ( & format ! ( "{} is never {}: `{}`" , descr, participle, name) ) . emit ( )
682
+ let mut err = lint. build ( & format ! ( "{} is never {}: `{}`" , descr, participle, name) ) ;
683
+ let hir = self . tcx . hir ( ) ;
684
+ if let Some ( encl_scope) = hir. get_enclosing_scope ( id) {
685
+ if let Some ( encl_def_id) = hir. opt_local_def_id ( encl_scope) {
686
+ if let Some ( ign_traits) = self . ignored_derived_traits . get ( & encl_def_id. to_def_id ( ) ) {
687
+ let traits_str = ign_traits
688
+ . iter ( )
689
+ . map ( |t| format ! ( "`{}`" , self . tcx. item_name( * t) ) ) . collect :: < Vec < _ > > ( )
690
+ . join ( " and " ) ;
691
+ let plural_s = pluralize ! ( ign_traits. len( ) ) ;
692
+ let article = if ign_traits. len ( ) > 1 { "" } else { "a " } ;
693
+ let is_are = if ign_traits. len ( ) > 1 { "these are" } else { "this is" } ;
694
+ let msg = format ! ( "`{}` has {}derived impl{} for the trait{} {}, but {} ignored during dead code analysis" ,
695
+ self . tcx. item_name( encl_def_id. to_def_id( ) ) ,
696
+ article,
697
+ plural_s,
698
+ plural_s,
699
+ traits_str,
700
+ is_are) ;
701
+ if let Some ( span) = self . tcx . def_ident_span ( encl_def_id) {
702
+ err. span_note ( span, & msg) ;
703
+ } else {
704
+ err. note ( & msg) ;
705
+ }
706
+ }
707
+ }
708
+ }
709
+ err. emit ( ) ;
670
710
} ) ;
671
711
}
672
712
}
@@ -796,7 +836,7 @@ impl<'tcx> Visitor<'tcx> for DeadVisitor<'tcx> {
796
836
797
837
pub fn check_crate ( tcx : TyCtxt < ' _ > ) {
798
838
let access_levels = & tcx. privacy_access_levels ( ( ) ) ;
799
- let live_symbols = find_live ( tcx, access_levels) ;
800
- let mut visitor = DeadVisitor { tcx, live_symbols } ;
839
+ let ( live_symbols, ignored_derived_traits ) = find_live ( tcx, access_levels) ;
840
+ let mut visitor = DeadVisitor { tcx, live_symbols, ignored_derived_traits } ;
801
841
tcx. hir ( ) . walk_toplevel_module ( & mut visitor) ;
802
842
}
0 commit comments