Skip to content

Commit 42cf6da

Browse files
authored
Rollup merge of #112345 - bvanjoi:fix-112342, r=nilstrieb,est31
fix(expand): prevent infinity loop in macro containing only "///" Fixes #112342 Issue #112342 was caused by an infinity loop in `parse_tt_inner`, and the state of it is as follows: - `matcher`: `[Sequence, Token(Doc), SequenceKleeneOpNoSep(op: ZeroOrMore), Eof]` - loop: | Iteration | Action | | - | - | | 0 | enter `Sequence`| | 1 | enter `Token(Doc)` and `mp.idx += 1` had been executed | | 2 | enter `SequenceKleeneOpNoSep` and reset `mp.idx` to `1` | | 3 | enter `Token(Doc)` again| To prevent the infinite loop, a check for whether it only contains `DocComment` in `check_lhs_no_empty_seq` had been added.
2 parents c6fda40 + 5eafab3 commit 42cf6da

File tree

6 files changed

+212
-10
lines changed

6 files changed

+212
-10
lines changed

compiler/rustc_expand/src/mbe/macro_parser.rs

+1
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,7 @@ pub(super) fn compute_locs(matcher: &[TokenTree]) -> Vec<MatcherLoc> {
249249
}
250250

251251
/// A single matcher position, representing the state of matching.
252+
#[derive(Debug)]
252253
struct MatcherPos {
253254
/// The index into `TtParser::locs`, which represents the "dot".
254255
idx: usize,

compiler/rustc_expand/src/mbe/macro_rules.rs

+35-10
Original file line numberDiff line numberDiff line change
@@ -628,6 +628,40 @@ fn check_lhs_nt_follows(sess: &ParseSess, def: &ast::Item, lhs: &mbe::TokenTree)
628628
// after parsing/expansion. we can report every error in every macro this way.
629629
}
630630

