Repository: auto-impl-rs/auto_impl (rust)

Issue:
Support mutable function arguments
`auto_impl` should support trait methods of the form

```
fn foo(&self, mut bar: Bar) {
    // default implementation that mutates bar
}
```


Pull request:
support ref mut pattern bindings
Closes #77 

The `ref` and `mut` pattern bindings are trivial for us to support, since we don't need to try trace subpatterns around.

Patch:
diff --git a/src/gen.rs b/src/gen.rs
index 8620f62..7c5d483 100644
--- a/src/gen.rs
+++ b/src/gen.rs
@@ -1,11 +1,8 @@
 use proc_macro2::{Span as Span2, TokenStream as TokenStream2};
 use proc_macro_error::{abort, emit_error};
 use quote::{ToTokens, TokenStreamExt};
-use syn::{
-    spanned::Spanned, Attribute, FnArg, Ident, ItemTrait, Lifetime, Pat, PatIdent, ReturnType,
-    Signature, TraitBound, TraitBoundModifier, TraitItem, TraitItemConst, TraitItemMethod,
-    TraitItemType, Type, TypeParamBound, WherePredicate,
-};
+use syn::{spanned::Spanned, Attribute, FnArg, Ident, ItemTrait, Lifetime, Pat, PatIdent, ReturnType, Signature, TraitBound, TraitBoundModifier, TraitItem, TraitItemConst, TraitItemMethod, TraitItemType, Type, TypeParamBound, WherePredicate, PatType, Token};
+use syn::punctuated::Punctuated;
 
 use crate::{
     analyze::find_suitable_param_names,
@@ -341,24 +338,6 @@ fn gen_fn_type_for_trait(proxy_type: &ProxyType, trait_def: &ItemTrait) -> Token
         );
     }
 
-    // Function traits cannot support generics in their arguments
-    // These would require HRTB for types instead of just lifetimes
-    for type_param in sig.generics.type_params() {
-        emit_error!(
-            type_param.span(),
-            "the trait '{}' cannot be implemented for Fn-traits: generic arguments are not allowed",
-            trait_def.ident,
-        );
-    }
-
-    for const_param in sig.generics.const_params() {
-        emit_error!(
-            const_param.span(),
-            "the trait '{}' cannot be implemented for Fn-traits: constant arguments are not allowed",
-            trait_def.ident,
-        );
-    }
-
     // =======================================================================
     // Check if the trait can be implemented for the given proxy type
     let self_type = SelfType::from_sig(sig);
