diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 06b1b1523edbd..59498978948b0 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -827,6 +827,65 @@ impl<'a> Parser<'a> { None } + pub(super) fn recover_closure_body( + &mut self, + mut err: DiagnosticBuilder<'a, ErrorGuaranteed>, + before: token::Token, + prev: token::Token, + token: token::Token, + lo: Span, + decl_hi: Span, + ) -> PResult<'a, P> { + err.span_label(lo.to(decl_hi), "while parsing the body of this closure"); + match before.kind { + token::OpenDelim(Delimiter::Brace) + if !matches!(token.kind, token::OpenDelim(Delimiter::Brace)) => + { + // `{ || () }` should have been `|| { () }` + err.multipart_suggestion( + "you might have meant to open the body of the closure, instead of enclosing \ + the closure in a block", + vec![ + (before.span, String::new()), + (prev.span.shrink_to_hi(), " {".to_string()), + ], + Applicability::MaybeIncorrect, + ); + err.emit(); + self.eat_to_tokens(&[&token::CloseDelim(Delimiter::Brace)]); + } + token::OpenDelim(Delimiter::Parenthesis) + if !matches!(token.kind, token::OpenDelim(Delimiter::Brace)) => + { + // We are within a function call or tuple, we can emit the error + // and recover. + self.eat_to_tokens(&[&token::CloseDelim(Delimiter::Parenthesis), &token::Comma]); + + err.multipart_suggestion_verbose( + "you might have meant to open the body of the closure", + vec![ + (prev.span.shrink_to_hi(), " {".to_string()), + (self.token.span.shrink_to_lo(), "}".to_string()), + ], + Applicability::MaybeIncorrect, + ); + err.emit(); + } + _ if !matches!(token.kind, token::OpenDelim(Delimiter::Brace)) => { + // We don't have a heuristic to correctly identify where the block + // should be closed. + err.multipart_suggestion_verbose( + "you might have meant to open the body of the closure", + vec![(prev.span.shrink_to_hi(), " {".to_string())], + Applicability::HasPlaceholders, + ); + return Err(err); + } + _ => return Err(err), + } + Ok(self.mk_expr_err(lo.to(self.token.span))) + } + /// Eats and discards tokens until one of `kets` is encountered. Respects token trees, /// passes through any errors encountered. Used for error recovery. pub(super) fn eat_to_tokens(&mut self, kets: &[&TokenKind]) { diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 1d883e16f9dc7..a9f497e55e687 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -2209,6 +2209,7 @@ impl<'a> Parser<'a> { fn parse_expr_closure(&mut self) -> PResult<'a, P> { let lo = self.token.span; + let before = self.prev_token.clone(); let binder = if self.check_keyword(kw::For) { let lo = self.token.span; let lifetime_defs = self.parse_late_bound_lifetime_defs()?; @@ -2239,7 +2240,12 @@ impl<'a> Parser<'a> { FnRetTy::Default(_) => { let restrictions = self.restrictions - Restrictions::STMT_EXPR - Restrictions::ALLOW_LET; - self.parse_expr_res(restrictions, None)? + let prev = self.prev_token.clone(); + let token = self.token.clone(); + match self.parse_expr_res(restrictions, None) { + Ok(expr) => expr, + 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). @@ -2459,10 +2465,16 @@ impl<'a> Parser<'a> { /// Parses a `let $pat = $expr` pseudo-expression. fn parse_expr_let(&mut self, restrictions: Restrictions) -> PResult<'a, P> { let is_recovered = if !restrictions.contains(Restrictions::ALLOW_LET) { - Some(self.sess.emit_err(errors::ExpectedExpressionFoundLet { + let err = errors::ExpectedExpressionFoundLet { span: self.token.span, reason: ForbiddenLetReason::OtherForbidden, - })) + }; + if self.prev_token.kind == token::BinOp(token::Or) { + // This was part of a closure, the that part of the parser recover. + return Err(err.into_diagnostic(&self.sess.span_diagnostic)); + } else { + Some(self.sess.emit_err(err)) + } } else { None }; diff --git a/tests/ui/expr/malformed_closure/missing_block_in_fn_call.fixed b/tests/ui/expr/malformed_closure/missing_block_in_fn_call.fixed new file mode 100644 index 0000000000000..b81515cda9ac8 --- /dev/null +++ b/tests/ui/expr/malformed_closure/missing_block_in_fn_call.fixed @@ -0,0 +1,10 @@ +// run-rustfix +fn main() { + let _ = vec![1, 2, 3].into_iter().map(|x| { + let y = x; //~ ERROR expected expression, found `let` statement + y + }); + let _: () = foo(); //~ ERROR mismatched types +} + +fn foo() {} diff --git a/tests/ui/expr/malformed_closure/missing_block_in_fn_call.rs b/tests/ui/expr/malformed_closure/missing_block_in_fn_call.rs new file mode 100644 index 0000000000000..e47ad562fb033 --- /dev/null +++ b/tests/ui/expr/malformed_closure/missing_block_in_fn_call.rs @@ -0,0 +1,10 @@ +// run-rustfix +fn main() { + let _ = vec![1, 2, 3].into_iter().map(|x| + let y = x; //~ ERROR expected expression, found `let` statement + y + ); + let _: () = foo; //~ ERROR mismatched types +} + +fn foo() {} diff --git a/tests/ui/expr/malformed_closure/missing_block_in_fn_call.stderr b/tests/ui/expr/malformed_closure/missing_block_in_fn_call.stderr new file mode 100644 index 0000000000000..fbb7e0e4df55d --- /dev/null +++ b/tests/ui/expr/malformed_closure/missing_block_in_fn_call.stderr @@ -0,0 +1,38 @@ +error: expected expression, found `let` statement + --> $DIR/missing_block_in_fn_call.rs:4:9 + | +LL | let _ = vec![1, 2, 3].into_iter().map(|x| + | --- while parsing the body of this closure +LL | let y = x; + | ^^^ + | + = note: only supported directly in conditions of `if` and `while` expressions +help: you might have meant to open the body of the closure + | +LL ~ let _ = vec![1, 2, 3].into_iter().map(|x| { +LL | let y = x; +LL | y +LL ~ }); + | + +error[E0308]: mismatched types + --> $DIR/missing_block_in_fn_call.rs:7:17 + | +LL | let _: () = foo; + | -- ^^^ expected `()`, found fn item + | | + | expected due to this +... +LL | fn foo() {} + | -------- function `foo` defined here + | + = note: expected unit type `()` + found fn item `fn() {foo}` +help: use parentheses to call this function + | +LL | let _: () = foo(); + | ++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/expr/malformed_closure/missing_block_in_let_binding.rs b/tests/ui/expr/malformed_closure/missing_block_in_let_binding.rs new file mode 100644 index 0000000000000..1ee215d150dbe --- /dev/null +++ b/tests/ui/expr/malformed_closure/missing_block_in_let_binding.rs @@ -0,0 +1,6 @@ +fn main() { + let x = |x| + let y = x; //~ ERROR expected expression, found `let` statement + let _ = () + (); + y +} diff --git a/tests/ui/expr/malformed_closure/missing_block_in_let_binding.stderr b/tests/ui/expr/malformed_closure/missing_block_in_let_binding.stderr new file mode 100644 index 0000000000000..d4640fba96cfe --- /dev/null +++ b/tests/ui/expr/malformed_closure/missing_block_in_let_binding.stderr @@ -0,0 +1,16 @@ +error: expected expression, found `let` statement + --> $DIR/missing_block_in_let_binding.rs:3:9 + | +LL | let x = |x| + | --- while parsing the body of this closure +LL | let y = x; + | ^^^ + | + = note: only supported directly in conditions of `if` and `while` expressions +help: you might have meant to open the body of the closure + | +LL | let x = |x| { + | + + +error: aborting due to previous error + diff --git a/tests/ui/expr/malformed_closure/ruby_style_closure_parse_error.fixed b/tests/ui/expr/malformed_closure/ruby_style_closure_parse_error.fixed new file mode 100644 index 0000000000000..8014dc87c0809 --- /dev/null +++ b/tests/ui/expr/malformed_closure/ruby_style_closure_parse_error.fixed @@ -0,0 +1,9 @@ +// run-rustfix +fn main() { + let _ = vec![1, 2, 3].into_iter().map(|x| { + let y = x; //~ ERROR expected expression, found `let` statement + y + }); + let _: () = foo(); //~ ERROR mismatched types +} +fn foo() {} diff --git a/tests/ui/expr/malformed_closure/ruby_style_closure_parse_error.rs b/tests/ui/expr/malformed_closure/ruby_style_closure_parse_error.rs new file mode 100644 index 0000000000000..9e4aca888ad72 --- /dev/null +++ b/tests/ui/expr/malformed_closure/ruby_style_closure_parse_error.rs @@ -0,0 +1,9 @@ +// run-rustfix +fn main() { + let _ = vec![1, 2, 3].into_iter().map({|x| + let y = x; //~ ERROR expected expression, found `let` statement + y + }); + let _: () = foo; //~ ERROR mismatched types +} +fn foo() {} diff --git a/tests/ui/expr/malformed_closure/ruby_style_closure_parse_error.stderr b/tests/ui/expr/malformed_closure/ruby_style_closure_parse_error.stderr new file mode 100644 index 0000000000000..5fc48d73b14d8 --- /dev/null +++ b/tests/ui/expr/malformed_closure/ruby_style_closure_parse_error.stderr @@ -0,0 +1,36 @@ +error: expected expression, found `let` statement + --> $DIR/ruby_style_closure_parse_error.rs:4:9 + | +LL | let _ = vec![1, 2, 3].into_iter().map({|x| + | --- while parsing the body of this closure +LL | let y = x; + | ^^^ + | + = note: only supported directly in conditions of `if` and `while` expressions +help: you might have meant to open the body of the closure, instead of enclosing the closure in a block + | +LL - let _ = vec![1, 2, 3].into_iter().map({|x| +LL + let _ = vec![1, 2, 3].into_iter().map(|x| { + | + +error[E0308]: mismatched types + --> $DIR/ruby_style_closure_parse_error.rs:7:17 + | +LL | let _: () = foo; + | -- ^^^ expected `()`, found fn item + | | + | expected due to this +LL | } +LL | fn foo() {} + | -------- function `foo` defined here + | + = note: expected unit type `()` + found fn item `fn() {foo}` +help: use parentheses to call this function + | +LL | let _: () = foo(); + | ++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/or-patterns/or-patterns-syntactic-fail.stderr b/tests/ui/or-patterns/or-patterns-syntactic-fail.stderr index c16a90368e194..e09194d5d3976 100644 --- a/tests/ui/or-patterns/or-patterns-syntactic-fail.stderr +++ b/tests/ui/or-patterns/or-patterns-syntactic-fail.stderr @@ -2,9 +2,15 @@ error: expected identifier, found `:` --> $DIR/or-patterns-syntactic-fail.rs:11:19 | LL | let _ = |A | B: E| (); - | ^ expected identifier + | ---- ^ expected identifier + | | + | while parsing the body of this closure | = note: type ascription syntax has been removed, see issue #101728 +help: you might have meant to open the body of the closure + | +LL | let _ = |A | { B: E| (); + | + error: top-level or-patterns are not allowed in function parameters --> $DIR/or-patterns-syntactic-fail.rs:18:13 diff --git a/tests/ui/parser/issues/issue-32505.rs b/tests/ui/parser/issues/issue-32505.rs index f31c00e5cc3fb..d95e7dc7d9eff 100644 --- a/tests/ui/parser/issues/issue-32505.rs +++ b/tests/ui/parser/issues/issue-32505.rs @@ -1,5 +1,6 @@ pub fn test() { foo(|_|) //~ ERROR expected expression, found `)` + //~^ ERROR cannot find function `foo` in this scope } fn main() { } diff --git a/tests/ui/parser/issues/issue-32505.stderr b/tests/ui/parser/issues/issue-32505.stderr index cdd779a93ef91..27ad2c3e5be11 100644 --- a/tests/ui/parser/issues/issue-32505.stderr +++ b/tests/ui/parser/issues/issue-32505.stderr @@ -2,7 +2,21 @@ error: expected expression, found `)` --> $DIR/issue-32505.rs:2:12 | LL | foo(|_|) - | ^ expected expression + | ---^ expected expression + | | + | while parsing the body of this closure + | +help: you might have meant to open the body of the closure + | +LL | foo(|_| {}) + | ++ + +error[E0425]: cannot find function `foo` in this scope + --> $DIR/issue-32505.rs:2:5 + | +LL | foo(|_|) + | ^^^ not found in this scope -error: aborting due to previous error +error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0425`.