From 2a7837262f67887b7e5908181000d36036dc5485 Mon Sep 17 00:00:00 2001
From: Michael Howell <michael@notriddle.com>
Date: Fri, 25 Mar 2022 13:53:03 -0700
Subject: [PATCH] diagnostics: correct generic bounds with doubled colon

Fixes #95208
---
 .../rustc_parse/src/parser/diagnostics.rs     | 28 +++++++++++++++++++
 compiler/rustc_parse/src/parser/generics.rs   |  1 +
 .../generics/issue-95208-ignore-qself.fixed   | 11 ++++++++
 .../ui/generics/issue-95208-ignore-qself.rs   | 11 ++++++++
 .../generics/issue-95208-ignore-qself.stderr  | 10 +++++++
 src/test/ui/generics/issue-95208.fixed        | 11 ++++++++
 src/test/ui/generics/issue-95208.rs           | 11 ++++++++
 src/test/ui/generics/issue-95208.stderr       | 10 +++++++
 8 files changed, 93 insertions(+)
 create mode 100644 src/test/ui/generics/issue-95208-ignore-qself.fixed
 create mode 100644 src/test/ui/generics/issue-95208-ignore-qself.rs
 create mode 100644 src/test/ui/generics/issue-95208-ignore-qself.stderr
 create mode 100644 src/test/ui/generics/issue-95208.fixed
 create mode 100644 src/test/ui/generics/issue-95208.rs
 create mode 100644 src/test/ui/generics/issue-95208.stderr

diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs
index b5b628a3f55..534fd0d4816 100644
--- a/compiler/rustc_parse/src/parser/diagnostics.rs
+++ b/compiler/rustc_parse/src/parser/diagnostics.rs
@@ -2369,6 +2369,34 @@ impl<'a> Parser<'a> {
         Err(err)
     }
 
+    crate fn maybe_recover_bounds_doubled_colon(&mut self, ty: &Ty) -> PResult<'a, ()> {
+        let TyKind::Path(qself, path) = &ty.kind else { return Ok(()) };
+        let qself_position = qself.as_ref().map(|qself| qself.position);
+        for (i, segments) in path.segments.windows(2).enumerate() {
+            if qself_position.map(|pos| i < pos).unwrap_or(false) {
+                continue;
+            }
+            if let [a, b] = segments {
+                let (a_span, b_span) = (a.span(), b.span());
+                let between_span = a_span.shrink_to_hi().to(b_span.shrink_to_lo());
+                if self.span_to_snippet(between_span).as_ref().map(|a| &a[..]) == Ok(":: ") {
+                    let mut err = self.struct_span_err(
+                        path.span.shrink_to_hi(),
+                        "expected `:` followed by trait or lifetime",
+                    );
+                    err.span_suggestion(
+                        between_span,
+                        "use single colon",
+                        ": ".to_owned(),
+                        Applicability::MachineApplicable,
+                    );
+                    return Err(err);
+                }
+            }
+        }
+        Ok(())
+    }
+
     /// Parse and throw away a parenthesized comma separated
     /// sequence of patterns until `)` is reached.
     fn skip_pat_list(&mut self) -> PResult<'a, ()> {
diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs
index d625080dee4..29fe2b76101 100644
--- a/compiler/rustc_parse/src/parser/generics.rs
+++ b/compiler/rustc_parse/src/parser/generics.rs
@@ -312,6 +312,7 @@ impl<'a> Parser<'a> {
                 id: ast::DUMMY_NODE_ID,
             }))
         } else {
+            self.maybe_recover_bounds_doubled_colon(&ty)?;
             self.unexpected()
         }
     }
diff --git a/src/test/ui/generics/issue-95208-ignore-qself.fixed b/src/test/ui/generics/issue-95208-ignore-qself.fixed
new file mode 100644
index 00000000000..608b4a20fbc
--- /dev/null
+++ b/src/test/ui/generics/issue-95208-ignore-qself.fixed
@@ -0,0 +1,11 @@
+// run-rustfix
+
+#[allow(unused)]
+struct Struct<T>(T);
+
+impl<T: Iterator> Struct<T> where <T as std:: iter::Iterator>::Item: std::fmt::Display {
+//~^ ERROR expected `:` followed by trait or lifetime
+//~| HELP use single colon
+}
+
+fn main() {}
diff --git a/src/test/ui/generics/issue-95208-ignore-qself.rs b/src/test/ui/generics/issue-95208-ignore-qself.rs
new file mode 100644
index 00000000000..da7efd576d1
--- /dev/null
+++ b/src/test/ui/generics/issue-95208-ignore-qself.rs
@@ -0,0 +1,11 @@
+// run-rustfix
+
+#[allow(unused)]
+struct Struct<T>(T);
+
+impl<T: Iterator> Struct<T> where <T as std:: iter::Iterator>::Item:: std::fmt::Display {
+//~^ ERROR expected `:` followed by trait or lifetime
+//~| HELP use single colon
+}
+
+fn main() {}
diff --git a/src/test/ui/generics/issue-95208-ignore-qself.stderr b/src/test/ui/generics/issue-95208-ignore-qself.stderr
new file mode 100644
index 00000000000..acbc1300d00
--- /dev/null
+++ b/src/test/ui/generics/issue-95208-ignore-qself.stderr
@@ -0,0 +1,10 @@
+error: expected `:` followed by trait or lifetime
+  --> $DIR/issue-95208-ignore-qself.rs:6:88
+   |
+LL | impl<T: Iterator> Struct<T> where <T as std:: iter::Iterator>::Item:: std::fmt::Display {
+   |                                                                    ---                 ^
+   |                                                                    |
+   |                                                                    help: use single colon: `:`
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/generics/issue-95208.fixed b/src/test/ui/generics/issue-95208.fixed
new file mode 100644
index 00000000000..a0b1e886ca2
--- /dev/null
+++ b/src/test/ui/generics/issue-95208.fixed
@@ -0,0 +1,11 @@
+// run-rustfix
+
+#[allow(unused)]
+struct Struct<T>(T);
+
+impl<T> Struct<T> where T: std::fmt::Display {
+//~^ ERROR expected `:` followed by trait or lifetime
+//~| HELP use single colon
+}
+
+fn main() {}
diff --git a/src/test/ui/generics/issue-95208.rs b/src/test/ui/generics/issue-95208.rs
new file mode 100644
index 00000000000..0e3083484ff
--- /dev/null
+++ b/src/test/ui/generics/issue-95208.rs
@@ -0,0 +1,11 @@
+// run-rustfix
+
+#[allow(unused)]
+struct Struct<T>(T);
+
+impl<T> Struct<T> where T:: std::fmt::Display {
+//~^ ERROR expected `:` followed by trait or lifetime
+//~| HELP use single colon
+}
+
+fn main() {}
diff --git a/src/test/ui/generics/issue-95208.stderr b/src/test/ui/generics/issue-95208.stderr
new file mode 100644
index 00000000000..559527663e8
--- /dev/null
+++ b/src/test/ui/generics/issue-95208.stderr
@@ -0,0 +1,10 @@
+error: expected `:` followed by trait or lifetime
+  --> $DIR/issue-95208.rs:6:46
+   |
+LL | impl<T> Struct<T> where T:: std::fmt::Display {
+   |                          ---                 ^
+   |                          |
+   |                          help: use single colon: `:`
+
+error: aborting due to previous error
+