Skip to content

Commit

Permalink
[WIP] Expand attributes in left-to-right order
Browse files Browse the repository at this point in the history
  • Loading branch information
petrochenkov committed May 5, 2021
1 parent 342db70 commit 7bfa11d
Show file tree
Hide file tree
Showing 18 changed files with 782 additions and 444 deletions.
12 changes: 12 additions & 0 deletions compiler/rustc_attr/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,18 @@ pub fn is_builtin_attr(attr: &Attribute) -> bool {
attr.is_doc_comment() || attr.ident().filter(|ident| is_builtin_attr_name(ident.name)).is_some()
}

pub fn is_inert_builtin_attr(attr: &Attribute) -> bool {
attr.is_doc_comment()
|| attr
.ident()
.filter(|ident| {
ident.name != sym::cfg
&& ident.name != sym::cfg_attr
&& is_builtin_attr_name(ident.name)
})
.is_some()
}

enum AttrError {
MultipleItem(String),
UnknownMetaItem(String, &'static [&'static str]),
Expand Down
46 changes: 22 additions & 24 deletions compiler/rustc_builtin_macros/src/cfg_eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,50 +35,48 @@ crate fn cfg_eval(ecx: &ExtCtxt<'_>, annotatable: Annotatable) -> Vec<Annotatabl
config_tokens: true,
},
};
let annotatable = visitor.configure_annotatable(annotatable);
vec![annotatable]
visitor.configure_annotatable(annotatable).map_or(Vec::new(), |annotatable| vec![annotatable])
}

struct CfgEval<'a, 'b> {
cfg: &'a mut StripUnconfigured<'b>,
}

