about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/libsyntax/ext/tt/macro_rules.rs112
-rw-r--r--src/test/compile-fail/macro-error.rs19
-rw-r--r--src/test/compile-fail/type-macros-fail.rs2
3 files changed, 83 insertions, 50 deletions
diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs
index a34998196e5..43647ea4a2e 100644
--- a/src/libsyntax/ext/tt/macro_rules.rs
+++ b/src/libsyntax/ext/tt/macro_rules.rs
@@ -10,10 +10,10 @@
 
 use ast::{self, TokenTree};
 use codemap::{Span, DUMMY_SP};
-use ext::base::{ExtCtxt, MacResult, SyntaxExtension};
+use ext::base::{DummyResult, ExtCtxt, MacResult, SyntaxExtension};
 use ext::base::{NormalTT, TTMacroExpander};
 use ext::tt::macro_parser::{Success, Error, Failure};
-use ext::tt::macro_parser::{NamedMatch, MatchedSeq, MatchedNonterminal};
+use ext::tt::macro_parser::{MatchedSeq, MatchedNonterminal};
 use ext::tt::macro_parser::parse;
 use parse::lexer::new_tt_reader;
 use parse::parser::Parser;
@@ -129,16 +129,20 @@ impl<'a> MacResult for ParserAnyMacro<'a> {
 struct MacroRulesMacroExpander {
     name: ast::Ident,
     imported_from: Option<ast::Ident>,
-    lhses: Vec<Rc<NamedMatch>>,
-    rhses: Vec<Rc<NamedMatch>>,
+    lhses: Vec<TokenTree>,
+    rhses: Vec<TokenTree>,
+    valid: bool,
 }
 
 impl TTMacroExpander for MacroRulesMacroExpander {
     fn expand<'cx>(&self,
                    cx: &'cx mut ExtCtxt,
                    sp: Span,
-                   arg: &[ast::TokenTree])
+                   arg: &[TokenTree])
                    -> Box<MacResult+'cx> {
+        if !self.valid {
+            return DummyResult::any(sp);
+        }
         generic_extension(cx,
                           sp,
                           self.name,
@@ -154,9 +158,9 @@ fn generic_extension<'cx>(cx: &'cx ExtCtxt,
                           sp: Span,
                           name: ast::Ident,
                           imported_from: Option<ast::Ident>,
-                          arg: &[ast::TokenTree],
-                          lhses: &[Rc<NamedMatch>],
-                          rhses: &[Rc<NamedMatch>])
+                          arg: &[TokenTree],
+                          lhses: &[TokenTree],
+                          rhses: &[TokenTree])
                           -> Box<MacResult+'cx> {
     if cx.trace_macros() {
         println!("{}! {{ {} }}",
@@ -169,25 +173,17 @@ fn generic_extension<'cx>(cx: &'cx ExtCtxt,
     let mut best_fail_msg = "internal error: ran no matchers".to_string();
 
     for (i, lhs) in lhses.iter().enumerate() { // try each arm's matchers
-        match **lhs {
-          MatchedNonterminal(NtTT(ref lhs_tt)) => {
-            let lhs_tt = match **lhs_tt {
-                TokenTree::Delimited(_, ref delim) => &delim.tts[..],
-                _ => panic!(cx.span_fatal(sp, "malformed macro lhs"))
-            };
-
-            match TokenTree::parse(cx, lhs_tt, arg) {
-              Success(named_matches) => {
-                let rhs = match *rhses[i] {
-                    // okay, what's your transcriber?
-                    MatchedNonterminal(NtTT(ref tt)) => {
-                        match **tt {
-                            // ignore delimiters
-                            TokenTree::Delimited(_, ref delimed) => delimed.tts.clone(),
-                            _ => panic!(cx.span_fatal(sp, "macro rhs must be delimited")),
-                        }
-                    },
-                    _ => cx.span_bug(sp, "bad thing in rhs")
+        let lhs_tt = match *lhs {
+            TokenTree::Delimited(_, ref delim) => &delim.tts[..],
+            _ => cx.span_bug(sp, "malformed macro lhs")
+        };
+
+        match TokenTree::parse(cx, lhs_tt, arg) {
+            Success(named_matches) => {
+                let rhs = match rhses[i] {
+                    // ignore delimiters
+                    TokenTree::Delimited(_, ref delimed) => delimed.tts.clone(),
+                    _ => cx.span_bug(sp, "malformed macro rhs"),
                 };
                 // rhs has holes ( `$id` and `$(...)` that need filled)
                 let trncbr = new_tt_reader(&cx.parse_sess().span_diagnostic,
@@ -207,17 +203,14 @@ fn generic_extension<'cx>(cx: &'cx ExtCtxt,
                     site_span: sp,
                     macro_ident: name
                 })
-              }
-              Failure(sp, ref msg) => if sp.lo >= best_fail_spot.lo {
+            }
+            Failure(sp, ref msg) => if sp.lo >= best_fail_spot.lo {
                 best_fail_spot = sp;
                 best_fail_msg = (*msg).clone();
-              },
-              Error(err_sp, ref msg) => {
+            },
+            Error(err_sp, ref msg) => {
                 panic!(cx.span_fatal(err_sp.substitute_dummy(sp), &msg[..]))
-              }
             }
-          }
-          _ => cx.bug("non-matcher found in parsed lhses")
         }
     }
 
@@ -282,9 +275,16 @@ pub fn compile<'cx>(cx: &'cx mut ExtCtxt,
         }
     };
 
+    let mut valid = true;
+
     // Extract the arguments:
     let lhses = match **argument_map.get(&lhs_nm.name).unwrap() {
-        MatchedSeq(ref s, _) => /* FIXME (#2543) */ (*s).clone(),
+        MatchedSeq(ref s, _) => {
+            s.iter().map(|m| match **m {
+                MatchedNonterminal(NtTT(ref tt)) => (**tt).clone(),
+                _ => cx.span_bug(def.span, "wrong-structured lhs")
+            }).collect()
+        }
         _ => cx.span_bug(def.span, "wrong-structured lhs")
     };
 
@@ -293,41 +293,55 @@ pub fn compile<'cx>(cx: &'cx mut ExtCtxt,
     }
 
     let rhses = match **argument_map.get(&rhs_nm.name).unwrap() {
-        MatchedSeq(ref s, _) => /* FIXME (#2543) */ (*s).clone(),
+        MatchedSeq(ref s, _) => {
+            s.iter().map(|m| match **m {
+                MatchedNonterminal(NtTT(ref tt)) => (**tt).clone(),
+                _ => cx.span_bug(def.span, "wrong-structured rhs")
+            }).collect()
+        }
         _ => cx.span_bug(def.span, "wrong-structured rhs")
     };
 
+    for rhs in &rhses {
+        valid &= check_rhs(cx, rhs);
+    }
+
     let exp: Box<_> = Box::new(MacroRulesMacroExpander {
         name: def.ident,
         imported_from: def.imported_from,
         lhses: lhses,
         rhses: rhses,
+        valid: valid,
     });
 
     NormalTT(exp, Some(def.span), def.allow_internal_unstable)
 }
 
