Skip to content

Commit 6a4d4a2

Browse files
committed
Lint unused associated types
1 parent 8e0b68e commit 6a4d4a2

File tree

12 files changed

+202
-33
lines changed

12 files changed

+202
-33
lines changed

compiler/rustc_passes/src/dead.rs

Lines changed: 89 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use rustc_hir::{self as hir, Node, PatKind, QPath};
1717
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
1818
use rustc_middle::middle::privacy::Level;
1919
use rustc_middle::query::Providers;
20-
use rustc_middle::ty::{self, AssocTag, TyCtxt};
20+
use rustc_middle::ty::{self, AssocTag, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor};
2121
use rustc_middle::{bug, span_bug};
2222
use rustc_session::lint::builtin::DEAD_CODE;
2323
use rustc_session::lint::{self, LintExpectationId};
@@ -110,6 +110,56 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
110110
}
111111
}
112112

113+
fn check_assoc_ty(&mut self, def_id: LocalDefId) {
114+
let def_kind = self.tcx.def_kind(def_id);
115+
116+
match def_kind {
117+
// Check `T::Ty` used in types of inputs and output
118+
DefKind::Fn | DefKind::AssocFn => {
119+
let fn_sig = self.tcx.fn_sig(def_id).instantiate_identity();
120+
for ty in fn_sig.inputs().skip_binder() {
121+
self.visit_middle_ty(ty.clone());
122+
}
123+
self.visit_middle_ty(fn_sig.output().skip_binder().clone());
124+
}
125+
// Check `T::Ty` used in assoc type
126+
DefKind::AssocTy => {
127+
if matches!(self.tcx.def_kind(self.tcx.local_parent(def_id)), DefKind::Impl { .. })
128+
|| matches!(
129+
self.tcx.hir_expect_trait_item(def_id).kind,
130+
hir::TraitItemKind::Type(_, Some(_))
131+
)
132+
{
133+
self.visit_middle_ty(self.tcx.type_of(def_id).instantiate_identity());
134+
}
135+
}
136+
// Check `T::Ty` used in assoc const's type
137+
DefKind::AssocConst => {
138+
self.visit_middle_ty(self.tcx.type_of(def_id).instantiate_identity());
139+
}
140+
_ => (),
141+
}
142+
143+
if matches!(
144+
def_kind,
145+
DefKind::Struct
146+
| DefKind::Union
147+
| DefKind::Enum
148+
| DefKind::Fn
149+
| DefKind::Const
150+
| DefKind::Trait
151+
| DefKind::Impl { .. }
152+
| DefKind::AssocFn
153+
| DefKind::AssocTy
154+
| DefKind::AssocConst
155+
) {
156+
let preds = self.tcx.predicates_of(def_id).instantiate_identity(self.tcx);
157+
for pred in preds.iter() {
158+
<Self as TypeVisitor<TyCtxt<'tcx>>>::visit_predicate(self, pred.0.as_predicate());
159+
}
160+
}
161+
}
162+
113163
fn insert_def_id(&mut self, def_id: DefId) {
114164
if let Some(def_id) = def_id.as_local() {
115165
debug_assert!(!should_explore(self.tcx, def_id));
@@ -119,12 +169,6 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
119169

120170
fn handle_res(&mut self, res: Res) {
121171
match res {
122-
Res::Def(
123-
DefKind::Const | DefKind::AssocConst | DefKind::AssocTy | DefKind::TyAlias,
124-
def_id,
125-
) => {
126-
self.check_def_id(def_id);
127-
}
128172
Res::PrimTy(..) | Res::SelfCtor(..) | Res::Local(..) => {}
129173
Res::Def(DefKind::Ctor(CtorOf::Variant, ..), ctor_def_id) => {
130174
// Using a variant in patterns should not make the variant live,
@@ -376,6 +420,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
376420
continue;
377421
}
378422

423+
self.check_assoc_ty(id);
379424
self.visit_node(self.tcx.hir_node_by_def_id(id))?;
380425
}
381426

@@ -429,15 +474,6 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
429474
intravisit::walk_item(self, item)
430475
}
431476
hir::ItemKind::ForeignMod { .. } => ControlFlow::Continue(()),
432-
hir::ItemKind::Trait(.., trait_item_refs) => {
433-
// mark assoc ty live if the trait is live
434-
for trait_item in trait_item_refs {
435-
if matches!(self.tcx.def_kind(trait_item.owner_id), DefKind::AssocTy) {
436-
self.check_def_id(trait_item.owner_id.to_def_id());
437-
}
438-
}
439-
intravisit::walk_item(self, item)
440-
}
441477
_ => intravisit::walk_item(self, item),
442478
},
443479
Node::TraitItem(trait_item) => {
@@ -523,6 +559,10 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
523559

524560
true
525561
}
562+
563+
fn visit_middle_ty(&mut self, ty: Ty<'tcx>) {
564+
<Self as TypeVisitor<TyCtxt<'tcx>>>::visit_ty(self, ty);
565+
}
526566
}
527567