fn flat_map_annotatable(vis: &mut impl MutVisitor, annotatable: Annotatable) -> Annotatable {
fn flat_map_annotatable(
vis: &mut impl MutVisitor,
annotatable: Annotatable,
) -> Option<Annotatable> {
// Since the item itself has already been configured by the InvocationCollector,
// we know that fold result vector will contain exactly one element
match annotatable {
Annotatable::Item(item) => Annotatable::Item(vis.flat_map_item(item).pop().unwrap()),
Annotatable::Item(item) => vis.flat_map_item(item).pop().map(Annotatable::Item),
Annotatable::TraitItem(item) => {
Annotatable::TraitItem(vis.flat_map_trait_item(item).pop().unwrap())
vis.flat_map_trait_item(item).pop().map(Annotatable::TraitItem)
}
Annotatable::ImplItem(item) => {
Annotatable::ImplItem(vis.flat_map_impl_item(item).pop().unwrap())
vis.flat_map_impl_item(item).pop().map(Annotatable::ImplItem)
}
Annotatable::ForeignItem(item) => {
Annotatable::ForeignItem(vis.flat_map_foreign_item(item).pop().unwrap())
vis.flat_map_foreign_item(item).pop().map(Annotatable::ForeignItem)
}
Annotatable::Stmt(stmt) => {
Annotatable::Stmt(stmt.map(|stmt| vis.flat_map_stmt(stmt).pop().unwrap()))
vis.flat_map_stmt(stmt.into_inner()).pop().map(P).map(Annotatable::Stmt)
}
Annotatable::Expr(mut expr) => Annotatable::Expr({
Annotatable::Expr(mut expr) => {
vis.visit_expr(&mut expr);
expr
}),
Annotatable::Arm(arm) => Annotatable::Arm(vis.flat_map_arm(arm).pop().unwrap()),
Annotatable::ExprField(field) => {
Annotatable::ExprField(vis.flat_map_expr_field(field).pop().unwrap())
Some(Annotatable::Expr(expr))
}
Annotatable::PatField(fp) => {
Annotatable::PatField(vis.flat_map_pat_field(fp).pop().unwrap())
Annotatable::Arm(arm) => vis.flat_map_arm(arm).pop().map(Annotatable::Arm),
Annotatable::ExprField(field) => {
vis.flat_map_expr_field(field).pop().map(Annotatable::ExprField)
}
Annotatable::PatField(fp) => vis.flat_map_pat_field(fp).pop().map(Annotatable::PatField),
Annotatable::GenericParam(param) => {
Annotatable::GenericParam(vis.flat_map_generic_param(param).pop().unwrap())
}
Annotatable::Param(param) => Annotatable::Param(vis.flat_map_param(param).pop().unwrap()),
Annotatable::FieldDef(sf) => {
Annotatable::FieldDef(vis.flat_map_field_def(sf).pop().unwrap())
vis.flat_map_generic_param(param).pop().map(Annotatable::GenericParam)
}
Annotatable::Variant(v) => Annotatable::Variant(vis.flat_map_variant(v).pop().unwrap()),
Annotatable::Param(param) => vis.flat_map_param(param).pop().map(Annotatable::Param),
Annotatable::FieldDef(sf) => vis.flat_map_field_def(sf).pop().map(Annotatable::FieldDef),
Annotatable::Variant(v) => vis.flat_map_variant(v).pop().map(Annotatable::Variant),
}
}

Expand Down Expand Up @@ -123,11 +121,11 @@ impl CfgEval<'_, '_> {
self.cfg.configure(node)
}

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

// The majority of parsed attribute targets will never need to have early cfg-expansion
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_builtin_macros/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use rustc_ast::attr;
use rustc_ast::ptr::P;
use rustc_ast_pretty::pprust;
use rustc_expand::base::*;
use rustc_expand::config::StripUnconfigured;
use rustc_session::Session;
use rustc_span::symbol::{sym, Ident, Symbol};
use rustc_span::Span;
Expand Down Expand Up @@ -55,6 +56,12 @@ pub fn expand_test(
item: Annotatable,
) -> Vec<Annotatable> {
check_builtin_macro_attribute(cx, meta_item, sym::test);
let mut cfg =
StripUnconfigured { sess: cx.sess, features: cx.ecfg.features, config_tokens: true };
let item = match cfg.configure(item) {
Some(item) => item,
None => return Vec::new(),
};
expand_test_or_bench(cx, attr_sp, item, false)
}

Expand Down
60 changes: 59 additions & 1 deletion compiler/rustc_expand/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::module::DirOwnership;

use rustc_ast::ptr::P;
use rustc_ast::token::{self, Nonterminal};
use rustc_ast::tokenstream::{CanSynthesizeMissingTokens, TokenStream};
use rustc_ast::tokenstream::{CanSynthesizeMissingTokens, LazyTokenStream, TokenStream};
use rustc_ast::visit::{AssocCtxt, Visitor};
use rustc_ast::{self as ast, AstLike, Attribute, Item, NodeId, PatKind};
use rustc_attr::{self as attr, Deprecation, Stability};
Expand Down Expand Up @@ -46,6 +46,64 @@ pub enum Annotatable {
Variant(ast::Variant),
}

impl AstLike for Annotatable {
const SUPPORTS_CUSTOM_INNER_ATTRS: bool = true;

fn attrs(&self) -> &[Attribute] {
match *self {
Annotatable::Item(ref item) => &item.attrs,
Annotatable::TraitItem(ref trait_item) => &trait_item.attrs,
Annotatable::ImplItem(ref impl_item) => &impl_item.attrs,
Annotatable::ForeignItem(ref foreign_item) => &foreign_item.attrs,
Annotatable::Stmt(ref stmt) => stmt.attrs(),
Annotatable::Expr(ref expr) => &expr.attrs,
Annotatable::Arm(ref arm) => &arm.attrs,
Annotatable::ExprField(ref field) => &field.attrs,
Annotatable::PatField(ref fp) => &fp.attrs,
Annotatable::GenericParam(ref gp) => &gp.attrs,
Annotatable::Param(ref p) => &p.attrs,
Annotatable::FieldDef(ref sf) => &sf.attrs,
Annotatable::Variant(ref v) => &v.attrs(),
}
}

fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<Attribute>)) {
match self {
Annotatable::Item(item) => item.visit_attrs(f),
Annotatable::TraitItem(trait_item) => trait_item.visit_attrs(f),
Annotatable::ImplItem(impl_item) => impl_item.visit_attrs(f),
Annotatable::ForeignItem(foreign_item) => foreign_item.visit_attrs(f),
Annotatable::Stmt(stmt) => stmt.visit_attrs(f),
Annotatable::Expr(expr) => expr.visit_attrs(f),
Annotatable::Arm(arm) => arm.visit_attrs(f),
Annotatable::ExprField(field) => field.visit_attrs(f),
Annotatable::PatField(fp) => fp.visit_attrs(f),
Annotatable::GenericParam(gp) => gp.visit_attrs(f),
Annotatable::Param(p) => p.visit_attrs(f),
Annotatable::FieldDef(sf) => sf.visit_attrs(f),
Annotatable::Variant(v) => v.visit_attrs(f),
}
}

fn tokens_mut(&mut self) -> Option<&mut Option<LazyTokenStream>> {
match self {
Annotatable::Item(item) => item.tokens_mut(),
Annotatable::TraitItem(trait_item) => trait_item.tokens_mut(),
Annotatable::ImplItem(impl_item) => impl_item.tokens_mut(),
Annotatable::ForeignItem(foreign_item) => foreign_item.tokens_mut(),
Annotatable::Stmt(stmt) => stmt.tokens_mut(),
Annotatable::Expr(expr) => expr.tokens_mut(),
Annotatable::Arm(arm) => arm.tokens_mut(),
Annotatable::ExprField(field) => field.tokens_mut(),
Annotatable::PatField(fp) => fp.tokens_mut(),
Annotatable::GenericParam(gp) => gp.tokens_mut(),
Annotatable::Param(p) => p.tokens_mut(),
Annotatable::FieldDef(sf) => sf.tokens_mut(),
Annotatable::Variant(v) => v.tokens_mut(),
}
}
}

impl Annotatable {
pub fn span(&self) -> Span {
match *self {
Expand Down
127 changes: 93 additions & 34 deletions compiler/rustc_expand/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,66 @@ impl<'a> StripUnconfigured<'a> {
.collect()
}

crate fn expand_cfg_attr(&mut self, attr: Attribute) -> Vec<Attribute> {
let (cfg_predicate, expanded_attrs) = match self.parse_cfg_attr(&attr) {
None => return vec![],
Some(r) => r,
};

if !attr::cfg_matches(&cfg_predicate, &self.sess.parse_sess, self.features) {
return vec![];
}

// We call `process_cfg_attr` recursively in case there's a
// `cfg_attr` inside of another `cfg_attr`. E.g.
// `#[cfg_attr(false, cfg_attr(true, some_attr))]`.
expanded_attrs
.into_iter()
.map(|(item, span)| {
let orig_tokens = attr.tokens().to_tokenstream();

// We are taking an attribute of the form `#[cfg_attr(pred, attr)]`
// and producing an attribute of the form `#[attr]`. We
// have captured tokens for `attr` itself, but we need to
// synthesize tokens for the wrapper `#` and `[]`, which
// we do below.

// Use the `#` in `#[cfg_attr(pred, attr)]` as the `#` token
// for `attr` when we expand it to `#[attr]`
let mut orig_trees = orig_tokens.trees();
let pound_token = match orig_trees.next().unwrap() {
TokenTree::Token(token @ Token { kind: TokenKind::Pound, .. }) => token,
_ => panic!("Bad tokens for attribute {:?}", attr),
};
let pound_span = pound_token.span;

let mut trees = vec![(AttrAnnotatedTokenTree::Token(pound_token), Spacing::Alone)];
if attr.style == AttrStyle::Inner {
// For inner attributes, we do the same thing for the `!` in `#![some_attr]`
let bang_token = match orig_trees.next().unwrap() {
TokenTree::Token(token @ Token { kind: TokenKind::Not, .. }) => token,
_ => panic!("Bad tokens for attribute {:?}", attr),
};
trees.push((AttrAnnotatedTokenTree::Token(bang_token), Spacing::Alone));
}
// We don't really have a good span to use for the syntheized `[]`
// in `#[attr]`, so just use the span of the `#` token.
let bracket_group = AttrAnnotatedTokenTree::Delimited(
DelimSpan::from_single(pound_span),
DelimToken::Bracket,
item.tokens
.as_ref()
.unwrap_or_else(|| panic!("Missing tokens for {:?}", item))
.create_token_stream(),
);
trees.push((bracket_group, Spacing::Alone));
let tokens = Some(LazyTokenStream::new(AttrAnnotatedTokenStream::new(trees)));

attr::mk_attr_from_item(item, tokens, attr.style, span)
})
.collect()
}

fn parse_cfg_attr(&self, attr: &Attribute) -> Option<(MetaItem, Vec<(AttrItem, Span)>)> {
match attr.get_normal_item().args {
ast::MacArgs::Delimited(dspan, delim, ref tts) if !tts.is_empty() => {
Expand Down Expand Up @@ -453,43 +513,42 @@ impl<'a> StripUnconfigured<'a> {

/// Determines if a node with the given attributes should be included in this configuration.
fn in_cfg(&self, attrs: &[Attribute]) -> bool {
attrs.iter().all(|attr| {
if !is_cfg(self.sess, attr) {
attrs.iter().all(|attr| !is_cfg(self.sess, attr) || self.cfg_true(attr))
}

crate fn cfg_true(&self, attr: &Attribute) -> bool {
let meta_item = match validate_attr::parse_meta(&self.sess.parse_sess, attr) {
Ok(meta_item) => meta_item,
Err(mut err) => {
err.emit();
return true;
}
let meta_item = match validate_attr::parse_meta(&self.sess.parse_sess, attr) {
Ok(meta_item) => meta_item,
Err(mut err) => {
err.emit();
return true;
}
};
let error = |span, msg, suggestion: &str| {
let mut err = self.sess.parse_sess.span_diagnostic.struct_span_err(span, msg);
if !suggestion.is_empty() {
err.span_suggestion(
span,
"expected syntax is",
suggestion.into(),
Applicability::MaybeIncorrect,
);
}
err.emit();
true
};
let span = meta_item.span;
match meta_item.meta_item_list() {
None => error(span, "`cfg` is not followed by parentheses", "cfg(/* predicate */)"),
Some([]) => error(span, "`cfg` predicate is not specified", ""),
Some([_, .., l]) => error(l.span(), "multiple `cfg` predicates are specified", ""),
Some([single]) => match single.meta_item() {
Some(meta_item) => {
attr::cfg_matches(meta_item, &self.sess.parse_sess, self.features)
}
None => error(single.span(), "`cfg` predicate key cannot be a literal", ""),
},
};
let error = |span, msg, suggestion: &str| {
let mut err = self.sess.parse_sess.span_diagnostic.struct_span_err(span, msg);
if !suggestion.is_empty() {
err.span_suggestion(
span,
"expected syntax is",
suggestion.into(),
Applicability::MaybeIncorrect,
);
}
})
err.emit();
true
};
let span = meta_item.span;
match meta_item.meta_item_list() {
None => error(span, "`cfg` is not followed by parentheses", "cfg(/* predicate */)"),
Some([]) => error(span, "`cfg` predicate is not specified", ""),
Some([_, .., l]) => error(l.span(), "multiple `cfg` predicates are specified", ""),
Some([single]) => match single.meta_item() {
Some(meta_item) => {
attr::cfg_matches(meta_item, &self.sess.parse_sess, self.features)
}
None => error(single.span(), "`cfg` predicate key cannot be a literal", ""),
},
}
}

/// If attributes are not allowed on expressions, emit an error for `attr`
Expand Down
Loading

0 comments on commit 7bfa11d

Please sign in to comment.