Skip to content

Commit 1e998ba

Browse files
committed
Auto merge of #158496 - obeis:move-check-rustc-pub-transparen, r=<try>
Move `check_rustc_pub_transparent` into the attribute parser
2 parents 3ead112 + b6b2e9c commit 1e998ba

7 files changed

Lines changed: 120 additions & 37 deletions

File tree

compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
use rustc_feature::AttributeStability;
2+
use rustc_hir::attrs::ReprAttr;
3+
use rustc_hir::find_attr;
24

35
use super::prelude::*;
6+
use crate::session_diagnostics::RustcPubTransparent;
47

58
pub(crate) struct RustcAsPtrParser;
69
impl NoArgsAttributeParser for RustcAsPtrParser {
@@ -26,6 +29,17 @@ impl NoArgsAttributeParser for RustcPubTransparentParser {
2629
]);
2730
const STABILITY: AttributeStability = unstable!(rustc_attrs);
2831
const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcPubTransparent;
32+
33+
fn finalize_check(cx: &FinalizeContext<'_, '_>, attr_span: Span) {
34+
// `#[rustc_pub_transparent]` may only be applied to `#[repr(transparent)]` types.
35+
let is_transparent = find_attr!(
36+
cx.parsed_attrs,
37+
Repr { reprs, .. } if reprs.iter().any(|(r, _)| r == &ReprAttr::ReprTransparent)
38+
);
39+
if !is_transparent {
40+
cx.emit_err(RustcPubTransparent { span: cx.target_span, attr_span });
41+
}
42+
}
2943
}
3044

3145
pub(crate) struct RustcPassByValueParser;

compiler/rustc_attr_parsing/src/attributes/mod.rs

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use rustc_span::edition::Edition;
2626
use rustc_span::{Span, Symbol};
2727
use thin_vec::ThinVec;
2828

29-
use crate::context::{AcceptContext, FinalizeContext};
29+
use crate::context::{AcceptContext, FinalizeCheckFn, FinalizeContext};
3030
use crate::parser::ArgParser;
3131
use crate::session_diagnostics::UnusedMultiple;
3232
use crate::target_checking::AllowedTargets;
@@ -117,6 +117,20 @@ pub(crate) trait AttributeParser: Default + 'static {
117117
/// every single syntax item that could have attributes applied to it.
118118
/// Your accept mappings should determine whether this returns something.
119119
fn finalize(self, cx: &FinalizeContext<'_, '_>) -> Option<AttributeKind>;
120+
121+
/// If this parser produced an attribute, optionally returns a cross-attribute check
122+
/// to run once *all* attributes on the item have been finalized, together with the
123+
/// span it should be reported at.
124+
///
125+
/// Running after finalization means the check can inspect the fully parsed attributes
126+
/// via [`FinalizeContext::parsed_attrs`], which are not yet all available during
127+
/// [`finalize`](Self::finalize). This is queried right before `finalize` consumes the
128+
/// parser state.
129+
///
130+
/// Defaults to no check.
131+
fn deferred_finalize_check(&self) -> Option<(FinalizeCheckFn, Span)> {
132+
None
133+
}
120134
}
121135

