]> git.lizzy.rs Git - rust.git/commitdiff
Auto merge of #24155 - chris-chambers:stmt_macros, r=sfackler
authorbors <bors@rust-lang.org>
Sat, 11 Apr 2015 08:07:34 +0000 (08:07 +0000)
committerbors <bors@rust-lang.org>
Sat, 11 Apr 2015 08:07:34 +0000 (08:07 +0000)
Statement macros are now treated somewhat like item macros, in that a statement macro can now expand into a series of statements, rather than just a single statement.

This allows statement macros to be nested inside other kinds of macros and expand properly, where previously the expansion would only work when no nesting was present.

See:
- `src/test/run-pass/macro-stmt_macro_in_expr_macro.rs`
- `src/test/run-pass/macro-nested_stmt_macro.rs`

This changes the interface of the MacResult trait.  make_stmt has become make_stmts and now returns a vector, rather than a single item.  Plugin writers who were implementing MacResult will have breakage, as well as anyone using MacEager::stmt.

See:
- `src/libsyntax/ext/base.rs`

This also causes a minor difference in behavior to the diagnostics produced by certain malformed macros.

See:
- `src/test/compile-fail/macro-incomplete-parse.rs`

src/libsyntax/ext/base.rs
src/libsyntax/ext/expand.rs
src/libsyntax/ext/tt/macro_rules.rs
src/libsyntax/util/small_vector.rs
src/test/compile-fail/macro-incomplete-parse.rs
src/test/run-pass/macro-nested_stmt_macros.rs [new file with mode: 0644]
src/test/run-pass/macro-stmt_macro_in_expr_macro.rs [new file with mode: 0644]

index 80ee92608a520e965598a6ec62177a9356a5a4b1..346fb3580e134811f699baf7dfbd50a11c97871d 100644 (file)
@@ -208,10 +208,11 @@ fn expand<'cx>(&self,
 }
 
 // Use a macro because forwarding to a simple function has type system issues
