about summary refs log tree commit diff
diff options
context:
space:
mode:
authorHuon Wilson <dbau.pp+github@gmail.com>2013-10-02 14:43:15 +1000
committerHuon Wilson <dbau.pp+github@gmail.com>2013-10-02 14:43:15 +1000
commit8284df9e7c3b26897d65935eaa7acee1a956a450 (patch)
tree394c978dc2b603cf2b28879b67ce405f528c1e17
parentbbbafc4e466e8026d30b9c47d1f104fd44815bef (diff)
downloadrust-8284df9e7c3b26897d65935eaa7acee1a956a450.tar.gz
rust-8284df9e7c3b26897d65935eaa7acee1a956a450.zip
syntax: indicate an error when a macro ignores trailing tokens.
That is, only a single expression or item gets parsed, so if there are
any extra tokens (e.g. the start of another item/expression) the user
should be told, rather than silently dropping them.

An example:

    macro_rules! foo {
        () => {
            println("hi");
            println("bye);
        }
    }

would expand to just `println("hi")`, which is almost certainly not
what the programmer wanted.

Fixes #8012.
-rw-r--r--src/libsyntax/ext/deriving/cmp/eq.rs2
-rw-r--r--src/libsyntax/ext/tt/macro_rules.rs33
-rw-r--r--src/test/compile-fail/macro-incomplete-parse.rs26
3 files changed, 56 insertions, 5 deletions
diff --git a/src/libsyntax/ext/deriving/cmp/eq.rs b/src/libsyntax/ext/deriving/cmp/eq.rs
index d147875d5e1..468e3e5e7f2 100644
--- a/src/libsyntax/ext/deriving/cmp/eq.rs
+++ b/src/libsyntax/ext/deriving/cmp/eq.rs
@@ -39,7 +39,7 @@ pub fn expand_deriving_eq(cx: @ExtCtxt,
                 ret_ty: Literal(Path::new(~["bool"])),
                 const_nonmatching: true,
                 combine_substructure: $f
-            },
+            }
         }
     );
 
diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs
index a717535358b..de0afe23668 100644
--- a/src/libsyntax/ext/tt/macro_rules.rs
+++ b/src/libsyntax/ext/tt/macro_rules.rs
@@ -21,22 +21,47 @@ use ext::tt::macro_parser::{parse, parse_or_else, success, failure};
 use parse::lexer::{new_tt_reader, reader};
 use parse::parser::Parser;
 use parse::token::{get_ident_interner, special_idents, gensym_ident, ident_to_str};
-use parse::token::{FAT_ARROW, SEMI, nt_matchers, nt_tt};
+use parse::token::{FAT_ARROW, SEMI, nt_matchers, nt_tt, EOF};
 use print;
 
 struct ParserAnyMacro {
     parser: @Parser,
 }
 
+impl ParserAnyMacro {
+    /// Make sure we don't have any tokens left to parse, so we don't
+    /// silently drop anything. `allow_semi` is so that "optional"
+    /// semilons at the end of normal expressions aren't complained
+    /// about e.g. the semicolon in `macro_rules! kapow( () => {
+    /// fail!(); } )` doesn't get picked up by .parse_expr(), but it's
+    /// allowed to be there.
+    fn ensure_complete_parse(&self, allow_semi: bool) {
+        if allow_semi && *self.parser.token == SEMI {
+            self.parser.bump()
+        }
+        if *self.parser.token != EOF {
+            let msg = format!("macro expansion ignores token `{}` and any following",
+                              self.parser.this_token_to_str());
+            self.parser.span_err(*self.parser.span, msg);
+        }
+    }
+}
+
 impl AnyMacro for ParserAnyMacro {
     fn make_expr(&self) -> @ast::Expr {
-        self.parser.parse_expr()
+        let ret = self.parser.parse_expr();
+        self.ensure_complete_parse(true);
+        ret
     }
     fn make_item(&self) -> Option<@ast::item> {
-        self.parser.parse_item(~[])     // no attrs
+        let ret = self.parser.parse_item(~[]);     // no attrs
+        self.ensure_complete_parse(false);
+        ret
     }
     fn make_stmt(&self) -> @ast::Stmt {
-        self.parser.parse_stmt(~[])     // no attrs
+        let ret = self.parser.parse_stmt(~[]);     // no attrs
+        self.ensure_complete_parse(true);
+        ret
     }
 }
 
diff --git a/src/test/compile-fail/macro-incomplete-parse.rs b/src/test/compile-fail/macro-incomplete-parse.rs
new file mode 100644
index 00000000000..615a85c2e7e
--- /dev/null
+++ b/src/test/compile-fail/macro-incomplete-parse.rs
@@ -0,0 +1,26 @@
+// Copyright 2013 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.
+
+macro_rules! ignored_item {
+    () => {
+        fn foo() {}
+        fn bar() {} //~ ERROR macro expansion ignores token `fn`
+    }
+}
+
+macro_rules! ignored_expr {
+    () => ( 1, 2 ) //~ ERROR macro expansion ignores token `,`
+}
+
+ignored_item!()
+
+fn main() {
+    ignored_expr!()
+}