Skip to content

Self parameter not required in resolvers #1016

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
tsoutsman opened this issue Jan 14, 2022 · 1 comment
Closed

Self parameter not required in resolvers #1016

tsoutsman opened this issue Jan 14, 2022 · 1 comment
Assignees
Labels
bug Something isn't working duplicate This issue or pull request already exists

Comments

@tsoutsman
Copy link

Describe the bug
The self parameter is not required in resolvers, even if they use self.

This isn't a bug per se, but it leads to some pretty confusing code, so I don't think it should be allowed.

To Reproduce
The following compiles:

struct Human {
    name: String,
}

#[juniper::graphql_object]
impl Human {
    // Note the lack of &self
    fn name() -> &str {
        self.name.as_ref()
    }
}

Expected behaviour
Compilation failure.

Additional context
This happens because of how the graphql_object macro transforms the resolvers.

Using cargo expand, the example above expands to:

Click to see full output
#![feature(prelude_import)]
#[prelude_import]
use std::prelude::rust_2018::*;
#[macro_use]
extern crate std;
fn main() {}
struct Human {
    name: String,
}
impl<__S> ::juniper::marker::IsOutputType<__S> for Human
where
    __S: ::juniper::ScalarValue,
{
    fn mark() {
        <<&str as ::juniper::IntoResolvable<
            '_,
            __S,
            _,
            <Self as ::juniper::GraphQLValue<__S>>::Context,
        >>::Type as ::juniper::marker::IsOutputType<__S>>::mark();
    }
}
impl<__S> ::juniper::marker::GraphQLObjectType<__S> for Human where __S: ::juniper::ScalarValue {}
impl<__S> ::juniper::GraphQLType<__S> for Human
where
    __S: ::juniper::ScalarValue,
{
    fn name(_: &Self::TypeInfo) -> Option<&'static str> {
        Some("Human")
    }
    fn meta<'r>(
        info: &Self::TypeInfo,
        registry: &mut ::juniper::Registry<'r, __S>,
    ) -> ::juniper::meta::MetaType<'r, __S>
    where
        __S: 'r,
    {
        let fields = [registry.field_convert::<&str, _, Self::Context>("name", info)];
        let meta = registry.build_object_type::<Human>(info, &fields);
        meta.into_meta()
    }
}
impl<__S> ::juniper::GraphQLValue<__S> for Human
where
    __S: ::juniper::ScalarValue,
{
    type Context = ();
    type TypeInfo = ();
    fn type_name<'__i>(&self, info: &'__i Self::TypeInfo) -> Option<&'__i str> {
        <Self as ::juniper::GraphQLType<__S>>::name(info)
    }
    #[allow(unused_variables)]
    #[allow(unused_mut)]
    fn resolve_field(
        &self,
        _info: &(),
        field: &str,
        args: &::juniper::Arguments<__S>,
        executor: &::juniper::Executor<Self::Context, __S>,
    ) -> ::juniper::ExecutionResult<__S> {
        match field {
            "name" => {
                let res: &str = (|| self.name.as_ref())();
                ::juniper::IntoResolvable::into(res, executor.context()).and_then(|res| match res {
                    Some((ctx, r)) => executor.replaced_context(ctx).resolve_with_ctx(&(), &r),
                    None => Ok(::juniper::Value::null()),
                })
            }
            _ => {
                {
                    ::std::rt::panic_fmt(::core::fmt::Arguments::new_v1(
                        &["Field ", " not found on type "],
                        &match (&field, &<Self as ::juniper::GraphQLType<__S>>::name(_info)) {
                            _args => [
                                ::core::fmt::ArgumentV1::new(_args.0, ::core::fmt::Display::fmt),
                                ::core::fmt::ArgumentV1::new(_args.1, ::core::fmt::Debug::fmt),
                            ],
                        },
                    ))
                };
            }
        }
    }
    fn concrete_type_name(&self, _: &Self::Context, _: &Self::TypeInfo) -> String {
        "Human".to_string()
    }
}
impl<__S> ::juniper::GraphQLValueAsync<__S> for Human
where
    __S: ::juniper::ScalarValue,
    __S: Send + Sync,
    Self: Sync,
{
    fn resolve_field_async<'b>(
        &'b self,
        info: &'b Self::TypeInfo,
        field: &'b str,
        args: &'b ::juniper::Arguments<__S>,
        executor: &'b ::juniper::Executor<Self::Context, __S>,
    ) -> ::juniper::BoxFuture<'b, ::juniper::ExecutionResult<__S>>
    where
        __S: Send + Sync,
    {
        use ::juniper::futures::future;
        use ::juniper::GraphQLType;
        match field {
            "name" => {
                let res: &str = (|| self.name.as_ref())();
                let res2 = ::juniper::IntoResolvable::into(res, executor.context());
                let f = async move {
                    match res2 {
                        Ok(Some((ctx, r))) => {
                            let sub = executor.replaced_context(ctx);
                            sub.resolve_with_ctx_async(&(), &r).await
                        }
                        Ok(None) => Ok(::juniper::Value::null()),
                        Err(e) => Err(e),
                    }
                };
                use ::juniper::futures::future;
                future::FutureExt::boxed(f)
            }
            _ => {
                {
                    ::std::rt::panic_fmt(::core::fmt::Arguments::new_v1(
                        &["Field ", " not found on type "],
                        &match (&field, &<Self as ::juniper::GraphQLType<__S>>::name(info)) {
                            _args => [
                                ::core::fmt::ArgumentV1::new(_args.0, ::core::fmt::Display::fmt),
                                ::core::fmt::ArgumentV1::new(_args.1, ::core::fmt::Debug::fmt),
                            ],
                        },
                    ))
                };
            }
        }
    }
}

