summaryrefslogtreecommitdiffstats
path: root/tests/ui/let-else
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:19:13 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:19:13 +0000
commit218caa410aa38c29984be31a5229b9fa717560ee (patch)
treec54bd55eeb6e4c508940a30e94c0032fbd45d677 /tests/ui/let-else
parentReleasing progress-linux version 1.67.1+dfsg1-1~progress7.99u1. (diff)
downloadrustc-218caa410aa38c29984be31a5229b9fa717560ee.tar.xz
rustc-218caa410aa38c29984be31a5229b9fa717560ee.zip
Merging upstream version 1.68.2+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
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 000000000..a3921b803
--- /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 000000000..f5f9b2f5f
--- /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 000000000..7369b4938
--- /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 000000000..f76dfc15b
--- /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 000000000..0cb97acee
--- /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 000000000..5b164f347
--- /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 000000000..33acb6c6a
--- /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 000000000..3b2b9066c
--- /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 000000000..bbb1c7bea
--- /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 000000000..05b8a9169
--- /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 000000000..955f33ee1
--- /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 000000000..065787cab
--- /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 000000000..1524d0102
--- /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 000000000..023fab8fe
--- /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 000000000..b0a6264a1
--- /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 000000000..a153b3af0
--- /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 000000000..45f2b6b3b
--- /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 000000000..ff2d9f240
--- /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 000000000..dd1365a9e
--- /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 000000000..53ac398b8
--- /dev/null
+++ b/tests/ui/let-else/let-else-bindings.rs
@@ -0,0 +1,75 @@
+// run-pass
+// adapted from tests/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 000000000..20e558ca9
--- /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 000000000..f88179a94
--- /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 000000000..edee65762
--- /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 000000000..a75c770dd
--- /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 000000000..5603b946f
--- /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 000000000..cb01e4c18
--- /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 000000000..713fd986e
--- /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 000000000..bdecbf708
--- /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 000000000..60fdf825a
--- /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 000000000..052a5a8c7
--- /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 000000000..bf78a079c
--- /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 000000000..d1f1a69bf
--- /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 000000000..7d6cb2386
--- /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 000000000..e91e5de84
--- /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 000000000..01cf2f73e
--- /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 000000000..e8c54ca7a
--- /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 000000000..c63fd61c5
--- /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 000000000..f4b338eb0
--- /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 000000000..73d4e5f34
--- /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 000000000..d87ac90c1
--- /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 000000000..99029ff33
--- /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 000000000..91fcc5d7e
--- /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 000000000..941e588b1
--- /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 000000000..08c07dd1a
--- /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 000000000..a5442dd82
--- /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 000000000..78551fcc4
--- /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 000000000..62fc65731
--- /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 000000000..687e235d4
--- /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 000000000..56b9e0733
--- /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 000000000..a0fb6c683
--- /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 000000000..78a67769e
--- /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 000000000..3b4f09829
--- /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 000000000..25770094b
--- /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 000000000..064025e03
--- /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 000000000..ee378abcf
--- /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 000000000..6b4642d2f
--- /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 000000000..c23eaa997
--- /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 000000000..1a75310c9
--- /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 000000000..470a11d47
--- /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 000000000..3505533e6
--- /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);
+}