about summary refs log tree commit diff
diff options
context:
space:
mode:
authorSteven Fackler <sfackler@gmail.com>2013-11-24 23:08:53 -0800
committerSteven Fackler <sfackler@palantir.com>2013-11-26 13:56:02 -0800
commitc144752a2de4ffe3a2a22da9a8309ca2ecd85c58 (patch)
tree66ab1d0cce647d2f0863a94bdb127a511ad8fe62
parent09f84aa8f4298489828720c048ec7f769338c0e2 (diff)
downloadrust-c144752a2de4ffe3a2a22da9a8309ca2ecd85c58.tar.gz
rust-c144752a2de4ffe3a2a22da9a8309ca2ecd85c58.zip
Support multiple item macros
Closes #4375
-rw-r--r--src/librustc/front/config.rs34
-rw-r--r--src/librustc/front/std_inject.rs5
-rw-r--r--src/librustc/front/test.rs5
-rw-r--r--src/librustc/middle/astencode.rs12
-rw-r--r--src/librustpkg/util.rs5
-rw-r--r--src/libsyntax/ext/base.rs3
-rw-r--r--src/libsyntax/ext/expand.rs114
-rw-r--r--src/libsyntax/ext/tt/macro_rules.rs13
-rw-r--r--src/libsyntax/fold.rs53
-rw-r--r--src/libsyntax/util/small_vector.rs28
-rw-r--r--src/test/compile-fail/macro-incomplete-parse.rs3
-rw-r--r--src/test/run-pass/macro-multiple-items.rs29
12 files changed, 180 insertions, 124 deletions
diff --git a/src/librustc/front/config.rs b/src/librustc/front/config.rs
index 1602cbb6a9b..ee1935207b1 100644
--- a/src/librustc/front/config.rs
+++ b/src/librustc/front/config.rs
@@ -48,14 +48,6 @@ pub fn strip_items(crate: ast::Crate,
     ctxt.fold_crate(crate)
 }
 
