rust-lang / rust-lang/rust-clippy
duplicated_attributes: false positive with async-graphql Interface derive
Open
Nobody has claimed this yet.
C-bug
I-false-positive
- Dominant language
- Rust
- Stars
- 13.5k
- Forks
- 2.2k
- Avg merge
- 2d 10h
- Merged PRs (30d)
- 32
Description
Summary
Don't have a stand-alone reproducible example yet, but I have this:
#[derive(Interface)]
#[graphql(field(
name = "messages",
ty = "Option<MessageConnection>",
arg(name = "after", ty = "Option<String>"),
arg(name = "before", ty = "Option<String>"),
arg(name = "first", ty = "Option<i32>"),
arg(name = "last", ty = "Option<i32>")
))]
#[graphql(field(name = "id", ty = "ID",))]
pub(super) enum Sender<'a> {
User(User),
Email(EmailSender<'a>),
}
See also here: https://async-graphql.github.io/async-graphql/en/define_interface.html.
Expanded macro output
#[allow(clippy::all, clippy::pedantic)]
impl<'a> Sender<'a> {
#[inline]
pub async fn r#messages<'ctx>(
&self,
ctx: &'ctx async_graphql::Context<'ctx>,
arg0: Option<String>,
arg1: Option<String>,
arg2: Option<i32>,
arg3: Option<i32>,
) -> async_graphql::Result<Option<MessageConnection>> {
match self {
Sender::User(obj) => obj
.r#messages(ctx, arg0, arg1, arg2, arg3)
.await
.map_err(|err| ::std::convert::Into::<async_graphql::Error>::into(err))
.map(::std::convert::Into::into),
Sender::Email(obj) => obj
.r#messages(ctx, arg0, arg1, arg2, arg3)
.await
.map_err(|err| ::std::convert::Into::<async_graphql::Error>::into(err))
.map(::std::convert::Into::into),
}
}
#[inline]
pub async fn r#id<'ctx>(
&self,
ctx: &'ctx async_graphql::Context<'ctx>,
) -> async_graphql::Result<ID> {
match self {
Sender::User(obj) => obj
.r#id(ctx)
.await
.map_err(|err| ::std::convert::Into::<async_graphql::Error>::into(err))
.map(::std::convert::Into::into),
Sender::Email(obj) => obj
.r#id(ctx)
.await
.map_err(|err| ::std::convert::Into::<async_graphql::Error>::into(err))
.map(::std::convert::Into::into),
}
}
}
#[allow(clippy::all, clippy::pedantic)]
#[async_graphql::async_trait::async_trait]
impl<'a> async_graphql::resolver_utils::ContainerType for Sender<'a> {
async fn resolve_field(
&self,
ctx: &async_graphql::Context<'_>,
) -> async_graphql::ServerResult<::std::option::Option<async_graphql::Value>> {
if ctx.item.node.name.node == "messages" {
let (_, arg0) =
ctx.param_value::<Option<String>>("after", ::std::option::Option::None)?;
let (_, arg1) =
ctx.param_value::<Option<String>>("before", ::std::option::Option::None)?;
let (_, arg2) = ctx.param_value::<Option<i32>>("first", ::std::option::Option::None)?;
let (_, arg3) = ctx.param_value::<Option<i32>>("last", ::std::option::Option::None)?;
let ctx_obj = ctx.with_selection_set(&ctx.item.node.selection_set);
return async_graphql::OutputType::resolve(
&self
.r#messages(ctx, arg0, arg1, arg2, arg3)
.await
.map_err(|err| {
::std::convert::Into::<async_graphql::Error>::into(err)
.into_server_error(ctx.item.pos)
})?,
&ctx_obj,
ctx.item,
)
.await
.map(::std::option::Option::Some);
}
if ctx.item.node.name.node == "id" {
let ctx_obj = ctx.with_selection_set(&ctx.item.node.selection_set);
return async_graphql::OutputType::resolve(
&self.r#id(ctx).await.map_err(|err| {
::std::convert::Into::<async_graphql::Error>::into(err)
.into_server_error(ctx.item.pos)
})?,
&ctx_obj,
ctx.item,
)
.await
.map(::std::option::Option::Some);
}
::std::result::Result::Ok(::std::option::Option::None)
}
fn collect_all_fields<'__life>(
&'__life self,
ctx: &async_graphql::ContextSelectionSet<'__life>,
fields: &mut async_graphql::resolver_utils::Fields<'__life>,
) -> async_graphql::ServerResult<()> {
match self {
Sender::User(obj) => obj.collect_all_fields(ctx, fields),
Sender::Email(obj) => obj.collect_all_fields(ctx, fields),
}
}
}
#[allow(clippy::all, clippy::pedantic)]
#[async_graphql::async_trait::async_trait]
impl<'a> async_graphql::OutputType for Sender<'a> {
fn type_name() -> ::std::borrow::Cow<'static, ::std::primitive::str> {
::std::borrow::Cow::Borrowed("Sender")
}
fn introspection_type_name(&self) -> ::std::borrow::Cow<'static, ::std::primitive::str> {
match self {
Sender::User(obj) => <User as async_graphql::OutputType>::type_name(),
Sender::Email(obj) => <EmailSender<'a> as async_graphql::OutputType>::type_name(),
}
}
fn create_type_info(registry: &mut async_graphql::registry::Registry) -> ::std::string::String {
registry.create_output_type:: <Self,_>(async_graphql::registry::MetaTypeId::Interface, |registry|{
<User as async_graphql::OutputType> ::create_type_info(registry);
registry.add_implements(& <User as async_graphql::OutputType> ::type_name(), ::std::convert::AsRef::as_ref(& ::std::borrow::Cow::Borrowed("Sender")));
<EmailSender<'a>as async_graphql::OutputType> ::create_type_info(registry);
registry.add_implements(& <EmailSender<'a>as async_graphql::OutputType> ::type_name(), ::std::convert::AsRef::as_ref(& ::std::borrow::Cow::Borrowed("Sender")));
async_graphql::registry::MetaType::Interface {
name: ::std::borrow::Cow::into_owned(::std::borrow::Cow::Borrowed("Sender")),description: ::std::option::Option::None,fields:{
let mut fields = async_graphql::indexmap::IndexMap::new();
fields.insert(::std::string::ToString::to_string("messages"),async_graphql::registry::MetaField {
name: ::std::string::ToString::to_string("messages"),description: ::std::option::Option::None,args:{
let mut args = async_graphql::indexmap::IndexMap::new();
args.insert(::std::borrow::ToOwned::to_owned("after"),async_graphql::registry::MetaInputValue {
name: ::std::string::ToString::to_string("after"),description: ::std::option::Option::None,ty: <Option<String>as async_graphql::InputType> ::create_type_info(registry),default_value: ::std::option::Option::None,visible: ::std::option::Option::None,inaccessible:false,tags: ::std::vec![],is_secret:false,
});
args.insert(::std::borrow::ToOwned::to_owned("before"),async_graphql::registry::MetaInputValue {
name: ::std::string::ToString::to_string("before"),description: ::std::option::Option::None,ty: <Option<String>as async_graphql::InputType> ::create_type_info(registry),default_value: ::std::option::Option::None,visible: ::std::option::Option::None,inaccessible:false,tags: ::std::vec![],is_secret:false,
});
args.insert(::std::borrow::ToOwned::to_owned("first"),async_graphql::registry::MetaInputValue {
name: ::std::string::ToString::to_string("first"),description: ::std::option::Option::None,ty: <Option<i32>as async_graphql::InputType> ::create_type_info(registry),default_value: ::std::option::Option::None,visible: ::std::option::Option::None,inaccessible:false,tags: ::std::vec![],is_secret:false,
});
args.insert(::std::borrow::ToOwned::to_owned("last"),async_graphql::registry::MetaInputValue {
name: ::std::string::ToString::to_string("last"),description: ::std::option::Option::None,ty: <Option<i32>as async_graphql::InputType> ::create_type_info(registry),default_value: ::std::option::Option::None,visible: ::std::option::Option::None,inaccessible:false,tags: ::std::vec![],is_secret:false,
});
args
},ty: <Option<MessageConnection>as async_graphql::OutputType> ::create_type_info(registry),deprecation:async_graphql::registry::Deprecation::NoDeprecated,cache_control: ::std::default::Default::default(),external:false,provides: ::std::option::Option::None,requires: ::std::option::Option::None,shareable:false,inaccessible:false,tags: ::std::vec![],override_from: ::std::option::Option::None,visible: ::std::option::Option::None,compute_complexity: ::std::option::Option::None,directive_invocations: ::std::vec![],
});
fields.insert(::std::string::ToString::to_string("id"),async_graphql::registry::MetaField {
name: ::std::string::ToString::to_string("id"),description: ::std::option::Option::None,args:{
let mut args = async_graphql::indexmap::IndexMap::new();
args
},ty: <ID as async_graphql::OutputType> ::create_type_info(registry),deprecation:async_graphql::registry::Deprecation::NoDeprecated,cache_control: ::std::default::Default::default(),external:false,provides: ::std::option::Option::None,requires: ::std::option::Option::None,shareable:false,inaccessible:false,tags: ::std::vec![],override_from: ::std::option::Option::None,visible: ::std::option::Option::None,compute_complexity: ::std::option::Option::None,directive_invocations: ::std::vec![],
});
fields
},possible_types:{
let mut possible_types = async_graphql::indexmap::IndexSet::new();
possible_types.insert(<User as async_graphql::OutputType> ::type_name().into_owned());
possible_types.insert(<EmailSender<'a>as async_graphql::OutputType> ::type_name().into_owned());
possible_types
},extends:false,keys: ::std::option::Option::None,visible: ::std::option::Option::None,inaccessible:false,tags: ::std::vec![],rust_typename: ::std::option::Option::Some(::std::any::type_name:: <Self>()),
}
})
}
async fn resolve(
&self,
ctx: &async_graphql::ContextSelectionSet<'_>,
_field: &async_graphql::Positioned<async_graphql::parser::types::Field>,
) -> async_graphql::ServerResult<async_graphql::Value> {
async_graphql::resolver_utils::resolve_container(ctx, self).await
}
}
impl<'a> async_graphql::InterfaceType for Sender<'a> {}
Lint Name
duplicated_attributes
Reproducer
No response
Version
rustc 1.79.0 (129f3b996 2024-06-10)
binary: rustc
commit-hash: 129f3b9964af4d4a709d1383930ade12dfe7c081
commit-date: 2024-06-10
host: aarch64-apple-darwin
release: 1.79.0
LLVM version: 18.1.7
cc @GuillaumeGomez (who seems to have created this lint)
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
No source file or test is named, and the issue says it lacks a standalone reproducer. Start by reducing the async-graphql Interface derive example and expanded macro output into a reproducer for duplicated_attributes. Done means the valid derive no longer produces the false-positive lint, with a regression test covering it.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- tooling
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100