about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDylan DPC <99973273+Dylan-DPC@users.noreply.github.com>2023-06-07 18:01:30 +0530
committerGitHub <noreply@github.com>2023-06-07 18:01:30 +0530
commit42cf6da6af4dd738bacf851fc6361a8e3dfb8138 (patch)
tree9fb0f9884feb95524bc6837b05b2768891c7234d
parentc6fda401f6d03c3947c6604a709e6a6239c76c4f (diff)
parent5eafab30ba0721451f5114c5b27b37870bb3955a (diff)
downloadrust-42cf6da6af4dd738bacf851fc6361a8e3dfb8138.tar.gz
rust-42cf6da6af4dd738bacf851fc6361a8e3dfb8138.zip
Rollup merge of #112345 - bvanjoi:fix-112342, r=nilstrieb,est31
fix(expand): prevent infinity loop in macro containing only "///"

Fixes https://github.com/rust-lang/rust/issues/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.
-rw-r--r--compiler/rustc_expand/src/mbe/macro_parser.rs1
-rw-r--r--compiler/rustc_expand/src/mbe/macro_rules.rs45
-rw-r--r--tests/ui/macros/issue-112342-1.rs49
-rw-r--r--tests/ui/macros/issue-112342-1.stderr64
-rw-r--r--tests/ui/macros/issue-112342-2.rs39
-rw-r--r--tests/ui/macros/issue-112342-2.stderr24
6 files changed, 212 insertions, 10 deletions
diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs
index 1c222fb4a89..f0e67cfd50e 100644
--- a/compiler/rustc_expand/src/mbe/macro_parser.rs
+++ b/compiler/rustc_expand/src/mbe/macro_parser.rs
@@ -249,6 +249,7 @@ pub(super) fn compute_locs(matcher: &[TokenTree]) -> Vec<MatcherLoc> {
 }
 
 /// A single matcher position, representing the state of matching.
+#[derive(Debug)]
 struct MatcherPos {
     /// The index into `TtParser::locs`, which represents the "dot".
     idx: usize,
diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs
index e4c65a2049b..ee9616a0f0a 100644
--- a/compiler/rustc_expand/src/mbe/macro_rules.rs
+++ b/compiler/rustc_expand/src/mbe/macro_rules.rs
@@ -628,6 +628,40 @@ fn check_lhs_nt_follows(sess: &ParseSess, def: &ast::Item, lhs: &mbe::TokenTree)
     // after parsing/expansion. we can report every error in every macro this way.
 }
 
+fn is_empty_token_tree(sess: &ParseSess, seq: &mbe::SequenceRepetition) -> bool {
+    if seq.separator.is_some() {
+        false
+    } else {
+        let mut is_empty = true;
+        let mut iter = seq.tts.iter().peekable();
+        while let Some(tt) = iter.next() {
+            match tt {
+                mbe::TokenTree::MetaVarDecl(_, _, Some(NonterminalKind::Vis)) => {}
+                mbe::TokenTree::Token(t @ Token { kind: DocComment(..), .. }) => {
+                    let mut now = t;
+                    while let Some(&mbe::TokenTree::Token(
+                        next @ Token { kind: DocComment(..), .. },
+                    )) = iter.peek()
+                    {
+                        now = next;
+                        iter.next();
+                    }
+                    let span = t.span.to(now.span);
+                    sess.span_diagnostic.span_note_without_error(
+                        span,
+                        "doc comments are ignored in matcher position",
+                    );
+                }
+                mbe::TokenTree::Sequence(_, sub_seq)
+                    if (sub_seq.kleene.op == mbe::KleeneOp::ZeroOrMore
+                        || sub_seq.kleene.op == mbe::KleeneOp::ZeroOrOne) => {}
+                _ => is_empty = false,
+            }
+        }
+        is_empty
+    }
+}
+
 /// Checks 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: &[mbe::TokenTree]) -> bool {
