Skip to content

Commit

Permalink
error on duplicate matcher bindings
Browse files Browse the repository at this point in the history
  • Loading branch information
mark-i-m committed Feb 7, 2019
1 parent d173180 commit 1b41c9a
Showing 1 changed file with 49 additions and 5 deletions.
54 changes: 49 additions & 5 deletions src/libsyntax/ext/tt/macro_rules.rs
Expand Up @@ -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;

Expand Down Expand Up @@ -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"));

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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,
Expand Down

0 comments on commit 1b41c9a

Please sign in to comment.