From 8449d10a29b09c0852660ee168a77083d8179cf2 Mon Sep 17 00:00:00 2001 From: mu001999 Date: Thu, 27 Jun 2024 14:11:54 +0800 Subject: [PATCH] Extend rules of dead code analysis for impls for adts to impls for types refer to adts --- compiler/rustc_passes/src/dead.rs | 32 ++++++++----- .../dead-code/unused-impl-for-non-adts.rs | 45 +++++++++++++++++++ .../dead-code/unused-impl-for-non-adts.stderr | 20 +++++++++ 3 files changed, 87 insertions(+), 10 deletions(-) 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 bbd586386dd27..0a3b6ae06df3a 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -54,7 +54,24 @@ impl Publicness { } } -fn struct_all_fields_are_public(tcx: TyCtxt<'_>, id: DefId) -> bool { +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 + } + } + TyKind::Slice(ty) | TyKind::Array(ty, _) => adt_of(ty), + TyKind::Ptr(ty) | TyKind::Ref(_, ty) => adt_of(ty.ty), + _ => None, + } +} + +fn struct_all_fields_are_public(tcx: TyCtxt<'_>, id: LocalDefId) -> bool { // treat PhantomData and positional ZST as public, // we don't want to lint types which only have them, // cause it's a common way to use such types to check things like well-formedness @@ -79,10 +96,7 @@ fn struct_all_fields_are_public(tcx: TyCtxt<'_>, id: DefId) -> bool { /// for enum and union, just check they are public, /// and doesn't solve types like &T for now, just skip them fn ty_ref_to_pub_struct(tcx: TyCtxt<'_>, ty: &hir::Ty<'_>) -> Publicness { - if let TyKind::Path(hir::QPath::Resolved(_, path)) = ty.kind - && let Res::Def(def_kind, def_id) = path.res - && def_id.is_local() - { + if let Some((def_id, def_kind)) = adt_of(ty) { return match def_kind { DefKind::Enum | DefKind::Union => { let ty_is_public = tcx.visibility(def_id).is_public(); @@ -584,10 +598,8 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { } 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() + if let Some((local_def_id, def_kind)) = + adt_of(self.tcx.hir().item(impl_id).expect_impl().self_ty) && matches!(def_kind, DefKind::Struct | DefKind::Enum | DefKind::Union) { if let Some(trait_item_id) = self.tcx.associated_item(impl_item_id).trait_item_def_id @@ -931,7 +943,7 @@ fn create_and_seed_worklist( match tcx.def_kind(id) { DefKind::Impl { .. } => false, DefKind::AssocConst | DefKind::AssocTy | DefKind::AssocFn => !matches!(tcx.associated_item(id).container, AssocItemContainer::ImplContainer), - DefKind::Struct => struct_all_fields_are_public(tcx, id.to_def_id()) || has_allow_dead_code_or_lang_attr(tcx, id).is_some(), + DefKind::Struct => struct_all_fields_are_public(tcx, id) || has_allow_dead_code_or_lang_attr(tcx, id).is_some(), _ => true }) .map(|id| (id, ComesFromAllowExpect::No)) 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 +