Skip to main content

cfg_if/
lib.rs

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