diff options
| author | Matthias Krüger <476013+matthiaskrgr@users.noreply.github.com> | 2025-03-31 14:36:21 +0200 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-03-31 14:36:21 +0200 | 
| commit | 0a579d5247aee300800405669f27e868ae860d38 (patch) | |
| tree | 0936a4cf3237df4d74047affd619612145c19800 /compiler/rustc_parse/src/parser/expr.rs | |
| parent | e15161d528f4d14c998f8023526e595f468bd5b0 (diff) | |
| parent | dbda7d44b8c6fcb737f790986a4884f4bbd6b126 (diff) | |
| download | rust-0a579d5247aee300800405669f27e868ae860d38.tar.gz rust-0a579d5247aee300800405669f27e868ae860d38.zip | |
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
Diffstat (limited to 'compiler/rustc_parse/src/parser/expr.rs')
| -rw-r--r-- | compiler/rustc_parse/src/parser/expr.rs | 61 | 
1 files changed, 52 insertions, 9 deletions
| diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 92e83577f1b..00e75cc2c2f 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -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)) { | 
