summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNick Fitzgerald <fitzgen@gmail.com>2017-07-19 15:55:29 -0700
committerNick Fitzgerald <fitzgen@gmail.com>2017-07-20 14:47:11 -0700
commit0b720a311ba243c66965a060af7dd3d9bc102315 (patch)
treeb2510df39fe430af585c98cdf2e505f3de7cf77a
parentb9b564869ff73f8725d857d4288812780305cc2d (diff)
Only whitelist items which we intend to generate code for
This makes only generating certain kinds of items more robust, since we don't need to keep checking whether codegen is enabled for different kinds of items all over the place, and can do it the once. It should also reduce the number of items we have to consider in our various analyses for which we don't ultimately care about the answer. Fixes #826
-rw-r--r--src/codegen/mod.rs41
-rw-r--r--src/ir/cant_derive_debug.rs1
-rw-r--r--src/ir/comp.rs6
-rw-r--r--src/ir/context.rs6
-rw-r--r--src/ir/function.rs56
-rw-r--r--src/ir/item.rs24
-rw-r--r--src/ir/named.rs1
-rw-r--r--src/ir/traversal.rs57
-rw-r--r--tests/expectations/tests/gen-constructors-neg.rs4
-rw-r--r--tests/expectations/tests/gen-destructors-neg.rs4
-rw-r--r--tests/expectations/tests/issue-826-generating-methods-when-asked-not-to.rs21
-rw-r--r--tests/headers/issue-826-generating-methods-when-asked-not-to.hpp5
12 files changed, 187 insertions, 39 deletions
diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs
index 6f2bf96b..cf4fad50 100644
--- a/src/codegen/mod.rs
+++ b/src/codegen/mod.rs
@@ -294,6 +294,10 @@ impl CodeGenerator for Item {
result: &mut CodegenResult<'a>,
whitelisted_items: &ItemSet,
_extra: &()) {
+ if !self.is_enabled_for_codegen(ctx) {
+ return;
+ }
+
if self.is_hidden(ctx) || result.seen(self.id()) {
debug!("<Item as CodeGenerator>::codegen: Ignoring hidden or seen: \
self = {:?}",
@@ -316,19 +320,13 @@ impl CodeGenerator for Item {
module.codegen(ctx, result, whitelisted_items, self);
}
ItemKind::Function(ref fun) => {
- if ctx.options().codegen_config.functions {
- fun.codegen(ctx, result, whitelisted_items, self);
- }
+ fun.codegen(ctx, result, whitelisted_items, self);
}
ItemKind::Var(ref var) => {
- if ctx.options().codegen_config.vars {
- var.codegen(ctx, result, whitelisted_items, self);
- }
+ var.codegen(ctx, result, whitelisted_items, self);
}
ItemKind::Type(ref ty) => {
- if ctx.options().codegen_config.types {
- ty.codegen(ctx, result, whitelisted_items, self);
- }
+ ty.codegen(ctx, result, whitelisted_items, self);
}
}
}
@@ -419,6 +417,7 @@ impl CodeGenerator for Var {
item: &Item) {
use ir::var::VarType;
debug!("<Var as CodeGenerator>::codegen: item = {:?}", item);
+ debug_assert!(item.is_enabled_for_codegen(ctx));
let canonical_name = item.canonical_name(ctx);
@@ -522,6 +521,7 @@ impl CodeGenerator for Type {
whitelisted_items: &ItemSet,
item: &Item) {
debug!("<Type as CodeGenerator>::codegen: item = {:?}", item);
+ debug_assert!(item.is_enabled_for_codegen(ctx));
match *self.kind() {
TypeKind::Void |
@@ -705,6 +705,8 @@ impl<'a> CodeGenerator for Vtable<'a> {
_whitelisted_items: &ItemSet,
item: &Item) {
assert_eq!(item.id(), self.item_id);
+ debug_assert!(item.is_enabled_for_codegen(ctx));
+
// For now, generate an empty struct, later we should generate function
// pointers and whatnot.
let attributes = vec![attributes::repr("C")];
@@ -745,6 +747,8 @@ impl CodeGenerator for TemplateInstantiation {
result: &mut CodegenResult<'a>,
_whitelisted_items: &ItemSet,
item: &Item) {
+ debug_assert!(item.is_enabled_for_codegen(ctx));
+
// Although uses of instantiations don't need code generation, and are
// just converted to rust types in fields, vars, etc, we take this
// opportunity to generate tests for their layout here. If the
@@ -1376,6 +1380,7 @@ impl CodeGenerator for CompInfo {
whitelisted_items: &ItemSet,
item: &Item) {
debug!("<CompInfo as CodeGenerator>::codegen: item = {:?}", item);
+ debug_assert!(item.is_enabled_for_codegen(ctx));
// Don't output classes with template parameters that aren't types, and
// also don't output template specializations, neither total or partial.
@@ -1897,6 +1902,18 @@ impl MethodCodegen for Method {
result: &mut CodegenResult<'a>,
whitelisted_items: &ItemSet,
_parent: &CompInfo) {
+ assert!({
+ let cc = &ctx.options().codegen_config;
+ match self.kind() {
+ MethodKind::Constructor => cc.constructors,
+ MethodKind::Destructor => cc.destructors,
+ MethodKind::VirtualDestructor => cc.destructors,
+ MethodKind::Static |
+ MethodKind::Normal |
+ MethodKind::Virtual => cc.methods,
+ }
+ });
+
if self.is_virtual() {
return; // FIXME
}
@@ -2287,6 +2304,7 @@ impl CodeGenerator for Enum {
_whitelisted_items: &ItemSet,
item: &Item) {
debug!("<Enum as CodeGenerator>::codegen: item = {:?}", item);
+ debug_assert!(item.is_enabled_for_codegen(ctx));
let name = item.canonical_name(ctx);
let enum_ty = item.expect_type();
@@ -3026,6 +3044,7 @@ impl CodeGenerator for Function {
_whitelisted_items: &ItemSet,
item: &Item) {
debug!("<Function as CodeGenerator>::codegen: item = {:?}", item);
+ debug_assert!(item.is_enabled_for_codegen(ctx));
// Similar to static member variables in a class template, we can't
// generate bindings to template functions, because the set of
@@ -3202,7 +3221,9 @@ impl CodeGenerator for ObjCInterface {
ctx: &BindgenContext,
result: &mut CodegenResult<'a>,
_whitelisted_items: &ItemSet,
- _: &Item) {
+ item: &Item) {
+ debug_assert!(item.is_enabled_for_codegen(ctx));
+
let mut impl_items = vec![];
let mut trait_items = vec![];
diff --git a/src/ir/cant_derive_debug.rs b/src/ir/cant_derive_debug.rs
index 465f068c..f6a7fab7 100644
--- a/src/ir/cant_derive_debug.rs
+++ b/src/ir/cant_derive_debug.rs
@@ -67,6 +67,7 @@ impl<'ctx, 'gen> CantDeriveDebugAnalysis<'ctx, 'gen> {
EdgeKind::TemplateParameterDefinition => true,
EdgeKind::Constructor |
+ EdgeKind::Destructor |
EdgeKind::FunctionReturn |
EdgeKind::FunctionParameter |
EdgeKind::InnerType |
diff --git a/src/ir/comp.rs b/src/ir/comp.rs
index c7c2d82a..0960ee11 100644
--- a/src/ir/comp.rs
+++ b/src/ir/comp.rs
@@ -1534,7 +1534,11 @@ impl Trace for CompInfo {
}
for method in self.methods() {
- tracer.visit_kind(method.signature, EdgeKind::Method);
+ if method.is_destructor() {
+ tracer.visit_kind(method.signature, EdgeKind::Destructor);
+ } else {
+ tracer.visit_kind(method.signature, EdgeKind::Method);
+ }
}
for &ctor in self.constructors() {
diff --git a/src/ir/context.rs b/src/ir/context.rs
index b9b5af3e..a0c76186 100644
--- a/src/ir/context.rs
+++ b/src/ir/context.rs
@@ -186,7 +186,7 @@ pub struct WhitelistedItems<'ctx, 'gen>
'gen,
ItemSet,
Vec<ItemId>,
- fn(Edge) -> bool>,
+ for<'a> fn(&'a BindgenContext, Edge) -> bool>,
}
impl<'ctx, 'gen> Iterator for WhitelistedItems<'ctx, 'gen>
@@ -215,7 +215,7 @@ impl<'ctx, 'gen> WhitelistedItems<'ctx, 'gen>
where R: IntoIterator<Item = ItemId>,
{
let predicate = if ctx.options().whitelist_recursively {
- traversal::all_edges
+ traversal::codegen_edges
} else {
traversal::no_edges
};
@@ -1573,6 +1573,8 @@ impl<'ctx> BindgenContext<'ctx> {
assert!(self.current_module == self.root_module);
let roots = self.items()
+ // Only consider items that are enabled for codegen.
+ .filter(|&(_, item)| item.is_enabled_for_codegen(self))
.filter(|&(_, item)| {
// If nothing is explicitly whitelisted, then everything is fair
// game.
diff --git a/src/ir/function.rs b/src/ir/function.rs
index 71bb0dd4..663bb8e3 100644
--- a/src/ir/function.rs
+++ b/src/ir/function.rs
@@ -1,17 +1,47 @@
//! Intermediate representation for C/C++ functions and methods.
+use super::comp::MethodKind;
use super::context::{BindgenContext, ItemId};
use super::dot::DotAttributes;
use super::item::Item;
use super::traversal::{EdgeKind, Trace, Tracer};
use super::ty::TypeKind;
use clang;
-use clang_sys::CXCallingConv;
+use clang_sys::{self, CXCallingConv};
use ir::derive::CanTriviallyDeriveDebug;
use parse::{ClangItemParser, ClangSubItemParser, ParseError, ParseResult};
use std::io;
use syntax::abi;
+/// What kind of a function are we looking at?
+#[derive(Debug, Copy, Clone, PartialEq)]
+pub enum FunctionKind {
+ /// A plain, free function.
+ Function,
+ /// A method of some kind.
+ Method(MethodKind),
+}
+
+impl FunctionKind {
+ fn from_cursor(cursor: &clang::Cursor) -> Option<FunctionKind> {
+ Some(match cursor.kind() {
+ clang_sys::CXCursor_FunctionDecl => FunctionKind::Function,
+ clang_sys::CXCursor_Constructor => FunctionKind::Method(MethodKind::Constructor),
+ clang_sys::CXCursor_Destructor => FunctionKind::Method(MethodKind::Destructor),
+ clang_sys::CXCursor_CXXMethod => {
+ if cursor.method_is_virtual() {
+ FunctionKind::Method(MethodKind::Virtual)
+ } else if cursor.method_is_static() {
+ FunctionKind::Method(MethodKind::Static)
+ } else {
+ FunctionKind::Method(MethodKind::Normal)
+ }
+ }
+ _ => return None,
+ })
+ }
+}
+
/// A function declaration, with a signature, arguments, and argument names.
///
/// The argument names vector must be the same length as the ones in the
@@ -29,6 +59,9 @@ pub struct Function {
/// The doc comment on the function, if any.
comment: Option<String>,
+
+ /// The kind of function this is.
+ kind: FunctionKind,
}
impl Function {
@@ -36,13 +69,15 @@ impl Function {
pub fn new(name: String,
mangled_name: Option<String>,
sig: ItemId,
- comment: Option<String>)
+ comment: Option<String>,
+ kind: FunctionKind)
-> Self {
Function {
name: name,
mangled_name: mangled_name,
signature: sig,
comment: comment,
+ kind: kind,
}
}
@@ -60,6 +95,11 @@ impl Function {
pub fn signature(&self) -> ItemId {
self.signature
}
+
+ /// Get this function's kind.
+ pub fn kind(&self) -> FunctionKind {
+ self.kind
+ }
}
impl DotAttributes for Function {
@@ -357,12 +397,10 @@ impl ClangSubItemParser for Function {
context: &mut BindgenContext)
-> Result<ParseResult<Self>, ParseError> {
use clang_sys::*;
- match cursor.kind() {
- CXCursor_FunctionDecl |
- CXCursor_Constructor |
- CXCursor_Destructor |
- CXCursor_CXXMethod => {}
- _ => return Err(ParseError::Continue),
+
+ let kind = match FunctionKind::from_cursor(&cursor) {
+ None => return Err(ParseError::Continue),
+ Some(k) => k,
};
debug!("Function::parse({:?}, {:?})", cursor, cursor.cur_type());
@@ -415,7 +453,7 @@ impl ClangSubItemParser for Function {
let comment = cursor.raw_comment();
- let function = Self::new(name, mangled_name, sig, comment);
+ let function = Self::new(name, mangled_name, sig, comment, kind);
Ok(ParseResult::New(function, Some(cursor)))
}
}
diff --git a/src/ir/item.rs b/src/ir/item.rs
index 6712c8a7..2754c838 100644
--- a/src/ir/item.rs
+++ b/src/ir/item.rs
@@ -3,10 +3,11 @@
use super::super::codegen::CONSTIFIED_ENUM_MODULE_REPR_NAME;
use super::annotations::Annotations;
use super::comment;
+use super::comp::MethodKind;
use super::context::{BindgenContext, ItemId, PartialType};
use super::derive::{CanDeriveCopy, CanDeriveDebug, CanDeriveDefault};
use super::dot::DotAttributes;
-use super::function::Function;
+use super::function::{Function, FunctionKind};
use super::item_kind::ItemKind;
use super::layout::Opaque;
use super::module::Module;
@@ -874,6 +875,27 @@ impl Item {
_ => false,
}
}
+
+ /// Is this item of a kind that is enabled for code generation?
+ pub fn is_enabled_for_codegen(&self, ctx: &BindgenContext) -> bool {
+ let cc = &ctx.options().codegen_config;
+ match *self.kind() {
+ ItemKind::Module(..) => true,
+ ItemKind::Var(_) => cc.vars,
+ ItemKind::Type(_) => cc.types,
+ ItemKind::Function(ref f) => {
+ match f.kind() {
+ FunctionKind::Function => cc.functions,
+ FunctionKind::Method(MethodKind::Constructor) => cc.constructors,
+ FunctionKind::Method(MethodKind::Destructor) |
+ FunctionKind::Method(MethodKind::VirtualDestructor) => cc.destructors,
+ FunctionKind::Method(MethodKind::Static) |
+ FunctionKind::Method(MethodKind::Normal) |
+ FunctionKind::Method(MethodKind::Virtual) => cc.methods,
+ }
+ }
+ }
+ }
}
impl IsOpaque for ItemId {
diff --git a/src/ir/named.rs b/src/ir/named.rs
index a36ded64..5351f309 100644
--- a/src/ir/named.rs
+++ b/src/ir/named.rs
@@ -211,6 +211,7 @@ impl<'ctx, 'gen> UsedTemplateParameters<'ctx, 'gen> {
EdgeKind::BaseMember |
EdgeKind::Field |
EdgeKind::Constructor |
+ EdgeKind::Destructor |
EdgeKind::VarType |
EdgeKind::FunctionReturn |
EdgeKind::FunctionParameter |
diff --git a/src/ir/traversal.rs b/src/ir/traversal.rs
index cb27e8db..d4b081f5 100644
--- a/src/ir/traversal.rs
+++ b/src/ir/traversal.rs
@@ -139,6 +139,19 @@ pub enum EdgeKind {
/// ```
Constructor,
+ /// An edge from a class or struct type to its destructor function. For
+ /// example, the edge from `Doggo` to `Doggo::~Doggo()`:
+ ///
+ /// ```C++
+ /// struct Doggo {
+ /// char* wow;
+ ///
+ /// public:
+ /// ~Doggo();
+ /// };
+ /// ```
+ Destructor,
+
/// An edge from a function declaration to its return type. For example, the
/// edge from `foo` to `int`:
///
@@ -172,26 +185,54 @@ pub enum EdgeKind {
pub trait TraversalPredicate {
/// Should the traversal follow this edge, and visit everything that is
/// reachable through it?
- fn should_follow(&self, edge: Edge) -> bool;
+ fn should_follow(&self, ctx: &BindgenContext, edge: Edge) -> bool;
}
-impl TraversalPredicate for fn(Edge) -> bool {
- fn should_follow(&self, edge: Edge) -> bool {
- (*self)(edge)
+impl TraversalPredicate for for<'a> fn(&'a BindgenContext, Edge) -> bool {
+ fn should_follow(&self, ctx: &BindgenContext, edge: Edge) -> bool {
+ (*self)(ctx, edge)
}
}
/// A `TraversalPredicate` implementation that follows all edges, and therefore
/// traversals using this predicate will see the whole IR graph reachable from
/// the traversal's roots.
-pub fn all_edges(_: Edge) -> bool {
+pub fn all_edges(_: &BindgenContext, _: Edge) -> bool {
true
}
+/// A `TraversalPredicate` implementation that only follows edges to items that
+/// are enabled for code generation. This lets us skip considering items for
+/// which we won't generate any bindings to.
+pub fn codegen_edges(ctx: &BindgenContext, edge: Edge) -> bool {
+ let cc = &ctx.options().codegen_config;
+ match edge.kind {
+ EdgeKind::Generic => ctx.resolve_item(edge.to).is_enabled_for_codegen(ctx),
+
+ // We statically know the kind of item that non-generic edges can point
+ // to, so we don't need to actually resolve the item and check
+ // `Item::is_enabled_for_codegen`.
+ EdgeKind::TemplateParameterDefinition |
+ EdgeKind::TemplateArgument |
+ EdgeKind::TemplateDeclaration |
+ EdgeKind::BaseMember |
+ EdgeKind::Field |
+ EdgeKind::InnerType |
+ EdgeKind::FunctionReturn |
+ EdgeKind::FunctionParameter |
+ EdgeKind::VarType |
+ EdgeKind::TypeReference => cc.types,
+ EdgeKind::InnerVar => cc.vars,
+ EdgeKind::Method => cc.methods,
+ EdgeKind::Constructor => cc.constructors,
+ EdgeKind::Destructor => cc.destructors,
+ }
+}
+
/// A `TraversalPredicate` implementation that never follows any edges, and
/// therefore traversals using this predicate will only visit the traversal's
/// roots.
-pub fn no_edges(_: Edge) -> bool {
+pub fn no_edges(_: &BindgenContext, _: Edge) -> bool {
false
}
@@ -401,7 +442,7 @@ impl<'ctx, 'gen, Storage, Queue, Predicate> Tracer
{
fn visit_kind(&mut self, item: ItemId, kind: EdgeKind) {
let edge = Edge::new(item, kind);
- if !self.predicate.should_follow(edge) {
+ if !self.predicate.should_follow(self.ctx, edge) {
return;
}
@@ -451,7 +492,7 @@ pub type AssertNoDanglingItemsTraversal<'ctx, 'gen> =
'gen,
Paths<'ctx, 'gen>,
VecDeque<ItemId>,
- fn(Edge) -> bool>;
+ for<'a> fn(&'a BindgenContext, Edge) -> bool>;
#[cfg(test)]
mod tests {
diff --git a/tests/expectations/tests/gen-constructors-neg.rs b/tests/expectations/tests/gen-constructors-neg.rs
index 834d5f2e..c894b95c 100644
--- a/tests/expectations/tests/gen-constructors-neg.rs
+++ b/tests/expectations/tests/gen-constructors-neg.rs
@@ -19,7 +19,3 @@ fn bindgen_test_layout_Foo() {
impl Clone for Foo {
fn clone(&self) -> Self { *self }
}
-extern "C" {
- #[link_name = "_ZN3FooC1Ei"]
- pub fn Foo_Foo(this: *mut Foo, a: ::std::os::raw::c_int);
-}
diff --git a/tests/expectations/tests/gen-destructors-neg.rs b/tests/expectations/tests/gen-destructors-neg.rs
index c7c97104..64373d75 100644
--- a/tests/expectations/tests/gen-destructors-neg.rs
+++ b/tests/expectations/tests/gen-destructors-neg.rs
@@ -21,7 +21,3 @@ fn bindgen_test_layout_Foo() {
"Alignment of field: " , stringify ! ( Foo ) , "::" ,
stringify ! ( bar ) ));
}
-extern "C" {
- #[link_name = "_ZN3FooD1Ev"]
- pub fn Foo_Foo_destructor(this: *mut Foo);
-}
diff --git a/tests/expectations/tests/issue-826-generating-methods-when-asked-not-to.rs b/tests/expectations/tests/issue-826-generating-methods-when-asked-not-to.rs
new file mode 100644
index 00000000..c894b95c
--- /dev/null
+++ b/tests/expectations/tests/issue-826-generating-methods-when-asked-not-to.rs
@@ -0,0 +1,21 @@
+/* automatically generated by rust-bindgen */
+
+
+#![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)]
+
+
+#[repr(C)]
+#[derive(Debug, Default, Copy)]
+pub struct Foo {
+ pub _address: u8,
+}
+#[test]
+fn bindgen_test_layout_Foo() {
+ assert_eq!(::std::mem::size_of::<Foo>() , 1usize , concat ! (
+ "Size of: " , stringify ! ( Foo ) ));
+ assert_eq! (::std::mem::align_of::<Foo>() , 1usize , concat ! (
+ "Alignment of " , stringify ! ( Foo ) ));
+}
+impl Clone for Foo {
+ fn clone(&self) -> Self { *self }
+}
diff --git a/tests/headers/issue-826-generating-methods-when-asked-not-to.hpp b/tests/headers/issue-826-generating-methods-when-asked-not-to.hpp
new file mode 100644
index 00000000..f6b2ed30
--- /dev/null
+++ b/tests/headers/issue-826-generating-methods-when-asked-not-to.hpp
@@ -0,0 +1,5 @@
+// bindgen-flags: --ignore-methods -- --target=i686-pc-win32
+
+struct Foo {
+ void test();
+};