@@ -17,10 +17,10 @@ use crate::parse::token::Token::*;
17
17
use crate :: symbol:: Symbol ;
18
18
use crate :: tokenstream:: { DelimSpan , TokenStream , TokenTree } ;
19
19
20
- use syntax_pos:: { Span , DUMMY_SP } ;
20
+ use syntax_pos:: { Span , DUMMY_SP , symbol :: Ident } ;
21
21
use log:: debug;
22
22
23
- use rustc_data_structures:: fx:: FxHashMap ;
23
+ use rustc_data_structures:: fx:: { FxHashMap } ;
24
24
use std:: borrow:: Cow ;
25
25
use std:: collections:: hash_map:: Entry ;
26
26
@@ -246,8 +246,12 @@ fn generic_extension<'cx>(cx: &'cx mut ExtCtxt<'_>,
246
246
// Holy self-referential!
247
247
248
248
/// 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 {
251
255
let lhs_nm = ast:: Ident :: with_empty_ctxt ( Symbol :: gensym ( "lhs" ) ) ;
252
256
let rhs_nm = ast:: Ident :: with_empty_ctxt ( Symbol :: gensym ( "rhs" ) ) ;
253
257
@@ -355,7 +359,9 @@ pub fn compile(sess: &ParseSess, features: &Features, def: &ast::Item, edition:
355
359
356
360
// don't abort iteration early, so that errors for multiple lhses can be reported
357
361
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 ( ) ) ;
359
365
}
360
366
361
367
let expander: Box < _ > = Box :: new ( MacroRulesMacroExpander {
@@ -456,6 +462,44 @@ fn check_lhs_no_empty_seq(sess: &ParseSess, tts: &[quoted::TokenTree]) -> bool {
456
462
true
457
463
}
458
464
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
+
459
503
fn check_rhs ( sess : & ParseSess , rhs : & quoted:: TokenTree ) -> bool {
460
504
match * rhs {
461
505
quoted:: TokenTree :: Delimited ( ..) => return true ,
0 commit comments