Skip to content

Commit ba038cd

Browse files
committed
Auto merge of rust-lang#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 rust-lang#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 rust-lang#138748 r? `@fmease` or reassign if you're busy/don't wanna review this
2 parents 7bfd952 + dbda7d4 commit ba038cd

File tree

4 files changed

+101
-16
lines changed

4 files changed

+101
-16
lines changed

compiler/rustc_parse/src/errors.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -810,16 +810,16 @@ pub(crate) enum WrapInParentheses {
810810

811811
#[derive(Diagnostic)]
812812
#[diag(parse_array_brackets_instead_of_braces)]
813-
pub(crate) struct ArrayBracketsInsteadOfSpaces {
813+
pub(crate) struct ArrayBracketsInsteadOfBraces {
814814
#[primary_span]
815815
pub span: Span,
816816
#[subdiagnostic]
817-
pub sub: ArrayBracketsInsteadOfSpacesSugg,
817+
pub sub: ArrayBracketsInsteadOfBracesSugg,
818818
}
819819

820820
#[derive(Subdiagnostic)]
821821
#[multipart_suggestion(parse_suggestion, applicability = "maybe-incorrect")]
822-
pub(crate) struct ArrayBracketsInsteadOfSpacesSugg {
822+
pub(crate) struct ArrayBracketsInsteadOfBracesSugg {
823823
#[suggestion_part(code = "[")]
824824
pub left: Span,
825825
#[suggestion_part(code = "]")]

compiler/rustc_parse/src/parser/expr.rs

+52-9
Original file line numberDiff line numberDiff line change
@@ -2200,7 +2200,9 @@ impl<'a> Parser<'a> {
22002200
}
22012201

22022202
fn is_array_like_block(&mut self) -> bool {
2203-
self.look_ahead(1, |t| matches!(t.kind, TokenKind::Ident(..) | TokenKind::Literal(_)))
2203+
matches!(self.token.kind, TokenKind::OpenDelim(Delimiter::Brace))
2204+
&& self
2205+
.look_ahead(1, |t| matches!(t.kind, TokenKind::Ident(..) | TokenKind::Literal(_)))
22042206
&& self.look_ahead(2, |t| t == &token::Comma)
22052207
&& self.look_ahead(3, |t| t.can_begin_expr())
22062208
}
@@ -2212,9 +2214,9 @@ impl<'a> Parser<'a> {
22122214
let mut snapshot = self.create_snapshot_for_diagnostic();
22132215
match snapshot.parse_expr_array_or_repeat(exp!(CloseBrace)) {
22142216
Ok(arr) => {
2215-
let guar = self.dcx().emit_err(errors::ArrayBracketsInsteadOfSpaces {
2217+
let guar = self.dcx().emit_err(errors::ArrayBracketsInsteadOfBraces {
22162218
span: arr.span,
2217-
sub: errors::ArrayBracketsInsteadOfSpacesSugg {
2219+
sub: errors::ArrayBracketsInsteadOfBracesSugg {
22182220
left: lo,
22192221
right: snapshot.prev_token.span,
22202222
},
@@ -2337,7 +2339,8 @@ impl<'a> Parser<'a> {
23372339
let capture_clause = self.parse_capture_clause()?;
23382340
let (fn_decl, fn_arg_span) = self.parse_fn_block_decl()?;
23392341
let decl_hi = self.prev_token.span;
2340-
let mut body = match fn_decl.output {
2342+
let mut body = match &fn_decl.output {
2343+
// No return type.
23412344
FnRetTy::Default(_) => {
23422345
let restrictions =
23432346
self.restrictions - Restrictions::STMT_EXPR - Restrictions::ALLOW_LET;
@@ -2349,11 +2352,8 @@ impl<'a> Parser<'a> {
23492352
Err(err) => self.recover_closure_body(err, before, prev, token, lo, decl_hi)?,
23502353
}
23512354
}
2352-
_ => {
2353-
// If an explicit return type is given, require a block to appear (RFC 968).
2354-
let body_lo = self.token.span;
2355-
self.parse_expr_block(None, body_lo, BlockCheckMode::Default)?
2356-
}
2355+
// Explicit return type (`->`) needs block `-> T { }`.
2356+
FnRetTy::Ty(ty) => self.parse_closure_block_body(ty.span)?,
23572357
};
23582358

23592359
match coroutine_kind {
@@ -2405,6 +2405,49 @@ impl<'a> Parser<'a> {
24052405
Ok(closure)
24062406
}
24072407

2408+
/// If an explicit return type is given, require a block to appear (RFC 968).
2409+
fn parse_closure_block_body(&mut self, ret_span: Span) -> PResult<'a, P<Expr>> {
2410+
if self.may_recover()
2411+
&& self.token.can_begin_expr()
2412+
&& !matches!(self.token.kind, TokenKind::OpenDelim(Delimiter::Brace))
2413+
&& !self.token.is_whole_block()
2414+
{
2415+
let snapshot = self.create_snapshot_for_diagnostic();
2416+
let restrictions =
2417+
self.restrictions - Restrictions::STMT_EXPR - Restrictions::ALLOW_LET;
2418+
let tok = self.token.clone();
2419+
match self.parse_expr_res(restrictions, AttrWrapper::empty()) {
2420+
Ok((expr, _)) => {
2421+
let descr = super::token_descr(&tok);
2422+
let mut diag = self
2423+
.dcx()
2424+
.struct_span_err(tok.span, format!("expected `{{`, found {descr}"));
2425+
diag.span_label(
2426+
ret_span,
2427+
"explicit return type requires closure body to be enclosed in braces",
2428+
);
2429+
diag.multipart_suggestion_verbose(
2430+
"wrap the expression in curly braces",
2431+
vec![
2432+
(expr.span.shrink_to_lo(), "{ ".to_string()),
2433+
(expr.span.shrink_to_hi(), " }".to_string()),
2434+
],
2435+
Applicability::MachineApplicable,
2436+
);
2437+
diag.emit();
2438+
return Ok(expr);
2439+
}
2440+
Err(diag) => {
2441+
diag.cancel();
2442+
self.restore_snapshot(snapshot);
2443+
}
2444+
}
2445+
}
2446+
2447+
let body_lo = self.token.span;
2448+
self.parse_expr_block(None, body_lo, BlockCheckMode::Default)
2449+
}
2450+
24082451
/// Parses an optional `move` or `use` prefix to a closure-like construct.
24092452
fn parse_capture_clause(&mut self) -> PResult<'a, CaptureBy> {
24102453
if self.eat_keyword(exp!(Move)) {
+15-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,21 @@
11
// Test that we cannot parse a closure with an explicit return type
22
// unless it uses braces.
33

4-
fn main() {
4+
fn needs_braces_1() {
55
let x = || -> i32 22;
66
//~^ ERROR expected `{`, found `22`
77
}
8+
9+
// Check other delimiters too.
10+
11+
fn needs_braces_2() {
12+
let x = || -> (i32, i32) (1, 2);
13+
//~^ ERROR expected `{`, found `(`
14+
}
15+
16+
fn needs_braces_3() {
17+
let c = || -> [i32; 2] [1, 2];
18+
//~^ ERROR expected `{`, found `[`
19+
}
20+
21+
fn main() {}

tests/ui/parser/closure-return-syntax.stderr

+31-3
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,40 @@ error: expected `{`, found `22`
22
--> $DIR/closure-return-syntax.rs:5:23
33
|
44
LL | let x = || -> i32 22;
5-
| ^^ expected `{`
5+
| --- ^^
6+
| |
7+
| explicit return type requires closure body to be enclosed in braces
68
|
7-
help: you might have meant to write this as part of a block
9+
help: wrap the expression in curly braces
810
|
911
LL | let x = || -> i32 { 22 };
1012
| + +
1113

12-
error: aborting due to 1 previous error
14+
error: expected `{`, found `(`
15+
--> $DIR/closure-return-syntax.rs:12:34
16+
|
17+
LL | let x = || -> (i32, i32) (1, 2);
18+
| ---------- ^
19+
| |
20+
| explicit return type requires closure body to be enclosed in braces
21+
|
22+
help: wrap the expression in curly braces
23+
|
24+
LL | let x = || -> (i32, i32) { (1, 2) };
25+
| + +
26+
27+
error: expected `{`, found `[`
28+
--> $DIR/closure-return-syntax.rs:17:32
29+
|
30+
LL | let c = || -> [i32; 2] [1, 2];
31+
| -------- ^
32+
| |
33+
| explicit return type requires closure body to be enclosed in braces
34+
|
35+
help: wrap the expression in curly braces
36+
|
37+
LL | let c = || -> [i32; 2] { [1, 2] };
38+
| + +
39+
40+
error: aborting due to 3 previous errors
1341

0 commit comments

Comments
 (0)