diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
commit | 698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch) | |
tree | 173a775858bd501c378080a10dca74132f05bc50 /vendor/if_chain/src/lib.rs | |
parent | Initial commit. (diff) | |
download | rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.tar.xz rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.zip |
Adding upstream version 1.64.0+dfsg1.upstream/1.64.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/if_chain/src/lib.rs')
-rw-r--r-- | vendor/if_chain/src/lib.rs | 314 |
1 files changed, 314 insertions, 0 deletions
diff --git a/vendor/if_chain/src/lib.rs b/vendor/if_chain/src/lib.rs new file mode 100644 index 000000000..e72f7aa88 --- /dev/null +++ b/vendor/if_chain/src/lib.rs @@ -0,0 +1,314 @@ +//! This crate provides a single macro called `if_chain!`. +//! +//! `if_chain!` lets you write long chains of nested `if` and `if let` +//! statements without the associated rightward drift. It also supports multiple +//! patterns (e.g. `if let Foo(a) | Bar(a) = b`) in places where Rust would +//! normally not allow them. +//! +//! See the associated [blog post] for the background behind this crate. +//! +//! [blog post]: https://lambda.xyz/blog/if-chain +//! +//! # Note about recursion limits +//! +//! If you run into "recursion limit reached" errors while using this macro, try +//! adding +//! +//! ```rust,ignore +//! #![recursion_limit = "1000"] +//! ``` +//! +//! to the top of your crate. +//! +//! # Examples +//! +//! ## Quick start +//! +//! ```rust,ignore +//! if_chain! { +//! if let Some(y) = x; +//! if y.len() == 2; +//! if let Some(z) = y; +//! then { +//! do_stuff_with(z); +//! } +//! } +//! ``` +//! +//! becomes +//! +//! ```rust,ignore +//! if let Some(y) = x { +//! if y.len() == 2 { +//! if let Some(z) = y { +//! do_stuff_with(z); +//! } +//! } +//! } +//! ``` +//! +//! ## Fallback values with `else` +//! +//! ```rust,ignore +//! if_chain! { +//! if let Some(y) = x; +//! if let Some(z) = y; +//! then { +//! do_stuff_with(z) +//! } else { +//! do_something_else() +//! } +//! } +//! ``` +//! +//! becomes +//! +//! ```rust,ignore +//! if let Some(y) = x { +//! if let Some(z) = y { +//! do_stuff_with(z) +//! } else { +//! do_something_else() +//! } +//! } else { +//! do_something_else() +//! } +//! ``` +//! +//! ## Intermediate variables with `let` +//! +//! ```rust,ignore +//! if_chain! { +//! if let Some(y) = x; +//! let z = y.some().complicated().expression(); +//! if z == 42; +//! then { +//! do_stuff_with(y); +//! } +//! } +//! ``` +//! +//! becomes +//! +//! ```rust,ignore +//! if let Some(y) = x { +//! let z = y.some().complicated().expression(); +//! if z == 42 { +//! do_stuff_with(y); +//! } +//! } +//! ``` +//! +//! ## Type ascription +//! +//! ```rust,ignore +//! let mut x = some_generic_computation(); +//! if_chain! { +//! if x > 7; +//! let y: u32 = another_generic_computation(); +//! then { x += y } +//! else { x += 1 } +//! } +//! ``` +//! +//! becomes +//! +//! ```rust,ignore +//! let mut x = some_generic_computation(); +//! if x > 7 { +//! let y: u32 = another_generic_computation(); +//! x += y +//! } else { +//! x += 1 +//! } +//! ``` +//! +//! ## Multiple patterns +//! +//! ```rust,ignore +//! if_chain! { +//! if let Foo(y) | Bar(y) | Baz(y) = x; +//! let Bubbles(z) | Buttercup(z) | Blossom(z) = y; +//! then { do_stuff_with(z) } +//! } +//! ``` +//! +//! becomes +//! +//! ```rust,ignore +//! match x { +//! Foo(y) | Bar(y) | Baz(y) => match y { +//! Bubbles(z) | Buttercup(z) | Blossom(z) => do_stuff_with(z) +//! }, +//! _ => {} +//! } +//! ``` +//! +//! Note that if you use a plain `let`, then `if_chain!` assumes that the +//! pattern is *irrefutable* (always matches) and doesn't add a fallback branch. + +#![cfg_attr(not(test), no_std)] + +/// Macro for writing nested `if let` expressions. +/// +/// See the crate documentation for information on how to use this macro. +#[macro_export(local_inner_macros)] +macro_rules! if_chain { + ($($tt:tt)*) => { + __if_chain! { @init () $($tt)* } + }; +} + +#[doc(hidden)] +#[macro_export(local_inner_macros)] +macro_rules! __if_chain { + // Expand with both a successful case and a fallback + (@init ($($tt:tt)*) then { $($then:tt)* } else { $($other:tt)* }) => { + __if_chain! { @expand { $($other)* } $($tt)* then { $($then)* } } + }; + // Expand with no fallback + (@init ($($tt:tt)*) then { $($then:tt)* }) => { + __if_chain! { @expand {} $($tt)* then { $($then)* } } + }; + // Munch everything until either of the arms above can be matched. + // Munched tokens are placed into `$($tt)*` + (@init ($($tt:tt)*) $head:tt $($tail:tt)*) => { + __if_chain! { @init ($($tt)* $head) $($tail)* } + }; + + // `let` with a single pattern + (@expand { $($other:tt)* } let $pat:pat = $expr:expr; $($tt:tt)+) => { + { + let $pat = $expr; + __if_chain! { @expand { $($other)* } $($tt)+ } + } + }; + // `let` with a single identifier and a type hint + (@expand { $($other:tt)* } let $ident:ident: $ty:ty = $expr:expr; $($tt:tt)+) => { + { + let $ident: $ty = $expr; + __if_chain! { @expand { $($other)* } $($tt)+ } + } + }; + // `let` with multiple patterns + (@expand { $($other:tt)* } let $pat1:pat | $($pat:pat)|+ = $expr:expr; $($tt:tt)+) => { + match $expr { + $pat1 | $($pat)|+ => __if_chain! { @expand { $($other)* } $($tt)+ } + } + }; + // `if let` with a single pattern + (@expand {} if let $pat:pat = $expr:expr; $($tt:tt)+) => { + if let $pat = $expr { + __if_chain! { @expand {} $($tt)+ } + } + }; + // `if let` with a single pattern and a fallback + (@expand { $($other:tt)+ } if let $pat:pat = $expr:expr; $($tt:tt)+) => { + if let $pat = $expr { + __if_chain! { @expand { $($other)+ } $($tt)+ } + } else { + $($other)+ + } + }; + // `if let` with multiple matterns and a fallback (if present) + (@expand { $($other:tt)* } if let $pat1:pat | $($pat:pat)|+ = $expr:expr; $($tt:tt)+) => { + match $expr { + $pat1 | $($pat)|+ => { __if_chain! { @expand { $($other)* } $($tt)+ } }, + _ => { $($other)* } + } + }; + // `if` with a successful case + (@expand {} if $expr:expr; $($tt:tt)+) => { + if $expr { + __if_chain! { @expand {} $($tt)+ } + } + }; + // `if` with both a successful case and a fallback + (@expand { $($other:tt)+ } if $expr:expr; $($tt:tt)+) => { + if $expr { + __if_chain! { @expand { $($other)+ } $($tt)+ } + } else { + $($other)+ + } + }; + // Final macro call + (@expand { $($other:tt)* } then { $($then:tt)* }) => { + $($then)* + }; +} + +#[cfg(test)] +mod tests { + #[test] + fn simple() { + let x: Option<Result<Option<String>, (u32, u32)>> = Some(Err((41, 42))); + let mut success = false; + if_chain! { + if let Some(y) = x; + if let Err(z) = y; + let (_, b) = z; + if b == 42; + then { success = true; } + } + assert!(success); + } + + #[test] + fn empty() { + let success; + if_chain! { + then { success = true; } + } + assert!(success); + } + + #[test] + fn empty_with_else() { + let success; + if_chain! { + then { success = true; } + else { unreachable!(); } + } + assert!(success); + } + + #[test] + fn if_let_multiple_patterns() { + #[derive(Copy, Clone)] + enum Robot { Nano, Biscuit1, Biscuit2 } + for &(robot, expected) in &[ + (Robot::Nano, false), + (Robot::Biscuit1, true), + (Robot::Biscuit2, true), + ] { + let is_biscuit = if_chain! { + if let Robot::Biscuit1 | Robot::Biscuit2 = robot; + then { true } else { false } + }; + assert_eq!(is_biscuit, expected); + } + } + + #[test] + fn let_multiple_patterns() { + let x: Result<u32, u32> = Ok(42); + if_chain! { + let Ok(x) | Err(x) = x; + then { assert_eq!(x, 42); } + else { panic!(); } + } + } + + #[test] + fn let_type_annotation_patterns() { + let mut x = 1; + if_chain! { + if x > 0; + let y: u32 = 2; + + then { x += y; } + else { x += 1; } + }; + assert_eq!(x, 3); + } +} |