about summary refs log tree commit diff
path: root/tests/ui/let-else
diff options
context:
space:
mode:
authorAlbert Larsan <74931857+albertlarsan68@users.noreply.github.com>2023-01-05 09:13:28 +0100
committerAlbert Larsan <74931857+albertlarsan68@users.noreply.github.com>2023-01-11 09:32:08 +0000
commitcf2dff2b1e3fa55fa5415d524200070d0d7aacfe (patch)
tree40a88d9a46aaf3e8870676eb2538378b75a263eb /tests/ui/let-else
parentca855e6e42787ecd062d81d53336fe6788ef51a9 (diff)
downloadrust-cf2dff2b1e3fa55fa5415d524200070d0d7aacfe.tar.gz
rust-cf2dff2b1e3fa55fa5415d524200070d0d7aacfe.zip
Move /src/test to /tests
Diffstat (limited to 'tests/ui/let-else')
-rw-r--r--tests/ui/let-else/const-fn.rs18
-rw-r--r--tests/ui/let-else/issue-100103.rs15
-rw-r--r--tests/ui/let-else/issue-102317.rs20
-rw-r--r--tests/ui/let-else/issue-94176.rs10
-rw-r--r--tests/ui/let-else/issue-94176.stderr19
-rw-r--r--tests/ui/let-else/issue-99975.rs20
-rw-r--r--tests/ui/let-else/let-else-allow-in-expr.rs28
-rw-r--r--tests/ui/let-else/let-else-allow-in-expr.stderr20
-rw-r--r--tests/ui/let-else/let-else-allow-unused.rs15
-rw-r--r--tests/ui/let-else/let-else-allow-unused.stderr14
-rw-r--r--tests/ui/let-else/let-else-binding-explicit-mut-annotated.rs16
-rw-r--r--tests/ui/let-else/let-else-binding-explicit-mut-annotated.stderr25
-rw-r--r--tests/ui/let-else/let-else-binding-explicit-mut-borrow.rs13
-rw-r--r--tests/ui/let-else/let-else-binding-explicit-mut-borrow.stderr9
-rw-r--r--tests/ui/let-else/let-else-binding-explicit-mut-pass.rs13
-rw-r--r--tests/ui/let-else/let-else-binding-explicit-mut.rs20
-rw-r--r--tests/ui/let-else/let-else-binding-explicit-mut.stderr21
-rw-r--r--tests/ui/let-else/let-else-binding-immutable.rs10
-rw-r--r--tests/ui/let-else/let-else-binding-immutable.stderr9
-rw-r--r--tests/ui/let-else/let-else-bindings.rs75
-rw-r--r--tests/ui/let-else/let-else-bool-binop-init.fixed8
-rw-r--r--tests/ui/let-else/let-else-bool-binop-init.rs8
-rw-r--r--tests/ui/let-else/let-else-bool-binop-init.stderr24
-rw-r--r--tests/ui/let-else/let-else-brace-before-else.fixed26
-rw-r--r--tests/ui/let-else/let-else-brace-before-else.rs26
-rw-r--r--tests/ui/let-else/let-else-brace-before-else.stderr46
-rw-r--r--tests/ui/let-else/let-else-check.rs17
-rw-r--r--tests/ui/let-else/let-else-check.stderr20
-rw-r--r--tests/ui/let-else/let-else-deref-coercion-annotated.rs77
-rw-r--r--tests/ui/let-else/let-else-deref-coercion.rs75
-rw-r--r--tests/ui/let-else/let-else-deref-coercion.stderr19
-rw-r--r--tests/ui/let-else/let-else-destructuring.rs17
-rw-r--r--tests/ui/let-else/let-else-destructuring.stderr17
-rw-r--r--tests/ui/let-else/let-else-drop-order.rs270
-rw-r--r--tests/ui/let-else/let-else-drop-order.run.stdout51
-rw-r--r--tests/ui/let-else/let-else-if.rs8
-rw-r--r--tests/ui/let-else/let-else-if.stderr17
-rw-r--r--tests/ui/let-else/let-else-irrefutable.rs11
-rw-r--r--tests/ui/let-else/let-else-irrefutable.stderr21
-rw-r--r--tests/ui/let-else/let-else-missing-semicolon.rs9
-rw-r--r--tests/ui/let-else/let-else-missing-semicolon.stderr18
-rw-r--r--tests/ui/let-else/let-else-no-double-error.rs12
-rw-r--r--tests/ui/let-else/let-else-no-double-error.stderr9
-rw-r--r--tests/ui/let-else/let-else-non-copy.rs45
-rw-r--r--tests/ui/let-else/let-else-non-diverging.rs22
-rw-r--r--tests/ui/let-else/let-else-non-diverging.stderr55
-rw-r--r--tests/ui/let-else/let-else-ref-bindings-pass.rs71
-rw-r--r--tests/ui/let-else/let-else-ref-bindings.rs62
-rw-r--r--tests/ui/let-else/let-else-ref-bindings.stderr83
-rw-r--r--tests/ui/let-else/let-else-run-pass.rs35
-rw-r--r--tests/ui/let-else/let-else-scope.rs5
-rw-r--r--tests/ui/let-else/let-else-scope.stderr9
-rw-r--r--tests/ui/let-else/let-else-slicing-error.rs9
-rw-r--r--tests/ui/let-else/let-else-slicing-error.stderr11
-rw-r--r--tests/ui/let-else/let-else-source-expr-nomove-pass.rs17
-rw-r--r--tests/ui/let-else/let-else-temp-borrowck.rs26
-rw-r--r--tests/ui/let-else/let-else-temporary-lifetime.rs99
-rw-r--r--tests/ui/let-else/let-else-then-diverge.rs17
-rw-r--r--tests/ui/let-else/let-else-then-diverge.stderr14
-rw-r--r--tests/ui/let-else/let-else.rs8
60 files changed, 1784 insertions, 0 deletions
diff --git a/tests/ui/let-else/const-fn.rs b/tests/ui/let-else/const-fn.rs
new file mode 100644
index 00000000000..a3921b8033f
--- /dev/null
+++ b/tests/ui/let-else/const-fn.rs
@@ -0,0 +1,18 @@
+// run-pass
+// issue #101932
+
+
+const fn foo(a: Option<i32>) -> i32 {
+    let Some(a) = a else {
+        return 42
+    };
+
+    a + 1
+}
+
+fn main() {
+    const A: i32 = foo(None);
+    const B: i32 = foo(Some(1));
+
+    println!("{} {}", A, B);
+}
diff --git a/tests/ui/let-else/issue-100103.rs b/tests/ui/let-else/issue-100103.rs
new file mode 100644
index 00000000000..f5f9b2f5f06
--- /dev/null
+++ b/tests/ui/let-else/issue-100103.rs
@@ -0,0 +1,15 @@
+// edition:2021
+// check-pass
+
+#![feature(try_blocks)]
+
+
+fn main() {
+    let _: Result<i32, i32> = try {
+        let Some(x) = Some(0) else {
+            Err(1)?
+        };
+
+        x
+    };
+}
diff --git a/tests/ui/let-else/issue-102317.rs b/tests/ui/let-else/issue-102317.rs
new file mode 100644
index 00000000000..7369b4938ee
--- /dev/null
+++ b/tests/ui/let-else/issue-102317.rs
@@ -0,0 +1,20 @@
+// issue #102317
+// build-pass
+// compile-flags: --edition 2021 -C opt-level=3 -Zvalidate-mir
+
+struct SegmentJob;
+
+impl Drop for SegmentJob {
+    fn drop(&mut self) {}
+}
+
+pub async fn run() -> Result<(), ()> {
+    let jobs = Vec::<SegmentJob>::new();
+    let Some(_job) = jobs.into_iter().next() else {
+        return Ok(())
+    };
+
+    Ok(())
+}
+
+fn main() {}
diff --git a/tests/ui/let-else/issue-94176.rs b/tests/ui/let-else/issue-94176.rs
new file mode 100644
index 00000000000..f76dfc15bc1
--- /dev/null
+++ b/tests/ui/let-else/issue-94176.rs
@@ -0,0 +1,10 @@
+// Issue #94176: wrong span for the error message of a mismatched type error,
+// if the function uses a `let else` construct.
+
+
+pub fn test(a: Option<u32>) -> Option<u32> { //~ ERROR mismatched types
+    let Some(_) = a else { return None; };
+    println!("Foo");
+}
+
+fn main() {}
diff --git a/tests/ui/let-else/issue-94176.stderr b/tests/ui/let-else/issue-94176.stderr
new file mode 100644
index 00000000000..0cb97aceebf
--- /dev/null
+++ b/tests/ui/let-else/issue-94176.stderr
@@ -0,0 +1,19 @@
+error[E0308]: mismatched types
+  --> $DIR/issue-94176.rs:5:32
+   |
+LL | pub fn test(a: Option<u32>) -> Option<u32> {
+   |        ----                    ^^^^^^^^^^^ expected enum `Option`, found `()`
+   |        |
+   |        implicitly returns `()` as its body has no tail or `return` expression
+   |
+   = note:   expected enum `Option<u32>`
+           found unit type `()`
+help: consider returning the local binding `a`
+   |
+LL ~     println!("Foo");
+LL +     a
+   |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/let-else/issue-99975.rs b/tests/ui/let-else/issue-99975.rs
new file mode 100644
index 00000000000..5b164f347e7
--- /dev/null
+++ b/tests/ui/let-else/issue-99975.rs
@@ -0,0 +1,20 @@
+// run-pass
+// compile-flags: -C opt-level=3 -Zvalidate-mir
+
+
+
+fn return_result() -> Option<String> {
+    Some("ok".to_string())
+}
+
+fn start() -> String {
+    let Some(content) = return_result() else {
+        return "none".to_string()
+    };
+
+    content
+}
+
+fn main() {
+    start();
+}
diff --git a/tests/ui/let-else/let-else-allow-in-expr.rs b/tests/ui/let-else/let-else-allow-in-expr.rs
new file mode 100644
index 00000000000..33acb6c6a29
--- /dev/null
+++ b/tests/ui/let-else/let-else-allow-in-expr.rs
@@ -0,0 +1,28 @@
+#![deny(unused_variables)]
+
+fn main() {
+    let Some(_): Option<u32> = ({
+        let x = 1; //~ ERROR unused variable: `x`
+        Some(1)
+    }) else {
+        return;
+    };
+
+    #[allow(unused_variables)]
+    let Some(_): Option<u32> = ({
+        let x = 1;
+        Some(1)
+    }) else {
+        return;
+    };
+
+    let Some(_): Option<u32> = ({
+        #[allow(unused_variables)]
+        let x = 1;
+        Some(1)
+    }) else {
+        return;
+    };
+
+    let x = 1; //~ ERROR unused variable: `x`
+}
diff --git a/tests/ui/let-else/let-else-allow-in-expr.stderr b/tests/ui/let-else/let-else-allow-in-expr.stderr
new file mode 100644
index 00000000000..3b2b9066c90
--- /dev/null
+++ b/tests/ui/let-else/let-else-allow-in-expr.stderr
@@ -0,0 +1,20 @@
+error: unused variable: `x`
+  --> $DIR/let-else-allow-in-expr.rs:5:13
+   |
+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-allow-in-expr.rs:1:9
+   |
+LL | #![deny(unused_variables)]
+   |         ^^^^^^^^^^^^^^^^
+
+error: unused variable: `x`
+  --> $DIR/let-else-allow-in-expr.rs:27:9
+   |
+LL |     let x = 1;
+   |         ^ help: if this is intentional, prefix it with an underscore: `_x`
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/let-else/let-else-allow-unused.rs b/tests/ui/let-else/let-else-allow-unused.rs
new file mode 100644
index 00000000000..bbb1c7beaf8
--- /dev/null
+++ b/tests/ui/let-else/let-else-allow-unused.rs
@@ -0,0 +1,15 @@
+// issue #89807
+
+
+
+#[deny(unused_variables)]
+
+fn main() {
+    let value = Some(String::new());
+    #[allow(unused)]
+    let banana = 1;
+    #[allow(unused)]
+    let Some(chaenomeles) = value.clone() else { return }; // OK
+
+    let Some(chaenomeles) = value else { return }; //~ ERROR unused variable: `chaenomeles`
+}
diff --git a/tests/ui/let-else/let-else-allow-unused.stderr b/tests/ui/let-else/let-else-allow-unused.stderr
new file mode 100644
index 00000000000..05b8a9169fb
--- /dev/null
+++ b/tests/ui/let-else/let-else-allow-unused.stderr
@@ -0,0 +1,14 @@
+error: unused variable: `chaenomeles`
+  --> $DIR/let-else-allow-unused.rs:14:14
+   |
+LL |     let Some(chaenomeles) = value else { return };
+   |              ^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_chaenomeles`
+   |
+note: the lint level is defined here
+  --> $DIR/let-else-allow-unused.rs:5:8
+   |
+LL | #[deny(unused_variables)]
+   |        ^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/tests/ui/let-else/let-else-binding-explicit-mut-annotated.rs b/tests/ui/let-else/let-else-binding-explicit-mut-annotated.rs
new file mode 100644
index 00000000000..955f33ee134
--- /dev/null
+++ b/tests/ui/let-else/let-else-binding-explicit-mut-annotated.rs
@@ -0,0 +1,16 @@
+// from rfc2005 test suite
+
+
+
+// Verify the binding mode shifts - only when no `&` are auto-dereferenced is the
+// final default binding mode mutable.
+
+fn main() {
+    let Some(n): &mut Option<i32> = &&Some(5i32) else { return }; //~ ERROR mismatched types
+    *n += 1;
+    let _ = n;
+
+    let Some(n): &mut Option<i32> = &&mut Some(5i32) else { return }; //~ ERROR mismatched types
+    *n += 1;
+    let _ = n;
+}
diff --git a/tests/ui/let-else/let-else-binding-explicit-mut-annotated.stderr b/tests/ui/let-else/let-else-binding-explicit-mut-annotated.stderr
new file mode 100644
index 00000000000..065787cab08
--- /dev/null
+++ b/tests/ui/let-else/let-else-binding-explicit-mut-annotated.stderr
@@ -0,0 +1,25 @@
+error[E0308]: mismatched types
+  --> $DIR/let-else-binding-explicit-mut-annotated.rs:9:37
+   |
+LL |     let Some(n): &mut Option<i32> = &&Some(5i32) else { return };
+   |                  ----------------   ^^^^^^^^^^^^ types differ in mutability
+   |                  |
+   |                  expected due to this
+   |
+   = note: expected mutable reference `&mut Option<i32>`
+                      found reference `&&Option<i32>`
+
+error[E0308]: mismatched types
+  --> $DIR/let-else-binding-explicit-mut-annotated.rs:13:37
+   |
+LL |     let Some(n): &mut Option<i32> = &&mut Some(5i32) else { return };
+   |                  ----------------   ^^^^^^^^^^^^^^^^ types differ in mutability
+   |                  |
+   |                  expected due to this
+   |
+   = note: expected mutable reference `&mut Option<i32>`
+                      found reference `&&mut Option<i32>`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/let-else/let-else-binding-explicit-mut-borrow.rs b/tests/ui/let-else/let-else-binding-explicit-mut-borrow.rs
new file mode 100644
index 00000000000..1524d010233
--- /dev/null
+++ b/tests/ui/let-else/let-else-binding-explicit-mut-borrow.rs
@@ -0,0 +1,13 @@
+// Slightly different from explicit-mut-annotated -- this won't show an error until borrowck.
+// Should it show a type error instead?
+
+
+
+fn main() {
+    let Some(n): &mut Option<i32> = &mut &Some(5i32) else {
+        //~^ ERROR cannot borrow data in a `&` reference as mutable
+        return
+    };
+    *n += 1;
+    let _ = n;
+}
diff --git a/tests/ui/let-else/let-else-binding-explicit-mut-borrow.stderr b/tests/ui/let-else/let-else-binding-explicit-mut-borrow.stderr
new file mode 100644
index 00000000000..023fab8fe4a
--- /dev/null
+++ b/tests/ui/let-else/let-else-binding-explicit-mut-borrow.stderr
@@ -0,0 +1,9 @@
+error[E0596]: cannot borrow data in a `&` reference as mutable
+  --> $DIR/let-else-binding-explicit-mut-borrow.rs:7:37
+   |
+LL |     let Some(n): &mut Option<i32> = &mut &Some(5i32) else {
+   |                                     ^^^^^^^^^^^^^^^^ cannot borrow as mutable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0596`.
diff --git a/tests/ui/let-else/let-else-binding-explicit-mut-pass.rs b/tests/ui/let-else/let-else-binding-explicit-mut-pass.rs
new file mode 100644
index 00000000000..b0a6264a10d
--- /dev/null
+++ b/tests/ui/let-else/let-else-binding-explicit-mut-pass.rs
@@ -0,0 +1,13 @@
+// check-pass
+
+
+
+fn main() {
+    let Some(n) = &mut &mut Some(5i32) else { return; };
+    *n += 1; // OK
+    let _ = n;
+
+    let Some(n): &mut Option<i32> = &mut &mut Some(5i32) else { return; };
+    *n += 1; // OK
+    let _ = n;
+}
diff --git a/tests/ui/let-else/let-else-binding-explicit-mut.rs b/tests/ui/let-else/let-else-binding-explicit-mut.rs
new file mode 100644
index 00000000000..a153b3af0de
--- /dev/null
+++ b/tests/ui/let-else/let-else-binding-explicit-mut.rs
@@ -0,0 +1,20 @@
+// from rfc2005 test suite
+
+
+
+// Verify the binding mode shifts - only when no `&` are auto-dereferenced is the
+// final default binding mode mutable.
+
+fn main() {
+    let Some(n) = &&Some(5i32) else { return };
+    *n += 1; //~ ERROR cannot assign to `*n`, which is behind a `&` reference
+    let _ = n;
+
+    let Some(n) = &mut &Some(5i32) else { return };
+    *n += 1; //~ ERROR cannot assign to `*n`, which is behind a `&` reference
+    let _ = n;
+
+    let Some(n) = &&mut Some(5i32) else { return };
+    *n += 1; //~ ERROR cannot assign to `*n`, which is behind a `&` reference
+    let _ = n;
+}
diff --git a/tests/ui/let-else/let-else-binding-explicit-mut.stderr b/tests/ui/let-else/let-else-binding-explicit-mut.stderr
new file mode 100644
index 00000000000..45f2b6b3bce
--- /dev/null
+++ b/tests/ui/let-else/let-else-binding-explicit-mut.stderr
@@ -0,0 +1,21 @@
+error[E0594]: cannot assign to `*n`, which is behind a `&` reference
+  --> $DIR/let-else-binding-explicit-mut.rs:10:5
+   |
+LL |     *n += 1;
+   |     ^^^^^^^ `n` is a `&` reference, so the data it refers to cannot be written
+
+error[E0594]: cannot assign to `*n`, which is behind a `&` reference
+  --> $DIR/let-else-binding-explicit-mut.rs:14:5
+   |
+LL |     *n += 1;
+   |     ^^^^^^^ `n` is a `&` reference, so the data it refers to cannot be written
+
+error[E0594]: cannot assign to `*n`, which is behind a `&` reference
+  --> $DIR/let-else-binding-explicit-mut.rs:18:5
+   |
+LL |     *n += 1;
+   |     ^^^^^^^ `n` is a `&` reference, so the data it refers to cannot be written
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0594`.
diff --git a/tests/ui/let-else/let-else-binding-immutable.rs b/tests/ui/let-else/let-else-binding-immutable.rs
new file mode 100644
index 00000000000..ff2d9f240a0
--- /dev/null
+++ b/tests/ui/let-else/let-else-binding-immutable.rs
@@ -0,0 +1,10 @@
+// from rfc2005 test suite
+
+
+
+pub fn main() {
+    let Some(x) = &Some(3) else {
+        panic!();
+    };
+    *x += 1; //~ ERROR: cannot assign to `*x`, which is behind a `&` reference
+}
diff --git a/tests/ui/let-else/let-else-binding-immutable.stderr b/tests/ui/let-else/let-else-binding-immutable.stderr
new file mode 100644
index 00000000000..dd1365a9ef0
--- /dev/null
+++ b/tests/ui/let-else/let-else-binding-immutable.stderr
@@ -0,0 +1,9 @@
+error[E0594]: cannot assign to `*x`, which is behind a `&` reference
+  --> $DIR/let-else-binding-immutable.rs:9:5
+   |
+LL |     *x += 1;
+   |     ^^^^^^^ `x` is a `&` reference, so the data it refers to cannot be written
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0594`.
diff --git a/tests/ui/let-else/let-else-bindings.rs b/tests/ui/let-else/let-else-bindings.rs
new file mode 100644
index 00000000000..7d2cad978fc
--- /dev/null
+++ b/tests/ui/let-else/let-else-bindings.rs
@@ -0,0 +1,75 @@
+// run-pass
+// adapted from src/test/ui/binding/if-let.rs
+
+#![allow(dead_code)]
+
+fn none() -> bool {
+    let None = Some("test") else {
+        return true;
+    };
+    false
+}
+
+fn ok() -> bool {
+    let Ok(()) = Err::<(),&'static str>("test") else {
+        return true;
+    };
+    false
+}
+
+pub fn main() {
+    let x = Some(3);
+    let Some(y) = x else {
+        panic!("let-else panicked");
+    };
+    assert_eq!(y, 3);
+    let Some(_) = x else {
+        panic!("bad match");
+    };
+    assert!(none());
+    assert!(ok());
+
+    assert!((|| {
+        let 1 = 2 else {
+            return true;
+        };
+        false
+    })());
+
+    enum Foo {
+        One,
+        Two(usize),
+        Three(String, isize),
+    }
+
+    let foo = Foo::Three("three".to_string(), 42);
+    let one = || {
+        let Foo::One = foo else {
+            return true;
+        };
+        false
+    };
+    assert!(one());
+    let two = || {
+        let Foo::Two(_x) = foo else {
+            return true;
+        };
+        false
+    };
+    assert!(two());
+    let three = || {
+        let Foo::Three(s, _x) = foo else {
+            return false;
+        };
+        s == "three"
+    };
+    assert!(three());
+
+    let a@Foo::Two(_) = Foo::Two(42_usize) else {
+        panic!("bad match")
+    };
+    let Foo::Two(b) = a else {
+        panic!("panic in nested `if let`");
+    };
+    assert_eq!(b, 42_usize);
+}
diff --git a/tests/ui/let-else/let-else-bool-binop-init.fixed b/tests/ui/let-else/let-else-bool-binop-init.fixed
new file mode 100644
index 00000000000..20e558ca909
--- /dev/null
+++ b/tests/ui/let-else/let-else-bool-binop-init.fixed
@@ -0,0 +1,8 @@
+// run-rustfix
+
+
+
+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/tests/ui/let-else/let-else-bool-binop-init.rs b/tests/ui/let-else/let-else-bool-binop-init.rs
new file mode 100644
index 00000000000..f88179a940b
--- /dev/null
+++ b/tests/ui/let-else/let-else-bool-binop-init.rs
@@ -0,0 +1,8 @@
+// run-rustfix
+
+
+
+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/tests/ui/let-else/let-else-bool-binop-init.stderr b/tests/ui/let-else/let-else-bool-binop-init.stderr
new file mode 100644
index 00000000000..edee6576244
--- /dev/null
+++ b/tests/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 parentheses
+   |
+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 parentheses
+   |
+LL |     let true = (true || false) else { return };
+   |                +             +
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/let-else/let-else-brace-before-else.fixed b/tests/ui/let-else/let-else-brace-before-else.fixed
new file mode 100644
index 00000000000..a75c770ddf2
--- /dev/null
+++ b/tests/ui/let-else/let-else-brace-before-else.fixed
@@ -0,0 +1,26 @@
+// run-rustfix
+
+
+
+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/tests/ui/let-else/let-else-brace-before-else.rs b/tests/ui/let-else/let-else-brace-before-else.rs
new file mode 100644
index 00000000000..5603b946f38
--- /dev/null
+++ b/tests/ui/let-else/let-else-brace-before-else.rs
@@ -0,0 +1,26 @@
+// run-rustfix
+
+
+
+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/tests/ui/let-else/let-else-brace-before-else.stderr b/tests/ui/let-else/let-else-brace-before-else.stderr
new file mode 100644
index 00000000000..cb01e4c18a1
--- /dev/null
+++ b/tests/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: wrap the expression in parentheses
+   |
+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: wrap the expression in parentheses
+   |
+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: wrap the expression in parentheses
+   |
+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: wrap the expression in parentheses
+   |
+LL |     let Some(1) = (unsafe { unsafe_fn() }) else {
+   |                   +                      +
+
+error: aborting due to 4 previous errors
+
diff --git a/tests/ui/let-else/let-else-check.rs b/tests/ui/let-else/let-else-check.rs
new file mode 100644
index 00000000000..713fd986e97
--- /dev/null
+++ b/tests/ui/let-else/let-else-check.rs
@@ -0,0 +1,17 @@
+#![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 Some(_): Option<u32> = Some(Default::default()) else {
+        let x = 1; //~ ERROR unused variable: `x`
+        return;
+    };
+
+    let x = 1; //~ ERROR unused variable: `x`
+}
diff --git a/tests/ui/let-else/let-else-check.stderr b/tests/ui/let-else/let-else-check.stderr
new file mode 100644
index 00000000000..bdecbf708f9
--- /dev/null
+++ b/tests/ui/let-else/let-else-check.stderr
@@ -0,0 +1,20 @@
+error: unused variable: `x`
+  --> $DIR/let-else-check.rs:12:13
+   |
+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:1:9
+   |
+LL | #![deny(unused_variables)]
+   |         ^^^^^^^^^^^^^^^^
+
+error: unused variable: `x`
+  --> $DIR/let-else-check.rs:16:9
+   |
+LL |     let x = 1;
+   |         ^ help: if this is intentional, prefix it with an underscore: `_x`
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/let-else/let-else-deref-coercion-annotated.rs b/tests/ui/let-else/let-else-deref-coercion-annotated.rs
new file mode 100644
index 00000000000..60fdf825a33
--- /dev/null
+++ b/tests/ui/let-else/let-else-deref-coercion-annotated.rs
@@ -0,0 +1,77 @@
+// check-pass
+//
+// Taken from https://github.com/rust-lang/rust/blob/6cc0a764e082d9c0abcf37a768d5889247ba13e2/compiler/rustc_typeck/src/check/_match.rs#L445-L462
+//
+// We attempt to `let Bar::Present(_): &mut Bar = foo else { ... }` where foo is meant to
+// Deref/DerefMut to Bar. You can do this with an irrefutable binding, so it should work with
+// let-else too.
+
+
+use std::ops::{Deref, DerefMut};
+
+struct Foo(Bar);
+
+enum Bar {
+    Present(u32),
+    Absent,
+}
+impl Deref for Foo {
+    type Target = Bar;
+    fn deref(&self) -> &Bar {
+        &self.0
+    }
+}
+impl DerefMut for Foo {
+    fn deref_mut(&mut self) -> &mut Bar {
+        &mut self.0
+    }
+}
+impl Bar {
+    fn bar(&self) -> Option<u32> {
+        let Bar::Present(z): &Bar = self else {
+            return None;
+        };
+        return Some(*z);
+    }
+}
+impl Foo {
+    fn set_bar_annotated(&mut self, value: u32) {
+        let Bar::Present(z): &mut Bar = self else { // OK
+            return;
+        };
+        *z = value;
+    }
+}
+
+fn main() {
+    let mut foo = Foo(Bar::Present(1));
+    foo.set_bar_annotated(42);
+    assert_eq!(foo.bar(), Some(42));
+    irrefutable::inner();
+}
+
+// The original, to show it works for irrefutable let decls
+mod irrefutable {
+    use std::ops::{Deref, DerefMut};
+    struct Foo(Bar);
+    struct Bar(u32);
+    impl Deref for Foo {
+        type Target = Bar;
+        fn deref(&self) -> &Bar {
+            &self.0
+        }
+    }
+    impl DerefMut for Foo {
+        fn deref_mut(&mut self) -> &mut Bar {
+            &mut self.0
+        }
+    }
+    fn foo(x: &mut Foo) {
+        let Bar(z): &mut Bar = x; // OK
+        *z = 42;
+        assert_eq!((x.0).0, 42);
+    }
+    pub fn inner() {
+        foo(&mut Foo(Bar(1)));
+    }
+}
diff --git a/tests/ui/let-else/let-else-deref-coercion.rs b/tests/ui/let-else/let-else-deref-coercion.rs
new file mode 100644
index 00000000000..052a5a8c73c
--- /dev/null
+++ b/tests/ui/let-else/let-else-deref-coercion.rs
@@ -0,0 +1,75 @@
+// Taken from https://github.com/rust-lang/rust/blob/6cc0a764e082d9c0abcf37a768d5889247ba13e2/compiler/rustc_typeck/src/check/_match.rs#L445-L462
+//
+// We attempt to `let Bar::Present(_) = foo else { ... }` where foo is meant to Deref/DerefMut to
+// Bar. This fails, you must add a type annotation like `let _: &mut Bar = _ else { ... }`
+
+
+use std::ops::{Deref, DerefMut};
+
+struct Foo(Bar);
+
+enum Bar {
+    Present(u32),
+    Absent,
+}
+impl Deref for Foo {
+    type Target = Bar;
+    fn deref(&self) -> &Bar {
+        &self.0
+    }
+}
+impl DerefMut for Foo {
+    fn deref_mut(&mut self) -> &mut Bar {
+        &mut self.0
+    }
+}
+impl Bar {
+    fn bar(&self) -> Option<u32> {
+        let Bar::Present(z): &Bar = self else {
+            return None;
+        };
+        return Some(*z);
+    }
+}
+impl Foo {
+    // Try without the type annotation
+    fn set_bar_unannotated(&mut self, value: u32) {
+        let Bar::Present(z) = self else { //~ ERROR mismatched types
+            return;
+        };
+        *z = value;
+    }
+}
+
+fn main() {
+    let mut foo = Foo(Bar::Present(1));
+    foo.set_bar_unannotated(54);
+    assert_eq!(foo.bar(), Some(54));
+    irrefutable::inner();
+}
+
+// The original, to show it fails for irrefutable let decls
+mod irrefutable {
+    use std::ops::{Deref, DerefMut};
+    struct Foo(Bar);
+    struct Bar(u32);
+    impl Deref for Foo {
+        type Target = Bar;
+        fn deref(&self) -> &Bar {
+            &self.0
+        }
+    }
+    impl DerefMut for Foo {
+        fn deref_mut(&mut self) -> &mut Bar {
+            &mut self.0
+        }
+    }
+    fn foo(x: &mut Foo) {
+        let Bar(z) = x; //~ ERROR mismatched types
+        *z = 54;
+        assert_eq!((x.0).0, 54);
+    }
+    pub fn inner() {
+        foo(&mut Foo(Bar(1)));
+    }
+}
diff --git a/tests/ui/let-else/let-else-deref-coercion.stderr b/tests/ui/let-else/let-else-deref-coercion.stderr
new file mode 100644
index 00000000000..bf78a079cdf
--- /dev/null
+++ b/tests/ui/let-else/let-else-deref-coercion.stderr
@@ -0,0 +1,19 @@
+error[E0308]: mismatched types
+  --> $DIR/let-else-deref-coercion.rs:37:13
+   |
+LL |         let Bar::Present(z) = self else {
+   |             ^^^^^^^^^^^^^^^   ---- this expression has type `&mut Foo`
+   |             |
+   |             expected struct `Foo`, found enum `Bar`
+
+error[E0308]: mismatched types
+  --> $DIR/let-else-deref-coercion.rs:68:13
+   |
+LL |         let Bar(z) = x;
+   |             ^^^^^^   - this expression has type `&mut irrefutable::Foo`
+   |             |
+   |             expected struct `Foo`, found struct `Bar`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/let-else/let-else-destructuring.rs b/tests/ui/let-else/let-else-destructuring.rs
new file mode 100644
index 00000000000..d1f1a69bfc2
--- /dev/null
+++ b/tests/ui/let-else/let-else-destructuring.rs
@@ -0,0 +1,17 @@
+#[derive(Debug)]
+enum Foo {
+    Done,
+    Nested(Option<&'static Foo>),
+}
+
+fn walk(mut value: &Foo) {
+    loop {
+        println!("{:?}", value);
+        &Foo::Nested(Some(value)) = value else { break }; //~ ERROR invalid left-hand side of assignment
+        //~^ERROR <assignment> ... else { ... } is not allowed
+    }
+}
+
+fn main() {
+    walk(&Foo::Done);
+}
diff --git a/tests/ui/let-else/let-else-destructuring.stderr b/tests/ui/let-else/let-else-destructuring.stderr
new file mode 100644
index 00000000000..7d6cb238644
--- /dev/null
+++ b/tests/ui/let-else/let-else-destructuring.stderr
@@ -0,0 +1,17 @@
+error: <assignment> ... else { ... } is not allowed
+  --> $DIR/let-else-destructuring.rs:10:9
+   |
+LL |         &Foo::Nested(Some(value)) = value else { break };
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0070]: invalid left-hand side of assignment
+  --> $DIR/let-else-destructuring.rs:10:35
+   |
+LL |         &Foo::Nested(Some(value)) = value else { break };
+   |         ------------------------- ^
+   |         |
+   |         cannot assign to this expression
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0070`.
diff --git a/tests/ui/let-else/let-else-drop-order.rs b/tests/ui/let-else/let-else-drop-order.rs
new file mode 100644
index 00000000000..e91e5de84e4
--- /dev/null
+++ b/tests/ui/let-else/let-else-drop-order.rs
@@ -0,0 +1,270 @@
+// run-pass
+// edition:2021
+// check-run-results
+//
+// Drop order tests for let else
+//
+// Mostly this ensures two things:
+// 1. That let and let else temporary drop order is the same.
+//    This is a specific design request: https://github.com/rust-lang/rust/pull/93628#issuecomment-1047140316
+// 2. That the else block truly only runs after the
+//    temporaries have dropped.
+//
+// We also print some nice tables for an overview by humans.
+// Changes in those tables are considered breakages, but the
+// important properties 1 and 2 are also enforced by the code.
+// This is important as it's easy to update the stdout file
+// with a --bless and miss the impact of that change.
+
+
+#![allow(irrefutable_let_patterns)]
+
+use std::cell::RefCell;
+use std::rc::Rc;
+
+#[derive(Clone)]
+struct DropAccountant(Rc<RefCell<Vec<Vec<String>>>>);
+
+impl DropAccountant {
+    fn new() -> Self {
+        Self(Default::default())
+    }
+    fn build_droppy(&self, v: u32) -> Droppy<u32> {
+        Droppy(self.clone(), v)
+    }
+    fn build_droppy_enum_none(&self, _v: u32) -> ((), DroppyEnum<u32>) {
+        ((), DroppyEnum::None(self.clone()))
+    }
+    fn new_list(&self, s: impl ToString) {
+        self.0.borrow_mut().push(vec![s.to_string()]);
+    }
+    fn push(&self, s: impl ToString) {
+        let s = s.to_string();
+        let mut accounts = self.0.borrow_mut();
+        accounts.last_mut().unwrap().push(s);
+    }
+    fn print_table(&self) {
+        println!();
+
+        let accounts = self.0.borrow();
+        let before_last = &accounts[accounts.len() - 2];
+        let last = &accounts[accounts.len() - 1];
+        let before_last = get_comma_list(before_last);
+        let last = get_comma_list(last);
+        const LINES: &[&str] = &[
+            "vanilla",
+            "&",
+            "&mut",
+            "move",
+            "fn(this)",
+            "tuple",
+            "array",
+            "ref &",
+            "ref mut &mut",
+        ];
+        let max_len = LINES.iter().map(|v| v.len()).max().unwrap();
+        let max_len_before = before_last.iter().map(|v| v.len()).max().unwrap();
+        let max_len_last = last.iter().map(|v| v.len()).max().unwrap();
+
+        println!(
+            "| {: <max_len$} | {: <max_len_before$} | {: <max_len_last$} |",
+            "construct", before_last[0], last[0]
+        );
+        println!("| {:-<max_len$} | {:-<max_len_before$} | {:-<max_len_last$} |", "", "", "");
+
+        for ((l, l_before), l_last) in
+            LINES.iter().zip(before_last[1..].iter()).zip(last[1..].iter())
+        {
+            println!(
+                "| {: <max_len$} | {: <max_len_before$} | {: <max_len_last$} |",
+                l, l_before, l_last,
+            );
+        }
+    }
+    #[track_caller]
+    fn assert_all_equal_to(&self, st: &str) {
+        let accounts = self.0.borrow();
+        let last = &accounts[accounts.len() - 1];
+        let last = get_comma_list(last);
+        for line in last[1..].iter() {
+            assert_eq!(line.trim(), st.trim());
+        }
+    }
+    #[track_caller]
+    fn assert_equality_last_two_lists(&self) {
+        let accounts = self.0.borrow();
+        let last = &accounts[accounts.len() - 1];
+        let before_last = &accounts[accounts.len() - 2];
+        for (l, b) in last[1..].iter().zip(before_last[1..].iter()) {
+            if !(l == b || l == "n/a" || b == "n/a") {
+                panic!("not equal: '{last:?}' != '{before_last:?}'");
+            }
+        }
+    }
+}
+
+fn get_comma_list(sl: &[String]) -> Vec<String> {
+    std::iter::once(sl[0].clone())
+        .chain(sl[1..].chunks(2).map(|c| c.join(",")))
+        .collect::<Vec<String>>()
+}
+
+struct Droppy<T>(DropAccountant, T);
+
+impl<T> Drop for Droppy<T> {
+    fn drop(&mut self) {
+        self.0.push("drop");
+    }
+}
+
+#[allow(dead_code)]
+enum DroppyEnum<T> {
+    Some(DropAccountant, T),
+    None(DropAccountant),
+}
+
+impl<T> Drop for DroppyEnum<T> {
+    fn drop(&mut self) {
+        match self {
+            DroppyEnum::Some(acc, _inner) => acc,
+            DroppyEnum::None(acc) => acc,
+        }
+        .push("drop");
+    }
+}
+
+macro_rules! nestings_with {
+    ($construct:ident, $binding:pat, $exp:expr) => {
+        // vanilla:
+        $construct!($binding, $exp.1);
+
+        // &:
+        $construct!(&$binding, &$exp.1);
+
+        // &mut:
+        $construct!(&mut $binding, &mut ($exp.1));
+
+        {
+            // move:
+            let w = $exp;
+            $construct!(
+                $binding,
+                {
+                    let w = w;
+                    w
+                }
+                .1
+            );
+        }
+
+        // fn(this):
+        $construct!($binding, std::convert::identity($exp).1);
+    };
+}
+
+macro_rules! nestings {
+    ($construct:ident, $binding:pat, $exp:expr) => {
+        nestings_with!($construct, $binding, $exp);
+
+        // tuple:
+        $construct!(($binding, 77), ($exp.1, 77));
+
+        // array:
+        $construct!([$binding], [$exp.1]);
+    };
+}
+
+macro_rules! let_else {
+    ($acc:expr, $v:expr, $binding:pat, $build:ident) => {
+        let acc = $acc;
+        let v = $v;
+
+        macro_rules! let_else_construct {
+            ($arg:pat, $exp:expr) => {
+                loop {
+                    let $arg = $exp else {
+                        acc.push("else");
+                        break;
+                    };
+                    acc.push("body");
+                    break;
+                }
+            };
+        }
+        nestings!(let_else_construct, $binding, acc.$build(v));
+        // ref &:
+        let_else_construct!($binding, &acc.$build(v).1);
+
+        // ref mut &mut:
+        let_else_construct!($binding, &mut acc.$build(v).1);
+    };
+}
+
+macro_rules! let_ {
+    ($acc:expr, $binding:tt) => {
+        let acc = $acc;
+
+        macro_rules! let_construct {
+            ($arg:pat, $exp:expr) => {{
+                let $arg = $exp;
+                acc.push("body");
+            }};
+        }
+        let v = 0;
+        {
+            nestings_with!(let_construct, $binding, acc.build_droppy(v));
+        }
+        acc.push("n/a");
+        acc.push("n/a");
+        acc.push("n/a");
+        acc.push("n/a");
+
+        // ref &:
+        let_construct!($binding, &acc.build_droppy(v).1);
+
+        // ref mut &mut:
+        let_construct!($binding, &mut acc.build_droppy(v).1);
+    };
+}
+
+fn main() {
+    let acc = DropAccountant::new();
+
+    println!(" --- matching cases ---");
+
+    // Ensure that let and let else have the same behaviour
+    acc.new_list("let _");
+    let_!(&acc, _);
+    acc.new_list("let else _");
+    let_else!(&acc, 0, _, build_droppy);
+    acc.assert_equality_last_two_lists();
+    acc.print_table();
+
+    // Ensure that let and let else have the same behaviour
+    acc.new_list("let _v");
+    let_!(&acc, _v);
+    acc.new_list("let else _v");
+    let_else!(&acc, 0, _v, build_droppy);
+    acc.assert_equality_last_two_lists();
+    acc.print_table();
+
+    println!();
+
+    println!(" --- mismatching cases ---");
+
+    acc.new_list("let else _ mismatch");
+    let_else!(&acc, 1, DroppyEnum::Some(_, _), build_droppy_enum_none);
+    acc.new_list("let else _v mismatch");
+    let_else!(&acc, 1, DroppyEnum::Some(_, _v), build_droppy_enum_none);
+    acc.print_table();
+    // This ensures that we always drop before visiting the else case
+    acc.assert_all_equal_to("drop,else");
+
+    acc.new_list("let else 0 mismatch");
+    let_else!(&acc, 1, 0, build_droppy);
+    acc.new_list("let else 0 mismatch");
+    let_else!(&acc, 1, 0, build_droppy);
+    acc.print_table();
+    // This ensures that we always drop before visiting the else case
+    acc.assert_all_equal_to("drop,else");
+}
diff --git a/tests/ui/let-else/let-else-drop-order.run.stdout b/tests/ui/let-else/let-else-drop-order.run.stdout
new file mode 100644
index 00000000000..01cf2f73e17
--- /dev/null
+++ b/tests/ui/let-else/let-else-drop-order.run.stdout
@@ -0,0 +1,51 @@
+ --- matching cases ---
+
+| construct    | let _     | let else _ |
+| ------------ | --------- | ---------- |
+| vanilla      | drop,body | drop,body  |
+| &            | body,drop | body,drop  |
+| &mut         | body,drop | body,drop  |
+| move         | drop,body | drop,body  |
+| fn(this)     | drop,body | drop,body  |
+| tuple        | n/a,n/a   | drop,body  |
+| array        | n/a,n/a   | drop,body  |
+| ref &        | body,drop | body,drop  |
+| ref mut &mut | body,drop | body,drop  |
+
+| construct    | let _v    | let else _v |
+| ------------ | --------- | ----------- |
+| vanilla      | drop,body | drop,body   |
+| &            | body,drop | body,drop   |
+| &mut         | body,drop | body,drop   |
+| move         | drop,body | drop,body   |
+| fn(this)     | drop,body | drop,body   |
+| tuple        | n/a,n/a   | drop,body   |
+| array        | n/a,n/a   | drop,body   |
+| ref &        | body,drop | body,drop   |
+| ref mut &mut | body,drop | body,drop   |
+
+ --- mismatching cases ---
+
+| construct    | let else _ mismatch | let else _v mismatch |
+| ------------ | ------------------- | -------------------- |
+| vanilla      | drop,else           | drop,else            |
+| &            | drop,else           | drop,else            |
+| &mut         | drop,else           | drop,else            |
+| move         | drop,else           | drop,else            |
+| fn(this)     | drop,else           | drop,else            |
+| tuple        | drop,else           | drop,else            |
+| array        | drop,else           | drop,else            |
+| ref &        | drop,else           | drop,else            |
+| ref mut &mut | drop,else           | drop,else            |
+
+| construct    | let else 0 mismatch | let else 0 mismatch |
+| ------------ | ------------------- | ------------------- |
+| vanilla      | drop,else           | drop,else           |
+| &            | drop,else           | drop,else           |
+| &mut         | drop,else           | drop,else           |
+| move         | drop,else           | drop,else           |
+| fn(this)     | drop,else           | drop,else           |
+| tuple        | drop,else           | drop,else           |
+| array        | drop,else           | drop,else           |
+| ref &        | drop,else           | drop,else           |
+| ref mut &mut | drop,else           | drop,else           |
diff --git a/tests/ui/let-else/let-else-if.rs b/tests/ui/let-else/let-else-if.rs
new file mode 100644
index 00000000000..e8c54ca7ab2
--- /dev/null
+++ b/tests/ui/let-else/let-else-if.rs
@@ -0,0 +1,8 @@
+fn main() {
+    let Some(_) = Some(()) else if true {
+        //~^ ERROR conditional `else if` is not supported for `let...else`
+        return;
+    } else {
+        return;
+    };
+}
diff --git a/tests/ui/let-else/let-else-if.stderr b/tests/ui/let-else/let-else-if.stderr
new file mode 100644
index 00000000000..c63fd61c5de
--- /dev/null
+++ b/tests/ui/let-else/let-else-if.stderr
@@ -0,0 +1,17 @@
+error: conditional `else if` is not supported for `let...else`
+  --> $DIR/let-else-if.rs:2:33
+   |
+LL |     let Some(_) = Some(()) else if true {
+   |                                 ^^ expected `{`
+   |
+help: try placing this code inside a block
+   |
+LL ~     let Some(_) = Some(()) else { if true {
+LL |
+ ...
+LL |         return;
+LL ~     } };
+   |
+
+error: aborting due to previous error
+
diff --git a/tests/ui/let-else/let-else-irrefutable.rs b/tests/ui/let-else/let-else-irrefutable.rs
new file mode 100644
index 00000000000..f4b338eb0af
--- /dev/null
+++ b/tests/ui/let-else/let-else-irrefutable.rs
@@ -0,0 +1,11 @@
+// check-pass
+
+fn main() {
+    let x = 1 else { return }; //~ WARN irrefutable `let...else` pattern
+
+    // Multiline else blocks should not get printed
+    let x = 1 else { //~ WARN irrefutable `let...else` pattern
+        eprintln!("problem case encountered");
+        return
+    };
+}
diff --git a/tests/ui/let-else/let-else-irrefutable.stderr b/tests/ui/let-else/let-else-irrefutable.stderr
new file mode 100644
index 00000000000..73d4e5f3483
--- /dev/null
+++ b/tests/ui/let-else/let-else-irrefutable.stderr
@@ -0,0 +1,21 @@
+warning: irrefutable `let...else` pattern
+  --> $DIR/let-else-irrefutable.rs:4:5
+   |
+LL |     let x = 1 else { return };
+   |     ^^^^^^^^^
+   |
+   = note: this pattern will always match, so the `else` clause is useless
+   = help: consider removing the `else` clause
+   = note: `#[warn(irrefutable_let_patterns)]` on by default
+
+warning: irrefutable `let...else` pattern
+  --> $DIR/let-else-irrefutable.rs:7:5
+   |
+LL |     let x = 1 else {
+   |     ^^^^^^^^^
+   |
+   = note: this pattern will always match, so the `else` clause is useless
+   = help: consider removing the `else` clause
+
+warning: 2 warnings emitted
+
diff --git a/tests/ui/let-else/let-else-missing-semicolon.rs b/tests/ui/let-else/let-else-missing-semicolon.rs
new file mode 100644
index 00000000000..d87ac90c1a7
--- /dev/null
+++ b/tests/ui/let-else/let-else-missing-semicolon.rs
@@ -0,0 +1,9 @@
+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/tests/ui/let-else/let-else-missing-semicolon.stderr b/tests/ui/let-else/let-else-missing-semicolon.stderr
new file mode 100644
index 00000000000..99029ff33fe
--- /dev/null
+++ b/tests/ui/let-else/let-else-missing-semicolon.stderr
@@ -0,0 +1,18 @@
+error: expected `;`, found keyword `let`
+  --> $DIR/let-else-missing-semicolon.rs:4:6
+   |
+LL |     }
+   |      ^ help: add `;` here
+LL |     let _ = "";
+   |     --- unexpected token
+
+error: expected `;`, found `}`
+  --> $DIR/let-else-missing-semicolon.rs:8:6
+   |
+LL |     }
+   |      ^ help: add `;` here
+LL | }
+   | - unexpected token
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/let-else/let-else-no-double-error.rs b/tests/ui/let-else/let-else-no-double-error.rs
new file mode 100644
index 00000000000..91fcc5d7e91
--- /dev/null
+++ b/tests/ui/let-else/let-else-no-double-error.rs
@@ -0,0 +1,12 @@
+// from rfc2005 test suite
+
+
+
+// Without caching type lookups in FnCtxt.resolve_ty_and_def_ufcs
+// the error below would be reported twice (once when checking
+// for a non-ref pattern, once when processing the pattern).
+
+fn main() {
+    let foo = 22;
+    let u32::XXX = foo else { return }; //~ ERROR: no associated item named `XXX` found for type `u32` in the current scope [E0599]
+}
diff --git a/tests/ui/let-else/let-else-no-double-error.stderr b/tests/ui/let-else/let-else-no-double-error.stderr
new file mode 100644
index 00000000000..941e588b176
--- /dev/null
+++ b/tests/ui/let-else/let-else-no-double-error.stderr
@@ -0,0 +1,9 @@
+error[E0599]: no associated item named `XXX` found for type `u32` in the current scope
+  --> $DIR/let-else-no-double-error.rs:11:14
+   |
+LL |     let u32::XXX = foo else { return };
+   |              ^^^ associated item not found in `u32`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0599`.
diff --git a/tests/ui/let-else/let-else-non-copy.rs b/tests/ui/let-else/let-else-non-copy.rs
new file mode 100644
index 00000000000..08c07dd1a43
--- /dev/null
+++ b/tests/ui/let-else/let-else-non-copy.rs
@@ -0,0 +1,45 @@
+// run-pass
+//
+// This is derived from a change to compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs, in
+// preparation for adopting let-else within the compiler (thanks @est31):
+//
+// ```
+// -    let place = if let mir::VarDebugInfoContents::Place(p) = var.value { p } else { continue };
+// +    let mir::VarDebugInfoContents::Place(place) = var.value else { continue };
+// ```
+//
+// The move was due to mir::Place being Copy, but mir::VarDebugInfoContents not being Copy.
+
+
+
+#[derive(Copy, Clone)]
+struct Copyable;
+
+enum NonCopy {
+    Thing(Copyable),
+    #[allow(unused)]
+    Other,
+}
+
+struct Wrapper {
+    field: NonCopy,
+}
+
+fn let_else() {
+    let vec = vec![Wrapper { field: NonCopy::Thing(Copyable) }];
+    for item in &vec {
+        let NonCopy::Thing(_copyable) = item.field else { continue };
+    }
+}
+
+fn if_let() {
+    let vec = vec![Wrapper { field: NonCopy::Thing(Copyable) }];
+    for item in &vec {
+        let _copyable = if let NonCopy::Thing(copyable) = item.field { copyable } else { continue };
+    }
+}
+
+fn main() {
+    let_else();
+    if_let();
+}
diff --git a/tests/ui/let-else/let-else-non-diverging.rs b/tests/ui/let-else/let-else-non-diverging.rs
new file mode 100644
index 00000000000..a5442dd82f0
--- /dev/null
+++ b/tests/ui/let-else/let-else-non-diverging.rs
@@ -0,0 +1,22 @@
+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
+
+    // Ensure that uninhabited types do not "diverge".
+    // This might be relaxed in the future, but when it is,
+    // it should be an explicitly wanted decision.
+    let Some(x) = Some(1) else { foo::<Uninhabited>() }; //~ ERROR does not diverge
+}
+
+enum Uninhabited {}
+
+fn foo<T>() -> T {
+    panic!()
+}
diff --git a/tests/ui/let-else/let-else-non-diverging.stderr b/tests/ui/let-else/let-else-non-diverging.stderr
new file mode 100644
index 00000000000..78551fcc434
--- /dev/null
+++ b/tests/ui/let-else/let-else-non-diverging.stderr
@@ -0,0 +1,55 @@
+error[E0308]: `else` clause of `let...else` does not diverge
+  --> $DIR/let-else-non-diverging.rs:2:32
+   |
+LL |       let Some(x) = Some(1) else {
+   |  ________________________________^
+LL | |         Some(2)
+LL | |     };
+   | |_____^ expected `!`, found enum `Option`
+   |
+   = note: expected type `!`
+              found enum `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:5:32
+   |
+LL |       let Some(x) = Some(1) else {
+   |  ________________________________^
+LL | |         if 1 == 1 {
+LL | |             panic!();
+LL | |         }
+LL | |     };
+   | |_____^ expected `!`, found `()`
+   |
+   = note:   expected type `!`
+           found unit 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:10:32
+   |
+LL |     let Some(x) = Some(1) else { Some(2) };
+   |                                ^^^^^^^^^^^ expected `!`, found enum `Option`
+   |
+   = note: expected type `!`
+              found enum `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:15:32
+   |
+LL |     let Some(x) = Some(1) else { foo::<Uninhabited>() };
+   |                                ^^^^^^^^^^^^^^^^^^^^^^^^ expected `!`, found enum `Uninhabited`
+   |
+   = note: expected type `!`
+              found enum `Uninhabited`
+   = help: try adding a diverging expression, such as `return` or `panic!(..)`
+   = help: ...or use `match` instead of `let...else`
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/let-else/let-else-ref-bindings-pass.rs b/tests/ui/let-else/let-else-ref-bindings-pass.rs
new file mode 100644
index 00000000000..62fc65731cd
--- /dev/null
+++ b/tests/ui/let-else/let-else-ref-bindings-pass.rs
@@ -0,0 +1,71 @@
+// check-pass
+
+
+#![allow(unused_variables)]
+
+fn ref_() {
+    let bytes: Vec<u8> = b"Hello"[..].to_vec();
+    let some = Some(bytes);
+
+    let Some(ref a) = Some(()) else { return };
+
+    // | ref | type annotation | & |
+    // | --- | --------------- | - |
+    // | x   | x               |   | error
+    // | x   | x               | x | error
+    // |     | x               |   | error
+    // |     | x               | x | error
+    // | x   |                 |   |
+    let Some(ref a) = some else { return }; // OK
+    let b: &[u8] = a;
+
+    // | x   |                 | x |
+    let Some(ref a) = &some else { return }; // OK
+    let b: &[u8] = a;
+
+
+    // |     |                 | x |
+    let Some(a) = &some else { return }; // OK
+    let b: &[u8] = a;
+
+    let Some(a): Option<&[u8]> = some.as_deref() else { return }; // OK
+    let b: &[u8] = a;
+    let Some(ref  a): Option<&[u8]> = some.as_deref() else { return }; // OK
+    let b: &[u8] = a;
+}
+
+fn ref_mut() {
+    // This `ref mut` case had an ICE, see issue #89960
+    let Some(ref mut a) = Some(()) else { return };
+
+    let bytes: Vec<u8> = b"Hello"[..].to_vec();
+    let mut some = Some(bytes);
+
+    // | ref mut | type annotation | &mut |
+    // | ------- | --------------- | ---- |
+    // | x       | x               |      | error
+    // | x       | x               | x    | error
+    // |         | x               |      | error
+    // |         | x               | x    | error
+    // | x       |                 |      |
+    let Some(ref mut a) = some else { return }; // OK
+    let b: &mut [u8] = a;
+
+    // | x       |                 | x    |
+    let Some(ref mut a) = &mut some else { return }; // OK
+    let b: &mut [u8] = a;
+
+    // |         |                 | x    |
+    let Some(a) = &mut some else { return }; // OK
+    let b: &mut [u8] = a;
+
+    let Some(a): Option<&mut [u8]> = some.as_deref_mut() else { return }; // OK
+    let b: &mut [u8] = a;
+    let Some(ref mut a): Option<&mut [u8]> = some.as_deref_mut() else { return }; // OK
+    let b: &mut [u8] = a;
+}
+
+fn main() {
+    ref_();
+    ref_mut();
+}
diff --git a/tests/ui/let-else/let-else-ref-bindings.rs b/tests/ui/let-else/let-else-ref-bindings.rs
new file mode 100644
index 00000000000..687e235d47e
--- /dev/null
+++ b/tests/ui/let-else/let-else-ref-bindings.rs
@@ -0,0 +1,62 @@
+#![allow(unused_variables)]
+
+
+fn ref_() {
+    let bytes: Vec<u8> = b"Hello"[..].to_vec();
+    let some = Some(bytes);
+
+    let Some(ref a) = Some(()) else { return };
+
+    // | ref | type annotation | & |
+    // | --- | --------------- | - |
+    // | x   |                 |   | OK
+    // | x   |                 | x | OK
+    // |     |                 | x | OK
+    // | x   | x               |   |
+    let Some(ref a): Option<&[u8]> = some else { return }; //~ ERROR mismatched types
+    let b: & [u8] = a;
+
+    // | x   | x               | x |
+    let Some(ref a): Option<&[u8]> = &some else { return }; //~ ERROR mismatched types
+    let b: & [u8] = a;
+
+    // |     | x               |   |
+    let Some(a): Option<&[u8]> = some else { return }; //~ ERROR mismatched types
+    let b: &[u8] = a;
+    // |     | x               | x |
+    let Some(a): Option<&[u8]> = &some else { return }; //~ ERROR mismatched types
+    let b: &[u8] = a;
+}
+
+fn ref_mut() {
+    // This `ref mut` case had an ICE, see issue #89960
+    let Some(ref mut a) = Some(()) else { return };
+
+    let bytes: Vec<u8> = b"Hello"[..].to_vec();
+    let mut some = Some(bytes);
+
+    // | ref mut | type annotation | &mut |
+    // | ------- | --------------- | ---- |
+    // | x       |                 |      | OK
+    // | x       |                 | x    | OK
+    // |         |                 | x    | OK
+    // | x       | x               |      |
+    let Some(ref mut a): Option<&mut [u8]> = some else { return }; //~ ERROR mismatched types
+    let b: &mut [u8] = a;
+
+    // | x       | x               | x    | (nope)
+    let Some(ref mut a): Option<&mut [u8]> = &mut some else { return }; //~ ERROR mismatched types
+    let b: &mut [u8] = a;
+
+    // |         | x               |      |
+    let Some(a): Option<&mut [u8]> = some else { return }; //~ ERROR mismatched types
+    let b: &mut [u8] = a;
+    // |         | x               | x    |
+    let Some(a): Option<&mut [u8]> = &mut some else { return }; //~ ERROR mismatched types
+    let b: &mut [u8] = a;
+}
+
+fn main() {
+    ref_();
+    ref_mut();
+}
diff --git a/tests/ui/let-else/let-else-ref-bindings.stderr b/tests/ui/let-else/let-else-ref-bindings.stderr
new file mode 100644
index 00000000000..56b9e073330
--- /dev/null
+++ b/tests/ui/let-else/let-else-ref-bindings.stderr
@@ -0,0 +1,83 @@
+error[E0308]: mismatched types
+  --> $DIR/let-else-ref-bindings.rs:16:38
+   |
+LL |     let Some(ref a): Option<&[u8]> = some else { return };
+   |                                      ^^^^ expected `&[u8]`, found struct `Vec`
+   |
+   = note: expected enum `Option<&[u8]>`
+              found enum `Option<Vec<u8>>`
+
+error[E0308]: mismatched types
+  --> $DIR/let-else-ref-bindings.rs:20:38
+   |
+LL |     let Some(ref a): Option<&[u8]> = &some else { return };
+   |                                      ^^^^^ expected enum `Option`, found `&Option<Vec<u8>>`
+   |
+   = note:   expected enum `Option<&[u8]>`
+           found reference `&Option<Vec<u8>>`
+
+error[E0308]: mismatched types
+  --> $DIR/let-else-ref-bindings.rs:24:34
+   |
+LL |     let Some(a): Option<&[u8]> = some else { return };
+   |                  -------------   ^^^^ expected `&[u8]`, found struct `Vec`
+   |                  |
+   |                  expected due to this
+   |
+   = note: expected enum `Option<&[u8]>`
+              found enum `Option<Vec<u8>>`
+
+error[E0308]: mismatched types
+  --> $DIR/let-else-ref-bindings.rs:27:34
+   |
+LL |     let Some(a): Option<&[u8]> = &some else { return };
+   |                  -------------   ^^^^^ expected enum `Option`, found `&Option<Vec<u8>>`
+   |                  |
+   |                  expected due to this
+   |
+   = note:   expected enum `Option<&[u8]>`
+           found reference `&Option<Vec<u8>>`
+
+error[E0308]: mismatched types
+  --> $DIR/let-else-ref-bindings.rs:44:46
+   |
+LL |     let Some(ref mut a): Option<&mut [u8]> = some else { return };
+   |                                              ^^^^ expected `&mut [u8]`, found struct `Vec`
+   |
+   = note: expected enum `Option<&mut [u8]>`
+              found enum `Option<Vec<u8>>`
+
+error[E0308]: mismatched types
+  --> $DIR/let-else-ref-bindings.rs:48:46
+   |
+LL |     let Some(ref mut a): Option<&mut [u8]> = &mut some else { return };
+   |                                              ^^^^^^^^^ expected enum `Option`, found mutable reference
+   |
+   = note:           expected enum `Option<&mut [u8]>`
+           found mutable reference `&mut Option<Vec<u8>>`
+
+error[E0308]: mismatched types
+  --> $DIR/let-else-ref-bindings.rs:52:38
+   |
+LL |     let Some(a): Option<&mut [u8]> = some else { return };
+   |                  -----------------   ^^^^ expected `&mut [u8]`, found struct `Vec`
+   |                  |
+   |                  expected due to this
+   |
+   = note: expected enum `Option<&mut [u8]>`
+              found enum `Option<Vec<u8>>`
+
+error[E0308]: mismatched types
+  --> $DIR/let-else-ref-bindings.rs:55:38
+   |
+LL |     let Some(a): Option<&mut [u8]> = &mut some else { return };
+   |                  -----------------   ^^^^^^^^^ expected enum `Option`, found mutable reference
+   |                  |
+   |                  expected due to this
+   |
+   = note:           expected enum `Option<&mut [u8]>`
+           found mutable reference `&mut Option<Vec<u8>>`
+
+error: aborting due to 8 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/let-else/let-else-run-pass.rs b/tests/ui/let-else/let-else-run-pass.rs
new file mode 100644
index 00000000000..a0fb6c683f8
--- /dev/null
+++ b/tests/ui/let-else/let-else-run-pass.rs
@@ -0,0 +1,35 @@
+// run-pass
+
+
+
+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/tests/ui/let-else/let-else-scope.rs b/tests/ui/let-else/let-else-scope.rs
new file mode 100644
index 00000000000..78a67769ef2
--- /dev/null
+++ b/tests/ui/let-else/let-else-scope.rs
@@ -0,0 +1,5 @@
+fn main() {
+    let Some(x) = Some(2) else {
+        panic!("{}", x); //~ ERROR cannot find value `x` in this scope
+    };
+}
diff --git a/tests/ui/let-else/let-else-scope.stderr b/tests/ui/let-else/let-else-scope.stderr
new file mode 100644
index 00000000000..3b4f0982940
--- /dev/null
+++ b/tests/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:3: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`.
diff --git a/tests/ui/let-else/let-else-slicing-error.rs b/tests/ui/let-else/let-else-slicing-error.rs
new file mode 100644
index 00000000000..25770094bce
--- /dev/null
+++ b/tests/ui/let-else/let-else-slicing-error.rs
@@ -0,0 +1,9 @@
+// issue #92069
+
+
+fn main() {
+    let nums = vec![5, 4, 3, 2, 1];
+    let [x, y] = nums else { //~ ERROR expected an array or slice
+        return;
+    };
+}
diff --git a/tests/ui/let-else/let-else-slicing-error.stderr b/tests/ui/let-else/let-else-slicing-error.stderr
new file mode 100644
index 00000000000..064025e0345
--- /dev/null
+++ b/tests/ui/let-else/let-else-slicing-error.stderr
@@ -0,0 +1,11 @@
+error[E0529]: expected an array or slice, found `Vec<{integer}>`
+  --> $DIR/let-else-slicing-error.rs:6:9
+   |
+LL |     let [x, y] = nums else {
+   |         ^^^^^^   ---- help: consider slicing here: `nums[..]`
+   |         |
+   |         pattern cannot match with input type `Vec<{integer}>`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0529`.
diff --git a/tests/ui/let-else/let-else-source-expr-nomove-pass.rs b/tests/ui/let-else/let-else-source-expr-nomove-pass.rs
new file mode 100644
index 00000000000..ee378abcf2b
--- /dev/null
+++ b/tests/ui/let-else/let-else-source-expr-nomove-pass.rs
@@ -0,0 +1,17 @@
+// run-pass
+// issue #89688
+
+
+
+fn example_let_else(value: Option<String>) {
+    let Some(inner) = value else {
+        println!("other: {:?}", value); // OK
+        return;
+    };
+    println!("inner: {}", inner);
+}
+
+fn main() {
+    example_let_else(Some("foo".into()));
+    example_let_else(None);
+}
diff --git a/tests/ui/let-else/let-else-temp-borrowck.rs b/tests/ui/let-else/let-else-temp-borrowck.rs
new file mode 100644
index 00000000000..6b4642d2f98
--- /dev/null
+++ b/tests/ui/let-else/let-else-temp-borrowck.rs
@@ -0,0 +1,26 @@
+// run-pass
+//
+// from issue #93951, where borrowck complained the temporary that `foo(&x)` was stored in was to
+// be dropped sometime after `x` was. It then suggested adding a semicolon that was already there.
+
+
+use std::fmt::Debug;
+
+fn foo<'a>(x: &'a str) -> Result<impl Debug + 'a, ()> {
+    Ok(x)
+}
+
+fn let_else() {
+    let x = String::from("Hey");
+    let Ok(_) = foo(&x) else { return };
+}
+
+fn if_let() {
+    let x = String::from("Hey");
+    let _ = if let Ok(s) = foo(&x) { s } else { return };
+}
+
+fn main() {
+    let_else();
+    if_let();
+}
diff --git a/tests/ui/let-else/let-else-temporary-lifetime.rs b/tests/ui/let-else/let-else-temporary-lifetime.rs
new file mode 100644
index 00000000000..c23eaa997fe
--- /dev/null
+++ b/tests/ui/let-else/let-else-temporary-lifetime.rs
@@ -0,0 +1,99 @@
+// run-pass
+// compile-flags: -Zvalidate-mir
+
+use std::fmt::Display;
+use std::rc::Rc;
+use std::sync::atomic::{AtomicU8, Ordering};
+
+static TRACKER: AtomicU8 = AtomicU8::new(0);
+
+#[derive(Default)]
+struct Droppy {
+    inner: u32,
+}
+
+impl Drop for Droppy {
+    fn drop(&mut self) {
+        TRACKER.store(1, Ordering::Release);
+        println!("I've been dropped");
+    }
+}
+
+fn foo<'a>(x: &'a str) -> Result<impl Display + 'a, ()> {
+    Ok(x)
+}
+
+fn main() {
+    assert_eq!(TRACKER.load(Ordering::Acquire), 0);
+    let 0 = Droppy::default().inner else { return };
+    assert_eq!(TRACKER.load(Ordering::Acquire), 1);
+    println!("Should have dropped 👆");
+
+    {
+        // cf. https://github.com/rust-lang/rust/pull/99518#issuecomment-1191520030
+        struct Foo<'a>(&'a mut u32);
+
+        impl<'a> Drop for Foo<'a> {
+            fn drop(&mut self) {
+                *self.0 = 0;
+            }
+        }
+        let mut foo = 0;
+        let Foo(0) = Foo(&mut foo) else {
+            *&mut foo = 1;
+            todo!()
+        };
+    }
+    {
+        let x = String::from("Hey");
+
+        let Ok(s) = foo(&x) else { panic!() };
+        assert_eq!(s.to_string(), x);
+    }
+    {
+        // test let-else drops temps after statement
+        let rc = Rc::new(0);
+        let 0 = *rc.clone() else { unreachable!() };
+        Rc::try_unwrap(rc).unwrap();
+    }
+    {
+        let mut rc = Rc::new(0);
+        let mut i = 0;
+        loop {
+            if i > 3 {
+                break;
+            }
+            let 1 = *rc.clone() else {
+                if let Ok(v) = Rc::try_unwrap(rc) {
+                    rc = Rc::new(v);
+                } else {
+                    panic!()
+                }
+                i += 1;
+                continue
+            };
+        }
+    }
+    {
+        fn must_pass() {
+            let rc = Rc::new(());
+            let &None = &Some(Rc::clone(&rc)) else {
+                Rc::try_unwrap(rc).unwrap();
+                return;
+            };
+            unreachable!();
+        }
+        must_pass();
+    }
+    {
+        // test let-else drops temps before else block
+        // NOTE: this test has to be the last block in the `main`
+        // body.
+        let rc = Rc::new(0);
+        let 1 = *rc.clone() else {
+            Rc::try_unwrap(rc).unwrap();
+            return;
+        };
+        unreachable!();
+    }
+}
diff --git a/tests/ui/let-else/let-else-then-diverge.rs b/tests/ui/let-else/let-else-then-diverge.rs
new file mode 100644
index 00000000000..1a75310c94f
--- /dev/null
+++ b/tests/ui/let-else/let-else-then-diverge.rs
@@ -0,0 +1,17 @@
+// popped up in #94012, where an alternative desugaring was
+// causing unreachable code errors
+
+#![deny(unused_variables)]
+#![deny(unreachable_code)]
+
+fn let_else_diverge() -> bool {
+    let Some(_) = Some("test") else {
+        let x = 5; //~ ERROR unused variable: `x`
+        return false;
+    };
+    return true;
+}
+
+fn main() {
+    let_else_diverge();
+}
diff --git a/tests/ui/let-else/let-else-then-diverge.stderr b/tests/ui/let-else/let-else-then-diverge.stderr
new file mode 100644
index 00000000000..470a11d4769
--- /dev/null
+++ b/tests/ui/let-else/let-else-then-diverge.stderr
@@ -0,0 +1,14 @@
+error: unused variable: `x`
+  --> $DIR/let-else-then-diverge.rs:9:13
+   |
+LL |         let x = 5;
+   |             ^ help: if this is intentional, prefix it with an underscore: `_x`
+   |
+note: the lint level is defined here
+  --> $DIR/let-else-then-diverge.rs:4:9
+   |
+LL | #![deny(unused_variables)]
+   |         ^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/tests/ui/let-else/let-else.rs b/tests/ui/let-else/let-else.rs
new file mode 100644
index 00000000000..3505533e63f
--- /dev/null
+++ b/tests/ui/let-else/let-else.rs
@@ -0,0 +1,8 @@
+// run-pass
+
+fn main() {
+    let Some(x) = Some(1) else {
+        return;
+    };
+    assert_eq!(x, 1);
+}