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 } ;
@@ -15,6 +16,7 @@ use rustc_middle::middle::privacy;
15
16
use rustc_middle:: ty:: { self , DefIdTree , TyCtxt } ;
16
17
use rustc_session:: lint;
17
18
use rustc_span:: symbol:: { sym, Symbol } ;
19
+ use rustc_span:: Span ;
18
20
use std:: mem;
19
21
20
22
// Any local node that may call something in its body block should be
@@ -47,6 +49,10 @@ struct MarkSymbolVisitor<'tcx> {
47
49
ignore_variant_stack : Vec < DefId > ,
48
50
// maps from tuple struct constructors to tuple struct items
49
51
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 ) > > ,
50
56
}
51
57
52
58
impl < ' tcx > MarkSymbolVisitor < ' tcx > {
@@ -242,14 +248,24 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
242
248
/// Automatically generated items marked with `rustc_trivial_field_reads`
243
249
/// will be ignored for the purposes of dead code analysis (see PR #85200
244
250
/// for discussion).
245
- fn should_ignore_item ( & self , def_id : DefId ) -> bool {
251
+ fn should_ignore_item ( & mut self , def_id : DefId ) -> bool {
246
252
if let Some ( impl_of) = self . tcx . impl_of_method ( def_id) {
247
253
if !self . tcx . has_attr ( impl_of, sym:: automatically_derived) {
248
254
return false ;
249
255
}
250
256
251
257
if let Some ( trait_of) = self . tcx . trait_id_of_impl ( impl_of) {
252
258
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
+ }
253
269
return true ;
254
270
}
255
271
}
@@ -571,7 +587,7 @@ fn create_and_seed_worklist<'tcx>(
571
587
fn find_live < ' tcx > (
572
588
tcx : TyCtxt < ' tcx > ,
573
589
access_levels : & privacy:: AccessLevels ,
574
- ) -> FxHashSet < LocalDefId > {
590
+ ) -> ( FxHashSet < LocalDefId > , FxHashMap < DefId , Vec < ( Span , DefId ) > > ) {
575
591
let ( worklist, struct_constructors) = create_and_seed_worklist ( tcx, access_levels) ;
576
592
let mut symbol_visitor = MarkSymbolVisitor {
577
593
worklist,
@@ -584,14 +600,16 @@ fn find_live<'tcx>(
584
600
pub_visibility : false ,
585
601
ignore_variant_stack : vec ! [ ] ,
586
602
struct_constructors,
603
+ ignored_derived_traits : FxHashMap :: default ( ) ,
587
604
} ;
588
605
symbol_visitor. mark_live_symbols ( ) ;
589
- symbol_visitor. live_symbols
606
+ ( symbol_visitor. live_symbols , symbol_visitor . ignored_derived_traits )
590
607
}
591
608
592
609
struct DeadVisitor < ' tcx > {
593
610
tcx : TyCtxt < ' tcx > ,
594
611
live_symbols : FxHashSet < LocalDefId > ,
612
+ ignored_derived_traits : FxHashMap < DefId , Vec < ( Span , DefId ) > > ,
595
613
}
596
614
597
615
impl < ' tcx > DeadVisitor < ' tcx > {
@@ -660,7 +678,37 @@ impl<'tcx> DeadVisitor<'tcx> {
660
678
self . tcx . struct_span_lint_hir ( lint:: builtin:: DEAD_CODE , id, span, |lint| {
661
679
let def_id = self . tcx . hir ( ) . local_def_id ( id) ;
662
680
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 ( ) ;
664
712
} ) ;
665
713
}
666
714
}
@@ -790,7 +838,7 @@ impl<'tcx> Visitor<'tcx> for DeadVisitor<'tcx> {
790
838
791
839
pub fn check_crate ( tcx : TyCtxt < ' _ > ) {
792
840
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 } ;
795
843
tcx. hir ( ) . walk_toplevel_module ( & mut visitor) ;
796
844
}
0 commit comments