-
Notifications
You must be signed in to change notification settings - Fork 1.7k
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
fix: rewrite code_action generate_delegate_trait
#16112
Conversation
Here's an simple example explaining the third step mentioned above. Say we have the following code: struct B<T> {
a: T
}
trait Trait<T> {
fn f(&self) -> &T;
}
impl<T> Trait<T> for B<T> {
fn f(&self) -> &T { &self.a }
}
struct S<T> {
a: B<T>
} To generate a delegate impl for // generated after rewritten
impl<T> Trait<T> for S<T> {
fn f(&self) -> &T {
<B<T> as Trait<T0>>::f(&self.a)
}
} However, if we do not match and replace instantiated generic arguments, we will get impl<T, T0> Trait<T> for S<T0> {
fn f(&self) -> &T {
<B<T0> as Trait<T>>::f(&self.a)
}
} which is not correct. |
pub fn generic_arg_list(&self) -> Option<ast::GenericArgList> { | ||
if let ast::Type::PathType(path_type) = self { | ||
path_type.path()?.segment()?.generic_arg_list() | ||
} else { | ||
None | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There maybe more than one generic arg list in a type, Qux<(Foo<A, B>, Bar<C, D>>)
for example, also generic args might apear in other spots like [T]
, (T, U, V)
etc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for pointing this out! I hadn't considered this situation before.
These situations might make the analysis a bit more complex; I need to reorganize my current approach and think about how to elegantly address this issue. ❤️
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Small comment, but thats something that could be addressed as a follow up
Thanks! |
☀️ Test successful - checks-actions |
…t, r=Veykril internal: clean and enhance readability for `generate_delegate_trait` Continue from #16112 This PR primarily involves some cleanup and simple refactoring work, including: - Adding numerous comments to layer the code and explain the behavior of each step. - Renaming some variables to make them more sensible. - Simplify certain operations using a more elegant approach. The goal is to make this intricate implementation clearer and facilitate future maintenance. In addition to this, the PR also removes redundant `path_transform` operations for `type_gen_args`. Taking the example of `impl Trait<T1> for S<S1>`, where `S1` is considered. The struct `S` must be in the file where the user triggers code actions, so there's no need for the `path_transform`. Furthermore, before performing the transform, we've already renamed `S1`, ensuring it won't clash with existing generics parameters. Therefore, there's no need to transform it.
I've made substantial enhancements to the "generate delegate trait" code action in rust-analyzer. Here's a summary of the changes:
Resolved the "Can’t find [email protected] in AstIdMap" error
Fix #15804, fix #15968, fix #15108
The issue stemmed from an incorrect application of PathTransform in the original code. Previously, a new 'impl' was generated first and then transformed, causing PathTransform to fail in locating the correct AST node, resulting in an error. I rectified this by performing the transformation before generating the new 'impl' (using make::impl_trait), ensuring a step-by-step transformation of associated items.
Rectified generation of
Self
typegenerate_delegate_trait
is unable to properly handle trait withSelf
type.Let's take the following code as an example:
Here, if we implement
Trait
forS
, the type off
should be() -> Self
, i.e.() -> S
. However we cannot automatically generate a function that constructsS
.To ensure that the code action doesn't generate delegate traits for traits with Self types, I add a function named
has_self_type
to handle it.Extended support for generics in structs and fields within this code action
The former version of
generate_delegate_trait
cannot handle structs with generics properly. Here's an example:The former version will generates improper code:
The rewritten version can handle generics properly:
See more examples in added unit tests.
I enabled support for generic structs in
generate_delegate_trait
through the following steps (using the code example provided):S
and the ones in the impl ofB
, I renamed the generic parameters ofS
.B
's parameters are instantiated withinS
, the original generic parameters ofB
needed removal withinS
(to avoid errors from redundant parameters). An important consideration here arises when Trait and B share parameters inB
's impl. In such cases, these shared generic parameters cannot be removed.B
's type inS
and its type in the impl. Given that some generic parameters in the impl are instantiated inB
, I replaced these parameters with their instantiated results using PathTransform. For instance, in the example provided, matchingB<A>
andB<T2>
, whereT2
is instantiated asA
, I replaced all occurrences ofT2
in the impl withA
(i.e. apply the instantiated generic arguments to the params).For a more detailed explanation, please refer to the code and comments. I welcome suggestions and any further questions!