Skip to content

Commit 2c130ae

Browse files
committed
Make traits / trait methods detected by the dead code lint!
1 parent 44a430f commit 2c130ae

File tree

4 files changed

+73
-84
lines changed

4 files changed

+73
-84
lines changed

compiler/rustc_passes/src/dead.rs

Lines changed: 48 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
// is dead.
55

66
use hir::def_id::{LocalDefIdMap, LocalDefIdSet};
7-
use hir::AssocItemKind;
7+
use hir::ItemKind;
88
use itertools::Itertools;
99
use rustc_data_structures::unord::UnordSet;
1010
use rustc_errors::MultiSpan;
@@ -16,7 +16,7 @@ use rustc_hir::{Node, PatKind, TyKind};
1616
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
1717
use rustc_middle::middle::privacy::Level;
1818
use rustc_middle::query::Providers;
19-
use rustc_middle::ty::{self, TyCtxt};
19+
use rustc_middle::ty::{self, TyCtxt, Visibility};
2020
use rustc_session::lint;
2121
use rustc_span::symbol::{sym, Symbol};
2222
use rustc_target::abi::FieldIdx;
@@ -377,20 +377,46 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
377377
intravisit::walk_item(self, item)
378378
}
379379
hir::ItemKind::ForeignMod { .. } => {}
380+
hir::ItemKind::Trait(..) => {
381+
for impl_def_id in self.tcx.all_impls(item.owner_id.to_def_id()) {
382+
if let Some(local_def_id) = impl_def_id.as_local()
383+
&& let ItemKind::Impl(impl_ref) =
384+
self.tcx.hir().expect_item(local_def_id).kind
385+
{
386+
// skip items
387+
// mark dependent traits live
388+
intravisit::walk_generics(self, impl_ref.generics);
389+
// mark dependent parameters live
390+
intravisit::walk_path(self, impl_ref.of_trait.unwrap().path);
391+
}
392+
}
393+
394+
intravisit::walk_item(self, item)
395+
}
380396
_ => intravisit::walk_item(self, item),
381397
},
382398
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-
// ```
399+
// mark corresponing ImplTerm live
400+
let def_id = trait_item.owner_id.to_def_id();
401+
if let Some(trait_def_id) = self.tcx.trait_of_item(def_id) {
402+
// mark the trait live
403+
self.check_def_id(trait_def_id);
404+
405+
for impl_def in self.tcx.all_impls(trait_def_id) {
406+
if let Some(impl_def_id) = impl_def.as_local()
407+
&& let ItemKind::Impl(impl_ref) =
408+
self.tcx.hir().expect_item(impl_def_id).kind
409+
{
410+
// mark self_ty live
411+
intravisit::walk_ty(self, impl_ref.self_ty);
412+
for impl_item in impl_ref.items {
413+
if Some(def_id) == impl_item.trait_item_def_id {
414+
self.check_def_id(impl_item.id.owner_id.to_def_id());
415+
}
416+
}
417+
}
418+
}
419+
}
394420
intravisit::walk_trait_item(self, trait_item);
395421
}
396422
Node::ImplItem(impl_item) => {
@@ -639,23 +665,6 @@ fn check_item<'tcx>(
639665
}
640666
}
641667
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-
659668
// get DefIds from another query
660669
let local_def_ids = tcx
661670
.associated_item_def_ids(id.owner_id)
@@ -664,9 +673,10 @@ fn check_item<'tcx>(
664673

