Skip to content

Commit 51ea050

Browse files
committed
reject macros with empty repetitions
1 parent 95abee1 commit 51ea050

File tree

2 files changed

+100
-1
lines changed

2 files changed

+100
-1
lines changed

src/libsyntax/ext/tt/macro_rules.rs

+38-1
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,7 @@ pub fn compile(sess: &ParseSess, def: &ast::MacroDef) -> SyntaxExtension {
332332
(**tt).clone()
333333
}
334334
_ => sess.span_diagnostic.span_bug(def.span, "wrong-structured lhs")
335-
}).collect()
335+
}).collect::<Vec<TokenTree>>()
336336
}
337337
_ => sess.span_diagnostic.span_bug(def.span, "wrong-structured lhs")
338338
};
@@ -351,6 +351,11 @@ pub fn compile(sess: &ParseSess, def: &ast::MacroDef) -> SyntaxExtension {
351351
valid &= check_rhs(sess, rhs);
352352
}
353353

354+
// don't abort iteration early, so that errors for multiple lhses can be reported
355+
for lhs in &lhses {
356+
valid &= check_lhs_no_empty_seq(sess, &[lhs.clone()])
357+
}
358+
354359
let exp: Box<_> = Box::new(MacroRulesMacroExpander {
355360
name: def.ident,
356361
imported_from: def.imported_from,
@@ -377,6 +382,38 @@ fn check_lhs_nt_follows(sess: &ParseSess, lhs: &TokenTree) -> bool {
377382
// after parsing/expansion. we can report every error in every macro this way.
378383
}
379384

385+
/// Check that the lhs contains no repetition which could match an empty token
386+
/// tree, because then the matcher would hang indefinitely.
387+
fn check_lhs_no_empty_seq(sess: &ParseSess, tts: &[TokenTree]) -> bool {
388+
for tt in tts {
389+
match *tt {
390+
TokenTree::Token(_, _) => (),
391+
TokenTree::Delimited(_, ref del) => if !check_lhs_no_empty_seq(sess, &del.tts) {
392+
return false;
393+
},
394+
TokenTree::Sequence(span, ref seq) => {
395+
if seq.separator.is_none() {
396+
if seq.tts.iter().all(|seq_tt| {
397+
match *seq_tt {
398+
TokenTree::Sequence(_, ref sub_seq) =>
399+
sub_seq.op == tokenstream::KleeneOp::ZeroOrMore,
400+
_ => false,
401+
}
402+
}) {
403+
sess.span_diagnostic.span_err(span, "repetition matches empty token tree");
404+
return false;
405+
}
406+
}
407+
if !check_lhs_no_empty_seq(sess, &seq.tts) {
408+
return false;
409+
}
410+
}
411+
}
412+
}
413+
414+
true
415+
}
416+
380417
fn check_rhs(sess: &ParseSess, rhs: &TokenTree) -> bool {
381418
match *rhs {
382419
TokenTree::Delimited(..) => return true,

src/test/compile-fail/issue-5067.rs

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
macro_rules! foo {
12+
( $()* ) => {};
13+
//~^ ERROR repetition matches empty token tree
14+
( $()+ ) => {};
15+
//~^ ERROR repetition matches empty token tree
16+
17+
( $(),* ) => {}; // PASS
18+
( $(),+ ) => {}; // PASS
19+
20+
( [$()*] ) => {};
21+
//~^ ERROR repetition matches empty token tree
22+
( [$()+] ) => {};
23+
//~^ ERROR repetition matches empty token tree
24+
25+
( [$(),*] ) => {}; // PASS
26+
( [$(),+] ) => {}; // PASS
27+
28+
( $($()* $(),* $(a)* $(a),* )* ) => {};
29+
//~^ ERROR repetition matches empty token tree
30+
( $($()* $(),* $(a)* $(a),* )+ ) => {};
31+
//~^ ERROR repetition matches empty token tree
32+
33+
( $(a $(),* $(a)* $(a),* )* ) => {}; // PASS
34+
( $($(a)+ $(),* $(a)* $(a),* )+ ) => {}; // PASS
35+
36+
( $(a $()+)* ) => {};
37+
//~^ ERROR repetition matches empty token tree
38+
( $(a $()*)+ ) => {};
39+
//~^ ERROR repetition matches empty token tree
40+
}
41+
42+
43+
// --- Original Issue --- //
44+
45+
macro_rules! make_vec {
46+
(a $e1:expr $($(, a $e2:expr)*)*) => ([$e1 $($(, $e2)*)*]);
47+
//~^ ERROR repetition matches empty token tree
48+
}
49+
50+
fn main() {
51+
let _ = make_vec!(a 1, a 2, a 3);
52+
}
53+
54+
55+
// --- Minified Issue --- //
56+
57+
macro_rules! m {
58+
( $()* ) => {}
59+
//~^ ERROR repetition matches empty token tree
60+
}
61+
62+
m!();

0 commit comments

Comments
 (0)