) -> SmallVector> {
+ match item.node {
+ ast::ItemKind::Mod(..)
+ | ast::ItemKind::ForeignMod(..)
+ | ast::ItemKind::Ty(..)
+ | ast::ItemKind::Enum(..)
+ | ast::ItemKind::Struct(..)
+ | ast::ItemKind::Union(..)
+ | ast::ItemKind::Mac(..)
+ | ast::ItemKind::MacroDef(..)
+ | ast::ItemKind::Use(..)
+ | ast::ItemKind::ExternCrate(..)
+ | ast::ItemKind::Const(..) => fold::noop_fold_item(item, self),
+
+ ast::ItemKind::Static(..)
+ | ast::ItemKind::Fn(..)
+ | ast::ItemKind::GlobalAsm(..)
+ | ast::ItemKind::Trait(..)
+ | ast::ItemKind::DefaultImpl(..)
+ | ast::ItemKind::Impl(..) => SmallVector::default(),
+ }
+ }
+
+ fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
+ fold::noop_fold_mac(mac, self)
+ }
+}
+
+struct MyVisitor<'b> {
+ parse_sess: &'b ParseSess,
+ map: &'b mut HashMap>,
+}
+
+impl<'a, 'b> Visitor<'a> for MyVisitor<'b> {
+ fn visit_item(&mut self, item: &'a ast::Item) {
+ if let ast::ItemKind::MacroDef(..) = item.node {
+ self.map.insert(
+ item.ident.name,
+ Rc::new(macro_rules::compile(self.parse_sess, item)),
+ );
+ }
+ visit::walk_item(self, item);
+ }
+
+ fn visit_mac(&mut self, _: &'a ast::Mac) {
+ /* ignore macros */
+ }
+}
+
+impl Default for TestGenerator {
+ fn default() -> Self {
+ Self::new()
+ }
+}
diff --git a/ctest/testcrate/Cargo.toml b/ctest/testcrate/Cargo.toml
new file mode 100644
index 0000000000000..c3be18b5e993b
--- /dev/null
+++ b/ctest/testcrate/Cargo.toml
@@ -0,0 +1,35 @@
+[package]
+name = "testcrate"
+version = "0.1.0"
+authors = ["Alex Crichton "]
+build = "build.rs"
+edition = "2021"
+publish = false
+
+[build-dependencies]
+ctest2 = { path = ".." }
+cc = "1.0"
+
+[dependencies]
+libc = "0.2"
+
+[lib]
+name = "testcrate"
+test = false
+doctest = false
+
+[[bin]]
+name = "t1"
+test = false
+
+[[bin]]
+name = "t2"
+test = false
+
+[[bin]]
+name = "t1_cxx"
+test = false
+
+[[bin]]
+name = "t2_cxx"
+test = false
diff --git a/ctest/testcrate/build.rs b/ctest/testcrate/build.rs
new file mode 100644
index 0000000000000..7c9d554ef5148
--- /dev/null
+++ b/ctest/testcrate/build.rs
@@ -0,0 +1,99 @@
+fn main() {
+ use std::env;
+ let opt_level = env::var("OPT_LEVEL")
+ .ok()
+ .and_then(|s| s.parse().ok())
+ .unwrap_or(0);
+ let profile = env::var("PROFILE").unwrap_or_default();
+ if profile == "release" || opt_level >= 2 {
+ println!("cargo:rustc-cfg=optimized");
+ }
+ cc::Build::new()
+ .include("src")
+ .warnings(false)
+ .file("src/t1.c")
+ .compile("libt1.a");
+ println!("cargo:rerun-if-changed=src/t1.c");
+ println!("cargo:rerun-if-changed=src/t1.h");
+ cc::Build::new()
+ .warnings(false)
+ .file("src/t2.c")
+ .compile("libt2.a");
+ println!("cargo:rerun-if-changed=src/t2.c");
+ println!("cargo:rerun-if-changed=src/t2.h");
+ ctest2::TestGenerator::new()
+ .header("t1.h")
+ .include("src")
+ .fn_cname(|a, b| b.unwrap_or(a).to_string())
+ .type_name(move |ty, is_struct, is_union| match ty {
+ "T1Union" => ty.to_string(),
+ "Transparent" => ty.to_string(),
+ t if is_struct => format!("struct {}", t),
+ t if is_union => format!("union {}", t),
+ t => t.to_string(),
+ })
+ .volatile_item(t1_volatile)
+ .array_arg(t1_arrays)
+ .skip_roundtrip(|n| n == "Arr")
+ .generate("src/t1.rs", "t1gen.rs");
+ ctest2::TestGenerator::new()
+ .header("t2.h")
+ .include("src")
+ .type_name(move |ty, is_struct, is_union| match ty {
+ "T2Union" => ty.to_string(),
+ t if is_struct => format!("struct {}", t),
+ t if is_union => format!("union {}", t),
+ t => t.to_string(),
+ })
+ .skip_roundtrip(|_| true)
+ .generate("src/t2.rs", "t2gen.rs");
+
+ ctest2::TestGenerator::new()
+ .header("t1.h")
+ .language(ctest2::Lang::CXX)
+ .include("src")
+ .fn_cname(|a, b| b.unwrap_or(a).to_string())
+ .type_name(move |ty, is_struct, is_union| match ty {
+ "T1Union" => ty.to_string(),
+ "Transparent" => ty.to_string(),
+ t if is_struct => format!("struct {}", t),
+ t if is_union => format!("union {}", t),
+ t => t.to_string(),
+ })
+ .volatile_item(t1_volatile)
+ .array_arg(t1_arrays)
+ .skip_roundtrip(|n| n == "Arr")
+ .generate("src/t1.rs", "t1gen_cxx.rs");
+ ctest2::TestGenerator::new()
+ .header("t2.h")
+ .language(ctest2::Lang::CXX)
+ .include("src")
+ .type_name(move |ty, is_struct, is_union| match ty {
+ "T2Union" => ty.to_string(),
+ t if is_struct => format!("struct {}", t),
+ t if is_union => format!("union {}", t),
+ t => t.to_string(),
+ })
+ .skip_roundtrip(|_| true)
+ .generate("src/t2.rs", "t2gen_cxx.rs");
+}
+
+fn t1_volatile(i: ctest2::VolatileItemKind) -> bool {
+ use ctest2::VolatileItemKind::*;
+ match i {
+ StructField(ref n, ref f) if n == "V" && f == "v" => true,
+ Static(ref n) if n == "vol_ptr" => true,
+ FunctionArg(ref n, 0) if n == "T1_vol0" => true,
+ FunctionArg(ref n, 1) if n == "T1_vol2" => true,
+ FunctionRet(ref n) if n == "T1_vol1" || n == "T1_vol2" => true,
+ Static(ref n) if n == "T1_fn_ptr_vol" => true,
+ _ => false,
+ }
+}
+
+fn t1_arrays(n: &str, i: usize) -> bool {
+ match n {
+ "T1r" | "T1s" | "T1t" | "T1v" if i == 0 => true,
+ _ => false,
+ }
+}
diff --git a/ctest/testcrate/src/bin/t1.rs b/ctest/testcrate/src/bin/t1.rs
new file mode 100644
index 0000000000000..b49f8babf6b7f
--- /dev/null
+++ b/ctest/testcrate/src/bin/t1.rs
@@ -0,0 +1,7 @@
+#![cfg(not(test))]
+#![deny(warnings)]
+
+use libc::*;
+use testcrate::t1::*;
+
+include!(concat!(env!("OUT_DIR"), "/t1gen.rs"));
diff --git a/ctest/testcrate/src/bin/t1_cxx.rs b/ctest/testcrate/src/bin/t1_cxx.rs
new file mode 100644
index 0000000000000..f98c217362b2f
--- /dev/null
+++ b/ctest/testcrate/src/bin/t1_cxx.rs
@@ -0,0 +1,7 @@
+#![cfg(not(test))]
+#![deny(warnings)]
+
+use libc::*;
+use testcrate::t1::*;
+
+include!(concat!(env!("OUT_DIR"), "/t1gen_cxx.rs"));
diff --git a/ctest/testcrate/src/bin/t2.rs b/ctest/testcrate/src/bin/t2.rs
new file mode 100644
index 0000000000000..80a4ab563b1d6
--- /dev/null
+++ b/ctest/testcrate/src/bin/t2.rs
@@ -0,0 +1,6 @@
+#![cfg(not(test))]
+#![deny(warnings)]
+
+use testcrate::t2::*;
+
+include!(concat!(env!("OUT_DIR"), "/t2gen.rs"));
diff --git a/ctest/testcrate/src/bin/t2_cxx.rs b/ctest/testcrate/src/bin/t2_cxx.rs
new file mode 100644
index 0000000000000..982652013e627
--- /dev/null
+++ b/ctest/testcrate/src/bin/t2_cxx.rs
@@ -0,0 +1,6 @@
+#![cfg(not(test))]
+#![deny(warnings)]
+
+use testcrate::t2::*;
+
+include!(concat!(env!("OUT_DIR"), "/t2gen_cxx.rs"));
diff --git a/ctest/testcrate/src/lib.rs b/ctest/testcrate/src/lib.rs
new file mode 100644
index 0000000000000..7c749733dc655
--- /dev/null
+++ b/ctest/testcrate/src/lib.rs
@@ -0,0 +1,2 @@
+pub mod t1;
+pub mod t2;
diff --git a/ctest/testcrate/src/t1.c b/ctest/testcrate/src/t1.c
new file mode 100644
index 0000000000000..50c7b61864799
--- /dev/null
+++ b/ctest/testcrate/src/t1.c
@@ -0,0 +1,75 @@
+#include
+#include
+#include "t1.h"
+
+void T1a(void) {}
+void* T1b(void) { return NULL; }
+void* T1c(void* a) { return NULL; }
+int32_t T1d(unsigned a ) { return 0; }
+void T1e(unsigned a, const struct T1Bar* b) { }
+void T1f(void) {}
+void T1g(int32_t* a) {}
+void T1h(const int32_t* b) {}
+void T1i(int32_t a[4]) {}
+void T1j(const int32_t b[4]) {}
+void T1o(int32_t (*a)[4]) {}
+void T1p(int32_t (*const a)[4]) {}
+
+void T1r(Arr a) {}
+void T1s(const Arr a) {}
+void T1t(Arr* a) {}
+void T1v(const Arr* a) {}
+
+unsigned T1static = 3;
+
+const uint8_t T1_static_u8 = 42;
+uint8_t T1_static_mut_u8 = 37;
+
+uint8_t foo(uint8_t a, uint8_t b) { return a + b; }
+void bar(uint8_t a) { return; }
+void baz(void) { return; }
+
+uint32_t (*nested(uint8_t arg))(uint16_t) {
+ return NULL;
+}
+
+uint32_t (*nested2(uint8_t(*arg0)(uint8_t), uint16_t(*arg1)(uint16_t)))(uint16_t) {
+ return NULL;
+}
+
+uint8_t (*T1_static_mut_fn_ptr)(uint8_t, uint8_t) = foo;
+uint8_t (*const T1_static_const_fn_ptr_unsafe)(uint8_t, uint8_t) = foo;
+void (*const T1_static_const_fn_ptr_unsafe2)(uint8_t) = bar;
+void (*const T1_static_const_fn_ptr_unsafe3)(void) = baz;
+
+const uint8_t T1_static_right = 7;
+uint8_t (*T1_static_right2)(uint8_t, uint8_t) = foo;
+
+uint32_t (*(*T1_fn_ptr_s)(uint8_t))(uint16_t) = nested;
+uint32_t (*(*T1_fn_ptr_s2)(uint8_t(*arg0)(uint8_t), uint16_t(*arg1)(uint16_t)))(uint16_t) = nested2;
+
+const int32_t T1_arr0[2] = {0, 0};
+const int32_t T1_arr1[2][3] = {{0, 0, 0}, {0, 0, 0}};
+const int32_t T1_arr2[1][2][3] = {{{0, 0, 0}, {0, 0, 0}}};
+
+int32_t T1_arr3[2] = {0, 0};
+int32_t T1_arr4[2][3] = {{0, 0, 0}, {0, 0, 0}};
+int32_t T1_arr5[1][2][3] = {{{0, 0, 0}, {0, 0, 0}}};
+
+int32_t T1_arr42[1][2][3] = {{{0, 0, 0}, {0, 0, 0}}};
+const int16_t* T1_sref = (void*)(1337);
+
+const int32_t* T1_mut_opt_ref = NULL;
+int32_t* T1_mut_opt_mut_ref = NULL;
+const int32_t* T1_const_opt_const_ref = NULL;
+
+void (*const T1_opt_fn1)(void) = baz;
+uint32_t (*(*T1_opt_fn2)(uint8_t))(uint16_t) = nested;
+uint32_t (*(*T1_opt_fn3)(uint8_t(*arg0)(uint8_t), uint16_t(*arg1)(uint16_t)))(uint16_t) = nested2;
+
+volatile uint8_t* vol_ptr = NULL;
+void* T1_vol0(volatile void* x, void* a) { return a? a: (void*)x; }
+volatile void* T1_vol1(void* x, void* b) { return b? (volatile void*)x : (volatile void*)x; }
+volatile void* T1_vol2(void* c, volatile void* x) { return c? x : x; }
+
+uint8_t (* volatile T1_fn_ptr_vol)(uint8_t, uint8_t) = foo;
diff --git a/ctest/testcrate/src/t1.cpp b/ctest/testcrate/src/t1.cpp
new file mode 120000
index 0000000000000..1627f65e030cd
--- /dev/null
+++ b/ctest/testcrate/src/t1.cpp
@@ -0,0 +1 @@
+t1.c
\ No newline at end of file
diff --git a/ctest/testcrate/src/t1.h b/ctest/testcrate/src/t1.h
new file mode 100644
index 0000000000000..79afebddc3290
--- /dev/null
+++ b/ctest/testcrate/src/t1.h
@@ -0,0 +1,179 @@
+#include
+
+typedef int32_t T1Foo;
+
+#define T1N 5
+#define T1S "foo"
+
+struct T1Bar {
+ int32_t a;
+ uint32_t b;
+ T1Foo c;
+ uint8_t d;
+ int64_t e[T1N];
+ int64_t f[T1N][2];
+};
+
+struct T1Baz {
+ uint64_t a;
+ struct T1Bar b;
+};
+
+typedef union {
+ uint64_t a;
+ uint32_t b;
+} T1Union;
+
+union T1NoTypedefUnion {
+ uint64_t a;
+ uint32_t b;
+};
+
+struct T1StructWithUnion {
+ union T1NoTypedefUnion u;
+};
+
+typedef double T1TypedefDouble;
+typedef int* T1TypedefPtr;
+typedef struct T1Bar T1TypedefStruct;
+
+void T1a(void);
+void* T1b(void);
+void* T1c(void*);
+int32_t T1d(unsigned);
+void T1e(unsigned, const struct T1Bar*);
+void T1f(void);
+void T1g(int32_t* a);
+void T1h(const int32_t* b);
+void T1i(int32_t a[4]);
+void T1j(const int32_t b[4]);
+void T1o(int32_t (*a)[4]);
+void T1p(int32_t (*const a)[4]);
+
+typedef int32_t (Arr)[4];
+typedef int32_t Transparent;
+
+void T1r(Arr a);
+void T1s(const Arr a);
+void T1t(Arr* a);
+void T1v(const Arr* a);
+
+#define T1C 4
+
+extern uint32_t T1static;
+extern const uint8_t T1_static_u8;
+uint8_t T1_static_mut_u8;
+uint8_t (*T1_static_mut_fn_ptr)(uint8_t, uint8_t);
+extern uint8_t (*const T1_static_const_fn_ptr_unsafe)(uint8_t, uint8_t);
+extern void (*const T1_static_const_fn_ptr_unsafe2)(uint8_t);
+extern void (*const T1_static_const_fn_ptr_unsafe3)(void);
+
+extern const uint8_t T1_static_right;
+uint8_t (*T1_static_right2)(uint8_t, uint8_t);
+
+// T1_fn_ptr_nested: function pointer to a function, taking a uint8_t, and
+// returning a function pointer to a function taking a uint16_t and returning a
+// uint32_t
+uint32_t (*(*T1_fn_ptr_s)(uint8_t))(uint16_t);
+
+// T1_fn_ptr_nested: function pointer to a function, taking a function pointer
+// uint8_t -> uint8_t, and returning a function pointer to a function taking a
+// uint16_t and returning a uint32_t
+uint32_t (*(*T1_fn_ptr_s2)(uint8_t(*)(uint8_t), uint16_t(*)(uint16_t)))(uint16_t);
+
+extern const int32_t T1_arr0[2];
+extern const int32_t T1_arr1[2][3];
+extern const int32_t T1_arr2[1][2][3];
+
+extern int32_t T1_arr3[2];
+extern int32_t T1_arr4[2][3];
+extern int32_t T1_arr5[1][2][3];
+
+extern int32_t T1_arr42[1][2][3];
+
+extern const int16_t* T1_sref;
+
+extern const int32_t* T1_mut_opt_ref;
+extern int32_t* T1_mut_opt_mut_ref;
+extern const int32_t* T1_const_opt_const_ref;
+
+extern void (*const T1_opt_fn1)(void);
+uint32_t (*(*T1_opt_fn2)(uint8_t))(uint16_t);
+uint32_t (*(*T1_opt_fn3)(uint8_t(*)(uint8_t), uint16_t(*)(uint16_t)))(uint16_t);
+
+
+struct Q {
+ uint8_t* q0;
+ uint8_t** q1;
+ uint8_t q2;
+};
+
+
+struct T1_conflict_foo {
+ int a;
+};
+
+struct T1_conflict{
+ int foo;
+};
+
+// test packed structs
+//
+// on msvc there is only pragma pack
+// on clang and gcc there is a packed attribute
+
+# pragma pack(push,1)
+
+struct Pack {
+ uint8_t a;
+ uint16_t b;
+};
+
+# pragma pack(pop)
+
+# pragma pack(push,4)
+
+struct Pack4 {
+ uint8_t a;
+ uint32_t b;
+};
+
+# pragma pack(pop)
+
+// volatile pointers in struct fields:
+struct V {
+ volatile uint8_t* v;
+};
+
+// volatile pointers in externs:
+extern volatile uint8_t* vol_ptr;
+
+// volatile pointers in function arguments:
+void* T1_vol0(volatile void*, void*);
+volatile void* T1_vol1(void*, void*);
+volatile void* T1_vol2(void*, volatile void*);
+
+// volatile function pointers:
+uint8_t (*volatile T1_fn_ptr_vol)(uint8_t, uint8_t);
+
+#define LOG_MAX_LINE_LENGTH (1400)
+
+typedef struct {
+ long tv_sec;
+ int tv_usec;
+} timeval;
+
+typedef struct
+{
+ long level;
+ char const *file;
+ long line;
+ char const *module;
+ timeval tv;
+ char message[LOG_MAX_LINE_LENGTH];
+} log_record_t;
+
+typedef struct
+{
+ long double inner;
+} LongDoubleWrap;
diff --git a/ctest/testcrate/src/t1.rs b/ctest/testcrate/src/t1.rs
new file mode 100644
index 0000000000000..74896994eade6
--- /dev/null
+++ b/ctest/testcrate/src/t1.rs
@@ -0,0 +1,209 @@
+#![allow(dead_code)]
+
+use libc::*;
+
+pub type T1Foo = i32;
+pub const T1S: &str = "foo";
+
+pub const T1N: i32 = 5;
+
+macro_rules! i {
+ ($i:item) => {
+ $i
+ };
+}
+
+#[repr(C)]
+pub struct T1Bar {
+ pub a: i32,
+ pub b: u32,
+ pub c: T1Foo,
+ pub d: u8,
+ pub e: [i64; T1N as usize],
+ pub f: [[i64; 2]; T1N as usize],
+}
+
+#[repr(C)]
+pub struct T1Baz {
+ pub a: u64,
+ pub b: T1Bar,
+}
+
+#[repr(C)]
+pub union T1Union {
+ pub a: u64,
+ pub b: u32,
+}
+
+#[repr(C)]
+pub union T1NoTypedefUnion {
+ pub a: u64,
+ pub b: u32,
+}
+
+#[repr(C)]
+pub struct T1StructWithUnion {
+ pub u: T1NoTypedefUnion,
+}
+
+#[repr(transparent)]
+pub struct Transparent(i32);
+
+pub type T1TypedefDouble = c_double;
+pub type T1TypedefPtr = *mut c_int;
+pub type T1TypedefStruct = T1Bar;
+
+i! {
+ pub const T1C: u32 = 4;
+}
+
+const NOT_PRESENT: u32 = 5;
+
+pub type Arr = [i32; 4];
+
+extern "C" {
+ pub fn T1a();
+ pub fn T1b() -> *mut c_void;
+ pub fn T1c(a: *mut c_void) -> *mut c_void;
+ pub fn T1d(a: c_uint) -> i32;
+ pub fn T1e(a: c_uint, b: *const T1Bar);
+
+ #[link_name = "T1f"]
+ #[allow(clippy::unused_unit)]
+ pub fn f() -> ();
+
+ pub fn T1g(a: *mut [i32; 4]);
+ pub fn T1h(a: *const [i32; 4]) -> !;
+ pub fn T1i(a: *mut [i32; 4]);
+ pub fn T1j(a: *const [i32; 4]) -> !;
+ pub fn T1o(a: *mut *mut [i32; 4]);
+ pub fn T1p(a: *const *const [i32; 4]) -> !;
+
+ pub fn T1r(a: *mut Arr);
+ pub fn T1s(a: *const Arr) -> !;
+ pub fn T1t(a: *mut *mut Arr);
+ pub fn T1v(a: *const *const Arr) -> !;
+
+ pub static T1static: c_uint;
+}
+
+pub fn foo() {
+ assert_eq!(1, 1);
+}
+
+extern "C" {
+ pub static T1_static_u8: u8;
+ pub static mut T1_static_mut_u8: u8;
+ pub static mut T1_static_mut_fn_ptr: extern "C" fn(u8, u8) -> u8;
+ pub static T1_static_const_fn_ptr_unsafe: unsafe extern "C" fn(u8, u8) -> u8;
+ pub static T1_static_const_fn_ptr_unsafe2: unsafe extern "C" fn(u8) -> ();
+ pub static T1_static_const_fn_ptr_unsafe3: unsafe extern "C" fn() -> ();
+
+ #[link_name = "T1_static_right"]
+ pub static T1_static_wrong: u8;
+ #[link_name = "T1_static_right2"]
+ pub static mut T1_static_wrong2: extern "C" fn(u8, u8) -> u8;
+
+ pub static T1_fn_ptr_s: unsafe extern "C" fn(u8) -> extern "C" fn(u16) -> u32;
+ pub static T1_fn_ptr_s2: unsafe extern "C" fn(
+ extern "C" fn(u8) -> u8,
+ extern "C" fn(u16) -> u16,
+ ) -> extern "C" fn(u16) -> u32;
+
+ pub static T1_arr0: [i32; 2];
+ pub static T1_arr1: [[i32; 3]; 2];
+ pub static T1_arr2: [[[i32; 3]; 2]; 1];
+
+ pub static mut T1_arr3: [i32; 2];
+ pub static mut T1_arr4: [[i32; 3]; 2];
+ pub static mut T1_arr5: [[[i32; 3]; 2]; 1];
+
+ #[link_name = "T1_arr42"]
+ pub static mut T1_arr6: [[[i32; 3]; 2]; 1];
+
+ pub static mut T1_sref: &'static i16;
+
+ pub static mut T1_mut_opt_ref: Option<&'static i32>;
+ pub static mut T1_mut_opt_mut_ref: Option<&'static mut i32>;
+ pub static T1_const_opt_const_ref: Option<&'static i32>;
+
+ pub static T1_opt_fn1: Option ()>;
+ pub static T1_opt_fn2: Option extern "C" fn(u16) -> u32>;
+ pub static T1_opt_fn3: Option<
+ unsafe extern "C" fn(
+ extern "C" fn(u8) -> u8,
+ extern "C" fn(u16) -> u16,
+ ) -> extern "C" fn(u16) -> u32,
+ >;
+}
+
+#[repr(C)]
+pub struct Q {
+ pub q0: *mut u8,
+ pub q1: *mut *mut u8,
+ pub q2: u8,
+}
+
+#[repr(C)]
+pub struct T1_conflict_foo {
+ a: i32,
+}
+
+#[repr(C)]
+pub struct T1_conflict {
+ pub foo: i32,
+}
+
+#[repr(C, packed)]
+pub struct Pack {
+ pub a: u8,
+ pub b: u16,
+}
+
+#[repr(C, packed(4))]
+pub struct Pack4 {
+ pub a: u8,
+ pub b: u32,
+}
+
+#[repr(C)]
+pub struct V {
+ pub v: *mut u8,
+}
+
+extern "C" {
+ pub static mut vol_ptr: *mut u8;
+ pub fn T1_vol0(arg0: *mut c_void, arg1: *mut c_void) -> *mut c_void;
+ pub fn T1_vol1(arg0: *mut c_void, arg1: *mut c_void) -> *mut c_void;
+ pub fn T1_vol2(arg0: *mut c_void, arg1: *mut c_void) -> *mut c_void;
+ pub static T1_fn_ptr_vol: Option u8>;
+}
+
+pub const LOG_MAX_LINE_LENGTH: usize = 1400;
+
+#[repr(C)]
+struct timeval {
+ tv_sec: c_long,
+ tv_usec: c_int,
+}
+
+#[repr(C)]
+struct log_record_t {
+ level: c_long,
+ file: *const c_char,
+ line: c_long,
+ module: *const c_char,
+ tv: timeval,
+ message: [c_char; LOG_MAX_LINE_LENGTH],
+}
+
+#[cfg(not(any(target_pointer_width = "16", target_pointer_width = "32")))]
+#[repr(C, align(16))]
+struct LongDoubleWrap {
+ inner: u128,
+}
+#[cfg(any(target_pointer_width = "16", target_pointer_width = "32"))]
+#[repr(C)]
+struct LongDoubleWrap {
+ inner: c_double,
+}
diff --git a/ctest/testcrate/src/t2.c b/ctest/testcrate/src/t2.c
new file mode 100644
index 0000000000000..ceeddfcf509ea
--- /dev/null
+++ b/ctest/testcrate/src/t2.c
@@ -0,0 +1,2 @@
+
+void T2a() {}
diff --git a/ctest/testcrate/src/t2.cpp b/ctest/testcrate/src/t2.cpp
new file mode 120000
index 0000000000000..02be41dad0810
--- /dev/null
+++ b/ctest/testcrate/src/t2.cpp
@@ -0,0 +1 @@
+t2.c
\ No newline at end of file
diff --git a/ctest/testcrate/src/t2.h b/ctest/testcrate/src/t2.h
new file mode 100644
index 0000000000000..9f99e11a1e79d
--- /dev/null
+++ b/ctest/testcrate/src/t2.h
@@ -0,0 +1,23 @@
+#include
+
+typedef int32_t T2Foo;
+typedef int8_t T2Bar;
+
+typedef T2Foo T2TypedefFoo;
+typedef unsigned T2TypedefInt;
+
+struct T2Baz {
+ int8_t _a;
+ int64_t a;
+ uint32_t b;
+};
+
+typedef struct {
+ uint32_t a;
+ int64_t b;
+} T2Union;
+
+static void T2a(void) {}
+
+#define T2C 4
+#define T2S "a"
diff --git a/ctest/testcrate/src/t2.rs b/ctest/testcrate/src/t2.rs
new file mode 100644
index 0000000000000..bafeaef7cd897
--- /dev/null
+++ b/ctest/testcrate/src/t2.rs
@@ -0,0 +1,36 @@
+use libc::*;
+
+pub type T2Foo = u32;
+pub type T2Bar = u32;
+
+pub type T2TypedefFoo = T2Foo;
+pub type T2TypedefInt = c_int;
+
+macro_rules! i {
+ ($i:item) => {
+ $i
+ };
+}
+
+#[repr(C)]
+#[derive(Debug)]
+pub struct T2Baz {
+ pub a: i64,
+ pub b: u32,
+}
+
+#[repr(C)]
+pub union T2Union {
+ pub a: u32,
+ pub b: i64,
+}
+
+pub const T2C: i32 = 5;
+
+i! {
+ pub const T2S: &str = "b";
+}
+
+extern "C" {
+ pub fn T2a();
+}
diff --git a/ctest/testcrate/tests/all.rs b/ctest/testcrate/tests/all.rs
new file mode 100644
index 0000000000000..e3cdbd245ff50
--- /dev/null
+++ b/ctest/testcrate/tests/all.rs
@@ -0,0 +1,124 @@
+use std::collections::HashSet;
+use std::env;
+use std::process::{Command, ExitStatus};
+
+fn cmd(name: &str) -> Command {
+ let mut p = env::current_exe().unwrap();
+ p.pop();
+ if p.file_name().unwrap().to_str() == Some("deps") {
+ p.pop();
+ }
+ p.push(name);
+ Command::new(p)
+}
+
+#[test]
+fn t1() {
+ let (o, status) = output(&mut cmd("t1"));
+ assert!(status.success(), "{}", o);
+ assert!(!o.contains("bad "), "{}", o);
+ eprintln!("o: {}", o);
+}
+
+#[test]
+fn t1_cxx() {
+ let (o, status) = output(&mut cmd("t1_cxx"));
+ assert!(status.success(), "{}", o);
+ assert!(!o.contains("bad "), "{}", o);
+}
+
+#[test]
+fn t2() {
+ let (o, status) = output(&mut cmd("t2"));
+ assert!(!status.success(), "{}", o);
+ let errors = [
+ "bad T2Foo signed",
+ "bad T2TypedefFoo signed",
+ "bad T2TypedefInt signed",
+ "bad T2Bar size",
+ "bad T2Bar align",
+ "bad T2Bar signed",
+ "bad T2Baz size",
+ "bad field offset a of T2Baz",
+ "bad field type a of T2Baz",
+ "bad field offset b of T2Baz",
+ "bad field type b of T2Baz",
+ "bad T2a function pointer",
+ "bad T2C value at byte 0",
+ "bad T2S string",
+ "bad T2Union size",
+ "bad field type b of T2Union",
+ "bad field offset b of T2Union",
+ ];
+ let mut errors = errors.iter().cloned().collect::>();
+
+ let mut bad = false;
+ for line in o.lines().filter(|l| l.starts_with("bad ")) {
+ let msg = &line[..line.find(":").unwrap()];
+ if !errors.remove(&msg) {
+ println!("unknown error: {}", msg);
+ bad = true;
+ }
+ }
+
+ for error in errors {
+ println!("didn't find error: {}", error);
+ bad = true;
+ }
+ if bad {
+ println!("output was:\n\n{}", o);
+ panic!();
+ }
+}
+
+#[test]
+fn t2_cxx() {
+ let (o, status) = output(&mut cmd("t2_cxx"));
+ assert!(!status.success(), "{}", o);
+ let errors = [
+ "bad T2Foo signed",
+ "bad T2TypedefFoo signed",
+ "bad T2TypedefInt signed",
+ "bad T2Bar size",
+ "bad T2Bar align",
+ "bad T2Bar signed",
+ "bad T2Baz size",
+ "bad field offset a of T2Baz",
+ "bad field type a of T2Baz",
+ "bad field offset b of T2Baz",
+ "bad field type b of T2Baz",
+ "bad T2a function pointer",
+ "bad T2C value at byte 0",
+ "bad T2S string",
+ "bad T2Union size",
+ "bad field type b of T2Union",
+ "bad field offset b of T2Union",
+ ];
+ let mut errors = errors.iter().cloned().collect::>();
+
+ let mut bad = false;
+ for line in o.lines().filter(|l| l.starts_with("bad ")) {
+ let msg = &line[..line.find(":").unwrap()];
+ if !errors.remove(&msg) {
+ println!("unknown error: {}", msg);
+ bad = true;
+ }
+ }
+
+ for error in errors {
+ println!("didn't find error: {}", error);
+ bad = true;
+ }
+ if bad {
+ println!("output was:\n\n{}", o);
+ panic!();
+ }
+}
+
+fn output(cmd: &mut Command) -> (String, ExitStatus) {
+ let output = cmd.output().unwrap();
+ let stdout = String::from_utf8(output.stdout).unwrap();
+ let stderr = String::from_utf8(output.stderr).unwrap();
+
+ (stdout + &stderr, output.status)
+}