diff options
| author | Simon Sapin <simon.sapin@exyr.org> | 2019-10-16 22:40:37 +0200 |
|---|---|---|
| committer | Simon Sapin <simon.sapin@exyr.org> | 2019-10-23 15:35:34 +0200 |
| commit | f69293ae808dea61a2dacee6057ca5bb0d7dc817 (patch) | |
| tree | 1a6c703a7cb180933e1ca12c6d2ee5de8484bc00 /src/test | |
| parent | f466f52c1bf8f2e4454e31c683a88625ad4b4033 (diff) | |
| download | rust-f69293ae808dea61a2dacee6057ca5bb0d7dc817.tar.gz rust-f69293ae808dea61a2dacee6057ca5bb0d7dc817.zip | |
Add `core::macros::matches!( $expr, $pat ) -> bool`
# Motivation This macro is: * General-purpose (not domain-specific) * Simple (the implementation is short) * Very popular [on crates.io](https://crates.io/crates/matches) (currently 37th in all-time downloads) * The two previous points combined make it number one in [left-pad index](https://twitter.com/bascule/status/1184523027888988160) score As such, I feel it is a good candidate for inclusion in the standard library. In fact I already felt that way five years ago: https://github.com/rust-lang/rust/pull/14685 (Although the proof of popularity was not as strong at the time.) Back then, the main concern was that this macro may not be quite universally-enough useful to belong in the prelude. # API Therefore, this PR adds the macro such that using it requires one of: ``` use core::macros::matches; use std::macros::matches; ``` Like arms of a `match` expression, the macro supports multiple patterns separated by `|` and optionally followed by `if` and a guard expression: ``` let foo = 'f'; assert!(matches!(foo, 'A'..='Z' | 'a'..='z')); let bar = Some(4); assert!(matches!(bar, Some(x) if x > 2)); ``` # Implementation constraints A combination of reasons make it tricky for a standard library macro not to be in the prelude. Currently, all public `macro_rules` macros in the standard library macros end up “in the prelude” of every crate not through `use std::prelude::v1::*;` like for other kinds of items, but through `#[macro_use]` on `extern crate std;`. (Both are injected by `src/libsyntax_ext/standard_library_imports.rs`.) `#[macro_use]` seems to import every macro that is available at the top-level of a crate, even if through a `pub use` re-export. Therefore, for `matches!` not to be in the prelude, we need it to be inside of a module rather than at the root of `core` or `std`. However, the only way to make a `macro_rules` macro public outside of the crate where it is defined appears to be `#[macro_export]`. This exports the macro at the root of the crate regardless of which module defines it. See [macro scoping]( https://doc.rust-lang.org/reference/macros-by-example.html#scoping-exporting-and-importing) in the reference. Therefore, the macro needs to be defined in a crate that is not `core` or `std`. # Implementation This PR adds a new `matches_macro` crate as a private implementation detail of the standard library. This crate is `#![no_core]` so that libcore can depend on it. It contains a `macro_rules` definition with `#[macro_export]`. libcore and libstd each have a new public `macros` module that contains a `pub use` re-export of the macro. Both the module and the macro are unstable, for now. The existing private `macros` modules are renamed `prelude_macros`, though their respective source remains in `macros.rs` files.
Diffstat (limited to 'src/test')
| -rw-r--r-- | src/test/ui/macros/unknown-builtin.stderr | 2 | ||||
| -rw-r--r-- | src/test/ui/matches_macro_imported.rs | 13 | ||||
| -rw-r--r-- | src/test/ui/matches_macro_not_in_the_prelude.rs | 7 | ||||
| -rw-r--r-- | src/test/ui/matches_macro_not_in_the_prelude.stderr | 8 |
4 files changed, 29 insertions, 1 deletions
diff --git a/src/test/ui/macros/unknown-builtin.stderr b/src/test/ui/macros/unknown-builtin.stderr index 33b7b055b4e..27992b466ba 100644 --- a/src/test/ui/macros/unknown-builtin.stderr +++ b/src/test/ui/macros/unknown-builtin.stderr @@ -5,7 +5,7 @@ LL | macro_rules! unknown { () => () } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: cannot find a built-in macro with name `line` - --> <::core::macros::builtin::line macros>:1:1 + --> <::core::prelude_macros::builtin::line macros>:1:1 | LL | () => { } | ^^^^^^^^^ diff --git a/src/test/ui/matches_macro_imported.rs b/src/test/ui/matches_macro_imported.rs new file mode 100644 index 00000000000..76b7e692cee --- /dev/null +++ b/src/test/ui/matches_macro_imported.rs @@ -0,0 +1,13 @@ +// run-pass + +#![feature(matches_macro)] + +use std::macros::matches; + +fn main() { + let foo = 'f'; + assert!(matches!(foo, 'A'..='Z' | 'a'..='z')); + + let foo = '_'; + assert!(!matches!(foo, 'A'..='Z' | 'a'..='z')); +} diff --git a/src/test/ui/matches_macro_not_in_the_prelude.rs b/src/test/ui/matches_macro_not_in_the_prelude.rs new file mode 100644 index 00000000000..489c7b86645 --- /dev/null +++ b/src/test/ui/matches_macro_not_in_the_prelude.rs @@ -0,0 +1,7 @@ +#![feature(matches_macro)] + +fn main() { + let foo = 'f'; + assert!(matches!(foo, 'A'..='Z' | 'a'..='z')); + //~^ Error: cannot find macro `matches` in this scope +} diff --git a/src/test/ui/matches_macro_not_in_the_prelude.stderr b/src/test/ui/matches_macro_not_in_the_prelude.stderr new file mode 100644 index 00000000000..0abe29a835b --- /dev/null +++ b/src/test/ui/matches_macro_not_in_the_prelude.stderr @@ -0,0 +1,8 @@ +error: cannot find macro `matches` in this scope + --> $DIR/matches_macro_not_in_the_prelude.rs:5:13 + | +LL | assert!(matches!(foo, 'A'..='Z' | 'a'..='z')); + | ^^^^^^^ + +error: aborting due to previous error + |
