diff --git a/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs b/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs index 76bddacd20bf4..4233075d8858b 100644 --- a/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs +++ b/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs @@ -14,6 +14,18 @@ impl NoArgsAttributeParser for RustcAsPtrParser { const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcAsPtr; } +pub(crate) struct RustcNoDeadCodeWarningParser; +impl NoArgsAttributeParser for RustcNoDeadCodeWarningParser { + const PATH: &[Symbol] = &[sym::rustc_no_dead_code_warning]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + Allow(Target::Struct), + Allow(Target::Enum), + Allow(Target::TyAlias), + ]); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcNoDeadCodeWarning; +} + pub(crate) struct RustcPubTransparentParser; impl NoArgsAttributeParser for RustcPubTransparentParser { const PATH: &[Symbol] = &[sym::rustc_pub_transparent]; diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 8d92ec50e10c4..0380df545892c 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -313,6 +313,7 @@ attribute_parsers!( Single>, Single>, Single>, + Single>, Single>, Single>, Single>, diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 053caee258f7b..9699f8f92517b 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -746,6 +746,11 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_no_writable, "`#[rustc_no_writable]` stops the compiler from considering mutable reference arguments of this function as implicitly writable" ), + rustc_attr!( + rustc_no_dead_code_warning, + "`#[rustc_no_dead_code_warning]` attribute is used to mark certain types that do not need \ + to propagate dead code warnings as field members in other types" + ), // ========================================================================== // Internal attributes, Testing: diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 05f398058ecaa..27f9b71dd9ac3 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -1497,6 +1497,9 @@ pub enum AttributeKind { diverging_block_default: Option, }, + /// Represents `#[rustc_no_dead_code_warning]` + RustcNoDeadCodeWarning, + /// Represents `#[rustc_no_implicit_autorefs]` RustcNoImplicitAutorefs, diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index ad4d0728888bf..3d780be628c66 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -160,6 +160,7 @@ impl AttributeKind { RustcMustMatchExhaustively(..) => Yes, RustcNeverReturnsNullPtr => Yes, RustcNeverTypeOptions { .. } => No, + RustcNoDeadCodeWarning => Yes, RustcNoImplicitAutorefs => Yes, RustcNoImplicitBounds => No, RustcNoMirInline => Yes, diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 24e9b71de5914..a141f56be47f2 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -347,6 +347,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::RustcMustMatchExhaustively(..) | AttributeKind::RustcNeverReturnsNullPtr | AttributeKind::RustcNeverTypeOptions {..} + | AttributeKind::RustcNoDeadCodeWarning | AttributeKind::RustcNoImplicitAutorefs | AttributeKind::RustcNoImplicitBounds | AttributeKind::RustcNoMirInline diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 981bfed363dcc..e95831d5a42b3 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1766,6 +1766,7 @@ symbols! { rustc_must_match_exhaustively, rustc_never_returns_null_ptr, rustc_never_type_options, + rustc_no_dead_code_warning, rustc_no_implicit_autorefs, rustc_no_implicit_bounds, rustc_no_mir_inline, diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index f56a4d7308e90..840e2ae216af0 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -808,6 +808,7 @@ impl !Sync for *mut T {} /// [drop check]: Drop#drop-check #[lang = "phantom_data"] #[stable(feature = "rust1", since = "1.0.0")] +#[rustc_no_dead_code_warning] pub struct PhantomData; #[stable(feature = "rust1", since = "1.0.0")] @@ -1023,6 +1024,7 @@ pub auto trait Unpin {} // will likely eventually be deprecated, and all new code should be using `UnsafePinned` instead. #[stable(feature = "pin", since = "1.33.0")] #[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +#[rustc_no_dead_code_warning] pub struct PhantomPinned; #[stable(feature = "pin", since = "1.33.0")] diff --git a/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs b/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs index 332c19e140b3e..c3f29f13fdc71 100644 --- a/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs +++ b/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs @@ -2,12 +2,12 @@ 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::{is_rustc_no_dead_code_warning_attr, sym}; use clippy_utils::visitors::{Visitable, for_each_expr}; 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_hir::{Block, Expr, ExprKind, Impl, Item, ItemKind, Node, QPath, TyKind, VariantData}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{Ty, TypeckResults}; use rustc_session::declare_lint_pass; @@ -184,7 +184,8 @@ fn check_struct<'tcx>( .iter() .filter_map(|field| { if field_accesses.contains(&field.ident.name) - || field.ty.basic_res().is_lang_item(cx, LangItem::PhantomData) + || is_rustc_no_dead_code_warning_attr(cx.tcx, field.ty.basic_res()) + // We exclude certain types (e.g. PhantomData, PhantomPinned) marked with { None } else { diff --git a/src/tools/clippy/clippy_lints/src/pub_underscore_fields.rs b/src/tools/clippy/clippy_lints/src/pub_underscore_fields.rs index 64724f7d01d00..58273306f61c0 100644 --- a/src/tools/clippy/clippy_lints/src/pub_underscore_fields.rs +++ b/src/tools/clippy/clippy_lints/src/pub_underscore_fields.rs @@ -1,9 +1,9 @@ use clippy_config::Conf; use clippy_config::types::PubUnderscoreFieldsBehaviour; -use clippy_utils::attrs::is_doc_hidden; +use clippy_utils::attrs::{is_doc_hidden, is_rustc_no_dead_code_warning_attr}; use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::res::{MaybeDef, MaybeResPath}; -use rustc_hir::{FieldDef, Item, ItemKind, LangItem}; +use clippy_utils::res::{MaybeResPath}; +use rustc_hir::{FieldDef, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; @@ -76,7 +76,7 @@ impl<'tcx> LateLintPass<'tcx> for PubUnderscoreFields { // We ignore fields that have `#[doc(hidden)]`. && !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_rustc_no_dead_code_warning_attr(cx.tcx, field.ty.basic_res()) { span_lint_hir_and_then( cx, diff --git a/src/tools/clippy/clippy_utils/src/attrs.rs b/src/tools/clippy/clippy_utils/src/attrs.rs index 6e74347713695..d3344674fedd1 100644 --- a/src/tools/clippy/clippy_utils/src/attrs.rs +++ b/src/tools/clippy/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,11 @@ pub fn is_doc_hidden(attrs: &[impl AttributeExt]) -> bool { attrs.iter().any(AttributeExt::is_doc_hidden) } +/// Checks whether the original type is marked as `#[rustc_no_dead_code_warning]` +pub fn is_rustc_no_dead_code_warning_attr(tcx: TyCtxt<'_>, res: &Res) -> bool { + res.opt_def_id().map(|def_id| find_attr!(tcx, def_id, RustcNoDeadCodeWarning)).unwrap_or(false) +} + /// 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()