about summary refs log tree commit diff
path: root/src/libsyntax
diff options
context:
space:
mode:
authorAlex Crichton <alex@alexcrichton.com>2015-01-15 14:11:39 -0800
committerAlex Crichton <alex@alexcrichton.com>2015-01-15 14:11:39 -0800
commit98d4d4997e300bb712254a67184caf8892af4be0 (patch)
tree21e271dd83e7f1b0e04083834868c2780756783a /src/libsyntax
parent87dce0c138af6887704fc2379b7a75e05a1e8b18 (diff)
parent98d471120a09607ad429bebf56c97b512bdf9847 (diff)
downloadrust-98d4d4997e300bb712254a67184caf8892af4be0.tar.gz
rust-98d4d4997e300bb712254a67184caf8892af4be0.zip
rollup merge of #21052: nick29581/methods-ext
Allows modifiers to be used on methods, associated types, etc.

r? @sfackler
Diffstat (limited to 'src/libsyntax')
-rw-r--r--src/libsyntax/ast.rs2
-rw-r--r--src/libsyntax/ext/base.rs106
-rw-r--r--src/libsyntax/ext/expand.rs284
-rw-r--r--src/libsyntax/fold.rs87
4 files changed, 363 insertions, 116 deletions
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index 61bc1865517..0ea429116b0 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -957,7 +957,7 @@ pub type Mac = Spanned<Mac_>;
 pub enum Mac_ {
     // NB: the additional ident for a macro_rules-style macro is actually
     // stored in the enclosing item. Oog.
-    MacInvocTT(Path, Vec<TokenTree> , SyntaxContext),   // new macro-invocation
+    MacInvocTT(Path, Vec<TokenTree>, SyntaxContext),   // new macro-invocation
 }
 
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Show, Copy)]
diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs
index 9b9d8a9ceb3..f2498abfa6a 100644
--- a/src/libsyntax/ext/base.rs
+++ b/src/libsyntax/ext/base.rs
@@ -73,6 +73,108 @@ impl<F> ItemModifier for F
     }
 }
 
