From 57488f79f80ca41343bcfc43ea3f29e9757619b2 Mon Sep 17 00:00:00 2001 From: Mahdi Ali-Raihan Date: Fri, 22 May 2026 15:37:48 -0400 Subject: [PATCH] Add a clippy attribute #[clippy::no_dead_field_warning] to prevent certain types from propagating dead field warnings (e.g. PhantomData, PhantomPinned) --- clippy_lints/src/missing_fields_in_debug.rs | 7 +++++-- clippy_lints/src/pub_underscore_fields.rs | 5 +++-- clippy_utils/src/attrs.rs | 15 +++++++++++++++ clippy_utils/src/sym.rs | 1 + 4 files changed, 24 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/missing_fields_in_debug.rs b/clippy_lints/src/missing_fields_in_debug.rs index 332c19e140b3..27f4f1a1da9b 100644 --- a/clippy_lints/src/missing_fields_in_debug.rs +++ b/clippy_lints/src/missing_fields_in_debug.rs @@ -2,13 +2,13 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::{MaybeDef, MaybeResPath}; -use clippy_utils::sym; use clippy_utils::visitors::{Visitable, for_each_expr}; +use clippy_utils::{is_clippy_no_dead_field_warning_attr, sym}; use rustc_ast::LitKind; use rustc_data_structures::fx::FxHashSet; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Block, Expr, ExprKind, Impl, Item, ItemKind, LangItem, Node, QPath, TyKind, VariantData}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty::{Ty, TypeckResults}; use rustc_session::declare_lint_pass; use rustc_span::{Span, Symbol}; @@ -185,6 +185,9 @@ fn check_struct<'tcx>( .filter_map(|field| { if field_accesses.contains(&field.ident.name) || field.ty.basic_res().is_lang_item(cx, LangItem::PhantomData) + // We exclude certain types (e.g. PhantomData, PhantomPinned) marked with + // this attribute + || is_clippy_no_dead_field_warning_attr(cx.sess(), cx.tcx, field.ty.basic_res()) { None } else { diff --git a/clippy_lints/src/pub_underscore_fields.rs b/clippy_lints/src/pub_underscore_fields.rs index 64724f7d01d0..4278070ecbfb 100644 --- a/clippy_lints/src/pub_underscore_fields.rs +++ b/clippy_lints/src/pub_underscore_fields.rs @@ -1,10 +1,10 @@ use clippy_config::Conf; use clippy_config::types::PubUnderscoreFieldsBehaviour; -use clippy_utils::attrs::is_doc_hidden; +use clippy_utils::attrs::{is_clippy_no_dead_field_warning_attr, is_doc_hidden}; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::res::{MaybeDef, MaybeResPath}; use rustc_hir::{FieldDef, Item, ItemKind, LangItem}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; declare_clippy_lint! { @@ -77,6 +77,7 @@ impl<'tcx> LateLintPass<'tcx> for PubUnderscoreFields { && !is_doc_hidden(cx.tcx.hir_attrs(field.hir_id)) // We ignore fields that are `PhantomData`. && !field.ty.basic_res().is_lang_item(cx, LangItem::PhantomData) + && !is_clippy_no_dead_field_warning_attr(cx.sess(), cx.tcx, field.ty.basic_res()) { span_lint_hir_and_then( cx, diff --git a/clippy_utils/src/attrs.rs b/clippy_utils/src/attrs.rs index 6e7434771369..30a6b812f39e 100644 --- a/clippy_utils/src/attrs.rs +++ b/clippy_utils/src/attrs.rs @@ -4,6 +4,7 @@ use crate::source::SpanRangeExt; use crate::{sym, tokenize_with_text}; use rustc_ast::attr::AttributeExt; use rustc_errors::Applicability; +use rustc_hir::def::Res; use rustc_hir::find_attr; use rustc_lexer::TokenKind; use rustc_lint::LateContext; @@ -91,6 +92,20 @@ pub fn is_doc_hidden(attrs: &[impl AttributeExt]) -> bool { attrs.iter().any(AttributeExt::is_doc_hidden) } +/// Checks whether the original type is marked as `#[clippy::no_dead_field_warning]` +pub fn is_clippy_no_dead_field_warning_attr(sess: &Session, tcx: TyCtxt<'_>, res: &Res) -> bool { + res.opt_def_id().is_some_and(|def_id| { + get_builtin_attr( + sess, + #[allow(deprecated)] + tcx.get_all_attrs(def_id), + sym::no_dead_field_warning, + ) + .next() + .is_some() + }) +} + /// Checks whether the given ADT, or any of its fields/variants, are marked as `#[non_exhaustive]` pub fn has_non_exhaustive_attr(tcx: TyCtxt<'_>, adt: AdtDef<'_>) -> bool { adt.is_variant_list_non_exhaustive() diff --git a/clippy_utils/src/sym.rs b/clippy_utils/src/sym.rs index 6a7637fb1858..53e714563290 100644 --- a/clippy_utils/src/sym.rs +++ b/clippy_utils/src/sym.rs @@ -440,6 +440,7 @@ generate! { next_if_eq, next_multiple_of, next_tuple, + no_dead_field_warning, nth, ok, ok_or,