Parse assoc type bounds in generic params and provide custom diagnostic
This commit is contained in:
parent
9ad1e7c46c
commit
60560bc2a2
3 changed files with 80 additions and 38 deletions
|
@ -100,13 +100,31 @@ impl<'a> Parser<'a> {
|
||||||
} else if self.check_ident() {
|
} else if self.check_ident() {
|
||||||
// Parse type parameter.
|
// Parse type parameter.
|
||||||
params.push(self.parse_ty_param(attrs)?);
|
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 {
|
} else {
|
||||||
// Check for trailing attributes and stop parsing.
|
// Check for trailing attributes and stop parsing.
|
||||||
if !attrs.is_empty() {
|
if !attrs.is_empty() {
|
||||||
if !params.is_empty() {
|
if !params.is_empty() {
|
||||||
self.struct_span_err(
|
self.struct_span_err(
|
||||||
attrs[0].span,
|
attrs[0].span,
|
||||||
&format!("trailing attribute after generic parameter"),
|
"trailing attribute after generic parameter",
|
||||||
)
|
)
|
||||||
.span_label(attrs[0].span, "attributes must go before parameters")
|
.span_label(attrs[0].span, "attributes must go before parameters")
|
||||||
.emit();
|
.emit();
|
||||||
|
@ -202,6 +220,22 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
));
|
));
|
||||||
} else if self.check_type() {
|
} else if self.check_type() {
|
||||||
|
where_clause.predicates.push(self.parse_ty_where_predicate()?);
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self.eat(&token::Comma) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
where_clause.span = lo.to(self.prev_span);
|
||||||
|
Ok(where_clause)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_ty_where_predicate(&mut self) -> PResult<'a, ast::WherePredicate> {
|
||||||
|
let lo = self.token.span;
|
||||||
// Parse optional `for<'a, 'b>`.
|
// Parse optional `for<'a, 'b>`.
|
||||||
// This `for` is parsed greedily and applies to the whole predicate,
|
// This `for` is parsed greedily and applies to the whole predicate,
|
||||||
// the bounded type can have its own `for` applying only to it.
|
// the bounded type can have its own `for` applying only to it.
|
||||||
|
@ -216,40 +250,29 @@ impl<'a> Parser<'a> {
|
||||||
let ty = self.parse_ty()?;
|
let ty = self.parse_ty()?;
|
||||||
if self.eat(&token::Colon) {
|
if self.eat(&token::Colon) {
|
||||||
let bounds = self.parse_generic_bounds(Some(self.prev_span))?;
|
let bounds = self.parse_generic_bounds(Some(self.prev_span))?;
|
||||||
where_clause.predicates.push(ast::WherePredicate::BoundPredicate(
|
Ok(ast::WherePredicate::BoundPredicate(
|
||||||
ast::WhereBoundPredicate {
|
ast::WhereBoundPredicate {
|
||||||
span: lo.to(self.prev_span),
|
span: lo.to(self.prev_span),
|
||||||
bound_generic_params: lifetime_defs,
|
bound_generic_params: lifetime_defs,
|
||||||
bounded_ty: ty,
|
bounded_ty: ty,
|
||||||
bounds,
|
bounds,
|
||||||
}
|
}
|
||||||
));
|
))
|
||||||
// FIXME: Decide what should be used here, `=` or `==`.
|
// FIXME: Decide what should be used here, `=` or `==`.
|
||||||
// FIXME: We are just dropping the binders in lifetime_defs on the floor here.
|
// FIXME: We are just dropping the binders in lifetime_defs on the floor here.
|
||||||
} else if self.eat(&token::Eq) || self.eat(&token::EqEq) {
|
} else if self.eat(&token::Eq) || self.eat(&token::EqEq) {
|
||||||
let rhs_ty = self.parse_ty()?;
|
let rhs_ty = self.parse_ty()?;
|
||||||
where_clause.predicates.push(ast::WherePredicate::EqPredicate(
|
Ok(ast::WherePredicate::EqPredicate(
|
||||||
ast::WhereEqPredicate {
|
ast::WhereEqPredicate {
|
||||||
span: lo.to(self.prev_span),
|
span: lo.to(self.prev_span),
|
||||||
lhs_ty: ty,
|
lhs_ty: ty,
|
||||||
rhs_ty,
|
rhs_ty,
|
||||||
id: ast::DUMMY_NODE_ID,
|
id: ast::DUMMY_NODE_ID,
|
||||||
}
|
}
|
||||||
));
|
))
|
||||||
} else {
|
} else {
|
||||||
return self.unexpected();
|
self.unexpected()
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if !self.eat(&token::Comma) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
where_clause.span = lo.to(self.prev_span);
|
|
||||||
Ok(where_clause)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn choose_generics_over_qpath(&self) -> bool {
|
pub(super) fn choose_generics_over_qpath(&self) -> bool {
|
||||||
|
|
11
src/test/ui/parser/assoc-type-in-type-arg.rs
Normal file
11
src/test/ui/parser/assoc-type-in-type-arg.rs
Normal file
|
@ -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() {}
|
8
src/test/ui/parser/assoc-type-in-type-arg.stderr
Normal file
8
src/test/ui/parser/assoc-type-in-type-arg.stderr
Normal file
|
@ -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
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue