Skip to content

Commit eb18746

Browse files
committed
Add assert_matches!(expr, pat).
1 parent 7f32f62 commit eb18746

File tree

3 files changed

+101
-25
lines changed

3 files changed

+101
-25
lines changed

library/core/src/macros/mod.rs

+52
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,58 @@ macro_rules! assert_ne {
110110
});
111111
}
112112

113+
/// Asserts that an expression matches a pattern.
114+
///
115+
/// On panic, this macro will print the value of the expression with its
116+
/// debug representation.
117+
///
118+
/// Like [`assert!`], this macro has a second form, where a custom
119+
/// panic message can be provided.
120+
///
121+
/// # Examples
122+
///
123+
/// ```
124+
/// let a = 1u32.checked_add(2);
125+
/// let b = 1u32.checked_sub(2);
126+
/// assert_matches!(a, Some(_));
127+
/// assert_matches!(b, None);
128+
/// ```
129+
#[macro_export]
130+
#[unstable(feature = "assert_matches", issue = "none")]
131+
#[allow_internal_unstable(core_panic)]
132+
macro_rules! assert_matches {
133+
($left:expr, $right:pat $(,)?) => ({
134+
match &$left {
135+
left_val => {
136+
if let $right = left_val {
137+
// OK
138+
} else {
139+
$crate::panicking::assert_matches_failed(
140+
&*left_val,
141+
$crate::stringify!($right),
142+
$crate::option::Option::None
143+
);
144+
}
145+
}
146+
}
147+
});
148+
($left:expr, $right:expr, $($arg:tt)+) => ({
149+
match &$left {
150+
left_val => {
151+
if let $right = left_val {
152+
// OK
153+
} else {
154+
$crate::panicking::assert_matches_failed(
155+
&*left_val,
156+
$crate::stringify!($right),
157+
$crate::option::Option::Some($crate::format_args!($($arg)+))
158+
);
159+
}
160+
}
161+
}
162+
});
163+
}
164+
113165
/// Asserts that a boolean expression is `true` at runtime.
114166
///
115167
/// This will invoke the [`panic!`] macro if the provided expression cannot be

library/core/src/panicking.rs

+46-23
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ pub fn panic_fmt(fmt: fmt::Arguments<'_>) -> ! {
9797
pub enum AssertKind {
9898
Eq,
9999
Ne,
100+
Match,
100101
}
101102

102103
/// Internal function for `assert_eq!` and `assert_ne!` macros
@@ -113,32 +114,54 @@ where
113114
T: fmt::Debug + ?Sized,
114115
U: fmt::Debug + ?Sized,
115116
{
116-
#[track_caller]
117-
fn inner(
118-
kind: AssertKind,
119-
left: &dyn fmt::Debug,
120-
right: &dyn fmt::Debug,
121-
args: Option<fmt::Arguments<'_>>,
122-
) -> ! {
123-
let op = match kind {
124-
AssertKind::Eq => "==",
125-
AssertKind::Ne => "!=",
126-
};
127-
128-
match args {
129-
Some(args) => panic!(
130-
r#"assertion failed: `(left {} right)`
117+
assert_failed_inner(kind, &left, &right, args)
118+
}
119+
120+
/// Internal function for `assert_match!`
121+
#[cold]
122+
#[track_caller]
123+
#[doc(hidden)]
124+
pub fn assert_matches_failed<T: fmt::Debug + ?Sized>(
125+
left: &T,
126+
right: &str,
127+
args: Option<fmt::Arguments<'_>>,
128+
) -> ! {
129+
// Use the Display implementation to display the pattern.
130+
struct Pattern<'a>(&'a str);
131+
impl fmt::Debug for Pattern<'_> {
132+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
133+
fmt::Display::fmt(self.0, f)
134+
}
135+
}
136+
assert_failed_inner(AssertKind::Match, &left, &Pattern(right), args);
137+
}
138+
139+
/// Non-generic version of the above functions, to avoid code bloat.
140+
#[track_caller]
141+
fn assert_failed_inner(
142+
kind: AssertKind,
143+
left: &dyn fmt::Debug,
144+
right: &dyn fmt::Debug,
145+
args: Option<fmt::Arguments<'_>>,
146+
) -> ! {
147+
let op = match kind {
148+
AssertKind::Eq => "==",
149+
AssertKind::Ne => "!=",
150+
AssertKind::Match => "matches",
151+
};
152+
153+
match args {
154+
Some(args) => panic!(
155+
r#"assertion failed: `(left {} right)`
131156
left: `{:?}`,
132157
right: `{:?}: {}`"#,
133-
op, left, right, args
134-
),
135-
None => panic!(
136-
r#"assertion failed: `(left {} right)`
158+
op, left, right, args
159+
),
160+
None => panic!(
161+
r#"assertion failed: `(left {} right)`
137162
left: `{:?}`,
138163
right: `{:?}`"#,
139-
op, left, right,
140-
),
141-
}
164+
op, left, right,
165+
),
142166
}
143-
inner(kind, &left, &right, args)
144167
}

library/std/src/lib.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,7 @@
228228
#![feature(arbitrary_self_types)]
229229
#![feature(array_error_internals)]
230230
#![feature(asm)]
231+
#![feature(assert_matches)]
231232
#![feature(associated_type_bounds)]
232233
#![feature(atomic_mut_ptr)]
233234
#![feature(box_syntax)]
@@ -550,8 +551,8 @@ pub use std_detect::detect;
550551
#[stable(feature = "rust1", since = "1.0.0")]
551552
#[allow(deprecated, deprecated_in_future)]
552553
pub use core::{
553-
assert_eq, assert_ne, debug_assert, debug_assert_eq, debug_assert_ne, matches, r#try, todo,
554-
unimplemented, unreachable, write, writeln,
554+
assert_eq, assert_matches, assert_ne, debug_assert, debug_assert_eq, debug_assert_ne, matches,
555+
r#try, todo, unimplemented, unreachable, write, writeln,
555556
};
556557

557558
// Re-export built-in macros defined through libcore.

0 commit comments

Comments
 (0)