1
Fork 0

Make dedicated recovery for missing braces on closure with return

This commit is contained in:
Michael Goulet 2025-03-20 15:44:44 +00:00
parent f90f43d62b
commit dbda7d44b8
2 changed files with 59 additions and 12 deletions

View file

@ -2329,7 +2329,8 @@ impl<'a> Parser<'a> {
let capture_clause = self.parse_capture_clause()?;
let (fn_decl, fn_arg_span) = self.parse_fn_block_decl()?;
let decl_hi = self.prev_token.span;
let mut body = match fn_decl.output {
let mut body = match &fn_decl.output {
// No return type.
FnRetTy::Default(_) => {
let restrictions =
self.restrictions - Restrictions::STMT_EXPR - Restrictions::ALLOW_LET;
@ -2341,11 +2342,8 @@ impl<'a> Parser<'a> {
Err(err) => self.recover_closure_body(err, before, prev, token, lo, decl_hi)?,
}
}
_ => {
// If an explicit return type is given, require a block to appear (RFC 968).
let body_lo = self.token.span;
self.parse_expr_block(None, body_lo, BlockCheckMode::Default)?
}
// Explicit return type (`->`) needs block `-> T { }`.
FnRetTy::Ty(ty) => self.parse_closure_block_body(ty.span)?,
};
match coroutine_kind {
@ -2397,6 +2395,49 @@ impl<'a> Parser<'a> {
Ok(closure)
}
/// If an explicit return type is given, require a block to appear (RFC 968).
fn parse_closure_block_body(&mut self, ret_span: Span) -> PResult<'a, P<Expr>> {
if self.may_recover()
&& self.token.can_begin_expr()
&& !matches!(self.token.kind, TokenKind::OpenDelim(Delimiter::Brace))
&& !self.token.is_whole_block()
{
let snapshot = self.create_snapshot_for_diagnostic();
let restrictions =
self.restrictions - Restrictions::STMT_EXPR - Restrictions::ALLOW_LET;
let tok = self.token.clone();
match self.parse_expr_res(restrictions, AttrWrapper::empty()) {
Ok((expr, _)) => {
let descr = super::token_descr(&tok);
let mut diag = self
.dcx()
.struct_span_err(tok.span, format!("expected `{{`, found {descr}"));
diag.span_label(
ret_span,
"explicit return type requires closure body to be enclosed in braces",
);
diag.multipart_suggestion_verbose(
"wrap the expression in curly braces",
vec![
(expr.span.shrink_to_lo(), "{ ".to_string()),
(expr.span.shrink_to_hi(), " }".to_string()),
],
Applicability::MachineApplicable,
);
diag.emit();
return Ok(expr);
}
Err(diag) => {
diag.cancel();
self.restore_snapshot(snapshot);
}
}
}
let body_lo = self.token.span;
self.parse_expr_block(None, body_lo, BlockCheckMode::Default)
}
/// Parses an optional `move` or `use` prefix to a closure-like construct.
fn parse_capture_clause(&mut self) -> PResult<'a, CaptureBy> {
if self.eat_keyword(exp!(Move)) {