From 4e219e6335a04cae15a784063f8cbe021076c933 Mon Sep 17 00:00:00 2001
From: Fabian Wolff <fabian.wolff@alumni.ethz.ch>
Date: Fri, 4 Jun 2021 19:48:24 +0200
Subject: [PATCH 1/2] Remove incorrect assertion in type parsing code

---
 compiler/rustc_parse/src/parser/ty.rs   |  1 -
 src/test/ui/parser/issue-84148-1.rs     |  4 ++++
 src/test/ui/parser/issue-84148-1.stderr | 23 ++++++++++++++++++
 src/test/ui/parser/issue-84148-2.rs     |  4 ++++
 src/test/ui/parser/issue-84148-2.stderr | 31 +++++++++++++++++++++++++
 5 files changed, 62 insertions(+), 1 deletion(-)
 create mode 100644 src/test/ui/parser/issue-84148-1.rs
 create mode 100644 src/test/ui/parser/issue-84148-1.stderr
 create mode 100644 src/test/ui/parser/issue-84148-2.rs
 create mode 100644 src/test/ui/parser/issue-84148-2.stderr

diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs
index 89cf2d7876e1d..de5a5632600e4 100644
--- a/compiler/rustc_parse/src/parser/ty.rs
+++ b/compiler/rustc_parse/src/parser/ty.rs
@@ -334,7 +334,6 @@ impl<'a> Parser<'a> {
         mut bounds: GenericBounds,
         plus: bool,
     ) -> PResult<'a, TyKind> {