+#[derive(Show,Clone)]
+pub enum Annotatable {
+    Item(P<ast::Item>),
+    TraitItem(ast::TraitItem),
+    ImplItem(ast::ImplItem),
+}
+
+impl Annotatable {
+    pub fn attrs(&self) -> &[ast::Attribute] {
+        match *self {
+            Annotatable::Item(ref i) => &i.attrs[],
+            Annotatable::TraitItem(ref i) => match *i {
+                ast::TraitItem::RequiredMethod(ref tm) => &tm.attrs[],
+                ast::TraitItem::ProvidedMethod(ref m) => &m.attrs[],
+                ast::TraitItem::TypeTraitItem(ref at) => &at.attrs[],
+            },
+            Annotatable::ImplItem(ref i) => match *i {
+                ast::ImplItem::MethodImplItem(ref m) => &m.attrs[],
+                ast::ImplItem::TypeImplItem(ref t) => &t.attrs[],
+            }
+        }
+    }
+
+    pub fn fold_attrs(self, attrs: Vec<ast::Attribute>) -> Annotatable {
+        match self {
+            Annotatable::Item(i) => Annotatable::Item(P(ast::Item {
+                attrs: attrs,
+                ..(*i).clone()
+            })),
+            Annotatable::TraitItem(i) => match i {
+                ast::TraitItem::RequiredMethod(tm) => Annotatable::TraitItem(
+                    ast::TraitItem::RequiredMethod(
+                        ast::TypeMethod { attrs: attrs, ..tm })),
+                ast::TraitItem::ProvidedMethod(m) => Annotatable::TraitItem(
+                    ast::TraitItem::ProvidedMethod(P(
+                        ast::Method { attrs: attrs, ..(*m).clone() }))),
+                ast::TraitItem::TypeTraitItem(at) => Annotatable::TraitItem(
+                    ast::TraitItem::TypeTraitItem(P(
+                        ast::AssociatedType { attrs: attrs, ..(*at).clone() }))),
+            },
+            Annotatable::ImplItem(i) => match i {
+                ast::ImplItem::MethodImplItem(m) => Annotatable::ImplItem(
+                    ast::ImplItem::MethodImplItem(P(
+                        ast::Method { attrs: attrs, ..(*m).clone() }))),
+                ast::ImplItem::TypeImplItem(t) => Annotatable::ImplItem(
+                    ast::ImplItem::TypeImplItem(P(
+                        ast::Typedef { attrs: attrs, ..(*t).clone() }))),
+            }
+        }
+    }
+
+    pub fn expect_item(self) -> P<ast::Item> {
+        match self {
+            Annotatable::Item(i) => i,
+            _ => panic!("expected Item")
+        }
+    }
+
+    pub fn expect_trait_item(self) -> ast::TraitItem {
+        match self {
+            Annotatable::TraitItem(i) => i,
+            _ => panic!("expected Item")
+        }
+    }
+
+    pub fn expect_impl_item(self) -> ast::ImplItem {
+        match self {
+            Annotatable::ImplItem(i) => i,
+            _ => panic!("expected Item")
+        }
+    }
+}
+
+// A more flexible ItemModifier (ItemModifier 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.
+// 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;
+}
+
+impl<F> MultiItemModifier for F
+    where F: Fn(&mut ExtCtxt,
+                Span,
+                &ast::MetaItem,
+                Annotatable) -> Annotatable
+{
+    fn expand(&self,
+              ecx: &mut ExtCtxt,
+              span: Span,
+              meta_item: &ast::MetaItem,
+              item: Annotatable)
+              -> Annotatable {
+        (*self)(ecx, span, meta_item, item)
+    }
+}
+
 /// Represents a thing that maps token trees to Macro Results
 pub trait TTMacroExpander {
     fn expand<'cx>(&self,
@@ -299,6 +401,10 @@ pub enum SyntaxExtension {
     /// in-place.
     Modifier(Box<ItemModifier + 'static>),
 
+    /// A syntax extension that is attached to an item and modifies it
+    /// in-place. More flexible version than Modifier.
+    MultiModifier(Box<MultiItemModifier + 'static>),
+
     /// A normal, function-like syntax extension.
     ///
     /// `bytes!` is a `NormalTT`.
diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs
index 9ef996ac317..c95bdeefd45 100644
--- a/src/libsyntax/ext/expand.rs
+++ b/src/libsyntax/ext/expand.rs
@@ -395,81 +395,15 @@ pub fn expand_item(it: P<ast::Item>, fld: &mut MacroExpander)
                    -> SmallVector<P<ast::Item>> {
     let it = expand_item_modifiers(it, fld);
 
-    let mut decorator_items = SmallVector::zero();
-    let mut new_attrs = Vec::new();
-    for attr in it.attrs.iter() {
-        let mname = attr.name();
-
-        match fld.cx.syntax_env.find(&intern(mname.get())) {
-            Some(rc) => match *rc {
-                Decorator(ref dec) => {
-                    attr::mark_used(attr);
-
-                    fld.cx.bt_push(ExpnInfo {
-                        call_site: attr.span,
-                        callee: NameAndSpan {
-                            name: mname.get().to_string(),
-                            format: MacroAttribute,
-                            span: None
-                        }
-                    });
-
-                    // we'd ideally decorator_items.push_all(expand_item(item, fld)),
-                    // but that double-mut-borrows fld
-                    let mut items: SmallVector<P<ast::Item>> = SmallVector::zero();
-                    dec.expand(fld.cx, attr.span, &*attr.node.value, &*it,
-                               box |&mut : item| items.push(item));
-                    decorator_items.extend(items.into_iter()
-                        .flat_map(|item| expand_item(item, fld).into_iter()));
-
-                    fld.cx.bt_pop();
-                }
-                _ => new_attrs.push((*attr).clone()),
-            },
-            _ => new_attrs.push((*attr).clone()),
-        }
-    }
-
-    let mut new_items = match it.node {
-        ast::ItemMac(..) => expand_item_mac(it, fld),
-        ast::ItemMod(_) | ast::ItemForeignMod(_) => {
-            let valid_ident =
-                it.ident.name != parse::token::special_idents::invalid.name;
-
-            if valid_ident {
-                fld.cx.mod_push(it.ident);
-            }
-            let macro_use = contains_macro_use(fld, &new_attrs[]);
-            let result = with_exts_frame!(fld.cx.syntax_env,
-                                          macro_use,
-                                          noop_fold_item(it, fld));
-            if valid_ident {
-                fld.cx.mod_pop();
-            }
-            result
-        },
-        _ => {
-            let it = P(ast::Item {
-                attrs: new_attrs,
-                ..(*it).clone()
-            });
-            noop_fold_item(it, fld)
-        }
-    };
-
-    new_items.push_all(decorator_items);
-    new_items
+    expand_annotatable(Annotatable::Item(it), fld)
+        .into_iter().map(|i| i.expect_item()).collect()
 }
 
 fn expand_item_modifiers(mut it: P<ast::Item>, fld: &mut MacroExpander)
                          -> P<ast::Item> {
     // partition the attributes into ItemModifiers and others
-    let (modifiers, other_attrs): (Vec<_>, _) = it.attrs.iter().cloned().partition(|attr| {
-        match fld.cx.syntax_env.find(&intern(attr.name().get())) {
-            Some(rc) => match *rc { Modifier(_) => true, _ => false },
-            _ => false
-        }
-    });
+    let (modifiers, other_attrs) = modifiers(&it.attrs, fld);
+
     // update the attrs, leave everything else alone. Is this mutation really a good idea?
     it = P(ast::Item {
         attrs: other_attrs,
@@ -477,7 +411,8 @@ fn expand_item_modifiers(mut it: P<ast::Item>, fld: &mut MacroExpander)
     });
 
     if modifiers.is_empty() {
-        return it;
+        let it = expand_item_multi_modifier(Annotatable::Item(it), fld);
+        return it.expect_item();
     }
 
     for attr in modifiers.iter() {
@@ -504,7 +439,12 @@ fn expand_item_modifiers(mut it: P<ast::Item>, fld: &mut MacroExpander)
         }
     }
 
-    // expansion may have added new ItemModifiers
+    // Expansion may have added new ItemModifiers.
+    // It is possible, that an item modifier could expand to a multi-modifier or
+    // vice versa. In this case we will expand all modifiers before multi-modifiers,
+    // which might give an odd ordering. However, I think it is unlikely that the
+    // two kinds will be mixed, and I old-style multi-modifiers should be deprecated
+    // anyway.
     expand_item_modifiers(it, fld)
 }
 
@@ -1029,6 +969,196 @@ impl<'a> Folder for PatIdentRenamer<'a> {
     }
 }
 
+fn expand_annotatable(a: Annotatable,
+                      fld: &mut MacroExpander)
+                      -> SmallVector<Annotatable> {
+    let a = expand_item_multi_modifier(a, fld);
+
+    let mut decorator_items = SmallVector::zero();
+    let mut new_attrs = Vec::new();
+    for attr in a.attrs().iter() {
+        let mname = attr.name();
+
+        match fld.cx.syntax_env.find(&intern(mname.get())) {
+            Some(rc) => match *rc {
+                Decorator(ref dec) => {
+                    let it = match a {
+                        Annotatable::Item(ref it) => it,
+                        // ItemDecorators are only implemented for Items.
+                        _ => break,
+                    };
+
+                    attr::mark_used(attr);
+
+                    fld.cx.bt_push(ExpnInfo {
+                        call_site: attr.span,
+                        callee: NameAndSpan {
+                            name: mname.get().to_string(),
+                            format: MacroAttribute,
+                            span: None
+                        }
+                    });
+
+                    // we'd ideally decorator_items.push_all(expand_item(item, fld)),
+                    // but that double-mut-borrows fld
+                    let mut items: SmallVector<P<ast::Item>> = SmallVector::zero();
+                    dec.expand(fld.cx, attr.span, &*attr.node.value, &**it,
+                               box |&mut: item| items.push(item));
+                    decorator_items.extend(items.into_iter()
+                        .flat_map(|item| expand_item(item, fld).into_iter()));
+
+                    fld.cx.bt_pop();
+                }
+                _ => new_attrs.push((*attr).clone()),
+            },
+            _ => new_attrs.push((*attr).clone()),
+        }
+    }
+
+    let mut new_items: SmallVector<Annotatable> = match a {
+        Annotatable::Item(it) => match it.node {
+            ast::ItemMac(..) => {
+                expand_item_mac(it, fld).into_iter().map(|i| Annotatable::Item(i)).collect()
+            }
+            ast::ItemMod(_) | ast::ItemForeignMod(_) => {
+                let valid_ident =
+                    it.ident.name != parse::token::special_idents::invalid.name;
+
+                if valid_ident {
+                    fld.cx.mod_push(it.ident);
+                }
+                let macro_use = contains_macro_use(fld, &new_attrs[]);
+                let result = with_exts_frame!(fld.cx.syntax_env,
+                                              macro_use,
+                                              noop_fold_item(it, fld));
+                if valid_ident {
+                    fld.cx.mod_pop();
+                }
+                result.into_iter().map(|i| Annotatable::Item(i)).collect()
+            },
+            _ => {
+                let it = P(ast::Item {
+                    attrs: new_attrs,
+                    ..(*it).clone()
+                });
+                noop_fold_item(it, fld).into_iter().map(|i| Annotatable::Item(i)).collect()
+            }
+        },
+        Annotatable::TraitItem(it) => match it {
+            ast::TraitItem::ProvidedMethod(m) => {
+                expand_method(m, fld).into_iter().map(|m|
+                    Annotatable::TraitItem(ast::TraitItem::ProvidedMethod(m))).collect()
+            }
+            ast::TraitItem::RequiredMethod(m) => {
+                SmallVector::one(Annotatable::TraitItem(
+                    ast::TraitItem::RequiredMethod(fld.fold_type_method(m))))
+            }
+            ast::TraitItem::TypeTraitItem(t) => {
+                SmallVector::one(Annotatable::TraitItem(
+                    ast::TraitItem::TypeTraitItem(P(fld.fold_associated_type((*t).clone())))))
+            }
+        },
+        Annotatable::ImplItem(it) => match it {
+            ast::ImplItem::MethodImplItem(m) => {
+                expand_method(m, fld).into_iter().map(|m|
+                    Annotatable::ImplItem(ast::ImplItem::MethodImplItem(m))).collect()
+            }
+            ast::ImplItem::TypeImplItem(t) => {
+                SmallVector::one(Annotatable::ImplItem(
+                    ast::ImplItem::TypeImplItem(P(fld.fold_typedef((*t).clone())))))
+            }
+        }
+    };
+
+    new_items.push_all(decorator_items.into_iter().map(|i| Annotatable::Item(i)).collect());
+    new_items
+}
+
+fn expand_trait_item(i: ast::TraitItem,
+                     fld: &mut MacroExpander)
+                     -> SmallVector<ast::TraitItem> {
+    expand_annotatable(Annotatable::TraitItem(i), fld)
+        .into_iter().map(|i| i.expect_trait_item()).collect()
+
+}
+
+fn expand_impl_item(i: ast::ImplItem,
+                    fld: &mut MacroExpander)
+                    -> SmallVector<ast::ImplItem> {
+    expand_annotatable(Annotatable::ImplItem(i), fld)
+        .into_iter().map(|i| i.expect_impl_item()).collect()
+}
+
+// partition the attributes into ItemModifiers and others
+fn modifiers(attrs: &Vec<ast::Attribute>,
+             fld: &MacroExpander)
+             -> (Vec<ast::Attribute>, Vec<ast::Attribute>) {
+    attrs.iter().cloned().partition(|attr| {
+        match fld.cx.syntax_env.find(&intern(attr.name().get())) {
+            Some(rc) => match *rc {
+                Modifier(_) => true,
+                _ => false
+            },
+            _ => false
+        }
+    })
+}
+
+// partition the attributes into MultiModifiers and others
+fn multi_modifiers(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().get())) {
+            Some(rc) => match *rc {
+                MultiModifier(_) => true,
+                _ => false
+            },
+            _ => false
+        }
+    })
+}
+
+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.iter() {
+        let mname = attr.name();
+
+        match fld.cx.syntax_env.find(&intern(mname.get())) {
+            Some(rc) => match *rc {
+                MultiModifier(ref mac) => {
+                    attr::mark_used(attr);
+                    fld.cx.bt_push(ExpnInfo {
+                        call_site: attr.span,
+                        callee: NameAndSpan {
+                            name: mname.get().to_string(),
+                            format: MacroAttribute,
+                            span: None,
+                        }
+                    });
+                    it = mac.expand(fld.cx, attr.span, &*attr.node.value, it);
+                    fld.cx.bt_pop();
+                }
+                _ => unreachable!()
+            },
+            _ => unreachable!()
+        }
+    }
+
+    // Expansion may have added new ItemModifiers.
+    expand_item_multi_modifier(it, fld)
+}
+
 // expand a method
 fn expand_method(m: P<ast::Method>, fld: &mut MacroExpander) -> SmallVector<P<ast::Method>> {
     m.and_then(|m| match m.node {
@@ -1042,7 +1172,7 @@ fn expand_method(m: P<ast::Method>, fld: &mut MacroExpander) -> SmallVector<P<as
                       vis) => {
             let id = fld.new_id(m.id);
             let (rewritten_fn_decl, rewritten_body)
-                = expand_and_rename_fn_decl_and_block(decl,body,fld);
+                = expand_and_rename_fn_decl_and_block(decl, body, fld);
             SmallVector::one(P(ast::Method {
                     attrs: m.attrs.move_map(|a| fld.fold_attribute(a)),
                     id: id,
@@ -1147,6 +1277,14 @@ impl<'a, 'b> Folder for MacroExpander<'a, 'b> {
         expand_arm(arm, self)
     }
 
+    fn fold_trait_item(&mut self, i: ast::TraitItem) -> SmallVector<ast::TraitItem> {
+        expand_trait_item(i, self)
+    }
+
+    fn fold_impl_item(&mut self, i: ast::ImplItem) -> SmallVector<ast::ImplItem> {
+        expand_impl_item(i, self)
+    }
+
     fn fold_method(&mut self, method: P<ast::Method>) -> SmallVector<P<ast::Method>> {
         expand_method(method, self)
     }
diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs
index 2a704349295..16c29c9b5eb 100644
--- a/src/libsyntax/fold.rs
+++ b/src/libsyntax/fold.rs
@@ -102,6 +102,14 @@ pub trait Folder : Sized {
         noop_fold_item_underscore(i, self)
     }
 
+    fn fold_trait_item(&mut self, i: TraitItem) -> SmallVector<TraitItem> {
+        noop_fold_trait_item(i, self)
+    }
+
+    fn fold_impl_item(&mut self, i: ImplItem) -> SmallVector<ImplItem> {
+        noop_fold_impl_item(i, self)
+    }
+
     fn fold_fn_decl(&mut self, d: P<FnDecl>) -> P<FnDecl> {
         noop_fold_fn_decl(d, self)
     }
@@ -1007,21 +1015,9 @@ pub fn noop_fold_item_underscore<T: Folder>(i: Item_, folder: &mut T) -> Item_ {
             ItemStruct(struct_def, folder.fold_generics(generics))
         }
         ItemImpl(unsafety, polarity, generics, ifce, ty, impl_items) => {
-            let mut new_impl_items = Vec::new();
-            for impl_item in impl_items.iter() {
-                match *impl_item {
-                    MethodImplItem(ref x) => {
-                        for method in folder.fold_method((*x).clone())
-                                            .into_iter() {
-                            new_impl_items.push(MethodImplItem(method))
-                        }
-                    }
-                    TypeImplItem(ref t) => {
-                        new_impl_items.push(TypeImplItem(
-                                P(folder.fold_typedef((**t).clone()))));
-                    }
-                }
-            }
+            let new_impl_items = impl_items.into_iter().flat_map(|item| {
+                folder.fold_impl_item(item).into_iter()
+            }).collect();
             let ifce = match ifce {
                 None => None,
                 Some(ref trait_ref) => {
@@ -1035,43 +1031,50 @@ pub fn noop_fold_item_underscore<T: Folder>(i: Item_, folder: &mut T) -> Item_ {
                      folder.fold_ty(ty),
                      new_impl_items)
         }
-        ItemTrait(unsafety, generics, bounds, methods) => {
+        ItemTrait(unsafety, generics, bounds, items) => {
             let bounds = folder.fold_bounds(bounds);
-            let methods = methods.into_iter().flat_map(|method| {
-                let r = match method {
-                    RequiredMethod(m) => {
-                            SmallVector::one(RequiredMethod(
-                                    folder.fold_type_method(m)))
-                                .into_iter()
-                    }
-                    ProvidedMethod(method) => {
-                        // the awkward collect/iter idiom here is because
-                        // even though an iter and a map satisfy the same
-                        // trait bound, they're not actually the same type, so
-                        // the method arms don't unify.
-                        let methods: SmallVector<ast::TraitItem> =
-                            folder.fold_method(method).into_iter()
-                            .map(|m| ProvidedMethod(m)).collect();
-                        methods.into_iter()
-                    }
-                    TypeTraitItem(at) => {
-                        SmallVector::one(TypeTraitItem(P(
-                                    folder.fold_associated_type(
-                                        (*at).clone()))))
-                            .into_iter()
-                    }
-                };
-                r
+            let items = items.into_iter().flat_map(|item| {
+                folder.fold_trait_item(item).into_iter()
             }).collect();
             ItemTrait(unsafety,
                       folder.fold_generics(generics),
                       bounds,
-                      methods)
+                      items)
         }
         ItemMac(m) => ItemMac(folder.fold_mac(m)),
     }
 }
 
+pub fn noop_fold_trait_item<T: Folder>(i: TraitItem, folder: &mut T) -> SmallVector<TraitItem> {
+    match i {
+        RequiredMethod(m) => {
+                SmallVector::one(RequiredMethod(
+                        folder.fold_type_method(m)))
+        }
+        ProvidedMethod(method) => {
+            folder.fold_method(method).into_iter()
+                .map(|m| ProvidedMethod(m)).collect()
+        }
+        TypeTraitItem(at) => {
+            SmallVector::one(TypeTraitItem(P(
+                        folder.fold_associated_type(
+                            (*at).clone()))))
+        }
+    }
+}
+
+pub fn noop_fold_impl_item<T: Folder>(i: ImplItem, folder: &mut T) -> SmallVector<ImplItem> {
+    match i {
+        MethodImplItem(ref x) => {
+            folder.fold_method((*x).clone()).into_iter().map(|m| MethodImplItem(m)).collect()
+        }
+        TypeImplItem(ref t) => {
+            SmallVector::one(TypeImplItem(
+                    P(folder.fold_typedef((**t).clone()))))
+        }
+    }
+}
+
 pub fn noop_fold_type_method<T: Folder>(m: TypeMethod, fld: &mut T) -> TypeMethod {
     let TypeMethod {
         id,