To allow these braced macro invocation, this PR removes the optional expression from `ast::Block` and instead uses a `StmtKind::Expr` at the end of the statement list.
Currently, braced macro invocations in blocks can expand into statements (and items) except when they are last in a block, in which case they can only expand into expressions.
For example,
```rust
macro_rules! make_stmt {
() => { let x = 0; }
}
fn f() {
make_stmt! {} //< This is OK...
let x = 0; //< ... unless this line is commented out.
}
```
Fixes #34418.
}
pub fn assign_node_ids(sess: &Session, krate: ast::Crate) -> ast::Crate {
- use syntax::codemap::Spanned;
+ use syntax::ptr::P;
+ use syntax::util::move_map::MoveMap;
+
struct NodeIdAssigner<'a> {
sess: &'a Session,
}
assert_eq!(old_id, ast::DUMMY_NODE_ID);
self.sess.next_node_id()
}
- if let Some(Spanned { node: ast::StmtKind::Expr(expr, _), span }) = stmt {
+
+ fn fold_block(&mut self, block: P<ast::Block>) -> P<ast::Block> {
+ block.map(|mut block| {
+ block.id = self.new_id(block.id);
+
+ let stmt = block.stmts.pop();
+ block.stmts = block.stmts.move_flat_map(|s| self.fold_stmt(s).into_iter());
- let id = expr.id;
- block.stmts.push(Spanned {
++ if let Some(ast::Stmt { node: ast::StmtKind::Expr(expr), span, .. }) = stmt {
+ let expr = self.fold_expr(expr);
- node: ast::StmtKind::Expr(expr, id)
++ block.stmts.push(ast::Stmt {
++ id: expr.id,
++ node: ast::StmtKind::Expr(expr),
+ span: span,
+ });
+ } else if let Some(stmt) = stmt {
+ block.stmts.extend(self.fold_stmt(stmt));
+ }
+
+ block
+ })
+ }
}
let krate = time(sess.time_passes(),
fn fold_block(&mut self, b: P<ast::Block>) -> P<ast::Block> {
fn expr_to_block(rules: ast::BlockCheckMode, e: Option<P<ast::Expr>>) -> P<ast::Block> {
P(ast::Block {
- expr: e,
- stmts: vec![],
- stmts: e.map(|e| codemap::Spanned {
++ stmts: e.map(|e| ast::Stmt {
++ id: ast::DUMMY_NODE_ID,
+ span: e.span,
- node: ast::StmtKind::Expr(e, ast::DUMMY_NODE_ID),
++ node: ast::StmtKind::Expr(e),
+ }).into_iter().collect(),
rules: rules,
id: ast::DUMMY_NODE_ID,
- span: codemap::DUMMY_SP,
+ span: syntax_pos::DUMMY_SP,
})
}
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash)]
pub enum StmtKind {
- /// Could be an item or a local (let) binding:
- Decl(P<Decl>, NodeId),
+ /// A local (let) binding.
+ Local(P<Local>),
- /// Expr without trailing semi-colon
- Expr(P<Expr>, NodeId),
+ /// An item definition.
+ Item(P<Item>),
- /// Expr without trailing semi-colon (must have unit type).
- /// Expr with trailing semi-colon (may have any type):
- Semi(P<Expr>, NodeId),
++ /// Expr without trailing semi-colon.
+ Expr(P<Expr>),
- Mac(P<Mac>, MacStmtStyle, ThinAttributes),
-}
+ Semi(P<Expr>),
-impl StmtKind {
- pub fn id(&self) -> Option<NodeId> {
- match *self {
- StmtKind::Decl(_, id) => Some(id),
- StmtKind::Expr(_, id) => Some(id),
- StmtKind::Semi(_, id) => Some(id),
- StmtKind::Mac(..) => None,
- }
- }
-
- pub fn attrs(&self) -> &[Attribute] {
- HasAttrs::attrs(self)
- }
+ Mac(P<(Mac, MacStmtStyle, ThinVec<Attribute>)>),
}
#[derive(Clone, Copy, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
}
fn stmt_expr(&self, expr: P<ast::Expr>) -> ast::Stmt {
- respan(expr.span, ast::StmtKind::Expr(expr, ast::DUMMY_NODE_ID))
++ ast::Stmt {
++ id: ast::DUMMY_NODE_ID,
++ span: expr.span,
++ node: ast::StmtKind::Expr(expr),
++ }
+ }
+
+ fn stmt_semi(&self, expr: P<ast::Expr>) -> ast::Stmt {
- respan(expr.span, ast::StmtKind::Semi(expr, ast::DUMMY_NODE_ID))
+ ast::Stmt {
+ id: ast::DUMMY_NODE_ID,
+ span: expr.span,
+ node: ast::StmtKind::Semi(expr),
+ }
}
fn stmt_let(&self, sp: Span, mutbl: bool, ident: ast::Ident,
init: Some(ex),
id: ast::DUMMY_NODE_ID,
span: sp,
- attrs: None,
+ attrs: ast::ThinVec::new(),
});
- let decl = respan(sp, ast::DeclKind::Local(local));
- P(respan(sp, ast::StmtKind::Decl(P(decl), ast::DUMMY_NODE_ID)))
+ P(ast::Stmt {
+ id: ast::DUMMY_NODE_ID,
+ node: ast::StmtKind::Local(local),
+ span: sp,
+ })
}
- fn block(&self, span: Span, stmts: Vec<ast::Stmt>,
- expr: Option<P<Expr>>) -> P<ast::Block> {
- self.block_all(span, stmts, expr)
- }
-
fn stmt_item(&self, sp: Span, item: P<ast::Item>) -> ast::Stmt {
- let decl = respan(sp, ast::DeclKind::Item(item));
- respan(sp, ast::StmtKind::Decl(P(decl), ast::DUMMY_NODE_ID))
+ ast::Stmt {
+ id: ast::DUMMY_NODE_ID,
+ node: ast::StmtKind::Item(item),
+ span: sp,
+ }
}
fn block_expr(&self, expr: P<ast::Expr>) -> P<ast::Block> {
- self.block_all(expr.span, Vec::new(), Some(expr))
- }
- fn block_all(&self,
- span: Span,
- stmts: Vec<ast::Stmt>,
- expr: Option<P<ast::Expr>>) -> P<ast::Block> {
- P(ast::Block {
- stmts: stmts,
- expr: expr,
- id: ast::DUMMY_NODE_ID,
- rules: BlockCheckMode::Default,
- span: span,
- })
- self.block(expr.span, vec![Spanned {
++ self.block(expr.span, vec![ast::Stmt {
++ id: ast::DUMMY_NODE_ID,
+ span: expr.span,
- node: ast::StmtKind::Expr(expr, ast::DUMMY_NODE_ID),
++ node: ast::StmtKind::Expr(expr),
+ }])
+ }
+ fn block(&self, span: Span, stmts: Vec<ast::Stmt>) -> P<ast::Block> {
+ P(ast::Block {
+ stmts: stmts,
+ id: ast::DUMMY_NODE_ID,
+ rules: BlockCheckMode::Default,
+ span: span,
+ })
}
fn expr(&self, span: Span, node: ast::ExprKind) -> P<ast::Expr> {
),
}),
span: sp(17,18),
- attrs: None,}),
- ast::DUMMY_NODE_ID),
+ attrs: ThinVec::new()})),
+ id: ast::DUMMY_NODE_ID,
span: sp(17,19)}),
- expr: None,
id: ast::DUMMY_NODE_ID,
rules: ast::BlockCheckMode::Default, // no idea
span: sp(15,21),
let body_expr = self.parse_expr()?;
P(ast::Block {
id: ast::DUMMY_NODE_ID,
- stmts: vec![],
span: body_expr.span,
- expr: Some(body_expr),
- stmts: vec![Spanned {
++ stmts: vec![Stmt {
+ span: body_expr.span,
- node: StmtKind::Expr(body_expr, ast::DUMMY_NODE_ID),
++ node: StmtKind::Expr(body_expr),
++ id: ast::DUMMY_NODE_ID,
+ }],
rules: BlockCheckMode::Default,
})
}
/// Precondition: already parsed the '{'.
fn parse_block_tail(&mut self, lo: BytePos, s: BlockCheckMode) -> PResult<'a, P<Block>> {
let mut stmts = vec![];
- let mut expr = None;
while !self.eat(&token::CloseDelim(token::Brace)) {
- let Spanned {node, span} = if let Some(s) = self.parse_stmt_() {
+ let Stmt {node, span, ..} = if let Some(s) = self.parse_stmt_() {
s
} else if self.token == token::Eof {
break;
// Found only `;` or `}`.
continue;
};
+
match node {
- StmtKind::Expr(e, _) => {
+ StmtKind::Expr(e) => {
- self.handle_expression_like_statement(e, span, &mut stmts, &mut expr)?;
+ self.handle_expression_like_statement(e, span, &mut stmts)?;
}
- StmtKind::Mac(mac, MacStmtStyle::NoBraces, attrs) => {
- // statement macro without braces
- match self.token {
- token::Semi => {
- stmts.push(Spanned {
- node: StmtKind::Mac(mac, MacStmtStyle::Semicolon, attrs),
- span: mk_sp(span.lo, self.span.hi),
- });
- self.bump();
- }
- _ => {
- let e = self.mk_mac_expr(span.lo, span.hi,
- mac.and_then(|m| m.node),
- None);
- let lo = e.span.lo;
- let e = self.parse_dot_or_call_expr_with(e, lo, attrs)?;
- let e = self.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(e))?;
- self.handle_expression_like_statement(e, span, &mut stmts)?;
- }
- }
- }
- StmtKind::Mac(m, style, attrs) => {
- // statement macro; might be an expr
- match self.token {
- token::Semi => {
- stmts.push(Spanned {
- node: StmtKind::Mac(m, MacStmtStyle::Semicolon, attrs),
- span: mk_sp(span.lo, self.span.hi),
- });
- self.bump();
- }
- _ => {
- stmts.push(Spanned {
- node: StmtKind::Mac(m, style, attrs),
- span: span
- });
- }
- }
+ StmtKind::Mac(mac) => {
- self.handle_macro_in_block(mac.unwrap(), span, &mut stmts, &mut expr)?;
++ self.handle_macro_in_block(mac.unwrap(), span, &mut stmts)?;
}
_ => { // all other kinds of statements:
let mut hi = span.hi;
}))
}
- stmts: &mut Vec<Stmt>,
- last_block_expr: &mut Option<P<Expr>>)
+ fn handle_macro_in_block(&mut self,
+ (mac, style, attrs): (ast::Mac, MacStmtStyle, ThinVec<Attribute>),
+ span: Span,
- self.handle_expression_like_statement(e, span, stmts, last_block_expr)?;
++ stmts: &mut Vec<Stmt>)
+ -> PResult<'a, ()> {
+ if style == MacStmtStyle::NoBraces {
+ // statement macro without braces; might be an
+ // expr depending on whether a semicolon follows
+ match self.token {
+ token::Semi => {
+ stmts.push(Stmt {
+ id: ast::DUMMY_NODE_ID,
+ node: StmtKind::Mac(P((mac, MacStmtStyle::Semicolon, attrs))),
+ span: mk_sp(span.lo, self.span.hi),
+ });
+ self.bump();
+ }
+ _ => {
+ let e = self.mk_mac_expr(span.lo, span.hi, mac.node, ThinVec::new());
+ let lo = e.span.lo;
+ let e = self.parse_dot_or_call_expr_with(e, lo, attrs)?;
+ let e = self.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(e))?;
- token::CloseDelim(token::Brace) => {
- // if a block ends in `m!(arg)` without
- // a `;`, it must be an expr
- *last_block_expr = Some(self.mk_mac_expr(span.lo, span.hi, mac.node, attrs));
- }
++ self.handle_expression_like_statement(e, span, stmts)?;
+ }
+ }
+ } else {
+ // statement macro; might be an expr
+ match self.token {
+ token::Semi => {
+ stmts.push(Stmt {
+ id: ast::DUMMY_NODE_ID,
+ node: StmtKind::Mac(P((mac, MacStmtStyle::Semicolon, attrs))),
+ span: mk_sp(span.lo, self.span.hi),
+ });
+ self.bump();
+ }
+ _ => {
+ stmts.push(Stmt {
+ id: ast::DUMMY_NODE_ID,
+ node: StmtKind::Mac(P((mac, style, attrs))),
+ span: span
+ });
+ }
+ }
+ }
+ Ok(())
+ }
+
fn handle_expression_like_statement(&mut self,
e: P<Expr>,
span: Span,
span: span_with_semi,
});
}
- token::CloseDelim(token::Brace) => *last_block_expr = Some(e),
_ => {
- stmts.push(Spanned {
- node: StmtKind::Expr(e, ast::DUMMY_NODE_ID),
+ stmts.push(Stmt {
+ id: ast::DUMMY_NODE_ID,
+ node: StmtKind::Expr(e),
span: span
});
}
pub fn print_stmt(&mut self, st: &ast::Stmt) -> io::Result<()> {
try!(self.maybe_print_comment(st.span.lo));
match st.node {
- ast::StmtKind::Decl(ref decl, _) => {
- try!(self.print_decl(&decl));
+ ast::StmtKind::Local(ref loc) => {
+ try!(self.print_outer_attributes(&loc.attrs));
+ try!(self.space_if_not_bol());
+ try!(self.ibox(INDENT_UNIT));
+ try!(self.word_nbsp("let"));
+
+ try!(self.ibox(INDENT_UNIT));
+ try!(self.print_local_decl(&loc));
+ try!(self.end());
+ if let Some(ref init) = loc.init {
+ try!(self.nbsp());
+ try!(self.word_space("="));
+ try!(self.print_expr(&init));
+ }
++ try!(word(&mut self.s, ";"));
+ self.end()?;
}
- ast::StmtKind::Expr(ref expr, _) => {
+ ast::StmtKind::Item(ref item) => self.print_item(&item)?,
+ ast::StmtKind::Expr(ref expr) => {
try!(self.space_if_not_bol());
try!(self.print_expr_outer_attr_style(&expr, false));
+ if parse::classify::expr_requires_semi_to_be_stmt(expr) {
+ try!(word(&mut self.s, ";"));
+ }
}
- ast::StmtKind::Semi(ref expr, _) => {
+ ast::StmtKind::Semi(ref expr) => {
try!(self.space_if_not_bol());
try!(self.print_expr_outer_attr_style(&expr, false));
try!(word(&mut self.s, ";"));
try!(self.print_inner_attributes(attrs));
- for st in &blk.stmts {
- try!(self.print_stmt(st));
- }
- match blk.expr {
- Some(ref expr) => {
- try!(self.space_if_not_bol());
- try!(self.print_expr_outer_attr_style(&expr, false));
- try!(self.maybe_print_trailing_comment(expr.span, Some(blk.span.hi)));
+ for (i, st) in blk.stmts.iter().enumerate() {
+ match st.node {
- ast::StmtKind::Expr(ref expr, _) if i == blk.stmts.len() - 1 => {
++ ast::StmtKind::Expr(ref expr) if i == blk.stmts.len() - 1 => {
+ try!(self.space_if_not_bol());
+ try!(self.print_expr_outer_attr_style(&expr, false));
+ try!(self.maybe_print_trailing_comment(expr.span, Some(blk.span.hi)));
+ }
+ _ => try!(self.print_stmt(st)),
}
- _ => ()
}
+
try!(self.bclose_maybe_open(blk.span, indented, close_box));
self.ann.post(self, NodeBlock(blk))
}
_ => false
};
- if !default_return || !body.stmts.is_empty() || body.expr.is_none() {
- try!(self.print_block_unclosed(&body));
- } else {
- // we extract the block, so as not to create another set of boxes
- let i_expr = body.expr.as_ref().unwrap();
- match i_expr.node {
- ast::ExprKind::Block(ref blk) => {
+ match body.stmts.last().map(|stmt| &stmt.node) {
- Some(&ast::StmtKind::Expr(ref i_expr, _)) if default_return &&
- body.stmts.len() == 1 => {
++ Some(&ast::StmtKind::Expr(ref i_expr)) if default_return &&
++ body.stmts.len() == 1 => {
+ // we extract the block, so as not to create another set of boxes
+ if let ast::ExprKind::Block(ref blk) = i_expr.node {
- try!(self.print_block_unclosed_with_attrs(
- &blk,
- i_expr.attrs.as_attr_slice()));
+ try!(self.print_block_unclosed_with_attrs(&blk, &i_expr.attrs));
- }
- _ => {
+ } else {
// this is a bare expression
try!(self.print_expr(&i_expr));
try!(self.end()); // need to close a box
walk_list!(visitor, visit_attribute, &struct_field.attrs);
}
-pub fn walk_block<'v, V: Visitor<'v>>(visitor: &mut V, block: &'v Block) {
+pub fn walk_block<V: Visitor>(visitor: &mut V, block: &Block) {
walk_list!(visitor, visit_stmt, &block.stmts);
- walk_list!(visitor, visit_expr, &block.expr);
}
-pub fn walk_stmt<'v, V: Visitor<'v>>(visitor: &mut V, statement: &'v Stmt) {
+pub fn walk_stmt<V: Visitor>(visitor: &mut V, statement: &Stmt) {
match statement.node {
- StmtKind::Decl(ref declaration, _) => visitor.visit_decl(declaration),
- StmtKind::Expr(ref expression, _) | StmtKind::Semi(ref expression, _) => {
+ StmtKind::Local(ref local) => visitor.visit_local(local),
+ StmtKind::Item(ref item) => visitor.visit_item(item),
+ StmtKind::Expr(ref expression) | StmtKind::Semi(ref expression) => {
visitor.visit_expr(expression)
}
- StmtKind::Mac(ref mac, _, ref attrs) => {
+ StmtKind::Mac(ref mac) => {
+ let (ref mac, _, ref attrs) = **mac;
visitor.visit_mac(mac);
- for attr in attrs.as_attr_slice() {
+ for attr in attrs.iter() {
visitor.visit_attribute(attr);
}
}
let name = ecx.ident_of(name);
let item = ecx.item(sp, name, vec![], st);
- let decl = respan(sp, ast::DeclKind::Item(item));
+ let stmt = ast::Stmt {
+ id: ast::DUMMY_NODE_ID,
+ node: ast::StmtKind::Item(item),
+ span: sp,
+ };
// Wrap the declaration in a block so that it forms a single expression.
- ecx.expr_block(ecx.block(sp, vec![stmt], Some(ecx.expr_ident(sp, name))))
- ecx.expr_block(ecx.block(sp, vec![
- respan(sp, ast::StmtKind::Decl(P(decl), ast::DUMMY_NODE_ID)),
- ecx.stmt_expr(ecx.expr_ident(sp, name)),
- ]))
++ ecx.expr_block(ecx.block(sp, vec![stmt, ecx.stmt_expr(ecx.expr_ident(sp, name))]))
}
/// Actually builds the expression which the iformat! block will be expanded