about summary refs log tree commit diff
path: root/src/libsyntax/ext
diff options
context:
space:
mode:
authormark <markm@cs.wisc.edu>2019-01-14 19:14:02 -0600
committerMark Mansi <markm@cs.wisc.edu>2019-02-07 12:15:31 -0600
commit1b41c9a42e3d5231df8621ae4b900cddecc12a75 (patch)
tree5e49be60e8392e4f5caabc4064ca2fae606d4cb4 /src/libsyntax/ext
parentd1731801163df1d3a8d4ddfa68adac2ec833ef7f (diff)
downloadrust-1b41c9a42e3d5231df8621ae4b900cddecc12a75.tar.gz
rust-1b41c9a42e3d5231df8621ae4b900cddecc12a75.zip
error on duplicate matcher bindings
Diffstat (limited to 'src/libsyntax/ext')
-rw-r--r--src/libsyntax/ext/tt/macro_rules.rs54
1 files changed, 49 insertions, 5 deletions
diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs
index b3ecaeaedbb..61a5b2cb0d2 100644
--- a/src/libsyntax/ext/tt/macro_rules.rs
+++ b/src/libsyntax/ext/tt/macro_rules.rs
@@ -17,10 +17,10 @@ use crate::parse::token::Token::*;
 use crate::symbol::Symbol;
 use crate::tokenstream::{DelimSpan, TokenStream, TokenTree};
 
-use syntax_pos::{Span, DUMMY_SP};
+use syntax_pos::{Span, DUMMY_SP, symbol::Ident};
 use log::debug;
 
-use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::fx::{FxHashMap};
 use std::borrow::Cow;
 use std::collections::hash_map::Entry;
 
@@ -246,8 +246,12 @@ fn generic_extension<'cx>(cx: &'cx mut ExtCtxt<'_>,
 // Holy self-referential!
 
 /// Converts a `macro_rules!` invocation into a syntax extension.
-pub fn compile(sess: &ParseSess, features: &Features, def: &ast::Item, edition: Edition)
-               -> SyntaxExtension {
+pub fn compile(
+    sess: &ParseSess,
+    features: &Features,
+    def: &ast::Item,
+    edition: Edition
+) -> SyntaxExtension {
     let lhs_nm = ast::Ident::with_empty_ctxt(Symbol::gensym("lhs"));
     let rhs_nm = ast::Ident::with_empty_ctxt(Symbol::gensym("rhs"));
 
@@ -355,7 +359,9 @@ pub fn compile(sess: &ParseSess, features: &Features, def: &ast::Item, edition:
 
     // 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()])
+        valid &= check_lhs_no_empty_seq(sess, &[lhs.clone()]);
+        valid &=
+            check_lhs_duplicate_matcher_bindings(sess, &[lhs.clone()], &mut FxHashMap::default());
     }
 
     let expander: Box<_> = Box::new(MacroRulesMacroExpander {
@@ -456,6 +462,44 @@ fn check_lhs_no_empty_seq(sess: &ParseSess, tts: &[quoted::TokenTree]) -> bool {
     true
 }
 
+/// Check that the LHS contains no duplicate matcher bindings. e.g. `$a:expr, $a:expr` would be
+/// illegal, since it would be ambiguous which `$a` to use if we ever needed to.
+fn check_lhs_duplicate_matcher_bindings(
+    sess: &ParseSess,
+    tts: &[quoted::TokenTree],
+    metavar_names: &mut FxHashMap<Ident, Span>
+) -> bool {
+    use self::quoted::TokenTree;
+    for tt in tts {
+        match *tt {
+            TokenTree::MetaVarDecl(span, name, _kind) => {
+                if let Some(&prev_span) = metavar_names.get(&name) {
+                    sess.span_diagnostic
+                        .struct_span_err(span, "duplicate matcher binding")
+                        .span_note(prev_span, "previous declaration was here")
+                        .emit();
+                    return false;
+                } else {
+                    metavar_names.insert(name, span);
+                }
+            }
+            TokenTree::Delimited(_, ref del) => {
+                if !check_lhs_duplicate_matcher_bindings(sess, &del.tts, metavar_names) {
+                    return false;
+                }
+            },
+            TokenTree::Sequence(_, ref seq) => {
+                if !check_lhs_duplicate_matcher_bindings(sess, &seq.tts, metavar_names) {
+                    return false;
+                }
+            }
+            _ => {}
+        }
+    }
+
+    true
+}
+
 fn check_rhs(sess: &ParseSess, rhs: &quoted::TokenTree) -> bool {
     match *rhs {
         quoted::TokenTree::Delimited(..) => return true,