From 6046be42ab911cf2eddd41cffaa0467098bb2317 Mon Sep 17 00:00:00 2001
From: mark <markm@cs.wisc.edu>
Date: Mon, 14 Jan 2019 15:50:33 -0600
Subject: [PATCH 1/3] fix nested matchers with ?

---
 src/libsyntax/ext/tt/macro_rules.rs | 14 ++++++++++----
 1 file changed, 10 insertions(+), 4 deletions(-)

diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs
index 24202ca8fbdc0..9a129e7e8fcd8 100644
--- a/src/libsyntax/ext/tt/macro_rules.rs
+++ b/src/libsyntax/ext/tt/macro_rules.rs
@@ -435,7 +435,8 @@ fn check_lhs_no_empty_seq(sess: &ParseSess, tts: &[quoted::TokenTree]) -> bool {
                     match *seq_tt {
                         TokenTree::MetaVarDecl(_, _, id) => id.name == "vis",
                         TokenTree::Sequence(_, ref sub_seq) =>
-                            sub_seq.op == quoted::KleeneOp::ZeroOrMore,
+                            sub_seq.op == quoted::KleeneOp::ZeroOrMore
+                            || sub_seq.op == quoted::KleeneOp::ZeroOrOne,
                         _ => false,
                     }
                 }) {
@@ -543,7 +544,10 @@ impl FirstSets {
                         }
 
                         // Reverse scan: Sequence comes before `first`.
-                        if subfirst.maybe_empty || seq_rep.op == quoted::KleeneOp::ZeroOrMore {
+                        if subfirst.maybe_empty
+                           || seq_rep.op == quoted::KleeneOp::ZeroOrMore
+                           || seq_rep.op == quoted::KleeneOp::ZeroOrOne
+                        {
                             // If sequence is potentially empty, then
                             // union them (preserving first emptiness).
                             first.add_all(&TokenSet { maybe_empty: true, ..subfirst });
@@ -591,8 +595,10 @@ impl FirstSets {
 
                             assert!(first.maybe_empty);
                             first.add_all(subfirst);
-                            if subfirst.maybe_empty ||
-                               seq_rep.op == quoted::KleeneOp::ZeroOrMore {
+                            if subfirst.maybe_empty
+                               || seq_rep.op == quoted::KleeneOp::ZeroOrMore
+                               || seq_rep.op == quoted::KleeneOp::ZeroOrOne
+                            {
                                 // continue scanning for more first
                                 // tokens, but also make sure we
                                 // restore empty-tracking state

From dabe86db44c97698bc6a4ab2f8923a3f65b151f0 Mon Sep 17 00:00:00 2001
From: mark <markm@cs.wisc.edu>
Date: Tue, 15 Jan 2019 11:31:49 -0600
Subject: [PATCH 2/3] update/add tests

---
 src/test/ui/issues/issue-5067.rs      | 37 ++++++++++---
 src/test/ui/issues/issue-57597.rs     | 80 +++++++++++++++++++++++++++
 src/test/ui/issues/issue-57597.stderr | 74 +++++++++++++++++++++++++
 3 files changed, 183 insertions(+), 8 deletions(-)
 create mode 100644 src/test/ui/issues/issue-57597.rs
 create mode 100644 src/test/ui/issues/issue-57597.stderr

diff --git a/src/test/ui/issues/issue-5067.rs b/src/test/ui/issues/issue-5067.rs
index 526a68311462e..616fd09907a2b 100644
--- a/src/test/ui/issues/issue-5067.rs
+++ b/src/test/ui/issues/issue-5067.rs
@@ -1,37 +1,59 @@
 #![allow(unused_macros)]
 
+// Tests that repetition matchers cannot match the empty token tree (since that would be
+// ambiguous).
+
+// edition:2018
+
 macro_rules! foo {
     ( $()* ) => {};
     //~^ ERROR repetition matches empty token tree
     ( $()+ ) => {};
     //~^ ERROR repetition matches empty token tree
-
+    ( $()? ) => {};
+    //~^ ERROR repetition matches empty token tree
     ( $(),* ) => {}; // PASS
     ( $(),+ ) => {}; // PASS
-
+    // `?` cannot have a separator...
     ( [$()*] ) => {};
     //~^ ERROR repetition matches empty token tree
     ( [$()+] ) => {};
     //~^ ERROR repetition matches empty token tree
-
+    ( [$()?] ) => {};
+    //~^ ERROR repetition matches empty token tree
     ( [$(),*] ) => {}; // PASS
     ( [$(),+] ) => {}; // PASS
-
+    // `?` cannot have a separator...
     ( $($()* $(),* $(a)* $(a),* )* ) => {};
     //~^ ERROR repetition matches empty token tree
     ( $($()* $(),* $(a)* $(a),* )+ ) => {};
     //~^ ERROR repetition matches empty token tree
-
+    ( $($()* $(),* $(a)* $(a),* )? ) => {};
+    //~^ ERROR repetition matches empty token tree
+    ( $($()? $(),* $(a)? $(a),* )* ) => {};
+    //~^ ERROR repetition matches empty token tree
+    ( $($()? $(),* $(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)+ $(),* $(a)* $(a),* )? ) => {}; // PASS
+
+    ( $(a     $(),* $(a)? $(a),* )* ) => {}; // PASS
+    ( $($(a)+ $(),* $(a)? $(a),* )+ ) => {}; // PASS
+    ( $($(a)+ $(),* $(a)? $(a),* )? ) => {}; // PASS
 
     ( $(a $()+)* ) => {};
     //~^ ERROR repetition matches empty token tree
     ( $(a $()*)+ ) => {};
     //~^ ERROR repetition matches empty token tree
+    ( $(a $()+)? ) => {};
+    //~^ ERROR repetition matches empty token tree
+    ( $(a $()?)+ ) => {};
+    //~^ ERROR repetition matches empty token tree
 }
 
-
 // --- Original Issue --- //
 
 macro_rules! make_vec {
@@ -43,11 +65,10 @@ fn main() {
     let _ = make_vec![a 1, a 2, a 3];
 }
 
-
 // --- Minified Issue --- //
 
 macro_rules! m {
-    ( $()* ) => {}
+    ( $()* ) => {};
     //~^ ERROR repetition matches empty token tree
 }
 
diff --git a/src/test/ui/issues/issue-57597.rs b/src/test/ui/issues/issue-57597.rs
new file mode 100644
index 0000000000000..ebeb3fe07adb4
--- /dev/null
+++ b/src/test/ui/issues/issue-57597.rs
@@ -0,0 +1,80 @@
+// Regression test for #57597.
+//
+// Make sure that nested matchers work correctly rather than causing an infinite loop or crash.
+
+// edition:2018
+
+macro_rules! foo1 {
+    ($($($i:ident)?)+) => {};
+    //~^ ERROR repetition matches empty token tree
+}
+
+macro_rules! foo2 {
+    ($($($i:ident)?)*) => {};
+    //~^ ERROR repetition matches empty token tree
+}
+
+macro_rules! foo3 {
+    ($($($i:ident)?)?) => {};
+    //~^ ERROR repetition matches empty token tree
+}
+
+macro_rules! foo4 {
+    ($($($($i:ident)?)?)?) => {};
+    //~^ ERROR repetition matches empty token tree
+}
+
+macro_rules! foo5 {
+    ($($($($i:ident)*)?)?) => {};
+    //~^ ERROR repetition matches empty token tree
+}
+
+macro_rules! foo6 {
+    ($($($($i:ident)?)*)?) => {};
+    //~^ ERROR repetition matches empty token tree
+}
+
+macro_rules! foo7 {
+    ($($($($i:ident)?)?)*) => {};
+    //~^ ERROR repetition matches empty token tree
+}
+
+macro_rules! foo8 {
+    ($($($($i:ident)*)*)?) => {};
+    //~^ ERROR repetition matches empty token tree
+}
+
+macro_rules! foo9 {
+    ($($($($i:ident)?)*)*) => {};
+    //~^ ERROR repetition matches empty token tree
+}
+
+macro_rules! foo10 {
+    ($($($($i:ident)?)*)+) => {};
+    //~^ ERROR repetition matches empty token tree
+}
+
+macro_rules! foo11 {
+    ($($($($i:ident)+)?)*) => {};
+    //~^ ERROR repetition matches empty token tree
+}
+
+macro_rules! foo12 {
+    ($($($($i:ident)+)*)?) => {};
+    //~^ ERROR repetition matches empty token tree
+}
+
+fn main() {
+    foo1!();
+    foo2!();
+    foo3!();
+    foo4!();
+    foo5!();
+    foo6!();
+    foo7!();
+    foo8!();
+    foo9!();
+    foo10!();
+    foo11!();
+    foo12!();
+}
diff --git a/src/test/ui/issues/issue-57597.stderr b/src/test/ui/issues/issue-57597.stderr
new file mode 100644
index 0000000000000..0a02ac8c499b6
--- /dev/null
+++ b/src/test/ui/issues/issue-57597.stderr
@@ -0,0 +1,74 @@
+error: repetition matches empty token tree
+  --> $DIR/issue-57597.rs:8:7
+   |
+LL |     ($($($i:ident)?)+) => {};
+   |       ^^^^^^^^^^^^^^
+
+error: repetition matches empty token tree
+  --> $DIR/issue-57597.rs:13:7
+   |
+LL |     ($($($i:ident)?)*) => {};
+   |       ^^^^^^^^^^^^^^
+
+error: repetition matches empty token tree
+  --> $DIR/issue-57597.rs:18:7
+   |
+LL |     ($($($i:ident)?)?) => {};
+   |       ^^^^^^^^^^^^^^
+
+error: repetition matches empty token tree
+  --> $DIR/issue-57597.rs:23:7
+   |
+LL |     ($($($($i:ident)?)?)?) => {};
+   |       ^^^^^^^^^^^^^^^^^^
+
+error: repetition matches empty token tree
+  --> $DIR/issue-57597.rs:28:7
+   |
+LL |     ($($($($i:ident)*)?)?) => {};
+   |       ^^^^^^^^^^^^^^^^^^
+
+error: repetition matches empty token tree
+  --> $DIR/issue-57597.rs:33:7
+   |
+LL |     ($($($($i:ident)?)*)?) => {};
+   |       ^^^^^^^^^^^^^^^^^^
+
+error: repetition matches empty token tree
+  --> $DIR/issue-57597.rs:38:7
+   |
+LL |     ($($($($i:ident)?)?)*) => {};
+   |       ^^^^^^^^^^^^^^^^^^
+
+error: repetition matches empty token tree
+  --> $DIR/issue-57597.rs:43:7
+   |
+LL |     ($($($($i:ident)*)*)?) => {};
+   |       ^^^^^^^^^^^^^^^^^^
+
+error: repetition matches empty token tree
+  --> $DIR/issue-57597.rs:48:7
+   |
+LL |     ($($($($i:ident)?)*)*) => {};
+   |       ^^^^^^^^^^^^^^^^^^
+
+error: repetition matches empty token tree
+  --> $DIR/issue-57597.rs:53:7
+   |
+LL |     ($($($($i:ident)?)*)+) => {};
+   |       ^^^^^^^^^^^^^^^^^^
+
+error: repetition matches empty token tree
+  --> $DIR/issue-57597.rs:58:7
+   |
+LL |     ($($($($i:ident)+)?)*) => {};
+   |       ^^^^^^^^^^^^^^^^^^
+
+error: repetition matches empty token tree
+  --> $DIR/issue-57597.rs:63:7
+   |
+LL |     ($($($($i:ident)+)*)?) => {};
+   |       ^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 12 previous errors
+

From aa1ce32b105ff506257019a22684a98b61ab166a Mon Sep 17 00:00:00 2001
From: mark <markm@cs.wisc.edu>
Date: Tue, 15 Jan 2019 16:40:10 -0600
Subject: [PATCH 3/3] update test output

---
 src/test/ui/issues/issue-5067.stderr | 72 +++++++++++++++++++++++-----
 1 file changed, 60 insertions(+), 12 deletions(-)

diff --git a/src/test/ui/issues/issue-5067.stderr b/src/test/ui/issues/issue-5067.stderr
index 433b7c8c04907..7ffc6071407c5 100644
--- a/src/test/ui/issues/issue-5067.stderr
+++ b/src/test/ui/issues/issue-5067.stderr
@@ -1,62 +1,110 @@
 error: repetition matches empty token tree
-  --> $DIR/issue-5067.rs:4:8
+  --> $DIR/issue-5067.rs:9:8
    |
 LL |     ( $()* ) => {};
    |        ^^
 
 error: repetition matches empty token tree
-  --> $DIR/issue-5067.rs:6:8
+  --> $DIR/issue-5067.rs:11:8
    |
 LL |     ( $()+ ) => {};
    |        ^^
 
 error: repetition matches empty token tree
-  --> $DIR/issue-5067.rs:12:9
+  --> $DIR/issue-5067.rs:13:8
+   |
+LL |     ( $()? ) => {};
+   |        ^^
+
+error: repetition matches empty token tree
+  --> $DIR/issue-5067.rs:18:9
    |
 LL |     ( [$()*] ) => {};
    |         ^^
 
 error: repetition matches empty token tree
-  --> $DIR/issue-5067.rs:14:9
+  --> $DIR/issue-5067.rs:20:9
    |
 LL |     ( [$()+] ) => {};
    |         ^^
 
 error: repetition matches empty token tree
-  --> $DIR/issue-5067.rs:20:8
+  --> $DIR/issue-5067.rs:22:9
+   |
+LL |     ( [$()?] ) => {};
+   |         ^^
+
+error: repetition matches empty token tree
+  --> $DIR/issue-5067.rs:27:8
    |
 LL |     ( $($()* $(),* $(a)* $(a),* )* ) => {};
    |        ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: repetition matches empty token tree
-  --> $DIR/issue-5067.rs:22:8
+  --> $DIR/issue-5067.rs:29:8
    |
 LL |     ( $($()* $(),* $(a)* $(a),* )+ ) => {};
    |        ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: repetition matches empty token tree
-  --> $DIR/issue-5067.rs:28:12
+  --> $DIR/issue-5067.rs:31:8
+   |
+LL |     ( $($()* $(),* $(a)* $(a),* )? ) => {};
+   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: repetition matches empty token tree
+  --> $DIR/issue-5067.rs:33:8
+   |
+LL |     ( $($()? $(),* $(a)? $(a),* )* ) => {};
+   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: repetition matches empty token tree
+  --> $DIR/issue-5067.rs:35:8
+   |
+LL |     ( $($()? $(),* $(a)? $(a),* )+ ) => {};
+   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: repetition matches empty token tree
+  --> $DIR/issue-5067.rs:37:8
+   |
+LL |     ( $($()? $(),* $(a)? $(a),* )? ) => {};
+   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: repetition matches empty token tree
+  --> $DIR/issue-5067.rs:47:12
    |
 LL |     ( $(a $()+)* ) => {};
    |            ^^
 
 error: repetition matches empty token tree
-  --> $DIR/issue-5067.rs:30:12
+  --> $DIR/issue-5067.rs:49:12
    |
 LL |     ( $(a $()*)+ ) => {};
    |            ^^
 
 error: repetition matches empty token tree
-  --> $DIR/issue-5067.rs:38:18
+  --> $DIR/issue-5067.rs:51:12
+   |
+LL |     ( $(a $()+)? ) => {};
+   |            ^^
+
+error: repetition matches empty token tree
+  --> $DIR/issue-5067.rs:53:12
+   |
+LL |     ( $(a $()?)+ ) => {};
+   |            ^^
+
+error: repetition matches empty token tree
+  --> $DIR/issue-5067.rs:60:18
    |
 LL |     (a $e1:expr $($(, a $e2:expr)*)*) => ([$e1 $($(, $e2)*)*]);
    |                  ^^^^^^^^^^^^^^^^^^
 
 error: repetition matches empty token tree
-  --> $DIR/issue-5067.rs:50:8
+  --> $DIR/issue-5067.rs:71:8
    |
-LL |     ( $()* ) => {}
+LL |     ( $()* ) => {};
    |        ^^
 
-error: aborting due to 10 previous errors
+error: aborting due to 18 previous errors