-macro_rules! make_stmt_default {
+macro_rules! make_stmts_default {
     ($me:expr) => {
         $me.make_expr().map(|e| {
-            P(codemap::respan(e.span, ast::StmtExpr(e, ast::DUMMY_NODE_ID)))
+            SmallVector::one(P(codemap::respan(
+                e.span, ast::StmtExpr(e, ast::DUMMY_NODE_ID))))
         })
     }
 }
@@ -238,12 +239,12 @@ fn make_pat(self: Box<Self>) -> Option<P<ast::Pat>> {
         None
     }
 
-    /// Create a statement.
+    /// Create zero or more statements.
     ///
     /// By default this attempts to create an expression statement,
     /// returning None if that fails.
-    fn make_stmt(self: Box<Self>) -> Option<P<ast::Stmt>> {
-        make_stmt_default!(self)
+    fn make_stmts(self: Box<Self>) -> Option<SmallVector<P<ast::Stmt>>> {
+        make_stmts_default!(self)
     }
 }
 
@@ -276,7 +277,7 @@ pub fn $fld(v: $t) -> Box<MacResult> {
     pat: P<ast::Pat>,
     items: SmallVector<P<ast::Item>>,
     impl_items: SmallVector<P<ast::ImplItem>>,
-    stmt: P<ast::Stmt>,
+    stmts: SmallVector<P<ast::Stmt>>,
 }
 
 impl MacResult for MacEager {
@@ -292,10 +293,10 @@ fn make_impl_items(self: Box<Self>) -> Option<SmallVector<P<ast::ImplItem>>> {
         self.impl_items
     }
 
-    fn make_stmt(self: Box<Self>) -> Option<P<ast::Stmt>> {
-        match self.stmt {
-            None => make_stmt_default!(self),
-            s => s,
+    fn make_stmts(self: Box<Self>) -> Option<SmallVector<P<ast::Stmt>>> {
+        match self.stmts.as_ref().map_or(0, |s| s.len()) {
+            0 => make_stmts_default!(self),
+            _ => self.stmts,
         }
     }
 
@@ -384,10 +385,11 @@ fn make_impl_items(self: Box<DummyResult>) -> Option<SmallVector<P<ast::ImplItem
             Some(SmallVector::zero())
         }
     }
-    fn make_stmt(self: Box<DummyResult>) -> Option<P<ast::Stmt>> {
-        Some(P(codemap::respan(self.span,
-                               ast::StmtExpr(DummyResult::raw_expr(self.span),
-                                             ast::DUMMY_NODE_ID))))
+    fn make_stmts(self: Box<DummyResult>) -> Option<SmallVector<P<ast::Stmt>>> {
+        Some(SmallVector::one(P(
+            codemap::respan(self.span,
+                            ast::StmtExpr(DummyResult::raw_expr(self.span),
+                                          ast::DUMMY_NODE_ID)))))
     }
 }
 
index dd0c7d716b7979e462145a5dfa5fef7f44d28132..5f4ec01791c88506ec04ec3c6167761cc9a77294 100644 (file)
@@ -720,37 +720,49 @@ pub fn expand_item_mac(it: P<ast::Item>,
 }
 
 /// Expand a stmt
-fn expand_stmt(s: Stmt, fld: &mut MacroExpander) -> SmallVector<P<Stmt>> {
-    let (mac, style) = match s.node {
+fn expand_stmt(stmt: P<Stmt>, fld: &mut MacroExpander) -> SmallVector<P<Stmt>> {
+    let stmt = stmt.and_then(|stmt| stmt);
+    let (mac, style) = match stmt.node {
         StmtMac(mac, style) => (mac, style),
-        _ => return expand_non_macro_stmt(s, fld)
+        _ => return expand_non_macro_stmt(stmt, fld)
     };
-    let expanded_stmt = match expand_mac_invoc(mac.and_then(|m| m), s.span,
-                                                |r| r.make_stmt(),
-                                                mark_stmt, fld) {
-        Some(stmt) => stmt,
-        None => {
-            return SmallVector::zero();
+
+    let maybe_new_items =
+        expand_mac_invoc(mac.and_then(|m| m), stmt.span,
+                         |r| r.make_stmts(),
+                         |stmts, mark| stmts.move_map(|m| mark_stmt(m, mark)),
+                         fld);
+
+    let mut fully_expanded = match maybe_new_items {
+        Some(stmts) => {
+            // Keep going, outside-in.
+            let new_items = stmts.into_iter().flat_map(|s| {
+                fld.fold_stmt(s).into_iter()
+            }).collect();
+            fld.cx.bt_pop();
+            new_items
         }
+        None => SmallVector::zero()
     };
 
-    // Keep going, outside-in.
-    let fully_expanded = fld.fold_stmt(expanded_stmt);
-    fld.cx.bt_pop();
-
+    // If this is a macro invocation with a semicolon, then apply that
+    // semicolon to the final statement produced by expansion.
     if style == MacStmtWithSemicolon {
-        fully_expanded.into_iter().map(|s| s.map(|Spanned {node, span}| {
-            Spanned {
-                node: match node {
-                    StmtExpr(e, stmt_id) => StmtSemi(e, stmt_id),
-                    _ => node /* might already have a semi */
-                },
-                span: span
-            }
-        })).collect()
-    } else {
-        fully_expanded
+        if let Some(stmt) = fully_expanded.pop() {
+            let new_stmt = stmt.map(|Spanned {node, span}| {
+                Spanned {
+                    node: match node {
+                        StmtExpr(e, stmt_id) => StmtSemi(e, stmt_id),
+                        _ => node /* might already have a semi */
+                    },
+                    span: span
+                }
+            });
+            fully_expanded.push(new_stmt);
+        }
     }
+
+    fully_expanded
 }
 
 // expand a non-macro stmt. this is essentially the fallthrough for
@@ -1355,7 +1367,7 @@ fn fold_item_underscore(&mut self, item: ast::Item_) -> ast::Item_ {
     }
 
     fn fold_stmt(&mut self, stmt: P<ast::Stmt>) -> SmallVector<P<ast::Stmt>> {
-        stmt.and_then(|stmt| expand_stmt(stmt, self))
+        expand_stmt(stmt, self)
     }
 
     fn fold_block(&mut self, block: P<Block>) -> P<Block> {
@@ -1502,8 +1514,8 @@ fn mark_pat(pat: P<ast::Pat>, m: Mrk) -> P<ast::Pat> {
 }
 
 // apply a given mark to the given stmt. Used following the expansion of a macro.
-fn mark_stmt(expr: P<ast::Stmt>, m: Mrk) -> P<ast::Stmt> {
-    Marker{mark:m}.fold_stmt(expr)
+fn mark_stmt(stmt: P<ast::Stmt>, m: Mrk) -> P<ast::Stmt> {
+    Marker{mark:m}.fold_stmt(stmt)
         .expect_one("marking a stmt didn't return exactly one stmt")
 }
 
index 009075540fa1e01cbcf9f312ad009859c86420b7..730da6cc59419f3ea71566d1edf2b36fe9ecb711 100644 (file)
@@ -99,10 +99,24 @@ fn make_impl_items(self: Box<ParserAnyMacro<'a>>)
         Some(ret)
     }
 
-    fn make_stmt(self: Box<ParserAnyMacro<'a>>) -> Option<P<ast::Stmt>> {
-        let ret = self.parser.borrow_mut().parse_stmt();
-        self.ensure_complete_parse(true);
-        ret
+    fn make_stmts(self: Box<ParserAnyMacro<'a>>)
+                 -> Option<SmallVector<P<ast::Stmt>>> {
+        let mut ret = SmallVector::zero();
+        loop {
+            let mut parser = self.parser.borrow_mut();
+            match parser.token {
+                token::Eof => break,
+                _ => match parser.parse_stmt_nopanic() {
+                    Ok(maybe_stmt) => match maybe_stmt {
+                        Some(stmt) => ret.push(stmt),
+                        None => (),
+                    },
+                    Err(_) => break,
+                }
+            }
+        }
+        self.ensure_complete_parse(false);
+        Some(ret)
     }
 }
 
index 1649934f4b10e5d4628d20e9ed26370bb9e5a7f6..6b864d5294766fabffa4448c4440f997c32d6d22 100644 (file)
@@ -69,6 +69,20 @@ pub fn as_slice<'a>(&'a self) -> &'a [T] {
         }
     }
 
+    pub fn pop(&mut self) -> Option<T> {
+        match self.repr {
+            Zero => None,
+            One(..) => {
+                let one = mem::replace(&mut self.repr, Zero);
+                match one {
+                    One(v1) => Some(v1),
+                    _ => unreachable!()
+                }
+            }
+            Many(ref mut vs) => vs.pop(),
+        }
+    }
+
     pub fn push(&mut self, v: T) {
         match self.repr {
             Zero => self.repr = One(v),
index b3ce84c7e6f1a2dbfcd46c49e2f9e13910146c0c..32770d9018938b96da18a91a61abc5beeb0e8330 100644 (file)
@@ -17,7 +17,8 @@ fn bar() {}
 }
 
 macro_rules! ignored_expr {
-    () => ( 1, 2 ) //~ ERROR macro expansion ignores token `,`
+    () => ( 1,  //~ ERROR unexpected token: `,`
+            2 ) //~ ERROR macro expansion ignores token `2`
 }
 
 macro_rules! ignored_pat {
diff --git a/src/test/run-pass/macro-nested_stmt_macros.rs b/src/test/run-pass/macro-nested_stmt_macros.rs
new file mode 100644 (file)
index 0000000..5997a4f
--- /dev/null
@@ -0,0 +1,32 @@
+// Copyright 2015 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.
+
+macro_rules! foo {
+    () => {
+        struct Bar;
+        struct Baz;
+    }
+}
+
+macro_rules! grault {
+    () => {
+        foo!();
+        struct Xyzzy;
+    }
+}
+
+fn static_assert_exists<T>() { }
+
+fn main() {
+    grault!();
+    static_assert_exists::<Bar>();
+    static_assert_exists::<Baz>();
+    static_assert_exists::<Xyzzy>();
+}
diff --git a/src/test/run-pass/macro-stmt_macro_in_expr_macro.rs b/src/test/run-pass/macro-stmt_macro_in_expr_macro.rs
new file mode 100644 (file)
index 0000000..c5badd7
--- /dev/null
@@ -0,0 +1,29 @@
+// Copyright 2015 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.
+
+macro_rules! foo {
+    () => {
+        struct Bar;
+        struct Baz;
+    }
+}
+
+macro_rules! grault {
+    () => {{
+        foo!();
+        struct Xyzzy;
+        0
+    }}
+}
+
+fn main() {
+    let x = grault!();
+    assert_eq!(x, 0);
+}