Skip to content

Commit 57fac56

Browse files
committed
Start a best-effort warning cycle.
1 parent c1b850d commit 57fac56

File tree

3 files changed

+59
-6
lines changed

3 files changed

+59
-6
lines changed

src/libsyntax/ext/tt/macro_rules.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ impl<'a> MacResult for ParserAnyMacro<'a> {
123123
let mut parser = self.parser.borrow_mut();
124124
match parser.token {
125125
token::Eof => break,
126-
_ => match parser.parse_full_stmt() {
126+
_ => match parser.parse_full_stmt(true) {
127127
Ok(maybe_stmt) => match maybe_stmt {
128128
Some(stmt) => ret.push(stmt),
129129
None => (),

src/libsyntax/parse/parser.rs

+36-5
Original file line numberDiff line numberDiff line change
@@ -4044,7 +4044,7 @@ impl<'a> Parser<'a> {
40444044
let mut stmts = vec![];
40454045

40464046
while !self.eat(&token::CloseDelim(token::Brace)) {
4047-
if let Some(stmt) = self.parse_full_stmt()? {
4047+
if let Some(stmt) = self.parse_full_stmt(false)? {
40484048
stmts.push(stmt);
40494049
} else if self.token == token::Eof {
40504050
break;
@@ -4064,7 +4064,7 @@ impl<'a> Parser<'a> {
40644064

40654065
/// Parse a statement, including the trailing semicolon.
40664066
/// This parses expression statements that begin with macros correctly (c.f. `parse_stmt`).
4067-
pub fn parse_full_stmt(&mut self) -> PResult<'a, Option<Stmt>> {
4067+
pub fn parse_full_stmt(&mut self, macro_expanded: bool) -> PResult<'a, Option<Stmt>> {
40684068
let mut stmt = match self.parse_stmt_() {
40694069
Some(stmt) => stmt,
40704070
None => return Ok(None),
@@ -4075,6 +4075,23 @@ impl<'a> Parser<'a> {
40754075
self.token == token::Semi || self.token == token::Eof {
40764076
stmt.node = StmtKind::Mac(mac);
40774077
} else {
4078+
// We used to incorrectly stop parsing macro-expanded statements here.
4079+
// If the next token will be an error anyway but could have parsed with the
4080+
// earlier behavior, stop parsing here and emit a warning to avoid breakage.
4081+
if macro_expanded && self.token.can_begin_expr() && match self.token {
4082+
// These tokens can continue an expression, so we can't stop parsing and warn.
4083+
token::OpenDelim(token::Paren) | token::OpenDelim(token::Bracket) |
4084+
token::BinOp(token::Minus) | token::BinOp(token::Star) |
4085+
token::BinOp(token::And) | token::BinOp(token::Or) |
4086+
token::AndAnd | token::OrOr |
4087+
token::DotDot | token::DotDotDot => false,
4088+
_ => true,
4089+
} {
4090+
self.warn_missing_semicolon();
4091+
stmt.node = StmtKind::Mac(mac);
4092+
return Ok(Some(stmt));
4093+
}
4094+
40784095
let (mac, _style, attrs) = mac.unwrap();
40794096
let e = self.mk_mac_expr(stmt.span.lo, stmt.span.hi, mac.node, ThinVec::new());
40804097
let e = self.parse_dot_or_call_expr_with(e, stmt.span.lo, attrs)?;
@@ -4083,11 +4100,12 @@ impl<'a> Parser<'a> {
40834100
}
40844101
}
40854102

4086-
stmt = self.handle_trailing_semicolon(stmt)?;
4103+
stmt = self.handle_trailing_semicolon(stmt, macro_expanded)?;
40874104
Ok(Some(stmt))
40884105
}
40894106

4090-
fn handle_trailing_semicolon(&mut self, mut stmt: Stmt) -> PResult<'a, Stmt> {
4107+
fn handle_trailing_semicolon(&mut self, mut stmt: Stmt, macro_expanded: bool)
4108+
-> PResult<'a, Stmt> {
40914109
match stmt.node {
40924110
StmtKind::Expr(ref expr) if self.token != token::Eof => {
40934111
// expression without semicolon
@@ -4102,7 +4120,12 @@ impl<'a> Parser<'a> {
41024120
}
41034121
}
41044122
StmtKind::Local(..) => {
4105-
self.expect_one_of(&[token::Semi], &[])?;
4123+
// We used to incorrectly allow a macro-expanded let statement to lack a semicolon.
4124+
if macro_expanded && self.token != token::Semi {
4125+
self.warn_missing_semicolon();
4126+
} else {
4127+
self.expect_one_of(&[token::Semi], &[])?;
4128+
}
41064129
}
41074130
_ => {}
41084131
}
@@ -4115,6 +4138,14 @@ impl<'a> Parser<'a> {
41154138
Ok(stmt)
41164139
}
41174140

4141+
fn warn_missing_semicolon(&self) {
4142+
self.diagnostic().struct_span_warn(self.span, {
4143+
&format!("expected `;`, found `{}`", self.this_token_to_string())
4144+
}).note({
4145+
"This was erroneously allowed and will become a hard error in a future release"
4146+
}).emit();
4147+
}
4148+
41184149
// Parses a sequence of bounds if a `:` is found,
41194150
// otherwise returns empty list.
41204151
fn parse_colon_then_ty_param_bounds(&mut self,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright 2016 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+
#![feature(rustc_attrs)]
12+
#![allow(unused)]
13+
14+
macro_rules! m {
15+
($($e1:expr),*; $($e2:expr),*) => {
16+
$( let x = $e1 )*; //~ WARN expected `;`
17+
$( println!("{}", $e2) )*; //~ WARN expected `;`
18+
}
19+
}
20+
21+
#[rustc_error]
22+
fn main() { m!(0, 0; 0, 0); } //~ ERROR compilation successful

0 commit comments

Comments
 (0)