|
| 1 | +//! Macros used internally. |
| 2 | +//! |
| 3 | +//! These may technically be exported, but that's only to make them available to internal |
| 4 | +//! project dependencies. The `#[doc(hidden)]` mark indicates that these are not stable or supported |
| 5 | +//! APIs, and should not be relied upon by external dependees. |
| 6 | +
|
| 7 | +/// The single macro export of the [`cfg-if`](https://docs.rs/cfg-if) crate. |
| 8 | +/// |
| 9 | +/// It is packaged here to avoid pulling in another dependency. The stdlib does the same[^1]. |
| 10 | +/// |
| 11 | +/// [^1]: https://github.com/rust-lang/rust/blob/a2db9280539229a3b8a084a09886670a57bc7e9c/library/compiler-builtins/libm/src/math/support/macros.rs#L1 |
| 12 | +#[doc(hidden)] |
| 13 | +#[macro_export] |
| 14 | +macro_rules! cfg_if { |
| 15 | + // match if/else chains with a final `else` |
| 16 | + ( |
| 17 | + $( |
| 18 | + if #[cfg( $i_meta:meta )] { $( $i_tokens:tt )* } |
| 19 | + ) else+ |
| 20 | + else { $( $e_tokens:tt )* } |
| 21 | + ) => { |
| 22 | + $crate::cfg_if! { |
| 23 | + @__items () ; |
| 24 | + $( |
| 25 | + (( $i_meta ) ( $( $i_tokens )* )) , |
| 26 | + )+ |
| 27 | + (() ( $( $e_tokens )* )) , |
| 28 | + }; |
| 29 | + }; |
| 30 | + |
| 31 | + // match if/else chains lacking a final `else` |
| 32 | + ( |
| 33 | + if #[cfg( $i_meta:meta )] { $( $i_tokens:tt )* } |
| 34 | + $( |
| 35 | + else if #[cfg( $e_meta:meta )] { $( $e_tokens:tt )* } |
| 36 | + )* |
| 37 | + ) => { |
| 38 | + $crate::cfg_if! { |
| 39 | + @__items () ; |
| 40 | + (( $i_meta ) ( $( $i_tokens )* )) , |
| 41 | + $( |
| 42 | + (( $e_meta ) ( $( $e_tokens )* )) , |
| 43 | + )* |
| 44 | + }; |
| 45 | + }; |
| 46 | + |
| 47 | + // Internal and recursive macro to emit all the items |
| 48 | + // |
| 49 | + // Collects all the previous cfgs in a list at the beginning, so they can be |
| 50 | + // negated. After the semicolon are all the remaining items. |
| 51 | + (@__items ( $( $_:meta , )* ) ; ) => {}; |
| 52 | + ( |
| 53 | + @__items ( $( $no:meta , )* ) ; |
| 54 | + (( $( $yes:meta )? ) ( $( $tokens:tt )* )) , |
| 55 | + $( $rest:tt , )* |
| 56 | + ) => { |
| 57 | + // Emit all items within one block, applying an appropriate #[cfg]. The |
| 58 | + // #[cfg] will require all `$yes` matchers specified and must also negate |
| 59 | + // all previous matchers. |
| 60 | + #[cfg(all( |
| 61 | + $( $yes , )? |
| 62 | + not(any( $( $no ),* )) |
| 63 | + ))] |
| 64 | + $crate::cfg_if! { @__identity $( $tokens )* } |
| 65 | + |
| 66 | + // Recurse to emit all other items in `$rest`, and when we do so add all |
| 67 | + // our `$yes` matchers to the list of `$no` matchers as future emissions |
| 68 | + // will have to negate everything we just matched as well. |
| 69 | + $crate::cfg_if! { |
| 70 | + @__items ( $( $no , )* $( $yes , )? ) ; |
| 71 | + $( $rest , )* |
| 72 | + }; |
| 73 | + }; |
| 74 | + |
| 75 | + // Internal macro to make __apply work out right for different match types, |
| 76 | + // because of how macros match/expand stuff. |
| 77 | + (@__identity $( $tokens:tt )* ) => { |
| 78 | + $( $tokens )* |
| 79 | + }; |
| 80 | +} |
| 81 | + |
| 82 | +/// Similar to [`cfg_if`](cfg_if), but accepts a list of expressions, and generates an internal |
| 83 | +/// closure to return each value. |
| 84 | +/// |
| 85 | +/// The main reason this is necessary is because attaching `#[cfg(...)]` annotations to certain |
| 86 | +/// types of statements requires a nightly feature, or `cfg_if` would be enough on its own. This |
| 87 | +/// macro's restricted interface allows it to generate a closure as a circumlocution that is legal |
| 88 | +/// on stable rust. |
| 89 | +/// |
| 90 | +/// Note that any `return` operation within the expressions provided to this macro will apply to the |
| 91 | +/// generated closure, not the enclosing scope--it cannot be used to interfere with external |
| 92 | +/// control flow. |
| 93 | +/// |
| 94 | +/// The generated closure is non-[`const`](const@keyword), so cannot be used inside `const` methods. |
| 95 | +#[doc(hidden)] |
| 96 | +#[macro_export] |
| 97 | +macro_rules! cfg_if_expr { |
| 98 | + // Match =>, chains, maybe with a final _ => catchall clause. |
| 99 | + ( |
| 100 | + $( $ret_ty:ty : )? |
| 101 | + $( |
| 102 | + #[cfg( $i_meta:meta )] => $i_val:expr |
| 103 | + ),+ , |
| 104 | + _ => $rem_val:expr $(,)? |
| 105 | + ) => { |
| 106 | + (|| $( -> $ret_ty )? { |
| 107 | + $crate::cfg_if_expr! { |
| 108 | + @__items (); |
| 109 | + $( |
| 110 | + (( $i_meta ) ( |
| 111 | + #[allow(unreachable_code)] |
| 112 | + return $i_val ; |
| 113 | + )) , |
| 114 | + )+ |
| 115 | + (() ( |
| 116 | + #[allow(unreachable_code)] |
| 117 | + return $rem_val ; |
| 118 | + )) , |
| 119 | + } |
| 120 | + })() |
| 121 | + }; |
| 122 | + // Match =>, chains *without* any _ => clause. |
| 123 | + ( |
| 124 | + $( $ret_ty:ty : )? |
| 125 | + $( |
| 126 | + #[cfg( $i_meta:meta )] => $i_val:expr |
| 127 | + ),+ $(,)? |
| 128 | + ) => { |
| 129 | + (|| $( -> $ret_ty )? { |
| 130 | + $crate::cfg_if_expr! { |
| 131 | + @__items (); |
| 132 | + $( |
| 133 | + (( $i_meta ) ( |
| 134 | + #[allow(unreachable_code)] |
| 135 | + return $i_val ; |
| 136 | + )) , |
| 137 | + )+ |
| 138 | + } |
| 139 | + })() |
| 140 | + }; |
| 141 | + |
| 142 | + (@__items ( $( $_:meta , )* ) ; ) => {}; |
| 143 | + ( |
| 144 | + @__items ( $( $no:meta , )* ) ; |
| 145 | + (( $( $yes:meta )? ) ( $( $tokens:tt )* )) , |
| 146 | + $( $rest:tt , )* |
| 147 | + ) => { |
| 148 | + #[cfg(all( |
| 149 | + $( $yes , )? |
| 150 | + not(any( $( $no ),* )) |
| 151 | + ))] |
| 152 | + $crate::cfg_if_expr! { @__identity $( $tokens )* } |
| 153 | + |
| 154 | + $crate::cfg_if_expr! { |
| 155 | + @__items ( $( $no , )* $( $yes , )? ) ; |
| 156 | + $( $rest , )* |
| 157 | + }; |
| 158 | + }; |
| 159 | + (@__identity $( $tokens:tt )* ) => { |
| 160 | + $( $tokens )* |
| 161 | + }; |
| 162 | +} |
0 commit comments