Skip to content

Commit a70dc29

Browse files
authored
Rollup merge of rust-lang#127017 - mu001999-contrib:dead/enhance, r=pnkfelix
Extend rules of dead code analysis for impls for adts to impls for types refer to adts The rules of dead code analysis for impl blocks can be extended to self types which refer to adts. So that we can lint the following unused struct and trait: ```rust struct Foo; //~ ERROR struct `Foo` is never constructed trait Trait { //~ ERROR trait `Trait` is never used fn foo(&self) {} } impl Trait for &Foo {} ``` r? `@pnkfelix`
2 parents ceae371 + 8449d10 commit a70dc29

File tree

3 files changed

+87
-10
lines changed

3 files changed

+87
-10
lines changed

compiler/rustc_passes/src/dead.rs

+22-10
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,24 @@ impl Publicness {
5454
}
5555
}
5656

57-
fn struct_all_fields_are_public(tcx: TyCtxt<'_>, id: DefId) -> bool {
57+
fn adt_of<'tcx>(ty: &hir::Ty<'tcx>) -> Option<(LocalDefId, DefKind)> {
58+
match ty.kind {
59+
TyKind::Path(hir::QPath::Resolved(_, path)) => {
60+
if let Res::Def(def_kind, def_id) = path.res
61+
&& let Some(local_def_id) = def_id.as_local()
62+
{
63+
Some((local_def_id, def_kind))
64+
} else {
65+
None
66+
}
67+
}
68+
TyKind::Slice(ty) | TyKind::Array(ty, _) => adt_of(ty),
69+
TyKind::Ptr(ty) | TyKind::Ref(_, ty) => adt_of(ty.ty),
70+
_ => None,
71+
}
72+
}
73+
74+
fn struct_all_fields_are_public(tcx: TyCtxt<'_>, id: LocalDefId) -> bool {
5875
// treat PhantomData and positional ZST as public,
5976
// we don't want to lint types which only have them,
6077
// cause it's a common way to use such types to check things like well-formedness
@@ -79,10 +96,7 @@ fn struct_all_fields_are_public(tcx: TyCtxt<'_>, id: DefId) -> bool {
7996
/// for enum and union, just check they are public,
8097
/// and doesn't solve types like &T for now, just skip them
8198
fn ty_ref_to_pub_struct(tcx: TyCtxt<'_>, ty: &hir::Ty<'_>) -> Publicness {
82-
if let TyKind::Path(hir::QPath::Resolved(_, path)) = ty.kind
83-
&& let Res::Def(def_kind, def_id) = path.res
84-
&& def_id.is_local()
85-
{
99+
if let Some((def_id, def_kind)) = adt_of(ty) {
86100
return match def_kind {
87101
DefKind::Enum | DefKind::Union => {
88102
let ty_is_public = tcx.visibility(def_id).is_public();
@@ -565,10 +579,8 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
565579
}
566580

567581
fn impl_item_with_used_self(&mut self, impl_id: hir::ItemId, impl_item_id: LocalDefId) -> bool {
568-
if let TyKind::Path(hir::QPath::Resolved(_, path)) =
569-
self.tcx.hir().item(impl_id).expect_impl().self_ty.kind
570-
&& let Res::Def(def_kind, def_id) = path.res
571-
&& let Some(local_def_id) = def_id.as_local()
582+
if let Some((local_def_id, def_kind)) =
583+
adt_of(self.tcx.hir().item(impl_id).expect_impl().self_ty)
572584
&& matches!(def_kind, DefKind::Struct | DefKind::Enum | DefKind::Union)
573585
{
574586
if let Some(trait_item_id) = self.tcx.associated_item(impl_item_id).trait_item_def_id
@@ -915,7 +927,7 @@ fn create_and_seed_worklist(
915927
match tcx.def_kind(id) {
916928
DefKind::Impl { .. } => false,
917929
DefKind::AssocConst | DefKind::AssocTy | DefKind::AssocFn => !matches!(tcx.associated_item(id).container, AssocItemContainer::ImplContainer),
918-
DefKind::Struct => struct_all_fields_are_public(tcx, id.to_def_id()) || has_allow_dead_code_or_lang_attr(tcx, id).is_some(),
930+
DefKind::Struct => struct_all_fields_are_public(tcx, id) || has_allow_dead_code_or_lang_attr(tcx, id).is_some(),
919931
_ => true
920932
})
921933
.map(|id| (id, ComesFromAllowExpect::No))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#![deny(dead_code)]
2+
3+
struct Foo; //~ ERROR struct `Foo` is never constructed
4+
5+
trait Trait { //~ ERROR trait `Trait` is never used
6+
fn foo(&self) {}
7+
}
8+
9+
impl Trait for Foo {}
10+
11+
impl Trait for [Foo] {}
12+
impl<const N: usize> Trait for [Foo; N] {}
13+
14+
impl Trait for *const Foo {}
15+
impl Trait for *mut Foo {}
16+
17+
impl Trait for &Foo {}
18+
impl Trait for &&Foo {}
19+
impl Trait for &mut Foo {}
20+
21+
impl Trait for [&Foo] {}
22+
impl Trait for &[Foo] {}
23+
impl Trait for &*const Foo {}
24+
25+
pub trait Trait2 {
26+
fn foo(&self) {}
27+
}
28+
29+
impl Trait2 for Foo {}
30+
31+
impl Trait2 for [Foo] {}
32+
impl<const N: usize> Trait2 for [Foo; N] {}
33+
34+
impl Trait2 for *const Foo {}
35+
impl Trait2 for *mut Foo {}
36+
37+
impl Trait2 for &Foo {}
38+
impl Trait2 for &&Foo {}
39+
impl Trait2 for &mut Foo {}
40+
41+
impl Trait2 for [&Foo] {}
42+
impl Trait2 for &[Foo] {}
43+
impl Trait2 for &*const Foo {}
44+
45+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
error: struct `Foo` is never constructed
2+
--> $DIR/unused-impl-for-non-adts.rs:3:8
3+
|
4+
LL | struct Foo;
5+
| ^^^
6+
|
7+
note: the lint level is defined here
8+
--> $DIR/unused-impl-for-non-adts.rs:1:9
9+
|
10+
LL | #![deny(dead_code)]
11+
| ^^^^^^^^^
12+
13+
error: trait `Trait` is never used
14+
--> $DIR/unused-impl-for-non-adts.rs:5:7
15+
|
16+
LL | trait Trait {
17+
| ^^^^^
18+
19+
error: aborting due to 2 previous errors
20+

0 commit comments

Comments
 (0)