Skip to content

Commit 0232e28

Browse files
committed
Lints unused assoc consts and assoc tys
1 parent dc85766 commit 0232e28

8 files changed

+248
-20
lines changed

compiler/rustc_passes/src/dead.rs

+139-11
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use rustc_hir::{self as 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, AssocItemContainer, Ty, TyCtxt};
19+
use rustc_middle::ty::{self, AssocItemContainer, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor};
2020
use rustc_middle::{bug, span_bug};
2121
use rustc_session::lint;
2222
use rustc_session::lint::builtin::DEAD_CODE;
@@ -113,7 +113,10 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
113113

114114
fn handle_res(&mut self, res: Res) {
115115
match res {
116-
Res::Def(DefKind::Const | DefKind::AssocConst | DefKind::TyAlias, def_id) => {
116+
Res::Def(
117+
DefKind::Const | DefKind::AssocConst | DefKind::AssocTy | DefKind::TyAlias,
118+
def_id,
119+
) => {
117120
self.check_def_id(def_id);
118121
}
119122
_ if self.in_pat => {}
@@ -405,6 +408,13 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
405408
intravisit::walk_item(self, item)
406409
}
407410
hir::ItemKind::ForeignMod { .. } => {}
411+
hir::ItemKind::Fn(..) => {
412+
// check `T::Ty` in the types of inputs and output
413+
// the result of type_of maybe different from the fn sig,
414+
// so we also check the fn sig
415+
self.visit_middle_fn_sig(item.owner_id.def_id);
416+
intravisit::walk_item(self, item)
417+
}
408418
_ => intravisit::walk_item(self, item),
409419
},
410420
Node::TraitItem(trait_item) => {
@@ -415,6 +425,20 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
415425
self.check_def_id(trait_id);
416426
}
417427

428+
match trait_item.kind {
429+
hir::TraitItemKind::Fn(..) => {
430+
// check `T::Ty` in the types of inputs and output
431+
// the result of type_of maybe different from the fn sig,
432+
// so we also check the fn sig
433+
self.visit_middle_fn_sig(trait_item.owner_id.def_id)
434+
}
435+
hir::TraitItemKind::Type(.., Some(_)) | hir::TraitItemKind::Const(..) => {
436+
// check `type X = T::Ty;` or `const X: T::Ty;`
437+
self.visit_middle_ty_by_def_id(trait_item.owner_id.def_id)
438+
}
439+
_ => (),
440+
}
441+
418442
intravisit::walk_trait_item(self, trait_item);
419443
}
420444
Node::ImplItem(impl_item) => {
@@ -436,6 +460,20 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
436460
_ => {}
437461
}
438462
}
463+
464+
match impl_item.kind {
465+
hir::ImplItemKind::Fn(..) => {
466+
// check `T::Ty` in the types of inputs and output
467+
// the result of type_of maybe different from the fn sig,
468+
// so we also check the fn sig
469+
self.visit_middle_fn_sig(impl_item.owner_id.def_id)
470+
}
471+
hir::ImplItemKind::Type(..) | hir::ImplItemKind::Const(..) => {
472+
// check `type X = T::Ty;` or `const X: T::Ty;`
473+
self.visit_middle_ty_by_def_id(impl_item.owner_id.def_id)
474+
}
475+
}
476+
439477
intravisit::walk_impl_item(self, impl_item);
440478
}
441479
Node::ForeignItem(foreign_item) => {
@@ -478,7 +516,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
478516
fn item_should_be_checked(&mut self, impl_id: hir::ItemId, local_def_id: LocalDefId) -> bool {
479517
let trait_def_id = match self.tcx.def_kind(local_def_id) {
480518
// for assoc impl items of traits, we concern the corresponding trait items are used or not
481-
DefKind::AssocFn => self
519+
DefKind::AssocConst | DefKind::AssocTy | DefKind::AssocFn => self
482520
.tcx
483521
.associated_item(local_def_id)
484522
.trait_item_def_id
@@ -506,6 +544,22 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
506544
// the impl/impl item is used if the trait/trait item is used and the ty is used
507545
ty_def_id.map(|def_id| self.live_symbols.contains(&def_id)).unwrap_or(true)
508546
}
547+
548+
fn visit_middle_ty(&mut self, ty: Ty<'tcx>) {
549+
<Self as TypeVisitor<TyCtxt<'tcx>>>::visit_ty(self, ty);
550+
}
551+
552+
fn visit_middle_ty_by_def_id(&mut self, def_id: LocalDefId) {
553+
self.visit_middle_ty(self.tcx.type_of(def_id).instantiate_identity());
554+
}
555+
556+
fn visit_middle_fn_sig(&mut self, def_id: LocalDefId) {
557+
let fn_sig = self.tcx.fn_sig(def_id).instantiate_identity();
558+
for ty in fn_sig.inputs().skip_binder() {
559+
self.visit_middle_ty(ty.clone());
560+
}
561+
self.visit_middle_ty(fn_sig.output().skip_binder().clone());
562+
}
509563
}
510564

511565
impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> {
@@ -537,6 +591,19 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> {
537591
intravisit::walk_struct_def(self, def);
538592
}
539593

594+
fn visit_field_def(&mut self, s: &'tcx rustc_hir::FieldDef<'tcx>) {
595+
// check `field: T::Ty`
596+
// marks assoc types live whether the field is not used or not
597+
// there are three situations:
598+
// 1. the field is used, it's good
599+
// 2. the field is not used but marked like `#[allow(dead_code)]`,
600+
// it's annoying to mark the assoc type `#[allow(dead_code)]` again
601+
// 3. the field is not used, and will be linted
602+
// the assoc type will be linted after removing the unused field
603+
self.visit_middle_ty_by_def_id(s.def_id);
604+
intravisit::walk_field_def(self, s);
605+
}
606+
540607
fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
541608
match expr.kind {
542609
hir::ExprKind::Path(ref qpath @ hir::QPath::TypeRelative(..)) => {
@@ -569,6 +636,9 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> {
569636
_ => (),
570637
}
571638

639+
// check the expr_ty if its type is `T::Ty`
640+
self.visit_middle_ty(self.typeck_results().expr_ty(expr));
641+
572642
intravisit::walk_expr(self, expr);
573643
}
574644

@@ -590,6 +660,9 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> {
590660
self.handle_field_pattern_match(pat, res, fields);
591661
}
592662
PatKind::Path(ref qpath) => {
663+
if let ty::Adt(adt, _) = self.typeck_results().node_type(pat.hir_id).kind() {
664+
self.check_def_id(adt.did());
665+
}
593666
let res = self.typeck_results().qpath_res(qpath, pat.hir_id);
594667
self.handle_res(res);
595668
}
@@ -606,6 +679,24 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> {
606679

607680
fn visit_path(&mut self, path: &hir::Path<'tcx>, _: hir::HirId) {
608681
self.handle_res(path.res);
682+
683+
if let Res::Def(def_kind, def_id) = path.res
684+
&& matches!(
685+
def_kind,
686+
DefKind::Fn
687+
| DefKind::AssocFn
688+
| DefKind::AssocTy
689+
| DefKind::Struct
690+
| DefKind::Union
691+
| DefKind::Enum
692+
)
693+
{
694+
let preds = self.tcx.predicates_of(def_id).instantiate_identity(self.tcx);
695+
for pred in preds.iter() {
696+
<Self as TypeVisitor<TyCtxt<'tcx>>>::visit_predicate(self, pred.0.as_predicate());
697+
}
698+
}
699+
609700
intravisit::walk_path(self, path);
610701
}
611702

@@ -638,6 +729,41 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> {
638729

639730
self.in_pat = in_pat;
640731
}
732+
733+
fn visit_poly_trait_ref(&mut self, t: &'tcx hir::PolyTraitRef<'tcx>) {
734+
// mark the assoc type/const appears in poly-trait-ref live
735+
if let Some(pathsegment) = t.trait_ref.path.segments.last()
736+
&& let Some(args) = pathsegment.args
737+
{
738+
for constraint in args.constraints {
739+
if let Some(item) = self
740+
.tcx
741+
.associated_items(pathsegment.res.def_id())
742+
.filter_by_name_unhygienic(constraint.ident.name)
743+
.find(|i| {
744+
matches!(i.kind, ty::AssocKind::Const | ty::AssocKind::Type)
745+
&& i.ident(self.tcx).normalize_to_macros_2_0() == constraint.ident
746+
})
747+
&& let Some(local_def_id) = item.def_id.as_local()
748+
{
749+
self.worklist.push((local_def_id, ComesFromAllowExpect::No));
750+
}
751+
}
752+
}
753+
intravisit::walk_poly_trait_ref(self, t);
754+
}
755+
}
756+
757+
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for MarkSymbolVisitor<'tcx> {
758+
fn visit_ty(&mut self, ty: Ty<'tcx>) {
759+
match ty.kind() {
760+
ty::Alias(_, alias) => {
761+
self.check_def_id(alias.def_id);
762+
}
763+
_ => (),
764+
}
765+
ty.super_visit_with(self);
766+
}
641767
}
642768

643769
fn has_allow_dead_code_or_lang_attr(
@@ -648,6 +774,7 @@ fn has_allow_dead_code_or_lang_attr(
648774
tcx.has_attr(def_id, sym::lang)
649775
// Stable attribute for #[lang = "panic_impl"]
650776
|| tcx.has_attr(def_id, sym::panic_handler)
777+
|| tcx.has_attr(def_id, sym::async_fn_kind_upvars)
651778
}
652779

653780
fn has_allow_expect_dead_code(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
@@ -736,10 +863,7 @@ fn check_item<'tcx>(
736863

737864
// And we access the Map here to get HirId from LocalDefId
738865
for local_def_id in local_def_ids {
739-
if !matches!(tcx.def_kind(local_def_id), DefKind::AssocFn) {
740-
worklist.push((local_def_id, ComesFromAllowExpect::No));
741-
} else if let Some(comes_from_allow) =
742-
has_allow_dead_code_or_lang_attr(tcx, local_def_id)
866+
if let Some(comes_from_allow) = has_allow_dead_code_or_lang_attr(tcx, local_def_id)
743867
{
744868
worklist.push((local_def_id, comes_from_allow));
745869
} else if of_trait {
@@ -768,10 +892,13 @@ fn check_trait_item(
768892
worklist: &mut Vec<(LocalDefId, ComesFromAllowExpect)>,
769893
id: hir::TraitItemId,
770894
) {
771-
use hir::TraitItemKind::{Const, Fn};
772-
if matches!(tcx.def_kind(id.owner_id), DefKind::AssocConst | DefKind::AssocFn) {
895+
use hir::TraitItemKind::{Const, Fn, Type};
896+
if matches!(
897+
tcx.def_kind(id.owner_id),
898+
DefKind::AssocConst | DefKind::AssocTy | DefKind::AssocFn
899+
) {
773900
let trait_item = tcx.hir().trait_item(id);
774-
if matches!(trait_item.kind, Const(_, Some(_)) | Fn(..))
901+
if matches!(trait_item.kind, Const(_, Some(_)) | Type(..) | Fn(..))
775902
&& let Some(comes_from_allow) =
776903
has_allow_dead_code_or_lang_attr(tcx, trait_item.owner_id.def_id)
777904
{
@@ -813,7 +940,7 @@ fn create_and_seed_worklist(
813940
// checks impls and impl-items later
814941
match tcx.def_kind(id) {
815942
DefKind::Impl { of_trait } => !of_trait,
816-
DefKind::AssocFn => {
943+
DefKind::AssocConst | DefKind::AssocTy | DefKind::AssocFn => {
817944
// still check public trait items, and impl items not of trait
818945
let assoc_item = tcx.associated_item(id);
819946
!matches!(assoc_item.container, AssocItemContainer::ImplContainer)
@@ -1104,6 +1231,7 @@ impl<'tcx> DeadVisitor<'tcx> {
11041231
}
11051232
match self.tcx.def_kind(def_id) {
11061233
DefKind::AssocConst
1234+
| DefKind::AssocTy
11071235
| DefKind::AssocFn
11081236
| DefKind::Fn
11091237
| DefKind::Static { .. }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//@ check-pass
2+
3+
#![deny(dead_code)]
4+
5+
trait UInt: Copy + From<u8> {}
6+
7+
impl UInt for u16 {}
8+
9+
trait Int: Copy {
10+
type Unsigned: UInt;
11+
12+
fn as_unsigned(self) -> Self::Unsigned;
13+
}
14+
15+
impl Int for i16 {
16+
type Unsigned = u16;
17+
18+
fn as_unsigned(self) -> u16 {
19+
self as _
20+
}
21+
}
22+
23+
fn priv_func<T: Int>(x: u8, y: T) -> (T::Unsigned, T::Unsigned) {
24+
(T::Unsigned::from(x), y.as_unsigned())
25+
}
26+
27+
pub fn pub_func(x: u8, y: i16) -> (u16, u16) {
28+
priv_func(x, y)
29+
}
30+
31+
fn main() {}

tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.rs

-8
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
struct T1; //~ ERROR struct `T1` is never constructed
44
pub struct T2(i32); //~ ERROR field `0` is never read
5-
struct T3;
65

76
trait Trait1 { //~ ERROR trait `Trait1` is never used
87
const UNUSED: i32;
@@ -42,11 +41,4 @@ impl Trait2 for T2 {
4241
const USED: i32 = 0;
4342
}
4443

45-
impl Trait3 for T3 {
46-
const USED: i32 = 0;
47-
fn construct_self() -> Self {
48-
Self
49-
}
50-
}
51-
5244
fn main() {}

tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ LL | pub struct T2(i32);
2121
= help: consider removing this field
2222

2323
error: trait `Trait1` is never used
24-
--> $DIR/unused-adt-impl-pub-trait-with-assoc-const.rs:7:7
24+
--> $DIR/unused-adt-impl-pub-trait-with-assoc-const.rs:6:7
2525
|
2626
LL | trait Trait1 {
2727
| ^^^^^^
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#![deny(dead_code)]
2+
3+
trait Trait {
4+
const UNUSED_CONST: i32; //~ ERROR associated constant `UNUSED_CONST` is never used
5+
const USED_CONST: i32;
6+
7+
fn foo(&self) {}
8+
}
9+
10+
pub struct T(());
11+
12+
impl Trait for T {
13+
const UNUSED_CONST: i32 = 0;
14+
const USED_CONST: i32 = 1;
15+
}
16+
17+
fn main() {
18+
T(()).foo();
19+
T::USED_CONST;
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
error: associated constant `UNUSED_CONST` is never used
2+
--> $DIR/unused-assoc-const.rs:4:11
3+
|
4+
LL | trait Trait {
5+
| ----- associated constant in this trait
6+
LL | const UNUSED_CONST: i32;
7+
| ^^^^^^^^^^^^
8+
|
9+
note: the lint level is defined here
10+
--> $DIR/unused-assoc-const.rs:1:9
11+
|
12+
LL | #![deny(dead_code)]
13+
| ^^^^^^^^^
14+
15+
error: aborting due to 1 previous error
16+
+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#![deny(dead_code)]
2+
3+
trait Tr {
4+
type X; //~ ERROR associated type `X` is never used
5+
type Y;
6+
type Z;
7+
}
8+
9+
impl Tr for () {
10+
type X = Self;
11+
type Y = Self;
12+
type Z = Self;
13+
}
14+
15+
trait Tr2 {
16+
type X;
17+
}
18+
19+
fn foo<T: Tr>() -> impl Tr<Y = ()> where T::Z: Copy {}
20+
fn bar<T: ?Sized>() {}
21+
22+
fn main() {
23+
foo::<()>();
24+
bar::<dyn Tr2<X = ()>>();
25+
}

0 commit comments

Comments
 (0)