bitflags_attr_macros/lib.rs
1use proc_macro::TokenStream;
2use quote::ToTokens;
3use syn::{Error, Result};
4use typed::{Args, Bitflag};
5
6mod typed;
7
8/// An attribute macro that transforms an C-like enum into a bitflag struct type implementing an
9/// ergonomic end-user API, similar to the `bitflags` crate, and implementing many helpful traits
10/// (listed in more details below).
11///
12/// The attribute requires that the [`Clone`] and [`Copy`] traits are derived for the type.
13///
14/// [`Clone`]: ::core::clone::Clone
15/// [`Copy`]: ::core::marker::Copy
16///
17/// ## Examples
18///
19/// Generate a flags type using `u8` as the bits type:
20/// ```rust
21/// # use bitflag_attr::bitflag;
22///
23/// #[bitflag(u8)]
24/// #[derive(Clone, Copy)]
25/// enum Flags {
26/// A = 1,
27/// B = 1 << 1,
28/// C = 0b0000_0100,
29/// }
30/// ```
31///
32/// Flags may refer to other flags using their names:
33///
34/// ```rust
35/// # use bitflag_attr::bitflag;
36///
37/// #[bitflag(u8)]
38/// #[derive(Clone, Copy)]
39/// enum Flags {
40/// A = 1,
41/// B = 1 << 1,
42/// C = 0b0000_0100,
43/// AB = A | B,
44/// }
45/// ```
46///
47/// Flags may also refer to other flags using their `bits` method value, like `bitflags` crate:
48///
49/// ```rust
50/// # use bitflag_attr::bitflag;
51///
52/// #[bitflag(u8)]
53/// #[derive(Clone, Copy)]
54/// enum Flags {
55/// A = 1,
56/// B = 1 << 1,
57/// C = 0b0000_0100,
58/// AB = Flags::A.bits() | Flags::B.bits(),
59/// }
60/// ```
61///
62/// It's possible to use more derives and attributes by simply adding them
63///
64/// ```rust
65/// # use core::fmt::Debug as ExternalDerive
66///
67/// #[bitflag(u8)]
68/// #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, ExternalDerive)]
69/// enum Flags {
70/// A = 1,
71/// B = 1 << 1,
72/// C = 0b0000_0100,
73/// AB = Flags::A.bits() | Flags::B.bits(),
74/// }
75/// ```
76///
77/// ## Known and unknown flags
78///
79/// The variant of the enum are flags. They will be expanded to type-associated constants. Every
80/// variant value is a known flag, while every not specified value is a unknown flag.
81///
82/// There are operation that will truncate out the unknown values. But tha can be configured if
83/// desired; more on that on [Externally defined flags](#externally-defined-flags)
84///
85/// ## Externally defined flags
86///
87/// If you're generating flags types from an external source, such as a C API, you can use the
88/// `#[non_exhaustive]` attribute to communicate to the bitflags macro that there may be more valid
89/// flags then the known flags.
90///
91/// Without extra configuration, it defaults to `!0` (all bits set) as a mask of all bits the
92/// external source may ever set, i.e. all bits are considered as possible values.
93///
94/// ```
95/// use bitflag_attr::bitflag;
96///
97/// #[bitflag(u32)]
98/// #[non_exhaustive] // All bits are considered as possible values.
99/// #[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)]
100/// pub enum Flags {
101/// /// The value `A`, at bit position `0`.
102/// A = 0b00000001,
103/// /// The value `B`, at bit position `1`.
104/// B = 0b00000010,
105/// /// The value `C`, at bit position `2`.
106/// C = 0b00000100,
107///
108/// /// The combination of `A`, `B`, and `C`.
109/// ABC = A | B | C,
110/// }
111/// ```
112///
113/// But you can also configure it using the helper attribute `reserved_bits` with the value of
114/// valid bits that the external source may ever set.
115///
116/// ```
117/// use bitflag_attr::bitflag;
118///
119/// #[bitflag(u32)]
120/// #[non_exhaustive] // Communicate there is more potential valid flags than the known flags
121/// #[reserved_bits = 0b001001111] // Specify the reserved bits to take into consideration.
122/// #[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)]
123/// pub enum Flags {
124/// /// The value `A`, at bit position `0`.
125/// A = 0b00000001,
126/// /// The value `B`, at bit position `1`.
127/// B = 0b00000010,
128/// /// The value `C`, at bit position `2`.
129/// C = 0b00000100,
130///
131/// /// The combination of `A`, `B`, and `C`.
132/// ABC = A | B | C,
133/// }
134/// ```
135///
136/// Why should you do this? Generated methods like `all` and truncating operators like `!` only
137/// consider bits in defined flags. Adding an unnamed flag makes those methods consider additional
138/// bits, without generating additional constants for them. It helps compatibility when the external
139/// source may start setting additional bits at any time.
140///
141/// ## Type representation
142///
143/// By default, the generated flag type will be `#[repr(transparent)]`, but you can explicit it on
144/// the definition as long is one of the supported ones (`C`, `Rust` and `transparent`):
145///
146/// ```rust
147/// # use bitflag_attr::bitflag;
148///
149/// #[repr(C)]
150/// #[bitflag(u8)]
151/// #[derive(Clone, Copy)]
152/// enum Flags {
153/// A = 1,
154/// B = 1 << 1,
155/// C = 1 << 2,
156/// }
157/// ```
158///
159/// ## Generated trait implementations
160///
161/// This macro generates some trait implementations: [`ops:Not`], [`ops:BitAnd`],
162/// [`ops:BitOr`], [`ops:BitXor`], [`ops:BitAndAssign`], [`ops:BitOrAssign`], [`ops:BitXorAssign`],
163/// [`fmt::Binary`], [`fmt::LowerHex`], [`fmt::UpperHex`], [`fmt::Octal`], [`From`], [`Extend`],
164/// [`FromIterator`], [`FromStr`] and [`IntoIterator`].
165///
166/// The custom [`fmt::Debug`] implementation will only be generated if it is included in the
167/// `#[derive(...)]` parameters.
168///
169/// The custom [`Default`] implementation will only be generated if it is included in the
170/// `#[derive(...)]` parameters.
171///
172/// ### Debug derive
173///
174/// The `bitflag` macro handles the [`fmt::Debug`] if specified in the derive list. When specified,
175/// a customized implementation is produced by the macro where it outputs human-readable, binary,
176/// octal and hexadecimal outputs of the flags value.
177///
178/// ```
179/// # use bitflag_attr::bitflag;
180///
181/// #[bitflag(u32)]
182/// #[derive(Debug, Clone, Copy)]
183/// pub enum Flags {
184/// A = 0b00000001,
185/// B = 0b00000010,
186/// C = 0b00000100,
187/// }
188/// ```
189///
190/// ### Default derive
191///
192/// The `bitflag` macro handles the [`Default`] if specified in the derive list. Without specifying
193/// a default variant, the default implementation is the same as a empty flag:
194///
195/// ```rust
196/// # use bitflag_attr::bitflag;
197///
198/// #[bitflag(u8)]
199/// #[derive(Clone, Copy, Default)] // Default is the same as `Flags::empty()`
200/// enum Flags {
201/// A = 1,
202/// B = 1 << 1,
203/// C = 1 << 2,
204/// }
205/// ```
206///
207/// But it can be specified like deriving [`Default`] on enum, using the `#[default]` helper attribute:
208///
209/// ```rust
210/// # use bitflag_attr::bitflag;
211///
212/// #[bitflag(u8)]
213/// #[derive(Clone, Copy, Default)]
214/// enum Flags {
215/// A = 1,
216/// B = 1 << 1,
217/// #[default] // `Flags::C` are the default value returned by `Default::default()`
218/// C = 1 << 2,
219/// }
220/// ```
221///
222/// ### Serde feature
223///
224/// If the crate is compiled with the `serde` feature, this crate will generate implementations for
225/// the `serde::{Serialize, Deserialize}` traits if they are included in the `#[derive(...)]`
226/// parameters, but it will not import/re-export these traits, your project must have `serde` as
227/// a direct dependency.
228///
229/// ```no_run
230/// use bitflag_attr::bitflag;
231/// use serde::{Serialize, Deserialize};
232///
233/// #[bitflag(u32)]
234/// #[derive(Debug, Clone, Copy, Serialize, Deserialize)]
235/// pub enum Flags {
236/// /// The value `A`, at bit position `0`.
237/// A = 0b00000001,
238/// /// The value `B`, at bit position `1`.
239/// B = 0b00000010,
240/// /// The value `C`, at bit position `2`.
241/// C = 0b00000100,
242///
243/// /// The combination of `A`, `B`, and `C`.
244/// ABC = A | B | C,
245/// }
246/// ```
247///
248/// ### Arbitrary feature
249///
250/// If the crate is compiled with the `arbitrary` feature, this crate will generate implementations for
251/// the `arbitrary::Arbitrary` traits if they are included in the `#[derive(...)]`
252/// parameters, but it will not import/re-export these traits, your project must have `arbitrary` as
253/// a direct dependency.
254///
255/// ```no_run
256/// use bitflag_attr::bitflag;
257/// use arbitrary::Arbitrary;
258///
259/// #[bitflag(u32)]
260/// #[derive(Clone, Copy, Arbitrary)]
261/// enum Color {
262/// RED = 0x1,
263/// GREEN = 0x02,
264/// BLUE = 0x4,
265/// }
266/// ```
267///
268/// ### Bytemuck feature
269///
270/// If the crate is compiled with the `bytemuck` feature, this crate will generate implementations for
271/// the `bytemuck::{Pod, Zeroable}` traits if they are included in the `#[derive(...)]`
272/// parameters, but it will not import/re-export these traits, your project must have `bytemuck` as
273/// a direct dependency.
274///
275/// ```no_run
276/// use bitflag_attr::bitflag;
277/// use bytemuck::{Pod, Zeroable};
278///
279/// #[bitflag(u32)]
280/// #[derive(Debug, Clone, Copy, Pod, Zeroable)]
281/// pub enum Flags {
282/// /// The value `A`, at bit position `0`.
283/// A = 0b00000001,
284/// /// The value `B`, at bit position `1`.
285/// B = 0b00000010,
286/// /// The value `C`, at bit position `2`.
287/// C = 0b00000100,
288///
289/// /// The combination of `A`, `B`, and `C`.
290/// ABC = A | B | C,
291/// }
292/// ```
293///
294/// ### `const-mut-ref` feature
295///
296/// If the crate is compiled with the `const-mut-ref` feature, all type-associated API that takes
297/// `&mut self` will be generated as **const-fn**, meaning they can be used on `const` context.
298///
299/// **Note:** `&mut` on const function was stabilized on Rust 1.83.0, so using this feature flag on
300/// Rust versions below that will cause compilation errors
301///
302/// ### Custom types feature
303///
304/// If the crate is compiled with the `custom-types` feature, it allows to use more than the types
305/// defined in Rust `core` (`i8`,`u8`,`i16`,`u16`,`i32`,`u32`,`i64`,`u64`,`i128`,`u128`,`isize`,
306/// `usize`,`c_char`,`c_schar`,`c_uchar`,`c_short`,`c_ushort`,`c_int`,`c_uint`,`c_long`,`c_ulong`,
307/// `c_longlong`,`c_ulonglong`) and the unix types alias in the `libc` crate as long as it is a type
308/// alias to one of those types.
309///
310/// The reason it is behind a feature flag is that to ensure the validity of such constrain, we have
311/// to pay the price of having much worse error messages. With this feature enabled, a invalid type
312/// will cause a massive wall of error message.
313///
314///
315/// # More Examples
316///
317/// ```
318/// use bitflag_attr::bitflag;
319///
320/// #[bitflag(u32)]
321/// #[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)]
322/// pub enum Flags {
323/// /// The value `A`, at bit position `0`.
324/// A = 0b00000001,
325/// /// The value `B`, at bit position `1`.
326/// B = 0b00000010,
327/// /// The value `C`, at bit position `2`.
328/// C = 0b00000100,
329///
330/// /// The combination of `A`, `B`, and `C`.
331/// ABC = A | B | C,
332/// }
333/// ```
334///
335/// Without generating [`fmt::Debug`]:
336///
337/// ```
338/// use bitflag_attr::bitflag;
339///
340/// #[bitflag(u32)]
341/// #[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)]
342/// pub enum Flags {
343/// /// The value `A`, at bit position `0`.
344/// A = 0b00000001,
345/// /// The value `B`, at bit position `1`.
346/// B = 0b00000010,
347/// /// The value `C`, at bit position `2`.
348/// C = 0b00000100,
349///
350/// /// The combination of `A`, `B`, and `C`.
351/// ABC = A | B | C,
352/// }
353/// ```
354///
355/// # Syntax
356///
357/// ```rust,no_run
358/// #[bitflag($ty)]
359/// #[repr($repr_kind)] // optional: defaults to `repr(transparent)`
360/// #[non_exhaustive] // optional: If set, reserved_bits default to `!0`
361/// #[reserved_bits = $custom_extra_valid_expr] // optional
362/// #[derive(Clone, Copy, $other_derives)]
363/// $visibility enum $StructName {
364/// FlagOne = flag1_value_expr,
365/// FlagTwo = flag2_value_expr,
366/// // ...
367/// FlagN = flagn_value_expr,
368/// }
369/// ```
370///
371/// [`fmt::Debug`]: core::fmt::Debug
372/// [`ops:Not`]: core::ops::Not
373/// [`ops:BitAnd`]: core::ops::BitAnd
374/// [`ops:BitOr`]: core::ops::BitOr
375/// [`ops:BitXor`]: core::ops::BitXor
376/// [`ops:BitAndAssign`]: core::ops::BitAndAssign
377/// [`ops:BitOrAssign`]: core::ops::BitOrAssign
378/// [`ops:BitXorAssign`]: core::ops::BitXorAssign
379/// [`fmt::Binary`]: core::fmt::Binary
380/// [`fmt::LowerHex`]: core::fmt::LowerHex
381/// [`fmt::UpperHex`]: core::fmt::UpperHex
382/// [`fmt::Octal`]: core::fmt::Octal
383/// [`From`]: From
384/// [`FromStr`]: core::str::FromStr
385/// [`Default`]: core::default::Default
386/// [`IntoIterator`]: core::iter::IntoIterator
387/// [`Extend`]: core::iter::Extend
388/// [`FromIterator`]: core::iter::FromIterator
389#[proc_macro_attribute]
390pub fn bitflag(attr: TokenStream, item: TokenStream) -> TokenStream {
391 match bitflag_impl(attr, item) {
392 Ok(ts) => ts,
393 Err(err) => err.into_compile_error().into(),
394 }
395}
396
397fn bitflag_impl(attr: TokenStream, item: TokenStream) -> Result<TokenStream> {
398 let args: Args = syn::parse(attr)
399 .map_err(|err| Error::new(err.span(), "unexpected token: expected a `{integer}` type"))?;
400
401 let bitflag = Bitflag::parse(args, item)?;
402
403 Ok(bitflag.to_token_stream().into())
404}