Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 89 additions & 22 deletions compiler/rustc_passes/src/dead.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use rustc_hir::{self as hir, Node, PatKind, QPath};
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc_middle::middle::privacy::Level;
use rustc_middle::query::Providers;
use rustc_middle::ty::{self, AssocTag, TyCtxt};
use rustc_middle::ty::{self, AssocTag, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor};
use rustc_middle::{bug, span_bug};
use rustc_session::lint::builtin::DEAD_CODE;
use rustc_session::lint::{self, LintExpectationId};
Expand Down Expand Up @@ -110,6 +110,56 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
}
}

fn check_assoc_ty(&mut self, def_id: LocalDefId) {
let def_kind = self.tcx.def_kind(def_id);

match def_kind {
// Check `T::Ty` used in types of inputs and output
DefKind::Fn | DefKind::AssocFn => {
let fn_sig = self.tcx.fn_sig(def_id).instantiate_identity();
for ty in fn_sig.inputs().skip_binder() {
self.visit_middle_ty(ty.clone());
}
self.visit_middle_ty(fn_sig.output().skip_binder().clone());
}
// Check `T::Ty` used in assoc type
DefKind::AssocTy => {
if matches!(self.tcx.def_kind(self.tcx.local_parent(def_id)), DefKind::Impl { .. })
|| matches!(
self.tcx.hir_expect_trait_item(def_id).kind,
hir::TraitItemKind::Type(_, Some(_))
)
{
self.visit_middle_ty(self.tcx.type_of(def_id).instantiate_identity());
}
}
// Check `T::Ty` used in assoc const's type
DefKind::AssocConst => {
self.visit_middle_ty(self.tcx.type_of(def_id).instantiate_identity());
}
_ => (),
}

if matches!(
def_kind,
DefKind::Struct
| DefKind::Union
| DefKind::Enum
| DefKind::Fn
| DefKind::Const
| DefKind::Trait
| DefKind::Impl { .. }
| DefKind::AssocFn
| DefKind::AssocTy
| DefKind::AssocConst
) {
let preds = self.tcx.predicates_of(def_id).instantiate_identity(self.tcx);
for pred in preds.iter() {
<Self as TypeVisitor<TyCtxt<'tcx>>>::visit_predicate(self, pred.0.as_predicate());
}
}
}

