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