-
Notifications
You must be signed in to change notification settings - Fork 440
Closed
Labels
bugSomething isn't workingSomething isn't workingduplicateThis issue or pull request already existsThis issue or pull request already exists
Description
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.
Metadata
Metadata
Assignees
Labels
bugSomething isn't workingSomething isn't workingduplicateThis issue or pull request already existsThis issue or pull request already exists