-fn filter_item(cx: &Context, item: @ast::item) -> Option<@ast::item> {
-    if item_in_cfg(cx, item) {
-        Some(item)
-    } else {
-        None
-    }
-}
-
 fn filter_view_item<'r>(cx: &Context, view_item: &'r ast::view_item)
                         -> Option<&'r ast::view_item> {
     if view_item_in_cfg(cx, view_item) {
@@ -66,9 +58,10 @@ fn filter_view_item<'r>(cx: &Context, view_item: &'r ast::view_item)
 }
 
 fn fold_mod(cx: &Context, m: &ast::_mod) -> ast::_mod {
-    let filtered_items = m.items.iter().filter_map(|a| {
-        filter_item(cx, *a).and_then(|x| cx.fold_item(x))
-    }).collect();
+    let filtered_items = m.items.iter()
+            .filter(|&a| item_in_cfg(cx, *a))
+            .flat_map(|&x| cx.fold_item(x).move_iter())
+            .collect();
     let filtered_view_items = m.view_items.iter().filter_map(|a| {
         filter_view_item(cx, a).map(|x| cx.fold_view_item(x))
     }).collect();
@@ -122,28 +115,25 @@ fn fold_item_underscore(cx: &Context, item: &ast::item_) -> ast::item_ {
     fold::noop_fold_item_underscore(&item, cx)
 }
 
-fn filter_stmt(cx: &Context, stmt: @ast::Stmt) -> Option<@ast::Stmt> {
+fn retain_stmt(cx: &Context, stmt: @ast::Stmt) -> bool {
     match stmt.node {
       ast::StmtDecl(decl, _) => {
         match decl.node {
           ast::DeclItem(item) => {
-            if item_in_cfg(cx, item) {
-                Some(stmt)
-            } else {
-                None
-            }
+            item_in_cfg(cx, item)
           }
-          _ => Some(stmt)
+          _ => true
         }
       }
-      _ => Some(stmt),
+      _ => true
     }
 }
 
 fn fold_block(cx: &Context, b: &ast::Block) -> ast::Block {
-    let resulting_stmts = b.stmts.iter().filter_map(|a| {
-        filter_stmt(cx, *a).and_then(|stmt| cx.fold_stmt(stmt))
-    }).collect();
+    let resulting_stmts = b.stmts.iter()
+            .filter(|&a| retain_stmt(cx, *a))
+            .flat_map(|&stmt| cx.fold_stmt(stmt).move_iter())
+            .collect();
     let filtered_view_items = b.view_items.iter().filter_map(|a| {
         filter_view_item(cx, a).map(|x| cx.fold_view_item(x))
     }).collect();
diff --git a/src/librustc/front/std_inject.rs b/src/librustc/front/std_inject.rs
index c2d0251b23c..a40f8183e19 100644
--- a/src/librustc/front/std_inject.rs
+++ b/src/librustc/front/std_inject.rs
@@ -19,6 +19,7 @@ use syntax::codemap;
 use syntax::fold::ast_fold;
 use syntax::fold;
 use syntax::opt_vec;
+use syntax::util::small_vector::SmallVector;
 
 static STD_VERSION: &'static str = "0.9-pre";
 
@@ -98,14 +99,14 @@ impl fold::ast_fold for StandardLibraryInjector {
         }
     }
 
-    fn fold_item(&self, item: @ast::item) -> Option<@ast::item> {
+    fn fold_item(&self, item: @ast::item) -> SmallVector<@ast::item> {
         if !no_prelude(item.attrs) {
             // only recur if there wasn't `#[no_implicit_prelude];`
             // on this item, i.e. this means that the prelude is not
             // implicitly imported though the whole subtree
             fold::noop_fold_item(item, self)
         } else {
-            Some(item)
+            SmallVector::one(item)
         }
     }
 
diff --git a/src/librustc/front/test.rs b/src/librustc/front/test.rs
index 4d1998c8fd7..ca82502f84b 100644
--- a/src/librustc/front/test.rs
+++ b/src/librustc/front/test.rs
@@ -26,6 +26,7 @@ use syntax::fold;
 use syntax::opt_vec;
 use syntax::print::pprust;
 use syntax::{ast, ast_util};
+use syntax::util::small_vector::SmallVector;
 
 struct Test {
     span: Span,
@@ -76,7 +77,7 @@ impl fold::ast_fold for TestHarnessGenerator {
         }
     }
 
-    fn fold_item(&self, i: @ast::item) -> Option<@ast::item> {
+    fn fold_item(&self, i: @ast::item) -> SmallVector<@ast::item> {
         self.cx.path.push(i.ident);
         debug!("current path: {}",
                ast_util::path_name_i(self.cx.path.clone()));
@@ -108,7 +109,7 @@ impl fold::ast_fold for TestHarnessGenerator {
 
         let res = fold::noop_fold_item(i, self);
         self.cx.path.pop();
-        return res;
+        res
     }
 
     fn fold_mod(&self, m: &ast::_mod) -> ast::_mod {
diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs
index c0dc904b9a3..2658f7efa1c 100644
--- a/src/librustc/middle/astencode.rs
+++ b/src/librustc/middle/astencode.rs
@@ -33,6 +33,7 @@ use syntax::codemap;
 use syntax::fold::*;
 use syntax::fold;
 use syntax::parse::token;
+use syntax::util::small_vector::SmallVector;
 use syntax;
 
 use std::at_vec;
@@ -347,13 +348,20 @@ fn simplify_ast(ii: &ast::inlined_item) -> ast::inlined_item {
 
     match *ii {
         //hack: we're not dropping items
-        ast::ii_item(i) => ast::ii_item(fld.fold_item(i).unwrap()),
+        ast::ii_item(i) => ast::ii_item(get_only_one(fld.fold_item(i))),
         ast::ii_method(d, is_provided, m) =>
           ast::ii_method(d, is_provided, fld.fold_method(m)),
         ast::ii_foreign(i) => ast::ii_foreign(fld.fold_foreign_item(i))
     }
 }
 
+fn get_only_one<T>(mut v: SmallVector<T>) -> T {
+    if v.len() != 1 {
+        fail!("Attempting to extract unique member but there isn't one");
+    }
+    v.pop()
+}
+
 fn decode_ast(par_doc: ebml::Doc) -> ast::inlined_item {
     let chi_doc = par_doc.get(c::tag_tree as uint);
     let mut d = reader::Decoder(chi_doc);
@@ -379,7 +387,7 @@ fn renumber_ast(xcx: @ExtendedDecodeContext, ii: ast::inlined_item)
         xcx: xcx,
     };
     match ii {
-        ast::ii_item(i) => ast::ii_item(fld.fold_item(i).unwrap()),
+        ast::ii_item(i) => ast::ii_item(get_only_one(fld.fold_item(i))),
         ast::ii_method(d, is_provided, m) =>
           ast::ii_method(xcx.tr_def_id(d), is_provided, fld.fold_method(m)),
         ast::ii_foreign(i) => ast::ii_foreign(fld.fold_foreign_item(i)),
diff --git a/src/librustpkg/util.rs b/src/librustpkg/util.rs
index 9842508a5c3..ec5922cf67a 100644
--- a/src/librustpkg/util.rs
+++ b/src/librustpkg/util.rs
@@ -22,6 +22,7 @@ use syntax::{ast, attr, codemap, diagnostic, fold, visit};
 use syntax::attr::AttrMetaMethods;
 use syntax::fold::ast_fold;
 use syntax::visit::Visitor;
+use syntax::util::small_vector::SmallVector;
 use rustc::back::link::output_type_exe;
 use rustc::back::link;
 use rustc::driver::session::{lib_crate, bin_crate};
@@ -99,7 +100,7 @@ fn fold_mod(_ctx: @mut ReadyCtx, m: &ast::_mod, fold: &CrateSetup)
 }
 
 fn fold_item(ctx: @mut ReadyCtx, item: @ast::item, fold: &CrateSetup)
-             -> Option<@ast::item> {
+             -> SmallVector<@ast::item> {
     ctx.path.push(item.ident);
 
     let mut cmds = ~[];
@@ -142,7 +143,7 @@ struct CrateSetup {
 }
 
 impl fold::ast_fold for CrateSetup {
-    fn fold_item(&self, item: @ast::item) -> Option<@ast::item> {
+    fn fold_item(&self, item: @ast::item) -> SmallVector<@ast::item> {
         fold_item(self.ctx, item, self)
     }
     fn fold_mod(&self, module: &ast::_mod) -> ast::_mod {
diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs
index ccb88e0c9c0..deaa821cd45 100644
--- a/src/libsyntax/ext/base.rs
+++ b/src/libsyntax/ext/base.rs
@@ -18,6 +18,7 @@ use ext::expand;
 use parse;
 use parse::token;
 use parse::token::{ident_to_str, intern, str_to_ident};
+use util::small_vector::SmallVector;
 
 use std::hashmap::HashMap;
 
@@ -131,7 +132,7 @@ pub type SyntaxExpanderTTItemFunNoCtxt =
 
 pub trait AnyMacro {
     fn make_expr(&self) -> @ast::Expr;
-    fn make_item(&self) -> Option<@ast::item>;
+    fn make_items(&self) -> SmallVector<@ast::item>;
     fn make_stmt(&self) -> @ast::Stmt;
 }
 
diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs
index f23e13b8931..3719362a7df 100644
--- a/src/libsyntax/ext/expand.rs
+++ b/src/libsyntax/ext/expand.rs
@@ -28,6 +28,7 @@ use parse::token;
 use parse::token::{fresh_mark, fresh_name, ident_to_str, intern};
 use visit;
 use visit::Visitor;
+use util::small_vector::SmallVector;
 
 use std::vec;
 
@@ -310,7 +311,7 @@ pub fn expand_item(extsbox: @mut SyntaxEnv,
                    cx: @ExtCtxt,
                    it: @ast::item,
                    fld: &MacroExpander)
-                   -> Option<@ast::item> {
+                   -> SmallVector<@ast::item> {
     match it.node {
         ast::item_mac(*) => expand_item_mac(extsbox, cx, it, fld),
         ast::item_mod(_) | ast::item_foreign_mod(_) => {
@@ -337,7 +338,7 @@ pub fn expand_item_mac(extsbox: @mut SyntaxEnv,
                        cx: @ExtCtxt,
                        it: @ast::item,
                        fld: &MacroExpander)
-                       -> Option<@ast::item> {
+                       -> SmallVector<@ast::item> {
     let (pth, tts, ctxt) = match it.node {
         item_mac(codemap::Spanned {
             node: mac_invoc_tt(ref pth, ref tts, ctxt),
@@ -396,28 +397,30 @@ pub fn expand_item_mac(extsbox: @mut SyntaxEnv,
             it.span, format!("{}! is not legal in item position", extnamestr))
     };
 
-    let maybe_it = match expanded {
+    let items = match expanded {
         MRItem(it) => {
-            mark_item(it,fm)
-                .and_then(|i| fld.fold_item(i))
+            mark_item(it,fm).move_iter()
+                .flat_map(|i| fld.fold_item(i).move_iter())
+                .collect()
         }
         MRExpr(_) => {
             cx.span_fatal(pth.span, format!("expr macro in item position: {}", extnamestr))
         }
         MRAny(any_macro) => {
-            any_macro.make_item()
-                     .and_then(|i| mark_item(i,fm))
-                     .and_then(|i| fld.fold_item(i))
+            any_macro.make_items().move_iter()
+                    .flat_map(|i| mark_item(i, fm).move_iter())
+                    .flat_map(|i| fld.fold_item(i).move_iter())
+                    .collect()
         }
         MRDef(ref mdef) => {
             // yikes... no idea how to apply the mark to this. I'm afraid
             // we're going to have to wait-and-see on this one.
             insert_macro(*extsbox,intern(mdef.name), @SE((*mdef).ext));
-            None
+            SmallVector::zero()
         }
     };
     cx.bt_pop();
-    return maybe_it;
+    return items;
 }
 
 
@@ -442,7 +445,7 @@ pub fn expand_stmt(extsbox: @mut SyntaxEnv,
                    cx: @ExtCtxt,
                    s: &Stmt,
                    fld: &MacroExpander)
-                   -> Option<@Stmt> {
+                   -> SmallVector<@Stmt> {
     // why the copying here and not in expand_expr?
     // looks like classic changed-in-only-one-place
     let (pth, tts, semi, ctxt) = match s.node {
@@ -461,7 +464,7 @@ pub fn expand_stmt(extsbox: @mut SyntaxEnv,
     }
     let extname = &pth.segments[0].identifier;
     let extnamestr = ident_to_str(extname);
-    let fully_expanded: @ast::Stmt = match (*extsbox).find(&extname.name) {
+    let fully_expanded: SmallVector<@Stmt> = match (*extsbox).find(&extname.name) {
         None => {
             cx.span_fatal(pth.span, format!("macro undefined: '{}'", extnamestr))
         }
@@ -501,22 +504,15 @@ pub fn expand_stmt(extsbox: @mut SyntaxEnv,
             let marked_after = mark_stmt(expanded,fm);
 
             // Keep going, outside-in.
-            let fully_expanded = match fld.fold_stmt(marked_after) {
-                Some(stmt) => {
-                    let fully_expanded = &stmt.node;
-                    cx.bt_pop();
-                    @Spanned {
-                        span: stmt.span,
-                        node: (*fully_expanded).clone(),
-                    }
-                }
-                None => {
-                    cx.span_fatal(pth.span,
-                                  "macro didn't expand to a statement")
-                }
-            };
-
-            fully_expanded
+            let fully_expanded = fld.fold_stmt(marked_after);
+            if fully_expanded.is_empty() {
+                cx.span_fatal(pth.span,
+                              "macro didn't expand to a statement");
+            }
+            cx.bt_pop();
+            fully_expanded.move_iter()
+                    .map(|s| @Spanned { span: s.span, node: s.node.clone() })
+                    .collect()
         }
 
         _ => {
@@ -525,21 +521,23 @@ pub fn expand_stmt(extsbox: @mut SyntaxEnv,
         }
     };
 
-    match fully_expanded.node {
-        StmtExpr(e, stmt_id) if semi => {
-            Some(@Spanned {
-                span: fully_expanded.span,
-                node: StmtSemi(e, stmt_id),
-            })
+    fully_expanded.move_iter().map(|s| {
+        match s.node {
+            StmtExpr(e, stmt_id) if semi => {
+                @Spanned {
+                    span: s.span,
+                    node: StmtSemi(e, stmt_id)
+                }
+            }
+            _ => s /* might already have a semi */
         }
-        _ => Some(fully_expanded), /* might already have a semi */
-    }
+    }).collect()
 }
 
 // expand a non-macro stmt. this is essentially the fallthrough for
 // expand_stmt, above.
 fn expand_non_macro_stmt(exts: SyntaxEnv, s: &Stmt, fld: &MacroExpander)
-                         -> Option<@Stmt> {
+                         -> SmallVector<@Stmt> {
     // is it a let?
     match s.node {
         StmtDecl(@Spanned {
@@ -590,7 +588,7 @@ fn expand_non_macro_stmt(exts: SyntaxEnv, s: &Stmt, fld: &MacroExpander)
                     id: id,
                     span: span,
                 };
-            Some(@Spanned {
+            SmallVector::one(@Spanned {
                 node: StmtDecl(@Spanned {
                         node: DeclLocal(rewritten_local),
                         span: stmt_span
@@ -679,13 +677,9 @@ pub fn expand_block_elts(exts: SyntaxEnv, b: &Block, fld: &MacroExpander)
     let pending_renames = block_info.pending_renames;
     let rename_fld = renames_to_fold(pending_renames);
     let new_view_items = b.view_items.map(|x| fld.fold_view_item(x));
-    let mut new_stmts = ~[];
-    for x in b.stmts.iter() {
-        match fld.fold_stmt(mustbesome(rename_fld.fold_stmt(*x))) {
-            Some(s) => new_stmts.push(s),
-            None => ()
-        }
-    }
+    let new_stmts = b.stmts.iter()
+            .flat_map(|x| fld.fold_stmt(mustbeone(rename_fld.fold_stmt(*x))).move_iter())
+            .collect();
     let new_expr = b.expr.map(|x| fld.fold_expr(rename_fld.fold_expr(x)));
     Block{
         view_items: new_view_items,
@@ -697,13 +691,12 @@ pub fn expand_block_elts(exts: SyntaxEnv, b: &Block, fld: &MacroExpander)
     }
 }
 
-// rename_fold should never return "None".
-// (basically, just .get() with a better message...)
-fn mustbesome<T>(val : Option<T>) -> T {
-    match val {
-        Some(v) => v,
-        None => fail!("rename_fold returned None")
+// rename_fold should never return anything other than one thing
+fn mustbeone<T>(mut val : SmallVector<T>) -> T {
+    if val.len() != 1 {
+        fail!("rename_fold didn't return one value");
     }
+    val.pop()
 }
 
 // get the (innermost) BlockInfo from an exts stack
@@ -741,10 +734,11 @@ pub fn renames_to_fold(renames: @mut ~[(ast::Ident,ast::Name)]) -> @ast_fold {
 
 // perform a bunch of renames
 fn apply_pending_renames(folder : @ast_fold, stmt : ast::Stmt) -> @ast::Stmt {
-    match folder.fold_stmt(&stmt) {
-        Some(s) => s,
-        None => fail!("renaming of stmt produced None")
+    let mut stmts = folder.fold_stmt(&stmt);
+    if stmts.len() != 1 {
+        fail!("renaming of stmt did not produce one stmt");
     }
+    stmts.pop()
 }
 
 
@@ -1025,14 +1019,14 @@ impl ast_fold for MacroExpander {
                          self)
     }
 
-    fn fold_item(&self, item: @ast::item) -> Option<@ast::item> {
+    fn fold_item(&self, item: @ast::item) -> SmallVector<@ast::item> {
         expand_item(self.extsbox,
                     self.cx,
                     item,
                     self)
     }
 
-    fn fold_stmt(&self, stmt: &ast::Stmt) -> Option<@ast::Stmt> {
+    fn fold_stmt(&self, stmt: &ast::Stmt) -> SmallVector<@ast::Stmt> {
         expand_stmt(self.extsbox,
                     self.cx,
                     stmt,
@@ -1191,11 +1185,15 @@ fn mark_expr(expr : @ast::Expr, m : Mrk) -> @ast::Expr {
 
 // apply a given mark to the given stmt. Used following the expansion of a macro.
 fn mark_stmt(expr : &ast::Stmt, m : Mrk) -> @ast::Stmt {
-    new_mark_folder(m).fold_stmt(expr).unwrap()
+    let mut stmts = new_mark_folder(m).fold_stmt(expr);
+    if stmts.len() != 1 {
+        fail!("marking a stmt didn't return a stmt");
+    }
+    stmts.pop()
 }
 
 // apply a given mark to the given item. Used following the expansion of a macro.
-fn mark_item(expr : @ast::item, m : Mrk) -> Option<@ast::item> {
+fn mark_item(expr : @ast::item, m : Mrk) -> SmallVector<@ast::item> {
     new_mark_folder(m).fold_item(expr)
 }
 
diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs
index 343116df642..3cc00ef8199 100644
--- a/src/libsyntax/ext/tt/macro_rules.rs
+++ b/src/libsyntax/ext/tt/macro_rules.rs
@@ -24,6 +24,7 @@ use parse::attr::parser_attr;
 use parse::token::{get_ident_interner, special_idents, gensym_ident, ident_to_str};
 use parse::token::{FAT_ARROW, SEMI, nt_matchers, nt_tt, EOF};
 use print;
+use util::small_vector::SmallVector;
 
 struct ParserAnyMacro {
     parser: @Parser,
@@ -54,9 +55,15 @@ impl AnyMacro for ParserAnyMacro {
         self.ensure_complete_parse(true);
         ret
     }
-    fn make_item(&self) -> Option<@ast::item> {
-        let attrs = self.parser.parse_outer_attributes();
-        let ret = self.parser.parse_item(attrs);
+    fn make_items(&self) -> SmallVector<@ast::item> {
+        let mut ret = SmallVector::zero();
+        loop {
+            let attrs = self.parser.parse_outer_attributes();
+            match self.parser.parse_item(attrs) {
+                Some(item) => ret.push(item),
+                None => break
+            }
+        }
         self.ensure_complete_parse(false);
         ret
     }
diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs
index b268988c3c5..4c0653a3c04 100644
--- a/src/libsyntax/fold.rs
+++ b/src/libsyntax/fold.rs
@@ -13,6 +13,7 @@ use ast;
 use codemap::{respan, Span, Spanned};
 use parse::token;
 use opt_vec::OptVec;
+use util::small_vector::SmallVector;
 
 // We may eventually want to be able to fold over type parameters, too.
 pub trait ast_fold {
@@ -113,7 +114,7 @@ pub trait ast_fold {
         }
     }
 
-    fn fold_item(&self, i: @item) -> Option<@item> {
+    fn fold_item(&self, i: @item) -> SmallVector<@item> {
         noop_fold_item(i, self)
     }
 
@@ -159,7 +160,7 @@ pub trait ast_fold {
         noop_fold_block(b, self)
     }
 
-    fn fold_stmt(&self, s: &Stmt) -> Option<@Stmt> {
+    fn fold_stmt(&self, s: &Stmt) -> SmallVector<@Stmt> {
         noop_fold_stmt(s, self)
     }
 
@@ -216,23 +217,20 @@ pub trait ast_fold {
         }
     }
 
-    fn fold_decl(&self, d: @Decl) -> Option<@Decl> {
+    fn fold_decl(&self, d: @Decl) -> SmallVector<@Decl> {
         let node = match d.node {
-            DeclLocal(ref l) => Some(DeclLocal(self.fold_local(*l))),
+            DeclLocal(ref l) => SmallVector::one(DeclLocal(self.fold_local(*l))),
             DeclItem(it) => {
-                match self.fold_item(it) {
-                    Some(it_folded) => Some(DeclItem(it_folded)),
-                    None => None,
-                }
+                self.fold_item(it).move_iter().map(|i| DeclItem(i)).collect()
             }
         };
 
-        node.map(|node| {
+        node.move_iter().map(|node| {
             @Spanned {
                 node: node,
                 span: d.span,
             }
-        })
+        }).collect()
     }
 
     fn fold_expr(&self, e: @Expr) -> @Expr {
@@ -618,13 +616,7 @@ fn fold_variant_arg_<T:ast_fold>(va: &variant_arg, folder: &T)
 
 pub fn noop_fold_block<T:ast_fold>(b: &Block, folder: &T) -> Block {
     let view_items = b.view_items.map(|x| folder.fold_view_item(x));
-    let mut stmts = ~[];
-    for stmt in b.stmts.iter() {
-        match folder.fold_stmt(*stmt) {
-            None => {}
-            Some(stmt) => stmts.push(stmt)
-        }
-    }
+    let stmts = b.stmts.iter().flat_map(|s| folder.fold_stmt(*s).move_iter()).collect();
     ast::Block {
         view_items: view_items,
         stmts: stmts,
@@ -711,7 +703,7 @@ pub fn noop_fold_mod<T:ast_fold>(m: &_mod, folder: &T) -> _mod {
         view_items: m.view_items
                      .iter()
                      .map(|x| folder.fold_view_item(x)).collect(),
-        items: m.items.iter().filter_map(|x| folder.fold_item(*x)).collect(),
+        items: m.items.iter().flat_map(|x| folder.fold_item(*x).move_iter()).collect(),
     }
 }
 
@@ -728,10 +720,10 @@ pub fn noop_fold_crate<T:ast_fold>(c: Crate, folder: &T) -> Crate {
 }
 
 pub fn noop_fold_item<T:ast_fold>(i: @ast::item, folder: &T)
-                                  -> Option<@ast::item> {
+                                  -> SmallVector<@ast::item> {
     let fold_attribute = |x| fold_attribute_(x, folder);
 
-    Some(@ast::item {
+    SmallVector::one(@ast::item {
         ident: folder.fold_ident(i.ident),
         attrs: i.attrs.map(|e| fold_attribute(*e)),
         id: folder.new_id(i.id),
@@ -867,27 +859,26 @@ pub fn noop_fold_expr<T:ast_fold>(e: @ast::Expr, folder: &T) -> @ast::Expr {
     }
 }
 
-pub fn noop_fold_stmt<T:ast_fold>(s: &Stmt, folder: &T) -> Option<@Stmt> {
-    let node = match s.node {
+pub fn noop_fold_stmt<T:ast_fold>(s: &Stmt, folder: &T) -> SmallVector<@Stmt> {
+    let nodes = match s.node {
         StmtDecl(d, nid) => {
-            match folder.fold_decl(d) {
-                Some(d) => Some(StmtDecl(d, folder.new_id(nid))),
-                None => None,
-            }
+            folder.fold_decl(d).move_iter()
+                    .map(|d| StmtDecl(d, folder.new_id(nid)))
+                    .collect()
         }
         StmtExpr(e, nid) => {
-            Some(StmtExpr(folder.fold_expr(e), folder.new_id(nid)))
+            SmallVector::one(StmtExpr(folder.fold_expr(e), folder.new_id(nid)))
         }
         StmtSemi(e, nid) => {
-            Some(StmtSemi(folder.fold_expr(e), folder.new_id(nid)))
+            SmallVector::one(StmtSemi(folder.fold_expr(e), folder.new_id(nid)))
         }
-        StmtMac(ref mac, semi) => Some(StmtMac(folder.fold_mac(mac), semi))
+        StmtMac(ref mac, semi) => SmallVector::one(StmtMac(folder.fold_mac(mac), semi))
     };
 
-    node.map(|node| @Spanned {
+    nodes.move_iter().map(|node| @Spanned {
         node: node,
         span: folder.new_span(s.span),
-    })
+    }).collect()
 }
 
 #[cfg(test)]
diff --git a/src/libsyntax/util/small_vector.rs b/src/libsyntax/util/small_vector.rs
index c6b223f7c15..0196a0212e2 100644
--- a/src/libsyntax/util/small_vector.rs
+++ b/src/libsyntax/util/small_vector.rs
@@ -73,6 +73,22 @@ impl<T> SmallVector<T> {
         }
     }
 
+    pub fn pop(&mut self) -> T {
+        match *self {
+            Zero => fail!("attempted to pop from an empty SmallVector"),
+            One(*) => {
+                let mut tmp = Zero;
+                util::swap(self, &mut tmp);
+                match tmp {
+                    One(v) => v,
+                    _ => unreachable!()
+                }
+            }
+            // Should this reduce to a One if possible?
+            Many(ref mut vs) => vs.pop()
+        }
+    }
+
     pub fn get<'a>(&'a self, idx: uint) -> &'a T {
         match *self {
             One(ref v) if idx == 0 => v,
@@ -177,6 +193,18 @@ mod test {
     }
 
     #[test]
+    fn test_pop() {
+        let mut v = SmallVector::one(1);
+        assert_eq!(1, v.pop());
+        assert_eq!(0, v.len());
+
+        let mut v= SmallVector::many(~[1, 2]);
+        assert_eq!(2, v.pop());
+        assert_eq!(1, v.pop());
+        assert_eq!(0, v.len());
+    }
+
+    #[test]
     fn test_from_iterator() {
         let v: SmallVector<int> = (~[1, 2, 3]).move_iter().collect();
         assert_eq!(3, v.len());
diff --git a/src/test/compile-fail/macro-incomplete-parse.rs b/src/test/compile-fail/macro-incomplete-parse.rs
index 598e0706adc..98e0ecfad86 100644
--- a/src/test/compile-fail/macro-incomplete-parse.rs
+++ b/src/test/compile-fail/macro-incomplete-parse.rs
@@ -13,7 +13,8 @@
 macro_rules! ignored_item {
     () => {
         fn foo() {}
-        fn bar() {} //~ ERROR macro expansion ignores token `fn`
+        fn bar() {}
+        , //~ ERROR macro expansion ignores token `,`
     }
 }
 
diff --git a/src/test/run-pass/macro-multiple-items.rs b/src/test/run-pass/macro-multiple-items.rs
new file mode 100644
index 00000000000..c96f8c7b7bb
--- /dev/null
+++ b/src/test/run-pass/macro-multiple-items.rs
@@ -0,0 +1,29 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// xfail-pretty - token trees can't pretty print
+
+#[feature(macro_rules)];
+
+macro_rules! make_foo(
+    () => (
+        struct Foo;
+
+        impl Foo {
+            fn bar(&self) {}
+        }
+    )
+)
+
+make_foo!()
+
+pub fn main() {
+    Foo.bar()
+}