Repository: dtolnay/cxx (rust)

Issue:
Allow wildcard argument names in extern function signature
Currently we reject this for no particular reason. Seems like an oversight.

```rust
#[cxx::bridge]
mod ffi {
    extern "C" {
        fn repro(_: &str);
    }
}
```

```console
error[cxxbridge]: unsupported signature

    ┌── src/main.rs:4:18 ───
    │
  4 │         fn repro(_: &str);  // but `s: &str` works
    │                  ^^^^^^^ unsupported signature
```

Pull request:
Allow wildcard argument names.
This adds support for wildcard variable names by internally giving them unique names.

Fixes #154.

This seems simpler than trying to ensure we catch all uses of an ident named "_" and naming them appropriately.

Patch:
diff --git a/syntax/parse.rs b/syntax/parse.rs
index d14806d9..a425c566 100644
--- a/syntax/parse.rs
+++ b/syntax/parse.rs
@@ -6,9 +6,9 @@ use crate::syntax::{
 use quote::{format_ident, quote};
 use syn::punctuated::Punctuated;
 use syn::{
-    Abi, Error, Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemType, GenericArgument, Item,
-    ItemForeignMod, ItemStruct, Pat, PathArguments, Result, ReturnType, Token, Type as RustType,
-    TypeBareFn, TypePath, TypeReference, TypeSlice,
+    Abi, Error, Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemType, GenericArgument, Ident,
+    Item, ItemForeignMod, ItemStruct, Pat, PathArguments, Result, ReturnType, Token,
+    Type as RustType, TypeBareFn, TypePath, TypeReference, TypeSlice,
 };
 
 pub mod kw {
@@ -193,6 +193,9 @@ fn parse_extern_fn(foreign_fn: &ForeignItemFn, lang: Lang) -> Result<ExternFn> {
             FnArg::Typed(arg) => {
                 let ident = match arg.pat.as_ref() {
                     Pat::Ident(pat) => pat.ident.clone(),
+                    Pat::Wild(pat) => {
+                        Ident::new(&format!("_{}", args.len()), pat.underscore_token.span)
+                    }
                     _ => return Err(Error::new_spanned(arg, "unsupported signature")),
                 };
                 let ty = parse_type(&arg.ty)?;


Test patch:
diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs
index d2c860ed..69ef2b0c 100644
--- a/tests/ffi/lib.rs
+++ b/tests/ffi/lib.rs
@@ -34,6 +34,8 @@ pub mod ffi {
         fn c_return_ref_vector(c: &C) -> &CxxVector<u8>;
         fn c_return_rust_vec() -> Vec<u8>;
         fn c_return_ref_rust_vec(c: &C) -> &Vec<u8>;
+        fn c_return_identity(_: usize) -> usize;
+        fn c_return_sum(_: usize, _: usize) -> usize;
 
         fn c_take_primitive(n: usize);
         fn c_take_shared(shared: Shared);
@@ -86,6 +88,8 @@ pub mod ffi {
         fn r_return_unique_ptr_string() -> UniquePtr<CxxString>;
         fn r_return_rust_vec() -> Vec<u8>;
         fn r_return_ref_rust_vec(shared: &Shared) -> &Vec<u8>;
+        fn r_return_identity(_: usize) -> usize;
+        fn r_return_sum(_: usize, _: usize) -> usize;
 
         fn r_take_primitive(n: usize);
         fn r_take_shared(shared: Shared);
@@ -184,6 +188,14 @@ fn r_return_ref_rust_vec(shared: &ffi::Shared) -> &Vec<u8> {
     unimplemented!()
 }
 
+fn r_return_identity(n: usize) -> usize {
+    n
+}
+
+fn r_return_sum(n1: usize, n2: usize) -> usize {
+    n1 + n2
+}
+
 fn r_take_primitive(n: usize) {
     assert_eq!(n, 2020);
 }
diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc
index c0b5888f..11096ba2 100644
--- a/tests/ffi/tests.cc
+++ b/tests/ffi/tests.cc
@@ -102,6 +102,14 @@ const rust::Vec<uint8_t> &c_return_ref_rust_vec(const C &c) {
   throw std::runtime_error("unimplemented");
 }
 
+size_t c_return_identity(size_t n) {
+  return n;
+}
+
+size_t c_return_sum(size_t n1, size_t n2) {
+  return n1 + n2;
+}
+
 void c_take_primitive(size_t n) {
   if (n == 2020) {
     cxx_test_suite_set_correct();
@@ -265,6 +273,8 @@ extern "C" const char *cxx_run_test() noexcept {
   ASSERT(std::string(r_return_str(Shared{2020})) == "2020");
   ASSERT(std::string(r_return_rust_string()) == "2020");
   ASSERT(*r_return_unique_ptr_string() == "2020");
+  ASSERT(r_return_identity(2020) == 2020);
+  ASSERT(r_return_sum(2020, 1) == 2021);
 
   r_take_primitive(2020);
   r_take_shared(Shared{2020});
diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h
index 1d617ab8..4600d643 100644
--- a/tests/ffi/tests.h
+++ b/tests/ffi/tests.h
@@ -38,6 +38,8 @@ std::unique_ptr<std::vector<C>> c_return_unique_ptr_vector_opaque();
 const std::vector<uint8_t> &c_return_ref_vector(const C &c);
 rust::Vec<uint8_t> c_return_rust_vec();
 const rust::Vec<uint8_t> &c_return_ref_rust_vec(const C &c);
+size_t c_return_identity(size_t n);
+size_t c_return_sum(size_t n1, size_t n2);
 
 void c_take_primitive(size_t n);
 void c_take_shared(Shared shared);
diff --git a/tests/test.rs b/tests/test.rs
index 07c23bd3..4fd52d35 100644
--- a/tests/test.rs
+++ b/tests/test.rs
@@ -49,6 +49,8 @@ fn test_c_return() {
             .map(|o| o.z)
             .sum(),
     );
+    assert_eq!(2020, ffi::c_return_identity(2020));
+    assert_eq!(2021, ffi::c_return_sum(2020, 1));
 }
 
 #[test]