122136
/// Alternative to [`AttributeParser`] that automatically handles state management.
@@ -185,11 +199,15 @@ impl<T: SingleAttributeParser> AttributeParser for Single<T> {
185199
const ALLOWED_TARGETS: AllowedTargets<'_> = T::ALLOWED_TARGETS;
186200
const SAFETY: AttributeSafety = T::SAFETY;
187201

188-
fn finalize(self, cx: &FinalizeContext<'_, '_>) -> Option<AttributeKind> {
189-
let (kind, span) = self.1?;
190-
T::finalize_check(cx, span);
202+
fn finalize(self, _cx: &FinalizeContext<'_, '_>) -> Option<AttributeKind> {
203+
let (kind, _span) = self.1?;
191204
Some(kind)
192205
}
206+
207+
fn deferred_finalize_check(&self) -> Option<(FinalizeCheckFn, Span)> {
208+
let (_, span) = self.1.as_ref()?;
209+
Some((<T as SingleAttributeParser>::finalize_check, *span))
210+
}
193211
}
194212

195213
pub(crate) enum OnDuplicate {
@@ -375,12 +393,12 @@ impl<T: CombineAttributeParser> AttributeParser for Combine<T> {
375393
const ALLOWED_TARGETS: AllowedTargets<'_> = T::ALLOWED_TARGETS;
376394
const SAFETY: AttributeSafety = T::SAFETY;
377395

378-
fn finalize(self, cx: &FinalizeContext<'_, '_>) -> Option<AttributeKind> {
379-
if let Some(first_span) = self.first_span {
380-
T::finalize_check(cx, first_span);
381-
Some(T::CONVERT(self.items, first_span))
382-
} else {
383-
None
384-
}
396+
fn finalize(self, _cx: &FinalizeContext<'_, '_>) -> Option<AttributeKind> {
397+
let first_span = self.first_span?;
398+
Some(T::CONVERT(self.items, first_span))
399+
}
400+
401+
fn deferred_finalize_check(&self) -> Option<(FinalizeCheckFn, Span)> {
402+
Some((<T as CombineAttributeParser>::finalize_check, self.first_span?))
385403
}
386404
}

compiler/rustc_attr_parsing/src/context.rs

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ use rustc_ast::{AttrStyle, MetaItemLit, Safety};
1212
use rustc_data_structures::sync::{DynSend, DynSync};
1313
use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, Level, MultiSpan};
1414
use rustc_feature::AttributeStability;
15-
use rustc_hir::AttrPath;
1615
use rustc_hir::attrs::AttributeKind;
16+
use rustc_hir::{AttrPath, Attribute};
1717
use rustc_parse::parser::Recovery;
1818
use rustc_session::Session;
1919
use rustc_session::lint::{Lint, LintId};
@@ -93,7 +93,23 @@ pub(super) struct GroupTypeInnerAccept {
9393

9494
pub(crate) type AcceptFn =
9595
Box<dyn for<'sess, 'a> Fn(&mut AcceptContext<'_, 'sess>, &ArgParser) + Send + Sync>;
96-
pub(crate) type FinalizeFn = fn(&mut FinalizeContext<'_, '_>) -> Option<AttributeKind>;
96+
pub(crate) type FinalizeFn = fn(&mut FinalizeContext<'_, '_>) -> FinalizeOutput;
97+
98+
/// A cross-attribute check that runs *after* all attributes on an item have been
99+
/// finalized, so it can inspect the fully parsed attributes via
100+
/// [`FinalizeContext::parsed_attrs`]. The [`Span`] is the span of the attribute the
101+
/// check is associated with, used for diagnostics.
102+
pub(crate) type FinalizeCheckFn = fn(&FinalizeContext<'_, '_>, Span);
103+
104+
/// The result of finalizing a single attribute parser.
105+
pub(crate) struct FinalizeOutput {
106+
/// The attribute the parser produced, if any.
107+
pub(crate) attr: Option<AttributeKind>,
108+
/// A check to run once *all* attributes on the item have been finalized, together
109+
/// with the span it should be reported at. Deferred so that it can inspect the fully
110+
/// parsed attributes via [`FinalizeContext::parsed_attrs`].
111+
pub(crate) deferred_check: Option<(FinalizeCheckFn, Span)>,
112+
}
97113

98114
macro_rules! attribute_parsers {
99115
(
@@ -122,7 +138,11 @@ macro_rules! attribute_parsers {
122138
allowed_targets: <$names as crate::attributes::AttributeParser>::ALLOWED_TARGETS,
123139
finalizer: |cx| {
124140
let state = STATE_OBJECT.take();
125-
state.finalize(cx)
141+
// Compute the deferred check (if any) before consuming
142+
// the state in `finalize`.
143+
let deferred_check = state.deferred_finalize_check();
144+
let attr = state.finalize(cx);
145+
FinalizeOutput { attr, deferred_check }
126146
}
127147
});
128148
}
@@ -777,6 +797,15 @@ pub(crate) struct FinalizeContext<'p, 'sess> {
777797
/// Usually, you should use normal attribute parsing logic instead,
778798
/// especially when making a *denylist* of other attributes.
779799
pub(crate) all_attrs: &'p [RefPathParser<'p>],
800+
801+
/// All attributes that have been parsed on this syntax node.
802+
///
803+
/// Unlike [`all_attrs`](Self::all_attrs), which only contains the *paths* of the
804+
/// attributes, this contains the fully parsed attributes. It is only populated when
805+
/// running the deferred `finalize_check`s, which happen after all attributes on the
806+
/// item have been finalized. During finalization itself this is empty, since the
807+
/// attributes are not all available yet.
808+
pub(crate) parsed_attrs: &'p [Attribute],
780809
}
781810

782811
impl<'p, 'sess: 'p> Deref for FinalizeContext<'p, 'sess> {

compiler/rustc_attr_parsing/src/interface.rs

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym};
1818

1919
use crate::attributes::AttributeSafety;
2020
use crate::context::{
21-
ATTRIBUTE_PARSERS, AcceptContext, FinalizeContext, FinalizeFn, SharedContext,
21+
ATTRIBUTE_PARSERS, AcceptContext, FinalizeCheckFn, FinalizeContext, FinalizeFn, FinalizeOutput,
22+
SharedContext,
2223
};
2324
use crate::early_parsed::{EARLY_PARSED_ATTRIBUTES, EarlyParsedState};
2425
use crate::parser::{AllowExprMetavar, ArgParser, PathParser, RefPathParser};
@@ -459,8 +460,14 @@ impl<'sess> AttributeParser<'sess> {
459460
}
460461

461462
early_parsed_state.finalize_early_parsed_attributes(&mut attributes);
463+
464+
// First, run all finalizers to produce the parsed attributes. Cross-attribute
465+
// checks that need to inspect the fully parsed attributes are deferred until all
466+
// finalizers have run (see below), since the parsed attributes are not yet all
467+
// available here.
468+
let mut deferred_checks: Vec<(FinalizeCheckFn, Span)> = Vec::new();
462469
for f in &finalizers {
463-
if let Some(attr) = f(&mut FinalizeContext {
470+
let FinalizeOutput { attr, deferred_check } = f(&mut FinalizeContext {
464471
shared: SharedContext {
465472
cx: self,
466473
target_span,
@@ -470,9 +477,34 @@ impl<'sess> AttributeParser<'sess> {
470477
has_lint_been_emitted: AtomicBool::new(false),
471478
},
472479
all_attrs: &attr_paths,
473-
}) {
480+
parsed_attrs: &[],
481+
});
482+
if let Some(attr) = attr {
474483
attributes.push(Attribute::Parsed(attr));
475484
}
485+
if let Some(deferred_check) = deferred_check {
486+
deferred_checks.push(deferred_check);
487+
}
488+
}
489+
490+
// Now that all attributes have been parsed, run the deferred checks. These can
491+
// inspect the fully parsed attributes via `FinalizeContext::parsed_attrs`.
492+
for (check, attr_span) in deferred_checks {
493+
check(
494+
&FinalizeContext {
495+
shared: SharedContext {
496+
cx: self,
497+
target_span,
498+
target,
499+
emit_lint: &mut emit_lint,
500+
#[cfg(debug_assertions)]
501+
has_lint_been_emitted: AtomicBool::new(false),
502+
},
503+
all_attrs: &attr_paths,
504+
parsed_attrs: &attributes,
505+
},
506+
attr_span,
507+
);
476508
}
477509

478510
if !matches!(self.should_emit, ShouldEmit::Nothing) && target == Target::WherePredicate {

compiler/rustc_attr_parsing/src/session_diagnostics.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,15 @@ pub(crate) struct BothFfiConstAndPure {
2020
pub attr_span: Span,
2121
}
2222

23+
#[derive(Diagnostic)]
24+
#[diag("attribute should be applied to `#[repr(transparent)]` types")]
25+
pub(crate) struct RustcPubTransparent {
26+
#[primary_span]
27+
pub attr_span: Span,
28+
#[label("not a `#[repr(transparent)]` type")]
29+
pub span: Span,
30+
}
31+
2332
#[derive(Diagnostic)]
2433
#[diag("{$attr_str} attribute cannot have empty value")]
2534
pub(crate) struct DocAliasEmpty<'a> {

compiler/rustc_passes/src/check_attr.rs

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -207,9 +207,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
207207
AttributeKind::RustcDumpObjectLifetimeDefaults => {
208208
self.check_dump_object_lifetime_defaults(hir_id);
209209
}
210-
&AttributeKind::RustcPubTransparent(attr_span) => {
211-
self.check_rustc_pub_transparent(attr_span, span, attrs)
212-
}
213210
AttributeKind::Naked(..) => self.check_naked(hir_id, target),
214211
AttributeKind::TrackCaller(attr_span) => {
215212
self.check_track_caller(hir_id, *attr_span, attrs, target)
@@ -385,6 +382,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
385382
AttributeKind::RustcPassIndirectlyInNonRusticAbis(..) => (),
386383
AttributeKind::RustcPreserveUbChecks => (),
387384
AttributeKind::RustcProcMacroDecls => (),
385+
AttributeKind::RustcPubTransparent(..) => (),
388386
AttributeKind::RustcReallocator => (),
389387
AttributeKind::RustcRegions => (),
390388
AttributeKind::RustcReservationImpl(..) => (),
@@ -1573,14 +1571,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
15731571
}
15741572
}
15751573

1576-
fn check_rustc_pub_transparent(&self, attr_span: Span, span: Span, attrs: &[Attribute]) {
1577-
if !find_attr!(attrs, Repr { reprs, .. } => reprs.iter().any(|(r, _)| r == &ReprAttr::ReprTransparent))
1578-
.unwrap_or(false)
1579-
{
1580-
self.dcx().emit_err(diagnostics::RustcPubTransparent { span, attr_span });
1581-
}
1582-
}
1583-
15841574
fn check_rustc_force_inline(&self, hir_id: HirId, attrs: &[Attribute], target: Target) {
15851575
if let (Target::Closure, None) = (
15861576
target,

compiler/rustc_passes/src/diagnostics.rs

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -242,15 +242,6 @@ pub(crate) struct RustcAllowConstFnUnstable {
242242
pub span: Span,
243243
}
244244

245-
#[derive(Diagnostic)]
246-
#[diag("attribute should be applied to `#[repr(transparent)]` types")]
247-
pub(crate) struct RustcPubTransparent {
248-
#[primary_span]
249-
pub attr_span: Span,
250-
#[label("not a `#[repr(transparent)]` type")]
251-
pub span: Span,
252-
}
253-
254245
#[derive(Diagnostic)]
255246
#[diag("attribute cannot be applied to a `async`, `gen` or `async gen` function")]
256247
pub(crate) struct RustcForceInlineCoro {

0 commit comments

Comments
 (0)