From 6189e6cdba6d12ab65baafd95c5f8a6e0cc37937 Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Sat, 2 Jul 2016 09:01:21 +0000 Subject: [PATCH 1/7] Clean up statement parsing without changing the semantics of `parse_stmt`. --- src/libsyntax/ast.rs | 13 +++ src/libsyntax/ext/expand.rs | 9 +- src/libsyntax/parse/parser.rs | 149 ++++++++++------------------------ 3 files changed, 59 insertions(+), 112 deletions(-) diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index cc033cec8b8b1..99e37ec80c081 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -804,6 +804,19 @@ pub struct Stmt { pub span: Span, } +impl Stmt { + pub fn add_trailing_semicolon(mut self) -> Self { + self.node = match self.node { + StmtKind::Expr(expr) => StmtKind::Semi(expr), + StmtKind::Mac(mac) => StmtKind::Mac(mac.map(|(mac, _style, attrs)| { + (mac, MacStmtStyle::Semicolon, attrs) + })), + node @ _ => node, + }; + self + } +} + impl fmt::Debug for Stmt { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "stmt({}: {})", self.id.to_string(), pprust::stmt_to_string(self)) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index b2b63d0dbb4bd..220e0a753c30b 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -444,14 +444,7 @@ fn expand_stmt(stmt: Stmt, fld: &mut MacroExpander) -> SmallVector { // semicolon to the final statement produced by expansion. if style == MacStmtStyle::Semicolon { if let Some(stmt) = fully_expanded.pop() { - fully_expanded.push(Stmt { - id: stmt.id, - node: match stmt.node { - StmtKind::Expr(expr) => StmtKind::Semi(expr), - _ => stmt.node /* might already have a semi */ - }, - span: stmt.span, - }); + fully_expanded.push(stmt.add_trailing_semicolon()); } } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index e4875b7c244fd..0dd8c199827d2 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -3789,7 +3789,13 @@ impl<'a> Parser<'a> { self.span_err(self.last_span, message); } - /// Parse a statement. may include decl. + /// Parse a statement. This stops just before trailing semicolons on everything but items. + /// e.g. a `StmtKind::Semi` parses to a `StmtKind::Expr`, leaving the trailing `;` unconsumed. + /// + /// Also, if a macro begins an expression statement, this only parses the macro. For example, + /// ```rust + /// vec![1].into_iter(); //< `parse_stmt` only parses the "vec![1]" + /// ``` pub fn parse_stmt(&mut self) -> PResult<'a, Option> { Ok(self.parse_stmt_()) } @@ -4038,36 +4044,14 @@ impl<'a> Parser<'a> { let mut stmts = vec![]; while !self.eat(&token::CloseDelim(token::Brace)) { - let Stmt {node, span, ..} = if let Some(s) = self.parse_stmt_() { - s + if let Some(stmt) = self.parse_stmt_() { + stmts.push(self.finish_parsing_statement(stmt)?); } else if self.token == token::Eof { break; } else { // Found only `;` or `}`. continue; }; - - match node { - StmtKind::Expr(e) => { - self.handle_expression_like_statement(e, span, &mut stmts)?; - } - StmtKind::Mac(mac) => { - self.handle_macro_in_block(mac.unwrap(), span, &mut stmts)?; - } - _ => { // all other kinds of statements: - let mut hi = span.hi; - if classify::stmt_ends_with_semi(&node) { - self.expect(&token::Semi)?; - hi = self.last_span.hi; - } - - stmts.push(Stmt { - id: ast::DUMMY_NODE_ID, - node: node, - span: mk_sp(span.lo, hi) - }); - } - } } Ok(P(ast::Block { @@ -4078,93 +4062,50 @@ impl<'a> Parser<'a> { })) } - fn handle_macro_in_block(&mut self, - (mac, style, attrs): (ast::Mac, MacStmtStyle, ThinVec), - span: Span, - stmts: &mut Vec) - -> 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))?; - 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 - }); - } + /// Finish parsing expressions that start with macros and handle trailing semicolons + /// (or the lack thereof) -- c.f. `parse_stmt`. + fn finish_parsing_statement(&mut self, mut stmt: Stmt) -> PResult<'a, Stmt> { + if let StmtKind::Mac(mac) = stmt.node { + if mac.1 != MacStmtStyle::NoBraces || self.token == token::Semi { + stmt.node = StmtKind::Mac(mac); + } else { + let (mac, _style, attrs) = mac.unwrap(); + let e = self.mk_mac_expr(stmt.span.lo, stmt.span.hi, mac.node, ThinVec::new()); + let e = self.parse_dot_or_call_expr_with(e, stmt.span.lo, attrs)?; + let e = self.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(e))?; + stmt.node = StmtKind::Expr(e); } } - Ok(()) + + self.handle_trailing_semicolon(stmt) } - fn handle_expression_like_statement(&mut self, - e: P, - span: Span, - stmts: &mut Vec) - -> PResult<'a, ()> { - // expression without semicolon - if classify::expr_requires_semi_to_be_stmt(&e) { - // Just check for errors and recover; do not eat semicolon yet. - if let Err(mut e) = - self.expect_one_of(&[], &[token::Semi, token::CloseDelim(token::Brace)]) - { - e.emit(); - self.recover_stmt(); + fn handle_trailing_semicolon(&mut self, mut stmt: Stmt) -> PResult<'a, Stmt> { + match stmt.node { + StmtKind::Expr(ref expr) => { + // expression without semicolon + if classify::expr_requires_semi_to_be_stmt(expr) { + // Just check for errors and recover; do not eat semicolon yet. + if let Err(mut e) = + self.expect_one_of(&[], &[token::Semi, token::CloseDelim(token::Brace)]) + { + e.emit(); + self.recover_stmt(); + } + } } + StmtKind::Local(..) => { + self.expect_one_of(&[token::Semi], &[])?; + } + _ => {} } - match self.token { - token::Semi => { - self.bump(); - let span_with_semi = Span { - lo: span.lo, - hi: self.last_span.hi, - expn_id: span.expn_id, - }; - stmts.push(Stmt { - id: ast::DUMMY_NODE_ID, - node: StmtKind::Semi(e), - span: span_with_semi, - }); - } - _ => { - stmts.push(Stmt { - id: ast::DUMMY_NODE_ID, - node: StmtKind::Expr(e), - span: span - }); - } + if self.eat(&token::Semi) { + stmt = stmt.add_trailing_semicolon(); } - Ok(()) + + stmt.span.hi = self.last_span.hi; + Ok(stmt) } // Parses a sequence of bounds if a `:` is found, From 57c56dd7e04bcd25ed578f27e65ec2379299f44b Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Sat, 2 Jul 2016 09:32:23 +0000 Subject: [PATCH 2/7] Parse macro-expanded statements like ordinary statements. --- src/libsyntax/ext/tt/macro_rules.rs | 8 +++++++- src/libsyntax/parse/parser.rs | 4 ++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index 84572b84963f3..7b09a44b814ff 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -118,12 +118,18 @@ impl<'a> MacResult for ParserAnyMacro<'a> { fn make_stmts(self: Box>) -> Option> { + let parse_stmt = |parser: &mut Parser<'a>| -> ::parse::PResult<'a, _> { + Ok(match parser.parse_stmt()? { + Some(stmt) => Some(parser.finish_parsing_statement(stmt)?), + None => None, + }) + }; let mut ret = SmallVector::zero(); loop { let mut parser = self.parser.borrow_mut(); match parser.token { token::Eof => break, - _ => match parser.parse_stmt() { + _ => match parse_stmt(&mut parser) { Ok(maybe_stmt) => match maybe_stmt { Some(stmt) => ret.push(stmt), None => (), diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 0dd8c199827d2..b2cbe252344f7 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -4064,7 +4064,7 @@ impl<'a> Parser<'a> { /// Finish parsing expressions that start with macros and handle trailing semicolons /// (or the lack thereof) -- c.f. `parse_stmt`. - fn finish_parsing_statement(&mut self, mut stmt: Stmt) -> PResult<'a, Stmt> { + pub fn finish_parsing_statement(&mut self, mut stmt: Stmt) -> PResult<'a, Stmt> { if let StmtKind::Mac(mac) = stmt.node { if mac.1 != MacStmtStyle::NoBraces || self.token == token::Semi { stmt.node = StmtKind::Mac(mac); @@ -4082,7 +4082,7 @@ impl<'a> Parser<'a> { fn handle_trailing_semicolon(&mut self, mut stmt: Stmt) -> PResult<'a, Stmt> { match stmt.node { - StmtKind::Expr(ref expr) => { + StmtKind::Expr(ref expr) if self.token != token::Eof => { // expression without semicolon if classify::expr_requires_semi_to_be_stmt(expr) { // Just check for errors and recover; do not eat semicolon yet. From 337236870dd33497e921aa6a0227533f47fac852 Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Sat, 2 Jul 2016 15:49:50 +0000 Subject: [PATCH 3/7] Fix fallout. --- src/doc/book/macros.md | 4 ++-- src/librustc_save_analysis/dump_visitor.rs | 2 +- src/test/compile-fail/macro-incomplete-parse.rs | 2 +- src/test/run-pass/simd-intrinsic-generic-cast.rs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/doc/book/macros.md b/src/doc/book/macros.md index f535fb96af895..9f40829f4233f 100644 --- a/src/doc/book/macros.md +++ b/src/doc/book/macros.md @@ -328,7 +328,7 @@ invocation site. Code such as the following will not work: ```rust,ignore macro_rules! foo { - () => (let x = 3); + () => (let x = 3;); } fn main() { @@ -342,7 +342,7 @@ tagged with the right syntax context. ```rust macro_rules! foo { - ($v:ident) => (let $v = 3); + ($v:ident) => (let $v = 3;); } fn main() { diff --git a/src/librustc_save_analysis/dump_visitor.rs b/src/librustc_save_analysis/dump_visitor.rs index 4ffb547730549..5e967f3250f71 100644 --- a/src/librustc_save_analysis/dump_visitor.rs +++ b/src/librustc_save_analysis/dump_visitor.rs @@ -57,7 +57,7 @@ macro_rules! down_cast_data { data } else { span_bug!($sp, "unexpected data kind: {:?}", $id); - } + }; }; } diff --git a/src/test/compile-fail/macro-incomplete-parse.rs b/src/test/compile-fail/macro-incomplete-parse.rs index 8d515622e53ff..c2ac99d1f6a2d 100644 --- a/src/test/compile-fail/macro-incomplete-parse.rs +++ b/src/test/compile-fail/macro-incomplete-parse.rs @@ -19,7 +19,7 @@ macro_rules! ignored_item { } macro_rules! ignored_expr { - () => ( 1, //~ ERROR expected expression, found `,` + () => ( 1, //~ ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `,` 2 ) } diff --git a/src/test/run-pass/simd-intrinsic-generic-cast.rs b/src/test/run-pass/simd-intrinsic-generic-cast.rs index a20dd3ef72a54..74c0f39e2e3c5 100644 --- a/src/test/run-pass/simd-intrinsic-generic-cast.rs +++ b/src/test/run-pass/simd-intrinsic-generic-cast.rs @@ -113,7 +113,7 @@ fn main() { // product macro ($from: ident $(, $from_: ident)*: $($to: ident),*) => { fn $from() { unsafe { $( test!($from, $to); )* } } - tests!($($from_),*: $($to),*) + tests!($($from_),*: $($to),*); }; ($($types: ident),*) => {{ tests!($($types),* : $($types),*); From 52d485fe0d11d18c51c8868872d472f07b8e06d2 Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Tue, 5 Jul 2016 11:21:25 +0000 Subject: [PATCH 4/7] Fix bug in the pretty printer. --- src/libsyntax/print/pprust.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index ce30c3de75958..8866ffc2575cc 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -1638,9 +1638,8 @@ impl<'a> State<'a> { _ => token::Paren }; try!(self.print_mac(&mac, delim)); - match style { - ast::MacStmtStyle::Braces => {} - _ => try!(word(&mut self.s, ";")), + if style == ast::MacStmtStyle::Semicolon { + try!(word(&mut self.s, ";")); } } } From 759b8a8e7dbe4e9ada9f33847ab8d698ee020165 Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Tue, 5 Jul 2016 08:21:25 +0000 Subject: [PATCH 5/7] Allow macro-expanded macros in trailing expression positions to expand into statements: ```rust macro_rules! m { () => { let x = 1; x } } macro_rules! n { () => { m!() //< This can now expand into statements }} fn main() { n!(); } ``` and revert needless fallout fixes. --- src/libsyntax/parse/parser.rs | 3 ++- src/test/run-pass/simd-intrinsic-generic-cast.rs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index b2cbe252344f7..454320337edcb 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -4066,7 +4066,8 @@ impl<'a> Parser<'a> { /// (or the lack thereof) -- c.f. `parse_stmt`. pub fn finish_parsing_statement(&mut self, mut stmt: Stmt) -> PResult<'a, Stmt> { if let StmtKind::Mac(mac) = stmt.node { - if mac.1 != MacStmtStyle::NoBraces || self.token == token::Semi { + if mac.1 != MacStmtStyle::NoBraces || + self.token == token::Semi || self.token == token::Eof { stmt.node = StmtKind::Mac(mac); } else { let (mac, _style, attrs) = mac.unwrap(); diff --git a/src/test/run-pass/simd-intrinsic-generic-cast.rs b/src/test/run-pass/simd-intrinsic-generic-cast.rs index 74c0f39e2e3c5..a20dd3ef72a54 100644 --- a/src/test/run-pass/simd-intrinsic-generic-cast.rs +++ b/src/test/run-pass/simd-intrinsic-generic-cast.rs @@ -113,7 +113,7 @@ fn main() { // product macro ($from: ident $(, $from_: ident)*: $($to: ident),*) => { fn $from() { unsafe { $( test!($from, $to); )* } } - tests!($($from_),*: $($to),*); + tests!($($from_),*: $($to),*) }; ($($types: ident),*) => {{ tests!($($types),* : $($types),*); From c1b850d3041e5c36dc133c22b59319b179f5ddc6 Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Thu, 7 Jul 2016 04:12:20 +0000 Subject: [PATCH 6/7] cleanup: Refactor parser method `finish_parsing_statement` -> `parse_full_stmt`. --- src/libsyntax/ext/tt/macro_rules.rs | 8 +------- src/libsyntax/parse/parser.rs | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index 7b09a44b814ff..50ac99efc0089 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -118,18 +118,12 @@ impl<'a> MacResult for ParserAnyMacro<'a> { fn make_stmts(self: Box>) -> Option> { - let parse_stmt = |parser: &mut Parser<'a>| -> ::parse::PResult<'a, _> { - Ok(match parser.parse_stmt()? { - Some(stmt) => Some(parser.finish_parsing_statement(stmt)?), - None => None, - }) - }; let mut ret = SmallVector::zero(); loop { let mut parser = self.parser.borrow_mut(); match parser.token { token::Eof => break, - _ => match parse_stmt(&mut parser) { + _ => match parser.parse_full_stmt() { Ok(maybe_stmt) => match maybe_stmt { Some(stmt) => ret.push(stmt), None => (), diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 454320337edcb..5617d223e8c46 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -4044,8 +4044,8 @@ impl<'a> Parser<'a> { let mut stmts = vec![]; while !self.eat(&token::CloseDelim(token::Brace)) { - if let Some(stmt) = self.parse_stmt_() { - stmts.push(self.finish_parsing_statement(stmt)?); + if let Some(stmt) = self.parse_full_stmt()? { + stmts.push(stmt); } else if self.token == token::Eof { break; } else { @@ -4062,9 +4062,14 @@ impl<'a> Parser<'a> { })) } - /// Finish parsing expressions that start with macros and handle trailing semicolons - /// (or the lack thereof) -- c.f. `parse_stmt`. - pub fn finish_parsing_statement(&mut self, mut stmt: Stmt) -> PResult<'a, Stmt> { + /// Parse a statement, including the trailing semicolon. + /// This parses expression statements that begin with macros correctly (c.f. `parse_stmt`). + pub fn parse_full_stmt(&mut self) -> PResult<'a, Option> { + let mut stmt = match self.parse_stmt_() { + Some(stmt) => stmt, + None => return Ok(None), + }; + if let StmtKind::Mac(mac) = stmt.node { if mac.1 != MacStmtStyle::NoBraces || self.token == token::Semi || self.token == token::Eof { @@ -4078,7 +4083,8 @@ impl<'a> Parser<'a> { } } - self.handle_trailing_semicolon(stmt) + stmt = self.handle_trailing_semicolon(stmt)?; + Ok(Some(stmt)) } fn handle_trailing_semicolon(&mut self, mut stmt: Stmt) -> PResult<'a, Stmt> { From 57fac56cb51d1a8ca0f6d76f869ccbb0a67b0f45 Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Tue, 12 Jul 2016 03:56:19 +0000 Subject: [PATCH 7/7] Start a best-effort warning cycle. --- src/libsyntax/ext/tt/macro_rules.rs | 2 +- src/libsyntax/parse/parser.rs | 41 ++++++++++++++++--- .../compile-fail/missing-semicolon-warning.rs | 22 ++++++++++ 3 files changed, 59 insertions(+), 6 deletions(-) create mode 100644 src/test/compile-fail/missing-semicolon-warning.rs diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index 50ac99efc0089..db12ef24f7149 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -123,7 +123,7 @@ impl<'a> MacResult for ParserAnyMacro<'a> { let mut parser = self.parser.borrow_mut(); match parser.token { token::Eof => break, - _ => match parser.parse_full_stmt() { + _ => match parser.parse_full_stmt(true) { Ok(maybe_stmt) => match maybe_stmt { Some(stmt) => ret.push(stmt), None => (), diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 5617d223e8c46..c6374e59c1bc4 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -4044,7 +4044,7 @@ impl<'a> Parser<'a> { let mut stmts = vec![]; while !self.eat(&token::CloseDelim(token::Brace)) { - if let Some(stmt) = self.parse_full_stmt()? { + if let Some(stmt) = self.parse_full_stmt(false)? { stmts.push(stmt); } else if self.token == token::Eof { break; @@ -4064,7 +4064,7 @@ impl<'a> Parser<'a> { /// Parse a statement, including the trailing semicolon. /// This parses expression statements that begin with macros correctly (c.f. `parse_stmt`). - pub fn parse_full_stmt(&mut self) -> PResult<'a, Option> { + pub fn parse_full_stmt(&mut self, macro_expanded: bool) -> PResult<'a, Option> { let mut stmt = match self.parse_stmt_() { Some(stmt) => stmt, None => return Ok(None), @@ -4075,6 +4075,23 @@ impl<'a> Parser<'a> { self.token == token::Semi || self.token == token::Eof { stmt.node = StmtKind::Mac(mac); } else { + // We used to incorrectly stop parsing macro-expanded statements here. + // If the next token will be an error anyway but could have parsed with the + // earlier behavior, stop parsing here and emit a warning to avoid breakage. + if macro_expanded && self.token.can_begin_expr() && match self.token { + // These tokens can continue an expression, so we can't stop parsing and warn. + token::OpenDelim(token::Paren) | token::OpenDelim(token::Bracket) | + token::BinOp(token::Minus) | token::BinOp(token::Star) | + token::BinOp(token::And) | token::BinOp(token::Or) | + token::AndAnd | token::OrOr | + token::DotDot | token::DotDotDot => false, + _ => true, + } { + self.warn_missing_semicolon(); + stmt.node = StmtKind::Mac(mac); + return Ok(Some(stmt)); + } + let (mac, _style, attrs) = mac.unwrap(); let e = self.mk_mac_expr(stmt.span.lo, stmt.span.hi, mac.node, ThinVec::new()); let e = self.parse_dot_or_call_expr_with(e, stmt.span.lo, attrs)?; @@ -4083,11 +4100,12 @@ impl<'a> Parser<'a> { } } - stmt = self.handle_trailing_semicolon(stmt)?; + stmt = self.handle_trailing_semicolon(stmt, macro_expanded)?; Ok(Some(stmt)) } - fn handle_trailing_semicolon(&mut self, mut stmt: Stmt) -> PResult<'a, Stmt> { + fn handle_trailing_semicolon(&mut self, mut stmt: Stmt, macro_expanded: bool) + -> PResult<'a, Stmt> { match stmt.node { StmtKind::Expr(ref expr) if self.token != token::Eof => { // expression without semicolon @@ -4102,7 +4120,12 @@ impl<'a> Parser<'a> { } } StmtKind::Local(..) => { - self.expect_one_of(&[token::Semi], &[])?; + // We used to incorrectly allow a macro-expanded let statement to lack a semicolon. + if macro_expanded && self.token != token::Semi { + self.warn_missing_semicolon(); + } else { + self.expect_one_of(&[token::Semi], &[])?; + } } _ => {} } @@ -4115,6 +4138,14 @@ impl<'a> Parser<'a> { Ok(stmt) } + fn warn_missing_semicolon(&self) { + self.diagnostic().struct_span_warn(self.span, { + &format!("expected `;`, found `{}`", self.this_token_to_string()) + }).note({ + "This was erroneously allowed and will become a hard error in a future release" + }).emit(); + } + // Parses a sequence of bounds if a `:` is found, // otherwise returns empty list. fn parse_colon_then_ty_param_bounds(&mut self, diff --git a/src/test/compile-fail/missing-semicolon-warning.rs b/src/test/compile-fail/missing-semicolon-warning.rs new file mode 100644 index 0000000000000..bbc958b87a571 --- /dev/null +++ b/src/test/compile-fail/missing-semicolon-warning.rs @@ -0,0 +1,22 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(rustc_attrs)] +#![allow(unused)] + +macro_rules! m { + ($($e1:expr),*; $($e2:expr),*) => { + $( let x = $e1 )*; //~ WARN expected `;` + $( println!("{}", $e2) )*; //~ WARN expected `;` + } +} + +#[rustc_error] +fn main() { m!(0, 0; 0, 0); } //~ ERROR compilation successful