Skip to content

Commit 5590fc0

Browse files
Make cfg and cfg_attr trace attributes into early parsed attributes
1 parent 03fb7ee commit 5590fc0

15 files changed

Lines changed: 247 additions & 143 deletions

File tree

compiler/rustc_ast/src/ast.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3446,6 +3446,8 @@ impl AttrItemKind {
34463446
/// When adding new early parsed attributes, consider whether they should be pretty printed.
34473447
#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)]
34483448
pub enum EarlyParsedAttribute {
3449+
CfgTrace(CfgEntry),
3450+
CfgAttrTrace,
34493451
}
34503452

34513453
impl AttrItem {
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
use std::fmt;
2+
3+
use rustc_macros::{Decodable, Encodable, HashStable_Generic};
4+
use rustc_span::{Span, Symbol};
5+
use thin_vec::ThinVec;
6+
7+
use crate::attr::version::RustcVersion;
8+
9+
#[derive(Encodable, Decodable, Clone, Debug, PartialEq, Eq, Hash, HashStable_Generic)]
10+
pub enum CfgEntry {
11+
All(ThinVec<CfgEntry>, Span),
12+
Any(ThinVec<CfgEntry>, Span),
13+
Not(Box<CfgEntry>, Span),
14+
Bool(bool, Span),
15+
NameValue { name: Symbol, value: Option<Symbol>, span: Span },
16+
Version(Option<RustcVersion>, Span),
17+
}
18+
19+
impl CfgEntry {
20+
pub fn lower_spans(&mut self, lower_span: impl Copy + Fn(Span) -> Span) {
21+
match self {
22+
CfgEntry::All(subs, span) | CfgEntry::Any(subs, span) => {
23+
*span = lower_span(*span);
24+
subs.iter_mut().for_each(|sub| sub.lower_spans(lower_span));
25+
}
26+
CfgEntry::Not(sub, span) => {
27+
*span = lower_span(*span);
28+
sub.lower_spans(lower_span);
29+
}
30+
CfgEntry::Bool(_, span)
31+
| CfgEntry::NameValue { span, .. }
32+
| CfgEntry::Version(_, span) => {
33+
*span = lower_span(*span);
34+
}
35+
}
36+
}
37+
38+
pub fn span(&self) -> Span {
39+
let (Self::All(_, span)
40+
| Self::Any(_, span)
41+
| Self::Not(_, span)
42+
| Self::Bool(_, span)
43+
| Self::NameValue { span, .. }
44+
| Self::Version(_, span)) = self;
45+
*span
46+
}
47+
48+
/// Same as `PartialEq` but doesn't check spans and ignore order of cfgs.
49+
pub fn is_equivalent_to(&self, other: &Self) -> bool {
50+
match (self, other) {
51+
(Self::All(a, _), Self::All(b, _)) | (Self::Any(a, _), Self::Any(b, _)) => {
52+
a.len() == b.len() && a.iter().all(|a| b.iter().any(|b| a.is_equivalent_to(b)))
53+
}
54+
(Self::Not(a, _), Self::Not(b, _)) => a.is_equivalent_to(b),
55+
(Self::Bool(a, _), Self::Bool(b, _)) => a == b,
56+
(
57+
Self::NameValue { name: name1, value: value1, .. },
58+
Self::NameValue { name: name2, value: value2, .. },
59+
) => name1 == name2 && value1 == value2,
60+
(Self::Version(a, _), Self::Version(b, _)) => a == b,
61+
_ => false,
62+
}
63+
}
64+
}
65+
66+
impl fmt::Display for CfgEntry {
67+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68+
fn write_entries(
69+
name: &str,
70+
entries: &[CfgEntry],
71+
f: &mut fmt::Formatter<'_>,
72+
) -> fmt::Result {
73+
write!(f, "{name}(")?;
74+
for (nb, entry) in entries.iter().enumerate() {
75+
if nb != 0 {
76+
f.write_str(", ")?;
77+
}
78+
entry.fmt(f)?;
79+
}
80+
f.write_str(")")
81+
}
82+
match self {
83+
Self::All(entries, _) => write_entries("all", entries, f),
84+
Self::Any(entries, _) => write_entries("any", entries, f),
85+
Self::Not(entry, _) => write!(f, "not({entry})"),
86+
Self::Bool(value, _) => write!(f, "{value}"),
87+
Self::NameValue { name, value, .. } => {
88+
match value {
89+
// We use `as_str` and debug display to have characters escaped and `"`
90+
// characters surrounding the string.
91+
Some(value) => write!(f, "{name} = {:?}", value.as_str()),
92+
None => write!(f, "{name}"),
93+
}
94+
}
95+
Self::Version(version, _) => match version {
96+
Some(version) => write!(f, "{version}"),
97+
None => Ok(()),
98+
},
99+
}
100+
}
101+
}
Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,10 @@
1-
use std::borrow::Cow;
21
use std::fmt::{self, Display};
32
use std::sync::OnceLock;
43

5-
use rustc_error_messages::{DiagArgValue, IntoDiagArg};
6-
use rustc_macros::{
7-
BlobDecodable, Encodable, HashStable_Generic, PrintAttribute, current_rustc_version,
8-
};
9-
10-
use crate::attrs::PrintAttribute;
4+
use rustc_macros::{BlobDecodable, Encodable, HashStable_Generic, current_rustc_version};
115

126
#[derive(Encodable, BlobDecodable, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
13-
#[derive(HashStable_Generic, PrintAttribute)]
7+
#[derive(HashStable_Generic)]
148
pub struct RustcVersion {
159
pub major: u16,
1610
pub minor: u16,
@@ -47,9 +41,3 @@ impl Display for RustcVersion {
4741
write!(formatter, "{}.{}.{}", self.major, self.minor, self.patch)
4842
}
4943
}
50-
51-
impl IntoDiagArg for RustcVersion {
52-
fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
53-
DiagArgValue::Str(Cow::Owned(self.to_string()))
54-
}
55-
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
use rustc_ast::EarlyParsedAttribute;
2+
use rustc_ast::attr::data_structures::CfgEntry;
3+
use rustc_hir::Attribute;
4+
use rustc_hir::attrs::AttributeKind;
5+
use rustc_span::{Span, Symbol, sym};
6+
use thin_vec::ThinVec;
7+
8+
pub(crate) const EARLY_PARSED_ATTRIBUTES: &[&[Symbol]] =
9+
&[&[sym::cfg_trace], &[sym::cfg_attr_trace]];
10+
11+
/// This struct contains the state necessary to convert early parsed attributes to hir attributes
12+
/// The only conversion that really happens here is that multiple early parsed attributes are
13+
/// merged into a single hir attribute, representing their combined state.
14+
/// FIXME: We should make this a nice and extendable system if this is going to be used more often
15+
#[derive(Default)]
16+
pub(crate) struct EarlyParsedState {
17+
/// Attribute state for `#[cfg]` trace attributes
18+
cfg_trace: ThinVec<(CfgEntry, Span)>,
19+
20+
/// Attribute state for `#[cfg_attr]` trace attributes
21+
/// The arguments of these attributes is no longer relevant for any later passes, only their presence.
22+
/// So we discard the arguments here.
23+
cfg_attr_trace: bool,
24+
}
25+
26+
impl EarlyParsedState {
27+
pub(crate) fn accept_early_parsed_attribute(
28+
&mut self,
29+
attr_span: Span,
30+
lower_span: impl Copy + Fn(Span) -> Span,
31+
parsed: &EarlyParsedAttribute,
32+
) {
33+
match parsed {
34+
EarlyParsedAttribute::CfgTrace(cfg) => {
35+
let mut cfg = cfg.clone();
36+
cfg.lower_spans(lower_span);
37+
self.cfg_trace.push((cfg, attr_span));
38+
}
39+
EarlyParsedAttribute::CfgAttrTrace => {
40+
self.cfg_attr_trace = true;
41+
}
42+
}
43+
}
44+
45+
pub(crate) fn finalize_early_parsed_attributes(self, attributes: &mut Vec<Attribute>) {
46+
if !self.cfg_trace.is_empty() {
47+
attributes.push(Attribute::Parsed(AttributeKind::CfgTrace(self.cfg_trace)));
48+
}
49+
if self.cfg_attr_trace {
50+
attributes.push(Attribute::Parsed(AttributeKind::CfgAttrTrace));
51+
}
52+
}
53+
}

compiler/rustc_attr_parsing/src/interface.rs

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::convert::identity;
22

33
use rustc_ast as ast;
44
use rustc_ast::token::DocFragmentKind;
5-
use rustc_ast::{AttrStyle, NodeId, Safety};
5+
use rustc_ast::{AttrItemKind, AttrStyle, NodeId, Safety};
66
use rustc_errors::DiagCtxtHandle;
77
use rustc_feature::{AttributeTemplate, Features};
88
use rustc_hir::attrs::AttributeKind;
@@ -13,6 +13,7 @@ use rustc_session::lint::BuiltinLintDiag;
1313
use rustc_span::{DUMMY_SP, Span, Symbol, sym};
1414

1515
use crate::context::{AcceptContext, FinalizeContext, SharedContext, Stage};
16+
use crate::early_parsed::{EARLY_PARSED_ATTRIBUTES, EarlyParsedState};
1617
use crate::parser::{ArgParser, PathParser, RefPathParser};
1718
use crate::session_diagnostics::ParsedDescription;
1819
use crate::{Early, Late, OmitDoc, ShouldEmit};
@@ -146,8 +147,12 @@ impl<'sess> AttributeParser<'sess, Early> {
146147
normal_attr.item.path.segments.iter().map(|seg| seg.ident.name).collect::<Vec<_>>();
147148

148149
let path = AttrPath::from_ast(&normal_attr.item.path, identity);
149-
let args =
150-
ArgParser::from_attr_args(&normal_attr.item.args, &parts, &sess.psess, emit_errors)?;
150+
let args = ArgParser::from_attr_args(
151+
&normal_attr.item.args.unparsed_ref().unwrap(),
152+
&parts,
153+
&sess.psess,
154+
emit_errors,
155+
)?;
151156
Self::parse_single_args(
152157
sess,
153158
attr.span,
@@ -263,12 +268,12 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
263268
target_id: S::Id,
264269
target: Target,
265270
omit_doc: OmitDoc,
266-
267271
lower_span: impl Copy + Fn(Span) -> Span,
268272
mut emit_lint: impl FnMut(AttributeLint<S::Id>),
269273
) -> Vec<Attribute> {
270274
let mut attributes = Vec::new();
271275
let mut attr_paths: Vec<RefPathParser<'_>> = Vec::new();
276+
let mut early_parsed_state = EarlyParsedState::default();
272277

273278
for attr in attrs {
274279
// If we're only looking for a single attribute, skip all the ones we don't care about.
@@ -288,6 +293,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
288293
continue;
289294
}
290295

296+
let attr_span = lower_span(attr.span);
291297
match &attr.kind {
292298
ast::AttrKind::DocComment(comment_kind, symbol) => {
293299
if omit_doc == OmitDoc::Skip {
@@ -297,14 +303,23 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
297303
attributes.push(Attribute::Parsed(AttributeKind::DocComment {
298304
style: attr.style,
299305
kind: DocFragmentKind::Sugared(*comment_kind),
300-
span: lower_span(attr.span),
306+
span: attr_span,
301307
comment: *symbol,
302308
}))
303309
}
304310
ast::AttrKind::Normal(n) => {
305311
attr_paths.push(PathParser(&n.item.path));
306312
let attr_path = AttrPath::from_ast(&n.item.path, lower_span);
307313

314+
let args = match &n.item.args {
315+
AttrItemKind::Unparsed(args) => args,
316+
AttrItemKind::Parsed(parsed) => {
317+
early_parsed_state
318+
.accept_early_parsed_attribute(attr_span, lower_span, parsed);
319+
continue;
320+
}
321+
};
322+
308323
self.check_attribute_safety(
309324
&attr_path,
310325
lower_span(n.item.span()),
@@ -318,7 +333,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
318333

319334
if let Some(accepts) = S::parsers().accepters.get(parts.as_slice()) {
320335
let Some(args) = ArgParser::from_attr_args(
321-
&n.item.args,
336+
args,
322337
&parts,
323338
&self.sess.psess,
324339
self.stage.should_emit(),
@@ -351,7 +366,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
351366
attributes.push(Attribute::Parsed(AttributeKind::DocComment {
352367
style: attr.style,
353368
kind: DocFragmentKind::Raw(nv.value_span),
354-
span: attr.span,
369+
span: attr_span,
355370
comment,
356371
}));
357372
continue;
@@ -365,7 +380,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
365380
target_id,
366381
emit_lint: &mut emit_lint,
367382
},
368-
attr_span: lower_span(attr.span),
383+
attr_span,
369384
inner_span: lower_span(n.item.span()),
370385
attr_style: attr.style,
371386
parsed_description: ParsedDescription::Attribute,
@@ -396,17 +411,18 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
396411

397412
attributes.push(Attribute::Unparsed(Box::new(AttrItem {
398413
path: attr_path.clone(),
399-
args: self.lower_attr_args(&n.item.args, lower_span),
414+
args: self
415+
.lower_attr_args(n.item.args.unparsed_ref().unwrap(), lower_span),
400416
id: HashIgnoredAttrId { attr_id: attr.id },
401417
style: attr.style,
402-
span: lower_span(attr.span),
418+
span: attr_span,
403419
})));
404420
}
405421
}
406422
}
407423
}
408424

409-
let mut parsed_attributes = Vec::new();
425+
early_parsed_state.finalize_early_parsed_attributes(&mut attributes);
410426
for f in &S::parsers().finalizers {
411427
if let Some(attr) = f(&mut FinalizeContext {
412428
shared: SharedContext {
@@ -417,18 +433,16 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
417433
},
418434
all_attrs: &attr_paths,
419435
}) {
420-
parsed_attributes.push(Attribute::Parsed(attr));
436+
attributes.push(Attribute::Parsed(attr));
421437
}
422438
}
423439

424-
attributes.extend(parsed_attributes);
425-
426440
attributes
427441
}
428442

429443
/// Returns whether there is a parser for an attribute with this name
430444
pub fn is_parsed_attribute(path: &[Symbol]) -> bool {
431-
Late::parsers().accepters.contains_key(path)
445+
Late::parsers().accepters.contains_key(path) || EARLY_PARSED_ATTRIBUTES.contains(&path)
432446
}
433447

434448
fn lower_attr_args(&self, args: &ast::AttrArgs, lower_span: impl Fn(Span) -> Span) -> AttrArgs {

compiler/rustc_attr_parsing/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ mod interface;
9999
/// like lists or name-value pairs.
100100
pub mod parser;
101101

102+
mod early_parsed;
102103
mod safety;
103104
mod session_diagnostics;
104105
mod target_checking;

compiler/rustc_attr_parsing/src/safety.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use rustc_hir::AttrPath;
44
use rustc_hir::lints::{AttributeLint, AttributeLintKind};
55
use rustc_session::lint::LintId;
66
use rustc_session::lint::builtin::UNSAFE_ATTR_OUTSIDE_UNSAFE;
7-
use rustc_span::{Span, sym};
7+
use rustc_span::Span;
88

99
use crate::context::Stage;
1010
use crate::{AttributeParser, ShouldEmit};
@@ -23,11 +23,6 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
2323
}
2424

2525
let name = (attr_path.segments.len() == 1).then_some(attr_path.segments[0]);
26-
if let Some(name) = name
27-
&& [sym::cfg_trace, sym::cfg_attr_trace].contains(&name)
28-
{
29-
return;
30-
}
3126

3227
// FIXME: We should retrieve this information from the attribute parsers instead of from `BUILTIN_ATTRIBUTE_MAP`
3328
let builtin_attr_info = name.and_then(|name| BUILTIN_ATTRIBUTE_MAP.get(&name));

compiler/rustc_expand/src/config.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ use rustc_ast::tokenstream::{
77
AttrTokenStream, AttrTokenTree, LazyAttrTokenStream, Spacing, TokenTree,
88
};
99
use rustc_ast::{
10-
self as ast, AttrKind, AttrStyle, Attribute, HasAttrs, HasTokens, MetaItem, MetaItemInner,
11-
NodeId, NormalAttr,
10+
self as ast, AttrItemKind, AttrKind, AttrStyle, Attribute, EarlyParsedAttribute, HasAttrs,
11+
HasTokens, MetaItem, MetaItemInner, NodeId, NormalAttr,
1212
};
1313
use rustc_attr_parsing as attr;
1414
use rustc_attr_parsing::{
@@ -288,7 +288,9 @@ impl<'a> StripUnconfigured<'a> {
288288
pub(crate) fn expand_cfg_attr(&self, cfg_attr: &Attribute, recursive: bool) -> Vec<Attribute> {
289289
// A trace attribute left in AST in place of the original `cfg_attr` attribute.
290290
// It can later be used by lints or other diagnostics.
291-
let trace_attr = attr_into_trace(cfg_attr.clone(), sym::cfg_attr_trace);
291+
let mut trace_attr = cfg_attr.clone();
292+
trace_attr.replace_args(AttrItemKind::Parsed(EarlyParsedAttribute::CfgAttrTrace));
293+
let trace_attr = attr_into_trace(trace_attr, sym::cfg_attr_trace);
292294

293295
let Some((cfg_predicate, expanded_attrs)) =
294296
rustc_attr_parsing::parse_cfg_attr(cfg_attr, &self.sess, self.features)

0 commit comments

Comments
 (0)