diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-18 05:39:07 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-18 05:39:07 +0000 |
commit | af6b8ed095f88f1df2116cdc7a9d44872cfa6074 (patch) | |
tree | 1f2df671c1f8033d5ed83f056167a0911f8d2a57 /tests | |
parent | Initial commit. (diff) | |
download | rust-cbindgen-af6b8ed095f88f1df2116cdc7a9d44872cfa6074.tar.xz rust-cbindgen-af6b8ed095f88f1df2116cdc7a9d44872cfa6074.zip |
Adding upstream version 0.26.0.upstream/0.26.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
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")); |