|
3 | 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
4 | 4 |
|
5 | 5 | use proc_macro2::TokenStream; |
| 6 | +use std::collections::HashSet; |
| 7 | +use syn::fold::Fold; |
6 | 8 | use syn::parse::{Parse, ParseStream, Parser, Result as ParseResult}; |
7 | 9 |
|
8 | 10 | // $(#[$outer:meta])* |
@@ -84,17 +86,86 @@ struct Flag { |
84 | 86 | semicolon_token: Token![;], |
85 | 87 | } |
86 | 88 |
|
| 89 | +struct FlagValueFold<'a> { |
| 90 | + struct_name: &'a syn::Ident, |
| 91 | + flag_names: &'a HashSet<String>, |
| 92 | +} |
| 93 | + |
| 94 | +impl<'a> FlagValueFold<'a> { |
| 95 | + fn is_self(&self, ident: &syn::Ident) -> bool { |
| 96 | + ident == self.struct_name || ident == "Self" |
| 97 | + } |
| 98 | +} |
| 99 | + |
| 100 | +impl<'a> Fold for FlagValueFold<'a> { |
| 101 | + fn fold_expr(&mut self, node: syn::Expr) -> syn::Expr { |
| 102 | + // bitflags 2 doesn't expose `bits` publically anymore, and the documented way to |
| 103 | + // combine flags is using the `bits` method, e.g. |
| 104 | + // ``` |
| 105 | + // bitflags! { |
| 106 | + // struct Flags: u8 { |
| 107 | + // const A = 1; |
| 108 | + // const B = 1 << 1; |
| 109 | + // const AB = Flags::A.bits() | Flags::B.bits(); |
| 110 | + // } |
| 111 | + // } |
| 112 | + // ``` |
| 113 | + // As we're transforming the struct definition into `struct StructName { bits: T }` |
| 114 | + // as far as our bindings generation is concerned, `bits` is available as a field, |
| 115 | + // so by replacing `StructName::FLAG.bits()` with `StructName::FLAG.bits`, we make |
| 116 | + // e.g. `Flags::AB` available in the generated bindings. |
| 117 | + match node { |
| 118 | + syn::Expr::MethodCall(syn::ExprMethodCall { |
| 119 | + attrs, |
| 120 | + receiver, |
| 121 | + dot_token, |
| 122 | + method, |
| 123 | + args, |
| 124 | + .. |
| 125 | + }) if method == "bits" |
| 126 | + && args.is_empty() |
| 127 | + && matches!(&*receiver, |
| 128 | + syn::Expr::Path(syn::ExprPath { path, .. }) |
| 129 | + if path.segments.len() == 2 |
| 130 | + && self.is_self(&path.segments.first().unwrap().ident) |
| 131 | + && self |
| 132 | + .flag_names |
| 133 | + .contains(&path.segments.last().unwrap().ident.to_string())) => |
| 134 | + { |
| 135 | + return syn::Expr::Field(syn::ExprField { |
| 136 | + attrs, |
| 137 | + base: receiver, |
| 138 | + dot_token, |
| 139 | + member: syn::Member::Named(method), |
| 140 | + }); |
| 141 | + } |
| 142 | + _ => {} |
| 143 | + } |
| 144 | + syn::fold::fold_expr(self, node) |
| 145 | + } |
| 146 | +} |
| 147 | + |
87 | 148 | impl Flag { |
88 | | - fn expand(&self, struct_name: &syn::Ident, repr: &syn::Type) -> TokenStream { |
| 149 | + fn expand( |
| 150 | + &self, |
| 151 | + struct_name: &syn::Ident, |
| 152 | + repr: &syn::Type, |
| 153 | + flag_names: &HashSet<String>, |
| 154 | + ) -> TokenStream { |
89 | 155 | let Flag { |
90 | 156 | ref attrs, |
91 | 157 | ref name, |
92 | 158 | ref value, |
93 | 159 | .. |
94 | 160 | } = *self; |
| 161 | + let folded_value = FlagValueFold { |
| 162 | + struct_name, |
| 163 | + flag_names, |
| 164 | + } |
| 165 | + .fold_expr(value.clone()); |
95 | 166 | quote! { |
96 | 167 | #(#attrs)* |
97 | | - pub const #name : #struct_name = #struct_name { bits: (#value) as #repr }; |
| 168 | + pub const #name : #struct_name = #struct_name { bits: (#folded_value) as #repr }; |
98 | 169 | } |
99 | 170 | } |
100 | 171 | } |
@@ -130,8 +201,13 @@ impl Parse for Flags { |
130 | 201 | impl Flags { |
131 | 202 | fn expand(&self, struct_name: &syn::Ident, repr: &syn::Type) -> TokenStream { |
132 | 203 | let mut ts = quote! {}; |
| 204 | + let flag_names = self |
| 205 | + .0 |
| 206 | + .iter() |
| 207 | + .map(|flag| flag.name.to_string()) |
| 208 | + .collect::<HashSet<_>>(); |
133 | 209 | for flag in &self.0 { |
134 | | - ts.extend(flag.expand(struct_name, repr)); |
| 210 | + ts.extend(flag.expand(struct_name, repr, &flag_names)); |
135 | 211 | } |
136 | 212 | ts |
137 | 213 | } |
|
0 commit comments