Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_attr_parsing/src/attributes/link_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 });
Expand Down
14 changes: 14 additions & 0 deletions compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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: &FinalizeCheckContext<'_, '_>, 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;
Expand Down
71 changes: 46 additions & 25 deletions compiler/rustc_attr_parsing/src/attributes/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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, FinalizeCheckContext, FinalizeCheckFn, FinalizeContext};
use crate::parser::ArgParser;
use crate::session_diagnostics::UnusedMultiple;
use crate::target_checking::AllowedTargets;
Expand Down Expand Up @@ -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<AttributeKind>;

/// 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 [`FinalizeCheckContext::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.
Expand Down Expand Up @@ -148,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<AttributeKind>;

/// 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`].
Expand Down Expand Up @@ -185,11 +200,15 @@ impl<T: SingleAttributeParser> AttributeParser for Single<T> {
const ALLOWED_TARGETS: AllowedTargets<'_> = T::ALLOWED_TARGETS;
const SAFETY: AttributeSafety = T::SAFETY;

fn finalize(self, cx: &FinalizeContext<'_, '_>) -> Option<AttributeKind> {
let (kind, span) = self.1?;
T::finalize_check(cx, span);
fn finalize(self, _cx: &FinalizeContext<'_, '_>) -> Option<AttributeKind> {
let (kind, _span) = self.1?;
Some(kind)
}

fn deferred_finalize_check(&self) -> Option<(FinalizeCheckFn, Span)> {
let (_, span) = self.1.as_ref()?;
Some((<T as SingleAttributeParser>::finalize_check, *span))
}
}

pub(crate) enum OnDuplicate {
Expand Down Expand Up @@ -269,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<T: NoArgsAttributeParser>(PhantomData<T>);
Expand All @@ -299,7 +319,7 @@ impl<T: NoArgsAttributeParser> SingleAttributeParser for WithoutArgs<T> {
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)
}
}
Expand Down Expand Up @@ -336,13 +356,14 @@ pub(crate) trait CombineAttributeParser: 'static {
args: &ArgParser,
) -> impl IntoIterator<Item = Self::Item>;

/// 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`].
Expand Down Expand Up @@ -375,12 +396,12 @@ impl<T: CombineAttributeParser> AttributeParser for Combine<T> {
const ALLOWED_TARGETS: AllowedTargets<'_> = T::ALLOWED_TARGETS;
const SAFETY: AttributeSafety = T::SAFETY;

fn finalize(self, cx: &FinalizeContext<'_, '_>) -> Option<AttributeKind> {
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<AttributeKind> {
let first_span = self.first_span?;
Some(T::CONVERT(self.items, first_span))
}

fn deferred_finalize_check(&self) -> Option<(FinalizeCheckFn, Span)> {
Some((<T as CombineAttributeParser>::finalize_check, self.first_span?))
}
}
2 changes: 1 addition & 1 deletion compiler/rustc_attr_parsing/src/attributes/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
63 changes: 60 additions & 3 deletions compiler/rustc_attr_parsing/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -94,7 +94,24 @@ pub(super) struct GroupTypeInnerAccept {

pub(crate) type AcceptFn =
Box<dyn for<'sess> Fn(&mut AcceptContext<'_, 'sess>, &ArgParser) + Send + Sync>;
pub(crate) type FinalizeFn = fn(&mut FinalizeContext<'_, '_>) -> Option<AttributeKind>;

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
/// [`FinalizeCheckContext::parsed_attrs`]. The [`Span`] is the span of the attribute the
/// check is associated with, used for diagnostics.
pub(crate) type FinalizeCheckFn = fn(&FinalizeCheckContext<'_, '_>, Span);

/// The result of finalizing a single attribute parser.
pub(crate) struct FinalizeOutput {
/// The attribute the parser produced, if any.
pub(crate) attr: Option<AttributeKind>,
/// 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 [`FinalizeCheckContext::parsed_attrs`].
pub(crate) deferred_check: Option<(FinalizeCheckFn, Span)>,
}

macro_rules! attribute_parsers {
(
Expand Down Expand Up @@ -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 }
}
});
}
Expand Down Expand Up @@ -792,6 +813,42 @@ impl<'p, 'sess: 'p> DerefMut for FinalizeContext<'p, 'sess> {
}
}

/// 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), this contains the fully parsed attributes.
pub(crate) parsed_attrs: &'p [Attribute],
}

impl<'p, 'sess: 'p> Deref for FinalizeCheckContext<'p, 'sess> {
type Target = SharedContext<'p, 'sess>;

fn deref(&self) -> &Self::Target {
&self.shared
}
}

impl<'p, 'sess: 'p> DerefMut for FinalizeCheckContext<'p, 'sess> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.shared
}
}

impl<'p, 'sess: 'p> Deref for SharedContext<'p, 'sess> {
type Target = AttributeParser<'sess>;

Expand Down
37 changes: 34 additions & 3 deletions compiler/rustc_attr_parsing/src/interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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, FinalizeCheckContext, FinalizeCheckFn, FinalizeContext,
FinalizeFn, FinalizeOutput, SharedContext,
};
use crate::parser::{AllowExprMetavar, ArgParser, PathParser, RefPathParser};
use crate::session_diagnostics::ParsedDescription;
Expand Down Expand Up @@ -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,
Expand All @@ -459,9 +466,33 @@ impl<'sess> AttributeParser<'sess> {
has_lint_been_emitted: AtomicBool::new(false),
},
all_attrs: &attr_paths,
}) {
});
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 `FinalizeCheckContext::parsed_attrs`.
for (check, attr_span) in deferred_checks {
check(
&FinalizeCheckContext {
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 {
Expand Down
9 changes: 9 additions & 0 deletions compiler/rustc_attr_parsing/src/session_diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand Down
12 changes: 1 addition & 11 deletions compiler/rustc_passes/src/check_attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -386,6 +383,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
AttributeKind::RustcPassIndirectlyInNonRusticAbis(..) => (),
AttributeKind::RustcPreserveUbChecks => (),
AttributeKind::RustcProcMacroDecls => (),
AttributeKind::RustcPubTransparent(..) => (),
AttributeKind::RustcReallocator => (),
AttributeKind::RustcRegions => (),
AttributeKind::RustcReservationImpl(..) => (),
Expand Down Expand Up @@ -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,
Expand Down
Loading
Loading