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

Issue:
Field argument descriptions don't get applied if using raw identifier in `arguments`
This is quite an obscure bug that I doubt many will run into, but I did hit while building juniper-from-schema.

**Describe the bug**
If you define an object like this:

```rust
#[graphql_object]
impl Query {
    #[graphql(
        arguments(
            a(description = "foo"),
            b(description = "bar"),
        )
    )]
    fn add(a: i32, b: i32) -> i32 {
        a + b
    }
}
```

Then the docs on the arguments show up just fine in GraphiQL.

However if you use raw identifiers

```rust
#[graphql_object]
impl Query {
    #[graphql(
        arguments(
            r#a(description = "foo"),
            r#b(description = "bar"),
        )
    )]
    fn add(r#a: i32, r#b: i32) -> i32 {
        a + b
    }
}
```

Then they don't show up

**To Reproduce**
Example warp app with GraphiQL that I used for debugging this

```rust
use juniper::*;
use warp::Filter;

type Schema = RootNode<'static, Query, EmptyMutation<()>, EmptySubscription<()>>;

fn schema() -> Schema {
    Schema::new(
        Query,
        EmptyMutation::<()>::new(),
        EmptySubscription::<()>::new(),
    )
}

struct Query;

#[graphql_object]
impl Query {
    #[graphql(
        arguments(
            r#a(description = "foo"),
            r#b(description = "bar"),
        )
    )]
    fn add(r#a: i32, r#b: i32) -> i32 {
        a + b
    }
}

#[tokio::main]
async fn main() {
    let state = warp::any().map(move || ());

    let graphql_filter = juniper_warp::make_graphql_filter(schema(), state.boxed());

    let graphiql_filter = warp::get()
        .and(warp::path("graphiql"))
        .and(juniper_warp::graphiql_filter("/graphql", None));

    warp::serve(graphiql_filter.or(warp::path("graphql").and(graphql_filter)))
        .run(([127, 0, 0, 1], 8080))
        .await
}
```


Discussion / hints:
@davidpdrsn but why do we need use raw arguments in proc macro tokens? The arguments are anyway wiil be named as `a` and `b` in schema. `r#`awing arguments is only required for usual Rust code, in proc macro we are free to use even keyword, so `r#`awing is not required.
In juniper-from-schema I use raw identifiers when generating code for all idents that come from the schema in case users put Rust keywords in the schema. I get that it isn't strictly necessary in this case but rather than checking if something is a keyword I would rather just use raw idents everywhere.

Fix patch:
diff --git a/juniper_codegen/src/util/mod.rs b/juniper_codegen/src/util/mod.rs
index 062b52b1f..b636d2802 100644
--- a/juniper_codegen/src/util/mod.rs
+++ b/juniper_codegen/src/util/mod.rs
@@ -11,11 +11,12 @@ use proc_macro_error::abort;
 use quote::quote;
 use span_container::SpanContainer;
 use syn::{
+    ext::IdentExt as _,
     parse::{Parse, ParseStream},
     parse_quote,
     punctuated::Punctuated,
     spanned::Spanned,
-    token, Attribute, Lit, Meta, MetaList, MetaNameValue, NestedMeta,
+    token, Attribute, Ident, Lit, Meta, MetaList, MetaNameValue, NestedMeta,
 };
 
 use crate::common::parse::ParseBufferExt as _;
@@ -450,7 +451,7 @@ pub struct FieldAttributeArgument {
 
 impl Parse for FieldAttributeArgument {
     fn parse(input: ParseStream) -> syn::Result<Self> {
-        let name = input.parse()?;
+        let name = input.parse::<Ident>()?.unraw();
 
         let mut arg = Self {
             name,


Test patch:
diff --git a/integration_tests/juniper_tests/src/codegen/impl_object.rs b/integration_tests/juniper_tests/src/codegen/impl_object.rs
index 6349f7aae..7a7245ff6 100644
--- a/integration_tests/juniper_tests/src/codegen/impl_object.rs
+++ b/integration_tests/juniper_tests/src/codegen/impl_object.rs
@@ -94,3 +94,46 @@ mod fallible {
         }
     }
 }
+
+mod raw_argument {
+    use juniper::{
+        graphql_object, graphql_value, EmptyMutation, EmptySubscription, RootNode, Variables,
+    };
+
+    struct Obj;
+
+    #[graphql_object]
+    impl Obj {
+        #[graphql(arguments(r#arg(description = "The only argument")))]
+        fn test(&self, arg: String) -> String {
+            arg
+        }
+    }
+
+    #[tokio::test]
+    async fn named_correctly() {
+        let doc = r#"{
+            __type(name: "Obj") {
+                fields {
+                    args {
+                        name
+                    }
+                }
+            }
+        }"#;
+
+        let schema = RootNode::new(
+            Obj,
+            EmptyMutation::<()>::new(),
+            EmptySubscription::<()>::new(),
+        );
+
+        assert_eq!(
+            juniper::execute(&doc, None, &schema, &Variables::new(), &()).await,
+            Ok((
+                graphql_value!({"__type": {"fields": [{"args": [{"name": "arg"}]}]}}),
+                vec![],
+            )),
+        );
+    }
+}
diff --git a/juniper/src/macros/tests/args.rs b/juniper/src/macros/tests/args.rs
index b98ebc324..9f5d038d8 100644
--- a/juniper/src/macros/tests/args.rs
+++ b/juniper/src/macros/tests/args.rs
@@ -58,6 +58,11 @@ impl Root {
         arg
     }
 
+    #[graphql(arguments(r#arg(description = "The arg")))]
+    fn single_arg_descr_raw_idents(arg: i32) -> i32 {
+        arg
+    }
+
     #[graphql(arguments(
         arg1(description = "The first arg",),
         arg2(description = "The second arg")
@@ -66,9 +71,17 @@ impl Root {
         arg1 + arg2
     }
 
+    #[graphql(arguments(
+        r#arg1(description = "The first arg",),
+        r#arg2(description = "The second arg")
+    ))]
+    fn multi_args_descr_raw_idents(arg1: i32, arg2: i32) -> i32 {
+        arg1 + arg2
+    }
+
     #[graphql(arguments(
         arg1(description = "The first arg",),
