Skip to content

Commit 7bfa11d

Browse files
committed
[WIP] Expand attributes in left-to-right order
1 parent 342db70 commit 7bfa11d

18 files changed

+782
-444
lines changed

compiler/rustc_attr/src/builtin.rs

+12
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,18 @@ pub fn is_builtin_attr(attr: &Attribute) -> bool {
1515
attr.is_doc_comment() || attr.ident().filter(|ident| is_builtin_attr_name(ident.name)).is_some()
1616
}
1717

18+
pub fn is_inert_builtin_attr(attr: &Attribute) -> bool {
19+
attr.is_doc_comment()
20+
|| attr
21+
.ident()
22+
.filter(|ident| {
23+
ident.name != sym::cfg
24+
&& ident.name != sym::cfg_attr
25+
&& is_builtin_attr_name(ident.name)
26+
})
27+
.is_some()
28+
}
29+
1830
enum AttrError {
1931
MultipleItem(String),
2032
UnknownMetaItem(String, &'static [&'static str]),

compiler/rustc_builtin_macros/src/cfg_eval.rs

+22-24
Original file line numberDiff line numberDiff line change
@@ -35,50 +35,48 @@ crate fn cfg_eval(ecx: &ExtCtxt<'_>, annotatable: Annotatable) -> Vec<Annotatabl
3535
config_tokens: true,
3636
},
3737
};
38-
let annotatable = visitor.configure_annotatable(annotatable);
39-
vec![annotatable]
38+
visitor.configure_annotatable(annotatable).map_or(Vec::new(), |annotatable| vec![annotatable])
4039
}
4140

4241
struct CfgEval<'a, 'b> {
4342
cfg: &'a mut StripUnconfigured<'b>,
4443
}
4544

