Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce the assert_matches! macro family #59260

Closed
wants to merge 2 commits into from
Closed

Introduce the assert_matches! macro family #59260

wants to merge 2 commits into from

Conversation

Kerollmops
Copy link
Contributor

@Kerollmops Kerollmops commented Mar 17, 2019

This pull request introduce two new macros, the assert_matches! and debug_assert_matches!.
A new family is born 🎉

These macros can be very helpful when doing tests, more practical than doing a match and a panic by hand. It could be classified into the same category of tools than the dgb! macro or the std::convert::identity function:

Too small to be a crate. Used often enough to make it into std.

If you wanted to know why I did not use the ? macro operator it is because it seems that the libcore crate is not under 2018 edition therefore this operator is not available. As a workaround I used a macro that can have two different inputs: one with the if guard and the other without it.

You can test it in the playground.

assert_matches!(Some(3), Some(_));
assert_matches!(Some(3), Some(3) | None | Some(2));

assert_matches!(Err(3), Ok(x) | Err(x) if x >= 3);

assert_matches!(&[3, 4][..], &[3, x] | &[x, 3] if x >= 4);

assert_matches!(3, e @ 0..=3 if e >= 3);

@rust-highfive
Copy link
Collaborator

r? @alexcrichton

(rust_highfive has picked a reviewer for you, use r? to override)

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Mar 17, 2019
@rust-highfive
Copy link
Collaborator

The job x86_64-gnu-llvm-6.0 of your PR failed on Travis (raw log). Through arcane magic we have determined that the following fragments from the build log may contain information about the problem.

Click to expand the log.
travis_time:end:09e7ade8:start=1552847650688491171,finish=1552847724640775798,duration=73952284627
$ git checkout -qf FETCH_HEAD
travis_fold:end:git.checkout

Encrypted environment variables have been removed for security reasons.
See https://docs.travis-ci.com/user/pull-requests/#pull-requests-and-security-restrictions
$ export SCCACHE_BUCKET=rust-lang-ci-sccache2
$ export SCCACHE_REGION=us-west-1
$ export GCP_CACHE_BUCKET=rust-lang-ci-cache
Setting environment variables from .travis.yml
---
travis_time:start:test_debuginfo
Check compiletest suite=debuginfo mode=debuginfo-both (x86_64-unknown-linux-gnu -> x86_64-unknown-linux-gnu)
[01:23:14] 
[01:23:14] running 120 tests
[01:23:40] .iiiii...i.....i..i...i..i.i...i.ii...i.....i..i....i..........iiii..........i...ii...i.......ii.i.i 100/120
[01:23:45] .i......iii.i.....ii
[01:23:45] 
[01:23:45]  finished in 30.527
[01:23:45] travis_fold:end:test_debuginfo

