Repository: dtolnay/cxx (rust)

Issue:
Support &self shorthand if there is only one extern type in the block
As currently implemented, Rust associated methods and C++ member functions are currently specified with `self: &T` syntax.

```rust
extern "C" {
    type C;
    fn f(self: &C);
}
```

Inside of extern blocks that contain exactly one extern type, we should allow the usual `&self` and `&mut self` shorthand as well.

```rust
extern "C" {
    type C;
    fn f(&self);
}
```

Pull request:
Allow &self without a type when the block only has one type
Fixes #124.

Patch:
diff --git a/syntax/parse.rs b/syntax/parse.rs
index d8bc0e53..4e785889 100644
--- a/syntax/parse.rs
+++ b/syntax/parse.rs
@@ -91,16 +91,28 @@ fn parse_foreign_mod(foreign_mod: ItemForeignMod) -> Result<Vec<Api>> {
         Lang::Rust => Api::RustFunction,
     };
 
-    let mut items = Vec::new();
+    let mut types = Vec::new();
     for foreign in &foreign_mod.items {
         match foreign {
             ForeignItem::Type(foreign) => {
                 check_reserved_name(&foreign.ident)?;
                 let ety = parse_extern_type(foreign)?;
-                items.push(api_type(ety));
+                types.push(ety);
             }
+            _ => {}
+        }
+    }
+    let single_type = if types.len() == 1 {
+        Some(&types[0])
+    } else {
+        None
+    };
+    let mut items = Vec::new();
+    for foreign in &foreign_mod.items {
+        match foreign {
+            ForeignItem::Type(_) => {}
             ForeignItem::Fn(foreign) => {
-                let efn = parse_extern_fn(foreign, lang)?;
+                let efn = parse_extern_fn(foreign, lang, &single_type)?;
                 items.push(api_function(efn));
             }
             ForeignItem::Macro(foreign) if foreign.mac.path.is_ident("include") => {
@@ -110,6 +122,7 @@ fn parse_foreign_mod(foreign_mod: ItemForeignMod) -> Result<Vec<Api>> {
             _ => return Err(Error::new_spanned(foreign, "unsupported foreign item")),
         }
     }
+    items.extend(types.into_iter().map(|ety| api_type(ety)));
     Ok(items)
 }
 
@@ -141,7 +154,11 @@ fn parse_extern_type(foreign_type: &ForeignItemType) -> Result<ExternType> {
     })
 }
 
-fn parse_extern_fn(foreign_fn: &ForeignItemFn, lang: Lang) -> Result<ExternFn> {
+fn parse_extern_fn(
+    foreign_fn: &ForeignItemFn,
+    lang: Lang,
+    single_type: &Option<&ExternType>,
+) -> Result<ExternFn> {
     let generics = &foreign_fn.sig.generics;
     if !generics.params.is_empty() || generics.where_clause.is_some() {
         return Err(Error::new_spanned(
@@ -161,8 +178,19 @@ fn parse_extern_fn(foreign_fn: &ForeignItemFn, lang: Lang) -> Result<ExternFn> {
     for arg in foreign_fn.sig.inputs.pairs() {
         let (arg, comma) = arg.into_tuple();
         match arg {
-            FnArg::Receiver(receiver) => {
-                return Err(Error::new_spanned(receiver, "unsupported signature"))
+            FnArg::Receiver(rcvr) => {
+                if let Some(ety) = single_type {
+                    if let Some((and, _)) = rcvr.reference {
+                        receiver = Some(Receiver {
+                            ampersand: and,
+                            mutability: rcvr.mutability,
+                            var: Token![self](ety.ident.span()),
+                            ty: ety.ident.clone(),
+                        });
+                        continue;
+                    }
+                }
+                return Err(Error::new_spanned(rcvr, "unsupported signature"));
             }
             FnArg::Typed(arg) => {
                 let ident = match arg.pat.as_ref() {


Test patch:
diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs
index 4da57403..efdd1fa6 100644
--- a/tests/ffi/lib.rs
+++ b/tests/ffi/lib.rs
@@ -52,6 +52,8 @@ pub mod ffi {
 
         fn get(self: &C) -> usize;
         fn set(self: &mut C, n: usize) -> usize;
+        fn get2(&self) -> usize;
+        fn set2(&mut self, n: usize) -> usize;
     }
 
     extern "Rust" {
diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc
index 8893ee84..619485a3 100644
--- a/tests/ffi/tests.cc
+++ b/tests/ffi/tests.cc
@@ -15,11 +15,18 @@ C::C(size_t n) : n(n) {}
 
 size_t C::get() const { return this->n; }
 
+size_t C::get2() const { return this->n; }
+
 size_t C::set(size_t n) {
   this->n = n;
   return this->n;
 }
 
+size_t C::set2(size_t n) {
+  this->n = n;
+  return this->n;
+}
+
 size_t c_return_primitive() { return 2020; }
 
 Shared c_return_shared() { return Shared{2020}; }
diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h
index 43ac229f..ed6d5419 100644
--- a/tests/ffi/tests.h
+++ b/tests/ffi/tests.h
@@ -13,6 +13,8 @@ public:
   C(size_t n);
   size_t get() const;
   size_t set(size_t n);
+  size_t get2() const;
+  size_t set2(size_t n);
 
 private:
   size_t n;
diff --git a/tests/test.rs b/tests/test.rs
index eb14d363..1cd85dfd 100644
--- a/tests/test.rs
+++ b/tests/test.rs
@@ -102,8 +102,8 @@ fn test_c_method_calls() {
     assert_eq!(2020, old_value);
     assert_eq!(2021, unique_ptr.set(2021));
     assert_eq!(2021, unique_ptr.get());
-    assert_eq!(old_value, unique_ptr.set(old_value));
-    assert_eq!(old_value, unique_ptr.get())
+    assert_eq!(old_value, unique_ptr.set2(old_value));
+    assert_eq!(old_value, unique_ptr.get2())
 }
 
 #[no_mangle]