Skip to content

Commit 999a162

Browse files
authored
Make traits / trait methods detected by the dead code lint!
1 parent 1ead476 commit 999a162

File tree

3 files changed

+136
-20
lines changed

3 files changed

+136
-20
lines changed

compiler/rustc_passes/src/dead.rs

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

66
use hir::def_id::{LocalDefIdMap, LocalDefIdSet};
7+
use hir::ItemKind;
78
use rustc_data_structures::unord::UnordSet;
89
use rustc_errors::MultiSpan;
910
use rustc_hir as hir;
@@ -14,7 +15,7 @@ use rustc_hir::{Node, PatKind, TyKind};
1415
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
1516
use rustc_middle::middle::privacy::Level;
1617
use rustc_middle::query::Providers;
17-
use rustc_middle::ty::{self, TyCtxt};
18+
use rustc_middle::ty::{self, TyCtxt, Visibility};
1819
use rustc_session::lint;
1920
use rustc_session::lint::builtin::DEAD_CODE;
2021
use rustc_span::symbol::{sym, Symbol};
@@ -376,9 +377,46 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
376377
intravisit::walk_item(self, item)
377378
}
378379
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+
}
379396
_ => intravisit::walk_item(self, item),
380397
},
381398
Node::TraitItem(trait_item) => {
399+
// mark corresponing ImplTerm live
400+
let trait_item_id = trait_item.owner_id.to_def_id();
401+
if let Some(trait_id) = self.tcx.trait_of_item(trait_item_id) {
402+
// mark the trait live
403+
self.check_def_id(trait_id);
404+
405+
for impl_id in self.tcx.all_impls(trait_id) {
406+
if let Some(local_impl_id) = impl_id.as_local()
407+
&& let ItemKind::Impl(impl_ref) =
408+
self.tcx.hir().expect_item(local_impl_id).kind
409+
{
410+
// mark self_ty live
411+
intravisit::walk_ty(self, impl_ref.self_ty);
412+
if let Some(&impl_item_id) =
413+
self.tcx.impl_item_implementor_ids(impl_id).get(&trait_item_id)
414+
{
415+
self.check_def_id(impl_item_id);
416+
}
417+
}
418+
}
419+
}
382420
intravisit::walk_trait_item(self, trait_item);
383421
}
384422
Node::ImplItem(impl_item) => {
@@ -627,10 +665,6 @@ fn check_item<'tcx>(
627665
}
628666
}
629667
DefKind::Impl { of_trait } => {
630-
if of_trait {
631-
worklist.push((id.owner_id.def_id, ComesFromAllowExpect::No));
632-
}
633-
634668
// get DefIds from another query
635669
let local_def_ids = tcx
636670
.associated_item_def_ids(id.owner_id)
@@ -639,7 +673,11 @@ fn check_item<'tcx>(
639673

640674
// And we access the Map here to get HirId from LocalDefId
641675
for id in local_def_ids {
642-
if of_trait {
676+
// for impl trait blocks, mark associate functions live if the trait is public
677+
if of_trait
678+
&& (!matches!(tcx.def_kind(id), DefKind::AssocFn)
679+
|| tcx.local_visibility(id) == Visibility::Public)
680+
{
643681
worklist.push((id, ComesFromAllowExpect::No));
644682
} else if let Some(comes_from_allow) = has_allow_dead_code_or_lang_attr(tcx, id) {
645683
worklist.push((id, comes_from_allow));
@@ -670,7 +708,7 @@ fn check_trait_item(
670708
use hir::TraitItemKind::{Const, Fn};
671709
if matches!(tcx.def_kind(id.owner_id), DefKind::AssocConst | DefKind::AssocFn) {
672710
let trait_item = tcx.hir().trait_item(id);
673-
if matches!(trait_item.kind, Const(_, Some(_)) | Fn(_, hir::TraitFn::Provided(_)))
711+
if matches!(trait_item.kind, Const(_, Some(_)) | Fn(..))
674712
&& let Some(comes_from_allow) =
675713
has_allow_dead_code_or_lang_attr(tcx, trait_item.owner_id.def_id)
676714
{
@@ -939,7 +977,8 @@ impl<'tcx> DeadVisitor<'tcx> {
939977
| DefKind::TyAlias
940978
| DefKind::Enum
941979
| DefKind::Union
942-
| DefKind::ForeignTy => self.warn_dead_code(def_id, "used"),
980+
| DefKind::ForeignTy
981+
| DefKind::Trait => self.warn_dead_code(def_id, "used"),
943982
DefKind::Struct => self.warn_dead_code(def_id, "constructed"),
944983
DefKind::Variant | DefKind::Field => bug!("should be handled specially"),
945984
_ => {}
@@ -964,18 +1003,33 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) {
9641003
let module_items = tcx.hir_module_items(module);
9651004

9661005
for item in module_items.items() {
967-
if let hir::ItemKind::Impl(impl_item) = tcx.hir().item(item).kind {
968-
let mut dead_items = Vec::new();
969-
for item in impl_item.items {
970-
let def_id = item.id.owner_id.def_id;
971-
if !visitor.is_live_code(def_id) {
972-
let name = tcx.item_name(def_id.to_def_id());
973-
let level = visitor.def_lint_level(def_id);
1006+
let def_kind = tcx.def_kind(item.owner_id);
9741007

975-
dead_items.push(DeadItem { def_id, name, level })
1008+
let mut dead_codes = Vec::new();
1009+
// if we have diagnosed the trait, do not diagnose unused methods
1010+
if matches!(def_kind, DefKind::Impl { .. })
1011+
|| (def_kind == DefKind::Trait && live_symbols.contains(&item.owner_id.def_id))
1012+
{
1013+
for &def_id in tcx.associated_item_def_ids(item.owner_id.def_id) {
1014+
// We have diagnosed unused methods in traits
1015+
if matches!(def_kind, DefKind::Impl { of_trait: true })
1016+
&& tcx.def_kind(def_id) == DefKind::AssocFn
1017+
|| def_kind == DefKind::Trait && tcx.def_kind(def_id) != DefKind::AssocFn
1018+
{
1019+
continue;
1020+
}
1021+
1022+
if let Some(local_def_id) = def_id.as_local()
1023+
&& !visitor.is_live_code(local_def_id)
1024+
{
1025+
let name = tcx.item_name(def_id);
1026+
let level = visitor.def_lint_level(local_def_id);
1027+
dead_codes.push(DeadItem { def_id: local_def_id, name, level });
9761028
}
9771029
}
978-
visitor.warn_multiple(item.owner_id.def_id, "used", dead_items, ReportOn::NamedField);
1030+
}
1031+
if !dead_codes.is_empty() {
1032+
visitor.warn_multiple(item.owner_id.def_id, "used", dead_codes, ReportOn::NamedField);
9791033
}
9801034

9811035
if !live_symbols.contains(&item.owner_id.def_id) {
@@ -988,7 +1042,6 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) {
9881042
continue;
9891043
}
9901044

991-
let def_kind = tcx.def_kind(item.owner_id);
9921045
if let DefKind::Struct | DefKind::Union | DefKind::Enum = def_kind {
9931046
let adt = tcx.adt_def(item.owner_id);
9941047
let mut dead_variants = Vec::new();
@@ -1035,8 +1088,6 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) {
10351088
for foreign_item in module_items.foreign_items() {
10361089
visitor.check_definition(foreign_item.owner_id.def_id);
10371090
}
1038-
1039-
// We do not warn trait items.
10401091
}
10411092

10421093
pub(crate) fn provide(providers: &mut Providers) {
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#![deny(dead_code)]
2+
3+
enum Category {
4+
Dead, //~ ERROR variant `Dead` is never constructed
5+
Used,
6+
}
7+
8+
trait UnusedTrait { //~ ERROR trait `UnusedTrait` is never used
9+
fn this_is_unused(&self) -> Category {
10+
Category::Dead
11+
}
12+
}
13+
14+
struct UnusedStruct; //~ ERROR struct `UnusedStruct` is never constructed
15+
16+
impl UnusedTrait for UnusedStruct {
17+
fn this_is_unused(&self) -> Category {
18+
Category::Used
19+
}
20+
}
21+
22+
mod private {
23+
#[derive(Debug)]
24+
struct UnusedStruct; //~ ERROR struct `UnusedStruct` is never constructed
25+
}
26+
27+
fn main() {
28+
let _c = Category::Used;
29+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
error: variant `Dead` is never constructed
2+
--> $DIR/issue-41883.rs:4:5
3+
|
4+
LL | enum Category {
5+
| -------- variant in this enum
6+
LL | Dead,
7+
| ^^^^
8+
|
9+
note: the lint level is defined here
10+
--> $DIR/issue-41883.rs:1:9
11+
|
12+
LL | #![deny(dead_code)]
13+
| ^^^^^^^^^
14+
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+
27+
error: struct `UnusedStruct` is never constructed
28+
--> $DIR/issue-41883.rs:24:12
29+
|
30+
LL | struct UnusedStruct;
31+
| ^^^^^^^^^^^^
32+
|
33+
= note: `UnusedStruct` has a derived impl for the trait `Debug`, but this is intentionally ignored during dead code analysis
34+
35+
error: aborting due to 4 previous errors
36+

0 commit comments

Comments
 (0)