From 867554ad7c571adea299ecb7ad99641ee22dc91b Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Thu, 13 Jan 2022 15:44:17 -0800
Subject: [PATCH] Fix suggesting turbofish with lifetime arguments

---
 .../rustc_parse/src/parser/diagnostics.rs     | 39 +++++++------
 compiler/rustc_parse/src/parser/expr.rs       |  8 ++-
 ...st-missing-braces-without-turbofish.stderr | 24 ++++----
 src/test/ui/did_you_mean/issue-40396.stderr   | 14 ++---
 .../require-parens-for-chained-comparison.rs  | 24 +++++---
 ...quire-parens-for-chained-comparison.stderr | 55 +++++++++++++++----
 src/test/ui/suggestions/issue-82566-1.stderr  |  6 +-
 src/test/ui/suggestions/issue-82566-2.stderr  |  6 +-
 8 files changed, 115 insertions(+), 61 deletions(-)

diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs
index 4121a759c37f9..19d6de09dae42 100644
--- a/compiler/rustc_parse/src/parser/diagnostics.rs
+++ b/compiler/rustc_parse/src/parser/diagnostics.rs
@@ -27,7 +27,7 @@ use std::mem::take;
 use tracing::{debug, trace};
 
 const TURBOFISH_SUGGESTION_STR: &str =
