From 89d2600d01dec0fa73e11edc9c5bb435e4ec1285 Mon Sep 17 00:00:00 2001
From: Cameron Steffen <cam.steffen94@gmail.com>
Date: Tue, 22 Jun 2021 13:00:58 -0500
Subject: [PATCH 01/13] Add let-else to AST

---
 compiler/rustc_ast/src/ast.rs                 | 33 +++++++++++++++++--
 compiler/rustc_ast/src/mut_visit.rs           | 13 ++++++--
 compiler/rustc_ast/src/visit.rs               |  5 ++-
 compiler/rustc_ast_lowering/src/lib.rs        |  2 +-
 compiler/rustc_ast_pretty/src/pprust/state.rs | 10 ++++--
 .../src/deriving/debug.rs                     |  4 +--
 compiler/rustc_expand/src/build.rs            |  6 ++--
 compiler/rustc_lint/src/unused.rs             |  2 +-
 compiler/rustc_parse/src/parser/stmt.rs       | 32 ++++++++++++------
 compiler/rustc_resolve/src/late.rs            | 11 +++++--
 10 files changed, 92 insertions(+), 26 deletions(-)

diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index e1ea464dedb1e..0632d937c4c17 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -1005,13 +1005,42 @@ pub struct Local {
     pub id: NodeId,
     pub pat: P<Pat>,
     pub ty: Option<P<Ty>>,
-    /// Initializer expression to set the value, if any.
-    pub init: Option<P<Expr>>,
+    pub kind: LocalKind,
     pub span: Span,
     pub attrs: AttrVec,
     pub tokens: Option<LazyTokenStream>,
 }
 
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub enum LocalKind {
+    /// Local declaration.
+    /// Example: `let x;`
+    Decl,
+    /// Local declaration with an initializer.
+    /// Example: `let x = y;`
+    Init(P<Expr>),
+    /// Local declaration with an initializer and an `else` clause.
+    /// Example: `let Some(x) = y else { return };`
+    InitElse(P<Expr>, P<Block>),
+}
+
+impl LocalKind {
+    pub fn init(&self) -> Option<&Expr> {
+        match self {
+            Self::Decl => None,
+            Self::Init(i) | Self::InitElse(i, _) => Some(i),
+        }
+    }
+
+    pub fn init_else_opt(&self) -> Option<(&Expr, Option<&Block>)> {
+        match self {
+            Self::Decl => None,
+            Self::Init(init) => Some((init, None)),
+            Self::InitElse(init, els) => Some((init, Some(els))),
+        }
+    }
+}
+
 /// An arm of a 'match'.
 ///
 /// E.g., `0..=10 => { println!("match!") }` as in
diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs
index fb4db6005aca5..368a23e34290d 100644
--- a/compiler/rustc_ast/src/mut_visit.rs
+++ b/compiler/rustc_ast/src/mut_visit.rs
@@ -571,11 +571,20 @@ pub fn noop_visit_parenthesized_parameter_data<T: MutVisitor>(
 }
 
 pub fn noop_visit_local<T: MutVisitor>(local: &mut P<Local>, vis: &mut T) {
-    let Local { id, pat, ty, init, span, attrs, tokens } = local.deref_mut();
+    let Local { id, pat, ty, kind, span, attrs, tokens } = local.deref_mut();
     vis.visit_id(id);
     vis.visit_pat(pat);
     visit_opt(ty, |ty| vis.visit_ty(ty));
-    visit_opt(init, |init| vis.visit_expr(init));
+    match kind {
+        LocalKind::Decl => {}
+        LocalKind::Init(init) => {
+            vis.visit_expr(init);
+        }
+        LocalKind::InitElse(init, els) => {
+            vis.visit_expr(init);
+            vis.visit_block(els);
+        }
+    }
     vis.visit_span(span);
     visit_thin_attrs(attrs, vis);
     visit_lazy_tts(tokens, vis);
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index dd8927496e019..c30f711b39707 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -242,7 +242,10 @@ pub fn walk_local<'a, V: Visitor<'a>>(visitor: &mut V, local: &'a Local) {
     }
     visitor.visit_pat(&local.pat);
     walk_list!(visitor, visit_ty, &local.ty);
-    walk_list!(visitor, visit_expr, &local.init);
+    if let Some((init, els)) = local.kind.init_else_opt() {
+        visitor.visit_expr(init);
+        walk_list!(visitor, visit_block, els);
+    }
 }
 
 pub fn walk_label<'a, V: Visitor<'a>>(visitor: &mut V, label: &'a Label) {
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index b71fcb7a349cc..8efb1680d856f 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -1798,7 +1798,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             .ty
             .as_ref()
             .map(|t| self.lower_ty(t, ImplTraitContext::Disallowed(ImplTraitPosition::Binding)));
-        let init = l.init.as_ref().map(|e| self.lower_expr(e));
+        let init = l.kind.init().map(|init| self.lower_expr(init));
         let hir_id = self.lower_node_id(l.id);
         self.lower_attrs(hir_id, &l.attrs);
         hir::Local {
diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs
index 949e7a1fbb815..3cf04be160c64 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state.rs
@@ -1518,13 +1518,19 @@ impl<'a> State<'a> {
                 self.ibox(INDENT_UNIT);
                 self.print_local_decl(loc);
                 self.end();
-                if let Some(ref init) = loc.init {
+                if let Some((init, els)) = loc.kind.init_else_opt() {
                     self.nbsp();
                     self.word_space("=");
                     self.print_expr(init);
+                    if let Some(els) = els {
+                        self.cbox(INDENT_UNIT);
+                        self.ibox(INDENT_UNIT);
+                        self.s.word(" else ");
+                        self.print_block(els);
+                    }
                 }
                 self.s.word(";");
-                self.end();
+                self.end(); // `let` ibox
             }
             ast::StmtKind::Item(ref item) => self.print_item(item),
             ast::StmtKind::Expr(ref expr) => {
diff --git a/compiler/rustc_builtin_macros/src/deriving/debug.rs b/compiler/rustc_builtin_macros/src/deriving/debug.rs
index cc6dac52d7663..14506f296bf95 100644
--- a/compiler/rustc_builtin_macros/src/deriving/debug.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/debug.rs
@@ -3,7 +3,7 @@ use crate::deriving::generic::*;
 use crate::deriving::path_std;
 
 use rustc_ast::ptr::P;
-use rustc_ast::{self as ast, Expr, MetaItem};
+use rustc_ast::{self as ast, Expr, LocalKind, MetaItem};
 use rustc_expand::base::{Annotatable, ExtCtxt};
 use rustc_span::symbol::{sym, Ident};
 use rustc_span::{Span, DUMMY_SP};
@@ -135,8 +135,8 @@ fn stmt_let_underscore(cx: &mut ExtCtxt<'_>, sp: Span, expr: P<ast::Expr>) -> as
     let local = P(ast::Local {
         pat: cx.pat_wild(sp),
         ty: None,
-        init: Some(expr),
         id: ast::DUMMY_NODE_ID,
+        kind: LocalKind::Init(expr),
         span: sp,
         attrs: ast::AttrVec::new(),
         tokens: None,
diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs
index 824df2757ea90..1d83ecbfd404b 100644
--- a/compiler/rustc_expand/src/build.rs
+++ b/compiler/rustc_expand/src/build.rs
@@ -2,7 +2,7 @@ use crate::base::ExtCtxt;
 
 use rustc_ast::attr;
 use rustc_ast::ptr::P;
-use rustc_ast::{self as ast, AttrVec, BlockCheckMode, Expr, PatKind, UnOp};
+use rustc_ast::{self as ast, AttrVec, BlockCheckMode, Expr, LocalKind, PatKind, UnOp};
 use rustc_span::source_map::Spanned;
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
 
@@ -153,8 +153,8 @@ impl<'a> ExtCtxt<'a> {
         let local = P(ast::Local {
             pat,
             ty: None,
-            init: Some(ex),
             id: ast::DUMMY_NODE_ID,
+            kind: LocalKind::Init(ex),
             span: sp,
             attrs: AttrVec::new(),
             tokens: None,
@@ -167,8 +167,8 @@ impl<'a> ExtCtxt<'a> {
         let local = P(ast::Local {
             pat: self.pat_wild(span),
             ty: Some(ty),
-            init: None,
             id: ast::DUMMY_NODE_ID,
+            kind: LocalKind::Decl,
             span,
             attrs: AttrVec::new(),
             tokens: None,
diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs
index be137884b4b27..a289b379fc813 100644
--- a/compiler/rustc_lint/src/unused.rs
+++ b/compiler/rustc_lint/src/unused.rs
@@ -618,7 +618,7 @@ trait UnusedDelimLint {
     fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) {
         match s.kind {
             StmtKind::Local(ref local) if Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX => {
-                if let Some(ref value) = local.init {
+                if let Some(value) = local.kind.init() {
                     self.check_unused_delims_expr(
                         cx,
                         &value,
diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs
index 85515bd2a6314..d083c379f7720 100644
--- a/compiler/rustc_parse/src/parser/stmt.rs
+++ b/compiler/rustc_parse/src/parser/stmt.rs
@@ -11,8 +11,9 @@ use rustc_ast as ast;
 use rustc_ast::ptr::P;
 use rustc_ast::token::{self, TokenKind};
 use rustc_ast::util::classify;
-use rustc_ast::AstLike;
-use rustc_ast::{AttrStyle, AttrVec, Attribute, MacCall, MacCallStmt, MacStmtStyle};
+use rustc_ast::{
+    AstLike, AttrStyle, AttrVec, Attribute, LocalKind, MacCall, MacCallStmt, MacStmtStyle,
+};
 use rustc_ast::{Block, BlockCheckMode, Expr, ExprKind, Local, Stmt};
 use rustc_ast::{StmtKind, DUMMY_NODE_ID};
 use rustc_errors::{Applicability, PResult};
@@ -292,8 +293,19 @@ impl<'a> Parser<'a> {
                 return Err(err);
             }
         };
+        let kind = match init {
+            None => LocalKind::Decl,
+            Some(init) => {
+                if self.eat_keyword(kw::Else) {
+                    let els = self.parse_block()?;
+                    LocalKind::InitElse(init, els)
+                } else {
+                    LocalKind::Init(init)
+                }
+            }
+        };
         let hi = if self.token == token::Semi { self.token.span } else { self.prev_token.span };
-        Ok(P(ast::Local { ty, pat, init, id: DUMMY_NODE_ID, span: lo.to(hi), attrs, tokens: None }))
+        Ok(P(ast::Local { ty, pat, kind, id: DUMMY_NODE_ID, span: lo.to(hi), attrs, tokens: None }))
     }
 
     /// Parses the RHS of a local variable declaration (e.g., `= 14;`).
@@ -495,13 +507,13 @@ impl<'a> Parser<'a> {
             StmtKind::Expr(_) | StmtKind::MacCall(_) => {}
             StmtKind::Local(ref mut local) if let Err(e) = self.expect_semi() => {
                 // We might be at the `,` in `let x = foo<bar, baz>;`. Try to recover.
-                match &mut local.init {
-                    Some(ref mut expr) => {
-                        self.check_mistyped_turbofish_with_multiple_type_params(e, expr)?;
-                        // We found `foo<bar, baz>`, have we fully recovered?
-                        self.expect_semi()?;
-                    }
-                    None => return Err(e),
+                match &mut local.kind {
+                    LocalKind::Init(expr) | LocalKind::InitElse(expr, _) => {
+                            self.check_mistyped_turbofish_with_multiple_type_params(e, expr)?;
+                            // We found `foo<bar, baz>`, have we fully recovered?
+                            self.expect_semi()?;
+                        }
+                        LocalKind::Decl => return Err(e),
                 }
                 eat_semi = false;
             }
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index 0b552aa07f517..5c7b4b028227e 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -454,7 +454,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
             _ => Some((
                 local.pat.span,
                 local.ty.as_ref().map(|ty| ty.span),
-                local.init.as_ref().map(|init| init.span),
+                local.kind.init().map(|init| init.span),
             )),
         };
         let original = replace(&mut self.diagnostic_metadata.current_let_binding, local_spans);
@@ -1426,7 +1426,14 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
         walk_list!(self, visit_ty, &local.ty);
 
         // Resolve the initializer.
-        walk_list!(self, visit_expr, &local.init);
+        if let Some((init, els)) = local.kind.init_else_opt() {
+            self.visit_expr(init);
+
+            // Resolve the `else` block
+            if let Some(els) = els {
+                self.visit_block(els);
+            }
+        }
 
         // Resolve the pattern.
         self.resolve_pattern_top(&local.pat, PatternSource::Let);

From 581abbfc6ddf7a6fcf090fc98fa7e659a6f15622 Mon Sep 17 00:00:00 2001
From: Cameron Steffen <cam.steffen94@gmail.com>
Date: Wed, 4 Aug 2021 13:09:02 -0500
Subject: [PATCH 02/13] Move some methods to block module

---
 compiler/rustc_ast_lowering/src/block.rs | 109 +++++++++++++++++++++++
 compiler/rustc_ast_lowering/src/lib.rs   |  97 +-------------------
 2 files changed, 111 insertions(+), 95 deletions(-)
 create mode 100644 compiler/rustc_ast_lowering/src/block.rs

diff --git a/compiler/rustc_ast_lowering/src/block.rs b/compiler/rustc_ast_lowering/src/block.rs
new file mode 100644
index 0000000000000..223825ad896d4
--- /dev/null
+++ b/compiler/rustc_ast_lowering/src/block.rs
@@ -0,0 +1,109 @@
+use crate::{ImplTraitContext, ImplTraitPosition, LoweringContext};
+use rustc_ast::{Block, BlockCheckMode, Local, Stmt, StmtKind};
+use rustc_hir as hir;
+
+use smallvec::{smallvec, SmallVec};
+
+impl<'a, 'hir> LoweringContext<'a, 'hir> {
+    pub(super) fn lower_block(
+        &mut self,
+        b: &Block,
+        targeted_by_break: bool,
+    ) -> &'hir hir::Block<'hir> {
+        self.arena.alloc(self.lower_block_noalloc(b, targeted_by_break))
+    }
+
+    pub(super) fn lower_block_noalloc(
+        &mut self,
+        b: &Block,
+        targeted_by_break: bool,
+    ) -> hir::Block<'hir> {
+        let (stmts, expr) = match &*b.stmts {
+            [stmts @ .., Stmt { kind: StmtKind::Expr(e), .. }] => (stmts, Some(&*e)),
+            stmts => (stmts, None),
+        };
+        let stmts = self.arena.alloc_from_iter(stmts.iter().flat_map(|stmt| self.lower_stmt(stmt)));
+        let expr = expr.map(|e| self.lower_expr(e));
+        let rules = self.lower_block_check_mode(&b.rules);
+        let hir_id = self.lower_node_id(b.id);
+
+        hir::Block { hir_id, stmts, expr, rules, span: self.lower_span(b.span), targeted_by_break }
+    }
+
+    fn lower_stmt(&mut self, s: &Stmt) -> SmallVec<[hir::Stmt<'hir>; 1]> {
+        let (hir_id, kind) = match s.kind {
+            StmtKind::Local(ref l) => {
+                let l = self.lower_local(l);
+                let hir_id = self.lower_node_id(s.id);
+                self.alias_attrs(hir_id, l.hir_id);
+                return smallvec![hir::Stmt {
+                    hir_id,
+                    kind: hir::StmtKind::Local(self.arena.alloc(l)),
+                    span: self.lower_span(s.span),
+                }];
+            }
+            StmtKind::Item(ref it) => {
+                // Can only use the ID once.
+                let mut id = Some(s.id);
+                return self
+                    .lower_item_id(it)
+                    .into_iter()
+                    .map(|item_id| {
+                        let hir_id = id
+                            .take()
+                            .map(|id| self.lower_node_id(id))
+                            .unwrap_or_else(|| self.next_id());
+
+                        hir::Stmt {
+                            hir_id,
+                            kind: hir::StmtKind::Item(item_id),
+                            span: self.lower_span(s.span),
+                        }
+                    })
+                    .collect();
+            }
+            StmtKind::Expr(ref e) => {
+                let e = self.lower_expr(e);
+                let hir_id = self.lower_node_id(s.id);
+                self.alias_attrs(hir_id, e.hir_id);
+                (hir_id, hir::StmtKind::Expr(e))
+            }
+            StmtKind::Semi(ref e) => {
+                let e = self.lower_expr(e);
+                let hir_id = self.lower_node_id(s.id);
+                self.alias_attrs(hir_id, e.hir_id);
+                (hir_id, hir::StmtKind::Semi(e))
+            }
+            StmtKind::Empty => return smallvec![],
+            StmtKind::MacCall(..) => panic!("shouldn't exist here"),
+        };
+        smallvec![hir::Stmt { hir_id, kind, span: self.lower_span(s.span) }]
+    }
+
+    fn lower_local(&mut self, l: &Local) -> hir::Local<'hir> {
+        let ty = l
+            .ty
+            .as_ref()
+            .map(|t| self.lower_ty(t, ImplTraitContext::Disallowed(ImplTraitPosition::Binding)));
+        let init = l.kind.init().map(|init| self.lower_expr(init));
+        let hir_id = self.lower_node_id(l.id);
+        self.lower_attrs(hir_id, &l.attrs);
+        hir::Local {
+            hir_id,
+            ty,
+            pat: self.lower_pat(&l.pat),
+            init,
+            span: self.lower_span(l.span),
+            source: hir::LocalSource::Normal,
+        }
+    }
+
+    fn lower_block_check_mode(&mut self, b: &BlockCheckMode) -> hir::BlockCheckMode {
+        match *b {
+            BlockCheckMode::Default => hir::BlockCheckMode::DefaultBlock,
+            BlockCheckMode::Unsafe(u) => {
+                hir::BlockCheckMode::UnsafeBlock(self.lower_unsafe_source(u))
+            }
+        }
+    }
+}
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index 8efb1680d856f..dce67bfb5d574 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -64,7 +64,7 @@ use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_span::{Span, DUMMY_SP};
 use rustc_target::spec::abi::Abi;
 
-use smallvec::{smallvec, SmallVec};
+use smallvec::SmallVec;
 use std::collections::BTreeMap;
 use std::mem;
 use tracing::{debug, trace};
@@ -77,6 +77,7 @@ macro_rules! arena_vec {
 }
 
 mod asm;
+mod block;
 mod expr;
 mod item;
 mod pat;
@@ -1793,24 +1794,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         )
     }
 
-    fn lower_local(&mut self, l: &Local) -> hir::Local<'hir> {
-        let ty = l
-            .ty
-            .as_ref()
-            .map(|t| self.lower_ty(t, ImplTraitContext::Disallowed(ImplTraitPosition::Binding)));
-        let init = l.kind.init().map(|init| self.lower_expr(init));
-        let hir_id = self.lower_node_id(l.id);
-        self.lower_attrs(hir_id, &l.attrs);
-        hir::Local {
-            hir_id,
-            ty,
-            pat: self.lower_pat(&l.pat),
-            init,
-            span: self.lower_span(l.span),
-            source: hir::LocalSource::Normal,
-        }
-    }
-
     fn lower_fn_params_to_names(&mut self, decl: &FnDecl) -> &'hir [Ident] {
         // Skip the `...` (`CVarArgs`) trailing arguments from the AST,
         // as they are not explicit in HIR/Ty function signatures.
@@ -2396,23 +2379,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         bounds.iter().map(move |bound| self.lower_param_bound(bound, itctx.reborrow()))
     }
 
-    fn lower_block(&mut self, b: &Block, targeted_by_break: bool) -> &'hir hir::Block<'hir> {
-        self.arena.alloc(self.lower_block_noalloc(b, targeted_by_break))
-    }
-
-    fn lower_block_noalloc(&mut self, b: &Block, targeted_by_break: bool) -> hir::Block<'hir> {
-        let (stmts, expr) = match &*b.stmts {
-            [stmts @ .., Stmt { kind: StmtKind::Expr(e), .. }] => (stmts, Some(&*e)),
-            stmts => (stmts, None),
-        };
-        let stmts = self.arena.alloc_from_iter(stmts.iter().flat_map(|stmt| self.lower_stmt(stmt)));
-        let expr = expr.map(|e| self.lower_expr(e));
-        let rules = self.lower_block_check_mode(&b.rules);
-        let hir_id = self.lower_node_id(b.id);
-
-        hir::Block { hir_id, stmts, expr, rules, span: self.lower_span(b.span), targeted_by_break }
-    }
-
     /// Lowers a block directly to an expression, presuming that it
     /// has no attributes and is not targeted by a `break`.
     fn lower_block_expr(&mut self, b: &Block) -> hir::Expr<'hir> {
@@ -2427,65 +2393,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         })
     }
 
-    fn lower_stmt(&mut self, s: &Stmt) -> SmallVec<[hir::Stmt<'hir>; 1]> {
-        let (hir_id, kind) = match s.kind {
-            StmtKind::Local(ref l) => {
-                let l = self.lower_local(l);
-                let hir_id = self.lower_node_id(s.id);
-                self.alias_attrs(hir_id, l.hir_id);
-                return smallvec![hir::Stmt {
-                    hir_id,
-                    kind: hir::StmtKind::Local(self.arena.alloc(l)),
-                    span: self.lower_span(s.span),
-                }];
-            }
-            StmtKind::Item(ref it) => {
-                // Can only use the ID once.
-                let mut id = Some(s.id);
-                return self
-                    .lower_item_id(it)
-                    .into_iter()
-                    .map(|item_id| {
-                        let hir_id = id
-                            .take()
-                            .map(|id| self.lower_node_id(id))
-                            .unwrap_or_else(|| self.next_id());
-
-                        hir::Stmt {
-                            hir_id,
-                            kind: hir::StmtKind::Item(item_id),
-                            span: self.lower_span(s.span),
-                        }
-                    })
-                    .collect();
-            }
-            StmtKind::Expr(ref e) => {
-                let e = self.lower_expr(e);
-                let hir_id = self.lower_node_id(s.id);
-                self.alias_attrs(hir_id, e.hir_id);
-                (hir_id, hir::StmtKind::Expr(e))
-            }
-            StmtKind::Semi(ref e) => {
-                let e = self.lower_expr(e);
-                let hir_id = self.lower_node_id(s.id);
-                self.alias_attrs(hir_id, e.hir_id);
-                (hir_id, hir::StmtKind::Semi(e))
-            }
-            StmtKind::Empty => return smallvec![],
-            StmtKind::MacCall(..) => panic!("shouldn't exist here"),
-        };
-        smallvec![hir::Stmt { hir_id, kind, span: self.lower_span(s.span) }]
-    }
-
-    fn lower_block_check_mode(&mut self, b: &BlockCheckMode) -> hir::BlockCheckMode {
-        match *b {
-            BlockCheckMode::Default => hir::BlockCheckMode::DefaultBlock,
-            BlockCheckMode::Unsafe(u) => {
-                hir::BlockCheckMode::UnsafeBlock(self.lower_unsafe_source(u))
-            }
-        }
-    }
-
     fn lower_unsafe_source(&mut self, u: UnsafeSource) -> hir::UnsafeSource {
         match u {
             CompilerGenerated => hir::UnsafeSource::CompilerGenerated,

From 120d46e25507bae6ba1a621205388f8b7be106a2 Mon Sep 17 00:00:00 2001
From: Cameron Steffen <cam.steffen94@gmail.com>
Date: Wed, 4 Aug 2021 12:47:58 -0500
Subject: [PATCH 03/13] Refactor lower_stmts

---
 compiler/rustc_ast_lowering/src/block.rs | 111 ++++++++++++-----------
 1 file changed, 56 insertions(+), 55 deletions(-)

diff --git a/compiler/rustc_ast_lowering/src/block.rs b/compiler/rustc_ast_lowering/src/block.rs
index 223825ad896d4..4c650ad3dcbd1 100644
--- a/compiler/rustc_ast_lowering/src/block.rs
+++ b/compiler/rustc_ast_lowering/src/block.rs
@@ -2,7 +2,7 @@ use crate::{ImplTraitContext, ImplTraitPosition, LoweringContext};
 use rustc_ast::{Block, BlockCheckMode, Local, Stmt, StmtKind};
 use rustc_hir as hir;
 
-use smallvec::{smallvec, SmallVec};
+use smallvec::SmallVec;
 
 impl<'a, 'hir> LoweringContext<'a, 'hir> {
     pub(super) fn lower_block(
@@ -18,66 +18,67 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         b: &Block,
         targeted_by_break: bool,
     ) -> hir::Block<'hir> {
-        let (stmts, expr) = match &*b.stmts {
-            [stmts @ .., Stmt { kind: StmtKind::Expr(e), .. }] => (stmts, Some(&*e)),
-            stmts => (stmts, None),
-        };
-        let stmts = self.arena.alloc_from_iter(stmts.iter().flat_map(|stmt| self.lower_stmt(stmt)));
-        let expr = expr.map(|e| self.lower_expr(e));
+        let (stmts, expr) = self.lower_stmts(&b.stmts);
         let rules = self.lower_block_check_mode(&b.rules);
         let hir_id = self.lower_node_id(b.id);
-
         hir::Block { hir_id, stmts, expr, rules, span: self.lower_span(b.span), targeted_by_break }
     }
 
-    fn lower_stmt(&mut self, s: &Stmt) -> SmallVec<[hir::Stmt<'hir>; 1]> {
-        let (hir_id, kind) = match s.kind {
-            StmtKind::Local(ref l) => {
-                let l = self.lower_local(l);
-                let hir_id = self.lower_node_id(s.id);
-                self.alias_attrs(hir_id, l.hir_id);
-                return smallvec![hir::Stmt {
-                    hir_id,
-                    kind: hir::StmtKind::Local(self.arena.alloc(l)),
-                    span: self.lower_span(s.span),
-                }];
-            }
-            StmtKind::Item(ref it) => {
-                // Can only use the ID once.
-                let mut id = Some(s.id);
-                return self
-                    .lower_item_id(it)
-                    .into_iter()
-                    .map(|item_id| {
-                        let hir_id = id
-                            .take()
-                            .map(|id| self.lower_node_id(id))
-                            .unwrap_or_else(|| self.next_id());
-
-                        hir::Stmt {
-                            hir_id,
-                            kind: hir::StmtKind::Item(item_id),
-                            span: self.lower_span(s.span),
-                        }
-                    })
-                    .collect();
-            }
-            StmtKind::Expr(ref e) => {
-                let e = self.lower_expr(e);
-                let hir_id = self.lower_node_id(s.id);
-                self.alias_attrs(hir_id, e.hir_id);
-                (hir_id, hir::StmtKind::Expr(e))
-            }
-            StmtKind::Semi(ref e) => {
-                let e = self.lower_expr(e);
-                let hir_id = self.lower_node_id(s.id);
-                self.alias_attrs(hir_id, e.hir_id);
-                (hir_id, hir::StmtKind::Semi(e))
+    fn lower_stmts(
+        &mut self,
+        mut ast_stmts: &[Stmt],
+    ) -> (&'hir [hir::Stmt<'hir>], Option<&'hir hir::Expr<'hir>>) {
+        let mut stmts = SmallVec::<[hir::Stmt<'hir>; 8]>::new();
+        let mut expr = None;
+        while let [s, tail @ ..] = ast_stmts {
+            match s.kind {
+                StmtKind::Local(ref l) => {
+                    let l = self.lower_local(l);
+                    let hir_id = self.lower_node_id(s.id);
+                    self.alias_attrs(hir_id, l.hir_id);
+                    let kind = hir::StmtKind::Local(self.arena.alloc(l));
+                    let span = self.lower_span(s.span);
+                    stmts.push(hir::Stmt { hir_id, kind, span });
+                }
+                StmtKind::Item(ref it) => {
+                    stmts.extend(self.lower_item_id(it).into_iter().enumerate().map(
+                        |(i, item_id)| {
+                            let hir_id = match i {
+                                0 => self.lower_node_id(s.id),
+                                _ => self.next_id(),
+                            };
+                            let kind = hir::StmtKind::Item(item_id);
+                            let span = self.lower_span(s.span);
+                            hir::Stmt { hir_id, kind, span }
+                        },
+                    ));
+                }
+                StmtKind::Expr(ref e) => {
+                    let e = self.lower_expr(e);
+                    if tail.is_empty() {
+                        expr = Some(e);
+                    } else {
+                        let hir_id = self.lower_node_id(s.id);
+                        self.alias_attrs(hir_id, e.hir_id);
+                        let kind = hir::StmtKind::Expr(e);
+                        let span = self.lower_span(s.span);
+                        stmts.push(hir::Stmt { hir_id, kind, span });
+                    }
+                }
+                StmtKind::Semi(ref e) => {
+                    let e = self.lower_expr(e);
+                    let hir_id = self.lower_node_id(s.id);
+                    self.alias_attrs(hir_id, e.hir_id);
+                    let kind = hir::StmtKind::Semi(e);
+                    let span = self.lower_span(s.span);
+                    stmts.push(hir::Stmt { hir_id, kind, span });
+                }
+                StmtKind::Empty => {}
+                StmtKind::MacCall(..) => panic!("shouldn't exist here"),
             }
-            StmtKind::Empty => return smallvec![],
-            StmtKind::MacCall(..) => panic!("shouldn't exist here"),
-        };
-        smallvec![hir::Stmt { hir_id, kind, span: self.lower_span(s.span) }]
+            ast_stmts = &ast_stmts[1..];
+        }
+        (self.arena.alloc_from_iter(stmts), expr)
     }
 
     fn lower_local(&mut self, l: &Local) -> hir::Local<'hir> {

From ae32e88909350c631a26b3a37a9f07dd9656dba8 Mon Sep 17 00:00:00 2001
From: Cameron Steffen <cam.steffen94@gmail.com>
Date: Sun, 29 Aug 2021 17:48:05 -0500
Subject: [PATCH 04/13] Lower let-else to HIR

---
 compiler/rustc_ast_lowering/src/block.rs | 97 ++++++++++++++++++++----
 compiler/rustc_span/src/hygiene.rs       |  2 +
 2 files changed, 83 insertions(+), 16 deletions(-)

diff --git a/compiler/rustc_ast_lowering/src/block.rs b/compiler/rustc_ast_lowering/src/block.rs
index 4c650ad3dcbd1..f4584e8bbdff1 100644
--- a/compiler/rustc_ast_lowering/src/block.rs
+++ b/compiler/rustc_ast_lowering/src/block.rs
@@ -1,6 +1,8 @@
 use crate::{ImplTraitContext, ImplTraitPosition, LoweringContext};
-use rustc_ast::{Block, BlockCheckMode, Local, Stmt, StmtKind};
+use rustc_ast::{AttrVec, Block, BlockCheckMode, Expr, Local, LocalKind, Stmt, StmtKind};
 use rustc_hir as hir;
+use rustc_span::symbol::Ident;
+use rustc_span::{sym, DesugaringKind};
 
 use smallvec::SmallVec;
 
@@ -32,13 +34,24 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         let mut expr = None;
         while let [s, tail @ ..] = ast_stmts {
             match s.kind {
-                StmtKind::Local(ref l) => {
-                    let l = self.lower_local(l);
+                StmtKind::Local(ref local) => {
                     let hir_id = self.lower_node_id(s.id);
-                    self.alias_attrs(hir_id, l.hir_id);
-                    let kind = hir::StmtKind::Local(self.arena.alloc(l));
-                    let span = self.lower_span(s.span);
-                    stmts.push(hir::Stmt { hir_id, kind, span });
+                    match &local.kind {
+                        LocalKind::InitElse(init, els) => {
+                            let (s, e) = self.lower_let_else(hir_id, local, init, els, tail);
+                            stmts.push(s);
+                            expr = Some(e);
+                            // remaining statements are in let-else expression
+                            break;
+                        }
+                        _ => {
+                            let local = self.lower_local(local);
+                            self.alias_attrs(hir_id, local.hir_id);
+                            let kind = hir::StmtKind::Local(local);
+                            let span = self.lower_span(s.span);
+                            stmts.push(hir::Stmt { hir_id, kind, span });
+                        }
+                    }
                 }
                 StmtKind::Item(ref it) => {
                     stmts.extend(self.lower_item_id(it).into_iter().enumerate().map(
@@ -81,22 +94,18 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         (self.arena.alloc_from_iter(stmts), expr)
     }
 
-    fn lower_local(&mut self, l: &Local) -> hir::Local<'hir> {
+    fn lower_local(&mut self, l: &Local) -> &'hir hir::Local<'hir> {
         let ty = l
             .ty
             .as_ref()
             .map(|t| self.lower_ty(t, ImplTraitContext::Disallowed(ImplTraitPosition::Binding)));
         let init = l.kind.init().map(|init| self.lower_expr(init));
         let hir_id = self.lower_node_id(l.id);
+        let pat = self.lower_pat(&l.pat);
+        let span = self.lower_span(l.span);
+        let source = hir::LocalSource::Normal;
         self.lower_attrs(hir_id, &l.attrs);
-        hir::Local {
-            hir_id,
-            ty,
-            pat: self.lower_pat(&l.pat),
-            init,
-            span: self.lower_span(l.span),
-            source: hir::LocalSource::Normal,
-        }
+        self.arena.alloc(hir::Local { hir_id, ty, pat, init, span, source })
     }
 
     fn lower_block_check_mode(&mut self, b: &BlockCheckMode) -> hir::BlockCheckMode {
@@ -107,4 +116,60 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             }
         }
     }
+
+    fn lower_let_else(
+        &mut self,
+        stmt_hir_id: hir::HirId,
+        local: &Local,
+        init: &Expr,
+        els: &Block,
+        tail: &[Stmt],
+    ) -> (hir::Stmt<'hir>, &'hir hir::Expr<'hir>) {
+        let ty = local
+            .ty
+            .as_ref()
+            .map(|t| self.lower_ty(t, ImplTraitContext::Disallowed(ImplTraitPosition::Binding)));
+        let span = self.lower_span(local.span);
+        let span = self.mark_span_with_reason(DesugaringKind::LetElse, span, None);
+        let init = Some(self.lower_expr(init));
+        let val = Ident::with_dummy_span(sym::val);
+        let (pat, val_id) =
+            self.pat_ident_binding_mode(span, val, hir::BindingAnnotation::Unannotated);
+        let local_hir_id = self.lower_node_id(local.id);
+        self.lower_attrs(local_hir_id, &local.attrs);
+        // first statement which basically exists for the type annotation
+        let stmt = {
+            let local = self.arena.alloc(hir::Local {
+                hir_id: local_hir_id,
+                ty,
+                pat,
+                init,
+                span,
+                source: hir::LocalSource::Normal,
+            });
+            let kind = hir::StmtKind::Local(local);
+            hir::Stmt { hir_id: stmt_hir_id, kind, span }
+        };
+        let let_expr = {
+            let scrutinee = self.expr_ident(span, val, val_id);
+            let let_kind = hir::ExprKind::Let(self.lower_pat(&local.pat), scrutinee, span);
+            self.arena.alloc(self.expr(span, let_kind, AttrVec::new()))
+        };
+        let then_expr = {
+            let (stmts, expr) = self.lower_stmts(tail);
+            let block = self.block_all(span, stmts, expr);
+            self.arena.alloc(self.expr_block(block, AttrVec::new()))
+        };
+        let else_expr = {
+            let block = self.lower_block(els, false);
+            self.arena.alloc(self.expr_block(block, AttrVec::new()))
+        };
+        self.alias_attrs(else_expr.hir_id, local_hir_id);
+        let if_expr = self.arena.alloc(hir::Expr {
+            hir_id: self.next_id(),
+            span,
+            kind: hir::ExprKind::If(let_expr, then_expr, Some(else_expr)),
+        });
+        (stmt, if_expr)
+    }
 }
diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs
index e44a2e96598ce..c22093c5a4288 100644
--- a/compiler/rustc_span/src/hygiene.rs
+++ b/compiler/rustc_span/src/hygiene.rs
@@ -1097,6 +1097,7 @@ pub enum DesugaringKind {
     Async,
     Await,
     ForLoop(ForLoopLoc),
+    LetElse,
 }
 
 /// A location in the desugaring of a `for` loop
@@ -1117,6 +1118,7 @@ impl DesugaringKind {
             DesugaringKind::TryBlock => "`try` block",
             DesugaringKind::OpaqueTy => "`impl Trait`",
             DesugaringKind::ForLoop(_) => "`for` loop",
+            DesugaringKind::LetElse => "`let...else`",
         }
     }
 }

From 960ea093ab3b3b34afd4bf6b755d93184452d6af Mon Sep 17 00:00:00 2001
From: Cameron Steffen <cam.steffen94@gmail.com>
Date: Wed, 23 Jun 2021 16:40:06 -0500
Subject: [PATCH 05/13] Add let_else feature gate

---
 compiler/rustc_ast_lowering/src/block.rs           | 10 ++++++++++
 compiler/rustc_feature/src/active.rs               |  3 +++
 compiler/rustc_span/src/symbol.rs                  |  1 +
 src/test/ui/feature-gates/feature-gate-let_else.rs |  5 +++++
 .../ui/feature-gates/feature-gate-let_else.stderr  | 14 ++++++++++++++
 5 files changed, 33 insertions(+)
 create mode 100644 src/test/ui/feature-gates/feature-gate-let_else.rs
 create mode 100644 src/test/ui/feature-gates/feature-gate-let_else.stderr

diff --git a/compiler/rustc_ast_lowering/src/block.rs b/compiler/rustc_ast_lowering/src/block.rs
index f4584e8bbdff1..ca804ec6758b7 100644
--- a/compiler/rustc_ast_lowering/src/block.rs
+++ b/compiler/rustc_ast_lowering/src/block.rs
@@ -1,6 +1,7 @@
 use crate::{ImplTraitContext, ImplTraitPosition, LoweringContext};
 use rustc_ast::{AttrVec, Block, BlockCheckMode, Expr, Local, LocalKind, Stmt, StmtKind};
 use rustc_hir as hir;
+use rustc_session::parse::feature_err;
 use rustc_span::symbol::Ident;
 use rustc_span::{sym, DesugaringKind};
 
@@ -170,6 +171,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             span,
             kind: hir::ExprKind::If(let_expr, then_expr, Some(else_expr)),
         });
+        if !self.sess.features_untracked().let_else {
+            feature_err(
+                &self.sess.parse_sess,
+                sym::let_else,
+                local.span,
+                "`let...else` statements are unstable",
+            )
+            .emit();
+        }
         (stmt, if_expr)
     }
 }
diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs
index 1ff2c75966ab6..a3807a2bb9fde 100644
--- a/compiler/rustc_feature/src/active.rs
+++ b/compiler/rustc_feature/src/active.rs
@@ -676,6 +676,9 @@ declare_features! (
     /// Allows additional const parameter types, such as `&'static str` or user defined types
     (incomplete, adt_const_params, "1.56.0", Some(44580), None),
 
+    /// Allows `let...else` statements.
+    (active, let_else, "1.56.0", Some(87335), None),
+
     // -------------------------------------------------------------------------
     // feature-group-end: actual feature gates
     // -------------------------------------------------------------------------
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 19c02ba45c447..899c51e12ff1f 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -744,6 +744,7 @@ symbols! {
         le,
         len,
         let_chains,
+        let_else,
         lhs,
         lib,
         libc,
diff --git a/src/test/ui/feature-gates/feature-gate-let_else.rs b/src/test/ui/feature-gates/feature-gate-let_else.rs
new file mode 100644
index 0000000000000..3f04a9dabfd1f
--- /dev/null
+++ b/src/test/ui/feature-gates/feature-gate-let_else.rs
@@ -0,0 +1,5 @@
+fn main() {
+    let Some(x) = Some(1) else { //~ ERROR `let...else` statements are unstable
+        return;
+    };
+}
diff --git a/src/test/ui/feature-gates/feature-gate-let_else.stderr b/src/test/ui/feature-gates/feature-gate-let_else.stderr
new file mode 100644
index 0000000000000..8625260415467
--- /dev/null
+++ b/src/test/ui/feature-gates/feature-gate-let_else.stderr
@@ -0,0 +1,14 @@
+error[E0658]: `let...else` statements are unstable
+  --> $DIR/feature-gate-let_else.rs:2:5
+   |
+LL | /     let Some(x) = Some(1) else {
+LL | |         return;
+LL | |     };
+   | |______^
+   |
+   = note: see issue #87335 <https://github.com/rust-lang/rust/issues/87335> for more information
+   = help: add `#![feature(let_else)]` to the crate attributes to enable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0658`.

From 04d7903304e6b84a9d8a8d22bcefdcfa32418f57 Mon Sep 17 00:00:00 2001
From: Cameron Steffen <cam.steffen94@gmail.com>
Date: Sun, 25 Jul 2021 18:27:44 -0500
Subject: [PATCH 06/13] Fix clippy for let-else

---
 .../clippy/clippy_lints/src/non_expressive_names.rs  |  7 +++++--
 src/tools/clippy/clippy_utils/src/ast_utils.rs       | 12 +++++++++++-
 2 files changed, 16 insertions(+), 3 deletions(-)

diff --git a/src/tools/clippy/clippy_lints/src/non_expressive_names.rs b/src/tools/clippy/clippy_lints/src/non_expressive_names.rs
index ac21eb5275f0f..2ffc00b449d08 100644
--- a/src/tools/clippy/clippy_lints/src/non_expressive_names.rs
+++ b/src/tools/clippy/clippy_lints/src/non_expressive_names.rs
@@ -316,8 +316,11 @@ impl<'a, 'b> SimilarNamesLocalVisitor<'a, 'b> {
 
 impl<'a, 'tcx> Visitor<'tcx> for SimilarNamesLocalVisitor<'a, 'tcx> {
     fn visit_local(&mut self, local: &'tcx Local) {
-        if let Some(ref init) = local.init {
-            self.apply(|this| walk_expr(this, &**init));
+        if let Some((init, els)) = &local.kind.init_else_opt() {
+            self.apply(|this| walk_expr(this, init));
+            if let Some(els) = els {
+                self.apply(|this| walk_block(this, els));
+            }
         }
         // add the pattern after the expression because the bindings aren't available
         // yet in the init
diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs
index 7ea07a15aea51..133f6c29f7d21 100644
--- a/src/tools/clippy/clippy_utils/src/ast_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs
@@ -221,7 +221,7 @@ pub fn eq_stmt(l: &Stmt, r: &Stmt) -> bool {
         (Local(l), Local(r)) => {
             eq_pat(&l.pat, &r.pat)
                 && both(&l.ty, &r.ty, |l, r| eq_ty(l, r))
-                && eq_expr_opt(&l.init, &r.init)
+                && eq_local_kind(&l.kind, &r.kind)
                 && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
         },
         (Item(l), Item(r)) => eq_item(l, r, eq_item_kind),
@@ -234,6 +234,16 @@ pub fn eq_stmt(l: &Stmt, r: &Stmt) -> bool {
     }
 }
 
+pub fn eq_local_kind(l: &LocalKind, r: &LocalKind) -> bool {
+    use LocalKind::*;
+    match (l, r) {
+        (Decl, Decl) => true,
+        (Init(l), Init(r)) => eq_expr(l, r),
+        (InitElse(li, le), InitElse(ri, re)) => eq_expr(li, ri) && eq_block(le, re),
+        _ => false,
+    }
+}
+
 pub fn eq_item<K>(l: &Item<K>, r: &Item<K>, mut eq_kind: impl FnMut(&K, &K) -> bool) -> bool {
     eq_id(l.ident, r.ident)
         && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))

From cb4439a3150308490a00773aa4f2a5ef23586a42 Mon Sep 17 00:00:00 2001
From: Cameron Steffen <cam.steffen94@gmail.com>
Date: Sun, 25 Jul 2021 18:28:08 -0500
Subject: [PATCH 07/13] Temporary fix rustfmt for let-else

---
 src/tools/rustfmt/src/items.rs             | 9 +++++----
 src/tools/rustfmt/tests/source/let_else.rs | 3 +++
 src/tools/rustfmt/tests/target/let_else.rs | 3 +++
 3 files changed, 11 insertions(+), 4 deletions(-)
 create mode 100644 src/tools/rustfmt/tests/source/let_else.rs
 create mode 100644 src/tools/rustfmt/tests/target/let_else.rs

diff --git a/src/tools/rustfmt/src/items.rs b/src/tools/rustfmt/src/items.rs
index 0542358c6e7c5..2483d0570d9ea 100644
--- a/src/tools/rustfmt/src/items.rs
+++ b/src/tools/rustfmt/src/items.rs
@@ -48,7 +48,7 @@ impl Rewrite for ast::Local {
 
         skip_out_of_file_lines_range!(context, self.span);
 
-        if contains_skip(&self.attrs) {
+        if contains_skip(&self.attrs) || matches!(self.kind, ast::LocalKind::InitElse(..)) {
             return None;
         }
 
@@ -97,7 +97,7 @@ impl Rewrite for ast::Local {
                 infix.push_str(&rewrite);
             }
 
-            if self.init.is_some() {
+            if self.kind.init().is_some() {
                 infix.push_str(" =");
             }
 
@@ -106,11 +106,12 @@ impl Rewrite for ast::Local {
 
         result.push_str(&infix);
 
-        if let Some(ref ex) = self.init {
+        if let Some((init, _els)) = self.kind.init_else_opt() {
             // 1 = trailing semicolon;
             let nested_shape = shape.sub_width(1)?;
 
-            result = rewrite_assign_rhs(context, result, &**ex, nested_shape)?;
+            result = rewrite_assign_rhs(context, result, init, nested_shape)?;
+            // todo else
         }
 
         result.push(';');
diff --git a/src/tools/rustfmt/tests/source/let_else.rs b/src/tools/rustfmt/tests/source/let_else.rs
new file mode 100644
index 0000000000000..a6e816fb524b7
--- /dev/null
+++ b/src/tools/rustfmt/tests/source/let_else.rs
@@ -0,0 +1,3 @@
+fn main() {
+    let Some(1) = Some(1) else { return };
+}
diff --git a/src/tools/rustfmt/tests/target/let_else.rs b/src/tools/rustfmt/tests/target/let_else.rs
new file mode 100644
index 0000000000000..a6e816fb524b7
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/let_else.rs
@@ -0,0 +1,3 @@
+fn main() {
+    let Some(1) = Some(1) else { return };
+}

From 2f4e86b9ef457ff7b465e73e3aaabfcaa1d9d8e1 Mon Sep 17 00:00:00 2001
From: Cameron Steffen <cam.steffen94@gmail.com>
Date: Fri, 30 Jul 2021 11:30:12 -0500
Subject: [PATCH 08/13] Enforce diverging let...else

---
 .../src/infer/error_reporting/mod.rs          |  5 +++++
 compiler/rustc_middle/src/traits/mod.rs       |  3 +++
 .../src/traits/error_reporting/suggestions.rs |  9 ++++----
 compiler/rustc_typeck/src/check/expr.rs       | 21 ++++++++++++++++++-
 4 files changed, 33 insertions(+), 5 deletions(-)

diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index 299dcf5f17a87..d54933841fd4e 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -781,6 +781,10 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                     );
                 }
             }
+            ObligationCauseCode::LetElse => {
+                err.help("try adding a diverging expression, such as `return` or `panic!(..)`");
+                err.help("...or use `match` instead of `let...else`");
+            }
             _ => (),
         }
     }
@@ -2592,6 +2596,7 @@ impl<'tcx> ObligationCauseExt<'tcx> for ObligationCause<'tcx> {
             }
             IfExpression { .. } => Error0308("`if` and `else` have incompatible types"),
             IfExpressionWithNoElse => Error0317("`if` may be missing an `else` clause"),
+            LetElse => Error0308("`else` clause of `let...else` does not diverge"),
             MainFunctionType => Error0580("`main` function has wrong type"),
             StartFunctionType => Error0308("`#[start]` function has wrong type"),
             IntrinsicType => Error0308("intrinsic has wrong type"),
diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs
index 676cb7fe41d97..74edb17fe32f1 100644
--- a/compiler/rustc_middle/src/traits/mod.rs
+++ b/compiler/rustc_middle/src/traits/mod.rs
@@ -305,6 +305,9 @@ pub enum ObligationCauseCode<'tcx> {
     /// Intrinsic has wrong type
     IntrinsicType,
 
+    /// A let else block does not diverge
+    LetElse,
+
     /// Method receiver
     MethodReceiver,
 
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
index 40841a6e32d00..db3432b01422f 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -1928,7 +1928,11 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
             | ObligationCauseCode::OpaqueType
             | ObligationCauseCode::MiscObligation
             | ObligationCauseCode::WellFormed(..)
-            | ObligationCauseCode::MatchImpl(..) => {}
+            | ObligationCauseCode::MatchImpl(..)
+            | ObligationCauseCode::ReturnType
+            | ObligationCauseCode::ReturnValue(_)
+            | ObligationCauseCode::BlockTailExpression(_)
+            | ObligationCauseCode::LetElse => {}
             ObligationCauseCode::SliceOrArrayElem => {
                 err.note("slice and array elements must have `Sized` type");
             }
@@ -2338,9 +2342,6 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
                     predicate
                 ));
             }
-            ObligationCauseCode::ReturnType
-            | ObligationCauseCode::ReturnValue(_)
-            | ObligationCauseCode::BlockTailExpression(_) => (),
             ObligationCauseCode::TrivialBound => {
                 err.help("see issue #48214");
                 if tcx.sess.opts.unstable_features.is_nightly_build() {
diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs
index 51c646e500ca3..10f5b000aca1c 100644
--- a/compiler/rustc_typeck/src/check/expr.rs
+++ b/compiler/rustc_typeck/src/check/expr.rs
@@ -849,7 +849,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         coerce.coerce(self, &self.misc(sp), then_expr, then_ty);
 
         if let Some(else_expr) = opt_else_expr {
-            let else_ty = self.check_expr_with_expectation(else_expr, expected);
+            let else_ty = if sp.desugaring_kind() == Some(DesugaringKind::LetElse) {
+                // todo introduce `check_expr_with_expectation(.., Expectation::LetElse)`
+                //   for errors that point to the offending expression rather than the entire block.
+                //   We could use `check_expr_eq_type(.., tcx.types.never)`, but then there is no
+                //   way to detect that the expected type originated from let-else and provide
+                //   a customized error.
+                let else_ty = self.check_expr(else_expr);
+                let cause = self.cause(else_expr.span, ObligationCauseCode::LetElse);
+
+                if let Some(mut err) =
+                    self.demand_eqtype_with_origin(&cause, self.tcx.types.never, else_ty)
+                {
+                    err.emit();
+                    self.tcx.ty_error()
+                } else {
+                    else_ty
+                }
+            } else {
+                self.check_expr_with_expectation(else_expr, expected)
+            };
             let else_diverges = self.diverges.get();
 
             let opt_suggest_box_span =

From 29bc94ff0de00d79aa10c47603701592e1d3e340 Mon Sep 17 00:00:00 2001
From: Cameron Steffen <cam.steffen94@gmail.com>
Date: Fri, 30 Jul 2021 17:12:11 -0500
Subject: [PATCH 09/13] Handle let-else initializer edge case errors

---
 compiler/rustc_ast/src/util/classify.rs | 27 +++++++++++++++
 compiler/rustc_lint/src/unused.rs       | 43 +++++++++++++++--------
 compiler/rustc_parse/src/parser/stmt.rs | 46 +++++++++++++++++++++++++
 3 files changed, 101 insertions(+), 15 deletions(-)

diff --git a/compiler/rustc_ast/src/util/classify.rs b/compiler/rustc_ast/src/util/classify.rs
index 90786520fe802..6ea3db6d30372 100644
--- a/compiler/rustc_ast/src/util/classify.rs
+++ b/compiler/rustc_ast/src/util/classify.rs
@@ -23,3 +23,30 @@ pub fn expr_requires_semi_to_be_stmt(e: &ast::Expr) -> bool {
             | ast::ExprKind::TryBlock(..)
     )
 }
+
+/// If an expression ends with `}`, returns the innermost expression ending in the `}`
+pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<&ast::Expr> {
+    use ast::ExprKind::*;
+
+    loop {
+        match &expr.kind {
+            AddrOf(_, _, e)
+            | Assign(_, e, _)
+            | AssignOp(_, _, e)
+            | Binary(_, _, e)
+            | Box(e)
+            | Break(_, Some(e))
+            | Closure(.., e, _)
+            | Let(_, e, _)
+            | Range(_, Some(e), _)
+            | Ret(Some(e))
+            | Unary(_, e)
+            | Yield(Some(e)) => {
+                expr = e;
+            }
+            Async(..) | Block(..) | ForLoop(..) | If(..) | Loop(..) | Match(..) | Struct(..)
+            | TryBlock(..) | While(..) => break Some(expr),
+            _ => break None,
+        }
+    }
+}
diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs
index a289b379fc813..f04ac8dd9426f 100644
--- a/compiler/rustc_lint/src/unused.rs
+++ b/compiler/rustc_lint/src/unused.rs
@@ -1,7 +1,7 @@
 use crate::Lint;
 use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
 use rustc_ast as ast;
-use rustc_ast::util::parser;
+use rustc_ast::util::{classify, parser};
 use rustc_ast::{ExprKind, StmtKind};
 use rustc_ast_pretty::pprust;
 use rustc_errors::{pluralize, Applicability};
@@ -382,6 +382,7 @@ enum UnusedDelimsCtx {
     FunctionArg,
     MethodArg,
     AssignedValue,
+    AssignedValueLetElse,
     IfCond,
     WhileCond,
     ForIterExpr,
@@ -398,7 +399,9 @@ impl From<UnusedDelimsCtx> for &'static str {
         match ctx {
             UnusedDelimsCtx::FunctionArg => "function argument",
             UnusedDelimsCtx::MethodArg => "method argument",
-            UnusedDelimsCtx::AssignedValue => "assigned value",
+            UnusedDelimsCtx::AssignedValue | UnusedDelimsCtx::AssignedValueLetElse => {
+                "assigned value"
+            }
             UnusedDelimsCtx::IfCond => "`if` condition",
             UnusedDelimsCtx::WhileCond => "`while` condition",
             UnusedDelimsCtx::ForIterExpr => "`for` iterator expression",
@@ -441,14 +444,26 @@ trait UnusedDelimLint {
         right_pos: Option<BytePos>,
     );
 
-    fn is_expr_delims_necessary(inner: &ast::Expr, followed_by_block: bool) -> bool {
+    fn is_expr_delims_necessary(
+        inner: &ast::Expr,
+        followed_by_block: bool,
+        followed_by_else: bool,
+    ) -> bool {
+        if followed_by_else {
+            match inner.kind {
+                ast::ExprKind::Binary(op, ..) if op.node.lazy() => return true,
+                _ if classify::expr_trailing_brace(inner).is_some() => return true,
+                _ => {}
+            }
+        }
+
         // Prevent false-positives in cases like `fn x() -> u8 { ({ 0 } + 1) }`
         let lhs_needs_parens = {
             let mut innermost = inner;
             loop {
                 if let ExprKind::Binary(_, lhs, _rhs) = &innermost.kind {
                     innermost = lhs;
-                    if !rustc_ast::util::classify::expr_requires_semi_to_be_stmt(innermost) {
+                    if !classify::expr_requires_semi_to_be_stmt(innermost) {
                         break true;
                     }
                 } else {
@@ -618,15 +633,12 @@ trait UnusedDelimLint {
     fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) {
         match s.kind {
             StmtKind::Local(ref local) if Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX => {
-                if let Some(value) = local.kind.init() {
-                    self.check_unused_delims_expr(
-                        cx,
-                        &value,
-                        UnusedDelimsCtx::AssignedValue,
-                        false,
-                        None,
-                        None,
-                    );
+                if let Some((init, els)) = local.kind.init_else_opt() {
+                    let ctx = match els {
+                        None => UnusedDelimsCtx::AssignedValue,
+                        Some(_) => UnusedDelimsCtx::AssignedValueLetElse,
+                    };
+                    self.check_unused_delims_expr(cx, init, ctx, false, None, None);
                 }
             }
             StmtKind::Expr(ref expr) => {
@@ -702,7 +714,8 @@ impl UnusedDelimLint for UnusedParens {
     ) {
         match value.kind {
             ast::ExprKind::Paren(ref inner) => {
-                if !Self::is_expr_delims_necessary(inner, followed_by_block)
+                let followed_by_else = ctx == UnusedDelimsCtx::AssignedValueLetElse;
+                if !Self::is_expr_delims_necessary(inner, followed_by_block, followed_by_else)
                     && value.attrs.is_empty()
                     && !value.span.from_expansion()
                     && (ctx != UnusedDelimsCtx::LetScrutineeExpr
@@ -941,7 +954,7 @@ impl UnusedDelimLint for UnusedBraces {
                 // FIXME(const_generics): handle paths when #67075 is fixed.
                 if let [stmt] = inner.stmts.as_slice() {
                     if let ast::StmtKind::Expr(ref expr) = stmt.kind {
-                        if !Self::is_expr_delims_necessary(expr, followed_by_block)
+                        if !Self::is_expr_delims_necessary(expr, followed_by_block, false)
                             && (ctx != UnusedDelimsCtx::AnonConst
                                 || matches!(expr.kind, ast::ExprKind::Lit(_)))
                             && !cx.sess().source_map().is_multiline(value.span)
diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs
index d083c379f7720..068bd36af5524 100644
--- a/compiler/rustc_parse/src/parser/stmt.rs
+++ b/compiler/rustc_parse/src/parser/stmt.rs
@@ -298,6 +298,8 @@ impl<'a> Parser<'a> {
             Some(init) => {
                 if self.eat_keyword(kw::Else) {
                     let els = self.parse_block()?;
+                    self.check_let_else_init_bool_expr(&init);
+                    self.check_let_else_init_trailing_brace(&init);
                     LocalKind::InitElse(init, els)
                 } else {
                     LocalKind::Init(init)
@@ -308,6 +310,50 @@ impl<'a> Parser<'a> {
         Ok(P(ast::Local { ty, pat, kind, id: DUMMY_NODE_ID, span: lo.to(hi), attrs, tokens: None }))
     }
 
+    fn check_let_else_init_bool_expr(&self, init: &ast::Expr) {
+        if let ast::ExprKind::Binary(op, ..) = init.kind {
+            if op.node.lazy() {
+                let suggs = vec![
+                    (init.span.shrink_to_lo(), "(".to_string()),
+                    (init.span.shrink_to_hi(), ")".to_string()),
+                ];
+                self.struct_span_err(
+                    init.span,
+                    &format!(
+                        "a `{}` expression cannot be directly assigned in `let...else`",
+                        op.node.to_string()
+                    ),
+                )
+                .multipart_suggestion(
+                    "wrap the expression in parenthesis",
+                    suggs,
+                    Applicability::MachineApplicable,
+                )
+                .emit();
+            }
+        }
+    }
+
+    fn check_let_else_init_trailing_brace(&self, init: &ast::Expr) {
+        if let Some(trailing) = classify::expr_trailing_brace(init) {
+            let err_span = trailing.span.with_lo(trailing.span.hi() - BytePos(1));
+            let suggs = vec![
+                (trailing.span.shrink_to_lo(), "(".to_string()),
+                (trailing.span.shrink_to_hi(), ")".to_string()),
+            ];
+            self.struct_span_err(
+                err_span,
+                "right curly brace `}` before `else` in a `let...else` statement not allowed",
+            )
+            .multipart_suggestion(
+                "try wrapping the expression in parenthesis",
+                suggs,
+                Applicability::MachineApplicable,
+            )
+            .emit();
+        }
+    }
+
     /// Parses the RHS of a local variable declaration (e.g., `= 14;`).
     fn parse_initializer(&mut self, eq_optional: bool) -> PResult<'a, Option<P<Expr>>> {
         let eq_consumed = match self.token.kind {

From dc028f65688da4d56315bab9f6ed6cfa29e4af84 Mon Sep 17 00:00:00 2001
From: Cameron Steffen <cam.steffen94@gmail.com>
Date: Fri, 27 Aug 2021 12:53:50 -0500
Subject: [PATCH 10/13] Calculate LetSource later

---
 .../src/thir/pattern/check_match.rs           | 82 ++++++++++---------
 1 file changed, 42 insertions(+), 40 deletions(-)

diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
index 50cbe0f71f552..90c451517c03f 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
@@ -118,31 +118,6 @@ impl<'tcx> MatchVisitor<'_, 'tcx> {
         check_for_bindings_named_same_as_variants(self, pat);
     }
 
-    fn let_source(&mut self, pat: &'tcx hir::Pat<'tcx>, _expr: &hir::Expr<'_>) -> LetSource {
-        let hir = self.tcx.hir();
-        let parent = hir.get_parent_node(pat.hir_id);
-        let parent_parent = hir.get_parent_node(parent);
-        let parent_parent_node = hir.get(parent_parent);
-
-        let parent_parent_parent = hir.get_parent_node(parent_parent);
-        let parent_parent_parent_parent = hir.get_parent_node(parent_parent_parent);
-        let parent_parent_parent_parent_node = hir.get(parent_parent_parent_parent);
-
-        if let hir::Node::Expr(hir::Expr {
-            kind: hir::ExprKind::Loop(_, _, hir::LoopSource::While, _),
-            ..
-        }) = parent_parent_parent_parent_node
-        {
-            LetSource::WhileLet
-        } else if let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::If { .. }, .. }) =
-            parent_parent_node
-        {
-            LetSource::IfLet
-        } else {
-            LetSource::GenericLet
-        }
-    }
-
     fn lower_pattern<'p>(
         &self,
         cx: &mut MatchCheckCtxt<'p, 'tcx>,
@@ -172,10 +147,9 @@ impl<'tcx> MatchVisitor<'_, 'tcx> {
 
     fn check_let(&mut self, pat: &'tcx hir::Pat<'tcx>, expr: &hir::Expr<'_>, span: Span) {
         self.check_patterns(pat);
-        let ls = self.let_source(pat, expr);
         let mut cx = self.new_cx(expr.hir_id);
         let tpat = self.lower_pattern(&mut cx, pat, &mut false).0;
-        check_let_reachability(&mut cx, ls, pat.hir_id, &tpat, span);
+        check_let_reachability(&mut cx, pat.hir_id, &tpat, span);
     }
 
     fn check_match(
@@ -192,13 +166,7 @@ impl<'tcx> MatchVisitor<'_, 'tcx> {
             if let Some(hir::Guard::IfLet(ref pat, _)) = arm.guard {
                 self.check_patterns(pat);
                 let tpat = self.lower_pattern(&mut cx, pat, &mut false).0;
-                check_let_reachability(
-                    &mut cx,
-                    LetSource::IfLetGuard,
-                    pat.hir_id,
-                    &tpat,
-                    tpat.span,
-                );
+                check_let_reachability(&mut cx, pat.hir_id, &tpat, tpat.span);
             }
         }
 
@@ -397,7 +365,7 @@ fn unreachable_pattern(tcx: TyCtxt<'_>, span: Span, id: HirId, catchall: Option<
     });
 }
 
-fn irrefutable_let_pattern(id: HirId, ls: LetSource, span: Span, tcx: TyCtxt<'_>) {
+fn irrefutable_let_pattern(tcx: TyCtxt<'_>, id: HirId, span: Span) {
     macro_rules! emit_diag {
         (
             $lint:expr,
@@ -412,7 +380,8 @@ fn irrefutable_let_pattern(id: HirId, ls: LetSource, span: Span, tcx: TyCtxt<'_>
         }};
     }
 
-    tcx.struct_span_lint_hir(IRREFUTABLE_LET_PATTERNS, id, span, |lint| match ls {
+    let source = let_source(tcx, id);
+    tcx.struct_span_lint_hir(IRREFUTABLE_LET_PATTERNS, id, span, |lint| match source {
         LetSource::GenericLet => {
             emit_diag!(lint, "`let`", "`let` is useless", "removing `let`");
         }
@@ -445,7 +414,6 @@ fn irrefutable_let_pattern(id: HirId, ls: LetSource, span: Span, tcx: TyCtxt<'_>
 
 fn check_let_reachability<'p, 'tcx>(
     cx: &mut MatchCheckCtxt<'p, 'tcx>,
-    ls: LetSource,
     pat_id: HirId,
     pat: &'p super::Pat<'tcx>,
     span: Span,
@@ -454,13 +422,13 @@ fn check_let_reachability<'p, 'tcx>(
     let report = compute_match_usefulness(&cx, &arms, pat_id, pat.ty);
 
     report_arm_reachability(&cx, &report, |arm_index, arm_span, arm_hir_id, _| {
-        match ls {
+        match let_source(cx.tcx, pat_id) {
             LetSource::IfLet | LetSource::WhileLet => {
                 match arm_index {
                     // The arm with the user-specified pattern.
                     0 => unreachable_pattern(cx.tcx, arm_span, arm_hir_id, None),
                     // The arm with the wildcard pattern.
-                    1 => irrefutable_let_pattern(pat_id, ls, arm_span, cx.tcx),
+                    1 => irrefutable_let_pattern(cx.tcx, pat_id, arm_span),
                     _ => bug!(),
                 }
             }
@@ -473,7 +441,7 @@ fn check_let_reachability<'p, 'tcx>(
 
     if report.non_exhaustiveness_witnesses.is_empty() {
         // The match is exhaustive, i.e. the `if let` pattern is irrefutable.
-        irrefutable_let_pattern(pat_id, ls, span, cx.tcx);
+        irrefutable_let_pattern(cx.tcx, pat_id, span);
     }
 }
 
@@ -789,3 +757,37 @@ pub enum LetSource {
     IfLetGuard,
     WhileLet,
 }
+
+fn let_source(tcx: TyCtxt<'_>, pat_id: HirId) -> LetSource {
+    let hir = tcx.hir();
+    let parent = hir.get_parent_node(pat_id);
+    match hir.get(parent) {
+        hir::Node::Arm(hir::Arm {
+            guard: Some(hir::Guard::IfLet(&hir::Pat { hir_id, .. }, _)),
+            ..
+        }) if hir_id == pat_id => {
+            return LetSource::IfLetGuard;
+        }
+        _ => {}
+    }
+    let parent_parent = hir.get_parent_node(parent);
+    let parent_parent_node = hir.get(parent_parent);
+
+    let parent_parent_parent = hir.get_parent_node(parent_parent);
+    let parent_parent_parent_parent = hir.get_parent_node(parent_parent_parent);
+    let parent_parent_parent_parent_node = hir.get(parent_parent_parent_parent);
+
+    if let hir::Node::Expr(hir::Expr {
+        kind: hir::ExprKind::Loop(_, _, hir::LoopSource::While, _),
+        ..
+    }) = parent_parent_parent_parent_node
+    {
+        LetSource::WhileLet
+    } else if let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::If { .. }, .. }) =
+        parent_parent_node
+    {
+        LetSource::IfLet
+    } else {
+        LetSource::GenericLet
+    }
+}

From df9a2e0687895731e12f4a2651e8d70acd08872d Mon Sep 17 00:00:00 2001
From: Cameron Steffen <cam.steffen94@gmail.com>
Date: Fri, 30 Jul 2021 17:14:34 -0500
Subject: [PATCH 11/13] Handle irrufutable or unreachable let-else

---
 .../src/thir/pattern/check_match.rs           | 21 ++++++++++++-
 .../usefulness/top-level-alternation.rs       |  3 ++
 .../usefulness/top-level-alternation.stderr   | 30 +++++++++++--------
 3 files changed, 41 insertions(+), 13 deletions(-)

diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
index 90c451517c03f..b34c1e07be71c 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
@@ -17,7 +17,7 @@ use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_session::lint::builtin::BINDINGS_WITH_VARIANT_NAME;
 use rustc_session::lint::builtin::{IRREFUTABLE_LET_PATTERNS, UNREACHABLE_PATTERNS};
 use rustc_session::Session;
-use rustc_span::Span;
+use rustc_span::{DesugaringKind, ExpnKind, Span};
 use std::slice;
 
 crate fn check_match(tcx: TyCtxt<'_>, def_id: DefId) {
@@ -381,6 +381,10 @@ fn irrefutable_let_pattern(tcx: TyCtxt<'_>, id: HirId, span: Span) {
     }
 
     let source = let_source(tcx, id);
+    let span = match source {
+        LetSource::LetElse(span) => span,
+        _ => span,
+    };
     tcx.struct_span_lint_hir(IRREFUTABLE_LET_PATTERNS, id, span, |lint| match source {
         LetSource::GenericLet => {
             emit_diag!(lint, "`let`", "`let` is useless", "removing `let`");
@@ -401,6 +405,14 @@ fn irrefutable_let_pattern(tcx: TyCtxt<'_>, id: HirId, span: Span) {
                 "removing the guard and adding a `let` inside the match arm"
             );
         }
+        LetSource::LetElse(..) => {
+            emit_diag!(
+                lint,
+                "`let...else`",
+                "`else` clause is useless",
+                "removing the `else` clause"
+            );
+        }
         LetSource::WhileLet => {
             emit_diag!(
                 lint,
@@ -755,6 +767,7 @@ pub enum LetSource {
     GenericLet,
     IfLet,
     IfLetGuard,
+    LetElse(Span),
     WhileLet,
 }
 
@@ -768,6 +781,12 @@ fn let_source(tcx: TyCtxt<'_>, pat_id: HirId) -> LetSource {
         }) if hir_id == pat_id => {
             return LetSource::IfLetGuard;
         }
+        hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Let(..), span, .. }) => {
+            let expn_data = span.ctxt().outer_expn_data();
+            if let ExpnKind::Desugaring(DesugaringKind::LetElse) = expn_data.kind {
+                return LetSource::LetElse(expn_data.call_site);
+            }
+        }
         _ => {}
     }
     let parent_parent = hir.get_parent_node(parent);
diff --git a/src/test/ui/pattern/usefulness/top-level-alternation.rs b/src/test/ui/pattern/usefulness/top-level-alternation.rs
index 4b47b978930f3..076de846129e6 100644
--- a/src/test/ui/pattern/usefulness/top-level-alternation.rs
+++ b/src/test/ui/pattern/usefulness/top-level-alternation.rs
@@ -1,3 +1,5 @@
+#![feature(let_else)]
+
 #![deny(unreachable_patterns)]
 
 fn main() {
@@ -53,4 +55,5 @@ fn main() {
         1..=2 => {}, //~ ERROR unreachable pattern
         _ => {},
     }
+    let (0 | 0) = 0 else { return }; //~ ERROR unreachable pattern
 }
diff --git a/src/test/ui/pattern/usefulness/top-level-alternation.stderr b/src/test/ui/pattern/usefulness/top-level-alternation.stderr
index 76bc4f8d0910a..dd5936fdcc429 100644
--- a/src/test/ui/pattern/usefulness/top-level-alternation.stderr
+++ b/src/test/ui/pattern/usefulness/top-level-alternation.stderr
@@ -1,68 +1,74 @@
 error: unreachable pattern
-  --> $DIR/top-level-alternation.rs:4:23
+  --> $DIR/top-level-alternation.rs:6:23
    |
 LL |     while let 0..=2 | 1 = 0 {}
    |                       ^
    |
 note: the lint level is defined here
-  --> $DIR/top-level-alternation.rs:1:9
+  --> $DIR/top-level-alternation.rs:3:9
    |
 LL | #![deny(unreachable_patterns)]
    |         ^^^^^^^^^^^^^^^^^^^^
 
 error: unreachable pattern
-  --> $DIR/top-level-alternation.rs:5:20
+  --> $DIR/top-level-alternation.rs:7:20
    |
 LL |     if let 0..=2 | 1 = 0 {}
    |                    ^
 
 error: unreachable pattern
-  --> $DIR/top-level-alternation.rs:9:15
+  --> $DIR/top-level-alternation.rs:11:15
    |
 LL |             | 0 => {}
    |               ^
 
 error: unreachable pattern
-  --> $DIR/top-level-alternation.rs:14:15
+  --> $DIR/top-level-alternation.rs:16:15
    |
 LL |             | Some(0) => {}
    |               ^^^^^^^
 
 error: unreachable pattern
-  --> $DIR/top-level-alternation.rs:19:9
+  --> $DIR/top-level-alternation.rs:21:9
    |
 LL |         (0, 0) => {}
    |         ^^^^^^
 
 error: unreachable pattern
-  --> $DIR/top-level-alternation.rs:39:9
+  --> $DIR/top-level-alternation.rs:41:9
    |
 LL |         _ => {}
    |         ^
 
 error: unreachable pattern
-  --> $DIR/top-level-alternation.rs:43:9
+  --> $DIR/top-level-alternation.rs:45:9
    |
 LL |         Some(_) => {}
    |         ^^^^^^^
 
 error: unreachable pattern
-  --> $DIR/top-level-alternation.rs:44:9
+  --> $DIR/top-level-alternation.rs:46:9
    |
 LL |         None => {}
    |         ^^^^
 
 error: unreachable pattern
-  --> $DIR/top-level-alternation.rs:49:9
+  --> $DIR/top-level-alternation.rs:51:9
    |
 LL |         None | Some(_) => {}
    |         ^^^^^^^^^^^^^^
 
 error: unreachable pattern
-  --> $DIR/top-level-alternation.rs:53:9
+  --> $DIR/top-level-alternation.rs:55:9
    |
 LL |         1..=2 => {},
    |         ^^^^^
 
-error: aborting due to 10 previous errors
+error: unreachable pattern
+  --> $DIR/top-level-alternation.rs:58:14
+   |
+LL |     let (0 | 0) = 0 else { return };
+   |              ^
+
+error: aborting due to 11 previous errors
 

From 5b95df4bdc330f34213812ad65cae86ced90d80c Mon Sep 17 00:00:00 2001
From: Cameron Steffen <cam.steffen94@gmail.com>
Date: Sun, 1 Aug 2021 19:18:50 -0500
Subject: [PATCH 12/13] Add let-else tests

---
 .../let-else/let-else-bool-binop-init.fixed   |  8 ++++
 .../ui/let-else/let-else-bool-binop-init.rs   |  8 ++++
 .../let-else/let-else-bool-binop-init.stderr  | 24 ++++++++++
 .../let-else/let-else-brace-before-else.fixed | 26 +++++++++++
 .../ui/let-else/let-else-brace-before-else.rs | 26 +++++++++++
 .../let-else-brace-before-else.stderr         | 46 +++++++++++++++++++
 src/test/ui/let-else/let-else-check.rs        | 14 ++++++
 src/test/ui/let-else/let-else-check.stderr    | 14 ++++++
 src/test/ui/let-else/let-else-irrefutable.rs  |  7 +++
 .../ui/let-else/let-else-irrefutable.stderr   | 12 +++++
 .../ui/let-else/let-else-missing-semicolon.rs | 11 +++++
 .../let-else-missing-semicolon.stderr         | 18 ++++++++
 .../ui/let-else/let-else-non-diverging.rs     | 13 ++++++
 .../ui/let-else/let-else-non-diverging.stderr | 44 ++++++++++++++++++
 src/test/ui/let-else/let-else-run-pass.rs     | 35 ++++++++++++++
 src/test/ui/let-else/let-else-scope.rs        |  7 +++
 src/test/ui/let-else/let-else-scope.stderr    |  9 ++++
 17 files changed, 322 insertions(+)
 create mode 100644 src/test/ui/let-else/let-else-bool-binop-init.fixed
 create mode 100644 src/test/ui/let-else/let-else-bool-binop-init.rs
 create mode 100644 src/test/ui/let-else/let-else-bool-binop-init.stderr
 create mode 100644 src/test/ui/let-else/let-else-brace-before-else.fixed
 create mode 100644 src/test/ui/let-else/let-else-brace-before-else.rs
 create mode 100644 src/test/ui/let-else/let-else-brace-before-else.stderr
 create mode 100644 src/test/ui/let-else/let-else-check.rs
 create mode 100644 src/test/ui/let-else/let-else-check.stderr
 create mode 100644 src/test/ui/let-else/let-else-irrefutable.rs
 create mode 100644 src/test/ui/let-else/let-else-irrefutable.stderr
 create mode 100644 src/test/ui/let-else/let-else-missing-semicolon.rs
 create mode 100644 src/test/ui/let-else/let-else-missing-semicolon.stderr
 create mode 100644 src/test/ui/let-else/let-else-non-diverging.rs
 create mode 100644 src/test/ui/let-else/let-else-non-diverging.stderr
 create mode 100644 src/test/ui/let-else/let-else-run-pass.rs
 create mode 100644 src/test/ui/let-else/let-else-scope.rs
 create mode 100644 src/test/ui/let-else/let-else-scope.stderr

diff --git a/src/test/ui/let-else/let-else-bool-binop-init.fixed b/src/test/ui/let-else/let-else-bool-binop-init.fixed
new file mode 100644
index 0000000000000..e47f7f23d7e07
--- /dev/null
+++ b/src/test/ui/let-else/let-else-bool-binop-init.fixed
@@ -0,0 +1,8 @@
+// run-rustfix
+
+#![feature(let_else)]
+
+fn main() {
+    let true = (true && false) else { return }; //~ ERROR a `&&` expression cannot be directly assigned in `let...else`
+    let true = (true || false) else { return }; //~ ERROR a `||` expression cannot be directly assigned in `let...else`
+}
diff --git a/src/test/ui/let-else/let-else-bool-binop-init.rs b/src/test/ui/let-else/let-else-bool-binop-init.rs
new file mode 100644
index 0000000000000..e443fb0d6a376
--- /dev/null
+++ b/src/test/ui/let-else/let-else-bool-binop-init.rs
@@ -0,0 +1,8 @@
+// run-rustfix
+
+#![feature(let_else)]
+
+fn main() {
+    let true = true && false else { return }; //~ ERROR a `&&` expression cannot be directly assigned in `let...else`
+    let true = true || false else { return }; //~ ERROR a `||` expression cannot be directly assigned in `let...else`
+}
diff --git a/src/test/ui/let-else/let-else-bool-binop-init.stderr b/src/test/ui/let-else/let-else-bool-binop-init.stderr
new file mode 100644
index 0000000000000..6551e24cc83d0
--- /dev/null
+++ b/src/test/ui/let-else/let-else-bool-binop-init.stderr
@@ -0,0 +1,24 @@
+error: a `&&` expression cannot be directly assigned in `let...else`
+  --> $DIR/let-else-bool-binop-init.rs:6:16
+   |
+LL |     let true = true && false else { return };
+   |                ^^^^^^^^^^^^^
+   |
+help: wrap the expression in parenthesis
+   |
+LL |     let true = (true && false) else { return };
+   |                +             +
+
+error: a `||` expression cannot be directly assigned in `let...else`
+  --> $DIR/let-else-bool-binop-init.rs:7:16
+   |
+LL |     let true = true || false else { return };
+   |                ^^^^^^^^^^^^^
+   |
+help: wrap the expression in parenthesis
+   |
+LL |     let true = (true || false) else { return };
+   |                +             +
+
+error: aborting due to 2 previous errors
+
diff --git a/src/test/ui/let-else/let-else-brace-before-else.fixed b/src/test/ui/let-else/let-else-brace-before-else.fixed
new file mode 100644
index 0000000000000..fb4fd77791e03
--- /dev/null
+++ b/src/test/ui/let-else/let-else-brace-before-else.fixed
@@ -0,0 +1,26 @@
+// run-rustfix
+
+#![feature(let_else)]
+
+fn main() {
+    let Some(1) = ({ Some(1) }) else {
+        //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
+        return;
+    };
+    let Some(1) = (loop { break Some(1) }) else {
+        //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
+        return;
+    };
+    let 2 = 1 + (match 1 { n => n }) else {
+        //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
+        return;
+    };
+    let Some(1) = (unsafe { unsafe_fn() }) else {
+        //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
+        return;
+    };
+}
+
+unsafe fn unsafe_fn<T>() -> T {
+    unimplemented!();
+}
diff --git a/src/test/ui/let-else/let-else-brace-before-else.rs b/src/test/ui/let-else/let-else-brace-before-else.rs
new file mode 100644
index 0000000000000..c4c5a1ca28bbe
--- /dev/null
+++ b/src/test/ui/let-else/let-else-brace-before-else.rs
@@ -0,0 +1,26 @@
+// run-rustfix
+
+#![feature(let_else)]
+
+fn main() {
+    let Some(1) = { Some(1) } else {
+        //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
+        return;
+    };
+    let Some(1) = loop { break Some(1) } else {
+        //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
+        return;
+    };
+    let 2 = 1 + match 1 { n => n } else {
+        //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
+        return;
+    };
+    let Some(1) = unsafe { unsafe_fn() } else {
+        //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
+        return;
+    };
+}
+
+unsafe fn unsafe_fn<T>() -> T {
+    unimplemented!();
+}
diff --git a/src/test/ui/let-else/let-else-brace-before-else.stderr b/src/test/ui/let-else/let-else-brace-before-else.stderr
new file mode 100644
index 0000000000000..eac029c848b20
--- /dev/null
+++ b/src/test/ui/let-else/let-else-brace-before-else.stderr
@@ -0,0 +1,46 @@
+error: right curly brace `}` before `else` in a `let...else` statement not allowed
+  --> $DIR/let-else-brace-before-else.rs:6:29
+   |
+LL |     let Some(1) = { Some(1) } else {
+   |                             ^
+   |
+help: try wrapping the expression in parenthesis
+   |
+LL |     let Some(1) = ({ Some(1) }) else {
+   |                   +           +
+
+error: right curly brace `}` before `else` in a `let...else` statement not allowed
+  --> $DIR/let-else-brace-before-else.rs:10:40
+   |
+LL |     let Some(1) = loop { break Some(1) } else {
+   |                                        ^
+   |
+help: try wrapping the expression in parenthesis
+   |
+LL |     let Some(1) = (loop { break Some(1) }) else {
+   |                   +                      +
+
+error: right curly brace `}` before `else` in a `let...else` statement not allowed
+  --> $DIR/let-else-brace-before-else.rs:14:34
+   |
+LL |     let 2 = 1 + match 1 { n => n } else {
+   |                                  ^
+   |
+help: try wrapping the expression in parenthesis
+   |
+LL |     let 2 = 1 + (match 1 { n => n }) else {
+   |                 +                  +
+
+error: right curly brace `}` before `else` in a `let...else` statement not allowed
+  --> $DIR/let-else-brace-before-else.rs:18:40
+   |
+LL |     let Some(1) = unsafe { unsafe_fn() } else {
+   |                                        ^
+   |
+help: try wrapping the expression in parenthesis
+   |
+LL |     let Some(1) = (unsafe { unsafe_fn() }) else {
+   |                   +                      +
+
+error: aborting due to 4 previous errors
+
diff --git a/src/test/ui/let-else/let-else-check.rs b/src/test/ui/let-else/let-else-check.rs
new file mode 100644
index 0000000000000..ab763447ef7e1
--- /dev/null
+++ b/src/test/ui/let-else/let-else-check.rs
@@ -0,0 +1,14 @@
+#![feature(let_else)]
+
+#![deny(unused_variables)]
+
+fn main() {
+    // type annotation, attributes
+    #[allow(unused_variables)]
+    let Some(_): Option<u32> = Some(Default::default()) else {
+        let x = 1; // OK
+        return;
+    };
+
+    let x = 1; //~ ERROR unused variable: `x`
+}
diff --git a/src/test/ui/let-else/let-else-check.stderr b/src/test/ui/let-else/let-else-check.stderr
new file mode 100644
index 0000000000000..50e54d320b006
--- /dev/null
+++ b/src/test/ui/let-else/let-else-check.stderr
@@ -0,0 +1,14 @@
+error: unused variable: `x`
+  --> $DIR/let-else-check.rs:13:9
+   |
+LL |     let x = 1;
+   |         ^ help: if this is intentional, prefix it with an underscore: `_x`
+   |
+note: the lint level is defined here
+  --> $DIR/let-else-check.rs:3:9
+   |
+LL | #![deny(unused_variables)]
+   |         ^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/let-else/let-else-irrefutable.rs b/src/test/ui/let-else/let-else-irrefutable.rs
new file mode 100644
index 0000000000000..b1e09a1248ff7
--- /dev/null
+++ b/src/test/ui/let-else/let-else-irrefutable.rs
@@ -0,0 +1,7 @@
+// check-pass
+
+#![feature(let_else)]
+
+fn main() {
+    let x = 1 else { return }; //~ WARN irrefutable `let...else` pattern
+}
diff --git a/src/test/ui/let-else/let-else-irrefutable.stderr b/src/test/ui/let-else/let-else-irrefutable.stderr
new file mode 100644
index 0000000000000..e030c50d45dcf
--- /dev/null
+++ b/src/test/ui/let-else/let-else-irrefutable.stderr
@@ -0,0 +1,12 @@
+warning: irrefutable `let...else` pattern
+  --> $DIR/let-else-irrefutable.rs:6:5
+   |
+LL |     let x = 1 else { return };
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `#[warn(irrefutable_let_patterns)]` on by default
+   = note: this pattern will always match, so the `else` clause is useless
+   = help: consider removing the `else` clause
+
+warning: 1 warning emitted
+
diff --git a/src/test/ui/let-else/let-else-missing-semicolon.rs b/src/test/ui/let-else/let-else-missing-semicolon.rs
new file mode 100644
index 0000000000000..ed9d79f1ebde9
--- /dev/null
+++ b/src/test/ui/let-else/let-else-missing-semicolon.rs
@@ -0,0 +1,11 @@
+#![feature(let_else)]
+
+fn main() {
+    let Some(x) = Some(1) else {
+        return;
+    } //~ ERROR expected `;`, found keyword `let`
+    let _ = "";
+    let Some(x) = Some(1) else {
+        panic!();
+    } //~ ERROR expected `;`, found `}`
+}
diff --git a/src/test/ui/let-else/let-else-missing-semicolon.stderr b/src/test/ui/let-else/let-else-missing-semicolon.stderr
new file mode 100644
index 0000000000000..1818a0b126348
--- /dev/null
+++ b/src/test/ui/let-else/let-else-missing-semicolon.stderr
@@ -0,0 +1,18 @@
+error: expected `;`, found keyword `let`
+  --> $DIR/let-else-missing-semicolon.rs:6:6
+   |
+LL |     }
+   |      ^ help: add `;` here
+LL |     let _ = "";
+   |     --- unexpected token
+
+error: expected `;`, found `}`
+  --> $DIR/let-else-missing-semicolon.rs:10:6
+   |
+LL |     }
+   |      ^ help: add `;` here
+LL | }
+   | - unexpected token
+
+error: aborting due to 2 previous errors
+
diff --git a/src/test/ui/let-else/let-else-non-diverging.rs b/src/test/ui/let-else/let-else-non-diverging.rs
new file mode 100644
index 0000000000000..a1cee335aeeba
--- /dev/null
+++ b/src/test/ui/let-else/let-else-non-diverging.rs
@@ -0,0 +1,13 @@
+#![feature(let_else)]
+
+fn main() {
+    let Some(x) = Some(1) else { //~ ERROR does not diverge
+        Some(2)
+    };
+    let Some(x) = Some(1) else { //~ ERROR does not diverge
+        if 1 == 1 {
+            panic!();
+        }
+    };
+    let Some(x) = Some(1) else { Some(2) }; //~ ERROR does not diverge
+}
diff --git a/src/test/ui/let-else/let-else-non-diverging.stderr b/src/test/ui/let-else/let-else-non-diverging.stderr
new file mode 100644
index 0000000000000..fd5a18ce7ea51
--- /dev/null
+++ b/src/test/ui/let-else/let-else-non-diverging.stderr
@@ -0,0 +1,44 @@
+error[E0308]: `else` clause of `let...else` does not diverge
+  --> $DIR/let-else-non-diverging.rs:12:32
+   |
+LL |     let Some(x) = Some(1) else { Some(2) };
+   |                                ^^^^^^^^^^^ expected `!`, found enum `Option`
+   |
+   = note: expected type `!`
+              found type `Option<{integer}>`
+   = help: try adding a diverging expression, such as `return` or `panic!(..)`
+   = help: ...or use `match` instead of `let...else`
+
+error[E0308]: `else` clause of `let...else` does not diverge
+  --> $DIR/let-else-non-diverging.rs:7:32
+   |
+LL |       let Some(x) = Some(1) else {
+   |  ________________________________^
+LL | |         if 1 == 1 {
+LL | |             panic!();
+LL | |         }
+LL | |     };
+   | |_____^ expected `!`, found `()`
+   |
+   = note: expected type `!`
+              found type `()`
+   = help: try adding a diverging expression, such as `return` or `panic!(..)`
+   = help: ...or use `match` instead of `let...else`
+
+error[E0308]: `else` clause of `let...else` does not diverge
+  --> $DIR/let-else-non-diverging.rs:4:32
+   |
+LL |       let Some(x) = Some(1) else {
+   |  ________________________________^
+LL | |         Some(2)
+LL | |     };
+   | |_____^ expected `!`, found enum `Option`
+   |
+   = note: expected type `!`
+              found type `Option<{integer}>`
+   = help: try adding a diverging expression, such as `return` or `panic!(..)`
+   = help: ...or use `match` instead of `let...else`
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/let-else/let-else-run-pass.rs b/src/test/ui/let-else/let-else-run-pass.rs
new file mode 100644
index 0000000000000..5d96623236dab
--- /dev/null
+++ b/src/test/ui/let-else/let-else-run-pass.rs
@@ -0,0 +1,35 @@
+// run-pass
+
+#![feature(let_else)]
+
+fn main() {
+    #[allow(dead_code)]
+    enum MyEnum {
+        A(String),
+        B { f: String },
+        C,
+    }
+    // ref binding to non-copy value and or-pattern
+    let (MyEnum::A(ref x) | MyEnum::B { f: ref x }) = (MyEnum::B { f: String::new() }) else {
+        panic!();
+    };
+    assert_eq!(x, "");
+
+    // nested let-else
+    let mut x = 1;
+    loop {
+        let 4 = x else {
+            let 3 = x else {
+                x += 1;
+                continue;
+            };
+            break;
+        };
+        panic!();
+    }
+    assert_eq!(x, 3);
+
+    // else return
+    let Some(1) = Some(2) else { return };
+    panic!();
+}
diff --git a/src/test/ui/let-else/let-else-scope.rs b/src/test/ui/let-else/let-else-scope.rs
new file mode 100644
index 0000000000000..f17682db4c3bd
--- /dev/null
+++ b/src/test/ui/let-else/let-else-scope.rs
@@ -0,0 +1,7 @@
+#![feature(let_else)]
+
+fn main() {
+    let Some(x) = Some(2) else {
+        panic!("{}", x); //~ ERROR cannot find value `x` in this scope
+    };
+}
diff --git a/src/test/ui/let-else/let-else-scope.stderr b/src/test/ui/let-else/let-else-scope.stderr
new file mode 100644
index 0000000000000..4b3936eac4bd5
--- /dev/null
+++ b/src/test/ui/let-else/let-else-scope.stderr
@@ -0,0 +1,9 @@
+error[E0425]: cannot find value `x` in this scope
+  --> $DIR/let-else-scope.rs:5:22
+   |
+LL |         panic!("{}", x);
+   |                      ^ not found in this scope
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0425`.

From 3ff1d6bbf427cfb3d504092c93f261b49577170e Mon Sep 17 00:00:00 2001
From: Cameron Steffen <cam.steffen94@gmail.com>
Date: Sun, 1 Aug 2021 19:55:48 -0500
Subject: [PATCH 13/13] Fix tests

---
 src/test/ui/did_you_mean/issue-40396.stderr      |  4 ++--
 .../ui/parser/attr-stmt-expr-attr-bad.stderr     | 16 ++++++++--------
 src/test/ui/parser/issue-72253.rs                |  2 +-
 src/test/ui/parser/issue-72253.stderr            |  4 ++--
 src/test/ui/parser/issue-84117.rs                |  6 +++---
 src/test/ui/parser/issue-84117.stderr            | 12 ++++++------
 src/test/ui/parser/macro/issue-37234.stderr      |  4 ++--
 src/test/ui/parser/missing-semicolon.rs          |  2 +-
 src/test/ui/parser/missing-semicolon.stderr      |  4 ++--
 src/test/ui/parser/range-3.rs                    |  2 +-
 src/test/ui/parser/range-3.stderr                |  4 ++--
 src/test/ui/parser/range-4.rs                    |  2 +-
 src/test/ui/parser/range-4.stderr                |  4 ++--
 13 files changed, 33 insertions(+), 33 deletions(-)

diff --git a/src/test/ui/did_you_mean/issue-40396.stderr b/src/test/ui/did_you_mean/issue-40396.stderr
index f4bc5aef82dc3..d2938435ece31 100644
--- a/src/test/ui/did_you_mean/issue-40396.stderr
+++ b/src/test/ui/did_you_mean/issue-40396.stderr
@@ -31,11 +31,11 @@ help: use `::<...>` instead of `<...>` to specify type or const arguments
 LL |     (0..13).collect::<Vec<i32>();
    |                    ++
 
-error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, or an operator, found `,`
+error: expected one of `!`, `.`, `::`, `;`, `?`, `else`, `{`, or an operator, found `,`
   --> $DIR/issue-40396.rs:11:43
    |
 LL |     let x = std::collections::HashMap<i128, i128>::new();
-   |                                           ^ expected one of 7 possible tokens
+   |                                           ^ expected one of 8 possible tokens
    |
 help: use `::<...>` instead of `<...>` to specify type or const arguments
    |
diff --git a/src/test/ui/parser/attr-stmt-expr-attr-bad.stderr b/src/test/ui/parser/attr-stmt-expr-attr-bad.stderr
index 6dfe7aad6ea64..cec6980c008c4 100644
--- a/src/test/ui/parser/attr-stmt-expr-attr-bad.stderr
+++ b/src/test/ui/parser/attr-stmt-expr-attr-bad.stderr
@@ -12,11 +12,11 @@ error: expected expression, found `]`
 LL | #[cfg(FALSE)] fn e() { let _ = [#[attr]]; }
    |                                        ^ expected expression
 
-error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, or an operator, found `#`
+error: expected one of `!`, `.`, `::`, `;`, `?`, `else`, `{`, or an operator, found `#`
   --> $DIR/attr-stmt-expr-attr-bad.rs:9:35
    |
 LL | #[cfg(FALSE)] fn e() { let _ = foo#[attr](); }
-   |                                   ^ expected one of 7 possible tokens
+   |                                   ^ expected one of 8 possible tokens
 
 error: an inner attribute is not permitted in this context
   --> $DIR/attr-stmt-expr-attr-bad.rs:11:36
@@ -70,11 +70,11 @@ LL | #[cfg(FALSE)] fn e() { let _ = -#![attr] 0; }
    |
    = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them.
 
-error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, or an operator, found `#`
+error: expected one of `!`, `.`, `::`, `;`, `?`, `else`, `{`, or an operator, found `#`
   --> $DIR/attr-stmt-expr-attr-bad.rs:23:34
    |
 LL | #[cfg(FALSE)] fn e() { let _ = x #![attr] as Y; }
-   |                                  ^ expected one of 7 possible tokens
+   |                                  ^ expected one of 8 possible tokens
 
 error: an inner attribute is not permitted in this context
   --> $DIR/attr-stmt-expr-attr-bad.rs:25:35
@@ -372,11 +372,11 @@ error: unexpected token: `#`
 LL | #[cfg(FALSE)] fn e() { let _ = x.#![attr]foo(); }
    |                                  ^
 
-error: expected one of `.`, `;`, `?`, or an operator, found `#`
+error: expected one of `.`, `;`, `?`, `else`, or an operator, found `#`
   --> $DIR/attr-stmt-expr-attr-bad.rs:100:34
    |
 LL | #[cfg(FALSE)] fn e() { let _ = x.#![attr]foo(); }
-   |                                  ^ expected one of `.`, `;`, `?`, or an operator
+   |                                  ^ expected one of `.`, `;`, `?`, `else`, or an operator
 
 error: unexpected token: `#`
   --> $DIR/attr-stmt-expr-attr-bad.rs:103:34
@@ -384,11 +384,11 @@ error: unexpected token: `#`
 LL | #[cfg(FALSE)] fn e() { let _ = x.#[attr]foo(); }
    |                                  ^
 
-error: expected one of `.`, `;`, `?`, or an operator, found `#`
+error: expected one of `.`, `;`, `?`, `else`, or an operator, found `#`
   --> $DIR/attr-stmt-expr-attr-bad.rs:103:34
    |
 LL | #[cfg(FALSE)] fn e() { let _ = x.#[attr]foo(); }
-   |                                  ^ expected one of `.`, `;`, `?`, or an operator
+   |                                  ^ expected one of `.`, `;`, `?`, `else`, or an operator
 
 error: expected statement after outer attribute
   --> $DIR/attr-stmt-expr-attr-bad.rs:108:37
diff --git a/src/test/ui/parser/issue-72253.rs b/src/test/ui/parser/issue-72253.rs
index 6f9af73b039e4..1446a796fa0d5 100644
--- a/src/test/ui/parser/issue-72253.rs
+++ b/src/test/ui/parser/issue-72253.rs
@@ -1,6 +1,6 @@
 fn main() {
     let a = std::process::Command::new("echo")
         .arg("1")
-        ,arg("2") //~ ERROR expected one of `.`, `;`, `?`, or an operator, found `,`
+        ,arg("2") //~ ERROR expected one of `.`, `;`, `?`, `else`, or an operator, found `,`
         .output();
 }
diff --git a/src/test/ui/parser/issue-72253.stderr b/src/test/ui/parser/issue-72253.stderr
index 3819fd92a9e21..477fa09f495e2 100644
--- a/src/test/ui/parser/issue-72253.stderr
+++ b/src/test/ui/parser/issue-72253.stderr
@@ -1,8 +1,8 @@
-error: expected one of `.`, `;`, `?`, or an operator, found `,`
+error: expected one of `.`, `;`, `?`, `else`, or an operator, found `,`
   --> $DIR/issue-72253.rs:4:9
    |
 LL |         .arg("1")
-   |                  - expected one of `.`, `;`, `?`, or an operator
+   |                  - expected one of `.`, `;`, `?`, `else`, or an operator
 LL |         ,arg("2")
    |         ^ unexpected token
 
diff --git a/src/test/ui/parser/issue-84117.rs b/src/test/ui/parser/issue-84117.rs
index 0f20073591598..919585877cf9c 100644
--- a/src/test/ui/parser/issue-84117.rs
+++ b/src/test/ui/parser/issue-84117.rs
@@ -2,8 +2,8 @@ fn main() {
     let outer_local:e_outer<&str, { let inner_local:e_inner<&str, }
     //~^ ERROR expected one of `>`, a const expression
     //~| ERROR expected one of `>`, a const expression, lifetime, or type, found `}`
-    //~| ERROR expected one of `!`, `.`, `::`, `;`, `?`, `{`, or an operator, found `,`
-    //~| ERROR expected one of `!`, `.`, `::`, `;`, `?`, `{`, or an operator, found `,`
-    //~| ERROR expected one of `!`, `.`, `::`, `;`, `?`, `{`, or an operator, found `,`
+    //~| ERROR expected one of `!`, `.`, `::`, `;`, `?`, `else`, `{`, or an operator, found `,`
+    //~| ERROR expected one of `!`, `.`, `::`, `;`, `?`, `else`, `{`, or an operator, found `,`
+    //~| ERROR expected one of `!`, `.`, `::`, `;`, `?`, `else`, `{`, or an operator, found `,`
 }
 //~^ ERROR expected one of `,`, `:`, `=`, or `>`, found `}`
diff --git a/src/test/ui/parser/issue-84117.stderr b/src/test/ui/parser/issue-84117.stderr
index d667a4977d0d3..5b9cc53baa5eb 100644
--- a/src/test/ui/parser/issue-84117.stderr
+++ b/src/test/ui/parser/issue-84117.stderr
@@ -7,11 +7,11 @@ LL |     let outer_local:e_outer<&str, { let inner_local:e_inner<&str, }
    |                                         |          help: use `=` if you meant to assign
    |                                         while parsing the type for `inner_local`
 
-error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, or an operator, found `,`
+error: expected one of `!`, `.`, `::`, `;`, `?`, `else`, `{`, or an operator, found `,`
   --> $DIR/issue-84117.rs:2:65
    |
 LL |     let outer_local:e_outer<&str, { let inner_local:e_inner<&str, }
-   |                                                                 ^ expected one of 7 possible tokens
+   |                                                                 ^ expected one of 8 possible tokens
 
 error: expected one of `,`, `:`, `=`, or `>`, found `}`
   --> $DIR/issue-84117.rs:8:1
@@ -33,17 +33,17 @@ LL |     let outer_local:e_outer<&str, { let inner_local:e_inner<&str, }
    |                                         |          help: use `=` if you meant to assign
    |                                         while parsing the type for `inner_local`
 
-error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, or an operator, found `,`
+error: expected one of `!`, `.`, `::`, `;`, `?`, `else`, `{`, or an operator, found `,`
   --> $DIR/issue-84117.rs:2:65
    |
 LL |     let outer_local:e_outer<&str, { let inner_local:e_inner<&str, }
-   |                                                                 ^ expected one of 7 possible tokens
+   |                                                                 ^ expected one of 8 possible tokens
 
-error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, or an operator, found `,`
+error: expected one of `!`, `.`, `::`, `;`, `?`, `else`, `{`, or an operator, found `,`
   --> $DIR/issue-84117.rs:2:33
    |
 LL |     let outer_local:e_outer<&str, { let inner_local:e_inner<&str, }
-   |                                 ^ expected one of 7 possible tokens
+   |                                 ^ expected one of 8 possible tokens
 
 error: aborting due to 6 previous errors
 
diff --git a/src/test/ui/parser/macro/issue-37234.stderr b/src/test/ui/parser/macro/issue-37234.stderr
index f0ec79e53574a..8d9636d401c1e 100644
--- a/src/test/ui/parser/macro/issue-37234.stderr
+++ b/src/test/ui/parser/macro/issue-37234.stderr
@@ -1,8 +1,8 @@
-error: expected one of `.`, `;`, `?`, or an operator, found `""`
+error: expected one of `.`, `;`, `?`, `else`, or an operator, found `""`
   --> $DIR/issue-37234.rs:3:19
    |
 LL |         let x = 5 "";
-   |                   ^^ expected one of `.`, `;`, `?`, or an operator
+   |                   ^^ expected one of `.`, `;`, `?`, `else`, or an operator
 ...
 LL |     failed!();
    |     ---------- in this macro invocation
diff --git a/src/test/ui/parser/missing-semicolon.rs b/src/test/ui/parser/missing-semicolon.rs
index a24dfa761a60f..f68d177c01f1f 100644
--- a/src/test/ui/parser/missing-semicolon.rs
+++ b/src/test/ui/parser/missing-semicolon.rs
@@ -1,6 +1,6 @@
 macro_rules! m {
     ($($e1:expr),*; $($e2:expr),*) => {
-        $( let x = $e1 )*; //~ ERROR expected one of `.`, `;`, `?`, or
+        $( let x = $e1 )*; //~ ERROR expected one of `.`, `;`, `?`, `else`, or
         $( println!("{}", $e2) )*;
     }
 }
diff --git a/src/test/ui/parser/missing-semicolon.stderr b/src/test/ui/parser/missing-semicolon.stderr
index 68f0f440c46b9..72f76b6fe3f54 100644
--- a/src/test/ui/parser/missing-semicolon.stderr
+++ b/src/test/ui/parser/missing-semicolon.stderr
@@ -1,8 +1,8 @@
-error: expected one of `.`, `;`, `?`, or an operator, found keyword `let`
+error: expected one of `.`, `;`, `?`, `else`, or an operator, found keyword `let`
   --> $DIR/missing-semicolon.rs:3:12
    |
 LL |         $( let x = $e1 )*;
-   |            ^^^ expected one of `.`, `;`, `?`, or an operator
+   |            ^^^ expected one of `.`, `;`, `?`, `else`, or an operator
 ...
 LL | fn main() { m!(0, 0; 0, 0); }
    |             --------------- in this macro invocation
diff --git a/src/test/ui/parser/range-3.rs b/src/test/ui/parser/range-3.rs
index 931839fb282d1..2c917a24e908a 100644
--- a/src/test/ui/parser/range-3.rs
+++ b/src/test/ui/parser/range-3.rs
@@ -2,5 +2,5 @@
 
 pub fn main() {
     let r = 1..2..3;
-    //~^ ERROR expected one of `.`, `;`, `?`, or an operator, found `..`
+    //~^ ERROR expected one of `.`, `;`, `?`, `else`, or an operator, found `..`
 }
diff --git a/src/test/ui/parser/range-3.stderr b/src/test/ui/parser/range-3.stderr
index f866ea59983b6..340167f1804e3 100644
--- a/src/test/ui/parser/range-3.stderr
+++ b/src/test/ui/parser/range-3.stderr
@@ -1,8 +1,8 @@
-error: expected one of `.`, `;`, `?`, or an operator, found `..`
+error: expected one of `.`, `;`, `?`, `else`, or an operator, found `..`
   --> $DIR/range-3.rs:4:17
    |
 LL |     let r = 1..2..3;
-   |                 ^^ expected one of `.`, `;`, `?`, or an operator
+   |                 ^^ expected one of `.`, `;`, `?`, `else`, or an operator
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/parser/range-4.rs b/src/test/ui/parser/range-4.rs
index 20af956720579..c970c96de8490 100644
--- a/src/test/ui/parser/range-4.rs
+++ b/src/test/ui/parser/range-4.rs
@@ -2,5 +2,5 @@
 
 pub fn main() {
     let r = ..1..2;
-    //~^ ERROR expected one of `.`, `;`, `?`, or an operator, found `..`
+    //~^ ERROR expected one of `.`, `;`, `?`, `else`, or an operator, found `..`
 }
diff --git a/src/test/ui/parser/range-4.stderr b/src/test/ui/parser/range-4.stderr
index dcb85170c1d3b..720d489389bfb 100644
--- a/src/test/ui/parser/range-4.stderr
+++ b/src/test/ui/parser/range-4.stderr
@@ -1,8 +1,8 @@
-error: expected one of `.`, `;`, `?`, or an operator, found `..`
+error: expected one of `.`, `;`, `?`, `else`, or an operator, found `..`
   --> $DIR/range-4.rs:4:16
    |
 LL |     let r = ..1..2;
-   |                ^^ expected one of `.`, `;`, `?`, or an operator
+   |                ^^ expected one of `.`, `;`, `?`, `else`, or an operator
 
 error: aborting due to previous error