From 60560bc2a2c149e179cd7e58a8b48e06c2c4e3f4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Esteban=20K=C3=BCber?= <esteban@kuber.com.ar>
Date: Sat, 21 Sep 2019 17:11:09 -0700
Subject: [PATCH 1/2] Parse assoc type bounds in generic params and provide
 custom diagnostic

---
 src/libsyntax/parse/parser/generics.rs        | 99 ++++++++++++-------
 src/test/ui/parser/assoc-type-in-type-arg.rs  | 11 +++
 .../ui/parser/assoc-type-in-type-arg.stderr   |  8 ++
 3 files changed, 80 insertions(+), 38 deletions(-)
 create mode 100644 src/test/ui/parser/assoc-type-in-type-arg.rs
 create mode 100644 src/test/ui/parser/assoc-type-in-type-arg.stderr

diff --git a/src/libsyntax/parse/parser/generics.rs b/src/libsyntax/parse/parser/generics.rs
index 3e6118ad86f47..1ffdda19c80b5 100644
--- a/src/libsyntax/parse/parser/generics.rs
+++ b/src/libsyntax/parse/parser/generics.rs
@@ -100,13 +100,31 @@ impl<'a> Parser<'a> {
             } else if self.check_ident() {
                 // Parse type parameter.
                 params.push(self.parse_ty_param(attrs)?);
+            } else if self.token.can_begin_type() {
+                // Trying to write an associated type bound? (#26271)
+                let snapshot = self.clone();
+                match self.parse_ty_where_predicate() {
+                    Ok(where_predicate) => {
+                        self.struct_span_err(
+                            where_predicate.span(),
+                            "associated type bounds do not belong here",
+                        )
+                        .span_label(where_predicate.span(), "belongs in `where` clause")
+                        .emit();
+                    }
+                    Err(mut err) => {
+                        err.cancel();
+                        std::mem::replace(self, snapshot);
+                        break
+                    }
+                }
             } else {
                 // Check for trailing attributes and stop parsing.
                 if !attrs.is_empty() {
                     if !params.is_empty() {
                         self.struct_span_err(
                             attrs[0].span,
-                            &format!("trailing attribute after generic parameter"),
+                            "trailing attribute after generic parameter",
                         )
                         .span_label(attrs[0].span, "attributes must go before parameters")
                         .emit();
@@ -202,43 +220,7 @@ impl<'a> Parser<'a> {
                     }
                 ));
             } else if self.check_type() {
-                // Parse optional `for<'a, 'b>`.
-                // This `for` is parsed greedily and applies to the whole predicate,
-                // the bounded type can have its own `for` applying only to it.
-                // Examples:
-                // * `for<'a> Trait1<'a>: Trait2<'a /* ok */>`
-                // * `(for<'a> Trait1<'a>): Trait2<'a /* not ok */>`
-                // * `for<'a> for<'b> Trait1<'a, 'b>: Trait2<'a /* ok */, 'b /* not ok */>`
-                let lifetime_defs = self.parse_late_bound_lifetime_defs()?;
-
-                // Parse type with mandatory colon and (possibly empty) bounds,
-                // or with mandatory equality sign and the second type.
-                let ty = self.parse_ty()?;
-                if self.eat(&token::Colon) {
-                    let bounds = self.parse_generic_bounds(Some(self.prev_span))?;
-                    where_clause.predicates.push(ast::WherePredicate::BoundPredicate(
-                        ast::WhereBoundPredicate {
-                            span: lo.to(self.prev_span),
-                            bound_generic_params: lifetime_defs,
-                            bounded_ty: ty,
-                            bounds,
-                        }
-                    ));
-                // FIXME: Decide what should be used here, `=` or `==`.
-                // FIXME: We are just dropping the binders in lifetime_defs on the floor here.
-                } else if self.eat(&token::Eq) || self.eat(&token::EqEq) {
-                    let rhs_ty = self.parse_ty()?;
-                    where_clause.predicates.push(ast::WherePredicate::EqPredicate(
-                        ast::WhereEqPredicate {
-                            span: lo.to(self.prev_span),
-                            lhs_ty: ty,
-                            rhs_ty,
-                            id: ast::DUMMY_NODE_ID,
-                        }
-                    ));
-                } else {
-                    return self.unexpected();
-                }
+                where_clause.predicates.push(self.parse_ty_where_predicate()?);
             } else {
                 break
             }
@@ -252,6 +234,47 @@ impl<'a> Parser<'a> {
         Ok(where_clause)
     }
 