528568
impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> {
@@ -600,6 +640,9 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> {
600640
_ => (),
601641
}
602642

643+
// Check `T::Ty` used in the type of `expr`
644+
self.visit_middle_ty(self.typeck_results().expr_ty(expr));
645+
603646
intravisit::walk_expr(self, expr)
604647
}
605648

@@ -687,16 +730,16 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> {
687730
&& let Some(segment) = t.path.segments.last()
688731
&& let Some(args) = segment.args
689732
{
733+
// Mark assoc items used if they are constrained in the trait ref
690734
for constraint in args.constraints {
691735
if let Some(local_def_id) = self
692736
.tcx
693737
.associated_items(trait_def_id)
694-
.find_by_ident_and_kind(
695-
self.tcx,
696-
constraint.ident,
697-
AssocTag::Const,
698-
trait_def_id,
699-
)
738+
.filter_by_name_unhygienic(constraint.ident.name)
739+
.filter(|item| matches!(item.as_tag(), AssocTag::Const | AssocTag::Type))
740+
.find(|item| {
741+
self.tcx.hygienic_eq(constraint.ident, item.ident(self.tcx), trait_def_id)
742+
})
700743
.and_then(|item| item.def_id.as_local())
701744
{
702745
self.worklist.push((local_def_id, ComesFromAllowExpect::No));
@@ -706,6 +749,30 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> {
706749

707750
intravisit::walk_trait_ref(self, t)
708751
}
752+
753+
fn visit_field_def(&mut self, field_def: &'tcx hir::FieldDef<'tcx>) -> Self::Result {
754+
// Check `T::Ty` used in field's type, marks assoc types live whether the field is used or not
755+
// Consider that there would be three situations:
756+
// 1. `field` is used, it's good
757+
// 2. `field` is not used but marked like `#[allow(dead_code)]`,
758+
// it's annoying to mark the assoc type `#[allow(dead_code)]` again
759+
// 3. `field` is not used and will be linted,
760+
// the assoc type will be linted after removing the unused field
761+
self.visit_middle_ty(self.tcx.type_of(field_def.def_id).instantiate_identity());
762+
intravisit::walk_field_def(self, field_def)
763+
}
764+
}
765+
766+
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for MarkSymbolVisitor<'tcx> {
767+
fn visit_ty(&mut self, ty: Ty<'tcx>) {
768+
match ty.kind() {
769+
ty::Alias(_, alias) => {
770+
self.check_def_id(alias.def_id);
771+
}
772+
_ => (),
773+
}
774+
ty.super_visit_with(self);
775+
}
709776
}
710777

711778
fn has_allow_dead_code_or_lang_attr(

tests/ui/associated-type-bounds/union-bounds.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
trait Tr1: Copy { type As1: Copy; }
66
trait Tr2: Copy { type As2: Copy; }
7-
trait Tr3: Copy { type As3: Copy; }
7+
trait Tr3: Copy { #[allow(dead_code)] type As3: Copy; }
88
trait Tr4<'a>: Copy { type As4: Copy; }
99
trait Tr5: Copy { type As5: Copy; }
1010

tests/ui/associated-types/impl-wf-cycle-5.fixed

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ impl Fiz for bool {}
1111

1212
trait Grault {
1313
type A;
14-
type B;
14+
#[allow(dead_code)] type B;
1515
}
1616

1717
impl Grault for () {

tests/ui/associated-types/impl-wf-cycle-5.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ impl Fiz for bool {}
1111

1212
trait Grault {
1313
type A;
14-
type B;
14+
#[allow(dead_code)] type B;
1515
}
1616

1717
impl Grault for () {

tests/ui/associated-types/impl-wf-cycle-6.fixed

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ impl Fiz for bool {}
1111

1212
trait Grault {
1313
type A;
14-
type B;
14+
#[allow(dead_code)] type B;
1515
}
1616

1717
impl Grault for () {

tests/ui/associated-types/impl-wf-cycle-6.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ impl Fiz for bool {}
1111

1212
trait Grault {
1313
type A;
14-
type B;
14+
#[allow(dead_code)] type B;
1515
}
1616

1717
impl Grault for () {

tests/ui/generic-associated-types/collections.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ trait Collection<T> {
1010
type Iter<'iter>: Iterator<Item=&'iter T> where T: 'iter, Self: 'iter;
1111
type Family: CollectionFamily;
1212
// Test associated type defaults with parameters
13-
type Sibling<U>: Collection<U> =
13+
#[allow(dead_code)] type Sibling<U>: Collection<U> =
1414
<<Self as Collection<T>>::Family as CollectionFamily>::Member<U>;
1515

1616
fn empty() -> Self;
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
#![deny(dead_code)]
2+
3+
trait Tr {
4+
type A;
5+
type B;
6+
type C: Default;
7+
type D;
8+
type E;
9+
type F;
10+
type G;
11+
type H; //~ ERROR associated type `H` is never used
12+
}
13+
14+
impl Tr for i32 {
15+
type A = Self;
16+
type B = Self;
17+
type C = Self;
18+
type D = Self;
19+
type E = Self;
20+
type F = Self;
21+
type G = Self;
22+
type H = Self;
23+
}
24+
25+
trait Tr2 {
26+
type A;
27+
}
28+
29+
impl Tr2 for i32 {
30+
type A = Self;
31+
}
32+
33+
struct Foo<T: Tr2> {
34+
_x: T::A,
35+
}
36+
37+
struct Bar<T> {
38+
_x: T
39+
}
40+
41+
impl<T> Bar<T>
42+
where
43+
T: Tr2<A = i32>
44+
{}
45+
46+
type Baz<T> = <T as Tr>::G;
47+
48+
struct FooBaz<T: Tr> {
49+
_x: Baz<T>,
50+
}
51+
52+
fn foo<T: Tr>(t: impl Tr<A = T>) -> impl Tr
53+
where
54+
T::B: Copy
55+
{
56+
let _a: T::C = Default::default();
57+
baz::<T::F>();
58+
t
59+
}
60+
fn bar<T: ?Sized>() {}
61+
fn baz<T>() {}
62+
63+
trait Trait { type Ty; }
64+
65+
impl Trait for () { type Ty = (); }
66+
67+
pub struct Wrap(Inner<()>);
68+
struct Inner<T: Trait>(T::Ty); // <- use of QPath::TypeRelative `Ty` in a non-body only
69+
70+
impl Wrap {
71+
pub fn live(self) { _ = self.0; }
72+
}
73+
74+
fn main() {
75+
foo::<i32>(42);
76+
bar::<dyn Tr2<A = i32>>();
77+
let _d: <i32 as Tr>::D = Default::default();
78+
baz::<<i32 as Tr>::E>();
79+
baz::<Foo<i32>>();
80+
baz::<Bar<i32>>();
81+
baz::<FooBaz<i32>>();
82+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
error: associated type `H` is never used
2+
--> $DIR/unused-assoc-ty-in-used-trait.rs:11:10
3+
|
4+
LL | trait Tr {
5+
| -- associated type in this trait
6+
...
7+
LL | type H;
8+
| ^
9+
|
10+
note: the lint level is defined here
11+
--> $DIR/unused-assoc-ty-in-used-trait.rs:1:9
12+
|
13+
LL | #![deny(dead_code)]
14+
| ^^^^^^^^^
15+
16+
error: aborting due to 1 previous error
17+

tests/ui/sanitizer/cfi/sized-associated-ty.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
//@ run-pass
1616

1717
trait Foo {
18-
type Bar<'a>
18+
#[allow(dead_code)] type Bar<'a>
1919
where
2020
Self: Sized;
2121

0 commit comments

Comments
 (0)