665674
// And we access the Map here to get HirId from LocalDefId
666675
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.
676+
if of_trait
677+
&& (!matches!(tcx.def_kind(id), DefKind::AssocFn)
678+
|| tcx.local_visibility(id) == Visibility::Public)
679+
{
670680
worklist.push((id, ComesFromAllowExpect::No));
671681
} else if let Some(comes_from_allow) = has_allow_dead_code_or_lang_attr(tcx, id) {
672682
worklist.push((id, comes_from_allow));
@@ -697,7 +707,7 @@ fn check_trait_item(
697707
use hir::TraitItemKind::{Const, Fn};
698708
if matches!(tcx.def_kind(id.owner_id), DefKind::AssocConst | DefKind::AssocFn) {
699709
let trait_item = tcx.hir().trait_item(id);
700-
if matches!(trait_item.kind, Const(_, Some(_)) | Fn(_, hir::TraitFn::Provided(_)))
710+
if matches!(trait_item.kind, Const(_, Some(_)) | Fn(..))
701711
&& let Some(comes_from_allow) =
702712
has_allow_dead_code_or_lang_attr(tcx, trait_item.owner_id.def_id)
703713
{
@@ -965,14 +975,8 @@ impl<'tcx> DeadVisitor<'tcx> {
965975
| DefKind::TyAlias
966976
| DefKind::Enum
967977
| 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-
}
978+
| DefKind::ForeignTy
979+
| DefKind::Trait => self.warn_dead_code(def_id, "used"),
976980
DefKind::Struct => self.warn_dead_code(def_id, "constructed"),
977981
DefKind::Variant | DefKind::Field => bug!("should be handled specially"),
978982
_ => {}
@@ -1001,24 +1005,7 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) {
10011005
let mut dead_items = Vec::new();
10021006
for item in impl_item.items {
10031007
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 {
1008+
if !visitor.is_live_code(def_id) {
10221009
let name = tcx.item_name(def_id.to_def_id());
10231010
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
10241011
let level = tcx.lint_level_at_node(lint::builtin::DEAD_CODE, hir_id).0;
@@ -1093,9 +1080,7 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) {
10931080
}
10941081

10951082
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-
}
1083+
visitor.check_definition(trait_item.owner_id.def_id);
10991084
}
11001085
}
11011086

library/core/src/fmt/num.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ trait DisplayInt:
1515
fn zero() -> Self;
1616
fn from_u8(u: u8) -> Self;
1717
fn to_u8(&self) -> u8;
18+
#[allow(dead_code)]
1819
fn to_u16(&self) -> u16;
20+
#[allow(dead_code)]
1921
fn to_u32(&self) -> u32;
2022
fn to_u64(&self) -> u64;
2123
fn to_u128(&self) -> u128;

tests/ui/lint/dead-code/issue-41883.rs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,24 @@
1-
// edition: 2024
2-
// compile-flags: -Zunstable-options
31
#![deny(dead_code)]
42

53
enum Category {
64
Dead, //~ ERROR variant `Dead` is never constructed
75
Used,
86
}
97

10-
trait Demo {
8+
trait UnusedTrait { //~ ERROR trait `UnusedTrait` is never used
119
fn this_is_unused(&self) -> Category { //~ ERROR method `this_is_unused` is never used
1210
Category::Dead
1311
}
1412
}
1513

16-
impl Demo for () {
14+
struct UnusedStruct; //~ ERROR struct `UnusedStruct` is never constructed
15+
16+
impl UnusedTrait for UnusedStruct {
1717
fn this_is_unused(&self) -> Category { //~ ERROR method `this_is_unused` is never used
1818
Category::Used
1919
}
2020
}
2121

22-
impl UnusedTrait for () {}
23-
24-
trait UnusedTrait {} //~ ERROR trait `UnusedTrait` is never used
25-
2622
mod private {
2723
#[derive(Debug)]
2824
struct UnusedStruct; //~ ERROR struct `UnusedStruct` is never constructed
Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,50 @@
11
error: variant `Dead` is never constructed
2-
--> $DIR/issue-41883.rs:6:5
2+
--> $DIR/issue-41883.rs:4:5
33
|
44
LL | enum Category {
55
| -------- variant in this enum
66
LL | Dead,
77
| ^^^^
88
|
99
note: the lint level is defined here
10-
--> $DIR/issue-41883.rs:3:9
10+
--> $DIR/issue-41883.rs:1:9
1111
|
1212
LL | #![deny(dead_code)]
1313
| ^^^^^^^^^
1414

15+
error: trait `UnusedTrait` is never used
16+
--> $DIR/issue-41883.rs:8:7
17+
|
18+
LL | trait UnusedTrait {
19+
| ^^^^^^^^^^^
20+
21+
error: struct `UnusedStruct` is never constructed
22+
--> $DIR/issue-41883.rs:14:8
23+
|
24+
LL | struct UnusedStruct;
25+
| ^^^^^^^^^^^^
26+
1527
error: method `this_is_unused` is never used
1628
--> $DIR/issue-41883.rs:17:8
1729
|
18-
LL | impl Demo for () {
19-
| ---------------- method in this implementation
30+
LL | impl UnusedTrait for UnusedStruct {
31+
| --------------------------------- method in this implementation
2032
LL | fn this_is_unused(&self) -> Category {
2133
| ^^^^^^^^^^^^^^
2234

23-
error: trait `UnusedTrait` is never used
24-
--> $DIR/issue-41883.rs:24:7
25-
|
26-
LL | trait UnusedTrait {}
27-
| ^^^^^^^^^^^
28-
2935
error: method `this_is_unused` is never used
30-
--> $DIR/issue-41883.rs:11:8
36+
--> $DIR/issue-41883.rs:9:8
3137
|
3238
LL | fn this_is_unused(&self) -> Category {
3339
| ^^^^^^^^^^^^^^
3440

3541
error: struct `UnusedStruct` is never constructed
36-
--> $DIR/issue-41883.rs:28:12
42+
--> $DIR/issue-41883.rs:24:12
3743
|
3844
LL | struct UnusedStruct;
3945
| ^^^^^^^^^^^^
4046
|
4147
= note: `UnusedStruct` has a derived impl for the trait `Debug`, but this is intentionally ignored during dead code analysis
4248

43-
error: aborting due to 5 previous errors
49+
error: aborting due to 6 previous errors
4450

0 commit comments

Comments
 (0)