Suggest expressions that look like const generic arguments should be enclosed in brackets

Co-Authored-By: Esteban Kuber <github@kuber.com.ar>
This commit is contained in:
varkor 2020-10-03 19:30:32 +01:00
parent 1d2726726f
commit ac1454001c
19 changed files with 782 additions and 35 deletions

View file

@ -397,6 +397,13 @@ impl<'a> Parser<'a> {
while let Some(arg) = self.parse_angle_arg()? {
args.push(arg);
if !self.eat(&token::Comma) {
if !self.token.kind.should_end_const_arg() {
if self.handle_ambiguous_unbraced_const_arg(&mut args)? {
// We've managed to (partially) recover, so continue trying to parse
// arguments.
continue;
}
}
break;
}
}
@ -476,41 +483,50 @@ impl<'a> Parser<'a> {
Ok(self.mk_ty(span, ast::TyKind::Err))
}
/// We do not permit arbitrary expressions as const arguments. They must be one of:
/// - An expression surrounded in `{}`.
/// - A literal.
/// - A numeric literal prefixed by `-`.
pub(super) fn expr_is_valid_const_arg(&self, expr: &P<rustc_ast::Expr>) -> bool {
match &expr.kind {
ast::ExprKind::Block(_, _) | ast::ExprKind::Lit(_) => true,
ast::ExprKind::Unary(ast::UnOp::Neg, expr) => match &expr.kind {
ast::ExprKind::Lit(_) => true,
_ => false,
},
_ => false,
}
}
/// Parse a generic argument in a path segment.
/// This does not include constraints, e.g., `Item = u8`, which is handled in `parse_angle_arg`.
fn parse_generic_arg(&mut self) -> PResult<'a, Option<GenericArg>> {
let start = self.token.span;
let arg = if self.check_lifetime() && self.look_ahead(1, |t| !t.is_like_plus()) {
// Parse lifetime argument.
GenericArg::Lifetime(self.expect_lifetime())
} else if self.check_const_arg() {
// Parse const argument.
let expr = if let token::OpenDelim(token::Brace) = self.token.kind {
let value = if let token::OpenDelim(token::Brace) = self.token.kind {
self.parse_block_expr(
None,
self.token.span,
BlockCheckMode::Default,
ast::AttrVec::new(),
)?
} else if self.token.is_ident() {
// FIXME(const_generics): to distinguish between idents for types and consts,
// we should introduce a GenericArg::Ident in the AST and distinguish when
// lowering to the HIR. For now, idents for const args are not permitted.
if self.token.is_bool_lit() {
self.parse_literal_maybe_minus()?
} else {
let span = self.token.span;
let msg = "identifiers may currently not be used for const generics";
self.struct_span_err(span, msg).emit();
let block = self.mk_block_err(span);
self.mk_expr(span, ast::ExprKind::Block(block, None), ast::AttrVec::new())
}
} else {
self.parse_literal_maybe_minus()?
self.handle_unambiguous_unbraced_const_arg()?
};
GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value: expr })
GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value })
} else if self.check_type() {
// Parse type argument.
GenericArg::Type(self.parse_ty()?)
match self.parse_ty() {
Ok(ty) => GenericArg::Type(ty),
Err(err) => {
// Try to recover from possible `const` arg without braces.
return self.recover_const_arg(start, err).map(Some);
}
}
} else {
return Ok(None);
};