summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml2
-rw-r--r--Makefile4
-rwxr-xr-xsrc/bin/bindgen.rs10
-rw-r--r--src/clang.rs28
-rw-r--r--src/clangll.rs5
-rw-r--r--src/codegen/mod.rs34
-rw-r--r--src/ir/annotations.rs2
-rw-r--r--src/ir/comp.rs17
-rw-r--r--src/ir/context.rs20
-rw-r--r--src/ir/function.rs7
-rw-r--r--src/ir/item.rs225
-rw-r--r--src/ir/item_kind.rs1
-rw-r--r--src/ir/mod.rs5
-rw-r--r--src/ir/ty.rs248
-rw-r--r--src/ir/var.rs41
-rwxr-xr-xsrc/lib.rs82
-rw-r--r--tests/expectations/anon_enum.rs2
-rw-r--r--tests/expectations/const_enum_unnamed.rs31
-rw-r--r--tests/expectations/jsval_layout_opaque.rs39
-rw-r--r--tests/expectations/replaces_double.rs15
-rw-r--r--tests/expectations/template_alias.rs12
-rw-r--r--tests/expectations/template_alias_basic.rs7
-rw-r--r--tests/expectations/template_alias_namespace.rs21
-rw-r--r--tests/expectations/template_typedef_transitive_param.rs18
-rw-r--r--tests/expectations/vector.rs19
-rw-r--r--tests/expectations/whitelist_vars.rs10
-rw-r--r--tests/headers/class_with_typedef.hpp1
-rw-r--r--tests/headers/const_enum_unnamed.hpp9
-rw-r--r--tests/headers/replaces_double.hpp20
-rw-r--r--tests/headers/template_alias.hpp13
-rw-r--r--tests/headers/template_alias_basic.hpp4
-rw-r--r--tests/headers/template_alias_namespace.hpp13
-rw-r--r--tests/headers/template_typedef_transitive_param.hpp7
-rw-r--r--tests/headers/vector.hpp3
-rw-r--r--tests/headers/whitelist_vars.h4
-rw-r--r--tests/support.rs108
-rw-r--r--tests/test_builtins.rs10
-rw-r--r--tests/test_cmath.rs13
-rw-r--r--tests/test_cxx.rs34
-rw-r--r--tests/test_decl.rs10
-rw-r--r--tests/test_enum.rs132
-rw-r--r--tests/test_extern.rs8
-rw-r--r--tests/test_func.rs50
-rw-r--r--tests/test_struct.rs264
-rw-r--r--tests/test_union.rs271
-rw-r--r--tests/tests.rs149
-rwxr-xr-xtests/tools/run-bindgen.py198
47 files changed, 1059 insertions, 1167 deletions
diff --git a/.travis.yml b/.travis.yml
index 274aefc1..70db4df3 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -40,7 +40,7 @@ before_script:
script:
- cargo build --verbose --features llvm_stable
- - make test
+ - cargo test --features llvm_stable
- git add -A
- git diff @
- git diff-index --quiet HEAD
diff --git a/Makefile b/Makefile
index cad0f513..384ce323 100644
--- a/Makefile
+++ b/Makefile
@@ -12,8 +12,8 @@ $(BINDGEN):
[ -f $@ ] || cargo build --features llvm_stable
.PHONY: test
-test: regen-tests
- @echo > /dev/null
+test:
+ cargo test --features llvm_stable
.PHONY: regen-tests
diff --git a/src/bin/bindgen.rs b/src/bin/bindgen.rs
index 1b9d4806..cb84f09b 100755
--- a/src/bin/bindgen.rs
+++ b/src/bin/bindgen.rs
@@ -60,26 +60,30 @@ Options:
--no-type-renaming Don't rename types.
- --allow-unknown-types Don't fail if we encounter types we do not
- support, instead treat them as void
-
--emit-clang-ast Output the ast (for debugging purposes)
--use-msvc-mangling Handle MSVC C++ ABI mangling; requires that
target be set to (i686|x86_64)-pc-win32
--raw-line=<raw> Add a raw line at the beginning of the output.
+
--no-unstable-rust Avoid generating unstable rust.
+
--no-bitfield-methods Avoid generating methods for bitfield access.
+
--opaque-type=<type> Mark a type as opaque.
+
--blacklist-type=<type> Mark a type as hidden.
+
--whitelist-type=<type> Whitelist the type. If this set or any other
of the whitelisting sets is not empty, then
all the non-whitelisted types (or dependant)
won't be generated.
+
--whitelist-function=<regex> Whitelist all the free-standing functions
matching <regex>. Same behavior on emptyness
than the type whitelisting.
+
--whitelist-var=<regex> Whitelist all the free-standing variables
matching <regex>. Same behavior on emptyness
than the type whitelisting.
diff --git a/src/clang.rs b/src/clang.rs
index defdf547..de405c9d 100644
--- a/src/clang.rs
+++ b/src/clang.rs
@@ -56,7 +56,7 @@ impl Cursor {
}
pub fn mangling(&self) -> String {
- unsafe {
+ unsafe {
String_ { x: clang_Cursor_getMangling(self.x) }.to_string()
}
}
@@ -136,6 +136,19 @@ impl Cursor {
self.specialized().is_valid()
}
+ pub fn is_fully_specialized_template(&self) -> bool {
+ self.is_template() && self.num_template_args() > 0
+ }
+
+ pub fn is_in_non_fully_specialized_template(&self) -> bool {
+ if self.is_toplevel() {
+ return false;
+ }
+ let parent = self.semantic_parent();
+ (parent.is_template() && !parent.is_fully_specialized_template()) ||
+ parent.is_in_non_fully_specialized_template()
+ }
+
pub fn is_valid(&self) -> bool {
unsafe {
clang_isInvalid(self.kind()) == 0
@@ -523,16 +536,16 @@ impl Type {
}
}
- // array
+ // array, vector or complex.
pub fn elem_type(&self) -> Type {
unsafe {
- Type { x: clang_getArrayElementType(self.x) }
+ Type { x: clang_getElementType(self.x) }
}
}
- pub fn array_size(&self) -> usize {
+ pub fn num_elements(&self) -> usize {
unsafe {
- clang_getArraySize(self.x) as usize
+ clang_getNumElements(self.x) as usize
}
}
@@ -722,6 +735,7 @@ impl Drop for Index {
}
// Token
+#[derive(Debug)]
pub struct Token {
pub kind: CXTokenKind,
pub spelling: String,
@@ -1036,7 +1050,9 @@ pub fn kind_to_str(x: Enum_CXCursorKind) -> &'static str {
//CXCursor_FirstPreprocessing => "FirstPreprocessing",
//CXCursor_LastPreprocessing => "LastPreprocessing",
CXCursor_PackedAttr => "PackedAttr",
-
+ CXCursor_ModuleImportDecl => "ModuleImportDecl",
+ CXCursor_TypeAliasTemplateDecl => "TypeAliasTemplateDecl",
+ CXCursor_StaticAssert => "StaticAssert",
_ => "?",
}
}
diff --git a/src/clangll.rs b/src/clangll.rs
index 47f41ff1..272fa574 100644
--- a/src/clangll.rs
+++ b/src/clangll.rs
@@ -313,8 +313,11 @@ pub const CXCursor_InclusionDirective: c_uint = 503;
pub const CXCursor_FirstPreprocessing: c_uint = 500;
pub const CXCursor_LastPreprocessing: c_uint = 503;
pub const CXCursor_ModuleImportDecl: c_uint = 600;
+pub const CXCursor_TypeAliasTemplateDecl: c_uint = 601;
+pub const CXCursor_StaticAssert: c_uint = 602;
pub const CXCursor_FirstExtraDecl: c_uint = 600;
-pub const CXCursor_LastExtraDecl: c_uint = 600;
+pub const CXCursor_LastExtraDecl: c_uint = 602;
+pub const CXCursor_OverloadCandidate: c_uint = 700;
#[repr(C)]
#[derive(Copy, Clone)]
pub struct CXCursor {
diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs
index 324b2535..17789e19 100644
--- a/src/codegen/mod.rs
+++ b/src/codegen/mod.rs
@@ -16,6 +16,7 @@ use ir::layout::Layout;
use ir::annotations::FieldAccessorKind;
use std::ops;
+use std::borrow::Cow;
use std::mem;
use std::collections::BTreeSet;
use std::collections::HashSet;
@@ -41,7 +42,7 @@ struct CodegenResult {
/// The set of generated function/var names, needed because in C/C++ is legal to
/// do something like:
///
- /// ```
+ /// ```c++
/// extern "C" {
/// void foo();
/// extern int bar;
@@ -341,6 +342,12 @@ impl CodeGenerator for Type {
return;
}
TypeKind::Comp(ref ci) => ci.codegen(ctx, result, item),
+ TypeKind::TemplateAlias(inner, _) => {
+ // NB: The inner Alias will pick the correct
+ // applicable_template_args.
+ let inner_item = ctx.resolve_item(inner);
+ inner_item.expect_type().codegen(ctx, result, inner_item);
+ }
TypeKind::Alias(ref spelling, inner) => {
let inner_item = ctx.resolve_item(inner);
let name = item.canonical_name(ctx);
@@ -1226,6 +1233,9 @@ 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;
+
// A map where we keep a value -> variant relation.
let mut seen_values = HashMap::<_, String>::new();
let enum_ty = item.expect_type();
@@ -1258,11 +1268,21 @@ impl CodeGenerator for Enum {
if enum_ty.name().is_none() {
// NB: if we want to do this for other kind of nested
// enums we can probably mangle the name.
- if item.is_toplevel(ctx) {
- add_constant(enum_ty, &name, &variant_name,
- &variant_name, enum_rust_ty.clone(),
- result);
- }
+ let mangled_name = if item.is_toplevel(ctx) {
+ variant_name.clone()
+ } else {
+ if parent_canonical_name.is_none() {
+ parent_canonical_name = Some(item.parent_id().canonical_name(ctx));
+ }
+
+ Cow::Owned(
+ format!("{}_{}", parent_canonical_name.as_ref().unwrap(),
+ variant_name))
+ };
+
+ add_constant(enum_ty, &name, &mangled_name,
+ &variant_name, enum_rust_ty.clone(),
+ result);
}
entry.insert(variant_name.into_owned());
@@ -1361,6 +1381,7 @@ impl ToRustTy for Type {
let path = item.canonical_path(ctx);
aster::AstBuilder::new().ty().path().ids(path).build()
}
+ TypeKind::TemplateAlias(inner, ref template_args) |
TypeKind::TemplateRef(inner, ref template_args) => {
// PS: Sorry for the duplication here.
let mut inner_ty = inner.to_rust_ty(ctx).unwrap();
@@ -1619,6 +1640,7 @@ impl TypeCollector for Type {
TypeKind::Pointer(inner) |
TypeKind::Reference(inner) |
TypeKind::Array(inner, _) |
+ TypeKind::TemplateAlias(inner, _) |
TypeKind::Alias(_, inner) |
TypeKind::Named(_, Some(inner)) |
TypeKind::ResolvedTypeRef(inner)
diff --git a/src/ir/annotations.rs b/src/ir/annotations.rs
index cc61dbfd..64ab255d 100644
--- a/src/ir/annotations.rs
+++ b/src/ir/annotations.rs
@@ -87,7 +87,7 @@ impl Annotations {
///
/// the generated code would look something like:
///
- /// ```rust
+ /// ```c++
/// /** <div rustbindgen replaces="Bar"></div> */
/// struct Bar {
/// int x;
diff --git a/src/ir/comp.rs b/src/ir/comp.rs
index 2085ea1a..9d1a6366 100644
--- a/src/ir/comp.rs
+++ b/src/ir/comp.rs
@@ -1,6 +1,5 @@
use super::annotations::Annotations;
use super::context::BindgenContext;
-use super::context::TypeResolver;
use super::layout::Layout;
use super::item::{Item, ItemId};
use super::ty::{Type, RUST_DERIVE_IN_ARRAY_LIMIT};
@@ -206,7 +205,7 @@ impl CompInfo {
}
}
- pub fn can_derive_debug(&self, type_resolver: &TypeResolver, layout: Option<Layout>) -> bool {
+ pub fn can_derive_debug(&self, type_resolver: &BindgenContext, layout: Option<Layout>) -> bool {
// We can reach here recursively via template parameters of a member,
// for example.
if self.detect_derive_debug_cycle.get() {
@@ -249,7 +248,7 @@ impl CompInfo {
can_derive_debug
}
- pub fn is_unsized(&self, type_resolver: &TypeResolver) -> bool {
+ pub fn is_unsized(&self, type_resolver: &BindgenContext) -> bool {
!self.has_vtable(type_resolver) && self.fields.is_empty() &&
self.base_members.iter().all(|base| {
type_resolver
@@ -262,7 +261,7 @@ impl CompInfo {
})
}
- pub fn has_destructor(&self, type_resolver: &TypeResolver) -> bool {
+ pub fn has_destructor(&self, type_resolver: &BindgenContext) -> bool {
if self.detect_has_destructor_cycle.get() {
warn!("Cycle detected looking for destructors");
// Assume no destructor, since we don't have an explicit one.
@@ -299,7 +298,7 @@ impl CompInfo {
has_destructor
}
- pub fn can_derive_copy(&self, type_resolver: &TypeResolver, item: &Item) -> bool {
+ pub fn can_derive_copy(&self, type_resolver: &BindgenContext, item: &Item) -> bool {
// NOTE: Take into account that while unions in C and C++ are copied by
// default, the may have an explicit destructor in C++, so we can't
// defer this check just for the union case.
@@ -349,7 +348,7 @@ impl CompInfo {
// If we're a union without known layout, we try to compute it from our
// members. This is not ideal, but clang fails to report the size for
// these kind of unions, see test/headers/template_union.hpp
- pub fn layout(&self, type_resolver: &TypeResolver) -> Option<Layout> {
+ pub fn layout(&self, type_resolver: &BindgenContext) -> Option<Layout> {
use std::cmp;
// We can't do better than clang here, sorry.
@@ -384,7 +383,7 @@ impl CompInfo {
self.has_non_type_template_params
}
- pub fn has_vtable(&self, type_resolver: &TypeResolver) -> bool {
+ pub fn has_vtable(&self, type_resolver: &BindgenContext) -> bool {
self.has_vtable || self.base_members().iter().any(|base| {
type_resolver
.resolve_type(*base)
@@ -688,7 +687,7 @@ impl CompInfo {
}
pub fn signature_contains_named_type(&self,
- type_resolver: &TypeResolver,
+ type_resolver: &BindgenContext,
ty: &Type) -> bool {
// We don't generate these, so rather don't make the codegen step to
// think we got it covered.
@@ -719,7 +718,7 @@ impl CompInfo {
/// Returns whether this type needs an explicit vtable because it has
/// virtual methods and none of its base classes has already a vtable.
- pub fn needs_explicit_vtable(&self, type_resolver: &TypeResolver) -> bool {
+ pub fn needs_explicit_vtable(&self, type_resolver: &BindgenContext) -> bool {
self.has_vtable(type_resolver) && !self.base_members.iter().any(|base| {
// NB: Ideally, we could rely in all these types being `comp`, and
// life would be beautiful.
diff --git a/src/ir/context.rs b/src/ir/context.rs
index 4842eb94..da5f334f 100644
--- a/src/ir/context.rs
+++ b/src/ir/context.rs
@@ -421,7 +421,7 @@ impl<'ctx> BindgenContext<'ctx> {
///
/// To see an example of what this handles:
///
- /// ```
+ /// ```c++
/// template<typename T>
/// class Incomplete {
/// T p;
@@ -647,10 +647,18 @@ impl<'ctx> BindgenContext<'ctx> {
self.replacements.insert(name.into(), potential_ty);
}
- pub fn hidden_by_name(&self, name: &str) -> bool {
+ pub fn hidden_by_name(&self, name: &str, id: ItemId) -> bool {
debug_assert!(self.in_codegen_phase(),
"You're not supposed to call this yet");
- self.options.hidden_types.contains(name)
+ self.options.hidden_types.contains(name) ||
+ self.is_replaced_type(name, id)
+ }
+
+ pub fn is_replaced_type(&self, name: &str, id: ItemId) -> bool {
+ match self.replacements.get(name) {
+ Some(replaced_by) if *replaced_by != id => true,
+ _ => false,
+ }
}
pub fn opaque_by_name(&self, name: &str) -> bool {
@@ -713,9 +721,3 @@ impl<'ctx> BindgenContext<'ctx> {
self.current_module = previous_id;
}
}
-
-/// This was originally a type that only exposes the resolve_type operation to
-/// its consumers.
-///
-/// Later a made resolve_type public, so... meh. It should go away soon.
-pub type TypeResolver<'ctx> = BindgenContext<'ctx>;
diff --git a/src/ir/function.rs b/src/ir/function.rs
index b95ac57b..af170935 100644
--- a/src/ir/function.rs
+++ b/src/ir/function.rs
@@ -75,6 +75,13 @@ fn get_abi(cc: Enum_CXCallingConv) -> abi::Abi {
}
pub fn cursor_mangling(cursor: &clang::Cursor) -> Option<String> {
+ // We early return here because libclang may crash in some case
+ // if we pass in a variable inside a partial specialized template.
+ // See servo/rust-bindgen#67.
+ if cursor.is_in_non_fully_specialized_template() {
+ return None;
+ }
+
let mut mangling = cursor.mangling();
// Try to undo backend linkage munging (prepended _, generally)
diff --git a/src/ir/item.rs b/src/ir/item.rs
index 80085c8b..18f912c1 100644
--- a/src/ir/item.rs
+++ b/src/ir/item.rs
@@ -28,7 +28,7 @@ pub trait ItemCanonicalName {
///
/// To contrast with canonical_name, here's an example:
///
-/// ```
+/// ```c++
/// namespace foo {
/// const BAR = 3;
/// }
@@ -42,7 +42,7 @@ pub trait ItemCanonicalPath {
/// A single identifier for an item.
///
-/// TODO: Build stronger abstractions on top of this, like TypeId(ItemId), ...
+/// TODO: Build stronger abstractions on top of this, like TypeId(ItemId)?
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ItemId(usize);
@@ -78,6 +78,20 @@ impl ItemCanonicalPath for ItemId {
}
}
+/// An item is the base of the bindgen representation, it can be either a
+/// module, a type, a function, or a variable (see `ItemKind` for more
+/// information).
+///
+/// Items form a graph, and each item only stores the id of the parent.
+///
+/// The entry-point of this graph is the "root module", a meta-item used to hold
+/// all the top-level items.
+///
+/// An item may have a comment, and annotations (see the `annotations` module).
+///
+/// Note that even though we parse all the types of annotations in comments, not
+/// all of them apply to every item. Those rules are described in the
+/// `annotations` module.
#[derive(Debug)]
pub struct Item {
/// This item's id.
@@ -133,6 +147,24 @@ impl Item {
&mut self.kind
}
+ /// Returns whether this item is a top-level item, from the point of view of
+ /// bindgen.
+ ///
+ /// This point of view changes depending on whether namespaces are enabled
+ /// or not. That way, in the following example:
+ ///
+ /// ```c++
+ /// namespace foo {
+ /// static int var;
+ /// }
+ /// ```
+ ///
+ /// `var` would be a toplevel item if namespaces are disabled, but won't if
+ /// they aren't.
+ ///
+ /// This function is used to determine when the codegen phase should call
+ /// `codegen` on an item, since any item that is not top-level will be
+ /// generated by its parent.
pub fn is_toplevel(&self, ctx: &BindgenContext) -> bool {
// FIXME: Workaround for some types falling behind when parsing weird
// stl classes, for example.
@@ -167,6 +199,96 @@ impl Item {
self.kind().expect_function()
}
+ /// Checks whether an item contains in its "type signature" some named type.
+ ///
+ /// This function is used to avoid unused template parameter errors in Rust
+ /// when generating typedef declarations, and also to know whether we need
+ /// to generate a PhantomData member for a template parameter.
+ ///
+ /// For example, in code like the following:
+ ///
+ /// ```c++
+ /// template<typename T, typename U>
+ /// struct Foo {
+ /// T bar;
+ ///
+ /// struct Baz {
+ /// U bas;
+ /// };
+ /// };
+ /// ```
+ ///
+ /// Both Foo and Baz contain both `T` and `U` template parameters in their
+ /// signature:
+ ///
+ /// * `Foo<T, U>`
+ /// * `Bar<T, U>`
+ ///
+ /// But the structure for `Foo` would look like:
+ ///
+ /// ```rust
+ /// struct Foo<T, U> {
+ /// bar: T,
+ /// _phantom0: ::std::marker::PhantomData<U>,
+ /// }
+ /// ```
+ ///
+ /// because non of its member fields contained the `U` type in the
+ /// signature. Similarly, `Bar` would contain a `PhantomData<T>` type, for
+ /// the same reason.
+ ///
+ /// Note that this is somewhat similar to `applicable_template_args`, but
+ /// this also takes into account other kind of types, like arrays,
+ /// (`[T; 40]`), pointers: `*mut T`, etc...
+ ///
+ /// Normally we could do this check just in the `Type` kind, but we also
+ /// need to check the `applicable_template_args` more generally, since we
+ /// could need a type transitively from our parent, see the test added in
+ /// <https://github.com/servo/rust-bindgen/pull/85/commits/2a3f93074dd2898669dbbce6e97e5cc4405d7cb1>
+ ///
+ /// It's kind of unfortunate (in the sense that it's a sort of complex
+ /// process), but I think it should get all the cases.
+ fn signature_contains_named_type(&self, ctx: &BindgenContext, ty: &Type) -> bool {
+ debug_assert!(ty.is_named());
+ self.expect_type().signature_contains_named_type(ctx, ty) ||
+ self.applicable_template_args(ctx).iter().any(|template| {
+ ctx.resolve_type(*template).signature_contains_named_type(ctx, ty)
+ })
+ }
+
+ /// Returns the template arguments that apply to a struct. This is a concept
+ /// needed because of type declarations inside templates, for example:
+ ///
+ /// ```c++
+ /// template<typename T>
+ /// class Foo {
+ /// typedef T element_type;
+ /// typedef int Bar;
+ ///
+ /// template<typename U>
+ /// class Baz {
+ /// };
+ /// };
+ /// ```
+ ///
+ /// In this case, the applicable template arguments for the different types
+ /// would be:
+ ///
+ /// * `Foo`: [`T`]
+ /// * `Foo::element_type`: [`T`]
+ /// * `Foo::Bar`: [`T`]
+ /// * `Foo::Baz`: [`T`, `U`]
+ ///
+ /// You might notice that we can't generate something like:
+ ///
+ /// ```rust,ignore
+ /// type Foo_Bar<T> = ::std::os::raw::c_int;
+ /// ```
+ ///
+ /// since that would be invalid Rust. Still, conceptually, `Bar` *could* use
+ /// the template parameter type `T`, and that's exactly what this method
+ /// represents. The unused template parameters get stripped in the
+ /// `signature_contains_named_type` check.
pub fn applicable_template_args(&self, ctx: &BindgenContext) -> Vec<ItemId> {
let ty = match *self.kind() {
ItemKind::Type(ref ty) => ty,
@@ -197,7 +319,8 @@ impl Item {
TypeKind::Alias(_, inner) => {
let parent_args = ctx.resolve_item(self.parent_id())
.applicable_template_args(ctx);
- let inner = ctx.resolve_type(inner);
+ let inner = ctx.resolve_item(inner);
+
// Avoid unused type parameters, sigh.
parent_args.iter().cloned().filter(|arg| {
let arg = ctx.resolve_type(*arg);
@@ -206,6 +329,7 @@ impl Item {
}
// XXX Is this completely correct? Partial template specialization
// is hard anyways, sigh...
+ TypeKind::TemplateAlias(_, ref args) |
TypeKind::TemplateRef(_, ref args) => {
args.clone()
}
@@ -247,19 +371,31 @@ impl Item {
debug_assert!(ctx.in_codegen_phase(),
"You're not supposed to call this yet");
self.annotations.hide() ||
- ctx.hidden_by_name(&self.real_canonical_name(ctx, false))
+ ctx.hidden_by_name(&self.real_canonical_name(ctx, false, true), self.id)
}
pub fn is_opaque(&self, ctx: &BindgenContext) -> bool {
debug_assert!(ctx.in_codegen_phase(),
"You're not supposed to call this yet");
self.annotations.opaque() ||
- ctx.opaque_by_name(&self.real_canonical_name(ctx, false))
+ ctx.opaque_by_name(&self.real_canonical_name(ctx, false, true))
}
/// Get the canonical name without taking into account the replaces
/// annotation.
- fn real_canonical_name(&self, ctx: &BindgenContext, count_namespaces: bool) -> String {
+ ///
+ /// This is the base logic used to implement hiding and replacing via
+ /// annotations, and also to implement proper name mangling.
+ ///
+ /// The idea is that each generated type in the same "level" (read: module
+ /// or namespace) has a unique canonical name.
+ ///
+ /// This name should be derived from the immutable state contained in the
+ /// type and the parent chain, since it should be consistent.
+ fn real_canonical_name(&self,
+ ctx: &BindgenContext,
+ count_namespaces: bool,
+ for_name_checking: bool) -> String {
let base_name = match *self.kind() {
ItemKind::Type(ref ty) => {
match *ty.kind() {
@@ -277,11 +413,29 @@ impl Item {
TypeKind::Named(ref name, _) => {
return name.to_owned();
}
- _ => {}
- }
-
- ty.name().map(ToOwned::to_owned)
- .unwrap_or_else(|| format!("_bindgen_ty{}", self.id()))
+ // We call codegen on the inner type, but we do not want
+ // this alias's name to appear in the canonical name just
+ // because it is in the inner type's parent chain, so we use
+ // an empty base name.
+ //
+ // Note that this would be incorrect if this type could be
+ // referenced from, let's say, a member variable, but in
+ // that case the referenced type is the inner alias, so
+ // we're good there. If we wouldn't, a more complex solution
+ // would be needed.
+ TypeKind::TemplateAlias(inner, _) => {
+ if for_name_checking {
+ return ctx.resolve_item(inner).real_canonical_name(ctx, count_namespaces, false);
+ }
+ Some("")
+ }
+ // Else use the proper name, or fallback to a name with an
+ // id.
+ _ => {
+ ty.name()
+ }
+ }.map(ToOwned::to_owned)
+ .unwrap_or_else(|| format!("_bindgen_ty{}", self.id()))
}
ItemKind::Function(ref fun) => {
let mut base = fun.name().to_owned();
@@ -329,7 +483,12 @@ impl Item {
// TODO: allow modification of the mangling functions, maybe even per
// item type?
- format!("{}_{}", parent.canonical_name(ctx), base_name)
+ let parent = parent.canonical_name(ctx);
+ if parent.is_empty() {
+ base_name.to_owned()
+ } else {
+ format!("{}_{}", parent, base_name)
+ }
}
pub fn as_module_mut(&mut self) -> Option<&mut Module> {
@@ -382,12 +541,6 @@ impl ClangItemParser for Item {
let comment = cursor.raw_comment();
let annotations = Annotations::new(&cursor);
- // FIXME: The current_module logic is not really accurate. We should be
- // able to index modules by their Cursor, and locate the proper module
- // for a given item.
- //
- // We don't support modules properly though, so there's no rush for
- // this.
let current_module = context.current_module();
macro_rules! try_parse {
($what:ident) => {
@@ -444,7 +597,8 @@ impl ClangItemParser for Item {
if cursor.kind() == clangll::CXCursor_UnexposedDecl {
Err(ParseError::Recurse)
} else {
- error!("Unhandled cursor kind: {}", ::clang::kind_to_str(cursor.kind()));
+ error!("Unhandled cursor kind: {} ({})",
+ ::clang::kind_to_str(cursor.kind()), cursor.kind());
Err(ParseError::Continue)
}
}
@@ -456,6 +610,16 @@ impl ClangItemParser for Item {
Self::from_ty_or_ref_with_id(ItemId::next(), ty, location, parent_id, context)
}
+ /// Parse a C++ type. If we find a reference to a type that has not been
+ /// defined yet, use UnresolvedTypeRef as a placeholder.
+ ///
+ /// This logic is needed to avoid parsing items with the incorrect parent
+ /// and it's sort of complex to explain, so I'll just point to
+ /// `tests/headers/typeref.hpp` to see the kind of constructs that forced
+ /// this.
+ ///
+ /// Typerefs are resolved once parsing is completely done, see
+ /// `BindgenContext::resolve_typerefs`.
fn from_ty_or_ref_with_id(potential_id: ItemId,
ty: clang::Type,
location: Option<clang::Cursor>,
@@ -495,6 +659,14 @@ impl ClangItemParser for Item {
Self::from_ty_with_id(ItemId::next(), ty, location, parent_id, context)
}
+ /// This is one of the trickiest methods you'll find (probably along with
+ /// some of the ones that handle templates in `BindgenContext`).
+ ///
+ /// This method parses a type, given the potential id of that type (if
+ /// parsing it was correct), an optional location we're scanning, which is
+ /// critical some times to obtain information, an optional parent item id,
+ /// that will, if it's `None`, become the current module id, and the
+ /// context.
fn from_ty_with_id(id: ItemId,
ty: &clang::Type,
location: Option<clang::Cursor>,
@@ -661,7 +833,7 @@ impl ItemCanonicalName for Item {
if let Some(other_canon_type) = self.annotations.use_instead_of() {
return other_canon_type.to_owned();
}
- self.real_canonical_name(ctx, ctx.options().enable_cxx_namespaces)
+ self.real_canonical_name(ctx, ctx.options().enable_cxx_namespaces, false)
}
}
@@ -698,7 +870,18 @@ impl ItemCanonicalPath for Item {
}
let mut parent_path = self.parent_id().canonical_path(&ctx);
- parent_path.push(self.real_canonical_name(ctx, true));
+ if parent_path.last().map_or(false, |parent_name| parent_name.is_empty()) {
+ // This only happens (or should only happen) when we're an alias,
+ // and our parent is a templated alias, in which case the last
+ // component of the path will be empty.
+ let is_alias = match *self.expect_type().kind() {
+ TypeKind::Alias(..) => true,
+ _ => false,
+ };
+ debug_assert!(is_alias, "How can this ever happen?");
+ parent_path.pop().unwrap();
+ }
+ parent_path.push(self.real_canonical_name(ctx, true, false));
parent_path
}
diff --git a/src/ir/item_kind.rs b/src/ir/item_kind.rs
index b6f317a7..a47d23a3 100644
--- a/src/ir/item_kind.rs
+++ b/src/ir/item_kind.rs
@@ -15,6 +15,7 @@ pub enum ItemKind {
/// A function or method declaration.
Function(Function),
+
/// A variable declaration, most likely a static.
Var(Var),
}
diff --git a/src/ir/mod.rs b/src/ir/mod.rs
index 07ac3059..01f388b8 100644
--- a/src/ir/mod.rs
+++ b/src/ir/mod.rs
@@ -1,3 +1,8 @@
+//! The ir module defines bindgen's intermediate representation.
+//!
+//! Parsing C/C++ generates the IR, while code generation outputs Rust code from
+//! the IR.
+
pub mod annotations;
pub mod comp;
pub mod context;
diff --git a/src/ir/ty.rs b/src/ir/ty.rs
index 74452243..d74b1a0e 100644
--- a/src/ir/ty.rs
+++ b/src/ir/ty.rs
@@ -5,20 +5,20 @@ use super::item::{Item, ItemId};
use super::int::IntKind;
use super::layout::Layout;
use super::context::BindgenContext;
-use super::context::TypeResolver;
use parse::{ClangItemParser, ParseResult, ParseError};
use clang::{self, Cursor};
+/// The base representation of a type in bindgen.
+///
+/// A type has an optional name, which if present cannot be empty, a `layout`
+/// (size, alignment and packedness) if known, a `Kind`, which determines which
+/// kind of type it is, and whether the type is const.
#[derive(Debug)]
pub struct Type {
/// The name of the type, or None if it was an unnamed struct or union.
name: Option<String>,
/// The layout of the type, if known.
layout: Option<Layout>,
- /// Whether this type is marked as opaque.
- opaque: bool,
- /// Whether this type is marked as hidden.
- hide: bool,
/// The inner kind of the type
kind: TypeKind,
/// Whether this type is const-qualified.
@@ -42,8 +42,6 @@ impl Type {
Type {
name: name,
layout: layout,
- opaque: false,
- hide: false,
kind: kind,
is_const: is_const,
}
@@ -117,129 +115,142 @@ impl Type {
self.is_const
}
- pub fn layout(&self, type_resolver: &TypeResolver) -> Option<Layout> {
+ pub fn layout(&self, ctx: &BindgenContext) -> Option<Layout> {
use std::mem;
self.layout.or_else(|| {
match self.kind {
TypeKind::Comp(ref ci)
- => ci.layout(type_resolver),
+ => ci.layout(ctx),
// FIXME(emilio): This is a hack for anonymous union templates.
// Use the actual pointer size!
TypeKind::Pointer(..) |
TypeKind::BlockPointer
=> Some(Layout::new(mem::size_of::<*mut ()>(), mem::align_of::<*mut ()>())),
TypeKind::ResolvedTypeRef(inner)
- => type_resolver.resolve_type(inner).layout(type_resolver),
+ => ctx.resolve_type(inner).layout(ctx),
_ => None,
}
})
}
- pub fn is_opaque(&self, _type_resolver: &TypeResolver) -> bool {
- self.opaque
- }
-
- pub fn can_derive_debug(&self, type_resolver: &TypeResolver) -> bool {
- !self.is_opaque(type_resolver) && match self.kind {
+ /// Wether we can derive rust's `Debug` annotation in Rust. This should
+ /// ideally be a no-op that just returns `true`, but instead needs to be a
+ /// recursive method that checks whether all the proper members can derive
+ /// debug or not, because of the limit rust has on 32 items as max in the
+ /// array.
+ pub fn can_derive_debug(&self, ctx: &BindgenContext) -> bool {
+ match self.kind {
TypeKind::Array(t, len) => {
len <= RUST_DERIVE_IN_ARRAY_LIMIT &&
- type_resolver.resolve_type(t).can_derive_debug(type_resolver)
+ ctx.resolve_type(t).can_derive_debug(ctx)
}
TypeKind::ResolvedTypeRef(t) |
+ TypeKind::TemplateAlias(t, _) |
TypeKind::Alias(_, t) => {
- type_resolver.resolve_type(t).can_derive_debug(type_resolver)
+ ctx.resolve_type(t).can_derive_debug(ctx)
}
TypeKind::Comp(ref info) => {
- info.can_derive_debug(type_resolver, self.layout(type_resolver))
+ info.can_derive_debug(ctx, self.layout(ctx))
}
- _ => true,
+ _ => true,
}
}
- // For some reason, deriving copies of an array of a type that is not known
- // to be copy is a compile error. e.g.:
- //
- // #[derive(Copy)]
- // struct A<T> {
- // member: T,
- // }
- //
- // is fine, while:
- //
- // #[derive(Copy)]
- // struct A<T> {
- // member: [T; 1],
- // }
- //
- // is an error.
- //
- // That's the point of the existence of can_derive_copy_in_array().
- pub fn can_derive_copy_in_array(&self, type_resolver: &TypeResolver, item: &Item) -> bool {
+ /// For some reason, deriving copies of an array of a type that is not known
+ /// to be copy is a compile error. e.g.:
+ ///
+ /// ```rust
+ /// #[derive(Copy, Clone)]
+ /// struct A<T> {
+ /// member: T,
+ /// }
+ /// ```
+ ///
+ /// is fine, while:
+ ///
+ /// ```rust,ignore
+ /// #[derive(Copy, Clone)]
+ /// struct A<T> {
+ /// member: [T; 1],
+ /// }
+ /// ```
+ ///
+ /// is an error.
+ ///
+ /// That's the whole point of the existence of `can_derive_copy_in_array`.
+ pub fn can_derive_copy_in_array(&self, ctx: &BindgenContext, item: &Item) -> bool {
match self.kind {
TypeKind::ResolvedTypeRef(t) |
+ TypeKind::TemplateAlias(t, _) |
TypeKind::Alias(_, t) |
TypeKind::Array(t, _) => {
- type_resolver.resolve_item(t)
- .can_derive_copy_in_array(type_resolver)
+ ctx.resolve_item(t)
+ .can_derive_copy_in_array(ctx)
}
TypeKind::Named(..) => false,
- _ => self.can_derive_copy(type_resolver, item),
+ _ => self.can_derive_copy(ctx, item),
}
}
- pub fn can_derive_copy(&self, type_resolver: &TypeResolver, item: &Item) -> bool {
- !self.is_opaque(type_resolver) && match self.kind {
+ /// Wether we'd be able to derive the `Copy` trait in Rust or not. Same
+ /// rationale than `can_derive_debug`.
+ pub fn can_derive_copy(&self, ctx: &BindgenContext, item: &Item) -> bool {
+ match self.kind {
TypeKind::Array(t, len) => {
len <= RUST_DERIVE_IN_ARRAY_LIMIT &&
- type_resolver.resolve_item(t).can_derive_copy_in_array(type_resolver)
+ ctx.resolve_item(t).can_derive_copy_in_array(ctx)
}
TypeKind::ResolvedTypeRef(t) |
+ TypeKind::TemplateAlias(t, _) |
TypeKind::TemplateRef(t, _) |
TypeKind::Alias(_, t) => {
- type_resolver.resolve_item(t).can_derive_copy(type_resolver)
+ ctx.resolve_item(t).can_derive_copy(ctx)
}
TypeKind::Comp(ref info) => {
- info.can_derive_copy(type_resolver, item)
+ info.can_derive_copy(ctx, item)
}
_ => true,
}
}
- pub fn has_vtable(&self, type_resolver: &TypeResolver) -> bool {
+ /// Whether this type has a vtable.
+ pub fn has_vtable(&self, ctx: &BindgenContext) -> bool {
// FIXME: Can we do something about template parameters? Huh...
match self.kind {
TypeKind::TemplateRef(t, _) |
+ TypeKind::TemplateAlias(t, _) |
TypeKind::Alias(_, t) |
- TypeKind::ResolvedTypeRef(t) |
- TypeKind::Array(t, _) => {
- type_resolver.resolve_type(t).has_vtable(type_resolver)
+ TypeKind::ResolvedTypeRef(t) => {
+ ctx.resolve_type(t).has_vtable(ctx)
}
TypeKind::Comp(ref info) => {
- info.has_vtable(type_resolver)
+ info.has_vtable(ctx)
}
_ => false,
}
}
- pub fn has_destructor(&self, type_resolver: &TypeResolver) -> bool {
- self.is_opaque(type_resolver) || match self.kind {
+ /// Returns whether this type has a destructor.
+ pub fn has_destructor(&self, ctx: &BindgenContext) -> bool {
+ match self.kind {
TypeKind::TemplateRef(t, _) |
+ TypeKind::TemplateAlias(t, _) |
TypeKind::Alias(_, t) |
- TypeKind::ResolvedTypeRef(t) |
- TypeKind::Array(t, _) => {
- type_resolver.resolve_type(t).has_destructor(type_resolver)
+ TypeKind::ResolvedTypeRef(t) => {
+ ctx.resolve_type(t).has_destructor(ctx)
}
TypeKind::Comp(ref info) => {
- info.has_destructor(type_resolver)
+ info.has_destructor(ctx)
}
_ => false,
}
}
+ /// See the comment in `Item::signature_contains_named_type`.
pub fn signature_contains_named_type(&self,
- type_resolver: &TypeResolver,
+ ctx: &BindgenContext,
ty: &Type) -> bool {
debug_assert!(ty.is_named());
let name = match *ty.kind() {
@@ -254,29 +265,35 @@ impl Type {
TypeKind::Array(t, _) |
TypeKind::Pointer(t) |
TypeKind::Alias(_, t)
- => type_resolver.resolve_type(t)
- .signature_contains_named_type(type_resolver, ty),
+ => ctx.resolve_type(t)
+ .signature_contains_named_type(ctx, ty),
TypeKind::Function(ref sig) => {
sig.argument_types().iter().any(|&(_, arg)| {
- type_resolver.resolve_type(arg)
- .signature_contains_named_type(type_resolver, ty)
+ ctx.resolve_type(arg)
+ .signature_contains_named_type(ctx, ty)
}) ||
- type_resolver.resolve_type(sig.return_type())
- .signature_contains_named_type(type_resolver, ty)
+ ctx.resolve_type(sig.return_type())
+ .signature_contains_named_type(ctx, ty)
},
- TypeKind::TemplateRef(_inner, ref template_args) => {
+ TypeKind::TemplateAlias(_, ref template_args) |
+ TypeKind::TemplateRef(_, ref template_args) => {
template_args.iter().any(|arg| {
- type_resolver.resolve_type(*arg)
- .signature_contains_named_type(type_resolver, ty)
+ ctx.resolve_type(*arg)
+ .signature_contains_named_type(ctx, ty)
})
}
TypeKind::Comp(ref ci)
- => ci.signature_contains_named_type(type_resolver, ty),
+ => ci.signature_contains_named_type(ctx, ty),
_ => false,
}
}
- pub fn canonical_type<'tr>(&'tr self, type_resolver: &'tr TypeResolver) -> &'tr Type {
+ /// Returns the canonical type of this type, that is, the "inner type".
+ ///
+ /// For example, for a `typedef`, the canonical type would be the
+ /// `typedef`ed type, for a template specialization, would be the template
+ /// its specializing, and so on.
+ pub fn canonical_type<'tr>(&'tr self, ctx: &'tr BindgenContext) -> &'tr Type {
match self.kind {
TypeKind::Named(..) |
TypeKind::Array(..) |
@@ -293,8 +310,9 @@ impl Type {
TypeKind::ResolvedTypeRef(inner) |
TypeKind::Alias(_, inner) |
+ TypeKind::TemplateAlias(inner, _) |
TypeKind::TemplateRef(inner, _)
- => type_resolver.resolve_type(inner).canonical_type(type_resolver),
+ => ctx.resolve_type(inner).canonical_type(ctx),
TypeKind::UnresolvedTypeRef(..)
=> unreachable!("Should have been resolved after parsing!"),
@@ -302,6 +320,7 @@ impl Type {
}
}
+/// The kind of float this type represents.
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum FloatKind {
Float,
@@ -310,9 +329,6 @@ pub enum FloatKind {
}
/// The different kinds of types that we can parse.
-///
-/// TODO: The name in the Alias and Named kinds is a bit unsound, should be in
-/// type.name?
#[derive(Debug)]
pub enum TypeKind {
/// The void type.
@@ -328,6 +344,9 @@ pub enum TypeKind {
Float(FloatKind),
/// A type alias, with a name, that points to another type.
Alias(String, ItemId),
+ /// A templated alias, pointing to an inner Alias type, with template
+ /// parameters.
+ TemplateAlias(ItemId, Vec<ItemId>),
/// An array of a type and a lenght.
Array(ItemId, usize),
/// A function type, with a given signature.
@@ -362,18 +381,25 @@ pub enum TypeKind {
}
impl Type {
- pub fn is_unsized(&self, type_resolver: &TypeResolver) -> bool {
+ /// Whether this type is unsized, that is, has no members. This is used to
+ /// derive whether we should generate a dummy `_address` field for structs,
+ /// to comply to the C and C++ layouts, that specify that every type needs
+ /// to be addressable.
+ pub fn is_unsized(&self, ctx: &BindgenContext) -> bool {
+ debug_assert!(ctx.in_codegen_phase(), "Not yet");
+
match self.kind {
TypeKind::Void => true,
- TypeKind::Comp(ref ci) => ci.is_unsized(type_resolver),
+ TypeKind::Comp(ref ci) => ci.is_unsized(ctx),
TypeKind::Array(inner, size) => {
size == 0 ||
- type_resolver.resolve_type(inner).is_unsized(type_resolver)
+ ctx.resolve_type(inner).is_unsized(ctx)
}
TypeKind::ResolvedTypeRef(inner) |
TypeKind::Alias(_, inner) |
+ TypeKind::TemplateAlias(inner, _) |
TypeKind::TemplateRef(inner, _)
- => type_resolver.resolve_type(inner).is_unsized(type_resolver),
+ => ctx.resolve_type(inner).is_unsized(ctx),
TypeKind::Named(..) |
TypeKind::Int(..) |
TypeKind::Float(..) |
@@ -389,6 +415,11 @@ impl Type {
}
}
+ /// This is another of the nasty methods. This one is the one that takes
+ /// care of the core logic of converting a clang type to a `Type`.
+ ///
+ /// It's sort of nasty and full of special-casing, but hopefully the
+ /// comments in every special case justify why they're there.
pub fn from_clang_ty(potential_id: ItemId,
ty: &clang::Type,
location: Option<Cursor>,
@@ -445,6 +476,57 @@ impl Type {
.expect("C'mon");
TypeKind::Comp(complex)
}
+ CXCursor_TypeAliasTemplateDecl => {
+ debug!("TypeAliasTemplateDecl");
+
+ // We need to manually unwind this one.
+ let mut inner = Err(ParseError::Continue);
+ let mut args = vec![];
+
+ location.visit(|cur, _| {
+ match cur.kind() {
+ CXCursor_TypeAliasDecl => {
+ debug_assert!(cur.cur_type().kind() == CXType_Typedef);
+ inner = Item::from_ty(&cur.cur_type(),
+ Some(*cur),
+ Some(potential_id),
+ ctx);
+ }
+ CXCursor_TemplateTypeParameter => {
+ // See the comment in src/ir/comp.rs
+ // about the same situation.
+ if cur.spelling().is_empty() {
+ return CXChildVisit_Continue;
+ }
+
+ let default_type =
+ Item::from_ty(&cur.cur_type(),
+ Some(*cur),
+ Some(potential_id),
+ ctx).ok();
+ let param =
+ Item::named_type(cur.spelling(),
+ default_type,
+ potential_id, ctx);
+ args.push(param);
+ }
+ _ => {}
+ }
+ CXChildVisit_Continue
+ });
+
+ if inner.is_err() {
+ error!("Failed to parse templated type alias {:?}", location);
+ return Err(ParseError::Continue);
+ }
+
+ if args.is_empty() {
+ error!("Failed to get any template parameter, maybe a specialization? {:?}", location);
+ return Err(ParseError::Continue);
+ }
+
+ TypeKind::TemplateAlias(inner.unwrap(), args)
+ }
CXCursor_TemplateRef => {
let referenced = location.referenced();
return Self::from_clang_ty(potential_id,
@@ -522,7 +604,7 @@ impl Type {
let signature = try!(FunctionSig::from_ty(ty, &location.unwrap_or(cursor), ctx));
TypeKind::Function(signature)
}
- CXType_Typedef => {
+ CXType_Typedef => {
let inner = cursor.typedef_type();
let inner =
Item::from_ty_or_ref(inner, location, parent_id, ctx);
@@ -538,10 +620,16 @@ impl Type {
.expect("Not a complex type?");
TypeKind::Comp(complex)
}
+ // FIXME: We stub vectors as arrays since in 99% of the cases the
+ // layout is going to be correct, and there's no way we can generate
+ // vector types properly in Rust for now.
+ //
+ // That being said, that should be fixed eventually.
+ CXType_Vector |
CXType_ConstantArray => {
let inner = Item::from_ty(&ty.elem_type(), location, parent_id, ctx)
.expect("Not able to resolve array element?");
- TypeKind::Array(inner, ty.array_size())
+ TypeKind::Array(inner, ty.num_elements())
}
#[cfg(not(feature="llvm_stable"))]
CXType_Elaborated => {
diff --git a/src/ir/var.rs b/src/ir/var.rs
index ac59973b..23529bcd 100644
--- a/src/ir/var.rs
+++ b/src/ir/var.rs
@@ -64,7 +64,7 @@ impl ClangSubItemParser for Var {
use clangll::*;
match cursor.kind() {
CXCursor_MacroDefinition => {
- let value = match parse_int_literal_tokens(&cursor, context.translation_unit(), 1) {
+ let value = match parse_int_literal_tokens(&cursor, context.translation_unit()) {
None => return Err(ParseError::Continue),
Some(v) => v,
};
@@ -81,7 +81,9 @@ impl ClangSubItemParser for Var {
}
context.note_parsed_macro(name.clone());
- let ty = if value.abs() > u32::max_value() as i64 {
+ let ty = if value < 0 {
+ Item::builtin_type(TypeKind::Int(IntKind::Int), true, context)
+ } else if value.abs() > u32::max_value() as i64 {
Item::builtin_type(TypeKind::Int(IntKind::ULongLong), true, context)
} else {
Item::builtin_type(TypeKind::Int(IntKind::UInt), true, context)
@@ -114,10 +116,7 @@ impl ClangSubItemParser for Var {
// Try to parse a literal token value
cursor.visit(|c, _| {
if c.kind() == CXCursor_IntegerLiteral {
- value =
- parse_int_literal_tokens(&c,
- context.translation_unit(),
- 0);
+ value = parse_int_literal_tokens(&c, context.translation_unit());
}
CXChildVisit_Continue
});
@@ -137,24 +136,36 @@ impl ClangSubItemParser for Var {
}
}
+/// Try and parse the immediately found tokens from an unit (if any) to integers
fn parse_int_literal_tokens(cursor: &clang::Cursor,
- unit: &clang::TranslationUnit,
- which: usize) -> Option<i64> {
- use clangll::CXToken_Literal;
+ unit: &clang::TranslationUnit) -> Option<i64> {
+ use clangll::{CXToken_Literal, CXToken_Punctuation};
+
+ let mut lit = String::new();
let tokens = match unit.tokens(cursor) {
None => return None,
Some(tokens) => tokens,
};
- if tokens.len() <= which || tokens[which].kind != CXToken_Literal {
- return None;
+ for token in tokens {
+ match token.kind {
+ CXToken_Punctuation if token.spelling == "-" => {
+ // If there's ever any punctuation, we only need to worry about
+ // unary minus '-' (for now)
+ lit.push_str(&token.spelling);
+ },
+ CXToken_Literal => {
+ lit.push_str(&token.spelling);
+ break
+ },
+ _ => (),
+ }
}
- let s = &tokens[which].spelling;
// TODO: try to preserve hex literals?
- if s.starts_with("0x") {
- i64::from_str_radix(&s[2..], 16).ok()
+ if lit.starts_with("0x") {
+ i64::from_str_radix(&lit[2..], 16).ok()
} else {
- s.parse().ok()
+ lit.parse().ok()
}
}
diff --git a/src/lib.rs b/src/lib.rs
index 33bd66e7..44cadb4d 100755
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -30,7 +30,6 @@ use std::borrow::Borrow;
use std::io::{self, Write};
use std::fs::OpenOptions;
use std::path::Path;
-use std::marker;
use std::collections::HashSet;
use syntax::ast;
@@ -44,102 +43,110 @@ use ir::item::{Item, ItemId};
use parse::{ClangItemParser, ParseError};
use regex_set::RegexSet;
-#[derive(Debug)]
-pub struct Builder<'a> {
+#[derive(Debug,Default)]
+pub struct Builder {
options: BindgenOptions,
- // TODO: Before the logger was here, do we still want the lifetime?
- phantom: marker::PhantomData<&'a ()>,
}
-pub fn builder<'a>() -> Builder<'a> {
+pub fn builder() -> Builder {
Default::default()
}
-impl<'a> Builder<'a> {
- pub fn header<T: Into<String>>(&mut self, header: T) -> &mut Self {
+impl Builder {
+ pub fn header<T: Into<String>>(self, header: T) -> Builder {
self.clang_arg(header)
}
- pub fn match_pat<T: Into<String>>(&mut self, arg: T) -> &mut Self {
+ pub fn match_pat<T: Into<String>>(mut self, arg: T) -> Builder {
self.options.match_pat.insert(arg.into());
self
}
- pub fn hide_type<T: Into<String>>(&mut self, arg: T) -> &mut Self {
+ pub fn hide_type<T: Into<String>>(mut self, arg: T) -> Builder {
self.options.hidden_types.insert(arg.into());
self
}
- pub fn opaque_type<T: Into<String>>(&mut self, arg: T) -> &mut Self {
+ pub fn opaque_type<T: Into<String>>(mut self, arg: T) -> Builder {
self.options.opaque_types.insert(arg.into());
self
}
- pub fn whitelisted_type<T: Borrow<str>>(&mut self, arg: &T) -> &mut Self {
- self.options.whitelisted_types.insert(arg);
+ pub fn whitelisted_type<T: Borrow<str>>(mut self, arg: T) -> Builder {
+ self.options.whitelisted_types.insert(&arg);
+ self
+ }
+
+ pub fn whitelisted_function<T: Borrow<str>>(mut self, arg: T) -> Builder {
+ self.options.whitelisted_functions.insert(&arg);
+ self
+ }
+
+ pub fn whitelisted_var<T: Borrow<str>>(mut self, arg: T) -> Builder {
+ self.options.whitelisted_vars.insert(&arg);
self
}
- pub fn raw_line<T: Into<String>>(&mut self, arg: T) -> &mut Self {
+ pub fn raw_line<T: Into<String>>(mut self, arg: T) -> Builder {
self.options.raw_lines.push(arg.into());
self
}
- pub fn clang_arg<T: Into<String>>(&mut self, arg: T) -> &mut Self {
+ pub fn clang_arg<T: Into<String>>(mut self, arg: T) -> Builder {
self.options.clang_args.push(arg.into());
self
}
- pub fn link<T: Into<String>>(&mut self, library: T) -> &mut Self {
+ pub fn link<T: Into<String>>(mut self, library: T) -> Builder {
self.options.links.push((library.into(), LinkType::Default));
self
}
- pub fn link_static<T: Into<String>>(&mut self, library: T) -> &mut Self {
+ pub fn link_static<T: Into<String>>(mut self, library: T) -> Builder {
self.options.links.push((library.into(), LinkType::Static));
self
}
- pub fn link_framework<T: Into<String>>(&mut self, library: T) -> &mut Self {
+ pub fn link_framework<T: Into<String>>(mut self, library: T) -> Builder {
self.options.links.push((library.into(), LinkType::Framework));
self
}
- pub fn dtor_attr<T: Into<String>>(&mut self, attr: T) -> &mut Self {
+ pub fn dtor_attr<T: Into<String>>(mut self, attr: T) -> Builder {
self.options.dtor_attrs.push(attr.into());
self
}
- pub fn forbid_unknown_types(&mut self) -> &mut Self {
+ pub fn forbid_unknown_types(mut self) -> Builder {
self.options.fail_on_unknown_type = true;
self
}
- pub fn emit_builtins(&mut self) -> &mut Self {
+ pub fn emit_builtins(mut self) -> Builder {
self.options.builtins = true;
self
}
- pub fn no_bitfield_methods(&mut self) -> &mut Self {
+ pub fn no_bitfield_methods(mut self) -> Builder {
self.options.gen_bitfield_methods = false;
self
}
- pub fn no_unstable_rust(&mut self) -> &mut Self {
+ pub fn no_unstable_rust(mut self) -> Builder {
self.options.unstable_rust = false;
self
}
- pub fn rust_enums(&mut self, value: bool) -> &mut Self {
+ pub fn rust_enums(mut self, value: bool) -> Builder {
self.options.rust_enums = value;
self
}
- pub fn rename_types(&mut self, value: bool) -> &mut Self {
+ pub fn rename_types(mut self, value: bool) -> Builder {
self.options.rename_types = value;
self
}
- pub fn disable_class_constants(&mut self) -> &mut Self {
+ pub fn disable_class_constants(mut self) -> Builder {
self.options.class_constants = false;
self
}
@@ -149,15 +156,6 @@ impl<'a> Builder<'a> {
}
}
-impl<'a> Default for Builder<'a> {
- fn default() -> Builder<'a> {
- Builder {
- options: Default::default(),
- phantom: marker::PhantomData,
- }
- }
-}
-
/// Deprecated - use a `Builder` instead
#[derive(Debug)]
pub struct BindgenOptions {
@@ -296,20 +294,6 @@ impl Bindings {
}
}
-#[test]
-fn builder_state() {
- let logger = DummyLogger;
- let mut build = builder();
- {
- build.header("example.h");
- build.link_static("m");
- build.log(&logger);
- }
- assert!(build.logger.is_some());
- assert!(build.options.clang_args.binary_search(&"example.h".to_owned()).is_ok());
- assert!(build.options.links.binary_search(&("m".to_owned(), LinkType::Static)).is_ok());
-}
-
/// Determines whether the given cursor is in any of the files matched by the
/// options.
fn filter_builtins(ctx: &BindgenContext, cursor: &clang::Cursor) -> bool {
diff --git a/tests/expectations/anon_enum.rs b/tests/expectations/anon_enum.rs
index b06585b1..17212c12 100644
--- a/tests/expectations/anon_enum.rs
+++ b/tests/expectations/anon_enum.rs
@@ -10,6 +10,8 @@ pub struct Test {
pub foo: ::std::os::raw::c_int,
pub bar: f32,
}
+pub const Test_T_NONE: Test__bindgen_ty_bindgen_id_6 =
+ Test__bindgen_ty_bindgen_id_6::T_NONE;
#[repr(u32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum Test__bindgen_ty_bindgen_id_6 { T_NONE = 0, }
diff --git a/tests/expectations/const_enum_unnamed.rs b/tests/expectations/const_enum_unnamed.rs
new file mode 100644
index 00000000..e16dc405
--- /dev/null
+++ b/tests/expectations/const_enum_unnamed.rs
@@ -0,0 +1,31 @@
+/* automatically generated by rust-bindgen */
+
+
+#![allow(non_snake_case)]
+
+
+pub const FOO_BAR: _bindgen_ty_bindgen_id_1 =
+ _bindgen_ty_bindgen_id_1::FOO_BAR;
+pub const FOO_BAZ: _bindgen_ty_bindgen_id_1 =
+ _bindgen_ty_bindgen_id_1::FOO_BAZ;
+#[repr(u32)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+pub enum _bindgen_ty_bindgen_id_1 { FOO_BAR = 0, FOO_BAZ = 1, }
+#[repr(C)]
+#[derive(Debug, Copy)]
+pub struct Foo {
+ pub _address: u8,
+}
+pub const Foo_FOO_BAR: Foo__bindgen_ty_bindgen_id_5 =
+ Foo__bindgen_ty_bindgen_id_5::FOO_BAR;
+#[repr(u32)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+pub enum Foo__bindgen_ty_bindgen_id_5 { FOO_BAR = 10, }
+#[test]
+fn bindgen_test_layout_Foo() {
+ assert_eq!(::std::mem::size_of::<Foo>() , 1usize);
+ assert_eq!(::std::mem::align_of::<Foo>() , 1usize);
+}
+impl Clone for Foo {
+ fn clone(&self) -> Self { *self }
+}
diff --git a/tests/expectations/jsval_layout_opaque.rs b/tests/expectations/jsval_layout_opaque.rs
index dd432232..dc0ecad5 100644
--- a/tests/expectations/jsval_layout_opaque.rs
+++ b/tests/expectations/jsval_layout_opaque.rs
@@ -24,6 +24,7 @@ impl <T> ::std::clone::Clone for __BindgenUnionField<T> {
fn clone(&self) -> Self { Self::new() }
}
impl <T> ::std::marker::Copy for __BindgenUnionField<T> { }
+pub const JSVAL_ALIGNMENT: ::std::os::raw::c_uint = 8;
pub const JSVAL_TAG_SHIFT: ::std::os::raw::c_uint = 47;
#[repr(u8)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
@@ -93,8 +94,8 @@ pub enum JSWhyMagic {
#[derive(Debug, Copy)]
pub struct jsval_layout {
pub asBits: __BindgenUnionField<u64>,
- pub debugView: __BindgenUnionField<jsval_layout__bindgen_ty_bindgen_id_89>,
- pub s: __BindgenUnionField<jsval_layout__bindgen_ty_bindgen_id_96>,
+ pub debugView: __BindgenUnionField<jsval_layout__bindgen_ty_bindgen_id_90>,
+ pub s: __BindgenUnionField<jsval_layout__bindgen_ty_bindgen_id_97>,
pub asDouble: __BindgenUnionField<f64>,
pub asPtr: __BindgenUnionField<*mut ::std::os::raw::c_void>,
pub asWord: __BindgenUnionField<usize>,
@@ -103,20 +104,20 @@ pub struct jsval_layout {
}
#[repr(C)]
#[derive(Debug, Copy)]
-pub struct jsval_layout__bindgen_ty_bindgen_id_89 {
+pub struct jsval_layout__bindgen_ty_bindgen_id_90 {
pub _bitfield_1: u64,
}
#[test]
-fn bindgen_test_layout_jsval_layout__bindgen_ty_bindgen_id_89() {
- assert_eq!(::std::mem::size_of::<jsval_layout__bindgen_ty_bindgen_id_89>()
+fn bindgen_test_layout_jsval_layout__bindgen_ty_bindgen_id_90() {
+ assert_eq!(::std::mem::size_of::<jsval_layout__bindgen_ty_bindgen_id_90>()
, 8usize);
- assert_eq!(::std::mem::align_of::<jsval_layout__bindgen_ty_bindgen_id_89>()
+ assert_eq!(::std::mem::align_of::<jsval_layout__bindgen_ty_bindgen_id_90>()
, 8usize);
}
-impl Clone for jsval_layout__bindgen_ty_bindgen_id_89 {
+impl Clone for jsval_layout__bindgen_ty_bindgen_id_90 {
fn clone(&self) -> Self { *self }
}
-impl jsval_layout__bindgen_ty_bindgen_id_89 {
+impl jsval_layout__bindgen_ty_bindgen_id_90 {
#[inline]
pub fn payload47(&self) -> u64 {
unsafe {
@@ -149,36 +150,36 @@ impl jsval_layout__bindgen_ty_bindgen_id_89 {
}
#[repr(C)]
#[derive(Debug, Copy)]
-pub struct jsval_layout__bindgen_ty_bindgen_id_96 {
- pub payload: jsval_layout__bindgen_ty_bindgen_id_96__bindgen_ty_bindgen_id_97,
+pub struct jsval_layout__bindgen_ty_bindgen_id_97 {
+ pub payload: jsval_layout__bindgen_ty_bindgen_id_97__bindgen_ty_bindgen_id_98,
}
#[repr(C)]
#[derive(Debug, Copy)]
-pub struct jsval_layout__bindgen_ty_bindgen_id_96__bindgen_ty_bindgen_id_97 {
+pub struct jsval_layout__bindgen_ty_bindgen_id_97__bindgen_ty_bindgen_id_98 {
pub i32: __BindgenUnionField<i32>,
pub u32: __BindgenUnionField<u32>,
pub why: __BindgenUnionField<JSWhyMagic>,
pub bindgen_union_field: u32,
}
#[test]
-fn bindgen_test_layout_jsval_layout__bindgen_ty_bindgen_id_96__bindgen_ty_bindgen_id_97() {
- assert_eq!(::std::mem::size_of::<jsval_layout__bindgen_ty_bindgen_id_96__bindgen_ty_bindgen_id_97>()
+fn bindgen_test_layout_jsval_layout__bindgen_ty_bindgen_id_97__bindgen_ty_bindgen_id_98() {
+ assert_eq!(::std::mem::size_of::<jsval_layout__bindgen_ty_bindgen_id_97__bindgen_ty_bindgen_id_98>()
, 4usize);
- assert_eq!(::std::mem::align_of::<jsval_layout__bindgen_ty_bindgen_id_96__bindgen_ty_bindgen_id_97>()
+ assert_eq!(::std::mem::align_of::<jsval_layout__bindgen_ty_bindgen_id_97__bindgen_ty_bindgen_id_98>()
, 4usize);
}
impl Clone for
- jsval_layout__bindgen_ty_bindgen_id_96__bindgen_ty_bindgen_id_97 {
+ jsval_layout__bindgen_ty_bindgen_id_97__bindgen_ty_bindgen_id_98 {
fn clone(&self) -> Self { *self }
}
#[test]
-fn bindgen_test_layout_jsval_layout__bindgen_ty_bindgen_id_96() {
- assert_eq!(::std::mem::size_of::<jsval_layout__bindgen_ty_bindgen_id_96>()
+fn bindgen_test_layout_jsval_layout__bindgen_ty_bindgen_id_97() {
+ assert_eq!(::std::mem::size_of::<jsval_layout__bindgen_ty_bindgen_id_97>()
, 4usize);
- assert_eq!(::std::mem::align_of::<jsval_layout__bindgen_ty_bindgen_id_96>()
+ assert_eq!(::std::mem::align_of::<jsval_layout__bindgen_ty_bindgen_id_97>()
, 4usize);
}
-impl Clone for jsval_layout__bindgen_ty_bindgen_id_96 {
+impl Clone for jsval_layout__bindgen_ty_bindgen_id_97 {
fn clone(&self) -> Self { *self }
}
impl Clone for jsval_layout {
diff --git a/tests/expectations/replaces_double.rs b/tests/expectations/replaces_double.rs
new file mode 100644
index 00000000..50dafd42
--- /dev/null
+++ b/tests/expectations/replaces_double.rs
@@ -0,0 +1,15 @@
+/* automatically generated by rust-bindgen */
+
+
+#![allow(non_snake_case)]
+
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct Rooted<T> {
+ pub ptr: Rooted_MaybeWrapped<T>,
+}
+/**
+ * <div rustbindgen replaces="Rooted_MaybeWrapped"></div>
+ */
+pub type Rooted_MaybeWrapped<T> = T;
diff --git a/tests/expectations/template_alias.rs b/tests/expectations/template_alias.rs
new file mode 100644
index 00000000..6457381f
--- /dev/null
+++ b/tests/expectations/template_alias.rs
@@ -0,0 +1,12 @@
+/* automatically generated by rust-bindgen */
+
+
+#![allow(non_snake_case)]
+
+
+pub type Wrapped<T> = T;
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct Rooted<T> {
+ pub ptr: Wrapped<T>,
+}
diff --git a/tests/expectations/template_alias_basic.rs b/tests/expectations/template_alias_basic.rs
new file mode 100644
index 00000000..656fff33
--- /dev/null
+++ b/tests/expectations/template_alias_basic.rs
@@ -0,0 +1,7 @@
+/* automatically generated by rust-bindgen */
+
+
+#![allow(non_snake_case)]
+
+
+pub type Wrapped<T> = T;
diff --git a/tests/expectations/template_alias_namespace.rs b/tests/expectations/template_alias_namespace.rs
new file mode 100644
index 00000000..475c2b05
--- /dev/null
+++ b/tests/expectations/template_alias_namespace.rs
@@ -0,0 +1,21 @@
+/* automatically generated by rust-bindgen */
+
+
+#![allow(non_snake_case)]
+
+
+pub mod root {
+ use root;
+ pub mod JS {
+ use root;
+ pub mod detail {
+ use root;
+ pub type Wrapped<T> = T;
+ }
+ #[repr(C)]
+ #[derive(Debug, Copy, Clone)]
+ pub struct Rooted<T> {
+ pub ptr: root::JS::detail::Wrapped<T>,
+ }
+ }
+}
diff --git a/tests/expectations/template_typedef_transitive_param.rs b/tests/expectations/template_typedef_transitive_param.rs
new file mode 100644
index 00000000..166ddc3c
--- /dev/null
+++ b/tests/expectations/template_typedef_transitive_param.rs
@@ -0,0 +1,18 @@
+/* automatically generated by rust-bindgen */
+
+
+#![allow(non_snake_case)]
+
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct Wrapper<T> {
+ pub _address: u8,
+ pub _phantom_0: ::std::marker::PhantomData<T>,
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct Wrapper_Wrapped<T> {
+ pub t: T,
+}
+pub type Wrapper_Type<T> = Wrapper_Wrapped<T>;
diff --git a/tests/expectations/vector.rs b/tests/expectations/vector.rs
new file mode 100644
index 00000000..b8ca5735
--- /dev/null
+++ b/tests/expectations/vector.rs
@@ -0,0 +1,19 @@
+/* automatically generated by rust-bindgen */
+
+
+#![allow(non_snake_case)]
+
+
+#[repr(C)]
+#[derive(Debug, Copy)]
+pub struct foo {
+ pub mMember: [::std::os::raw::c_longlong; 1usize],
+}
+#[test]
+fn bindgen_test_layout_foo() {
+ assert_eq!(::std::mem::size_of::<foo>() , 8usize);
+ assert_eq!(::std::mem::align_of::<foo>() , 8usize);
+}
+impl Clone for foo {
+ fn clone(&self) -> Self { *self }
+}
diff --git a/tests/expectations/whitelist_vars.rs b/tests/expectations/whitelist_vars.rs
new file mode 100644
index 00000000..f7af24b2
--- /dev/null
+++ b/tests/expectations/whitelist_vars.rs
@@ -0,0 +1,10 @@
+/* automatically generated by rust-bindgen */
+
+
+#![allow(non_snake_case)]
+
+
+pub const NONE: ::std::os::raw::c_uint = 0;
+pub const FOO: ::std::os::raw::c_uint = 5;
+pub const FOOB: ::std::os::raw::c_int = -2;
+pub const FOOBAR: ::std::os::raw::c_int = -10;
diff --git a/tests/headers/class_with_typedef.hpp b/tests/headers/class_with_typedef.hpp
index f36c7b5d..41a3cfd7 100644
--- a/tests/headers/class_with_typedef.hpp
+++ b/tests/headers/class_with_typedef.hpp
@@ -1,4 +1,5 @@
// bindgen-flags: --no-type-renaming
+// bindgen-features: llvm_stable
typedef int AnotherInt;
diff --git a/tests/headers/const_enum_unnamed.hpp b/tests/headers/const_enum_unnamed.hpp
new file mode 100644
index 00000000..eb139434
--- /dev/null
+++ b/tests/headers/const_enum_unnamed.hpp
@@ -0,0 +1,9 @@
+
+enum {
+ FOO_BAR,
+ FOO_BAZ,
+};
+
+class Foo {
+ enum { FOO_BAR = 10 };
+};
diff --git a/tests/headers/replaces_double.hpp b/tests/headers/replaces_double.hpp
new file mode 100644
index 00000000..1a78b0d9
--- /dev/null
+++ b/tests/headers/replaces_double.hpp
@@ -0,0 +1,20 @@
+// bindgen-flags: --blacklist-type Wrapper -- --std=c++11
+
+template<typename T>
+struct Wrapper {
+ struct Wrapped {
+ T t;
+ };
+ using Type = Wrapped;
+};
+
+template<typename T>
+class Rooted {
+ using MaybeWrapped = typename Wrapper<T>::Type;
+ MaybeWrapped ptr;
+
+ /**
+ * <div rustbindgen replaces="Rooted_MaybeWrapped"></div>
+ */
+ using MaybeWrapped_simple = T;
+};
diff --git a/tests/headers/template_alias.hpp b/tests/headers/template_alias.hpp
new file mode 100644
index 00000000..646d9f40
--- /dev/null
+++ b/tests/headers/template_alias.hpp
@@ -0,0 +1,13 @@
+// bindgen-flags: -- -std=c++14
+
+namespace JS {
+namespace detail {
+ template <typename T>
+ using Wrapped = T;
+}
+
+template <typename T>
+struct Rooted {
+ detail::Wrapped<T> ptr;
+};
+}
diff --git a/tests/headers/template_alias_basic.hpp b/tests/headers/template_alias_basic.hpp
new file mode 100644
index 00000000..964f6e27
--- /dev/null
+++ b/tests/headers/template_alias_basic.hpp
@@ -0,0 +1,4 @@
+// bindgen-flags: -- -std=c++11
+
+template<typename T>
+using Wrapped = T;
diff --git a/tests/headers/template_alias_namespace.hpp b/tests/headers/template_alias_namespace.hpp
new file mode 100644
index 00000000..bd637166
--- /dev/null
+++ b/tests/headers/template_alias_namespace.hpp
@@ -0,0 +1,13 @@
+// bindgen-flags: --enable-cxx-namespaces -- -std=c++14
+
+namespace JS {
+namespace detail {
+ template <typename T>
+ using Wrapped = T;
+}
+
+template <typename T>
+struct Rooted {
+ detail::Wrapped<T> ptr;
+};
+}
diff --git a/tests/headers/template_typedef_transitive_param.hpp b/tests/headers/template_typedef_transitive_param.hpp
new file mode 100644
index 00000000..2269ac36
--- /dev/null
+++ b/tests/headers/template_typedef_transitive_param.hpp
@@ -0,0 +1,7 @@
+template<typename T>
+struct Wrapper {
+ struct Wrapped {
+ T t;
+ };
+ using Type = Wrapped;
+};
diff --git a/tests/headers/vector.hpp b/tests/headers/vector.hpp
new file mode 100644
index 00000000..4707f77f
--- /dev/null
+++ b/tests/headers/vector.hpp
@@ -0,0 +1,3 @@
+struct foo {
+ __attribute__((__vector_size__(1 * sizeof(long long)))) long long mMember;
+};
diff --git a/tests/headers/whitelist_vars.h b/tests/headers/whitelist_vars.h
new file mode 100644
index 00000000..07fa2815
--- /dev/null
+++ b/tests/headers/whitelist_vars.h
@@ -0,0 +1,4 @@
+#define NONE 0
+#define FOO 5
+#define FOOB -2
+#define FOOBAR (-10)
diff --git a/tests/support.rs b/tests/support.rs
deleted file mode 100644
index 0ac92f5f..00000000
--- a/tests/support.rs
+++ /dev/null
@@ -1,108 +0,0 @@
-use bindgen;
-use bindgen::{Logger, BindgenOptions};
-
-use syntax::ast;
-use syntax::codemap;
-use syntax::codemap::DUMMY_SP;
-use syntax::parse;
-use syntax::parse::token;
-use syntax::print::pprust;
-use syntax::ptr::P;
-
-struct TestLogger;
-
-impl Logger for TestLogger {
- fn error(&self, msg: &str) {
- println!("err: {}", msg);
- }
-
- fn warn(&self, msg: &str) {
- println!("warn: {}", msg);
- }
-}
-
-pub fn generate_bindings(mut options: BindgenOptions,
- filename: &str)
- -> Result<Vec<P<ast::Item>>, ()> {
- if filename.ends_with("hpp") {
- options.clang_args.push("-std=c++11".to_string());
- options.clang_args.push("-Wno-narrowing".to_string());
- }
- options.clang_args.push(filename.to_string());
-
- let logger = TestLogger;
- Ok(try!(bindgen::Bindings::generate(&options, Some(&logger as &Logger), None)).into_ast())
-}
-
-pub fn assert_bind_eq(options: BindgenOptions,
- filename: &str,
- reference_items_str: &str) {
- let ext_cx = mk_dummy_ext_ctxt();
- let generated_items =
- generate_bindings(options, &format!("tests/{}", filename)[..]).unwrap();
-
- let mut parser = parse::new_parser_from_source_str(ext_cx.parse_sess(), ext_cx.cfg(), "".to_string(), reference_items_str.to_string());
- let mut reference_items = Vec::new();
- while let Ok(Some(item)) = parser.parse_item() {
- reference_items.push(item);
- }
-
- // The ast::Items themselves have insignificant (for our purposes)
- // differences that make them difficult to compare directly. So, compare
- // rendered versions, which is not beautiful, but should work.
- let reference_rendered = render_items(&reference_items);
- let generated_rendered = render_items(&generated_items);
-
- if reference_rendered != generated_rendered {
- println!("Generated bindings for {} do not match the reference bindings.", filename);
- println!("");
- println!("Generated:");
- println!("");
- println!("{}", generated_rendered);
- println!("");
- println!("Reference:");
- println!("");
- println!("{}", reference_rendered);
- panic!();
- }
-}
-
-fn render_items(items: &Vec<P<ast::Item>>) -> String {
- pprust::to_string(|s| {
- let module = ast::Mod {
- inner: DUMMY_SP,
- items: items.clone(),
- };
- s.print_mod(&module, &[])
- })
-}
-
-pub struct DummyExtCtxt {
- sess: parse::ParseSess,
-}
-
-impl DummyExtCtxt {
- pub fn cfg(&self) -> ast::CrateConfig {
- vec!()
- }
- pub fn parse_sess(&self) -> &parse::ParseSess {
- &self.sess
- }
- pub fn call_site(&self) -> codemap::Span {
- codemap::Span {
- lo: codemap::BytePos(0),
- hi: codemap::BytePos(0),
- expn_id: codemap::NO_EXPANSION
- }
- }
- pub fn ident_of(&self, s: &str) -> ast::Ident {
- token::str_to_ident(s)
- }
- pub fn name_of(&self, s: &str) -> ast::Name {
- token::intern(s)
- }
-}
-
-fn mk_dummy_ext_ctxt<'a>() -> DummyExtCtxt {
- DummyExtCtxt { sess: parse::ParseSess::new() }
-}
diff --git a/tests/test_builtins.rs b/tests/test_builtins.rs
deleted file mode 100644
index c1ddce72..00000000
--- a/tests/test_builtins.rs
+++ /dev/null
@@ -1,10 +0,0 @@
-use bindgen;
-
-#[test]
-fn test_builtin_va_list() {
- let bindings = bindgen::builder().header("tests/headers/builtin_va_list.h")
- .emit_builtins().generate().unwrap().to_string();
- println!("{}", bindings);
- assert!(bindings.contains("__builtin_va_list"));
-}
-
diff --git a/tests/test_cmath.rs b/tests/test_cmath.rs
deleted file mode 100644
index 564e40e3..00000000
--- a/tests/test_cmath.rs
+++ /dev/null
@@ -1,13 +0,0 @@
-// Unused until we can generate code for tests
-
-#[allow(dead_code, non_camel_case_types)]
-pub mod ffi { bindgen!("/usr/include/math.h", link = "m"); }
-
-#[test]
-fn floor_is_bound_and_callable() {
- unsafe {
- assert_eq!(ffi::floor( 2.7), 2.0);
- assert_eq!(ffi::floor(-2.7), -3.0);
- assert_eq!(ffi::floor(-0.0), 0.0);
- }
-}
diff --git a/tests/test_cxx.rs b/tests/test_cxx.rs
deleted file mode 100644
index ea4e516f..00000000
--- a/tests/test_cxx.rs
+++ /dev/null
@@ -1,34 +0,0 @@
-use bindgen;
-use bindgen::BindgenOptions;
-use support::assert_bind_eq;
-
-fn cxx_options() -> BindgenOptions {
- let mut options = BindgenOptions::default();
- options.rename_types = false;
-
- options
-}
-
-#[test]
-fn test_cxx_class() {
- assert_bind_eq(cxx_options(), "headers/class.hpp", "
- #[repr(C)]
- #[derive(Copy, Clone)]
- pub struct C {
- pub a: ::std::os::raw::c_int,
- }");
-}
-
-#[test]
-fn test_cxx_template() {
- assert_bind_eq(cxx_options(), "headers/template.hpp", "
- #[repr(C)]
- #[derive(Copy, Clone)]
- pub struct Foo<T> {
- pub m_member: T,
- }
- extern \"C\" {
- #[link_name = \"_Z3bar3FooIiE\"]
- pub fn bar(foo: Foo<::std::os::raw::c_int>);
- }");
-}
diff --git a/tests/test_decl.rs b/tests/test_decl.rs
deleted file mode 100644
index 6858d416..00000000
--- a/tests/test_decl.rs
+++ /dev/null
@@ -1,10 +0,0 @@
-use support::assert_bind_eq;
-
-#[test]
-fn ptr_to_array() {
- assert_bind_eq(Default::default(), "headers/decl_ptr_to_array.h", "
- extern \"C\" {
- pub static mut foo: [::std::os::raw::c_int; 1usize];
- }
- ");
-}
diff --git a/tests/test_enum.rs b/tests/test_enum.rs
deleted file mode 100644
index 0a8a2bb8..00000000
--- a/tests/test_enum.rs
+++ /dev/null
@@ -1,132 +0,0 @@
-use bindgen::BindgenOptions;
-use support::assert_bind_eq;
-
-fn default_without_rust_enums() -> BindgenOptions {
- BindgenOptions { rust_enums: false, .. Default::default() }
-}
-
-#[test]
-fn with_simple_enum() {
- assert_bind_eq(Default::default(), "headers/enum.h", "
- #[repr(u32)]
- #[derive(Copy, Clone, Debug)]
- pub enum Enum_Foo { Bar = 0, Qux = 1, }
- #[repr(i32)]
- #[derive(Copy, Clone, Debug)]
- pub enum Enum_Neg { MinusOne = -1, One = 1, }
- ");
- assert_bind_eq(default_without_rust_enums(), "headers/enum.h", "
- type Enum_Foo = u32;
- const Bar: Enum_Foo = 0;
- const Qux: Enum_Foo = 1;
- type Enum_Neg = i32;
- const MinusOne: Enum_Neg = -1;
- const One: Enum_Neg = 1;
- ");
-}
-
-#[test]
-fn with_packed_enums() {
- assert_bind_eq(Default::default(), "headers/enum_packed.h", "
- #[repr(u8)]
- #[derive(Copy, Clone, Debug)]
- pub enum Enum_Foo { Bar = 0, Qux = 1, }
- #[repr(i8)]
- #[derive(Copy, Clone, Debug)]
- pub enum Enum_Neg { MinusOne = -1, One = 1, }
- #[repr(u16)]
- #[derive(Copy, Clone, Debug)]
- pub enum Enum_Bigger { Much = 255, Larger = 256, }
- ");
- assert_bind_eq(default_without_rust_enums(), "headers/enum_packed.h", "
- type Enum_Foo = u8;
- const Bar: Enum_Foo = 0;
- const Qux: Enum_Foo = 1;
- type Enum_Neg = i8;
- const MinusOne: Enum_Neg = -1;
- const One: Enum_Neg = 1;
- type Enum_Bigger = u16;
- const Much: Enum_Bigger = 255;
- const Larger: Enum_Bigger = 256;
- ");
-}
-
-#[test]
-fn with_duplicate_enum_value() {
- assert_bind_eq(Default::default(), "headers/enum_dupe.h", "
- pub const Dupe: Enum_Foo = Enum_Foo::Bar;
- #[repr(u32)]
- #[derive(Copy, Clone, Debug)]
- pub enum Enum_Foo { Bar = 1, }
- ");
- assert_bind_eq(default_without_rust_enums(), "headers/enum_dupe.h", "
- type Enum_Foo = u32;
- const Bar: Enum_Foo = 1;
- const Dupe: Enum_Foo = 1;
- ");
-}
-
-#[test]
-fn with_explicitly_typed_cxx_enum() {
- assert_bind_eq(Default::default(), "headers/enum_explicit_type.hpp", "
- #[repr(u8)]
- #[derive(Copy, Clone, Debug)]
- pub enum Enum_Foo { Bar = 0, Qux = 1, }
-
- #[repr(i8)]
- #[derive(Copy, Clone, Debug)]
- pub enum Enum_Neg { MinusOne = -1, One = 1, }
-
- #[repr(u16)]
- #[derive(Copy, Clone, Debug)]
- pub enum Enum_Bigger { Much = 255, Larger = 256, }
-
- #[repr(i64)]
- #[derive(Copy, Clone, Debug)]
- pub enum Enum_MuchLong { MuchLow = -4294967296, }
-
- #[repr(u64)]
- #[derive(Copy, Clone, Debug)]
- pub enum Enum_MuchLongLong { MuchHigh = 4294967296, }
- ");
- assert_bind_eq(default_without_rust_enums(), "headers/enum_explicit_type.hpp", "
- type Enum_Foo = u8;
- const Bar: Enum_Foo = 0;
- const Qux: Enum_Foo = 1;
- type Enum_Neg = i8;
- const MinusOne: Enum_Neg = -1;
- const One: Enum_Neg = 1;
- type Enum_Bigger = u16;
- const Much: Enum_Bigger = 255;
- const Larger: Enum_Bigger = 256;
- type Enum_MuchLong = i64;
- const MuchLow: Enum_MuchLong = -4294967296;
- type Enum_MuchLongLong = u64;
- const MuchHigh: Enum_MuchLongLong = 4294967296;
- ");
-}
-
-#[test]
-fn with_overflowed_enum_value() {
- assert_bind_eq(Default::default(), "headers/overflowed_enum.hpp", "
- #[repr(u32)]
- #[derive(Copy, Clone, Debug)]
- pub enum Enum_Foo {
- BAP_ARM = 9698489,
- BAP_X86 = 11960045,
- BAP_X86_64 = 3128633167,
- }
- #[repr(u16)]
- #[derive(Copy, Clone, Debug)]
- pub enum Enum_Bar { One = 1, Big = 2, }
- ");
- assert_bind_eq(default_without_rust_enums(), "headers/overflowed_enum.hpp", "
- type Enum_Foo = u32;
- const BAP_ARM: Enum_Foo = 9698489;
- const BAP_X86: Enum_Foo = 11960045;
- const BAP_X86_64: Enum_Foo = 3128633167;
- type Enum_Bar = u16;
- const One: Enum_Bar = 1;
- const Big: Enum_Bar = 2;
- ");
-}
diff --git a/tests/test_extern.rs b/tests/test_extern.rs
deleted file mode 100644
index bbe823ff..00000000
--- a/tests/test_extern.rs
+++ /dev/null
@@ -1,8 +0,0 @@
-use support::assert_bind_eq;
-
-#[test]
-fn extern_c_in_hpp() {
- assert_bind_eq(Default::default(), "headers/extern.hpp", "
- pub type foo = unsafe extern \"C\" fn(bar: ::std::os::raw::c_int) -> ::std::os::raw::c_int;
- ");
-}
diff --git a/tests/test_func.rs b/tests/test_func.rs
deleted file mode 100644
index b210186f..00000000
--- a/tests/test_func.rs
+++ /dev/null
@@ -1,50 +0,0 @@
-use support::assert_bind_eq;
-
-#[test]
-fn func_ptr() {
- assert_bind_eq(Default::default(), "headers/func_ptr.h", "
- extern \"C\" {
- pub static mut foo: ::std::option::Option<
- unsafe extern \"C\" fn(x: ::std::os::raw::c_int,
- y: ::std::os::raw::c_int) -> ::std::os::raw::c_int>;
- }
- ");
-}
-
-#[test]
-fn func_ptr_in_struct() {
- assert_bind_eq(Default::default(), "headers/func_ptr_in_struct.h", "
- #[repr(C)]
- #[derive(Copy, Clone)]
- pub struct Struct_Foo {
- pub bar: ::std::option::Option<
- unsafe extern \"C\" fn(x: ::std::os::raw::c_int,
- y: ::std::os::raw::c_int) -> Enum_baz>,
- }
- ");
-}
-
-#[test]
-fn func_proto() {
- assert_bind_eq(Default::default(), "headers/func_proto.h", "
- pub type foo = unsafe extern \"C\" fn(bar: ::std::os::raw::c_int) -> ::std::os::raw::c_int;
- ");
-}
-
-#[test]
-fn with_func_ptr_arg() {
- assert_bind_eq(Default::default(), "headers/func_with_func_ptr_arg.h", "
- extern \"C\" {
- pub fn foo(bar: ::std::option::Option<unsafe extern \"C\" fn()>);
- }
- ");
-}
-
-#[test]
-fn with_array_arg() {
- assert_bind_eq(Default::default(), "headers/func_with_array_arg.h", "
- extern \"C\" {
- pub fn f(x: *mut ::std::os::raw::c_int);
- }
- ");
-}
diff --git a/tests/test_struct.rs b/tests/test_struct.rs
deleted file mode 100644
index 47e165f1..00000000
--- a/tests/test_struct.rs
+++ /dev/null
@@ -1,264 +0,0 @@
-use support::assert_bind_eq;
-
-#[test]
-fn with_anon_struct() {
- assert_bind_eq(Default::default(), "headers/struct_with_anon_struct.h", "
- #[repr(C)]
- #[derive(Copy, Clone)]
- pub struct Struct_foo {
- pub bar: Struct_struct_with_anon_struct_h_unnamed_1,
- }
-
- #[repr(C)]
- #[derive(Copy, Clone)]
- pub struct Struct_struct_with_anon_struct_h_unnamed_1 {
- pub a: ::std::os::raw::c_int,
- pub b: ::std::os::raw::c_int,
- }");
-}
-
-#[test]
-fn with_anon_struct_array() {
- assert_bind_eq(Default::default(), "headers/struct_with_anon_struct_array.h", "
- #[repr(C)]
- #[derive(Copy, Clone)]
- pub struct Struct_foo {
- pub bar: [Struct_struct_with_anon_struct_array_h_unnamed_1; 2usize],
- pub baz: [[[Struct_struct_with_anon_struct_array_h_unnamed_2; 4usize]; 3usize]; 2usize],
- }
-
- #[repr(C)]
- #[derive(Copy, Clone)]
- pub struct Struct_struct_with_anon_struct_array_h_unnamed_1 {
- pub a: ::std::os::raw::c_int,
- pub b: ::std::os::raw::c_int,
- }
-
- #[repr(C)]
- #[derive(Copy, Clone)]
- pub struct Struct_struct_with_anon_struct_array_h_unnamed_2 {
- pub a: ::std::os::raw::c_int,
- pub b: ::std::os::raw::c_int,
- }");
-}
-
-#[test]
-fn with_anon_struct_pointer() {
- assert_bind_eq(Default::default(), "headers/struct_with_anon_struct_pointer.h", "
- #[repr(C)]
- #[derive(Copy, Clone)]
- pub struct Struct_foo {
- pub bar: *mut Struct_struct_with_anon_struct_pointer_h_unnamed_1,
- }
-
- #[repr(C)]
- #[derive(Copy, Clone)]
- pub struct Struct_struct_with_anon_struct_pointer_h_unnamed_1 {
- pub a: ::std::os::raw::c_int,
- pub b: ::std::os::raw::c_int,
- }");
-}
-
-#[test]
-fn with_anon_union() {
- assert_bind_eq(Default::default(), "headers/struct_with_anon_union.h", "
- #[repr(C)]
- #[derive(Copy, Clone)]
- pub struct Struct_foo {
- pub bar: Union_unnamed1,
- }
- impl ::std::clone::Clone for Struct_foo {
- fn clone(&self) -> Self { *self }
- }
- impl ::std::default::Default for Struct_foo {
- fn default() -> Self { unsafe { ::std::mem::zeroed() } }
- }
- #[repr(C)]
- #[derive(Copy, Clone, Debug)]
- pub struct Union_unnamed1 {
- pub _bindgen_data_: [u32; 1usize],
- }
- impl Union_unnamed1 {
- pub unsafe fn a(&mut self) -> *mut ::std::os::raw::c_uint {
- let raw: *mut u8 = ::std::mem::transmute(&self._bindgen_data_);
- ::std::mem::transmute(raw.offset(0))
- }
- pub unsafe fn b(&mut self) -> *mut ::std::os::raw::c_ushort {
- let raw: *mut u8 = ::std::mem::transmute(&self._bindgen_data_);
- ::std::mem::transmute(raw.offset(0))
- }
- }
- ");
-}
-
-#[test]
-fn with_anon_unnamed_struct() {
- assert_bind_eq(Default::default(), "headers/struct_with_anon_unnamed_struct.h", "
- #[repr(C)]
- #[derive(Copy)]
- #[derive(Debug)]
- pub struct Struct_foo {
- pub _bindgen_data_1_: [u32; 2usize],
- }
- impl Struct_foo {
- pub unsafe fn a(&mut self) -> *mut ::std::os::raw::c_uint {
- let raw: *mut u8 = ::std::mem::transmute(&self._bindgen_data_1_);
- ::std::mem::transmute(raw.offset(0))
- }
- pub unsafe fn b(&mut self) -> *mut ::std::os::raw::c_uint {
- let raw: *mut u8 = ::std::mem::transmute(&self._bindgen_data_1_);
- ::std::mem::transmute(raw.offset(4))
- }
- }
- impl ::std::clone::Clone for Struct_foo {
- fn clone(&self) -> Self { *self }
- }
- impl ::std::default::Default for Struct_foo {
- fn default() -> Self { unsafe { ::std::mem::zeroed() } }
- }
- ");
-}
-
-#[test]
-fn with_anon_unnamed_union() {
- assert_bind_eq(Default::default(), "headers/struct_with_anon_unnamed_union.h", "
- #[repr(C)]
- #[derive(Copy, Clone)]
- pub struct Struct_foo {
- pub _bindgen_data_1_: [u32; 1usize],
- }
- impl Struct_foo {
- pub unsafe fn a(&mut self) -> *mut ::std::os::raw::c_uint {
- let raw: *mut u8 = ::std::mem::transmute(&self._bindgen_data_1_);
- ::std::mem::transmute(raw.offset(0))
- }
- pub unsafe fn b(&mut self) -> *mut ::std::os::raw::c_ushort {
- let raw: *mut u8 = ::std::mem::transmute(&self._bindgen_data_1_);
- ::std::mem::transmute(raw.offset(0))
- }
- }
- impl ::std::clone::Clone for Struct_foo {
- fn clone(&self) -> Self { *self }
- }
- impl ::std::default::Default for Struct_foo {
- fn default() -> Self { unsafe { ::std::mem::zeroed() } }
- }
- ");
-}
-
-#[test]
-fn with_nesting() {
- assert_bind_eq(Default::default(), "headers/struct_with_nesting.h", "
- #[repr(C)]
- #[derive(Copy)]
- #[derive(Debug)]
- pub struct Struct_foo {
- pub a: ::std::os::raw::c_uint,
- pub _bindgen_data_1_: [u32; 1usize],
- }
- impl Struct_foo {
- pub unsafe fn b(&mut self) -> *mut ::std::os::raw::c_uint {
- let raw: *mut u8 = ::std::mem::transmute(&self._bindgen_data_1_);
- ::std::mem::transmute(raw.offset(0))
- }
- pub unsafe fn c1(&mut self) -> *mut ::std::os::raw::c_ushort {
- let raw: *mut u8 = ::std::mem::transmute(&self._bindgen_data_1_);
- ::std::mem::transmute(raw.offset(0))
- }
- pub unsafe fn c2(&mut self) -> *mut ::std::os::raw::c_ushort {
- let raw: *mut u8 = ::std::mem::transmute(&self._bindgen_data_1_);
- ::std::mem::transmute(raw.offset(2))
- }
- pub unsafe fn d1(&mut self) -> *mut ::std::os::raw::c_uchar {
- let raw: *mut u8 = ::std::mem::transmute(&self._bindgen_data_1_);
- ::std::mem::transmute(raw.offset(0))
- }
- pub unsafe fn d2(&mut self) -> *mut ::std::os::raw::c_uchar {
- let raw: *mut u8 = ::std::mem::transmute(&self._bindgen_data_1_);
- ::std::mem::transmute(raw.offset(1))
- }
- pub unsafe fn d3(&mut self) -> *mut ::std::os::raw::c_uchar {
- let raw: *mut u8 = ::std::mem::transmute(&self._bindgen_data_1_);
- ::std::mem::transmute(raw.offset(2))
- }
- pub unsafe fn d4(&mut self) -> *mut ::std::os::raw::c_uchar {
- let raw: *mut u8 = ::std::mem::transmute(&self._bindgen_data_1_);
- ::std::mem::transmute(raw.offset(3))
- }
- }
- impl ::std::clone::Clone for Struct_foo {
- fn clone(&self) -> Self { *self }
- }
- impl ::std::default::Default for Struct_foo {
- fn default() -> Self { unsafe { ::std::mem::zeroed() } }
- }
- ");
-}
-
-#[test]
-fn containing_fwd_decl_struct() {
- assert_bind_eq(Default::default(), "headers/struct_containing_forward_declared_struct.h", "
- #[repr(C)]
- #[derive(Copy, Clone)]
- pub struct Struct_a {
- pub val_a: *mut Struct_b,
- }
-
- #[repr(C)]
- #[derive(Copy, Clone)]
- pub struct Struct_b {
- pub val_b: ::std::os::raw::c_int,
- }
- ");
-}
-
-#[test]
-fn with_bitfields() {
- assert_bind_eq(Default::default(), "headers/struct_with_bitfields.h", "
- #[repr(C)]
- #[derive(Copy)]
- #[derive(Debug)]
- pub struct Struct_bitfield {
- pub _bindgen_bitfield_1_: ::std::os::raw::c_ushort,
- pub e: ::std::os::raw::c_int,
- pub _bindgen_bitfield_2_: ::std::os::raw::c_uint,
- pub _bindgen_bitfield_3_: ::std::os::raw::c_uint,
- }
-
- impl ::std::clone::Clone for Struct_bitfield {
- fn clone(&self) -> Self { *self }
- }
-
- impl ::std::default::Default for Struct_bitfield {
- fn default() -> Self { unsafe { ::std::mem::zeroed() } }
- }
- ");
-}
-
-#[test]
-fn with_fwd_decl_struct() {
- assert_bind_eq(Default::default(), "headers/forward_declared_struct.h", "
- #[repr(C)]
- #[derive(Copy, Clone)]
- pub struct Struct_a {
- pub b: ::std::os::raw::c_int,
- }
-
- #[repr(C)]
- #[derive(Copy, Clone)]
- pub struct Struct_c {
- pub d: ::std::os::raw::c_int,
- }");
-}
-
-
-#[test]
-fn packed_struct() {
- assert_bind_eq(Default::default(), "headers/struct_with_packing.h", "
- #[repr(C, packed)]
- #[derive(Copy, Clone)]
- pub struct Struct_a {
- pub b: ::std::os::raw::c_char,
- pub c: ::std::os::raw::c_short,
- }");
-}
diff --git a/tests/test_union.rs b/tests/test_union.rs
deleted file mode 100644
index f35e325a..00000000
--- a/tests/test_union.rs
+++ /dev/null
@@ -1,271 +0,0 @@
-use support::assert_bind_eq;
-
-#[test]
-fn with_anon_struct() {
- assert_bind_eq(Default::default(), "headers/union_with_anon_struct.h", "
- #[repr(C)]
- #[derive(Copy)]
- #[derive(Debug)]
- pub struct Union_foo {
- pub _bindgen_data_: [u32; 2usize],
- }
- impl Union_foo {
- pub unsafe fn bar(&mut self) -> *mut Struct_Unnamed1 {
- let raw: *mut u8 = ::std::mem::transmute(&self._bindgen_data_);
- ::std::mem::transmute(raw.offset(0))
- }
- }
- impl ::std::clone::Clone for Union_foo {
- fn clone(&self) -> Self { *self }
- }
- impl ::std::default::Default for Union_foo {
- fn default() -> Self { unsafe { ::std::mem::zeroed() } }
- }
- #[repr(C)]
- #[derive(Copy)]
- #[derive(Debug)]
- pub struct Struct_Unnamed1 {
- pub a: ::std::os::raw::c_uint,
- pub b: ::std::os::raw::c_uint,
- }
- impl ::std::clone::Clone for Struct_Unnamed1 {
- fn clone(&self) -> Self { *self }
- }
- impl ::std::default::Default for Struct_Unnamed1 {
- fn default() -> Self { unsafe { ::std::mem::zeroed() } }
- }
- ");
-}
-
-#[test]
-fn with_anon_struct_bitfield() {
- assert_bind_eq(Default::default(), "headers/union_with_anon_struct_bitfield.h", "
- #[repr(C)]
- #[derive(Copy)]
- #[derive(Debug)]
- pub struct Union_foo {
- pub _bindgen_data_: [u32; 1usize],
- }
-
- impl Union_foo {
- pub unsafe fn a(&mut self) -> *mut ::std::os::raw::c_int {
- let raw: *mut u8 = ::std::mem::transmute(&self._bindgen_data_);
- ::std::mem::transmute(raw.offset(0))
- }
- }
-
- impl ::std::clone::Clone for Union_foo {
- fn clone(&self) -> Self { *self }
- }
-
- impl ::std::default::Default for Union_foo {
- fn default() -> Self { unsafe { ::std::mem::zeroed() } }
- }
- ");
-}
-
-#[test]
-fn with_anon_union() {
- assert_bind_eq(Default::default(), "headers/union_with_anon_union.h", "
- #[repr(C)]
- #[derive(Copy)]
- #[derive(Debug)]
- pub struct Union_foo {
- pub _bindgen_data_: [u32; 1usize],
- }
- impl Union_foo {
- pub unsafe fn bar(&mut self) -> *mut Union_Unnamed1 {
- let raw: *mut u8 = ::std::mem::transmute(&self._bindgen_data_);
- ::std::mem::transmute(raw.offset(0))
- }
- }
- impl ::std::clone::Clone for Union_foo {
- fn clone(&self) -> Self { *self }
- }
- impl ::std::default::Default for Union_foo {
- fn default() -> Self { unsafe { ::std::mem::zeroed() } }
- }
- #[repr(C)]
- #[derive(Copy)]
- #[derive(Debug)]
- pub struct Union_Unnamed1 {
- pub _bindgen_data_: [u32; 1usize],
- }
- impl Union_Unnamed1 {
- pub unsafe fn a(&mut self) -> *mut ::std::os::raw::c_uint {
- let raw: *mut u8 = ::std::mem::transmute(&self._bindgen_data_);
- ::std::mem::transmute(raw.offset(0))
- }
- pub unsafe fn b(&mut self) -> *mut ::std::os::raw::c_ushort {
- let raw: *mut u8 = ::std::mem::transmute(&self._bindgen_data_);
- ::std::mem::transmute(raw.offset(0))
- }
- }
- impl ::std::clone::Clone for Union_Unnamed1 {
- fn clone(&self) -> Self { *self }
- }
- impl ::std::default::Default for Union_Unnamed1 {
- fn default() -> Self { unsafe { ::std::mem::zeroed() } }
- }
- ");
-}
-
-#[test]
-fn with_anon_unnamed_struct() {
- assert_bind_eq(Default::default(), "headers/union_with_anon_unnamed_struct.h", "
- #[repr(C)]
- #[derive(Copy)]
- #[derive(Debug)]
- pub struct Union_pixel {
- pub _bindgen_data_: [u32; 1usize],
- }
- impl Union_pixel {
- pub unsafe fn rgba(&mut self) -> *mut ::std::os::raw::c_uint {
- let raw: *mut u8 = ::std::mem::transmute(&self._bindgen_data_);
- ::std::mem::transmute(raw.offset(0))
- }
- pub unsafe fn r(&mut self) -> *mut ::std::os::raw::c_uchar {
- let raw: *mut u8 = ::std::mem::transmute(&self._bindgen_data_);
- ::std::mem::transmute(raw.offset(0))
- }
- pub unsafe fn g(&mut self) -> *mut ::std::os::raw::c_uchar {
- let raw: *mut u8 = ::std::mem::transmute(&self._bindgen_data_);
- ::std::mem::transmute(raw.offset(1))
- }
- pub unsafe fn b(&mut self) -> *mut ::std::os::raw::c_uchar {
- let raw: *mut u8 = ::std::mem::transmute(&self._bindgen_data_);
- ::std::mem::transmute(raw.offset(2))
- }
- pub unsafe fn a(&mut self) -> *mut ::std::os::raw::c_uchar {
- let raw: *mut u8 = ::std::mem::transmute(&self._bindgen_data_);
- ::std::mem::transmute(raw.offset(3))
- }
- }
- impl ::std::clone::Clone for Union_pixel {
- fn clone(&self) -> Self { *self }
- }
- impl ::std::default::Default for Union_pixel {
- fn default() -> Self { unsafe { ::std::mem::zeroed() } }
- }
- ");
-}
-
-#[test]
-fn with_anon_unnamed_union() {
- assert_bind_eq(Default::default(), "headers/union_with_anon_unnamed_union.h", "
- #[repr(C)]
- #[derive(Copy)]
- #[derive(Debug)]
- pub struct Union_foo {
- pub _bindgen_data_: [u32; 1usize],
- }
- impl Union_foo {
- pub unsafe fn a(&mut self) -> *mut ::std::os::raw::c_uint {
- let raw: *mut u8 = ::std::mem::transmute(&self._bindgen_data_);
- ::std::mem::transmute(raw.offset(0))
- }
- pub unsafe fn b(&mut self) -> *mut ::std::os::raw::c_ushort {
- let raw: *mut u8 = ::std::mem::transmute(&self._bindgen_data_);
- ::std::mem::transmute(raw.offset(0))
- }
- pub unsafe fn c(&mut self) -> *mut ::std::os::raw::c_uchar {
- let raw: *mut u8 = ::std::mem::transmute(&self._bindgen_data_);
- ::std::mem::transmute(raw.offset(0))
- }
- }
- impl ::std::clone::Clone for Union_foo {
- fn clone(&self) -> Self { *self }
- }
- impl ::std::default::Default for Union_foo {
- fn default() -> Self { unsafe { ::std::mem::zeroed() } }
- }
- ");
-}
-
-#[test]
-fn with_nesting() {
- assert_bind_eq(Default::default(), "headers/union_with_nesting.h", "
- #[repr(C)]
- #[derive(Copy)]
- #[derive(Debug)]
- pub struct Union_foo {
- pub _bindgen_data_: [u32; 1usize],
- }
- impl Union_foo {
- pub unsafe fn a(&mut self) -> *mut ::std::os::raw::c_uint {
- let raw: *mut u8 = ::std::mem::transmute(&self._bindgen_data_);
- ::std::mem::transmute(raw.offset(0))
- }
- pub unsafe fn b1(&mut self) -> *mut ::std::os::raw::c_ushort {
- let raw: *mut u8 = ::std::mem::transmute(&self._bindgen_data_);
- ::std::mem::transmute(raw.offset(0))
- }
- pub unsafe fn b2(&mut self) -> *mut ::std::os::raw::c_ushort {
- let raw: *mut u8 = ::std::mem::transmute(&self._bindgen_data_);
- ::std::mem::transmute(raw.offset(0))
- }
- pub unsafe fn c1(&mut self) -> *mut ::std::os::raw::c_ushort {
- let raw: *mut u8 = ::std::mem::transmute(&self._bindgen_data_);
- ::std::mem::transmute(raw.offset(2))
- }
- pub unsafe fn c2(&mut self) -> *mut ::std::os::raw::c_ushort {
- let raw: *mut u8 = ::std::mem::transmute(&self._bindgen_data_);
- ::std::mem::transmute(raw.offset(2))
- }
- }
- impl ::std::clone::Clone for Union_foo {
- fn clone(&self) -> Self { *self }
- }
- impl ::std::default::Default for Union_foo {
- fn default() -> Self { unsafe { ::std::mem::zeroed() } }
- }
- ");
-}
-
-#[test]
-fn with_derive_debug() {
- assert_bind_eq(Default::default(), "headers/union_with_big_member.h", "
- #[repr(C)]
- #[derive(Copy)]
- pub struct Union_WithBigArray {
- pub _bindgen_data_: [u32; 33usize],
- }
- impl Union_WithBigArray {
- pub unsafe fn a(&mut self) -> *mut ::std::os::raw::c_int {
- let raw: *mut u8 = ::std::mem::transmute(&self._bindgen_data_);
- ::std::mem::transmute(raw.offset(0))
- }
- pub unsafe fn b(&mut self) -> *mut [::std::os::raw::c_int; 33usize] {
- let raw: *mut u8 = ::std::mem::transmute(&self._bindgen_data_);
- ::std::mem::transmute(raw.offset(0))
- }
- }
- impl ::std::clone::Clone for Union_WithBigArray {
- fn clone(&self) -> Self { *self }
- }
- impl ::std::default::Default for Union_WithBigArray {
- fn default() -> Self { unsafe { ::std::mem::zeroed() } }
- }
- #[repr(C)]
- #[derive(Copy)]
- pub struct Union_WithBigMember {
- pub _bindgen_data_: [u32; 33usize],
- }
- impl Union_WithBigMember {
- pub unsafe fn a(&mut self) -> *mut ::std::os::raw::c_int {
- let raw: *mut u8 = ::std::mem::transmute(&self._bindgen_data_);
- ::std::mem::transmute(raw.offset(0))
- }
- pub unsafe fn b(&mut self) -> *mut Union_WithBigArray {
- let raw: *mut u8 = ::std::mem::transmute(&self._bindgen_data_);
- ::std::mem::transmute(raw.offset(0))
- }
- }
- impl ::std::clone::Clone for Union_WithBigMember {
- fn clone(&self) -> Self { *self }
- }
- impl ::std::default::Default for Union_WithBigMember {
- fn default() -> Self { unsafe { ::std::mem::zeroed() } }
- }
- ");
-}
diff --git a/tests/tests.rs b/tests/tests.rs
index 2e7072fe..66e70859 100644
--- a/tests/tests.rs
+++ b/tests/tests.rs
@@ -1,17 +1,134 @@
-#![allow(dead_code)]
-
+// We add this `extern crate` here to ensure that bindgen is up-to-date and
+// rebuilt, even though we aren't using any of its types or functions here, only
+// indirectly calling the executable.
+#[allow(dead_code)]
extern crate bindgen;
-extern crate syntex_syntax as syntax;
-
-mod support;
-
-// Unused until we can generate code for tests
-//mod test_cmath;
-mod test_cxx;
-mod test_enum;
-mod test_decl;
-mod test_extern;
-mod test_func;
-mod test_struct;
-mod test_union;
-mod test_builtins;
+
+use std::env;
+use std::fs;
+use std::io::Read;
+use std::path::{Path, PathBuf};
+use std::process;
+
+fn spawn_run_bindgen<P, Q, R>(run_bindgen: P, bindgen: Q, header: R) -> process::Child
+ where P: AsRef<Path>,
+ Q: AsRef<Path>,
+ R: AsRef<Path>
+{
+ let run_bindgen = run_bindgen.as_ref();
+ let bindgen = bindgen.as_ref();
+ let header = header.as_ref();
+
+ // Convert from "tests/headers/foo.hpp" to "tests/expectations/foo.rs" by
+ // saving the filename, popping off "headers/foo.hpp", pushing
+ // "expectations", pushing the saved filename, and finally modifying the
+ // extension.
+
+ let mut expected = PathBuf::from(header);
+ let file_name = expected.file_name()
+ .expect("Should have filename")
+ .to_os_string();
+ expected.pop();
+ expected.pop();
+ expected.push("expectations");
+ expected.push(file_name);
+ expected.set_extension("rs");
+
+ let mut cmd = process::Command::new(run_bindgen);
+ cmd.stdout(process::Stdio::piped())
+ .stderr(process::Stdio::piped())
+ .arg(bindgen)
+ .arg(header)
+ .arg(expected);
+
+ if cfg!(feature = "llvm_stable") {
+ cmd.arg("--feature")
+ .arg("llvm_stable");
+ }
+
+ cmd.spawn()
+ .expect("Should be able to spawn run-bindgen.py child process")
+}
+
+#[test]
+fn run_bindgen_tests() {
+ let crate_root = env::var("CARGO_MANIFEST_DIR")
+ .expect("should have CARGO_MANIFEST_DIR environment variable");
+
+ let mut run_bindgen = PathBuf::from(&crate_root);
+ run_bindgen.push("tests");
+ run_bindgen.push("tools");
+ run_bindgen.push("run-bindgen.py");
+
+ let mut bindgen = PathBuf::from(&crate_root);
+ bindgen.push("target");
+ bindgen.push("debug");
+ bindgen.push("bindgen");
+ if !bindgen.is_file() {
+ panic!("{} is not a file! Build bindgen before running tests.",
+ bindgen.display());
+ }
+
+ let mut headers_dir = PathBuf::from(&crate_root);
+ headers_dir.push("tests");
+ headers_dir.push("headers");
+
+ let entries = fs::read_dir(&headers_dir)
+ .expect("Should read directory")
+ .map(|result| result.expect("Should read directory entry"));
+
+ let tests = entries.filter(|entry| {
+ match entry.path().extension().map(|s| s.to_str()) {
+ Some(Some("h")) |
+ Some(Some("hpp")) => true,
+ _ => false,
+ }
+ });
+
+ // First spawn all child processes and collect them, then wait on each
+ // one. This runs the tests in parallel rather than serially.
+
+ let children: Vec<_> = tests.map(|entry| {
+ let child = spawn_run_bindgen(run_bindgen.clone(), bindgen.clone(), entry.path());
+ (entry.path(), child)
+ })
+ .collect();
+
+ let failures: Vec<_> = children.into_iter()
+ .filter_map(|(path, mut child)| {
+ let passed = child.wait()
+ .expect("Should wait on child process")
+ .success();
+
+ if passed { None } else { Some((path, child)) }
+ })
+ .collect();
+
+ let num_failures = failures.len();
+
+ for (path, child) in failures {
+ println!("FAIL: {}", path.display());
+
+ let mut buf = String::new();
+
+ child.stdout
+ .expect("should have stdout piped")
+ .read_to_string(&mut buf)
+ .expect("should read child's stdout");
+ for line in buf.lines() {
+ println!("child stdout> {}", line);
+ }
+
+ child.stderr
+ .expect("should have stderr piped")
+ .read_to_string(&mut buf)
+ .expect("should read child's stderr");
+ for line in buf.lines() {
+ println!("child stderr> {}", line);
+ }
+ }
+
+ if num_failures > 0 {
+ panic!("{} test failures!", num_failures);
+ }
+}
diff --git a/tests/tools/run-bindgen.py b/tests/tools/run-bindgen.py
index 0d8ed580..ddd5d11b 100755
--- a/tests/tools/run-bindgen.py
+++ b/tests/tools/run-bindgen.py
@@ -1,50 +1,190 @@
#!/usr/bin/env python
+from __future__ import print_function
+
+import argparse
+import difflib
import os
import sys
import subprocess
import tempfile
-BINDGEN_FLAGS_PREFIX = "// bindgen-flags: ";
-CLANG_FLAGS_SEPARATOR = "-- "
+BINDGEN_FLAGS_PREFIX = "// bindgen-flags: "
+BINDGEN_FEATURES_PREFIX = "// bindgen-features: "
+
COMMON_PRELUDE = """
#![allow(non_snake_case)]
"""
-if len(sys.argv) != 4:
- print("Usage: {} [bindgen-path] [c-path] [rust-path]\n".format(sys.argv[0]))
- sys.exit(1)
+DESCRIPTION = """
+Run bindgen on a test header and check the generated bindings against expected
+output.
+"""
+
+def make_parser():
+ """Make the commandline parser"""
+ parser = argparse.ArgumentParser(description=DESCRIPTION)
+ parser.add_argument("bindgen",
+ metavar="BINDGEN",
+ help="The path to the bindgen executable")
+ parser.add_argument("header",
+ metavar="HEADER",
+ help="The path to the input header")
+ parser.add_argument("rust_bindings",
+ metavar="RUST_BINDINGS",
+ help="The path to the generated rust output. If a file \
+ at this path already exists, the newly generated \
+ bindings will be checked against those extant \
+ expected bindings.")
+ parser.add_argument("--feature",
+ dest="features",
+ action="append",
+ nargs=1,
+ help="Run tests that depend on bindgen being built with \
+ the given feature.")
+ return parser
+
+def usage_and_exit(*args):
+ """Print the program usage and exit. If args are given, print them first"""
+ if len(args) > 0:
+ print(*args)
+ make_parser().print_help()
+ sys.exit(1)
+
+def parse_args():
+ """Get, parse, and validate commandline arguments."""
+ parser = make_parser()
+ args = parser.parse_args()
+
+ if args.features is None:
+ args.features = []
+
+ if not os.path.isfile(args.bindgen):
+ usage_and_exit("error: bindgen is not a file:", args.bindgen)
+
+ if not os.path.isfile(args.header):
+ usage_and_exit("error: header is not a file:", args.header)
+
+ return args
+
+def make_bindgen_env():
+ """Build the environment to run bindgen in."""
+ env = os.environ.copy()
+
+ # El Capitan likes to unset dyld variables
+ # https://forums.developer.apple.com/thread/9233
+ if "DYLD_LIBRARY_PATH" not in env and "LIBCLANG_PATH" in env:
+ env["DYLD_LIBRARY_PATH"] = env["LIBCLANG_PATH"]
+
+ return env
+
+def get_bindgen_flags_and_features(header_path):
+ """
+ Return the bindgen flags and features required for this header as a tuple
+ (flags, features).
+ """
+ found_flags = False
+ found_features = False
+
+ features = []
+ flags = ["--no-unstable-rust"]
+ for line in COMMON_PRELUDE.split("\n"):
+ flags.append("--raw-line")
+ flags.append(line)
+
+ with open(header_path) as f:
+ for line in f:
+ if not found_flags and line.startswith(BINDGEN_FLAGS_PREFIX):
+ flags.extend(line.strip().split(BINDGEN_FLAGS_PREFIX)[1].split(" "))
+ found_flags = True
+
+ if not found_features and line.startswith(BINDGEN_FEATURES_PREFIX):
+ features.extend(line.strip().split(BINDGEN_FEATURES_PREFIX)[1].split(" "))
+ found_features = True
+
+ if found_flags and found_features:
+ break
+
+ return (flags, features)
+
+def get_expected_bindings(rust_bindings_path):
+ """
+ Get the expected, generated rust bindings output, or None if there is no
+ expected output yet.
+ """
+ expected_bindings = None
+ if os.path.isfile(rust_bindings_path):
+ with open(rust_bindings_path) as f:
+ expected_bindings = f.read()
+ return expected_bindings
+
+def get_actual_bindings(rust_bindings_path):
+ """Get the actual generated rust bindings output."""
+ assert os.path.isfile(rust_bindings_path)
+ with open(rust_bindings_path) as f:
+ return f.read()
+
+def run_cmd(command, **kwargs):
+ """Run the given command, passing through **kwargs to subprocess.check_call"""
+ print("run-bindgen.py: running", command)
+ subprocess.check_call(command, **kwargs)
-[_, bindgen_path, c_path, rust_path] = sys.argv
+def generate_bindings(bindgen, flags, header, output):
+ """Generate the rust bindings."""
+ command = [bindgen, "-o", output]
+ command.extend(flags)
+ command.append(header)
+ run_cmd(command, cwd=os.getcwd(), env=make_bindgen_env())
-flags = ["--no-unstable-rust"]
+def test_generated_bindings(bindings):
+ """Run the generated bindings's #[test]s."""
+ name = None
+ # Do not delete the temp file, because we need to end the with block before
+ # we can run the tests.
+ with tempfile.NamedTemporaryFile(delete=False) as tests:
+ name = tests.name
+ run_cmd(["rustc", "--test", bindings, "-o", name])
+ run_cmd([name])
-with open(sys.argv[2]) as f:
- for line in f:
- if line.startswith(BINDGEN_FLAGS_PREFIX):
- flags.extend(line.strip().split(BINDGEN_FLAGS_PREFIX)[1].split(" "))
- break
+def check_actual_vs_expected(expected_bindings, rust_bindings_path):
+ """
+ Check the actual generated rust bindings versus our expected generated rust
+ bindings. If they don't match up, print a diff between them and exit with a
+ failure.
+ """
+ if expected_bindings is None:
+ return
-base_command = [bindgen_path, "-o", rust_path]
+ actual_bindings = get_actual_bindings(rust_bindings_path)
+ if actual_bindings == expected_bindings:
+ return
-for line in COMMON_PRELUDE.split("\n"):
- base_command.append("--raw-line")
- base_command.append(line)
+ print("error: actual generated bindings do not match expected generated bindings!")
-base_command.extend(flags)
-base_command.append(c_path)
+ def to_diffable(s):
+ return map(lambda l: l + "\n", s.split("\n"))
+
+ diff = difflib.unified_diff(to_diffable(expected_bindings),
+ to_diffable(actual_bindings),
+ fromfile="expected_bindings.rs",
+ tofile="actual_bindings.rs")
+ sys.stderr.writelines(diff)
+ sys.stderr.write("\n")
-env = os.environ.copy()
+ sys.exit(1)
+
+def main():
+ args = parse_args()
-# El Capitan likes to unset dyld variables
-# https://forums.developer.apple.com/thread/9233
-if "DYLD_LIBRARY_PATH" not in env and "LIBCLANG_PATH" in env:
- env["DYLD_LIBRARY_PATH"] = env["LIBCLANG_PATH"]
-subprocess.check_call(base_command, cwd=os.getcwd(), env=env)
+ (test_flags, test_features) = get_bindgen_flags_and_features(args.header)
+ if not all(f in args.features for f in test_features):
+ sys.exit(0)
+ expected_bindings = get_expected_bindings(args.rust_bindings)
+ generate_bindings(args.bindgen, test_flags, args.header, args.rust_bindings)
+ test_generated_bindings(args.rust_bindings)
+ check_actual_vs_expected(expected_bindings, args.rust_bindings)
+ sys.exit(0)
-name = None
-with tempfile.NamedTemporaryFile(delete=False) as tests:
- name = tests.name
- subprocess.check_call(["rustc", "--test", sys.argv[3], "-o", tests.name])
-subprocess.check_call([tests.name])
+if __name__ == "__main__":
+ main()