1 //! [![github]](https://github.com/dtolnay/rustversion) [![crates-io]](https://crates.io/crates/rustversion) [![docs-rs]](https://docs.rs/rustversion)
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
9 //! This crate provides macros for conditional compilation according to rustc
10 //! compiler version, analogous to [`#[cfg(...)]`][cfg] and
11 //! [`#[cfg_attr(...)]`][cfg_attr].
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
20 //! - <p style="margin-left:50px;text-indent:-50px">
21 //! <b><code>#[rustversion::stable]</code></b>
23 //! True on any stable compiler.
26 //! - <p style="margin-left:50px;text-indent:-50px">
27 //! <b><code>#[rustversion::stable(1.34)]</code></b>
29 //! True on exactly the specified stable compiler.
32 //! - <p style="margin-left:50px;text-indent:-50px">
33 //! <b><code>#[rustversion::beta]</code></b>
35 //! True on any beta compiler.
38 //! - <p style="margin-left:50px;text-indent:-50px">
39 //! <b><code>#[rustversion::nightly]</code></b>
41 //! True on any nightly compiler or dev build.
44 //! - <p style="margin-left:50px;text-indent:-50px">
45 //! <b><code>#[rustversion::nightly(2019-01-01)]</code></b>
47 //! True on exactly one nightly.
50 //! - <p style="margin-left:50px;text-indent:-50px">
51 //! <b><code>#[rustversion::since(1.34)]</code></b>
53 //! True on that stable release and any later compiler, including beta and
57 //! - <p style="margin-left:50px;text-indent:-50px">
58 //! <b><code>#[rustversion::since(2019-01-01)]</code></b>
60 //! True on that nightly and all newer ones.
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>
66 //! Negative of <i>#[rustversion::since(...)]</i>.
69 //! - <p style="margin-left:50px;text-indent:-50px">
70 //! <b><code>#[rustversion::not(</code></b><i>selector</i><b><code>)]</code></b>
72 //! Negative of any selector; for example <i>#[rustversion::not(nightly)]</i>.
75 //! - <p style="margin-left:50px;text-indent:-50px">
76 //! <b><code>#[rustversion::any(</code></b><i>selectors...</i><b><code>)]</code></b>
78 //! True if any of the comma-separated selectors is true; for example
79 //! <i>#[rustversion::any(stable, beta)]</i>.
82 //! - <p style="margin-left:50px;text-indent:-50px">
83 //! <b><code>#[rustversion::all(</code></b><i>selectors...</i><b><code>)]</code></b>
85 //! True if all of the comma-separated selectors are true; for example
86 //! <i>#[rustversion::all(since(1.31), before(1.34))]</i>.
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>
92 //! For conditional inclusion of attributes; analogous to
93 //! <code>cfg_attr</code>.
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]:
104 //! [pin]: https://blog.rust-lang.org/2019/02/28/Rust-1.33.0.html#pinning
107 //! # trait MyTrait {}
109 //! #[rustversion::since(1.33)]
110 //! use std::pin::Pin;
112 //! #[rustversion::since(1.33)]
113 //! impl<P: MyTrait> MyTrait for Pin<P> {
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].
121 //! [packed]: https://github.com/rust-lang/rust/blob/master/RELEASES.md#version-1330-2019-02-28
124 //! #[rustversion::attr(before(1.33), repr(packed))]
125 //! #[rustversion::attr(since(1.33), repr(packed(2)))]
126 //! struct Six(i16, i32);
129 //! println!("{}", std::mem::align_of::<Six>());
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.
138 //! use std::time::Duration;
140 //! #[rustversion::attr(since(1.32), const)]
141 //! fn duration_as_days(dur: Duration) -> u64 {
142 //! dur.as_secs() / 60 / 60 / 24
149 clippy
::cast_lossless
,
150 clippy
::cast_possible_truncation
,
151 clippy
::doc_markdown
,
152 clippy
::enum_glob_use
,
153 clippy
::from_iter_instead_of_collect
,
154 clippy
::module_name_repetitions
,
155 clippy
::must_use_candidate
,
156 clippy
::needless_doctest_main
,
157 clippy
::needless_pass_by_value
,
158 clippy
::redundant_else
,
159 clippy
::toplevel_ref_arg
,
160 clippy
::unreadable_literal
163 extern crate proc_macro
;
177 use crate::attr
::Then
;
178 use crate::error
::{Error, Result}
;
179 use crate::version
::Version
;
180 use proc_macro
::{Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree}
;
181 use std
::iter
::FromIterator
;
183 const RUSTVERSION
: Version
= include
!(concat
!(env
!("OUT_DIR"), "/version.rs"));
185 #[proc_macro_attribute]
186 pub fn stable(args
: TokenStream
, input
: TokenStream
) -> TokenStream
{
187 cfg("stable", args
, input
)
190 #[proc_macro_attribute]
191 pub fn beta(args
: TokenStream
, input
: TokenStream
) -> TokenStream
{
192 cfg("beta", args
, input
)
195 #[proc_macro_attribute]
196 pub fn nightly(args
: TokenStream
, input
: TokenStream
) -> TokenStream
{
197 cfg("nightly", args
, input
)
200 #[proc_macro_attribute]
201 pub fn since(args
: TokenStream
, input
: TokenStream
) -> TokenStream
{
202 cfg("since", args
, input
)
205 #[proc_macro_attribute]
206 pub fn before(args
: TokenStream
, input
: TokenStream
) -> TokenStream
{
207 cfg("before", args
, input
)
210 #[proc_macro_attribute]
211 pub fn not(args
: TokenStream
, input
: TokenStream
) -> TokenStream
{
212 cfg("not", args
, input
)
215 #[proc_macro_attribute]
216 pub fn any(args
: TokenStream
, input
: TokenStream
) -> TokenStream
{
217 cfg("any", args
, input
)
220 #[proc_macro_attribute]
221 pub fn all(args
: TokenStream
, input
: TokenStream
) -> TokenStream
{
222 cfg("all", args
, input
)
225 fn cfg(introducer
: &str, args
: TokenStream
, input
: TokenStream
) -> TokenStream
{
226 try_cfg(introducer
, args
, input
).unwrap_or_else(Error
::into_compile_error
)
229 fn try_cfg(introducer
: &str, args
: TokenStream
, input
: TokenStream
) -> Result
<TokenStream
> {
230 let introducer
= Ident
::new(introducer
, Span
::call_site());
232 let mut full_args
= TokenStream
::from(TokenTree
::Ident(introducer
));
233 if !args
.is_empty() {
234 full_args
.extend(std
::iter
::once(TokenTree
::Group(Group
::new(
235 Delimiter
::Parenthesis
,
240 let ref mut full_args
= iter
::new(full_args
);
241 let expr
= expr
::parse(full_args
)?
;
242 token
::parse_end(full_args
)?
;
244 if expr
.eval(RUSTVERSION
) {
247 Ok(TokenStream
::new())
251 #[proc_macro_attribute]
252 pub fn attr(args
: TokenStream
, input
: TokenStream
) -> TokenStream
{
254 .and_then(|args
| try_attr(args
, input
))
255 .unwrap_or_else(Error
::into_compile_error
)
258 fn try_attr(args
: attr
::Args
, input
: TokenStream
) -> Result
<TokenStream
> {
259 if !args
.condition
.eval(RUSTVERSION
) {
264 Then
::Const(const_token
) => constfn
::insert_const(input
, const_token
),
265 Then
::Attribute(then
) => {
266 // #[cfg_attr(all(), #then)]
267 Ok(TokenStream
::from_iter(
269 TokenTree
::Punct(Punct
::new('
#', Spacing::Alone)),
270 TokenTree
::Group(Group
::new(
272 TokenStream
::from_iter(vec
![
273 TokenTree
::Ident(Ident
::new("cfg_attr", Span
::call_site())),
274 TokenTree
::Group(Group
::new(
275 Delimiter
::Parenthesis
,
276 TokenStream
::from_iter(
278 TokenTree
::Ident(Ident
::new("all", Span
::call_site())),
279 TokenTree
::Group(Group
::new(
280 Delimiter
::Parenthesis
,
283 TokenTree
::Punct(Punct
::new('
,'
, Spacing
::Alone
)),