]>
Commit | Line | Data |
---|---|---|
f20569fa XL |
1 | //! [![github]](https://github.com/dtolnay/rustversion) [![crates-io]](https://crates.io/crates/rustversion) [![docs-rs]](https://docs.rs/rustversion) |
2 | //! | |
3 | //! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github | |
4 | //! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust | |
5 | //! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logoColor=white&logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K | |
6 | //! | |
7 | //! <br> | |
8 | //! | |
9 | //! This crate provides macros for conditional compilation according to rustc | |
10 | //! compiler version, analogous to [`#[cfg(...)]`][cfg] and | |
11 | //! [`#[cfg_attr(...)]`][cfg_attr]. | |
12 | //! | |
13 | //! [cfg]: https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg-attribute | |
14 | //! [cfg_attr]: https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute | |
15 | //! | |
16 | //! <br> | |
17 | //! | |
18 | //! # Selectors | |
19 | //! | |
20 | //! - <p style="margin-left:50px;text-indent:-50px"> | |
21 | //! <b><code>#[rustversion::stable]</code></b> | |
22 | //! —<br> | |
23 | //! True on any stable compiler. | |
24 | //! </p> | |
25 | //! | |
26 | //! - <p style="margin-left:50px;text-indent:-50px"> | |
27 | //! <b><code>#[rustversion::stable(1.34)]</code></b> | |
28 | //! —<br> | |
29 | //! True on exactly the specified stable compiler. | |
30 | //! </p> | |
31 | //! | |
32 | //! - <p style="margin-left:50px;text-indent:-50px"> | |
33 | //! <b><code>#[rustversion::beta]</code></b> | |
34 | //! —<br> | |
35 | //! True on any beta compiler. | |
36 | //! </p> | |
37 | //! | |
38 | //! - <p style="margin-left:50px;text-indent:-50px"> | |
39 | //! <b><code>#[rustversion::nightly]</code></b> | |
40 | //! —<br> | |
41 | //! True on any nightly compiler or dev build. | |
42 | //! </p> | |
43 | //! | |
44 | //! - <p style="margin-left:50px;text-indent:-50px"> | |
45 | //! <b><code>#[rustversion::nightly(2019-01-01)]</code></b> | |
46 | //! —<br> | |
47 | //! True on exactly one nightly. | |
48 | //! </p> | |
49 | //! | |
50 | //! - <p style="margin-left:50px;text-indent:-50px"> | |
51 | //! <b><code>#[rustversion::since(1.34)]</code></b> | |
52 | //! —<br> | |
53 | //! True on that stable release and any later compiler, including beta and | |
54 | //! nightly. | |
55 | //! </p> | |
56 | //! | |
57 | //! - <p style="margin-left:50px;text-indent:-50px"> | |
58 | //! <b><code>#[rustversion::since(2019-01-01)]</code></b> | |
59 | //! —<br> | |
60 | //! True on that nightly and all newer ones. | |
61 | //! </p> | |
62 | //! | |
63 | //! - <p style="margin-left:50px;text-indent:-50px"> | |
64 | //! <b><code>#[rustversion::before(</code></b><i>version or date</i><b><code>)]</code></b> | |
65 | //! —<br> | |
66 | //! Negative of <i>#[rustversion::since(...)]</i>. | |
67 | //! </p> | |
68 | //! | |
69 | //! - <p style="margin-left:50px;text-indent:-50px"> | |
70 | //! <b><code>#[rustversion::not(</code></b><i>selector</i><b><code>)]</code></b> | |
71 | //! —<br> | |
72 | //! Negative of any selector; for example <i>#[rustversion::not(nightly)]</i>. | |
73 | //! </p> | |
74 | //! | |
75 | //! - <p style="margin-left:50px;text-indent:-50px"> | |
76 | //! <b><code>#[rustversion::any(</code></b><i>selectors...</i><b><code>)]</code></b> | |
77 | //! —<br> | |
78 | //! True if any of the comma-separated selectors is true; for example | |
79 | //! <i>#[rustversion::any(stable, beta)]</i>. | |
80 | //! </p> | |
81 | //! | |
82 | //! - <p style="margin-left:50px;text-indent:-50px"> | |
83 | //! <b><code>#[rustversion::all(</code></b><i>selectors...</i><b><code>)]</code></b> | |
84 | //! —<br> | |
85 | //! True if all of the comma-separated selectors are true; for example | |
86 | //! <i>#[rustversion::all(since(1.31), before(1.34))]</i>. | |
87 | //! </p> | |
88 | //! | |
89 | //! - <p style="margin-left:50px;text-indent:-50px"> | |
90 | //! <b><code>#[rustversion::attr(</code></b><i>selector</i><b><code>, </code></b><i>attribute</i><b><code>)]</code></b> | |
91 | //! —<br> | |
92 | //! For conditional inclusion of attributes; analogous to | |
93 | //! <code>cfg_attr</code>. | |
94 | //! </p> | |
95 | //! | |
96 | //! <br> | |
97 | //! | |
98 | //! # Use cases | |
99 | //! | |
100 | //! Providing additional trait impls as types are stabilized in the standard library | |
101 | //! without breaking compatibility with older compilers; in this case Pin\<P\> | |
102 | //! stabilized in [Rust 1.33][pin]: | |
103 | //! | |
104 | //! [pin]: https://blog.rust-lang.org/2019/02/28/Rust-1.33.0.html#pinning | |
105 | //! | |
106 | //! ``` | |
107 | //! # trait MyTrait {} | |
108 | //! # | |
109 | //! #[rustversion::since(1.33)] | |
110 | //! use std::pin::Pin; | |
111 | //! | |
112 | //! #[rustversion::since(1.33)] | |
113 | //! impl<P: MyTrait> MyTrait for Pin<P> { | |
114 | //! /* ... */ | |
115 | //! } | |
116 | //! ``` | |
117 | //! | |
118 | //! Similar but for language features; the ability to control alignment greater than | |
119 | //! 1 of packed structs was stabilized in [Rust 1.33][packed]. | |
120 | //! | |
121 | //! [packed]: https://github.com/rust-lang/rust/blob/master/RELEASES.md#version-1330-2019-02-28 | |
122 | //! | |
123 | //! ``` | |
124 | //! #[rustversion::attr(before(1.33), repr(packed))] | |
125 | //! #[rustversion::attr(since(1.33), repr(packed(2)))] | |
126 | //! struct Six(i16, i32); | |
127 | //! | |
128 | //! fn main() { | |
129 | //! println!("{}", std::mem::align_of::<Six>()); | |
130 | //! } | |
131 | //! ``` | |
132 | //! | |
133 | //! Augmenting code with `const` as const impls are stabilized in the standard | |
134 | //! library. This use of `const` as an attribute is recognized as a special case | |
135 | //! by the rustversion::attr macro. | |
136 | //! | |
137 | //! ``` | |
138 | //! use std::time::Duration; | |
139 | //! | |
140 | //! #[rustversion::attr(since(1.32), const)] | |
141 | //! fn duration_as_days(dur: Duration) -> u64 { | |
142 | //! dur.as_secs() / 60 / 60 / 24 | |
143 | //! } | |
144 | //! ``` | |
145 | //! | |
146 | //! <br> | |
147 | ||
148 | extern crate proc_macro; | |
149 | ||
150 | mod attr; | |
151 | mod bound; | |
152 | mod constfn; | |
153 | mod date; | |
154 | mod error; | |
155 | mod expr; | |
156 | mod iter; | |
157 | mod release; | |
158 | mod time; | |
159 | mod token; | |
160 | mod version; | |
161 | ||
162 | use crate::attr::Then; | |
163 | use crate::error::{Error, Result}; | |
164 | use crate::version::Version; | |
165 | use proc_macro::{Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree}; | |
166 | use std::iter::FromIterator; | |
167 | ||
168 | const RUSTVERSION: Version = include!(concat!(env!("OUT_DIR"), "/version.rs")); | |
169 | ||
170 | #[proc_macro_attribute] | |
171 | pub fn stable(args: TokenStream, input: TokenStream) -> TokenStream { | |
172 | cfg("stable", args, input) | |
173 | } | |
174 | ||
175 | #[proc_macro_attribute] | |
176 | pub fn beta(args: TokenStream, input: TokenStream) -> TokenStream { | |
177 | cfg("beta", args, input) | |
178 | } | |
179 | ||
180 | #[proc_macro_attribute] | |
181 | pub fn nightly(args: TokenStream, input: TokenStream) -> TokenStream { | |
182 | cfg("nightly", args, input) | |
183 | } | |
184 | ||
185 | #[proc_macro_attribute] | |
186 | pub fn since(args: TokenStream, input: TokenStream) -> TokenStream { | |
187 | cfg("since", args, input) | |
188 | } | |
189 | ||
190 | #[proc_macro_attribute] | |
191 | pub fn before(args: TokenStream, input: TokenStream) -> TokenStream { | |
192 | cfg("before", args, input) | |
193 | } | |
194 | ||
195 | #[proc_macro_attribute] | |
196 | pub fn not(args: TokenStream, input: TokenStream) -> TokenStream { | |
197 | cfg("not", args, input) | |
198 | } | |
199 | ||
200 | #[proc_macro_attribute] | |
201 | pub fn any(args: TokenStream, input: TokenStream) -> TokenStream { | |
202 | cfg("any", args, input) | |
203 | } | |
204 | ||
205 | #[proc_macro_attribute] | |
206 | pub fn all(args: TokenStream, input: TokenStream) -> TokenStream { | |
207 | cfg("all", args, input) | |
208 | } | |
209 | ||
210 | fn cfg(introducer: &str, args: TokenStream, input: TokenStream) -> TokenStream { | |
211 | try_cfg(introducer, args, input).unwrap_or_else(Error::into_compile_error) | |
212 | } | |
213 | ||
214 | fn try_cfg(introducer: &str, args: TokenStream, input: TokenStream) -> Result<TokenStream> { | |
215 | let introducer = Ident::new(introducer, Span::call_site()); | |
216 | ||
217 | let mut full_args = TokenStream::from(TokenTree::Ident(introducer)); | |
218 | if !args.is_empty() { | |
219 | full_args.extend(std::iter::once(TokenTree::Group(Group::new( | |
220 | Delimiter::Parenthesis, | |
221 | args, | |
222 | )))); | |
223 | } | |
224 | ||
225 | let ref mut full_args = iter::new(full_args); | |
226 | let expr = expr::parse(full_args)?; | |
227 | token::parse_end(full_args)?; | |
228 | ||
229 | if expr.eval(RUSTVERSION) { | |
230 | Ok(input) | |
231 | } else { | |
232 | Ok(TokenStream::new()) | |
233 | } | |
234 | } | |
235 | ||
236 | #[proc_macro_attribute] | |
237 | pub fn attr(args: TokenStream, input: TokenStream) -> TokenStream { | |
238 | attr::parse(args) | |
239 | .and_then(|args| try_attr(args, input)) | |
240 | .unwrap_or_else(Error::into_compile_error) | |
241 | } | |
242 | ||
243 | fn try_attr(args: attr::Args, input: TokenStream) -> Result<TokenStream> { | |
244 | if !args.condition.eval(RUSTVERSION) { | |
245 | return Ok(input); | |
246 | } | |
247 | ||
248 | match args.then { | |
249 | Then::Const(const_token) => constfn::insert_const(input, const_token), | |
250 | Then::Attribute(then) => { | |
251 | // #[cfg_attr(all(), #then)] | |
252 | Ok(TokenStream::from_iter( | |
253 | vec![ | |
254 | TokenTree::Punct(Punct::new('#', Spacing::Alone)), | |
255 | TokenTree::Group(Group::new( | |
256 | Delimiter::Bracket, | |
257 | TokenStream::from_iter(vec![ | |
258 | TokenTree::Ident(Ident::new("cfg_attr", Span::call_site())), | |
259 | TokenTree::Group(Group::new( | |
260 | Delimiter::Parenthesis, | |
261 | TokenStream::from_iter( | |
262 | vec![ | |
263 | TokenTree::Ident(Ident::new("all", Span::call_site())), | |
264 | TokenTree::Group(Group::new( | |
265 | Delimiter::Parenthesis, | |
266 | TokenStream::new(), | |
267 | )), | |
268 | TokenTree::Punct(Punct::new(',', Spacing::Alone)), | |
269 | ] | |
270 | .into_iter() | |
271 | .chain(then), | |
272 | ), | |
273 | )), | |
274 | ]), | |
275 | )), | |
276 | ] | |
277 | .into_iter() | |
278 | .chain(input), | |
279 | )) | |
280 | } | |
281 | } | |
282 | } |