about summary refs log tree commit diff
path: root/src/libsyntax
diff options
context:
space:
mode:
authorTim Neumann <mail@timnn.me>2016-09-25 18:55:04 +0200
committerTim Neumann <mail@timnn.me>2016-09-26 06:59:06 +0200
commit51ea0504578c00b6754b666032c19366a197a480 (patch)
tree6f6def132307adb4fa5616718b93b5c1354df444 /src/libsyntax
parent95abee1a680f008fb97472294dd376a66e06d311 (diff)
downloadrust-51ea0504578c00b6754b666032c19366a197a480.tar.gz
rust-51ea0504578c00b6754b666032c19366a197a480.zip
reject macros with empty repetitions
Diffstat (limited to 'src/libsyntax')
-rw-r--r--src/libsyntax/ext/tt/macro_rules.rs39
1 files changed, 38 insertions, 1 deletions
diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs
index 0eed3e5898c..cb92f61ebaa 100644
--- a/src/libsyntax/ext/tt/macro_rules.rs
+++ b/src/libsyntax/ext/tt/macro_rules.rs
@@ -332,7 +332,7 @@ pub fn compile(sess: &ParseSess, def: &ast::MacroDef) -> SyntaxExtension {
                     (**tt).clone()
                 }
                 _ => sess.span_diagnostic.span_bug(def.span, "wrong-structured lhs")
-            }).collect()
+            }).collect::<Vec<TokenTree>>()
         }
         _ => sess.span_diagnostic.span_bug(def.span, "wrong-structured lhs")
     };
@@ -351,6 +351,11 @@ pub fn compile(sess: &ParseSess, def: &ast::MacroDef) -> SyntaxExtension {
         valid &= check_rhs(sess, rhs);
     }
 
+    // don't abort iteration early, so that errors for multiple lhses can be reported
+    for lhs in &lhses {
+        valid &= check_lhs_no_empty_seq(sess, &[lhs.clone()])
+    }
+
     let exp: Box<_> = Box::new(MacroRulesMacroExpander {
         name: def.ident,
         imported_from: def.imported_from,
@@ -377,6 +382,38 @@ fn check_lhs_nt_follows(sess: &ParseSess, lhs: &TokenTree) -> bool {
     // after parsing/expansion. we can report every error in every macro this way.
 }
 
+/// Check that the lhs contains no repetition which could match an empty token
+/// tree, because then the matcher would hang indefinitely.
+fn check_lhs_no_empty_seq(sess: &ParseSess, tts: &[TokenTree]) -> bool {
+    for tt in tts {
+        match *tt {
+            TokenTree::Token(_, _) => (),
+            TokenTree::Delimited(_, ref del) => if !check_lhs_no_empty_seq(sess, &del.tts) {
+                return false;
+            },
+            TokenTree::Sequence(span, ref seq) => {
+                if seq.separator.is_none() {
+                    if seq.tts.iter().all(|seq_tt| {
+                        match *seq_tt {
+                            TokenTree::Sequence(_, ref sub_seq) =>
+                                sub_seq.op == tokenstream::KleeneOp::ZeroOrMore,
+                            _ => false,
+                        }
+                    }) {
+                        sess.span_diagnostic.span_err(span, "repetition matches empty token tree");
+                        return false;
+                    }
+                }
+                if !check_lhs_no_empty_seq(sess, &seq.tts) {
+                    return false;
+                }
+            }
+        }
+    }
+
+    true
+}
+
 fn check_rhs(sess: &ParseSess, rhs: &TokenTree) -> bool {
     match *rhs {
         TokenTree::Delimited(..) => return true,