From 192ce21f9a25e6373e2891fd6db0cc70169d9cfc Mon Sep 17 00:00:00 2001 From: Obei Sideg Date: Sun, 28 Jun 2026 14:51:39 +0300 Subject: [PATCH 1/3] Make parsed attributes available in `finalize_check` the parsed attributes of an item were only fully populated after all finalizers had run, so `finalize_check` (run during finalization) could only inspect attribute *paths* via `FinalizeContext::all_attrs`, not the parsed `AttributeKind`s. split finalization into two passes: first run all finalizers to produce the parsed attributes, then run the cross-attribute checks. The checks are deferred via a new `AttributeParser::deferred_finalize_check`, and can now inspect the fully parsed attributes through a new `FinalizeContext::parsed_attrs` field. --- .../rustc_attr_parsing/src/attributes/mod.rs | 40 ++++++++++++++----- compiler/rustc_attr_parsing/src/context.rs | 36 +++++++++++++++-- compiler/rustc_attr_parsing/src/interface.rs | 38 ++++++++++++++++-- 3 files changed, 97 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index d3ddd79a97619..f1122779eecc0 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -26,7 +26,7 @@ use rustc_span::edition::Edition; use rustc_span::{Span, Symbol}; use thin_vec::ThinVec; -use crate::context::{AcceptContext, FinalizeContext}; +use crate::context::{AcceptContext, FinalizeCheckFn, FinalizeContext}; use crate::parser::ArgParser; use crate::session_diagnostics::UnusedMultiple; use crate::target_checking::AllowedTargets; @@ -117,6 +117,20 @@ pub(crate) trait AttributeParser: Default + 'static { /// every single syntax item that could have attributes applied to it. /// Your accept mappings should determine whether this returns something. fn finalize(self, cx: &FinalizeContext<'_, '_>) -> Option; + + /// If this parser produced an attribute, optionally returns a cross-attribute check + /// to run once *all* attributes on the item have been finalized, together with the + /// span it should be reported at. + /// + /// Running after finalization means the check can inspect the fully parsed attributes + /// via [`FinalizeContext::parsed_attrs`], which are not yet all available during + /// [`finalize`](Self::finalize). This is queried right before `finalize` consumes the + /// parser state. + /// + /// Defaults to no check. + fn deferred_finalize_check(&self) -> Option<(FinalizeCheckFn, Span)> { + None + } } /// Alternative to [`AttributeParser`] that automatically handles state management. @@ -185,11 +199,15 @@ impl AttributeParser for Single { const ALLOWED_TARGETS: AllowedTargets<'_> = T::ALLOWED_TARGETS; const SAFETY: AttributeSafety = T::SAFETY; - fn finalize(self, cx: &FinalizeContext<'_, '_>) -> Option { - let (kind, span) = self.1?; - T::finalize_check(cx, span); + fn finalize(self, _cx: &FinalizeContext<'_, '_>) -> Option { + let (kind, _span) = self.1?; Some(kind) } + + fn deferred_finalize_check(&self) -> Option<(FinalizeCheckFn, Span)> { + let (_, span) = self.1.as_ref()?; + Some((::finalize_check, *span)) + } } pub(crate) enum OnDuplicate { @@ -375,12 +393,12 @@ impl AttributeParser for Combine { const ALLOWED_TARGETS: AllowedTargets<'_> = T::ALLOWED_TARGETS; const SAFETY: AttributeSafety = T::SAFETY; - fn finalize(self, cx: &FinalizeContext<'_, '_>) -> Option { - if let Some(first_span) = self.first_span { - T::finalize_check(cx, first_span); - Some(T::CONVERT(self.items, first_span)) - } else { - None - } + fn finalize(self, _cx: &FinalizeContext<'_, '_>) -> Option { + let first_span = self.first_span?; + Some(T::CONVERT(self.items, first_span)) + } + + fn deferred_finalize_check(&self) -> Option<(FinalizeCheckFn, Span)> { + Some((::finalize_check, self.first_span?)) } } diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index efa43562aeeca..dd8a77fb38c4f 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -12,8 +12,8 @@ use rustc_ast::{AttrStyle, MetaItemLit, Safety}; use rustc_data_structures::sync::{DynSend, DynSync}; use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, Level, MultiSpan}; use rustc_feature::AttributeStability; -use rustc_hir::AttrPath; use rustc_hir::attrs::AttributeKind; +use rustc_hir::{AttrPath, Attribute}; use rustc_parse::parser::Recovery; use rustc_session::Session; use rustc_session::lint::{Lint, LintId}; @@ -94,7 +94,24 @@ pub(super) struct GroupTypeInnerAccept { pub(crate) type AcceptFn = Box Fn(&mut AcceptContext<'_, 'sess>, &ArgParser) + Send + Sync>; -pub(crate) type FinalizeFn = fn(&mut FinalizeContext<'_, '_>) -> Option; + +pub(crate) type FinalizeFn = fn(&mut FinalizeContext<'_, '_>) -> FinalizeOutput; + +/// A cross-attribute check that runs *after* all attributes on an item have been +/// finalized, so it can inspect the fully parsed attributes via +/// [`FinalizeContext::parsed_attrs`]. The [`Span`] is the span of the attribute the +/// check is associated with, used for diagnostics. +pub(crate) type FinalizeCheckFn = fn(&FinalizeContext<'_, '_>, Span); + +/// The result of finalizing a single attribute parser. +pub(crate) struct FinalizeOutput { + /// The attribute the parser produced, if any. + pub(crate) attr: Option, + /// A check to run once *all* attributes on the item have been finalized, together + /// with the span it should be reported at. Deferred so that it can inspect the fully + /// parsed attributes via [`FinalizeContext::parsed_attrs`]. + pub(crate) deferred_check: Option<(FinalizeCheckFn, Span)>, +} macro_rules! attribute_parsers { ( @@ -123,7 +140,11 @@ macro_rules! attribute_parsers { allowed_targets: <$names as crate::attributes::AttributeParser>::ALLOWED_TARGETS, finalizer: |cx| { let state = STATE_OBJECT.take(); - state.finalize(cx) + // Compute the deferred check (if any) before consuming + // the state in `finalize`. + let deferred_check = state.deferred_finalize_check(); + let attr = state.finalize(cx); + FinalizeOutput { attr, deferred_check } } }); } @@ -776,6 +797,15 @@ pub(crate) struct FinalizeContext<'p, 'sess> { /// Usually, you should use normal attribute parsing logic instead, /// especially when making a *denylist* of other attributes. pub(crate) all_attrs: &'p [RefPathParser<'p>], + + /// All attributes that have been parsed on this syntax node. + /// + /// Unlike [`all_attrs`](Self::all_attrs), which only contains the *paths* of the + /// attributes, this contains the fully parsed attributes. It is only populated when + /// running the deferred `finalize_check`s, which happen after all attributes on the + /// item have been finalized. During finalization itself this is empty, since the + /// attributes are not all available yet. + pub(crate) parsed_attrs: &'p [Attribute], } impl<'p, 'sess: 'p> Deref for FinalizeContext<'p, 'sess> { diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index 54ba6c189b544..0fc38bfe65114 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -18,7 +18,8 @@ use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym}; use crate::attributes::AttributeSafety; use crate::context::{ - ATTRIBUTE_PARSERS, AcceptContext, FinalizeContext, FinalizeFn, SharedContext, + ATTRIBUTE_PARSERS, AcceptContext, FinalizeCheckFn, FinalizeContext, FinalizeFn, FinalizeOutput, + SharedContext, }; use crate::parser::{AllowExprMetavar, ArgParser, PathParser, RefPathParser}; use crate::session_diagnostics::ParsedDescription; @@ -448,8 +449,14 @@ impl<'sess> AttributeParser<'sess> { } synthetic_attr_state.finalize_synthetic_attrs(&mut attributes); + + // First, run all finalizers to produce the parsed attributes. Cross-attribute + // checks that need to inspect the fully parsed attributes are deferred until all + // finalizers have run (see below), since the parsed attributes are not yet all + // available here. + let mut deferred_checks: Vec<(FinalizeCheckFn, Span)> = Vec::new(); for f in &finalizers { - if let Some(attr) = f(&mut FinalizeContext { + let FinalizeOutput { attr, deferred_check } = f(&mut FinalizeContext { shared: SharedContext { cx: self, target_span, @@ -459,9 +466,34 @@ impl<'sess> AttributeParser<'sess> { has_lint_been_emitted: AtomicBool::new(false), }, all_attrs: &attr_paths, - }) { + parsed_attrs: &[], + }); + if let Some(attr) = attr { attributes.push(Attribute::Parsed(attr)); } + if let Some(deferred_check) = deferred_check { + deferred_checks.push(deferred_check); + } + } + + // Now that all attributes have been parsed, run the deferred checks. These can + // inspect the fully parsed attributes via `FinalizeContext::parsed_attrs`. + for (check, attr_span) in deferred_checks { + check( + &FinalizeContext { + shared: SharedContext { + cx: self, + target_span, + target, + emit_lint: &mut emit_lint, + #[cfg(debug_assertions)] + has_lint_been_emitted: AtomicBool::new(false), + }, + all_attrs: &attr_paths, + parsed_attrs: &attributes, + }, + attr_span, + ); } if !matches!(self.should_emit, ShouldEmit::Nothing) && target == Target::WherePredicate { From d2777f44151aa8b3d5d6955a483237518806dea2 Mon Sep 17 00:00:00 2001 From: Obei Sideg Date: Sun, 28 Jun 2026 14:51:49 +0300 Subject: [PATCH 2/3] Move `check_rustc_pub_transparent` into attribute parser the check that `#[rustc_pub_transparent]` is only applied to `#[repr(transparent)]` types needs to see the parsed `Repr` attribute, so it now lives in `RustcPubTransparentParser::finalize_check`, using the freshly available `FinalizeContext::parsed_attrs`. --- .../src/attributes/lint_helpers.rs | 14 ++++++++++++++ .../rustc_attr_parsing/src/session_diagnostics.rs | 9 +++++++++ compiler/rustc_passes/src/check_attr.rs | 12 +----------- compiler/rustc_passes/src/diagnostics.rs | 9 --------- 4 files changed, 24 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs b/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs index 38afeea07794a..85665ad2bb2a8 100644 --- a/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs +++ b/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs @@ -1,6 +1,9 @@ use rustc_feature::AttributeStability; +use rustc_hir::attrs::ReprAttr; +use rustc_hir::find_attr; use super::prelude::*; +use crate::session_diagnostics::RustcPubTransparent; pub(crate) struct RustcAsPtrParser; impl NoArgsAttributeParser for RustcAsPtrParser { @@ -26,6 +29,17 @@ impl NoArgsAttributeParser for RustcPubTransparentParser { ]); const STABILITY: AttributeStability = unstable!(rustc_attrs); const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcPubTransparent; + + fn finalize_check(cx: &FinalizeContext<'_, '_>, attr_span: Span) { + // `#[rustc_pub_transparent]` may only be applied to `#[repr(transparent)]` types. + let is_transparent = find_attr!( + cx.parsed_attrs, + Repr { reprs, .. } if reprs.iter().any(|(r, _)| r == &ReprAttr::ReprTransparent) + ); + if !is_transparent { + cx.emit_err(RustcPubTransparent { span: cx.target_span, attr_span }); + } + } } pub(crate) struct RustcPassByValueParser; diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index cbcec3ac22231..a5f6e7afc3dcc 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -20,6 +20,15 @@ pub(crate) struct BothFfiConstAndPure { pub attr_span: Span, } +#[derive(Diagnostic)] +#[diag("attribute should be applied to `#[repr(transparent)]` types")] +pub(crate) struct RustcPubTransparent { + #[primary_span] + pub attr_span: Span, + #[label("not a `#[repr(transparent)]` type")] + pub span: Span, +} + #[derive(Diagnostic)] #[diag("{$attr_str} attribute cannot have empty value")] pub(crate) struct DocAliasEmpty<'a> { diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 3139c8746b95a..506f9a1e81327 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -207,9 +207,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { AttributeKind::RustcDumpObjectLifetimeDefaults => { self.check_dump_object_lifetime_defaults(hir_id); } - &AttributeKind::RustcPubTransparent(attr_span) => { - self.check_rustc_pub_transparent(attr_span, span, attrs) - } AttributeKind::Naked(..) => self.check_naked(hir_id, target), AttributeKind::TrackCaller(attr_span) => { self.check_track_caller(hir_id, *attr_span, attrs, target) @@ -386,6 +383,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { AttributeKind::RustcPassIndirectlyInNonRusticAbis(..) => (), AttributeKind::RustcPreserveUbChecks => (), AttributeKind::RustcProcMacroDecls => (), + AttributeKind::RustcPubTransparent(..) => (), AttributeKind::RustcReallocator => (), AttributeKind::RustcRegions => (), AttributeKind::RustcReservationImpl(..) => (), @@ -1592,14 +1590,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } - fn check_rustc_pub_transparent(&self, attr_span: Span, span: Span, attrs: &[Attribute]) { - if !find_attr!(attrs, Repr { reprs, .. } => reprs.iter().any(|(r, _)| r == &ReprAttr::ReprTransparent)) - .unwrap_or(false) - { - self.dcx().emit_err(diagnostics::RustcPubTransparent { span, attr_span }); - } - } - fn check_rustc_force_inline(&self, hir_id: HirId, attrs: &[Attribute], target: Target) { if let (Target::Closure, None) = ( target, diff --git a/compiler/rustc_passes/src/diagnostics.rs b/compiler/rustc_passes/src/diagnostics.rs index 2589941d7dae1..664c4acc8ecc7 100644 --- a/compiler/rustc_passes/src/diagnostics.rs +++ b/compiler/rustc_passes/src/diagnostics.rs @@ -242,15 +242,6 @@ pub(crate) struct RustcAllowConstFnUnstable { pub span: Span, } -#[derive(Diagnostic)] -#[diag("attribute should be applied to `#[repr(transparent)]` types")] -pub(crate) struct RustcPubTransparent { - #[primary_span] - pub attr_span: Span, - #[label("not a `#[repr(transparent)]` type")] - pub span: Span, -} - #[derive(Diagnostic)] #[diag("attribute cannot be applied to a `async`, `gen` or `async gen` function")] pub(crate) struct RustcForceInlineCoro { From 02076ac55a3006cfd42950f04d8f10b6930ee03a Mon Sep 17 00:00:00 2001 From: Obei Sideg Date: Fri, 10 Jul 2026 21:12:15 +0300 Subject: [PATCH 3/3] Add `FinalizeCheckContext` for attribute parser --- .../src/attributes/codegen_attrs.rs | 2 +- .../src/attributes/link_attrs.rs | 2 +- .../src/attributes/lint_helpers.rs | 2 +- .../rustc_attr_parsing/src/attributes/mod.rs | 35 +++++++------- .../src/attributes/prelude.rs | 2 +- compiler/rustc_attr_parsing/src/context.rs | 47 +++++++++++++++---- compiler/rustc_attr_parsing/src/interface.rs | 9 ++-- 7 files changed, 64 insertions(+), 35 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index 1de0bba71ffd2..15c4714624a63 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -541,7 +541,7 @@ impl CombineAttributeParser for TargetFeatureParser { parse_tf_attribute(cx, args) } - fn finalize_check(cx: &FinalizeContext<'_, '_>, attr_span: Span) { + fn finalize_check(cx: &FinalizeCheckContext<'_, '_>, attr_span: Span) { // `#[target_feature]` is incompatible with lang item functions, // except on WASM where calling target-feature functions is safe (see #84988). if !cx.sess().target.is_like_wasm && !cx.sess().opts.actually_rustdoc { diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs index c76661b80e11d..07713590ff2e9 100644 --- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs @@ -576,7 +576,7 @@ impl NoArgsAttributeParser for FfiPureParser { const STABILITY: AttributeStability = unstable!(ffi_pure); const CREATE: fn(Span) -> AttributeKind = AttributeKind::FfiPure; - fn finalize_check(cx: &FinalizeContext<'_, '_>, attr_span: Span) { + fn finalize_check(cx: &FinalizeCheckContext<'_, '_>, attr_span: Span) { // `#[ffi_const]` functions cannot be `#[ffi_pure]`. if cx.all_attrs.iter().any(|a| a.word_is(sym::ffi_const)) { cx.emit_err(BothFfiConstAndPure { attr_span }); diff --git a/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs b/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs index 85665ad2bb2a8..8011beb05546f 100644 --- a/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs +++ b/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs @@ -30,7 +30,7 @@ impl NoArgsAttributeParser for RustcPubTransparentParser { const STABILITY: AttributeStability = unstable!(rustc_attrs); const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcPubTransparent; - fn finalize_check(cx: &FinalizeContext<'_, '_>, attr_span: Span) { + fn finalize_check(cx: &FinalizeCheckContext<'_, '_>, attr_span: Span) { // `#[rustc_pub_transparent]` may only be applied to `#[repr(transparent)]` types. let is_transparent = find_attr!( cx.parsed_attrs, diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index f1122779eecc0..0adae406df5c6 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -26,7 +26,7 @@ use rustc_span::edition::Edition; use rustc_span::{Span, Symbol}; use thin_vec::ThinVec; -use crate::context::{AcceptContext, FinalizeCheckFn, FinalizeContext}; +use crate::context::{AcceptContext, FinalizeCheckContext, FinalizeCheckFn, FinalizeContext}; use crate::parser::ArgParser; use crate::session_diagnostics::UnusedMultiple; use crate::target_checking::AllowedTargets; @@ -123,7 +123,7 @@ pub(crate) trait AttributeParser: Default + 'static { /// span it should be reported at. /// /// Running after finalization means the check can inspect the fully parsed attributes - /// via [`FinalizeContext::parsed_attrs`], which are not yet all available during + /// via [`FinalizeCheckContext::parsed_attrs`], which are not yet all available during /// [`finalize`](Self::finalize). This is queried right before `finalize` consumes the /// parser state. /// @@ -162,13 +162,14 @@ pub(crate) trait SingleAttributeParser: 'static { /// Converts a single syntactical attribute to a single semantic attribute, or [`AttributeKind`] fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option; - /// Optional cross-attribute validation, run once during finalization after all - /// attributes on the item have been parsed. Unlike [`convert`](Self::convert), this - /// has access to the sibling attributes via [`FinalizeContext::all_attrs`], so it can - /// reject incompatible combinations. `attr_span` is the span of this attribute. + /// Optional cross-attribute validation, run once *after* all attributes on the item + /// have been finalized. Unlike [`convert`](Self::convert), this has access to the + /// sibling attributes via [`FinalizeCheckContext::all_attrs`] and the fully parsed + /// attributes via [`FinalizeCheckContext::parsed_attrs`], so it can reject incompatible + /// combinations. `attr_span` is the span of this attribute. /// /// Defaults to a no-op. - fn finalize_check(_cx: &FinalizeContext<'_, '_>, _attr_span: Span) {} + fn finalize_check(_cx: &FinalizeCheckContext<'_, '_>, _attr_span: Span) {} } /// Use in combination with [`SingleAttributeParser`]. @@ -287,13 +288,14 @@ pub(crate) trait NoArgsAttributeParser: 'static { /// Create the [`AttributeKind`] given attribute's [`Span`]. const CREATE: fn(Span) -> AttributeKind; - /// Optional cross-attribute validation, run once during finalization after all - /// attributes on the item have been parsed. Has access to the sibling attributes via - /// [`FinalizeContext::all_attrs`], so it can reject incompatible combinations. + /// Optional cross-attribute validation, run once *after* all attributes on the item + /// have been finalized. Has access to the sibling attributes via + /// [`FinalizeCheckContext::all_attrs`] and the fully parsed attributes via + /// [`FinalizeCheckContext::parsed_attrs`], so it can reject incompatible combinations. /// `attr_span` is the span of this attribute. /// /// Defaults to a no-op. - fn finalize_check(_cx: &FinalizeContext<'_, '_>, _attr_span: Span) {} + fn finalize_check(_cx: &FinalizeCheckContext<'_, '_>, _attr_span: Span) {} } pub(crate) struct WithoutArgs(PhantomData); @@ -317,7 +319,7 @@ impl SingleAttributeParser for WithoutArgs { Some(T::CREATE(cx.attr_span)) } - fn finalize_check(cx: &FinalizeContext<'_, '_>, attr_span: Span) { + fn finalize_check(cx: &FinalizeCheckContext<'_, '_>, attr_span: Span) { T::finalize_check(cx, attr_span) } } @@ -354,13 +356,14 @@ pub(crate) trait CombineAttributeParser: 'static { args: &ArgParser, ) -> impl IntoIterator; - /// Optional cross-attribute validation, run once during finalization after all - /// attributes on the item have been parsed. Has access to the sibling attributes via - /// [`FinalizeContext::all_attrs`], so it can reject incompatible combinations. + /// Optional cross-attribute validation, run once *after* all attributes on the item + /// have been finalized. Has access to the sibling attributes via + /// [`FinalizeCheckContext::all_attrs`] and the fully parsed attributes via + /// [`FinalizeCheckContext::parsed_attrs`], so it can reject incompatible combinations. /// `attr_span` is the span of the first attribute that was encountered. /// /// Defaults to a no-op. - fn finalize_check(_cx: &FinalizeContext<'_, '_>, _attr_span: Span) {} + fn finalize_check(_cx: &FinalizeCheckContext<'_, '_>, _attr_span: Span) {} } /// Use in combination with [`CombineAttributeParser`]. diff --git a/compiler/rustc_attr_parsing/src/attributes/prelude.rs b/compiler/rustc_attr_parsing/src/attributes/prelude.rs index 4dd8f715f4387..744d5368580d8 100644 --- a/compiler/rustc_attr_parsing/src/attributes/prelude.rs +++ b/compiler/rustc_attr_parsing/src/attributes/prelude.rs @@ -15,7 +15,7 @@ pub(super) use crate::attributes::{ }; // contexts #[doc(hidden)] -pub(super) use crate::context::{AcceptContext, FinalizeContext}; +pub(super) use crate::context::{AcceptContext, FinalizeCheckContext, FinalizeContext}; #[doc(hidden)] pub(super) use crate::parser::*; // target checking diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index dd8a77fb38c4f..929b34d4a36bc 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -99,9 +99,9 @@ pub(crate) type FinalizeFn = fn(&mut FinalizeContext<'_, '_>) -> FinalizeOutput; /// A cross-attribute check that runs *after* all attributes on an item have been /// finalized, so it can inspect the fully parsed attributes via -/// [`FinalizeContext::parsed_attrs`]. The [`Span`] is the span of the attribute the +/// [`FinalizeCheckContext::parsed_attrs`]. The [`Span`] is the span of the attribute the /// check is associated with, used for diagnostics. -pub(crate) type FinalizeCheckFn = fn(&FinalizeContext<'_, '_>, Span); +pub(crate) type FinalizeCheckFn = fn(&FinalizeCheckContext<'_, '_>, Span); /// The result of finalizing a single attribute parser. pub(crate) struct FinalizeOutput { @@ -109,7 +109,7 @@ pub(crate) struct FinalizeOutput { pub(crate) attr: Option, /// A check to run once *all* attributes on the item have been finalized, together /// with the span it should be reported at. Deferred so that it can inspect the fully - /// parsed attributes via [`FinalizeContext::parsed_attrs`]. + /// parsed attributes via [`FinalizeCheckContext::parsed_attrs`]. pub(crate) deferred_check: Option<(FinalizeCheckFn, Span)>, } @@ -797,18 +797,45 @@ pub(crate) struct FinalizeContext<'p, 'sess> { /// Usually, you should use normal attribute parsing logic instead, /// especially when making a *denylist* of other attributes. pub(crate) all_attrs: &'p [RefPathParser<'p>], +} + +impl<'p, 'sess: 'p> Deref for FinalizeContext<'p, 'sess> { + type Target = SharedContext<'p, 'sess>; + + fn deref(&self) -> &Self::Target { + &self.shared + } +} + +impl<'p, 'sess: 'p> DerefMut for FinalizeContext<'p, 'sess> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.shared + } +} + +/// Context given to deferred cross-attribute checks (`finalize_check`). +/// +/// These checks run *after* every attribute on an item has been finalized, so unlike +/// [`FinalizeContext`] this context can also inspect the fully parsed attributes via +/// [`parsed_attrs`](Self::parsed_attrs). +pub(crate) struct FinalizeCheckContext<'p, 'sess> { + pub(crate) shared: SharedContext<'p, 'sess>, + + /// A list of all attribute on this syntax node. + /// + /// Useful for compatibility checks with other attributes. + /// + /// Unlike [`parsed_attrs`](Self::parsed_attrs), this only contains the *paths* of the + /// attributes. + pub(crate) all_attrs: &'p [RefPathParser<'p>], /// All attributes that have been parsed on this syntax node. /// - /// Unlike [`all_attrs`](Self::all_attrs), which only contains the *paths* of the - /// attributes, this contains the fully parsed attributes. It is only populated when - /// running the deferred `finalize_check`s, which happen after all attributes on the - /// item have been finalized. During finalization itself this is empty, since the - /// attributes are not all available yet. + /// Unlike [`all_attrs`](Self::all_attrs), this contains the fully parsed attributes. pub(crate) parsed_attrs: &'p [Attribute], } -impl<'p, 'sess: 'p> Deref for FinalizeContext<'p, 'sess> { +impl<'p, 'sess: 'p> Deref for FinalizeCheckContext<'p, 'sess> { type Target = SharedContext<'p, 'sess>; fn deref(&self) -> &Self::Target { @@ -816,7 +843,7 @@ impl<'p, 'sess: 'p> Deref for FinalizeContext<'p, 'sess> { } } -impl<'p, 'sess: 'p> DerefMut for FinalizeContext<'p, 'sess> { +impl<'p, 'sess: 'p> DerefMut for FinalizeCheckContext<'p, 'sess> { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.shared } diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index 0fc38bfe65114..cb05b8de06ce4 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -18,8 +18,8 @@ use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym}; use crate::attributes::AttributeSafety; use crate::context::{ - ATTRIBUTE_PARSERS, AcceptContext, FinalizeCheckFn, FinalizeContext, FinalizeFn, FinalizeOutput, - SharedContext, + ATTRIBUTE_PARSERS, AcceptContext, FinalizeCheckContext, FinalizeCheckFn, FinalizeContext, + FinalizeFn, FinalizeOutput, SharedContext, }; use crate::parser::{AllowExprMetavar, ArgParser, PathParser, RefPathParser}; use crate::session_diagnostics::ParsedDescription; @@ -466,7 +466,6 @@ impl<'sess> AttributeParser<'sess> { has_lint_been_emitted: AtomicBool::new(false), }, all_attrs: &attr_paths, - parsed_attrs: &[], }); if let Some(attr) = attr { attributes.push(Attribute::Parsed(attr)); @@ -477,10 +476,10 @@ impl<'sess> AttributeParser<'sess> { } // Now that all attributes have been parsed, run the deferred checks. These can - // inspect the fully parsed attributes via `FinalizeContext::parsed_attrs`. + // inspect the fully parsed attributes via `FinalizeCheckContext::parsed_attrs`. for (check, attr_span) in deferred_checks { check( - &FinalizeContext { + &FinalizeCheckContext { shared: SharedContext { cx: self, target_span,