diff options
| author | Trevor Gross <t.gross35@gmail.com> | 2025-06-20 23:25:55 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-06-20 23:25:55 -0400 |
| commit | c93fac7d64394c6b926d863c1903d2e91fd2e41d (patch) | |
| tree | ede03935161542a7cded6910189cba25c3f88e26 | |
| parent | 8751016e20ff13f36327ec1527448522e8dc7247 (diff) | |
| parent | 52167e04e62a84fe5073c4074733c72f96961ace (diff) | |
| download | rust-c93fac7d64394c6b926d863c1903d2e91fd2e41d.tar.gz rust-c93fac7d64394c6b926d863c1903d2e91fd2e41d.zip | |
Rollup merge of #142485 - mu001999-contrib:dead-code/adt-pattern, r=petrochenkov
Marks ADT live if it appears in pattern Marks ADT live if it appears in pattern, it implies the construction of the ADT. 1. Then we can detect unused private ADTs impl `Default`, without special logics for `Default` and other std traits. 2. We can also remove `rustc_trivial_field_reads` on `Default`, and the logic in `should_ignore_item` (introduced by rust-lang/rust#126302). Fixes rust-lang/rust#120770 Extracted from rust-lang/rust#128637. r? `@petrochenkov`
24 files changed, 161 insertions, 67 deletions
diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 4257d8e8d16..4738036318d 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -234,7 +234,14 @@ 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, _) => { + // Marks the ADT live if its variant appears as the pattern, + // considering cases when we have `let T(x) = foo()` and `fn foo<T>() -> 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) + } _ => span_bug!(lhs.span, "non-ADT in struct pattern"), }; for pat in pats { @@ -254,7 +261,11 @@ 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, _) => { + // Marks the ADT live if its variant appears as the pattern + 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; @@ -359,31 +370,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(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) { @@ -494,38 +480,25 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { impl_id: hir::ItemId, local_def_id: LocalDefId, ) -> bool { - if self.should_ignore_item(local_def_id.to_def_id()) { - return false; - } - let trait_def_id = match self.tcx.def_kind(local_def_id) { // assoc impl items of traits are live if the corresponding trait items are live - DefKind::AssocFn => self.tcx.associated_item(local_def_id).trait_item_def_id, + DefKind::AssocFn => self + .tcx + .associated_item(local_def_id) + .trait_item_def_id + .and_then(|def_id| def_id.as_local()), // 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) - .and_then(|trait_ref| Some(trait_ref.skip_binder().def_id)), + .and_then(|trait_ref| trait_ref.skip_binder().def_id.as_local()), _ => None, }; - if let Some(trait_def_id) = trait_def_id { - if let Some(trait_def_id) = trait_def_id.as_local() - && !self.live_symbols.contains(&trait_def_id) - { - return false; - } - - // FIXME: legacy logic to check whether the function may construct `Self`, - // this can be removed after supporting marking ADTs appearing in patterns - // as live, then we can check private impls of public traits directly - if 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) - && self.tcx.visibility(trait_def_id).is_public() - { - return true; - } + if let Some(trait_def_id) = trait_def_id + && !self.live_symbols.contains(&trait_def_id) + { + return false; } // The impl or impl item is used if the corresponding trait or trait item is used and the ty is used. @@ -635,6 +608,11 @@ 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 044997a81a9..0a15cedfb55 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/incremental/track-deps-in-new-solver.rs b/tests/incremental/track-deps-in-new-solver.rs index fb013b2b24a..51cd6b89e37 100644 --- a/tests/incremental/track-deps-in-new-solver.rs +++ b/tests/incremental/track-deps-in-new-solver.rs @@ -3,6 +3,8 @@ //@ compile-flags: -Znext-solver //@ check-pass +#![allow(dead_code)] + pub trait Future { type Error; fn poll() -> Self::Error; diff --git a/tests/ui/const-generics/issues/issue-86535-2.rs b/tests/ui/const-generics/issues/issue-86535-2.rs index 8d064f3eeb1..5c9132fe54d 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<const N: &'static ()>; +struct Bar<const N: &'static ()>; //~ WARN struct `Bar` is never constructed impl<const N: &'static ()> Foo for Bar<N> { 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 00000000000..0ba74836575 --- /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<const N: &'static ()>; + | ^^^ + | + = 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 62454f4a388..2cdf801c156 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<const S: &'static str>; +struct F<const S: &'static str>; //~ WARN struct `F` is never constructed impl<const S: &'static str> 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 00000000000..84d6c1c11ff --- /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<const S: &'static str>; + | ^ + | + = note: `#[warn(dead_code)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/derives/clone-debug-dead-code.stderr b/tests/ui/derives/clone-debug-dead-code.stderr index 34b7f929ec5..38be486e332 100644 --- a/tests/ui/derives/clone-debug-dead-code.stderr +++ b/tests/ui/derives/clone-debug-dead-code.stderr @@ -40,7 +40,7 @@ LL | struct D { f: () } | | | field in this struct | - = note: `D` has derived impls for the traits `Debug` and `Clone`, but these are intentionally ignored during dead code analysis + = note: `D` has derived impls for the traits `Clone` and `Debug`, but these are intentionally ignored during dead code analysis error: field `f` is never read --> $DIR/clone-debug-dead-code.rs:21:12 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 886fc1d0058..d8eceeff678 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,6 @@ //@ run-rustfix +#![allow(dead_code)] struct S<T>(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 f3271993867..c2e511c0d05 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,6 @@ //@ run-rustfix +#![allow(dead_code)] struct S<T>(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 5aafc8b64d4..22e68463a8c 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:7:18 | LL | impl<T: Default> impl Default for S<T> { | ^^^^^ 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:7:18 | LL | impl<T: Default> impl Default for S<T> { | ^^^^^^^^^^^^ error: unexpected `impl` keyword - --> $DIR/extra-impl-in-trait-impl.rs:12:6 + --> $DIR/extra-impl-in-trait-impl.rs:13: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:13:6 | LL | impl impl Default for S2 { | ^^^^^^^^^^^^ diff --git a/tests/ui/lint/dead-code/issue-41883.stderr b/tests/ui/lint/dead-code/issue-41883.stderr index cf079e4dda3..47ccef9a530 100644 --- a/tests/ui/lint/dead-code/issue-41883.stderr +++ b/tests/ui/lint/dead-code/issue-41883.stderr @@ -29,8 +29,6 @@ error: struct `UnusedStruct` is never constructed | LL | struct UnusedStruct; | ^^^^^^^^^^^^ - | - = note: `UnusedStruct` has a derived impl for the trait `Debug`, but this is intentionally ignored during dead code analysis error: aborting due to 4 previous errors diff --git a/tests/ui/lint/dead-code/issue-59003.rs b/tests/ui/lint/dead-code/issue-59003.rs index e3dcaca5778..319cf2db149 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/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 00000000000..25777438456 --- /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 00000000000..7c1a4b45977 --- /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/multiple-dead-codes-in-the-same-struct.stderr b/tests/ui/lint/dead-code/multiple-dead-codes-in-the-same-struct.stderr index b992005318f..25a7d96cb89 100644 --- a/tests/ui/lint/dead-code/multiple-dead-codes-in-the-same-struct.stderr +++ b/tests/ui/lint/dead-code/multiple-dead-codes-in-the-same-struct.stderr @@ -56,8 +56,6 @@ warning: struct `Foo` is never constructed | LL | struct Foo(usize, #[allow(unused)] usize); | ^^^ - | - = note: `Foo` has a derived impl for the trait `Debug`, but this is intentionally ignored during dead code analysis error: aborting due to 2 previous errors; 2 warnings emitted 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 00000000000..43a2e431904 --- /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<T: Pod>(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-adt-impl-pub-trait-with-assoc-const.rs b/tests/ui/lint/dead-code/unused-adt-impl-pub-trait-with-assoc-const.rs index 5b755d62a05..415eb4138de 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,7 @@ struct T1; //~ ERROR struct `T1` is never constructed pub struct T2(i32); //~ ERROR field `0` is never read -struct T3; +struct T3; //~ ERROR struct `T3` is never constructed trait Trait1 { //~ ERROR trait `Trait1` is never used const UNUSED: i32; 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 2441a3f868d..778dadee153 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 @@ -20,11 +20,17 @@ LL | pub struct T2(i32); | = help: consider removing this field +error: struct `T3` is never constructed + --> $DIR/unused-adt-impl-pub-trait-with-assoc-const.rs:5:8 + | +LL | struct T3; + | ^^ + error: trait `Trait1` is never used --> $DIR/unused-adt-impl-pub-trait-with-assoc-const.rs:7:7 | LL | trait Trait1 { | ^^^^^^ -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors 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 330ad32dd57..f20b7cb66ee 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 bbb0bd7be70..7422f9a39f3 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 | diff --git a/tests/ui/parser/issues/issue-105366.fixed b/tests/ui/parser/issues/issue-105366.fixed index 7157b647524..95419dc07f2 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<i32> for Foo { diff --git a/tests/ui/parser/issues/issue-105366.rs b/tests/ui/parser/issues/issue-105366.rs index dc3cb8b343d..3278b737991 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<i32> for Foo { diff --git a/tests/ui/parser/issues/issue-105366.stderr b/tests/ui/parser/issues/issue-105366.stderr index d8c79a0e0ea..225e436b4aa 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<i32> for Foo { | ^^ |
