diff options
-rw-r--r-- | .travis.yml | 2 | ||||
-rwxr-xr-x | src/clang.rs | 9 | ||||
-rwxr-xr-x | src/codegen/mod.rs | 129 | ||||
-rw-r--r-- | src/ir/comp.rs | 33 | ||||
-rw-r--r-- | src/ir/context.rs | 2 | ||||
-rw-r--r-- | src/ir/function.rs | 23 | ||||
-rw-r--r-- | src/ir/item.rs | 59 | ||||
-rw-r--r-- | src/ir/mod.rs | 1 | ||||
-rw-r--r-- | src/ir/ty.rs | 67 | ||||
-rw-r--r-- | src/ir/type_collector.rs | 23 | ||||
-rwxr-xr-x | src/lib.rs | 1 | ||||
-rw-r--r-- | tests/tests.rs | 11 |
12 files changed, 212 insertions, 148 deletions
diff --git a/.travis.yml b/.travis.yml index 031c61ff..5d3395e5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -41,6 +41,8 @@ before_script: script: - cargo build --verbose --features llvm_stable - cargo test --features llvm_stable + - cargo build --release --verbose --features llvm_stable + - cargo test --release --features llvm_stable - git add -A - git diff @ - git diff-index --quiet HEAD diff --git a/src/clang.rs b/src/clang.rs index 0297ac0a..07680367 100755 --- a/src/clang.rs +++ b/src/clang.rs @@ -670,9 +670,12 @@ impl Type { /// Given that this type is an array, vector, or complex type, return the /// type of its elements. - pub fn elem_type(&self) -> Type { - unsafe { - Type { x: clang_getElementType(self.x) } + pub fn elem_type(&self) -> Option<Type> { + let current_type = Type { x: unsafe { clang_getElementType(self.x) } }; + if current_type.kind() != CXType_Invalid { + Some(current_type) + } else { + None } } diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index 65900739..a656c2c1 100755 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -14,11 +14,11 @@ use ir::item_kind::ItemKind; use ir::comp::{CompKind, CompInfo, Field, Method}; use ir::layout::Layout; use ir::annotations::FieldAccessorKind; +use ir::type_collector::{ItemSet, TypeCollector}; use std::ops; use std::borrow::Cow; use std::mem; -use std::collections::BTreeSet; use std::collections::HashSet; use std::collections::hash_map::{HashMap, Entry}; @@ -1584,133 +1584,6 @@ impl CodeGenerator for Function { } } -type ItemSet = BTreeSet<ItemId>; - -trait TypeCollector { - type Extra; - - fn collect_types(&self, - context: &BindgenContext, - types: &mut ItemSet, - extra: &Self::Extra); -} - -impl TypeCollector for ItemId { - type Extra = (); - - fn collect_types(&self, - context: &BindgenContext, - types: &mut ItemSet, - extra: &()) { - context.resolve_item(*self).collect_types(context, types, extra); - } -} - -impl TypeCollector for Item { - type Extra = (); - - fn collect_types(&self, - context: &BindgenContext, - types: &mut ItemSet, - _extra: &()) { - if self.is_hidden(context) || types.contains(&self.id()) { - return; - } - - match *self.kind() { - ItemKind::Type(ref ty) => { - types.insert(self.id()); - if !self.is_opaque(context) { - ty.collect_types(context, types, self); - } - } - _ => {}, // FIXME. - } - } -} - -impl TypeCollector for Type { - type Extra = Item; - - fn collect_types(&self, - context: &BindgenContext, - types: &mut ItemSet, - item: &Item) { - match *self.kind() { - TypeKind::Pointer(inner) | - TypeKind::Reference(inner) | - TypeKind::Array(inner, _) | - TypeKind::TemplateAlias(inner, _) | - TypeKind::Alias(_, inner) | - TypeKind::Named(_, Some(inner)) | - TypeKind::ResolvedTypeRef(inner) - => inner.collect_types(context, types, &()), - - TypeKind::TemplateRef(inner, ref template_args) => { - inner.collect_types(context, types, &()); - for item in template_args { - item.collect_types(context, types, &()); - } - } - TypeKind::Comp(ref ci) => ci.collect_types(context, types, item), - TypeKind::Function(ref sig) => { - sig.collect_types(context, types, item) - } - // FIXME: Pending types! - ref other @ _ => { - debug!("Ignoring: {:?}", other); - }, - } - } -} - -impl TypeCollector for FunctionSig { - type Extra = Item; - - fn collect_types(&self, - context: &BindgenContext, - types: &mut ItemSet, - _item: &Item) { - self.return_type().collect_types(context, types, &()); - - for &(_, ty) in self.argument_types() { - ty.collect_types(context, types, &()); - } - } -} - -impl TypeCollector for CompInfo { - type Extra = Item; - - fn collect_types(&self, - context: &BindgenContext, - types: &mut ItemSet, - item: &Item) { - if let Some(template) = self.specialized_template() { - template.collect_types(context, types, &()); - } - - let applicable_template_args = item.applicable_template_args(context); - for arg in applicable_template_args { - arg.collect_types(context, types, &()); - } - - for base in self.base_members() { - base.collect_types(context, types, &()); - } - - for field in self.fields() { - field.ty().collect_types(context, types, &()); - } - - for ty in self.inner_types() { - ty.collect_types(context, types, &()); - } - - // FIXME(emilio): Methods, VTable? - } -} - pub fn codegen(context: &mut BindgenContext) -> Vec<P<ast::Item>> { context.gen(|context| { let mut result = CodegenResult::new(); diff --git a/src/ir/comp.rs b/src/ir/comp.rs index 355adb5c..98003936 100644 --- a/src/ir/comp.rs +++ b/src/ir/comp.rs @@ -5,6 +5,7 @@ use super::context::BindgenContext; use super::layout::Layout; use super::item::{Item, ItemId}; use super::ty::{Type, RUST_DERIVE_IN_ARRAY_LIMIT}; +use super::type_collector::{ItemSet, TypeCollector}; use std::cell::Cell; use std::cmp; use parse::{ClangItemParser, ParseError}; @@ -802,3 +803,35 @@ impl CompInfo { }) } } + +impl TypeCollector for CompInfo { + type Extra = Item; + + fn collect_types(&self, + context: &BindgenContext, + types: &mut ItemSet, + item: &Item) { + if let Some(template) = self.specialized_template() { + template.collect_types(context, types, &()); + } + + let applicable_template_args = item.applicable_template_args(context); + for arg in applicable_template_args { + arg.collect_types(context, types, &()); + } + + for base in self.base_members() { + base.collect_types(context, types, &()); + } + + for field in self.fields() { + field.ty().collect_types(context, types, &()); + } + + for ty in self.inner_types() { + ty.collect_types(context, types, &()); + } + + // FIXME(emilio): Methods, VTable? + } +} diff --git a/src/ir/context.rs b/src/ir/context.rs index 65c62817..729a1c02 100644 --- a/src/ir/context.rs +++ b/src/ir/context.rs @@ -32,7 +32,7 @@ enum TypeKey { // context. struct GenContext<'ctx>(ExtCtxt<'ctx>); -impl<'ctx> fmt::Debug for GenContext <'ctx> { +impl<'ctx> fmt::Debug for GenContext<'ctx> { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { write!(fmt, "GenContext {{ ... }}") } diff --git a/src/ir/function.rs b/src/ir/function.rs index 0cf23f43..e9fd4a25 100644 --- a/src/ir/function.rs +++ b/src/ir/function.rs @@ -1,8 +1,9 @@ //! Intermediate representation for C/C++ functions and methods. +use super::context::BindgenContext; use super::item::{Item, ItemId}; use super::ty::TypeKind; -use super::context::BindgenContext; +use super::type_collector::{ItemSet, TypeCollector}; use syntax::abi; use clang; use clangll::Enum_CXCallingConv; @@ -97,13 +98,16 @@ pub fn cursor_mangling(cursor: &clang::Cursor) -> Option<String> { } let mut mangling = cursor.mangling(); + if mangling.is_empty() { + return None; + } // Try to undo backend linkage munging (prepended _, generally) if cfg!(target_os = "macos") { mangling.remove(0); } - if mangling.is_empty() { None } else { Some(mangling) } + Some(mangling) } impl FunctionSig { @@ -246,3 +250,18 @@ impl ClangSubItemParser for Function { Ok(ParseResult::New(function, Some(cursor))) } } + +impl TypeCollector for FunctionSig { + type Extra = Item; + + fn collect_types(&self, + context: &BindgenContext, + types: &mut ItemSet, + _item: &Item) { + self.return_type().collect_types(context, types, &()); + + for &(_, ty) in self.argument_types() { + ty.collect_types(context, types, &()); + } + } +} diff --git a/src/ir/item.rs b/src/ir/item.rs index 0d409f04..a6d15cd7 100644 --- a/src/ir/item.rs +++ b/src/ir/item.rs @@ -4,6 +4,7 @@ use regex::Regex; use super::context::BindgenContext; use super::item_kind::ItemKind; use super::ty::{Type, TypeKind}; +use super::type_collector::{ItemSet, TypeCollector}; use super::function::Function; use super::module::Module; use super::annotations::Annotations; @@ -11,7 +12,6 @@ use std::cell::{Cell, RefCell}; use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering}; use parse::{ClangItemParser, ClangSubItemParser, ParseError, ParseResult}; use clang; -use clangll; /// A trait to get the canonical name from an item. /// @@ -76,6 +76,40 @@ impl ItemCanonicalPath for ItemId { } } +impl TypeCollector for ItemId { + type Extra = (); + + fn collect_types(&self, + context: &BindgenContext, + types: &mut ItemSet, + extra: &()) { + context.resolve_item(*self).collect_types(context, types, extra); + } +} + +impl TypeCollector for Item { + type Extra = (); + + fn collect_types(&self, + context: &BindgenContext, + types: &mut ItemSet, + _extra: &()) { + if self.is_hidden(context) || types.contains(&self.id()) { + return; + } + + match *self.kind() { + ItemKind::Type(ref ty) => { + types.insert(self.id()); + if !self.is_opaque(context) { + ty.collect_types(context, types, self); + } + } + _ => {}, // FIXME. + } + } +} + /// 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). @@ -566,12 +600,12 @@ impl Item { static ref RE_ENDS_WITH_BINDGEN_TY: Regex = Regex::new(r"_bindgen_ty(_\d+)+$").unwrap(); static ref RE_ENDS_WITH_BINDGEN_MOD: Regex = Regex::new(r"_bindgen_mod(_\d+)+$").unwrap(); } - + let (re, kind) = match *self.kind() { ItemKind::Module(..) => (&*RE_ENDS_WITH_BINDGEN_MOD, "mod"), _ => (&*RE_ENDS_WITH_BINDGEN_TY, "ty"), }; - + let parent_name = parent_name.and_then(|n| if n.is_empty() { None } else { Some(n) }); match (parent_name, base_name) { (Some(parent), Some(base)) => format!("{}_{}", parent, base), @@ -636,6 +670,7 @@ impl ClangItemParser for Item { use ir::function::Function; use ir::module::Module; use ir::var::Var; + use clangll::*; if !cursor.is_valid() { return Err(ParseError::Continue); @@ -697,11 +732,23 @@ impl ClangItemParser for Item { } // Guess how does clang treat extern "C" blocks? - if cursor.kind() == clangll::CXCursor_UnexposedDecl { + if cursor.kind() == CXCursor_UnexposedDecl { Err(ParseError::Recurse) } else { - error!("Unhandled cursor kind: {} ({})", - ::clang::kind_to_str(cursor.kind()), cursor.kind()); + // We whitelist cursors here known to be unhandled, to prevent being + // too noisy about this. + match cursor.kind() { + CXCursor_MacroDefinition | + CXCursor_InclusionDirective => { + debug!("Unhandled cursor kind {:?}: {:?}", + cursor.kind(), cursor); + }, + _ =>{ + error!("Unhandled cursor kind {:?}: {:?}", + cursor.kind(), cursor); + } + } + Err(ParseError::Continue) } } diff --git a/src/ir/mod.rs b/src/ir/mod.rs index 01f388b8..3c658a4a 100644 --- a/src/ir/mod.rs +++ b/src/ir/mod.rs @@ -14,4 +14,5 @@ pub mod item_kind; pub mod layout; pub mod module; pub mod ty; +pub mod type_collector; pub mod var; diff --git a/src/ir/ty.rs b/src/ir/ty.rs index 442e0122..a914ce14 100644 --- a/src/ir/ty.rs +++ b/src/ir/ty.rs @@ -7,6 +7,7 @@ use super::item::{Item, ItemId}; use super::int::IntKind; use super::layout::Layout; use super::context::BindgenContext; +use super::type_collector::{ItemSet, TypeCollector}; use parse::{ClangItemParser, ParseResult, ParseError}; use clang::{self, Cursor}; @@ -602,7 +603,18 @@ impl Type { return Err(ParseError::Recurse); } - error!("invalid type {:?}", ty); + // If the type name is empty we're probably + // over-recursing to find a template parameter name + // or something like that, so just don't be too + // noisy with it since it causes confusion, see for + // example the discussion in: + // + // https://github.com/jamesmunns/teensy3-rs/issues/9 + if !ty.spelling().is_empty() { + error!("invalid type {:?}", ty); + } else { + warn!("invalid type {:?}", ty); + } return Err(ParseError::Continue); } } @@ -613,7 +625,11 @@ impl Type { return Err(ParseError::Recurse); } - error!("invalid type `{}`", ty.spelling()); + if !ty.spelling().is_empty() { + error!("invalid type {:?}", ty); + } else { + warn!("invalid type {:?}", ty); + } return Err(ParseError::Continue); } } @@ -647,7 +663,9 @@ impl Type { CXType_VariableArray | CXType_DependentSizedArray | CXType_IncompleteArray => { - let inner = Item::from_ty(&ty.elem_type(), location, parent_id, ctx) + let inner = Item::from_ty(ty.elem_type().as_ref() + .expect("Not an appropriate type?"), + location, parent_id, ctx) .expect("Not able to resolve array element?"); TypeKind::Pointer(inner) } @@ -679,14 +697,18 @@ impl Type { // That being said, that should be fixed eventually. CXType_Vector | CXType_ConstantArray => { - let inner = Item::from_ty(&ty.elem_type(), location, parent_id, ctx) + let inner = Item::from_ty(ty.elem_type().as_ref() + .expect("Not an appropriate type?"), + location, parent_id, ctx) .expect("Not able to resolve array element?"); TypeKind::Array(inner, ty.num_elements().unwrap()) } // A complex number is always a real and an imaginary part, so // represent that as a two-item array. CXType_Complex => { - let inner = Item::from_ty(&ty.elem_type(), location, parent_id, ctx) + let inner = Item::from_ty(ty.elem_type().as_ref() + .expect("Not an appropriate type?"), + location, parent_id, ctx) .expect("Not able to resolve array element?"); TypeKind::Array(inner, 2) } @@ -709,3 +731,38 @@ impl Type { Ok(ParseResult::New(ty, Some(cursor.canonical()))) } } + +impl TypeCollector for Type { + type Extra = Item; + + fn collect_types(&self, + context: &BindgenContext, + types: &mut ItemSet, + item: &Item) { + match *self.kind() { + TypeKind::Pointer(inner) | + TypeKind::Reference(inner) | + TypeKind::Array(inner, _) | + TypeKind::TemplateAlias(inner, _) | + TypeKind::Alias(_, inner) | + TypeKind::Named(_, Some(inner)) | + TypeKind::ResolvedTypeRef(inner) + => inner.collect_types(context, types, &()), + + TypeKind::TemplateRef(inner, ref template_args) => { + inner.collect_types(context, types, &()); + for item in template_args { + item.collect_types(context, types, &()); + } + } + TypeKind::Comp(ref ci) => ci.collect_types(context, types, item), + TypeKind::Function(ref sig) => { + sig.collect_types(context, types, item) + } + // FIXME: Pending types! + ref other @ _ => { + debug!("Ignoring: {:?}", other); + }, + } + } +} diff --git a/src/ir/type_collector.rs b/src/ir/type_collector.rs new file mode 100644 index 00000000..a829b165 --- /dev/null +++ b/src/ir/type_collector.rs @@ -0,0 +1,23 @@ +//! Collecting type items. + +use std::collections::BTreeSet; +use super::context::BindgenContext; +use super::item::ItemId; + +/// A set of items. +pub type ItemSet = BTreeSet<ItemId>; + +/// Collect all the type items referenced by this item. +pub trait TypeCollector { + /// If a particular type needs extra information beyond what it has in + /// `self` and `context` to find its referenced type items, its + /// implementation can define this associated type, forcing callers to pass + /// the needed information through. + type Extra; + + /// Add each type item referenced by `self` into the `types` set. + fn collect_types(&self, + context: &BindgenContext, + types: &mut ItemSet, + extra: &Self::Extra); +} @@ -12,6 +12,7 @@ #![cfg_attr(feature = "clippy", plugin(clippy))] #![deny(missing_docs)] +#![deny(warnings)] // We internally use the deprecated BindgenOptions all over the place. Once we // remove its `pub` declaration, we can un-deprecate it and remove this pragma. diff --git a/tests/tests.rs b/tests/tests.rs index 003c0f1a..addaa5ad 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -64,7 +64,11 @@ fn run_bindgen_tests() { let mut bindgen = PathBuf::from(&crate_root); bindgen.push("target"); - bindgen.push("debug"); + if cfg!(debug_assertions) { + bindgen.push("debug"); + } else { + bindgen.push("release"); + } bindgen.push("bindgen"); if !bindgen.is_file() { panic!("{} is not a file! Build bindgen before running tests.", @@ -92,8 +96,9 @@ fn run_bindgen_tests() { .and_then(|x| x.parse::<usize>().ok()) .unwrap_or(TEST_BATCH_DEFAULT_SIZE); - // Spawn batch_size child to run in parallel - // and wait on all of them before processing the next batch + // Spawn `batch_size` children to run in parallel and wait on all of them + // before processing the next batch. This puts a limit on the resources + // consumed when testing, so that we don't overload the system. let children = tests.chunks(batch_size).map(|x| { x.iter().map(|entry| { |