Skip to content

Commit

Permalink
Rollup merge of rust-lang#36721 - TimNN:infinite-emptiness, r=nrc
Browse files Browse the repository at this point in the history
reject macros with empty repetitions

Fixes rust-lang#5067 by checking the lhs of `macro_rules!` for repetitions which could match an empty token tree.
  • Loading branch information
Jonathan Turner authored Sep 26, 2016
2 parents 1ec306e + 51ea050 commit c4fc532
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 1 deletion.
39 changes: 38 additions & 1 deletion src/libsyntax/ext/tt/macro_rules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,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")
};
Expand All @@ -262,6 +262,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,
Expand All @@ -288,6 +293,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,
Expand Down
62 changes: 62 additions & 0 deletions src/test/compile-fail/issue-5067.rs
Original file line number Diff line number Diff line change
@@ -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!();

0 comments on commit c4fc532

Please sign in to comment.