diff options
48 files changed, 1070 insertions, 1165 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 @@ -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 f98644ce..de405c9d 100644 --- a/src/clang.rs +++ b/src/clang.rs @@ -735,6 +735,7 @@ impl Drop for Index { } // Token +#[derive(Debug)] pub struct Token { pub kind: CXTokenKind, pub spelling: String, @@ -1049,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 4ebc48bf..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(); @@ -1423,7 +1444,8 @@ impl ToRustTy for Type { if inner_ty.canonical_type(ctx).is_function() { ty } else { - ty.to_ptr(inner.expect_type().is_const(), ctx.span()) + let is_const = self.is_const() || inner.expect_type().is_const(); + ty.to_ptr(is_const, ctx.span()) } } TypeKind::Named(..) => { @@ -1618,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 d31b7adf..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) @@ -548,8 +547,8 @@ impl CompInfo { 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); + let param = Item::named_type(cur.spelling(), default_type, + potential_id, ctx); ci.template_args.push(param); } CXCursor_CXXBaseSpecifier => { @@ -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/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 89ef893c..bf45b9fc 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); 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() } } @@ -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/class.rs b/tests/expectations/class.rs index 5951e0e6..4f736342 100644 --- a/tests/expectations/class.rs +++ b/tests/expectations/class.rs @@ -72,3 +72,52 @@ fn bindgen_test_layout_WithUnion() { impl Clone for WithUnion { fn clone(&self) -> Self { *self } } +#[repr(C)] +#[derive(Debug, Copy)] +pub struct RealAbstractionWithTonsOfMethods { + pub _address: u8, +} +#[test] +fn bindgen_test_layout_RealAbstractionWithTonsOfMethods() { + assert_eq!(::std::mem::size_of::<RealAbstractionWithTonsOfMethods>() , + 1usize); + assert_eq!(::std::mem::align_of::<RealAbstractionWithTonsOfMethods>() , + 1usize); +} +extern "C" { + #[link_name = "_ZNK32RealAbstractionWithTonsOfMethods3barEv"] + pub fn RealAbstractionWithTonsOfMethods_bar(this: + *const RealAbstractionWithTonsOfMethods); +} +extern "C" { + #[link_name = "_ZN32RealAbstractionWithTonsOfMethods3barEv"] + pub fn RealAbstractionWithTonsOfMethods_bar1(this: + *mut RealAbstractionWithTonsOfMethods); +} +extern "C" { + #[link_name = "_ZN32RealAbstractionWithTonsOfMethods3barEi"] + pub fn RealAbstractionWithTonsOfMethods_bar2(this: + *mut RealAbstractionWithTonsOfMethods, + foo: ::std::os::raw::c_int); +} +extern "C" { + #[link_name = "_ZN32RealAbstractionWithTonsOfMethods3staEv"] + pub fn RealAbstractionWithTonsOfMethods_sta(); +} +impl Clone for RealAbstractionWithTonsOfMethods { + fn clone(&self) -> Self { *self } +} +impl RealAbstractionWithTonsOfMethods { + #[inline] + pub unsafe fn bar(&self) { RealAbstractionWithTonsOfMethods_bar(&*self) } + #[inline] + pub unsafe fn bar1(&mut self) { + RealAbstractionWithTonsOfMethods_bar1(&mut *self) + } + #[inline] + pub unsafe fn bar2(&mut self, foo: ::std::os::raw::c_int) { + RealAbstractionWithTonsOfMethods_bar2(&mut *self, foo) + } + #[inline] + pub unsafe fn sta() { RealAbstractionWithTonsOfMethods_sta() } +} 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/const_tparam.rs b/tests/expectations/const_tparam.rs index 59649626..3ed10d28 100644 --- a/tests/expectations/const_tparam.rs +++ b/tests/expectations/const_tparam.rs @@ -7,5 +7,6 @@ #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct C<T> { - pub foo: *mut T, + pub foo: *const T, + pub bar: *mut T, } 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/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.hpp b/tests/headers/class.hpp index a34d4d8e..e753f186 100644 --- a/tests/headers/class.hpp +++ b/tests/headers/class.hpp @@ -18,3 +18,12 @@ union Union { class WithUnion { Union data; }; + +class RealAbstractionWithTonsOfMethods { + void foo(); +public: + void bar() const; + void bar(); + void bar(int foo); + static void sta(); +}; 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/const_tparam.hpp b/tests/headers/const_tparam.hpp index a2db574c..05f26e4a 100644 --- a/tests/headers/const_tparam.hpp +++ b/tests/headers/const_tparam.hpp @@ -1,4 +1,5 @@ template<typename T> class C { const T* const foo; + const T* bar; }; 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/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() |