-        assert_ne!(self.token, token::Question);
         if plus {
             self.eat_plus(); // `+`, or `+=` gets split and `+` is discarded
             bounds.append(&mut self.parse_generic_bounds(Some(self.prev_token.span))?);
diff --git a/src/test/ui/parser/issue-84148-1.rs b/src/test/ui/parser/issue-84148-1.rs
new file mode 100644
index 0000000000000..25f7ba4d1f88e
--- /dev/null
+++ b/src/test/ui/parser/issue-84148-1.rs
@@ -0,0 +1,4 @@
+fn f(t:for<>t?)
+//~^ ERROR: expected parameter name
+//~| ERROR: expected one of
+//~| ERROR: expected one of
diff --git a/src/test/ui/parser/issue-84148-1.stderr b/src/test/ui/parser/issue-84148-1.stderr
new file mode 100644
index 0000000000000..98506568d82c2
--- /dev/null
+++ b/src/test/ui/parser/issue-84148-1.stderr
@@ -0,0 +1,23 @@
+error: expected parameter name, found `?`
+  --> $DIR/issue-84148-1.rs:1:14
+   |
+LL | fn f(t:for<>t?)
+   |              ^ expected parameter name
+
+error: expected one of `(`, `)`, `+`, `,`, `::`, or `<`, found `?`
+  --> $DIR/issue-84148-1.rs:1:14
+   |
+LL | fn f(t:for<>t?)
+   |              ^
+   |              |
+   |              expected one of `(`, `)`, `+`, `,`, `::`, or `<`
+   |              help: missing `,`
+
+error: expected one of `->`, `;`, `where`, or `{`, found `<eof>`
+  --> $DIR/issue-84148-1.rs:1:15
+   |
+LL | fn f(t:for<>t?)
+   |               ^ expected one of `->`, `;`, `where`, or `{`
+
+error: aborting due to 3 previous errors
+
diff --git a/src/test/ui/parser/issue-84148-2.rs b/src/test/ui/parser/issue-84148-2.rs
new file mode 100644
index 0000000000000..257a3fd67207e
--- /dev/null
+++ b/src/test/ui/parser/issue-84148-2.rs
@@ -0,0 +1,4 @@
+// error-pattern: this file contains an unclosed delimiter
+// error-pattern: expected parameter name
+// error-pattern: expected one of
+fn f(t:for<>t?
diff --git a/src/test/ui/parser/issue-84148-2.stderr b/src/test/ui/parser/issue-84148-2.stderr
new file mode 100644
index 0000000000000..6f314da436070
--- /dev/null
+++ b/src/test/ui/parser/issue-84148-2.stderr
@@ -0,0 +1,31 @@
+error: this file contains an unclosed delimiter
+  --> $DIR/issue-84148-2.rs:4:16
+   |
+LL | fn f(t:for<>t?
+   |     -          ^
+   |     |
+   |     unclosed delimiter
+
+error: expected parameter name, found `?`
+  --> $DIR/issue-84148-2.rs:4:14
+   |
+LL | fn f(t:for<>t?
+   |              ^ expected parameter name
+
+error: expected one of `(`, `)`, `+`, `,`, `::`, or `<`, found `?`
+  --> $DIR/issue-84148-2.rs:4:14
+   |
+LL | fn f(t:for<>t?
+   |              ^
+   |              |
+   |              expected one of `(`, `)`, `+`, `,`, `::`, or `<`
+   |              help: missing `,`
+
+error: expected one of `->`, `;`, `where`, or `{`, found `<eof>`
+  --> $DIR/issue-84148-2.rs:4:16
+   |
+LL | fn f(t:for<>t?
+   |                ^ expected one of `->`, `;`, `where`, or `{`
+
+error: aborting due to 4 previous errors
+

From 6a6a605a61dfacfa974582484236155ce041a21d Mon Sep 17 00:00:00 2001
From: Fabian Wolff <fabian.wolff@alumni.ethz.ch>
Date: Fri, 4 Jun 2021 22:46:57 +0200
Subject: [PATCH 2/2] Fix handling of unmatched angle brackets in parser

---
 compiler/rustc_parse/src/parser/path.rs      | 84 +++++++++++---------
 src/test/ui/parser/issue-84104.rs            |  3 +
 src/test/ui/parser/issue-84104.stderr        | 16 ++++
 src/test/ui/parser/unmatched-langle-1.rs     |  9 +++
 src/test/ui/parser/unmatched-langle-1.stderr | 22 +++++
 src/test/ui/parser/unmatched-langle-2.rs     | 15 ++++
 src/test/ui/parser/unmatched-langle-2.stderr |  8 ++
 7 files changed, 120 insertions(+), 37 deletions(-)
 create mode 100644 src/test/ui/parser/issue-84104.rs
 create mode 100644 src/test/ui/parser/issue-84104.stderr
 create mode 100644 src/test/ui/parser/unmatched-langle-1.rs
 create mode 100644 src/test/ui/parser/unmatched-langle-1.stderr
 create mode 100644 src/test/ui/parser/unmatched-langle-2.rs
 create mode 100644 src/test/ui/parser/unmatched-langle-2.stderr

diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs
index 9cc600d9ede02..953c6915068af 100644
--- a/compiler/rustc_parse/src/parser/path.rs
+++ b/compiler/rustc_parse/src/parser/path.rs
@@ -352,49 +352,59 @@ impl<'a> Parser<'a> {
         debug!("parse_generic_args_with_leading_angle_bracket_recovery: (snapshotting)");
         match self.parse_angle_args() {
             Ok(args) => Ok(args),
-            Err(ref mut e) if is_first_invocation && self.unmatched_angle_bracket_count > 0 => {
-                // Cancel error from being unable to find `>`. We know the error
-                // must have been this due to a non-zero unmatched angle bracket
-                // count.
-                e.cancel();
-
+            Err(mut e) if is_first_invocation && self.unmatched_angle_bracket_count > 0 => {
                 // Swap `self` with our backup of the parser state before attempting to parse
                 // generic arguments.
                 let snapshot = mem::replace(self, snapshot.unwrap());
 
-                debug!(
-                    "parse_generic_args_with_leading_angle_bracket_recovery: (snapshot failure) \
-                     snapshot.count={:?}",
-                    snapshot.unmatched_angle_bracket_count,
-                );
-
                 // Eat the unmatched angle brackets.
-                for _ in 0..snapshot.unmatched_angle_bracket_count {
-                    self.eat_lt();
-                }
-
-                // Make a span over ${unmatched angle bracket count} characters.
-                let span = lo.with_hi(lo.lo() + BytePos(snapshot.unmatched_angle_bracket_count));
-                self.struct_span_err(
-                    span,
-                    &format!(
-                        "unmatched angle bracket{}",
-                        pluralize!(snapshot.unmatched_angle_bracket_count)
-                    ),
-                )
-                .span_suggestion(
-                    span,
-                    &format!(
-                        "remove extra angle bracket{}",
-                        pluralize!(snapshot.unmatched_angle_bracket_count)
-                    ),
-                    String::new(),
-                    Applicability::MachineApplicable,
-                )
-                .emit();
+                let all_angle_brackets = (0..snapshot.unmatched_angle_bracket_count)
+                    .fold(true, |a, _| a && self.eat_lt());
+
+                if !all_angle_brackets {
+                    // If there are other tokens in between the extraneous `<`s, we cannot simply
+                    // suggest to remove them. This check also prevents us from accidentally ending
+                    // up in the middle of a multibyte character (issue #84104).
+                    let _ = mem::replace(self, snapshot);
+                    Err(e)
+                } else {
+                    // Cancel error from being unable to find `>`. We know the error
+                    // must have been this due to a non-zero unmatched angle bracket
+                    // count.
+                    e.cancel();
+
+                    debug!(
+                        "parse_generic_args_with_leading_angle_bracket_recovery: (snapshot failure) \
+                         snapshot.count={:?}",
+                        snapshot.unmatched_angle_bracket_count,
+                    );
+
+                    // Make a span over ${unmatched angle bracket count} characters.
+                    // This is safe because `all_angle_brackets` ensures that there are only `<`s,
+                    // i.e. no multibyte characters, in this range.
+                    let span =
+                        lo.with_hi(lo.lo() + BytePos(snapshot.unmatched_angle_bracket_count));
+                    self.struct_span_err(
+                        span,
+                        &format!(
+                            "unmatched angle bracket{}",
+                            pluralize!(snapshot.unmatched_angle_bracket_count)
+                        ),
+                    )
+                    .span_suggestion(
+                        span,
+                        &format!(
+                            "remove extra angle bracket{}",
+                            pluralize!(snapshot.unmatched_angle_bracket_count)
+                        ),
+                        String::new(),
+                        Applicability::MachineApplicable,
+                    )
+                    .emit();
 
-                // Try again without unmatched angle bracket characters.
-                self.parse_angle_args()
+                    // Try again without unmatched angle bracket characters.
+                    self.parse_angle_args()
+                }
             }
             Err(e) => Err(e),
         }
diff --git a/src/test/ui/parser/issue-84104.rs b/src/test/ui/parser/issue-84104.rs
new file mode 100644
index 0000000000000..998949b94a4ba
--- /dev/null
+++ b/src/test/ui/parser/issue-84104.rs
@@ -0,0 +1,3 @@
+// error-pattern: this file contains an unclosed delimiter
+// error-pattern: expected one of
+#[i=i::<ښܖ<
diff --git a/src/test/ui/parser/issue-84104.stderr b/src/test/ui/parser/issue-84104.stderr
new file mode 100644
index 0000000000000..aff31f2c97149
--- /dev/null
+++ b/src/test/ui/parser/issue-84104.stderr
@@ -0,0 +1,16 @@
+error: this file contains an unclosed delimiter
+  --> $DIR/issue-84104.rs:3:13
+   |
+LL | #[i=i::<ښܖ<
+   |  -          ^
+   |  |
+   |  unclosed delimiter
+
+error: expected one of `>`, a const expression, lifetime, or type, found `]`
+  --> $DIR/issue-84104.rs:3:13
+   |
+LL | #[i=i::<ښܖ<
+   |             ^ expected one of `>`, a const expression, lifetime, or type
+
+error: aborting due to 2 previous errors
+
diff --git a/src/test/ui/parser/unmatched-langle-1.rs b/src/test/ui/parser/unmatched-langle-1.rs
new file mode 100644
index 0000000000000..fdf2ae398014b
--- /dev/null
+++ b/src/test/ui/parser/unmatched-langle-1.rs
@@ -0,0 +1,9 @@
+// Check that a suggestion is issued if there are too many `<`s in a
+// generic argument list, and that the parser recovers properly.
+
+fn main() {
+    foo::<<<<Ty<i32>>();
+    //~^ ERROR: unmatched angle brackets
+    //~| ERROR: cannot find function `foo` in this scope [E0425]
+    //~| ERROR: cannot find type `Ty` in this scope [E0412]
+}
diff --git a/src/test/ui/parser/unmatched-langle-1.stderr b/src/test/ui/parser/unmatched-langle-1.stderr
new file mode 100644
index 0000000000000..c8072b4c59ad2
--- /dev/null
+++ b/src/test/ui/parser/unmatched-langle-1.stderr
@@ -0,0 +1,22 @@
+error: unmatched angle brackets
+  --> $DIR/unmatched-langle-1.rs:5:10
+   |
+LL |     foo::<<<<Ty<i32>>();
+   |          ^^^ help: remove extra angle brackets
+
+error[E0425]: cannot find function `foo` in this scope
+  --> $DIR/unmatched-langle-1.rs:5:5
+   |
+LL |     foo::<<<<Ty<i32>>();
+   |     ^^^ not found in this scope
+
+error[E0412]: cannot find type `Ty` in this scope
+  --> $DIR/unmatched-langle-1.rs:5:14
+   |
+LL |     foo::<<<<Ty<i32>>();
+   |              ^^ not found in this scope
+
+error: aborting due to 3 previous errors
+
+Some errors have detailed explanations: E0412, E0425.
+For more information about an error, try `rustc --explain E0412`.
diff --git a/src/test/ui/parser/unmatched-langle-2.rs b/src/test/ui/parser/unmatched-langle-2.rs
new file mode 100644
index 0000000000000..8de0d7d89e4e7
--- /dev/null
+++ b/src/test/ui/parser/unmatched-langle-2.rs
@@ -0,0 +1,15 @@
+// When there are too many opening `<`s, the compiler would previously
+// suggest nonsense if the `<`s were interspersed with other tokens:
+//
+//   error: unmatched angle brackets
+//    --> unmatched-langle.rs:2:10
+//     |
+//   2 |     foo::<Ty<<<i32>();
+//     |          ^^^ help: remove extra angle brackets
+//
+// This test makes sure that this is no longer happening.
+
+fn main() {
+    foo::<Ty<<<i32>();
+    //~^ ERROR: expected `::`, found `(`
+}
diff --git a/src/test/ui/parser/unmatched-langle-2.stderr b/src/test/ui/parser/unmatched-langle-2.stderr
new file mode 100644
index 0000000000000..773bb33d8d3f3
--- /dev/null
+++ b/src/test/ui/parser/unmatched-langle-2.stderr
@@ -0,0 +1,8 @@
+error: expected `::`, found `(`
+  --> $DIR/unmatched-langle-2.rs:13:20
+   |
+LL |     foo::<Ty<<<i32>();
+   |                    ^ expected `::`
+
+error: aborting due to previous error
+