diff --git a/libbindgen/build.rs b/libbindgen/build.rs index 228a7f2212..98bb76a9bc 100644 --- a/libbindgen/build.rs +++ b/libbindgen/build.rs @@ -38,13 +38,14 @@ mod testgen { .replace(|c| !char::is_alphanumeric(c), "_") .replace("__", "_") .to_lowercase(); - let _ = writeln!(dst, "test_header!(header_{}, {:?});", - func, entry.path()); + writeln!(dst, "test_header!(header_{}, {:?});", + func, entry.path()).unwrap(); } _ => {} } } - let _ = dst.flush(); + + dst.flush().unwrap(); } } diff --git a/libbindgen/src/clang.rs b/libbindgen/src/clang.rs index d10457b3b0..637823ead8 100644 --- a/libbindgen/src/clang.rs +++ b/libbindgen/src/clang.rs @@ -264,7 +264,7 @@ impl Cursor { /// Given that this cursor's referent is reference type, get the cursor /// pointing to the referenced type. - pub fn referenced(&self) -> Option { + pub fn referenced(&self) -> Option { unsafe { let ret = Cursor { x: clang_getCursorReferenced(self.x), @@ -475,6 +475,11 @@ impl Cursor { pub fn is_virtual_base(&self) -> bool { unsafe { clang_isVirtualBase(self.x) != 0 } } + + /// Try to evaluate this cursor. + pub fn evaluate(&self) -> EvalResult { + EvalResult::new(*self) + } } extern "C" fn visit_children(cur: CXCursor, @@ -933,7 +938,9 @@ impl Into for CXString { } unsafe { let c_str = CStr::from_ptr(clang_getCString(self) as *const _); - c_str.to_string_lossy().into_owned() + let ret = c_str.to_string_lossy().into_owned(); + clang_disposeString(self); + ret } } } @@ -1259,3 +1266,70 @@ pub fn ast_dump(c: &Cursor, depth: isize) -> Enum_CXVisitorResult { pub fn extract_clang_version() -> String { unsafe { clang_getClangVersion().into() } } + +/// A wrapper for the result of evaluating an expression. +#[derive(Debug)] +pub struct EvalResult { + x: CXEvalResult, +} + +#[cfg(feature = "llvm_stable")] +impl EvalResult { + /// Create a dummy EvalResult. + pub fn new(_: Cursor) -> Self { + EvalResult { + x: ptr::null_mut(), + } + } + + /// Not useful in llvm 3.8. + pub fn as_double(&self) -> Option { + None + } + + /// Not useful in llvm 3.8. + pub fn as_int(&self) -> Option { + None + } +} + +#[cfg(not(feature = "llvm_stable"))] +impl EvalResult { + /// Evaluate `cursor` and return the result. + pub fn new(cursor: Cursor) -> Self { + EvalResult { + x: unsafe { clang_Cursor_Evaluate(cursor.x) }, + } + } + + fn kind(&self) -> Enum_CXEvalResultKind { + unsafe { clang_EvalResult_getKind(self.x) } + } + + /// Try to get back the result as a double. + pub fn as_double(&self) -> Option { + match self.kind() { + CXEval_Float => { + Some(unsafe { clang_EvalResult_getAsDouble(self.x) } as f64) + } + _ => None, + } + } + + /// Try to get back the result as an integer. + pub fn as_int(&self) -> Option { + match self.kind() { + CXEval_Int => { + Some(unsafe { clang_EvalResult_getAsInt(self.x) } as i32) + } + _ => None, + } + } +} + +#[cfg(not(feature = "llvm_stable"))] +impl Drop for EvalResult { + fn drop(&mut self) { + unsafe { clang_EvalResult_dispose(self.x) }; + } +} diff --git a/libbindgen/src/clangll.rs b/libbindgen/src/clangll.rs index b974311769..e37baf257a 100644 --- a/libbindgen/src/clangll.rs +++ b/libbindgen/src/clangll.rs @@ -7,7 +7,22 @@ #![allow(non_upper_case_globals)] #![cfg_attr(rustfmt, rustfmt_skip)] -use ::std::os::raw::{ c_char, c_int, c_long, c_longlong, c_uint, c_ulong, c_ulonglong, c_void}; +use ::std::os::raw::{c_char, c_int, c_long, c_longlong, c_uint, c_ulong, c_ulonglong, c_void}; + +#[cfg(not(feature = "llvm_stable"))] +use std::os::raw::c_double; + + +pub type CXEvalResult = *mut c_void; +pub type Enum_CXEvalResultKind = c_uint; + +pub const CXEval_Int: c_uint = 1; +pub const CXEval_Float: c_uint = 2; +pub const CXEval_ObjCStrLiteral: c_uint = 3; +pub const CXEval_StrLiteral: c_uint = 4; +pub const CXEval_CFStr: c_uint = 5; +pub const CXEval_Other: c_uint = 6; +pub const CXEval_UnExposed: c_uint = 0; pub type ptrdiff_t = c_long; pub type size_t = c_ulong; @@ -1436,4 +1451,16 @@ extern "C" { offset: *mut c_uint); pub fn clang_indexLoc_getCXSourceLocation(loc: CXIdxLoc) -> CXSourceLocation; + #[cfg(not(feature="llvm_stable"))] + pub fn clang_Cursor_Evaluate(C: CXCursor) -> CXEvalResult; + #[cfg(not(feature="llvm_stable"))] + pub fn clang_EvalResult_getKind(E: CXEvalResult) -> Enum_CXEvalResultKind; + #[cfg(not(feature="llvm_stable"))] + pub fn clang_EvalResult_getAsInt(E: CXEvalResult) -> c_int; + #[cfg(not(feature="llvm_stable"))] + pub fn clang_EvalResult_getAsDouble(E: CXEvalResult) -> c_double; + #[cfg(not(feature="llvm_stable"))] + pub fn clang_EvalResult_getAsStr(E: CXEvalResult) -> *const c_char; + #[cfg(not(feature="llvm_stable"))] + pub fn clang_EvalResult_dispose(E: CXEvalResult); } diff --git a/libbindgen/src/codegen/helpers.rs b/libbindgen/src/codegen/helpers.rs index 6e5a6f0e31..8c3d3cea7c 100644 --- a/libbindgen/src/codegen/helpers.rs +++ b/libbindgen/src/codegen/helpers.rs @@ -132,4 +132,37 @@ pub mod ast_ty { expr.int(val) } } + + pub fn byte_array_expr(bytes: &[u8]) -> P { + let mut vec = Vec::with_capacity(bytes.len() + 1); + for byte in bytes { + vec.push(int_expr(*byte as i64)); + } + vec.push(int_expr(0)); + + let kind = ast::ExprKind::Vec(vec); + + aster::AstBuilder::new().expr().build_expr_kind(kind) + } + + pub fn cstr_expr(mut string: String) -> P { + string.push('\0'); + aster::AstBuilder::new() + .expr() + .build_lit(aster::AstBuilder::new().lit().byte_str(string)) + } + + pub fn float_expr(f: f64) -> P { + use aster::str::ToInternedString; + let mut string = f.to_string(); + + // So it gets properly recognised as a floating point constant. + if !string.contains('.') { + string.push('.'); + } + + let interned_str = string.as_str().to_interned_string(); + let kind = ast::LitKind::FloatUnsuffixed(interned_str); + aster::AstBuilder::new().expr().lit().build_lit(kind) + } } diff --git a/libbindgen/src/codegen/mod.rs b/libbindgen/src/codegen/mod.rs index 99ec56f456..ceb023f796 100644 --- a/libbindgen/src/codegen/mod.rs +++ b/libbindgen/src/codegen/mod.rs @@ -304,6 +304,7 @@ impl CodeGenerator for Var { ctx: &BindgenContext, result: &mut CodegenResult, item: &Item) { + use ir::var::VarType; debug!("::codegen: item = {:?}", item); let canonical_name = item.canonical_name(ctx); @@ -320,10 +321,44 @@ impl CodeGenerator for Var { .item() .pub_() .const_(canonical_name) - .expr() - .build(helpers::ast_ty::int_expr(val)) - .build(ty); - result.push(const_item) + .expr(); + let item = match *val { + VarType::Int(val) => { + const_item.build(helpers::ast_ty::int_expr(val)) + .build(ty) + } + VarType::String(ref bytes) => { + // Account the trailing zero. + // + // TODO: Here we ignore the type we just made up, probably + // we should refactor how the variable type and ty id work. + let len = bytes.len() + 1; + let ty = quote_ty!(ctx.ext_cx(), [u8; $len]); + + match String::from_utf8(bytes.clone()) { + Ok(string) => { + const_item.build(helpers::ast_ty::cstr_expr(string)) + .build(quote_ty!(ctx.ext_cx(), &'static $ty)) + } + Err(..) => { + const_item + .build(helpers::ast_ty::byte_array_expr(bytes)) + .build(ty) + } + } + } + VarType::Float(f) => { + const_item.build(helpers::ast_ty::float_expr(f)) + .build(ty) + } + VarType::Char(c) => { + const_item + .build(aster::AstBuilder::new().expr().lit().byte(c)) + .build(ty) + } + }; + + result.push(item); } else { let mut attrs = vec![]; if let Some(mangled) = self.mangled_name() { diff --git a/libbindgen/src/ir/context.rs b/libbindgen/src/ir/context.rs index 857219780e..bda3e3c8a5 100644 --- a/libbindgen/src/ir/context.rs +++ b/libbindgen/src/ir/context.rs @@ -222,7 +222,7 @@ impl<'ctx> BindgenContext<'ctx> { error!("Valid declaration with no USR: {:?}, {:?}", declaration, location); - return; + TypeKey::Declaration(declaration) }; let old = self.types.insert(key, id); @@ -609,8 +609,7 @@ impl<'ctx> BindgenContext<'ctx> { -> Option { use clangll::{CXCursor_ClassTemplate, CXCursor_ClassTemplatePartialSpecialization, - CXCursor_TypeAliasTemplateDecl, - CXCursor_TypeRef}; + CXCursor_TypeAliasTemplateDecl, CXCursor_TypeRef}; debug!("builtin_or_resolved_ty: {:?}, {:?}, {:?}", ty, location, diff --git a/libbindgen/src/ir/item.rs b/libbindgen/src/ir/item.rs index 1f05f92f39..253db8c005 100644 --- a/libbindgen/src/ir/item.rs +++ b/libbindgen/src/ir/item.rs @@ -840,11 +840,7 @@ impl ClangItemParser for Item { ctx: &mut BindgenContext) -> ItemId { let id = ctx.next_item_id(); - Self::from_ty_or_ref_with_id(id, - ty, - location, - parent_id, - ctx) + Self::from_ty_or_ref_with_id(id, ty, location, parent_id, ctx) } /// Parse a C++ type. If we find a reference to a type that has not been diff --git a/libbindgen/src/ir/ty.rs b/libbindgen/src/ir/ty.rs index 34af2db502..1cd255a574 100644 --- a/libbindgen/src/ir/ty.rs +++ b/libbindgen/src/ir/ty.rs @@ -123,6 +123,14 @@ impl Type { Self::new(Some(name), None, kind, false) } + /// Is this a floating point type? + pub fn is_float(&self) -> bool { + match self.kind { + TypeKind::Float(..) => true, + _ => false, + } + } + /// Is this an integer type? pub fn is_integer(&self) -> bool { match self.kind { @@ -667,7 +675,7 @@ impl Type { TypeKind::TemplateAlias(inner.unwrap(), args) } CXCursor_TemplateRef => { - let referenced = location.referenced().expect("expected value, got none"); + let referenced = location.referenced().unwrap(); let referenced_ty = referenced.cur_type(); let referenced_declaration = Some(referenced_ty.declaration()); @@ -679,7 +687,7 @@ impl Type { ctx); } CXCursor_TypeRef => { - let referenced = location.referenced().expect("expected value, got none"); + let referenced = location.referenced().unwrap(); let referenced_ty = referenced.cur_type(); let referenced_declaration = Some(referenced_ty.declaration()); diff --git a/libbindgen/src/ir/var.rs b/libbindgen/src/ir/var.rs index d0c4d9cacd..3270b33216 100644 --- a/libbindgen/src/ir/var.rs +++ b/libbindgen/src/ir/var.rs @@ -8,7 +8,20 @@ use super::context::{BindgenContext, ItemId}; use super::function::cursor_mangling; use super::int::IntKind; use super::item::Item; -use super::ty::TypeKind; +use super::ty::{FloatKind, TypeKind}; + +/// The type for a constant variable. +#[derive(Debug)] +pub enum VarType { + /// An integer. + Int(i64), + /// A floating point number. + Float(f64), + /// A character. + Char(u8), + /// A string, not necessarily well-formed utf-8. + String(Vec), +} /// A `Var` is our intermediate representation of a variable. #[derive(Debug)] @@ -19,9 +32,8 @@ pub struct Var { mangled_name: Option, /// The type of the variable. ty: ItemId, - /// TODO: support non-integer constants? - /// The integer value of the variable. - val: Option, + /// The value of the variable, that needs to be suitable for `ty`. + val: Option, /// Whether this variable is const. is_const: bool, } @@ -31,7 +43,7 @@ impl Var { pub fn new(name: String, mangled: Option, ty: ItemId, - val: Option, + val: Option, is_const: bool) -> Var { assert!(!name.is_empty()); @@ -50,8 +62,8 @@ impl Var { } /// The value of this constant variable, if any. - pub fn val(&self) -> Option { - self.val + pub fn val(&self) -> Option<&VarType> { + self.val.as_ref() } /// Get this variable's type. @@ -76,6 +88,7 @@ impl ClangSubItemParser for Var { -> Result, ParseError> { use clangll::*; use cexpr::expr::EvalResult; + use cexpr::literal::CChar; match cursor.kind() { CXCursor_MacroDefinition => { let value = parse_macro(ctx, &cursor, ctx.translation_unit()); @@ -105,13 +118,32 @@ impl ClangSubItemParser for Var { // enforce utf8 there, so we should have already panicked at // this point. let name = String::from_utf8(id).unwrap(); - let (int_kind, val) = match value { - // TODO(emilio): Handle the non-invalid ones! - EvalResult::Float(..) | - EvalResult::Char(..) | - EvalResult::Str(..) | + let (type_kind, val) = match value { EvalResult::Invalid => return Err(ParseError::Continue), - + EvalResult::Float(f) => { + (TypeKind::Float(FloatKind::Float), VarType::Float(f)) + } + EvalResult::Char(c) => { + let c = match c { + CChar::Char(c) => { + assert_eq!(c.len_utf8(), 1); + c as u8 + } + CChar::Raw(c) => { + assert!(c <= ::std::u8::MAX as u64); + c as u8 + } + }; + + (TypeKind::Int(IntKind::U8), VarType::Char(c)) + } + EvalResult::Str(val) => { + let char_ty = + Item::builtin_type(TypeKind::Int(IntKind::U8), + true, + ctx); + (TypeKind::Pointer(char_ty), VarType::String(val)) + } EvalResult::Int(Wrapping(value)) => { let kind = ctx.options() .type_chooser @@ -131,11 +163,11 @@ impl ClangSubItemParser for Var { } }); - (kind, value) + (TypeKind::Int(kind), VarType::Int(value)) } }; - let ty = Item::builtin_type(TypeKind::Int(int_kind), true, ctx); + let ty = Item::builtin_type(type_kind, true, ctx); Ok(ParseResult::New(Var::new(name, None, ty, Some(val), true), Some(cursor))) @@ -159,19 +191,37 @@ impl ClangSubItemParser for Var { // tests/headers/inner_const.hpp // // That's fine because in that case we know it's not a literal. - let value = ctx.safe_resolve_type(ty) - .and_then(|t| t.safe_canonical_type(ctx)) - .and_then(|t| if t.is_integer() { Some(t) } else { None }) - .and_then(|_| { - get_integer_literal_from_cursor(&cursor, - ctx.translation_unit()) - }); + let canonical_ty = ctx.safe_resolve_type(ty) + .and_then(|t| t.safe_canonical_type(ctx)); + + let is_integer = canonical_ty.map_or(false, |t| t.is_integer()); + let is_float = canonical_ty.map_or(false, |t| t.is_float()); + + // TODO: We could handle `char` more gracefully. + // TODO: Strings, though the lookup is a bit more hard (we need + // to look at the canonical type of the pointee too, and check + // is char, u8, or i8 I guess). + let value = if is_integer { + cursor.evaluate() + .as_int() + .map(|val| val as i64) + .or_else(|| { + let tu = ctx.translation_unit(); + get_integer_literal_from_cursor(&cursor, tu) + }) + .map(VarType::Int) + } else if is_float { + cursor.evaluate() + .as_double() + .map(VarType::Float) + } else { + None + }; let mangling = cursor_mangling(&cursor); - let var = Var::new(name, mangling, ty, value, is_const); - Ok(ParseResult::New(var, Some(cursor))) + Ok(ParseResult::New(var, Some(cursor))) } _ => { /* TODO */ diff --git a/libbindgen/tests/expectations/tests/constant-evaluate.rs b/libbindgen/tests/expectations/tests/constant-evaluate.rs new file mode 100644 index 0000000000..27b8d8296c --- /dev/null +++ b/libbindgen/tests/expectations/tests/constant-evaluate.rs @@ -0,0 +1,15 @@ +/* automatically generated by rust-bindgen */ + + +#![allow(non_snake_case)] + + +pub const foo: _bindgen_ty_1 = _bindgen_ty_1::foo; +pub const bar: _bindgen_ty_1 = _bindgen_ty_1::bar; +#[repr(u32)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum _bindgen_ty_1 { foo = 4, bar = 8, } +pub const BAZ: ::std::os::raw::c_longlong = 24; +pub const fuzz: f64 = 51.; +pub const BAZZ: ::std::os::raw::c_char = 53; +pub const WAT: ::std::os::raw::c_char = 0; diff --git a/libbindgen/tests/expectations/tests/macro_const.rs b/libbindgen/tests/expectations/tests/macro_const.rs new file mode 100644 index 0000000000..9e7eb420ed --- /dev/null +++ b/libbindgen/tests/expectations/tests/macro_const.rs @@ -0,0 +1,12 @@ +/* automatically generated by rust-bindgen */ + + +#![allow(non_snake_case)] + + +pub const foo: &'static [u8; 4usize] = b"bar\x00"; +pub const CHAR: u8 = b'b'; +pub const CHARR: u8 = b'\x00'; +pub const FLOAT: f32 = 5.09; +pub const FLOAT_EXPR: f32 = 0.005; +pub const INVALID_UTF8: [u8; 5usize] = [240, 40, 140, 40, 0]; diff --git a/libbindgen/tests/expectations/tests/msvc-no-usr.rs b/libbindgen/tests/expectations/tests/msvc-no-usr.rs new file mode 100644 index 0000000000..8cab8cdc2f --- /dev/null +++ b/libbindgen/tests/expectations/tests/msvc-no-usr.rs @@ -0,0 +1,19 @@ +/* automatically generated by rust-bindgen */ + + +#![allow(non_snake_case)] + + +#[repr(C)] +#[derive(Debug, Copy)] +pub struct A { + pub foo: usize, +} +#[test] +fn bindgen_test_layout_A() { + assert_eq!(::std::mem::size_of::() , 8usize); + assert_eq!(::std::mem::align_of::() , 8usize); +} +impl Clone for A { + fn clone(&self) -> Self { *self } +} diff --git a/tests/expectations/tests/type_alias_partial_template_especialization.rs b/libbindgen/tests/expectations/tests/type_alias_partial_template_especialization.rs similarity index 100% rename from tests/expectations/tests/type_alias_partial_template_especialization.rs rename to libbindgen/tests/expectations/tests/type_alias_partial_template_especialization.rs diff --git a/tests/expectations/tests/type_alias_template_specialized.rs b/libbindgen/tests/expectations/tests/type_alias_template_specialized.rs similarity index 100% rename from tests/expectations/tests/type_alias_template_specialized.rs rename to libbindgen/tests/expectations/tests/type_alias_template_specialized.rs diff --git a/libbindgen/tests/headers/constant-evaluate.h b/libbindgen/tests/headers/constant-evaluate.h new file mode 100644 index 0000000000..2790d603c1 --- /dev/null +++ b/libbindgen/tests/headers/constant-evaluate.h @@ -0,0 +1,11 @@ +// bindgen-unstable + +enum { + foo = 4, + bar = 8, +}; + +const long long BAZ = (1 << foo) | bar; +const double fuzz = (1 + 50.0f); +const char BAZZ = '5'; +const char WAT = '\0'; diff --git a/libbindgen/tests/headers/macro_const.h b/libbindgen/tests/headers/macro_const.h new file mode 100644 index 0000000000..c28a3f6bc8 --- /dev/null +++ b/libbindgen/tests/headers/macro_const.h @@ -0,0 +1,7 @@ +#define foo "bar" +#define CHAR 'b' +#define CHARR '\0' +#define FLOAT 5.09f +#define FLOAT_EXPR (5 / 1000.0f) + +#define INVALID_UTF8 "\xf0\x28\x8c\x28" diff --git a/libbindgen/tests/headers/msvc-no-usr.hpp b/libbindgen/tests/headers/msvc-no-usr.hpp new file mode 100644 index 0000000000..b15e49f52d --- /dev/null +++ b/libbindgen/tests/headers/msvc-no-usr.hpp @@ -0,0 +1,8 @@ + +typedef unsigned long long size_t; + +class A { + const size_t foo; + + A() : foo(5) {} +}; diff --git a/tests/headers/type_alias_partial_template_especialization.hpp b/libbindgen/tests/headers/type_alias_partial_template_especialization.hpp similarity index 100% rename from tests/headers/type_alias_partial_template_especialization.hpp rename to libbindgen/tests/headers/type_alias_partial_template_especialization.hpp diff --git a/tests/headers/type_alias_template_specialized.hpp b/libbindgen/tests/headers/type_alias_template_specialized.hpp similarity index 100% rename from tests/headers/type_alias_template_specialized.hpp rename to libbindgen/tests/headers/type_alias_template_specialized.hpp diff --git a/libbindgen/tests/tests.rs b/libbindgen/tests/tests.rs index c07864062b..cb26c88a4e 100644 --- a/libbindgen/tests/tests.rs +++ b/libbindgen/tests/tests.rs @@ -7,7 +7,7 @@ extern crate log; extern crate shlex; use std::fs; -use std::io::{BufRead, BufReader, Error, ErrorKind, Read}; +use std::io::{BufRead, BufReader, Error, ErrorKind, Read, Write}; use std::path::PathBuf; #[path="../../src/options.rs"] @@ -35,11 +35,18 @@ fn compare_generated_header(header: &PathBuf, }; let mut buffer = String::new(); - let f = try!(fs::File::open(&expected)); - let _ = try!(BufReader::new(f).read_to_string(&mut buffer)); + { + if let Ok(expected_file) = fs::File::open(&expected) { + try!(BufReader::new(expected_file).read_to_string(&mut buffer)); + } + } if output == buffer { - return Ok(()); + if !output.is_empty() { + return Ok(()); + } + return Err(Error::new(ErrorKind::Other, + "Something's gone really wrong!")) } println!("diff expected generated"); @@ -53,21 +60,34 @@ fn compare_generated_header(header: &PathBuf, diff::Result::Right(r) => println!("+{}", r), } } + + // Override the diff. + { + let mut expected_file = try!(fs::File::create(&expected)); + try!(expected_file.write_all(output.as_bytes())); + } + Err(Error::new(ErrorKind::Other, "Header and binding differ!")) } fn create_bindgen_builder(header: &PathBuf) - -> Result { + -> Result, Error> { let source = try!(fs::File::open(header)); let reader = BufReader::new(source); // Scoop up bindgen-flags from test header - let line: String = try!(reader.lines().take(1).collect()); - let flags: Vec = if line.contains("bindgen-flags:") { - line.split("bindgen-flags:").last().and_then(shlex::split) - } else { - None - }.unwrap_or(Vec::with_capacity(2)); + let mut flags = Vec::with_capacity(2); + + for line in reader.lines().take(2) { + let line = try!(line); + if line.contains("bindgen-flags: ") { + let extra_flags = line.split("bindgen-flags: ") + .last().and_then(shlex::split).unwrap(); + flags.extend(extra_flags.into_iter()); + } else if line.contains("bindgen-unstable") && cfg!(feature = "llvm_stable") { + return Ok(None) + } + } // Fool builder_from_flags() into believing it has real env::args_os... // - add "bindgen" as executable name 0th element @@ -89,7 +109,8 @@ fn create_bindgen_builder(header: &PathBuf) .map(ToString::to_string) .chain(flags.into_iter()); - builder_from_flags(args).map(|(builder, _)| builder.no_unstable_rust()) + builder_from_flags(args) + .map(|(builder, _)| Some(builder.no_unstable_rust())) } macro_rules! test_header { @@ -97,9 +118,18 @@ macro_rules! test_header { #[test] fn $function() { let header = PathBuf::from($header); - let _ = create_bindgen_builder(&header) - .and_then(|builder| compare_generated_header(&header, builder)) - .map_err(|err| panic!(format!("{}", err)) ); + let result = create_bindgen_builder(&header) + .and_then(|builder| { + if let Some(builder) = builder { + compare_generated_header(&header, builder) + } else { + Ok(()) + } + }); + + if let Err(err) = result { + panic!("{}", err); + } } ) }