Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
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: &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;
Expand Down
40 changes: 29 additions & 11 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, 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 [`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.
Expand Down Expand Up @@ -185,11 +199,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 @@ -375,12 +393,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?))
}
}
35 changes: 32 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 @@ -93,7 +93,23 @@ pub(super) struct GroupTypeInnerAccept {

pub(crate) type AcceptFn =
Box<dyn for<'sess, 'a> 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
/// [`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<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 [`FinalizeContext::parsed_attrs`].
pub(crate) deferred_check: Option<(FinalizeCheckFn, Span)>,
}

macro_rules! attribute_parsers {
(
Expand Down Expand Up @@ -122,7 +138,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 @@ -777,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> {
Expand Down
38 changes: 35 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, FinalizeCheckFn, FinalizeContext, FinalizeFn, FinalizeOutput,
SharedContext,
};
use crate::early_parsed::{EARLY_PARSED_ATTRIBUTES, EarlyParsedState};
use crate::parser::{AllowExprMetavar, ArgParser, PathParser, RefPathParser};
Expand Down Expand Up @@ -459,8 +460,14 @@ impl<'sess> AttributeParser<'sess> {
}

early_parsed_state.finalize_early_parsed_attributes(&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 @@ -470,9 +477,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 {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it doesn't make sense for check to use the FinalizeContext.
Because of this choice, in the code above you have to lie and give an &[].

Can you make a new context for this?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added the new context.

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 @@ -385,6 +382,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
AttributeKind::RustcPassIndirectlyInNonRusticAbis(..) => (),
AttributeKind::RustcPreserveUbChecks => (),
AttributeKind::RustcProcMacroDecls => (),
AttributeKind::RustcPubTransparent(..) => (),
AttributeKind::RustcReallocator => (),
AttributeKind::RustcRegions => (),
AttributeKind::RustcReservationImpl(..) => (),
Expand Down Expand Up @@ -1550,14 +1548,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
9 changes: 0 additions & 9 deletions compiler/rustc_passes/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Loading