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<Expr>> {
+        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 91bb2d9eb666f..7172b567a344f 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<Expr>> {
         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<Expr>> {
         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 <https://github.com/rust-lang/rust/issues/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`.