From 2516db265aa1087194f4c4b4943fc88284971ddf Mon Sep 17 00:00:00 2001 From: Jose Torres Date: Thu, 6 Aug 2026 21:46:57 -0400 Subject: [PATCH 01/21] 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 dddd61e9f0ba0..21ba017cc6571 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -474,7 +474,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 6e0669840d48c..81c4d3d22d3eb 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -438,11 +438,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 f124b8bceaded..868fb1b38802e 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 6a4c58afc16f3..785eb6ffb4f2e 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 2ae71e8753287eb96c1a4e80d23ec44cca0f3841 Mon Sep 17 00:00:00 2001 From: Jose Torres Date: Thu, 6 Aug 2026 21:52:58 -0400 Subject: [PATCH 02/21] 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 21ba017cc6571..5f989b6058020 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -474,7 +474,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 aa42902b15db3273f4ac0f1ba3c1865b21e66717 Mon Sep 17 00:00:00 2001 From: Jose Torres Date: Tue, 11 Aug 2026 22:25:13 -0400 Subject: [PATCH 03/21] 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 5f989b6058020..30c58f9381b5c 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -474,8 +474,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 af3db65bd0a63..6bbc24568a86c 100644 --- a/compiler/rustc_builtin_macros/src/deriving/clone.rs +++ b/compiler/rustc_builtin_macros/src/deriving/clone.rs @@ -104,7 +104,10 @@ pub(crate) fn expand_deriving_clone( explicit_self: true, nonself_args: Vec::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 81c4d3d22d3eb..6241f3e6f1c83 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -436,10 +436,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 868fb1b38802e..6ee9d0ae77cfc 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 b24b06a96d9957e5bcd67201fc716183b0d66027 Mon Sep 17 00:00:00 2001 From: Jose Torres Date: Wed, 12 Aug 2026 21:33:55 -0400 Subject: [PATCH 04/21] 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 e49f2ac2923ec6b798810b284c5c9bd8f861fc6a Mon Sep 17 00:00:00 2001 From: Jose Torres Date: Wed, 12 Aug 2026 22:00:00 -0400 Subject: [PATCH 05/21] 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 c41a4e4fd6b8d..c667cfbdfb76e 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: vec![(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(Box::new(|a, b, c| { diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index 785eb6ffb4f2e..d5c0043d52565 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 d60acd11fa09e12950dfb9d15feaf7624ff2a55d Mon Sep 17 00:00:00 2001 From: Jose Torres Date: Wed, 12 Aug 2026 22:05:46 -0400 Subject: [PATCH 06/21] 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 30c58f9381b5c..cf995fb3b54c1 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -474,10 +474,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 6241f3e6f1c83..7f173eea8c98b 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -436,18 +436,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 efc60e4457396afa0f4245fbf72d0d7ce757444d Mon Sep 17 00:00:00 2001 From: Jose Torres Date: Wed, 12 Aug 2026 22:23:23 -0400 Subject: [PATCH 07/21] 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 7f173eea8c98b..c397625ec177a 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -438,9 +438,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 938133cc7f2e9f09c61d58d04c3ad169b8a91442 Mon Sep 17 00:00:00 2001 From: Jose Torres Date: Wed, 19 Aug 2026 18:39:38 -0400 Subject: [PATCH 08/21] 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 c397625ec177a..fd6633476e1a6 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -438,10 +438,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 6fa2a60f17704aaff323f15a6b5138476fedcf48 Mon Sep 17 00:00:00 2001 From: Jose Torres Date: Wed, 19 Aug 2026 18:47:15 -0400 Subject: [PATCH 09/21] 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 d0b387dbc8b25..66fb53389978e 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 { @@ -1304,6 +1333,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>; @@ -1316,6 +1346,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"), @@ -1490,6 +1521,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) => @@ -1504,6 +1536,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) => @@ -1598,6 +1631,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) => @@ -1615,6 +1649,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) => @@ -1749,6 +1784,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 @@ -1766,6 +1802,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 @@ -1779,6 +1816,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 @@ -1794,6 +1832,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 @@ -1811,6 +1850,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 32cef99dc393c2b115f16088c667d0b1bab8e12b Mon Sep 17 00:00:00 2001 From: Jose Torres Date: Wed, 19 Aug 2026 18:52:19 -0400 Subject: [PATCH 10/21] 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 fd6633476e1a6..39b2422ee136c 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -432,9 +432,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 d1cc7e145f77db53d44150bbf057c30d67f4eabf Mon Sep 17 00:00:00 2001 From: Jose Torres Date: Wed, 19 Aug 2026 19:56:35 -0400 Subject: [PATCH 11/21] 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 7196365306e91976224d1e3e1e304fbb2546df5c Mon Sep 17 00:00:00 2001 From: Jose Torres Date: Wed, 19 Aug 2026 20:58:42 -0400 Subject: [PATCH 12/21] blessing deriving all codegen test --- tests/ui/derives/deriving-all-codegen.stdout | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/ui/derives/deriving-all-codegen.stdout b/tests/ui/derives/deriving-all-codegen.stdout index 66fb53389978e..657269e734e3a 100644 --- a/tests/ui/derives/deriving-all-codegen.stdout +++ b/tests/ui/derives/deriving-all-codegen.stdout @@ -1277,6 +1277,7 @@ 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 { @@ -1303,6 +1304,7 @@ enum Fieldless10 { #[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"; From 7173d6f33771333fa8f6d91c999d7a218b3ef18f Mon Sep 17 00:00:00 2001 From: Jose Torres Date: Sat, 22 Aug 2026 22:15:06 -0400 Subject: [PATCH 13/21] 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 cf995fb3b54c1..8bc425c2165d9 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -474,8 +474,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 39b2422ee136c..93c6f078f2fe9 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 @@ -426,7 +426,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { continue; } - self.visit_node(self.tcx.hir_node_by_def_id(id))?; + self.visit_node(id)?; } ControlFlow::Continue(()) @@ -436,9 +436,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(); @@ -447,16 +450,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 4f4c22ad3a240dbec2f90373a77d31d6c28267fa Mon Sep 17 00:00:00 2001 From: Jose Torres Date: Sun, 23 Aug 2026 17:52:14 -0400 Subject: [PATCH 14/21] 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 8bc425c2165d9..0251d98e619a5 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -477,6 +477,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 93c6f078f2fe9..e399477b15e53 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 @@ -435,12 +435,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(); @@ -451,8 +448,6 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { self.ignored_derived_traits.entry(adt_def_id).or_default().insert(trait_ref.def_id); } } - - true } fn visit_node( @@ -461,9 +456,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 e496b2d480e7c8657d19110604e6f76d74f010ff Mon Sep 17 00:00:00 2001 From: Jose Torres Date: Sun, 23 Aug 2026 18:58:36 -0400 Subject: [PATCH 15/21] 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 0251d98e619a5..254cb753d3a17 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -475,9 +475,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 e399477b15e53..8f579c4b0fae0 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -432,10 +432,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) @@ -456,8 +454,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 3f535a85aa8011ec86b02167c63b10dd00fbdf5f Mon Sep 17 00:00:00 2001 From: Jose Torres Date: Sun, 23 Aug 2026 22:20:06 -0400 Subject: [PATCH 16/21] 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 009892d8d1d7d542b8a5bfb758e93038d2a4fe56 Mon Sep 17 00:00:00 2001 From: Jose Torres Date: Sun, 23 Aug 2026 22:20:55 -0400 Subject: [PATCH 17/21] 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 7f12f2faa1cffe5d1542d2361b93615bccfe2a8e Mon Sep 17 00:00:00 2001 From: Jose Torres Date: Sun, 23 Aug 2026 22:23:19 -0400 Subject: [PATCH 18/21] 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 95eef6e52cf5da8dcc31e1445381c361869c2791 Mon Sep 17 00:00:00 2001 From: Jose Torres Date: Sun, 23 Aug 2026 22:34:39 -0400 Subject: [PATCH 19/21] 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 a0e5d96a1008dee900c1a0b7fb9dc1922d7b6a79 Mon Sep 17 00:00:00 2001 From: Jose Torres Date: Sun, 23 Aug 2026 23:23:29 -0400 Subject: [PATCH 20/21] 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 8112d198d751ff71b875e3014b065cd2fc2e92d6 Mon Sep 17 00:00:00 2001 From: Jose Torres Date: Wed, 26 Aug 2026 18:14:06 -0400 Subject: [PATCH 21/21] 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 8f579c4b0fae0..824d9d940e81a 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 @@ -426,17 +426,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(); @@ -445,19 +447,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(()); }