reject macros with empty repetitions

This commit is contained in:
Tim Neumann 2016-09-25 18:55:04 +02:00
parent 95abee1a68
commit 51ea050457
2 changed files with 100 additions and 1 deletions

View File

@ -332,7 +332,7 @@ pub fn compile(sess: &ParseSess, def: &ast::MacroDef) -> SyntaxExtension {
(**tt).clone()
}
_ => sess.span_diagnostic.span_bug(def.span, "wrong-structured lhs")
}).collect()
}).collect::<Vec<TokenTree>>()
}
_ => sess.span_diagnostic.span_bug(def.span, "wrong-structured lhs")
};
@ -351,6 +351,11 @@ pub fn compile(sess: &ParseSess, def: &ast::MacroDef) -> SyntaxExtension {
valid &= check_rhs(sess, rhs);
}
// 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()])
}
let exp: Box<_> = Box::new(MacroRulesMacroExpander {
name: def.ident,
imported_from: def.imported_from,
@ -377,6 +382,38 @@ fn check_lhs_nt_follows(sess: &ParseSess, lhs: &TokenTree) -> bool {
// after parsing/expansion. we can report every error in every macro this way.
}
/// Check that the lhs contains no repetition which could match an empty token
/// tree, because then the matcher would hang indefinitely.
fn check_lhs_no_empty_seq(sess: &ParseSess, tts: &[TokenTree]) -> bool {
for tt in tts {
match *tt {
TokenTree::Token(_, _) => (),
TokenTree::Delimited(_, ref del) => if !check_lhs_no_empty_seq(sess, &del.tts) {
return false;
},
TokenTree::Sequence(span, ref seq) => {
if seq.separator.is_none() {
if seq.tts.iter().all(|seq_tt| {
match *seq_tt {
TokenTree::Sequence(_, ref sub_seq) =>
sub_seq.op == tokenstream::KleeneOp::ZeroOrMore,
_ => false,
}
}) {
sess.span_diagnostic.span_err(span, "repetition matches empty token tree");
return false;
}
}
if !check_lhs_no_empty_seq(sess, &seq.tts) {
return false;
}
}
}
}
true
}
fn check_rhs(sess: &ParseSess, rhs: &TokenTree) -> bool {
match *rhs {
TokenTree::Delimited(..) => return true,

View File

@ -0,0 +1,62 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
macro_rules! foo {
( $()* ) => {};
//~^ ERROR repetition matches empty token tree
( $()+ ) => {};
//~^ ERROR repetition matches empty token tree
( $(),* ) => {}; // PASS
( $(),+ ) => {}; // PASS
( [$()*] ) => {};
//~^ ERROR repetition matches empty token tree
( [$()+] ) => {};
//~^ ERROR repetition matches empty token tree
( [$(),*] ) => {}; // PASS
( [$(),+] ) => {}; // PASS
( $($()* $(),* $(a)* $(a),* )* ) => {};
//~^ ERROR repetition matches empty token tree
( $($()* $(),* $(a)* $(a),* )+ ) => {};
//~^ ERROR repetition matches empty token tree
( $(a $(),* $(a)* $(a),* )* ) => {}; // PASS
( $($(a)+ $(),* $(a)* $(a),* )+ ) => {}; // PASS
( $(a $()+)* ) => {};
//~^ ERROR repetition matches empty token tree
( $(a $()*)+ ) => {};
//~^ ERROR repetition matches empty token tree
}
// --- Original Issue --- //
macro_rules! make_vec {
(a $e1:expr $($(, a $e2:expr)*)*) => ([$e1 $($(, $e2)*)*]);
//~^ ERROR repetition matches empty token tree
}
fn main() {
let _ = make_vec!(a 1, a 2, a 3);
}
// --- Minified Issue --- //
macro_rules! m {
( $()* ) => {}
//~^ ERROR repetition matches empty token tree
}
m!();