2
2
// closely. The idea is that all reachable symbols are live, codes called
3
3
// from live codes are live, and everything else is dead.
4
4
5
+ use itertools:: Itertools ;
5
6
use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
6
- use rustc_errors:: pluralize;
7
+ use rustc_errors:: { pluralize, MultiSpan } ;
7
8
use rustc_hir as hir;
8
9
use rustc_hir:: def:: { CtorOf , DefKind , Res } ;
9
10
use rustc_hir:: def_id:: { DefId , LocalDefId } ;
@@ -183,10 +184,10 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
183
184
}
184
185
}
185
186
186
- if let hir:: ExprKind :: Assign ( lhs, rhs, _) = assign. kind {
187
- if check_for_self_assign_helper ( self . typeck_results ( ) , lhs, rhs)
187
+ if let hir:: ExprKind :: Assign ( lhs, rhs, _) = assign. kind
188
+ && check_for_self_assign_helper ( self . typeck_results ( ) , lhs, rhs)
188
189
&& !assign. span . from_expansion ( )
189
- {
190
+ {
190
191
let is_field_assign = matches ! ( lhs. kind, hir:: ExprKind :: Field ( ..) ) ;
191
192
self . tcx . struct_span_lint_hir (
192
193
lint:: builtin:: DEAD_CODE ,
@@ -201,7 +202,6 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
201
202
. emit ( ) ;
202
203
} ,
203
204
)
204
- }
205
205
}
206
206
}
207
207
@@ -251,33 +251,30 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
251
251
return false ;
252
252
}
253
253
254
- if let Some ( trait_of) = self . tcx . trait_id_of_impl ( impl_of) {
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 ( adt_def_id) = adt_def. did ( ) . as_local ( ) {
259
- self . ignored_derived_traits
260
- . entry ( adt_def_id)
261
- . or_default ( )
262
- . push ( ( trait_of, impl_of) ) ;
263
- }
264
- }
265
- return true ;
254
+ if let Some ( trait_of) = self . tcx . trait_id_of_impl ( impl_of)
255
+ && self . tcx . has_attr ( trait_of, sym:: rustc_trivial_field_reads)
256
+ {
257
+ let trait_ref = self . tcx . impl_trait_ref ( impl_of) . unwrap ( ) ;
258
+ if let ty:: Adt ( adt_def, _) = trait_ref. self_ty ( ) . kind ( )
259
+ && let Some ( adt_def_id) = adt_def. did ( ) . as_local ( )
260
+ {
261
+ self . ignored_derived_traits
262
+ . entry ( adt_def_id)
263
+ . or_default ( )
264
+ . push ( ( trait_of, impl_of) ) ;
266
265
}
266
+ return true ;
267
267
}
268
268
}
269
269
270
270
return false ;
271
271
}
272
272
273
273
fn visit_node ( & mut self , node : Node < ' tcx > ) {
274
- if let Some ( item_def_id) = match node {
275
- Node :: ImplItem ( hir:: ImplItem { def_id, .. } ) => Some ( def_id. to_def_id ( ) ) ,
276
- _ => None ,
277
- } {
278
- if self . should_ignore_item ( item_def_id) {
279
- return ;
280
- }
274
+ if let Node :: ImplItem ( hir:: ImplItem { def_id, .. } ) = node
275
+ && self . should_ignore_item ( def_id. to_def_id ( ) )
276
+ {
277
+ return ;
281
278
}
282
279
283
280
let had_repr_c = self . repr_has_repr_c ;
@@ -534,10 +531,10 @@ fn check_item<'tcx>(
534
531
}
535
532
DefKind :: Struct => {
536
533
let item = tcx. hir ( ) . item ( id) ;
537
- if let hir:: ItemKind :: Struct ( ref variant_data, _) = item. kind {
538
- if let Some ( ctor_hir_id) = variant_data. ctor_hir_id ( ) {
539
- struct_constructors . insert ( tcx . hir ( ) . local_def_id ( ctor_hir_id ) , item . def_id ) ;
540
- }
534
+ if let hir:: ItemKind :: Struct ( ref variant_data, _) = item. kind
535
+ && let Some ( ctor_hir_id) = variant_data. ctor_hir_id ( )
536
+ {
537
+ struct_constructors . insert ( tcx . hir ( ) . local_def_id ( ctor_hir_id ) , item . def_id ) ;
541
538
}
542
539
}
543
540
DefKind :: GlobalAsm => {
@@ -626,6 +623,13 @@ fn live_symbols_and_ignored_derived_traits<'tcx>(
626
623
( symbol_visitor. live_symbols , symbol_visitor. ignored_derived_traits )
627
624
}
628
625
626
+ struct DeadVariant {
627
+ hir_id : hir:: HirId ,
628
+ span : Span ,
629
+ name : Symbol ,
630
+ level : lint:: Level ,
631
+ }
632
+
629
633
struct DeadVisitor < ' tcx > {
630
634
tcx : TyCtxt < ' tcx > ,
631
635
live_symbols : & ' tcx FxHashSet < LocalDefId > ,
@@ -677,56 +681,129 @@ impl<'tcx> DeadVisitor<'tcx> {
677
681
let inherent_impls = self . tcx . inherent_impls ( def_id) ;
678
682
for & impl_did in inherent_impls. iter ( ) {
679
683
for item_did in self . tcx . associated_item_def_ids ( impl_did) {
680
- if let Some ( def_id) = item_did. as_local ( ) {
681
- if self . live_symbols . contains ( & def_id) {
682
- return true ;
683
- }
684
+ if let Some ( def_id) = item_did. as_local ( )
685
+ && self . live_symbols . contains ( & def_id)
686
+ {
687
+ return true ;
684
688
}
685
689
}
686
690
}
687
691
false
688
692
}
689
693
694
+ fn warn_multiple_dead_codes (
695
+ & self ,
696
+ dead_codes : & [ ( hir:: HirId , Span , Symbol ) ] ,
697
+ participle : & str ,
698
+ parent_hir_id : Option < hir:: HirId > ,
699
+ ) {
700
+ if let Some ( ( id, _, name) ) = dead_codes. first ( )
701
+ && !name. as_str ( ) . starts_with ( '_' )
702
+ {
703
+ self . tcx . struct_span_lint_hir (
704
+ lint:: builtin:: DEAD_CODE ,
705
+ * id,
706
+ MultiSpan :: from_spans (
707
+ dead_codes. iter ( ) . map ( |( _, span, _) | * span) . collect ( ) ,
708
+ ) ,
709
+ |lint| {
710
+ let def_id = self . tcx . hir ( ) . local_def_id ( * id) ;
711
+ let descr = self . tcx . def_kind ( def_id) . descr ( def_id. to_def_id ( ) ) ;
712
+ let span_len = dead_codes. len ( ) ;
713
+ let names = match & dead_codes. iter ( ) . map ( |( _, _, n) | n. to_string ( ) ) . collect :: < Vec < _ > > ( ) [ ..]
714
+ {
715
+ _ if span_len > 6 => String :: new ( ) ,
716
+ [ name] => format ! ( "`{name}` " ) ,
717
+ [ names @ .., last] => {
718
+ format ! ( "{} and `{last}` " , names. iter( ) . map( |name| format!( "`{name}`" ) ) . join( ", " ) )
719
+ }
720
+ [ ] => unreachable ! ( ) ,
721
+ } ;
722
+ let mut err = lint. build ( & format ! (
723
+ "{these}{descr}{s} {names}{are} never {participle}" ,
724
+ these = if span_len > 6 { "multiple " } else { "" } ,
725
+ s = pluralize!( span_len) ,
726
+ are = pluralize!( "is" , span_len) ,
727
+ ) ) ;
728
+ let hir = self . tcx . hir ( ) ;
729
+ if let Some ( parent_hir_id) = parent_hir_id
730
+ && let Some ( parent_node) = hir. find ( parent_hir_id)
731
+ && let Node :: Item ( item) = parent_node
732
+ {
733
+ let def_id = self . tcx . hir ( ) . local_def_id ( parent_hir_id) ;
734
+ let parent_descr = self . tcx . def_kind ( def_id) . descr ( def_id. to_def_id ( ) ) ;
735
+ err. span_label (
736
+ item. ident . span ,
737
+ format ! (
738
+ "{descr}{s} in this {parent_descr}" ,
739
+ s = pluralize!( span_len)
740
+ ) ,
741
+ ) ;
742
+ }
743
+ if let Some ( encl_scope) = hir. get_enclosing_scope ( * id)
744
+ && let Some ( encl_def_id) = hir. opt_local_def_id ( encl_scope)
745
+ && let Some ( ign_traits) = self . ignored_derived_traits . get ( & encl_def_id)
746
+ {
747
+ let traits_str = ign_traits
748
+ . iter ( )
749
+ . map ( |( trait_id, _) | format ! ( "`{}`" , self . tcx. item_name( * trait_id) ) )
750
+ . collect :: < Vec < _ > > ( )
751
+ . join ( " and " ) ;
752
+ let plural_s = pluralize ! ( ign_traits. len( ) ) ;
753
+ let article = if ign_traits. len ( ) > 1 { "" } else { "a " } ;
754
+ let is_are = if ign_traits. len ( ) > 1 { "these are" } else { "this is" } ;
755
+ let msg = format ! (
756
+ "`{}` has {}derived impl{} for the trait{} {}, but {} \
757
+ intentionally ignored during dead code analysis",
758
+ self . tcx. item_name( encl_def_id. to_def_id( ) ) ,
759
+ article,
760
+ plural_s,
761
+ plural_s,
762
+ traits_str,
763
+ is_are
764
+ ) ;
765
+ err. note ( & msg) ;
766
+ }
767
+ err. emit ( ) ;
768
+ } ,
769
+ ) ;
770
+ }
771
+ }
772
+
773
+ fn warn_dead_fields_and_variants (
774
+ & self ,
775
+ hir_id : hir:: HirId ,
776
+ participle : & str ,
777
+ dead_codes : Vec < DeadVariant > ,
778
+ ) {
779
+ let mut dead_codes = dead_codes
780
+ . iter ( )
781
+ . filter ( |v| !v. name . as_str ( ) . starts_with ( '_' ) )
782
+ . map ( |v| v)
783
+ . collect :: < Vec < & DeadVariant > > ( ) ;
784
+ if dead_codes. is_empty ( ) {
785
+ return ;
786
+ }
787
+ dead_codes. sort_by_key ( |v| v. level ) ;
788
+ for ( _, group) in & dead_codes. into_iter ( ) . group_by ( |v| v. level ) {
789
+ self . warn_multiple_dead_codes (
790
+ & group
791
+ . map ( |v| ( v. hir_id , v. span , v. name ) )
792
+ . collect :: < Vec < ( hir:: HirId , Span , Symbol ) > > ( ) ,
793
+ participle,
794
+ Some ( hir_id) ,
795
+ ) ;
796
+ }
797
+ }
798
+
690
799
fn warn_dead_code (
691
800
& mut self ,
692
801
id : hir:: HirId ,
693
802
span : rustc_span:: Span ,
694
803
name : Symbol ,
695
804
participle : & str ,
696
805
) {
697
- if !name. as_str ( ) . starts_with ( '_' ) {
698
- self . tcx . struct_span_lint_hir ( lint:: builtin:: DEAD_CODE , id, span, |lint| {
699
- let def_id = self . tcx . hir ( ) . local_def_id ( id) ;
700
- let descr = self . tcx . def_kind ( def_id) . descr ( def_id. to_def_id ( ) ) ;
701
- let mut err = lint. build ( & format ! ( "{} is never {}: `{}`" , descr, participle, name) ) ;
702
- let hir = self . tcx . hir ( ) ;
703
- if let Some ( encl_scope) = hir. get_enclosing_scope ( id)
704
- && let Some ( encl_def_id) = hir. opt_local_def_id ( encl_scope)
705
- && let Some ( ign_traits) = self . ignored_derived_traits . get ( & encl_def_id)
706
- {
707
- let traits_str = ign_traits
708
- . iter ( )
709
- . map ( |( trait_id, _) | format ! ( "`{}`" , self . tcx. item_name( * trait_id) ) )
710
- . collect :: < Vec < _ > > ( )
711
- . join ( " and " ) ;
712
- let plural_s = pluralize ! ( ign_traits. len( ) ) ;
713
- let article = if ign_traits. len ( ) > 1 { "" } else { "a " } ;
714
- let is_are = if ign_traits. len ( ) > 1 { "these are" } else { "this is" } ;
715
- let msg = format ! (
716
- "`{}` has {}derived impl{} for the trait{} {}, but {} \
717
- intentionally ignored during dead code analysis",
718
- self . tcx. item_name( encl_def_id. to_def_id( ) ) ,
719
- article,
720
- plural_s,
721
- plural_s,
722
- traits_str,
723
- is_are
724
- ) ;
725
- err. note ( & msg) ;
726
- }
727
- err. emit ( ) ;
728
- } ) ;
729
- }
806
+ self . warn_multiple_dead_codes ( & [ ( id, span, name) ] , participle, None ) ;
730
807
}
731
808
}
732
809
@@ -780,15 +857,40 @@ impl<'tcx> Visitor<'tcx> for DeadVisitor<'tcx> {
780
857
// This visitor should only visit a single module at a time.
781
858
fn visit_mod ( & mut self , _: & ' tcx hir:: Mod < ' tcx > , _: Span , _: hir:: HirId ) { }
782
859
860
+ fn visit_enum_def (
861
+ & mut self ,
862
+ enum_definition : & ' tcx hir:: EnumDef < ' tcx > ,
863
+ generics : & ' tcx hir:: Generics < ' tcx > ,
864
+ item_id : hir:: HirId ,
865
+ _: Span ,
866
+ ) {
867
+ intravisit:: walk_enum_def ( self , enum_definition, generics, item_id) ;
868
+ let dead_variants = enum_definition
869
+ . variants
870
+ . iter ( )
871
+ . filter_map ( |variant| {
872
+ if self . should_warn_about_variant ( & variant) {
873
+ Some ( DeadVariant {
874
+ hir_id : variant. id ,
875
+ span : variant. span ,
876
+ name : variant. ident . name ,
877
+ level : self . tcx . lint_level_at_node ( lint:: builtin:: DEAD_CODE , variant. id ) . 0 ,
878
+ } )
879
+ } else {
880
+ None
881
+ }
882
+ } )
883
+ . collect ( ) ;
884
+ self . warn_dead_fields_and_variants ( item_id, "constructed" , dead_variants)
885
+ }
886
+
783
887
fn visit_variant (
784
888
& mut self ,
785
889
variant : & ' tcx hir:: Variant < ' tcx > ,
786
890
g : & ' tcx hir:: Generics < ' tcx > ,
787
891
id : hir:: HirId ,
788
892
) {
789
- if self . should_warn_about_variant ( & variant) {
790
- self . warn_dead_code ( variant. id , variant. span , variant. ident . name , "constructed" ) ;
791
- } else {
893
+ if !self . should_warn_about_variant ( & variant) {
792
894
intravisit:: walk_variant ( self , variant, g, id) ;
793
895
}
794
896
}
@@ -800,11 +902,35 @@ impl<'tcx> Visitor<'tcx> for DeadVisitor<'tcx> {
800
902
intravisit:: walk_foreign_item ( self , fi) ;
801
903
}
802
904
803
- fn visit_field_def ( & mut self , field : & ' tcx hir:: FieldDef < ' tcx > ) {
804
- if self . should_warn_about_field ( & field) {
805
- self . warn_dead_code ( field. hir_id , field. span , field. ident . name , "read" ) ;
806
- }
807
- intravisit:: walk_field_def ( self , field) ;
905
+ fn visit_variant_data (
906
+ & mut self ,
907
+ def : & ' tcx hir:: VariantData < ' tcx > ,
908
+ _: Symbol ,
909
+ _: & hir:: Generics < ' _ > ,
910
+ id : hir:: HirId ,
911
+ _: rustc_span:: Span ,
912
+ ) {
913
+ intravisit:: walk_struct_def ( self , def) ;
914
+ let dead_fields = def
915
+ . fields ( )
916
+ . iter ( )
917
+ . filter_map ( |field| {
918
+ if self . should_warn_about_field ( & field) {
919
+ Some ( DeadVariant {
920
+ hir_id : field. hir_id ,
921
+ span : field. span ,
922
+ name : field. ident . name ,
923
+ level : self
924
+ . tcx
925
+ . lint_level_at_node ( lint:: builtin:: DEAD_CODE , field. hir_id )
926
+ . 0 ,
927
+ } )
928
+ } else {
929
+ None
930
+ }
931
+ } )
932
+ . collect ( ) ;
933
+ self . warn_dead_fields_and_variants ( id, "read" , dead_fields)
808
934
}
809
935
810
936
fn visit_impl_item ( & mut self , impl_item : & ' tcx hir:: ImplItem < ' tcx > ) {
0 commit comments