diff options
29 files changed, 741 insertions, 368 deletions
diff --git a/.travis.yml b/.travis.yml index 83dac23d..dae798ac 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,13 @@ language: rust - +env: + - LLVM_VERSION=3.4 + - LLVM_VERSION=3.5 before_install: - - yes | sudo add-apt-repository "deb http://llvm.org/apt/precise/ llvm-toolchain-precise-3.4 main" + - yes | sudo add-apt-repository "deb http://llvm.org/apt/precise/ llvm-toolchain-precise-${LLVM_VERSION} main" - yes | sudo add-apt-repository "deb http://ppa.launchpad.net/ubuntu-toolchain-r/test/ubuntu precise main" - sudo apt-get update install: - - sudo apt-get install --force-yes libclang-3.4-dev + - sudo apt-get install --force-yes libclang-${LLVM_VERSION}-dev script: - - LIBCLANG_PATH=/usr/lib/llvm-3.4/lib cargo test --verbose --jobs 1 + - export LIBCLANG_PATH=/usr/lib/llvm-${LLVM_VERSION}/lib + - cargo build --verbose @@ -17,3 +17,6 @@ plugin = true name = "bindgen" doc = false + +[[test]] +name = "tests" @@ -2,24 +2,24 @@ use std::os; use std::io::fs::PathExtensions; fn main() { - let clang_dir = if let Some(dir) = os::getenv("LIBCLANG_PATH") { - dir - } else if cfg!(any(target_os = "linux", target_os = "freebsd")) { - "/usr/lib".to_string() - } else if cfg!(target_os = "macos") { - "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib".to_string() - } else { - panic!("Platform not supported"); - }; + let clang_dir = if let Some(dir) = os::getenv("LIBCLANG_PATH") { + dir + } else if cfg!(any(target_os = "linux", target_os = "freebsd")) { + "/usr/lib".to_string() + } else if cfg!(target_os = "macos") { + "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib".to_string() + } else { + panic!("Platform not supported"); + }; - let clang_dir = Path::new(clang_dir); + let clang_dir = Path::new(clang_dir); - let clang_lib = os::dll_filename("clang"); + let clang_lib = os::dll_filename("clang"); - let clang_path = clang_dir.join(clang_lib.clone()); - if !clang_path.exists() { - panic!("Unable to find {}", clang_lib); - } + let clang_path = clang_dir.join(clang_lib.clone()); + if !clang_path.exists() { + panic!("Unable to find {}", clang_lib); + } - println!("cargo:rustc-flags=-l clang -L {}", clang_dir.as_str().unwrap()); -}
\ No newline at end of file + println!("cargo:rustc-flags=-l clang -L {}", clang_dir.as_str().unwrap()); +} @@ -211,10 +211,11 @@ pub fn gen_mod(links: &[(String, LinkType)], globs: Vec<Global>, span: Span) -> GFunc(vi) => { let v = vi.borrow(); match v.ty { - TFunc(ref rty, ref aty, var, abi) => { + TFuncPtr(ref sig) => { let decl = cfunc_to_rs(&mut ctx, v.name.clone(), - &**rty, aty.as_slice(), var); - (abi, decl) + &*sig.ret_ty, sig.args.as_slice(), + sig.is_variadic); + (sig.abi, decl) } _ => unreachable!() } @@ -237,7 +238,9 @@ pub fn gen_mod(links: &[(String, LinkType)], globs: Vec<Global>, span: Span) -> map }; - defs.push(mk_extern(&mut ctx, links, vars, abi::C)); + if !Vec::is_empty(&vars) { + defs.push(mk_extern(&mut ctx, links, vars, abi::C)); + } for (abi, funcs) in funcs.into_iter() { defs.push(mk_extern(&mut ctx, links, funcs, abi)); @@ -465,8 +468,6 @@ fn comp_to_rs(ctx: &mut GenCtx, kind: CompKind, name: String, } fn cstruct_to_rs(ctx: &mut GenCtx, name: String, members: Vec<CompMember>) -> Vec<P<ast::Item>> { - let mut unnamed: uint = 0; - let mut fields = vec!(); let mut methods = vec!(); // Nested composites may need to emit declarations and implementations as @@ -491,7 +492,7 @@ fn cstruct_to_rs(ctx: &mut GenCtx, name: String, members: Vec<CompMember>) -> Ve } else { rust_type_id(ctx, f.name.clone()) }; - + let f_ty = P(cty_to_rs(ctx, &f.ty)); fields.push(respan(ctx.span, ast::StructField_ { @@ -928,9 +929,13 @@ fn cty_to_rs(ctx: &mut GenCtx, ty: &Type) -> ast::Ty { let ty = cty_to_rs(ctx, &**t); mk_arrty(ctx, &ty, s) }, - &TFunc(ref rty, ref atys, var, abi) => { - let decl = cfuncty_to_rs(ctx, &**rty, atys.as_slice(), var); - mk_fnty(ctx, &decl, abi) + &TFuncPtr(ref sig) => { + let decl = cfuncty_to_rs(ctx, &*sig.ret_ty, sig.args.as_slice(), sig.is_variadic); + mk_fnty(ctx, &decl, sig.abi) + }, + &TFuncProto(ref sig) => { + let decl = cfuncty_to_rs(ctx, &*sig.ret_ty, sig.args.as_slice(), sig.is_variadic); + mk_fn_proto_ty(ctx, &decl, sig.abi) }, &TNamed(ref ti) => { let id = rust_type_id(ctx, ti.borrow().name.clone()); @@ -1007,6 +1012,21 @@ fn mk_arrty(ctx: &GenCtx, base: &ast::Ty, n: uint) -> ast::Ty { }; } +fn mk_fn_proto_ty(ctx: &mut GenCtx, decl: &ast::FnDecl, abi: abi::Abi) -> ast::Ty { + let fnty = ast::TyBareFn(P(ast::BareFnTy { + unsafety: ast::Unsafety::Normal, + abi: abi, + lifetimes: Vec::new(), + decl: P(decl.clone()) + })); + + ast::Ty { + id: ast::DUMMY_NODE_ID, + node: fnty, + span: ctx.span, + } +} + fn mk_fnty(ctx: &mut GenCtx, decl: &ast::FnDecl, abi: abi::Abi) -> ast::Ty { let fnty = ast::TyBareFn(P(ast::BareFnTy { unsafety: ast::Unsafety::Normal, @@ -22,6 +22,7 @@ use types::Global; mod types; mod clangll; +#[allow(dead_code)] mod clang; mod gen; mod parser; @@ -59,6 +60,7 @@ impl Default for BindgenOptions { } } +#[deriving(Copy)] pub enum LinkType { Default, Static, @@ -173,6 +175,7 @@ fn builtin_names() -> HashSet<String> { let keys = [ "__va_list_tag", "__va_list", + "__builtin_va_list", ]; keys.iter().all(|s| { diff --git a/src/parser.rs b/src/parser.rs index 3cc77239..2622a944 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -60,6 +60,7 @@ fn match_pattern(ctx: &mut ClangParserCtx, cursor: &Cursor) -> bool { } fn decl_name(ctx: &mut ClangParserCtx, cursor: &Cursor) -> Global { + let cursor = &cursor.canonical(); let mut new_decl = false; let override_enum_ty = ctx.options.override_enum_ty; let decl = match ctx.name.entry(*cursor) { @@ -167,22 +168,14 @@ fn conv_ptr_ty(ctx: &mut ClangParserCtx, ty: &cx::Type, cursor: &Cursor, layout: let ret_ty = ty.ret_type(); let decl = ty.declaration(); return if ret_ty.kind() != CXType_Invalid { - let mut args_lst = vec!(); - cursor.visit(|c, _| { - if c.kind() == CXCursor_ParmDecl { - args_lst.push((c.spelling(), conv_ty(ctx, &c.cur_type(), c))); - } - CXChildVisit_Continue - }); - - let ret_ty = box conv_ty(ctx, &ret_ty, cursor); - let abi = get_abi(ty.call_conv()); - - TFunc(ret_ty, args_lst, ty.is_variadic(), abi) + TFuncPtr(mk_fn_sig(ctx, ty, cursor)) } else if decl.kind() != CXCursor_NoDeclFound { - TPtr(box conv_decl_ty(ctx, &decl), is_const, layout) + TPtr(box conv_decl_ty(ctx, &decl), ty.is_const(), layout) + } else if cursor.kind() == CXCursor_VarDecl { + let can_ty = ty.canonical_type(); + conv_ty(ctx, &can_ty, cursor) } else { - TPtr(box TVoid, is_const, layout) + TPtr(box TVoid, ty.is_const(), layout) }; } CXType_Typedef => { @@ -199,6 +192,41 @@ fn conv_ptr_ty(ctx: &mut ClangParserCtx, ty: &cx::Type, cursor: &Cursor, layout: } } +fn mk_fn_sig(ctx: &mut ClangParserCtx, ty: &cx::Type, cursor: &Cursor) -> il::FuncSig { + let args_lst: Vec<(String, il::Type)> = match cursor.kind() { + CXCursor_FunctionDecl => { + // For CXCursor_FunctionDecl, cursor.args() is the reliable way to + // get parameter names and types. + cursor.args().iter().map(|arg| { + let arg_name = arg.spelling(); + (arg_name, conv_ty(ctx, &arg.cur_type(), arg)) + }).collect() + } + _ => { + // For non-CXCursor_FunctionDecl, visiting the cursor's children is + // the only reliable way to get parameter names. + let mut args_lst = vec!(); + cursor.visit(|c, _| { + if c.kind() == CXCursor_ParmDecl { + args_lst.push((c.spelling(), conv_ty(ctx, &c.cur_type(), c))); + } + CXChildVisit_Continue + }); + args_lst + } + }; + + let ret_ty = box conv_ty(ctx, &ty.ret_type(), cursor); + let abi = get_abi(ty.call_conv()); + + il::FuncSig { + ret_ty: ret_ty, + args: args_lst, + is_variadic: ty.is_variadic(), + abi: abi, + } +} + fn conv_decl_ty(ctx: &mut ClangParserCtx, cursor: &Cursor) -> il::Type { return match cursor.kind() { CXCursor_StructDecl => { @@ -226,7 +254,7 @@ fn conv_decl_ty(ctx: &mut ClangParserCtx, cursor: &Cursor) -> il::Type { } fn conv_ty(ctx: &mut ClangParserCtx, ty: &cx::Type, cursor: &Cursor) -> il::Type { - debug!("conv_ty: ty=`{}`", type_to_str(ty.kind())); + debug!("conv_ty: ty=`{}` sp=`{}` loc=`{}`", type_to_str(ty.kind()), cursor.spelling(), cursor.location()); let layout = Layout::new(ty.size(), ty.align()); return match ty.kind() { CXType_Void | CXType_Invalid => TVoid, @@ -250,6 +278,7 @@ fn conv_ty(ctx: &mut ClangParserCtx, ty: &cx::Type, cursor: &Cursor) -> il::Type CXType_VariableArray | CXType_DependentSizedArray | CXType_IncompleteArray => { conv_ptr_ty(ctx, &ty.elem_type(), cursor, layout) } + CXType_FunctionProto => TFuncProto(mk_fn_sig(ctx, ty, cursor)), CXType_Record | CXType_Typedef | CXType_Unexposed | @@ -303,8 +332,9 @@ fn visit_composite(cursor: &Cursor, parent: &Cursor, } // The Clang C api does not fully expose composite fields, but it - // does expose them in a way that can be detected. When the current - // field kind is CXType_Unexposed and the previous member is a + // does expose them in a way that can be detected. When the current + // field kind is CXType_Unexposed, CXType_Pointer or + // CXType_ConstantArray and the previous member is a // composite type--the same type as this field-- then this is a // composite field. e.g.: // @@ -314,36 +344,58 @@ fn visit_composite(cursor: &Cursor, parent: &Cursor, // char b; // } bar; // }; - let is_composite = if cursor.cur_type().kind() == CXType_Unexposed { - if let TComp(ref ty_compinfo) = ty { - match members.last() { - Some(&CompMember::Comp(ref c)) => c.borrow().deref() as *const _ == ty_compinfo.borrow().deref() as *const _, - _ => false + // + // struct foo { + // union { + // int a; + // char b; + // } *bar; + // }; + // + // struct foo { + // union { + // int a; + // char b; + // } bar[3]; + // }; + // + let is_composite = match (cursor.cur_type().kind(), &ty) { + (CXType_Unexposed, &TComp(ref ty_compinfo)) | + (CXType_Pointer, &TPtr(box TComp(ref ty_compinfo), _, _)) | + (CXType_ConstantArray, &TArray(box TComp(ref ty_compinfo), _, _)) => { + if let Some(&CompMember::Comp(ref c)) = members.last() { + c.borrow().deref() as *const _ == ty_compinfo.borrow().deref() as *const _ + } else { + false } - } else { false } - } else { false }; - + }, + _ => false + }; let field = FieldInfo::new(name, ty.clone(), bit); if is_composite { if let Some(CompMember::Comp(c)) = members.pop() { members.push(CompMember::CompField(c, field)); } else { - panic!(); // Checks in is_composite make this unreachable. + unreachable!(); // Checks in is_composite make this unreachable. } } else { members.push(CompMember::Field(field)); } } CXCursor_StructDecl | CXCursor_UnionDecl => { - let cursor = &cursor.canonical(); - let decl = decl_name(ctx, cursor); - let ci = decl.compinfo(); - cursor.visit(|c, p| { - let mut ci_ = ci.borrow_mut(); - visit_composite(c, p, ctx, &mut ci_.members) + fwd_decl(ctx, cursor, |ctx_| { + // If the struct is anonymous (i.e. declared here) then it + // cannot be used elsewhere and so does not need to be added + // to globals otherwise it will be declared later and a global. + let decl = decl_name(ctx_, cursor); + let ci = decl.compinfo(); + cursor.visit(|c, p| { + let mut ci_ = ci.borrow_mut(); + visit_composite(c, p, ctx_, &mut ci_.members) + }); + members.push(CompMember::Comp(decl.compinfo())); }); - members.push(CompMember::Comp(ci)); } _ => { // XXX: Some kind of warning would be nice, but this produces far @@ -370,107 +422,89 @@ fn visit_enum(cursor: &Cursor, return CXChildVisit_Continue; } -fn visit_top<'r>(cur: &'r Cursor, - parent: &'r Cursor, +fn visit_top<'r>(cursor: &Cursor, ctx: &mut ClangParserCtx) -> Enum_CXVisitorResult { - let cursor = if ctx.options.builtin_names.contains(&parent.spelling()) { - parent - } else { - cur - }; - if !match_pattern(ctx, cursor) { return CXChildVisit_Continue; } - let cursor = &cursor.canonical(); - match cursor.kind() { CXCursor_StructDecl | CXCursor_UnionDecl => { - fwd_decl(ctx, cursor, |ctx_| { - let decl = decl_name(ctx_, cursor); - let ci = decl.compinfo(); - cursor.visit(|c, p| { - let mut ci_ = ci.borrow_mut(); - visit_composite(c, p, ctx_, &mut ci_.members) - }); - ctx_.globals.push(GComp(ci)); - }); - - CXChildVisit_Continue - } - CXCursor_EnumDecl => { - fwd_decl(ctx, cursor, |ctx_| { - let decl = decl_name(ctx_, cursor); - let ei = decl.enuminfo(); - cursor.visit(|c, _| { - let mut ei_ = ei.borrow_mut(); - visit_enum(c, &mut ei_.items) - }); - ctx_.globals.push(GEnum(ei)); - }); - return CXChildVisit_Continue; - } - CXCursor_FunctionDecl => { - let linkage = cursor.linkage(); - if linkage != CXLinkage_External && linkage != CXLinkage_UniqueExternal { - return CXChildVisit_Continue; - } - - let args_lst: Vec<(String, il::Type)> = cursor.args().iter().map(|arg| { - let arg_name = arg.spelling(); - (arg_name, conv_ty(ctx, &arg.cur_type(), cursor)) - }).collect(); - - let ty = cursor.cur_type(); - let ret_ty = box conv_ty(ctx, &cursor.ret_type(), cursor); - let abi = get_abi(ty.call_conv()); - - let func = decl_name(ctx, cursor); - let vi = func.varinfo(); - let mut vi = vi.borrow_mut(); - vi.ty = TFunc(ret_ty.clone(), args_lst.clone(), ty.is_variadic(), abi); - ctx.globals.push(func); - - return CXChildVisit_Continue; - } - CXCursor_VarDecl => { - let linkage = cursor.linkage(); - if linkage != CXLinkage_External && linkage != CXLinkage_UniqueExternal { - return CXChildVisit_Continue; - } - - let ty = conv_ty(ctx, &cursor.cur_type(), cursor); - let var = decl_name(ctx, cursor); - let vi = var.varinfo(); - let mut vi = vi.borrow_mut(); - vi.ty = ty.clone(); - vi.is_const = cursor.cur_type().is_const(); - ctx.globals.push(var); - - return CXChildVisit_Continue; - } - CXCursor_TypedefDecl => { - let mut under_ty = cursor.typedef_type(); - if under_ty.kind() == CXType_Unexposed { - under_ty = under_ty.canonical_type(); - } - - let ty = conv_ty(ctx, &under_ty, cursor); - let typedef = decl_name(ctx, cursor); - let ti = typedef.typeinfo(); - let mut ti = ti.borrow_mut(); - ti.ty = ty.clone(); - ctx.globals.push(typedef); - - opaque_ty(ctx, &under_ty); - - return CXChildVisit_Continue; - } - CXCursor_FieldDecl => { - return CXChildVisit_Continue; - } - _ => return CXChildVisit_Continue, + fwd_decl(ctx, cursor, |ctx_| { + let decl = decl_name(ctx_, cursor); + let ci = decl.compinfo(); + cursor.visit(|c, p| { + let mut ci_ = ci.borrow_mut(); + visit_composite(c, p, ctx_, &mut ci_.members) + }); + ctx_.globals.push(GComp(ci)); + }); + return CXChildVisit_Continue; + } + CXCursor_EnumDecl => { + fwd_decl(ctx, cursor, |ctx_| { + let decl = decl_name(ctx_, cursor); + let ei = decl.enuminfo(); + cursor.visit(|c, _| { + let mut ei_ = ei.borrow_mut(); + visit_enum(c, &mut ei_.items) + }); + ctx_.globals.push(GEnum(ei)); + }); + return CXChildVisit_Continue; + } + CXCursor_FunctionDecl => { + let linkage = cursor.linkage(); + if linkage != CXLinkage_External && linkage != CXLinkage_UniqueExternal { + return CXChildVisit_Continue; + } + + let func = decl_name(ctx, cursor); + let vi = func.varinfo(); + let mut vi = vi.borrow_mut(); + + vi.ty = TFuncPtr(mk_fn_sig(ctx, &cursor.cur_type(), cursor)); + ctx.globals.push(func); + + return CXChildVisit_Continue; + } + CXCursor_VarDecl => { + let linkage = cursor.linkage(); + if linkage != CXLinkage_External && linkage != CXLinkage_UniqueExternal { + return CXChildVisit_Continue; + } + + let ty = conv_ty(ctx, &cursor.cur_type(), cursor); + let var = decl_name(ctx, cursor); + let vi = var.varinfo(); + let mut vi = vi.borrow_mut(); + vi.ty = ty.clone(); + vi.is_const = cursor.cur_type().is_const(); + ctx.globals.push(var); + + return CXChildVisit_Continue; + } + CXCursor_TypedefDecl => { + let mut under_ty = cursor.typedef_type(); + if under_ty.kind() == CXType_Unexposed { + under_ty = under_ty.canonical_type(); + } + + let ty = conv_ty(ctx, &under_ty, cursor); + let typedef = decl_name(ctx, cursor); + let ti = typedef.typeinfo(); + let mut ti = ti.borrow_mut(); + ti.ty = ty.clone(); + ctx.globals.push(typedef); + + opaque_ty(ctx, &under_ty); + + return CXChildVisit_Continue; + } + CXCursor_FieldDecl => { + return CXChildVisit_Continue; + } + _ => return CXChildVisit_Continue, } } @@ -523,11 +557,11 @@ pub fn parse(options: ClangParserOptions, logger: &Logger) -> Result<Vec<Global> cursor.visit(|cur, _| ast_dump(cur, 0)); } - cursor.visit(|cur, parent| visit_top(cur, parent, &mut ctx)); + cursor.visit(|cur, _| visit_top(cur, &mut ctx)); while !ctx.builtin_defs.is_empty() { let c = ctx.builtin_defs.remove(0).unwrap(); - c.visit(|cur, parent| visit_top(cur, parent, &mut ctx)); + visit_top(&c.definition(), &mut ctx); } unit.dispose(); diff --git a/src/types.rs b/src/types.rs index 1a0b353b..fbd108b2 100644 --- a/src/types.rs +++ b/src/types.rs @@ -70,18 +70,28 @@ impl fmt::Show for Global { } #[deriving(Clone, PartialEq)] +pub struct FuncSig { + pub ret_ty: Box<Type>, + pub args: Vec<(String, Type)>, + pub is_variadic: bool, + pub abi: abi::Abi, +} + +#[deriving(Clone, PartialEq)] pub enum Type { TVoid, TInt(IKind, Layout), TFloat(FKind, Layout), TPtr(Box<Type>, bool, Layout), TArray(Box<Type>, uint, Layout), - TFunc(Box<Type>, Vec<(String, Type)>, bool, abi::Abi), + TFuncProto(FuncSig), + TFuncPtr(FuncSig), TNamed(Rc<RefCell<TypeInfo>>), TComp(Rc<RefCell<CompInfo>>), TEnum(Rc<RefCell<EnumInfo>>) } +#[allow(dead_code)] impl Type { pub fn size(&self) -> uint { match self { @@ -93,7 +103,8 @@ impl Type { &TComp(ref ci) => ci.borrow().layout.size, &TEnum(ref ei) => ei.borrow().layout.size, &TVoid => 0, - &TFunc(..) => 0, + &TFuncProto(..) => 0, + &TFuncPtr(..) => 0, } } @@ -107,7 +118,8 @@ impl Type { &TComp(ref ci) => ci.borrow().layout.align, &TEnum(ref ei) => ei.borrow().layout.align, &TVoid => 0, - &TFunc(..) => 0, + &TFuncProto(..) => 0, + &TFuncPtr(..) => 0, } } } diff --git a/tests/forward_declared_struct.rs b/tests/forward_declared_struct.rs deleted file mode 100644 index a5d95743..00000000 --- a/tests/forward_declared_struct.rs +++ /dev/null @@ -1,12 +0,0 @@ -#![feature(phase)] - -#[phase(plugin)] -extern crate bindgen; - -extern crate libc; - -#[test] -fn test_struct_containing_forward_declared_struct() { - mod ffi { bindgen!("headers/struct_containing_forward_declared_struct.h"); } - // Check that struct b is not duplicated -}
\ No newline at end of file diff --git a/tests/func_ptr.rs b/tests/func_ptr.rs deleted file mode 100644 index 7851c4ff..00000000 --- a/tests/func_ptr.rs +++ /dev/null @@ -1,17 +0,0 @@ -extern crate bindgen; -extern crate regex; -extern crate syntax; - -mod util; - -#[test] -fn test_func_ptr() { - let output = util::generate_unpretty_output("tests/headers/func_ptr.h"); - assert_eq!(output, r##"extern "C" { pub static mut foo: ::std::option::Option<extern "C" fn(x: ::libc::c_int, y: ::libc::c_int) -> ::libc::c_int>; }"##); -} - -#[test] -fn test_func_ptr_in_struct() { - let output = util::generate_unpretty_output("tests/headers/func_ptr_in_struct.h"); - assert_eq!(output, r##"#[repr(C)] #[deriving(Copy)] pub struct Struct_Foo { pub bar: ::std::option::Option<extern "C" fn(x: ::libc::c_int, y: ::libc::c_int) -> Enum_baz>, } extern "C" { }"##); -} diff --git a/tests/headers/builtin_va_list.h b/tests/headers/builtin_va_list.h new file mode 100644 index 00000000..b3951bb7 --- /dev/null +++ b/tests/headers/builtin_va_list.h @@ -0,0 +1,6 @@ +#include "stdarg.h" + +struct a +{ + va_list b; +};
\ No newline at end of file diff --git a/tests/headers/decl_ptr_to_array.h b/tests/headers/decl_ptr_to_array.h new file mode 100644 index 00000000..3222cbd4 --- /dev/null +++ b/tests/headers/decl_ptr_to_array.h @@ -0,0 +1 @@ +int (*foo)[1]; diff --git a/tests/headers/forward_declared_struct.h b/tests/headers/forward_declared_struct.h new file mode 100644 index 00000000..2a69450c --- /dev/null +++ b/tests/headers/forward_declared_struct.h @@ -0,0 +1,11 @@ +struct a; + +struct a { + int b; +}; + +struct c { + int d; +}; + +struct c;
\ No newline at end of file diff --git a/tests/headers/func_proto.h b/tests/headers/func_proto.h new file mode 100644 index 00000000..51139ca9 --- /dev/null +++ b/tests/headers/func_proto.h @@ -0,0 +1 @@ +typedef int foo(int bar); diff --git a/tests/headers/func_with_func_ptr_arg.h b/tests/headers/func_with_func_ptr_arg.h new file mode 100644 index 00000000..629c84ab --- /dev/null +++ b/tests/headers/func_with_func_ptr_arg.h @@ -0,0 +1 @@ +void foo(void (*bar)()); diff --git a/tests/headers/struct_containing_forward_declared_struct.h b/tests/headers/struct_containing_forward_declared_struct.h index c68af042..d38aca2f 100644 --- a/tests/headers/struct_containing_forward_declared_struct.h +++ b/tests/headers/struct_containing_forward_declared_struct.h @@ -4,4 +4,4 @@ struct a { struct b { int val_b; -};
\ No newline at end of file +}; diff --git a/tests/headers/struct_with_anon_struct_array.h b/tests/headers/struct_with_anon_struct_array.h new file mode 100644 index 00000000..2d9d7bdd --- /dev/null +++ b/tests/headers/struct_with_anon_struct_array.h @@ -0,0 +1,6 @@ +struct foo { + struct { + int a; + int b; + } bar[2]; +}; diff --git a/tests/headers/struct_with_anon_struct_pointer.h b/tests/headers/struct_with_anon_struct_pointer.h new file mode 100644 index 00000000..0c486d84 --- /dev/null +++ b/tests/headers/struct_with_anon_struct_pointer.h @@ -0,0 +1,6 @@ +struct foo { + struct { + int a; + int b; + } *bar; +}; diff --git a/tests/headers/unnamed_bitfields.h b/tests/headers/unnamed_bitfields.h index 6ca9ec76..229bd728 100644 --- a/tests/headers/unnamed_bitfields.h +++ b/tests/headers/unnamed_bitfields.h @@ -1,10 +1,9 @@ -struct bitfield -{ +struct bitfield { unsigned short - a :1, - b :1, - c :1, + a :1, + b :1, + c :1, :1, :2, - d :2 -};
\ No newline at end of file + d :2; +}; diff --git a/tests/struct.rs b/tests/struct.rs deleted file mode 100644 index 980ea558..00000000 --- a/tests/struct.rs +++ /dev/null @@ -1,75 +0,0 @@ -#![feature(phase)] - -#[phase(plugin)] -extern crate bindgen; - -extern crate libc; - -#[test] -fn test_struct_with_anon_struct() { - mod ffi { bindgen!("headers/struct_with_anon_struct.h"); } - let mut x = ffi::Struct_foo { bar: ffi::Struct_Unnamed1 { a: 0, b: 0 } }; - - x.bar.a = 1; - x.bar.b = 2; - - assert_eq!(x.bar.a, 1); - assert_eq!(x.bar.b, 2); -} - -#[test] -fn test_struct_with_anon_union() { - mod ffi { bindgen!("headers/struct_with_anon_union.h"); } - let mut x = ffi::Struct_foo { bar: ffi::Union_Unnamed1 { _bindgen_data_: [0] } }; - - unsafe { - *x.bar.a() = 0x12345678; - assert_eq!(*x.bar.a(), 0x12345678); - assert_eq!(*x.bar.b(), 0x5678); - } -} - -#[test] -fn test_struct_with_anon_unnamed_struct() { - mod ffi { bindgen!("headers/struct_with_anon_unnamed_struct.h"); } - let mut x = ffi::Struct_foo { _bindgen_data_1_: [0, 0] }; - - unsafe { - *x.a() = 0x12345678; - *x.b() = 0x87654321; - assert_eq!(*x.a(), 0x12345678); - assert_eq!(*x.b(), 0x87654321); - } -} - -#[test] -fn test_struct_with_anon_unnamed_union() { - mod ffi { bindgen!("headers/struct_with_anon_unnamed_union.h"); } - let mut x = ffi::Struct_foo { _bindgen_data_1_: [0] }; - - unsafe { - *x.a() = 0x12345678; - assert_eq!(*x.a(), 0x12345678); - assert_eq!(*x.b(), 0x5678); - } -} - -#[test] -fn test_struct_with_nesting() { - mod ffi { bindgen!("headers/struct_with_nesting.h"); } - let mut x = ffi::Struct_foo { a: 0, _bindgen_data_1_: [0] }; - - unsafe { - x.a = 0x12345678; - *x.b() = 0x87654321; - - assert_eq!(x.a, 0x12345678); - assert_eq!(*x.b(), 0x87654321); - assert_eq!(*x.c1(), 0x4321); - assert_eq!(*x.c2(), 0x8765); - assert_eq!(*x.d1(), 0x21); - assert_eq!(*x.d2(), 0x43); - assert_eq!(*x.d3(), 0x65); - assert_eq!(*x.d4(), 0x87); - } -} diff --git a/tests/support.rs b/tests/support.rs new file mode 100644 index 00000000..a9b6dc4d --- /dev/null +++ b/tests/support.rs @@ -0,0 +1,220 @@ +use bindgen; +use bindgen::{Logger, BindgenOptions}; +use std::default::Default; +use std::fmt; +use syntax::ast; +use syntax::print::pprust; +use syntax::ptr::P; + +use syntax::codemap; +use syntax::codemap::{Span, DUMMY_SP}; +use syntax::parse; +use syntax::parse::token; + +struct TestLogger; + +impl Logger for TestLogger { + fn error(&self, msg: &str) { + println!("err: {}", msg); + } + + fn warn(&self, msg: &str) { + println!("warn: {}", msg); + } +} + +pub fn generate_bindings(filename: &str) -> Result<Vec<P<ast::Item>>, ()> { + let mut options:BindgenOptions = Default::default(); + options.clang_args.push(filename.to_string()); + + let logger = TestLogger; + Ok(try!(bindgen::Bindings::generate(&options, Some(&logger as &Logger), None)).into_ast()) +} + +pub fn test_bind_eq(filename: &str, f:|ext_cx: DummyExtCtxt| -> Vec<Option<P<ast::Item>>>) { + let ext_cx = mk_dummy_ext_ctxt(); + let items = normalize_attr_ids(generate_bindings(filename).unwrap()); + let quoted = normalize_attr_ids(f(ext_cx).into_iter().map(|x| x.unwrap()).collect()); + assert_eq!(PrettyItems { items: items }, + PrettyItems { items: quoted }); +} + +macro_rules! assert_bind_eq { + ($filename:expr, $ext_cx:ident, $($quote:expr),*) => { + ::support::test_bind_eq(concat!("tests/", $filename), |ext_cx| { + let $ext_cx = &ext_cx; + vec!($($quote),*) + }); + } +} + +pub struct DummyExtCtxt { + sess: parse::ParseSess, +} + +impl DummyExtCtxt { + pub fn cfg(&self) -> ast::CrateConfig { + vec!() + } + pub fn parse_sess(&self) -> &parse::ParseSess { + &self.sess + } + pub fn call_site(&self) -> codemap::Span { + codemap::Span { + lo: codemap::BytePos(0), + hi: codemap::BytePos(0), + expn_id: codemap::NO_EXPANSION + } + } + pub fn ident_of(&self, s: &str) -> ast::Ident { + token::str_to_ident(s) + } + pub fn name_of(&self, s: &str) -> ast::Name { + token::intern(s) + } +} + +fn mk_dummy_ext_ctxt<'a>() -> DummyExtCtxt { + DummyExtCtxt { sess: parse::new_parse_sess() } +} + +#[deriving(PartialEq)] +struct PrettyItems { + pub items: Vec<P<ast::Item>>, +} + +impl fmt::Show for PrettyItems { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let output = pprust::to_string(|s| { + let module = ast::Mod { + inner: DUMMY_SP, + view_items: Vec::new(), + items: self.items.clone(), + }; + s.print_mod(&module, &[]) + }); + write!(f, "\n{}\n", output) + } +} + +// libsyntax uses a thread-local variable to create unique AttrId values during +// parsing and quoting. normalize_attr_ids makes sure that all AttrId values +// for the passed `items` start at zero and proceed upward. This is necessary +// to correctly compare otherwise-identical ASTs. +fn normalize_attr_ids(items: Vec<P<ast::Item>>) -> Vec<P<ast::Item>> { + use std::mem; + use syntax::visit::*; + + struct Vis<'a> { + attr_id: uint, + items: Vec<P<ast::Item>>, + } + + impl<'a> Vis<'a> { + // TODO: visit_crate? ast::visit::Visitor does not deal with them directly, + // but concievably, it could eventually come up. + + fn rewrite_attrs(&mut self, attrs: Vec<ast::Attribute>) -> Vec<ast::Attribute> { + attrs.into_iter().map(|mut attr| { + attr.node.id = ast::AttrId(self.attr_id); + self.attr_id += 1; + attr + }).collect() + } + } + + unsafe fn force_mutable<T>(x: &T) -> &mut T { + mem::transmute(x) + } + + macro_rules! rewrite_attrs { + ($self_:ident, $item:expr) => { + unsafe { + let unsafe_item = force_mutable($item); + let new_attrs = mem::replace(&mut unsafe_item.attrs, vec!()); + let new_attrs = $self_.rewrite_attrs(new_attrs); + mem::replace(&mut unsafe_item.attrs, new_attrs); + } + } + } + + impl<'a, 'v> Visitor<'v> for Vis<'a> { + fn visit_item(&mut self, i: &ast::Item) { + rewrite_attrs!(self, i); + + match i.node { + ast::ItemImpl(_, _, _, _, ref impl_items) => { + for impl_item in impl_items.iter() { + match *impl_item { + ast::ImplItem::MethodImplItem(_) => { } + ast::ImplItem::TypeImplItem(ref typedef) => { + rewrite_attrs!(self, typedef.deref()); + } + } + } + } + _ => { } + } + + walk_item(self, i); + } + + fn visit_foreign_item(&mut self, i: &ast::ForeignItem) { + rewrite_attrs!(self, i); + walk_foreign_item(self, i); + } + + fn visit_fn(&mut self, fk: FnKind, fd: &ast::FnDecl, b: &ast::Block, s: Span, _: ast::NodeId) { + match fk { + FkItemFn(_, _, _, _) | FkFnBlock => { } + FkMethod(_, _, method) => { + rewrite_attrs!(self, method); + } + } + walk_fn(self, fk, fd, b, s); + } + + fn visit_arm(&mut self, a: &ast::Arm) { + rewrite_attrs!(self, a); + walk_arm(self, a); + } + + fn visit_ty_method(&mut self, t: &ast::TypeMethod) { + rewrite_attrs!(self, t); + walk_ty_method(self, t); + } + + fn visit_variant(&mut self, v: &ast::Variant, g: &ast::Generics) { + rewrite_attrs!(self, &v.node); + walk_variant(self, v, g); + } + + fn visit_view_item(&mut self, i: &ast::ViewItem) { + rewrite_attrs!(self, i); + walk_view_item(self, i); + } + + fn visit_struct_field(&mut self, s: &ast::StructField) { + rewrite_attrs!(self, &s.node); + walk_struct_field(self, s); + } + + fn visit_trait_item(&mut self, t: &ast::TraitItem) { + match *t { + ast::TraitItem::RequiredMethod(_) | + ast::TraitItem::ProvidedMethod(_) => { } + ast::TraitItem::TypeTraitItem(ref assoc_ty) => { + rewrite_attrs!(self, assoc_ty.deref()); + } + } + walk_trait_item(self, t); + } + } + + let mut visitor = Vis { attr_id: 0, items: vec!() }; + for item in items.into_iter() { + visitor.visit_item(item.deref()); + visitor.items.push(item); + } + visitor.items +} diff --git a/tests/test_builtins.rs b/tests/test_builtins.rs new file mode 100644 index 00000000..35cd573c --- /dev/null +++ b/tests/test_builtins.rs @@ -0,0 +1,13 @@ +#![feature(phase)] + +#[phase(plugin)] +extern crate bindgen; + +extern crate libc; + +#[test] +fn test_builtin_va_list() { + mod ffi { bindgen!("headers/builtin_va_list.h", emit_builtins = true); } + // Should test for more than compilation. +} + diff --git a/tests/cmath.rs b/tests/test_cmath.rs index f3fa1840..73e2cef1 100644 --- a/tests/cmath.rs +++ b/tests/test_cmath.rs @@ -1,20 +1,12 @@ -#![feature(phase)] - -#[phase(plugin)] -extern crate bindgen; - -extern crate libc; - #[allow(dead_code)] #[allow(non_snake_case)] #[allow(non_camel_case_types)] #[allow(non_upper_case_globals)] -pub mod ffi { - bindgen!("/usr/include/math.h", link = "m"); -} +#[allow(raw_pointer_deriving)] +pub mod ffi { bindgen!("/usr/include/math.h", link = "m"); } #[test] -fn test_floor_is_bound_and_callable() { +fn floor_is_bound_and_callable() { unsafe { assert_eq!(ffi::floor( 2.7), 2.0); assert_eq!(ffi::floor(-2.7), -3.0); diff --git a/tests/test_decl.rs b/tests/test_decl.rs new file mode 100644 index 00000000..5f83d00b --- /dev/null +++ b/tests/test_decl.rs @@ -0,0 +1,11 @@ +#[test] +fn ptr_to_array() { + assert_bind_eq!("headers/decl_ptr_to_array.h", cx, + quote_item!(cx, + extern "C" { + pub static mut foo: [::libc::c_int, ..1u]; + } + ) + ); + +} diff --git a/tests/test_func.rs b/tests/test_func.rs new file mode 100644 index 00000000..83bc2696 --- /dev/null +++ b/tests/test_func.rs @@ -0,0 +1,45 @@ +#[test] +fn func_ptr() { + assert_bind_eq!("headers/func_ptr.h", cx, + quote_item!(cx, + extern "C" { + pub static mut foo: ::std::option::Option< + extern "C" fn(x: ::libc::c_int, + y: ::libc::c_int) -> ::libc::c_int>; + } + ) + ); +} + +#[test] +fn func_ptr_in_struct() { + assert_bind_eq!("headers/func_ptr_in_struct.h", cx, + quote_item!(cx, + #[repr(C)] + #[deriving(Copy)] + pub struct Struct_Foo { + pub bar: ::std::option::Option< + extern "C" fn(x: ::libc::c_int, + y: ::libc::c_int) -> Enum_baz>, + } + ) + ); +} + +#[test] +fn func_proto() { + assert_bind_eq!("headers/func_proto.h", cx, + quote_item!(cx, + pub type foo = extern "C" fn(bar: ::libc::c_int) -> ::libc::c_int; + )); +} + +#[test] +fn with_func_ptr_arg() { + assert_bind_eq!("headers/func_with_func_ptr_arg.h", cx, + quote_item!(cx, + extern "C" { + pub fn foo(bar: ::std::option::Option<extern "C" fn()>); + } + )); +} diff --git a/tests/test_struct.rs b/tests/test_struct.rs new file mode 100644 index 00000000..d3c52aef --- /dev/null +++ b/tests/test_struct.rs @@ -0,0 +1,146 @@ +#[test] +fn with_anon_struct() { + mod ffi { bindgen!("headers/struct_with_anon_struct.h"); } + let mut x = ffi::Struct_foo { bar: ffi::Struct_Unnamed1 { a: 0, b: 0 } }; + + x.bar.a = 1; + x.bar.b = 2; + + assert_eq!(x.bar.a, 1); + assert_eq!(x.bar.b, 2); +} + +#[test] +fn with_anon_struct_array() { + mod ffi { bindgen!("headers/struct_with_anon_struct_array.h"); } + let mut x = ffi::Struct_foo { bar: [ + ffi::Struct_Unnamed1 { a: 0, b: 0 }, + ffi::Struct_Unnamed1 { a: 1, b: 1 } ] + }; + + x.bar[1].a = 1; + x.bar[1].b = 2; + + assert_eq!(x.bar[1].a, 1); + assert_eq!(x.bar[1].b, 2); +} + +#[test] +fn with_anon_struct_pointer() { + mod ffi { bindgen!("headers/struct_with_anon_struct_pointer.h"); } + let mut unnamed = box ffi::Struct_Unnamed1 { a: 0, b: 0 }; + + unsafe { + let mut x = ffi::Struct_foo { bar: ::std::mem::transmute(unnamed) }; + + (*x.bar).a = 1; + (*x.bar).b = 2; + + assert_eq!((*x.bar).a, 1); + assert_eq!((*x.bar).b, 2); + + ::std::ptr::read(x.bar); + } +} + +#[test] +fn with_anon_union() { + mod ffi { bindgen!("headers/struct_with_anon_union.h"); } + let mut x = ffi::Struct_foo { bar: ffi::Union_Unnamed1 { _bindgen_data_: [0] } }; + + unsafe { + *x.bar.a() = 0x12345678; + assert_eq!(*x.bar.a(), 0x12345678); + assert_eq!(*x.bar.b(), 0x5678); + } +} + +#[test] +fn with_anon_unnamed_struct() { + mod ffi { bindgen!("headers/struct_with_anon_unnamed_struct.h"); } + let mut x = ffi::Struct_foo { _bindgen_data_1_: [0, 0] }; + + unsafe { + *x.a() = 0x12345678; + *x.b() = 0x87654321; + assert_eq!(*x.a(), 0x12345678); + assert_eq!(*x.b(), 0x87654321); + } +} + +#[test] +fn with_anon_unnamed_union() { + mod ffi { bindgen!("headers/struct_with_anon_unnamed_union.h"); } + let mut x = ffi::Struct_foo { _bindgen_data_1_: [0] }; + + unsafe { + *x.a() = 0x12345678; + assert_eq!(*x.a(), 0x12345678); + assert_eq!(*x.b(), 0x5678); + } +} + +#[test] +fn with_nesting() { + mod ffi { bindgen!("headers/struct_with_nesting.h"); } + let mut x = ffi::Struct_foo { a: 0, _bindgen_data_1_: [0] }; + + unsafe { + x.a = 0x12345678; + *x.b() = 0x87654321; + + assert_eq!(x.a, 0x12345678); + assert_eq!(*x.b(), 0x87654321); + assert_eq!(*x.c1(), 0x4321); + assert_eq!(*x.c2(), 0x8765); + assert_eq!(*x.d1(), 0x21); + assert_eq!(*x.d2(), 0x43); + assert_eq!(*x.d3(), 0x65); + assert_eq!(*x.d4(), 0x87); + } +} + +#[test] +fn with_contain_fwd_decl_struct() { + assert_bind_eq!("headers/struct_containing_forward_declared_struct.h", cx, + quote_item!(cx, + #[repr(C)] + #[deriving(Copy)] + pub struct Struct_a { + pub val_a: *mut Struct_b, + } + ), + quote_item!(cx, + #[repr(C)] + #[deriving(Copy)] + pub struct Struct_b; + )); +} + +#[test] +fn with_unnamed_bitfields() { + assert_bind_eq!("headers/unnamed_bitfields.h", cx, + quote_item!(cx, + #[repr(C)] + #[deriving(Copy)] + pub struct Struct_bitfield { + pub a: ::libc::c_ushort, + pub b: ::libc::c_ushort, + pub c: ::libc::c_ushort, + pub unnamed_field1: ::libc::c_ushort, + pub unnamed_field2: ::libc::c_ushort, + pub d: ::libc::c_ushort, + } + )); +} + +#[test] +fn with_fwd_decl_struct() { + mod ffi { bindgen!("headers/forward_declared_struct.h"); } + + let a = ffi::Struct_a { b: 1 }; + let c = ffi::Struct_c { d: 1 }; + + a.b; + c.d; +} diff --git a/tests/union.rs b/tests/test_union.rs index 2bdb313e..33a3713d 100644 --- a/tests/union.rs +++ b/tests/test_union.rs @@ -1,12 +1,5 @@ -#![feature(phase)] - -#[phase(plugin)] -extern crate bindgen; - -extern crate libc; - #[test] -fn test_union_with_anon_struct() { +fn with_anon_struct() { // XXX: Rustc thinks that the anonymous struct, bar, is unused. #[allow(dead_code)] mod ffi { bindgen!("headers/union_with_anon_struct.h"); } @@ -22,7 +15,7 @@ fn test_union_with_anon_struct() { } #[test] -fn test_union_with_anon_union() { +fn with_anon_union() { mod ffi { bindgen!("headers/union_with_anon_union.h"); } let mut x = ffi::Union_foo { _bindgen_data_: [0] }; @@ -35,7 +28,7 @@ fn test_union_with_anon_union() { } #[test] -fn test_union_with_anon_unnamed_struct() { +fn with_anon_unnamed_struct() { mod ffi { bindgen!("headers/union_with_anon_unnamed_struct.h"); } let mut x = ffi::Union_pixel { _bindgen_data_: [0] }; @@ -54,7 +47,7 @@ fn test_union_with_anon_unnamed_struct() { } #[test] -fn test_union_with_anon_unnamed_union() { +fn with_anon_unnamed_union() { mod ffi { bindgen!("headers/union_with_anon_unnamed_union.h"); } let mut x = ffi::Union_foo { _bindgen_data_: [0] }; @@ -68,7 +61,7 @@ fn test_union_with_anon_unnamed_union() { } #[test] -fn test_union_with_nesting() { +fn with_nesting() { mod ffi { bindgen!("headers/union_with_nesting.h"); } let mut x = ffi::Union_foo { _bindgen_data_: [0] }; diff --git a/tests/tests.rs b/tests/tests.rs new file mode 100644 index 00000000..6515f6de --- /dev/null +++ b/tests/tests.rs @@ -0,0 +1,21 @@ +#![feature(globs)] +#![feature(macro_rules)] +#![feature(phase)] +#![feature(quote)] + +#[phase(plugin)] +extern crate bindgen; + +extern crate bindgen; +extern crate libc; +extern crate syntax; + +#[macro_escape] +mod support; + +mod test_cmath; +mod test_decl; +mod test_func; +mod test_struct; +mod test_union; +mod test_builtins; diff --git a/tests/unnamed_bitfields.rs b/tests/unnamed_bitfields.rs deleted file mode 100644 index a5bb1332..00000000 --- a/tests/unnamed_bitfields.rs +++ /dev/null @@ -1,12 +0,0 @@ -#![feature(phase)] - -#[phase(plugin)] -extern crate bindgen; - -extern crate libc; - -#[test] -fn test_unnamed_bitfields() { - mod ffi { bindgen!("headers/unnamed_bitfields.h"); } - // Check that there are no unnamed struct fields -}
\ No newline at end of file diff --git a/tests/util.rs b/tests/util.rs deleted file mode 100644 index 8ee3d213..00000000 --- a/tests/util.rs +++ /dev/null @@ -1,58 +0,0 @@ -extern crate bindgen; -extern crate regex; -extern crate syntax; - -use bindgen::{Logger, BindgenOptions}; -use regex::Regex; -use std::default::Default; -use syntax::ast; -use syntax::codemap::DUMMY_SP; -use syntax::print::pp; -use syntax::print::pprust; -use syntax::ptr::P; - -struct TestLogger; - -impl Logger for TestLogger { - fn error(&self, msg: &str) { - println!("err: {}", msg); - } - - fn warn(&self, msg: &str) { - println!("warn: {}", msg); - } -} - -pub fn generate_bindings(filename: &str) -> Result<Vec<P<ast::Item>>, ()> { - let mut options:BindgenOptions = Default::default(); - options.clang_args.push(filename.to_string()); - - let logger = TestLogger; - Ok(try!(bindgen::Bindings::generate(&options, Some(&logger as &Logger), None)).into_ast()) -} - -pub fn generate_unpretty_output(filename: &str) -> String { - let output = pprust::to_string(|s| { - // HACK: Replace the default printer with a very wide printer. This - // makes it easier to reason about how the unpretty'd output - // will look, at the cost of tying us to the implementation - // details of pprust::to_string. - s.s = pp::mk_printer(box Vec::new(), 4096); - - let items = generate_bindings(filename).unwrap(); - - let module = ast::Mod { - inner: DUMMY_SP, - view_items: Vec::new(), - items: items, - }; - s.print_mod(&module, &[]) - }); - - unpretty(output.as_slice()) -} - -pub fn unpretty(s: &str) -> String { - let re = Regex::new(r"\s+").unwrap(); - re.replace_all(s, " ") -} |