From ebfe7b0fd1e70df45b507ef01cba274ef9b393f7 Mon Sep 17 00:00:00 2001 From: Jose Torres Date: Thu, 6 Aug 2026 21:46:57 -0400 Subject: [PATCH 01/22] moving trivial field reads attr to macros --- .../rustc_attr_parsing/src/attributes/rustc_internal.rs | 2 +- compiler/rustc_passes/src/dead.rs | 7 +++---- library/core/src/clone.rs | 2 +- library/core/src/fmt/mod.rs | 2 +- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs index 0df3d9a626bee..f32c036cc19b8 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -401,7 +401,7 @@ pub(crate) struct RustcTrivialFieldReadsParser; impl NoArgsAttributeParser for RustcTrivialFieldReadsParser { const PATH: &[Symbol] = &[sym::rustc_trivial_field_reads]; - const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Trait)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::MacroDef)]); const STABILITY: AttributeStability = unstable!(rustc_attrs); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcTrivialFieldReads; } diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index d6a8b2c2f5eb6..f41c1ffa87467 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -450,11 +450,10 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { fn should_ignore_impl_item(&mut self, impl_item: &hir::ImplItem<'_>) -> bool { if let hir::ImplItemImplKind::Trait { .. } = impl_item.impl_kind && let impl_of = self.tcx.local_parent(impl_item.owner_id.def_id) - && self.tcx.is_automatically_derived(impl_of.to_def_id()) - && let trait_ref = - self.tcx.impl_trait_ref(impl_of).instantiate_identity().skip_norm_wip() - && find_attr!(self.tcx, trait_ref.def_id, RustcTrivialFieldReads) + && let Some(macro_def_id) = impl_item.span.ctxt().outer_expn_data().macro_def_id + && find_attr!(self.tcx, macro_def_id, RustcTrivialFieldReads) { + let trait_ref = self.tcx.impl_trait_ref(impl_of).instantiate_identity().skip_norm_wip(); if let ty::Adt(adt_def, _) = trait_ref.self_ty().kind() && let Some(adt_def_id) = adt_def.did().as_local() { diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs index 126ab37bc0b71..bbe28168f244c 100644 --- a/library/core/src/clone.rs +++ b/library/core/src/clone.rs @@ -189,7 +189,6 @@ mod uninit; #[stable(feature = "rust1", since = "1.0.0")] #[lang = "clone"] #[rustc_diagnostic_item = "Clone"] -#[rustc_trivial_field_reads] #[rustc_const_unstable(feature = "const_clone", issue = "142757")] pub const trait Clone: Sized { /// Returns a duplicate of the value. @@ -286,6 +285,7 @@ pub const unsafe trait TrivialClone: [const] Clone {} #[rustc_builtin_macro] #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] #[allow_internal_unstable(core_intrinsics, derive_clone_copy_internals, trivial_clone)] +#[rustc_trivial_field_reads] pub macro Clone($item:item) { /* compiler built-in */ } diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index 43dba18375992..87f909ec9f5c7 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -1049,7 +1049,6 @@ impl Display for Arguments<'_> { )] #[doc(alias = "{:?}")] #[rustc_diagnostic_item = "Debug"] -#[rustc_trivial_field_reads] pub trait Debug: PointeeSized { #[doc = include_str!("fmt_trait_method_doc.md")] /// @@ -1090,6 +1089,7 @@ pub(crate) mod macros { #[rustc_builtin_macro] #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] #[allow_internal_unstable(core_intrinsics, fmt_helpers_for_derive)] + #[rustc_trivial_field_reads] pub macro Debug($item:item) { /* compiler built-in */ } From 1b9c77982a77ea61356a5c7742fffc6aaefe9eaf Mon Sep 17 00:00:00 2001 From: Jose Torres Date: Thu, 6 Aug 2026 21:52:58 -0400 Subject: [PATCH 02/22] formatting --- compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs index f32c036cc19b8..cbd125abb01e5 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -401,7 +401,8 @@ pub(crate) struct RustcTrivialFieldReadsParser; impl NoArgsAttributeParser for RustcTrivialFieldReadsParser { const PATH: &[Symbol] = &[sym::rustc_trivial_field_reads]; - const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::MacroDef)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = + AllowedTargets::AllowList(&[Allow(Target::MacroDef)]); const STABILITY: AttributeStability = unstable!(rustc_attrs); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcTrivialFieldReads; } From 7f8e375e8c62efc12947025e7e5c2c643611b5de Mon Sep 17 00:00:00 2001 From: Jose Torres Date: Tue, 11 Aug 2026 22:25:13 -0400 Subject: [PATCH 03/22] moving attribute for Clone --- .../src/attributes/rustc_internal.rs | 6 ++++-- compiler/rustc_builtin_macros/src/deriving/clone.rs | 5 ++++- compiler/rustc_passes/src/dead.rs | 12 ++++++++++-- library/core/src/clone.rs | 3 +-- 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs index cbd125abb01e5..9aef0c561c462 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -401,8 +401,10 @@ pub(crate) struct RustcTrivialFieldReadsParser; impl NoArgsAttributeParser for RustcTrivialFieldReadsParser { const PATH: &[Symbol] = &[sym::rustc_trivial_field_reads]; - const ALLOWED_TARGETS: AllowedTargets<'_> = - AllowedTargets::AllowList(&[Allow(Target::MacroDef)]); + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ + Allow(Target::MacroDef), + Allow(Target::Method(MethodKind::TraitImpl)), + ]); const STABILITY: AttributeStability = unstable!(rustc_attrs); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcTrivialFieldReads; } diff --git a/compiler/rustc_builtin_macros/src/deriving/clone.rs b/compiler/rustc_builtin_macros/src/deriving/clone.rs index b4374e32f6051..cff9220ee215d 100644 --- a/compiler/rustc_builtin_macros/src/deriving/clone.rs +++ b/compiler/rustc_builtin_macros/src/deriving/clone.rs @@ -98,7 +98,10 @@ pub(crate) fn expand_deriving_clone( explicit_self: true, nonself_args: SmallVec::new(), ret_ty: Self_, - attributes: thin_vec![cx.attr_word(sym::inline, span)], + attributes: thin_vec![ + cx.attr_word(sym::inline, span), + cx.attr_word(sym::rustc_trivial_field_reads, span) + ], fieldless_variants_strategy: FieldlessVariantsStrategy::Default, combine_substructure: substructure, }], diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index f41c1ffa87467..c0faec2bbb4a9 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -448,10 +448,18 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { /// will be ignored for the purposes of dead code analysis (see PR #85200 /// for discussion). fn should_ignore_impl_item(&mut self, impl_item: &hir::ImplItem<'_>) -> bool { + let direct_attr = find_attr!(self.tcx, impl_item.owner_id.def_id, RustcTrivialFieldReads); + + let legacy_macro_attr = + impl_item.span.ctxt().outer_expn_data().macro_def_id.is_some_and(|macro_def_id| { + find_attr!(self.tcx, macro_def_id, RustcTrivialFieldReads) + }); + + let has_trivial_field_reads_attr = legacy_macro_attr || direct_attr; + if let hir::ImplItemImplKind::Trait { .. } = impl_item.impl_kind && let impl_of = self.tcx.local_parent(impl_item.owner_id.def_id) - && let Some(macro_def_id) = impl_item.span.ctxt().outer_expn_data().macro_def_id - && find_attr!(self.tcx, macro_def_id, RustcTrivialFieldReads) + && has_trivial_field_reads_attr { let trait_ref = self.tcx.impl_trait_ref(impl_of).instantiate_identity().skip_norm_wip(); if let ty::Adt(adt_def, _) = trait_ref.self_ty().kind() diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs index bbe28168f244c..df4e421a2b690 100644 --- a/library/core/src/clone.rs +++ b/library/core/src/clone.rs @@ -284,8 +284,7 @@ pub const unsafe trait TrivialClone: [const] Clone {} /// Derive macro generating an impl of the trait `Clone`. #[rustc_builtin_macro] #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] -#[allow_internal_unstable(core_intrinsics, derive_clone_copy_internals, trivial_clone)] -#[rustc_trivial_field_reads] +#[allow_internal_unstable(core_intrinsics, derive_clone_copy_internals, trivial_clone, rustc_attrs)] pub macro Clone($item:item) { /* compiler built-in */ } From 949df5792d21088eb6dc01332d526fe7f76f5e08 Mon Sep 17 00:00:00 2001 From: Jose Torres Date: Wed, 12 Aug 2026 21:33:55 -0400 Subject: [PATCH 04/22] adding test --- .../dead-code/trivial-field-reads-method.rs | 59 +++++++++++++++++++ .../trivial-field-reads-method.stderr | 17 ++++++ 2 files changed, 76 insertions(+) create mode 100644 tests/ui/lint/dead-code/trivial-field-reads-method.rs create mode 100644 tests/ui/lint/dead-code/trivial-field-reads-method.stderr diff --git a/tests/ui/lint/dead-code/trivial-field-reads-method.rs b/tests/ui/lint/dead-code/trivial-field-reads-method.rs new file mode 100644 index 0000000000000..91aacef2e1454 --- /dev/null +++ b/tests/ui/lint/dead-code/trivial-field-reads-method.rs @@ -0,0 +1,59 @@ + //! Checks that `#[rustc_trivial_field_reads]` applies per method + //! (issue #160621) + + #![feature(rustc_attrs)] + #![deny(dead_code)] + + trait Access { + fn get_a(&self) -> u32; + fn get_b(&self) -> u32; + } + + struct S { + a: u32, //~ ERROR field `a` is never read + b: u32 + } + + impl Access for S { + #[rustc_trivial_field_reads] + fn get_a(&self) -> u32 { + self.a + } + + fn get_b(&self) -> u32 { + self.b + } + } + + struct T { + a: u32, + b: u32 + } + + impl Access for T { + fn get_a(&self) -> u32 { + self.a + } + + fn get_b(&self) -> u32 { + self.b + } + } + + fn main() { + let s = S { + a: 0, + b: 0 + }; + + let _ = s.get_a(); + let _ = s.get_b(); + + let t = T { + a: 0, + b: 0 + }; + + let _ = t.get_a(); + let _ = t.get_b(); + } diff --git a/tests/ui/lint/dead-code/trivial-field-reads-method.stderr b/tests/ui/lint/dead-code/trivial-field-reads-method.stderr new file mode 100644 index 0000000000000..cd355c8760351 --- /dev/null +++ b/tests/ui/lint/dead-code/trivial-field-reads-method.stderr @@ -0,0 +1,17 @@ +error: field `a` is never read + --> $DIR/trivial-field-reads-method.rs:13:5 + | +LL | struct S { + | - field in this struct +LL | a: u32, + | ^ + | + = note: `S` has a derived impl for the trait `Access`, but this is intentionally ignored during dead code analysis +note: the lint level is defined here + --> $DIR/trivial-field-reads-method.rs:5:11 + | +LL | #![deny(dead_code)] + | ^^^^^^^^^ + +error: aborting due to 1 previous error + From 6de1a8dbd65464ee95c93907c770965514b5e9c1 Mon Sep 17 00:00:00 2001 From: Jose Torres Date: Wed, 12 Aug 2026 22:00:00 -0400 Subject: [PATCH 05/22] moving attr on Debug, remove from minicore --- compiler/rustc_builtin_macros/src/deriving/debug.rs | 5 ++++- library/core/src/fmt/mod.rs | 3 +-- tests/ui/traits/const-traits/auxiliary/minicore.rs | 1 - 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/deriving/debug.rs b/compiler/rustc_builtin_macros/src/deriving/debug.rs index 94e0b704e6c08..3b07e1c0e67e6 100644 --- a/compiler/rustc_builtin_macros/src/deriving/debug.rs +++ b/compiler/rustc_builtin_macros/src/deriving/debug.rs @@ -32,7 +32,10 @@ pub(crate) fn expand_deriving_debug( explicit_self: true, nonself_args: smallvec![(fmtr, sym::character('f'))], ret_ty: Path(path_std!(fmt::Result)), - attributes: thin_vec![cx.attr_word(sym::inline, span)], + attributes: thin_vec![ + cx.attr_word(sym::inline, span), + cx.attr_word(sym::rustc_trivial_field_reads, span) + ], fieldless_variants_strategy: FieldlessVariantsStrategy::SpecializeIfAllVariantsFieldless, combine_substructure: combine_substructure(show_substructure), diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index 87f909ec9f5c7..894ac93fb9467 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -1088,8 +1088,7 @@ pub(crate) mod macros { /// Derive macro generating an impl of the trait `Debug`. #[rustc_builtin_macro] #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] - #[allow_internal_unstable(core_intrinsics, fmt_helpers_for_derive)] - #[rustc_trivial_field_reads] + #[allow_internal_unstable(core_intrinsics, fmt_helpers_for_derive, rustc_attrs)] pub macro Debug($item:item) { /* compiler built-in */ } diff --git a/tests/ui/traits/const-traits/auxiliary/minicore.rs b/tests/ui/traits/const-traits/auxiliary/minicore.rs index e1d1135e6d4ec..a357a727ef9b1 100644 --- a/tests/ui/traits/const-traits/auxiliary/minicore.rs +++ b/tests/ui/traits/const-traits/auxiliary/minicore.rs @@ -445,7 +445,6 @@ impl Deref for Ref<'_, T> { } #[lang = "clone"] -#[rustc_trivial_field_reads] pub const trait Clone: Sized { fn clone(&self) -> Self; fn clone_from(&mut self, source: &Self) From 609cb03faa2813e76aff54281b4c092e3ec3938d Mon Sep 17 00:00:00 2001 From: Jose Torres Date: Wed, 12 Aug 2026 22:05:46 -0400 Subject: [PATCH 06/22] remove temporary changes --- .../src/attributes/rustc_internal.rs | 6 ++---- compiler/rustc_passes/src/dead.rs | 11 +---------- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs index 9aef0c561c462..bee1abf4099a3 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -401,10 +401,8 @@ pub(crate) struct RustcTrivialFieldReadsParser; impl NoArgsAttributeParser for RustcTrivialFieldReadsParser { const PATH: &[Symbol] = &[sym::rustc_trivial_field_reads]; - const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ - Allow(Target::MacroDef), - Allow(Target::Method(MethodKind::TraitImpl)), - ]); + const ALLOWED_TARGETS: AllowedTargets<'_> = + AllowedTargets::AllowList(&[Allow(Target::Method(MethodKind::TraitImpl))]); const STABILITY: AttributeStability = unstable!(rustc_attrs); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcTrivialFieldReads; } diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index c0faec2bbb4a9..8989985eeab66 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -448,18 +448,9 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { /// will be ignored for the purposes of dead code analysis (see PR #85200 /// for discussion). fn should_ignore_impl_item(&mut self, impl_item: &hir::ImplItem<'_>) -> bool { - let direct_attr = find_attr!(self.tcx, impl_item.owner_id.def_id, RustcTrivialFieldReads); - - let legacy_macro_attr = - impl_item.span.ctxt().outer_expn_data().macro_def_id.is_some_and(|macro_def_id| { - find_attr!(self.tcx, macro_def_id, RustcTrivialFieldReads) - }); - - let has_trivial_field_reads_attr = legacy_macro_attr || direct_attr; - if let hir::ImplItemImplKind::Trait { .. } = impl_item.impl_kind && let impl_of = self.tcx.local_parent(impl_item.owner_id.def_id) - && has_trivial_field_reads_attr + && find_attr!(self.tcx, impl_item.owner_id.def_id, RustcTrivialFieldReads) { let trait_ref = self.tcx.impl_trait_ref(impl_of).instantiate_identity().skip_norm_wip(); if let ty::Adt(adt_def, _) = trait_ref.self_ty().kind() From b3773ee4277e2d3577bd4478413f3c3fd7f5f0af Mon Sep 17 00:00:00 2001 From: Jose Torres Date: Wed, 12 Aug 2026 22:23:23 -0400 Subject: [PATCH 07/22] revert closer to the original check --- compiler/rustc_passes/src/dead.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 8989985eeab66..a3a68d7ef2e29 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -450,9 +450,10 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { fn should_ignore_impl_item(&mut self, impl_item: &hir::ImplItem<'_>) -> bool { if let hir::ImplItemImplKind::Trait { .. } = impl_item.impl_kind && let impl_of = self.tcx.local_parent(impl_item.owner_id.def_id) - && find_attr!(self.tcx, impl_item.owner_id.def_id, RustcTrivialFieldReads) + && let trait_ref = + self.tcx.impl_trait_ref(impl_of).instantiate_identity().skip_norm_wip() + && find_attr!(self.tcx, trait_ref.def_id, RustcTrivialFieldReads) { - let trait_ref = self.tcx.impl_trait_ref(impl_of).instantiate_identity().skip_norm_wip(); if let ty::Adt(adt_def, _) = trait_ref.self_ty().kind() && let Some(adt_def_id) = adt_def.did().as_local() { From dc662df3b8b4cf2db671e697ed17711bd09cc1f2 Mon Sep 17 00:00:00 2001 From: Jose Torres Date: Wed, 19 Aug 2026 18:39:38 -0400 Subject: [PATCH 08/22] checking on impl item --- compiler/rustc_passes/src/dead.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index a3a68d7ef2e29..af8a820d62f25 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -450,10 +450,10 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { fn should_ignore_impl_item(&mut self, impl_item: &hir::ImplItem<'_>) -> bool { if let hir::ImplItemImplKind::Trait { .. } = impl_item.impl_kind && let impl_of = self.tcx.local_parent(impl_item.owner_id.def_id) - && let trait_ref = - self.tcx.impl_trait_ref(impl_of).instantiate_identity().skip_norm_wip() - && find_attr!(self.tcx, trait_ref.def_id, RustcTrivialFieldReads) + && find_attr!(self.tcx, impl_item.owner_id.def_id, RustcTrivialFieldReads) { + let trait_ref = self.tcx.impl_trait_ref(impl_of).instantiate_identity().skip_norm_wip(); + if let ty::Adt(adt_def, _) = trait_ref.self_ty().kind() && let Some(adt_def_id) = adt_def.did().as_local() { From 0b7febf7184eee705b4e58682075a3946f5a3feb Mon Sep 17 00:00:00 2001 From: Jose Torres Date: Wed, 19 Aug 2026 18:47:15 -0400 Subject: [PATCH 09/22] bless existing tests --- tests/ui/derives/deriving-all-codegen.stdout | 40 ++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/tests/ui/derives/deriving-all-codegen.stdout b/tests/ui/derives/deriving-all-codegen.stdout index 320c1b5861162..d6d9c25cecd35 100644 --- a/tests/ui/derives/deriving-all-codegen.stdout +++ b/tests/ui/derives/deriving-all-codegen.stdout @@ -32,6 +32,7 @@ unsafe impl ::core::clone::TrivialClone for Empty { } #[automatically_derived] impl ::core::clone::Clone for Empty { #[inline] + #[rustc_trivial_field_reads] fn clone(&self) -> Empty { *self } } #[automatically_derived] @@ -39,6 +40,7 @@ impl ::core::marker::Copy for Empty { } #[automatically_derived] impl ::core::fmt::Debug for Empty { #[inline] + #[rustc_trivial_field_reads] fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { ::core::fmt::Formatter::write_str(f, "Empty") } @@ -95,6 +97,7 @@ unsafe impl ::core::clone::TrivialClone for Point { } #[automatically_derived] impl ::core::clone::Clone for Point { #[inline] + #[rustc_trivial_field_reads] fn clone(&self) -> Point { let _: ::core::clone::AssertParamIsClone; *self @@ -105,6 +108,7 @@ impl ::core::marker::Copy for Point { } #[automatically_derived] impl ::core::fmt::Debug for Point { #[inline] + #[rustc_trivial_field_reads] fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { ::core::fmt::Formatter::debug_struct_field2_finish(f, "Point", "x", &self.x, "y", &&self.y) @@ -179,6 +183,7 @@ unsafe impl ::core::clone::TrivialClone for PackedPoint { } #[automatically_derived] impl ::core::clone::Clone for PackedPoint { #[inline] + #[rustc_trivial_field_reads] fn clone(&self) -> PackedPoint { let _: ::core::clone::AssertParamIsClone; *self @@ -189,6 +194,7 @@ impl ::core::marker::Copy for PackedPoint { } #[automatically_derived] impl ::core::fmt::Debug for PackedPoint { #[inline] + #[rustc_trivial_field_reads] fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { ::core::fmt::Formatter::debug_struct_field2_finish(f, "PackedPoint", "x", &{ self.x }, "y", &&{ self.y }) @@ -257,6 +263,7 @@ unsafe impl ::core::clone::TrivialClone for TupleSingleField { } #[automatically_derived] impl ::core::clone::Clone for TupleSingleField { #[inline] + #[rustc_trivial_field_reads] fn clone(&self) -> TupleSingleField { let _: ::core::clone::AssertParamIsClone; *self @@ -267,6 +274,7 @@ impl ::core::marker::Copy for TupleSingleField { } #[automatically_derived] impl ::core::fmt::Debug for TupleSingleField { #[inline] + #[rustc_trivial_field_reads] fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { ::core::fmt::Formatter::debug_tuple_field1_finish(f, "TupleSingleField", &&self.0) @@ -332,6 +340,7 @@ unsafe impl ::core::clone::TrivialClone for SingleField { } #[automatically_derived] impl ::core::clone::Clone for SingleField { #[inline] + #[rustc_trivial_field_reads] fn clone(&self) -> SingleField { let _: ::core::clone::AssertParamIsClone; *self @@ -342,6 +351,7 @@ impl ::core::marker::Copy for SingleField { } #[automatically_derived] impl ::core::fmt::Debug for SingleField { #[inline] + #[rustc_trivial_field_reads] fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { ::core::fmt::Formatter::debug_struct_field1_finish(f, "SingleField", "foo", &&self.foo) @@ -416,6 +426,7 @@ unsafe impl ::core::clone::TrivialClone for Big { } #[automatically_derived] impl ::core::clone::Clone for Big { #[inline] + #[rustc_trivial_field_reads] fn clone(&self) -> Big { let _: ::core::clone::AssertParamIsClone; *self @@ -426,6 +437,7 @@ impl ::core::marker::Copy for Big { } #[automatically_derived] impl ::core::fmt::Debug for Big { #[inline] + #[rustc_trivial_field_reads] fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { let names: &'static _ = &["b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8"]; @@ -631,6 +643,7 @@ struct NonCopy(u32); #[automatically_derived] impl ::core::clone::Clone for NonCopy { #[inline] + #[rustc_trivial_field_reads] fn clone(&self) -> NonCopy { NonCopy(::core::clone::Clone::clone(&self.0)) } @@ -643,6 +656,7 @@ struct PackedNonCopy(u32); #[automatically_derived] impl ::core::clone::Clone for PackedNonCopy { #[inline] + #[rustc_trivial_field_reads] fn clone(&self) -> PackedNonCopy { PackedNonCopy(::core::clone::Clone::clone(&{ self.0 })) } @@ -654,6 +668,7 @@ struct ManualCopy(u32); #[automatically_derived] impl ::core::clone::Clone for ManualCopy { #[inline] + #[rustc_trivial_field_reads] fn clone(&self) -> ManualCopy { ManualCopy(::core::clone::Clone::clone(&self.0)) } @@ -667,6 +682,7 @@ struct PackedManualCopy(u32); #[automatically_derived] impl ::core::clone::Clone for PackedManualCopy { #[inline] + #[rustc_trivial_field_reads] fn clone(&self) -> PackedManualCopy { PackedManualCopy(::core::clone::Clone::clone(&{ self.0 })) } @@ -678,6 +694,7 @@ struct Unsized([u32]); #[automatically_derived] impl ::core::fmt::Debug for Unsized { #[inline] + #[rustc_trivial_field_reads] fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { ::core::fmt::Formatter::debug_tuple_field1_finish(f, "Unsized", &&self.0) @@ -741,6 +758,7 @@ struct Generic { impl ::core::clone::Clone for Generic where T::A: ::core::clone::Clone { #[inline] + #[rustc_trivial_field_reads] fn clone(&self) -> Generic { Generic { t: ::core::clone::Clone::clone(&self.t), @@ -757,6 +775,7 @@ impl impl ::core::fmt::Debug for Generic where T::A: ::core::fmt::Debug { #[inline] + #[rustc_trivial_field_reads] fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { ::core::fmt::Formatter::debug_struct_field3_finish(f, "Generic", "t", &self.t, "ta", &self.ta, "u", &&self.u) @@ -860,6 +879,7 @@ impl where T::A: ::core::clone::Clone + ::core::marker::Copy { #[inline] + #[rustc_trivial_field_reads] fn clone(&self) -> PackedGeneric { PackedGeneric(::core::clone::Clone::clone(&{ self.0 }), ::core::clone::Clone::clone(&{ self.1 }), @@ -877,6 +897,7 @@ impl where T::A: ::core::fmt::Debug + ::core::marker::Copy { #[inline] + #[rustc_trivial_field_reads] fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { ::core::fmt::Formatter::debug_tuple_field3_finish(f, "PackedGeneric", &{ self.0 }, &{ self.1 }, &&{ self.2 }) @@ -983,6 +1004,7 @@ unsafe impl ::core::clone::TrivialClone for Enum0 { } #[automatically_derived] impl ::core::clone::Clone for Enum0 { #[inline] + #[rustc_trivial_field_reads] fn clone(&self) -> Enum0 { *self } } #[automatically_derived] @@ -990,6 +1012,7 @@ impl ::core::marker::Copy for Enum0 { } #[automatically_derived] impl ::core::fmt::Debug for Enum0 { #[inline] + #[rustc_trivial_field_reads] fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { match *self {} } @@ -1038,6 +1061,7 @@ enum Enum1 { #[automatically_derived] impl ::core::clone::Clone for Enum1 { #[inline] + #[rustc_trivial_field_reads] fn clone(&self) -> Enum1 { match self { Enum1::Single { x: __self_0 } => @@ -1048,6 +1072,7 @@ impl ::core::clone::Clone for Enum1 { #[automatically_derived] impl ::core::fmt::Debug for Enum1 { #[inline] + #[rustc_trivial_field_reads] fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { match self { Enum1::Single { x: __self_0 } => @@ -1115,11 +1140,13 @@ enum Fieldless1 { #[automatically_derived] impl ::core::clone::Clone for Fieldless1 { #[inline] + #[rustc_trivial_field_reads] fn clone(&self) -> Fieldless1 { Fieldless1::A } } #[automatically_derived] impl ::core::fmt::Debug for Fieldless1 { #[inline] + #[rustc_trivial_field_reads] fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { ::core::fmt::Formatter::write_str(f, "A") } @@ -1178,6 +1205,7 @@ unsafe impl ::core::clone::TrivialClone for Fieldless { } #[automatically_derived] impl ::core::clone::Clone for Fieldless { #[inline] + #[rustc_trivial_field_reads] fn clone(&self) -> Fieldless { *self } } #[automatically_derived] @@ -1185,6 +1213,7 @@ impl ::core::marker::Copy for Fieldless { } #[automatically_derived] impl ::core::fmt::Debug for Fieldless { #[inline] + #[rustc_trivial_field_reads] fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { ::core::fmt::Formatter::write_str(f, match self { @@ -1261,6 +1290,7 @@ unsafe impl ::core::clone::TrivialClone for Mixed { } #[automatically_derived] impl ::core::clone::Clone for Mixed { #[inline] + #[rustc_trivial_field_reads] fn clone(&self) -> Mixed { let _: ::core::clone::AssertParamIsClone; let _: ::core::clone::AssertParamIsClone>; @@ -1273,6 +1303,7 @@ impl ::core::marker::Copy for Mixed { } #[automatically_derived] impl ::core::fmt::Debug for Mixed { #[inline] + #[rustc_trivial_field_reads] fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { match self { Mixed::P => ::core::fmt::Formatter::write_str(f, "P"), @@ -1447,6 +1478,7 @@ enum Fielded { X(u32), Y(bool), Z(Option), } #[automatically_derived] impl ::core::clone::Clone for Fielded { #[inline] + #[rustc_trivial_field_reads] fn clone(&self) -> Fielded { match self { Fielded::X(__self_0) => @@ -1461,6 +1493,7 @@ impl ::core::clone::Clone for Fielded { #[automatically_derived] impl ::core::fmt::Debug for Fielded { #[inline] + #[rustc_trivial_field_reads] fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { match self { Fielded::X(__self_0) => @@ -1555,6 +1588,7 @@ enum EnumGeneric { One(T), Two(U), } impl ::core::clone::Clone for EnumGeneric { #[inline] + #[rustc_trivial_field_reads] fn clone(&self) -> EnumGeneric { match self { EnumGeneric::One(__self_0) => @@ -1572,6 +1606,7 @@ impl ::core::marker::Copy impl ::core::fmt::Debug for EnumGeneric { #[inline] + #[rustc_trivial_field_reads] fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { match self { EnumGeneric::One(__self_0) => @@ -1706,6 +1741,7 @@ unsafe impl ::core::clone::TrivialClone for Union { } #[automatically_derived] impl ::core::clone::Clone for Union { #[inline] + #[rustc_trivial_field_reads] fn clone(&self) -> Union { let _: ::core::clone::AssertParamIsCopy; *self @@ -1723,6 +1759,7 @@ unsafe impl ::core::clone::TrivialClone for FooCopyClone { } #[automatically_derived] impl ::core::clone::Clone for FooCopyClone { #[inline] + #[rustc_trivial_field_reads] fn clone(&self) -> FooCopyClone { let _: ::core::clone::AssertParamIsClone; *self @@ -1736,6 +1773,7 @@ unsafe impl ::core::clone::TrivialClone for FooCloneCopy { } #[automatically_derived] impl ::core::clone::Clone for FooCloneCopy { #[inline] + #[rustc_trivial_field_reads] fn clone(&self) -> FooCloneCopy { let _: ::core::clone::AssertParamIsClone; *self @@ -1751,6 +1789,7 @@ unsafe impl ::core::clone::TrivialClone for FooCopyAndClone { } #[automatically_derived] impl ::core::clone::Clone for FooCopyAndClone { #[inline] + #[rustc_trivial_field_reads] fn clone(&self) -> FooCopyAndClone { let _: ::core::clone::AssertParamIsClone; *self @@ -1768,6 +1807,7 @@ impl ::core::marker::Copy for FooCloneAndCopy { } #[automatically_derived] impl ::core::clone::Clone for FooCloneAndCopy { #[inline] + #[rustc_trivial_field_reads] fn clone(&self) -> FooCloneAndCopy { FooCloneAndCopy(::core::clone::Clone::clone(&self.0)) } From f26181b15a411105d6f78fb347469fde61415a1e Mon Sep 17 00:00:00 2001 From: Jose Torres Date: Wed, 19 Aug 2026 18:52:19 -0400 Subject: [PATCH 10/22] update code comment --- compiler/rustc_passes/src/dead.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index af8a820d62f25..4191561b45fc3 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -444,9 +444,9 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { ControlFlow::Continue(()) } - /// Automatically generated items marked with `rustc_trivial_field_reads` + /// Trait impl methods marked with `rustc_trivial_field_reads` /// will be ignored for the purposes of dead code analysis (see PR #85200 - /// for discussion). + /// for discussion, and PR #160666). fn should_ignore_impl_item(&mut self, impl_item: &hir::ImplItem<'_>) -> bool { if let hir::ImplItemImplKind::Trait { .. } = impl_item.impl_kind && let impl_of = self.tcx.local_parent(impl_item.owner_id.def_id) From 7dbd378dcaabfe23f7cdf8116fac8b0424167617 Mon Sep 17 00:00:00 2001 From: Jose Torres Date: Wed, 19 Aug 2026 19:56:35 -0400 Subject: [PATCH 11/22] bless macro stats tests --- tests/ui/stats/macro-stats.stderr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ui/stats/macro-stats.stderr b/tests/ui/stats/macro-stats.stderr index c70895067eb5f..b940734acd3f0 100644 --- a/tests/ui/stats/macro-stats.stderr +++ b/tests/ui/stats/macro-stats.stderr @@ -2,13 +2,13 @@ macro-stats ==================================================================== macro-stats MACRO EXPANSION STATS: macro_stats macro-stats Macro Name Uses Lines Avg Lines Bytes Avg Bytes macro-stats ----------------------------------------------------------------------------------- -macro-stats #[derive(Clone)] 8 67 8.4 1_879 234.9 +macro-stats #[derive(Clone)] 8 75 9.4 2_143 267.9 macro-stats #[derive(Hash)] 2 17 8.5 565 282.5 macro-stats q! 1 26 26.0 519 519.0 macro-stats #[derive(Ord)] 1 15 15.0 503 503.0 macro-stats #[derive(Default)] 2 16 8.0 403 201.5 macro-stats #[derive(Eq)] 1 11 11.0 312 312.0 -macro-stats #[derive(Debug)] 1 8 8.0 277 277.0 +macro-stats #[derive(Debug)] 1 9 9.0 310 310.0 macro-stats #[derive(PartialEq)] 1 9 9.0 267 267.0 macro-stats #[derive(PartialOrd)] 1 8 8.0 254 254.0 macro-stats #[derive(Copy)] 1 2 2.0 61 61.0 From 6bc0535887fb3b3af4327a9582c87ffdc0ac513c Mon Sep 17 00:00:00 2001 From: Jose Torres Date: Wed, 19 Aug 2026 20:58:42 -0400 Subject: [PATCH 12/22] blessing deriving all codegen test --- tests/ui/derives/deriving-all-codegen.stdout | 48 ++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/tests/ui/derives/deriving-all-codegen.stdout b/tests/ui/derives/deriving-all-codegen.stdout index d6d9c25cecd35..79f6505479e65 100644 --- a/tests/ui/derives/deriving-all-codegen.stdout +++ b/tests/ui/derives/deriving-all-codegen.stdout @@ -1272,6 +1272,54 @@ impl ::core::cmp::Ord for Fieldless { } } +<<<<<<< HEAD +======= +// A C-like, fieldless enum with variants of varying name lengths. +enum Fieldless0 { A, BBB, CC, } +#[automatically_derived] +impl ::core::fmt::Debug for Fieldless0 { + #[inline] + #[rustc_trivial_field_reads] + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + ::core::fmt::Formatter::write_str(f, + match self { + Fieldless0::A => "A", + Fieldless0::BBB => "BBB", + Fieldless0::CC => "CC", + }) + } +} + +// A C-like, fieldless enum with 10 variants. +enum Fieldless10 { + AAAAA, + BBBB, + CC, + DDDDDDDD, + E, + FFFFFFFFFFFFF, + GGGGGG, + Hatsune, + IIIIIII, + JJJJJJJJJ, +} +#[automatically_derived] +impl ::core::fmt::Debug for Fieldless10 { + #[inline] + #[rustc_trivial_field_reads] + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + static __NAMES: &str = + "AAAAABBBBCCDDDDDDDDEFFFFFFFFFFFFFGGGGGGHatsuneIIIIIIIJJJJJJJJJ"; + static __OFFSET: [usize; 11] = + [0usize, 5usize, 9usize, 11usize, 19usize, 20usize, 33usize, + 39usize, 46usize, 53usize, 62usize]; + let __d = ::core::intrinsics::discriminant_value(self) as usize; + ::core::fmt::Formatter::debug_c_like_enum_write_str(f, __NAMES, + &__OFFSET, __d) + } +} + +>>>>>>> 7196365306e (blessing deriving all codegen test) // An enum with multiple fieldless and fielded variants. enum Mixed { From f3a904b825f139ac7a0ecc1c91fcddd536374d55 Mon Sep 17 00:00:00 2001 From: Jose Torres Date: Sat, 22 Aug 2026 22:15:06 -0400 Subject: [PATCH 13/22] expanding attr to impl blocks and refactoring should_explore to not check iff the attribute is applied --- .../src/attributes/rustc_internal.rs | 6 +- compiler/rustc_passes/src/dead.rs | 16 +++-- .../dead-code/trivial-field-reads-method.rs | 70 ++++++++++++------- .../trivial-field-reads-method.stderr | 20 ++++-- 4 files changed, 73 insertions(+), 39 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs index bee1abf4099a3..bd87bbceef1f5 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -401,8 +401,10 @@ pub(crate) struct RustcTrivialFieldReadsParser; impl NoArgsAttributeParser for RustcTrivialFieldReadsParser { const PATH: &[Symbol] = &[sym::rustc_trivial_field_reads]; - const ALLOWED_TARGETS: AllowedTargets<'_> = - AllowedTargets::AllowList(&[Allow(Target::Method(MethodKind::TraitImpl))]); + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ + Allow(Target::Method(MethodKind::TraitImpl)), + Allow(Target::Method(MethodKind::Inherent)), + ]); const STABILITY: AttributeStability = unstable!(rustc_attrs); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcTrivialFieldReads; } diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 4191561b45fc3..e431e5e790c8a 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -59,7 +59,7 @@ fn should_explore(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { | DefKind::ExternCrate | DefKind::Use | DefKind::Ctor(..) - | DefKind::ForeignMod => true, + | DefKind::ForeignMod => !find_attr!(tcx, def_id, RustcTrivialFieldReads), DefKind::TyParam | DefKind::ConstParam @@ -438,7 +438,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { continue; } - self.visit_node(self.tcx.hir_node_by_def_id(id))?; + self.visit_node(id)?; } ControlFlow::Continue(()) @@ -448,9 +448,12 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { /// will be ignored for the purposes of dead code analysis (see PR #85200 /// for discussion, and PR #160666). fn should_ignore_impl_item(&mut self, impl_item: &hir::ImplItem<'_>) -> bool { + if !find_attr!(self.tcx, impl_item.owner_id.def_id, RustcTrivialFieldReads) { + return false; + } + if let hir::ImplItemImplKind::Trait { .. } = impl_item.impl_kind && let impl_of = self.tcx.local_parent(impl_item.owner_id.def_id) - && find_attr!(self.tcx, impl_item.owner_id.def_id, RustcTrivialFieldReads) { let trait_ref = self.tcx.impl_trait_ref(impl_of).instantiate_identity().skip_norm_wip(); @@ -459,16 +462,17 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { { self.ignored_derived_traits.entry(adt_def_id).or_default().insert(trait_ref.def_id); } - return true; } - false + true } fn visit_node( &mut self, - node: Node<'tcx>, + def_id: LocalDefId, ) -> as Visitor<'tcx>>::Result { + let node = self.tcx.hir_node_by_def_id(def_id); + if let Node::ImplItem(impl_item) = node && self.should_ignore_impl_item(impl_item) { diff --git a/tests/ui/lint/dead-code/trivial-field-reads-method.rs b/tests/ui/lint/dead-code/trivial-field-reads-method.rs index 91aacef2e1454..dea59eef0039c 100644 --- a/tests/ui/lint/dead-code/trivial-field-reads-method.rs +++ b/tests/ui/lint/dead-code/trivial-field-reads-method.rs @@ -1,36 +1,36 @@ - //! Checks that `#[rustc_trivial_field_reads]` applies per method - //! (issue #160621) +//! Checks that `#[rustc_trivial_field_reads]` applies per method +//! (issue #160621) - #![feature(rustc_attrs)] - #![deny(dead_code)] +#![feature(rustc_attrs)] +#![deny(dead_code)] - trait Access { - fn get_a(&self) -> u32; - fn get_b(&self) -> u32; - } +trait Access { + fn get_a(&self) -> u32; + fn get_b(&self) -> u32; +} - struct S { +struct S { a: u32, //~ ERROR field `a` is never read b: u32 - } +} - impl Access for S { - #[rustc_trivial_field_reads] - fn get_a(&self) -> u32 { - self.a - } +impl Access for S { + #[rustc_trivial_field_reads] + fn get_a(&self) -> u32 { + self.a + } - fn get_b(&self) -> u32 { - self.b - } - } + fn get_b(&self) -> u32 { + self.b + } +} - struct T { +struct T { a: u32, b: u32 - } +} - impl Access for T { +impl Access for T { fn get_a(&self) -> u32 { self.a } @@ -38,9 +38,25 @@ fn get_b(&self) -> u32 { self.b } - } +} - fn main() { +struct Square { + width: u32, //~ ERROR field `width` is never read + height: u32 +} + +impl Square { + #[rustc_trivial_field_reads] + fn width(&self) -> u32 { + self.width + } + + fn height(&self) -> u32 { + self.height + } +} + +fn main() { let s = S { a: 0, b: 0 @@ -56,4 +72,8 @@ let _ = t.get_a(); let _ = t.get_b(); - } + + let square = Square { width: 0, height: 0 }; + let _ = square.width(); + let _ = square.height(); +} diff --git a/tests/ui/lint/dead-code/trivial-field-reads-method.stderr b/tests/ui/lint/dead-code/trivial-field-reads-method.stderr index cd355c8760351..20fb73b858135 100644 --- a/tests/ui/lint/dead-code/trivial-field-reads-method.stderr +++ b/tests/ui/lint/dead-code/trivial-field-reads-method.stderr @@ -1,17 +1,25 @@ error: field `a` is never read --> $DIR/trivial-field-reads-method.rs:13:5 | -LL | struct S { - | - field in this struct +LL | struct S { + | - field in this struct LL | a: u32, | ^ | = note: `S` has a derived impl for the trait `Access`, but this is intentionally ignored during dead code analysis note: the lint level is defined here - --> $DIR/trivial-field-reads-method.rs:5:11 + --> $DIR/trivial-field-reads-method.rs:5:9 | -LL | #![deny(dead_code)] - | ^^^^^^^^^ +LL | #![deny(dead_code)] + | ^^^^^^^^^ -error: aborting due to 1 previous error +error: field `width` is never read + --> $DIR/trivial-field-reads-method.rs:44:5 + | +LL | struct Square { + | ------ field in this struct +LL | width: u32, + | ^^^^^ + +error: aborting due to 2 previous errors From b2bde6142ab764aa14f2f338e1a9049cf84d1735 Mon Sep 17 00:00:00 2001 From: Jose Torres Date: Sun, 23 Aug 2026 17:52:14 -0400 Subject: [PATCH 14/22] allow free functions as a target, move find_attr check eagerly in visit_node --- .../src/attributes/rustc_internal.rs | 1 + compiler/rustc_passes/src/dead.rs | 18 ++++++------------ .../dead-code/trivial-field-reads-method.rs | 18 ++++++++++++++++++ .../trivial-field-reads-method.stderr | 10 +++++++++- 4 files changed, 34 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs index bd87bbceef1f5..b6e11af845d5c 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -404,6 +404,7 @@ impl NoArgsAttributeParser for RustcTrivialFieldReadsParser { const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Method(MethodKind::TraitImpl)), Allow(Target::Method(MethodKind::Inherent)), + Allow(Target::Fn), ]); const STABILITY: AttributeStability = unstable!(rustc_attrs); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcTrivialFieldReads; diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index e431e5e790c8a..16172950008f6 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -59,7 +59,7 @@ fn should_explore(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { | DefKind::ExternCrate | DefKind::Use | DefKind::Ctor(..) - | DefKind::ForeignMod => !find_attr!(tcx, def_id, RustcTrivialFieldReads), + | DefKind::ForeignMod => true, DefKind::TyParam | DefKind::ConstParam @@ -447,12 +447,9 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { /// Trait impl methods marked with `rustc_trivial_field_reads` /// will be ignored for the purposes of dead code analysis (see PR #85200 /// for discussion, and PR #160666). - fn should_ignore_impl_item(&mut self, impl_item: &hir::ImplItem<'_>) -> bool { - if !find_attr!(self.tcx, impl_item.owner_id.def_id, RustcTrivialFieldReads) { - return false; - } - - if let hir::ImplItemImplKind::Trait { .. } = impl_item.impl_kind + fn should_ignore_impl_item(&mut self, node: Node<'_>) { + if let Node::ImplItem(impl_item) = node + && let hir::ImplItemImplKind::Trait { .. } = impl_item.impl_kind && let impl_of = self.tcx.local_parent(impl_item.owner_id.def_id) { let trait_ref = self.tcx.impl_trait_ref(impl_of).instantiate_identity().skip_norm_wip(); @@ -463,8 +460,6 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { self.ignored_derived_traits.entry(adt_def_id).or_default().insert(trait_ref.def_id); } } - - true } fn visit_node( @@ -473,9 +468,8 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { ) -> as Visitor<'tcx>>::Result { let node = self.tcx.hir_node_by_def_id(def_id); - if let Node::ImplItem(impl_item) = node - && self.should_ignore_impl_item(impl_item) - { + if find_attr!(self.tcx, def_id, RustcTrivialFieldReads) { + self.should_ignore_impl_item(node); return ControlFlow::Continue(()); } diff --git a/tests/ui/lint/dead-code/trivial-field-reads-method.rs b/tests/ui/lint/dead-code/trivial-field-reads-method.rs index dea59eef0039c..f810e341f0900 100644 --- a/tests/ui/lint/dead-code/trivial-field-reads-method.rs +++ b/tests/ui/lint/dead-code/trivial-field-reads-method.rs @@ -56,6 +56,20 @@ impl Square { } } +struct U { + a: u32, //~ ERROR field `a` is never read + b: u32 +} + +#[rustc_trivial_field_reads] +fn foo(u: &U) -> u32 { + u.a +} + +fn bar(u: &U) -> u32 { + u.b +} + fn main() { let s = S { a: 0, @@ -76,4 +90,8 @@ fn main() { let square = Square { width: 0, height: 0 }; let _ = square.width(); let _ = square.height(); + + let u = U { a: 0, b:0 }; + let _ = foo(&u); + let _ = bar(&u); } diff --git a/tests/ui/lint/dead-code/trivial-field-reads-method.stderr b/tests/ui/lint/dead-code/trivial-field-reads-method.stderr index 20fb73b858135..7bb5eebf21cb4 100644 --- a/tests/ui/lint/dead-code/trivial-field-reads-method.stderr +++ b/tests/ui/lint/dead-code/trivial-field-reads-method.stderr @@ -21,5 +21,13 @@ LL | struct Square { LL | width: u32, | ^^^^^ -error: aborting due to 2 previous errors +error: field `a` is never read + --> $DIR/trivial-field-reads-method.rs:60:5 + | +LL | struct U { + | - field in this struct +LL | a: u32, + | ^ + +error: aborting due to 3 previous errors From c3526b721c6a324fc62c4ea920f5c05f89d7813c Mon Sep 17 00:00:00 2001 From: Jose Torres Date: Sun, 23 Aug 2026 18:58:36 -0400 Subject: [PATCH 15/22] expanding target to traits with a default method also moving and updating stale comments --- .../src/attributes/rustc_internal.rs | 5 ++-- compiler/rustc_passes/src/dead.rs | 10 +++---- .../dead-code/trivial-field-reads-method.rs | 28 ++++++++++++++++++- .../trivial-field-reads-method.stderr | 10 ++++++- 4 files changed, 44 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs index b6e11af845d5c..bd7b8770cd604 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -402,9 +402,10 @@ pub(crate) struct RustcTrivialFieldReadsParser; impl NoArgsAttributeParser for RustcTrivialFieldReadsParser { const PATH: &[Symbol] = &[sym::rustc_trivial_field_reads]; const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ - Allow(Target::Method(MethodKind::TraitImpl)), - Allow(Target::Method(MethodKind::Inherent)), Allow(Target::Fn), + Allow(Target::Method(MethodKind::Inherent)), + Allow(Target::Method(MethodKind::Trait { body: true })), + Allow(Target::Method(MethodKind::TraitImpl)), ]); const STABILITY: AttributeStability = unstable!(rustc_attrs); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcTrivialFieldReads; diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 16172950008f6..512e5208282c3 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -444,10 +444,8 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { ControlFlow::Continue(()) } - /// Trait impl methods marked with `rustc_trivial_field_reads` - /// will be ignored for the purposes of dead code analysis (see PR #85200 - /// for discussion, and PR #160666). - fn should_ignore_impl_item(&mut self, node: Node<'_>) { + /// Records trait impls skipped so that we can note that its fields look unused in the error message + fn record_impl_item_to_ignore(&mut self, node: Node<'_>) { if let Node::ImplItem(impl_item) = node && let hir::ImplItemImplKind::Trait { .. } = impl_item.impl_kind && let impl_of = self.tcx.local_parent(impl_item.owner_id.def_id) @@ -468,8 +466,10 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { ) -> as Visitor<'tcx>>::Result { let node = self.tcx.hir_node_by_def_id(def_id); + // Items marked with `rustc_trivial_field_reads` will be ignored for the purposes + // of dead code analysis (see PR #85200 for discussion, and PR #160666) if find_attr!(self.tcx, def_id, RustcTrivialFieldReads) { - self.should_ignore_impl_item(node); + self.record_impl_item_to_ignore(node); return ControlFlow::Continue(()); } diff --git a/tests/ui/lint/dead-code/trivial-field-reads-method.rs b/tests/ui/lint/dead-code/trivial-field-reads-method.rs index f810e341f0900..c97336ed12c23 100644 --- a/tests/ui/lint/dead-code/trivial-field-reads-method.rs +++ b/tests/ui/lint/dead-code/trivial-field-reads-method.rs @@ -1,4 +1,4 @@ -//! Checks that `#[rustc_trivial_field_reads]` applies per method +//! Checks that `#[rustc_trivial_field_reads]` on the allowed targets //! (issue #160621) #![feature(rustc_attrs)] @@ -70,6 +70,26 @@ fn bar(u: &U) -> u32 { u.b } +struct Whatever { + a: u32, //~ ERROR field `a` is never read + b: u32, +} + +trait ReadWhatever { + #[rustc_trivial_field_reads] + fn read_a(&self, value: &Whatever) -> u32 { + value.a + } + + fn read_b(&self, value: &Whatever) -> u32 { + value.b + } +} + +struct Reader; + +impl ReadWhatever for Reader {} + fn main() { let s = S { a: 0, @@ -94,4 +114,10 @@ fn main() { let u = U { a: 0, b:0 }; let _ = foo(&u); let _ = bar(&u); + + let whatever = Whatever { a: 0, b: 0 }; + let reader = Reader; + + let _ = reader.read_a(&whatever); + let _ = reader.read_b(&whatever); } diff --git a/tests/ui/lint/dead-code/trivial-field-reads-method.stderr b/tests/ui/lint/dead-code/trivial-field-reads-method.stderr index 7bb5eebf21cb4..e1de46a00fb57 100644 --- a/tests/ui/lint/dead-code/trivial-field-reads-method.stderr +++ b/tests/ui/lint/dead-code/trivial-field-reads-method.stderr @@ -29,5 +29,13 @@ LL | struct U { LL | a: u32, | ^ -error: aborting due to 3 previous errors +error: field `a` is never read + --> $DIR/trivial-field-reads-method.rs:74:5 + | +LL | struct Whatever { + | -------- field in this struct +LL | a: u32, + | ^ + +error: aborting due to 4 previous errors From 6282fbd384522615427c50ae4393b9f74dfb0e71 Mon Sep 17 00:00:00 2001 From: Jose Torres Date: Sun, 23 Aug 2026 22:20:06 -0400 Subject: [PATCH 16/22] adding invalid targets tests, rename introduced dead code tests --- .../trivial-field-reads-invalid-targets.rs | 23 ++++ ...trivial-field-reads-invalid-targets.stderr | 26 ++++ .../trivial-field-reads-valid-targets.rs | 123 ++++++++++++++++++ .../trivial-field-reads-valid-targets.stderr | 41 ++++++ 4 files changed, 213 insertions(+) create mode 100644 tests/ui/lint/dead-code/trivial-field-reads-invalid-targets.rs create mode 100644 tests/ui/lint/dead-code/trivial-field-reads-invalid-targets.stderr create mode 100644 tests/ui/lint/dead-code/trivial-field-reads-valid-targets.rs create mode 100644 tests/ui/lint/dead-code/trivial-field-reads-valid-targets.stderr diff --git a/tests/ui/lint/dead-code/trivial-field-reads-invalid-targets.rs b/tests/ui/lint/dead-code/trivial-field-reads-invalid-targets.rs new file mode 100644 index 0000000000000..34c65652516c1 --- /dev/null +++ b/tests/ui/lint/dead-code/trivial-field-reads-invalid-targets.rs @@ -0,0 +1,23 @@ +//! Checks that `#[rustc_trivial_field_reads]` are not allowed due to invalid targets + +#![feature(rustc_attrs)] +#![deny(dead_code)] + +trait Whatever { + #[rustc_trivial_field_reads] //~ERROR the `rustc_trivial_field_reads` attribute cannot be used on required trait methods + fn read(&self); +} + +#[rustc_trivial_field_reads] //~ERROR the `rustc_trivial_field_reads` attribute cannot be used on traits +trait Whatever1 {} + +#[rustc_trivial_field_reads] //~ERROR the `rustc_trivial_field_reads` attribute cannot be used on macro defs + macro_rules! number { + () => { + 67 + }; + } + +fn main() { + let _ = number!(); +} \ No newline at end of file diff --git a/tests/ui/lint/dead-code/trivial-field-reads-invalid-targets.stderr b/tests/ui/lint/dead-code/trivial-field-reads-invalid-targets.stderr new file mode 100644 index 0000000000000..83cdc1e17ea8e --- /dev/null +++ b/tests/ui/lint/dead-code/trivial-field-reads-invalid-targets.stderr @@ -0,0 +1,26 @@ +error: the `rustc_trivial_field_reads` attribute cannot be used on required trait methods + --> $DIR/trivial-field-reads-invalid-targets.rs:8:7 + | +LL | #[rustc_trivial_field_reads] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: the `rustc_trivial_field_reads` attribute can only be applied to functions with a body + +error: the `rustc_trivial_field_reads` attribute cannot be used on traits + --> $DIR/trivial-field-reads-invalid-targets.rs:12:3 + | +LL | #[rustc_trivial_field_reads] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: the `rustc_trivial_field_reads` attribute can only be applied to functions + +error: the `rustc_trivial_field_reads` attribute cannot be used on macro defs + --> $DIR/trivial-field-reads-invalid-targets.rs:15:3 + | +LL | #[rustc_trivial_field_reads] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: the `rustc_trivial_field_reads` attribute can only be applied to functions + +error: aborting due to 3 previous errors + diff --git a/tests/ui/lint/dead-code/trivial-field-reads-valid-targets.rs b/tests/ui/lint/dead-code/trivial-field-reads-valid-targets.rs new file mode 100644 index 0000000000000..c97336ed12c23 --- /dev/null +++ b/tests/ui/lint/dead-code/trivial-field-reads-valid-targets.rs @@ -0,0 +1,123 @@ +//! Checks that `#[rustc_trivial_field_reads]` on the allowed targets +//! (issue #160621) + +#![feature(rustc_attrs)] +#![deny(dead_code)] + +trait Access { + fn get_a(&self) -> u32; + fn get_b(&self) -> u32; +} + +struct S { + a: u32, //~ ERROR field `a` is never read + b: u32 +} + +impl Access for S { + #[rustc_trivial_field_reads] + fn get_a(&self) -> u32 { + self.a + } + + fn get_b(&self) -> u32 { + self.b + } +} + +struct T { + a: u32, + b: u32 +} + +impl Access for T { + fn get_a(&self) -> u32 { + self.a + } + + fn get_b(&self) -> u32 { + self.b + } +} + +struct Square { + width: u32, //~ ERROR field `width` is never read + height: u32 +} + +impl Square { + #[rustc_trivial_field_reads] + fn width(&self) -> u32 { + self.width + } + + fn height(&self) -> u32 { + self.height + } +} + +struct U { + a: u32, //~ ERROR field `a` is never read + b: u32 +} + +#[rustc_trivial_field_reads] +fn foo(u: &U) -> u32 { + u.a +} + +fn bar(u: &U) -> u32 { + u.b +} + +struct Whatever { + a: u32, //~ ERROR field `a` is never read + b: u32, +} + +trait ReadWhatever { + #[rustc_trivial_field_reads] + fn read_a(&self, value: &Whatever) -> u32 { + value.a + } + + fn read_b(&self, value: &Whatever) -> u32 { + value.b + } +} + +struct Reader; + +impl ReadWhatever for Reader {} + +fn main() { + let s = S { + a: 0, + b: 0 + }; + + let _ = s.get_a(); + let _ = s.get_b(); + + let t = T { + a: 0, + b: 0 + }; + + let _ = t.get_a(); + let _ = t.get_b(); + + let square = Square { width: 0, height: 0 }; + let _ = square.width(); + let _ = square.height(); + + let u = U { a: 0, b:0 }; + let _ = foo(&u); + let _ = bar(&u); + + let whatever = Whatever { a: 0, b: 0 }; + let reader = Reader; + + let _ = reader.read_a(&whatever); + let _ = reader.read_b(&whatever); +} diff --git a/tests/ui/lint/dead-code/trivial-field-reads-valid-targets.stderr b/tests/ui/lint/dead-code/trivial-field-reads-valid-targets.stderr new file mode 100644 index 0000000000000..e1de46a00fb57 --- /dev/null +++ b/tests/ui/lint/dead-code/trivial-field-reads-valid-targets.stderr @@ -0,0 +1,41 @@ +error: field `a` is never read + --> $DIR/trivial-field-reads-method.rs:13:5 + | +LL | struct S { + | - field in this struct +LL | a: u32, + | ^ + | + = note: `S` has a derived impl for the trait `Access`, but this is intentionally ignored during dead code analysis +note: the lint level is defined here + --> $DIR/trivial-field-reads-method.rs:5:9 + | +LL | #![deny(dead_code)] + | ^^^^^^^^^ + +error: field `width` is never read + --> $DIR/trivial-field-reads-method.rs:44:5 + | +LL | struct Square { + | ------ field in this struct +LL | width: u32, + | ^^^^^ + +error: field `a` is never read + --> $DIR/trivial-field-reads-method.rs:60:5 + | +LL | struct U { + | - field in this struct +LL | a: u32, + | ^ + +error: field `a` is never read + --> $DIR/trivial-field-reads-method.rs:74:5 + | +LL | struct Whatever { + | -------- field in this struct +LL | a: u32, + | ^ + +error: aborting due to 4 previous errors + From f4e1a1d5326fc201aad40a4369d4e9472b2f5803 Mon Sep 17 00:00:00 2001 From: Jose Torres Date: Sun, 23 Aug 2026 22:20:55 -0400 Subject: [PATCH 17/22] remove old named tests --- .../dead-code/trivial-field-reads-method.rs | 123 ------------------ .../trivial-field-reads-method.stderr | 41 ------ 2 files changed, 164 deletions(-) delete mode 100644 tests/ui/lint/dead-code/trivial-field-reads-method.rs delete mode 100644 tests/ui/lint/dead-code/trivial-field-reads-method.stderr diff --git a/tests/ui/lint/dead-code/trivial-field-reads-method.rs b/tests/ui/lint/dead-code/trivial-field-reads-method.rs deleted file mode 100644 index c97336ed12c23..0000000000000 --- a/tests/ui/lint/dead-code/trivial-field-reads-method.rs +++ /dev/null @@ -1,123 +0,0 @@ -//! Checks that `#[rustc_trivial_field_reads]` on the allowed targets -//! (issue #160621) - -#![feature(rustc_attrs)] -#![deny(dead_code)] - -trait Access { - fn get_a(&self) -> u32; - fn get_b(&self) -> u32; -} - -struct S { - a: u32, //~ ERROR field `a` is never read - b: u32 -} - -impl Access for S { - #[rustc_trivial_field_reads] - fn get_a(&self) -> u32 { - self.a - } - - fn get_b(&self) -> u32 { - self.b - } -} - -struct T { - a: u32, - b: u32 -} - -impl Access for T { - fn get_a(&self) -> u32 { - self.a - } - - fn get_b(&self) -> u32 { - self.b - } -} - -struct Square { - width: u32, //~ ERROR field `width` is never read - height: u32 -} - -impl Square { - #[rustc_trivial_field_reads] - fn width(&self) -> u32 { - self.width - } - - fn height(&self) -> u32 { - self.height - } -} - -struct U { - a: u32, //~ ERROR field `a` is never read - b: u32 -} - -#[rustc_trivial_field_reads] -fn foo(u: &U) -> u32 { - u.a -} - -fn bar(u: &U) -> u32 { - u.b -} - -struct Whatever { - a: u32, //~ ERROR field `a` is never read - b: u32, -} - -trait ReadWhatever { - #[rustc_trivial_field_reads] - fn read_a(&self, value: &Whatever) -> u32 { - value.a - } - - fn read_b(&self, value: &Whatever) -> u32 { - value.b - } -} - -struct Reader; - -impl ReadWhatever for Reader {} - -fn main() { - let s = S { - a: 0, - b: 0 - }; - - let _ = s.get_a(); - let _ = s.get_b(); - - let t = T { - a: 0, - b: 0 - }; - - let _ = t.get_a(); - let _ = t.get_b(); - - let square = Square { width: 0, height: 0 }; - let _ = square.width(); - let _ = square.height(); - - let u = U { a: 0, b:0 }; - let _ = foo(&u); - let _ = bar(&u); - - let whatever = Whatever { a: 0, b: 0 }; - let reader = Reader; - - let _ = reader.read_a(&whatever); - let _ = reader.read_b(&whatever); -} diff --git a/tests/ui/lint/dead-code/trivial-field-reads-method.stderr b/tests/ui/lint/dead-code/trivial-field-reads-method.stderr deleted file mode 100644 index e1de46a00fb57..0000000000000 --- a/tests/ui/lint/dead-code/trivial-field-reads-method.stderr +++ /dev/null @@ -1,41 +0,0 @@ -error: field `a` is never read - --> $DIR/trivial-field-reads-method.rs:13:5 - | -LL | struct S { - | - field in this struct -LL | a: u32, - | ^ - | - = note: `S` has a derived impl for the trait `Access`, but this is intentionally ignored during dead code analysis -note: the lint level is defined here - --> $DIR/trivial-field-reads-method.rs:5:9 - | -LL | #![deny(dead_code)] - | ^^^^^^^^^ - -error: field `width` is never read - --> $DIR/trivial-field-reads-method.rs:44:5 - | -LL | struct Square { - | ------ field in this struct -LL | width: u32, - | ^^^^^ - -error: field `a` is never read - --> $DIR/trivial-field-reads-method.rs:60:5 - | -LL | struct U { - | - field in this struct -LL | a: u32, - | ^ - -error: field `a` is never read - --> $DIR/trivial-field-reads-method.rs:74:5 - | -LL | struct Whatever { - | -------- field in this struct -LL | a: u32, - | ^ - -error: aborting due to 4 previous errors - From 52bd2b33497a7c2869fba9d06db756f43cba263d Mon Sep 17 00:00:00 2001 From: Jose Torres Date: Sun, 23 Aug 2026 22:23:19 -0400 Subject: [PATCH 18/22] formatting --- tests/ui/lint/dead-code/trivial-field-reads-invalid-targets.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/lint/dead-code/trivial-field-reads-invalid-targets.rs b/tests/ui/lint/dead-code/trivial-field-reads-invalid-targets.rs index 34c65652516c1..f40ccab615511 100644 --- a/tests/ui/lint/dead-code/trivial-field-reads-invalid-targets.rs +++ b/tests/ui/lint/dead-code/trivial-field-reads-invalid-targets.rs @@ -20,4 +20,4 @@ trait Whatever1 {} fn main() { let _ = number!(); -} \ No newline at end of file +} From b8adfe0300d852b1c00ee1e5866d1ff4d3a685eb Mon Sep 17 00:00:00 2001 From: Jose Torres Date: Sun, 23 Aug 2026 22:34:39 -0400 Subject: [PATCH 19/22] move invalid targets test to attributes tests directory --- .../trivial-field-reads-invalid-targets.rs | 0 .../trivial-field-reads-invalid-targets.stderr | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename tests/ui/{lint/dead-code => attributes}/trivial-field-reads-invalid-targets.rs (100%) rename tests/ui/{lint/dead-code => attributes}/trivial-field-reads-invalid-targets.stderr (100%) diff --git a/tests/ui/lint/dead-code/trivial-field-reads-invalid-targets.rs b/tests/ui/attributes/trivial-field-reads-invalid-targets.rs similarity index 100% rename from tests/ui/lint/dead-code/trivial-field-reads-invalid-targets.rs rename to tests/ui/attributes/trivial-field-reads-invalid-targets.rs diff --git a/tests/ui/lint/dead-code/trivial-field-reads-invalid-targets.stderr b/tests/ui/attributes/trivial-field-reads-invalid-targets.stderr similarity index 100% rename from tests/ui/lint/dead-code/trivial-field-reads-invalid-targets.stderr rename to tests/ui/attributes/trivial-field-reads-invalid-targets.stderr From cf0f6ce7824aca0042251d49b63f03b5df3eadf3 Mon Sep 17 00:00:00 2001 From: Jose Torres Date: Sun, 23 Aug 2026 23:23:29 -0400 Subject: [PATCH 20/22] forget to bless tests after move and rename --- .../trivial-field-reads-invalid-targets.stderr | 6 +++--- .../dead-code/trivial-field-reads-valid-targets.stderr | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/ui/attributes/trivial-field-reads-invalid-targets.stderr b/tests/ui/attributes/trivial-field-reads-invalid-targets.stderr index 83cdc1e17ea8e..c93ffd40427e3 100644 --- a/tests/ui/attributes/trivial-field-reads-invalid-targets.stderr +++ b/tests/ui/attributes/trivial-field-reads-invalid-targets.stderr @@ -1,5 +1,5 @@ error: the `rustc_trivial_field_reads` attribute cannot be used on required trait methods - --> $DIR/trivial-field-reads-invalid-targets.rs:8:7 + --> $DIR/trivial-field-reads-invalid-targets.rs:7:7 | LL | #[rustc_trivial_field_reads] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,7 +7,7 @@ LL | #[rustc_trivial_field_reads] = help: the `rustc_trivial_field_reads` attribute can only be applied to functions with a body error: the `rustc_trivial_field_reads` attribute cannot be used on traits - --> $DIR/trivial-field-reads-invalid-targets.rs:12:3 + --> $DIR/trivial-field-reads-invalid-targets.rs:11:3 | LL | #[rustc_trivial_field_reads] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -15,7 +15,7 @@ LL | #[rustc_trivial_field_reads] = help: the `rustc_trivial_field_reads` attribute can only be applied to functions error: the `rustc_trivial_field_reads` attribute cannot be used on macro defs - --> $DIR/trivial-field-reads-invalid-targets.rs:15:3 + --> $DIR/trivial-field-reads-invalid-targets.rs:14:3 | LL | #[rustc_trivial_field_reads] | ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/lint/dead-code/trivial-field-reads-valid-targets.stderr b/tests/ui/lint/dead-code/trivial-field-reads-valid-targets.stderr index e1de46a00fb57..b5b86b70e46f1 100644 --- a/tests/ui/lint/dead-code/trivial-field-reads-valid-targets.stderr +++ b/tests/ui/lint/dead-code/trivial-field-reads-valid-targets.stderr @@ -1,5 +1,5 @@ error: field `a` is never read - --> $DIR/trivial-field-reads-method.rs:13:5 + --> $DIR/trivial-field-reads-valid-targets.rs:13:5 | LL | struct S { | - field in this struct @@ -8,13 +8,13 @@ LL | a: u32, | = note: `S` has a derived impl for the trait `Access`, but this is intentionally ignored during dead code analysis note: the lint level is defined here - --> $DIR/trivial-field-reads-method.rs:5:9 + --> $DIR/trivial-field-reads-valid-targets.rs:5:9 | LL | #![deny(dead_code)] | ^^^^^^^^^ error: field `width` is never read - --> $DIR/trivial-field-reads-method.rs:44:5 + --> $DIR/trivial-field-reads-valid-targets.rs:44:5 | LL | struct Square { | ------ field in this struct @@ -22,7 +22,7 @@ LL | width: u32, | ^^^^^ error: field `a` is never read - --> $DIR/trivial-field-reads-method.rs:60:5 + --> $DIR/trivial-field-reads-valid-targets.rs:60:5 | LL | struct U { | - field in this struct @@ -30,7 +30,7 @@ LL | a: u32, | ^ error: field `a` is never read - --> $DIR/trivial-field-reads-method.rs:74:5 + --> $DIR/trivial-field-reads-valid-targets.rs:74:5 | LL | struct Whatever { | -------- field in this struct From d1d3be221d7caee8bad0bcd0d355d45749e0b45a Mon Sep 17 00:00:00 2001 From: Jose Torres Date: Wed, 26 Aug 2026 18:14:06 -0400 Subject: [PATCH 21/22] restoring previous commit change, putting attr lookup in should_explore --- compiler/rustc_passes/src/dead.rs | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 512e5208282c3..6b17fb8d3a687 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -59,7 +59,7 @@ fn should_explore(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { | DefKind::ExternCrate | DefKind::Use | DefKind::Ctor(..) - | DefKind::ForeignMod => true, + | DefKind::ForeignMod => !find_attr!(tcx, def_id, RustcTrivialFieldReads), DefKind::TyParam | DefKind::ConstParam @@ -438,17 +438,19 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { continue; } - self.visit_node(id)?; + self.visit_node(self.tcx.hir_node_by_def_id(id))?; } ControlFlow::Continue(()) } - /// Records trait impls skipped so that we can note that its fields look unused in the error message - fn record_impl_item_to_ignore(&mut self, node: Node<'_>) { - if let Node::ImplItem(impl_item) = node - && let hir::ImplItemImplKind::Trait { .. } = impl_item.impl_kind + /// Items marked with `rustc_trivial_field_reads` + /// will be ignored for the purposes of dead code analysis (see PR #85200 + /// for discussion, and PR #160666). + fn should_ignore_impl_item(&mut self, impl_item: &hir::ImplItem<'_>) -> bool { + if let hir::ImplItemImplKind::Trait { .. } = impl_item.impl_kind && let impl_of = self.tcx.local_parent(impl_item.owner_id.def_id) + && find_attr!(self.tcx, impl_item.owner_id.def_id, RustcTrivialFieldReads) { let trait_ref = self.tcx.impl_trait_ref(impl_of).instantiate_identity().skip_norm_wip(); @@ -457,19 +459,19 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { { self.ignored_derived_traits.entry(adt_def_id).or_default().insert(trait_ref.def_id); } + return true; } + + return false; } fn visit_node( &mut self, - def_id: LocalDefId, + node: Node<'tcx>, ) -> as Visitor<'tcx>>::Result { - let node = self.tcx.hir_node_by_def_id(def_id); - - // Items marked with `rustc_trivial_field_reads` will be ignored for the purposes - // of dead code analysis (see PR #85200 for discussion, and PR #160666) - if find_attr!(self.tcx, def_id, RustcTrivialFieldReads) { - self.record_impl_item_to_ignore(node); + if let Node::ImplItem(impl_item) = node + && self.should_ignore_impl_item(impl_item) + { return ControlFlow::Continue(()); } From 42b9de36f2553d4814fa8c929ed7375f9bd46b40 Mon Sep 17 00:00:00 2001 From: Jose Torres Date: Sat, 5 Sep 2026 10:59:36 -0400 Subject: [PATCH 22/22] fixing rebase --- tests/ui/derives/deriving-all-codegen.stdout | 48 -------------------- 1 file changed, 48 deletions(-) diff --git a/tests/ui/derives/deriving-all-codegen.stdout b/tests/ui/derives/deriving-all-codegen.stdout index 79f6505479e65..d6d9c25cecd35 100644 --- a/tests/ui/derives/deriving-all-codegen.stdout +++ b/tests/ui/derives/deriving-all-codegen.stdout @@ -1272,54 +1272,6 @@ impl ::core::cmp::Ord for Fieldless { } } -<<<<<<< HEAD -======= -// A C-like, fieldless enum with variants of varying name lengths. -enum Fieldless0 { A, BBB, CC, } -#[automatically_derived] -impl ::core::fmt::Debug for Fieldless0 { - #[inline] - #[rustc_trivial_field_reads] - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - ::core::fmt::Formatter::write_str(f, - match self { - Fieldless0::A => "A", - Fieldless0::BBB => "BBB", - Fieldless0::CC => "CC", - }) - } -} - -// A C-like, fieldless enum with 10 variants. -enum Fieldless10 { - AAAAA, - BBBB, - CC, - DDDDDDDD, - E, - FFFFFFFFFFFFF, - GGGGGG, - Hatsune, - IIIIIII, - JJJJJJJJJ, -} -#[automatically_derived] -impl ::core::fmt::Debug for Fieldless10 { - #[inline] - #[rustc_trivial_field_reads] - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - static __NAMES: &str = - "AAAAABBBBCCDDDDDDDDEFFFFFFFFFFFFFGGGGGGHatsuneIIIIIIIJJJJJJJJJ"; - static __OFFSET: [usize; 11] = - [0usize, 5usize, 9usize, 11usize, 19usize, 20usize, 33usize, - 39usize, 46usize, 53usize, 62usize]; - let __d = ::core::intrinsics::discriminant_value(self) as usize; - ::core::fmt::Formatter::debug_c_like_enum_write_str(f, __NAMES, - &__OFFSET, __d) - } -} - ->>>>>>> 7196365306e (blessing deriving all codegen test) // An enum with multiple fieldless and fielded variants. enum Mixed {