Skip to content

Commit 1b41c9a

Browse files
committed
error on duplicate matcher bindings
1 parent d173180 commit 1b41c9a

File tree

1 file changed

+49
-5
lines changed

1 file changed

+49
-5
lines changed

src/libsyntax/ext/tt/macro_rules.rs

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@ use crate::parse::token::Token::*;
1717
use crate::symbol::Symbol;
1818
use crate::tokenstream::{DelimSpan, TokenStream, TokenTree};
1919

20-
use syntax_pos::{Span, DUMMY_SP};
20+
use syntax_pos::{Span, DUMMY_SP, symbol::Ident};
2121
use log::debug;
2222

23-
use rustc_data_structures::fx::FxHashMap;
23+
use rustc_data_structures::fx::{FxHashMap};
2424
use std::borrow::Cow;
2525
use 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+
459503
fn check_rhs(sess: &ParseSess, rhs: &quoted::TokenTree) -> bool {
460504
match *rhs {
461505
quoted::TokenTree::Delimited(..) => return true,

0 commit comments

Comments
 (0)