The important part is:

fn resolve_field_async<'b>(
    &'b self,
    info: &'b Self::TypeInfo,
    field: &'b str,
    args: &'b ::juniper::Arguments<__S>,
    executor: &'b ::juniper::Executor<Self::Context, __S>,
) -> ::juniper::BoxFuture<'b, ::juniper::ExecutionResult<__S>>
where
    __S: Send + Sync,
{
    use ::juniper::futures::future;
    use ::juniper::GraphQLType;
    match field {
        "name" => {
            let res: &str = (|| self.name.as_ref())();
            let res2 = ::juniper::IntoResolvable::into(res, executor.context());
            let f = async move {
                match res2 {
                    Ok(Some((ctx, r))) => {
                        let sub = executor.replaced_context(ctx);
                        sub.resolve_with_ctx_async(&(), &r).await
                    }
                    Ok(None) => Ok(::juniper::Value::null()),
                    Err(e) => Err(e),
                }
            };
            use ::juniper::futures::future;
            future::FutureExt::boxed(f)
        }
        _ => {
            {
                ::std::rt::panic_fmt(::core::fmt::Arguments::new_v1(
                    &["Field ", " not found on type "],
                    &match (&field, &<Self as ::juniper::GraphQLType<__S>>::name(info)) {
                        _args => [
                            ::core::fmt::ArgumentV1::new(_args.0, ::core::fmt::Display::fmt),
                            ::core::fmt::ArgumentV1::new(_args.1, ::core::fmt::Debug::fmt),
                        ],
                    },
                ))
            };
        }
    }
}

In particular let res: &str = (|| self.name.as_ref())(); grabs self from the surrounding context.

@tsoutsman tsoutsman added bug Something isn't working needs-triage labels Jan 14, 2022
@tyranron
Copy link
Member

@tsoutsman this happens due to declared methods not being actual real Rust methods, but something artificial, which is desugared into in-place code block.

On the latest master this has been fixed already in #971.

@tyranron tyranron added duplicate This issue or pull request already exists and removed needs-triage labels Jan 14, 2022
@tyranron tyranron self-assigned this Jan 14, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working duplicate This issue or pull request already exists
Projects
None yet
Development

No branches or pull requests

2 participants