@@ -17,10 +17,10 @@ use crate::parse::token::Token::*;
1717use crate :: symbol:: Symbol ;
1818use crate :: tokenstream:: { DelimSpan , TokenStream , TokenTree } ;
1919
20- use syntax_pos:: { Span , DUMMY_SP } ;
20+ use syntax_pos:: { Span , DUMMY_SP , symbol :: Ident } ;
2121use log:: debug;
2222
23- use rustc_data_structures:: fx:: FxHashMap ;
23+ use rustc_data_structures:: fx:: { FxHashMap } ;
2424use std:: borrow:: Cow ;
2525use std:: collections:: hash_map:: Entry ;
2626
@@ -246,8 +246,12 @@ fn generic_extension<'cx>(cx: &'cx mut ExtCtxt<'_>,
246246// Holy self-referential!
247247
248248/// Converts a `macro_rules!` invocation into a syntax extension.
249- pub fn compile ( sess : & ParseSess , features : & Features , def : & ast:: Item , edition : Edition )
250- -> SyntaxExtension {
249+ pub fn compile (
250+ sess : & ParseSess ,
251+ features : & Features ,
252+ def : & ast:: Item ,
253+ edition : Edition
254+ ) -> SyntaxExtension {
251255 let lhs_nm = ast:: Ident :: with_empty_ctxt ( Symbol :: gensym ( "lhs" ) ) ;
252256 let rhs_nm = ast:: Ident :: with_empty_ctxt ( Symbol :: gensym ( "rhs" ) ) ;
253257
@@ -355,7 +359,9 @@ pub fn compile(sess: &ParseSess, features: &Features, def: &ast::Item, edition:
355359
356360 // don't abort iteration early, so that errors for multiple lhses can be reported
357361 for lhs in & lhses {
358- valid &= check_lhs_no_empty_seq ( sess, & [ lhs. clone ( ) ] )
362+ valid &= check_lhs_no_empty_seq ( sess, & [ lhs. clone ( ) ] ) ;
363+ valid &=
364+ check_lhs_duplicate_matcher_bindings ( sess, & [ lhs. clone ( ) ] , & mut FxHashMap :: default ( ) ) ;
359365 }
360366
361367 let expander: Box < _ > = Box :: new ( MacroRulesMacroExpander {
@@ -456,6 +462,44 @@ fn check_lhs_no_empty_seq(sess: &ParseSess, tts: &[quoted::TokenTree]) -> bool {
456462 true
457463}
458464
465+ /// Check that the LHS contains no duplicate matcher bindings. e.g. `$a:expr, $a:expr` would be
466+ /// illegal, since it would be ambiguous which `$a` to use if we ever needed to.
467+ fn check_lhs_duplicate_matcher_bindings (
468+ sess : & ParseSess ,
469+ tts : & [ quoted:: TokenTree ] ,
470+ metavar_names : & mut FxHashMap < Ident , Span >
471+ ) -> bool {
472+ use self :: quoted:: TokenTree ;
473+ for tt in tts {
474+ match * tt {
475+ TokenTree :: MetaVarDecl ( span, name, _kind) => {
476+ if let Some ( & prev_span) = metavar_names. get ( & name) {
477+ sess. span_diagnostic
478+ . struct_span_err ( span, "duplicate matcher binding" )
479+ . span_note ( prev_span, "previous declaration was here" )
480+ . emit ( ) ;
481+ return false ;
482+ } else {
483+ metavar_names. insert ( name, span) ;
484+ }
485+ }
486+ TokenTree :: Delimited ( _, ref del) => {
487+ if !check_lhs_duplicate_matcher_bindings ( sess, & del. tts , metavar_names) {
488+ return false ;
489+ }
490+ } ,
491+ TokenTree :: Sequence ( _, ref seq) => {
492+ if !check_lhs_duplicate_matcher_bindings ( sess, & seq. tts , metavar_names) {
493+ return false ;
494+ }
495+ }
496+ _ => { }
497+ }
498+ }
499+
500+ true
501+ }
502+
459503fn check_rhs ( sess : & ParseSess , rhs : & quoted:: TokenTree ) -> bool {
460504 match * rhs {
461505 quoted:: TokenTree :: Delimited ( ..) => return true ,
0 commit comments