From 4ff0c5288076ea626d533e6ce27ab61bc5db483d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Youn=20M=C3=A9lois?= <youn@melois.dev>
Date: Wed, 26 Feb 2025 18:57:06 +0100
Subject: [PATCH 01/11] perf: small optimization !iter.all => iter.any!

---
 bindgen/codegen/mod.rs | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs
index f5518e432d..ddd40d972e 100644
--- a/bindgen/codegen/mod.rs
+++ b/bindgen/codegen/mod.rs
@@ -5906,7 +5906,8 @@ pub(crate) mod utils {
 
             // Check that the suffix starts with '@' and is all ASCII decimals
             // after that.
-            if suffix[0] != b'@' || !suffix[1..].iter().all(u8::is_ascii_digit)
+            if suffix[0] != b'@' ||
+                suffix[1..].iter().any(|&c| !c.is_ascii_digit())
             {
                 return false;
             }

From e1a1eed0428cb72314f4a0165bcc533a5ba409aa Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Youn=20M=C3=A9lois?= <youn@melois.dev>
Date: Wed, 26 Feb 2025 19:58:57 +0100
Subject: [PATCH 02/11] test: add test for wrap-static-fns in C++ mode

---
 .../tests/generated/wrap_static_fns.cpp       | 33 +++++++
 .../tests/headers/wrap-static-fns.hpp         | 90 +++++++++++++++++++
 bindgen-tests/tests/tests.rs                  | 38 ++++++++
 3 files changed, 161 insertions(+)
 create mode 100644 bindgen-tests/tests/expectations/tests/generated/wrap_static_fns.cpp
 create mode 100644 bindgen-tests/tests/headers/wrap-static-fns.hpp

diff --git a/bindgen-tests/tests/expectations/tests/generated/wrap_static_fns.cpp b/bindgen-tests/tests/expectations/tests/generated/wrap_static_fns.cpp
new file mode 100644
index 0000000000..357acc9187
--- /dev/null
+++ b/bindgen-tests/tests/expectations/tests/generated/wrap_static_fns.cpp
@@ -0,0 +1,33 @@
+#include "tests/headers/wrap-static-fns.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 foo__extern(void) { return foo(); }
diff --git a/bindgen-tests/tests/headers/wrap-static-fns.hpp b/bindgen-tests/tests/headers/wrap-static-fns.hpp
new file mode 100644
index 0000000000..0aca49912c
--- /dev/null
+++ b/bindgen-tests/tests/headers/wrap-static-fns.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 <stdarg.h>
+#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..ad888a77bb 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_cpp() {
+    // 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");
+    println!("In path is ::: {}", expect_path.display());
+
+    let generated_path =
+        PathBuf::from(env::var("OUT_DIR").unwrap()).join("wrap_static_fns");
+    println!("Out path is ::: {}", generated_path.display());
+
+    let _bindings = Builder::default()
+        .header("tests/headers/wrap-static-fns.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.cpp");
+
+    let actual_cpp = fs::read_to_string(generated_path.with_extension("cpp"))
+        .expect("Could not read actual wrap_static_fns.cpp");
+
+    if expected_cpp != actual_cpp {
+        error_diff_mismatch(
+            &actual_cpp,
+            &expected_cpp,
+            None,
+            &expect_path.with_extension("cpp"),
+        )
+        .unwrap();
+    }
+}

From 25b321a96891dfd0cda22dc37e4a26b8b69fba8b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Youn=20M=C3=A9lois?= <youn@melois.dev>
Date: Wed, 26 Feb 2025 20:00:00 +0100
Subject: [PATCH 03/11] fix: name mangling check for C++

---
 Cargo.lock             | 10 ++++++++++
 Cargo.toml             |  1 +
 bindgen/Cargo.toml     |  1 +
 bindgen/codegen/mod.rs | 26 ++++++++++++++++++++++++++
 4 files changed, 38 insertions(+)

