Repository: graphql-rust/juniper (language: rust)

Issue:
Error when making input object mutable
I'm not sure if this is a problem with juniper, actix-web or my own knowledge of rust but I'm having trouble making an input object to be mutable. 

**To Reproduce**
Running the [example](https://github.com/actix/examples/tree/aa5750cafc9d6bc96e098c338b70fa618149ffd5/juniper) provided by actix updated to use juniper 0.13.0 and slightly changing the `createHuman`using both the old and new syntax I can't get `new_human` to be mutable:

```rust
/*
graphql_object!(MutationRoot: () |&self| {
    field createHuman(&executor, mut new_human: NewHuman) -> FieldResult<Human> {
        new_human.name = String::from("new name");
        Ok(Human{
            id: "1234".to_owned(),
            name: new_human.name,
            appears_in: new_human.appears_in,
            home_planet: new_human.home_planet,
        })
    }
});
*/

#[juniper::object]
impl MutationRoot {
    fn createHuman(mut new_human: NewHuman) -> FieldResult<Human> {
        new_human.name = String::from("new name");
        Ok(Human{
            id: "1234".to_owned(),
            name: new_human.name,
            appears_in: new_human.appears_in,
            home_planet: new_human.home_planet,
        })
    }
}
```

Running the example with the change above I get the following error:

```
error[E0594]: cannot assign to `new_human.name`, as `new_human` is not declared as mutable
  --> juniper/src/schema.rs:60:9
   |
59 |     fn createHuman(mut new_human: NewHuman) -> FieldResult<Human> {
   |                        --------- help: consider changing this to be mutable: `mut new_human`
60 |         new_human.name = String::from("new name");
   |         ^^^^^^^^^^^^^^ cannot assign

error: aborting due to previous error

error: Could not compile `juniper-example`.
```

---

Tested with Rust 1.36.0 on macOS 10.14.5


Discussion / hints:
This is a problem with the proc macro generation code.
The `mut` declaration is discarded in the final output.
Should be an easy fix.

Fix patch:
diff --git a/juniper/CHANGELOG.md b/juniper/CHANGELOG.md
index 894f5ea3a..70a3752ce 100644
--- a/juniper/CHANGELOG.md
+++ b/juniper/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 - No changes yet
 
+- Allow `mut` arguments for resolver functions in `#[object]` macros [#402](https://github.com/graphql-rust/juniper/pull/402)
+
 # [[0.13.0] 2019-07-19](https://github.com/graphql-rust/juniper/releases/tag/juniper-0.13.0)
 
 ### newtype ScalarValue derive
diff --git a/juniper_codegen/src/impl_object.rs b/juniper_codegen/src/impl_object.rs
index 3fe7af3ff..01fb7450a 100644
--- a/juniper_codegen/src/impl_object.rs
+++ b/juniper_codegen/src/impl_object.rs
@@ -129,8 +129,10 @@ pub fn build_object(args: TokenStream, body: TokenStream, is_internal: bool) ->
                             );
                         }
                         syn::FnArg::Captured(ref captured) => {
-                            let arg_ident = match &captured.pat {
-                                syn::Pat::Ident(ref pat_ident) => &pat_ident.ident,
+                            let (arg_ident, is_mut) = match &captured.pat {
+                                syn::Pat::Ident(ref pat_ident) => {
+                                    (&pat_ident.ident, pat_ident.mutability.is_some())
+                                }
                                 _ => {
                                     panic!("Invalid token for function argument");
                                 }
@@ -147,7 +149,7 @@ pub fn build_object(args: TokenStream, body: TokenStream, is_internal: bool) ->
                             else if util::type_is_identifier(&captured.ty, "Executor") {
                                 panic!("Invalid executor argument: to access the Executor, you need to specify the type as a reference.\nDid you mean &Executor?");
                             }
-                            // Check for executor arg.
+                            // Check for context arg.
                             else if context_type
                                 .clone()
                                 .map(|ctx| util::type_is_ref_of(&captured.ty, ctx))
@@ -167,12 +169,15 @@ pub fn build_object(args: TokenStream, body: TokenStream, is_internal: bool) ->
                                     quote!(captured.ty),
                                 );
                             } else {
+                                // Regular argument.
+
                                 let ty = &captured.ty;
                                 // TODO: respect graphql attribute overwrite.
                                 let final_name = util::to_camel_case(&arg_name);
                                 let expect_text = format!("Internal error: missing argument {} - validation must have failed", &final_name);
+                                let mut_modifier = if is_mut { quote!(mut) } else { quote!() };
                                 resolve_parts.push(quote!(
-                                    let #arg_ident = args
+                                    let #mut_modifier #arg_ident = args
                                         .get::<#ty>(#final_name)
                                         .expect(#expect_text);
                                 ));


Test patch:
diff --git a/juniper/src/macros/tests/impl_object.rs b/juniper/src/macros/tests/impl_object.rs
index 81427cd70..bbcfdb18b 100644
--- a/juniper/src/macros/tests/impl_object.rs
+++ b/juniper/src/macros/tests/impl_object.rs
@@ -101,6 +101,13 @@ impl<'a> Query {
     fn with_lifetime_child(&self) -> WithLifetime<'a> {
         WithLifetime { value: "blub" }
     }
+
+    fn with_mut_arg(mut arg: bool) -> bool {
+        if arg {
+            arg = !arg;
+        }
+        arg
+    }
 }
 
 #[derive(Default)]
@@ -216,6 +223,19 @@ fn object_introspect() {
                     "description": None,
                     "args": [],
                 },
+                {
+                    "name": "withMutArg",
+                    "description": None,
+                    "args": [
+                        {
+                            "name": "arg",
+                            "description": None,
+                            "type": {
+                                "name": None,
+                            },
+                        }
+                    ],
+                },
             ]
         })
     );
@@ -241,6 +261,7 @@ fn object_query() {
         withLifetimeChild {
             value
         }
+        withMutArg(arg: true)
     }
     "#;
     let schema = RootNode::new(Query { b: true }, EmptyMutation::<Context>::new());
@@ -264,6 +285,7 @@ fn object_query() {
             "argWithDescription": true,
             "withContextChild": { "ctx": true },
             "withLifetimeChild": { "value": "blub" },
+            "withMutArg": false,
         })
     );
 }