summaryrefslogtreecommitdiffstats
path: root/src/bindgen/bitflags.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/bindgen/bitflags.rs')
-rw-r--r--src/bindgen/bitflags.rs289
1 files changed, 289 insertions, 0 deletions
diff --git a/src/bindgen/bitflags.rs b/src/bindgen/bitflags.rs
new file mode 100644
index 0000000..2ca0be6
--- /dev/null
+++ b/src/bindgen/bitflags.rs
@@ -0,0 +1,289 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use proc_macro2::TokenStream;
+use std::collections::HashSet;
+use syn::fold::Fold;
+use syn::parse::{Parse, ParseStream, Parser, Result as ParseResult};
+
+// $(#[$outer:meta])*
+// ($($vis:tt)*) $BitFlags:ident: $T:ty {
+// $(
+// $(#[$inner:ident $($args:tt)*])*
+// const $Flag:ident = $value:expr;
+// )+
+// }
+#[derive(Debug)]
+pub struct BitflagsStruct {
+ attrs: Vec<syn::Attribute>,
+ vis: syn::Visibility,
+ #[allow(dead_code)]
+ struct_token: Token![struct],
+ name: syn::Ident,
+ #[allow(dead_code)]
+ colon_token: Token![:],
+ repr: syn::Type,
+ flags: Flags,
+}
+
+// impl $BitFlags:ident: $T:ty {
+// $(
+// $(#[$inner:ident $($args:tt)*])*
+// const $Flag:ident = $value:expr;
+// )+
+// }
+#[derive(Debug)]
+pub struct BitflagsImpl {
+ #[allow(dead_code)]
+ impl_token: Token![impl],
+ name: syn::Ident,
+ #[allow(dead_code)]
+ colon_token: Token![:],
+ repr: syn::Type,
+ flags: Flags,
+}
+
+#[derive(Debug)]
+pub enum Bitflags {
+ Struct(BitflagsStruct),
+ Impl(BitflagsImpl),
+}
+
+impl Bitflags {
+ pub fn expand(&self) -> (Option<syn::ItemStruct>, syn::ItemImpl) {
+ match self {
+ Bitflags::Struct(BitflagsStruct {
+ attrs,
+ vis,
+ name,
+ repr,
+ flags,
+ ..
+ }) => {
+ let struct_ = parse_quote! {
+ #(#attrs)*
+ #vis struct #name {
+ bits: #repr,
+ }
+ };
+
+ let consts = flags.expand(name, repr, false);
+ let impl_ = parse_quote! {
+ impl #name {
+ #consts
+ }
+ };
+
+ (Some(struct_), impl_)
+ }
+ Bitflags::Impl(BitflagsImpl {
+ name, repr, flags, ..
+ }) => {
+ let consts = flags.expand(name, repr, true);
+ let impl_: syn::ItemImpl = parse_quote! {
+ impl #name {
+ #consts
+ }
+ };
+ (None, impl_)
+ }
+ }
+ }
+}
+
+impl Parse for Bitflags {
+ fn parse(input: ParseStream) -> ParseResult<Self> {
+ Ok(if input.peek(Token![impl]) {
+ Self::Impl(BitflagsImpl {
+ impl_token: input.parse()?,
+ name: input.parse()?,
+ colon_token: input.parse()?,
+ repr: input.parse()?,
+ flags: input.parse()?,
+ })
+ } else {
+ Self::Struct(BitflagsStruct {
+ attrs: input.call(syn::Attribute::parse_outer)?,
+ vis: input.parse()?,
+ struct_token: input.parse()?,
+ name: input.parse()?,
+ colon_token: input.parse()?,
+ repr: input.parse()?,
+ flags: input.parse()?,
+ })
+ })
+ }
+}
+
+// $(#[$inner:ident $($args:tt)*])*
+// const $Flag:ident = $value:expr;
+#[derive(Debug)]
+struct Flag {
+ attrs: Vec<syn::Attribute>,
+ #[allow(dead_code)]
+ const_token: Token![const],
+ name: syn::Ident,
+ #[allow(dead_code)]
+ equals_token: Token![=],
+ value: syn::Expr,
+ #[allow(dead_code)]
+ semicolon_token: Token![;],
+}
+
+struct FlagValueFold<'a> {
+ struct_name: &'a syn::Ident,
+ flag_names: &'a HashSet<String>,
+ out_of_line: bool,
+}
+
+impl<'a> FlagValueFold<'a> {
+ fn is_self(&self, ident: &syn::Ident) -> bool {
+ ident == self.struct_name || ident == "Self"
+ }
+}
+
+impl<'a> Fold for FlagValueFold<'a> {
+ fn fold_expr(&mut self, node: syn::Expr) -> syn::Expr {
+ // bitflags 2 doesn't expose `bits` publically anymore, and the documented way to
+ // combine flags is using the `bits` method, e.g.
+ // ```
+ // bitflags! {
+ // struct Flags: u8 {
+ // const A = 1;
+ // const B = 1 << 1;
+ // const AB = Flags::A.bits() | Flags::B.bits();
+ // }
+ // }
+ // ```
+ // As we're transforming the struct definition into `struct StructName { bits: T }`
+ // as far as our bindings generation is concerned, `bits` is available as a field,
+ // so by replacing `StructName::FLAG.bits()` with `StructName::FLAG.bits`, we make
+ // e.g. `Flags::AB` available in the generated bindings.
+ // For out-of-line definitions of the struct(*), where the struct is defined as a
+ // newtype, we replace it with `StructName::FLAGS.0`.
+ // * definitions like:
+ // ```
+ // struct Flags(u8);
+ // bitflags! {
+ // impl Flags: u8 {
+ // const A = 1;
+ // const B = 1 << 1;
+ // const AB = Flags::A.bits() | Flags::B.bits();
+ // }
+ // }
+ // ```
+ match node {
+ syn::Expr::MethodCall(syn::ExprMethodCall {
+ attrs,
+ receiver,
+ dot_token,
+ method,
+ args,
+ ..
+ }) if method == "bits"
+ && args.is_empty()
+ && matches!(&*receiver,
+ syn::Expr::Path(syn::ExprPath { path, .. })
+ if path.segments.len() == 2
+ && self.is_self(&path.segments.first().unwrap().ident)
+ && self
+ .flag_names
+ .contains(&path.segments.last().unwrap().ident.to_string())) =>
+ {
+ return syn::Expr::Field(syn::ExprField {
+ attrs,
+ base: receiver,
+ dot_token,
+ member: if self.out_of_line {
+ syn::Member::Unnamed(parse_quote! {0})
+ } else {
+ syn::Member::Named(method)
+ },
+ });
+ }
+ _ => {}
+ }
+ syn::fold::fold_expr(self, node)
+ }
+}
+
+impl Flag {
+ fn expand(
+ &self,
+ struct_name: &syn::Ident,
+ repr: &syn::Type,
+ flag_names: &HashSet<String>,
+ out_of_line: bool,
+ ) -> TokenStream {
+ let Flag {
+ ref attrs,
+ ref name,
+ ref value,
+ ..
+ } = *self;
+ let folded_value = FlagValueFold {
+ struct_name,
+ flag_names,
+ out_of_line,
+ }
+ .fold_expr(value.clone());
+ let value = if out_of_line {
+ quote! { ((#folded_value) as #repr) }
+ } else {
+ quote! { { bits: (#folded_value) as #repr } }
+ };
+ quote! {
+ #(#attrs)*
+ pub const #name : #struct_name = #struct_name #value;
+ }
+ }
+}
+
+impl Parse for Flag {
+ fn parse(input: ParseStream) -> ParseResult<Self> {
+ Ok(Self {
+ attrs: input.call(syn::Attribute::parse_outer)?,
+ const_token: input.parse()?,
+ name: input.parse()?,
+ equals_token: input.parse()?,
+ value: input.parse()?,
+ semicolon_token: input.parse()?,
+ })
+ }
+}
+
+#[derive(Debug)]
+struct Flags(Vec<Flag>);
+
+impl Parse for Flags {
+ fn parse(input: ParseStream) -> ParseResult<Self> {
+ let content;
+ let _ = braced!(content in input);
+ let mut flags = vec![];
+ while !content.is_empty() {
+ flags.push(content.parse()?);
+ }
+ Ok(Flags(flags))
+ }
+}
+
+impl Flags {
+ fn expand(&self, struct_name: &syn::Ident, repr: &syn::Type, out_of_line: bool) -> TokenStream {
+ let mut ts = quote! {};
+ let flag_names = self
+ .0
+ .iter()
+ .map(|flag| flag.name.to_string())
+ .collect::<HashSet<_>>();
+ for flag in &self.0 {
+ ts.extend(flag.expand(struct_name, repr, &flag_names, out_of_line));
+ }
+ ts
+ }
+}
+
+pub fn parse(tokens: TokenStream) -> ParseResult<Bitflags> {
+ let parser = Bitflags::parse;
+ parser.parse2(tokens)
+}