diff --git a/src/libsyntax/parse/parser/generics.rs b/src/libsyntax/parse/parser/generics.rs index 3e6118ad86f..1ffdda19c80 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 00000000000..09765f01371 --- /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, ::TrSubtype: 'a> { + //~^ ERROR associated type bounds do not belong here + item: Item, + item_sub: &'a ::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 00000000000..06addb02417 --- /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, ::TrSubtype: 'a> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ belongs in `where` clause + +error: aborting due to previous error +