| Lower `bitflags!` directly, checked against the real crate af6e50f nandithebull 6h ago | 1 | //! Shims for macros whose expansion cannot usefully be followed. |
| 2 | //! |
| 3 | //! The general rule in this project is that a macro whose expansion is not |
| 4 | //! known is rejected. `bitflags!` is an argued exception, and the argument is |
| 5 | //! this: expanding it does not help. `cargo rustc -Zunpretty=expanded` on a |
| 6 | //! one-flag user produces 869 lines that still call into |
| 7 | //! `bitflags::{Bits, Flag, Flags, iter, parser}` — which are themselves |
| 8 | //! defined by 26 further `macro_rules!` across five files. The chain does not |
| 9 | //! terminate in code we could lower. |
| 10 | //! |
| 11 | //! What the macro *means*, though, is small, documented and stable: a newtype |
| 12 | //! over an integer with named constants and set operations. So it is lowered |
| 13 | //! directly. That is not approximating a semantic we cannot represent — it is |
| 14 | //! implementing one we can, and `tests/cases/034-bitflags.rs` checks the |
| 15 | //! result against the real crate rather than against this comment. |
| 16 | //! |
| 17 | //! Verified against bitflags 2.13.2. Two behaviours are worth naming because |
| 18 | //! they are not what a reimplementation would guess: `!x` is complemented and |
| 19 | //! then masked to `all()`, and `from_bits` returns `None` for any bit outside |
| 20 | //! `all()`. |
| 21 | |
| 22 | use syn::parse::{Parse, ParseStream}; |
| 23 | use syn::{braced, Expr, Ident, Token, Type, Visibility}; |
| 24 | |
| 25 | /// One `pub struct Name: Repr { const A = ..; }` inside a `bitflags!`. |
| 26 | pub struct FlagsDef { |
| 27 | pub name: Ident, |
| 28 | pub repr: Type, |
| 29 | pub flags: Vec<(Ident, Expr)>, |
| 30 | } |
| 31 | |
| 32 | /// The whole macro body, which may declare more than one type. |
| 33 | pub struct BitflagsInput(pub Vec<FlagsDef>); |
| 34 | |
| 35 | impl Parse for BitflagsInput { |
| 36 | fn parse(input: ParseStream) -> syn::Result<Self> { |
| 37 | let mut out = Vec::new(); |
| 38 | while !input.is_empty() { |
| 39 | // Attributes on the struct (`#[derive(..)]`) carry no meaning we |
| 40 | // need: the operations they derive are generated regardless. |
| 41 | let _ = input.call(syn::Attribute::parse_outer)?; |
| 42 | let _: Visibility = input.parse()?; |
| 43 | input.parse::<Token![struct]>()?; |
| 44 | let name: Ident = input.parse()?; |
| 45 | input.parse::<Token![:]>()?; |
| 46 | let repr: Type = input.parse()?; |
| 47 | |
| 48 | let body; |
| 49 | braced!(body in input); |
| 50 | let mut flags = Vec::new(); |
| 51 | while !body.is_empty() { |
| 52 | let _ = body.call(syn::Attribute::parse_outer)?; |
| 53 | body.parse::<Token![const]>()?; |
| 54 | let fname: Ident = body.parse()?; |
| 55 | body.parse::<Token![=]>()?; |
| 56 | let value: Expr = body.parse()?; |
| 57 | body.parse::<Token![;]>()?; |
| 58 | flags.push((fname, value)); |
| 59 | } |
| 60 | out.push(FlagsDef { name, repr, flags }); |
| 61 | } |
| 62 | Ok(BitflagsInput(out)) |
| 63 | } |
| 64 | } |