diff --git a/Cargo.lock b/Cargo.lock
index 4e88c224f5..f6c31cd4b1 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -43,6 +43,7 @@ dependencies = [
  "clang-sys",
  "clap",
  "clap_complete",
+ "cpp_demangle",
  "itertools",
  "log",
  "prettyplease",
@@ -187,6 +188,15 @@ dependencies = [
  "os_str_bytes",
 ]
 
+[[package]]
+name = "cpp_demangle"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96e58d342ad113c2b878f16d5d034c03be492ae460cdbc02b7f0f2284d310c7d"
+dependencies = [
+ "cfg-if",
+]
+
 [[package]]
 name = "either"
 version = "1.13.0"
diff --git a/Cargo.toml b/Cargo.toml
index b0a5bbb082..05aef7656c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -30,6 +30,7 @@ cexpr = "0.6"
 clang-sys = "1"
 clap = "4"
 clap_complete = "4"
+cpp_demangle = "0.4.4"
 env_logger = "0.10.0"
 itertools = { version = ">=0.10,<0.14", default-features = false }
 libloading = "0.8"
diff --git a/bindgen/Cargo.toml b/bindgen/Cargo.toml
index c01f8f0c44..8ca55b5959 100644
--- a/bindgen/Cargo.toml
+++ b/bindgen/Cargo.toml
@@ -32,6 +32,7 @@ cexpr.workspace = true
 clang-sys = { workspace = true, features = ["clang_11_0"] }
 clap = { workspace = true, features = ["derive"], optional = true }
 clap_complete = { workspace = true, optional = true }
+cpp_demangle.workspace = true
 itertools = { workspace = true }
 log = { workspace = true, optional = true }
 prettyplease = { workspace = true, optional = true, features = ["verbatim"] }
diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs
index ddd40d972e..09c870495b 100644
--- a/bindgen/codegen/mod.rs
+++ b/bindgen/codegen/mod.rs
@@ -5858,6 +5858,32 @@ pub(crate) mod utils {
             return true;
         }
 
+        // Check if the mangled name is a valid C++ symbol
+        if let Ok(demangled) = cpp_demangle::Symbol::new(mangled_name) {
+            let demangled_name = demangled.to_string().replace("::", "_");
+
+            // Check that the demangled name is longer than the canonical name
+            if demangled_name.len() <= canonical_name.len() {
+                return false;
+            }
+
+            // Check that the demangled name starts with the canonical name
+            if !demangled_name.starts_with(canonical_name) {
+                return false;
+            }
+
+            // Check that the suffix is a signature
+            if let Some(suffix) = demangled_name.get(canonical_name.len()..) {
+                if !suffix.starts_with('(') || !suffix.ends_with(')') {
+                    return false;
+                }
+            } else {
+                return false;
+            }
+
+            return true;
+        }
+
         // Working with &[u8] makes indexing simpler than with &str
         let canonical_name = canonical_name.as_bytes();
         let mangled_name = mangled_name.as_bytes();

From 2deeabe2be3c3e742fbe1d69e0c268af52daad93 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Youn=20M=C3=A9lois?= <youn@melois.dev>
Date: Wed, 26 Feb 2025 21:14:06 +0100
Subject: [PATCH 04/11] fix: C++ namespaces mangling

---
 bindgen/codegen/mod.rs | 42 ++++++++++++++++++++++--------------------
 1 file changed, 22 insertions(+), 20 deletions(-)

diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs
index 09c870495b..10715aa8d4 100644
--- a/bindgen/codegen/mod.rs
+++ b/bindgen/codegen/mod.rs
@@ -786,6 +786,7 @@ impl CodeGenerator for Var {
                     &canonical_name,
                     link_name,
                     None,
+                    ctx.options().enable_cxx_namespaces,
                 ) {
                     canonical_name.as_str()
                 } else {
@@ -4657,6 +4658,7 @@ impl CodeGenerator for Function {
                 &canonical_name,
                 mangled_name,
                 Some(abi),
+                ctx.options().enable_cxx_namespaces,
             ))
             .then(|| mangled_name)
         });