631+
fn is_empty_token_tree(sess: &ParseSess, seq: &mbe::SequenceRepetition) -> bool {
632+
if seq.separator.is_some() {
633+
false
634+
} else {
635+
let mut is_empty = true;
636+
let mut iter = seq.tts.iter().peekable();
637+
while let Some(tt) = iter.next() {
638+
match tt {
639+
mbe::TokenTree::MetaVarDecl(_, _, Some(NonterminalKind::Vis)) => {}
640+
mbe::TokenTree::Token(t @ Token { kind: DocComment(..), .. }) => {
641+
let mut now = t;
642+
while let Some(&mbe::TokenTree::Token(
643+
next @ Token { kind: DocComment(..), .. },
644+
)) = iter.peek()
645+
{
646+
now = next;
647+
iter.next();
648+
}
649+
let span = t.span.to(now.span);
650+
sess.span_diagnostic.span_note_without_error(
651+
span,
652+
"doc comments are ignored in matcher position",
653+
);
654+
}
655+
mbe::TokenTree::Sequence(_, sub_seq)
656+
if (sub_seq.kleene.op == mbe::KleeneOp::ZeroOrMore
657+
|| sub_seq.kleene.op == mbe::KleeneOp::ZeroOrOne) => {}
658+
_ => is_empty = false,
659+
}
660+
}
661+
is_empty
662+
}
663+
}
664+
631665
/// Checks that the lhs contains no repetition which could match an empty token
632666
/// tree, because then the matcher would hang indefinitely.
633667
fn check_lhs_no_empty_seq(sess: &ParseSess, tts: &[mbe::TokenTree]) -> bool {
@@ -644,16 +678,7 @@ fn check_lhs_no_empty_seq(sess: &ParseSess, tts: &[mbe::TokenTree]) -> bool {
644678
}
645679
}
646680
TokenTree::Sequence(span, seq) => {
647-
if seq.separator.is_none()
648-
&& seq.tts.iter().all(|seq_tt| match seq_tt {
649-
TokenTree::MetaVarDecl(_, _, Some(NonterminalKind::Vis)) => true,
650-
TokenTree::Sequence(_, sub_seq) => {
651-
sub_seq.kleene.op == mbe::KleeneOp::ZeroOrMore
652-
|| sub_seq.kleene.op == mbe::KleeneOp::ZeroOrOne
653-
}
654-
_ => false,
655-
})
656-
{
681+
if is_empty_token_tree(sess, seq) {
657682
let sp = span.entire();
658683
sess.span_diagnostic.span_err(sp, "repetition matches empty token tree");
659684
return false;

tests/ui/macros/issue-112342-1.rs

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// same as #95267, ignore doc comment although it's a bug.
2+
3+
macro_rules! m1 {
4+
(
5+
$(
6+
///
7+
)*
8+
//~^^^ERROR repetition matches empty token tree
9+
) => {};
10+
}
11+
12+
m1! {}
13+
14+
macro_rules! m2 {
15+
(
16+
$(
17+
///
18+
)+
19+
//~^^^ERROR repetition matches empty token tree
20+
) => {};
21+
}
22+
23+
m2! {}
24+
25+
macro_rules! m3 {
26+
(
27+
$(
28+
///
29+
)?
30+
//~^^^ERROR repetition matches empty token tree
31+
) => {};
32+
}
33+
34+
m3! {}
35+
36+
37+
macro_rules! m4 {
38+
(
39+
$(
40+
///
41+
///
42+
)*
43+
//~^^^^ERROR repetition matches empty token tree
44+
) => {};
45+
}
46+
47+
m4! {}
48+
49+
fn main() {}

tests/ui/macros/issue-112342-1.stderr

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
note: doc comments are ignored in matcher position
2+
--> $DIR/issue-112342-1.rs:6:13
3+
|
4+
LL | ///
5+
| ^^^
6+
7+
error: repetition matches empty token tree
8+
--> $DIR/issue-112342-1.rs:5:10
9+
|
10+
LL | $(
11+
| __________^
12+
LL | | ///
13+
LL | | )*
14+
| |_________^
15+
16+
note: doc comments are ignored in matcher position
17+
--> $DIR/issue-112342-1.rs:17:13
18+
|
19+
LL | ///
20+
| ^^^
21+
22+
error: repetition matches empty token tree
23+
--> $DIR/issue-112342-1.rs:16:10
24+
|
25+
LL | $(
26+
| __________^
27+
LL | | ///
28+
LL | | )+
29+
| |_________^
30+
31+
note: doc comments are ignored in matcher position
32+
--> $DIR/issue-112342-1.rs:28:13
33+
|
34+
LL | ///
35+
| ^^^
36+
37+
error: repetition matches empty token tree
38+
--> $DIR/issue-112342-1.rs:27:10
39+
|
40+
LL | $(
41+
| __________^
42+
LL | | ///
43+
LL | | )?
44+
| |_________^
45+
46+
note: doc comments are ignored in matcher position
47+
--> $DIR/issue-112342-1.rs:40:13
48+
|
49+
LL | / ///
50+
LL | | ///
51+
| |_______________^
52+
53+
error: repetition matches empty token tree
54+
--> $DIR/issue-112342-1.rs:39:10
55+
|
56+
LL | $(
57+
| __________^
58+
LL | | ///
59+
LL | | ///
60+
LL | | )*
61+
| |_________^
62+
63+
error: aborting due to 4 previous errors
64+

tests/ui/macros/issue-112342-2.rs

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// check-pass
2+
3+
// same as #95267, ignore doc comment although it's a bug.
4+
5+
macro_rules! m1 {
6+
(
7+
$(
8+
///
9+
$expr: expr,
10+
)*
11+
) => {};
12+
}
13+
14+
m1! {}
15+
16+
macro_rules! m2 {
17+
(
18+
$(
19+
///
20+
$expr: expr,
21+
///
22+
)*
23+
) => {};
24+
}
25+
26+
m2! {}
27+
28+
macro_rules! m3 {
29+
(
30+
$(
31+
///
32+
$tt: tt,
33+
)*
34+
) => {};
35+
}
36+
37+
m3! {}
38+
39+
fn main() {}

tests/ui/macros/issue-112342-2.stderr

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
note: doc comments are ignored in matcher position
2+
--> $DIR/issue-112342-2.rs:8:13
3+
|
4+
LL | ///
5+
| ^^^
6+
7+
note: doc comments are ignored in matcher position
8+
--> $DIR/issue-112342-2.rs:19:13
9+
|
10+
LL | ///
11+
| ^^^
12+
13+
note: doc comments are ignored in matcher position
14+
--> $DIR/issue-112342-2.rs:21:13
15+
|
16+
LL | ///
17+
| ^^^
18+
19+
note: doc comments are ignored in matcher position
20+
--> $DIR/issue-112342-2.rs:31:13
21+
|
22+
LL | ///
23+
| ^^^
24+

0 commit comments

Comments
 (0)