#![cfg_attr(
all(test, feature = "nightly"),
feature(box_patterns)
)]
#![cfg_attr(feature = "debug", feature(trace_macros))]
#![cfg_attr(not(test), no_std)]
#![cfg_attr(test, deny(warnings))]
#![cfg_attr(test, allow(unreachable_code))]
mod guard_unwrap;
#[cfg(feature = "debug")]
trace_macros!(true);
pub enum LetElseBodyMustDiverge {}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! __guard_output {
((($($imms:ident)*) ($($muts:ident)*)), [($($guard:tt)*) ($($pattern:tt)*) ($rhs:expr) ($diverge:expr)]) => {
__guard_impl!(@as_stmt
let ($($imms,)* $(mut $muts,)*) = { #[allow(unused_mut)]
match $rhs {
$($pattern)* => {
if $($guard)* { ($($imms,)* $($muts,)*)
} else {
let _: $crate::LetElseBodyMustDiverge = $diverge;
}
},
_ => {
let _: $crate::LetElseBodyMustDiverge = $diverge;
},
} }
)
};
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! __guard_impl {
(@as_stmt $s:stmt) => { $s };
(@collect () -> $($rest:tt)*) => {
__guard_output!($($rest)*)
};
(@collect (($($inside:tt)*) $($tail:tt)*) -> $idents:tt, $thru:tt) => {
__guard_impl!(@collect ($($inside)* $($tail)*) -> $idents, $thru)
};
(@collect ({$($inside:tt)*} $($tail:tt)*) -> $idents:tt, $thru:tt) => {
__guard_impl!(@collect ($($inside)* $($tail)*) -> $idents, $thru)
};
(@collect ([$($inside:tt)*] $($tail:tt)*) -> $idents:tt, $thru:tt) => {
__guard_impl!(@collect ($($inside)* $($tail)*) -> $idents, $thru)
};
(@collect (, $($tail:tt)*) -> $idents:tt, $thru:tt) => {
__guard_impl!(@collect ($($tail)*) -> $idents, $thru)
};
(@collect (.. $($tail:tt)*) -> $idents:tt, $thru:tt) => {
__guard_impl!(@collect ($($tail)*) -> $idents, $thru)
};
(@collect (@ $($tail:tt)*) -> $idents:tt, $thru:tt) => {
__guard_impl!(@collect ($($tail)*) -> $idents, $thru)
};
(@collect (_ $($tail:tt)*) -> $idents:tt, $thru:tt) => {
__guard_impl!(@collect ($($tail)*) -> $idents, $thru)
};
(@collect (& $($tail:tt)*) -> $idents:tt, $thru:tt) => {
__guard_impl!(@collect ($($tail)*) -> $idents, $thru)
};
(@collect (:: <$($generic:tt),*> $($tail:tt)*) -> $idents:tt, $thru:tt) => {
__guard_impl!(@collect ($($tail)*) -> $idents, $thru)
};
(@collect (:: $pathend:ident $($tail:tt)*) -> $idents:tt, $thru:tt) => {
__guard_impl!(@collect ($($tail)*) -> $idents, $thru)
};
(@collect (| $($tail:tt)*) -> $idents:tt, $thru:tt) => {
__guard_impl!(@collect () -> $idents, $thru) };
(@collect (if $($tail:tt)*) -> $idents:tt, [$guard:tt $($rest:tt)*]) => {
__guard_impl!(@collect () -> $idents, [($($tail)*) $($rest)*])
};
(@collect (box $($tail:tt)*) -> $idents:tt, $thru:tt) => {
__guard_impl!(@collect ($($tail)*) -> $idents, $thru)
};
(@collect ($id:ident: $($tail:tt)*) -> $idents:tt, $thru:tt) => {
__guard_impl!(@collect ($($tail)*) -> $idents, $thru)
};
(@collect ($pathcomp:ident :: $pathend:ident $($tail:tt)*) -> $idents:tt, $thru:tt) => {
__guard_impl!(@collect ($($tail)*) -> $idents, $thru)
};
(@collect ($id:ident ($($inside:tt)*) $($tail:tt)*) -> $idents:tt, $thru:tt) => {
__guard_impl!(@collect ($($inside)* $($tail)*) -> $idents, $thru)
};
(@collect ($id:ident {$($inside:tt)*} $($tail:tt)*) -> $idents:tt, $thru:tt) => {
__guard_impl!(@collect ($($inside)* $($tail)*) -> $idents, $thru)
};
(@collect (ref mut $id:ident $($tail:tt)*) -> (($($imms:ident)*) $muts:tt), $thru:tt) => {
__guard_impl!(@collect ($($tail)*) -> (($($imms)* $id) $muts), $thru)
};
(@collect (ref $id:ident $($tail:tt)*) -> (($($imms:ident)*) $muts:tt), $thru:tt) => {
__guard_impl!(@collect ($($tail)*) -> (($($imms)* $id) $muts), $thru)
};
(@collect (mut $id:ident $($tail:tt)*) -> ($imms:tt ($($muts:ident)*)), $thru:tt) => {
__guard_impl!(@collect ($($tail)*) -> ($imms ($($muts)* $id)), $thru)
};
(@collect ($id:ident $($tail:tt)*) -> (($($imms:ident)*) $muts:tt), $thru:tt) => {
__guard_impl!(@collect ($($tail)*) -> (($($imms)* $id) $muts), $thru)
};
(@split (else { $($diverge:tt)* } = $($tail:tt)*) -> ($pat:tt $guard:tt)) => {
__guard_impl!(@collect $pat -> (() ()), [$guard $pat ($($tail)*) ({ $($diverge)* })])
};
(@split (= $($tail:tt)*) -> ($pat:tt $guard:tt)) => {
__guard_impl!(@split expr ($($tail)*) -> ($pat $guard ()))
};
(@split (if $($tail:tt)*) -> ($pat:tt $guard:tt)) => {
__guard_impl!(@split guard ($($tail)*) -> ($pat ()))
};
(@split guard (= $($tail:tt)*) -> ($pat:tt $guard:tt)) => {
__guard_impl!(@split expr ($($tail)*) -> ($pat $guard ()))
};
(@split guard (else { $($diverge:tt)* } = $($tail:tt)*) -> ($pat:tt $guard:tt)) => {
__guard_impl!(@collect $pat -> (() ()), [$guard $pat ($($tail)*) ({ $($diverge)* })])
};
(@split guard ($head:tt $($tail:tt)*) -> ($pat:tt ($($guard:tt)*))) => {
__guard_impl!(@split guard ($($tail)*) -> ($pat ($($guard)* $head)))
};
(@split ($head:tt $($tail:tt)*) -> (($($pat:tt)*) $guard:tt)) => {
__guard_impl!(@split ($($tail)*) -> (($($pat)* $head) $guard))
};
(@split expr (else { $($tail:tt)* }) -> ($pat:tt $guard:tt $expr:tt)) => {
__guard_impl!(@collect $pat -> (() ()), [$guard $pat $expr ({ $($tail)* })])
};
(@split expr (else { $($body:tt)* } $($tail:tt)*) -> ($pat:tt $guard:tt ($($expr:tt)*))) => {
__guard_impl!(@split expr ($($tail)*) -> ($pat $guard ($($expr)* else { $($body)* })))
};
(@split expr ($head:tt $($tail:tt)*) -> ($pat:tt $guard:tt ($($expr:tt)*))) => {
__guard_impl!(@split expr ($($tail)*) -> ($pat $guard ($($expr)* $head)))
};
({ $($diverge:tt)* } unless $rhs:expr => $($pattern:tt)*) => {
__guard_impl!(@collect ($($pattern)*) -> (() ()), [(true) ($($pattern)*) ($rhs) ({$($diverge)*})])
};
(let $($tail:tt)*) => {
__guard_impl!(@split ($($tail)*) -> (() (true)))
};
}
#[macro_export(local_inner_macros)]
macro_rules! guard {
($($input:tt)*) => {
__guard_impl!($($input)*)
};
}
#[cfg(test)]
mod tests {
#[derive(Debug)]
enum Stuff {
A(Option<i32>, Option<i32>),
B { foo: Result<i32, i32>, bar: i32 },
C(i32),
D,
}
#[derive(Copy, Clone)]
struct Point {
x: i32,
y: i32,
}
struct Person {
name: Option<String>,
}
#[test]
fn various() {
let origin = Point { x: 0, y: 0 };
let p = Some(Person {
name: Some("Steve".to_owned()),
});
let opt = Stuff::A(Some(42), Some(43));
let copt = Stuff::C(42);
let dopt = Stuff::D;
let mut thing = Stuff::B {
foo: Ok(44),
bar: 45,
};
guard!({ return } unless Some(&42) => Some(&x));
println!("{}", x);
guard!({ return } unless Stuff::C(42) => Stuff::B { bar, .. } | Stuff::C(bar));
println!("{}", bar);
guard!({ return } unless Some(42) => Some(x));
println!("{}", x);
guard!({ return } unless Some(origin) => Some(Point { x, y }));
println!("{} {}", x, y);
guard!({ return } unless Some(origin) => Some(Point { x: x1, y: y1 }));
println!("{} {}", x1, y1);
guard!({ return } unless Some(origin) => Some(Point { x, .. }));
println!("{}", x);
guard!({ return } unless Some(origin) => Some(Point { y: y1, .. }));
println!("{}", y1);
guard!({ return } unless origin => Point { x, y: _y } if _y == 0);
println!("{}", x);
guard!({ return } unless p => Some(Person { name: ref x @ Some(_), .. }));
println!("{:?}", x);
guard!({ return } unless (Some(42), Some(43)) => (Some(x), Some(y)));
println!("{} {}", x, y);
guard!({ return } unless opt => Stuff::A(Some(x), Some(y)));
println!("{} {}", x, y);
guard!({ return } unless opt => Stuff::A(Option::Some::<i32>(x), _));
println!("{}", x);
guard!({ return } unless thing => Stuff::B { foo: Ok(ref mut x), .. });
*x += 1;
println!("{}", x);
guard!({ return } unless thing => self::Stuff::B { foo: Ok(mut x), .. });
x *= 2;
println!("{}", x);
guard!({ return } unless copt => Stuff::C(_));
guard!({ return } unless dopt => self::Stuff::D);
}
#[test]
fn empty() {
use self::Stuff::D;
struct Empty;
let dopt = D;
guard!({ return } unless dopt => D{});
guard!({ return } unless Some(Empty) => Some(Empty {}));
}
#[cfg(feature = "nightly")]
#[test]
fn nightly() {
let foo = (Box::new(42), [1, 2, 3]);
guard!({ return } unless Some(foo) => Some((box x, _)));
println!("{}", x);
let mut foo = Some((Box::new(42), [1, 2, 3]));
{
guard!({ return } unless foo => Some((box ref x, _)));
println!("{}", x);
}
{
guard!({ return } unless foo => Some((box ref mut x, _)));
println!("{}", x);
}
{
guard!({ return } unless foo => Some((box mut x, _)));
x -= 1;
println!("{}", x);
}
let foo = (Box::new(42), [1, 2, 3]);
guard!({ return } unless Some(foo) => Some((_, [a, b, c])));
println!("{} {} {}", a, b, c);
let foo = (Box::new(42), [1, 2, 3]);
guard!({ return } unless Some((foo.0, &foo.1)) => Some((box x, &[head, tail @ ..])));
println!("{} {} {:?}", x, head, tail);
}
#[test]
fn new_syntax() {
let opt = Some((1, 2));
guard!(let Some((a, b)) = opt else { panic!() });
println!("{} {}", a, b);
guard!(let Some((a, b)) = if true { opt } else { opt } else { panic!() });
println!("{} {}", a, b);
guard!(let Some((a, b)) = if false { opt } else { opt } else { panic!() });
println!("{} {}", a, b);
guard!(let Some((a, b)) if b > 0 = opt else { panic!() });
println!("{} {}", a, b);
guard!(let Some((a, b)) else { panic!() } = opt);
println!("{} {}", a, b);
guard!(let Some((a, b)) else { panic!() } = if true { opt } else { opt });
println!("{} {}", a, b);
guard!(let Some((a, b)) else { panic!() } = if false { opt } else { opt });
println!("{} {}", a, b);
guard!(let Some((a, b)) if b > 0 else { panic!() } = opt);
println!("{} {}", a, b);
}
#[test]
fn rfc_runpass() {
#[allow(dead_code)]
enum MyEnum {
A(&'static str),
B { f: &'static str },
C,
}
guard!(let MyEnum::A(ref x) | MyEnum::B { f: ref x } = MyEnum::B { f: "" } else {
panic!();
});
assert_eq!(*x, "");
let mut x = 1;
loop {
guard!(let Some(_y) if _y == 4 = Some(x) else {
guard!(let Some(_y) if _y == 3 = Some(x) else {
x += 1;
continue;
});
break;
});
panic!();
}
assert_eq!(x, 3);
guard!(let Option::None = Some(2) else { return });
panic!();
}
}