Skip to content
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

feature: add the "Recursively add derive" assist #18118

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

IvarWithoutBones
Copy link
Contributor

This introduces a new assist that adds the derive attributes from a struct or enum to the types of its fields, recursively:
Peek 2024-09-16 00-24

The derive attributes will be copied over if the field type meets the following criteria:

  • It resides within the same crate as the "top-level" type, to avoid editing the crate registry.
  • It does not already have the same derive attribute.
  • It does not have a manual implementation of the trait likely implemented by the derive.

The last criteria is a bit of a guess since it isn't possible to know what trait implementation(s) a derive macro generates. If a trait has the same name as the derive macro and the top-level type (from which we're copying the derive attribute) implements it, it'll be used to check for manual impls. This seems to work well in practice :)

Fixes #12330

@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Sep 15, 2024
Copy link
Contributor

@ChayimFriedman2 ChayimFriedman2 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall a good PR, left some comments.

crates/test-utils/src/fixture.rs Outdated Show resolved Hide resolved
crates/ide-assists/src/handlers/recursively_add_derive.rs Outdated Show resolved Hide resolved
match list {
ast::FieldList::RecordFieldList(list) => {
record = Some(
list.fields().filter_map(|field| ctx.sema.resolve_type(&field.ty()?)?.as_adt()),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems unfortunate to exclude things only because they are not ADT or not local, e.g. references will be excluded, and Box, and Vec, while all may have a conditional implementation for arbitrary types.

A better strategy will be when the type is non-local, to instead include its generic parameters (or their generic parameters, recursively). An even better (but harder) strategy will be to look if the type has a conditional implementation and what are its conditions (this doesn't have to go in the initial implementation, though).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I completely forgot about that, I'll look into it 👍

crates/ide-assists/src/handlers/recursively_add_derive.rs Outdated Show resolved Hide resolved
crates/ide-assists/src/handlers/recursively_add_derive.rs Outdated Show resolved Hide resolved
@IvarWithoutBones
Copy link
Contributor Author

Thanks for the reviews, I've fixed most comments. Supporting non-ADTs and using SyntaxEditor will take a bit more investigation so I'll push that once it's ready.

This commit adds the core::hash::Hash derive macro to the prelude, just like rustc does. They define
the macro in a private core::hash::macros module, presumably to differentiate between the macro and
trait (who otherwise share the same path). Only the macro is in the prelude, which minicore now mirrors.

I've also added support for multiple "line regions" in minicore, as the prelude re-export needs both
"hash" and "derive". The syntax looks as follows: "// :hash, derive".
This introduces a new assist that adds the derive attributes from a struct or enum to the types of
its fields, recursively. It is recursive in the sense that the fields of field types are also
considered.

The derive attributes will be copied over if the field type meets the following criteria:
* It resides within the same workspace as the "top-level" type, to avoid editing the crate registry.
* It does not already have the same derive attribute.
* It does not have a manual implementation of the trait.

The last criteria is a bit of a guess since it isn't possible to know what trait implementation(s) a
derive macro generates. If a trait has the same name as the derive macro and the top-level type
(which already has the derive attribute) implements it, it'll be used to check for manual impls.
This seems to work well in practice.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-review Status: Awaiting review from the assignee but also interested parties.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Feature Request] Derive trait recursively
5 participants