mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 06:44:35 +00:00
Auto merge of #115416 - c410-f3r:match_cfg, r=Amanieu
Add the `cfg_match!` macro # Movitation Adds a match-like version of the `cfg_if` crate without a RFC [for the same reasons that caused `matches!` to be included in the standard library](https://github.com/rust-lang/rust/pull/65479). * General-purpose (not domain-specific) * Simple (the implementation is short) and useful (things can become difficult with several `cfg`s) * Very popular [on crates.io ](https://crates.io/crates/cfg-if) (currently 3th in all-time downloads) * The two previous points combined make it number three in [left-pad index](https://twitter.com/bascule/status/1184523027888988160) score ```rust match_cfg! { cfg(unix) => { fn foo() { /* unix specific functionality */ } } cfg(target_pointer_width = "32") => { fn foo() { /* non-unix, 32-bit functionality */ } } _ => { fn foo() { /* fallback implementation */ } } } ``` # Considerations A match-like syntax feels more natural in the sense that each macro fragment resembles an arm but I personally don't mind switching to any other desired syntax. The lack of `#[ ... ]` is intended to reduce typing, nevertheless, the same reasoning described above can also be applied to this aspect. Since blocks are intended to only contain items, anything but `cfg` is not expected to be supported at the current or future time. ~~Credits goes to `@gnzlbg` because most of the code was shamelessly copied from https://github.com/gnzlbg/match_cfg.~~ Credits goes to `@alexcrichton` because most of the code was shamelessly copied from https://github.com/rust-lang/cfg-if.
This commit is contained in:
commit
8a6bae2824
@ -321,6 +321,95 @@ pub macro debug_assert_matches($($arg:tt)*) {
|
||||
}
|
||||
}
|
||||
|
||||
/// A macro for defining `#[cfg]` match-like statements.
|
||||
///
|
||||
/// It is similar to the `if/elif` C preprocessor macro by allowing definition of a cascade of
|
||||
/// `#[cfg]` cases, emitting the implementation which matches first.
|
||||
///
|
||||
/// This allows you to conveniently provide a long list `#[cfg]`'d blocks of code
|
||||
/// without having to rewrite each clause multiple times.
|
||||
///
|
||||
/// Trailing `_` wildcard match arms are **optional** and they indicate a fallback branch when
|
||||
/// all previous declarations do not evaluate to true.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(cfg_match)]
|
||||
///
|
||||
/// cfg_match! {
|
||||
/// cfg(unix) => {
|
||||
/// fn foo() { /* unix specific functionality */ }
|
||||
/// }
|
||||
/// cfg(target_pointer_width = "32") => {
|
||||
/// fn foo() { /* non-unix, 32-bit functionality */ }
|
||||
/// }
|
||||
/// _ => {
|
||||
/// fn foo() { /* fallback implementation */ }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
#[unstable(feature = "cfg_match", issue = "115585")]
|
||||
#[rustc_diagnostic_item = "cfg_match"]
|
||||
macro_rules! cfg_match {
|
||||
// with a final wildcard
|
||||
(
|
||||
$(cfg($initial_meta:meta) => { $($initial_tokens:item)* })+
|
||||
_ => { $($extra_tokens:item)* }
|
||||
) => {
|
||||
cfg_match! {
|
||||
@__items ();
|
||||
$((($initial_meta) ($($initial_tokens)*)),)+
|
||||
(() ($($extra_tokens)*)),
|
||||
}
|
||||
};
|
||||
|
||||
// without a final wildcard
|
||||
(
|
||||
$(cfg($extra_meta:meta) => { $($extra_tokens:item)* })*
|
||||
) => {
|
||||
cfg_match! {
|
||||
@__items ();
|
||||
$((($extra_meta) ($($extra_tokens)*)),)*
|
||||
}
|
||||
};
|
||||
|
||||
// Internal and recursive macro to emit all the items
|
||||
//
|
||||
// Collects all the previous cfgs in a list at the beginning, so they can be
|
||||
// negated. After the semicolon is all the remaining items.
|
||||
(@__items ($($_:meta,)*);) => {};
|
||||
(
|
||||
@__items ($($no:meta,)*);
|
||||
(($($yes:meta)?) ($($tokens:item)*)),
|
||||
$($rest:tt,)*
|
||||
) => {
|
||||
// Emit all items within one block, applying an appropriate #[cfg]. The
|
||||
// #[cfg] will require all `$yes` matchers specified and must also negate
|
||||
// all previous matchers.
|
||||
#[cfg(all(
|
||||
$($yes,)?
|
||||
not(any($($no),*))
|
||||
))]
|
||||
cfg_match! { @__identity $($tokens)* }
|
||||
|
||||
// Recurse to emit all other items in `$rest`, and when we do so add all
|
||||
// our `$yes` matchers to the list of `$no` matchers as future emissions
|
||||
// will have to negate everything we just matched as well.
|
||||
cfg_match! {
|
||||
@__items ($($no,)* $($yes,)?);
|
||||
$($rest,)*
|
||||
}
|
||||
};
|
||||
|
||||
// Internal macro to make __apply work out right for different match types,
|
||||
// because of how macros match/expand stuff.
|
||||
(@__identity $($tokens:item)*) => {
|
||||
$($tokens)*
|
||||
};
|
||||
}
|
||||
|
||||
/// Returns whether the given expression matches any of the given patterns.
|
||||
///
|
||||
/// Like in a `match` expression, the pattern can be optionally followed by `if`
|
||||
|
@ -96,6 +96,7 @@
|
||||
#![feature(const_option_ext)]
|
||||
#![feature(const_result)]
|
||||
#![cfg_attr(target_has_atomic = "128", feature(integer_atomics))]
|
||||
#![cfg_attr(test, feature(cfg_match))]
|
||||
#![feature(int_roundings)]
|
||||
#![feature(slice_group_by)]
|
||||
#![feature(split_array)]
|
||||
@ -139,6 +140,7 @@ mod hash;
|
||||
mod intrinsics;
|
||||
mod iter;
|
||||
mod lazy;
|
||||
#[cfg(test)]
|
||||
mod macros;
|
||||
mod manually_drop;
|
||||
mod mem;
|
||||
|
@ -1,3 +1,25 @@
|
||||
trait Trait {
|
||||
fn blah(&self);
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
struct Struct;
|
||||
|
||||
impl Trait for Struct {
|
||||
cfg_match! {
|
||||
cfg(feature = "blah") => {
|
||||
fn blah(&self) {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
fn blah(&self) {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn assert_eq_trailing_comma() {
|
||||
assert_eq!(1, 1,);
|
||||
@ -18,3 +40,135 @@ fn assert_ne_trailing_comma() {
|
||||
fn matches_leading_pipe() {
|
||||
matches!(1, | 1 | 2 | 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cfg_match_basic() {
|
||||
cfg_match! {
|
||||
cfg(target_pointer_width = "64") => { fn f0_() -> bool { true }}
|
||||
}
|
||||
|
||||
cfg_match! {
|
||||
cfg(unix) => { fn f1_() -> bool { true }}
|
||||
cfg(any(target_os = "macos", target_os = "linux")) => { fn f1_() -> bool { false }}
|
||||
}
|
||||
|
||||
cfg_match! {
|
||||
cfg(target_pointer_width = "32") => { fn f2_() -> bool { false }}
|
||||
cfg(target_pointer_width = "64") => { fn f2_() -> bool { true }}
|
||||
}
|
||||
|
||||
cfg_match! {
|
||||
cfg(target_pointer_width = "16") => { fn f3_() -> i32 { 1 }}
|
||||
_ => { fn f3_() -> i32 { 2 }}
|
||||
}
|
||||
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
assert!(f0_());
|
||||
|
||||
#[cfg(unix)]
|
||||
assert!(f1_());
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
assert!(!f2_());
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
assert!(f2_());
|
||||
|
||||
#[cfg(not(target_pointer_width = "16"))]
|
||||
assert_eq!(f3_(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cfg_match_debug_assertions() {
|
||||
cfg_match! {
|
||||
cfg(debug_assertions) => {
|
||||
assert!(cfg!(debug_assertions));
|
||||
assert_eq!(4, 2+2);
|
||||
}
|
||||
_ => {
|
||||
assert!(cfg!(not(debug_assertions)));
|
||||
assert_eq!(10, 5+5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
#[test]
|
||||
fn cfg_match_no_duplication_on_64() {
|
||||
cfg_match! {
|
||||
cfg(windows) => {
|
||||
fn foo() {}
|
||||
}
|
||||
cfg(unix) => {
|
||||
fn foo() {}
|
||||
}
|
||||
cfg(target_pointer_width = "64") => {
|
||||
fn foo() {}
|
||||
}
|
||||
}
|
||||
foo();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cfg_match_options() {
|
||||
cfg_match! {
|
||||
cfg(test) => {
|
||||
use core::option::Option as Option2;
|
||||
fn works1() -> Option2<u32> { Some(1) }
|
||||
}
|
||||
_ => { fn works1() -> Option<u32> { None } }
|
||||
}
|
||||
|
||||
cfg_match! {
|
||||
cfg(feature = "foo") => { fn works2() -> bool { false } }
|
||||
cfg(test) => { fn works2() -> bool { true } }
|
||||
_ => { fn works2() -> bool { false } }
|
||||
}
|
||||
|
||||
cfg_match! {
|
||||
cfg(feature = "foo") => { fn works3() -> bool { false } }
|
||||
_ => { fn works3() -> bool { true } }
|
||||
}
|
||||
|
||||
cfg_match! {
|
||||
cfg(test) => {
|
||||
use core::option::Option as Option3;
|
||||
fn works4() -> Option3<u32> { Some(1) }
|
||||
}
|
||||
}
|
||||
|
||||
cfg_match! {
|
||||
cfg(feature = "foo") => { fn works5() -> bool { false } }
|
||||
cfg(test) => { fn works5() -> bool { true } }
|
||||
}
|
||||
|
||||
assert!(works1().is_some());
|
||||
assert!(works2());
|
||||
assert!(works3());
|
||||
assert!(works4().is_some());
|
||||
assert!(works5());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cfg_match_two_functions() {
|
||||
cfg_match! {
|
||||
cfg(target_pointer_width = "64") => {
|
||||
fn foo1() {}
|
||||
fn bar1() {}
|
||||
}
|
||||
_ => {
|
||||
fn foo2() {}
|
||||
fn bar2() {}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
{
|
||||
foo1();
|
||||
bar1();
|
||||
}
|
||||
#[cfg(not(target_pointer_width = "64"))]
|
||||
{
|
||||
foo2();
|
||||
bar2();
|
||||
}
|
||||
}
|
||||
|
@ -665,6 +665,9 @@ pub use core::{
|
||||
)]
|
||||
pub use core::concat_bytes;
|
||||
|
||||
#[unstable(feature = "cfg_match", issue = "115585")]
|
||||
pub use core::cfg_match;
|
||||
|
||||
#[stable(feature = "core_primitive", since = "1.43.0")]
|
||||
pub use core::primitive;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user