@@ -644,16 +678,7 @@ fn check_lhs_no_empty_seq(sess: &ParseSess, tts: &[mbe::TokenTree]) -> bool {
                 }
             }
             TokenTree::Sequence(span, seq) => {
-                if seq.separator.is_none()
-                    && seq.tts.iter().all(|seq_tt| match seq_tt {
-                        TokenTree::MetaVarDecl(_, _, Some(NonterminalKind::Vis)) => true,
-                        TokenTree::Sequence(_, sub_seq) => {
-                            sub_seq.kleene.op == mbe::KleeneOp::ZeroOrMore
-                                || sub_seq.kleene.op == mbe::KleeneOp::ZeroOrOne
-                        }
-                        _ => false,
-                    })
-                {
+                if is_empty_token_tree(sess, seq) {
                     let sp = span.entire();
                     sess.span_diagnostic.span_err(sp, "repetition matches empty token tree");
                     return false;
diff --git a/tests/ui/macros/issue-112342-1.rs b/tests/ui/macros/issue-112342-1.rs
new file mode 100644
index 00000000000..bd2abe7f697
--- /dev/null
+++ b/tests/ui/macros/issue-112342-1.rs
@@ -0,0 +1,49 @@
+// same as #95267, ignore doc comment although it's a bug.
+
+macro_rules! m1 {
+    (
+        $(
+            ///
+        )*
+        //~^^^ERROR repetition matches empty token tree
+    ) => {};
+}
+
+m1! {}
+
+macro_rules! m2 {
+    (
+        $(
+            ///
+        )+
+        //~^^^ERROR repetition matches empty token tree
+    ) => {};
+}
+
+m2! {}
+
+macro_rules! m3 {
+    (
+        $(
+            ///
+        )?
+        //~^^^ERROR repetition matches empty token tree
+    ) => {};
+}
+
+m3! {}
+
+
+macro_rules! m4 {
+    (
+        $(
+            ///
+            ///
+        )*
+        //~^^^^ERROR repetition matches empty token tree
+    ) => {};
+}
+
+m4! {}
+
+fn main() {}
diff --git a/tests/ui/macros/issue-112342-1.stderr b/tests/ui/macros/issue-112342-1.stderr
new file mode 100644
index 00000000000..f2d82bf599e
--- /dev/null
+++ b/tests/ui/macros/issue-112342-1.stderr
@@ -0,0 +1,64 @@
+note: doc comments are ignored in matcher position
+  --> $DIR/issue-112342-1.rs:6:13
+   |
+LL |             ///
+   |             ^^^
+
+error: repetition matches empty token tree
+  --> $DIR/issue-112342-1.rs:5:10
+   |
+LL |           $(
+   |  __________^
+LL | |             ///
+LL | |         )*
+   | |_________^
+
+note: doc comments are ignored in matcher position
+  --> $DIR/issue-112342-1.rs:17:13
+   |
+LL |             ///
+   |             ^^^
+
+error: repetition matches empty token tree
+  --> $DIR/issue-112342-1.rs:16:10
+   |
+LL |           $(
+   |  __________^
+LL | |             ///
+LL | |         )+
+   | |_________^
+
+note: doc comments are ignored in matcher position
+  --> $DIR/issue-112342-1.rs:28:13
+   |
+LL |             ///
+   |             ^^^
+
+error: repetition matches empty token tree
+  --> $DIR/issue-112342-1.rs:27:10
+   |
+LL |           $(
+   |  __________^
+LL | |             ///
+LL | |         )?
+   | |_________^
+
+note: doc comments are ignored in matcher position
+  --> $DIR/issue-112342-1.rs:40:13
+   |
+LL | /             ///
+LL | |             ///
+   | |_______________^
+
+error: repetition matches empty token tree
+  --> $DIR/issue-112342-1.rs:39:10
+   |
+LL |           $(
+   |  __________^
+LL | |             ///
+LL | |             ///
+LL | |         )*
+   | |_________^
+
+error: aborting due to 4 previous errors
+
diff --git a/tests/ui/macros/issue-112342-2.rs b/tests/ui/macros/issue-112342-2.rs
new file mode 100644
index 00000000000..e797aff94d2
--- /dev/null
+++ b/tests/ui/macros/issue-112342-2.rs
@@ -0,0 +1,39 @@
+// check-pass
+
+// same as #95267, ignore doc comment although it's a bug.
+
+macro_rules! m1 {
+    (
+        $(
+            ///
+            $expr: expr,
+        )*
+    ) => {};
+}
+
+m1! {}
+
+macro_rules! m2 {
+    (
+        $(
+            ///
+            $expr: expr,
+            ///
+        )*
+    ) => {};
+}
+
+m2! {}
+
+macro_rules! m3 {
+    (
+        $(
+            ///
+            $tt: tt,
+        )*
+    ) => {};
+}
+
+m3! {}
+
+fn main() {}
diff --git a/tests/ui/macros/issue-112342-2.stderr b/tests/ui/macros/issue-112342-2.stderr
new file mode 100644
index 00000000000..8c1b6f9471b
--- /dev/null
+++ b/tests/ui/macros/issue-112342-2.stderr
@@ -0,0 +1,24 @@
+note: doc comments are ignored in matcher position
+  --> $DIR/issue-112342-2.rs:8:13
+   |
+LL |             ///
+   |             ^^^
+
+note: doc comments are ignored in matcher position
+  --> $DIR/issue-112342-2.rs:19:13
+   |
+LL |             ///
+   |             ^^^
+
+note: doc comments are ignored in matcher position
+  --> $DIR/issue-112342-2.rs:21:13
+   |
+LL |             ///
+   |             ^^^
+
+note: doc comments are ignored in matcher position
+  --> $DIR/issue-112342-2.rs:31:13
+   |
+LL |             ///
+   |             ^^^
+