---
[01:35:13] ....iiiii........................................................................................... 100/2297
[01:35:26] ......................................................................ii............................ 200/2297
[01:35:40] ..........................................................................................i......... 300/2297
[01:35:57] .................................................................................................... 400/2297
[01:36:09] ......................i..i.F...F.................................................................... 500/2297
[01:36:36] .................................................................................................... 700/2297
[01:36:49] .................................................................................................... 800/2297
[01:37:02] .................................................................................................... 900/2297
[01:37:15] .................................................................................................... 1000/2297
---
[01:40:24] ....i............................................................................................
[01:40:24] failures:
[01:40:24] 
[01:40:24] ---- macros.rs - assert_matches (line 246) stdout ----
[01:40:24] error: cannot find macro `assert_matches!` in this scope
[01:40:24]  --> macros.rs:250:1
[01:40:24]   |
[01:40:24] 7 | assert_matches!(Err(3), Ok(x) | Err(x) if x >= 3);
[01:40:24] 
[01:40:24] 
[01:40:24] error: cannot find macro `assert_matches!` in this scope
[01:40:24]  --> macros.rs:249:1
[01:40:24]   |
[01:40:24] 6 | assert_matches!(Some(3), Some(3) | None | Some(2));
[01:40:24] 
[01:40:24] error: aborting due to 2 previous errors
[01:40:24] 
[01:40:24] thread 'macros.rs - assert_matches (line 246)' panicked at 'couldn't compile the test', src/librustdoc/test.rs:310:13
[01:40:24] thread 'macros.rs - assert_matches (line 246)' panicked at 'couldn't compile the test', src/librustdoc/test.rs:310:13
[01:40:24] note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
[01:40:24] 
[01:40:24] ---- macros.rs - debug_assert_matches (line 287) stdout ----
[01:40:24] error: cannot find macro `debug_assert_matches!` in this scope
[01:40:24]  --> macros.rs:291:1
[01:40:24]   |
[01:40:24] 7 | debug_assert_matches!(Err(3), Ok(x) | Err(x) if x >= 3);
[01:40:24]   | ^^^^^^^^^^^^^^^^^^^^ help: you could try the macro: `debug_assert_eq`
[01:40:24] error: cannot find macro `debug_assert_matches!` in this scope
[01:40:24]  --> macros.rs:290:1
[01:40:24]   |
[01:40:24]   |
[01:40:24] 6 | debug_assert_matches!(Some(3), Some(_));
[01:40:24]   | ^^^^^^^^^^^^^^^^^^^^ help: you could try the macro: `debug_assert_eq`
[01:40:24] error: aborting due to 2 previous errors
[01:40:24] 
[01:40:24] thread 'macros.rs - debug_assert_matches (line 287)' panicked at 'couldn't compile the test', src/librustdoc/test.rs:310:13
[01:40:24] 
---
[01:40:24] 
[01:40:24] error: test failed, to rerun pass '--doc'
[01:40:24] 
[01:40:24] 
[01:40:24] command did not execute successfully: "/checkout/obj/build/x86_64-unknown-linux-gnu/stage0/bin/cargo" "test" "--target" "x86_64-unknown-linux-gnu" "-j" "4" "--release" "--locked" "--color" "always" "--features" "panic-unwind backtrace" "--manifest-path" "/checkout/src/libstd/Cargo.toml" "-p" "core" "--" "--quiet"
[01:40:24] 
[01:40:24] 
[01:40:24] failed to run: /checkout/obj/build/bootstrap/debug/bootstrap test
[01:40:24] Build completed unsuccessfully in 0:29:16
[01:40:24] Build completed unsuccessfully in 0:29:16
[01:40:24] Makefile:48: recipe for target 'check' failed
[01:40:24] make: *** [check] Error 1
The command "stamp sh -x -c "$RUN_SCRIPT"" exited with 2.
travis_time:start:01dad884
$ date && (curl -fs --head https://google.com | grep ^Date: | sed 's/Date: //g' || true)
Sun Mar 17 20:15:58 UTC 2019
---
travis_time:end:0275fde6:start=1552853760392065028,finish=1552853760396841678,duration=4776650
travis_fold:end:after_failure.3
travis_fold:start:after_failure.4
travis_time:start:026a0411
$ ln -s . checkout && for CORE in obj/cores/core.*; do EXE=$(echo $CORE | sed 's|obj/cores/core\.[0-9]*\.!checkout!\(.*\)|\1|;y|!|/|'); if [ -f "$EXE" ]; then printf travis_fold":start:crashlog\n\033[31;1m%s\033[0m\n" "$CORE"; gdb --batch -q -c "$CORE" "$EXE" -iex 'set auto-load off' -iex 'dir src/' -iex 'set sysroot .' -ex bt -ex q; echo travis_fold":"end:crashlog; fi; done || true
travis_fold:end:after_failure.4
travis_fold:start:after_failure.5
travis_time:start:0ee27140
travis_time:start:0ee27140
$ cat ./obj/build/x86_64-unknown-linux-gnu/native/asan/build/lib/asan/clang_rt.asan-dynamic-i386.vers || true
cat: ./obj/build/x86_64-unknown-linux-gnu/native/asan/build/lib/asan/clang_rt.asan-dynamic-i386.vers: No such file or directory
travis_fold:end:after_failure.5
travis_fold:start:after_failure.6
travis_time:start:2eb72f64
$ dmesg | grep -i kill

I'm a bot! I can only do what humans tell me to, so if this was not helpful or you have suggestions for improvements, please ping or otherwise contact @TimNN. (Feature Requests)

@lachlansneff
Copy link

Really cool idea, I like this macro a lot.

@Kerollmops
Copy link
Contributor Author

Kerollmops commented Mar 17, 2019

I do not know how to bring this unstable macro into scope, tried with the #[feature!(_)] global attribute but it doesn't seems to work.

@rust-highfive
Copy link
Collaborator

The job x86_64-gnu-llvm-6.0 of your PR failed on Travis (raw log). Through arcane magic we have determined that the following fragments from the build log may contain information about the problem.

Click to expand the log.
travis_time:end:05ab8d68:start=1552858074727356956,finish=1552858147531147383,duration=72803790427
$ git checkout -qf FETCH_HEAD
travis_fold:end:git.checkout

Encrypted environment variables have been removed for security reasons.
See https://docs.travis-ci.com/user/pull-requests/#pull-requests-and-security-restrictions
$ export SCCACHE_BUCKET=rust-lang-ci-sccache2
$ export SCCACHE_REGION=us-west-1
$ export GCP_CACHE_BUCKET=rust-lang-ci-cache
Setting environment variables from .travis.yml
---
travis_time:start:test_debuginfo
Check compiletest suite=debuginfo mode=debuginfo-both (x86_64-unknown-linux-gnu -> x86_64-unknown-linux-gnu)
[01:21:25] 
[01:21:25] running 120 tests
[01:21:51] .iiiii...i.....i..i...i..i.i...i.ii...i.....i..i....i..........iiii..........i...ii...i.......ii.i.i 100/120
[01:21:56] .i......iii.i.....ii
[01:21:56] 
[01:21:56]  finished in 30.702
[01:21:56] travis_fold:end:test_debuginfo

---
[01:33:29] ....iiiii........................................................................................... 100/2297
[01:33:42] ......................................................................ii............................ 200/2297
[01:33:56] ..........................................................................................i......... 300/2297
[01:34:13] .................................................................................................... 400/2297
[01:34:26] ......................i..i.F...F.................................................................... 500/2297
[01:34:53] .................................................................................................... 700/2297
[01:35:06] .................................................................................................... 800/2297
[01:35:20] .................................................................................................... 900/2297
[01:35:34] .................................................................................................... 1000/2297
---
[01:38:43] ....i............................................................................................
[01:38:43] failures:
[01:38:43] 
[01:38:43] ---- macros.rs - assert_matches (line 246) stdout ----
[01:38:43] error: cannot find macro `assert_matches!` in this scope
[01:38:43]  --> macros.rs:250:1
[01:38:43]   |
[01:38:43] 7 | assert_matches!(Err(3), Ok(x) | Err(x) if x >= 3);
[01:38:43] 
[01:38:43] 
[01:38:43] error: cannot find macro `assert_matches!` in this scope
[01:38:43]  --> macros.rs:249:1
[01:38:43]   |
[01:38:43] 6 | assert_matches!(Some(3), Some(3) | None | Some(2));
[01:38:43] 
[01:38:43] error: aborting due to 2 previous errors
[01:38:43] 
[01:38:43] thread 'macros.rs - assert_matches (line 246)' panicked at 'couldn't compile the test', src/librustdoc/test.rs:310:13
[01:38:43] thread 'macros.rs - assert_matches (line 246)' panicked at 'couldn't compile the test', src/librustdoc/test.rs:310:13
[01:38:43] note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
[01:38:43] 
[01:38:43] ---- macros.rs - debug_assert_matches (line 287) stdout ----
[01:38:43] error: cannot find macro `debug_assert_matches!` in this scope
[01:38:43]  --> macros.rs:291:1
[01:38:43]   |
[01:38:43] 7 | debug_assert_matches!(Err(3), Ok(x) | Err(x) if x >= 3);
[01:38:43]   | ^^^^^^^^^^^^^^^^^^^^ help: you could try the macro: `debug_assert_eq`
[01:38:43] error: cannot find macro `debug_assert_matches!` in this scope
[01:38:43]  --> macros.rs:290:1
[01:38:43]   |
[01:38:43]   |
[01:38:43] 6 | debug_assert_matches!(Some(3), Some(_));
[01:38:43]   | ^^^^^^^^^^^^^^^^^^^^ help: you could try the macro: `debug_assert_eq`
[01:38:43] error: aborting due to 2 previous errors
[01:38:43] 
[01:38:43] thread 'macros.rs - debug_assert_matches (line 287)' panicked at 'couldn't compile the test', src/librustdoc/test.rs:310:13
[01:38:43] 
---
[01:38:43] 
[01:38:43] error: test failed, to rerun pass '--doc'
[01:38:43] 
[01:38:43] 
[01:38:43] command did not execute successfully: "/checkout/obj/build/x86_64-unknown-linux-gnu/stage0/bin/cargo" "test" "--target" "x86_64-unknown-linux-gnu" "-j" "4" "--release" "--locked" "--color" "always" "--features" "panic-unwind backtrace" "--manifest-path" "/checkout/src/libstd/Cargo.toml" "-p" "core" "--" "--quiet"
[01:38:43] 
[01:38:43] 
[01:38:43] failed to run: /checkout/obj/build/bootstrap/debug/bootstrap test
[01:38:43] Build completed unsuccessfully in 0:29:26
[01:38:43] Build completed unsuccessfully in 0:29:26
[01:38:43] make: *** [check] Error 1
[01:38:43] Makefile:48: recipe for target 'check' failed
The command "stamp sh -x -c "$RUN_SCRIPT"" exited with 2.
travis_time:start:0d516c44
$ date && (curl -fs --head https://google.com | grep ^Date: | sed 's/Date: //g' || true)
Sun Mar 17 23:08:00 UTC 2019
---
travis_time:end:056e778d:start=1552864082561173174,finish=1552864082623274644,duration=62101470
travis_fold:end:after_failure.4
travis_fold:start:after_failure.5
travis_time:start:0c3add40
$ cat ./obj/build/x86_64-unknown-linux-gnu/native/asan/build/lib/asan/clang_rt.asan-dynamic-i386.vers || true
cat: ./obj/build/x86_64-unknown-linux-gnu/native/asan/build/lib/asan/clang_rt.asan-dynamic-i386.vers: No such file or directory
travis_fold:end:after_failure.5
travis_fold:start:after_failure.6
travis_time:start:057d78e0
$ dmesg | grep -i kill

I'm a bot! I can only do what humans tell me to, so if this was not helpful or you have suggestions for improvements, please ping or otherwise contact @TimNN. (Feature Requests)

@Centril
Copy link
Contributor

Centril commented Mar 17, 2019

I would strongly prefer not baking a matcher $($pat:pat)|+ into the standard library at this time because it may impact attempts to crater run in #54883. This macro as formulated now is forwards compatible with having macros2.0 to allow pat fragments to match on or-patterns (p | q), please don't change that by treating each $pat individually.

Finally, with #53667 (which I'm working on implementing) you can encode an is!(...) macro like so: macro_rules! is { ($e:expr) => if $e { true } else { false } }. This should allow writing assert!(is!(let Some(a) = foo(b) && let Some(c) = bar(c)) which I believe scales generally better. Future language design may even turn let p = e into an expression at type bool itself (or e is p in @petrochenkov's notation). As such, I think this macro as designed will not be optimal in the future in terms of flexibility and will not compose well with new language improvements.

@alexcrichton
Copy link
Member

Thanks for the PR @Kerollmops! This is a pretty large-ish feature to libstd, however, so I was wondering if there's an accompanying RFC for it? If not I think this feature would want to go through an RFC before being added to libstd (even unstable)

@bors
Copy link
Contributor

bors commented Mar 28, 2019

☔ The latest upstream changes (presumably #59478) made this pull request unmergeable. Please resolve the merge conflicts.

@Dylan-DPC-zz
Copy link

ping from triage @KarolinePlum you need to resolve the conflicts so we can merge this

@Dylan-DPC-zz Dylan-DPC-zz added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Apr 8, 2019
@Dylan-DPC-zz
Copy link

ping from triage @KarolinePlum
Unfortunately we haven't heard from you on this in a while, so I'm closing the PR to keep things tidy. Don't worry though, if you'll have time again in the future please reopen this PR, we'll be happy to review it again!

@Dylan-DPC-zz Dylan-DPC-zz added S-inactive Status: Inactive and waiting on the author. This is often applied to closed PRs. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Apr 29, 2019
@Xanewok
Copy link
Member

Xanewok commented May 14, 2020

Since matches! made its way into libstd, is there an interest to reopen this PR?

I'd imagine it'd be just as useful as assert_eq for manual inspection if it were to print the mismatched values, rather the plain token tree for assert!(matches!(...)).

@joshtriplett
Copy link
Member

We now have an assert_matches!: #82770

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-inactive Status: Inactive and waiting on the author. This is often applied to closed PRs.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants