1
Fork 0

Rollup merge of #138749 - compiler-errors:closure-recovery, r=fmease

Fix closure recovery for missing block when return type is specified

Firstly, fix the `is_array_like_block` condition to make sure we're actually recovering a mistyped *block* rather than some other delimited expression. This fixes #138748.

Secondly, split out the recovery of missing braces on a closure body into a separate recovery. Right now, the suggestion `"you might have meant to write this as part of a block"` originates from `suggest_fixes_misparsed_for_loop_head`, which feels kinda brittle and coincidental since AFAICT that recovery wasn't ever really intended to fix this.

We also can make this `MachineApplicable` in this case.

Fixes #138748

r? `@fmease` or reassign if you're busy/don't wanna review this
This commit is contained in:
Matthias Krüger 2025-03-31 14:36:21 +02:00 committed by GitHub
commit 0a579d5247
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 101 additions and 16 deletions

View file

@ -810,16 +810,16 @@ pub(crate) enum WrapInParentheses {
#[derive(Diagnostic)]
#[diag(parse_array_brackets_instead_of_braces)]
pub(crate) struct ArrayBracketsInsteadOfSpaces {
pub(crate) struct ArrayBracketsInsteadOfBraces {
#[primary_span]
pub span: Span,
#[subdiagnostic]
pub sub: ArrayBracketsInsteadOfSpacesSugg,
pub sub: ArrayBracketsInsteadOfBracesSugg,
}
#[derive(Subdiagnostic)]
#[multipart_suggestion(parse_suggestion, applicability = "maybe-incorrect")]
pub(crate) struct ArrayBracketsInsteadOfSpacesSugg {
pub(crate) struct ArrayBracketsInsteadOfBracesSugg {
#[suggestion_part(code = "[")]
pub left: Span,
#[suggestion_part(code = "]")]

View file

@ -2200,7 +2200,9 @@ impl<'a> Parser<'a> {
}
fn is_array_like_block(&mut self) -> bool {
self.look_ahead(1, |t| matches!(t.kind, TokenKind::Ident(..) | TokenKind::Literal(_)))
matches!(self.token.kind, TokenKind::OpenDelim(Delimiter::Brace))
&& self
.look_ahead(1, |t| matches!(t.kind, TokenKind::Ident(..) | TokenKind::Literal(_)))
&& self.look_ahead(2, |t| t == &token::Comma)
&& self.look_ahead(3, |t| t.can_begin_expr())
}
@ -2212,9 +2214,9 @@ impl<'a> Parser<'a> {
let mut snapshot = self.create_snapshot_for_diagnostic();
match snapshot.parse_expr_array_or_repeat(exp!(CloseBrace)) {
Ok(arr) => {
let guar = self.dcx().emit_err(errors::ArrayBracketsInsteadOfSpaces {
let guar = self.dcx().emit_err(errors::ArrayBracketsInsteadOfBraces {
span: arr.span,
sub: errors::ArrayBracketsInsteadOfSpacesSugg {
sub: errors::ArrayBracketsInsteadOfBracesSugg {
left: lo,
right: snapshot.prev_token.span,
},
@ -2337,7 +2339,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;
@ -2349,11 +2352,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 {
@ -2405,6 +2405,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)) {

View file

@ -1,7 +1,21 @@
// Test that we cannot parse a closure with an explicit return type
// unless it uses braces.
fn main() {
fn needs_braces_1() {
let x = || -> i32 22;
//~^ ERROR expected `{`, found `22`
}
// Check other delimiters too.
fn needs_braces_2() {
let x = || -> (i32, i32) (1, 2);
//~^ ERROR expected `{`, found `(`
}
fn needs_braces_3() {
let c = || -> [i32; 2] [1, 2];
//~^ ERROR expected `{`, found `[`
}
fn main() {}

View file

@ -2,12 +2,40 @@ error: expected `{`, found `22`
--> $DIR/closure-return-syntax.rs:5:23
|
LL | let x = || -> i32 22;
| ^^ expected `{`
| --- ^^
| |
| explicit return type requires closure body to be enclosed in braces
|
help: you might have meant to write this as part of a block
help: wrap the expression in curly braces
|
LL | let x = || -> i32 { 22 };
| + +
error: aborting due to 1 previous error
error: expected `{`, found `(`
--> $DIR/closure-return-syntax.rs:12:34
|
LL | let x = || -> (i32, i32) (1, 2);
| ---------- ^
| |
| explicit return type requires closure body to be enclosed in braces
|
help: wrap the expression in curly braces
|
LL | let x = || -> (i32, i32) { (1, 2) };
| + +
error: expected `{`, found `[`
--> $DIR/closure-return-syntax.rs:17:32
|
LL | let c = || -> [i32; 2] [1, 2];
| -------- ^
| |
| explicit return type requires closure body to be enclosed in braces
|
help: wrap the expression in curly braces
|
LL | let c = || -> [i32; 2] { [1, 2] };
| + +
error: aborting due to 3 previous errors