diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index b5e4dc63e847e..d2185df52e0f4 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -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; @@ -41,29 +42,31 @@ pub enum Annotatable { ImplItem(P), } -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) -> Annotatable { + fn map_attrs) -> Vec>(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) -> Annotatable { + self.map_attrs(|_| attrs) + } pub fn expect_item(self) -> P { match self { @@ -120,9 +123,7 @@ impl 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, @@ -130,22 +131,26 @@ pub trait MultiItemModifier { span: Span, meta_item: &ast::MetaItem, item: Annotatable) - -> Annotatable; + -> Vec; } -impl MultiItemModifier for F - where F: Fn(&mut ExtCtxt, - Span, - &ast::MetaItem, - Annotatable) -> Annotatable +impl MultiItemModifier for F + where F: Fn(&mut ExtCtxt, Span, &ast::MetaItem, Annotatable) -> T, + T: Into>, { fn expand(&self, ecx: &mut ExtCtxt, span: Span, meta_item: &ast::MetaItem, item: Annotatable) - -> Annotatable { - (*self)(ecx, span, meta_item, item) + -> Vec { + (*self)(ecx, span, meta_item, item).into() + } +} + +impl Into> for Annotatable { + fn into(self) -> Vec { + vec![self] } } diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 83ddc79af8474..69e8bfdcea3c8 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -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; @@ -712,11 +713,7 @@ impl<'a> Folder for PatIdentRenamer<'a> { } } -fn expand_annotatable(a: Annotatable, - fld: &mut MacroExpander) - -> SmallVector { - let a = expand_item_multi_modifier(a, fld); - +fn expand_multi_modified(a: Annotatable, fld: &mut MacroExpander) -> SmallVector { let new_items: SmallVector = match a { Annotatable::Item(it) => match it.node { ast::ItemKind::Mac(..) => { @@ -795,29 +792,6 @@ fn decorate(a: Annotatable, fld: &mut MacroExpander) -> SmallVector 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, Vec) { - 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, @@ -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 { + 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) diff --git a/src/test/run-pass-fulldeps/auxiliary/macro_crate_test.rs b/src/test/run-pass-fulldeps/auxiliary/macro_crate_test.rs index 3516f566e8a1f..a22c3ba484926 100644 --- a/src/test/run-pass-fulldeps/auxiliary/macro_crate_test.rs +++ b/src/test/run-pass-fulldeps/auxiliary/macro_crate_test.rs @@ -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 { 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) => { @@ -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) => { @@ -89,7 +90,7 @@ fn expand_into_foo_multi(cx: &mut ExtCtxt, _ => unreachable!("trait parsed to something other than trait") } }) - } + ], } } diff --git a/src/test/run-pass-fulldeps/macro-crate.rs b/src/test/run-pass-fulldeps/macro-crate.rs index 3a38196366915..d17adff007c63 100644 --- a/src/test/run-pass-fulldeps/macro-crate.rs +++ b/src/test/run-pass-fulldeps/macro-crate.rs @@ -40,6 +40,8 @@ pub fn main() { assert_eq!(Foo2::Bar2, Foo2::Bar2); test(None::); + let _ = Foo3::Bar; + let x = 10i32; assert_eq!(x.foo(), 42); let x = 10u8;