+    fn parse_ty_where_predicate(&mut self) -> PResult<'a, ast::WherePredicate> {
+        let lo = self.token.span;
+        // Parse optional `for<'a, 'b>`.
+        // This `for` is parsed greedily and applies to the whole predicate,
+        // the bounded type can have its own `for` applying only to it.
+        // Examples:
+        // * `for<'a> Trait1<'a>: Trait2<'a /* ok */>`
+        // * `(for<'a> Trait1<'a>): Trait2<'a /* not ok */>`
+        // * `for<'a> for<'b> Trait1<'a, 'b>: Trait2<'a /* ok */, 'b /* not ok */>`
+        let lifetime_defs = self.parse_late_bound_lifetime_defs()?;
+
+        // Parse type with mandatory colon and (possibly empty) bounds,
+        // or with mandatory equality sign and the second type.
+        let ty = self.parse_ty()?;
+        if self.eat(&token::Colon) {
+            let bounds = self.parse_generic_bounds(Some(self.prev_span))?;
+            Ok(ast::WherePredicate::BoundPredicate(
+                ast::WhereBoundPredicate {
+                    span: lo.to(self.prev_span),
+                    bound_generic_params: lifetime_defs,
+                    bounded_ty: ty,
+                    bounds,
+                }
+            ))
+        // FIXME: Decide what should be used here, `=` or `==`.
+        // FIXME: We are just dropping the binders in lifetime_defs on the floor here.
+        } else if self.eat(&token::Eq) || self.eat(&token::EqEq) {
+            let rhs_ty = self.parse_ty()?;
+            Ok(ast::WherePredicate::EqPredicate(
+                ast::WhereEqPredicate {
+                    span: lo.to(self.prev_span),
+                    lhs_ty: ty,
+                    rhs_ty,
+                    id: ast::DUMMY_NODE_ID,
+                }
+            ))
+        } else {
+            self.unexpected()
+        }
+    }
+
     pub(super) fn choose_generics_over_qpath(&self) -> bool {
         // There's an ambiguity between generic parameters and qualified paths in impls.
         // If we see `<` it may start both, so we have to inspect some following tokens.
diff --git a/src/test/ui/parser/assoc-type-in-type-arg.rs b/src/test/ui/parser/assoc-type-in-type-arg.rs
new file mode 100644
index 0000000000000..09765f01371aa
--- /dev/null
+++ b/src/test/ui/parser/assoc-type-in-type-arg.rs
@@ -0,0 +1,11 @@
+trait Tr {
+    type TrSubtype;
+}
+
+struct Bar<'a, Item: Tr, <Item as Tr>::TrSubtype: 'a> {
+    //~^ ERROR associated type bounds do not belong here
+    item: Item,
+    item_sub: &'a <Item as Tr>::TrSubtype,
+}
+
+fn main() {}
diff --git a/src/test/ui/parser/assoc-type-in-type-arg.stderr b/src/test/ui/parser/assoc-type-in-type-arg.stderr
new file mode 100644
index 0000000000000..06addb0241725
--- /dev/null
+++ b/src/test/ui/parser/assoc-type-in-type-arg.stderr
@@ -0,0 +1,8 @@
+error: associated type bounds do not belong here
+  --> $DIR/assoc-type-in-type-arg.rs:5:26
+   |
+LL | struct Bar<'a, Item: Tr, <Item as Tr>::TrSubtype: 'a> {
+   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^ belongs in `where` clause
+
+error: aborting due to previous error
+

From 0f2f16db5364663b4a2f092bba61dc3b056902e3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Esteban=20K=C3=BCber?= <esteban@kuber.com.ar>
Date: Sat, 21 Sep 2019 18:57:37 -0700
Subject: [PATCH 2/2] review comments: wording

---
 src/libsyntax/parse/parser/generics.rs           | 2 +-
 src/test/ui/parser/assoc-type-in-type-arg.rs     | 2 +-
 src/test/ui/parser/assoc-type-in-type-arg.stderr | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/libsyntax/parse/parser/generics.rs b/src/libsyntax/parse/parser/generics.rs
index 1ffdda19c80b5..2ecd9cca3c64b 100644
--- a/src/libsyntax/parse/parser/generics.rs
+++ b/src/libsyntax/parse/parser/generics.rs
@@ -107,7 +107,7 @@ impl<'a> Parser<'a> {
                     Ok(where_predicate) => {
                         self.struct_span_err(
                             where_predicate.span(),
-                            "associated type bounds do not belong here",
+                            "bounds on associated types do not belong here",
                         )
                         .span_label(where_predicate.span(), "belongs in `where` clause")
                         .emit();
diff --git a/src/test/ui/parser/assoc-type-in-type-arg.rs b/src/test/ui/parser/assoc-type-in-type-arg.rs
index 09765f01371aa..000956ea24fad 100644
--- a/src/test/ui/parser/assoc-type-in-type-arg.rs
+++ b/src/test/ui/parser/assoc-type-in-type-arg.rs
@@ -3,7 +3,7 @@ trait Tr {
 }
 
 struct Bar<'a, Item: Tr, <Item as Tr>::TrSubtype: 'a> {
-    //~^ ERROR associated type bounds do not belong here
+    //~^ ERROR bounds on associated types do not belong here
     item: Item,
     item_sub: &'a <Item as Tr>::TrSubtype,
 }
diff --git a/src/test/ui/parser/assoc-type-in-type-arg.stderr b/src/test/ui/parser/assoc-type-in-type-arg.stderr
index 06addb0241725..b637702f21e90 100644
--- a/src/test/ui/parser/assoc-type-in-type-arg.stderr
+++ b/src/test/ui/parser/assoc-type-in-type-arg.stderr
@@ -1,4 +1,4 @@
-error: associated type bounds do not belong here
+error: bounds on associated types do not belong here
   --> $DIR/assoc-type-in-type-arg.rs:5:26
    |
 LL | struct Bar<'a, Item: Tr, <Item as Tr>::TrSubtype: 'a> {