summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml11
-rw-r--r--Cargo.toml3
-rw-r--r--build.rs34
-rw-r--r--src/gen.rs40
-rw-r--r--src/lib.rs3
-rw-r--r--src/parser.rs292
-rw-r--r--src/types.rs18
-rw-r--r--tests/forward_declared_struct.rs12
-rw-r--r--tests/func_ptr.rs17
-rw-r--r--tests/headers/builtin_va_list.h6
-rw-r--r--tests/headers/decl_ptr_to_array.h1
-rw-r--r--tests/headers/forward_declared_struct.h11
-rw-r--r--tests/headers/func_proto.h1
-rw-r--r--tests/headers/func_with_func_ptr_arg.h1
-rw-r--r--tests/headers/struct_containing_forward_declared_struct.h2
-rw-r--r--tests/headers/struct_with_anon_struct_array.h6
-rw-r--r--tests/headers/struct_with_anon_struct_pointer.h6
-rw-r--r--tests/headers/unnamed_bitfields.h13
-rw-r--r--tests/struct.rs75
-rw-r--r--tests/support.rs220
-rw-r--r--tests/test_builtins.rs13
-rw-r--r--tests/test_cmath.rs (renamed from tests/cmath.rs)14
-rw-r--r--tests/test_decl.rs11
-rw-r--r--tests/test_func.rs45
-rw-r--r--tests/test_struct.rs146
-rw-r--r--tests/test_union.rs (renamed from tests/union.rs)17
-rw-r--r--tests/tests.rs21
-rw-r--r--tests/unnamed_bitfields.rs12
-rw-r--r--tests/util.rs58
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
diff --git a/Cargo.toml b/Cargo.toml
index 1af1c619..a85fa8a9 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -17,3 +17,6 @@ plugin = true
name = "bindgen"
doc = false
+
+[[test]]
+name = "tests"
diff --git a/build.rs b/build.rs
index fbc8e952..47b613c8 100644
--- a/build.rs
+++ b/build.rs
@@ -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());
+}
diff --git a/src/gen.rs b/src/gen.rs
index 68629250..4d331c9e 100644
--- a/src/gen.rs
+++ b/src/gen.rs
@@ -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,
diff --git a/src/lib.rs b/src/lib.rs
index 5bd7ed6a..73b6358f 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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, " ")
-}