| #![warn(clippy::manual_let_else)] | |
| // Ensure that we don't conflict with match -> if let lints | |
| #![deny(clippy::single_match, clippy::single_match_else)] | |
| #![allow(irrefutable_let_patterns)] | |
| #![expect(clippy::let_unit_value, clippy::redundant_at_rest_pattern)] | |
| fn f() -> Result<u32, u32> { | |
| Ok(0) | |
| } | |
| fn g() -> Option<()> { | |
| None | |
| } | |
| fn h() -> (Option<()>, Option<()>) { | |
| (None, None) | |
| } | |
| enum Variant { | |
| Foo, | |
| Bar(u32), | |
| Baz(u32), | |
| } | |
| fn build_enum() -> Variant { | |
| Variant::Foo | |
| } | |
| fn main() {} | |
| fn fire() { | |
| let Some(v) = g() else { return }; | |
| let Some(v) = g() else { return }; | |
| loop { | |
| // More complex pattern for the identity arm and diverging arm | |
| let ((Some(v), None) | (None, Some(v))) = h() else { continue }; | |
| // Custom enums are supported as long as the "else" arm is a simple _ | |
| let (Variant::Bar(v) | Variant::Baz(v)) = build_enum() else { continue }; | |
| } | |
| // There is a _ in the diverging arm | |
| // TODO also support unused bindings aka _v | |
| let Ok(v) = f() else { return }; | |
| // Err(()) is an allowed pattern | |
| let Ok(v) = f().map_err(|_| ()) else { return }; | |
| let f = Variant::Bar(1); | |
| let (Variant::Bar(_value) | Variant::Baz(_value)) = f else { return }; | |
| let Some(Variant::Bar(_value) | Variant::Baz(_value)) = Some(build_enum()) else { return }; | |
| let data = [1_u8, 2, 3, 4, 0, 0, 0, 0]; | |
| let ([data @ .., 0, 0, 0, 0] | [data @ .., 0, 0] | [data @ .., 0]) = data.as_slice() else { return }; | |
| } | |
| fn not_fire() { | |
| // Multiple diverging arms | |
| let v = match h() { | |
| _ => panic!(), | |
| (None, Some(_v)) => return, | |
| (Some(v), None) => v, | |
| }; | |
| // Multiple identity arms | |
| let v = match h() { | |
| _ => panic!(), | |
| (None, Some(v)) => v, | |
| (Some(v), None) => v, | |
| }; | |
| // No diverging arm at all, only identity arms. | |
| // This is no case for let else, but destructuring assignment. | |
| let v = match f() { | |
| Ok(v) => v, | |
| Err(e) => e, | |
| }; | |
| // The identity arm has a guard | |
| let v = match g() { | |
| Some(v) if g().is_none() => v, | |
| _ => return, | |
| }; | |
| // The diverging arm has a guard | |
| let v = match f() { | |
| Err(v) if v > 0 => panic!(), | |
| Ok(v) | Err(v) => v, | |
| }; | |
| // The diverging arm creates a binding | |
| let v = match f() { | |
| Ok(v) => v, | |
| Err(e) => panic!("error: {e}"), | |
| }; | |
| // Custom enum where the diverging arm | |
| // explicitly mentions the variant | |
| let v = match build_enum() { | |
| Variant::Foo => return, | |
| Variant::Bar(v) | Variant::Baz(v) => v, | |
| }; | |
| // The custom enum is surrounded by an Err() | |
| let v = match Err(build_enum()) { | |
| Ok(v) | Err(Variant::Bar(v) | Variant::Baz(v)) => v, | |
| Err(Variant::Foo) => return, | |
| }; | |
| // Issue 10241 | |
| // The non-divergent arm arrives in second position and | |
| // may cover values already matched in the first arm. | |
| let v = match h() { | |
| (Some(_), Some(_)) | (None, None) => return, | |
| (Some(v), _) | (None, Some(v)) => v, | |
| }; | |
| let v = match build_enum() { | |
| _ => return, | |
| Variant::Bar(v) | Variant::Baz(v) => v, | |
| }; | |
| let data = [1_u8, 2, 3, 4, 0, 0, 0, 0]; | |
| let data = match data.as_slice() { | |
| [] | [0, 0] => return, | |
| [data @ .., 0, 0, 0, 0] | [data @ .., 0, 0] | [data @ ..] => data, | |
| }; | |
| } | |
| fn issue11579() { | |
| let Some(msg) = Some("hi") else { unreachable!("can't happen") }; | |
| } | |
| #[derive(Clone, Copy)] | |
| struct Issue9939<T> { | |
| avalanche: T, | |
| } | |
| fn issue9939() { | |
| let issue = Some(Issue9939 { avalanche: 1 }); | |
| let Some(Issue9939 { avalanche: tornado }) = issue else { unreachable!("can't happen") }; | |
| let issue = Some(Issue9939 { avalanche: true }); | |
| let Some(Issue9939 { avalanche: acid_rain }) = issue else { unreachable!("can't happen") }; | |
| assert_eq!(tornado, 1); | |
| assert!(acid_rain); | |
| // without shadowing | |
| let _x @ Some(Issue9939 { avalanche: _y }) = issue else { unreachable!("can't happen") }; | |
| // with shadowing | |
| let Some(Issue9939 { avalanche: _x }) = issue else { unreachable!("can't happen") }; | |
| } | |
| #[derive(Clone, Copy)] | |
| struct Issue9939b<T, U> { | |
| earthquake: T, | |
| hurricane: U, | |
| } | |
| fn issue9939b() { | |
| let issue = Some(Issue9939b { | |
| earthquake: true, | |
| hurricane: 1, | |
| }); | |
| let issue @ Some(Issue9939b { earthquake: flood, hurricane: drought }) = issue else { unreachable!("can't happen") }; | |
| assert_eq!(drought, 1); | |
| assert!(flood); | |
| assert!(issue.is_some()); | |
| // without shadowing | |
| let _x @ Some(Issue9939b { earthquake: erosion, hurricane: _y }) = issue else { unreachable!("can't happen") }; | |
| assert!(erosion); | |
| // with shadowing | |
| let Some(Issue9939b { earthquake: erosion, hurricane: _x }) = issue else { unreachable!("can't happen") }; | |
| assert!(erosion); | |
| } | |
| mod issue16433 { | |
| // https://github.com/rust-lang/rust-clippy/issues/16433 | |
| struct A { | |
| a: u32, | |
| b: u32, | |
| } | |
| fn foo() { | |
| let a = A { a: 1, b: 1 }; | |
| let A { a: first_arg, .. } = a else { return }; | |
| } | |
| } |