-        arg2(description = "The second arg")
+        arg2(description = "The second arg",)
     ))]
     fn multi_args_descr_trailing_comma(arg1: i32, arg2: i32) -> i32 {
         arg1 + arg2
@@ -106,6 +119,11 @@ impl Root {
         arg
     }
 
+    #[graphql(arguments(r#arg(default = 123, description = "The arg")))]
+    fn arg_with_default_descr_raw_ident(arg: i32) -> i32 {
+        arg
+    }
+
     #[graphql(arguments(
         arg1(default = 123, description = "The first arg"),
         arg2(default = 456, description = "The second arg")
@@ -114,6 +132,14 @@ impl Root {
         arg1 + arg2
     }
 
+    #[graphql(arguments(
+        r#arg1(default = 123, description = "The first arg"),
+        r#arg2(default = 456, description = "The second arg")
+    ))]
+    fn multi_args_with_default_descr_raw_ident(arg1: i32, arg2: i32) -> i32 {
+        arg1 + arg2
+    }
+
     #[graphql(arguments(
         arg1(default = 123, description = "The first arg",),
         arg2(default = 456, description = "The second arg",)
@@ -122,6 +148,14 @@ impl Root {
         arg1 + arg2
     }
 
+    #[graphql(arguments(
+        r#arg1(default = 123, description = "The first arg",),
+        r#arg2(default = 456, description = "The second arg",)
+    ))]
+    fn multi_args_with_default_trailing_comma_descr_raw_ident(arg1: i32, arg2: i32) -> i32 {
+        arg1 + arg2
+    }
+
     #[graphql(
         arguments(
             arg1(
@@ -460,6 +494,40 @@ async fn introspect_field_single_arg_descr() {
     .await;
 }
 
+#[tokio::test]
+async fn introspect_field_single_arg_descr_raw_idents() {
+    run_args_info_query("singleArgDescrRawIdents", |args| {
+        assert_eq!(args.len(), 1);
+
+        assert!(args.contains(&Value::object(
+            vec![
+                ("name", Value::scalar("arg")),
+                ("description", Value::scalar("The arg")),
+                ("defaultValue", Value::null()),
+                (
+                    "type",
+                    Value::object(
+                        vec![
+                            ("name", Value::null()),
+                            (
+                                "ofType",
+                                Value::object(
+                                    vec![("name", Value::scalar("Int"))].into_iter().collect(),
+                                ),
+                            ),
+                        ]
+                        .into_iter()
+                        .collect(),
+                    ),
+                ),
+            ]
+            .into_iter()
+            .collect(),
+        )));
+    })
+    .await;
+}
+
 #[tokio::test]
 async fn introspect_field_multi_args_descr() {
     run_args_info_query("multiArgsDescr", |args| {
@@ -520,6 +588,66 @@ async fn introspect_field_multi_args_descr() {
     .await;
 }
 
+#[tokio::test]
+async fn introspect_field_multi_args_descr_raw_idents() {
+    run_args_info_query("multiArgsDescrRawIdents", |args| {
+        assert_eq!(args.len(), 2);
+
+        assert!(args.contains(&Value::object(
+            vec![
+                ("name", Value::scalar("arg1")),
+                ("description", Value::scalar("The first arg")),
+                ("defaultValue", Value::null()),
+                (
+                    "type",
+                    Value::object(
+                        vec![
+                            ("name", Value::null()),
+                            (
+                                "ofType",
+                                Value::object(
+                                    vec![("name", Value::scalar("Int"))].into_iter().collect(),
+                                ),
+                            ),
+                        ]
+                        .into_iter()
+                        .collect(),
+                    ),
+                ),
+            ]
+            .into_iter()
+            .collect(),
+        )));
+
+        assert!(args.contains(&Value::object(
+            vec![
+                ("name", Value::scalar("arg2")),
+                ("description", Value::scalar("The second arg")),
+                ("defaultValue", Value::null()),
+                (
+                    "type",
+                    Value::object(
+                        vec![
+                            ("name", Value::null()),
+                            (
+                                "ofType",
+                                Value::object(
+                                    vec![("name", Value::scalar("Int"))].into_iter().collect(),
+                                ),
+                            ),
+                        ]
+                        .into_iter()
+                        .collect(),
+                    ),
+                ),
+            ]
+            .into_iter()
+            .collect(),
+        )));
+    })
+    .await;
+}
+
 #[tokio::test]
 async fn introspect_field_multi_args_descr_trailing_comma() {
     run_args_info_query("multiArgsDescrTrailingComma", |args| {
@@ -786,6 +914,32 @@ async fn introspect_field_arg_with_default_descr() {
     .await;
 }
 
+#[tokio::test]
+async fn introspect_field_arg_with_default_descr_raw_ident() {
+    run_args_info_query("argWithDefaultDescrRawIdent", |args| {
+        assert_eq!(args.len(), 1);
+
+        assert!(args.contains(&Value::object(
+            vec![
+                ("name", Value::scalar("arg")),
+                ("description", Value::scalar("The arg")),
+                ("defaultValue", Value::scalar("123")),
+                (
+                    "type",
+                    Value::object(
+                        vec![("name", Value::scalar("Int")), ("ofType", Value::null())]
+                            .into_iter()
+                            .collect(),
+                    ),
+                ),
+            ]
+            .into_iter()
+            .collect(),
+        )));
+    })
+    .await;
+}
+
 #[tokio::test]
 async fn introspect_field_multi_args_with_default_descr() {
     run_args_info_query("multiArgsWithDefaultDescr", |args| {
@@ -830,6 +984,50 @@ async fn introspect_field_multi_args_with_default_descr() {
     .await;
 }
 
+#[tokio::test]
+async fn introspect_field_multi_args_with_default_descr_raw_ident() {
+    run_args_info_query("multiArgsWithDefaultDescrRawIdent", |args| {
+        assert_eq!(args.len(), 2);
+
+        assert!(args.contains(&Value::object(
+            vec![
+                ("name", Value::scalar("arg1")),
+                ("description", Value::scalar("The first arg")),
+                ("defaultValue", Value::scalar("123")),
+                (
+                    "type",
+                    Value::object(
+                        vec![("name", Value::scalar("Int")), ("ofType", Value::null())]
+                            .into_iter()
+                            .collect(),
+                    ),
+                ),
+            ]
+            .into_iter()
+            .collect(),
+        )));
+
+        assert!(args.contains(&Value::object(
+            vec![
+                ("name", Value::scalar("arg2")),
+                ("description", Value::scalar("The second arg")),
+                ("defaultValue", Value::scalar("456")),
+                (
+                    "type",
+                    Value::object(
+                        vec![("name", Value::scalar("Int")), ("ofType", Value::null())]
+                            .into_iter()
+                            .collect(),
+                    ),
+                ),
+            ]
+            .into_iter()
+            .collect(),
+        )));
+    })
+    .await;
+}
+
 #[tokio::test]
 async fn introspect_field_multi_args_with_default_trailing_comma_descr() {
     run_args_info_query("multiArgsWithDefaultTrailingCommaDescr", |args| {
@@ -874,6 +1072,50 @@ async fn introspect_field_multi_args_with_default_trailing_comma_descr() {
     .await;
 }
 
+#[tokio::test]
+async fn introspect_field_multi_args_with_default_trailing_comma_descr_raw_ident() {
+    run_args_info_query("multiArgsWithDefaultTrailingCommaDescrRawIdent", |args| {
+        assert_eq!(args.len(), 2);
+
+        assert!(args.contains(&Value::object(
+            vec![
+                ("name", Value::scalar("arg1")),
+                ("description", Value::scalar("The first arg")),
+                ("defaultValue", Value::scalar("123")),
+                (
+                    "type",
+                    Value::object(
+                        vec![("name", Value::scalar("Int")), ("ofType", Value::null())]
+                            .into_iter()
+                            .collect(),
+                    ),
+                ),
+            ]
+            .into_iter()
+            .collect(),
+        )));
+
+        assert!(args.contains(&Value::object(
+            vec![
+                ("name", Value::scalar("arg2")),
+                ("description", Value::scalar("The second arg")),
+                ("defaultValue", Value::scalar("456")),
+                (
+                    "type",
+                    Value::object(
+                        vec![("name", Value::scalar("Int")), ("ofType", Value::null())]
+                            .into_iter()
+                            .collect(),
+                    ),
+                ),
+            ]
+            .into_iter()
+            .collect(),
+        )));
+    })
+    .await;
+}
+
 #[tokio::test]
 async fn introspect_field_args_with_complex_default() {
     run_args_info_query("argsWithComplexDefault", |args| {