44// is dead.
55
66use hir:: def_id:: { LocalDefIdMap , LocalDefIdSet } ;
7- use hir:: AssocItemKind ;
7+ use hir:: ItemKind ;
88use itertools:: Itertools ;
99use rustc_data_structures:: unord:: UnordSet ;
1010use rustc_errors:: MultiSpan ;
@@ -16,7 +16,7 @@ use rustc_hir::{Node, PatKind, TyKind};
1616use rustc_middle:: middle:: codegen_fn_attrs:: CodegenFnAttrFlags ;
1717use rustc_middle:: middle:: privacy:: Level ;
1818use rustc_middle:: query:: Providers ;
19- use rustc_middle:: ty:: { self , TyCtxt } ;
19+ use rustc_middle:: ty:: { self , TyCtxt , Visibility } ;
2020use rustc_session:: lint;
2121use rustc_span:: symbol:: { sym, Symbol } ;
2222use rustc_target:: abi:: FieldIdx ;
@@ -377,20 +377,42 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
377377 intravisit:: walk_item ( self , item)
378378 }
379379 hir:: ItemKind :: ForeignMod { .. } => { }
380+ hir:: ItemKind :: Trait ( ..) => {
381+ // mark dependent traits live
382+ for impl_def_id in self . tcx . all_impls ( item. owner_id . to_def_id ( ) ) {
383+ if let Some ( local_def_id) = impl_def_id. as_local ( )
384+ && let ItemKind :: Impl ( impl_ref) =
385+ self . tcx . hir ( ) . expect_item ( local_def_id) . kind
386+ {
387+ intravisit:: walk_generics ( self , impl_ref. generics ) ;
388+ }
389+ }
390+
391+ intravisit:: walk_item ( self , item)
392+ }
380393 _ => intravisit:: walk_item ( self , item) ,
381394 } ,
382395 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- // ```
396+ // mark corresponing ImplTerm live
397+ let def_id = trait_item. owner_id . to_def_id ( ) ;
398+ if let Some ( trait_def_id) = self . tcx . trait_of_item ( def_id) {
399+ // for assoc fn without self, mark the trait live
400+ self . check_def_id ( trait_def_id) ;
401+
402+ for impl_def in self . tcx . all_impls ( trait_def_id) {
403+ if let Some ( impl_def_id) = impl_def. as_local ( )
404+ && let ItemKind :: Impl ( impl_ref) =
405+ self . tcx . hir ( ) . expect_item ( impl_def_id) . kind
406+ {
407+ self . check_def_id ( impl_def) ;
408+ for impl_item in impl_ref. items {
409+ if Some ( def_id) == impl_item. trait_item_def_id {
410+ self . check_def_id ( impl_item. id . owner_id . to_def_id ( ) ) ;
411+ }
412+ }
413+ }
414+ }
415+ }
394416 intravisit:: walk_trait_item ( self , trait_item) ;
395417 }
396418 Node :: ImplItem ( impl_item) => {
@@ -639,23 +661,6 @@ fn check_item<'tcx>(
639661 }
640662 }
641663 DefKind :: Impl { 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 ( ) {
656- worklist. push ( ( id. owner_id . def_id , ComesFromAllowExpect :: No ) ) ;
657- }
658-
659664 // get DefIds from another query
660665 let local_def_ids = tcx
661666 . associated_item_def_ids ( id. owner_id )
@@ -664,9 +669,9 @@ fn check_item<'tcx>(
664669
665670 // And we access the Map here to get HirId from LocalDefId
666671 for id in local_def_ids {
667- if of_trait {
668- // FIXME: not push impl item into worklist by default,
669- // pushed when corresponding trait items are reachable.
672+ if tcx . local_visibility ( id ) == Visibility :: Public
673+ || of_trait && ! matches ! ( tcx . def_kind ( id ) , DefKind :: AssocFn )
674+ {
670675 worklist. push ( ( id, ComesFromAllowExpect :: No ) ) ;
671676 } else if let Some ( comes_from_allow) = has_allow_dead_code_or_lang_attr ( tcx, id) {
672677 worklist. push ( ( id, comes_from_allow) ) ;
@@ -697,7 +702,7 @@ fn check_trait_item(
697702 use hir:: TraitItemKind :: { Const , Fn } ;
698703 if matches ! ( tcx. def_kind( id. owner_id) , DefKind :: AssocConst | DefKind :: AssocFn ) {
699704 let trait_item = tcx. hir ( ) . trait_item ( id) ;
700- if matches ! ( trait_item. kind, Const ( _, Some ( _) ) | Fn ( _ , hir :: TraitFn :: Provided ( _ ) ) )
705+ if matches ! ( trait_item. kind, Const ( _, Some ( _) ) | Fn ( .. ) )
701706 && let Some ( comes_from_allow) =
702707 has_allow_dead_code_or_lang_attr ( tcx, trait_item. owner_id . def_id )
703708 {
@@ -965,14 +970,8 @@ impl<'tcx> DeadVisitor<'tcx> {
965970 | DefKind :: TyAlias
966971 | DefKind :: Enum
967972 | DefKind :: Union
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- }
973+ | DefKind :: ForeignTy
974+ | DefKind :: Trait => self . warn_dead_code ( def_id, "used" ) ,
976975 DefKind :: Struct => self . warn_dead_code ( def_id, "constructed" ) ,
977976 DefKind :: Variant | DefKind :: Field => bug ! ( "should be handled specially" ) ,
978977 _ => { }
@@ -1001,24 +1000,7 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) {
10011000 let mut dead_items = Vec :: new ( ) ;
10021001 for item in impl_item. items {
10031002 let def_id = item. id . owner_id . 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 {
1003+ if !visitor. is_live_code ( def_id) {
10221004 let name = tcx. item_name ( def_id. to_def_id ( ) ) ;
10231005 let hir_id = tcx. hir ( ) . local_def_id_to_hir_id ( def_id) ;
10241006 let level = tcx. lint_level_at_node ( lint:: builtin:: DEAD_CODE , hir_id) . 0 ;
@@ -1093,9 +1075,7 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) {
10931075 }
10941076
10951077 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- }
1078+ visitor. check_definition ( trait_item. owner_id . def_id ) ;
10991079 }
11001080}
11011081
0 commit comments