@@ -5851,6 +5853,7 @@ pub(crate) mod utils {
         canonical_name: &str,
         mangled_name: &str,
         call_conv: Option<ClangAbi>,
+        enable_cxx_namespaces: bool,
     ) -> bool {
         // If the mangled name and the canonical name are the same then no
         // mangling can have happened between the two versions.
@@ -5859,29 +5862,28 @@ pub(crate) mod utils {
         }
 
         // Check if the mangled name is a valid C++ symbol
-        if let Ok(demangled) = cpp_demangle::Symbol::new(mangled_name) {
-            let demangled_name = demangled.to_string().replace("::", "_");
-
-            // Check that the demangled name is longer than the canonical name
-            if demangled_name.len() <= canonical_name.len() {
-                return false;
-            }
-
-            // Check that the demangled name starts with the canonical name
-            if !demangled_name.starts_with(canonical_name) {
-                return false;
-            }
+        if let Ok(demangled) = ::cpp_demangle::Symbol::new(mangled_name) {
+            let demangled_name = match demangled.demangle(
+                &::cpp_demangle::DemangleOptions::default()
+                    .no_params()
+                    .no_return_type(),
+            ) {
+                Ok(name) => name,
+                Err(_) => demangled.to_string(),
+            };
 
-            // Check that the suffix is a signature
-            if let Some(suffix) = demangled_name.get(canonical_name.len()..) {
-                if !suffix.starts_with('(') || !suffix.ends_with(')') {
-                    return false;
-                }
+            // Either remove the namespace or replace "::" with "_" if cxx namespaces are enabled
+            let demangled_name = if enable_cxx_namespaces {
+                demangled_name
+                    .rsplit_once("::")
+                    .map(|(_, name)| name.to_string())
+                    .unwrap_or(demangled_name)
             } else {
-                return false;
-            }
+                demangled_name.replace("::", "_")
+            };
 
-            return true;
+            // Now that we processed the demangled name, we can compare it with the canonical name
+            return canonical_name == demangled_name;
         }
 
         // Working with &[u8] makes indexing simpler than with &str

From 720293e7bdf60c75dd5c9980e7b1555b6d1f9915 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Youn=20M=C3=A9lois?= <youn@melois.dev>
Date: Wed, 26 Feb 2025 22:22:08 +0100
Subject: [PATCH 05/11] test: fix expected generated static fn in namespace

---
 .../tests/expectations/tests/generated/wrap_static_fns.cpp      | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/bindgen-tests/tests/expectations/tests/generated/wrap_static_fns.cpp b/bindgen-tests/tests/expectations/tests/generated/wrap_static_fns.cpp
index 357acc9187..f3b35c97da 100644
--- a/bindgen-tests/tests/expectations/tests/generated/wrap_static_fns.cpp
+++ b/bindgen-tests/tests/expectations/tests/generated/wrap_static_fns.cpp
@@ -30,4 +30,4 @@ void wrap_as_variadic_fn2__extern(int i, ...) {
     wrap_as_variadic_fn2(i, ap);
     va_end(ap);
 }
-int foo__extern(void) { return foo(); }
+int qux_foo__extern(void) { return qux::foo(); }

From d0b13447a26a7a6955e6952515ca5fd41c32ec01 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Youn=20M=C3=A9lois?= <youn@melois.dev>
Date: Thu, 27 Feb 2025 13:00:29 +0100
Subject: [PATCH 06/11] fix: wrap static function serialization

---
 bindgen/codegen/serialize.rs | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)

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) = {

From de9de851ef6acc1c1f110de50313f726fd0fa895 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Youn=20M=C3=A9lois?= <youn@melois.dev>
Date: Thu, 27 Feb 2025 15:34:53 +0100
Subject: [PATCH 07/11] Revert "fix: C++ namespaces mangling"

This reverts commit 2deeabe2be3c3e742fbe1d69e0c268af52daad93.
---
 bindgen/codegen/mod.rs | 42 ++++++++++++++++++++----------------------
 1 file changed, 20 insertions(+), 22 deletions(-)

diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs
index 10715aa8d4..09c870495b 100644
--- a/bindgen/codegen/mod.rs
+++ b/bindgen/codegen/mod.rs
@@ -786,7 +786,6 @@ impl CodeGenerator for Var {
                     &canonical_name,
                     link_name,
                     None,
-                    ctx.options().enable_cxx_namespaces,
                 ) {
                     canonical_name.as_str()
                 } else {
@@ -4658,7 +4657,6 @@ impl CodeGenerator for Function {
                 &canonical_name,
                 mangled_name,
                 Some(abi),
-                ctx.options().enable_cxx_namespaces,
             ))
             .then(|| mangled_name)
         });
