diff --git a/bindgen-tests/tests/expectations/tests/generated/wrap_static_fns_cxx.cpp b/bindgen-tests/tests/expectations/tests/generated/wrap_static_fns_cxx.cpp new file mode 100644 index 0000000000..0526b9791c --- /dev/null +++ b/bindgen-tests/tests/expectations/tests/generated/wrap_static_fns_cxx.cpp @@ -0,0 +1,33 @@ +#include "tests/headers/wrap-static-fns-cxx.hpp" + +// Static wrappers + +int foo__extern(void) { return foo(); } +int bar__extern(void) { return bar(); } +int takes_ptr__extern(int *arg) { return takes_ptr(arg); } +int takes_fn_ptr__extern(int (*f) (int)) { return takes_fn_ptr(f); } +int takes_fn__extern(int (f) (int)) { return takes_fn(f); } +int takes_alias__extern(func f) { return takes_alias(f); } +int takes_qualified__extern(const int *const *arg) { return takes_qualified(arg); } +enum foo takes_enum__extern(const enum foo f) { return takes_enum(f); } +void nevermore__extern(void) { nevermore(); } +int takes_fn_with_no_args__extern(int (f) (void)) { return takes_fn_with_no_args(f); } +void no_extra_argument__extern(__builtin_va_list va) { no_extra_argument(va); } +int many_va_list__extern(int i, __builtin_va_list va1, __builtin_va_list va2) { return many_va_list(i, va1, va2); } +int wrap_as_variadic_fn1__extern(int i, ...) { + int ret; + va_list ap; + + va_start(ap, i); + ret = wrap_as_variadic_fn1(i, ap); + va_end(ap); + return ret; +} +void wrap_as_variadic_fn2__extern(int i, ...) { + va_list ap; + + va_start(ap, i); + wrap_as_variadic_fn2(i, ap); + va_end(ap); +} +int qux_foo__extern(void) { return qux::foo(); } diff --git a/bindgen-tests/tests/expectations/tests/wrap-static-fns-cxx.rs b/bindgen-tests/tests/expectations/tests/wrap-static-fns-cxx.rs new file mode 100644 index 0000000000..d5054bcec0 --- /dev/null +++ b/bindgen-tests/tests/expectations/tests/wrap-static-fns-cxx.rs @@ -0,0 +1,95 @@ +#![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)] +unsafe extern "C" { + #[link_name = "\u{1}_ZL3foov"] + pub fn foo() -> ::std::os::raw::c_int; +} +unsafe extern "C" { + #[link_name = "\u{1}_ZL3barv"] + pub fn bar() -> ::std::os::raw::c_int; +} +unsafe extern "C" { + #[link_name = "\u{1}_ZL9takes_ptrPi"] + pub fn takes_ptr(arg: *mut ::std::os::raw::c_int) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + #[link_name = "\u{1}_ZL12takes_fn_ptrPFiiE"] + pub fn takes_fn_ptr( + f: ::std::option::Option< + unsafe extern "C" fn(arg1: ::std::os::raw::c_int) -> ::std::os::raw::c_int, + >, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + #[link_name = "\u{1}_ZL8takes_fnPFiiE"] + pub fn takes_fn( + f: ::std::option::Option< + unsafe extern "C" fn(arg1: ::std::os::raw::c_int) -> ::std::os::raw::c_int, + >, + ) -> ::std::os::raw::c_int; +} +pub type func = ::std::option::Option< + unsafe extern "C" fn(arg1: ::std::os::raw::c_int) -> ::std::os::raw::c_int, +>; +unsafe extern "C" { + #[link_name = "\u{1}_ZL11takes_aliasPFiiE"] + pub fn takes_alias(f: func) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + #[link_name = "\u{1}_ZL15takes_qualifiedPKPKi"] + pub fn takes_qualified( + arg: *const *const ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int; +} +pub const foo_BAR: foo = 0; +pub type foo = ::std::os::raw::c_uint; +unsafe extern "C" { + #[link_name = "\u{1}_ZL10takes_enum3foo"] + pub fn takes_enum(f: foo) -> foo; +} +unsafe extern "C" { + #[link_name = "\u{1}_ZL9nevermorev"] + pub fn nevermore(); +} +unsafe extern "C" { + #[link_name = "\u{1}_ZL21takes_fn_with_no_argsPFivE"] + pub fn takes_fn_with_no_args( + f: ::std::option::Option ::std::os::raw::c_int>, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + #[link_name = "\u{1}_ZL17no_extra_argumentP13__va_list_tag"] + pub fn no_extra_argument(va: *mut __va_list_tag); +} +unsafe extern "C" { + #[link_name = "\u{1}_ZL12many_va_listiP13__va_list_tagS0_"] + pub fn many_va_list( + i: ::std::os::raw::c_int, + va1: *mut __va_list_tag, + va2: *mut __va_list_tag, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + #[link_name = "\u{1}_ZL20wrap_as_variadic_fn1iP13__va_list_tag"] + pub fn wrap_as_variadic_fn1_wrapped( + i: ::std::os::raw::c_int, + ... + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + #[link_name = "\u{1}_ZL20wrap_as_variadic_fn2iP13__va_list_tag"] + pub fn wrap_as_variadic_fn2_wrapped(i: ::std::os::raw::c_int, ...); +} +unsafe extern "C" { + #[link_name = "\u{1}_ZN3quxL3fooEv"] + pub fn qux_foo() -> ::std::os::raw::c_int; +} +pub type __builtin_va_list = [__va_list_tag; 1usize]; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct __va_list_tag { + pub gp_offset: ::std::os::raw::c_uint, + pub fp_offset: ::std::os::raw::c_uint, + pub overflow_arg_area: *mut ::std::os::raw::c_void, + pub reg_save_area: *mut ::std::os::raw::c_void, + _unused: [u8; 0], +} diff --git a/bindgen-tests/tests/headers/wrap-static-fns-cxx.hpp b/bindgen-tests/tests/headers/wrap-static-fns-cxx.hpp new file mode 100644 index 0000000000..0aca49912c --- /dev/null +++ b/bindgen-tests/tests/headers/wrap-static-fns-cxx.hpp @@ -0,0 +1,90 @@ +// bindgen-flags: --wrap-static-fns +// bindgen-parse-callbacks: wrap-as-variadic-fn + +// to avoid polluting the expectation tests we put the stdarg.h behind a conditional +// variable only used in bindgen-integration +#ifdef USE_VA_HEADER +#include +#endif + +static inline int foo() { + return 11; +} +static int bar(); +static int bar() { + return 1; +} +inline int baz() { + return 2; +} + +static inline int takes_ptr(int* arg) { + return *arg + 1; +} + +static inline int takes_fn_ptr(int (*f)(int)) { + return f(1); +} + +static inline int takes_fn(int (f)(int)) { + return f(2); +} + +typedef int (func)(int); + +static inline int takes_alias(func f) { + return f(3); +} + +static inline int takes_qualified(const int *const *arg) { + return **arg; +} + +enum foo { + BAR = 0x0, +}; + +static inline enum foo takes_enum(const enum foo f) { + return f; +} + +static inline void nevermore() { + while (1) { } +} + +static inline int takes_fn_with_no_args(int(f)(void)) { + return f(); +} + +static inline int variadic(int x, ...) { + return x; +} + +static inline void no_extra_argument(__builtin_va_list va) {} + +static inline int many_va_list(int i, __builtin_va_list va1, __builtin_va_list va2) { + return i; +} + +#ifndef USE_VA_HEADER +static inline int wrap_as_variadic_fn1(int i, __builtin_va_list va) { + return i; +} + +static inline void wrap_as_variadic_fn2(int i, __builtin_va_list va) {} +#else +static inline int wrap_as_variadic_fn1(int i, va_list va) { + int res = 0; + + for (int j = 0; j < i; j++) + res += (int) va_arg(va, int); + + return res; +} + +static inline void wrap_as_variadic_fn2(int i, va_list va) {} +#endif + +namespace qux { + static int foo(); +} diff --git a/bindgen-tests/tests/tests.rs b/bindgen-tests/tests/tests.rs index b808dfcd43..69d5e9af38 100644 --- a/bindgen-tests/tests/tests.rs +++ b/bindgen-tests/tests/tests.rs @@ -757,3 +757,41 @@ fn test_wrap_static_fns() { .unwrap(); } } + +#[test] +fn test_wrap_static_fns_cxx() { + // This test is for testing diffs of the generated C source and header files + // TODO: If another such feature is added, convert this test into a more generic + // test that looks at `tests/headers/generated` directory. + let expect_path = PathBuf::from("tests/expectations/tests/generated") + .join("wrap_static_fns_cxx"); + println!("In path is ::: {}", expect_path.display()); + + let generated_path = + PathBuf::from(env::var("OUT_DIR").unwrap()).join("wrap_static_fns_cxx"); + println!("Out path is ::: {}", generated_path.display()); + + let _bindings = Builder::default() + .header("tests/headers/wrap-static-fns-cxx.hpp") + .wrap_static_fns(true) + .wrap_static_fns_path(generated_path.display().to_string()) + .parse_callbacks(Box::new(parse_callbacks::WrapAsVariadicFn)) + .generate() + .expect("Failed to generate bindings"); + + let expected_cpp = fs::read_to_string(expect_path.with_extension("cpp")) + .expect("Could not read generated wrap_static_fns_cxx.cpp"); + + let actual_cpp = fs::read_to_string(generated_path.with_extension("cpp")) + .expect("Could not read actual wrap_static_fns_cxx.cpp"); + + if expected_cpp != actual_cpp { + error_diff_mismatch( + &actual_cpp, + &expected_cpp, + None, + &expect_path.with_extension("cpp"), + ) + .unwrap(); + } +} diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index f5518e432d..b64a4b1a36 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -4677,7 +4677,7 @@ impl CodeGenerator for Function { let should_wrap = is_internal && ctx.options().wrap_static_fns && - link_name_attr.is_none(); + (link_name_attr.is_none() || utils::is_cpp(ctx)); if should_wrap { let name = canonical_name.clone() + ctx.wrap_static_fns_suffix(); @@ -5201,6 +5201,15 @@ pub(crate) mod utils { use std::path::PathBuf; use std::str::FromStr; + pub(super) fn is_cpp(context: &BindgenContext) -> bool { + args_are_cpp(&context.options().clang_args) || + context + .options() + .input_headers + .iter() + .any(|h| file_is_cpp(h)) + } + pub(super) fn serialize_items( result: &CodegenResult, context: &BindgenContext, @@ -5220,14 +5229,8 @@ pub(crate) mod utils { std::fs::create_dir_all(dir)?; } - let is_cpp = args_are_cpp(&context.options().clang_args) || - context - .options() - .input_headers - .iter() - .any(|h| file_is_cpp(h)); - - let source_path = path.with_extension(if is_cpp { "cpp" } else { "c" }); + let source_path = + path.with_extension(if is_cpp(context) { "cpp" } else { "c" }); let mut code = Vec::new(); diff --git a/bindgen/codegen/serialize.rs b/bindgen/codegen/serialize.rs index c7bb6cecef..84a4e00750 100644 --- a/bindgen/codegen/serialize.rs +++ b/bindgen/codegen/serialize.rs @@ -79,7 +79,16 @@ impl<'a> CSerialize<'a> for Function { assert!(!signature.is_variadic()); - let name = self.name(); + // Get the function name + namespace + let name = { + let path = item.path_for_allowlisting(ctx).clone(); + if path.get(0).is_some_and(|part| part == "root") { + &path[1..] + } else { + &path[..] + } + .join("::") + }; // Function arguments stored as `(name, type_id)` tuples. let args = { @@ -114,7 +123,11 @@ impl<'a> CSerialize<'a> for Function { }; // The name used for the wrapper self. - let wrap_name = format!("{name}{}", ctx.wrap_static_fns_suffix()); + let wrap_name = format!( + "{}{}", + item.canonical_name(ctx), + ctx.wrap_static_fns_suffix() + ); // The function's return type let (ret_item, ret_ty) = {