]> git.lizzy.rs Git - rust.git/commitdiff
Start a best-effort warning cycle.
authorJeffrey Seyfried <jeffrey.seyfried@gmail.com>
Tue, 12 Jul 2016 03:56:19 +0000 (03:56 +0000)
committerJeffrey Seyfried <jeffrey.seyfried@gmail.com>
Wed, 13 Jul 2016 04:50:35 +0000 (04:50 +0000)
src/libsyntax/ext/tt/macro_rules.rs
src/libsyntax/parse/parser.rs
src/test/compile-fail/missing-semicolon-warning.rs [new file with mode: 0644]

index 50ac99efc0089dfee88191ef6612448a30bdacca..db12ef24f7149fd95fd3ab362af7606ff517793b 100644 (file)
@@ -123,7 +123,7 @@ fn make_stmts(self: Box<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 => (),
index 5617d223e8c461aa950bd8cc209bc87b72cc944a..c6374e59c1bc4b0e9a525e6b96b59ab99e799007 100644 (file)
@@ -4044,7 +4044,7 @@ fn parse_block_tail(&mut self, lo: BytePos, s: BlockCheckMode) -> PResult<'a, P<
         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 @@ fn parse_block_tail(&mut self, lo: BytePos, s: BlockCheckMode) -> PResult<'a, P<
 
     /// 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<Stmt>> {
+    pub fn parse_full_stmt(&mut self, macro_expanded: bool) -> PResult<'a, Option<Stmt>> {
         let mut stmt = match self.parse_stmt_() {
             Some(stmt) => stmt,
             None => return Ok(None),
@@ -4075,6 +4075,23 @@ pub fn parse_full_stmt(&mut self) -> PResult<'a, Option<Stmt>> {
                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 @@ pub fn parse_full_stmt(&mut self) -> PResult<'a, Option<Stmt>> {
             }
         }
 
-        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 @@ fn handle_trailing_semicolon(&mut self, mut stmt: Stmt) -> PResult<'a, Stmt> {
                 }
             }
             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 @@ fn handle_trailing_semicolon(&mut self, mut stmt: Stmt) -> PResult<'a, Stmt> {
         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 (file)
index 0000000..bbc958b
--- /dev/null
@@ -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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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