-fn check_lhs_nt_follows(cx: &mut ExtCtxt, lhs: &NamedMatch, sp: Span) {
-    // lhs is going to be like MatchedNonterminal(NtTT(TokenTree::Delimited(...))), where the
+fn check_lhs_nt_follows(cx: &mut ExtCtxt, lhs: &TokenTree, sp: Span) {
+    // lhs is going to be like TokenTree::Delimited(...), where the
     // entire lhs is those tts. Or, it can be a "bare sequence", not wrapped in parens.
     match lhs {
-        &MatchedNonterminal(NtTT(ref inner)) => match &**inner {
-            &TokenTree::Delimited(_, ref tts) => {
-                check_matcher(cx, tts.tts.iter(), &Eof);
-            },
-            tt @ &TokenTree::Sequence(..) => {
-                check_matcher(cx, Some(tt).into_iter(), &Eof);
-            },
-            _ => cx.span_err(sp, "Invalid macro matcher; matchers must be contained \
-               in balanced delimiters or a repetition indicator")
+        &TokenTree::Delimited(_, ref tts) => {
+            check_matcher(cx, tts.tts.iter(), &Eof);
         },
-        _ => cx.span_bug(sp, "wrong-structured lhs for follow check (didn't find a \
-           MatchedNonterminal)")
+        tt @ &TokenTree::Sequence(..) => {
+            check_matcher(cx, Some(tt).into_iter(), &Eof);
+        },
+        _ => cx.span_err(sp, "Invalid macro matcher; matchers must be contained \
+                              in balanced delimiters or a repetition indicator")
     };
     // we don't abort on errors on rejection, the driver will do that for us
     // after parsing/expansion. we can report every error in every macro this way.
 }
 
+fn check_rhs(cx: &mut ExtCtxt, rhs: &TokenTree) -> bool {
+    match *rhs {
+        TokenTree::Delimited(..) => return true,
+        _ => cx.span_err(rhs.get_span(), "macro rhs must be delimited")
+    }
+    false
+}
+
 // returns the last token that was checked, for TokenTree::Sequence. this gets used later on.
 fn check_matcher<'a, I>(cx: &mut ExtCtxt, matcher: I, follow: &Token)
 -> Option<(Span, Token)> where I: Iterator<Item=&'a TokenTree> {
diff --git a/src/test/compile-fail/macro-error.rs b/src/test/compile-fail/macro-error.rs
new file mode 100644
index 00000000000..817b675aedf
--- /dev/null
+++ b/src/test/compile-fail/macro-error.rs
@@ -0,0 +1,19 @@
+// Copyright 2015 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.
+
+// Check that we report errors at macro definition, not expansion.
+
+macro_rules! foo {
+    ($a:expr) => $a; //~ ERROR macro rhs must be delimited
+}
+
+fn main() {
+    foo!(0);
+}
diff --git a/src/test/compile-fail/type-macros-fail.rs b/src/test/compile-fail/type-macros-fail.rs
index d51176a925d..756f5d4547a 100644
--- a/src/test/compile-fail/type-macros-fail.rs
+++ b/src/test/compile-fail/type-macros-fail.rs
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 macro_rules! Id {
-    { $T:tt } => $T
+    ($T:tt) => ($T);
 }
 
 struct Foo<T> {