Skip to content

Commit 43af1eb

Browse files
glandiumemilio
authored andcommitted
Handle bitflags bits method calls
1 parent f72e447 commit 43af1eb

File tree

3 files changed

+84
-8
lines changed

3 files changed

+84
-8
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ heck = "0.4"
3333
[dependencies.syn]
3434
version = "1.0.88"
3535
default-features = false
36-
features = ["clone-impls", "extra-traits", "full", "parsing", "printing"]
36+
features = ["clone-impls", "extra-traits", "fold", "full", "parsing", "printing"]
3737

3838
[dev-dependencies]
3939
serial_test = "0.5.0"

src/bindgen/bitflags.rs

Lines changed: 79 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
44

55
use proc_macro2::TokenStream;
6+
use std::collections::HashSet;
7+
use syn::fold::Fold;
68
use syn::parse::{Parse, ParseStream, Parser, Result as ParseResult};
79

810
// $(#[$outer:meta])*
@@ -84,17 +86,86 @@ struct Flag {
8486
semicolon_token: Token![;],
8587
}
8688

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+
87148
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 {
89155
let Flag {
90156
ref attrs,
91157
ref name,
92158
ref value,
93159
..
94160
} = *self;
161+
let folded_value = FlagValueFold {
162+
struct_name,
163+
flag_names,
164+
}
165+
.fold_expr(value.clone());
95166
quote! {
96167
#(#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 };
98169
}
99170
}
100171
}
@@ -130,8 +201,13 @@ impl Parse for Flags {
130201
impl Flags {
131202
fn expand(&self, struct_name: &syn::Ident, repr: &syn::Type) -> TokenStream {
132203
let mut ts = quote! {};
204+
let flag_names = self
205+
.0
206+
.iter()
207+
.map(|flag| flag.name.to_string())
208+
.collect::<HashSet<_>>();
133209
for flag in &self.0 {
134-
ts.extend(flag.expand(struct_name, repr));
210+
ts.extend(flag.expand(struct_name, repr, &flag_names));
135211
}
136212
ts
137213
}

tests/rust/bitflags.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@ bitflags! {
1313
const START = 1 << 1;
1414
/// 'end'
1515
const END = 1 << 2;
16-
const ALIAS = Self::END.bits;
16+
const ALIAS = Self::END.bits();
1717
/// 'flex-start'
1818
const FLEX_START = 1 << 3;
19-
const MIXED = 1 << 4 | AlignFlags::FLEX_START.bits | AlignFlags::END.bits;
20-
const MIXED_SELF = 1 << 5 | AlignFlags::FLEX_START.bits | AlignFlags::END.bits;
19+
const MIXED = 1 << 4 | AlignFlags::FLEX_START.bits() | AlignFlags::END.bits();
20+
const MIXED_SELF = 1 << 5 | AlignFlags::FLEX_START.bits() | AlignFlags::END.bits();
2121
}
2222
}
2323

@@ -34,7 +34,7 @@ bitflags! {
3434
pub struct LargeFlags: u64 {
3535
/// Flag with a very large shift that usually would be narrowed.
3636
const LARGE_SHIFT = 1u64 << 44;
37-
const INVERTED = !Self::LARGE_SHIFT.bits;
37+
const INVERTED = !Self::LARGE_SHIFT.bits();
3838
}
3939
}
4040

0 commit comments

Comments
 (0)