@@ -595,8 +574,8 @@ fn gen_method_item(
     // If this method has a `#[auto_impl(keep_default_for(...))]` attribute for
     // the given proxy type, we don't generate anything for this impl block.
     if should_keep_default_for(item, proxy_type) {
-        if item.default.is_some() {
-            return Ok(TokenStream2::new());
+        return if item.default.is_some() {
+            Ok(TokenStream2::new())
         } else {
             emit_error!(
                 item.sig.span(),
@@ -604,20 +583,37 @@ fn gen_method_item(
                     method (no body is provided)",
                 item.sig.ident,
             );
-            return Err(());
+            Err(())
         }
     }
 
     // Determine the kind of the method, determined by the self type.
     let sig = &item.sig;
-    let self_arg = SelfType::from_sig(sig);
+    let self_arg = SelfType::from_sig(&sig);
     let attrs = filter_attrs(&item.attrs);
 
     // Check self type and proxy type combination
     check_receiver_compatible(proxy_type, self_arg, &trait_def.ident, sig.span().into());
 
     // Generate the list of argument used to call the method.
-    let args = get_arg_list(sig.inputs.iter());
+    let (inputs, args) = get_arg_list(sig.inputs.iter());
+
+    // Construct a signature we'll use to generate the proxy method impl
+    // This is _almost_ the same as the original, except we use the inputs constructed
+    // alongside the args. These may be slightly different than the original trait.
+    let sig = Signature {
+        constness: item.sig.constness,
+        asyncness: item.sig.asyncness,
+        unsafety: item.sig.unsafety,
+        abi: item.sig.abi.clone(),
+        fn_token: item.sig.fn_token,
+        ident: item.sig.ident.clone(),
+        generics: item.sig.generics.clone(),
+        paren_token: item.sig.paren_token,
+        inputs,
+        variadic: item.sig.variadic.clone(),
+        output: item.sig.output.clone(),
+    };
 
     // Build the turbofish type parameters. We need to pass type parameters
     // explicitly as they cannot be inferred in all cases (e.g. something like
@@ -787,25 +783,40 @@ fn check_receiver_compatible(
 /// Generates a list of comma-separated arguments used to call the function.
 /// Currently, only simple names are valid and more complex pattern will lead
 /// to an error being emitted. `self` parameters are ignored.
-fn get_arg_list<'a>(inputs: impl Iterator<Item = &'a FnArg>) -> TokenStream2 {
+fn get_arg_list<'a>(original_inputs: impl Iterator<Item = &'a FnArg>) -> (Punctuated<FnArg, Token![,]>, TokenStream2) {
     let mut args = TokenStream2::new();
+    let mut inputs = Punctuated::new();
 
-    for arg in inputs {
+    for arg in original_inputs {
         match arg {
             FnArg::Typed(arg) => {
                 // Make sure the argument pattern is a simple name. In
                 // principle, we could probably support patterns, but it's
                 // not that important now.
                 if let Pat::Ident(PatIdent {
-                    by_ref: None,
-                    mutability: None,
+                    by_ref: _by_ref,
+                    mutability: _mutability,
                     ident,
                     subpat: None,
-                    ..
+                    attrs,
                 }) = &*arg.pat
                 {
                     // Add name plus trailing comma to tokens
                     args.append_all(quote! { #ident , });
+
+                    // Add an input argument that omits the `mut` and `ref` pattern bindings
+                    inputs.push(FnArg::Typed(PatType {
+                        attrs: arg.attrs.clone(),
+                        pat: Box::new(Pat::Ident(PatIdent {
+                            attrs: attrs.clone(),
+                            by_ref: None,
+                            mutability: None,
+                            ident: ident.clone(),
+                            subpat: None,
+                        })),
+                        colon_token: arg.colon_token,
+                        ty: arg.ty.clone(),
+                    }))
                 } else {
                     emit_error!(
                         arg.pat.span(), "argument patterns are not supported by #[auto-impl]";
@@ -817,11 +828,13 @@ fn get_arg_list<'a>(inputs: impl Iterator<Item = &'a FnArg>) -> TokenStream2 {
 
             // There is only one such argument. We handle it elsewhere and
             // can ignore it here.
-            FnArg::Receiver(_) => {}
+            FnArg::Receiver(arg) => {
+                inputs.push(FnArg::Receiver(arg.clone()));
+            }
         }
     }
 
-    args
+    (inputs, args)
 }
 
 /// Checks if the given method has the attribute `#[auto_impl(keep_default_for(...))]`


Test patch:
diff --git a/tests/compile-fail/fn_const_generics.rs b/tests/compile-fail/fn_const_generics.rs
deleted file mode 100644
index 2d1920e..0000000
--- a/tests/compile-fail/fn_const_generics.rs
+++ /dev/null
@@ -1,10 +0,0 @@
-use auto_impl::auto_impl;
-
-
-#[auto_impl(Fn)]
-trait Greeter {
-    fn greet<const N: usize>(&self, id: usize);
-}
-
-
-fn main() {}
diff --git a/tests/compile-fail/fn_const_generics.stderr b/tests/compile-fail/fn_const_generics.stderr
deleted file mode 100644
index ed01199..0000000
--- a/tests/compile-fail/fn_const_generics.stderr
+++ /dev/null
@@ -1,5 +0,0 @@
-error: the trait 'Greeter' cannot be implemented for Fn-traits: constant arguments are not allowed
- --> tests/compile-fail/fn_const_generics.rs:6:14
-  |
-6 |     fn greet<const N: usize>(&self, id: usize);
-  |              ^^^^^
diff --git a/tests/compile-fail/fn_generics.rs b/tests/compile-fail/fn_generics.rs
deleted file mode 100644
index ae49ffc..0000000
--- a/tests/compile-fail/fn_generics.rs
+++ /dev/null
@@ -1,11 +0,0 @@
-use std::fmt::Display;
-use auto_impl::auto_impl;
-
-
-#[auto_impl(Fn)]
-trait Greeter {
-    fn greet<T: Display>(&self, name: T);
-}
-
-
-fn main() {}
diff --git a/tests/compile-fail/fn_generics.stderr b/tests/compile-fail/fn_generics.stderr
deleted file mode 100644
index ff4e10f..0000000
--- a/tests/compile-fail/fn_generics.stderr
+++ /dev/null
@@ -1,5 +0,0 @@
-error: the trait 'Greeter' cannot be implemented for Fn-traits: generic arguments are not allowed
- --> tests/compile-fail/fn_generics.rs:7:14
-  |
-7 |     fn greet<T: Display>(&self, name: T);
-  |              ^
diff --git a/tests/compile-pass/self_by_value_mut.rs b/tests/compile-pass/self_by_value_mut.rs
new file mode 100644
index 0000000..a633a0b
--- /dev/null
+++ b/tests/compile-pass/self_by_value_mut.rs
@@ -0,0 +1,17 @@
+use auto_impl::auto_impl;
+
+
+struct Data {
+    id: usize,
+}
+
+#[auto_impl(&, Box)]
+trait Foo {
+    #[auto_impl(keep_default_for(&))]
+    fn foo(&self, ref mut data: Data) {
+        data.id += 1;
+    }
+}
+
+
+fn main() {}