From 27ddb1b433ffb82eae5144a3d4b4db6cc3fc623b Mon Sep 17 00:00:00 2001 From: mu001999 Date: Mon, 5 Aug 2024 22:54:37 +0800 Subject: [PATCH 1/5] Refactor the two-phase check for impls and impl items, makes the logic cleaner --- compiler/rustc_passes/src/dead.rs | 183 ++++++++++++------------------ 1 file changed, 75 insertions(+), 108 deletions(-) diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index b62d94d65f1f4..9827f7ac00f11 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -17,7 +17,7 @@ use rustc_hir::{self as 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, TyCtxt}; +use rustc_middle::ty::{self, AssocItemContainer, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_session::lint; use rustc_session::lint::builtin::DEAD_CODE; @@ -44,15 +44,18 @@ fn should_explore(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { ) } -fn ty_ref_to_pub_struct(tcx: TyCtxt<'_>, ty: &hir::Ty<'_>) -> bool { - if let TyKind::Path(hir::QPath::Resolved(_, path)) = ty.kind - && let Res::Def(def_kind, def_id) = path.res - && def_id.is_local() - && matches!(def_kind, DefKind::Struct | DefKind::Enum | DefKind::Union) - { - tcx.visibility(def_id).is_public() - } else { - true +fn adt_of<'tcx>(ty: &hir::Ty<'tcx>) -> Option<(LocalDefId, DefKind)> { + match ty.kind { + TyKind::Path(hir::QPath::Resolved(_, path)) => { + if let Res::Def(def_kind, def_id) = path.res + && let Some(local_def_id) = def_id.as_local() + { + Some((local_def_id, def_kind)) + } else { + None + } + } + _ => None, } } @@ -420,20 +423,6 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { intravisit::walk_item(self, item) } hir::ItemKind::ForeignMod { .. } => {} - hir::ItemKind::Trait(..) => { - for &impl_def_id in self.tcx.local_trait_impls(item.owner_id.def_id) { - if let ItemKind::Impl(impl_ref) = self.tcx.hir_expect_item(impl_def_id).kind - { - // skip items - // mark dependent traits live - intravisit::walk_generics(self, impl_ref.generics); - // mark dependent parameters live - intravisit::walk_path(self, impl_ref.of_trait.unwrap().path); - } - } - - intravisit::walk_item(self, item) - } _ => intravisit::walk_item(self, item), }, Node::TraitItem(trait_item) => { @@ -442,30 +431,8 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { if let Some(trait_id) = self.tcx.trait_of_item(trait_item_id) { // mark the trait live self.check_def_id(trait_id); - - for impl_id in self.tcx.all_impls(trait_id) { - if let Some(local_impl_id) = impl_id.as_local() - && let ItemKind::Impl(impl_ref) = - self.tcx.hir_expect_item(local_impl_id).kind - { - if !matches!(trait_item.kind, hir::TraitItemKind::Type(..)) - && !ty_ref_to_pub_struct(self.tcx, impl_ref.self_ty) - { - // skip methods of private ty, - // they would be solved in `solve_rest_impl_items` - continue; - } - - // mark self_ty live - intravisit::walk_unambig_ty(self, impl_ref.self_ty); - if let Some(&impl_item_id) = - self.tcx.impl_item_implementor_ids(impl_id).get(&trait_item_id) - { - self.check_def_id(impl_item_id); - } - } - } } + intravisit::walk_trait_item(self, trait_item); } Node::ImplItem(impl_item) => { @@ -508,11 +475,11 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { } } - fn solve_rest_impl_items(&mut self, mut unsolved_impl_items: Vec<(hir::ItemId, LocalDefId)>) { + fn solve_rest_items(&mut self, mut unsolved_items: Vec<(hir::ItemId, LocalDefId)>) { let mut ready; - (ready, unsolved_impl_items) = - unsolved_impl_items.into_iter().partition(|&(impl_id, impl_item_id)| { - self.impl_item_with_used_self(impl_id, impl_item_id) + (ready, unsolved_items) = + unsolved_items.into_iter().partition(|&(impl_id, local_def_id)| { + self.item_should_be_checked(impl_id, local_def_id) }); while !ready.is_empty() { @@ -520,36 +487,43 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { ready.into_iter().map(|(_, id)| (id, ComesFromAllowExpect::No)).collect(); self.mark_live_symbols(); - (ready, unsolved_impl_items) = - unsolved_impl_items.into_iter().partition(|&(impl_id, impl_item_id)| { - self.impl_item_with_used_self(impl_id, impl_item_id) + (ready, unsolved_items) = + unsolved_items.into_iter().partition(|&(impl_id, local_def_id)| { + self.item_should_be_checked(impl_id, local_def_id) }); } } - fn impl_item_with_used_self(&mut self, impl_id: hir::ItemId, impl_item_id: LocalDefId) -> bool { - if let TyKind::Path(hir::QPath::Resolved(_, path)) = - self.tcx.hir_item(impl_id).expect_impl().self_ty.kind - && let Res::Def(def_kind, def_id) = path.res - && let Some(local_def_id) = def_id.as_local() - && matches!(def_kind, DefKind::Struct | DefKind::Enum | DefKind::Union) - { - if self.tcx.visibility(impl_item_id).is_public() { - // for the public method, we don't know the trait item is used or not, - // so we mark the method live if the self is used - return self.live_symbols.contains(&local_def_id); - } + fn item_should_be_checked(&mut self, impl_id: hir::ItemId, local_def_id: LocalDefId) -> bool { + let trait_def_id = match self.tcx.def_kind(local_def_id) { + // for assoc impl items of traits, we concern the corresponding trait items are used or not + DefKind::AssocFn => self + .tcx + .associated_item(local_def_id) + .trait_item_def_id + .and_then(|def_id| def_id.as_local()), + // for impl items, we concern the corresonding traits are used or not + DefKind::Impl { of_trait: true } => self + .tcx + .impl_trait_ref(impl_id.owner_id.def_id) + .and_then(|trait_ref| trait_ref.skip_binder().def_id.as_local()), + _ => None, + }; - if let Some(trait_item_id) = self.tcx.associated_item(impl_item_id).trait_item_def_id - && let Some(local_id) = trait_item_id.as_local() - { - // for the private method, we can know the trait item is used or not, - // so we mark the method live if the self is used and the trait item is used - return self.live_symbols.contains(&local_id) - && self.live_symbols.contains(&local_def_id); - } + if !trait_def_id.map(|def_id| self.live_symbols.contains(&def_id)).unwrap_or(true) { + return false; } - false + + // we only check the ty is used or not for ADTs defined locally + let ty_def_id = adt_of(self.tcx.hir_item(impl_id).expect_impl().self_ty).and_then( + |(local_def_id, def_kind)| { + matches!(def_kind, DefKind::Struct | DefKind::Enum | DefKind::Union) + .then_some(local_def_id) + }, + ); + + // the impl/impl item is used if the trait/trait item is used and the ty is used + ty_def_id.map(|def_id| self.live_symbols.contains(&def_id)).unwrap_or(true) } } @@ -738,7 +712,7 @@ fn check_item<'tcx>( tcx: TyCtxt<'tcx>, worklist: &mut Vec<(LocalDefId, ComesFromAllowExpect)>, struct_constructors: &mut LocalDefIdMap, - unsolved_impl_items: &mut Vec<(hir::ItemId, LocalDefId)>, + unsolved_items: &mut Vec<(hir::ItemId, LocalDefId)>, id: hir::ItemId, ) { let allow_dead_code = has_allow_dead_code_or_lang_attr(tcx, id.owner_id.def_id); @@ -770,35 +744,24 @@ fn check_item<'tcx>( .iter() .filter_map(|def_id| def_id.as_local()); - let ty_is_pub = ty_ref_to_pub_struct(tcx, tcx.hir_item(id).expect_impl().self_ty); + if let Some(comes_from_allow) = + has_allow_dead_code_or_lang_attr(tcx, id.owner_id.def_id) + { + worklist.push((id.owner_id.def_id, comes_from_allow)); + } else if of_trait { + unsolved_items.push((id, id.owner_id.def_id)); + } // And we access the Map here to get HirId from LocalDefId for local_def_id in local_def_ids { - // check the function may construct Self - let mut may_construct_self = false; - if let Some(fn_sig) = - tcx.hir_fn_sig_by_hir_id(tcx.local_def_id_to_hir_id(local_def_id)) - { - may_construct_self = - matches!(fn_sig.decl.implicit_self, hir::ImplicitSelfKind::None); - } - - // for trait impl blocks, - // mark the method live if the self_ty is public, - // or the method is public and may construct self - if of_trait - && (!matches!(tcx.def_kind(local_def_id), DefKind::AssocFn) - || tcx.visibility(local_def_id).is_public() - && (ty_is_pub || may_construct_self)) - { + if !matches!(tcx.def_kind(local_def_id), DefKind::AssocFn) { worklist.push((local_def_id, ComesFromAllowExpect::No)); } else if let Some(comes_from_allow) = has_allow_dead_code_or_lang_attr(tcx, local_def_id) { worklist.push((local_def_id, comes_from_allow)); } else if of_trait { - // private method || public method not constructs self - unsolved_impl_items.push((id, local_def_id)); + unsolved_items.push((id, local_def_id)); } } } @@ -864,6 +827,18 @@ fn create_and_seed_worklist( effective_vis .is_public_at_level(Level::Reachable) .then_some(id) + .filter(|&id| + // checks impls and impl-items later + match tcx.def_kind(id) { + DefKind::Impl { of_trait } => !of_trait, + DefKind::AssocFn => { + // still check public trait items, and impl items not of trait + let assoc_item = tcx.associated_item(id); + !matches!(assoc_item.container, AssocItemContainer::Impl) + || assoc_item.trait_item_def_id.is_none() + }, + _ => true + }) .map(|id| (id, ComesFromAllowExpect::No)) }) // Seed entry point @@ -893,7 +868,7 @@ fn live_symbols_and_ignored_derived_traits( tcx: TyCtxt<'_>, (): (), ) -> (LocalDefIdSet, LocalDefIdMap>) { - let (worklist, struct_constructors, unsolved_impl_items) = create_and_seed_worklist(tcx); + let (worklist, struct_constructors, unsolved_items) = create_and_seed_worklist(tcx); let mut symbol_visitor = MarkSymbolVisitor { worklist, tcx, @@ -907,7 +882,7 @@ fn live_symbols_and_ignored_derived_traits( ignored_derived_traits: Default::default(), }; symbol_visitor.mark_live_symbols(); - symbol_visitor.solve_rest_impl_items(unsolved_impl_items); + symbol_visitor.solve_rest_items(unsolved_items); (symbol_visitor.live_symbols, symbol_visitor.ignored_derived_traits) } @@ -1186,19 +1161,11 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) { let def_kind = tcx.def_kind(item.owner_id); let mut dead_codes = Vec::new(); - // if we have diagnosed the trait, do not diagnose unused methods - if matches!(def_kind, DefKind::Impl { .. }) + // if we have diagnosed the trait, do not diagnose unused assoc items + if matches!(def_kind, DefKind::Impl { of_trait: false }) || (def_kind == DefKind::Trait && live_symbols.contains(&item.owner_id.def_id)) { for &def_id in tcx.associated_item_def_ids(item.owner_id.def_id) { - // We have diagnosed unused methods in traits - if matches!(def_kind, DefKind::Impl { of_trait: true }) - && tcx.def_kind(def_id) == DefKind::AssocFn - || def_kind == DefKind::Trait && tcx.def_kind(def_id) != DefKind::AssocFn - { - continue; - } - if let Some(local_def_id) = def_id.as_local() && !visitor.is_live_code(local_def_id) { From 820b20e4d3b00cff40d01e1fc6b2fe6eb89ffbc0 Mon Sep 17 00:00:00 2001 From: mu001999 Date: Mon, 5 Aug 2024 23:04:40 +0800 Subject: [PATCH 2/5] Marks the adt live if it appears in pattern, removes specifal logic for Default --- compiler/rustc_passes/src/dead.rs | 39 ++++++------------- library/core/src/default.rs | 1 - .../alias-type-used-as-generic-arg-in-impl.rs | 19 +++++++++ .../lint-unused-adt-appeared-in-pattern.rs | 37 ++++++++++++++++++ ...lint-unused-adt-appeared-in-pattern.stderr | 20 ++++++++++ ...nt-adt-appeared-in-pattern-issue-120770.rs | 32 +++++++++++++++ .../dead-code/unused-struct-derive-default.rs | 1 + .../unused-struct-derive-default.stderr | 1 - 8 files changed, 121 insertions(+), 29 deletions(-) create mode 100644 tests/ui/lint/dead-code/alias-type-used-as-generic-arg-in-impl.rs create mode 100644 tests/ui/lint/dead-code/lint-unused-adt-appeared-in-pattern.rs create mode 100644 tests/ui/lint/dead-code/lint-unused-adt-appeared-in-pattern.stderr create mode 100644 tests/ui/lint/dead-code/not-lint-adt-appeared-in-pattern-issue-120770.rs diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 9827f7ac00f11..ef903a8a3785d 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -231,7 +231,10 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { pats: &[hir::PatField<'_>], ) { let variant = match self.typeck_results().node_type(lhs.hir_id).kind() { - ty::Adt(adt, _) => adt.variant_of_res(res), + ty::Adt(adt, _) => { + self.check_def_id(adt.did()); + adt.variant_of_res(res) + } _ => span_bug!(lhs.span, "non-ADT in struct pattern"), }; for pat in pats { @@ -251,7 +254,10 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { dotdot: hir::DotDotPos, ) { let variant = match self.typeck_results().node_type(lhs.hir_id).kind() { - ty::Adt(adt, _) => adt.variant_of_res(res), + ty::Adt(adt, _) => { + self.check_def_id(adt.did()); + adt.variant_of_res(res) + } _ => { self.tcx.dcx().span_delayed_bug(lhs.span, "non-ADT in tuple struct pattern"); return; @@ -356,31 +362,6 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { return false; } - // don't ignore impls for Enums and pub Structs whose methods don't have self receiver, - // cause external crate may call such methods to construct values of these types - if let Some(local_impl_of) = impl_of.as_local() - && let Some(local_def_id) = def_id.as_local() - && let Some(fn_sig) = - self.tcx.hir_fn_sig_by_hir_id(self.tcx.local_def_id_to_hir_id(local_def_id)) - && matches!(fn_sig.decl.implicit_self, hir::ImplicitSelfKind::None) - && let TyKind::Path(hir::QPath::Resolved(_, path)) = - self.tcx.hir_expect_item(local_impl_of).expect_impl().self_ty.kind - && let Res::Def(def_kind, did) = path.res - { - match def_kind { - // for example, #[derive(Default)] pub struct T(i32); - // external crate can call T::default() to construct T, - // so that don't ignore impl Default for pub Enum and Structs - DefKind::Struct | DefKind::Union if self.tcx.visibility(did).is_public() => { - return false; - } - // don't ignore impl Default for Enums, - // cause we don't know which variant is constructed - DefKind::Enum => return false, - _ => (), - }; - } - if let Some(trait_of) = self.tcx.trait_id_of_impl(impl_of) && self.tcx.has_attr(trait_of, sym::rustc_trivial_field_reads) { @@ -622,6 +603,10 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> { fn visit_pat_expr(&mut self, expr: &'tcx rustc_hir::PatExpr<'tcx>) { match &expr.kind { rustc_hir::PatExprKind::Path(qpath) => { + // mark the type of variant live when meeting E::V in expr + if let ty::Adt(adt, _) = self.typeck_results().node_type(expr.hir_id).kind() { + self.check_def_id(adt.did()); + } let res = self.typeck_results().qpath_res(qpath, expr.hir_id); self.handle_res(res); } diff --git a/library/core/src/default.rs b/library/core/src/default.rs index 044997a81a9a2..0a15cedfb552d 100644 --- a/library/core/src/default.rs +++ b/library/core/src/default.rs @@ -103,7 +103,6 @@ use crate::ascii::Char as AsciiChar; /// ``` #[rustc_diagnostic_item = "Default"] #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_trivial_field_reads] pub trait Default: Sized { /// Returns the "default value" for a type. /// diff --git a/tests/ui/lint/dead-code/alias-type-used-as-generic-arg-in-impl.rs b/tests/ui/lint/dead-code/alias-type-used-as-generic-arg-in-impl.rs new file mode 100644 index 0000000000000..4857ef6a9b8bf --- /dev/null +++ b/tests/ui/lint/dead-code/alias-type-used-as-generic-arg-in-impl.rs @@ -0,0 +1,19 @@ +//@ check-pass + +#![deny(dead_code)] + +struct T(X); + +type A = T; + +trait Tr { + fn foo(); +} + +impl Tr for T> { + fn foo() {} +} + +fn main() { + T::>::foo(); +} diff --git a/tests/ui/lint/dead-code/lint-unused-adt-appeared-in-pattern.rs b/tests/ui/lint/dead-code/lint-unused-adt-appeared-in-pattern.rs new file mode 100644 index 0000000000000..25777438456b6 --- /dev/null +++ b/tests/ui/lint/dead-code/lint-unused-adt-appeared-in-pattern.rs @@ -0,0 +1,37 @@ +#![deny(dead_code)] + +struct Foo(u8); //~ ERROR struct `Foo` is never constructed + +enum Bar { //~ ERROR enum `Bar` is never used + Var1(u8), + Var2(u8), +} + +pub trait Tr1 { + fn f1() -> Self; +} + +impl Tr1 for Foo { + fn f1() -> Foo { + let f = Foo(0); + let Foo(tag) = f; + Foo(tag) + } +} + +impl Tr1 for Bar { + fn f1() -> Bar { + let b = Bar::Var1(0); + let b = if let Bar::Var1(_) = b { + Bar::Var1(0) + } else { + Bar::Var2(0) + }; + match b { + Bar::Var1(_) => Bar::Var2(0), + Bar::Var2(_) => Bar::Var1(0), + } + } +} + +fn main() {} diff --git a/tests/ui/lint/dead-code/lint-unused-adt-appeared-in-pattern.stderr b/tests/ui/lint/dead-code/lint-unused-adt-appeared-in-pattern.stderr new file mode 100644 index 0000000000000..7c1a4b4597755 --- /dev/null +++ b/tests/ui/lint/dead-code/lint-unused-adt-appeared-in-pattern.stderr @@ -0,0 +1,20 @@ +error: struct `Foo` is never constructed + --> $DIR/lint-unused-adt-appeared-in-pattern.rs:3:8 + | +LL | struct Foo(u8); + | ^^^ + | +note: the lint level is defined here + --> $DIR/lint-unused-adt-appeared-in-pattern.rs:1:9 + | +LL | #![deny(dead_code)] + | ^^^^^^^^^ + +error: enum `Bar` is never used + --> $DIR/lint-unused-adt-appeared-in-pattern.rs:5:6 + | +LL | enum Bar { + | ^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/lint/dead-code/not-lint-adt-appeared-in-pattern-issue-120770.rs b/tests/ui/lint/dead-code/not-lint-adt-appeared-in-pattern-issue-120770.rs new file mode 100644 index 0000000000000..43a2e43190433 --- /dev/null +++ b/tests/ui/lint/dead-code/not-lint-adt-appeared-in-pattern-issue-120770.rs @@ -0,0 +1,32 @@ +//@ check-pass + +#![deny(dead_code)] + +#[repr(u8)] +#[derive(Copy, Clone, Debug)] +pub enum RecordField { + Target = 1, + Level, + Module, + File, + Line, + NumArgs, +} + +unsafe trait Pod {} + +#[repr(transparent)] +struct RecordFieldWrapper(RecordField); + +unsafe impl Pod for RecordFieldWrapper {} + +fn try_read(buf: &[u8]) -> T { + unsafe { std::ptr::read_unaligned(buf.as_ptr() as *const T) } +} + +pub fn foo(buf: &[u8]) -> RecordField { + let RecordFieldWrapper(tag) = try_read(buf); + tag +} + +fn main() {} diff --git a/tests/ui/lint/dead-code/unused-struct-derive-default.rs b/tests/ui/lint/dead-code/unused-struct-derive-default.rs index 330ad32dd5709..f20b7cb66ee51 100644 --- a/tests/ui/lint/dead-code/unused-struct-derive-default.rs +++ b/tests/ui/lint/dead-code/unused-struct-derive-default.rs @@ -22,4 +22,5 @@ pub struct T2 { fn main() { let _x: Used = Default::default(); + let _e: E = Default::default(); } diff --git a/tests/ui/lint/dead-code/unused-struct-derive-default.stderr b/tests/ui/lint/dead-code/unused-struct-derive-default.stderr index bbb0bd7be7064..7422f9a39f312 100644 --- a/tests/ui/lint/dead-code/unused-struct-derive-default.stderr +++ b/tests/ui/lint/dead-code/unused-struct-derive-default.stderr @@ -4,7 +4,6 @@ error: struct `T` is never constructed LL | struct T; | ^ | - = note: `T` has a derived impl for the trait `Default`, but this is intentionally ignored during dead code analysis note: the lint level is defined here --> $DIR/unused-struct-derive-default.rs:1:9 | From 02a109e7a66ad77c122fc22c2bfe86b1d6453d50 Mon Sep 17 00:00:00 2001 From: mu001999 Date: Mon, 5 Aug 2024 23:06:21 +0800 Subject: [PATCH 3/5] Extends rules to impls for types which refer to adts, like &Foo things --- compiler/rustc_passes/src/dead.rs | 2 + .../dead-code/unused-impl-for-non-adts.rs | 45 +++++++++++++++++++ .../dead-code/unused-impl-for-non-adts.stderr | 20 +++++++++ 3 files changed, 67 insertions(+) create mode 100644 tests/ui/lint/dead-code/unused-impl-for-non-adts.rs create mode 100644 tests/ui/lint/dead-code/unused-impl-for-non-adts.stderr diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index ef903a8a3785d..d62f924721d6a 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -55,6 +55,8 @@ fn adt_of<'tcx>(ty: &hir::Ty<'tcx>) -> Option<(LocalDefId, DefKind)> { None } } + TyKind::Slice(ty) | TyKind::Array(ty, _) => adt_of(ty), + TyKind::Ptr(ty) | TyKind::Ref(_, ty) => adt_of(ty.ty), _ => None, } } diff --git a/tests/ui/lint/dead-code/unused-impl-for-non-adts.rs b/tests/ui/lint/dead-code/unused-impl-for-non-adts.rs new file mode 100644 index 0000000000000..46065dcee81e0 --- /dev/null +++ b/tests/ui/lint/dead-code/unused-impl-for-non-adts.rs @@ -0,0 +1,45 @@ +#![deny(dead_code)] + +struct Foo; //~ ERROR struct `Foo` is never constructed + +trait Trait { //~ ERROR trait `Trait` is never used + fn foo(&self) {} +} + +impl Trait for Foo {} + +impl Trait for [Foo] {} +impl Trait for [Foo; N] {} + +impl Trait for *const Foo {} +impl Trait for *mut Foo {} + +impl Trait for &Foo {} +impl Trait for &&Foo {} +impl Trait for &mut Foo {} + +impl Trait for [&Foo] {} +impl Trait for &[Foo] {} +impl Trait for &*const Foo {} + +pub trait Trait2 { + fn foo(&self) {} +} + +impl Trait2 for Foo {} + +impl Trait2 for [Foo] {} +impl Trait2 for [Foo; N] {} + +impl Trait2 for *const Foo {} +impl Trait2 for *mut Foo {} + +impl Trait2 for &Foo {} +impl Trait2 for &&Foo {} +impl Trait2 for &mut Foo {} + +impl Trait2 for [&Foo] {} +impl Trait2 for &[Foo] {} +impl Trait2 for &*const Foo {} + +fn main() {} diff --git a/tests/ui/lint/dead-code/unused-impl-for-non-adts.stderr b/tests/ui/lint/dead-code/unused-impl-for-non-adts.stderr new file mode 100644 index 0000000000000..e61fc403e810d --- /dev/null +++ b/tests/ui/lint/dead-code/unused-impl-for-non-adts.stderr @@ -0,0 +1,20 @@ +error: struct `Foo` is never constructed + --> $DIR/unused-impl-for-non-adts.rs:3:8 + | +LL | struct Foo; + | ^^^ + | +note: the lint level is defined here + --> $DIR/unused-impl-for-non-adts.rs:1:9 + | +LL | #![deny(dead_code)] + | ^^^^^^^^^ + +error: trait `Trait` is never used + --> $DIR/unused-impl-for-non-adts.rs:5:7 + | +LL | trait Trait { + | ^^^^^ + +error: aborting due to 2 previous errors + From 73b0eb456e23980219e469ade4d9d2acc102759f Mon Sep 17 00:00:00 2001 From: mu001999 Date: Mon, 5 Aug 2024 23:18:31 +0800 Subject: [PATCH 4/5] Lints unused assoc consts and unused trait with assoc tys --- compiler/rustc_passes/src/dead.rs | 164 ++++++++++++------ .../trait-only-used-as-type-bound.rs | 31 ++++ ...sed-adt-impl-pub-trait-with-assoc-const.rs | 8 - ...adt-impl-pub-trait-with-assoc-const.stderr | 2 +- tests/ui/lint/dead-code/unused-assoc-const.rs | 20 +++ .../lint/dead-code/unused-assoc-const.stderr | 16 ++ tests/ui/lint/dead-code/unused-assoc-ty.rs | 25 +++ .../ui/lint/dead-code/unused-assoc-ty.stderr | 16 ++ 8 files changed, 222 insertions(+), 60 deletions(-) create mode 100644 tests/ui/lint/dead-code/trait-only-used-as-type-bound.rs create mode 100644 tests/ui/lint/dead-code/unused-assoc-const.rs create mode 100644 tests/ui/lint/dead-code/unused-assoc-const.stderr create mode 100644 tests/ui/lint/dead-code/unused-assoc-ty.rs create mode 100644 tests/ui/lint/dead-code/unused-assoc-ty.stderr diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index d62f924721d6a..c12b55ec7b10c 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -17,7 +17,7 @@ use rustc_hir::{self as 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, Ty, TyCtxt}; +use rustc_middle::ty::{self, AssocItemContainer, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_session::lint; use rustc_session::lint::builtin::DEAD_CODE; @@ -44,7 +44,9 @@ fn should_explore(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { ) } -fn adt_of<'tcx>(ty: &hir::Ty<'tcx>) -> Option<(LocalDefId, DefKind)> { +/// Returns the local def id and def kind of the adt, +/// if the given ty refers to one local adt definition +fn adt_def_of_ty<'tcx>(ty: &hir::Ty<'tcx>) -> Option<(LocalDefId, DefKind)> { match ty.kind { TyKind::Path(hir::QPath::Resolved(_, path)) => { if let Res::Def(def_kind, def_id) = path.res @@ -55,8 +57,8 @@ fn adt_of<'tcx>(ty: &hir::Ty<'tcx>) -> Option<(LocalDefId, DefKind)> { None } } - TyKind::Slice(ty) | TyKind::Array(ty, _) => adt_of(ty), - TyKind::Ptr(ty) | TyKind::Ref(_, ty) => adt_of(ty.ty), + TyKind::Slice(ty) | TyKind::Array(ty, _) => adt_def_of_ty(ty), + TyKind::Ptr(ty) | TyKind::Ref(_, ty) => adt_def_of_ty(ty.ty), _ => None, } } @@ -114,7 +116,10 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { fn handle_res(&mut self, res: Res) { match res { - Res::Def(DefKind::Const | DefKind::AssocConst | DefKind::TyAlias, def_id) => { + Res::Def( + DefKind::AssocConst | DefKind::AssocTy | DefKind::Const | DefKind::TyAlias, + def_id, + ) => { self.check_def_id(def_id); } _ if self.in_pat => {} @@ -234,6 +239,10 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { ) { let variant = match self.typeck_results().node_type(lhs.hir_id).kind() { ty::Adt(adt, _) => { + // mark the adt live if its variant appears as the pattern + // considering cases when we have `let T(x) = foo()` and `fn foo() -> T;` + // we will lose the liveness info of `T` cause we cannot mark it live when visiting `foo` + // related issue: https://github.com/rust-lang/rust/issues/120770 self.check_def_id(adt.did()); adt.variant_of_res(res) } @@ -257,6 +266,10 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { ) { let variant = match self.typeck_results().node_type(lhs.hir_id).kind() { ty::Adt(adt, _) => { + // mark the adt live if its variant appears as the pattern + // considering cases when we have `let T(x) = foo()` and `fn foo() -> T;` + // we will lose the liveness info of `T` cause we cannot mark it live when visiting `foo` + // related issue: https://github.com/rust-lang/rust/issues/120770 self.check_def_id(adt.did()); adt.variant_of_res(res) } @@ -406,13 +419,21 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { intravisit::walk_item(self, item) } hir::ItemKind::ForeignMod { .. } => {} + hir::ItemKind::Trait(_, _, _, _, _, trait_item_refs) => { + // mark assoc ty live if the trait is live + for trait_item in trait_item_refs { + if matches!(trait_item.kind, hir::AssocItemKind::Type) { + self.check_def_id(trait_item.id.owner_id.to_def_id()); + } + } + intravisit::walk_item(self, item) + } _ => intravisit::walk_item(self, item), }, Node::TraitItem(trait_item) => { - // mark corresponding ImplTerm live + // mark the trait live let trait_item_id = trait_item.owner_id.to_def_id(); if let Some(trait_id) = self.tcx.trait_of_item(trait_item_id) { - // mark the trait live self.check_def_id(trait_id); } @@ -437,6 +458,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { _ => {} } } + intravisit::walk_impl_item(self, impl_item); } Node::ForeignItem(foreign_item) => { @@ -458,34 +480,20 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { } } - fn solve_rest_items(&mut self, mut unsolved_items: Vec<(hir::ItemId, LocalDefId)>) { - let mut ready; - (ready, unsolved_items) = - unsolved_items.into_iter().partition(|&(impl_id, local_def_id)| { - self.item_should_be_checked(impl_id, local_def_id) - }); - - while !ready.is_empty() { - self.worklist = - ready.into_iter().map(|(_, id)| (id, ComesFromAllowExpect::No)).collect(); - self.mark_live_symbols(); - - (ready, unsolved_items) = - unsolved_items.into_iter().partition(|&(impl_id, local_def_id)| { - self.item_should_be_checked(impl_id, local_def_id) - }); - } - } - + /// Returns an impl or impl item should be checked or not + /// `impl_id` is the `ItemId` of the impl or the impl item belongs to, + /// `local_def_id` may point to the impl or the impl item, + /// both impl and impl item that may pass to this function are of trait, + /// and added into the unsolved_items during `create_and_seed_worklist` fn item_should_be_checked(&mut self, impl_id: hir::ItemId, local_def_id: LocalDefId) -> bool { let trait_def_id = match self.tcx.def_kind(local_def_id) { - // for assoc impl items of traits, we concern the corresponding trait items are used or not - DefKind::AssocFn => self + // assoc impl items of traits are live if the corresponding trait items are live + DefKind::AssocConst | DefKind::AssocTy | DefKind::AssocFn => self .tcx .associated_item(local_def_id) .trait_item_def_id .and_then(|def_id| def_id.as_local()), - // for impl items, we concern the corresonding traits are used or not + // impl items are live if the corresponding traits are live DefKind::Impl { of_trait: true } => self .tcx .impl_trait_ref(impl_id.owner_id.def_id) @@ -493,20 +501,23 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { _ => None, }; - if !trait_def_id.map(|def_id| self.live_symbols.contains(&def_id)).unwrap_or(true) { + if let Some(def_id) = trait_def_id + && !self.live_symbols.contains(&def_id) + { return false; } // we only check the ty is used or not for ADTs defined locally - let ty_def_id = adt_of(self.tcx.hir_item(impl_id).expect_impl().self_ty).and_then( - |(local_def_id, def_kind)| { - matches!(def_kind, DefKind::Struct | DefKind::Enum | DefKind::Union) - .then_some(local_def_id) - }, - ); - // the impl/impl item is used if the trait/trait item is used and the ty is used - ty_def_id.map(|def_id| self.live_symbols.contains(&def_id)).unwrap_or(true) + if let Some((local_def_id, def_kind)) = + adt_def_of_ty(self.tcx.hir_item(impl_id).expect_impl().self_ty) + && matches!(def_kind, DefKind::Struct | DefKind::Enum | DefKind::Union) + && !self.live_symbols.contains(&local_def_id) + { + return false; + } + + true } } @@ -643,6 +654,29 @@ 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 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) + && 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); + } } fn has_allow_dead_code_or_lang_attr( @@ -726,7 +760,7 @@ fn check_item<'tcx>( } DefKind::Impl { of_trait } => { // get DefIds from another query - let local_def_ids = tcx + let associated_item_def_ids = tcx .associated_item_def_ids(id.owner_id) .iter() .filter_map(|def_id| def_id.as_local()); @@ -740,14 +774,16 @@ fn check_item<'tcx>( } // And we access the Map here to get HirId from LocalDefId - for local_def_id in local_def_ids { - if !matches!(tcx.def_kind(local_def_id), DefKind::AssocFn) { - worklist.push((local_def_id, ComesFromAllowExpect::No)); - } else if let Some(comes_from_allow) = - has_allow_dead_code_or_lang_attr(tcx, local_def_id) + for local_def_id in associated_item_def_ids { + if let Some(comes_from_allow) = has_allow_dead_code_or_lang_attr(tcx, local_def_id) { worklist.push((local_def_id, comes_from_allow)); } else if of_trait { + // we only care about assoc items of trait, + // because they cannot be visited directly + // so we mark them based on the trait/trait item + // and self ty are checked first and both live, + // but inherent assoc items can be visited and marked directly unsolved_items.push((id, local_def_id)); } } @@ -773,10 +809,13 @@ fn check_trait_item( worklist: &mut Vec<(LocalDefId, ComesFromAllowExpect)>, id: hir::TraitItemId, ) { - use hir::TraitItemKind::{Const, Fn}; - if matches!(tcx.def_kind(id.owner_id), DefKind::AssocConst | DefKind::AssocFn) { + use hir::TraitItemKind::{Const, Fn, Type}; + if matches!( + tcx.def_kind(id.owner_id), + DefKind::AssocConst | DefKind::AssocTy | DefKind::AssocFn + ) { let trait_item = tcx.hir_trait_item(id); - if matches!(trait_item.kind, Const(_, Some(_)) | Fn(..)) + if matches!(trait_item.kind, Const(_, Some(_)) | Type(..) | Fn(..)) && let Some(comes_from_allow) = has_allow_dead_code_or_lang_attr(tcx, trait_item.owner_id.def_id) { @@ -818,7 +857,7 @@ fn create_and_seed_worklist( // checks impls and impl-items later match tcx.def_kind(id) { DefKind::Impl { of_trait } => !of_trait, - DefKind::AssocFn => { + DefKind::AssocConst | DefKind::AssocTy | DefKind::AssocFn => { // still check public trait items, and impl items not of trait let assoc_item = tcx.associated_item(id); !matches!(assoc_item.container, AssocItemContainer::Impl) @@ -855,7 +894,7 @@ fn live_symbols_and_ignored_derived_traits( tcx: TyCtxt<'_>, (): (), ) -> (LocalDefIdSet, LocalDefIdMap>) { - let (worklist, struct_constructors, unsolved_items) = create_and_seed_worklist(tcx); + let (worklist, struct_constructors, mut unsolved_items) = create_and_seed_worklist(tcx); let mut symbol_visitor = MarkSymbolVisitor { worklist, tcx, @@ -868,8 +907,26 @@ fn live_symbols_and_ignored_derived_traits( struct_constructors, ignored_derived_traits: Default::default(), }; + symbol_visitor.mark_live_symbols(); - symbol_visitor.solve_rest_items(unsolved_items); + let mut rest_items_should_be_checked; + (rest_items_should_be_checked, unsolved_items) = + unsolved_items.into_iter().partition(|&(impl_id, local_def_id)| { + symbol_visitor.item_should_be_checked(impl_id, local_def_id) + }); + + while !rest_items_should_be_checked.is_empty() { + symbol_visitor.worklist = rest_items_should_be_checked + .into_iter() + .map(|(_, id)| (id, ComesFromAllowExpect::No)) + .collect(); + symbol_visitor.mark_live_symbols(); + + (rest_items_should_be_checked, unsolved_items) = + unsolved_items.into_iter().partition(|&(impl_id, local_def_id)| { + symbol_visitor.item_should_be_checked(impl_id, local_def_id) + }); + } (symbol_visitor.live_symbols, symbol_visitor.ignored_derived_traits) } @@ -1112,6 +1169,7 @@ impl<'tcx> DeadVisitor<'tcx> { } match self.tcx.def_kind(def_id) { DefKind::AssocConst + | DefKind::AssocTy | DefKind::AssocFn | DefKind::Fn | DefKind::Static { .. } @@ -1148,7 +1206,11 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) { let def_kind = tcx.def_kind(item.owner_id); let mut dead_codes = Vec::new(); - // if we have diagnosed the trait, do not diagnose unused assoc items + // only diagnose unused assoc items for inherient impl and used trait + // for unused assoc items in impls of trait, + // we have diagnosed them in the trait if they are unused, + // for unused assoc items in unused trait, + // we have diagnosed the unused trait if matches!(def_kind, DefKind::Impl { of_trait: false }) || (def_kind == DefKind::Trait && live_symbols.contains(&item.owner_id.def_id)) { diff --git a/tests/ui/lint/dead-code/trait-only-used-as-type-bound.rs b/tests/ui/lint/dead-code/trait-only-used-as-type-bound.rs new file mode 100644 index 0000000000000..fb994653e1b1f --- /dev/null +++ b/tests/ui/lint/dead-code/trait-only-used-as-type-bound.rs @@ -0,0 +1,31 @@ +//@ check-pass + +#![deny(dead_code)] + +trait UInt: Copy + From {} + +impl UInt for u16 {} + +trait Int: Copy { + type Unsigned: UInt; + + fn as_unsigned(self) -> Self::Unsigned; +} + +impl Int for i16 { + type Unsigned = u16; + + fn as_unsigned(self) -> u16 { + self as _ + } +} + +fn priv_func(x: u8, y: T) -> (T::Unsigned, T::Unsigned) { + (T::Unsigned::from(x), y.as_unsigned()) +} + +pub fn pub_func(x: u8, y: i16) -> (u16, u16) { + priv_func(x, y) +} + +fn main() {} diff --git a/tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.rs b/tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.rs index 5b755d62a0598..ab32f463d9677 100644 --- a/tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.rs +++ b/tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.rs @@ -2,7 +2,6 @@ struct T1; //~ ERROR struct `T1` is never constructed pub struct T2(i32); //~ ERROR field `0` is never read -struct T3; trait Trait1 { //~ ERROR trait `Trait1` is never used const UNUSED: i32; @@ -42,11 +41,4 @@ impl Trait2 for T2 { const USED: i32 = 0; } -impl Trait3 for T3 { - const USED: i32 = 0; - fn construct_self() -> Self { - Self - } -} - fn main() {} diff --git a/tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.stderr b/tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.stderr index 2441a3f868dc2..2d9ab363eb546 100644 --- a/tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.stderr +++ b/tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.stderr @@ -21,7 +21,7 @@ LL | pub struct T2(i32); = help: consider removing this field error: trait `Trait1` is never used - --> $DIR/unused-adt-impl-pub-trait-with-assoc-const.rs:7:7 + --> $DIR/unused-adt-impl-pub-trait-with-assoc-const.rs:6:7 | LL | trait Trait1 { | ^^^^^^ diff --git a/tests/ui/lint/dead-code/unused-assoc-const.rs b/tests/ui/lint/dead-code/unused-assoc-const.rs new file mode 100644 index 0000000000000..36e8315ad3605 --- /dev/null +++ b/tests/ui/lint/dead-code/unused-assoc-const.rs @@ -0,0 +1,20 @@ +#![deny(dead_code)] + +trait Trait { + const UNUSED_CONST: i32; //~ ERROR associated constant `UNUSED_CONST` is never used + const USED_CONST: i32; + + fn foo(&self) {} +} + +pub struct T(()); + +impl Trait for T { + const UNUSED_CONST: i32 = 0; + const USED_CONST: i32 = 1; +} + +fn main() { + T(()).foo(); + T::USED_CONST; +} diff --git a/tests/ui/lint/dead-code/unused-assoc-const.stderr b/tests/ui/lint/dead-code/unused-assoc-const.stderr new file mode 100644 index 0000000000000..78296d706638b --- /dev/null +++ b/tests/ui/lint/dead-code/unused-assoc-const.stderr @@ -0,0 +1,16 @@ +error: associated constant `UNUSED_CONST` is never used + --> $DIR/unused-assoc-const.rs:4:11 + | +LL | trait Trait { + | ----- associated constant in this trait +LL | const UNUSED_CONST: i32; + | ^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/unused-assoc-const.rs:1:9 + | +LL | #![deny(dead_code)] + | ^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/lint/dead-code/unused-assoc-ty.rs b/tests/ui/lint/dead-code/unused-assoc-ty.rs new file mode 100644 index 0000000000000..cc6433c3dc5de --- /dev/null +++ b/tests/ui/lint/dead-code/unused-assoc-ty.rs @@ -0,0 +1,25 @@ +#![deny(dead_code)] + +trait Tr { + type X; //~ ERROR associated type `X` is never used + type Y; + type Z; +} + +impl Tr for () { + type X = Self; + type Y = Self; + type Z = Self; +} + +trait Tr2 { + type X; +} + +fn foo() -> impl Tr where T::Z: Copy {} +fn bar() {} + +fn main() { + foo::<()>(); + bar::>(); +} diff --git a/tests/ui/lint/dead-code/unused-assoc-ty.stderr b/tests/ui/lint/dead-code/unused-assoc-ty.stderr new file mode 100644 index 0000000000000..62d33778d63e3 --- /dev/null +++ b/tests/ui/lint/dead-code/unused-assoc-ty.stderr @@ -0,0 +1,16 @@ +error: associated type `X` is never used + --> $DIR/unused-assoc-ty.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.rs:1:9 + | +LL | #![deny(dead_code)] + | ^^^^^^^^^ + +error: aborting due to 1 previous error + From e482d0b63147b41ae45ba82b3cbab7377abb3ab2 Mon Sep 17 00:00:00 2001 From: mu001999 Date: Mon, 5 Aug 2024 23:22:16 +0800 Subject: [PATCH 5/5] Add and bless tests --- .../ui/associated-type-bounds/union-bounds.rs | 2 +- .../ui/associated-types/impl-wf-cycle-5.fixed | 1 + tests/ui/associated-types/impl-wf-cycle-5.rs | 1 + .../associated-types/impl-wf-cycle-5.stderr | 4 +-- .../ui/associated-types/impl-wf-cycle-6.fixed | 1 + tests/ui/associated-types/impl-wf-cycle-6.rs | 1 + .../associated-types/impl-wf-cycle-6.stderr | 4 +-- .../ui/const-generics/cross_crate_complex.rs | 2 +- .../const-generics/cross_crate_complex.stderr | 10 ++++++++ .../ui/const-generics/issues/issue-86535-2.rs | 2 +- .../issues/issue-86535-2.stderr | 10 ++++++++ tests/ui/const-generics/issues/issue-86535.rs | 2 +- .../const-generics/issues/issue-86535.stderr | 10 ++++++++ tests/ui/deriving/deriving-in-macro.rs | 2 +- tests/ui/deriving/deriving-in-macro.stderr | 14 +++++++++++ .../generic-associated-types/collections.rs | 1 + .../missing-bounds.fixed | 2 ++ .../missing-bounds.rs | 2 ++ .../missing-bounds.stderr | 18 ++++++------- .../impl-trait/extra-impl-in-trait-impl.fixed | 2 ++ .../ui/impl-trait/extra-impl-in-trait-impl.rs | 2 ++ .../extra-impl-in-trait-impl.stderr | 8 +++--- tests/ui/lint/dead-code/issue-59003.rs | 2 +- tests/ui/lint/dead-code/unused-assoc-ty.rs | 25 ------------------- .../ui/lint/dead-code/unused-assoc-ty.stderr | 16 ------------ .../dead-code/unused-trait-with-assoc-ty.rs | 11 ++++++++ .../unused-trait-with-assoc-ty.stderr | 20 +++++++++++++++ tests/ui/parser/issues/issue-105366.fixed | 1 + tests/ui/parser/issues/issue-105366.rs | 1 + tests/ui/parser/issues/issue-105366.stderr | 2 +- tests/ui/pattern/issue-22546.rs | 2 +- tests/ui/pattern/issue-22546.stderr | 10 -------- tests/ui/sanitizer/cfi/sized-associated-ty.rs | 1 + tests/ui/traits/issue-38033.rs | 3 ++- 34 files changed, 118 insertions(+), 77 deletions(-) create mode 100644 tests/ui/const-generics/cross_crate_complex.stderr create mode 100644 tests/ui/const-generics/issues/issue-86535-2.stderr create mode 100644 tests/ui/const-generics/issues/issue-86535.stderr create mode 100644 tests/ui/deriving/deriving-in-macro.stderr delete mode 100644 tests/ui/lint/dead-code/unused-assoc-ty.rs delete mode 100644 tests/ui/lint/dead-code/unused-assoc-ty.stderr create mode 100644 tests/ui/lint/dead-code/unused-trait-with-assoc-ty.rs create mode 100644 tests/ui/lint/dead-code/unused-trait-with-assoc-ty.stderr delete mode 100644 tests/ui/pattern/issue-22546.stderr 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/const-generics/cross_crate_complex.rs b/tests/ui/const-generics/cross_crate_complex.rs index d13b69aa0cfb4..216a5268537d5 100644 --- a/tests/ui/const-generics/cross_crate_complex.rs +++ b/tests/ui/const-generics/cross_crate_complex.rs @@ -11,7 +11,7 @@ async fn foo() { async_in_foo(async_out_foo::<4>().await).await; } -struct Faz; +struct Faz; //~ WARN struct `Faz` is never constructed impl Foo for Faz {} impl Bar for Faz { diff --git a/tests/ui/const-generics/cross_crate_complex.stderr b/tests/ui/const-generics/cross_crate_complex.stderr new file mode 100644 index 0000000000000..7b320606b4b57 --- /dev/null +++ b/tests/ui/const-generics/cross_crate_complex.stderr @@ -0,0 +1,10 @@ +warning: struct `Faz` is never constructed + --> $DIR/cross_crate_complex.rs:14:8 + | +LL | struct Faz; + | ^^^ + | + = note: `#[warn(dead_code)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/const-generics/issues/issue-86535-2.rs b/tests/ui/const-generics/issues/issue-86535-2.rs index 8d064f3eeb1b7..5c9132fe54d3d 100644 --- a/tests/ui/const-generics/issues/issue-86535-2.rs +++ b/tests/ui/const-generics/issues/issue-86535-2.rs @@ -9,7 +9,7 @@ pub trait Foo { [(); Self::ASSOC_C]:; } -struct Bar; +struct Bar; //~ WARN struct `Bar` is never constructed impl Foo for Bar { const ASSOC_C: usize = 3; diff --git a/tests/ui/const-generics/issues/issue-86535-2.stderr b/tests/ui/const-generics/issues/issue-86535-2.stderr new file mode 100644 index 0000000000000..0ba748365754c --- /dev/null +++ b/tests/ui/const-generics/issues/issue-86535-2.stderr @@ -0,0 +1,10 @@ +warning: struct `Bar` is never constructed + --> $DIR/issue-86535-2.rs:12:8 + | +LL | struct Bar; + | ^^^ + | + = note: `#[warn(dead_code)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/const-generics/issues/issue-86535.rs b/tests/ui/const-generics/issues/issue-86535.rs index 62454f4a388a0..2cdf801c1561c 100644 --- a/tests/ui/const-generics/issues/issue-86535.rs +++ b/tests/ui/const-generics/issues/issue-86535.rs @@ -2,7 +2,7 @@ #![feature(adt_const_params, unsized_const_params, generic_const_exprs)] #![allow(incomplete_features, unused_variables)] -struct F; +struct F; //~ WARN struct `F` is never constructed impl X for F<{ S }> { const W: usize = 3; diff --git a/tests/ui/const-generics/issues/issue-86535.stderr b/tests/ui/const-generics/issues/issue-86535.stderr new file mode 100644 index 0000000000000..84d6c1c11ff6a --- /dev/null +++ b/tests/ui/const-generics/issues/issue-86535.stderr @@ -0,0 +1,10 @@ +warning: struct `F` is never constructed + --> $DIR/issue-86535.rs:5:8 + | +LL | struct F; + | ^ + | + = note: `#[warn(dead_code)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/deriving/deriving-in-macro.rs b/tests/ui/deriving/deriving-in-macro.rs index 493c1415c7fa2..cfb86963e0872 100644 --- a/tests/ui/deriving/deriving-in-macro.rs +++ b/tests/ui/deriving/deriving-in-macro.rs @@ -5,7 +5,7 @@ macro_rules! define_vec { () => ( mod foo { #[derive(PartialEq)] - pub struct bar; + pub struct bar; //~ WARN struct `bar` is never constructed } ) } diff --git a/tests/ui/deriving/deriving-in-macro.stderr b/tests/ui/deriving/deriving-in-macro.stderr new file mode 100644 index 0000000000000..cf262f575b0d5 --- /dev/null +++ b/tests/ui/deriving/deriving-in-macro.stderr @@ -0,0 +1,14 @@ +warning: struct `bar` is never constructed + --> $DIR/deriving-in-macro.rs:8:24 + | +LL | pub struct bar; + | ^^^ +... +LL | define_vec![]; + | ------------- in this macro invocation + | + = note: `#[warn(dead_code)]` on by default + = note: this warning originates in the macro `define_vec` (in Nightly builds, run with -Z macro-backtrace for more info) + +warning: 1 warning emitted + 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/generic-associated-types/missing-bounds.fixed b/tests/ui/generic-associated-types/missing-bounds.fixed index 703d3c1e0fb17..15cdd44d7f1f9 100644 --- a/tests/ui/generic-associated-types/missing-bounds.fixed +++ b/tests/ui/generic-associated-types/missing-bounds.fixed @@ -1,5 +1,7 @@ //@ run-rustfix +#![allow(dead_code)] + use std::ops::Add; struct A(B); diff --git a/tests/ui/generic-associated-types/missing-bounds.rs b/tests/ui/generic-associated-types/missing-bounds.rs index f40b422887311..dad111c8c15cf 100644 --- a/tests/ui/generic-associated-types/missing-bounds.rs +++ b/tests/ui/generic-associated-types/missing-bounds.rs @@ -1,5 +1,7 @@ //@ run-rustfix +#![allow(dead_code)] + use std::ops::Add; struct A(B); diff --git a/tests/ui/generic-associated-types/missing-bounds.stderr b/tests/ui/generic-associated-types/missing-bounds.stderr index 13e4d2498760e..97b88c26e3b38 100644 --- a/tests/ui/generic-associated-types/missing-bounds.stderr +++ b/tests/ui/generic-associated-types/missing-bounds.stderr @@ -1,5 +1,5 @@ error: equality constraints are not yet supported in `where` clauses - --> $DIR/missing-bounds.rs:37:33 + --> $DIR/missing-bounds.rs:39:33 | LL | impl Add for E where ::Output = B { | ^^^^^^^^^^^^^^^^^^^^^^ not supported @@ -12,7 +12,7 @@ LL + impl Add for E where B: Add { | error[E0308]: mismatched types - --> $DIR/missing-bounds.rs:11:11 + --> $DIR/missing-bounds.rs:13:11 | LL | impl Add for A where B: Add { | - expected this type parameter @@ -25,14 +25,14 @@ LL | A(self.0 + rhs.0) = note: expected type parameter `B` found associated type `::Output` help: the type constructed contains `::Output` due to the type of the argument passed - --> $DIR/missing-bounds.rs:11:9 + --> $DIR/missing-bounds.rs:13:9 | LL | A(self.0 + rhs.0) | ^^--------------^ | | | this argument influences the type of `A` note: tuple struct defined here - --> $DIR/missing-bounds.rs:5:8 + --> $DIR/missing-bounds.rs:7:8 | LL | struct A(B); | ^ @@ -42,7 +42,7 @@ LL | impl Add for A where B: Add { | ++++++++++++ error[E0308]: mismatched types - --> $DIR/missing-bounds.rs:21:14 + --> $DIR/missing-bounds.rs:23:14 | LL | impl Add for C { | - expected this type parameter @@ -55,7 +55,7 @@ LL | Self(self.0 + rhs.0) = note: expected type parameter `B` found associated type `::Output` note: tuple struct defined here - --> $DIR/missing-bounds.rs:15:8 + --> $DIR/missing-bounds.rs:17:8 | LL | struct C(B); | ^ @@ -65,7 +65,7 @@ LL | impl> Add for C { | ++++++++++++ error[E0369]: cannot add `B` to `B` - --> $DIR/missing-bounds.rs:31:21 + --> $DIR/missing-bounds.rs:33:21 | LL | Self(self.0 + rhs.0) | ------ ^ ----- B @@ -78,7 +78,7 @@ LL | impl> Add for D { | +++++++++++++++++++++++++++ error[E0308]: mismatched types - --> $DIR/missing-bounds.rs:42:14 + --> $DIR/missing-bounds.rs:44:14 | LL | impl Add for E where ::Output = B { | - expected this type parameter @@ -91,7 +91,7 @@ LL | Self(self.0 + rhs.0) = note: expected type parameter `B` found associated type `::Output` note: tuple struct defined here - --> $DIR/missing-bounds.rs:35:8 + --> $DIR/missing-bounds.rs:37:8 | LL | struct E(B); | ^ diff --git a/tests/ui/impl-trait/extra-impl-in-trait-impl.fixed b/tests/ui/impl-trait/extra-impl-in-trait-impl.fixed index 886fc1d005802..070dd8515e647 100644 --- a/tests/ui/impl-trait/extra-impl-in-trait-impl.fixed +++ b/tests/ui/impl-trait/extra-impl-in-trait-impl.fixed @@ -1,5 +1,7 @@ //@ run-rustfix +#![allow(dead_code)] + struct S(T); struct S2; diff --git a/tests/ui/impl-trait/extra-impl-in-trait-impl.rs b/tests/ui/impl-trait/extra-impl-in-trait-impl.rs index f3271993867cb..08fa5ba23f0ea 100644 --- a/tests/ui/impl-trait/extra-impl-in-trait-impl.rs +++ b/tests/ui/impl-trait/extra-impl-in-trait-impl.rs @@ -1,5 +1,7 @@ //@ run-rustfix +#![allow(dead_code)] + struct S(T); struct S2; diff --git a/tests/ui/impl-trait/extra-impl-in-trait-impl.stderr b/tests/ui/impl-trait/extra-impl-in-trait-impl.stderr index 5aafc8b64d4ff..91c7da5a04fb4 100644 --- a/tests/ui/impl-trait/extra-impl-in-trait-impl.stderr +++ b/tests/ui/impl-trait/extra-impl-in-trait-impl.stderr @@ -1,23 +1,23 @@ error: unexpected `impl` keyword - --> $DIR/extra-impl-in-trait-impl.rs:6:18 + --> $DIR/extra-impl-in-trait-impl.rs:8:18 | LL | impl impl Default for S { | ^^^^^ help: remove the extra `impl` | note: this is parsed as an `impl Trait` type, but a trait is expected at this position - --> $DIR/extra-impl-in-trait-impl.rs:6:18 + --> $DIR/extra-impl-in-trait-impl.rs:8:18 | LL | impl impl Default for S { | ^^^^^^^^^^^^ error: unexpected `impl` keyword - --> $DIR/extra-impl-in-trait-impl.rs:12:6 + --> $DIR/extra-impl-in-trait-impl.rs:14:6 | LL | impl impl Default for S2 { | ^^^^^ help: remove the extra `impl` | note: this is parsed as an `impl Trait` type, but a trait is expected at this position - --> $DIR/extra-impl-in-trait-impl.rs:12:6 + --> $DIR/extra-impl-in-trait-impl.rs:14:6 | LL | impl impl Default for S2 { | ^^^^^^^^^^^^ diff --git a/tests/ui/lint/dead-code/issue-59003.rs b/tests/ui/lint/dead-code/issue-59003.rs index e3dcaca577889..319cf2db1495f 100644 --- a/tests/ui/lint/dead-code/issue-59003.rs +++ b/tests/ui/lint/dead-code/issue-59003.rs @@ -4,8 +4,8 @@ #![deny(dead_code)] +#[allow(dead_code)] struct Foo { - #[allow(dead_code)] inner: u32, } diff --git a/tests/ui/lint/dead-code/unused-assoc-ty.rs b/tests/ui/lint/dead-code/unused-assoc-ty.rs deleted file mode 100644 index cc6433c3dc5de..0000000000000 --- a/tests/ui/lint/dead-code/unused-assoc-ty.rs +++ /dev/null @@ -1,25 +0,0 @@ -#![deny(dead_code)] - -trait Tr { - type X; //~ ERROR associated type `X` is never used - type Y; - type Z; -} - -impl Tr for () { - type X = Self; - type Y = Self; - type Z = Self; -} - -trait Tr2 { - type X; -} - -fn foo() -> impl Tr where T::Z: Copy {} -fn bar() {} - -fn main() { - foo::<()>(); - bar::>(); -} diff --git a/tests/ui/lint/dead-code/unused-assoc-ty.stderr b/tests/ui/lint/dead-code/unused-assoc-ty.stderr deleted file mode 100644 index 62d33778d63e3..0000000000000 --- a/tests/ui/lint/dead-code/unused-assoc-ty.stderr +++ /dev/null @@ -1,16 +0,0 @@ -error: associated type `X` is never used - --> $DIR/unused-assoc-ty.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.rs:1:9 - | -LL | #![deny(dead_code)] - | ^^^^^^^^^ - -error: aborting due to 1 previous error - diff --git a/tests/ui/lint/dead-code/unused-trait-with-assoc-ty.rs b/tests/ui/lint/dead-code/unused-trait-with-assoc-ty.rs new file mode 100644 index 0000000000000..e8116d83ebf1c --- /dev/null +++ b/tests/ui/lint/dead-code/unused-trait-with-assoc-ty.rs @@ -0,0 +1,11 @@ +#![deny(dead_code)] + +struct T1; //~ ERROR struct `T1` is never constructed + +trait Foo { type Unused; } //~ ERROR trait `Foo` is never used +impl Foo for T1 { type Unused = Self; } + +pub trait Bar { type Used; } +impl Bar for T1 { type Used = Self; } + +fn main() {} diff --git a/tests/ui/lint/dead-code/unused-trait-with-assoc-ty.stderr b/tests/ui/lint/dead-code/unused-trait-with-assoc-ty.stderr new file mode 100644 index 0000000000000..ab73c64063431 --- /dev/null +++ b/tests/ui/lint/dead-code/unused-trait-with-assoc-ty.stderr @@ -0,0 +1,20 @@ +error: struct `T1` is never constructed + --> $DIR/unused-trait-with-assoc-ty.rs:3:8 + | +LL | struct T1; + | ^^ + | +note: the lint level is defined here + --> $DIR/unused-trait-with-assoc-ty.rs:1:9 + | +LL | #![deny(dead_code)] + | ^^^^^^^^^ + +error: trait `Foo` is never used + --> $DIR/unused-trait-with-assoc-ty.rs:5:7 + | +LL | trait Foo { type Unused; } + | ^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/parser/issues/issue-105366.fixed b/tests/ui/parser/issues/issue-105366.fixed index 7157b647524dd..95419dc07f2cc 100644 --- a/tests/ui/parser/issues/issue-105366.fixed +++ b/tests/ui/parser/issues/issue-105366.fixed @@ -1,5 +1,6 @@ //@ run-rustfix +#[allow(dead_code)] struct Foo; impl From for Foo { diff --git a/tests/ui/parser/issues/issue-105366.rs b/tests/ui/parser/issues/issue-105366.rs index dc3cb8b343d32..3278b73799125 100644 --- a/tests/ui/parser/issues/issue-105366.rs +++ b/tests/ui/parser/issues/issue-105366.rs @@ -1,5 +1,6 @@ //@ run-rustfix +#[allow(dead_code)] struct Foo; fn From for Foo { diff --git a/tests/ui/parser/issues/issue-105366.stderr b/tests/ui/parser/issues/issue-105366.stderr index d8c79a0e0eaf6..225e436b4aa84 100644 --- a/tests/ui/parser/issues/issue-105366.stderr +++ b/tests/ui/parser/issues/issue-105366.stderr @@ -1,5 +1,5 @@ error: you might have meant to write `impl` instead of `fn` - --> $DIR/issue-105366.rs:5:1 + --> $DIR/issue-105366.rs:6:1 | LL | fn From for Foo { | ^^ diff --git a/tests/ui/pattern/issue-22546.rs b/tests/ui/pattern/issue-22546.rs index fd1d5fb6c4775..d5c5b68be78d7 100644 --- a/tests/ui/pattern/issue-22546.rs +++ b/tests/ui/pattern/issue-22546.rs @@ -15,7 +15,7 @@ impl Foo { } } -trait Tr { //~ WARN trait `Tr` is never used +trait Tr { type U; } diff --git a/tests/ui/pattern/issue-22546.stderr b/tests/ui/pattern/issue-22546.stderr deleted file mode 100644 index e067a95e4226c..0000000000000 --- a/tests/ui/pattern/issue-22546.stderr +++ /dev/null @@ -1,10 +0,0 @@ -warning: trait `Tr` is never used - --> $DIR/issue-22546.rs:18:7 - | -LL | trait Tr { - | ^^ - | - = note: `#[warn(dead_code)]` on by default - -warning: 1 warning emitted - diff --git a/tests/ui/sanitizer/cfi/sized-associated-ty.rs b/tests/ui/sanitizer/cfi/sized-associated-ty.rs index f5b4e22e9d99b..59a69b059014d 100644 --- a/tests/ui/sanitizer/cfi/sized-associated-ty.rs +++ b/tests/ui/sanitizer/cfi/sized-associated-ty.rs @@ -14,6 +14,7 @@ //@ run-pass trait Foo { + #[allow(dead_code)] type Bar<'a> where Self: Sized; diff --git a/tests/ui/traits/issue-38033.rs b/tests/ui/traits/issue-38033.rs index f3525bd13c4fa..4317244976287 100644 --- a/tests/ui/traits/issue-38033.rs +++ b/tests/ui/traits/issue-38033.rs @@ -19,7 +19,8 @@ trait IntoFuture { type Item; type Error; - fn into_future(self) -> Self::Future; //~ WARN method `into_future` is never used + fn into_future(self) -> Self::Future; + //~^ WARN method `into_future` is never used } impl IntoFuture for F {