-    "use `::<...>` instead of `<...>` to specify type or const arguments";
+    "use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments";
 
 /// Creates a placeholder argument.
 pub(super) fn dummy_arg(ident: Ident) -> Param {
@@ -731,21 +731,28 @@ impl<'a> Parser<'a> {
                     match x {
                         Ok((_, _, false)) => {
                             if self.eat(&token::Gt) {
-                                match self.parse_expr() {
-                                    Ok(_) => {
-                                        e.span_suggestion_verbose(
-                                            binop.span.shrink_to_lo(),
-                                            TURBOFISH_SUGGESTION_STR,
-                                            "::".to_string(),
-                                            Applicability::MaybeIncorrect,
-                                        );
-                                        e.emit();
-                                        *expr =
-                                            self.mk_expr_err(expr.span.to(self.prev_token.span));
-                                        return Ok(());
-                                    }
-                                    Err(mut err) => {
-                                        err.cancel();
+                                let turbo_err = e.span_suggestion_verbose(
+                                    binop.span.shrink_to_lo(),
+                                    TURBOFISH_SUGGESTION_STR,
+                                    "::".to_string(),
+                                    Applicability::MaybeIncorrect,
+                                );
+                                if self.check(&TokenKind::Semi) {
+                                    turbo_err.emit();
+                                    *expr = self.mk_expr_err(expr.span);
+                                    return Ok(());
+                                } else {
+                                    match self.parse_expr() {
+                                        Ok(_) => {
+                                            turbo_err.emit();
+                                            *expr = self
+                                                .mk_expr_err(expr.span.to(self.prev_token.span));
+                                            return Ok(());
+                                        }
+                                        Err(mut err) => {
+                                            turbo_err.cancel();
+                                            err.cancel();
+                                        }
                                     }
                                 }
                             }
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index f706a98a4fcfa..6991670f8003c 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -1443,7 +1443,7 @@ impl<'a> Parser<'a> {
         &mut self,
         label: Label,
         attrs: AttrVec,
-        consume_colon: bool,
+        mut consume_colon: bool,
     ) -> PResult<'a, P<Expr>> {
         let lo = label.ident.span;
         let label = Some(label);
@@ -1456,6 +1456,12 @@ impl<'a> Parser<'a> {
             self.parse_loop_expr(label, lo, attrs)
         } else if self.check(&token::OpenDelim(token::Brace)) || self.token.is_whole_block() {
             self.parse_block_expr(label, lo, BlockCheckMode::Default, attrs)
+        } else if !ate_colon && (self.check(&TokenKind::Comma) || self.check(&TokenKind::Gt)) {
+            // We're probably inside of a `Path<'a>` that needs a turbofish, so suppress the
+            // "must be followed by a colon" error.
+            self.diagnostic().delay_span_bug(lo, "this label wasn't parsed correctly");
+            consume_colon = false;
+            Ok(self.mk_expr_err(lo))
         } else {
             let msg = "expected `while`, `for`, `loop` or `{` after a label";
             self.struct_span_err(self.token.span, msg).span_label(self.token.span, msg).emit();
diff --git a/src/test/ui/const-generics/min_const_generics/const-expression-suggest-missing-braces-without-turbofish.stderr b/src/test/ui/const-generics/min_const_generics/const-expression-suggest-missing-braces-without-turbofish.stderr
index 11c3481f15afa..a6825b8458943 100644
--- a/src/test/ui/const-generics/min_const_generics/const-expression-suggest-missing-braces-without-turbofish.stderr
+++ b/src/test/ui/const-generics/min_const_generics/const-expression-suggest-missing-braces-without-turbofish.stderr
@@ -4,7 +4,7 @@ error: comparison operators cannot be chained
 LL |     foo<BAR + 3>();
    |        ^       ^
    |
-help: use `::<...>` instead of `<...>` to specify type or const arguments
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
    |
 LL |     foo::<BAR + 3>();
    |        ++
@@ -15,7 +15,7 @@ error: comparison operators cannot be chained
 LL |     foo<BAR + BAR>();
    |        ^         ^
    |
-help: use `::<...>` instead of `<...>` to specify type or const arguments
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
    |
 LL |     foo::<BAR + BAR>();
    |        ++
@@ -26,7 +26,7 @@ error: comparison operators cannot be chained
 LL |     foo<3 + 3>();
    |        ^     ^
    |
-help: use `::<...>` instead of `<...>` to specify type or const arguments
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
    |
 LL |     foo::<3 + 3>();
    |        ++
@@ -37,7 +37,7 @@ error: comparison operators cannot be chained
 LL |     foo<BAR - 3>();
    |        ^       ^
    |
-help: use `::<...>` instead of `<...>` to specify type or const arguments
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
    |
 LL |     foo::<BAR - 3>();
    |        ++
@@ -48,7 +48,7 @@ error: comparison operators cannot be chained
 LL |     foo<BAR - BAR>();
    |        ^         ^
    |
-help: use `::<...>` instead of `<...>` to specify type or const arguments
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
    |
 LL |     foo::<BAR - BAR>();
    |        ++
@@ -59,7 +59,7 @@ error: comparison operators cannot be chained
 LL |     foo<100 - BAR>();
    |        ^         ^
    |
-help: use `::<...>` instead of `<...>` to specify type or const arguments
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
    |
 LL |     foo::<100 - BAR>();
    |        ++
@@ -70,7 +70,7 @@ error: comparison operators cannot be chained
 LL |     foo<bar<i32>()>();
    |        ^   ^
    |
-help: use `::<...>` instead of `<...>` to specify type or const arguments
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
    |
 LL |     foo::<bar<i32>()>();
    |        ++
@@ -87,7 +87,7 @@ error: comparison operators cannot be chained
 LL |     foo<bar::<i32>()>();
    |        ^            ^
    |
-help: use `::<...>` instead of `<...>` to specify type or const arguments
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
    |
 LL |     foo::<bar::<i32>()>();
    |        ++
@@ -98,7 +98,7 @@ error: comparison operators cannot be chained
 LL |     foo<bar::<i32>() + BAR>();
    |        ^                  ^
    |
-help: use `::<...>` instead of `<...>` to specify type or const arguments
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
    |
 LL |     foo::<bar::<i32>() + BAR>();
    |        ++
@@ -109,7 +109,7 @@ error: comparison operators cannot be chained
 LL |     foo<bar::<i32>() - BAR>();
    |        ^                  ^
    |
-help: use `::<...>` instead of `<...>` to specify type or const arguments
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
    |
 LL |     foo::<bar::<i32>() - BAR>();
    |        ++
@@ -120,7 +120,7 @@ error: comparison operators cannot be chained
 LL |     foo<BAR - bar::<i32>()>();
    |        ^                  ^
    |
-help: use `::<...>` instead of `<...>` to specify type or const arguments
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
    |
 LL |     foo::<BAR - bar::<i32>()>();
    |        ++
@@ -131,7 +131,7 @@ error: comparison operators cannot be chained
 LL |     foo<BAR - bar::<i32>()>();
    |        ^                  ^
    |
-help: use `::<...>` instead of `<...>` to specify type or const arguments
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
    |
 LL |     foo::<BAR - bar::<i32>()>();
    |        ++
diff --git a/src/test/ui/did_you_mean/issue-40396.stderr b/src/test/ui/did_you_mean/issue-40396.stderr
index d2938435ece31..d0249efd094ff 100644
--- a/src/test/ui/did_you_mean/issue-40396.stderr
+++ b/src/test/ui/did_you_mean/issue-40396.stderr
@@ -4,7 +4,7 @@ error: comparison operators cannot be chained
 LL |     (0..13).collect<Vec<i32>>();
    |                    ^   ^
    |
-help: use `::<...>` instead of `<...>` to specify type or const arguments
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
    |
 LL |     (0..13).collect::<Vec<i32>>();
    |                    ++
@@ -15,7 +15,7 @@ error: comparison operators cannot be chained
 LL |     Vec<i32>::new();
    |        ^   ^
    |
-help: use `::<...>` instead of `<...>` to specify type or const arguments
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
    |
 LL |     Vec::<i32>::new();
    |        ++
@@ -26,7 +26,7 @@ error: comparison operators cannot be chained
 LL |     (0..13).collect<Vec<i32>();
    |                    ^   ^
    |
-help: use `::<...>` instead of `<...>` to specify type or const arguments
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
    |
 LL |     (0..13).collect::<Vec<i32>();
    |                    ++
@@ -37,7 +37,7 @@ error: expected one of `!`, `.`, `::`, `;`, `?`, `else`, `{`, or an operator, fo
 LL |     let x = std::collections::HashMap<i128, i128>::new();
    |                                           ^ expected one of 8 possible tokens
    |
-help: use `::<...>` instead of `<...>` to specify type or const arguments
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
    |
 LL |     let x = std::collections::HashMap::<i128, i128>::new();
    |                                      ++
@@ -48,7 +48,7 @@ error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found
 LL |         std::collections::HashMap<i128, i128>::new()
    |                                       ^ expected one of 8 possible tokens
    |
-help: use `::<...>` instead of `<...>` to specify type or const arguments
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
    |
 LL |         std::collections::HashMap::<i128, i128>::new()
    |                                  ++
@@ -59,7 +59,7 @@ error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found
 LL |         std::collections::HashMap<i128, i128>::new();
    |                                       ^ expected one of 8 possible tokens
    |
-help: use `::<...>` instead of `<...>` to specify type or const arguments
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
    |
 LL |         std::collections::HashMap::<i128, i128>::new();
    |                                  ++
@@ -70,7 +70,7 @@ error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found
 LL |         std::collections::HashMap<i128, i128>::new(1, 2);
    |                                       ^ expected one of 8 possible tokens
    |
-help: use `::<...>` instead of `<...>` to specify type or const arguments
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
    |
 LL |         std::collections::HashMap::<i128, i128>::new(1, 2);
    |                                  ++
diff --git a/src/test/ui/parser/require-parens-for-chained-comparison.rs b/src/test/ui/parser/require-parens-for-chained-comparison.rs
index e3ce6cd39bc24..68636f6b907ef 100644
--- a/src/test/ui/parser/require-parens-for-chained-comparison.rs
+++ b/src/test/ui/parser/require-parens-for-chained-comparison.rs
@@ -1,6 +1,3 @@
-fn f<T>() {}
-struct X;
-
 fn main() {
     false == false == false;
     //~^ ERROR comparison operators cannot be chained
@@ -12,15 +9,26 @@ fn main() {
 
     f<X>();
     //~^ ERROR comparison operators cannot be chained
-    //~| HELP use `::<...>` instead of `<...>` to specify type or const arguments
+    //~| HELP use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
 
     f<Result<Option<X>, Option<Option<X>>>(1, 2);
     //~^ ERROR comparison operators cannot be chained
-    //~| HELP use `::<...>` instead of `<...>` to specify type or const arguments
+    //~| HELP use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
+
+    let _ = f<u8, i8>();
+    //~^ ERROR expected one of
+    //~| HELP use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
+
+    let _ = f<'_, i8>();
+    //~^ ERROR expected one of
+    //~| HELP use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
+
+    f<'_>();
+    //~^ comparison operators cannot be chained
+    //~| HELP use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
 
-    use std::convert::identity;
-    let _ = identity<u8>;
+    let _ = f<u8>;
     //~^ ERROR comparison operators cannot be chained
-    //~| HELP use `::<...>` instead of `<...>` to specify type or const arguments
+    //~| HELP use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
     //~| HELP or use `(...)` if you meant to specify fn arguments
 }
diff --git a/src/test/ui/parser/require-parens-for-chained-comparison.stderr b/src/test/ui/parser/require-parens-for-chained-comparison.stderr
index 74429cb438c73..cde6f8c674f4b 100644
--- a/src/test/ui/parser/require-parens-for-chained-comparison.stderr
+++ b/src/test/ui/parser/require-parens-for-chained-comparison.stderr
@@ -1,5 +1,5 @@
 error: comparison operators cannot be chained
-  --> $DIR/require-parens-for-chained-comparison.rs:5:11
+  --> $DIR/require-parens-for-chained-comparison.rs:2:11
    |
 LL |     false == false == false;
    |           ^^       ^^
@@ -10,7 +10,7 @@ LL |     false == false && false == false;
    |                    ++++++++
 
 error: comparison operators cannot be chained
-  --> $DIR/require-parens-for-chained-comparison.rs:9:11
+  --> $DIR/require-parens-for-chained-comparison.rs:6:11
    |
 LL |     false == 0 < 2;
    |           ^^   ^
@@ -21,35 +21,68 @@ LL |     false == (0 < 2);
    |              +     +
 
 error: comparison operators cannot be chained
-  --> $DIR/require-parens-for-chained-comparison.rs:13:6
+  --> $DIR/require-parens-for-chained-comparison.rs:10:6
    |
 LL |     f<X>();
    |      ^ ^
    |
-help: use `::<...>` instead of `<...>` to specify type or const arguments
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
    |
 LL |     f::<X>();
    |      ++
 
 error: comparison operators cannot be chained
-  --> $DIR/require-parens-for-chained-comparison.rs:17:6
+  --> $DIR/require-parens-for-chained-comparison.rs:14:6
    |
 LL |     f<Result<Option<X>, Option<Option<X>>>(1, 2);
    |      ^      ^
    |
-help: use `::<...>` instead of `<...>` to specify type or const arguments
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
    |
 LL |     f::<Result<Option<X>, Option<Option<X>>>(1, 2);
    |      ++
 
+error: expected one of `!`, `.`, `::`, `;`, `?`, `else`, `{`, or an operator, found `,`
+  --> $DIR/require-parens-for-chained-comparison.rs:18:17
+   |
+LL |     let _ = f<u8, i8>();
+   |                 ^ expected one of 8 possible tokens
+   |
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
+   |
+LL |     let _ = f::<u8, i8>();
+   |              ++
+
+error: expected one of `.`, `:`, `;`, `?`, `else`, `for`, `loop`, `while`, `{`, or an operator, found `,`
+  --> $DIR/require-parens-for-chained-comparison.rs:22:17
+   |
+LL |     let _ = f<'_, i8>();
+   |                 ^ expected one of 10 possible tokens
+   |
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
+   |
+LL |     let _ = f::<'_, i8>();
+   |              ++
+
+error: comparison operators cannot be chained
+  --> $DIR/require-parens-for-chained-comparison.rs:26:6
+   |
+LL |     f<'_>();
+   |      ^  ^
+   |
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
+   |
+LL |     f::<'_>();
+   |      ++
+
 error: comparison operators cannot be chained
-  --> $DIR/require-parens-for-chained-comparison.rs:22:21
+  --> $DIR/require-parens-for-chained-comparison.rs:30:14
    |
-LL |     let _ = identity<u8>;
-   |                     ^  ^
+LL |     let _ = f<u8>;
+   |              ^  ^
    |
-   = help: use `::<...>` instead of `<...>` to specify type or const arguments
+   = help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
    = help: or use `(...)` if you meant to specify fn arguments
 
-error: aborting due to 5 previous errors
+error: aborting due to 8 previous errors
 
diff --git a/src/test/ui/suggestions/issue-82566-1.stderr b/src/test/ui/suggestions/issue-82566-1.stderr
index a05c59c786b0a..72f0f45fb8380 100644
--- a/src/test/ui/suggestions/issue-82566-1.stderr
+++ b/src/test/ui/suggestions/issue-82566-1.stderr
@@ -4,7 +4,7 @@ error: comparison operators cannot be chained
 LL |     T1<1>::C;
    |       ^ ^
    |
-help: use `::<...>` instead of `<...>` to specify type or const arguments
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
    |
 LL |     T1::<1>::C;
    |       ++
@@ -15,7 +15,7 @@ error: expected one of `.`, `;`, `?`, `}`, or an operator, found `,`
 LL |     T2<1, 2>::C;
    |         ^ expected one of `.`, `;`, `?`, `}`, or an operator
    |
-help: use `::<...>` instead of `<...>` to specify type or const arguments
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
    |
 LL |     T2::<1, 2>::C;
    |       ++
@@ -26,7 +26,7 @@ error: expected one of `.`, `;`, `?`, `}`, or an operator, found `,`
 LL |     T3<1, 2, 3>::C;
    |         ^ expected one of `.`, `;`, `?`, `}`, or an operator
    |
-help: use `::<...>` instead of `<...>` to specify type or const arguments
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
    |
 LL |     T3::<1, 2, 3>::C;
    |       ++
diff --git a/src/test/ui/suggestions/issue-82566-2.stderr b/src/test/ui/suggestions/issue-82566-2.stderr
index 8f30a301bb82f..ef9a414307cc9 100644
--- a/src/test/ui/suggestions/issue-82566-2.stderr
+++ b/src/test/ui/suggestions/issue-82566-2.stderr
@@ -4,7 +4,7 @@ error: comparison operators cannot be chained
 LL | fn foo1() -> [(); Foo1<10>::SUM] {
    |                       ^  ^
    |
-help: use `::<...>` instead of `<...>` to specify type or const arguments
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
    |
 LL | fn foo1() -> [(); Foo1::<10>::SUM] {
    |                       ++
@@ -15,7 +15,7 @@ error: expected one of `.`, `?`, `]`, or an operator, found `,`
 LL | fn foo2() -> [(); Foo2<10, 20>::SUM] {
    |                          ^ expected one of `.`, `?`, `]`, or an operator
    |
-help: use `::<...>` instead of `<...>` to specify type or const arguments
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
    |
 LL | fn foo2() -> [(); Foo2::<10, 20>::SUM] {
    |                       ++
@@ -26,7 +26,7 @@ error: expected one of `.`, `?`, `]`, or an operator, found `,`
 LL | fn foo3() -> [(); Foo3<10, 20, 30>::SUM] {
    |                          ^ expected one of `.`, `?`, `]`, or an operator
    |
-help: use `::<...>` instead of `<...>` to specify type or const arguments
+help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
    |
 LL | fn foo3() -> [(); Foo3::<10, 20, 30>::SUM] {
    |                       ++