summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-18 05:39:07 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-18 05:39:07 +0000
commitaf6b8ed095f88f1df2116cdc7a9d44872cfa6074 (patch)
tree1f2df671c1f8033d5ed83f056167a0911f8d2a57 /tests
parentInitial commit. (diff)
downloadrust-cbindgen-upstream.tar.xz
rust-cbindgen-upstream.zip
Adding upstream version 0.26.0.upstream/0.26.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'tests')
-rw-r--r--tests/depfile.rs108
-rw-r--r--tests/depfile/Readme.md11
-rw-r--r--tests/depfile/cbindgen_test.cmake27
-rw-r--r--tests/rust/abi_string.rs5
-rw-r--r--tests/rust/alias.rs32
-rw-r--r--tests/rust/annotation.rs43
-rw-r--r--tests/rust/array.rs7
-rw-r--r--tests/rust/array.toml2
-rw-r--r--tests/rust/asserted_cast.rs44
-rw-r--r--tests/rust/asserted_cast.toml10
-rw-r--r--tests/rust/assoc_const_conflict.rs9
-rw-r--r--tests/rust/assoc_constant.rs14
-rw-r--r--tests/rust/associated_constant_panic.rs7
-rw-r--r--tests/rust/associated_in_body.rs54
-rw-r--r--tests/rust/associated_in_body.toml11
-rw-r--r--tests/rust/bitflags.rs61
-rw-r--r--tests/rust/bitflags.toml5
-rw-r--r--tests/rust/body.rs55
-rw-r--r--tests/rust/body.toml41
-rw-r--r--tests/rust/box.rs19
-rw-r--r--tests/rust/box.toml18
-rw-r--r--tests/rust/cdecl.rs41
-rw-r--r--tests/rust/cell.rs11
-rw-r--r--tests/rust/cfg.rs64
-rw-r--r--tests/rust/cfg.toml25
-rw-r--r--tests/rust/cfg_2.rs32
-rw-r--r--tests/rust/cfg_2.toml11
-rw-r--r--tests/rust/cfg_field.rs11
-rw-r--r--tests/rust/char.rs7
-rw-r--r--tests/rust/const_conflict.rs9
-rw-r--r--tests/rust/const_generics.rs15
-rw-r--r--tests/rust/const_generics_arrayvec.rs17
-rw-r--r--tests/rust/const_generics_bool.rs57
-rw-r--r--tests/rust/const_generics_byte.rs29
-rw-r--r--tests/rust/const_generics_char.rs20
-rw-r--r--tests/rust/const_generics_constant.rs18
-rw-r--r--tests/rust/const_generics_thru.rs22
-rw-r--r--tests/rust/const_transparent.rs4
-rw-r--r--tests/rust/constant.rs38
-rw-r--r--tests/rust/constant_big.rs8
-rw-r--r--tests/rust/constant_constexpr.rs14
-rw-r--r--tests/rust/constant_constexpr.toml5
-rw-r--r--tests/rust/constant_sort_name.rs7
-rw-r--r--tests/rust/constant_sort_name.toml2
-rw-r--r--tests/rust/constant_sort_none.rs7
-rw-r--r--tests/rust/constant_user_defined_type.rs17
-rw-r--r--tests/rust/custom_header.rs3
-rw-r--r--tests/rust/custom_header.toml11
-rw-r--r--tests/rust/cython_options.rs0
-rw-r--r--tests/rust/cython_options.toml6
-rw-r--r--tests/rust/decl_name_conflicting.rs14
-rw-r--r--tests/rust/deprecated.rs52
-rw-r--r--tests/rust/deprecated.toml20
-rw-r--r--tests/rust/derive_ostream.rs59
-rw-r--r--tests/rust/destructor_and_copy_ctor.rs97
-rw-r--r--tests/rust/destructor_and_copy_ctor.toml16
-rw-r--r--tests/rust/display_list.rs18
-rw-r--r--tests/rust/doclength_short.rs16
-rw-r--r--tests/rust/doclength_short.toml1
-rw-r--r--tests/rust/docstyle_auto.rs4
-rw-r--r--tests/rust/docstyle_auto.toml1
-rw-r--r--tests/rust/docstyle_c99.rs4
-rw-r--r--tests/rust/docstyle_c99.toml1
-rw-r--r--tests/rust/docstyle_doxy.rs4
-rw-r--r--tests/rust/docstyle_doxy.toml1
-rw-r--r--tests/rust/documentation.rs21
-rw-r--r--tests/rust/documentation_attr.rs12
-rw-r--r--tests/rust/enum.rs166
-rw-r--r--tests/rust/enum.toml39
-rw-r--r--tests/rust/enum_discriminant.rs15
-rw-r--r--tests/rust/enum_discriminant.toml2
-rw-r--r--tests/rust/enum_self.rs15
-rw-r--r--tests/rust/euclid.rs81
-rw-r--r--tests/rust/exclude_generic_monomorph.rs10
-rw-r--r--tests/rust/exclude_generic_monomorph.toml24
-rw-r--r--tests/rust/export_name.rs4
-rw-r--r--tests/rust/extern.rs11
-rw-r--r--tests/rust/extern_2.rs11
-rw-r--r--tests/rust/fns.rs16
-rw-r--r--tests/rust/forward_declaration.rs21
-rw-r--r--tests/rust/forward_declaration.toml23
-rw-r--r--tests/rust/function_args.rs25
-rw-r--r--tests/rust/function_noreturn.rs14
-rw-r--r--tests/rust/function_noreturn.toml12
-rw-r--r--tests/rust/function_ptr.rs6
-rw-r--r--tests/rust/function_sort_name.rs15
-rw-r--r--tests/rust/function_sort_name.toml2
-rw-r--r--tests/rust/function_sort_none.rs15
-rw-r--r--tests/rust/generic_pointer.rs11
-rw-r--r--tests/rust/global_attr.rs1
-rw-r--r--tests/rust/global_variable.rs5
-rw-r--r--tests/rust/ignore.rs12
-rw-r--r--tests/rust/include.rs0
-rw-r--r--tests/rust/include.toml1
-rw-r--r--tests/rust/include_guard.rs3
-rw-r--r--tests/rust/include_guard.toml2
-rw-r--r--tests/rust/include_item.rs10
-rw-r--r--tests/rust/include_item.toml2
-rw-r--r--tests/rust/include_specific.rs0
-rw-r--r--tests/rust/include_specific.toml2
-rw-r--r--tests/rust/infinite_recursion_typedef_monomorph.rs2
-rw-r--r--tests/rust/inner_mod.rs10
-rw-r--r--tests/rust/item_types.rs12
-rw-r--r--tests/rust/item_types.toml3
-rw-r--r--tests/rust/item_types_renamed.rs12
-rw-r--r--tests/rust/item_types_renamed.toml4
-rw-r--r--tests/rust/layout.rs108
-rw-r--r--tests/rust/layout.toml29
-rw-r--r--tests/rust/layout_aligned_opaque.rs67
-rw-r--r--tests/rust/layout_aligned_opaque.toml22
-rw-r--r--tests/rust/layout_packed_opaque.rs61
-rw-r--r--tests/rust/layout_packed_opaque.toml22
-rw-r--r--tests/rust/lifetime_arg.rs14
-rw-r--r--tests/rust/linestyle_cr.rs8
-rw-r--r--tests/rust/linestyle_cr.toml1
-rw-r--r--tests/rust/linestyle_crlf.rs8
-rw-r--r--tests/rust/linestyle_crlf.toml1
-rw-r--r--tests/rust/linestyle_lf.rs8
-rw-r--r--tests/rust/linestyle_lf.toml1
-rw-r--r--tests/rust/mangle.rs19
-rw-r--r--tests/rust/mangle.toml3
-rw-r--r--tests/rust/manuallydrop.rs22
-rw-r--r--tests/rust/manuallydrop.toml18
-rw-r--r--tests/rust/maybeuninit.rs13
-rw-r--r--tests/rust/maybeuninit.toml18
-rw-r--r--tests/rust/monomorph_1.rs28
-rw-r--r--tests/rust/monomorph_2.rs15
-rw-r--r--tests/rust/monomorph_3.rs28
-rw-r--r--tests/rust/must_use.rs18
-rw-r--r--tests/rust/must_use.toml14
-rw-r--r--tests/rust/namespace_constant.rs11
-rw-r--r--tests/rust/namespace_constant.toml1
-rw-r--r--tests/rust/namespaces_constant.rs11
-rw-r--r--tests/rust/namespaces_constant.toml1
-rw-r--r--tests/rust/nested_import.rs1
-rw-r--r--tests/rust/no_includes.rs3
-rw-r--r--tests/rust/no_includes.toml1
-rw-r--r--tests/rust/non_pub_extern.rs13
-rw-r--r--tests/rust/nonnull.rs19
-rw-r--r--tests/rust/nonnull_attribute.rs55
-rw-r--r--tests/rust/nonnull_attribute.toml10
-rw-r--r--tests/rust/nonzero.rs30
-rw-r--r--tests/rust/nonzero.toml13
-rw-r--r--tests/rust/opaque.rs10
-rw-r--r--tests/rust/opaque.toml17
-rw-r--r--tests/rust/pin.rs8
-rw-r--r--tests/rust/pin.toml21
-rw-r--r--tests/rust/pragma_once.skip_warning_as_error.rs3
-rw-r--r--tests/rust/pragma_once.skip_warning_as_error.toml1
-rw-r--r--tests/rust/prefix.rs20
-rw-r--r--tests/rust/prefix.toml2
-rw-r--r--tests/rust/prefixed_struct_literal.rs14
-rw-r--r--tests/rust/prefixed_struct_literal.toml2
-rw-r--r--tests/rust/prefixed_struct_literal_deep.rs20
-rw-r--r--tests/rust/prefixed_struct_literal_deep.toml2
-rw-r--r--tests/rust/ptrs_as_arrays.rs19
-rw-r--r--tests/rust/raw_ident.rs20
-rw-r--r--tests/rust/raw_lines.rs3
-rw-r--r--tests/rust/raw_lines.toml3
-rw-r--r--tests/rust/rename.rs47
-rw-r--r--tests/rust/rename.toml5
-rw-r--r--tests/rust/rename_case.rs19
-rw-r--r--tests/rust/renaming_overrides_prefixing.rs13
-rw-r--r--tests/rust/renaming_overrides_prefixing.toml6
-rw-r--r--tests/rust/reserved.rs37
-rw-r--r--tests/rust/sentinel.rs23
-rw-r--r--tests/rust/sentinel.toml3
-rw-r--r--tests/rust/simplify_option_ptr.rs28
-rw-r--r--tests/rust/size_types.rs15
-rw-r--r--tests/rust/size_types.toml1
-rw-r--r--tests/rust/static.rs17
-rw-r--r--tests/rust/std_lib.rs6
-rw-r--r--tests/rust/struct.rs38
-rw-r--r--tests/rust/struct_literal.rs23
-rw-r--r--tests/rust/struct_literal_order.rs28
-rw-r--r--tests/rust/struct_self.rs14
-rw-r--r--tests/rust/style_crash.rs7
-rw-r--r--tests/rust/swift_name.rs133
-rw-r--r--tests/rust/swift_name.toml4
-rw-r--r--tests/rust/transform_op.rs43
-rw-r--r--tests/rust/transform_op.toml7
-rw-r--r--tests/rust/transparent.rs67
-rw-r--r--tests/rust/typedef.rs11
-rw-r--r--tests/rust/union.rs27
-rw-r--r--tests/rust/union_self.rs14
-rw-r--r--tests/rust/using_namespaces.rs3
-rw-r--r--tests/rust/using_namespaces.toml2
-rw-r--r--tests/rust/va_list.rs11
-rw-r--r--tests/rust/zst.rs10
-rw-r--r--tests/testing-helpers.h20
-rw-r--r--tests/tests.rs371
191 files changed, 3987 insertions, 0 deletions
diff --git a/tests/depfile.rs b/tests/depfile.rs
new file mode 100644
index 0000000..7d629f3
--- /dev/null
+++ b/tests/depfile.rs
@@ -0,0 +1,108 @@
+use std::fs::read_to_string;
+use std::path::PathBuf;
+use std::process::Command;
+
+static CBINDGEN_PATH: &str = env!("CARGO_BIN_EXE_cbindgen");
+
+fn test_project(project_path: &str) {
+ let mut cmake_cmd = Command::new("cmake");
+ cmake_cmd.arg("--version");
+ cmake_cmd
+ .output()
+ .expect("CMake --version failed - Is CMake installed?");
+
+ let mut cmake_configure = Command::new("cmake");
+ let build_dir = PathBuf::from(project_path).join("build");
+ if build_dir.exists() {
+ std::fs::remove_dir_all(&build_dir).expect("Failed to remove old build directory");
+ }
+ let project_dir = PathBuf::from(project_path);
+
+ let cbindgen_define = format!("-DCBINDGEN_PATH={}", CBINDGEN_PATH);
+ cmake_configure
+ .arg("-S")
+ .arg(project_path)
+ .arg("-B")
+ .arg(&build_dir)
+ .arg(cbindgen_define);
+ let output = cmake_configure.output().expect("Failed to execute process");
+ let stdout_str = String::from_utf8(output.stdout).unwrap();
+ let stderr_str = String::from_utf8(output.stderr).unwrap();
+ assert!(
+ output.status.success(),
+ "Configuring test project failed: stdout: `{}`, stderr: `{}`",
+ stdout_str,
+ stderr_str
+ );
+ let depfile_path = build_dir.join("depfile.d");
+ assert!(
+ !depfile_path.exists(),
+ "depfile should not exist before building"
+ );
+
+ // Do the clean first build
+ let mut cmake_build = Command::new("cmake");
+ cmake_build.arg("--build").arg(&build_dir);
+ let output = cmake_build.output().expect("Failed to execute process");
+ assert!(output.status.success(), "Building test project failed");
+ let out_str = String::from_utf8(output.stdout).unwrap();
+ assert!(
+ out_str.contains("Running cbindgen"),
+ "cbindgen rule did not run. Output: {}",
+ out_str
+ );
+
+ assert!(
+ depfile_path.exists(),
+ "depfile does not exist after building"
+ );
+
+ let expected_dependencies_filepath = PathBuf::from(project_path)
+ .join("expectations")
+ .join("dependencies");
+ assert!(
+ expected_dependencies_filepath.exists(),
+ "Test did not define expected dependencies. Please read the Readme.md"
+ );
+ let expected_deps =
+ read_to_string(expected_dependencies_filepath).expect("Failed to read dependencies");
+ let depinfo = read_to_string(depfile_path).expect("Failed to read dependencies");
+ // Assumes a single rule in the file - all deps are listed to the rhs of the `:`.
+ let actual_deps = depinfo.split(':').collect::<Vec<_>>()[1];
+ // Strip the line breaks.
+ let actual_deps = actual_deps.replace("\\\n", " ");
+ // I don't want to deal with supporting escaped whitespace when splitting at whitespace,
+ // so the tests don't support being run in a directory containing whitespace.
+ assert!(
+ !actual_deps.contains("\\ "),
+ "The tests directory may not contain any whitespace"
+ );
+ let dep_list: Vec<&str> = actual_deps.split_ascii_whitespace().collect();
+ let expected_dep_list: Vec<String> = expected_deps
+ .lines()
+ .map(|dep| project_dir.join(dep).to_str().unwrap().to_string())
+ .collect();
+ assert_eq!(dep_list, expected_dep_list);
+
+ let output = cmake_build.output().expect("Failed to execute process");
+ assert!(output.status.success(), "Building test project failed");
+ let out_str = String::from_utf8(output.stdout).unwrap();
+ assert!(
+ !out_str.contains("Running cbindgen"),
+ "cbindgen rule ran on second build"
+ );
+
+ std::fs::remove_dir_all(build_dir).expect("Failed to remove old build directory");
+}
+
+macro_rules! test_file {
+ ($test_function_name:ident, $name:expr, $file:tt) => {
+ #[test]
+ fn $test_function_name() {
+ test_project($file);
+ }
+ };
+}
+
+// This file is generated by build.rs
+include!(concat!(env!("OUT_DIR"), "/depfile_tests.rs"));
diff --git a/tests/depfile/Readme.md b/tests/depfile/Readme.md
new file mode 100644
index 0000000..d7f8b1a
--- /dev/null
+++ b/tests/depfile/Readme.md
@@ -0,0 +1,11 @@
+This a folder containing tests for `--depfile` parameter.
+Each test is in a subfolder and defines a minimum CMake project,
+which uses cbindgen to generate Rust bindings and the `--depfile`
+parameter to determine when to regenerate.
+The outer test can the build the project, assert that rebuilding does not regenerate the
+bindings, and then assert that touching the files involved does trigger rebuilding.
+
+The test project must contain an `expectations` folder, containing a file `dependencies`.
+This `dependencies` should list all files that should be listed as dependencies in the generated
+depfile. The paths should be relative to the project folder (i.e. to the folder containing
+`expectations`).
diff --git a/tests/depfile/cbindgen_test.cmake b/tests/depfile/cbindgen_test.cmake
new file mode 100644
index 0000000..752c3c3
--- /dev/null
+++ b/tests/depfile/cbindgen_test.cmake
@@ -0,0 +1,27 @@
+# Common code used across the different tests
+
+if(NOT DEFINED CBINDGEN_PATH)
+ message(FATAL_ERROR "Path to cbindgen not specified")
+endif()
+
+# Promote to cache
+set(CBINDGEN_PATH "${CBINDGEN_PATH}" CACHE INTERNAL "")
+
+function(add_cbindgen_command custom_target_name header_destination)
+ # Place the depfile always at the same location, so the outer test framework can locate the file easily
+ set(depfile_destination "${CMAKE_BINARY_DIR}/depfile.d")
+ add_custom_command(
+ OUTPUT
+ "${header_destination}" "${depfile_destination}"
+ COMMAND
+ "${CBINDGEN_PATH}"
+ --output "${header_destination}"
+ --depfile "${depfile_destination}"
+ ${ARGN}
+ DEPFILE "${depfile_destination}"
+ WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
+ COMMENT "Running cbindgen"
+ COMMAND_EXPAND_LISTS
+ )
+ add_custom_target("${custom_target_name}" ALL DEPENDS "${header_destination}")
+endfunction() \ No newline at end of file
diff --git a/tests/rust/abi_string.rs b/tests/rust/abi_string.rs
new file mode 100644
index 0000000..f685532
--- /dev/null
+++ b/tests/rust/abi_string.rs
@@ -0,0 +1,5 @@
+#[no_mangle]
+pub extern "C" fn c() {}
+
+#[no_mangle]
+pub extern "C-unwind" fn c_unwind() {}
diff --git a/tests/rust/alias.rs b/tests/rust/alias.rs
new file mode 100644
index 0000000..86e3e00
--- /dev/null
+++ b/tests/rust/alias.rs
@@ -0,0 +1,32 @@
+#[repr(C)]
+struct Dep {
+ a: i32,
+ b: f32,
+}
+
+#[repr(C)]
+struct Foo<X> {
+ a: X,
+ b: X,
+ c: Dep,
+}
+
+#[repr(u32)]
+enum Status {
+ Ok,
+ Err,
+}
+
+type IntFoo = Foo<i32>;
+type DoubleFoo = Foo<f64>;
+
+type Unit = i32;
+type SpecialStatus = Status;
+
+#[no_mangle]
+pub extern "C" fn root(
+ x: IntFoo,
+ y: DoubleFoo,
+ z: Unit,
+ w: SpecialStatus
+) { }
diff --git a/tests/rust/annotation.rs b/tests/rust/annotation.rs
new file mode 100644
index 0000000..7070592
--- /dev/null
+++ b/tests/rust/annotation.rs
@@ -0,0 +1,43 @@
+/// cbindgen:derive-lt=true
+/// cbindgen:derive-lte=true
+/// cbindgen:derive-constructor=true
+/// cbindgen:rename-all=GeckoCase
+#[repr(C)]
+struct A(i32);
+
+/// cbindgen:field-names=[x, y]
+#[repr(C)]
+struct B(i32, f32);
+
+/// cbindgen:trailing-values=[Z, W]
+#[repr(u32)]
+enum C {
+ X = 2,
+ Y,
+}
+
+/// cbindgen:derive-helper-methods=true
+#[repr(u8)]
+enum F {
+ Foo(i16),
+ Bar { x: u8, y: i16 },
+ Baz
+}
+
+/// cbindgen:derive-helper-methods
+#[repr(C, u8)]
+enum H {
+ Hello(i16),
+ There { x: u8, y: i16 },
+ Everyone
+}
+
+#[no_mangle]
+pub extern "C" fn root(
+ x: A,
+ y: B,
+ z: C,
+ f: F,
+ h: H,
+) { }
+
diff --git a/tests/rust/array.rs b/tests/rust/array.rs
new file mode 100644
index 0000000..fb33537
--- /dev/null
+++ b/tests/rust/array.rs
@@ -0,0 +1,7 @@
+#[repr(C)]
+enum Foo {
+ A([f32; 20])
+}
+
+#[no_mangle]
+pub extern "C" fn root(a: Foo) {}
diff --git a/tests/rust/array.toml b/tests/rust/array.toml
new file mode 100644
index 0000000..ea03916
--- /dev/null
+++ b/tests/rust/array.toml
@@ -0,0 +1,2 @@
+[enum]
+derive_helper_methods = true
diff --git a/tests/rust/asserted_cast.rs b/tests/rust/asserted_cast.rs
new file mode 100644
index 0000000..983131b
--- /dev/null
+++ b/tests/rust/asserted_cast.rs
@@ -0,0 +1,44 @@
+/// cbindgen:prefix-with-name
+#[repr(C, u8)]
+pub enum H {
+ /// cbindgen:variant-mut-cast-attributes=MY_ATTRS
+ Foo(i16),
+ /// cbindgen:variant-const-cast-attributes=MY_ATTRS
+ Bar { x: u8, y: i16 },
+ /// cbindgen:variant-is-attributes=MY_ATTRS
+ Baz
+}
+
+/// cbindgen:prefix-with-name
+#[repr(C, u8, u16)]
+pub enum I {
+ /// cbindgen:variant-constructor-attributes=MY_ATTRS
+ Foo(i16),
+ /// cbindgen:eq-attributes=MY_ATTRS
+ Bar { x: u8, y: i16 },
+ Baz
+}
+
+/// cbindgen:prefix-with-name
+#[repr(C, u8)]
+pub enum J {
+ Foo(i16),
+ Bar { x: u8, y: i16 },
+ Baz
+}
+
+/// cbindgen:prefix-with-name
+#[repr(u8)]
+pub enum K {
+ Foo(i16),
+ Bar { x: u8, y: i16 },
+ Baz
+}
+
+#[no_mangle]
+pub extern "C" fn foo(
+ h: H,
+ i: I,
+ j: J,
+ k: K,
+) {}
diff --git a/tests/rust/asserted_cast.toml b/tests/rust/asserted_cast.toml
new file mode 100644
index 0000000..4ed3557
--- /dev/null
+++ b/tests/rust/asserted_cast.toml
@@ -0,0 +1,10 @@
+header = """
+#define MY_ASSERT(...) do { } while (0)
+#define MY_ATTRS __attribute((noinline))
+"""
+
+[enum]
+derive_helper_methods = true
+derive_const_casts = true
+derive_mut_casts = true
+cast_assert_name = "MY_ASSERT"
diff --git a/tests/rust/assoc_const_conflict.rs b/tests/rust/assoc_const_conflict.rs
new file mode 100644
index 0000000..7907e32
--- /dev/null
+++ b/tests/rust/assoc_const_conflict.rs
@@ -0,0 +1,9 @@
+#[repr(C)]
+struct Foo {}
+
+pub const Foo_FOO: u32 = 42;
+
+impl Foo {
+ const FOO: i32 = 0;
+}
+
diff --git a/tests/rust/assoc_constant.rs b/tests/rust/assoc_constant.rs
new file mode 100644
index 0000000..57417ef
--- /dev/null
+++ b/tests/rust/assoc_constant.rs
@@ -0,0 +1,14 @@
+#[repr(C)]
+struct Foo {}
+
+impl Foo {
+ pub const GA: i32 = 10;
+ pub const BU: &'static str = "hello world";
+ pub const ZO: f32 = 3.14;
+
+ pub(crate) const DONT_EXPORT_CRATE: i32 = 20;
+ const DONT_EXPORT_PRIV: i32 = 30;
+}
+
+#[no_mangle]
+pub extern "C" fn root(x: Foo) { }
diff --git a/tests/rust/associated_constant_panic.rs b/tests/rust/associated_constant_panic.rs
new file mode 100644
index 0000000..ff0c8ec
--- /dev/null
+++ b/tests/rust/associated_constant_panic.rs
@@ -0,0 +1,7 @@
+pub trait F {
+ const B: u8;
+}
+
+impl F for u16 {
+ const B: u8 = 3;
+}
diff --git a/tests/rust/associated_in_body.rs b/tests/rust/associated_in_body.rs
new file mode 100644
index 0000000..8c76a13
--- /dev/null
+++ b/tests/rust/associated_in_body.rs
@@ -0,0 +1,54 @@
+bitflags! {
+ /// Constants shared by multiple CSS Box Alignment properties
+ ///
+ /// These constants match Gecko's `NS_STYLE_ALIGN_*` constants.
+ #[derive(MallocSizeOf)]
+ #[repr(C)]
+ pub struct AlignFlags: u8 {
+ /// 'auto'
+ const AUTO = 0;
+ /// 'normal'
+ const NORMAL = 1;
+ /// 'start'
+ const START = 1 << 1;
+ /// 'end'
+ const END = 1 << 2;
+ const ALIAS = Self::END.bits;
+ /// 'flex-start'
+ const FLEX_START = 1 << 3;
+ const MIXED = 1 << 4 | AlignFlags::FLEX_START.bits | AlignFlags::END.bits;
+ const MIXED_SELF = 1 << 5 | AlignFlags::FLEX_START.bits | AlignFlags::END.bits;
+ }
+}
+
+/// An arbitrary identifier for a native (OS compositor) surface
+#[repr(C)]
+#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)]
+pub struct NativeSurfaceId(pub u64);
+
+impl NativeSurfaceId {
+ /// A special id for the native surface that is used for debug / profiler overlays.
+ pub const DEBUG_OVERLAY: NativeSurfaceId = NativeSurfaceId(u64::MAX);
+}
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)]
+#[cfg_attr(feature = "capture", derive(Serialize))]
+#[cfg_attr(feature = "replay", derive(Deserialize))]
+pub struct NativeTileId {
+ pub surface_id: NativeSurfaceId,
+ pub x: i32,
+ pub y: i32,
+}
+
+impl NativeTileId {
+ /// A special id for the native surface that is used for debug / profiler overlays.
+ pub const DEBUG_OVERLAY: NativeTileId = NativeTileId {
+ surface_id: NativeSurfaceId::DEBUG_OVERLAY,
+ x: 0,
+ y: 0,
+ };
+}
+
+#[no_mangle]
+pub extern "C" fn root(flags: AlignFlags, tile: NativeTileId) {}
diff --git a/tests/rust/associated_in_body.toml b/tests/rust/associated_in_body.toml
new file mode 100644
index 0000000..a52609b
--- /dev/null
+++ b/tests/rust/associated_in_body.toml
@@ -0,0 +1,11 @@
+[struct]
+associated_constants_in_body = true
+
+[macro_expansion]
+bitflags = true
+
+[export]
+prefix = "Style" # Just ensuring they play well together :)
+
+[const]
+allow_constexpr = true
diff --git a/tests/rust/bitflags.rs b/tests/rust/bitflags.rs
new file mode 100644
index 0000000..cba8377
--- /dev/null
+++ b/tests/rust/bitflags.rs
@@ -0,0 +1,61 @@
+bitflags! {
+ /// Constants shared by multiple CSS Box Alignment properties
+ ///
+ /// These constants match Gecko's `NS_STYLE_ALIGN_*` constants.
+ #[derive(MallocSizeOf)]
+ #[repr(C)]
+ pub struct AlignFlags: u8 {
+ /// 'auto'
+ const AUTO = 0;
+ /// 'normal'
+ const NORMAL = 1;
+ /// 'start'
+ const START = 1 << 1;
+ /// 'end'
+ const END = 1 << 2;
+ const ALIAS = Self::END.bits();
+ /// 'flex-start'
+ const FLEX_START = 1 << 3;
+ const MIXED = 1 << 4 | AlignFlags::FLEX_START.bits() | AlignFlags::END.bits();
+ const MIXED_SELF = 1 << 5 | AlignFlags::FLEX_START.bits() | AlignFlags::END.bits();
+ }
+}
+
+bitflags! {
+ #[repr(C)]
+ pub struct DebugFlags: u32 {
+ /// Flag with the topmost bit set of the u32
+ const BIGGEST_ALLOWED = 1 << 31;
+ }
+}
+
+bitflags! {
+ #[repr(C)]
+ pub struct LargeFlags: u64 {
+ /// Flag with a very large shift that usually would be narrowed.
+ const LARGE_SHIFT = 1u64 << 44;
+ const INVERTED = !Self::LARGE_SHIFT.bits();
+ }
+}
+
+// bitflags 2 allows to define types out-of-line for custom derives
+// #[derive(SomeTrait)]
+#[repr(C)]
+pub struct OutOfLine(u32);
+
+bitflags! {
+ impl OutOfLine: u32 {
+ const A = 1;
+ const B = 2;
+ const AB = Self::A.bits() | Self::B.bits();
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn root(
+ flags: AlignFlags,
+ bigger_flags: DebugFlags,
+ largest_flags: LargeFlags,
+ out_of_line: OutOfLine,
+) {
+}
diff --git a/tests/rust/bitflags.toml b/tests/rust/bitflags.toml
new file mode 100644
index 0000000..465689e
--- /dev/null
+++ b/tests/rust/bitflags.toml
@@ -0,0 +1,5 @@
+[macro_expansion]
+bitflags = true
+
+[const]
+allow_constexpr = true
diff --git a/tests/rust/body.rs b/tests/rust/body.rs
new file mode 100644
index 0000000..fb275f9
--- /dev/null
+++ b/tests/rust/body.rs
@@ -0,0 +1,55 @@
+
+#[repr(C)]
+pub struct MyFancyStruct {
+ i: i32,
+}
+
+#[repr(C)]
+pub enum MyFancyEnum {
+ Foo,
+ Bar(i32),
+ Baz(i32),
+}
+
+#[repr(C)]
+pub enum MyCLikeEnum {
+ Foo1,
+ Bar1,
+ Baz1,
+}
+
+#[repr(C)]
+pub union MyUnion {
+ pub f: f32,
+ pub u: u32,
+}
+
+
+#[repr(C)]
+pub struct MyFancyStruct_Prepended {
+ i: i32,
+}
+
+#[repr(C)]
+pub enum MyFancyEnum_Prepended {
+ Foo_Prepended,
+ Bar_Prepended(i32),
+ Baz_Prepended(i32),
+}
+
+#[repr(C)]
+pub enum MyCLikeEnum_Prepended {
+ Foo1_Prepended,
+ Bar1_Prepended,
+ Baz1_Prepended,
+}
+
+#[repr(C)]
+pub union MyUnion_Prepended {
+ pub f: f32,
+ pub u: u32,
+}
+
+
+#[no_mangle]
+pub extern "C" fn root(s: MyFancyStruct, e: MyFancyEnum, c: MyCLikeEnum, u: MyUnion, sp: MyFancyStruct_Prepended, ep: MyFancyEnum_Prepended, cp: MyCLikeEnum_Prepended, up: MyUnion_Prepended) {}
diff --git a/tests/rust/body.toml b/tests/rust/body.toml
new file mode 100644
index 0000000..ab96d70
--- /dev/null
+++ b/tests/rust/body.toml
@@ -0,0 +1,41 @@
+[export.body]
+"MyFancyStruct" = """
+#ifdef __cplusplus
+ inline void foo();
+#endif
+"""
+
+"MyFancyEnum" = """
+#ifdef __cplusplus
+ inline void wohoo();
+#endif
+"""
+
+"MyCLikeEnum" = """
+ BogusVariantForSerializationForExample,
+"""
+
+"MyUnion" = """
+ int32_t extra_member;
+"""
+
+[export.pre_body]
+"MyFancyStruct_Prepended" = """
+#ifdef __cplusplus
+ inline void prepended_wohoo();
+#endif
+"""
+
+"MyFancyEnum_Prepended" = """
+#ifdef __cplusplus
+ inline void wohoo();
+#endif
+"""
+
+"MyCLikeEnum_Prepended" = """
+ BogusVariantForSerializationForExample,
+"""
+
+"MyUnion_Prepended" = """
+ int32_t extra_member;
+"""
diff --git a/tests/rust/box.rs b/tests/rust/box.rs
new file mode 100644
index 0000000..6cc852c
--- /dev/null
+++ b/tests/rust/box.rs
@@ -0,0 +1,19 @@
+#[repr(C)]
+pub struct MyStruct {
+ number: Box<i32>,
+}
+
+pub struct NotReprC<T> {
+ inner: T,
+}
+
+pub type Foo = NotReprC<Box<i32>>;
+
+#[no_mangle]
+pub extern "C" fn root(a: &Foo, with_box: &MyStruct) {}
+
+#[no_mangle]
+pub extern "C" fn drop_box(x: Box<i32>) {}
+
+#[no_mangle]
+pub extern "C" fn drop_box_opt(x: Option<Box<i32>>) {}
diff --git a/tests/rust/box.toml b/tests/rust/box.toml
new file mode 100644
index 0000000..84cb6b0
--- /dev/null
+++ b/tests/rust/box.toml
@@ -0,0 +1,18 @@
+header = """
+#if 0
+''' '
+#endif
+
+#ifdef __cplusplus
+template <typename T>
+using Box = T*;
+#endif
+
+#if 0
+' '''
+#endif
+"""
+[export]
+exclude = [
+ "Box",
+]
diff --git a/tests/rust/cdecl.rs b/tests/rust/cdecl.rs
new file mode 100644
index 0000000..399de8f
--- /dev/null
+++ b/tests/rust/cdecl.rs
@@ -0,0 +1,41 @@
+type A = fn ();
+type B = fn () -> ();
+type C = fn (i32, i32) -> bool;
+type D = fn (i32) -> fn (f32) -> bool;
+type E = fn () -> *const [i32; 16];
+
+type F = *const i32;
+type G = *const *const i32;
+type H = *const *mut i32;
+type I = *const [i32; 16];
+type J = *const fn (f32) -> f64;
+
+type K = [i32; 16];
+type L = [*const i32; 16];
+type M = [fn (i32, i32) -> bool; 16];
+type N = [fn (i32, i32) -> (); 16];
+
+#[no_mangle]
+pub extern "C" fn O() -> fn ()
+{ }
+
+type P = fn (named1st: i32, bool, named3rd: bool, _: i32);
+
+#[no_mangle]
+pub extern "C" fn root(
+ a: A,
+ b: B,
+ c: C,
+ d: D,
+ e: E,
+ f: F,
+ g: G,
+ h: H,
+ i: I,
+ j: J,
+ k: K,
+ l: L,
+ m: M,
+ n: N,
+ p: P
+) { }
diff --git a/tests/rust/cell.rs b/tests/rust/cell.rs
new file mode 100644
index 0000000..5f16dbb
--- /dev/null
+++ b/tests/rust/cell.rs
@@ -0,0 +1,11 @@
+#[repr(C)]
+pub struct MyStruct {
+ number: std::cell::Cell<i32>,
+}
+
+pub struct NotReprC<T> { inner: T }
+
+pub type Foo = NotReprC<std::cell::RefCell<i32>>;
+
+#[no_mangle]
+pub extern "C" fn root(a: &Foo, with_cell: &MyStruct) {}
diff --git a/tests/rust/cfg.rs b/tests/rust/cfg.rs
new file mode 100644
index 0000000..903c156
--- /dev/null
+++ b/tests/rust/cfg.rs
@@ -0,0 +1,64 @@
+#[cfg(all(unix, x11))]
+#[repr(u32)]
+enum FooType {
+ A,
+ B,
+ C,
+}
+
+#[cfg(all(unix, x11))]
+#[repr(C)]
+struct FooHandle {
+ ty: FooType,
+ x: i32,
+ y: f32,
+}
+
+#[cfg(any(windows, target_pointer_width="32"))]
+#[repr(u32)]
+enum BarType {
+ A,
+ B,
+ C,
+}
+
+#[repr(u8)]
+pub enum C {
+ C1,
+ C2,
+ #[cfg(windows)]
+ C3,
+ #[cfg(unix)]
+ C5 { int: i32 },
+}
+
+#[cfg(any(windows, target_pointer_width="32"))]
+#[repr(C)]
+struct BarHandle {
+ ty: BarType,
+ x: i32,
+ y: f32,
+}
+
+// FIXME(#634): Support deriving methods for structs with conditional fields.
+/// cbindgen:derive-eq=false
+/// cbindgen:derive-neq=false
+#[repr(C)]
+struct ConditionalField {
+ #[cfg(x11)]
+ field: i32,
+}
+
+#[cfg(all(unix, x11))]
+#[no_mangle]
+pub extern "C" fn root(a: FooHandle, c: C)
+{ }
+
+#[cfg(any(windows, target_pointer_width="32"))]
+#[no_mangle]
+pub extern "C" fn root(a: BarHandle, c: C)
+{ }
+
+#[no_mangle]
+pub extern "C" fn cond(a: ConditionalField)
+{ }
diff --git a/tests/rust/cfg.toml b/tests/rust/cfg.toml
new file mode 100644
index 0000000..4d6c4ca
--- /dev/null
+++ b/tests/rust/cfg.toml
@@ -0,0 +1,25 @@
+header = """
+#if 0
+DEF PLATFORM_UNIX = 0
+DEF PLATFORM_WIN = 0
+DEF X11 = 0
+DEF M_32 = 0
+#endif
+"""
+
+[defines]
+"unix" = "PLATFORM_UNIX"
+"windows" = "PLATFORM_WIN"
+"x11" = "X11"
+"target_pointer_width = 32" = "M_32"
+
+[enum]
+derive_tagged_enum_destructor = true
+derive_tagged_enum_copy_constructor = true
+derive_tagged_enum_copy_assignment = true
+derive_helper_methods = true
+private_default_tagged_enum_constructor = true
+
+[struct]
+derive_eq = true
+derive_neq = true
diff --git a/tests/rust/cfg_2.rs b/tests/rust/cfg_2.rs
new file mode 100644
index 0000000..4b4c959
--- /dev/null
+++ b/tests/rust/cfg_2.rs
@@ -0,0 +1,32 @@
+#[cfg(any(windows, unix))]
+#[repr(C)]
+struct Foo {
+ x: i32,
+}
+
+#[cfg(windows)]
+#[repr(C)]
+struct Bar {
+ y: Foo,
+}
+
+#[cfg(unix)]
+#[repr(C)]
+struct Bar {
+ z: Foo,
+}
+
+#[repr(C)]
+struct Root {
+ w: Bar,
+}
+
+#[cfg(windows)]
+pub const DEFAULT_X: i32 = 0x08;
+
+#[cfg(unix)]
+pub const DEFAULT_X: i32 = 0x2a;
+
+#[no_mangle]
+pub extern "C" fn root(a: Root)
+{ }
diff --git a/tests/rust/cfg_2.toml b/tests/rust/cfg_2.toml
new file mode 100644
index 0000000..4672d03
--- /dev/null
+++ b/tests/rust/cfg_2.toml
@@ -0,0 +1,11 @@
+header = """
+#if 0
+DEF DEFINED = 1
+DEF NOT_DEFINED = 0
+#endif
+"""
+
+[defines]
+"unix" = "DEFINED"
+"macos" = "NOT_DEFINED"
+"windows" = "NOT_DEFINED"
diff --git a/tests/rust/cfg_field.rs b/tests/rust/cfg_field.rs
new file mode 100644
index 0000000..aed5165
--- /dev/null
+++ b/tests/rust/cfg_field.rs
@@ -0,0 +1,11 @@
+struct Foo {
+ #[cfg(windows)]
+ x: i32,
+}
+
+pub fn foo() {
+ Foo {
+ #[cfg(windows)]
+ x: 0,
+ };
+}
diff --git a/tests/rust/char.rs b/tests/rust/char.rs
new file mode 100644
index 0000000..258e50f
--- /dev/null
+++ b/tests/rust/char.rs
@@ -0,0 +1,7 @@
+#[repr(C)]
+struct Foo {
+ a: char,
+}
+
+#[no_mangle]
+pub extern "C" fn root(a: Foo) {}
diff --git a/tests/rust/const_conflict.rs b/tests/rust/const_conflict.rs
new file mode 100644
index 0000000..041fecf
--- /dev/null
+++ b/tests/rust/const_conflict.rs
@@ -0,0 +1,9 @@
+#[repr(C)]
+struct Foo {}
+
+impl Foo {
+ const FOO: i32 = 0;
+}
+
+pub const Foo_FOO: u32 = 42;
+
diff --git a/tests/rust/const_generics.rs b/tests/rust/const_generics.rs
new file mode 100644
index 0000000..081b38e
--- /dev/null
+++ b/tests/rust/const_generics.rs
@@ -0,0 +1,15 @@
+#[repr(transparent)]
+pub struct CArrayString<const CAP: usize> {
+ pub chars: [i8; CAP],
+}
+
+pub const TITLE_SIZE: usize = 80;
+
+#[repr(C)]
+pub struct Book {
+ pub title: CArrayString<TITLE_SIZE>,
+ pub author: CArrayString<40>,
+}
+
+#[no_mangle]
+pub extern "C" fn root(a: *mut Book) {}
diff --git a/tests/rust/const_generics_arrayvec.rs b/tests/rust/const_generics_arrayvec.rs
new file mode 100644
index 0000000..459ddbc
--- /dev/null
+++ b/tests/rust/const_generics_arrayvec.rs
@@ -0,0 +1,17 @@
+#[repr(C)]
+pub struct ArrayVec<T, const CAP: usize> {
+ // the `len` first elements of the array are initialized
+ xs: [T; CAP],
+ len: u32,
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn push(v: *mut ArrayVec<*mut u8, 100>, elem: *mut u8) -> i32 {
+ if (*v).len < 100 {
+ (*v).xs[(*v).len] = elem;
+ (*v).len += 1;
+ 1
+ } else {
+ 0
+ }
+}
diff --git a/tests/rust/const_generics_bool.rs b/tests/rust/const_generics_bool.rs
new file mode 100644
index 0000000..3047b07
--- /dev/null
+++ b/tests/rust/const_generics_bool.rs
@@ -0,0 +1,57 @@
+use std::mem::MaybeUninit;
+
+use libc::c_char;
+
+#[repr(C)]
+pub struct HashTable<K, V, const IS_MAP: bool> {
+ num_buckets: usize,
+ capacity: usize,
+ occupied: *mut u8,
+ keys: *mut MaybeUninit<K>,
+ vals: *mut MaybeUninit<V>,
+}
+
+type Str = *const c_char;
+pub type HashMap<K, V> = HashTable<K, V, true>;
+pub type HashSet<K> = HashTable<K, u8, false>;
+
+impl<K, V, const IS_MAP: bool> HashTable<K, V, IS_MAP>
+{
+ pub fn new() -> Self {
+ HashTable {
+ num_buckets: 0,
+ capacity: 0,
+ occupied: std::ptr::null_mut(),
+ keys: std::ptr::null_mut(),
+ vals: std::ptr::null_mut(),
+ }
+ }
+}
+
+// with alias
+type MySet = HashTable<Str, c_char, false>;
+
+#[no_mangle]
+pub extern "C" fn new_set() -> *mut MySet {
+ Box::into_raw(Box::new(HashSet::new()))
+}
+
+type SetCallback = unsafe extern "C" fn(key: Str);
+
+#[no_mangle]
+pub unsafe extern "C" fn set_for_each(set: *const MySet, callback: SetCallback) {
+ todo!();
+}
+
+// without alias
+#[no_mangle]
+pub extern "C" fn new_map() -> *mut HashTable<Str, u64, true> {
+ Box::into_raw(Box::new(HashMap::new()))
+}
+
+type MapCallback = unsafe extern "C" fn(key: Str, val: u64);
+
+#[no_mangle]
+pub unsafe extern "C" fn map_for_each(map: *const HashTable<Str, u64, true>, callback: MapCallback) {
+ todo!();
+}
diff --git a/tests/rust/const_generics_byte.rs b/tests/rust/const_generics_byte.rs
new file mode 100644
index 0000000..e150567
--- /dev/null
+++ b/tests/rust/const_generics_byte.rs
@@ -0,0 +1,29 @@
+// Name mangling can cope with char-like byte literals.
+
+#[repr(C)]
+pub struct Parser<const OPEN: u8, const CLOSE: u8> {
+ pub buf: *mut u8,
+ pub len: usize,
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn init_parens_parser(p: *mut Parser<b'(', b')'>, buf: *mut u8, len: usize) {
+ unsafe {
+ *p = Parser { buf, len };
+ }
+}
+
+// The same type as above, because `b'(' == 40 && b')' == 41`. And it happens
+// to mangle to the same C identifier. It doesn't always work out that way!
+#[no_mangle]
+pub unsafe extern "C" fn destroy_parens_parser(p: *mut Parser<40, 41>) {
+ // nothing to do
+}
+
+
+#[no_mangle]
+pub unsafe extern "C" fn init_braces_parser(p: *mut Parser<b'{', b'}'>, buf: *mut u8, len: usize) {
+ unsafe {
+ *p = Parser { buf, len };
+ }
+}
diff --git a/tests/rust/const_generics_char.rs b/tests/rust/const_generics_char.rs
new file mode 100644
index 0000000..85bff32
--- /dev/null
+++ b/tests/rust/const_generics_char.rs
@@ -0,0 +1,20 @@
+use std::marker::PhantomData;
+
+#[repr(C)]
+struct TakeUntil<'a, const V: char>
+{
+ marker: PhantomData<&'a str>,
+ start: *const u8,
+ len: usize,
+ point: usize,
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn until_nul(start: *const u8, len: usize) -> TakeUntil<'a, '\0'> {
+ TakeUntil {
+ marker: PhantomData,
+ start,
+ len,
+ point: 0,
+ }
+}
diff --git a/tests/rust/const_generics_constant.rs b/tests/rust/const_generics_constant.rs
new file mode 100644
index 0000000..83ed860
--- /dev/null
+++ b/tests/rust/const_generics_constant.rs
@@ -0,0 +1,18 @@
+#[repr(C)]
+pub struct FixedPoint<const FRACTION_BITS: u16> {
+ value: u16,
+}
+
+pub const FONT_WEIGHT_FRACTION_BITS: u16 = 6;
+
+pub type FontWeightFixedPoint = FixedPoint<FONT_WEIGHT_FRACTION_BITS>;
+
+#[repr(C)]
+pub struct FontWeight(FontWeightFixedPoint);
+
+impl FontWeight {
+ pub const NORMAL: FontWeight = FontWeight(FontWeightFixedPoint { value: 400 << FONT_WEIGHT_FRACTION_BITS });
+}
+
+#[no_mangle]
+pub extern "C" fn root(w: FontWeight) {}
diff --git a/tests/rust/const_generics_thru.rs b/tests/rust/const_generics_thru.rs
new file mode 100644
index 0000000..51b279c
--- /dev/null
+++ b/tests/rust/const_generics_thru.rs
@@ -0,0 +1,22 @@
+// Propagating const arguments through generics that use generics.
+
+#[repr(C)]
+pub struct Inner<const N: usize> {
+ pub bytes: [u8; N],
+}
+
+#[repr(C)]
+pub struct Outer<const N: usize> {
+ pub inner: Inner<N>, // don't declare two different structs named `Inner_N`
+}
+
+#[no_mangle]
+pub extern "C" fn one() -> Outer<1> {
+ Outer { inner: Inner { bytes: [0] } }
+}
+
+#[no_mangle]
+pub extern "C" fn two() -> Outer<2> {
+ Outer { inner: Inner { bytes: [0, 0] } }
+}
+
diff --git a/tests/rust/const_transparent.rs b/tests/rust/const_transparent.rs
new file mode 100644
index 0000000..7032953
--- /dev/null
+++ b/tests/rust/const_transparent.rs
@@ -0,0 +1,4 @@
+#[repr(transparent)]
+struct Transparent { field: u8 }
+
+pub const FOO: Transparent = Transparent { field: 0 };
diff --git a/tests/rust/constant.rs b/tests/rust/constant.rs
new file mode 100644
index 0000000..637a2ce
--- /dev/null
+++ b/tests/rust/constant.rs
@@ -0,0 +1,38 @@
+pub const FOO: i32 = 10;
+pub const BAR: &'static str = "hello world";
+pub const DELIMITER: char = ':';
+pub const LEFTCURLY: char = '{';
+pub const QUOTE: char = '\'';
+pub const TAB: char = '\t';
+pub const NEWLINE: char = '\n';
+pub const HEART: char = '❤';
+pub const EQUID: char = '𐂃';
+pub const ZOM: f32 = 3.14;
+
+pub(crate) const DONT_EXPORT_CRATE: i32 = 20;
+const DONT_EXPORT_PRIV: i32 = 30;
+
+/// A single-line doc comment.
+pub const POS_ONE: i8 = 1;
+/// A
+/// multi-line
+/// doc
+/// comment.
+pub const NEG_ONE: i8 = -1;
+
+// Some doc for shifting //
+pub const SHIFT: i64 = 3;
+pub const XBOOL: i64 = 1;
+pub const XFALSE: i64 = (0 << SHIFT) | XBOOL;
+pub const XTRUE: i64 = 1 << (SHIFT | XBOOL);
+
+pub const CAST: u8 = 'A' as u8;
+pub const DOUBLE_CAST: u32 = 1 as f32 as u32;
+
+#[repr(C)]
+struct Foo {
+ x: [i32; FOO],
+}
+
+#[no_mangle]
+pub extern "C" fn root(x: Foo) {}
diff --git a/tests/rust/constant_big.rs b/tests/rust/constant_big.rs
new file mode 100644
index 0000000..94eb834
--- /dev/null
+++ b/tests/rust/constant_big.rs
@@ -0,0 +1,8 @@
+pub const UNSIGNED_NEEDS_ULL_SUFFIX: u64 = 0x8000_0000_0000_0000;
+pub const UNSIGNED_DOESNT_NEED_ULL_SUFFIX: u64 = 0x7000_0000_0000_0000;
+
+// i64::min_value()
+pub const SIGNED_NEEDS_ULL_SUFFIX: i64 = -9223372036854775808;
+
+// i64::min_value() + 1
+pub const SIGNED_DOESNT_NEED_ULL_SUFFIX: i64 = -9223372036854775807;
diff --git a/tests/rust/constant_constexpr.rs b/tests/rust/constant_constexpr.rs
new file mode 100644
index 0000000..c4d2b8c
--- /dev/null
+++ b/tests/rust/constant_constexpr.rs
@@ -0,0 +1,14 @@
+pub const CONSTANT_I64: i64 = 216;
+pub const CONSTANT_FLOAT32: f32 = 312.292;
+pub const DELIMITER: char = ':';
+pub const LEFTCURLY: char = '{';
+#[repr(C)]
+struct Foo {
+ x: i32,
+}
+
+pub const SomeFoo: Foo = Foo { x: 99, };
+
+impl Foo {
+ pub const CONSTANT_I64_BODY: i64 = 216;
+}
diff --git a/tests/rust/constant_constexpr.toml b/tests/rust/constant_constexpr.toml
new file mode 100644
index 0000000..5deb0ed
--- /dev/null
+++ b/tests/rust/constant_constexpr.toml
@@ -0,0 +1,5 @@
+[const]
+allow_constexpr = false
+
+[struct]
+associated_constants_in_body = true
diff --git a/tests/rust/constant_sort_name.rs b/tests/rust/constant_sort_name.rs
new file mode 100644
index 0000000..96c1185
--- /dev/null
+++ b/tests/rust/constant_sort_name.rs
@@ -0,0 +1,7 @@
+pub const B: u8 = 0;
+pub const A: u8 = 0;
+
+#[no_mangle]
+pub static D: u8 = 0;
+#[no_mangle]
+pub static C: u8 = 0;
diff --git a/tests/rust/constant_sort_name.toml b/tests/rust/constant_sort_name.toml
new file mode 100644
index 0000000..41e26d8
--- /dev/null
+++ b/tests/rust/constant_sort_name.toml
@@ -0,0 +1,2 @@
+[const]
+sort_by = "Name"
diff --git a/tests/rust/constant_sort_none.rs b/tests/rust/constant_sort_none.rs
new file mode 100644
index 0000000..96c1185
--- /dev/null
+++ b/tests/rust/constant_sort_none.rs
@@ -0,0 +1,7 @@
+pub const B: u8 = 0;
+pub const A: u8 = 0;
+
+#[no_mangle]
+pub static D: u8 = 0;
+#[no_mangle]
+pub static C: u8 = 0;
diff --git a/tests/rust/constant_user_defined_type.rs b/tests/rust/constant_user_defined_type.rs
new file mode 100644
index 0000000..cffe817
--- /dev/null
+++ b/tests/rust/constant_user_defined_type.rs
@@ -0,0 +1,17 @@
+#[repr(C)]
+pub struct S {
+ field: u8,
+}
+
+/// cbindgen:enum-class=false
+#[repr(C)]
+pub enum E {
+ V,
+}
+use E::*;
+
+pub type A = u8;
+
+pub const C1: S = S { field: 0 };
+pub const C2: E = V;
+pub const C3: A = 0;
diff --git a/tests/rust/custom_header.rs b/tests/rust/custom_header.rs
new file mode 100644
index 0000000..03de69f
--- /dev/null
+++ b/tests/rust/custom_header.rs
@@ -0,0 +1,3 @@
+#[no_mangle]
+pub extern "C" fn root() {
+}
diff --git a/tests/rust/custom_header.toml b/tests/rust/custom_header.toml
new file mode 100644
index 0000000..4b1c8ee
--- /dev/null
+++ b/tests/rust/custom_header.toml
@@ -0,0 +1,11 @@
+no_includes = true
+header = """
+#if 0
+# This file is generated by cbindgen. DO NOT EDIT
+#endif
+"""
+trailer = """
+#if 0
+# This is a simple test to ensure that trailers do not cause extra newlines in files
+#endif
+"""
diff --git a/tests/rust/cython_options.rs b/tests/rust/cython_options.rs
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/rust/cython_options.rs
diff --git a/tests/rust/cython_options.toml b/tests/rust/cython_options.toml
new file mode 100644
index 0000000..319352c
--- /dev/null
+++ b/tests/rust/cython_options.toml
@@ -0,0 +1,6 @@
+[cython]
+header = '"my_header.h"'
+
+[cython.cimports]
+"libc.stdint" = ["int8_t", "int16_t"]
+"libc.stddef" = ["*"]
diff --git a/tests/rust/decl_name_conflicting.rs b/tests/rust/decl_name_conflicting.rs
new file mode 100644
index 0000000..39e9658
--- /dev/null
+++ b/tests/rust/decl_name_conflicting.rs
@@ -0,0 +1,14 @@
+mod uhoh {
+ enum BindingType { Buffer, NotBuffer }
+}
+
+#[repr(u32)]
+pub enum BindingType { Buffer = 0, NotBuffer = 1 }
+
+#[repr(C)]
+pub struct BindGroupLayoutEntry {
+ pub ty: BindingType, // This is the repr(u32) enum
+}
+
+#[no_mangle]
+pub extern "C" fn root(entry: BindGroupLayoutEntry) {}
diff --git a/tests/rust/deprecated.rs b/tests/rust/deprecated.rs
new file mode 100644
index 0000000..3abf65a
--- /dev/null
+++ b/tests/rust/deprecated.rs
@@ -0,0 +1,52 @@
+#[no_mangle]
+#[deprecated]
+pub extern "C" fn deprecated_without_note() {}
+
+#[no_mangle]
+#[deprecated = "This is a note"]
+pub extern "C" fn deprecated_without_bracket() {}
+
+#[no_mangle]
+#[deprecated(note = "This is a note")]
+pub extern "C" fn deprecated_with_note() {}
+
+#[no_mangle]
+#[deprecated(note = "This is a note", since = "1.0.0")]
+pub extern "C" fn deprecated_with_note_and_since() {}
+
+#[no_mangle]
+#[deprecated(note = "This quote \" requires to be quoted, and this [\n] requires to be escaped")]
+pub extern "C" fn deprecated_with_note_which_requires_to_be_escaped() {}
+
+#[repr(i32)]
+#[deprecated]
+pub enum DeprecatedEnum {
+ A = 0,
+}
+
+#[repr(i32)]
+#[deprecated(note = "This is a note")]
+pub enum DeprecatedEnumWithNote {
+ B = 0,
+}
+
+#[repr(C)]
+#[deprecated]
+pub struct DeprecatedStruct {
+ pub a: i32,
+}
+
+#[repr(C)]
+#[deprecated(note = "This is a note")]
+pub struct DeprecatedStructWithNote {
+ pub a: i32,
+}
+
+#[no_mangle]
+pub extern "C" fn dummy(
+ a: DeprecatedEnum,
+ b: DeprecatedEnumWithNote,
+ c: DeprecatedStruct,
+ d: DeprecatedStructWithNote,
+) -> void {
+}
diff --git a/tests/rust/deprecated.toml b/tests/rust/deprecated.toml
new file mode 100644
index 0000000..4a2bad5
--- /dev/null
+++ b/tests/rust/deprecated.toml
@@ -0,0 +1,20 @@
+header = """
+#define DEPRECATED_FUNC __attribute__((deprecated))
+#define DEPRECATED_STRUCT __attribute__((deprecated))
+#define DEPRECATED_ENUM __attribute__((deprecated))
+#define DEPRECATED_FUNC_WITH_NOTE(...) __attribute__((deprecated(__VA_ARGS__)))
+#define DEPRECATED_STRUCT_WITH_NOTE(...) __attribute__((deprecated(__VA_ARGS__)))
+#define DEPRECATED_ENUM_WITH_NOTE(...) __attribute__((deprecated(__VA_ARGS__)))
+"""
+
+[fn]
+deprecated = "DEPRECATED_FUNC"
+deprecated_with_note = "DEPRECATED_FUNC_WITH_NOTE({})"
+
+[struct]
+deprecated = "DEPRECATED_STRUCT"
+deprecated_with_note = "DEPRECATED_STRUCT_WITH_NOTE({})"
+
+[enum]
+deprecated = "DEPRECATED_ENUM"
+deprecated_with_note = "DEPRECATED_ENUM_WITH_NOTE({})"
diff --git a/tests/rust/derive_ostream.rs b/tests/rust/derive_ostream.rs
new file mode 100644
index 0000000..e7e33fa
--- /dev/null
+++ b/tests/rust/derive_ostream.rs
@@ -0,0 +1,59 @@
+/// cbindgen:derive-ostream
+#[repr(C)]
+pub struct A(i32);
+
+/// cbindgen:field-names=[x, y]
+/// cbindgen:derive-ostream
+#[repr(C)]
+pub struct B(i32, f32);
+
+/// cbindgen:derive-ostream
+#[repr(u32)]
+pub enum C {
+ X = 2,
+ Y,
+}
+
+/// cbindgen:derive-ostream
+#[repr(C)]
+pub struct D {
+ List: u8,
+ Of: usize,
+ Things: B,
+}
+
+/// cbindgen:derive-ostream
+#[repr(u8)]
+pub enum F {
+ Foo(i16),
+ Bar { x: u8, y: i16 },
+ Baz
+}
+
+/// cbindgen:derive-ostream
+#[repr(C, u8)]
+pub enum H {
+ Hello(i16),
+ There { x: u8, y: i16 },
+ Everyone
+}
+
+/// cbindgen:derive-ostream=false
+#[repr(C, u8)]
+pub enum I {
+ /// cbindgen:derive-ostream=true
+ ThereAgain { x: u8, y: i16 },
+ SomethingElse
+}
+
+#[no_mangle]
+pub extern "C" fn root(
+ a: A,
+ b: B,
+ c: C,
+ d: D,
+ f: F,
+ h: H,
+ i: I,
+) { }
+
diff --git a/tests/rust/destructor_and_copy_ctor.rs b/tests/rust/destructor_and_copy_ctor.rs
new file mode 100644
index 0000000..9c96145
--- /dev/null
+++ b/tests/rust/destructor_and_copy_ctor.rs
@@ -0,0 +1,97 @@
+use std::ptr::NonNull;
+
+/// This will have a destructor manually implemented via variant_body, and
+/// similarly a Drop impl in Rust.
+#[repr(C)]
+pub struct OwnedSlice<T> {
+ len: usize,
+ ptr: NonNull<T>,
+}
+
+#[repr(u8)]
+pub enum FillRule { A, B }
+
+#[repr(C)]
+pub struct Polygon<LengthPercentage> {
+ pub fill: FillRule,
+ pub coordinates: OwnedSlice<LengthPercentage>,
+}
+
+#[repr(C, u8)]
+pub enum Foo<T> {
+ Bar,
+ Polygon1(Polygon<T>),
+ Slice1(OwnedSlice<T>),
+ Slice2(OwnedSlice<i32>),
+ Slice3 {
+ fill: FillRule,
+ coords: OwnedSlice<T>,
+ },
+ Slice4 {
+ fill: FillRule,
+ coords: OwnedSlice<i32>,
+ },
+}
+
+#[repr(u8)]
+pub enum Baz<T> {
+ Bar2,
+ Polygon21(Polygon<T>),
+ Slice21(OwnedSlice<T>),
+ Slice22(OwnedSlice<i32>),
+ Slice23 {
+ fill: FillRule,
+ coords: OwnedSlice<T>,
+ },
+ Slice24 {
+ fill: FillRule,
+ coords: OwnedSlice<i32>,
+ },
+}
+
+#[repr(u8)]
+pub enum Taz {
+ Bar3,
+ Taz1(i32),
+ Taz3(OwnedSlice<i32>),
+}
+
+/// cbindgen:derive-tagged-enum-destructor=false
+/// cbindgen:derive-tagged-enum-copy-constructor=false
+#[repr(u8)]
+pub enum Tazz {
+ Bar4,
+ Taz2(i32),
+}
+
+/// cbindgen:derive-tagged-enum-copy-assignment=false
+#[repr(u8)]
+pub enum Tazzz {
+ Bar5,
+ Taz5(i32),
+}
+
+#[repr(u8)]
+pub enum Tazzzz {
+ Taz6(i32),
+ Taz7(u32),
+}
+
+/// cbindgen:derive-eq=true
+/// cbindgen:derive-neq=true
+/// cbindgen:neq-attributes=NODISCARD
+/// cbindgen:eq-attributes=NODISCARD
+/// cbindgen:destructor-attributes=NOINLINE
+/// cbindgen:copy-constructor-attributes=NOINLINE
+/// cbindgen:copy-assignment-attributes=NOINLINE
+#[repr(u8)]
+pub enum Qux {
+ /// cbindgen:derive-eq=true
+ Qux1(i32),
+ /// cbindgen:derive-eq=true
+ Qux2(u32),
+}
+
+
+#[no_mangle]
+pub extern "C" fn root(a: &Foo<u32>, b: &Baz<i32>, c: &Taz, d: Tazz, e: &Tazzz, f: &Tazzzz, g: &Qux) {}
diff --git a/tests/rust/destructor_and_copy_ctor.toml b/tests/rust/destructor_and_copy_ctor.toml
new file mode 100644
index 0000000..a1d91e5
--- /dev/null
+++ b/tests/rust/destructor_and_copy_ctor.toml
@@ -0,0 +1,16 @@
+header = """
+#define NOINLINE __attribute__((noinline))
+#define NODISCARD [[nodiscard]]
+"""
+
+[enum]
+derive_tagged_enum_destructor = true
+derive_tagged_enum_copy_constructor = true
+derive_tagged_enum_copy_assignment = true
+derive_helper_methods = true
+private_default_tagged_enum_constructor = true
+
+[export.body]
+"OwnedSlice" = """
+ ~OwnedSlice() {}
+"""
diff --git a/tests/rust/display_list.rs b/tests/rust/display_list.rs
new file mode 100644
index 0000000..3b4a78a
--- /dev/null
+++ b/tests/rust/display_list.rs
@@ -0,0 +1,18 @@
+#[repr(u8)]
+pub enum DisplayItem {
+ Fill(Rect, Color),
+ Image { id: u32, bounds: Rect },
+ ClearScreen,
+}
+
+#[repr(C)]
+pub struct Rect { x: f32, y: f32, w: f32, h: f32 }
+
+#[repr(C)]
+pub struct Color { r: u8, g: u8, b: u8, a: u8 }
+
+#[no_mangle]
+pub extern "C" fn push_item(item: DisplayItem) -> bool {
+ ::std::mem::drop(item);
+ true
+}
diff --git a/tests/rust/doclength_short.rs b/tests/rust/doclength_short.rs
new file mode 100644
index 0000000..363a245
--- /dev/null
+++ b/tests/rust/doclength_short.rs
@@ -0,0 +1,16 @@
+/// The root of all evil.
+///
+/// But at least it contains some more documentation as someone would expect
+/// from a simple test case like this. Though, this shouldn't appear in the
+/// output.
+#[no_mangle]
+pub extern "C" fn root() {
+}
+
+/// A little above the root, and a lot more visible, with a run-on sentence
+/// to test going over the first line.
+///
+/// Still not here, though.
+#[no_mangle]
+pub extern "C" fn trunk() {
+}
diff --git a/tests/rust/doclength_short.toml b/tests/rust/doclength_short.toml
new file mode 100644
index 0000000..30a7d79
--- /dev/null
+++ b/tests/rust/doclength_short.toml
@@ -0,0 +1 @@
+documentation_length = "short"
diff --git a/tests/rust/docstyle_auto.rs b/tests/rust/docstyle_auto.rs
new file mode 100644
index 0000000..2b66147
--- /dev/null
+++ b/tests/rust/docstyle_auto.rs
@@ -0,0 +1,4 @@
+/// The root of all evil.
+#[no_mangle]
+pub extern "C" fn root() {
+}
diff --git a/tests/rust/docstyle_auto.toml b/tests/rust/docstyle_auto.toml
new file mode 100644
index 0000000..be9e72e
--- /dev/null
+++ b/tests/rust/docstyle_auto.toml
@@ -0,0 +1 @@
+documentation_style = "auto"
diff --git a/tests/rust/docstyle_c99.rs b/tests/rust/docstyle_c99.rs
new file mode 100644
index 0000000..2b66147
--- /dev/null
+++ b/tests/rust/docstyle_c99.rs
@@ -0,0 +1,4 @@
+/// The root of all evil.
+#[no_mangle]
+pub extern "C" fn root() {
+}
diff --git a/tests/rust/docstyle_c99.toml b/tests/rust/docstyle_c99.toml
new file mode 100644
index 0000000..c94138b
--- /dev/null
+++ b/tests/rust/docstyle_c99.toml
@@ -0,0 +1 @@
+documentation_style = "c99"
diff --git a/tests/rust/docstyle_doxy.rs b/tests/rust/docstyle_doxy.rs
new file mode 100644
index 0000000..2b66147
--- /dev/null
+++ b/tests/rust/docstyle_doxy.rs
@@ -0,0 +1,4 @@
+/// The root of all evil.
+#[no_mangle]
+pub extern "C" fn root() {
+}
diff --git a/tests/rust/docstyle_doxy.toml b/tests/rust/docstyle_doxy.toml
new file mode 100644
index 0000000..05e606a
--- /dev/null
+++ b/tests/rust/docstyle_doxy.toml
@@ -0,0 +1 @@
+documentation_style = "doxy"
diff --git a/tests/rust/documentation.rs b/tests/rust/documentation.rs
new file mode 100644
index 0000000..56da3c3
--- /dev/null
+++ b/tests/rust/documentation.rs
@@ -0,0 +1,21 @@
+/// The root of all evil.
+///
+/// But at least it contains some more documentation as someone would expect
+/// from a simple test case like this.
+///
+/// # Hint
+///
+/// Always ensure that everything is properly documented, even if you feel lazy.
+/// **Sometimes** it is also helpful to include some markdown formatting.
+///
+/// ////////////////////////////////////////////////////////////////////////////
+///
+/// Attention:
+///
+/// Rust is going to trim all leading `/` symbols. If you want to use them as a
+/// marker you need to add at least a single whitespace inbetween the tripple
+/// slash doc-comment marker and the rest.
+///
+#[no_mangle]
+pub extern "C" fn root() {
+}
diff --git a/tests/rust/documentation_attr.rs b/tests/rust/documentation_attr.rs
new file mode 100644
index 0000000..d88ce80
--- /dev/null
+++ b/tests/rust/documentation_attr.rs
@@ -0,0 +1,12 @@
+#[doc="With doc attr, each attr contribute to one line of document"]
+#[doc="like this one with a new line character at its end"]
+#[doc="and this one as well. So they are in the same paragraph"]
+#[doc=""]
+#[doc="Line ends with one new line\nshould not break"]
+#[doc=""]
+#[doc="Line ends with two spaces and a new line \nshould break to next line"]
+#[doc=""]
+#[doc="Line ends with two new lines\n\nShould break to next paragraph"]
+#[no_mangle]
+pub extern "C" fn root() {
+}
diff --git a/tests/rust/enum.rs b/tests/rust/enum.rs
new file mode 100644
index 0000000..19191ca
--- /dev/null
+++ b/tests/rust/enum.rs
@@ -0,0 +1,166 @@
+enum Opaque {
+ Foo(i32),
+ Bar,
+}
+
+#[repr(u64)]
+enum A {
+ a1 = 0,
+ a2 = 2,
+ a3,
+ a4 = 5,
+}
+
+#[repr(u32)]
+enum B {
+ b1 = 0,
+ b2 = 2,
+ b3,
+ b4 = 5,
+}
+
+#[repr(u16)]
+enum C {
+ c1 = 0,
+ c2 = 2,
+ c3,
+ c4 = 5,
+}
+
+#[repr(u8)]
+enum D {
+ d1 = 0,
+ d2 = 2,
+ d3,
+ d4 = 5,
+}
+
+#[repr(usize)]
+enum E {
+ e1 = 0,
+ e2 = 2,
+ e3,
+ e4 = 5,
+}
+
+#[repr(isize)]
+enum F {
+ f1 = 0,
+ f2 = 2,
+ f3,
+ f4 = 5,
+}
+
+#[repr(u8)]
+enum G {
+ Foo(i16),
+ Bar { x: u8, y: i16 },
+ Baz,
+}
+
+/// cbindgen:prefix-with-name
+#[repr(C)]
+enum H {
+ Foo(i16),
+ Bar { x: u8, y: i16 },
+ Baz,
+}
+
+/// cbindgen:prefix-with-name
+#[repr(C, u8)]
+enum I {
+ Foo(i16),
+ Bar { x: u8, y: i16 },
+ Baz,
+}
+
+#[repr(C, u8, u16)]
+enum J {
+ Foo(i16),
+ Bar { x: u8, y: i16 },
+ Baz,
+}
+
+#[repr(C, u8, unknown_hint)]
+enum K {
+ Foo(i16),
+ Bar { x: u8, y: i16 },
+ Baz,
+}
+
+#[repr(C)]
+enum L {
+ l1,
+ l2,
+ l3,
+ l4,
+}
+
+#[repr(i8)]
+enum M {
+ m1 = -1,
+ m2 = 0,
+ m3 = 1,
+}
+
+/// cbindgen:enum-class=false
+#[repr(C)]
+enum N {
+ n1,
+ n2,
+ n3,
+ n4,
+}
+
+/// cbindgen:enum-class=false
+#[repr(i8)]
+enum O {
+ o1,
+ o2,
+ o3,
+ o4,
+}
+
+#[repr(C, u8)]
+enum P {
+ P0(u8),
+ P1(u8, u8, u8),
+}
+
+#[repr(C)]
+enum Q {
+ Ok(Box<u32>),
+ Err(u32),
+}
+
+/// cbindgen:rename-variant-name-fields=None
+#[repr(C)]
+enum R {
+ IRFoo(i16),
+ IRBar { x: u8, y: i16 },
+ IRBaz,
+}
+
+#[no_mangle]
+pub extern "C" fn root(
+ opaque: *mut Opaque,
+ a: A,
+ b: B,
+ c: C,
+ d: D,
+ e: E,
+ f: F,
+ g: G,
+ h: H,
+ i: I,
+ j: J,
+ k: K,
+ l: L,
+ m: M,
+ n: N,
+ o: O,
+ p: P,
+ q: Q,
+ r: R,
+) {
+}
diff --git a/tests/rust/enum.toml b/tests/rust/enum.toml
new file mode 100644
index 0000000..20073cf
--- /dev/null
+++ b/tests/rust/enum.toml
@@ -0,0 +1,39 @@
+header = """
+#if 0
+''' '
+#endif
+
+#ifdef __cplusplus
+template <typename T>
+using Box = T*;
+#endif
+
+#if 0
+' '''
+#endif
+"""
+
+trailer = """
+#if 0
+''' '
+#endif
+
+#include <stddef.h>
+#include "testing-helpers.h"
+static_assert(offsetof(CBINDGEN_STRUCT(P), tag) == 0, "unexpected offset for tag");
+static_assert(offsetof(CBINDGEN_STRUCT(P), p0) == 1, "unexpected offset for p0");
+static_assert(offsetof(CBINDGEN_STRUCT(P), p0) == 1, "unexpected offset for p1");
+static_assert(sizeof(CBINDGEN_STRUCT(P)) == 4, "unexpected size for P");
+
+#if 0
+' '''
+#endif
+"""
+
+[export]
+exclude = [
+ "Box",
+]
+
+[export.rename]
+"I" = "ExI"
diff --git a/tests/rust/enum_discriminant.rs b/tests/rust/enum_discriminant.rs
new file mode 100644
index 0000000..79c270f
--- /dev/null
+++ b/tests/rust/enum_discriminant.rs
@@ -0,0 +1,15 @@
+pub const FOUR: i8 = 4;
+
+#[repr(i8)]
+enum E {
+ A = 1,
+ B = -1,
+ C = 1 + 2,
+ D = FOUR,
+ F = (5),
+ G = b'6' as i8,
+ H = false as i8,
+}
+
+#[no_mangle]
+pub extern "C" fn root(_: &E) {}
diff --git a/tests/rust/enum_discriminant.toml b/tests/rust/enum_discriminant.toml
new file mode 100644
index 0000000..24ebcb1
--- /dev/null
+++ b/tests/rust/enum_discriminant.toml
@@ -0,0 +1,2 @@
+[export.rename]
+"FOUR" = "FOURTY_FOUR"
diff --git a/tests/rust/enum_self.rs b/tests/rust/enum_self.rs
new file mode 100644
index 0000000..ffdc970
--- /dev/null
+++ b/tests/rust/enum_self.rs
@@ -0,0 +1,15 @@
+#[repr(C)]
+pub struct Foo<T> {
+ something: *const i32,
+ phantom: std::marker::PhantomData<T>,
+}
+
+#[repr(u8)]
+pub enum Bar {
+ Min(Foo<Self>),
+ Max(Foo<Self>),
+ Other,
+}
+
+#[no_mangle]
+pub extern "C" fn root(b: Bar) {}
diff --git a/tests/rust/euclid.rs b/tests/rust/euclid.rs
new file mode 100644
index 0000000..d3c2940
--- /dev/null
+++ b/tests/rust/euclid.rs
@@ -0,0 +1,81 @@
+struct UnknownUnit;
+struct LayoutUnit;
+
+#[repr(C)]
+struct TypedLength<T, Unit>(T, PhantomData<Unit>);
+
+#[repr(C)]
+struct TypedSideOffsets2D<T, U> {
+ top: T,
+ right: T,
+ bottom: T,
+ left: T,
+ _phantom: PhantomData<U>,
+}
+
+#[repr(C)]
+struct TypedSize2D<T, U> {
+ width: T,
+ height: T,
+ _phantom: PhantomData<U>,
+}
+
+#[repr(C)]
+struct TypedPoint2D<T, U> {
+ x: T,
+ y: T,
+ _phantom: PhantomData<U>,
+}
+
+#[repr(C)]
+struct TypedRect<T, U> {
+ origin: TypedPoint2D<T, U>,
+ size: TypedSize2D<T, U>,
+ _phantom: PhantomData<U>,
+}
+
+#[repr(C)]
+struct TypedTransform2D<T, Src, Dst> {
+ m11: T, m12: T,
+ m21: T, m22: T,
+ m31: T, m32: T,
+ _phantom: PhantomData<U>,
+}
+
+type Length<T> = TypedLength<T, UnknownUnit>;
+type SideOffsets2D<T> = TypedSideOffsets2D<T, UnknownUnit>;
+type Size2D<T> = TypedSize2D<T, UnknownUnit>;
+type Point2D<T> = TypedPoint2D<T, UnknownUnit>;
+type Rect<T> = TypedRect<T, UnknownUnit>;
+
+type LayoutLength = TypedLength<f32, LayoutUnit>;
+type LayoutSideOffsets2D = TypedSideOffsets2D<f32, LayoutUnit>;
+type LayoutSize2D = TypedSize2D<f32, LayoutUnit>;
+type LayoutPoint2D = TypedPoint2D<f32, LayoutUnit>;
+type LayoutRect = TypedRect<f32, LayoutUnit>;
+
+#[no_mangle]
+pub extern "C" fn root(
+ length_a: TypedLength<f32, UnknownUnit>,
+ length_b: TypedLength<f32, LayoutUnit>,
+ length_c: Length<f32>,
+ length_d: LayoutLength,
+ side_offsets_a: TypedSideOffsets2D<f32, UnknownUnit>,
+ side_offsets_b: TypedSideOffsets2D<f32, LayoutUnit>,
+ side_offsets_c: SideOffsets2D<f32>,
+ side_offsets_d: LayoutSideOffsets2D,
+ size_a: TypedSize2D<f32, UnknownUnit>,
+ size_b: TypedSize2D<f32, LayoutUnit>,
+ size_c: Size2D<f32>,
+ size_d: LayoutSize2D,
+ point_a: TypedPoint2D<f32, UnknownUnit>,
+ point_b: TypedPoint2D<f32, LayoutUnit>,
+ point_c: Point2D<f32>,
+ point_d: LayoutPoint2D,
+ rect_a: TypedRect<f32, UnknownUnit>,
+ rect_b: TypedRect<f32, LayoutUnit>,
+ rect_c: Rect<f32>,
+ rect_d: LayoutRect,
+ transform_a: TypedTransform2D<f32, UnknownUnit, LayoutUnit>,
+ transform_b: TypedTransform2D<f32, LayoutUnit, UnknownUnit>
+) { }
diff --git a/tests/rust/exclude_generic_monomorph.rs b/tests/rust/exclude_generic_monomorph.rs
new file mode 100644
index 0000000..78fd197
--- /dev/null
+++ b/tests/rust/exclude_generic_monomorph.rs
@@ -0,0 +1,10 @@
+#[repr(transparent)]
+pub struct Foo(NonZeroU64);
+
+#[repr(C)]
+pub struct Bar {
+ foo: Option<Foo>,
+}
+
+#[no_mangle]
+pub extern "C" fn root(f: Bar) {}
diff --git a/tests/rust/exclude_generic_monomorph.toml b/tests/rust/exclude_generic_monomorph.toml
new file mode 100644
index 0000000..15711ae
--- /dev/null
+++ b/tests/rust/exclude_generic_monomorph.toml
@@ -0,0 +1,24 @@
+language = "C"
+header = """
+#include <stdint.h>
+
+#if 0
+''' '
+#endif
+
+typedef uint64_t Option_Foo;
+
+#if 0
+' '''
+#endif
+
+#if 0
+from libc.stdint cimport uint64_t
+ctypedef uint64_t Option_Foo
+#endif
+"""
+
+[export]
+exclude = [
+ "Option_Foo",
+]
diff --git a/tests/rust/export_name.rs b/tests/rust/export_name.rs
new file mode 100644
index 0000000..5d17ee1
--- /dev/null
+++ b/tests/rust/export_name.rs
@@ -0,0 +1,4 @@
+#[export_name = "do_the_thing_with_export_name"]
+pub extern "C" fn do_the_thing() {
+ println!("doing the thing!");
+} \ No newline at end of file
diff --git a/tests/rust/extern.rs b/tests/rust/extern.rs
new file mode 100644
index 0000000..3bd83d1
--- /dev/null
+++ b/tests/rust/extern.rs
@@ -0,0 +1,11 @@
+#[repr(C)]
+struct Normal {
+ x: i32,
+ y: f32,
+}
+
+extern "C" {
+ fn foo() -> i32;
+
+ fn bar(a: Normal);
+}
diff --git a/tests/rust/extern_2.rs b/tests/rust/extern_2.rs
new file mode 100644
index 0000000..92a9c18
--- /dev/null
+++ b/tests/rust/extern_2.rs
@@ -0,0 +1,11 @@
+#[no_mangle]
+pub extern "C" fn first()
+{ }
+
+#[no_mangle]
+pub extern fn second()
+{ }
+
+#[no_mangle]
+pub fn third()
+{ }
diff --git a/tests/rust/fns.rs b/tests/rust/fns.rs
new file mode 100644
index 0000000..39c5622
--- /dev/null
+++ b/tests/rust/fns.rs
@@ -0,0 +1,16 @@
+#[repr(C)]
+pub struct Fns {
+ noArgs: fn(),
+ anonymousArg: fn(i32),
+ returnsNumber: fn() -> i32,
+ namedArgs: fn(first: i32, snd: i16) -> i8,
+ namedArgsWildcards: fn(_: i32, named: i16, _: i64) -> i8,
+}
+
+#[no_mangle]
+pub extern "C" fn root(_fns: Fns) {}
+
+#[no_mangle]
+pub extern "C" fn no_return() -> ! {
+ loop {}
+}
diff --git a/tests/rust/forward_declaration.rs b/tests/rust/forward_declaration.rs
new file mode 100644
index 0000000..e97b63e
--- /dev/null
+++ b/tests/rust/forward_declaration.rs
@@ -0,0 +1,21 @@
+#[repr(C)]
+struct TypeInfo {
+ data: TypeData,
+}
+
+#[repr(C)]
+enum TypeData {
+ Primitive,
+ Struct(StructInfo),
+}
+
+#[repr(C)]
+struct StructInfo {
+ fields: *const *const TypeInfo, // requires forward declaration
+ num_fields: usize,
+}
+
+#[no_mangle]
+pub extern "C" fn root(
+ x: TypeInfo,
+) {}
diff --git a/tests/rust/forward_declaration.toml b/tests/rust/forward_declaration.toml
new file mode 100644
index 0000000..4f1d1ad
--- /dev/null
+++ b/tests/rust/forward_declaration.toml
@@ -0,0 +1,23 @@
+header = """
+#if 0
+''' '
+#endif
+#if defined(CBINDGEN_STYLE_TYPE)
+/* ANONYMOUS STRUCTS DO NOT SUPPORT FORWARD DECLARATIONS!
+#endif
+#if 0
+' '''
+#endif
+"""
+
+trailer = """
+#if 0
+''' '
+#endif
+#if defined(CBINDGEN_STYLE_TYPE)
+*/
+#endif
+#if 0
+' '''
+#endif
+"""
diff --git a/tests/rust/function_args.rs b/tests/rust/function_args.rs
new file mode 100644
index 0000000..52a6a23
--- /dev/null
+++ b/tests/rust/function_args.rs
@@ -0,0 +1,25 @@
+#[no_mangle]
+pub unsafe extern fn array_print(a: &[u64]) {
+ eprintln!("{:?}", a);
+}
+
+#[no_mangle]
+pub unsafe extern fn array_test(a: [u64; 3]) {
+ array_print(&a);
+}
+
+#[no_mangle]
+pub unsafe extern fn unnamed(_: *const u64) {
+}
+
+#[no_mangle]
+pub unsafe extern fn pointer_test(a: *const u64) {
+ let a = std::slice::from_raw_parts(a, 3);
+ array_print(a);
+}
+
+#[no_mangle]
+pub unsafe extern fn print_from_rust() {
+ let a = [0, 1, 2];
+ array_print(&a);
+}
diff --git a/tests/rust/function_noreturn.rs b/tests/rust/function_noreturn.rs
new file mode 100644
index 0000000..d7777ad
--- /dev/null
+++ b/tests/rust/function_noreturn.rs
@@ -0,0 +1,14 @@
+#[no_mangle]
+pub extern "C" fn loop_forever() -> ! {
+ loop {}
+}
+
+#[no_mangle]
+pub extern "C" fn normal_return(arg: Example, other: extern "C" fn(u8) -> !) -> u8 {
+ 0
+}
+
+#[repr(C)]
+pub struct Example {
+ pub f: extern "C" fn(usize, usize) -> !,
+}
diff --git a/tests/rust/function_noreturn.toml b/tests/rust/function_noreturn.toml
new file mode 100644
index 0000000..33e88c2
--- /dev/null
+++ b/tests/rust/function_noreturn.toml
@@ -0,0 +1,12 @@
+after_includes = """
+#ifndef NO_RETURN_ATTR
+ #ifdef __GNUC__
+ #define NO_RETURN_ATTR __attribute__ ((noreturn))
+ #else // __GNUC__
+ #define NO_RETURN_ATTR
+ #endif // __GNUC__
+#endif // NO_RETURN_ATTR
+"""
+
+[fn]
+no_return = "NO_RETURN_ATTR"
diff --git a/tests/rust/function_ptr.rs b/tests/rust/function_ptr.rs
new file mode 100644
index 0000000..59adb52
--- /dev/null
+++ b/tests/rust/function_ptr.rs
@@ -0,0 +1,6 @@
+pub type MyCallback = Option<unsafe extern "C" fn(a: usize, b: usize)>;
+
+pub type MyOtherCallback = Option<unsafe extern "C" fn(a: usize, lot: usize, of: usize, args: usize, and_then_some: usize)>;
+
+#[no_mangle]
+pub extern "C" fn my_function(a: MyCallback, b: MyOtherCallback) {}
diff --git a/tests/rust/function_sort_name.rs b/tests/rust/function_sort_name.rs
new file mode 100644
index 0000000..ecd23c1
--- /dev/null
+++ b/tests/rust/function_sort_name.rs
@@ -0,0 +1,15 @@
+#[no_mangle]
+pub extern "C" fn C()
+{ }
+
+#[no_mangle]
+pub extern "C" fn B()
+{ }
+
+#[no_mangle]
+pub extern "C" fn D()
+{ }
+
+#[no_mangle]
+pub extern "C" fn A()
+{ }
diff --git a/tests/rust/function_sort_name.toml b/tests/rust/function_sort_name.toml
new file mode 100644
index 0000000..8607792
--- /dev/null
+++ b/tests/rust/function_sort_name.toml
@@ -0,0 +1,2 @@
+[fn]
+sort_by = "Name"
diff --git a/tests/rust/function_sort_none.rs b/tests/rust/function_sort_none.rs
new file mode 100644
index 0000000..ecd23c1
--- /dev/null
+++ b/tests/rust/function_sort_none.rs
@@ -0,0 +1,15 @@
+#[no_mangle]
+pub extern "C" fn C()
+{ }
+
+#[no_mangle]
+pub extern "C" fn B()
+{ }
+
+#[no_mangle]
+pub extern "C" fn D()
+{ }
+
+#[no_mangle]
+pub extern "C" fn A()
+{ }
diff --git a/tests/rust/generic_pointer.rs b/tests/rust/generic_pointer.rs
new file mode 100644
index 0000000..1167c76
--- /dev/null
+++ b/tests/rust/generic_pointer.rs
@@ -0,0 +1,11 @@
+#[repr(C)]
+pub struct Foo<T> {
+ a: T,
+}
+
+pub type Boo = Foo<*mut u8>;
+
+#[no_mangle]
+pub extern "C" fn root(
+ x: Boo,
+) { }
diff --git a/tests/rust/global_attr.rs b/tests/rust/global_attr.rs
new file mode 100644
index 0000000..15afd03
--- /dev/null
+++ b/tests/rust/global_attr.rs
@@ -0,0 +1 @@
+#![allow(unused_variables)]
diff --git a/tests/rust/global_variable.rs b/tests/rust/global_variable.rs
new file mode 100644
index 0000000..5fb8572
--- /dev/null
+++ b/tests/rust/global_variable.rs
@@ -0,0 +1,5 @@
+#[no_mangle]
+pub static mut MUT_GLOBAL_ARRAY: [c_char; 128] = [0; 128];
+
+#[no_mangle]
+pub static CONST_GLOBAL_ARRAY: [c_char; 128] = [0; 128];
diff --git a/tests/rust/ignore.rs b/tests/rust/ignore.rs
new file mode 100644
index 0000000..54ade32
--- /dev/null
+++ b/tests/rust/ignore.rs
@@ -0,0 +1,12 @@
+/// cbindgen:ignore
+#[no_mangle]
+pub extern "C" fn root() {}
+
+/// cbindgen:ignore
+///
+/// Something else.
+#[no_mangle]
+pub extern "C" fn another_root() {}
+
+#[no_mangle]
+pub extern "C" fn no_ignore_root() {}
diff --git a/tests/rust/include.rs b/tests/rust/include.rs
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/rust/include.rs
diff --git a/tests/rust/include.toml b/tests/rust/include.toml
new file mode 100644
index 0000000..7d4e0d4
--- /dev/null
+++ b/tests/rust/include.toml
@@ -0,0 +1 @@
+sys_includes = ["math.h"]
diff --git a/tests/rust/include_guard.rs b/tests/rust/include_guard.rs
new file mode 100644
index 0000000..03de69f
--- /dev/null
+++ b/tests/rust/include_guard.rs
@@ -0,0 +1,3 @@
+#[no_mangle]
+pub extern "C" fn root() {
+}
diff --git a/tests/rust/include_guard.toml b/tests/rust/include_guard.toml
new file mode 100644
index 0000000..e14abb3
--- /dev/null
+++ b/tests/rust/include_guard.toml
@@ -0,0 +1,2 @@
+include_guard = "INCLUDE_GUARD_H"
+no_includes = true
diff --git a/tests/rust/include_item.rs b/tests/rust/include_item.rs
new file mode 100644
index 0000000..9f07550
--- /dev/null
+++ b/tests/rust/include_item.rs
@@ -0,0 +1,10 @@
+#[repr(C)]
+struct A {
+ x: i32,
+ y: f32,
+}
+
+#[repr(C)]
+struct B {
+ data: A,
+}
diff --git a/tests/rust/include_item.toml b/tests/rust/include_item.toml
new file mode 100644
index 0000000..1dc6f25
--- /dev/null
+++ b/tests/rust/include_item.toml
@@ -0,0 +1,2 @@
+[export]
+include = ["B"]
diff --git a/tests/rust/include_specific.rs b/tests/rust/include_specific.rs
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/rust/include_specific.rs
diff --git a/tests/rust/include_specific.toml b/tests/rust/include_specific.toml
new file mode 100644
index 0000000..488ca92
--- /dev/null
+++ b/tests/rust/include_specific.toml
@@ -0,0 +1,2 @@
+sys_includes = ["math.h"]
+no_includes = true
diff --git a/tests/rust/infinite_recursion_typedef_monomorph.rs b/tests/rust/infinite_recursion_typedef_monomorph.rs
new file mode 100644
index 0000000..5c25e73
--- /dev/null
+++ b/tests/rust/infinite_recursion_typedef_monomorph.rs
@@ -0,0 +1,2 @@
+pub type TryVec<T> = fallible_collections::TryVec<T>;
+pub type TryString = fallible_collections::TryVec<u8>;
diff --git a/tests/rust/inner_mod.rs b/tests/rust/inner_mod.rs
new file mode 100644
index 0000000..89411ad
--- /dev/null
+++ b/tests/rust/inner_mod.rs
@@ -0,0 +1,10 @@
+mod foo {
+ #[repr(C)]
+ struct Foo {
+ x: f32,
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn root(a: foo::Foo)
+{ }
diff --git a/tests/rust/item_types.rs b/tests/rust/item_types.rs
new file mode 100644
index 0000000..b54b0ef
--- /dev/null
+++ b/tests/rust/item_types.rs
@@ -0,0 +1,12 @@
+
+pub const MY_CONST: u8 = 4;
+
+#[no_mangle]
+pub extern "C" fn ExternFunction() {
+}
+
+#[repr(u8)]
+pub enum OnlyThisShouldBeGenerated {
+ Foo,
+ Bar,
+}
diff --git a/tests/rust/item_types.toml b/tests/rust/item_types.toml
new file mode 100644
index 0000000..f997f33
--- /dev/null
+++ b/tests/rust/item_types.toml
@@ -0,0 +1,3 @@
+[export]
+item_types = ["enums"]
+include = ["OnlyThisShouldBeGenerated"]
diff --git a/tests/rust/item_types_renamed.rs b/tests/rust/item_types_renamed.rs
new file mode 100644
index 0000000..b54b0ef
--- /dev/null
+++ b/tests/rust/item_types_renamed.rs
@@ -0,0 +1,12 @@
+
+pub const MY_CONST: u8 = 4;
+
+#[no_mangle]
+pub extern "C" fn ExternFunction() {
+}
+
+#[repr(u8)]
+pub enum OnlyThisShouldBeGenerated {
+ Foo,
+ Bar,
+}
diff --git a/tests/rust/item_types_renamed.toml b/tests/rust/item_types_renamed.toml
new file mode 100644
index 0000000..4680ee8
--- /dev/null
+++ b/tests/rust/item_types_renamed.toml
@@ -0,0 +1,4 @@
+[export]
+item_types = ["enums"]
+include = ["OnlyThisShouldBeGenerated"]
+prefix = "Style"
diff --git a/tests/rust/layout.rs b/tests/rust/layout.rs
new file mode 100644
index 0000000..b15c675
--- /dev/null
+++ b/tests/rust/layout.rs
@@ -0,0 +1,108 @@
+#[repr(align(1), C)]
+pub struct Align1Struct {
+ pub arg1: usize,
+ pub arg2: *mut u8,
+}
+
+#[repr(align(2), C)]
+pub struct Align2Struct {
+ pub arg1: usize,
+ pub arg2: *mut u8,
+}
+
+#[repr(align(4), C)]
+pub struct Align4Struct {
+ pub arg1: usize,
+ pub arg2: *mut u8,
+}
+
+#[repr(align(8), C)]
+pub struct Align8Struct {
+ pub arg1: usize,
+ pub arg2: *mut u8,
+}
+
+#[repr(align(32), C)]
+pub struct Align32Struct {
+ pub arg1: usize,
+ pub arg2: *mut u8,
+}
+
+#[repr(packed, C)]
+pub struct PackedStruct {
+ pub arg1: usize,
+ pub arg2: *mut u8,
+}
+
+#[repr(align(1), C)]
+pub union Align1Union {
+ pub variant1: usize,
+ pub variant2: *mut u8,
+}
+
+#[repr(align(4), C)]
+pub union Align4Union {
+ pub variant1: usize,
+ pub variant2: *mut u8,
+}
+
+#[repr(align(16), C)]
+pub union Align16Union {
+ pub variant1: usize,
+ pub variant2: *mut u8,
+}
+
+#[repr(packed, C)]
+pub union PackedUnion {
+ pub variant1: usize,
+ pub variant2: *mut u8,
+}
+
+// #[repr(packed(n), C)] structs are currently unsupported.
+#[repr(packed(4), C)]
+pub struct UnsupportedPacked4Struct {
+ pub arg1: usize,
+ pub arg2: *mut u8,
+}
+
+// #[repr(packed(n), C)] unions are currently unsupported.
+#[repr(packed(4), C)]
+pub union UnsupportedPacked4Union {
+ pub variant1: usize,
+ pub variant2: *mut u8,
+}
+
+// #[repr(align(n), C)] enums are currently unsupported.
+#[repr(align(4), C)]
+pub enum UnsupportedAlign4Enum {
+ Variant1,
+ Variant2,
+}
+
+// Non-repr(C) structs aren't translated.
+#[repr(align(4))]
+pub struct RustAlign4Struct {
+ pub arg1: usize,
+ pub arg2: *mut u8,
+}
+
+// Non-repr(C) structs aren't translated.
+#[repr(packed)]
+pub struct RustPackedStruct {
+ pub arg1: usize,
+ pub arg2: *mut u8,
+}
+
+// Non-repr(C) unions aren't translated.
+#[repr(align(4))]
+pub struct RustAlign4Union {
+ pub arg1: usize,
+ pub arg2: *mut u8,
+}
+
+// Non-repr(C) unions aren't translated.
+#[repr(packed)]
+pub struct RustPackedUnion {
+ pub arg1: usize,
+ pub arg2: *mut u8,
+}
diff --git a/tests/rust/layout.toml b/tests/rust/layout.toml
new file mode 100644
index 0000000..a1316dc
--- /dev/null
+++ b/tests/rust/layout.toml
@@ -0,0 +1,29 @@
+header = """
+#define CBINDGEN_PACKED __attribute__ ((packed))
+#define CBINDGEN_ALIGNED(n) __attribute__ ((aligned(n)))
+"""
+
+[layout]
+packed = "CBINDGEN_PACKED"
+aligned_n = "CBINDGEN_ALIGNED"
+
+[export]
+include = [
+ "Align1Struct",
+ "Align2Struct",
+ "Align4Struct",
+ "Align8Struct",
+ "Align32Struct",
+ "PackedStruct",
+ "Align1Union",
+ "Align4Union",
+ "Align16Union",
+ "PackedUnion",
+ "UnsupportedPacked4Struct",
+ "UnsupportedPacked4Union",
+ "UnsupportedAlign4Enum",
+ "RustAlign4Struct",
+ "RustPackedStruct",
+ "RustAlign4Union",
+ "RustPackedUnion",
+]
diff --git a/tests/rust/layout_aligned_opaque.rs b/tests/rust/layout_aligned_opaque.rs
new file mode 100644
index 0000000..c499124
--- /dev/null
+++ b/tests/rust/layout_aligned_opaque.rs
@@ -0,0 +1,67 @@
+#[repr(packed, C)]
+pub struct PackedStruct {
+ pub arg1: usize,
+ pub arg2: *mut u8,
+}
+
+#[repr(packed, C)]
+pub union PackedUnion {
+ pub variant1: usize,
+ pub variant2: *mut u8,
+}
+
+// Opaque because aligned_n is not defined.
+#[repr(align(1), C)]
+pub union OpaqueAlign1Union {
+ pub variant1: usize,
+ pub variant2: *mut u8,
+}
+
+// Opaque because aligned_n is not defined.
+#[repr(align(4), C)]
+pub union OpaqueAlign4Union {
+ pub variant1: usize,
+ pub variant2: *mut u8,
+}
+
+// Opaque because aligned_n is not defined.
+#[repr(align(16), C)]
+pub union OpaqueAlign16Union {
+ pub variant1: usize,
+ pub variant2: *mut u8,
+}
+
+// Opaque because aligned_n is not defined.
+#[repr(align(1), C)]
+pub struct OpaqueAlign1Struct {
+ pub arg1: usize,
+ pub arg2: *mut u8,
+}
+
+// Opaque because aligned_n is not defined.
+#[repr(align(2), C)]
+pub struct OpaqueAlign2Struct {
+ pub arg1: usize,
+ pub arg2: *mut u8,
+}
+
+// Opaque because aligned_n is not defined.
+#[repr(align(4), C)]
+pub struct OpaqueAlign4Struct {
+ pub arg1: usize,
+ pub arg2: *mut u8,
+}
+
+// Opaque because aligned_n is not defined.
+#[repr(align(8), C)]
+pub struct OpaqueAlign8Struct {
+ pub arg1: usize,
+ pub arg2: *mut u8,
+}
+
+// Opaque because aligned_n is not defined.
+#[repr(align(32), C)]
+pub struct OpaqueAlign32Struct {
+ pub arg1: usize,
+ pub arg2: *mut u8,
+}
diff --git a/tests/rust/layout_aligned_opaque.toml b/tests/rust/layout_aligned_opaque.toml
new file mode 100644
index 0000000..1d416f1
--- /dev/null
+++ b/tests/rust/layout_aligned_opaque.toml
@@ -0,0 +1,22 @@
+header = """
+#define CBINDGEN_PACKED __attribute__ ((packed))
+#define CBINDGEN_ALIGNED(n) __attribute__ ((aligned(n)))
+"""
+
+[layout]
+# We do not define aligned_n.
+packed = "CBINDGEN_PACKED"
+
+[export]
+include = [
+ "PackedStruct",
+ "PackedUnion",
+ "OpaqueAlign1Union",
+ "OpaqueAlign4Union",
+ "OpaqueAlign16Union",
+ "OpaqueAlign1Struct",
+ "OpaqueAlign2Struct",
+ "OpaqueAlign4Struct",
+ "OpaqueAlign8Struct",
+ "OpaqueAlign32Struct",
+]
diff --git a/tests/rust/layout_packed_opaque.rs b/tests/rust/layout_packed_opaque.rs
new file mode 100644
index 0000000..d1e31c6
--- /dev/null
+++ b/tests/rust/layout_packed_opaque.rs
@@ -0,0 +1,61 @@
+#[repr(align(1), C)]
+pub union Align1Union {
+ pub variant1: usize,
+ pub variant2: *mut u8,
+}
+
+#[repr(align(4), C)]
+pub union Align4Union {
+ pub variant1: usize,
+ pub variant2: *mut u8,
+}
+
+#[repr(align(16), C)]
+pub union Align16Union {
+ pub variant1: usize,
+ pub variant2: *mut u8,
+}
+
+#[repr(align(1), C)]
+pub struct Align1Struct {
+ pub arg1: usize,
+ pub arg2: *mut u8,
+}
+
+#[repr(align(2), C)]
+pub struct Align2Struct {
+ pub arg1: usize,
+ pub arg2: *mut u8,
+}
+
+#[repr(align(4), C)]
+pub struct Align4Struct {
+ pub arg1: usize,
+ pub arg2: *mut u8,
+}
+
+#[repr(align(8), C)]
+pub struct Align8Struct {
+ pub arg1: usize,
+ pub arg2: *mut u8,
+}
+
+#[repr(align(32), C)]
+pub struct Align32Struct {
+ pub arg1: usize,
+ pub arg2: *mut u8,
+}
+
+// Opaque because packed is not defined.
+#[repr(packed, C)]
+pub struct OpaquePackedStruct {
+ pub arg1: usize,
+ pub arg2: *mut u8,
+}
+
+// Opaque because packed is not defined.
+#[repr(packed, C)]
+pub union OpaquePackedUnion {
+ pub variant1: usize,
+ pub variant2: *mut u8,
+}
diff --git a/tests/rust/layout_packed_opaque.toml b/tests/rust/layout_packed_opaque.toml
new file mode 100644
index 0000000..b726004
--- /dev/null
+++ b/tests/rust/layout_packed_opaque.toml
@@ -0,0 +1,22 @@
+header = """
+#define CBINDGEN_PACKED __attribute__ ((packed))
+#define CBINDGEN_ALIGNED(n) __attribute__ ((aligned(n)))
+"""
+
+[layout]
+# We do not define packed.
+aligned_n = "CBINDGEN_ALIGNED"
+
+[export]
+include = [
+ "Align1Union",
+ "Align4Union",
+ "Align16Union",
+ "Align1Struct",
+ "Align2Struct",
+ "Align4Struct",
+ "Align8Struct",
+ "Align32Struct",
+ "OpaquePackedStruct",
+ "OpaquePackedUnion",
+]
diff --git a/tests/rust/lifetime_arg.rs b/tests/rust/lifetime_arg.rs
new file mode 100644
index 0000000..b80fde2
--- /dev/null
+++ b/tests/rust/lifetime_arg.rs
@@ -0,0 +1,14 @@
+#[repr(C)]
+struct A<'a> {
+ data: &'a i32
+}
+
+#[repr(C)]
+enum E<'a> {
+ V,
+ U(&'a u8),
+}
+
+#[no_mangle]
+pub extern "C" fn root<'a>(_a: A<'a>, _e: E<'a>)
+{ }
diff --git a/tests/rust/linestyle_cr.rs b/tests/rust/linestyle_cr.rs
new file mode 100644
index 0000000..49a317b
--- /dev/null
+++ b/tests/rust/linestyle_cr.rs
@@ -0,0 +1,8 @@
+#[repr(C)]
+struct Dummy {
+ x: i32,
+ y: f32,
+}
+
+#[no_mangle]
+pub extern "C" fn root(d: Dummy) {}
diff --git a/tests/rust/linestyle_cr.toml b/tests/rust/linestyle_cr.toml
new file mode 100644
index 0000000..43a1517
--- /dev/null
+++ b/tests/rust/linestyle_cr.toml
@@ -0,0 +1 @@
+line_endings = "CR"
diff --git a/tests/rust/linestyle_crlf.rs b/tests/rust/linestyle_crlf.rs
new file mode 100644
index 0000000..49a317b
--- /dev/null
+++ b/tests/rust/linestyle_crlf.rs
@@ -0,0 +1,8 @@
+#[repr(C)]
+struct Dummy {
+ x: i32,
+ y: f32,
+}
+
+#[no_mangle]
+pub extern "C" fn root(d: Dummy) {}
diff --git a/tests/rust/linestyle_crlf.toml b/tests/rust/linestyle_crlf.toml
new file mode 100644
index 0000000..c0ae218
--- /dev/null
+++ b/tests/rust/linestyle_crlf.toml
@@ -0,0 +1 @@
+line_endings = "CRLF"
diff --git a/tests/rust/linestyle_lf.rs b/tests/rust/linestyle_lf.rs
new file mode 100644
index 0000000..49a317b
--- /dev/null
+++ b/tests/rust/linestyle_lf.rs
@@ -0,0 +1,8 @@
+#[repr(C)]
+struct Dummy {
+ x: i32,
+ y: f32,
+}
+
+#[no_mangle]
+pub extern "C" fn root(d: Dummy) {}
diff --git a/tests/rust/linestyle_lf.toml b/tests/rust/linestyle_lf.toml
new file mode 100644
index 0000000..446fd30
--- /dev/null
+++ b/tests/rust/linestyle_lf.toml
@@ -0,0 +1 @@
+line_endings = "LF"
diff --git a/tests/rust/mangle.rs b/tests/rust/mangle.rs
new file mode 100644
index 0000000..1ac7e13
--- /dev/null
+++ b/tests/rust/mangle.rs
@@ -0,0 +1,19 @@
+#[repr(C)]
+pub struct Foo<T> {
+ a: T,
+}
+
+pub type Boo = Foo<u8>;
+
+/// cbindgen:prefix-with-name=true
+#[repr(C)]
+pub enum Bar {
+ Some,
+ Thing,
+}
+
+#[no_mangle]
+pub extern "C" fn root(
+ x: Boo,
+ y: Bar,
+) { }
diff --git a/tests/rust/mangle.toml b/tests/rust/mangle.toml
new file mode 100644
index 0000000..a5d6b65
--- /dev/null
+++ b/tests/rust/mangle.toml
@@ -0,0 +1,3 @@
+[export.mangle]
+remove_underscores = true
+rename_types = "PascalCase"
diff --git a/tests/rust/manuallydrop.rs b/tests/rust/manuallydrop.rs
new file mode 100644
index 0000000..fea2537
--- /dev/null
+++ b/tests/rust/manuallydrop.rs
@@ -0,0 +1,22 @@
+#[repr(C)]
+pub struct Point {
+ x: i32,
+ y: i32,
+}
+
+#[repr(C)]
+pub struct MyStruct {
+ point: std::mem::ManuallyDrop<Point>,
+}
+
+pub struct NotReprC<T> {
+ inner: T,
+}
+
+pub type Foo = NotReprC<std::mem::ManuallyDrop<Point>>;
+
+#[no_mangle]
+pub extern "C" fn root(a: &Foo, with_manual_drop: &MyStruct) {}
+
+#[no_mangle]
+pub extern "C" fn take(with_manual_drop: std::mem::ManuallyDrop<Point>) {}
diff --git a/tests/rust/manuallydrop.toml b/tests/rust/manuallydrop.toml
new file mode 100644
index 0000000..7ffee9a
--- /dev/null
+++ b/tests/rust/manuallydrop.toml
@@ -0,0 +1,18 @@
+header = """
+#if 0
+''' '
+#endif
+
+#ifdef __cplusplus
+template <typename T>
+using ManuallyDrop = T;
+#endif
+
+#if 0
+' '''
+#endif
+"""
+[export]
+exclude = [
+ "ManuallyDrop",
+]
diff --git a/tests/rust/maybeuninit.rs b/tests/rust/maybeuninit.rs
new file mode 100644
index 0000000..8073167
--- /dev/null
+++ b/tests/rust/maybeuninit.rs
@@ -0,0 +1,13 @@
+#[repr(C)]
+pub struct MyStruct<'a> {
+ number: std::mem::MaybeUninit<&'a i32>,
+}
+
+pub struct NotReprC<T> {
+ inner: T,
+}
+
+pub type Foo<'a> = NotReprC<std::mem::MaybeUninit<&'a i32>>;
+
+#[no_mangle]
+pub extern "C" fn root<'a, 'b>(a: &'a Foo, with_maybe_uninit: &'b MyStruct) {}
diff --git a/tests/rust/maybeuninit.toml b/tests/rust/maybeuninit.toml
new file mode 100644
index 0000000..ba671fa
--- /dev/null
+++ b/tests/rust/maybeuninit.toml
@@ -0,0 +1,18 @@
+header = """
+#if 0
+''' '
+#endif
+
+#ifdef __cplusplus
+template <typename T>
+using MaybeUninit = T;
+#endif
+
+#if 0
+' '''
+#endif
+"""
+[export]
+exclude = [
+ "MaybeUninit",
+]
diff --git a/tests/rust/monomorph_1.rs b/tests/rust/monomorph_1.rs
new file mode 100644
index 0000000..a75cc23
--- /dev/null
+++ b/tests/rust/monomorph_1.rs
@@ -0,0 +1,28 @@
+#[repr(C)]
+struct Foo<T> {
+ data: *const T
+}
+
+struct Bar<T> {
+ data: *const T
+}
+
+#[repr(C)]
+struct Tuple<T, E> {
+ a: *const T,
+ b: *const E,
+}
+
+type Indirection<T> = Tuple<T, f32>;
+
+#[no_mangle]
+pub extern "C" fn root(
+ a: Foo<i32>,
+ b: Foo<f32>,
+ c: Bar<f32>,
+ d: Foo<Bar<f32>>,
+ e: Bar<Foo<f32>>,
+ f: Bar<Bar<f32>>,
+ g: Tuple<Foo<f32>, f32>,
+ h: Indirection<f32>
+) { }
diff --git a/tests/rust/monomorph_2.rs b/tests/rust/monomorph_2.rs
new file mode 100644
index 0000000..f81ea5b
--- /dev/null
+++ b/tests/rust/monomorph_2.rs
@@ -0,0 +1,15 @@
+#[repr(C)]
+struct List<T> {
+ members: *mut T,
+ count: usize
+}
+
+struct A;
+
+struct B;
+
+#[no_mangle]
+pub extern "C" fn foo(a: List<A>) { }
+
+#[no_mangle]
+pub extern "C" fn bar(b: List<B>) { }
diff --git a/tests/rust/monomorph_3.rs b/tests/rust/monomorph_3.rs
new file mode 100644
index 0000000..b1d69e4
--- /dev/null
+++ b/tests/rust/monomorph_3.rs
@@ -0,0 +1,28 @@
+#[repr(C)]
+union Foo<T> {
+ data: *const T
+}
+
+union Bar<T> {
+ data: *const T
+}
+
+#[repr(C)]
+union Tuple<T, E> {
+ a: *const T,
+ b: *const E,
+}
+
+type Indirection<T> = Tuple<T, f32>;
+
+#[no_mangle]
+pub extern "C" fn root(
+ a: Foo<i32>,
+ b: Foo<f32>,
+ c: Bar<f32>,
+ d: Foo<Bar<f32>>,
+ e: Bar<Foo<f32>>,
+ f: Bar<Bar<f32>>,
+ g: Tuple<Foo<f32>, f32>,
+ h: Indirection<f32>
+) { }
diff --git a/tests/rust/must_use.rs b/tests/rust/must_use.rs
new file mode 100644
index 0000000..6b2034f
--- /dev/null
+++ b/tests/rust/must_use.rs
@@ -0,0 +1,18 @@
+
+#[repr(C)]
+#[must_use]
+pub struct OwnedPtr<T> {
+ ptr: *mut T,
+}
+
+#[repr(C, u8)]
+#[must_use]
+pub enum MaybeOwnedPtr<T> {
+ Owned(*mut T),
+ None,
+}
+
+#[no_mangle]
+#[must_use]
+pub extern "C" fn maybe_consume(input: OwnedPtr<i32>) -> MaybeOwnedPtr<i32> {
+}
diff --git a/tests/rust/must_use.toml b/tests/rust/must_use.toml
new file mode 100644
index 0000000..939df10
--- /dev/null
+++ b/tests/rust/must_use.toml
@@ -0,0 +1,14 @@
+header = """
+#define MUST_USE_FUNC __attribute__((warn_unused_result))
+#define MUST_USE_STRUCT __attribute__((warn_unused))
+#define MUST_USE_ENUM /* nothing */
+"""
+
+[fn]
+must_use = "MUST_USE_FUNC"
+
+[struct]
+must_use = "MUST_USE_STRUCT"
+
+[enum]
+must_use = "MUST_USE_ENUM"
diff --git a/tests/rust/namespace_constant.rs b/tests/rust/namespace_constant.rs
new file mode 100644
index 0000000..01a8450
--- /dev/null
+++ b/tests/rust/namespace_constant.rs
@@ -0,0 +1,11 @@
+pub const FOO: i32 = 10;
+pub const BAR: &'static str = "hello world";
+pub const ZOM: f32 = 3.14;
+
+#[repr(C)]
+struct Foo {
+ x: [i32; FOO],
+}
+
+#[no_mangle]
+pub extern "C" fn root(x: Foo) { }
diff --git a/tests/rust/namespace_constant.toml b/tests/rust/namespace_constant.toml
new file mode 100644
index 0000000..1b9d94e
--- /dev/null
+++ b/tests/rust/namespace_constant.toml
@@ -0,0 +1 @@
+namespace = "constants"
diff --git a/tests/rust/namespaces_constant.rs b/tests/rust/namespaces_constant.rs
new file mode 100644
index 0000000..01a8450
--- /dev/null
+++ b/tests/rust/namespaces_constant.rs
@@ -0,0 +1,11 @@
+pub const FOO: i32 = 10;
+pub const BAR: &'static str = "hello world";
+pub const ZOM: f32 = 3.14;
+
+#[repr(C)]
+struct Foo {
+ x: [i32; FOO],
+}
+
+#[no_mangle]
+pub extern "C" fn root(x: Foo) { }
diff --git a/tests/rust/namespaces_constant.toml b/tests/rust/namespaces_constant.toml
new file mode 100644
index 0000000..7601ad3
--- /dev/null
+++ b/tests/rust/namespaces_constant.toml
@@ -0,0 +1 @@
+namespaces = ["constants", "test"]
diff --git a/tests/rust/nested_import.rs b/tests/rust/nested_import.rs
new file mode 100644
index 0000000..e080d88
--- /dev/null
+++ b/tests/rust/nested_import.rs
@@ -0,0 +1 @@
+use std::{result, marker::PhantomData};
diff --git a/tests/rust/no_includes.rs b/tests/rust/no_includes.rs
new file mode 100644
index 0000000..03de69f
--- /dev/null
+++ b/tests/rust/no_includes.rs
@@ -0,0 +1,3 @@
+#[no_mangle]
+pub extern "C" fn root() {
+}
diff --git a/tests/rust/no_includes.toml b/tests/rust/no_includes.toml
new file mode 100644
index 0000000..eed30dc
--- /dev/null
+++ b/tests/rust/no_includes.toml
@@ -0,0 +1 @@
+no_includes = true
diff --git a/tests/rust/non_pub_extern.rs b/tests/rust/non_pub_extern.rs
new file mode 100644
index 0000000..4174324
--- /dev/null
+++ b/tests/rust/non_pub_extern.rs
@@ -0,0 +1,13 @@
+#[no_mangle]
+static FIRST: u32 = 10;
+
+#[export_name = "RENAMED"]
+static SECOND: u32 = 42;
+
+#[no_mangle]
+extern "C" fn first()
+{ }
+
+#[export_name = "renamed"]
+extern fn second()
+{ }
diff --git a/tests/rust/nonnull.rs b/tests/rust/nonnull.rs
new file mode 100644
index 0000000..5e07c35
--- /dev/null
+++ b/tests/rust/nonnull.rs
@@ -0,0 +1,19 @@
+use std::ptr::NonNull;
+
+struct Opaque;
+
+#[repr(C)]
+pub struct Foo<T> {
+ a: NonNull<f32>,
+ b: NonNull<T>,
+ c: NonNull<Opaque>,
+ d: NonNull<NonNull<T>>,
+ e: NonNull<NonNull<f32>>,
+ f: NonNull<NonNull<Opaque>>,
+ g: Option<NonNull<T>>,
+ h: Option<NonNull<i32>>,
+ i: Option<NonNull<NonNull<i32>>>,
+}
+
+#[no_mangle]
+pub extern "C" fn root(arg: NonNull<i32>, foo: *mut Foo<u64>, d: NonNull<NonNull<Opaque>>) { }
diff --git a/tests/rust/nonnull_attribute.rs b/tests/rust/nonnull_attribute.rs
new file mode 100644
index 0000000..5089ba9
--- /dev/null
+++ b/tests/rust/nonnull_attribute.rs
@@ -0,0 +1,55 @@
+use std::ptr::NonNull;
+
+struct Opaque;
+
+#[repr(C)]
+pub struct Pointers<T> {
+ a: NonNull<f32>,
+ b: NonNull<T>,
+ c: NonNull<Opaque>,
+ d: NonNull<NonNull<T>>,
+ e: NonNull<NonNull<f32>>,
+ f: NonNull<NonNull<Opaque>>,
+ g: Option<NonNull<T>>,
+ h: Option<NonNull<i32>>,
+ i: Option<NonNull<NonNull<i32>>>,
+ j: *const T,
+ k: *mut T,
+}
+
+#[repr(C)]
+pub struct References<'a> {
+ a: &'a Opaque,
+ b: &'a mut Opaque,
+ c: Option<&'a Opaque>,
+ d: Option<&'a mut Opaque>,
+}
+
+#[no_mangle]
+pub extern "C" fn value_arg(arg: References<'static>) {}
+
+#[no_mangle]
+pub extern "C" fn mutltiple_args(
+ arg: NonNull<i32>,
+ foo: *mut Pointers<u64>,
+ d: NonNull<NonNull<Opaque>>,
+) {
+}
+
+#[no_mangle]
+pub extern "C" fn ref_arg(arg: &Pointers<u64>) {}
+
+#[no_mangle]
+pub extern "C" fn mut_ref_arg(arg: &mut Pointers<u64>) {}
+
+#[no_mangle]
+pub extern "C" fn optional_ref_arg(arg: Option<&Pointers<u64>>) {}
+
+#[no_mangle]
+pub extern "C" fn optional_mut_ref_arg(arg: Option<&mut Pointers<u64>>) {}
+
+#[no_mangle]
+pub extern "C" fn nullable_const_ptr(arg: *const Pointers<u64>) {}
+
+#[no_mangle]
+pub extern "C" fn nullable_mut_ptr(arg: *mut Pointers<u64>) {}
diff --git a/tests/rust/nonnull_attribute.toml b/tests/rust/nonnull_attribute.toml
new file mode 100644
index 0000000..8c051d7
--- /dev/null
+++ b/tests/rust/nonnull_attribute.toml
@@ -0,0 +1,10 @@
+header = """
+#ifdef __clang__
+#define CBINDGEN_NONNULL _Nonnull
+#else
+#define CBINDGEN_NONNULL
+#endif
+"""
+
+[ptr]
+non_null_attribute = "CBINDGEN_NONNULL" \ No newline at end of file
diff --git a/tests/rust/nonzero.rs b/tests/rust/nonzero.rs
new file mode 100644
index 0000000..9bc2cde
--- /dev/null
+++ b/tests/rust/nonzero.rs
@@ -0,0 +1,30 @@
+use std::num::*;
+
+#[repr(C)]
+pub struct NonZeroTest {
+ pub a: NonZeroU8,
+ pub b: NonZeroU16,
+ pub c: NonZeroU32,
+ pub d: NonZeroU64,
+ pub e: NonZeroI8,
+ pub f: NonZeroI16,
+ pub g: NonZeroI32,
+ pub h: NonZeroI64,
+ pub i: Option<NonZeroI64>,
+ pub j: *const Option<Option<NonZeroI64>>,
+}
+
+#[no_mangle]
+pub extern "C" fn root(
+ test: NonZeroTest,
+ a: NonZeroU8,
+ b: NonZeroU16,
+ c: NonZeroU32,
+ d: NonZeroU64,
+ e: NonZeroI8,
+ f: NonZeroI16,
+ g: NonZeroI32,
+ h: NonZeroI64,
+ i: Option<NonZeroI64>,
+ j: *const Option<Option<NonZeroI64>>,
+) {}
diff --git a/tests/rust/nonzero.toml b/tests/rust/nonzero.toml
new file mode 100644
index 0000000..3c656dc
--- /dev/null
+++ b/tests/rust/nonzero.toml
@@ -0,0 +1,13 @@
+header = """
+#if 0
+''' '
+#endif
+
+#ifdef __cplusplus
+struct NonZeroI64;
+#endif
+
+#if 0
+' '''
+#endif
+"""
diff --git a/tests/rust/opaque.rs b/tests/rust/opaque.rs
new file mode 100644
index 0000000..7e1c6be
--- /dev/null
+++ b/tests/rust/opaque.rs
@@ -0,0 +1,10 @@
+/// Fast hash map used internally.
+type FastHashMap<K, V> =
+ std::collections::HashMap<K, V, std::hash::BuildHasherDefault<std::collections::hash_map::DefaultHasher>>;
+
+pub type Foo = FastHashMap<i32, i32>;
+
+pub type Bar = Result<Foo, ()>;
+
+#[no_mangle]
+pub extern "C" fn root(a: &Foo, b: &Bar) {}
diff --git a/tests/rust/opaque.toml b/tests/rust/opaque.toml
new file mode 100644
index 0000000..cf56c97
--- /dev/null
+++ b/tests/rust/opaque.toml
@@ -0,0 +1,17 @@
+header = """
+#if 0
+''' '
+#endif
+
+#ifdef __cplusplus
+// These could be added as opaque types I guess.
+template <typename T>
+struct BuildHasherDefault;
+
+struct DefaultHasher;
+#endif
+
+#if 0
+' '''
+#endif
+"""
diff --git a/tests/rust/pin.rs b/tests/rust/pin.rs
new file mode 100644
index 0000000..c8781ae
--- /dev/null
+++ b/tests/rust/pin.rs
@@ -0,0 +1,8 @@
+#[repr(C)]
+struct PinTest {
+ pinned_box: Pin<Box<i32>>,
+ pinned_ref: Pin<&mut i32>
+}
+
+#[no_mangle]
+pub extern "C" fn root(s: Pin<&mut i32>, p: PinTest) {}
diff --git a/tests/rust/pin.toml b/tests/rust/pin.toml
new file mode 100644
index 0000000..5aeccf9
--- /dev/null
+++ b/tests/rust/pin.toml
@@ -0,0 +1,21 @@
+header = """
+#if 0
+''' '
+#endif
+
+#ifdef __cplusplus
+template <typename T>
+using Pin = T;
+template <typename T>
+using Box = T*;
+#endif
+
+#if 0
+' '''
+#endif
+"""
+[export]
+exclude = [
+ "Pin",
+ "Box"
+]
diff --git a/tests/rust/pragma_once.skip_warning_as_error.rs b/tests/rust/pragma_once.skip_warning_as_error.rs
new file mode 100644
index 0000000..03de69f
--- /dev/null
+++ b/tests/rust/pragma_once.skip_warning_as_error.rs
@@ -0,0 +1,3 @@
+#[no_mangle]
+pub extern "C" fn root() {
+}
diff --git a/tests/rust/pragma_once.skip_warning_as_error.toml b/tests/rust/pragma_once.skip_warning_as_error.toml
new file mode 100644
index 0000000..2d52bc7
--- /dev/null
+++ b/tests/rust/pragma_once.skip_warning_as_error.toml
@@ -0,0 +1 @@
+pragma_once = true
diff --git a/tests/rust/prefix.rs b/tests/rust/prefix.rs
new file mode 100644
index 0000000..89d4638
--- /dev/null
+++ b/tests/rust/prefix.rs
@@ -0,0 +1,20 @@
+pub const LEN: i32 = 22;
+
+pub type NamedLenArray = [i32; LEN];
+pub type ValuedLenArray = [i32; 22];
+
+#[repr(u8)]
+pub enum AbsoluteFontWeight {
+ Weight(f32),
+ Normal,
+ Bold,
+}
+
+#[no_mangle]
+pub extern "C" fn root(x: NamedLenArray, y: ValuedLenArray, z: AbsoluteFontWeight) {}
+
+#[no_mangle]
+pub const X: i64 = 22 << 22;
+
+#[no_mangle]
+pub const Y: i64 = X + X;
diff --git a/tests/rust/prefix.toml b/tests/rust/prefix.toml
new file mode 100644
index 0000000..648c142
--- /dev/null
+++ b/tests/rust/prefix.toml
@@ -0,0 +1,2 @@
+[export]
+prefix = "PREFIX_"
diff --git a/tests/rust/prefixed_struct_literal.rs b/tests/rust/prefixed_struct_literal.rs
new file mode 100644
index 0000000..1b8544d
--- /dev/null
+++ b/tests/rust/prefixed_struct_literal.rs
@@ -0,0 +1,14 @@
+#[repr(C)]
+struct Foo {
+ a: i32,
+ b: u32,
+}
+
+impl Foo {
+ pub const FOO: Foo = Foo{ a: 42, b: 47, };
+}
+
+pub const BAR: Foo = Foo{ a: 42, b: 1337, };
+
+#[no_mangle]
+pub extern "C" fn root(x: Foo) { }
diff --git a/tests/rust/prefixed_struct_literal.toml b/tests/rust/prefixed_struct_literal.toml
new file mode 100644
index 0000000..189d09a
--- /dev/null
+++ b/tests/rust/prefixed_struct_literal.toml
@@ -0,0 +1,2 @@
+[export]
+prefix = "PREFIX"
diff --git a/tests/rust/prefixed_struct_literal_deep.rs b/tests/rust/prefixed_struct_literal_deep.rs
new file mode 100644
index 0000000..bfa2d8d
--- /dev/null
+++ b/tests/rust/prefixed_struct_literal_deep.rs
@@ -0,0 +1,20 @@
+#[repr(C)]
+struct Foo {
+ a: i32,
+ b: u32,
+ bar: Bar,
+}
+
+#[repr(C)]
+struct Bar {
+ a: i32,
+}
+
+pub const VAL: Foo = Foo {
+ a: 42,
+ b: 1337,
+ bar: Bar { a: 323 },
+};
+
+#[no_mangle]
+pub extern "C" fn root(x: Foo) {}
diff --git a/tests/rust/prefixed_struct_literal_deep.toml b/tests/rust/prefixed_struct_literal_deep.toml
new file mode 100644
index 0000000..189d09a
--- /dev/null
+++ b/tests/rust/prefixed_struct_literal_deep.toml
@@ -0,0 +1,2 @@
+[export]
+prefix = "PREFIX"
diff --git a/tests/rust/ptrs_as_arrays.rs b/tests/rust/ptrs_as_arrays.rs
new file mode 100644
index 0000000..02cb908
--- /dev/null
+++ b/tests/rust/ptrs_as_arrays.rs
@@ -0,0 +1,19 @@
+/// cbindgen:ptrs-as-arrays=[[arg;3]]
+#[no_mangle]
+pub unsafe extern "C" fn ptr_as_array(n: u32, arg: *const u32, v: *const u64) {}
+
+/// cbindgen:ptrs-as-arrays=[[arg;3], [v; 4]]
+#[no_mangle]
+pub unsafe extern "C" fn ptr_as_array1(n: u32, arg: *const u32, v: *mut u64) {}
+
+/// cbindgen:ptrs-as-arrays=[[n;2], [arg; ], [v;], [k; 3]]
+#[no_mangle]
+pub unsafe extern "C" fn ptr_as_array2(n: u32, arg: *mut u32, v: *const u64) {}
+
+/// cbindgen:ptrs-as-arrays=[[a;2;3]]
+#[no_mangle]
+pub unsafe extern "C" fn ptr_as_array_wrong_syntax(arg: *mut u32, v: *const u32, _: *const u32) {}
+
+/// cbindgen:ptrs-as-arrays=[[_;2], [_;3]]
+#[no_mangle]
+pub unsafe extern "C" fn ptr_as_array_unnamed(_: *mut u32, _: *const u32) {}
diff --git a/tests/rust/raw_ident.rs b/tests/rust/raw_ident.rs
new file mode 100644
index 0000000..d3ceb56
--- /dev/null
+++ b/tests/rust/raw_ident.rs
@@ -0,0 +1,20 @@
+#[repr(u8)]
+pub enum r#Enum {
+ r#a,
+ r#b,
+}
+
+#[repr(C)]
+pub struct r#Struct {
+ r#field: r#Enum,
+}
+
+#[no_mangle]
+pub extern "C" fn r#fn(r#arg: r#Struct) {
+ println!("Hello world");
+}
+
+pub mod r#mod {
+ #[no_mangle]
+ pub static r#STATIC: r#Enum = r#Enum::r#b;
+}
diff --git a/tests/rust/raw_lines.rs b/tests/rust/raw_lines.rs
new file mode 100644
index 0000000..03de69f
--- /dev/null
+++ b/tests/rust/raw_lines.rs
@@ -0,0 +1,3 @@
+#[no_mangle]
+pub extern "C" fn root() {
+}
diff --git a/tests/rust/raw_lines.toml b/tests/rust/raw_lines.toml
new file mode 100644
index 0000000..13a2eba
--- /dev/null
+++ b/tests/rust/raw_lines.toml
@@ -0,0 +1,3 @@
+include_guard = "INCLUDE_GUARD_H"
+no_includes = false
+after_includes = "#define VERSION 1"
diff --git a/tests/rust/rename.rs b/tests/rust/rename.rs
new file mode 100644
index 0000000..ba10249
--- /dev/null
+++ b/tests/rust/rename.rs
@@ -0,0 +1,47 @@
+struct A {
+ x: i32,
+ y: f32,
+}
+
+#[repr(C)]
+struct B {
+ x: i32,
+ y: f32,
+}
+
+union C {
+ x: i32,
+ y: f32,
+}
+
+#[repr(C)]
+union D {
+ x: i32,
+ y: f32,
+}
+
+#[repr(u8)]
+enum E {
+ x = 0,
+ y = 1,
+}
+
+type F = A;
+
+#[no_mangle]
+pub static G: i32 = 10;
+
+pub const H: i32 = 10;
+
+pub const I: isize = 10 as *mut F as isize;
+
+#[no_mangle]
+pub extern "C" fn root(
+ a: *const A,
+ b: B,
+ c: C,
+ d: D,
+ e: E,
+ f: F,
+) { }
+
diff --git a/tests/rust/rename.toml b/tests/rust/rename.toml
new file mode 100644
index 0000000..1b76559
--- /dev/null
+++ b/tests/rust/rename.toml
@@ -0,0 +1,5 @@
+[export]
+prefix = "C_"
+
+[export.rename]
+"B" = "AwesomeB"
diff --git a/tests/rust/rename_case.rs b/tests/rust/rename_case.rs
new file mode 100644
index 0000000..e83227d
--- /dev/null
+++ b/tests/rust/rename_case.rs
@@ -0,0 +1,19 @@
+/// cbindgen:rename-all=CamelCase
+#[no_mangle]
+pub extern "C" fn test_camel_case(foo_bar: i32) {}
+
+/// cbindgen:rename-all=PascalCase
+#[no_mangle]
+pub extern "C" fn test_pascal_case(foo_bar: i32) {}
+
+/// cbindgen:rename-all=SnakeCase
+#[no_mangle]
+pub extern "C" fn test_snake_case(foo_bar: i32) {}
+
+/// cbindgen:rename-all=ScreamingSnakeCase
+#[no_mangle]
+pub extern "C" fn test_screaming_snake_case(foo_bar: i32) {}
+
+/// cbindgen:rename-all=GeckoCase
+#[no_mangle]
+pub extern "C" fn test_gecko_case(foo_bar: i32) {}
diff --git a/tests/rust/renaming_overrides_prefixing.rs b/tests/rust/renaming_overrides_prefixing.rs
new file mode 100644
index 0000000..3212ee5
--- /dev/null
+++ b/tests/rust/renaming_overrides_prefixing.rs
@@ -0,0 +1,13 @@
+struct A {
+ x: i32,
+ y: f32,
+}
+
+#[repr(C)]
+struct B {
+ x: i32,
+ y: f32,
+}
+
+#[no_mangle]
+pub extern "C" fn root(a: *const A, b: B) {}
diff --git a/tests/rust/renaming_overrides_prefixing.toml b/tests/rust/renaming_overrides_prefixing.toml
new file mode 100644
index 0000000..3cd8cf9
--- /dev/null
+++ b/tests/rust/renaming_overrides_prefixing.toml
@@ -0,0 +1,6 @@
+[export]
+prefix = "Style"
+renaming_overrides_prefixing = true
+
+[export.rename]
+"B" = "B" # B should remain unprefixed.
diff --git a/tests/rust/reserved.rs b/tests/rust/reserved.rs
new file mode 100644
index 0000000..df9d9a5
--- /dev/null
+++ b/tests/rust/reserved.rs
@@ -0,0 +1,37 @@
+#[repr(C)]
+struct A {
+ namespace: i32,
+ float: f32,
+}
+
+/// cbindgen:field-names=[namespace, float]
+#[repr(C)]
+struct B(i32, f32);
+
+#[repr(C, u8)]
+enum C {
+ D { namespace: i32, float: f32 },
+}
+
+#[repr(C, u8)]
+enum E {
+ Double(f64),
+ Float(f32),
+}
+
+#[repr(C, u8)]
+enum F {
+ double(f64),
+ float(f32),
+}
+
+#[no_mangle]
+pub extern "C" fn root(
+ a: A,
+ b: B,
+ c: C,
+ e: E,
+ f: F,
+ namespace: i32,
+ float: f32,
+) { }
diff --git a/tests/rust/sentinel.rs b/tests/rust/sentinel.rs
new file mode 100644
index 0000000..601b6ee
--- /dev/null
+++ b/tests/rust/sentinel.rs
@@ -0,0 +1,23 @@
+#[repr(u8)]
+pub enum A {
+ A1,
+ A2,
+ A3,
+}
+
+#[repr(u8)]
+pub enum B {
+ B1,
+ B2,
+ B3,
+}
+
+#[repr(u8)]
+pub enum C {
+ C1 { a: u32 },
+ C2 { b: u32 },
+ C3,
+}
+
+#[no_mangle]
+pub extern "C" fn root(a: A, b: B, c: C) {}
diff --git a/tests/rust/sentinel.toml b/tests/rust/sentinel.toml
new file mode 100644
index 0000000..281803f
--- /dev/null
+++ b/tests/rust/sentinel.toml
@@ -0,0 +1,3 @@
+[enum]
+add_sentinel = true
+prefix_with_name = true
diff --git a/tests/rust/simplify_option_ptr.rs b/tests/rust/simplify_option_ptr.rs
new file mode 100644
index 0000000..22eeef8
--- /dev/null
+++ b/tests/rust/simplify_option_ptr.rs
@@ -0,0 +1,28 @@
+
+struct Opaque();
+
+#[repr(C)]
+struct Foo {
+ x: Option<&Opaque>,
+ y: Option<&mut Opaque>,
+ z: Option<fn () -> ()>,
+ zz: *mut Option<fn () -> ()>,
+}
+
+#[repr(C)]
+union Bar {
+ x: Option<&Opaque>,
+ y: Option<&mut Opaque>,
+ z: Option<fn () -> ()>,
+ zz: *mut Option<fn () -> ()>,
+}
+
+#[no_mangle]
+pub extern "C" fn root(
+ a: Option<&Opaque>,
+ b: Option<&mut Opaque>,
+ c: Foo,
+ d: Bar,
+ e: *mut Option<*mut Opaque>,
+ f: extern "C" fn(Option<&Opaque>),
+) { }
diff --git a/tests/rust/size_types.rs b/tests/rust/size_types.rs
new file mode 100644
index 0000000..6dfaeb2
--- /dev/null
+++ b/tests/rust/size_types.rs
@@ -0,0 +1,15 @@
+type Usize = usize;
+type Isize = isize;
+
+#[repr(usize)]
+enum UE {
+ UV,
+}
+
+#[repr(isize)]
+enum IE {
+ IV,
+}
+
+#[no_mangle]
+pub extern "C" fn root(_: Usize, _: Isize, _: UE, _: IE) {}
diff --git a/tests/rust/size_types.toml b/tests/rust/size_types.toml
new file mode 100644
index 0000000..b020c68
--- /dev/null
+++ b/tests/rust/size_types.toml
@@ -0,0 +1 @@
+usize_is_size_t = true
diff --git a/tests/rust/static.rs b/tests/rust/static.rs
new file mode 100644
index 0000000..a18e4a2
--- /dev/null
+++ b/tests/rust/static.rs
@@ -0,0 +1,17 @@
+#[no_mangle]
+pub static NUMBER: i32 = 10;
+
+#[repr(C)]
+struct Foo {
+}
+
+struct Bar {
+}
+
+#[no_mangle]
+pub static mut FOO: Foo = Foo { };
+#[no_mangle]
+pub static BAR: Bar = Bar { };
+
+#[no_mangle]
+pub extern "C" fn root() { }
diff --git a/tests/rust/std_lib.rs b/tests/rust/std_lib.rs
new file mode 100644
index 0000000..05e0f2a
--- /dev/null
+++ b/tests/rust/std_lib.rs
@@ -0,0 +1,6 @@
+#[no_mangle]
+pub extern "C" fn root(
+ a: &Vec<String>,
+ b: &Option<i32>,
+ c: &Result<i32, String>
+) { }
diff --git a/tests/rust/struct.rs b/tests/rust/struct.rs
new file mode 100644
index 0000000..7569901
--- /dev/null
+++ b/tests/rust/struct.rs
@@ -0,0 +1,38 @@
+use std::marker::PhantomData;
+
+struct Opaque {
+ x: i32,
+ y: f32,
+}
+
+#[repr(C)]
+struct Normal {
+ x: i32,
+ y: f32,
+}
+
+#[repr(C)]
+struct NormalWithZST {
+ x: i32,
+ y: f32,
+ z: (),
+ w: PhantomData<i32>,
+ v: PhantomPinned,
+}
+
+/// cbindgen:rename-all=GeckoCase
+#[repr(C)]
+struct TupleRenamed(i32, f32);
+
+/// cbindgen:field-names=[x, y]
+#[repr(C)]
+struct TupleNamed(i32, f32);
+
+#[no_mangle]
+pub extern "C" fn root(
+ a: *mut Opaque,
+ b: Normal,
+ c: NormalWithZST,
+ d: TupleRenamed,
+ e: TupleNamed
+) { }
diff --git a/tests/rust/struct_literal.rs b/tests/rust/struct_literal.rs
new file mode 100644
index 0000000..733dac3
--- /dev/null
+++ b/tests/rust/struct_literal.rs
@@ -0,0 +1,23 @@
+#[repr(C)]
+struct Foo {
+ a: i32,
+ b: u32,
+}
+
+struct Bar {
+ a: i32,
+ b: u32,
+}
+
+impl Foo {
+ pub const FOO: Foo = Foo { a: 42, b: 47, };
+ pub const FOO2: Self = Foo { a: 42, b: 47, };
+ pub const FOO3: Self = Self { a: 42, b: 47, };
+ pub const BAZ: Bar = Bar { a: 42, b: 47, };
+}
+
+pub const BAR: Foo = Foo { a: 42, b: 1337, };
+pub const BAZZ: Bar = Bar { a: 42, b: 1337, };
+
+#[no_mangle]
+pub extern "C" fn root(x: Foo, bar: Bar) { }
diff --git a/tests/rust/struct_literal_order.rs b/tests/rust/struct_literal_order.rs
new file mode 100644
index 0000000..a5336c0
--- /dev/null
+++ b/tests/rust/struct_literal_order.rs
@@ -0,0 +1,28 @@
+#[repr(C)]
+struct ABC {
+ pub a: f32,
+ pub b: u32,
+ pub c: u32,
+}
+
+#[repr(C)]
+struct BAC {
+ pub b: u32,
+ pub a: f32,
+ pub c: i32,
+}
+
+impl ABC {
+ pub const abc: ABC = ABC { a: 1.0, b: 2, c: 3 };
+ pub const bac: ABC = ABC { b: 2, a: 1.0, c: 3 };
+ pub const cba: ABC = ABC { c: 3, b: 2, a: 1.0 };
+}
+
+impl BAC {
+ pub const abc: BAC = BAC { a: 2.0, b: 1, c: 3 };
+ pub const bac: BAC = BAC { b: 1, a: 2.0, c: 3 };
+ pub const cba: BAC = BAC { c: 3, b: 1, a: 2.0 };
+}
+
+#[no_mangle]
+pub extern "C" fn root(a1: ABC, a2: BAC) {}
diff --git a/tests/rust/struct_self.rs b/tests/rust/struct_self.rs
new file mode 100644
index 0000000..9432082
--- /dev/null
+++ b/tests/rust/struct_self.rs
@@ -0,0 +1,14 @@
+#[repr(C)]
+pub struct Foo<T> {
+ something: *const i32,
+ phantom: std::marker::PhantomData<T>,
+}
+
+#[repr(C)]
+pub struct Bar {
+ something: i32,
+ subexpressions: Foo<Self>,
+}
+
+#[no_mangle]
+pub extern "C" fn root(b: Bar) {}
diff --git a/tests/rust/style_crash.rs b/tests/rust/style_crash.rs
new file mode 100644
index 0000000..ef692d8
--- /dev/null
+++ b/tests/rust/style_crash.rs
@@ -0,0 +1,7 @@
+pub trait SpecifiedValueInfo {
+ const SUPPORTED_TYPES: u8 = 0;
+}
+
+impl<T: SpecifiedValueInfo> SpecifiedValueInfo for [T] {
+ const SUPPORTED_TYPES: u8 = T::SUPPORTED_TYPES;
+}
diff --git a/tests/rust/swift_name.rs b/tests/rust/swift_name.rs
new file mode 100644
index 0000000..01cfdbe
--- /dev/null
+++ b/tests/rust/swift_name.rs
@@ -0,0 +1,133 @@
+#[export_name="rust_print_hello_world"]
+pub extern fn say_hello() {
+ println!("Hello, World!");
+}
+
+#[repr(C)]
+pub struct SelfTypeTestStruct {
+ times: u8,
+}
+
+impl SelfTypeTestStruct {
+ #[export_name="SelfTypeTestStruct_should_exist_ref"]
+ #[no_mangle]
+ pub extern fn should_exist_ref(&self) {
+ println!("should_exist_ref");
+ }
+
+ #[export_name="SelfTypeTestStruct_should_exist_ref_mut"]
+ #[no_mangle]
+ pub extern fn should_exist_ref_mut(&mut self) {
+ println!("should_exist_ref_mut");
+ }
+
+ #[export_name="SelfTypeTestStruct_should_not_exist_box"]
+ #[no_mangle]
+ pub extern fn should_not_exist_box(self: Box<SelfTypeTestStruct>) {
+ println!("should_not_exist_box");
+ }
+
+ #[export_name="SelfTypeTestStruct_should_not_exist_return_box"]
+ #[no_mangle]
+ pub extern fn should_not_exist_box() -> Box<Self> {
+ println!("should_not_exist_box");
+ }
+
+ #[export_name="SelfTypeTestStruct_should_exist_annotated_self"]
+ #[no_mangle]
+ pub extern fn should_exist_annotated_self(self: Self) {
+ println!("should_exist_annotated_self");
+ }
+
+ #[export_name="SelfTypeTestStruct_should_exist_annotated_mut_self"]
+ #[no_mangle]
+ #[allow(unused_mut)]
+ pub extern fn should_exist_annotated_mut_self(mut self: Self) {
+ println!("should_exist_annotated_mut_self");
+ }
+
+ #[export_name="SelfTypeTestStruct_should_exist_annotated_by_name"]
+ #[no_mangle]
+ pub extern fn should_exist_annotated_by_name(self: SelfTypeTestStruct) {
+ println!("should_exist_annotated_by_name");
+ }
+
+ #[export_name="SelfTypeTestStruct_should_exist_annotated_mut_by_name"]
+ #[no_mangle]
+ #[allow(unused_mut)]
+ pub extern fn should_exist_annotated_mut_by_name(mut self: SelfTypeTestStruct) {
+ println!("should_exist_annotated_mut_by_name");
+ }
+
+ #[export_name="SelfTypeTestStruct_should_exist_unannotated"]
+ #[no_mangle]
+ pub extern fn should_exist_unannotated(self) {
+ println!("should_exist_unannotated");
+ }
+
+ #[export_name="SelfTypeTestStruct_should_exist_mut_unannotated"]
+ #[no_mangle]
+ #[allow(unused_mut)]
+ pub extern fn should_exist_mut_unannotated(mut self) {
+ println!("should_exist_mut_unannotated");
+ }
+}
+
+#[no_mangle]
+#[allow(unused_variables)]
+pub extern fn free_function_should_exist_ref(test_struct: &SelfTypeTestStruct) {
+ println!("free_function_should_exist_ref");
+}
+
+#[no_mangle]
+#[allow(unused_variables)]
+pub extern fn free_function_should_exist_ref_mut(test_struct: &mut SelfTypeTestStruct) {
+ println!("free_function_should_exist_ref_mut");
+}
+
+#[no_mangle]
+pub extern fn unnamed_argument(_: &mut SelfTypeTestStruct) {
+ println!("unnamed_argument");
+}
+
+#[no_mangle]
+#[allow(unused_variables)]
+pub extern fn free_function_should_not_exist_box(boxed: Box<SelfTypeTestStruct>) {
+ println!("free_function_should_not_exist_box");
+}
+
+#[no_mangle]
+#[allow(unused_variables)]
+pub extern fn free_function_should_exist_annotated_by_name(test_struct: SelfTypeTestStruct) {
+ println!("free_function_should_exist_annotated_by_name");
+}
+
+#[no_mangle]
+#[allow(unused_mut)]
+#[allow(unused_variables)]
+pub extern fn free_function_should_exist_annotated_mut_by_name(mut test_struct: SelfTypeTestStruct) {
+ println!("free_function_should_exist_annotated_mut_by_name");
+}
+
+struct Opaque {
+ times: u8
+}
+
+#[repr(C)]
+pub struct PointerToOpaque { ptr: *mut Opaque }
+
+impl PointerToOpaque {
+ #[export_name="PointerToOpaque_create"]
+ pub extern fn create(times: u8) -> PointerToOpaque {
+ PointerToOpaque { ptr: Box::into_raw(Box::new(Opaque { times })) }
+ }
+
+ #[export_name="PointerToOpaque_sayHello"]
+ pub extern fn say_hello(self: PointerToOpaque) {
+ if let Some(nonnull) = std::ptr::NonNull::new(self.ptr) {
+ for _ in 0 .. unsafe { nonnull.as_ref().times } {
+ println!("Hello!")
+ }
+ }
+ }
+}
diff --git a/tests/rust/swift_name.toml b/tests/rust/swift_name.toml
new file mode 100644
index 0000000..b04dc63
--- /dev/null
+++ b/tests/rust/swift_name.toml
@@ -0,0 +1,4 @@
+header = "#define CF_SWIFT_NAME(_name) __attribute__((swift_name(#_name)))"
+
+[fn]
+swift_name_macro = "CF_SWIFT_NAME"
diff --git a/tests/rust/transform_op.rs b/tests/rust/transform_op.rs
new file mode 100644
index 0000000..fd5c093
--- /dev/null
+++ b/tests/rust/transform_op.rs
@@ -0,0 +1,43 @@
+#[repr(C)]
+pub struct Point<T> {
+ pub x: T,
+ pub y: T,
+}
+
+#[repr(u8)]
+pub enum Foo<T> {
+ Foo { x: i32, y: Point<T>, z: Point<f32>, },
+ Bar(T),
+ Baz(Point<T>),
+ Bazz,
+}
+
+#[repr(C)]
+pub enum Bar<T> {
+ Bar1 { x: i32, y: Point<T>, z: Point<f32>, u: unsafe extern "C" fn(i32) -> i32, },
+ Bar2(T),
+ Bar3(Point<T>),
+ Bar4,
+}
+
+#[repr(u8)]
+pub enum Baz {
+ Baz1(Bar<u32>),
+ Baz2(Point<i32>),
+ Baz3,
+}
+
+#[repr(C, u8)]
+pub enum Taz {
+ Taz1(Bar<u32>),
+ Taz2(Baz),
+ Taz3,
+}
+
+#[no_mangle]
+pub extern "C" fn foo(
+ foo: *const Foo<i32>,
+ bar: *const Bar<i32>,
+ baz: *const Baz,
+ taz: *const Taz,
+) {}
diff --git a/tests/rust/transform_op.toml b/tests/rust/transform_op.toml
new file mode 100644
index 0000000..d11adff
--- /dev/null
+++ b/tests/rust/transform_op.toml
@@ -0,0 +1,7 @@
+[export]
+prefix = "Style"
+
+[enum]
+derive_helper_methods = true
+derive_const_casts = true
+derive_mut_casts = true
diff --git a/tests/rust/transparent.rs b/tests/rust/transparent.rs
new file mode 100644
index 0000000..6dc77c1
--- /dev/null
+++ b/tests/rust/transparent.rs
@@ -0,0 +1,67 @@
+struct DummyStruct;
+
+// Transparent struct tuple wrapping a struct.
+#[repr(transparent)]
+struct TransparentComplexWrappingStructTuple(DummyStruct);
+
+// Transparent struct tuple wrapping a primitive.
+#[repr(transparent)]
+struct TransparentPrimitiveWrappingStructTuple(u32);
+
+// Transparent structure wrapping a struct.
+#[repr(transparent)]
+struct TransparentComplexWrappingStructure { only_field: DummyStruct }
+
+// Transparent structure wrapping a primitive.
+#[repr(transparent)]
+struct TransparentPrimitiveWrappingStructure { only_field: u32 }
+
+// Transparent struct wrapper with a marker wrapping a struct.
+#[repr(transparent)]
+struct TransparentComplexWrapper<T> {
+ only_non_zero_sized_field: DummyStruct,
+ marker: PhantomData<T>,
+}
+
+// Transparent struct wrapper with a marker wrapping a primitive.
+#[repr(transparent)]
+struct TransparentPrimitiveWrapper<T> {
+ only_non_zero_sized_field: u32,
+ marker: PhantomData<T>,
+}
+
+// Associated constant declared before struct declaration.
+impl TransparentPrimitiveWithAssociatedConstants {
+ pub const ZERO: TransparentPrimitiveWithAssociatedConstants = TransparentPrimitiveWithAssociatedConstants {
+ bits: 0
+ };
+}
+
+// Transparent structure wrapping a primitive with associated constants.
+#[repr(transparent)]
+struct TransparentPrimitiveWithAssociatedConstants { bits: u32 }
+
+// Associated constant declared after struct declaration.
+impl TransparentPrimitiveWithAssociatedConstants {
+ pub const ONE: TransparentPrimitiveWithAssociatedConstants = TransparentPrimitiveWithAssociatedConstants {
+ bits: 1
+ };
+}
+
+enum EnumWithAssociatedConstantInImpl { A }
+
+impl EnumWithAssociatedConstantInImpl {
+ pub const TEN: TransparentPrimitiveWrappingStructure = TransparentPrimitiveWrappingStructure { only_field: 10 };
+}
+
+#[no_mangle]
+pub extern "C" fn root(
+ a: TransparentComplexWrappingStructTuple,
+ b: TransparentPrimitiveWrappingStructTuple,
+ c: TransparentComplexWrappingStructure,
+ d: TransparentPrimitiveWrappingStructure,
+ e: TransparentComplexWrapper<i32>,
+ f: TransparentPrimitiveWrapper<i32>,
+ g: TransparentPrimitiveWithAssociatedConstants,
+ h: EnumWithAssociatedConstantInImpl,
+) { }
diff --git a/tests/rust/typedef.rs b/tests/rust/typedef.rs
new file mode 100644
index 0000000..bf7ceb8
--- /dev/null
+++ b/tests/rust/typedef.rs
@@ -0,0 +1,11 @@
+#[repr(C)]
+struct Foo<T, U> {
+ x: T,
+ y: U,
+}
+
+type IntFoo<T> = Foo<i32, T>;
+
+#[no_mangle]
+pub extern "C" fn root(a: IntFoo<i32>)
+{ }
diff --git a/tests/rust/union.rs b/tests/rust/union.rs
new file mode 100644
index 0000000..b76f98a
--- /dev/null
+++ b/tests/rust/union.rs
@@ -0,0 +1,27 @@
+use std::marker::PhantomData;
+
+union Opaque {
+ x: i32,
+ y: f32,
+}
+
+#[repr(C)]
+union Normal {
+ x: i32,
+ y: f32,
+}
+
+#[repr(C)]
+union NormalWithZST {
+ x: i32,
+ y: f32,
+ z: (),
+ w: PhantomData<i32>,
+}
+
+#[no_mangle]
+pub extern "C" fn root(
+ a: *mut Opaque,
+ b: Normal,
+ c: NormalWithZST
+) { }
diff --git a/tests/rust/union_self.rs b/tests/rust/union_self.rs
new file mode 100644
index 0000000..77b0b0f
--- /dev/null
+++ b/tests/rust/union_self.rs
@@ -0,0 +1,14 @@
+#[repr(C)]
+pub struct Foo<T> {
+ something: *const i32,
+ phantom: std::marker::PhantomData<T>,
+}
+
+#[repr(C)]
+pub union Bar {
+ something: i32,
+ subexpressions: Foo<Self>,
+}
+
+#[no_mangle]
+pub extern "C" fn root(b: Bar) {}
diff --git a/tests/rust/using_namespaces.rs b/tests/rust/using_namespaces.rs
new file mode 100644
index 0000000..03de69f
--- /dev/null
+++ b/tests/rust/using_namespaces.rs
@@ -0,0 +1,3 @@
+#[no_mangle]
+pub extern "C" fn root() {
+}
diff --git a/tests/rust/using_namespaces.toml b/tests/rust/using_namespaces.toml
new file mode 100644
index 0000000..a1dd9a7
--- /dev/null
+++ b/tests/rust/using_namespaces.toml
@@ -0,0 +1,2 @@
+namespaces = ["root"]
+using_namespaces = ["std"] \ No newline at end of file
diff --git a/tests/rust/va_list.rs b/tests/rust/va_list.rs
new file mode 100644
index 0000000..13503dd
--- /dev/null
+++ b/tests/rust/va_list.rs
@@ -0,0 +1,11 @@
+use std::ffi::VaList;
+
+#[no_mangle]
+pub unsafe extern "C" fn va_list_test(mut ap: VaList) -> int32_t {
+ ap.arg()
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn va_list_test2(mut ap: ...) -> int32_t {
+ ap.arg()
+}
diff --git a/tests/rust/zst.rs b/tests/rust/zst.rs
new file mode 100644
index 0000000..8a3e467
--- /dev/null
+++ b/tests/rust/zst.rs
@@ -0,0 +1,10 @@
+#[repr(C)]
+pub struct TraitObject {
+ pub data: *mut (),
+ pub vtable: *mut (),
+}
+
+#[no_mangle]
+pub extern "C" fn root(ptr: *const (), t: TraitObject) -> *mut () {
+ std::ptr::null_mut()
+}
diff --git a/tests/testing-helpers.h b/tests/testing-helpers.h
new file mode 100644
index 0000000..29e4499
--- /dev/null
+++ b/tests/testing-helpers.h
@@ -0,0 +1,20 @@
+#ifndef testing_helpers_h
+#define testing_helpers_h
+
+// This is a helper file to easily add static_asserts to C / C++ tests.
+
+#ifndef __cplusplus
+#include <assert.h>
+#endif
+
+#if defined(CBINDGEN_STYLE_TAG) && !defined(__cplusplus)
+#define CBINDGEN_STRUCT(name) struct name
+#define CBINDGEN_UNION(name) union name
+#define CBINDGEN_ENUM(name) enum name
+#else
+#define CBINDGEN_STRUCT(name) name
+#define CBINDGEN_UNION(name) name
+#define CBINDGEN_ENUM(name) name
+#endif
+
+#endif
diff --git a/tests/tests.rs b/tests/tests.rs
new file mode 100644
index 0000000..3846f32
--- /dev/null
+++ b/tests/tests.rs
@@ -0,0 +1,371 @@
+extern crate cbindgen;
+
+use cbindgen::*;
+use std::collections::HashSet;
+use std::fs::File;
+use std::io::Read;
+use std::path::Path;
+use std::process::Command;
+use std::{env, fs, str};
+
+// Set automatically by cargo for integration tests
+static CBINDGEN_PATH: &str = env!("CARGO_BIN_EXE_cbindgen");
+
+fn style_str(style: Style) -> &'static str {
+ match style {
+ Style::Both => "both",
+ Style::Tag => "tag",
+ Style::Type => "type",
+ }
+}
+
+fn run_cbindgen(
+ path: &Path,
+ output: Option<&Path>,
+ language: Language,
+ cpp_compat: bool,
+ style: Option<Style>,
+ generate_depfile: bool,
+) -> (Vec<u8>, Option<String>) {
+ assert!(
+ !(output.is_none() && generate_depfile),
+ "generating a depfile requires outputting to a path"
+ );
+ let program = Path::new(CBINDGEN_PATH);
+ let mut command = Command::new(program);
+ if let Some(output) = output {
+ command.arg("--output").arg(output);
+ }
+ let cbindgen_depfile = if generate_depfile {
+ let depfile = tempfile::NamedTempFile::new().unwrap();
+ command.arg("--depfile").arg(depfile.path());
+ Some(depfile)
+ } else {
+ None
+ };
+
+ match language {
+ Language::Cxx => {}
+ Language::C => {
+ command.arg("--lang").arg("c");
+
+ if cpp_compat {
+ command.arg("--cpp-compat");
+ }
+ }
+ Language::Cython => {
+ command.arg("--lang").arg("cython");
+ }
+ }
+
+ if let Some(style) = style {
+ command.arg("--style").arg(style_str(style));
+ }
+
+ let config = path.with_extension("toml");
+ if config.exists() {
+ command.arg("--config").arg(config);
+ }
+
+ command.arg(path);
+
+ println!("Running: {:?}", command);
+ let cbindgen_output = command.output().expect("failed to execute process");
+
+ assert!(
+ cbindgen_output.status.success(),
+ "cbindgen failed: {:?} with error: {}",
+ output,
+ str::from_utf8(&cbindgen_output.stderr).unwrap_or_default()
+ );
+
+ let bindings = if let Some(output_path) = output {
+ let mut bindings = Vec::new();
+ // Ignore errors here, we have assertions on the expected output later.
+ let _ = File::open(output_path).map(|mut file| {
+ let _ = file.read_to_end(&mut bindings);
+ });
+ bindings
+ } else {
+ cbindgen_output.stdout
+ };
+
+ let depfile_contents = if let Some(mut depfile) = cbindgen_depfile {
+ let mut raw = Vec::new();
+ depfile.read_to_end(&mut raw).unwrap();
+ Some(
+ str::from_utf8(raw.as_slice())
+ .expect("Invalid encoding encountered in depfile")
+ .into(),
+ )
+ } else {
+ None
+ };
+ (bindings, depfile_contents)
+}
+
+fn compile(
+ cbindgen_output: &Path,
+ tests_path: &Path,
+ tmp_dir: &Path,
+ language: Language,
+ style: Option<Style>,
+ skip_warning_as_error: bool,
+) {
+ let cc = match language {
+ Language::Cxx => env::var("CXX").unwrap_or_else(|_| "g++".to_owned()),
+ Language::C => env::var("CC").unwrap_or_else(|_| "gcc".to_owned()),
+ Language::Cython => env::var("CYTHON").unwrap_or_else(|_| "cython".to_owned()),
+ };
+
+ let file_name = cbindgen_output
+ .file_name()
+ .expect("cbindgen output should be a file");
+ let mut object = tmp_dir.join(file_name);
+ object.set_extension("o");
+
+ let mut command = Command::new(cc);
+ match language {
+ Language::Cxx | Language::C => {
+ command.arg("-D").arg("DEFINED");
+ command.arg("-I").arg(tests_path);
+ command.arg("-Wall");
+ if !skip_warning_as_error {
+ command.arg("-Werror");
+ }
+ // `swift_name` is not recognzied by gcc.
+ command.arg("-Wno-attributes");
+ // clang warns about unused const variables.
+ command.arg("-Wno-unused-const-variable");
+ // clang also warns about returning non-instantiated templates (they could
+ // be specialized, but they're not so it's fine).
+ command.arg("-Wno-return-type-c-linkage");
+ // deprecated warnings should not be errors as it's intended
+ command.arg("-Wno-deprecated-declarations");
+
+ if let Language::Cxx = language {
+ // enum class is a c++11 extension which makes g++ on macos 10.14 error out
+ // inline variables are are a c++17 extension
+ command.arg("-std=c++17");
+ // Prevents warnings when compiling .c files as c++.
+ command.arg("-x").arg("c++");
+ if let Ok(extra_flags) = env::var("CXXFLAGS") {
+ command.args(extra_flags.split_whitespace());
+ }
+ } else if let Ok(extra_flags) = env::var("CFLAGS") {
+ command.args(extra_flags.split_whitespace());
+ }
+
+ if let Some(style) = style {
+ command.arg("-D");
+ command.arg(format!(
+ "CBINDGEN_STYLE_{}",
+ style_str(style).to_uppercase()
+ ));
+ }
+
+ command.arg("-o").arg(&object);
+ command.arg("-c").arg(cbindgen_output);
+ }
+ Language::Cython => {
+ command.arg("-Wextra");
+ if !skip_warning_as_error {
+ // Our tests contain code that is deprecated in Cython 3.0.
+ // Allowing warnings buys a little time.
+ // command.arg("-Werror");
+ }
+ command.arg("-3");
+ command.arg("-o").arg(&object);
+ command.arg(cbindgen_output);
+ }
+ }
+
+ println!("Running: {:?}", command);
+ let out = command.output().expect("failed to compile");
+ assert!(out.status.success(), "Output failed to compile: {:?}", out);
+
+ if object.exists() {
+ fs::remove_file(object).unwrap();
+ }
+}
+
+const SKIP_WARNING_AS_ERROR_SUFFIX: &str = ".skip_warning_as_error";
+
+#[allow(clippy::too_many_arguments)]
+fn run_compile_test(
+ name: &'static str,
+ path: &Path,
+ tmp_dir: &Path,
+ language: Language,
+ cpp_compat: bool,
+ style: Option<Style>,
+ cbindgen_outputs: &mut HashSet<Vec<u8>>,
+) {
+ let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
+ let tests_path = Path::new(&crate_dir).join("tests");
+ let mut generated_file = tests_path.join("expectations");
+ fs::create_dir_all(&generated_file).unwrap();
+
+ let style_ext = style
+ // Cython is sensitive to dots, so we can't include any dots.
+ .map(|style| match style {
+ Style::Both => "_both",
+ Style::Tag => "_tag",
+ Style::Type => "",
+ })
+ .unwrap_or_default();
+ let lang_ext = match language {
+ Language::Cxx => ".cpp",
+ Language::C if cpp_compat => ".compat.c",
+ Language::C => ".c",
+ // cbindgen is supposed to generate declaration files (`.pxd`), but `cython` compiler
+ // is extension-sensitive and won't work on them, so we use implementation files (`.pyx`)
+ // in the test suite.
+ Language::Cython => ".pyx",
+ };
+
+ let skip_warning_as_error = name.rfind(SKIP_WARNING_AS_ERROR_SUFFIX).is_some();
+
+ let source_file =
+ format!("{}{}{}", name, style_ext, lang_ext).replace(SKIP_WARNING_AS_ERROR_SUFFIX, "");
+
+ generated_file.push(source_file);
+
+ let (output_file, generate_depfile) = if env::var_os("CBINDGEN_TEST_VERIFY").is_some() {
+ (None, false)
+ } else {
+ (
+ Some(generated_file.as_path()),
+ // --depfile does not work in combination with expanding yet, so we blacklist expanding tests.
+ !(name.contains("expand") || name.contains("bitfield")),
+ )
+ };
+
+ let (cbindgen_output, depfile_contents) = run_cbindgen(
+ path,
+ output_file,
+ language,
+ cpp_compat,
+ style,
+ generate_depfile,
+ );
+ if generate_depfile {
+ let depfile = depfile_contents.expect("No depfile generated");
+ assert!(!depfile.is_empty());
+ let mut rules = depfile.split(':');
+ let target = rules.next().expect("No target found");
+ assert_eq!(target, generated_file.as_os_str().to_str().unwrap());
+ let sources = rules.next().unwrap();
+ // All the tests here only have one sourcefile.
+ assert!(
+ sources.contains(path.to_str().unwrap()),
+ "Path: {:?}, Depfile contents: {}",
+ path,
+ depfile
+ );
+ assert_eq!(rules.count(), 0, "More than 1 rule in the depfile");
+ }
+
+ if cbindgen_outputs.contains(&cbindgen_output) {
+ // We already generated an identical file previously.
+ if env::var_os("CBINDGEN_TEST_VERIFY").is_some() {
+ assert!(!generated_file.exists());
+ } else if generated_file.exists() {
+ fs::remove_file(&generated_file).unwrap();
+ }
+ } else {
+ if env::var_os("CBINDGEN_TEST_VERIFY").is_some() {
+ let prev_cbindgen_output = fs::read(&generated_file).unwrap();
+ assert_eq!(cbindgen_output, prev_cbindgen_output);
+ } else {
+ fs::write(&generated_file, &cbindgen_output).unwrap();
+ }
+
+ cbindgen_outputs.insert(cbindgen_output);
+
+ if env::var_os("CBINDGEN_TEST_NO_COMPILE").is_some() {
+ return;
+ }
+
+ compile(
+ &generated_file,
+ &tests_path,
+ tmp_dir,
+ language,
+ style,
+ skip_warning_as_error,
+ );
+
+ if language == Language::C && cpp_compat {
+ compile(
+ &generated_file,
+ &tests_path,
+ tmp_dir,
+ Language::Cxx,
+ style,
+ skip_warning_as_error,
+ );
+ }
+ }
+}
+
+fn test_file(name: &'static str, filename: &'static str) {
+ let test = Path::new(filename);
+ let tmp_dir = tempfile::Builder::new()
+ .prefix("cbindgen-test-output")
+ .tempdir()
+ .expect("Creating tmp dir failed");
+ let tmp_dir = tmp_dir.path();
+ // Run tests in deduplication priority order. C++ compatibility tests are run first,
+ // otherwise we would lose the C++ compiler run if they were deduplicated.
+ let mut cbindgen_outputs = HashSet::new();
+ for cpp_compat in &[true, false] {
+ for style in &[Style::Type, Style::Tag, Style::Both] {
+ run_compile_test(
+ name,
+ test,
+ tmp_dir,
+ Language::C,
+ *cpp_compat,
+ Some(*style),
+ &mut cbindgen_outputs,
+ );
+ }
+ }
+
+ run_compile_test(
+ name,
+ test,
+ tmp_dir,
+ Language::Cxx,
+ /* cpp_compat = */ false,
+ None,
+ &mut HashSet::new(),
+ );
+
+ // `Style::Both` should be identical to `Style::Tag` for Cython.
+ let mut cbindgen_outputs = HashSet::new();
+ for style in &[Style::Type, Style::Tag] {
+ run_compile_test(
+ name,
+ test,
+ tmp_dir,
+ Language::Cython,
+ /* cpp_compat = */ false,
+ Some(*style),
+ &mut cbindgen_outputs,
+ );
+ }
+}
+
+macro_rules! test_file {
+ ($test_function_name:ident, $name:expr, $file:tt) => {
+ #[test]
+ fn $test_function_name() {
+ test_file($name, $file);
+ }
+ };
+}
+
+// This file is generated by build.rs
+include!(concat!(env!("OUT_DIR"), "/tests.rs"));