46-
fn flat_map_annotatable(vis: &mut impl MutVisitor, annotatable: Annotatable) -> Annotatable {
45+
fn flat_map_annotatable(
46+
vis: &mut impl MutVisitor,
47+
annotatable: Annotatable,
48+
) -> Option<Annotatable> {
4749
// Since the item itself has already been configured by the InvocationCollector,
4850
// we know that fold result vector will contain exactly one element
4951
match annotatable {
50-
Annotatable::Item(item) => Annotatable::Item(vis.flat_map_item(item).pop().unwrap()),
52+
Annotatable::Item(item) => vis.flat_map_item(item).pop().map(Annotatable::Item),
5153
Annotatable::TraitItem(item) => {
52-
Annotatable::TraitItem(vis.flat_map_trait_item(item).pop().unwrap())
54+
vis.flat_map_trait_item(item).pop().map(Annotatable::TraitItem)
5355
}
5456
Annotatable::ImplItem(item) => {
55-
Annotatable::ImplItem(vis.flat_map_impl_item(item).pop().unwrap())
57+
vis.flat_map_impl_item(item).pop().map(Annotatable::ImplItem)
5658
}
5759
Annotatable::ForeignItem(item) => {
58-
Annotatable::ForeignItem(vis.flat_map_foreign_item(item).pop().unwrap())
60+
vis.flat_map_foreign_item(item).pop().map(Annotatable::ForeignItem)
5961
}
6062
Annotatable::Stmt(stmt) => {
61-
Annotatable::Stmt(stmt.map(|stmt| vis.flat_map_stmt(stmt).pop().unwrap()))
63+
vis.flat_map_stmt(stmt.into_inner()).pop().map(P).map(Annotatable::Stmt)
6264
}
63-
Annotatable::Expr(mut expr) => Annotatable::Expr({
65+
Annotatable::Expr(mut expr) => {
6466
vis.visit_expr(&mut expr);
65-
expr
66-
}),
67-
Annotatable::Arm(arm) => Annotatable::Arm(vis.flat_map_arm(arm).pop().unwrap()),
68-
Annotatable::ExprField(field) => {
69-
Annotatable::ExprField(vis.flat_map_expr_field(field).pop().unwrap())
67+
Some(Annotatable::Expr(expr))
7068
}
71-
Annotatable::PatField(fp) => {
72-
Annotatable::PatField(vis.flat_map_pat_field(fp).pop().unwrap())
69+
Annotatable::Arm(arm) => vis.flat_map_arm(arm).pop().map(Annotatable::Arm),
70+
Annotatable::ExprField(field) => {
71+
vis.flat_map_expr_field(field).pop().map(Annotatable::ExprField)
7372
}
73+
Annotatable::PatField(fp) => vis.flat_map_pat_field(fp).pop().map(Annotatable::PatField),
7474
Annotatable::GenericParam(param) => {
75-
Annotatable::GenericParam(vis.flat_map_generic_param(param).pop().unwrap())
76-
}
77-
Annotatable::Param(param) => Annotatable::Param(vis.flat_map_param(param).pop().unwrap()),
78-
Annotatable::FieldDef(sf) => {
79-
Annotatable::FieldDef(vis.flat_map_field_def(sf).pop().unwrap())
75+
vis.flat_map_generic_param(param).pop().map(Annotatable::GenericParam)
8076
}
81-
Annotatable::Variant(v) => Annotatable::Variant(vis.flat_map_variant(v).pop().unwrap()),
77+
Annotatable::Param(param) => vis.flat_map_param(param).pop().map(Annotatable::Param),
78+
Annotatable::FieldDef(sf) => vis.flat_map_field_def(sf).pop().map(Annotatable::FieldDef),
79+
Annotatable::Variant(v) => vis.flat_map_variant(v).pop().map(Annotatable::Variant),
8280
}
8381
}
8482

@@ -123,11 +121,11 @@ impl CfgEval<'_, '_> {
123121
self.cfg.configure(node)
124122
}
125123

126-
pub fn configure_annotatable(&mut self, mut annotatable: Annotatable) -> Annotatable {
124+
pub fn configure_annotatable(&mut self, mut annotatable: Annotatable) -> Option<Annotatable> {
127125
// Tokenizing and re-parsing the `Annotatable` can have a significant
128126
// performance impact, so try to avoid it if possible
129127
if !CfgFinder::has_cfg_or_cfg_attr(&annotatable) {
130-
return annotatable;
128+
return Some(annotatable);
131129
}
132130

133131
// The majority of parsed attribute targets will never need to have early cfg-expansion

compiler/rustc_builtin_macros/src/test.rs

+7
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use rustc_ast::attr;
77
use rustc_ast::ptr::P;
88
use rustc_ast_pretty::pprust;
99
use rustc_expand::base::*;
10+
use rustc_expand::config::StripUnconfigured;
1011
use rustc_session::Session;
1112
use rustc_span::symbol::{sym, Ident, Symbol};
1213
use rustc_span::Span;
@@ -55,6 +56,12 @@ pub fn expand_test(
5556
item: Annotatable,
5657
) -> Vec<Annotatable> {
5758
check_builtin_macro_attribute(cx, meta_item, sym::test);
59+
let mut cfg =
60+
StripUnconfigured { sess: cx.sess, features: cx.ecfg.features, config_tokens: true };
61+
let item = match cfg.configure(item) {
62+
Some(item) => item,
63+
None => return Vec::new(),
64+
};
5865
expand_test_or_bench(cx, attr_sp, item, false)
5966
}
6067

compiler/rustc_expand/src/base.rs

+59-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use crate::module::DirOwnership;
33

44
use rustc_ast::ptr::P;
55
use rustc_ast::token::{self, Nonterminal};
6-
use rustc_ast::tokenstream::{CanSynthesizeMissingTokens, TokenStream};
6+
use rustc_ast::tokenstream::{CanSynthesizeMissingTokens, LazyTokenStream, TokenStream};
77
use rustc_ast::visit::{AssocCtxt, Visitor};
88
use rustc_ast::{self as ast, AstLike, Attribute, Item, NodeId, PatKind};
99
use rustc_attr::{self as attr, Deprecation, Stability};
@@ -46,6 +46,64 @@ pub enum Annotatable {
4646
Variant(ast::Variant),
4747
}
4848

49+
impl AstLike for Annotatable {
50+
const SUPPORTS_CUSTOM_INNER_ATTRS: bool = true;
51+
52+
fn attrs(&self) -> &[Attribute] {
53+
match *self {
54+
Annotatable::Item(ref item) => &item.attrs,
55+
Annotatable::TraitItem(ref trait_item) => &trait_item.attrs,
56+
Annotatable::ImplItem(ref impl_item) => &impl_item.attrs,
57+
Annotatable::ForeignItem(ref foreign_item) => &foreign_item.attrs,
58+
Annotatable::Stmt(ref stmt) => stmt.attrs(),
59+
Annotatable::Expr(ref expr) => &expr.attrs,
60+
Annotatable::Arm(ref arm) => &arm.attrs,
61+
Annotatable::ExprField(ref field) => &field.attrs,
62+
Annotatable::PatField(ref fp) => &fp.attrs,
63+
Annotatable::GenericParam(ref gp) => &gp.attrs,
64+
Annotatable::Param(ref p) => &p.attrs,
65+
Annotatable::FieldDef(ref sf) => &sf.attrs,
66+
Annotatable::Variant(ref v) => &v.attrs(),
67+
}
68+
}
69+
70+
fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) {
71+
match self {
72+
Annotatable::Item(item) => item.visit_attrs(f),
73+
Annotatable::TraitItem(trait_item) => trait_item.visit_attrs(f),
74+
Annotatable::ImplItem(impl_item) => impl_item.visit_attrs(f),
75+
Annotatable::ForeignItem(foreign_item) => foreign_item.visit_attrs(f),
76+
Annotatable::Stmt(stmt) => stmt.visit_attrs(f),
77+
Annotatable::Expr(expr) => expr.visit_attrs(f),
78+
Annotatable::Arm(arm) => arm.visit_attrs(f),
79+
Annotatable::ExprField(field) => field.visit_attrs(f),
80+
Annotatable::PatField(fp) => fp.visit_attrs(f),
81+
Annotatable::GenericParam(gp) => gp.visit_attrs(f),
82+
Annotatable::Param(p) => p.visit_attrs(f),
83+
Annotatable::FieldDef(sf) => sf.visit_attrs(f),
84+
Annotatable::Variant(v) => v.visit_attrs(f),
85+
}
86+
}
87+
88+
fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> {
89+
match self {
90+
Annotatable::Item(item) => item.tokens_mut(),
91+
Annotatable::TraitItem(trait_item) => trait_item.tokens_mut(),
92+
Annotatable::ImplItem(impl_item) => impl_item.tokens_mut(),
93+
Annotatable::ForeignItem(foreign_item) => foreign_item.tokens_mut(),
94+
Annotatable::Stmt(stmt) => stmt.tokens_mut(),
95+
Annotatable::Expr(expr) => expr.tokens_mut(),
96+
Annotatable::Arm(arm) => arm.tokens_mut(),
97+
Annotatable::ExprField(field) => field.tokens_mut(),
98+
Annotatable::PatField(fp) => fp.tokens_mut(),
99+
Annotatable::GenericParam(gp) => gp.tokens_mut(),
100+
Annotatable::Param(p) => p.tokens_mut(),
101+
Annotatable::FieldDef(sf) => sf.tokens_mut(),
102+
Annotatable::Variant(v) => v.tokens_mut(),
103+
}
104+
}
105+
}
106+
49107
impl Annotatable {
50108
pub fn span(&self) -> Span {
51109
match *self {

compiler/rustc_expand/src/config.rs

+93-34
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,66 @@ impl<'a> StripUnconfigured<'a> {
415415
.collect()
416416
}
417417

418+
crate fn expand_cfg_attr(&mut self, attr: Attribute) -> Vec<Attribute> {
419+
let (cfg_predicate, expanded_attrs) = match self.parse_cfg_attr(&attr) {
420+
None => return vec![],
421+
Some(r) => r,
422+
};
423+
424+
if !attr::cfg_matches(&cfg_predicate, &self.sess.parse_sess, self.features) {
425+
return vec![];
426+
}
427+
428+
// We call `process_cfg_attr` recursively in case there's a
429+
// `cfg_attr` inside of another `cfg_attr`. E.g.
430+
// `#[cfg_attr(false, cfg_attr(true, some_attr))]`.
431+
expanded_attrs
432+
.into_iter()
433+
.map(|(item, span)| {
434+
let orig_tokens = attr.tokens().to_tokenstream();
435+
436+
// We are taking an attribute of the form `#[cfg_attr(pred, attr)]`
437+
// and producing an attribute of the form `#[attr]`. We
438+
// have captured tokens for `attr` itself, but we need to
439+
// synthesize tokens for the wrapper `#` and `[]`, which
440+
// we do below.
441+
442+
// Use the `#` in `#[cfg_attr(pred, attr)]` as the `#` token
443+
// for `attr` when we expand it to `#[attr]`
444+
let mut orig_trees = orig_tokens.trees();
445+
let pound_token = match orig_trees.next().unwrap() {
446+
TokenTree::Token(token @ Token { kind: TokenKind::Pound, .. }) => token,
447+
_ => panic!("Bad tokens for attribute {:?}", attr),
448+
};
449+
let pound_span = pound_token.span;
450+
451+
let mut trees = vec![(AttrAnnotatedTokenTree::Token(pound_token), Spacing::Alone)];
452+
if attr.style == AttrStyle::Inner {
453+
// For inner attributes, we do the same thing for the `!` in `#![some_attr]`
454+
let bang_token = match orig_trees.next().unwrap() {
455+
TokenTree::Token(token @ Token { kind: TokenKind::Not, .. }) => token,
456+
_ => panic!("Bad tokens for attribute {:?}", attr),
457+
};
458+
trees.push((AttrAnnotatedTokenTree::Token(bang_token), Spacing::Alone));
459+
}
460+
// We don't really have a good span to use for the syntheized `[]`
461+
// in `#[attr]`, so just use the span of the `#` token.
462+
let bracket_group = AttrAnnotatedTokenTree::Delimited(
463+
DelimSpan::from_single(pound_span),
464+
DelimToken::Bracket,
465+
item.tokens
466+
.as_ref()
467+
.unwrap_or_else(|| panic!("Missing tokens for {:?}", item))
468+
.create_token_stream(),
469+
);
470+
trees.push((bracket_group, Spacing::Alone));
471+
let tokens = Some(LazyTokenStream::new(AttrAnnotatedTokenStream::new(trees)));
472+
473+
attr::mk_attr_from_item(item, tokens, attr.style, span)
474+
})
475+
.collect()
476+
}
477+
418478
fn parse_cfg_attr(&self, attr: &Attribute) -> Option<(MetaItem, Vec<(AttrItem, Span)>)> {
419479
match attr.get_normal_item().args {
420480
ast::MacArgs::Delimited(dspan, delim, ref tts) if !tts.is_empty() => {
@@ -453,43 +513,42 @@ impl<'a> StripUnconfigured<'a> {
453513

454514
/// Determines if a node with the given attributes should be included in this configuration.
455515
fn in_cfg(&self, attrs: &[Attribute]) -> bool {
456-
attrs.iter().all(|attr| {
457-
if !is_cfg(self.sess, attr) {
516+
attrs.iter().all(|attr| !is_cfg(self.sess, attr) || self.cfg_true(attr))
517+
}
518+
519+
crate fn cfg_true(&self, attr: &Attribute) -> bool {
520+
let meta_item = match validate_attr::parse_meta(&self.sess.parse_sess, attr) {
521+
Ok(meta_item) => meta_item,
522+
Err(mut err) => {
523+
err.emit();
458524
return true;
459525
}
460-
let meta_item = match validate_attr::parse_meta(&self.sess.parse_sess, attr) {
461-
Ok(meta_item) => meta_item,
462-
Err(mut err) => {
463-
err.emit();
464-
return true;
465-
}
466-
};
467-
let error = |span, msg, suggestion: &str| {
468-
let mut err = self.sess.parse_sess.span_diagnostic.struct_span_err(span, msg);
469-
if !suggestion.is_empty() {
470-
err.span_suggestion(
471-
span,
472-
"expected syntax is",
473-
suggestion.into(),
474-
Applicability::MaybeIncorrect,
475-
);
476-
}
477-
err.emit();
478-
true
479-
};
480-
let span = meta_item.span;
481-
match meta_item.meta_item_list() {
482-
None => error(span, "`cfg` is not followed by parentheses", "cfg(/* predicate */)"),
483-
Some([]) => error(span, "`cfg` predicate is not specified", ""),
484-
Some([_, .., l]) => error(l.span(), "multiple `cfg` predicates are specified", ""),
485-
Some([single]) => match single.meta_item() {
486-
Some(meta_item) => {
487-
attr::cfg_matches(meta_item, &self.sess.parse_sess, self.features)
488-
}
489-
None => error(single.span(), "`cfg` predicate key cannot be a literal", ""),
490-
},
526+
};
527+
let error = |span, msg, suggestion: &str| {
528+
let mut err = self.sess.parse_sess.span_diagnostic.struct_span_err(span, msg);
529+
if !suggestion.is_empty() {
530+
err.span_suggestion(
531+
span,
532+
"expected syntax is",
533+
suggestion.into(),
534+
Applicability::MaybeIncorrect,
535+
);
491536
}
492-
})
537+
err.emit();
538+
true
539+
};
540+
let span = meta_item.span;
541+
match meta_item.meta_item_list() {
542+
None => error(span, "`cfg` is not followed by parentheses", "cfg(/* predicate */)"),
543+
Some([]) => error(span, "`cfg` predicate is not specified", ""),
544+
Some([_, .., l]) => error(l.span(), "multiple `cfg` predicates are specified", ""),
545+
Some([single]) => match single.meta_item() {
546+
Some(meta_item) => {
547+
attr::cfg_matches(meta_item, &self.sess.parse_sess, self.features)
548+
}
549+
None => error(single.span(), "`cfg` predicate key cannot be a literal", ""),
550+
},
551+
}
493552
}
494553

495554
/// If attributes are not allowed on expressions, emit an error for `attr`

0 commit comments

Comments
 (0)