summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xsrc/bin/bindgen.rs10
-rwxr-xr-xsrc/clang.rs17
-rwxr-xr-xsrc/codegen/mod.rs212
-rwxr-xr-xsrc/lib.rs13
-rw-r--r--tests/expectations/tests/bitfield-enum-basic.rs77
-rw-r--r--tests/headers/bitfield-enum-basic.h27
6 files changed, 308 insertions, 48 deletions
diff --git a/src/bin/bindgen.rs b/src/bin/bindgen.rs
index 2dbb1690..bdb3b292 100755
--- a/src/bin/bindgen.rs
+++ b/src/bin/bindgen.rs
@@ -28,6 +28,7 @@ Usage:
[--whitelist-type=<type>...] \
[--whitelist-function=<name>...] \
[--whitelist-var=<name>...] \
+ [--bitfield-enum=<name>...] \
<input-header> \
[-- <clang-args>...]
@@ -91,6 +92,10 @@ Options:
matching <regex>. Same behavior on emptyness
than the type whitelisting.
+ --bitfield-enum=<regex> Mark any enum whose name matches <regex> as a
+ set of bitfield flags instead of an
+ enumeration.
+
--dummy-uses=<path> For testing purposes, generate a C/C++ file
containing dummy uses of all types defined in
the input header.
@@ -163,6 +168,11 @@ fn parse_args_or_exit(args: Vec<String>) -> (BindgenOptions, Box<io::Write>) {
.expect("--whitelist-var expects a pattern");
options.whitelisted_vars.insert(&var_pat);
}
+ "--bitfield-enum" => {
+ let enum_pat = iter.next()
+ .expect("--bitfield-enum expects a pattern");
+ options.bitfield_enums.insert(&enum_pat);
+ }
"--" => {
while let Some(clang_arg) = iter.next() {
options.clang_args.push(clang_arg);
diff --git a/src/clang.rs b/src/clang.rs
index e6d78123..12397534 100755
--- a/src/clang.rs
+++ b/src/clang.rs
@@ -102,7 +102,9 @@ impl Cursor {
/// lexical parents.
pub fn fallible_semantic_parent(&self) -> Option<Cursor> {
let sp = unsafe {
- Cursor { x: clang_getCursorSemanticParent(self.x) }
+ Cursor {
+ x: clang_getCursorSemanticParent(self.x),
+ }
};
if sp == *self || !sp.is_valid() {
return None;
@@ -161,7 +163,8 @@ impl Cursor {
(semantic_parent.unwrap().kind() == CXCursor_Namespace ||
semantic_parent.unwrap().kind() == CXCursor_NamespaceAlias ||
semantic_parent.unwrap().kind() == CXCursor_NamespaceRef) {
- semantic_parent = semantic_parent.unwrap().fallible_semantic_parent();
+ semantic_parent = semantic_parent.unwrap()
+ .fallible_semantic_parent();
}
let tu = self.translation_unit();
@@ -363,9 +366,9 @@ impl Cursor {
///
/// Returns None if the cursor's referent is not an enum variant.
pub fn enum_val_unsigned(&self) -> Option<u64> {
- unsafe {
+ unsafe {
if self.kind() == CXCursor_EnumConstantDecl {
- Some(clang_getEnumConstantDeclUnsignedValue(self.x) as u64)
+ Some(clang_getEnumConstantDeclUnsignedValue(self.x) as u64)
} else {
None
}
@@ -691,11 +694,7 @@ impl Type {
let rt = Type {
x: unsafe { clang_getResultType(self.x) },
};
- if rt.is_valid() {
- Some(rt)
- } else {
- None
- }
+ if rt.is_valid() { Some(rt) } else { None }
}
/// Given that this type is a function type, get its calling convention. If
diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs
index 7fa8b19c..20f377e1 100755
--- a/src/codegen/mod.rs
+++ b/src/codegen/mod.rs
@@ -6,7 +6,7 @@ use aster;
use ir::annotations::FieldAccessorKind;
use ir::comp::{CompInfo, CompKind, Field, Method};
use ir::context::BindgenContext;
-use ir::enum_ty::Enum;
+use ir::enum_ty::{Enum, EnumVariant, EnumVariantValue};
use ir::function::{Function, FunctionSig};
use ir::int::IntKind;
use ir::item::{Item, ItemCanonicalName, ItemCanonicalPath, ItemId};
@@ -1224,6 +1224,117 @@ impl MethodCodegen for Method {
}
}
+/// A helper type to construct enums, either bitfield ones or rust-style ones.
+enum EnumBuilder<'a> {
+ Rust(aster::item::ItemEnumBuilder<aster::invoke::Identity>),
+ Bitfield {
+ canonical_name: &'a str,
+ aster: P<ast::Item>,
+ },
+}
+
+impl<'a> EnumBuilder<'a> {
+ /// Create a new enum given an item builder, a canonical name, a name for
+ /// the representation, and whether it should be represented as a rust enum.
+ fn new(aster: aster::item::ItemBuilder<aster::invoke::Identity>,
+ name: &'a str,
+ repr_name: &str,
+ is_rust: bool)
+ -> Self {
+ if is_rust {
+ EnumBuilder::Rust(aster.enum_(name))
+ } else {
+ EnumBuilder::Bitfield {
+ canonical_name: name,
+ aster: aster.tuple_struct(name)
+ .field()
+ .pub_()
+ .ty()
+ .id(repr_name)
+ .build(),
+ }
+ }
+ }
+
+ /// Add a variant to this enum.
+ fn with_variant(self,
+ ctx: &BindgenContext,
+ variant: &EnumVariant,
+ mangling_prefix: Option<&String>,
+ rust_ty: P<ast::Ty>,
+ result: &mut CodegenResult)
+ -> Self {
+ let variant_name = ctx.rust_mangle(variant.name());
+ let expr = aster::AstBuilder::new().expr();
+ let expr = match variant.val() {
+ EnumVariantValue::Signed(v) => expr.int(v),
+ EnumVariantValue::Unsigned(v) => expr.uint(v),
+ };
+
+ match self {
+ EnumBuilder::Rust(b) => {
+ EnumBuilder::Rust(b.with_variant_(ast::Variant_ {
+ name: ctx.rust_ident(&*variant_name),
+ attrs: vec![],
+ data: ast::VariantData::Unit(ast::DUMMY_NODE_ID),
+ disr_expr: Some(expr),
+ }))
+ }
+ EnumBuilder::Bitfield { canonical_name, .. } => {
+ let constant_name = match mangling_prefix {
+ Some(prefix) => {
+ Cow::Owned(format!("{}_{}", prefix, variant_name))
+ }
+ None => variant_name,
+ };
+
+ let constant = aster::AstBuilder::new()
+ .item()
+ .pub_()
+ .const_(&*constant_name)
+ .expr()
+ .call()
+ .id(canonical_name)
+ .arg()
+ .build(expr)
+ .build()
+ .build(rust_ty);
+ result.push(constant);
+ self
+ }
+ }
+ }
+
+ fn build(self,
+ ctx: &BindgenContext,
+ rust_ty: P<ast::Ty>,
+ result: &mut CodegenResult)
+ -> P<ast::Item> {
+ match self {
+ EnumBuilder::Rust(b) => b.build(),
+ EnumBuilder::Bitfield { canonical_name, aster } => {
+ let rust_ty_name = ctx.rust_ident_raw(canonical_name);
+ let prefix = ctx.trait_prefix();
+
+ let impl_ = quote_item!(ctx.ext_cx(),
+ impl ::$prefix::ops::BitOr<$rust_ty> for $rust_ty {
+ type Output = Self;
+
+ #[inline]
+ fn bitor(self, other: Self) -> Self {
+ $rust_ty_name(self.0 | other.0)
+ }
+ }
+ )
+ .unwrap();
+
+ result.push(impl_);
+ aster
+ }
+ }
+ }
+}
+
impl CodeGenerator for Enum {
type Extra = Item;
@@ -1231,10 +1342,9 @@ impl CodeGenerator for Enum {
ctx: &BindgenContext,
result: &mut CodegenResult,
item: &Item) {
- use ir::enum_ty::EnumVariantValue;
-
let name = item.canonical_name(ctx);
- let layout = item.expect_type().layout(ctx);
+ let enum_ty = item.expect_type();
+ let layout = enum_ty.layout(ctx);
let repr = self.repr().map(|repr| ctx.resolve_type(repr));
let repr = match repr {
@@ -1270,10 +1380,24 @@ impl CodeGenerator for Enum {
let mut builder = aster::AstBuilder::new().item().pub_();
+ let is_bitfield = {
+ ctx.options().bitfield_enums.matches(&name) ||
+ (enum_ty.name().is_none() &&
+ self.variants()
+ .iter()
+ .any(|v| ctx.options().bitfield_enums.matches(&v.name())))
+ };
+
+ let is_rust_enum = !is_bitfield;
+
// FIXME: Rust forbids repr with empty enums. Remove this condition when
// this is allowed.
- if !self.variants().is_empty() {
- builder = builder.with_attr(attributes::repr(repr_name));
+ if is_rust_enum {
+ if !self.variants().is_empty() {
+ builder = builder.with_attr(attributes::repr(repr_name));
+ }
+ } else {
+ builder = builder.with_attr(attributes::repr("C"));
}
if let Some(comment) = item.comment() {
@@ -1289,8 +1413,6 @@ impl CodeGenerator for Enum {
builder = builder.with_attr(derives);
- let mut builder = builder.enum_(&name);
-
fn add_constant(enum_: &Type,
// Only to avoid recomputing every time.
enum_canonical_name: &str,
@@ -1318,52 +1440,64 @@ impl CodeGenerator for Enum {
result.push(constant);
}
- // Used to mangle the constants we generate in the unnamed-enum case.
- let mut parent_canonical_name = None;
+ let mut builder =
+ EnumBuilder::new(builder, &name, repr_name, is_rust_enum);
// A map where we keep a value -> variant relation.
let mut seen_values = HashMap::<_, String>::new();
- let enum_ty = item.expect_type();
let enum_rust_ty = item.to_rust_ty(ctx);
+ let is_toplevel = item.is_toplevel(ctx);
+
+ // Used to mangle the constants we generate in the unnamed-enum case.
+ let parent_canonical_name = if is_toplevel {
+ None
+ } else {
+ Some(item.parent_id().canonical_name(ctx))
+ };
+
+ let constant_mangling_prefix = if enum_ty.name().is_none() {
+ parent_canonical_name.as_ref().map(|n| &*n)
+ } else {
+ Some(&name)
+ };
+
for variant in self.variants().iter() {
match seen_values.entry(variant.val()) {
Entry::Occupied(ref entry) => {
- let existing_variant_name = entry.get();
- let variant_name = ctx.rust_mangle(variant.name());
- add_constant(enum_ty,
- &name,
- &*variant_name,
- existing_variant_name,
- enum_rust_ty.clone(),
- result);
+ if is_rust_enum {
+ let existing_variant_name = entry.get();
+ let variant_name = ctx.rust_mangle(variant.name());
+ add_constant(enum_ty,
+ &name,
+ &*variant_name,
+ existing_variant_name,
+ enum_rust_ty.clone(),
+ result);
+ } else {
+ builder = builder.with_variant(ctx,
+ variant,
+ constant_mangling_prefix,
+ enum_rust_ty.clone(),
+ result);
+ }
}
Entry::Vacant(entry) => {
- let expr = aster::AstBuilder::new().expr();
- let expr = match variant.val() {
- EnumVariantValue::Signed(val) => expr.int(val),
- EnumVariantValue::Unsigned(val) => expr.uint(val),
- };
+ builder = builder.with_variant(ctx,
+ variant,
+ constant_mangling_prefix,
+ enum_rust_ty.clone(),
+ result);
+
let variant_name = ctx.rust_mangle(variant.name());
- builder = builder.with_variant_(ast::Variant_ {
- name: ctx.rust_ident(&*variant_name),
- attrs: vec![],
- data: ast::VariantData::Unit(ast::DUMMY_NODE_ID),
- disr_expr: Some(expr),
- });
// If it's an unnamed enum, we also generate a constant so
// it can be properly accessed.
- if enum_ty.name().is_none() {
+ if is_rust_enum && enum_ty.name().is_none() {
// NB: if we want to do this for other kind of nested
// enums we can probably mangle the name.
- let mangled_name = if item.is_toplevel(ctx) {
+ let mangled_name = if is_toplevel {
variant_name.clone()
} else {
- if parent_canonical_name.is_none() {
- parent_canonical_name = Some(item.parent_id()
- .canonical_name(ctx));
- }
-
let parent_name = parent_canonical_name.as_ref()
.unwrap();
@@ -1384,8 +1518,8 @@ impl CodeGenerator for Enum {
}
}
-
- result.push(builder.build());
+ let enum_ = builder.build(ctx, enum_rust_ty, result);
+ result.push(enum_);
}
}
diff --git a/src/lib.rs b/src/lib.rs
index 03dac3cc..316212dc 100755
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -166,6 +166,15 @@ impl Builder {
self
}
+ /// Mark the given enum (or set of enums, if using a pattern) as being
+ /// bitfield-like.
+ ///
+ /// This makes bindgen generate a type that isn't a rust `enum`.
+ pub fn bitfield_enum<T: Borrow<str>>(mut self, arg: T) -> Builder {
+ self.options.bitfield_enums.insert(&arg);
+ self
+ }
+
/// Add a string to prepend to the generated bindings. The string is passed
/// through without any modification.
pub fn raw_line<T: Into<String>>(mut self, arg: T) -> Builder {
@@ -262,6 +271,9 @@ pub struct BindgenOptions {
/// Whitelisted variables. See docs for `whitelisted_types` for more.
pub whitelisted_vars: RegexSet,
+ /// The enum patterns to mark an enum as bitfield.
+ pub bitfield_enums: RegexSet,
+
/// Whether we should generate builtins or not.
pub builtins: bool,
@@ -329,6 +341,7 @@ impl Default for BindgenOptions {
whitelisted_types: Default::default(),
whitelisted_functions: Default::default(),
whitelisted_vars: Default::default(),
+ bitfield_enums: Default::default(),
builtins: false,
links: vec![],
emit_ast: false,
diff --git a/tests/expectations/tests/bitfield-enum-basic.rs b/tests/expectations/tests/bitfield-enum-basic.rs
new file mode 100644
index 00000000..03e07de6
--- /dev/null
+++ b/tests/expectations/tests/bitfield-enum-basic.rs
@@ -0,0 +1,77 @@
+/* automatically generated by rust-bindgen */
+
+
+#![allow(non_snake_case)]
+
+
+pub const Foo_Bar: Foo = Foo(2);
+pub const Foo_Baz: Foo = Foo(4);
+pub const Foo_Duplicated: Foo = Foo(4);
+pub const Foo_Negative: Foo = Foo(-3);
+impl ::std::ops::BitOr<Foo> for Foo {
+ type
+ Output
+ =
+ Self;
+ #[inline]
+ fn bitor(self, other: Self) -> Self { Foo(self.0 | other.0) }
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+pub struct Foo(pub i32);
+pub const Buz_Bar: Buz = Buz(2);
+pub const Buz_Baz: Buz = Buz(4);
+pub const Buz_Duplicated: Buz = Buz(4);
+pub const Buz_Negative: Buz = Buz(-3);
+impl ::std::ops::BitOr<Buz> for Buz {
+ type
+ Output
+ =
+ Self;
+ #[inline]
+ fn bitor(self, other: Self) -> Self { Buz(self.0 | other.0) }
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+pub struct Buz(pub i8);
+pub const NS_FOO: _bindgen_ty_1 = _bindgen_ty_1(1);
+pub const NS_BAR: _bindgen_ty_1 = _bindgen_ty_1(2);
+impl ::std::ops::BitOr<_bindgen_ty_1> for _bindgen_ty_1 {
+ type
+ Output
+ =
+ Self;
+ #[inline]
+ fn bitor(self, other: Self) -> Self { _bindgen_ty_1(self.0 | other.0) }
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+pub struct _bindgen_ty_1(pub u32);
+#[repr(C)]
+#[derive(Debug, Copy)]
+pub struct Dummy {
+ pub _address: u8,
+}
+pub const Dummy_DUMMY_FOO: Dummy__bindgen_ty_1 = Dummy__bindgen_ty_1(1);
+pub const Dummy_DUMMY_BAR: Dummy__bindgen_ty_1 = Dummy__bindgen_ty_1(2);
+impl ::std::ops::BitOr<Dummy__bindgen_ty_1> for Dummy__bindgen_ty_1 {
+ type
+ Output
+ =
+ Self;
+ #[inline]
+ fn bitor(self, other: Self) -> Self {
+ Dummy__bindgen_ty_1(self.0 | other.0)
+ }
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+pub struct Dummy__bindgen_ty_1(pub u32);
+#[test]
+fn bindgen_test_layout_Dummy() {
+ assert_eq!(::std::mem::size_of::<Dummy>() , 1usize);
+ assert_eq!(::std::mem::align_of::<Dummy>() , 1usize);
+}
+impl Clone for Dummy {
+ fn clone(&self) -> Self { *self }
+}
diff --git a/tests/headers/bitfield-enum-basic.h b/tests/headers/bitfield-enum-basic.h
new file mode 100644
index 00000000..364bebf2
--- /dev/null
+++ b/tests/headers/bitfield-enum-basic.h
@@ -0,0 +1,27 @@
+// bindgen-flags: --bitfield-enum "Foo|Buz|NS_.*|DUMMY_.*" -- -std=c++11
+
+enum Foo {
+ Bar = 1 << 1,
+ Baz = 1 << 2,
+ Duplicated = 1 << 2,
+ Negative = -3,
+};
+
+enum class Buz : signed char {
+ Bar = 1 << 1,
+ Baz = 1 << 2,
+ Duplicated = 1 << 2,
+ Negative = -3,
+};
+
+enum {
+ NS_FOO = 1 << 0,
+ NS_BAR = 1 << 1,
+};
+
+class Dummy {
+ enum {
+ DUMMY_FOO = 1 << 0,
+ DUMMY_BAR = 1 << 1,
+ };
+};