diff options
-rwxr-xr-x | src/bin/bindgen.rs | 20 | ||||
-rwxr-xr-x | src/clang.rs | 306 | ||||
-rw-r--r-- | src/ir/comp.rs | 18 | ||||
-rw-r--r-- | src/ir/context.rs | 10 | ||||
-rw-r--r-- | src/ir/enum_ty.rs | 2 | ||||
-rw-r--r-- | src/ir/function.rs | 4 | ||||
-rw-r--r-- | src/ir/item.rs | 58 | ||||
-rw-r--r-- | src/ir/module.rs | 4 | ||||
-rw-r--r-- | src/ir/ty.rs | 6 | ||||
-rw-r--r-- | src/ir/var.rs | 2 | ||||
-rwxr-xr-x | src/lib.rs | 71 | ||||
-rw-r--r-- | src/uses.rs | 102 | ||||
-rw-r--r-- | tests/expectations/base-to-derived.rs | 19 | ||||
-rw-r--r-- | tests/headers/base-to-derived.hpp | 19 | ||||
-rw-r--r-- | tests/tests.rs | 80 | ||||
-rwxr-xr-x | tests/tools/run-bindgen.py | 14 | ||||
-rw-r--r-- | tests/uses/.gitignore | 2 |
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 => { @@ -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 |