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}