cfg_if/lib.rs
1//! A macro for defining `#[cfg]` if-else statements.
2//!
3//! The macro provided by this crate, `cfg_if`, is similar to the `if/elif` C
4//! preprocessor macro by allowing definition of a cascade of `#[cfg]` cases,
5//! emitting the implementation which matches first.
6//!
7//! This allows you to conveniently provide a long list `#[cfg]`'d blocks of code
8//! without having to rewrite each clause multiple times.
9//!
10//! # Example
11//!
12//! ```
13//! cfg_if::cfg_if! {
14//! if #[cfg(unix)] {
15//! fn foo() { /* unix specific functionality */ }
16//! } else if #[cfg(target_pointer_width = "32")] {
17//! fn foo() { /* non-unix, 32-bit functionality */ }
18//! } else {
19//! fn foo() { /* fallback implementation */ }
20//! }
21//! }
22//!
23//! # fn main() {}
24//! ```
25
26#![no_std]
27#![doc(html_root_url = "https://docs.rs/cfg-if")]
28#![deny(missing_docs)]
29#![cfg_attr(test, allow(unexpected_cfgs))] // we test with features that do not exist
30
31/// The main macro provided by this crate. See crate documentation for more
32/// information.
33#[macro_export]
34macro_rules! cfg_if {
35 (
36 if #[cfg( $($i_meta:tt)+ )] { $( $i_tokens:tt )* }
37 $(
38 else if #[cfg( $($ei_meta:tt)+ )] { $( $ei_tokens:tt )* }
39 )*
40 $(
41 else { $( $e_tokens:tt )* }
42 )?
43 ) => {
44 $crate::cfg_if! {
45 @__items () ;
46 (( $($i_meta)+ ) ( $( $i_tokens )* )),
47 $(
48 (( $($ei_meta)+ ) ( $( $ei_tokens )* )),
49 )*
50 $(
51 (() ( $( $e_tokens )* )),
52 )?
53 }
54 };
55
56 // Internal and recursive macro to emit all the items
57 //
58 // Collects all the previous cfgs in a list at the beginning, so they can be
59 // negated. After the semicolon are all the remaining items.
60 (@__items ( $( ($($_:tt)*) , )* ) ; ) => {};
61 (
62 @__items ( $( ($($no:tt)+) , )* ) ;
63 (( $( $($yes:tt)+ )? ) ( $( $tokens:tt )* )),
64 $( $rest:tt , )*
65 ) => {
66 // Emit all items within one block, applying an appropriate #[cfg]. The
67 // #[cfg] will require all `$yes` matchers specified and must also negate
68 // all previous matchers.
69 #[cfg(all(
70 $( $($yes)+ , )?
71 not(any( $( $($no)+ ),* ))
72 ))]
73 // Subtle: You might think we could put `$( $tokens )*` here. But if
74 // that contains multiple items then the `#[cfg(all(..))]` above would
75 // only apply to the first one. By wrapping `$( $tokens )*` in this
76 // macro call, we temporarily group the items into a single thing (the
77 // macro call) that will be included/excluded by the `#[cfg(all(..))]`
78 // as appropriate. If the `#[cfg(all(..))]` succeeds, the macro call
79 // will be included, and then evaluated, producing `$( $tokens )*`. See
80 // also the "issue #90" test below.
81 $crate::cfg_if! { @__temp_group $( $tokens )* }
82
83 // Recurse to emit all other items in `$rest`, and when we do so add all
84 // our `$yes` matchers to the list of `$no` matchers as future emissions
85 // will have to negate everything we just matched as well.
86 $crate::cfg_if! {
87 @__items ( $( ($($no)+) , )* $( ($($yes)+) , )? ) ;
88 $( $rest , )*
89 }
90 };
91
92 // See the "Subtle" comment above.
93 (@__temp_group $( $tokens:tt )* ) => {
94 $( $tokens )*
95 };
96}
97
98#[cfg(test)]
99mod tests {
100 cfg_if! {
101 if #[cfg(test)] {
102 use core::option::Option as Option2;
103 fn works1() -> Option2<u32> { Some(1) }
104 } else {
105 fn works1() -> Option<u32> { None }
106 }
107 }
108
109 cfg_if! {
110 if #[cfg(foo)] {
111 fn works2() -> bool { false }
112 } else if #[cfg(test)] {
113 fn works2() -> bool { true }
114 } else {
115 fn works2() -> bool { false }
116 }
117 }
118
119 cfg_if! {
120 if #[cfg(foo)] {
121 fn works3() -> bool { false }
122 } else {
123 fn works3() -> bool { true }
124 }
125 }
126
127 cfg_if! {
128 if #[cfg(test)] {
129 use core::option::Option as Option3;
130 fn works4() -> Option3<u32> { Some(1) }
131 }
132 }
133
134 cfg_if! {
135 if #[cfg(foo)] {
136 fn works5() -> bool { false }
137 } else if #[cfg(test)] {
138 fn works5() -> bool { true }
139 }
140 }
141
142 // In issue #90 there was a bug that caused only the first item within a
143 // block to be annotated with the produced `#[cfg(...)]`. In this example,
144 // it meant that the first `type _B` wasn't being omitted as it should have
145 // been, which meant we had two `type _B`s, which caused an error. See also
146 // the "Subtle" comment above.
147 cfg_if!(
148 if #[cfg(target_os = "no-such-operating-system-good-sir!")] {
149 type _A = usize;
150 type _B = usize;
151 } else {
152 type _A = i32;
153 type _B = i32;
154 }
155 );
156
157 #[cfg(not(msrv_test))]
158 cfg_if! {
159 if #[cfg(false)] {
160 fn works6() -> bool { false }
161 } else if #[cfg(true)] {
162 fn works6() -> bool { true }
163 } else if #[cfg(false)] {
164 fn works6() -> bool { false }
165 }
166 }
167
168 #[test]
169 fn it_works() {
170 assert!(works1().is_some());
171 assert!(works2());
172 assert!(works3());
173 assert!(works4().is_some());
174 assert!(works5());
175 #[cfg(not(msrv_test))]
176 assert!(works6());
177 }
178
179 #[test]
180 #[allow(clippy::assertions_on_constants)]
181 fn test_usage_within_a_function() {
182 cfg_if! {
183 if #[cfg(debug_assertions)] {
184 // we want to put more than one thing here to make sure that they
185 // all get configured properly.
186 assert!(cfg!(debug_assertions));
187 assert_eq!(4, 2 + 2);
188 } else {
189 assert!(works1().is_some());
190 assert_eq!(10, 5 + 5);
191 }
192 }
193 }
194
195 #[allow(dead_code)]
196 trait Trait {
197 fn blah(&self);
198 }
199
200 #[allow(dead_code)]
201 struct Struct;
202
203 impl Trait for Struct {
204 cfg_if! {
205 if #[cfg(feature = "blah")] {
206 fn blah(&self) { unimplemented!(); }
207 } else {
208 fn blah(&self) { unimplemented!(); }
209 }
210 }
211 }
212}