4
4
// is dead.
5
5
6
6
use hir:: def_id:: { LocalDefIdMap , LocalDefIdSet } ;
7
+ use hir:: AssocItemKind ;
7
8
use itertools:: Itertools ;
8
9
use rustc_data_structures:: unord:: UnordSet ;
9
10
use rustc_errors:: MultiSpan ;
@@ -379,6 +380,17 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
379
380
_ => intravisit:: walk_item ( self , item) ,
380
381
} ,
381
382
Node :: TraitItem ( trait_item) => {
383
+ // FIXME: could we get all impl terms for the trait item?
384
+ // then we can add them into worklist when the trait item is live,
385
+ // so that we won't lose any lost chain in the impl terms.
386
+ // this also allows us to lint such code:
387
+ // ```rust
388
+ // struct UnusedStruct; //~ WARN
389
+ // trait UnusedTrait { //~ WARN
390
+ // fn foo() {}
391
+ // }
392
+ // impl UnusedTrait for UnusedStruct { fn foo() {} }
393
+ // ```
382
394
intravisit:: walk_trait_item ( self , trait_item) ;
383
395
}
384
396
Node :: ImplItem ( impl_item) => {
@@ -627,7 +639,20 @@ fn check_item<'tcx>(
627
639
}
628
640
}
629
641
DefKind :: Impl { of_trait } => {
630
- if of_trait {
642
+ // lints unused struct and traits after 2024, e.g.,
643
+ // ```rust
644
+ // #[derive(Debug)]
645
+ // struct Unused; //~ WARN
646
+ //
647
+ // trait Foo {} //~ WARN
648
+ // impl Foo for () {}
649
+ // ```
650
+ // but we still cannot lint unused traits whose impl blocks have methods:
651
+ // ```rust
652
+ // trait Foo { fn foo(); }
653
+ // impl Foo for () { fn foo() {} }
654
+ // ```
655
+ if of_trait && !tcx. hir ( ) . item ( id) . span . source_callsite ( ) . at_least_rust_2024 ( ) {
631
656
worklist. push ( ( id. owner_id . def_id , ComesFromAllowExpect :: No ) ) ;
632
657
}
633
658
@@ -640,6 +665,8 @@ fn check_item<'tcx>(
640
665
// And we access the Map here to get HirId from LocalDefId
641
666
for id in local_def_ids {
642
667
if of_trait {
668
+ // FIXME: not push impl item into worklist by default,
669
+ // pushed when corresponding trait items are reachable.
643
670
worklist. push ( ( id, ComesFromAllowExpect :: No ) ) ;
644
671
} else if let Some ( comes_from_allow) = has_allow_dead_code_or_lang_attr ( tcx, id) {
645
672
worklist. push ( ( id, comes_from_allow) ) ;
@@ -939,6 +966,13 @@ impl<'tcx> DeadVisitor<'tcx> {
939
966
| DefKind :: Enum
940
967
| DefKind :: Union
941
968
| DefKind :: ForeignTy => self . warn_dead_code ( def_id, "used" ) ,
969
+ DefKind :: Trait => {
970
+ if let Some ( Node :: Item ( item) ) = self . tcx . hir ( ) . find_by_def_id ( def_id)
971
+ && item. span . at_least_rust_2024 ( )
972
+ {
973
+ self . warn_dead_code ( def_id, "used" )
974
+ }
975
+ }
942
976
DefKind :: Struct => self . warn_dead_code ( def_id, "constructed" ) ,
943
977
DefKind :: Variant | DefKind :: Field => bug ! ( "should be handled specially" ) ,
944
978
_ => { }
@@ -967,7 +1001,24 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) {
967
1001
let mut dead_items = Vec :: new ( ) ;
968
1002
for item in impl_item. items {
969
1003
let def_id = item. id . owner_id . def_id ;
970
- if !visitor. is_live_code ( def_id) {
1004
+ let is_dead_code = if !visitor. is_live_code ( def_id) {
1005
+ true
1006
+ } else if item. span . edition ( ) . at_least_rust_2024 ( ) // temporary
1007
+ && let AssocItemKind :: Fn { .. } = item. kind
1008
+ && let Some ( local_def_id) = item. trait_item_def_id . and_then ( DefId :: as_local)
1009
+ && !visitor. is_live_code ( local_def_id)
1010
+ && has_allow_dead_code_or_lang_attr ( tcx, local_def_id) . is_none ( )
1011
+ {
1012
+ // lint methods in impl if we are sure the corresponding methods in trait are dead,
1013
+ // but the chain of dead code within the methods in impl would be lost.
1014
+
1015
+ // FIXME: the better way is to mark trait items and corresponding impl items active,
1016
+ // then the rests are dead, which requires the above FIXME at line 383
1017
+ true
1018
+ } else {
1019
+ false
1020
+ } ;
1021
+ if is_dead_code {
971
1022
let name = tcx. item_name ( def_id. to_def_id ( ) ) ;
972
1023
let hir_id = tcx. hir ( ) . local_def_id_to_hir_id ( def_id) ;
973
1024
let level = tcx. lint_level_at_node ( lint:: builtin:: DEAD_CODE , hir_id) . 0 ;
@@ -1041,7 +1092,11 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) {
1041
1092
visitor. check_definition ( foreign_item. owner_id . def_id ) ;
1042
1093
}
1043
1094
1044
- // We do not warn trait items.
1095
+ for trait_item in module_items. trait_items ( ) {
1096
+ if tcx. hir ( ) . trait_item ( trait_item) . span . at_least_rust_2024 ( ) {
1097
+ visitor. check_definition ( trait_item. owner_id . def_id ) ;
1098
+ }
1099
+ }
1045
1100
}
1046
1101
1047
1102
pub ( crate ) fn provide ( providers : & mut Providers ) {
0 commit comments