Skip to content

Commit

Permalink
Auto merge of rust-lang#34253 - jseyfried:improve_multi_modifiers, r=nrc
Browse files Browse the repository at this point in the history
Allow `MultiItemModifier`s to expand into zero or many items

Fixes rust-lang#34223.
r? @nrc
  • Loading branch information
bors authored Jun 23, 2016
2 parents 4960f2f + 58372af commit 3908913
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 100 deletions.
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 @@ -40,29 +41,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 @@ -129,32 +132,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 @@ -724,11 +725,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 @@ -796,29 +793,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 @@ -864,46 +838,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

0 comments on commit 3908913

Please sign in to comment.