fn insert_def_id(&mut self, def_id: DefId) {
if let Some(def_id) = def_id.as_local() {
debug_assert!(!should_explore(self.tcx, def_id));
Expand All @@ -119,12 +169,6 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {

fn handle_res(&mut self, res: Res) {
match res {
Res::Def(
DefKind::Const | DefKind::AssocConst | DefKind::AssocTy | DefKind::TyAlias,
def_id,
) => {
self.check_def_id(def_id);
}
Res::PrimTy(..) | Res::SelfCtor(..) | Res::Local(..) => {}
Res::Def(DefKind::Ctor(CtorOf::Variant, ..), ctor_def_id) => {
// Using a variant in patterns should not make the variant live,
Expand Down Expand Up @@ -376,6 +420,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
continue;
}

self.check_assoc_ty(id);
self.visit_node(self.tcx.hir_node_by_def_id(id))?;
}

Expand Down Expand Up @@ -429,15 +474,6 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
intravisit::walk_item(self, item)
}
hir::ItemKind::ForeignMod { .. } => ControlFlow::Continue(()),
hir::ItemKind::Trait(.., trait_item_refs) => {
// mark assoc ty live if the trait is live
for trait_item in trait_item_refs {
if matches!(self.tcx.def_kind(trait_item.owner_id), DefKind::AssocTy) {
self.check_def_id(trait_item.owner_id.to_def_id());
}
}
intravisit::walk_item(self, item)
}
_ => intravisit::walk_item(self, item),
},
Node::TraitItem(trait_item) => {
Expand Down Expand Up @@ -523,6 +559,10 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {

true
}

fn visit_middle_ty(&mut self, ty: Ty<'tcx>) {
<Self as TypeVisitor<TyCtxt<'tcx>>>::visit_ty(self, ty);
}
}

impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> {
Expand Down Expand Up @@ -600,6 +640,9 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> {
_ => (),
}

// Check `T::Ty` used in the type of `expr`
self.visit_middle_ty(self.typeck_results().expr_ty(expr));

intravisit::walk_expr(self, expr)
}

Expand Down Expand Up @@ -687,16 +730,16 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> {
&& let Some(segment) = t.path.segments.last()
&& let Some(args) = segment.args
{
// Mark assoc items used if they are constrained in the trait ref
for constraint in args.constraints {
if let Some(local_def_id) = self
.tcx
.associated_items(trait_def_id)
.find_by_ident_and_kind(
self.tcx,
constraint.ident,
AssocTag::Const,
trait_def_id,
)
.filter_by_name_unhygienic(constraint.ident.name)
.filter(|item| matches!(item.as_tag(), AssocTag::Const | AssocTag::Type))
.find(|item| {
self.tcx.hygienic_eq(constraint.ident, item.ident(self.tcx), trait_def_id)
})
.and_then(|item| item.def_id.as_local())
{
self.worklist.push((local_def_id, ComesFromAllowExpect::No));
Expand All @@ -706,6 +749,30 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> {

intravisit::walk_trait_ref(self, t)
}

fn visit_field_def(&mut self, field_def: &'tcx hir::FieldDef<'tcx>) -> Self::Result {
// Check `T::Ty` used in field's type, marks assoc types live whether the field is used or not
// Consider that there would be three situations:
// 1. `field` is used, it's good
// 2. `field` is not used but marked like `#[allow(dead_code)]`,
// it's annoying to mark the assoc type `#[allow(dead_code)]` again
// 3. `field` is not used and will be linted,
// the assoc type will be linted after removing the unused field
self.visit_middle_ty(self.tcx.type_of(field_def.def_id).instantiate_identity());
intravisit::walk_field_def(self, field_def)
}
}

impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for MarkSymbolVisitor<'tcx> {
fn visit_ty(&mut self, ty: Ty<'tcx>) {
match ty.kind() {
ty::Alias(_, alias) => {
self.check_def_id(alias.def_id);
}
_ => (),
}
ty.super_visit_with(self);
}
}

fn has_allow_dead_code_or_lang_attr(
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/associated-type-bounds/union-bounds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

trait Tr1: Copy { type As1: Copy; }
trait Tr2: Copy { type As2: Copy; }
trait Tr3: Copy { type As3: Copy; }
trait Tr3: Copy { #[allow(dead_code)] type As3: Copy; }
trait Tr4<'a>: Copy { type As4: Copy; }
trait Tr5: Copy { type As5: Copy; }

Expand Down
2 changes: 1 addition & 1 deletion tests/ui/associated-types/impl-wf-cycle-5.fixed
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ impl Fiz for bool {}

trait Grault {
type A;
type B;
#[allow(dead_code)] type B;
}

impl Grault for () {
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/associated-types/impl-wf-cycle-5.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ impl Fiz for bool {}

trait Grault {
type A;
type B;
#[allow(dead_code)] type B;
}

impl Grault for () {
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/associated-types/impl-wf-cycle-6.fixed
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ impl Fiz for bool {}

trait Grault {
type A;
type B;
#[allow(dead_code)] type B;
}

impl Grault for () {
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/associated-types/impl-wf-cycle-6.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ impl Fiz for bool {}

trait Grault {
type A;
type B;
#[allow(dead_code)] type B;
}

impl Grault for () {
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/generic-associated-types/collections.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ trait Collection<T> {
type Iter<'iter>: Iterator<Item=&'iter T> where T: 'iter, Self: 'iter;
type Family: CollectionFamily;
// Test associated type defaults with parameters
type Sibling<U>: Collection<U> =
#[allow(dead_code)] type Sibling<U>: Collection<U> =
<<Self as Collection<T>>::Family as CollectionFamily>::Member<U>;

fn empty() -> Self;
Expand Down
82 changes: 82 additions & 0 deletions tests/ui/lint/dead-code/unused-assoc-ty-in-used-trait.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#![deny(dead_code)]

trait Tr {
type A;
type B;
type C: Default;
type D;
type E;
type F;
type G;
type H; //~ ERROR associated type `H` is never used
}

impl Tr for i32 {
type A = Self;
type B = Self;
type C = Self;
type D = Self;
type E = Self;
type F = Self;
type G = Self;
type H = Self;
}

trait Tr2 {
type A;
}

impl Tr2 for i32 {
type A = Self;
}

struct Foo<T: Tr2> {
_x: T::A,
}

struct Bar<T> {
_x: T
}

impl<T> Bar<T>
where
T: Tr2<A = i32>
{}

type Baz<T> = <T as Tr>::G;

struct FooBaz<T: Tr> {
_x: Baz<T>,
}

fn foo<T: Tr>(t: impl Tr<A = T>) -> impl Tr
where
T::B: Copy
{
let _a: T::C = Default::default();
baz::<T::F>();
t
}
fn bar<T: ?Sized>() {}
fn baz<T>() {}

trait Trait { type Ty; }

impl Trait for () { type Ty = (); }

pub struct Wrap(Inner<()>);
struct Inner<T: Trait>(T::Ty); // <- use of QPath::TypeRelative `Ty` in a non-body only

impl Wrap {
pub fn live(self) { _ = self.0; }
}

fn main() {
foo::<i32>(42);
bar::<dyn Tr2<A = i32>>();
let _d: <i32 as Tr>::D = Default::default();
baz::<<i32 as Tr>::E>();
baz::<Foo<i32>>();
baz::<Bar<i32>>();
baz::<FooBaz<i32>>();
}
17 changes: 17 additions & 0 deletions tests/ui/lint/dead-code/unused-assoc-ty-in-used-trait.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
error: associated type `H` is never used
--> $DIR/unused-assoc-ty-in-used-trait.rs:11:10
|
LL | trait Tr {
| -- associated type in this trait
...
LL | type H;
| ^
|
note: the lint level is defined here
--> $DIR/unused-assoc-ty-in-used-trait.rs:1:9
|
LL | #![deny(dead_code)]
| ^^^^^^^^^

error: aborting due to 1 previous error

12 changes: 12 additions & 0 deletions tests/ui/lint/dead-code/used-inherent-assoc-ty.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//@ check-pass

#![feature(inherent_associated_types)]
#![allow(incomplete_features)]
#[deny(dead_code)]

fn main() {
let _: Struct::Item = ();
}

struct Struct;
impl Struct { type Item = (); }
2 changes: 1 addition & 1 deletion tests/ui/sanitizer/cfi/sized-associated-ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
//@ run-pass

trait Foo {
type Bar<'a>
#[allow(dead_code)] type Bar<'a>
where
Self: Sized;

Expand Down
3 changes: 2 additions & 1 deletion tests/ui/traits/issue-38033.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@ trait Future {

trait IntoFuture {
type Future: Future<Item=Self::Item, Error=Self::Error>;
//~^ WARN associated items `Future` and `into_future` are never used
type Item;
type Error;

fn into_future(self) -> Self::Future; //~ WARN method `into_future` is never used
fn into_future(self) -> Self::Future;
}

impl<F: Future> IntoFuture for F {
Expand Down
8 changes: 5 additions & 3 deletions tests/ui/traits/issue-38033.stderr
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
warning: method `into_future` is never used
--> $DIR/issue-38033.rs:22:8
warning: associated items `Future` and `into_future` are never used
--> $DIR/issue-38033.rs:18:10
|
LL | trait IntoFuture {
| ---------- method in this trait
| ---------- associated items in this trait
LL | type Future: Future<Item=Self::Item, Error=Self::Error>;
| ^^^^^^
...
LL | fn into_future(self) -> Self::Future;
| ^^^^^^^^^^^
Expand Down
Loading