summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xsrc/bin/bindgen.rs20
-rwxr-xr-xsrc/clang.rs306
-rw-r--r--src/ir/comp.rs18
-rw-r--r--src/ir/context.rs10
-rw-r--r--src/ir/enum_ty.rs2
-rw-r--r--src/ir/function.rs4
-rw-r--r--src/ir/item.rs58
-rw-r--r--src/ir/module.rs4
-rw-r--r--src/ir/ty.rs6
-rw-r--r--src/ir/var.rs2
-rwxr-xr-xsrc/lib.rs71
-rw-r--r--src/uses.rs102
-rw-r--r--tests/expectations/base-to-derived.rs19
-rw-r--r--tests/headers/base-to-derived.hpp19
-rw-r--r--tests/tests.rs80
-rwxr-xr-xtests/tools/run-bindgen.py14
-rw-r--r--tests/uses/.gitignore2
17 files changed, 386 insertions, 351 deletions
diff --git a/src/bin/bindgen.rs b/src/bin/bindgen.rs
index 2a7995e7..17385d85 100755
--- a/src/bin/bindgen.rs
+++ b/src/bin/bindgen.rs
@@ -84,6 +84,10 @@ Options:
matching <regex>. Same behavior on emptyness
than the type whitelisting.
+ --dummy-uses=<path> For testing purposes, generate a C/C++ file
+ containing dummy uses of all types defined in
+ the input header.
+
<clang-args> Options other than stated above are passed
directly through to clang.
";
@@ -182,6 +186,11 @@ fn parse_args_or_exit(args: Vec<String>) -> (BindgenOptions, Box<io::Write>) {
"--use-msvc-mangling" => {
options.msvc_mangling = true;
}
+ "--dummy-uses" => {
+ let dummy_path = iter.next()
+ .expect("--dummy-uses expects a file path");
+ options.dummy_uses = Some(dummy_path);
+ }
other if source_file.is_none() => {
source_file = Some(other.into());
}
@@ -193,6 +202,12 @@ fn parse_args_or_exit(args: Vec<String>) -> (BindgenOptions, Box<io::Write>) {
if let Some(source_file) = source_file.take() {
options.clang_args.push(source_file);
+ options.input_header = options.clang_args.last().cloned();
+ } else {
+ options.input_header = options.clang_args
+ .iter()
+ .find(|arg| arg.ends_with(".h") || arg.ends_with(".hpp"))
+ .cloned();
}
let out = if let Some(ref path_name) = dest_file {
@@ -244,9 +259,12 @@ pub fn main() {
let (options, out) = parse_args_or_exit(bind_args);
- let bindings = Bindings::generate(options, None)
+ let mut bindings = Bindings::generate(options, None)
.expect("Unable to generate bindings");
+ bindings.write_dummy_uses()
+ .expect("Unable to write dummy uses to file.");
+
bindings.write(out)
.expect("Unable to write bindings to file.");
}
diff --git a/src/clang.rs b/src/clang.rs
index f8aac1c1..18216125 100755
--- a/src/clang.rs
+++ b/src/clang.rs
@@ -31,14 +31,6 @@ impl fmt::Debug for Cursor {
}
}
-/// A cursor visitor function.
-///
-/// The first argument is the AST node currently being visited. The second
-/// argument is the parent of the AST node currently being visited. The return
-/// value informs how traversal should proceed.
-pub type CursorVisitor<'s> = for<'a, 'b> FnMut(&'a Cursor, &'b Cursor)
- -> Enum_CXChildVisitResult + 's;
-
impl Cursor {
/// Get the Unified Symbol Resolution for this cursor's referent, if
/// available.
@@ -131,6 +123,9 @@ impl Cursor {
/// Return the number of template arguments used by this cursor's referent,
/// if the referent is either a template specialization or
/// declaration. Returns -1 otherwise.
+ ///
+ /// NOTE: This may not return `Some` for some non-fully specialized
+ /// templates, see #193 and #194.
pub fn num_template_args(&self) -> Option<u32> {
let n: c_int = unsafe { clang_Cursor_getNumTemplateArguments(self.x) };
@@ -193,7 +188,7 @@ impl Cursor {
/// Is the referent a fully specialized template specialization without any
/// remaining free template arguments?
pub fn is_fully_specialized_template(&self) -> bool {
- self.is_template() && self.num_template_args().unwrap() > 0
+ self.is_template() && self.num_template_args().unwrap_or(0) > 0
}
/// Is the referent a template specialization that still has remaining free
@@ -304,21 +299,14 @@ impl Cursor {
/// Traverse this cursor's referent and its children.
///
- /// Call the given function on each AST node traversed. See `CursorVisitor`
- /// for details on arguments passed to the function and how its return value
- /// is interpreted.
- pub fn visit<F>(&self, func: F)
- where F: for<'a, 'b> FnMut(&'a Cursor, &'b Cursor)
- -> Enum_CXChildVisitResult,
+ /// Call the given function on each AST node traversed.
+ pub fn visit<Visitor>(&self, mut visitor: Visitor)
+ where Visitor: FnMut(Cursor) -> Enum_CXChildVisitResult,
{
- let mut data: Box<CursorVisitor> = Box::new(func);
- let opt_visit =
- Some(visit_children as extern "C" fn(CXCursor,
- CXCursor,
- CXClientData)
- -> Enum_CXChildVisitResult);
unsafe {
- clang_visitChildren(self.x, opt_visit, mem::transmute(&mut data));
+ clang_visitChildren(self.x,
+ Some(visit_children::<Visitor>),
+ mem::transmute(&mut visitor));
}
}
@@ -482,17 +470,18 @@ impl Cursor {
}
}
-extern "C" fn visit_children(cur: CXCursor,
- parent: CXCursor,
- data: CXClientData)
- -> Enum_CXChildVisitResult {
- let func: &mut Box<CursorVisitor> = unsafe { mem::transmute(data) };
- (*func)(&Cursor {
- x: cur,
- },
- &Cursor {
- x: parent,
- })
+extern "C" fn visit_children<Visitor>(cur: CXCursor,
+ _parent: CXCursor,
+ data: CXClientData)
+ -> Enum_CXChildVisitResult
+ where Visitor: FnMut(Cursor) -> Enum_CXChildVisitResult,
+{
+ let func: &mut Visitor = unsafe { mem::transmute(data) };
+ let child = Cursor {
+ x: cur,
+ };
+
+ (*func)(child)
}
impl PartialEq for Cursor {
@@ -705,21 +694,6 @@ impl Type {
unsafe { clang_isFunctionTypeVariadic(self.x) != 0 }
}
- /// Given that this type is a function type, get the types of its
- /// parameters.
- pub fn arg_types(&self) -> Vec<Type> {
- unsafe {
- let num = clang_getNumArgTypes(self.x) as usize;
- let mut args = vec![];
- for i in 0..num {
- args.push(Type {
- x: clang_getArgType(self.x, i as c_uint),
- });
- }
- args
- }
- }
-
/// Given that this type is a function type, get the type of its return
/// value.
pub fn ret_type(&self) -> Option<Type> {
@@ -1172,239 +1146,13 @@ impl UnsavedFile {
}
/// Convert a cursor kind into a static string.
-pub fn kind_to_str(x: Enum_CXCursorKind) -> &'static str {
- match x {
- CXCursor_UnexposedDecl => "UnexposedDecl",
- CXCursor_StructDecl => "StructDecl",
- CXCursor_UnionDecl => "UnionDecl",
- CXCursor_ClassDecl => "ClassDecl",
- CXCursor_EnumDecl => "EnumDecl",
- CXCursor_FieldDecl => "FieldDecl",
- CXCursor_EnumConstantDecl => "EnumConstantDecl",
- CXCursor_FunctionDecl => "FunctionDecl",
- CXCursor_VarDecl => "VarDecl",
- CXCursor_ParmDecl => "ParmDecl",
- CXCursor_ObjCInterfaceDecl => "ObjCInterfaceDecl",
- CXCursor_ObjCCategoryDecl => "ObjCCategoryDecl",
- CXCursor_ObjCProtocolDecl => "ObjCProtocolDecl",
- CXCursor_ObjCPropertyDecl => "ObjCPropertyDecl",
- CXCursor_ObjCIvarDecl => "ObjCIvarDecl",
- CXCursor_ObjCInstanceMethodDecl => "ObjCInstanceMethodDecl",
- CXCursor_ObjCClassMethodDecl => "ObjCClassMethodDecl",
- CXCursor_ObjCImplementationDecl => "ObjCImplementationDecl",
- CXCursor_ObjCCategoryImplDecl => "ObjCCategoryImplDecl",
- CXCursor_TypedefDecl => "TypedefDecl",
- CXCursor_CXXMethod => "CXXMethod",
- CXCursor_Namespace => "Namespace",
- CXCursor_LinkageSpec => "LinkageSpec",
- CXCursor_Constructor => "Constructor",
- CXCursor_Destructor => "Destructor",
- CXCursor_ConversionFunction => "ConversionFunction",
- CXCursor_TemplateTypeParameter => "TemplateTypeParameter",
- CXCursor_NonTypeTemplateParameter => "NonTypeTemplateParameter",
- CXCursor_TemplateTemplateParameter => "TemplateTemplateParameter",
- CXCursor_FunctionTemplate => "FunctionTemplate",
- CXCursor_ClassTemplate => "ClassTemplate",
- CXCursor_ClassTemplatePartialSpecialization => {
- // FIXME: Ugly hack for rustfmt, should go away!
- //
- // I plan to convert this into an enum right away anyway, though.
- return "ClassTemplatePartialSpecialization";
- }
- CXCursor_NamespaceAlias => "NamespaceAlias",
- CXCursor_UsingDirective => "UsingDirective",
- CXCursor_UsingDeclaration => "UsingDeclaration",
- CXCursor_TypeAliasDecl => "TypeAliasDecl",
- CXCursor_ObjCSynthesizeDecl => "ObjCSynthesizeDecl",
- CXCursor_ObjCDynamicDecl => "ObjCDynamicDecl",
- CXCursor_CXXAccessSpecifier => "CXXAccessSpecifier",
- // CXCursor_FirstDecl => "FirstDecl",
- // CXCursor_LastDecl => "LastDecl",
- CXCursor_FirstRef => "FirstRef",
- // CXCursor_ObjCSuperClassRef => "ObjCSuperClassRef",
- CXCursor_ObjCProtocolRef => "ObjCProtocolRef",
- CXCursor_ObjCClassRef => "ObjCClassRef",
- CXCursor_TypeRef => "TypeRef",
- CXCursor_CXXBaseSpecifier => "CXXBaseSpecifier",
- CXCursor_TemplateRef => "TemplateRef",
- CXCursor_NamespaceRef => "NamespaceRef",
- CXCursor_MemberRef => "MemberRef",
- // CXCursor_LabelRef => "LabelRef",
- CXCursor_OverloadedDeclRef => "OverloadedDeclRef",
- CXCursor_VariableRef => "VariableRef",
- // CXCursor_LastRef => "LastRef",
- CXCursor_FirstInvalid => "FirstInvalid",
- // CXCursor_InvalidFile => "InvalidFile",
- CXCursor_NoDeclFound => "NoDeclFound",
- CXCursor_NotImplemented => "NotImplemented",
- CXCursor_InvalidCode => "InvalidCode",
- // CXCursor_LastInvalid => "LastInvalid",
- CXCursor_FirstExpr => "FirstExpr",
- // CXCursor_UnexposedExpr => "UnexposedExpr",
- CXCursor_DeclRefExpr => "DeclRefExpr",
- CXCursor_MemberRefExpr => "MemberRefExpr",
- CXCursor_CallExpr => "CallExpr",
- CXCursor_ObjCMessageExpr => "ObjCMessageExpr",
- CXCursor_BlockExpr => "BlockExpr",
- CXCursor_IntegerLiteral => "IntegerLiteral",
- CXCursor_FloatingLiteral => "FloatingLiteral",
- CXCursor_ImaginaryLiteral => "ImaginaryLiteral",
- CXCursor_StringLiteral => "StringLiteral",
- CXCursor_CharacterLiteral => "CharacterLiteral",
- CXCursor_ParenExpr => "ParenExpr",
- CXCursor_UnaryOperator => "UnaryOperator",
- CXCursor_ArraySubscriptExpr => "ArraySubscriptExpr",
- CXCursor_BinaryOperator => "BinaryOperator",
- CXCursor_CompoundAssignOperator => "CompoundAssignOperator",
- CXCursor_ConditionalOperator => "ConditionalOperator",
- CXCursor_CStyleCastExpr => "CStyleCastExpr",
- CXCursor_CompoundLiteralExpr => "CompoundLiteralExpr",
- CXCursor_InitListExpr => "InitListExpr",
- CXCursor_AddrLabelExpr => "AddrLabelExpr",
- CXCursor_StmtExpr => "StmtExpr",
- CXCursor_GenericSelectionExpr => "GenericSelectionExpr",
- CXCursor_GNUNullExpr => "GNUNullExpr",
- CXCursor_CXXStaticCastExpr => "CXXStaticCastExpr",
- CXCursor_CXXDynamicCastExpr => "CXXDynamicCastExpr",
- CXCursor_CXXReinterpretCastExpr => "CXXReinterpretCastExpr",
- CXCursor_CXXConstCastExpr => "CXXConstCastExpr",
- CXCursor_CXXFunctionalCastExpr => "CXXFunctionalCastExpr",
- CXCursor_CXXTypeidExpr => "CXXTypeidExpr",
- CXCursor_CXXBoolLiteralExpr => "CXXBoolLiteralExpr",
- CXCursor_CXXNullPtrLiteralExpr => "CXXNullPtrLiteralExpr",
- CXCursor_CXXThisExpr => "CXXThisExpr",
- CXCursor_CXXThrowExpr => "CXXThrowExpr",
- CXCursor_CXXNewExpr => "CXXNewExpr",
- CXCursor_CXXDeleteExpr => "CXXDeleteExpr",
- CXCursor_UnaryExpr => "UnaryExpr",
- CXCursor_ObjCStringLiteral => "ObjCStringLiteral",
- CXCursor_ObjCEncodeExpr => "ObjCEncodeExpr",
- CXCursor_ObjCSelectorExpr => "ObjCSelectorExpr",
- CXCursor_ObjCProtocolExpr => "ObjCProtocolExpr",
- CXCursor_ObjCBridgedCastExpr => "ObjCBridgedCastExpr",
- CXCursor_PackExpansionExpr => "PackExpansionExpr",
- CXCursor_SizeOfPackExpr => "SizeOfPackExpr",
- CXCursor_LambdaExpr => "LambdaExpr",
- CXCursor_ObjCBoolLiteralExpr => "ObjCBoolLiteralExpr",
- // CXCursor_LastExpr => "LastExpr",
- CXCursor_FirstStmt => "FirstStmt",
- // CXCursor_UnexposedStmt => "UnexposedStmt",
- CXCursor_LabelStmt => "LabelStmt",
- CXCursor_CompoundStmt => "CompoundStmt",
- CXCursor_CaseStmt => "CaseStmt",
- CXCursor_DefaultStmt => "DefaultStmt",
- CXCursor_IfStmt => "IfStmt",
- CXCursor_SwitchStmt => "SwitchStmt",
- CXCursor_WhileStmt => "WhileStmt",
- CXCursor_DoStmt => "DoStmt",
- CXCursor_ForStmt => "ForStmt",
- CXCursor_GotoStmt => "GotoStmt",
- CXCursor_IndirectGotoStmt => "IndirectGotoStmt",
- CXCursor_ContinueStmt => "ContinueStmt",
- CXCursor_BreakStmt => "BreakStmt",
- CXCursor_ReturnStmt => "ReturnStmt",
- CXCursor_AsmStmt => "AsmStmt",
- CXCursor_ObjCAtTryStmt => "ObjCAtTryStmt",
- CXCursor_ObjCAtCatchStmt => "ObjCAtCatchStmt",
- CXCursor_ObjCAtFinallyStmt => "ObjCAtFinallyStmt",
- CXCursor_ObjCAtThrowStmt => "ObjCAtThrowStmt",
- CXCursor_ObjCAtSynchronizedStmt => "ObjCAtSynchronizedStmt",
- CXCursor_ObjCAutoreleasePoolStmt => "ObjCAutoreleasePoolStmt",
- CXCursor_ObjCForCollectionStmt => "ObjCForCollectionStmt",
- CXCursor_CXXCatchStmt => "CXXCatchStmt",
- CXCursor_CXXTryStmt => "CXXTryStmt",
- CXCursor_CXXForRangeStmt => "CXXForRangeStmt",
- CXCursor_SEHTryStmt => "SEHTryStmt",
- CXCursor_SEHExceptStmt => "SEHExceptStmt",
- CXCursor_SEHFinallyStmt => "SEHFinallyStmt",
- CXCursor_NullStmt => "NullStmt",
- CXCursor_DeclStmt => "DeclStmt",
- // CXCursor_LastStmt => "LastStmt",
- CXCursor_TranslationUnit => "TranslationUnit",
- CXCursor_FirstAttr => "FirstAttr",
- // CXCursor_UnexposedAttr => "UnexposedAttr",
- CXCursor_IBActionAttr => "IBActionAttr",
- CXCursor_IBOutletAttr => "IBOutletAttr",
- CXCursor_IBOutletCollectionAttr => "IBOutletCollectionAttr",
- CXCursor_CXXFinalAttr => "CXXFinalAttr",
- CXCursor_CXXOverrideAttr => "CXXOverrideAttr",
- CXCursor_AnnotateAttr => "AnnotateAttr",
- CXCursor_AsmLabelAttr => "AsmLabelAttr",
- // CXCursor_LastAttr => "LastAttr",
- CXCursor_PreprocessingDirective => "PreprocessingDirective",
- CXCursor_MacroDefinition => "MacroDefinition",
- CXCursor_MacroExpansion => "MacroExpansion",
- // CXCursor_MacroInstantiation => "MacroInstantiation",
- CXCursor_InclusionDirective => "InclusionDirective",
- // CXCursor_FirstPreprocessing => "FirstPreprocessing",
- // CXCursor_LastPreprocessing => "LastPreprocessing",
- CXCursor_PackedAttr => "PackedAttr",
- CXCursor_ModuleImportDecl => "ModuleImportDecl",
- CXCursor_TypeAliasTemplateDecl => "TypeAliasTemplateDecl",
- CXCursor_StaticAssert => "StaticAssert",
- _ => "?",
- }
+pub fn kind_to_str(x: Enum_CXCursorKind) -> String {
+ unsafe { clang_getCursorKindSpelling(x) }.into()
}
/// Convert a type kind to a static string.
-pub fn type_to_str(x: Enum_CXTypeKind) -> &'static str {
- match x {
- CXType_Invalid => "Invalid",
- CXType_Unexposed => "Unexposed",
- CXType_Void => "Void",
- CXType_Bool => "Bool",
- CXType_Char_U => "Char_U",
- CXType_UChar => "UChar",
- CXType_Char16 => "Char16",
- CXType_Char32 => "Char32",
- CXType_UShort => "UShort",
- CXType_UInt => "UInt",
- CXType_ULong => "ULong",
- CXType_ULongLong => "ULongLong",
- CXType_UInt128 => "UInt128",
- CXType_Char_S => "Char_S",
- CXType_SChar => "SChar",
- CXType_WChar => "WChar",
- CXType_Short => "Short",
- CXType_Int => "Int",
- CXType_Long => "Long",
- CXType_LongLong => "LongLong",
- CXType_Int128 => "Int128",
- CXType_Float => "Float",
- CXType_Double => "Double",
- CXType_LongDouble => "LongDouble",
- CXType_NullPtr => "NullPtr",
- CXType_Overload => "Overload",
- CXType_Dependent => "Dependent",
- CXType_ObjCId => "ObjCId",
- CXType_ObjCClass => "ObjCClass",
- CXType_ObjCSel => "ObjCSel",
- // CXType_FirstBuiltin => "FirstBuiltin",
- // CXType_LastBuiltin => "LastBuiltin",
- CXType_Complex => "Complex",
- CXType_Pointer => "Pointer",
- CXType_BlockPointer => "BlockPointer",
- CXType_LValueReference => "LValueReference",
- CXType_RValueReference => "RValueReference",
- CXType_Record => "Record",
- CXType_Enum => "Enum",
- CXType_Typedef => "Typedef",
- CXType_ObjCInterface => "ObjCInterface",
- CXType_ObjCObjectPointer => "ObjCObjectPointer",
- CXType_FunctionNoProto => "FunctionNoProto",
- CXType_FunctionProto => "FunctionProto",
- CXType_ConstantArray => "ConstantArray",
- CXType_Vector => "Vector",
- CXType_IncompleteArray => "IncompleteArray",
- CXType_VariableArray => "VariableArray",
- CXType_DependentSizedArray => "DependentSizedArray",
- CXType_MemberPointer => "MemberPointer",
- #[cfg(not(feature="llvm_stable"))]
- CXType_Auto => "Auto",
- #[cfg(not(feature="llvm_stable"))]
- CXType_Elaborated => "Elaborated",
- _ => "?",
- }
+pub fn type_to_str(x: Enum_CXTypeKind) -> String {
+ unsafe { clang_getTypeKindSpelling(x).into() }
}
/// Dump the Clang AST to stdout for debugging purposes.
@@ -1423,7 +1171,7 @@ pub fn ast_dump(c: &Cursor, depth: isize) -> Enum_CXVisitorResult {
kind_to_str(c.kind()),
c.spelling(),
type_to_str(ct)));
- c.visit(|s, _: &Cursor| ast_dump(s, depth + 1));
+ c.visit(|s| ast_dump(&s, depth + 1));
print_indent(depth, ")");
CXChildVisit_Continue
}
diff --git a/src/ir/comp.rs b/src/ir/comp.rs
index 91be2eca..41e5c3d3 100644
--- a/src/ir/comp.rs
+++ b/src/ir/comp.rs
@@ -516,7 +516,7 @@ impl CompInfo {
.and_then(|c| Item::parse(c, None, ctx).ok());
let mut maybe_anonymous_struct_field = None;
- cursor.visit(|cur, _other| {
+ cursor.visit(|cur| {
if cur.kind() != CXCursor_FieldDecl {
if let Some((ty, _)) = maybe_anonymous_struct_field {
let field = Field::new(None, ty, None, None, None, false);
@@ -530,7 +530,7 @@ impl CompInfo {
match maybe_anonymous_struct_field.take() {
Some((ty, clang_ty)) => {
let mut used = false;
- cur.visit(|child, _| {
+ cur.visit(|child| {
if child.cur_type() == clang_ty {
used = true;
}
@@ -551,12 +551,12 @@ impl CompInfo {
let bit_width = cur.bit_width();
let field_type = Item::from_ty_or_ref(cur.cur_type(),
- Some(*cur),
+ Some(cur),
Some(potential_id),
ctx);
let comment = cur.raw_comment();
- let annotations = Annotations::new(cur);
+ let annotations = Annotations::new(&cur);
let name = cur.spelling();
let is_mutable = cursor.is_mutable_field();
@@ -576,7 +576,7 @@ impl CompInfo {
ci.fields.push(field);
// No we look for things like attributes and stuff.
- cur.visit(|cur, _| {
+ cur.visit(|cur| {
if cur.kind() == CXCursor_UnexposedAttr {
ci.found_unknown_attr = true;
}
@@ -594,7 +594,7 @@ impl CompInfo {
CXCursor_UnionDecl |
CXCursor_ClassTemplate |
CXCursor_ClassDecl => {
- let inner = Item::parse(*cur, Some(potential_id), ctx)
+ let inner = Item::parse(cur, Some(potential_id), ctx)
.expect("Inner ClassDecl");
if !ci.inner_types.contains(&inner) {
ci.inner_types.push(inner);
@@ -620,7 +620,7 @@ impl CompInfo {
}
let default_type = Item::from_ty(&cur.cur_type(),
- Some(*cur),
+ Some(cur),
Some(potential_id),
ctx)
.ok();
@@ -688,7 +688,7 @@ impl CompInfo {
// NB: This gets us an owned `Function`, not a
// `FunctionSig`.
let method_signature =
- Item::parse(*cur, Some(potential_id), ctx)
+ Item::parse(cur, Some(potential_id), ctx)
.expect("CXXMethod");
let is_const = cur.method_is_const();
@@ -727,7 +727,7 @@ impl CompInfo {
return CXChildVisit_Continue;
}
- let item = Item::parse(*cur, Some(potential_id), ctx)
+ let item = Item::parse(cur, Some(potential_id), ctx)
.expect("VarDecl");
ci.inner_vars.push(item);
}
diff --git a/src/ir/context.rs b/src/ir/context.rs
index 6c56eefe..c2214340 100644
--- a/src/ir/context.rs
+++ b/src/ir/context.rs
@@ -384,8 +384,10 @@ impl<'ctx> BindgenContext<'ctx> {
// because we remove it before the end of this function.
self.gen_ctx = Some(unsafe { mem::transmute(&ctx) });
- self.resolve_typerefs();
- self.process_replacements();
+ if !self.collected_typerefs() {
+ self.resolve_typerefs();
+ self.process_replacements();
+ }
let ret = cb(self);
self.gen_ctx = None;
@@ -490,14 +492,14 @@ impl<'ctx> BindgenContext<'ctx> {
use clangll::*;
let mut args = vec![];
let mut found_invalid_template_ref = false;
- location.visit(|c, _| {
+ location.visit(|c| {
if c.kind() == CXCursor_TemplateRef &&
c.cur_type().kind() == CXType_Invalid {
found_invalid_template_ref = true;
}
if c.kind() == CXCursor_TypeRef {
let new_ty = Item::from_ty_or_ref(c.cur_type(),
- Some(*c),
+ Some(c),
Some(with_id),
self);
args.push(new_ty);
diff --git a/src/ir/enum_ty.rs b/src/ir/enum_ty.rs
index fd7dbb45..e78184e7 100644
--- a/src/ir/enum_ty.rs
+++ b/src/ir/enum_ty.rs
@@ -70,7 +70,7 @@ impl Enum {
None => true,
};
- declaration.visit(|cursor, _| {
+ declaration.visit(|cursor| {
if cursor.kind() == CXCursor_EnumConstantDecl {
let name = cursor.spelling();
let comment = cursor.raw_comment();
diff --git a/src/ir/function.rs b/src/ir/function.rs
index c2e6ffd0..163e3039 100644
--- a/src/ir/function.rs
+++ b/src/ir/function.rs
@@ -167,10 +167,10 @@ impl FunctionSig {
// For non-CXCursor_FunctionDecl, visiting the cursor's children
// is the only reliable way to get parameter names.
let mut args = vec![];
- cursor.visit(|c, _| {
+ cursor.visit(|c| {
if c.kind() == CXCursor_ParmDecl {
let ty =
- Item::from_ty(&c.cur_type(), Some(*c), None, ctx)
+ Item::from_ty(&c.cur_type(), Some(c), None, ctx)
.expect("ParmDecl?");
let name = c.spelling();
let name =
diff --git a/src/ir/item.rs b/src/ir/item.rs
index 4e893e35..0c12b9a9 100644
--- a/src/ir/item.rs
+++ b/src/ir/item.rs
@@ -38,13 +38,44 @@ pub trait ItemCanonicalName {
/// }
/// ```
///
-/// For bar, the canonical path is `foo::BAR`, while the canonical name is just
-/// `BAR`.
+/// For bar, the canonical path is `vec!["foo", "BAR"]`, while the canonical
+/// name is just `"BAR"`.
pub trait ItemCanonicalPath {
/// Get the canonical path for this item.
fn canonical_path(&self, ctx: &BindgenContext) -> Vec<String>;
}
+/// A trait for iterating over an item and its parents and up its ancestor chain
+/// up to (but not including) the implicit root module.
+pub trait ItemAncestors {
+ /// Get an iterable over this item's ancestors.
+ fn ancestors<'a, 'b>(&self, ctx: &'a BindgenContext<'b>) -> ItemAncestorsIter<'a, 'b>;
+}
+
+/// An iterator over an item and its ancestors.
+pub struct ItemAncestorsIter<'a, 'b>
+ where 'b: 'a,
+{
+ item: ItemId,
+ ctx: &'a BindgenContext<'b>,
+}
+
+impl<'a, 'b> Iterator for ItemAncestorsIter<'a, 'b>
+ where 'b: 'a,
+{
+ type Item = ItemId;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ let item = self.ctx.resolve_item(self.item);
+ if item.parent_id() == self.item {
+ None
+ } else {
+ self.item = item.parent_id();
+ Some(item.id())
+ }
+ }
+}
+
/// A single identifier for an item.
///
/// TODO: Build stronger abstractions on top of this, like TypeId(ItemId)?
@@ -76,6 +107,25 @@ impl ItemCanonicalPath for ItemId {
}
}
+impl ItemAncestors for ItemId {
+ fn ancestors<'a, 'b>(&self,
+ ctx: &'a BindgenContext<'b>)
+ -> ItemAncestorsIter<'a, 'b> {
+ ItemAncestorsIter {
+ item: *self,
+ ctx: ctx,
+ }
+ }
+}
+
+impl ItemAncestors for Item {
+ fn ancestors<'a, 'b>(&self,
+ ctx: &'a BindgenContext<'b>)
+ -> ItemAncestorsIter<'a, 'b> {
+ self.id().ancestors(ctx)
+ }
+}
+
impl TypeCollector for ItemId {
type Extra = ();
@@ -966,11 +1016,11 @@ impl ClangItemParser for Item {
assert_eq!(popped_decl, declaration_to_look_for);
}
- location.visit(|cur, _other| {
+ location.visit(|cur| {
use clangll::*;
result = Item::from_ty_with_id(id,
ty,
- Some(*cur),
+ Some(cur),
parent_id,
ctx);
match result {
diff --git a/src/ir/module.rs b/src/ir/module.rs
index c582d3eb..42175b92 100644
--- a/src/ir/module.rs
+++ b/src/ir/module.rs
@@ -49,8 +49,8 @@ impl ClangSubItemParser for Module {
CXCursor_Namespace => {
let module_id = ctx.module(cursor);
ctx.with_module(module_id, |ctx, children| {
- cursor.visit(|cursor, _| {
- parse_one(ctx, *cursor, Some(module_id), children)
+ cursor.visit(|cursor| {
+ parse_one(ctx, cursor, Some(module_id), children)
})
});
diff --git a/src/ir/ty.rs b/src/ir/ty.rs
index 4d26cdff..81212029 100644
--- a/src/ir/ty.rs
+++ b/src/ir/ty.rs
@@ -547,14 +547,14 @@ impl Type {
let mut inner = Err(ParseError::Continue);
let mut args = vec![];
- location.visit(|cur, _| {
+ 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(cur),
Some(potential_id),
ctx);
}
@@ -567,7 +567,7 @@ impl Type {
let default_type =
Item::from_ty(&cur.cur_type(),
- Some(*cur),
+ Some(cur),
Some(potential_id),
ctx)
.ok();
diff --git a/src/ir/var.rs b/src/ir/var.rs
index 216d8185..33e56242 100644
--- a/src/ir/var.rs
+++ b/src/ir/var.rs
@@ -203,7 +203,7 @@ fn get_integer_literal_from_cursor(cursor: &clang::Cursor,
-> Option<i64> {
use clangll::*;
let mut value = None;
- cursor.visit(|c, _| {
+ cursor.visit(|c| {
match c.kind() {
CXCursor_IntegerLiteral |
CXCursor_UnaryOperator => {
diff --git a/src/lib.rs b/src/lib.rs
index 1592c275..92443ac8 100755
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -60,6 +60,7 @@ mod clang;
mod ir;
mod parse;
mod regex_set;
+mod uses;
#[cfg(rustfmt)]
mod codegen;
@@ -68,16 +69,17 @@ doc_mod!(clang, clang_docs);
doc_mod!(ir, ir_docs);
doc_mod!(parse, parse_docs);
doc_mod!(regex_set, regex_set_docs);
+doc_mod!(uses, uses_docs);
mod codegen {
include!(concat!(env!("OUT_DIR"), "/codegen.rs"));
}
-
use ir::context::BindgenContext;
use ir::item::{Item, ItemId};
use parse::{ClangItemParser, ParseError};
use regex_set::RegexSet;
+
use std::borrow::Borrow;
use std::collections::HashSet;
use std::fs::OpenOptions;
@@ -118,10 +120,19 @@ pub fn builder() -> Builder {
impl Builder {
/// Set the input C/C++ header.
- pub fn header<T: Into<String>>(self, header: T) -> Builder {
+ pub fn header<T: Into<String>>(mut self, header: T) -> Builder {
+ let header = header.into();
+ self.options.input_header = Some(header.clone());
self.clang_arg(header)
}
+ /// Generate a C/C++ file that includes the header and has dummy uses of
+ /// every type defined in the header.
+ pub fn dummy_uses<T: Into<String>>(mut self, dummy_uses: T) -> Builder {
+ self.options.dummy_uses = Some(dummy_uses.into());
+ self
+ }
+
/// Hide the given type from the generated bindings.
pub fn hide_type<T: Into<String>>(mut self, arg: T) -> Builder {
self.options.hidden_types.insert(arg.into());
@@ -200,7 +211,7 @@ impl Builder {
}
/// Generate the Rust bindings using the options built up thus far.
- pub fn generate(self) -> Result<Bindings, ()> {
+ pub fn generate<'ctx>(self) -> Result<Bindings<'ctx>, ()> {
Bindings::generate(self.options, None)
}
}
@@ -274,6 +285,13 @@ pub struct BindgenOptions {
/// The set of arguments to pass straight through to Clang.
pub clang_args: Vec<String>,
+
+ /// The input header file.
+ pub input_header: Option<String>,
+
+ /// Generate a dummy C/C++ file that includes the header and has dummy uses
+ /// of all types defined therein. See the `uses` module for more.
+ pub dummy_uses: Option<String>,
}
impl Default for BindgenOptions {
@@ -296,6 +314,8 @@ impl Default for BindgenOptions {
msvc_mangling: false,
raw_lines: vec![],
clang_args: vec![],
+ input_header: None,
+ dummy_uses: None,
}
}
}
@@ -314,20 +334,20 @@ pub enum LinkType {
}
/// Generated Rust bindings.
-#[derive(Debug, Clone)]
-pub struct Bindings {
+#[derive(Debug)]
+pub struct Bindings<'ctx> {
+ context: BindgenContext<'ctx>,
module: ast::Mod,
- raw_lines: Vec<String>,
}
-impl Bindings {
+impl<'ctx> Bindings<'ctx> {
/// Generate bindings for the given options.
///
/// Deprecated - use a `Builder` instead
#[deprecated]
pub fn generate(options: BindgenOptions,
span: Option<Span>)
- -> Result<Bindings, ()> {
+ -> Result<Bindings<'ctx>, ()> {
let span = span.unwrap_or(DUMMY_SP);
let mut context = BindgenContext::new(options);
@@ -339,8 +359,8 @@ impl Bindings {
};
Ok(Bindings {
+ context: context,
module: module,
- raw_lines: context.options().raw_lines.clone(),
})
}
@@ -376,11 +396,11 @@ impl Bindings {
try!(writer.write("/* automatically generated by rust-bindgen */\n\n"
.as_bytes()));
- for line in self.raw_lines.iter() {
+ for line in self.context.options().raw_lines.iter() {
try!(writer.write(line.as_bytes()));
try!(writer.write("\n".as_bytes()));
}
- if !self.raw_lines.is_empty() {
+ if !self.context.options().raw_lines.is_empty() {
try!(writer.write("\n".as_bytes()));
}
@@ -390,6 +410,29 @@ impl Bindings {
try!(eof(&mut ps.s));
ps.s.out.flush()
}
+
+ /// Generate and write dummy uses of all the types we parsed, if we've been
+ /// requested to do so in the options.
+ ///
+ /// See the `uses` module for more information.
+ pub fn write_dummy_uses(&mut self) -> io::Result<()> {
+ let file =
+ if let Some(ref dummy_path) = self.context.options().dummy_uses {
+ Some(try!(OpenOptions::new()
+ .write(true)
+ .truncate(true)
+ .create(true)
+ .open(dummy_path)))
+ } else {
+ None
+ };
+
+ if let Some(file) = file {
+ try!(uses::generate_dummy_uses(&mut self.context, file));
+ }
+
+ Ok(())
+ }
}
/// Determines whether the given cursor is in any of the files matched by the
@@ -418,7 +461,7 @@ pub fn parse_one(ctx: &mut BindgenContext,
Ok(id) => children.push(id),
Err(ParseError::Continue) => {}
Err(ParseError::Recurse) => {
- cursor.visit(|child, _| parse_one(ctx, *child, parent, children));
+ cursor.visit(|child| parse_one(ctx, child, parent, children));
}
}
CXChildVisit_Continue
@@ -437,12 +480,12 @@ fn parse(context: &mut BindgenContext) {
let cursor = context.translation_unit().cursor();
if context.options().emit_ast {
- cursor.visit(|cur, _| clang::ast_dump(cur, 0));
+ cursor.visit(|cur| clang::ast_dump(&cur, 0));
}
let root = context.root_module();
context.with_module(root, |context, children| {
- cursor.visit(|cursor, _| parse_one(context, *cursor, None, children))
+ cursor.visit(|cursor| parse_one(context, cursor, None, children))
});
assert!(context.current_module() == context.root_module(),
diff --git a/src/uses.rs b/src/uses.rs
new file mode 100644
index 00000000..47f72da6
--- /dev/null
+++ b/src/uses.rs
@@ -0,0 +1,102 @@
+//! Take in our IR and output a C/C++ file with dummy uses of each IR type.
+//!
+//! Say that we had this C++ header, `header.hpp`:
+//!
+//! ```c++
+//! class Point {
+//! int x;
+//! int y;
+//! }
+//!
+//! enum Bar {
+//! THIS,
+//! THAT,
+//! OTHER
+//! }
+//! ```
+//!
+//! If we generated dummy uses for this header, we would get a `.cpp` file like
+//! this:
+//!
+//! ```c++
+//! #include "header.hpp"
+//!
+//! void dummy(Point*) {}
+//! void dummy(Bar*) {}
+//! ```
+//!
+//! This is useful because we can compile this `.cpp` file into an object file,
+//! and then compare its debugging information to the debugging information
+//! generated for our Rust bindings. These two sets of debugging information had
+//! better agree on the C/C++ types' physical layout, or else our bindings are
+//! incorrect!
+//!
+//! "But you still haven't explained why we have to generate the dummy uses" you
+//! complain. Well if the types are never used, then they are elided when the
+//! C/C++ compiler generates debugging information.
+
+use ir::context::BindgenContext;
+use ir::item::{Item, ItemAncestors, ItemCanonicalName};
+use std::io;
+
+// Like `canonical_path`, except we always take namespaces into account, ignore
+// the generated names of anonymous items, and return a `String`.
+//
+// TODO: Would it be easier to try and demangle the USR?
+fn namespaced_name(ctx: &BindgenContext, item: &Item) -> String {
+ let mut names: Vec<_> = item.ancestors(ctx)
+ .map(|id| ctx.resolve_item(id).canonical_name(ctx))
+ .filter(|name| !name.starts_with("_bindgen_"))
+ .collect();
+ names.reverse();
+ names.join("::")
+}
+
+/// Generate the dummy uses for all the items in the given context, and write
+/// the dummy uses to `dest`.
+pub fn generate_dummy_uses<W>(ctx: &mut BindgenContext,
+ mut dest: W)
+ -> io::Result<()>
+ where W: io::Write,
+{
+ ctx.gen(|ctx| {
+ let input_header = ctx.options()
+ .input_header
+ .as_ref()
+ .expect("Should not generate dummy uses without an input header");
+
+ try!(writeln!(dest, "/* automatically generated by rust-bindgen */"));
+ try!(writeln!(dest, ""));
+ try!(writeln!(dest, "#include \"{}\"", input_header));
+ try!(writeln!(dest, ""));
+
+ let type_items = ctx.whitelisted_items()
+ .map(|id| ctx.resolve_item(id))
+ .filter(|item| {
+ // We only want type items.
+ if let Some(ty) = item.kind().as_type() {
+ // However, we don't want anonymous types, as we can't
+ // generate dummy uses for them.
+ ty.name().is_some() &&
+ // Nor do we want builtin types or named template type
+ // arguments. Again, we can't generate dummy uses for
+ // these.
+ !ty.is_builtin_or_named() &&
+ // And finally, we won't be creating any dummy
+ // specializations, so ignore template declarations and
+ // partial specializations.
+ item.applicable_template_args(ctx).is_empty()
+ } else {
+ false
+ }
+ })
+ .map(|item| namespaced_name(ctx, item))
+ .enumerate();
+
+ for (idx, name) in type_items {
+ try!(writeln!(dest, "void dummy{}({}*) {{ }}", idx, name));
+ }
+
+ Ok(())
+ })
+}
diff --git a/tests/expectations/base-to-derived.rs b/tests/expectations/base-to-derived.rs
new file mode 100644
index 00000000..c2af2c43
--- /dev/null
+++ b/tests/expectations/base-to-derived.rs
@@ -0,0 +1,19 @@
+/* automatically generated by rust-bindgen */
+
+
+#![allow(non_snake_case)]
+
+
+#[repr(C)]
+#[derive(Debug, Copy)]
+pub struct false_type {
+ pub _address: u8,
+}
+#[test]
+fn bindgen_test_layout_false_type() {
+ assert_eq!(::std::mem::size_of::<false_type>() , 1usize);
+ assert_eq!(::std::mem::align_of::<false_type>() , 1usize);
+}
+impl Clone for false_type {
+ fn clone(&self) -> Self { *self }
+}
diff --git a/tests/headers/base-to-derived.hpp b/tests/headers/base-to-derived.hpp
new file mode 100644
index 00000000..ea31e0f2
--- /dev/null
+++ b/tests/headers/base-to-derived.hpp
@@ -0,0 +1,19 @@
+// bindgen-flags: -- -std=c++11
+
+struct false_type {};
+
+template<typename _From, typename _To, bool>
+struct __is_base_to_derived_ref;
+
+template<typename _From, typename _To>
+struct __is_base_to_derived_ref<_From, _To, true>
+{
+ typedef _To type;
+
+ static constexpr bool value = type::value;
+};
+
+template<typename _From, typename _To>
+struct __is_base_to_derived_ref<_From, _To, false>
+: public false_type
+{ };
diff --git a/tests/tests.rs b/tests/tests.rs
index addaa5ad..4954ac6f 100644
--- a/tests/tests.rs
+++ b/tests/tests.rs
@@ -12,10 +12,13 @@ use std::process;
const TEST_BATCH_DEFAULT_SIZE: usize = 16;
-fn spawn_run_bindgen<P, Q, R>(run_bindgen: P, bindgen: Q, header: R) -> process::Child
+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>
+ R: AsRef<Path>,
{
let run_bindgen = run_bindgen.as_ref();
let bindgen = bindgen.as_ref();
@@ -36,19 +39,34 @@ fn spawn_run_bindgen<P, Q, R>(run_bindgen: P, bindgen: Q, header: R) -> process:
expected.push(file_name);
expected.set_extension("rs");
- let mut cmd = process::Command::new(run_bindgen);
- cmd.stdout(process::Stdio::piped())
+ // And the same style conversion as above, but for the dummy uses. We assume
+ // that .hpp means we should generate a .cpp uses file, and .h means we
+ // should generate a .c file.
+
+ let mut dummy_uses = PathBuf::from(header);
+ let file_name = dummy_uses.file_name()
+ .expect("Should still have filename")
+ .to_os_string();
+ dummy_uses.pop();
+ dummy_uses.pop();
+ dummy_uses.push("uses");
+ dummy_uses.push(file_name);
+ dummy_uses.set_extension(if header.extension().and_then(|s| s.to_str()) ==
+ Some("hpp") {
+ "cpp"
+ } else {
+ "c"
+ });
+
+ process::Command::new(run_bindgen)
+ .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()
+ .arg(expected)
+ .arg("--dummy-uses")
+ .arg(dummy_uses)
+ .spawn()
.expect("Should be able to spawn run-bindgen.py child process")
}
@@ -84,12 +102,12 @@ fn run_bindgen_tests() {
.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,
- }
- }).collect::<Vec<_>>();
+ match entry.path().extension().and_then(|s| s.to_str()) {
+ Some("h") | Some("hpp") => true,
+ _ => false,
+ }
+ })
+ .collect::<Vec<_>>();
let batch_size = env::var("BINDGEN_TEST_BATCH_SIZE")
.ok()
@@ -101,22 +119,26 @@ fn run_bindgen_tests() {
// consumed when testing, so that we don't overload the system.
let children = tests.chunks(batch_size).map(|x| {
- x.iter().map(|entry| {
- let child = spawn_run_bindgen(run_bindgen.clone(), bindgen.clone(), entry.path());
- (entry.path(), child)
- }).collect::<Vec<_>>()
+ x.iter()
+ .map(|entry| {
+ let child = spawn_run_bindgen(run_bindgen.clone(),
+ bindgen.clone(),
+ entry.path());
+ (entry.path(), child)
+ })
+ .collect::<Vec<_>>()
});
let failures: Vec<_> = children.flat_map(|x| {
- x.into_iter().filter_map(|(path, mut child)| {
- let passed = child.wait()
- .expect("Should wait on child process")
- .success();
+ x.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)) }
+ if passed { None } else { Some((path, child)) }
+ })
})
- })
- .collect();
+ .collect();
let num_failures = failures.len();
diff --git a/tests/tools/run-bindgen.py b/tests/tools/run-bindgen.py
index bc8b567b..1f5f504e 100755
--- a/tests/tools/run-bindgen.py
+++ b/tests/tools/run-bindgen.py
@@ -41,6 +41,10 @@ def make_parser():
nargs=1,
help="Run tests that depend on bindgen being built with \
the given feature.")
+ parser.add_argument("--dummy-uses",
+ dest="dummy_uses",
+ help="The path to generate dummy C/C++ uses of the \
+ whitelisted types from the input header at.")
return parser
def usage_and_exit(*args):
@@ -117,9 +121,11 @@ def run_cmd(command, **kwargs):
print("run-bindgen.py: running", command)
subprocess.check_call(command, **kwargs)
-def generate_bindings(bindgen, flags, header, output):
+def generate_bindings(bindgen, dummy_uses, flags, header, output):
"""Generate the rust bindings."""
command = [bindgen, "-o", output]
+ if dummy_uses:
+ command.extend(["--dummy-uses", dummy_uses])
command.extend(flags)
command.append(header)
run_cmd(command, cwd=os.getcwd(), env=make_bindgen_env())
@@ -166,7 +172,11 @@ def main():
test_flags = get_bindgen_flags(args.header)
expected_bindings = get_expected_bindings(args.rust_bindings)
- generate_bindings(args.bindgen, test_flags, args.header, args.rust_bindings)
+ generate_bindings(args.bindgen,
+ args.dummy_uses,
+ 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)
diff --git a/tests/uses/.gitignore b/tests/uses/.gitignore
new file mode 100644
index 00000000..40d7cb4c
--- /dev/null
+++ b/tests/uses/.gitignore
@@ -0,0 +1,2 @@
+*.c
+*.cpp