diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 55514883cb1f8..93a01d1299601 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -15,7 +15,7 @@ use rustc_hir::{Node, PatKind, TyKind}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::middle::privacy::Level; use rustc_middle::query::Providers; -use rustc_middle::ty::{self, AssocItemContainer, TyCtxt}; +use rustc_middle::ty::{self, AssocItemContainer, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor}; use rustc_middle::{bug, span_bug}; use rustc_session::lint; use rustc_session::lint::builtin::DEAD_CODE; @@ -450,7 +450,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { intravisit::walk_item(self, item) } hir::ItemKind::ForeignMod { .. } => {} - hir::ItemKind::Trait(_, _, _, _, trait_item_refs) => { + hir::ItemKind::Trait(..) => { for impl_def_id in self.tcx.all_impls(item.owner_id.to_def_id()) { if let Some(local_def_id) = impl_def_id.as_local() && let ItemKind::Impl(impl_ref) = @@ -463,12 +463,13 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { intravisit::walk_path(self, impl_ref.of_trait.unwrap().path); } } - // mark assoc ty live if the trait is live - for trait_item in trait_item_refs { - if let hir::AssocItemKind::Type = trait_item.kind { - self.check_def_id(trait_item.id.owner_id.to_def_id()); - } - } + intravisit::walk_item(self, item) + } + hir::ItemKind::Fn(..) => { + // check `T::Ty` in the types of inputs and output + // the result of type_of maybe different from the fn sig, + // so we also check the fn sig + self.visit_middle_fn_sig(item.owner_id.def_id); intravisit::walk_item(self, item) } _ => intravisit::walk_item(self, item), @@ -504,6 +505,21 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { } } } + + match trait_item.kind { + hir::TraitItemKind::Fn(..) => { + // check `T::Ty` in the types of inputs and output + // the result of type_of maybe different from the fn sig, + // so we also check the fn sig + self.visit_middle_fn_sig(trait_item.owner_id.def_id) + } + hir::TraitItemKind::Type(.., Some(_)) | hir::TraitItemKind::Const(..) => { + // check `type X = T::Ty;` or `const X: T::Ty;` + self.visit_middle_ty_by_def_id(trait_item.owner_id.def_id) + } + _ => (), + } + intravisit::walk_trait_item(self, trait_item); } Node::ImplItem(impl_item) => { @@ -525,6 +541,20 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { _ => {} } } + + match impl_item.kind { + hir::ImplItemKind::Fn(..) => { + // check `T::Ty` in the types of inputs and output + // the result of type_of maybe different from the fn sig, + // so we also check the fn sig + self.visit_middle_fn_sig(impl_item.owner_id.def_id) + } + hir::ImplItemKind::Type(..) | hir::ImplItemKind::Const(..) => { + // check `type X = T::Ty;` or `const X: T::Ty;` + self.visit_middle_ty_by_def_id(impl_item.owner_id.def_id) + } + } + intravisit::walk_impl_item(self, impl_item); } Node::ForeignItem(foreign_item) => { @@ -587,6 +617,22 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { false } } + + fn visit_middle_ty(&mut self, ty: Ty<'tcx>) { + >>::visit_ty(self, ty); + } + + fn visit_middle_ty_by_def_id(&mut self, def_id: LocalDefId) { + self.visit_middle_ty(self.tcx.type_of(def_id).instantiate_identity()); + } + + fn visit_middle_fn_sig(&mut self, def_id: LocalDefId) { + 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()); + } } impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> { @@ -618,6 +664,19 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> { intravisit::walk_struct_def(self, def); } + fn visit_field_def(&mut self, s: &'tcx rustc_hir::FieldDef<'tcx>) { + // check `field: T::Ty` + // marks assoc types live whether the field is not used or not + // there are three situations: + // 1. the field is used, it's good + // 2. the field is not used but marked like `#[allow(dead_code)]`, + // it's annoying to mark the assoc type `#[allow(dead_code)]` again + // 3. the field is not used, and will be linted + // the assoc type will be linted after removing the unused field + self.visit_middle_ty_by_def_id(s.def_id); + intravisit::walk_field_def(self, s); + } + fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { match expr.kind { hir::ExprKind::Path(ref qpath @ hir::QPath::TypeRelative(..)) => { @@ -650,6 +709,9 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> { _ => (), } + // check the expr_ty if its type is `T::Ty` + self.visit_middle_ty(self.typeck_results().expr_ty(expr)); + intravisit::walk_expr(self, expr); } @@ -722,6 +784,41 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> { self.in_pat = in_pat; } + + fn visit_poly_trait_ref(&mut self, t: &'tcx hir::PolyTraitRef<'tcx>) { + // mark the assoc type/const appears in poly-trait-ref live + if let Some(pathsegment) = t.trait_ref.path.segments.last() + && let Some(args) = pathsegment.args + { + for constraint in args.constraints { + if let Some(item) = self + .tcx + .associated_items(pathsegment.res.def_id()) + .filter_by_name_unhygienic(constraint.ident.name) + .find(|i| { + matches!(i.kind, ty::AssocKind::Const | ty::AssocKind::Type) + && i.ident(self.tcx).normalize_to_macros_2_0() == constraint.ident + }) + && let Some(local_def_id) = item.def_id.as_local() + { + self.worklist.push((local_def_id, ComesFromAllowExpect::No)); + } + } + } + intravisit::walk_poly_trait_ref(self, t); + } +} + +impl<'tcx> TypeVisitor> 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( diff --git a/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs b/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs index 1ccb6f36c8ef5..0a09f05d15378 100644 --- a/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs +++ b/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs @@ -4,7 +4,6 @@ use crate::layout; pub(crate) trait QueryContext { type Def: layout::Def; type Ref: layout::Ref; - type Scope: Copy; } #[cfg(test)] @@ -28,19 +27,16 @@ pub(crate) mod test { impl QueryContext for UltraMinimal { type Def = Def; type Ref = !; - type Scope = (); } } #[cfg(feature = "rustc")] mod rustc { use super::*; - use rustc_middle::ty::{Ty, TyCtxt}; + use rustc_middle::ty::TyCtxt; impl<'tcx> super::QueryContext for TyCtxt<'tcx> { type Def = layout::rustc::Def<'tcx>; type Ref = layout::rustc::Ref<'tcx>; - - type Scope = Ty<'tcx>; } } diff --git a/library/alloc/src/vec/in_place_collect.rs b/library/alloc/src/vec/in_place_collect.rs index 0dc193d82c535..96518e84a9b95 100644 --- a/library/alloc/src/vec/in_place_collect.rs +++ b/library/alloc/src/vec/in_place_collect.rs @@ -214,6 +214,7 @@ const fn needs_realloc(src_cap: usize, dst_cap: usize) -> bool { /// This provides a shorthand for the source type since local type aliases aren't a thing. #[rustc_specialization_trait] +#[allow(dead_code)] trait InPlaceCollect: SourceIter + InPlaceIterable { type Src; } diff --git a/library/core/src/ops/async_function.rs b/library/core/src/ops/async_function.rs index 48d1042d9df4a..689098722846e 100644 --- a/library/core/src/ops/async_function.rs +++ b/library/core/src/ops/async_function.rs @@ -147,6 +147,7 @@ mod internal_implementation_detail { // of the closure's self-capture, and these upvar types will be instantiated with // the `'closure_env` region provided to the associated type. #[lang = "async_fn_kind_upvars"] + #[allow(dead_code)] type Upvars<'closure_env, Inputs, Upvars, BorrowedUpvarsAsFnPtr>; } } diff --git a/tests/ui/associated-type-bounds/union-bounds.rs b/tests/ui/associated-type-bounds/union-bounds.rs index b9b92a96fb009..be726eee6e8d2 100644 --- a/tests/ui/associated-type-bounds/union-bounds.rs +++ b/tests/ui/associated-type-bounds/union-bounds.rs @@ -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; } diff --git a/tests/ui/associated-types/impl-wf-cycle-5.fixed b/tests/ui/associated-types/impl-wf-cycle-5.fixed index 1c2c0811a5006..9e57f61c72620 100644 --- a/tests/ui/associated-types/impl-wf-cycle-5.fixed +++ b/tests/ui/associated-types/impl-wf-cycle-5.fixed @@ -11,6 +11,7 @@ impl Fiz for bool {} trait Grault { type A; + #[allow(dead_code)] type B; } diff --git a/tests/ui/associated-types/impl-wf-cycle-5.rs b/tests/ui/associated-types/impl-wf-cycle-5.rs index 88a0b762c37bf..c152888cdfaa4 100644 --- a/tests/ui/associated-types/impl-wf-cycle-5.rs +++ b/tests/ui/associated-types/impl-wf-cycle-5.rs @@ -11,6 +11,7 @@ impl Fiz for bool {} trait Grault { type A; + #[allow(dead_code)] type B; } diff --git a/tests/ui/associated-types/impl-wf-cycle-5.stderr b/tests/ui/associated-types/impl-wf-cycle-5.stderr index 61edf18b43d97..8a36630ad1eb3 100644 --- a/tests/ui/associated-types/impl-wf-cycle-5.stderr +++ b/tests/ui/associated-types/impl-wf-cycle-5.stderr @@ -1,5 +1,5 @@ error[E0275]: overflow evaluating the requirement `<(T,) as Grault>::A == _` - --> $DIR/impl-wf-cycle-5.rs:22:1 + --> $DIR/impl-wf-cycle-5.rs:23:1 | LL | / impl Grault for (T,) LL | | @@ -12,7 +12,7 @@ LL | type A = (); | ------ associated type `<(T,) as Grault>::A` is specified here | note: required for `(T,)` to implement `Grault` - --> $DIR/impl-wf-cycle-5.rs:22:9 + --> $DIR/impl-wf-cycle-5.rs:23:9 | LL | impl Grault for (T,) | ^^^^^^ ^^^^ diff --git a/tests/ui/associated-types/impl-wf-cycle-6.fixed b/tests/ui/associated-types/impl-wf-cycle-6.fixed index ce98b9c2f02a5..6fa33748a3676 100644 --- a/tests/ui/associated-types/impl-wf-cycle-6.fixed +++ b/tests/ui/associated-types/impl-wf-cycle-6.fixed @@ -11,6 +11,7 @@ impl Fiz for bool {} trait Grault { type A; + #[allow(dead_code)] type B; } diff --git a/tests/ui/associated-types/impl-wf-cycle-6.rs b/tests/ui/associated-types/impl-wf-cycle-6.rs index a05ffcd6b4c3e..e1b895b55b52b 100644 --- a/tests/ui/associated-types/impl-wf-cycle-6.rs +++ b/tests/ui/associated-types/impl-wf-cycle-6.rs @@ -11,6 +11,7 @@ impl Fiz for bool {} trait Grault { type A; + #[allow(dead_code)] type B; } diff --git a/tests/ui/associated-types/impl-wf-cycle-6.stderr b/tests/ui/associated-types/impl-wf-cycle-6.stderr index 1c7495033183f..f2c8a9ae2e723 100644 --- a/tests/ui/associated-types/impl-wf-cycle-6.stderr +++ b/tests/ui/associated-types/impl-wf-cycle-6.stderr @@ -1,5 +1,5 @@ error[E0275]: overflow evaluating the requirement `<(T,) as Grault>::A == _` - --> $DIR/impl-wf-cycle-6.rs:22:1 + --> $DIR/impl-wf-cycle-6.rs:23:1 | LL | / impl Grault for (T,) LL | | @@ -11,7 +11,7 @@ LL | type A = (); | ------ associated type `<(T,) as Grault>::A` is specified here | note: required for `(T,)` to implement `Grault` - --> $DIR/impl-wf-cycle-6.rs:22:17 + --> $DIR/impl-wf-cycle-6.rs:23:17 | LL | impl Grault for (T,) | ^^^^^^ ^^^^ diff --git a/tests/ui/generic-associated-types/collections.rs b/tests/ui/generic-associated-types/collections.rs index 7239d226927df..5741db4830c38 100644 --- a/tests/ui/generic-associated-types/collections.rs +++ b/tests/ui/generic-associated-types/collections.rs @@ -10,6 +10,7 @@ trait Collection { type Iter<'iter>: Iterator where T: 'iter, Self: 'iter; type Family: CollectionFamily; // Test associated type defaults with parameters + #[allow(dead_code)] type Sibling: Collection = <>::Family as CollectionFamily>::Member; diff --git a/tests/ui/lint/dead-code/unused-assoc-ty-in-used-trait.rs b/tests/ui/lint/dead-code/unused-assoc-ty-in-used-trait.rs new file mode 100644 index 0000000000000..71369ab553777 --- /dev/null +++ b/tests/ui/lint/dead-code/unused-assoc-ty-in-used-trait.rs @@ -0,0 +1,23 @@ +#![deny(dead_code)] + +trait Tr { + type X; //~ ERROR associated type `X` is never used + type Y; +} + +impl Tr for () { + type X = Self; + type Y = Self; +} + +trait Tr2 { + type X; +} + +fn foo() -> impl Tr {} +fn bar() {} + +fn main() { + foo(); + bar::>(); +} diff --git a/tests/ui/lint/dead-code/unused-assoc-ty-in-used-trait.stderr b/tests/ui/lint/dead-code/unused-assoc-ty-in-used-trait.stderr new file mode 100644 index 0000000000000..b3373d5b4f153 --- /dev/null +++ b/tests/ui/lint/dead-code/unused-assoc-ty-in-used-trait.stderr @@ -0,0 +1,16 @@ +error: associated type `X` is never used + --> $DIR/unused-assoc-ty-in-used-trait.rs:4:10 + | +LL | trait Tr { + | -- associated type in this trait +LL | type X; + | ^ + | +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 + diff --git a/tests/ui/traits/issue-38033.rs b/tests/ui/traits/issue-38033.rs index f3525bd13c4fa..801ded51f791f 100644 --- a/tests/ui/traits/issue-38033.rs +++ b/tests/ui/traits/issue-38033.rs @@ -16,10 +16,11 @@ trait Future { trait IntoFuture { type Future: Future; + //~^ 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 IntoFuture for F { diff --git a/tests/ui/traits/issue-38033.stderr b/tests/ui/traits/issue-38033.stderr index 05385e8cf4d9d..34fee29d04dcb 100644 --- a/tests/ui/traits/issue-38033.stderr +++ b/tests/ui/traits/issue-38033.stderr @@ -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; + | ^^^^^^ ... LL | fn into_future(self) -> Self::Future; | ^^^^^^^^^^^