@@ -5853,7 +5851,6 @@ pub(crate) mod utils {
         canonical_name: &str,
         mangled_name: &str,
         call_conv: Option<ClangAbi>,
-        enable_cxx_namespaces: bool,
     ) -> bool {
         // If the mangled name and the canonical name are the same then no
         // mangling can have happened between the two versions.
@@ -5862,28 +5859,29 @@ pub(crate) mod utils {
         }
 
         // Check if the mangled name is a valid C++ symbol
-        if let Ok(demangled) = ::cpp_demangle::Symbol::new(mangled_name) {
-            let demangled_name = match demangled.demangle(
-                &::cpp_demangle::DemangleOptions::default()
-                    .no_params()
-                    .no_return_type(),
-            ) {
-                Ok(name) => name,
-                Err(_) => demangled.to_string(),
-            };
+        if let Ok(demangled) = cpp_demangle::Symbol::new(mangled_name) {
+            let demangled_name = demangled.to_string().replace("::", "_");
 
-            // Either remove the namespace or replace "::" with "_" if cxx namespaces are enabled
-            let demangled_name = if enable_cxx_namespaces {
-                demangled_name
-                    .rsplit_once("::")
-                    .map(|(_, name)| name.to_string())
-                    .unwrap_or(demangled_name)
+            // Check that the demangled name is longer than the canonical name
+            if demangled_name.len() <= canonical_name.len() {
+                return false;
+            }
+
+            // Check that the demangled name starts with the canonical name
+            if !demangled_name.starts_with(canonical_name) {
+                return false;
+            }
+
+            // Check that the suffix is a signature
+            if let Some(suffix) = demangled_name.get(canonical_name.len()..) {
+                if !suffix.starts_with('(') || !suffix.ends_with(')') {
+                    return false;
+                }
             } else {
-                demangled_name.replace("::", "_")
-            };
+                return false;
+            }
 
-            // Now that we processed the demangled name, we can compare it with the canonical name
-            return canonical_name == demangled_name;
+            return true;
         }
 
         // Working with &[u8] makes indexing simpler than with &str

From 32e478100cbfda5d06162598fb3e942a1ecca74a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Youn=20M=C3=A9lois?= <youn@melois.dev>
Date: Thu, 27 Feb 2025 15:34:58 +0100
Subject: [PATCH 08/11] Revert "fix: name mangling check for C++"

This reverts commit 25b321a96891dfd0cda22dc37e4a26b8b69fba8b.
---
 Cargo.lock             | 10 ----------
 Cargo.toml             |  1 -
 bindgen/Cargo.toml     |  1 -
 bindgen/codegen/mod.rs | 26 --------------------------
 4 files changed, 38 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index f6c31cd4b1..4e88c224f5 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -43,7 +43,6 @@ dependencies = [
  "clang-sys",
  "clap",
  "clap_complete",
- "cpp_demangle",
  "itertools",
  "log",
  "prettyplease",
@@ -188,15 +187,6 @@ dependencies = [
  "os_str_bytes",
 ]
 
-[[package]]
-name = "cpp_demangle"
-version = "0.4.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "96e58d342ad113c2b878f16d5d034c03be492ae460cdbc02b7f0f2284d310c7d"
-dependencies = [
- "cfg-if",
-]
-
 [[package]]
 name = "either"
 version = "1.13.0"
diff --git a/Cargo.toml b/Cargo.toml
index 05aef7656c..b0a5bbb082 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -30,7 +30,6 @@ cexpr = "0.6"
 clang-sys = "1"
 clap = "4"
 clap_complete = "4"
-cpp_demangle = "0.4.4"
 env_logger = "0.10.0"
 itertools = { version = ">=0.10,<0.14", default-features = false }
 libloading = "0.8"
diff --git a/bindgen/Cargo.toml b/bindgen/Cargo.toml
index 8ca55b5959..c01f8f0c44 100644
--- a/bindgen/Cargo.toml
+++ b/bindgen/Cargo.toml
@@ -32,7 +32,6 @@ cexpr.workspace = true
 clang-sys = { workspace = true, features = ["clang_11_0"] }
 clap = { workspace = true, features = ["derive"], optional = true }
 clap_complete = { workspace = true, optional = true }
-cpp_demangle.workspace = true
 itertools = { workspace = true }
 log = { workspace = true, optional = true }
 prettyplease = { workspace = true, optional = true, features = ["verbatim"] }
diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs
index 09c870495b..ddd40d972e 100644
--- a/bindgen/codegen/mod.rs
+++ b/bindgen/codegen/mod.rs
@@ -5858,32 +5858,6 @@ pub(crate) mod utils {
             return true;
         }
 
-        // Check if the mangled name is a valid C++ symbol
-        if let Ok(demangled) = cpp_demangle::Symbol::new(mangled_name) {
-            let demangled_name = demangled.to_string().replace("::", "_");
-
-            // Check that the demangled name is longer than the canonical name
-            if demangled_name.len() <= canonical_name.len() {
-                return false;
-            }
-
-            // Check that the demangled name starts with the canonical name
-            if !demangled_name.starts_with(canonical_name) {
-                return false;
-            }
-
-            // Check that the suffix is a signature
-            if let Some(suffix) = demangled_name.get(canonical_name.len()..) {
-                if !suffix.starts_with('(') || !suffix.ends_with(')') {
-                    return false;
-                }
-            } else {
-                return false;
-            }
-
-            return true;
-        }
-
         // Working with &[u8] makes indexing simpler than with &str
         let canonical_name = canonical_name.as_bytes();
         let mangled_name = mangled_name.as_bytes();

From 694441dd64c3dfb6106c33949605d376e5ccf3c5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Youn=20M=C3=A9lois?= <youn@melois.dev>
Date: Thu, 27 Feb 2025 15:38:34 +0100
Subject: [PATCH 09/11] fix: generate wrapped static functions for C++

---
 bindgen/codegen/mod.rs | 21 ++++++++++++---------
 1 file changed, 12 insertions(+), 9 deletions(-)

diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs
index ddd40d972e..00a0e84aa4 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();
 

From df94b915463e9e48212d22e57b77ff503917fe09 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Youn=20M=C3=A9lois?= <youn@melois.dev>
Date: Thu, 27 Feb 2025 15:40:00 +0100
Subject: [PATCH 10/11] Revert "perf: small optimization !iter.all =>
 iter.any!"

This reverts commit 4ff0c5288076ea626d533e6ce27ab61bc5db483d.
---
 bindgen/codegen/mod.rs | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs
index 00a0e84aa4..b64a4b1a36 100644
--- a/bindgen/codegen/mod.rs
+++ b/bindgen/codegen/mod.rs
@@ -5909,8 +5909,7 @@ pub(crate) mod utils {
 
             // Check that the suffix starts with '@' and is all ASCII decimals
             // after that.
-            if suffix[0] != b'@' ||
-                suffix[1..].iter().any(|&c| !c.is_ascii_digit())
+            if suffix[0] != b'@' || !suffix[1..].iter().all(u8::is_ascii_digit)
             {
                 return false;
             }

From 9bb551f5ba4ae42e914d367d15a48bde4eb2bd05 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Youn=20M=C3=A9lois?= <youn@melois.dev>
Date: Fri, 28 Feb 2025 19:27:02 +0100
Subject: [PATCH 11/11] test: rename files with cxx

---
 ...static_fns.cpp => wrap_static_fns_cxx.cpp} |  2 +-
 .../expectations/tests/wrap-static-fns-cxx.rs | 95 +++++++++++++++++++
 ...static-fns.hpp => wrap-static-fns-cxx.hpp} |  0
 bindgen-tests/tests/tests.rs                  | 12 +--
 4 files changed, 102 insertions(+), 7 deletions(-)
 rename bindgen-tests/tests/expectations/tests/generated/{wrap_static_fns.cpp => wrap_static_fns_cxx.cpp} (96%)
 create mode 100644 bindgen-tests/tests/expectations/tests/wrap-static-fns-cxx.rs
 rename bindgen-tests/tests/headers/{wrap-static-fns.hpp => wrap-static-fns-cxx.hpp} (100%)

diff --git a/bindgen-tests/tests/expectations/tests/generated/wrap_static_fns.cpp b/bindgen-tests/tests/expectations/tests/generated/wrap_static_fns_cxx.cpp
similarity index 96%
rename from bindgen-tests/tests/expectations/tests/generated/wrap_static_fns.cpp
rename to bindgen-tests/tests/expectations/tests/generated/wrap_static_fns_cxx.cpp
index f3b35c97da..0526b9791c 100644
--- a/bindgen-tests/tests/expectations/tests/generated/wrap_static_fns.cpp
+++ b/bindgen-tests/tests/expectations/tests/generated/wrap_static_fns_cxx.cpp
@@ -1,4 +1,4 @@
-#include "tests/headers/wrap-static-fns.hpp"
+#include "tests/headers/wrap-static-fns-cxx.hpp"
 
 // Static wrappers
 
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<unsafe extern "C" fn() -> ::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.hpp b/bindgen-tests/tests/headers/wrap-static-fns-cxx.hpp
similarity index 100%
rename from bindgen-tests/tests/headers/wrap-static-fns.hpp
rename to bindgen-tests/tests/headers/wrap-static-fns-cxx.hpp
diff --git a/bindgen-tests/tests/tests.rs b/bindgen-tests/tests/tests.rs
index ad888a77bb..69d5e9af38 100644
--- a/bindgen-tests/tests/tests.rs
+++ b/bindgen-tests/tests/tests.rs
@@ -759,20 +759,20 @@ fn test_wrap_static_fns() {
 }
 
 #[test]
-fn test_wrap_static_fns_cpp() {
+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");
+        .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");
+        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.hpp")
+        .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))
@@ -780,10 +780,10 @@ fn test_wrap_static_fns_cpp() {
         .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.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.cpp");
+        .expect("Could not read actual wrap_static_fns_cxx.cpp");
 
     if expected_cpp != actual_cpp {
         error_diff_mismatch(