Skip to content

Allow MultiItemModifiers to expand into zero or many items #34253

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jun 23, 2016
Merged
Show file tree
Hide file tree
Changes from all 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
59 changes: 32 additions & 27 deletions src/libsyntax/ext/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub use self::SyntaxExtension::*;

use ast;
use ast::{Name, PatKind};
use attr::HasAttrs;
use codemap;
use codemap::{CodeMap, Span, ExpnId, ExpnInfo, NO_EXPANSION};
use errors::DiagnosticBuilder;
Expand Down Expand Up @@ -41,29 +42,31 @@ pub enum Annotatable {
ImplItem(P<ast::ImplItem>),
}

impl Annotatable {
pub fn attrs(&self) -> &[ast::Attribute] {
impl HasAttrs for Annotatable {
fn attrs(&self) -> &[ast::Attribute] {
match *self {
Annotatable::Item(ref i) => &i.attrs,
Annotatable::TraitItem(ref ti) => &ti.attrs,
Annotatable::ImplItem(ref ii) => &ii.attrs,
Annotatable::Item(ref item) => &item.attrs,
Annotatable::TraitItem(ref trait_item) => &trait_item.attrs,
Annotatable::ImplItem(ref impl_item) => &impl_item.attrs,
}
}

pub fn fold_attrs(self, attrs: Vec<ast::Attribute>) -> Annotatable {
fn map_attrs<F: FnOnce(Vec<ast::Attribute>) -> Vec<ast::Attribute>>(self, f: F) -> Self {
match self {
Annotatable::Item(i) => Annotatable::Item(i.map(|i| ast::Item {
attrs: attrs,
..i
})),
Annotatable::TraitItem(i) => Annotatable::TraitItem(i.map(|ti| {
ast::TraitItem { attrs: attrs, ..ti }
})),
Annotatable::ImplItem(i) => Annotatable::ImplItem(i.map(|ii| {
ast::ImplItem { attrs: attrs, ..ii }
})),
Annotatable::Item(item) => Annotatable::Item(item.map_attrs(f)),
Annotatable::TraitItem(trait_item) => Annotatable::TraitItem(trait_item.map_attrs(f)),
Annotatable::ImplItem(impl_item) => Annotatable::ImplItem(impl_item.map_attrs(f)),
}
}
}

impl Annotatable {
pub fn attrs(&self) -> &[ast::Attribute] {
HasAttrs::attrs(self)
}
pub fn fold_attrs(self, attrs: Vec<ast::Attribute>) -> Annotatable {
self.map_attrs(|_| attrs)
}

pub fn expect_item(self) -> P<ast::Item> {
match self {
Expand Down Expand Up @@ -120,32 +123,34 @@ impl<F> MultiItemDecorator for F
}
}

// A more flexible ItemKind::Modifier (ItemKind::Modifier should go away, eventually, FIXME).
// meta_item is the annotation, item is the item being modified, parent_item
// is the impl or trait item is declared in if item is part of such a thing.
// `meta_item` is the annotation, and `item` is the item being modified.
// FIXME Decorators should follow the same pattern too.
pub trait MultiItemModifier {
fn expand(&self,
ecx: &mut ExtCtxt,
span: Span,
meta_item: &ast::MetaItem,
item: Annotatable)
-> Annotatable;
-> Vec<Annotatable>;
}

impl<F> MultiItemModifier for F
where F: Fn(&mut ExtCtxt,
Span,
&ast::MetaItem,
Annotatable) -> Annotatable
impl<F, T> MultiItemModifier for F
where F: Fn(&mut ExtCtxt, Span, &ast::MetaItem, Annotatable) -> T,
T: Into<Vec<Annotatable>>,
{
fn expand(&self,
ecx: &mut ExtCtxt,
span: Span,
meta_item: &ast::MetaItem,
item: Annotatable)
-> Annotatable {
(*self)(ecx, span, meta_item, item)
-> Vec<Annotatable> {
(*self)(ecx, span, meta_item, item).into()
}
}

impl Into<Vec<Annotatable>> for Annotatable {
fn into(self) -> Vec<Annotatable> {
vec![self]
}
}

Expand Down
99 changes: 34 additions & 65 deletions src/libsyntax/ext/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use ast::{Local, Ident, Mac_, Name, SpannedIdent};
use ast::{MacStmtStyle, Mrk, Stmt, StmtKind, ItemKind};
use ast::TokenTree;
use ast;
use attr::HasAttrs;
use ext::mtwt;
use ext::build::AstBuilder;
use attr;
Expand Down Expand Up @@ -712,11 +713,7 @@ impl<'a> Folder for PatIdentRenamer<'a> {
}
}

fn expand_annotatable(a: Annotatable,
fld: &mut MacroExpander)
-> SmallVector<Annotatable> {
let a = expand_item_multi_modifier(a, fld);

fn expand_multi_modified(a: Annotatable, fld: &mut MacroExpander) -> SmallVector<Annotatable> {
let new_items: SmallVector<Annotatable> = match a {
Annotatable::Item(it) => match it.node {
ast::ItemKind::Mac(..) => {
Expand Down Expand Up @@ -795,29 +792,6 @@ fn decorate(a: Annotatable, fld: &mut MacroExpander) -> SmallVector<Annotatable>
new_items
}

// Partition a set of attributes into one kind of attribute, and other kinds.
macro_rules! partition {
($fn_name: ident, $variant: ident) => {
#[allow(deprecated)] // The `allow` is needed because the `Modifier` variant might be used.
fn $fn_name(attrs: &[ast::Attribute],
fld: &MacroExpander)
-> (Vec<ast::Attribute>, Vec<ast::Attribute>) {
attrs.iter().cloned().partition(|attr| {
match fld.cx.syntax_env.find(intern(&attr.name())) {
Some(rc) => match *rc {
$variant(..) => true,
_ => false
},
_ => false
}
})
}
}
}

partition!(multi_modifiers, MultiModifier);


fn expand_decorators(a: Annotatable,
fld: &mut MacroExpander,
decorator_items: &mut SmallVector<Annotatable>,
Expand Down Expand Up @@ -861,46 +835,41 @@ fn expand_decorators(a: Annotatable,
}
}

fn expand_item_multi_modifier(mut it: Annotatable,
fld: &mut MacroExpander)
-> Annotatable {
let (modifiers, other_attrs) = multi_modifiers(it.attrs(), fld);

// Update the attrs, leave everything else alone. Is this mutation really a good idea?
it = it.fold_attrs(other_attrs);

if modifiers.is_empty() {
return it
}

for attr in &modifiers {
let mname = intern(&attr.name());

match fld.cx.syntax_env.find(mname) {
Some(rc) => match *rc {
MultiModifier(ref mac) => {
attr::mark_used(attr);
fld.cx.bt_push(ExpnInfo {
call_site: attr.span,
callee: NameAndSpan {
format: MacroAttribute(mname),
span: Some(attr.span),
// attributes can do whatever they like,
// for now
allow_internal_unstable: true,
}
});
it = mac.expand(fld.cx, attr.span, &attr.node.value, it);
fld.cx.bt_pop();
fn expand_annotatable(mut item: Annotatable, fld: &mut MacroExpander) -> SmallVector<Annotatable> {
let mut multi_modifier = None;
item = item.map_attrs(|mut attrs| {
for i in 0..attrs.len() {
if let Some(extension) = fld.cx.syntax_env.find(intern(&attrs[i].name())) {
if let MultiModifier(..) = *extension {
multi_modifier = Some((attrs.remove(i), extension));
break;
}
_ => unreachable!()
},
_ => unreachable!()
}
}
}
attrs
});

// Expansion may have added new ItemKind::Modifiers.
expand_item_multi_modifier(it, fld)
match multi_modifier {
None => expand_multi_modified(item, fld),
Some((attr, extension)) => match *extension {
MultiModifier(ref mac) => {
attr::mark_used(&attr);
fld.cx.bt_push(ExpnInfo {
call_site: attr.span,
callee: NameAndSpan {
format: MacroAttribute(intern(&attr.name())),
span: Some(attr.span),
// attributes can do whatever they like, for now
allow_internal_unstable: true,
}
});
let modified = mac.expand(fld.cx, attr.span, &attr.node.value, item);
fld.cx.bt_pop();
modified.into_iter().flat_map(|it| expand_annotatable(it, fld)).collect()
}
_ => unreachable!(),
}
}
}

fn expand_impl_item(ii: ast::ImplItem, fld: &mut MacroExpander)
Expand Down
17 changes: 9 additions & 8 deletions src/test/run-pass-fulldeps/auxiliary/macro_crate_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,16 @@ fn expand_identity(cx: &mut ExtCtxt, _span: Span, tts: &[TokenTree])
fn expand_into_foo_multi(cx: &mut ExtCtxt,
sp: Span,
attr: &MetaItem,
it: Annotatable) -> Annotatable {
it: Annotatable) -> Vec<Annotatable> {
match it {
Annotatable::Item(it) => {
Annotatable::Item(it) => vec![
Annotatable::Item(P(Item {
attrs: it.attrs.clone(),
..(*quote_item!(cx, enum Foo2 { Bar2, Baz2 }).unwrap()).clone()
}))
}
Annotatable::ImplItem(it) => {
})),
Annotatable::Item(quote_item!(cx, enum Foo3 { Bar }).unwrap()),
],
Annotatable::ImplItem(it) => vec![
quote_item!(cx, impl X { fn foo(&self) -> i32 { 42 } }).unwrap().and_then(|i| {
match i.node {
ItemKind::Impl(_, _, _, _, _, mut items) => {
Expand All @@ -79,8 +80,8 @@ fn expand_into_foo_multi(cx: &mut ExtCtxt,
_ => unreachable!("impl parsed to something other than impl")
}
})
}
Annotatable::TraitItem(it) => {
],
Annotatable::TraitItem(it) => vec![
quote_item!(cx, trait X { fn foo(&self) -> i32 { 0 } }).unwrap().and_then(|i| {
match i.node {
ItemKind::Trait(_, _, _, mut items) => {
Expand All @@ -89,7 +90,7 @@ fn expand_into_foo_multi(cx: &mut ExtCtxt,
_ => unreachable!("trait parsed to something other than trait")
}
})
}
],
}
}

Expand Down
2 changes: 2 additions & 0 deletions src/test/run-pass-fulldeps/macro-crate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ pub fn main() {
assert_eq!(Foo2::Bar2, Foo2::Bar2);
test(None::<Foo2>);

let _ = Foo3::Bar;

let x = 10i32;
assert_eq!(x.foo(), 42);
let x = 10u8;
Expand Down