Skip to content

Commit

Permalink
Add assert_matches!(expr, pat).
Browse files Browse the repository at this point in the history
  • Loading branch information
m-ou-se committed Mar 4, 2021
1 parent 7f32f62 commit eb18746
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 25 deletions.
52 changes: 52 additions & 0 deletions library/core/src/macros/mod.rs
Expand Up @@ -110,6 +110,58 @@ macro_rules! assert_ne {
});
}

/// Asserts that an expression matches a pattern.
///
/// On panic, this macro will print the value of the expression with its
/// debug representation.
///
/// Like [`assert!`], this macro has a second form, where a custom
/// panic message can be provided.
///
/// # Examples
///
/// ```
/// let a = 1u32.checked_add(2);
/// let b = 1u32.checked_sub(2);
/// assert_matches!(a, Some(_));
/// assert_matches!(b, None);
/// ```
#[macro_export]
#[unstable(feature = "assert_matches", issue = "none")]
#[allow_internal_unstable(core_panic)]
macro_rules! assert_matches {
($left:expr, $right:pat $(,)?) => ({
match &$left {
left_val => {
if let $right = left_val {
// OK
} else {
$crate::panicking::assert_matches_failed(
&*left_val,
$crate::stringify!($right),
$crate::option::Option::None
);
}
}
}
});
($left:expr, $right:expr, $($arg:tt)+) => ({
match &$left {
left_val => {
if let $right = left_val {
// OK
} else {
$crate::panicking::assert_matches_failed(
&*left_val,
$crate::stringify!($right),
$crate::option::Option::Some($crate::format_args!($($arg)+))
);
}
}
}
});
}

/// Asserts that a boolean expression is `true` at runtime.
///
/// This will invoke the [`panic!`] macro if the provided expression cannot be
Expand Down
69 changes: 46 additions & 23 deletions library/core/src/panicking.rs
Expand Up @@ -97,6 +97,7 @@ pub fn panic_fmt(fmt: fmt::Arguments<'_>) -> ! {
pub enum AssertKind {
Eq,
Ne,
Match,
}

/// Internal function for `assert_eq!` and `assert_ne!` macros
Expand All @@ -113,32 +114,54 @@ where
T: fmt::Debug + ?Sized,
U: fmt::Debug + ?Sized,
{
#[track_caller]
fn inner(
kind: AssertKind,
left: &dyn fmt::Debug,
right: &dyn fmt::Debug,
args: Option<fmt::Arguments<'_>>,
) -> ! {
let op = match kind {
AssertKind::Eq => "==",
AssertKind::Ne => "!=",
};

match args {
Some(args) => panic!(
r#"assertion failed: `(left {} right)`
assert_failed_inner(kind, &left, &right, args)
}

/// Internal function for `assert_match!`
#[cold]
#[track_caller]
#[doc(hidden)]
pub fn assert_matches_failed<T: fmt::Debug + ?Sized>(
left: &T,
right: &str,
args: Option<fmt::Arguments<'_>>,
) -> ! {
// Use the Display implementation to display the pattern.
struct Pattern<'a>(&'a str);
impl fmt::Debug for Pattern<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self.0, f)
}
}
assert_failed_inner(AssertKind::Match, &left, &Pattern(right), args);
}

/// Non-generic version of the above functions, to avoid code bloat.
#[track_caller]
fn assert_failed_inner(
kind: AssertKind,
left: &dyn fmt::Debug,
right: &dyn fmt::Debug,
args: Option<fmt::Arguments<'_>>,
) -> ! {
let op = match kind {
AssertKind::Eq => "==",
AssertKind::Ne => "!=",
AssertKind::Match => "matches",
};

match args {
Some(args) => panic!(
r#"assertion failed: `(left {} right)`
left: `{:?}`,
right: `{:?}: {}`"#,
op, left, right, args
),
None => panic!(
r#"assertion failed: `(left {} right)`
op, left, right, args
),
None => panic!(
r#"assertion failed: `(left {} right)`
left: `{:?}`,
right: `{:?}`"#,
op, left, right,
),
}
op, left, right,
),
}
inner(kind, &left, &right, args)
}
5 changes: 3 additions & 2 deletions library/std/src/lib.rs
Expand Up @@ -228,6 +228,7 @@
#![feature(arbitrary_self_types)]
#![feature(array_error_internals)]
#![feature(asm)]
#![feature(assert_matches)]
#![feature(associated_type_bounds)]
#![feature(atomic_mut_ptr)]
#![feature(box_syntax)]
Expand Down Expand Up @@ -550,8 +551,8 @@ pub use std_detect::detect;
#[stable(feature = "rust1", since = "1.0.0")]
#[allow(deprecated, deprecated_in_future)]
pub use core::{
assert_eq, assert_ne, debug_assert, debug_assert_eq, debug_assert_ne, matches, r#try, todo,
unimplemented, unreachable, write, writeln,
assert_eq, assert_matches, assert_ne, debug_assert, debug_assert_eq, debug_assert_ne, matches,
r#try, todo, unimplemented, unreachable, write, writeln,
};

// Re-export built-in macros defined through libcore.
Expand Down

0 comments on commit eb18746

Please sign in to comment.