Skip to content

Commit 0be4e0e

Browse files
committed
Auto merge of #24155 - chris-chambers:stmt_macros, r=sfackler
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`
2 parents 3a82753 + 77627ea commit 0be4e0e

File tree

7 files changed

+150
-46
lines changed

7 files changed

+150
-46
lines changed

src/libsyntax/ext/base.rs

+16-14
Original file line numberDiff line numberDiff line change
@@ -208,10 +208,11 @@ impl<F> IdentMacroExpander for F
208208
}
209209

210210
// Use a macro because forwarding to a simple function has type system issues
211-
macro_rules! make_stmt_default {
211+
macro_rules! make_stmts_default {
212212
($me:expr) => {
213213
$me.make_expr().map(|e| {
214-
P(codemap::respan(e.span, ast::StmtExpr(e, ast::DUMMY_NODE_ID)))
214+
SmallVector::one(P(codemap::respan(
215+
e.span, ast::StmtExpr(e, ast::DUMMY_NODE_ID))))
215216
})
216217
}
217218
}
@@ -238,12 +239,12 @@ pub trait MacResult {
238239
None
239240
}
240241

241-
/// Create a statement.
242+
/// Create zero or more statements.
242243
///
243244
/// By default this attempts to create an expression statement,
244245
/// returning None if that fails.
245-
fn make_stmt(self: Box<Self>) -> Option<P<ast::Stmt>> {
246-
make_stmt_default!(self)
246+
fn make_stmts(self: Box<Self>) -> Option<SmallVector<P<ast::Stmt>>> {
247+
make_stmts_default!(self)
247248
}
248249
}
249250

@@ -276,7 +277,7 @@ make_MacEager! {
276277
pat: P<ast::Pat>,
277278
items: SmallVector<P<ast::Item>>,
278279
impl_items: SmallVector<P<ast::ImplItem>>,
279-
stmt: P<ast::Stmt>,
280+
stmts: SmallVector<P<ast::Stmt>>,
280281
}
281282

282283
impl MacResult for MacEager {
@@ -292,10 +293,10 @@ impl MacResult for MacEager {
292293
self.impl_items
293294
}
294295

295-
fn make_stmt(self: Box<Self>) -> Option<P<ast::Stmt>> {
296-
match self.stmt {
297-
None => make_stmt_default!(self),
298-
s => s,
296+
fn make_stmts(self: Box<Self>) -> Option<SmallVector<P<ast::Stmt>>> {
297+
match self.stmts.as_ref().map_or(0, |s| s.len()) {
298+
0 => make_stmts_default!(self),
299+
_ => self.stmts,
299300
}
300301
}
301302

@@ -384,10 +385,11 @@ impl MacResult for DummyResult {
384385
Some(SmallVector::zero())
385386
}
386387
}
387-
fn make_stmt(self: Box<DummyResult>) -> Option<P<ast::Stmt>> {
388-
Some(P(codemap::respan(self.span,
389-
ast::StmtExpr(DummyResult::raw_expr(self.span),
390-
ast::DUMMY_NODE_ID))))
388+
fn make_stmts(self: Box<DummyResult>) -> Option<SmallVector<P<ast::Stmt>>> {
389+
Some(SmallVector::one(P(
390+
codemap::respan(self.span,
391+
ast::StmtExpr(DummyResult::raw_expr(self.span),
392+
ast::DUMMY_NODE_ID)))))
391393
}
392394
}
393395

src/libsyntax/ext/expand.rs

+39-27
Original file line numberDiff line numberDiff line change
@@ -720,37 +720,49 @@ pub fn expand_item_mac(it: P<ast::Item>,
720720
}
721721

722722
/// Expand a stmt
723-
fn expand_stmt(s: Stmt, fld: &mut MacroExpander) -> SmallVector<P<Stmt>> {
724-
let (mac, style) = match s.node {
723+
fn expand_stmt(stmt: P<Stmt>, fld: &mut MacroExpander) -> SmallVector<P<Stmt>> {
724+
let stmt = stmt.and_then(|stmt| stmt);
725+
let (mac, style) = match stmt.node {
725726
StmtMac(mac, style) => (mac, style),
726-
_ => return expand_non_macro_stmt(s, fld)
727+
_ => return expand_non_macro_stmt(stmt, fld)
727728
};
728-
let expanded_stmt = match expand_mac_invoc(mac.and_then(|m| m), s.span,
729-
|r| r.make_stmt(),
730-
mark_stmt, fld) {
731-
Some(stmt) => stmt,
732-
None => {
733-
return SmallVector::zero();
729+
730+
let maybe_new_items =
731+
expand_mac_invoc(mac.and_then(|m| m), stmt.span,
732+
|r| r.make_stmts(),
733+
|stmts, mark| stmts.move_map(|m| mark_stmt(m, mark)),
734+
fld);
735+
736+
let mut fully_expanded = match maybe_new_items {
737+
Some(stmts) => {
738+
// Keep going, outside-in.
739+
let new_items = stmts.into_iter().flat_map(|s| {
740+
fld.fold_stmt(s).into_iter()
741+
}).collect();
742+
fld.cx.bt_pop();
743+
new_items
734744
}
745+
None => SmallVector::zero()
735746
};
736747

737-
// Keep going, outside-in.
738-
let fully_expanded = fld.fold_stmt(expanded_stmt);
739-
fld.cx.bt_pop();
740-
748+
// If this is a macro invocation with a semicolon, then apply that
749+
// semicolon to the final statement produced by expansion.
741750
if style == MacStmtWithSemicolon {
742-
fully_expanded.into_iter().map(|s| s.map(|Spanned {node, span}| {
743-
Spanned {
744-
node: match node {
745-
StmtExpr(e, stmt_id) => StmtSemi(e, stmt_id),
746-
_ => node /* might already have a semi */
747-
},
748-
span: span
749-
}
750-
})).collect()
751-
} else {
752-
fully_expanded
751+
if let Some(stmt) = fully_expanded.pop() {
752+
let new_stmt = stmt.map(|Spanned {node, span}| {
753+
Spanned {
754+
node: match node {
755+
StmtExpr(e, stmt_id) => StmtSemi(e, stmt_id),
756+
_ => node /* might already have a semi */
757+
},
758+
span: span
759+
}
760+
});
761+
fully_expanded.push(new_stmt);
762+
}
753763
}
764+
765+
fully_expanded
754766
}
755767

756768
// expand a non-macro stmt. this is essentially the fallthrough for
@@ -1355,7 +1367,7 @@ impl<'a, 'b> Folder for MacroExpander<'a, 'b> {
13551367
}
13561368

13571369
fn fold_stmt(&mut self, stmt: P<ast::Stmt>) -> SmallVector<P<ast::Stmt>> {
1358-
stmt.and_then(|stmt| expand_stmt(stmt, self))
1370+
expand_stmt(stmt, self)
13591371
}
13601372

13611373
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> {
15021514
}
15031515

15041516
// apply a given mark to the given stmt. Used following the expansion of a macro.
1505-
fn mark_stmt(expr: P<ast::Stmt>, m: Mrk) -> P<ast::Stmt> {
1506-
Marker{mark:m}.fold_stmt(expr)
1517+
fn mark_stmt(stmt: P<ast::Stmt>, m: Mrk) -> P<ast::Stmt> {
1518+
Marker{mark:m}.fold_stmt(stmt)
15071519
.expect_one("marking a stmt didn't return exactly one stmt")
15081520
}
15091521

src/libsyntax/ext/tt/macro_rules.rs

+18-4
Original file line numberDiff line numberDiff line change
@@ -99,10 +99,24 @@ impl<'a> MacResult for ParserAnyMacro<'a> {
9999
Some(ret)
100100
}
101101

102-
fn make_stmt(self: Box<ParserAnyMacro<'a>>) -> Option<P<ast::Stmt>> {
103-
let ret = self.parser.borrow_mut().parse_stmt();
104-
self.ensure_complete_parse(true);
105-
ret
102+
fn make_stmts(self: Box<ParserAnyMacro<'a>>)
103+
-> Option<SmallVector<P<ast::Stmt>>> {
104+
let mut ret = SmallVector::zero();
105+
loop {
106+
let mut parser = self.parser.borrow_mut();
107+
match parser.token {
108+
token::Eof => break,
109+
_ => match parser.parse_stmt_nopanic() {
110+
Ok(maybe_stmt) => match maybe_stmt {
111+
Some(stmt) => ret.push(stmt),
112+
None => (),
113+
},
114+
Err(_) => break,
115+
}
116+
}
117+
}
118+
self.ensure_complete_parse(false);
119+
Some(ret)
106120
}
107121
}
108122

src/libsyntax/util/small_vector.rs

+14
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,20 @@ impl<T> SmallVector<T> {
6969
}
7070
}
7171

72+
pub fn pop(&mut self) -> Option<T> {
73+
match self.repr {
74+
Zero => None,
75+
One(..) => {
76+
let one = mem::replace(&mut self.repr, Zero);
77+
match one {
78+
One(v1) => Some(v1),
79+
_ => unreachable!()
80+
}
81+
}
82+
Many(ref mut vs) => vs.pop(),
83+
}
84+
}
85+
7286
pub fn push(&mut self, v: T) {
7387
match self.repr {
7488
Zero => self.repr = One(v),

src/test/compile-fail/macro-incomplete-parse.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ macro_rules! ignored_item {
1717
}
1818

1919
macro_rules! ignored_expr {
20-
() => ( 1, 2 ) //~ ERROR macro expansion ignores token `,`
20+
() => ( 1, //~ ERROR unexpected token: `,`
21+
2 ) //~ ERROR macro expansion ignores token `2`
2122
}
2223

2324
macro_rules! ignored_pat {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
macro_rules! foo {
12+
() => {
13+
struct Bar;
14+
struct Baz;
15+
}
16+
}
17+
18+
macro_rules! grault {
19+
() => {
20+
foo!();
21+
struct Xyzzy;
22+
}
23+
}
24+
25+
fn static_assert_exists<T>() { }
26+
27+
fn main() {
28+
grault!();
29+
static_assert_exists::<Bar>();
30+
static_assert_exists::<Baz>();
31+
static_assert_exists::<Xyzzy>();
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
macro_rules! foo {
12+
() => {
13+
struct Bar;
14+
struct Baz;
15+
}
16+
}
17+
18+
macro_rules! grault {
19+
() => {{
20+
foo!();
21+
struct Xyzzy;
22+
0
23+
}}
24+
}
25+
26+
fn main() {
27+
let x = grault!();
28+
assert_eq!(x, 0);
29+
}

0 commit comments

Comments
 (0)