about summary refs log tree commit diff
path: root/src/libsyntax_ext
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2019-04-29 14:50:17 +0000
committerbors <bors@rust-lang.org>2019-04-29 14:50:17 +0000
commit96d565be014d462e9bd235bb42ce768cabed16b5 (patch)
tree9981d6c72fb6fa52237e7a0d1d9bccb3a2e028aa /src/libsyntax_ext
parent00acb7b24337eb138c934bde7a1efa5ee6a8b797 (diff)
parentf29e9a5cb83ef6dca14652b323e2c00c36997a54 (diff)
downloadrust-96d565be014d462e9bd235bb42ce768cabed16b5.tar.gz
rust-96d565be014d462e9bd235bb42ce768cabed16b5.zip
Auto merge of #60039 - rasendubi:assert-trailing-junk, r=alexcrichton
Make assert! ensure the macro is parsed completely

Fixes https://github.com/rust-lang/rust/issues/60024
Diffstat (limited to 'src/libsyntax_ext')
-rw-r--r--src/libsyntax_ext/assert.rs81
1 files changed, 67 insertions, 14 deletions
diff --git a/src/libsyntax_ext/assert.rs b/src/libsyntax_ext/assert.rs
index d2c397e0ecc..cd69733571d 100644
--- a/src/libsyntax_ext/assert.rs
+++ b/src/libsyntax_ext/assert.rs
@@ -1,10 +1,11 @@
-use errors::DiagnosticBuilder;
+use errors::{Applicability, DiagnosticBuilder};
 
 use syntax::ast::{self, *};
 use syntax::source_map::Spanned;
 use syntax::ext::base::*;
 use syntax::ext::build::AstBuilder;
 use syntax::parse::token;
+use syntax::parse::parser::Parser;
 use syntax::print::pprust;
 use syntax::ptr::P;
 use syntax::symbol::Symbol;
@@ -74,17 +75,69 @@ fn parse_assert<'a>(
         return Err(err);
     }
 
-    Ok(Assert {
-        cond_expr: parser.parse_expr()?,
-        custom_message: if parser.eat(&token::Comma) {
-            let ts = parser.parse_tokens();
-            if !ts.is_empty() {
-                Some(ts)
-            } else {
-                None
-            }
-        } else {
-            None
-        },
-    })
+    let cond_expr = parser.parse_expr()?;
+
+    // Some crates use the `assert!` macro in the following form (note extra semicolon):
+    //
+    // assert!(
+    //     my_function();
+    // );
+    //
+    // Warn about semicolon and suggest removing it. Eventually, this should be turned into an
+    // error.
+    if parser.token == token::Semi {
+        let mut err = cx.struct_span_warn(sp, "macro requires an expression as an argument");
+        err.span_suggestion(
+            parser.span,
+            "try removing semicolon",
+            String::new(),
+            Applicability::MaybeIncorrect
+        );
+        err.note("this is going to be an error in the future");
+        err.emit();
+
+        parser.bump();
+    }
+
+    // Some crates use the `assert!` macro in the following form (note missing comma before
+    // message):
+    //
+    // assert!(true "error message");
+    //
+    // Parse this as an actual message, and suggest inserting a comma. Eventually, this should be
+    // turned into an error.
+    let custom_message = if let token::Literal(token::Lit::Str_(_), _) = parser.token {
+        let mut err = cx.struct_span_warn(parser.span, "unexpected string literal");
+        let comma_span = cx.source_map().next_point(parser.prev_span);
+        err.span_suggestion_short(
+            comma_span,
+            "try adding a comma",
+            ", ".to_string(),
+            Applicability::MaybeIncorrect
+        );
+        err.note("this is going to be an error in the future");
+        err.emit();
+
+        parse_custom_message(&mut parser)
+    } else if parser.eat(&token::Comma) {
+        parse_custom_message(&mut parser)
+    } else {
+        None
+    };
+
+    if parser.token != token::Eof {
+        parser.expect_one_of(&[], &[])?;
+        unreachable!();
+    }
+
+    Ok(Assert { cond_expr, custom_message })
+}
+
+fn parse_custom_message<'a>(parser: &mut Parser<'a>) -> Option<TokenStream> {
+    let ts = parser.parse_tokens();
+    if !ts.is_empty() {
+        Some(ts)
+    } else {
+        None
+    }
 }