From 218caa410aa38c29984be31a5229b9fa717560ee Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:19:13 +0200 Subject: Merging upstream version 1.68.2+dfsg1. Signed-off-by: Daniel Baumann --- tests/ui/let-else/const-fn.rs | 18 ++ tests/ui/let-else/issue-100103.rs | 15 ++ tests/ui/let-else/issue-102317.rs | 20 ++ tests/ui/let-else/issue-94176.rs | 10 + tests/ui/let-else/issue-94176.stderr | 19 ++ tests/ui/let-else/issue-99975.rs | 20 ++ tests/ui/let-else/let-else-allow-in-expr.rs | 28 +++ tests/ui/let-else/let-else-allow-in-expr.stderr | 20 ++ tests/ui/let-else/let-else-allow-unused.rs | 15 ++ tests/ui/let-else/let-else-allow-unused.stderr | 14 ++ .../let-else-binding-explicit-mut-annotated.rs | 16 ++ .../let-else-binding-explicit-mut-annotated.stderr | 25 ++ .../let-else-binding-explicit-mut-borrow.rs | 13 + .../let-else-binding-explicit-mut-borrow.stderr | 9 + .../let-else/let-else-binding-explicit-mut-pass.rs | 13 + tests/ui/let-else/let-else-binding-explicit-mut.rs | 20 ++ .../let-else/let-else-binding-explicit-mut.stderr | 21 ++ tests/ui/let-else/let-else-binding-immutable.rs | 10 + .../ui/let-else/let-else-binding-immutable.stderr | 9 + tests/ui/let-else/let-else-bindings.rs | 75 ++++++ tests/ui/let-else/let-else-bool-binop-init.fixed | 8 + tests/ui/let-else/let-else-bool-binop-init.rs | 8 + tests/ui/let-else/let-else-bool-binop-init.stderr | 24 ++ tests/ui/let-else/let-else-brace-before-else.fixed | 26 ++ tests/ui/let-else/let-else-brace-before-else.rs | 26 ++ .../ui/let-else/let-else-brace-before-else.stderr | 46 ++++ tests/ui/let-else/let-else-check.rs | 17 ++ tests/ui/let-else/let-else-check.stderr | 20 ++ .../let-else/let-else-deref-coercion-annotated.rs | 77 ++++++ tests/ui/let-else/let-else-deref-coercion.rs | 75 ++++++ tests/ui/let-else/let-else-deref-coercion.stderr | 19 ++ tests/ui/let-else/let-else-destructuring.rs | 17 ++ tests/ui/let-else/let-else-destructuring.stderr | 17 ++ tests/ui/let-else/let-else-drop-order.rs | 270 +++++++++++++++++++++ tests/ui/let-else/let-else-drop-order.run.stdout | 51 ++++ tests/ui/let-else/let-else-if.rs | 8 + tests/ui/let-else/let-else-if.stderr | 17 ++ tests/ui/let-else/let-else-irrefutable.rs | 11 + tests/ui/let-else/let-else-irrefutable.stderr | 21 ++ tests/ui/let-else/let-else-missing-semicolon.rs | 9 + .../ui/let-else/let-else-missing-semicolon.stderr | 18 ++ tests/ui/let-else/let-else-no-double-error.rs | 12 + tests/ui/let-else/let-else-no-double-error.stderr | 9 + tests/ui/let-else/let-else-non-copy.rs | 45 ++++ tests/ui/let-else/let-else-non-diverging.rs | 22 ++ tests/ui/let-else/let-else-non-diverging.stderr | 55 +++++ tests/ui/let-else/let-else-ref-bindings-pass.rs | 71 ++++++ tests/ui/let-else/let-else-ref-bindings.rs | 62 +++++ tests/ui/let-else/let-else-ref-bindings.stderr | 83 +++++++ tests/ui/let-else/let-else-run-pass.rs | 35 +++ tests/ui/let-else/let-else-scope.rs | 5 + tests/ui/let-else/let-else-scope.stderr | 9 + tests/ui/let-else/let-else-slicing-error.rs | 9 + tests/ui/let-else/let-else-slicing-error.stderr | 11 + .../let-else/let-else-source-expr-nomove-pass.rs | 17 ++ tests/ui/let-else/let-else-temp-borrowck.rs | 26 ++ tests/ui/let-else/let-else-temporary-lifetime.rs | 99 ++++++++ tests/ui/let-else/let-else-then-diverge.rs | 17 ++ tests/ui/let-else/let-else-then-diverge.stderr | 14 ++ tests/ui/let-else/let-else.rs | 8 + 60 files changed, 1784 insertions(+) create mode 100644 tests/ui/let-else/const-fn.rs create mode 100644 tests/ui/let-else/issue-100103.rs create mode 100644 tests/ui/let-else/issue-102317.rs create mode 100644 tests/ui/let-else/issue-94176.rs create mode 100644 tests/ui/let-else/issue-94176.stderr create mode 100644 tests/ui/let-else/issue-99975.rs create mode 100644 tests/ui/let-else/let-else-allow-in-expr.rs create mode 100644 tests/ui/let-else/let-else-allow-in-expr.stderr create mode 100644 tests/ui/let-else/let-else-allow-unused.rs create mode 100644 tests/ui/let-else/let-else-allow-unused.stderr create mode 100644 tests/ui/let-else/let-else-binding-explicit-mut-annotated.rs create mode 100644 tests/ui/let-else/let-else-binding-explicit-mut-annotated.stderr create mode 100644 tests/ui/let-else/let-else-binding-explicit-mut-borrow.rs create mode 100644 tests/ui/let-else/let-else-binding-explicit-mut-borrow.stderr create mode 100644 tests/ui/let-else/let-else-binding-explicit-mut-pass.rs create mode 100644 tests/ui/let-else/let-else-binding-explicit-mut.rs create mode 100644 tests/ui/let-else/let-else-binding-explicit-mut.stderr create mode 100644 tests/ui/let-else/let-else-binding-immutable.rs create mode 100644 tests/ui/let-else/let-else-binding-immutable.stderr create mode 100644 tests/ui/let-else/let-else-bindings.rs create mode 100644 tests/ui/let-else/let-else-bool-binop-init.fixed create mode 100644 tests/ui/let-else/let-else-bool-binop-init.rs create mode 100644 tests/ui/let-else/let-else-bool-binop-init.stderr create mode 100644 tests/ui/let-else/let-else-brace-before-else.fixed create mode 100644 tests/ui/let-else/let-else-brace-before-else.rs create mode 100644 tests/ui/let-else/let-else-brace-before-else.stderr create mode 100644 tests/ui/let-else/let-else-check.rs create mode 100644 tests/ui/let-else/let-else-check.stderr create mode 100644 tests/ui/let-else/let-else-deref-coercion-annotated.rs create mode 100644 tests/ui/let-else/let-else-deref-coercion.rs create mode 100644 tests/ui/let-else/let-else-deref-coercion.stderr create mode 100644 tests/ui/let-else/let-else-destructuring.rs create mode 100644 tests/ui/let-else/let-else-destructuring.stderr create mode 100644 tests/ui/let-else/let-else-drop-order.rs create mode 100644 tests/ui/let-else/let-else-drop-order.run.stdout create mode 100644 tests/ui/let-else/let-else-if.rs create mode 100644 tests/ui/let-else/let-else-if.stderr create mode 100644 tests/ui/let-else/let-else-irrefutable.rs create mode 100644 tests/ui/let-else/let-else-irrefutable.stderr create mode 100644 tests/ui/let-else/let-else-missing-semicolon.rs create mode 100644 tests/ui/let-else/let-else-missing-semicolon.stderr create mode 100644 tests/ui/let-else/let-else-no-double-error.rs create mode 100644 tests/ui/let-else/let-else-no-double-error.stderr create mode 100644 tests/ui/let-else/let-else-non-copy.rs create mode 100644 tests/ui/let-else/let-else-non-diverging.rs create mode 100644 tests/ui/let-else/let-else-non-diverging.stderr create mode 100644 tests/ui/let-else/let-else-ref-bindings-pass.rs create mode 100644 tests/ui/let-else/let-else-ref-bindings.rs create mode 100644 tests/ui/let-else/let-else-ref-bindings.stderr create mode 100644 tests/ui/let-else/let-else-run-pass.rs create mode 100644 tests/ui/let-else/let-else-scope.rs create mode 100644 tests/ui/let-else/let-else-scope.stderr create mode 100644 tests/ui/let-else/let-else-slicing-error.rs create mode 100644 tests/ui/let-else/let-else-slicing-error.stderr create mode 100644 tests/ui/let-else/let-else-source-expr-nomove-pass.rs create mode 100644 tests/ui/let-else/let-else-temp-borrowck.rs create mode 100644 tests/ui/let-else/let-else-temporary-lifetime.rs create mode 100644 tests/ui/let-else/let-else-then-diverge.rs create mode 100644 tests/ui/let-else/let-else-then-diverge.stderr create mode 100644 tests/ui/let-else/let-else.rs (limited to 'tests/ui/let-else') 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 { + 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 = 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::::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) -> Option { //~ 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) -> Option { + | ---- ^^^^^^^^^^^ expected enum `Option`, found `()` + | | + | implicitly returns `()` as its body has no tail or `return` expression + | + = note: expected enum `Option` + 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 { + 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 = ({ + let x = 1; //~ ERROR unused variable: `x` + Some(1) + }) else { + return; + }; + + #[allow(unused_variables)] + let Some(_): Option = ({ + let x = 1; + Some(1) + }) else { + return; + }; + + let Some(_): Option = ({ + #[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 = &&Some(5i32) else { return }; //~ ERROR mismatched types + *n += 1; + let _ = n; + + let Some(n): &mut Option = &&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 = &&Some(5i32) else { return }; + | ---------------- ^^^^^^^^^^^^ types differ in mutability + | | + | expected due to this + | + = note: expected mutable reference `&mut Option` + found reference `&&Option` + +error[E0308]: mismatched types + --> $DIR/let-else-binding-explicit-mut-annotated.rs:13:37 + | +LL | let Some(n): &mut Option = &&mut Some(5i32) else { return }; + | ---------------- ^^^^^^^^^^^^^^^^ types differ in mutability + | | + | expected due to this + | + = note: expected mutable reference `&mut Option` + found reference `&&mut Option` + +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 = &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 = &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 = &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 { + 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 { + 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 = Some(Default::default()) else { + let x = 1; // OK + return; + }; + + let Some(_): Option = 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 { + 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 { + 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 ... 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: ... 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>>>); + +impl DropAccountant { + fn new() -> Self { + Self(Default::default()) + } + fn build_droppy(&self, v: u32) -> Droppy { + Droppy(self.clone(), v) + } + fn build_droppy_enum_none(&self, _v: u32) -> ((), DroppyEnum) { + ((), 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!( + "| {: Vec { + std::iter::once(sl[0].clone()) + .chain(sl[1..].chunks(2).map(|c| c.join(","))) + .collect::>() +} + +struct Droppy(DropAccountant, T); + +impl Drop for Droppy { + fn drop(&mut self) { + self.0.push("drop"); + } +} + +#[allow(dead_code)] +enum DroppyEnum { + Some(DropAccountant, T), + None(DropAccountant), +} + +impl Drop for DroppyEnum { + 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::() }; //~ ERROR does not diverge +} + +enum Uninhabited {} + +fn foo() -> 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::() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^ 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 = 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 = 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 = 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 = 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>` + +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>` + | + = note: expected enum `Option<&[u8]>` + found reference `&Option>` + +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>` + +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>` + | | + | expected due to this + | + = note: expected enum `Option<&[u8]>` + found reference `&Option>` + +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>` + +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>` + +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>` + +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>` + +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) { + 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 { + 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 { + 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); +} -- cgit v1.2.3