-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Migrate introduce_named_generic
Assist to Use SyntaxFactory
#18483
base: master
Are you sure you want to change the base?
Migrate introduce_named_generic
Assist to Use SyntaxFactory
#18483
Conversation
Marking this as a draft for now since I’m running into a couple of errors with this assist
---- handlers::introduce_named_generic::tests::replace_two_impl_trait_with_generic_params stdout ----
Left:
fn foo<G, $0B: Bar>(foo: impl Foo, bar: B) {}
Right:
fn foo<$0G, B: Bar>(foo: impl Foo, bar: B) {}
Diff:
fn foo<$0G, $0B: Bar>(foo: impl Foo, bar: B) {}
thread 'handlers::introduce_named_generic::tests::replace_two_impl_trait_with_generic_params' panicked at crates/ide-assists/src/handlers/introduce_named_generic.rs:105:9:
text differs Here, only the cursor position appears to be off.
---- tests::generated::doctest_introduce_named_generic stdout ----
thread 'tests::generated::doctest_introduce_named_generic' panicked at /Users/tareknasser/.cargo/registry/src/index.crates.io-6f17d22bba15001f/rowan-0.15.15/src/cursor.rs:810:9:
immutable tree: fn foo(bar: impl Bar) {} |
let target = fn_.syntax().text_range(); | ||
acc.add( | ||
AssistId("introduce_named_generic", AssistKind::RefactorRewrite), | ||
"Replace impl trait with generic", | ||
target, | ||
|edit| { | ||
let impl_trait_type = edit.make_mut(impl_trait_type); | ||
let fn_ = edit.make_mut(fn_); | ||
let mut editor = edit.make_editor(&parent_node); | ||
let fn_generic_param_list = fn_.get_or_create_generic_param_list(); |
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.
This get_or_create_generic_param_list
mutates syntax tree if there is no generic params - as the function name shows 😄 - and causes immutable tree panics in rowan. (You can check this with RUST_BACKTRACE=full cargo test <panicking test name>
or $env:RUST_BACKTRACE="full"; cargo test <panicking test name>
if you are on Windows.)
This should be modified to copying existing generic params or making fresh new generic params rather than inserting into exsisting syntax tree.
#18385 might be a good reference working on SyntaxFactory
assist things
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.
The reason for panicking is because L35: let fn_ = edit.make_mut(fn_)
is being removed. Of course, it should be removed since we are migrating to SyntaxFactory
from mutable syntax tree and we should modify the L45
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 the review
This
get_or_create_generic_param_list
mutates syntax tree if there is no generic params ... This should be modified to copying existing generic params
How can I ‘copy existing generic params’ if there are none to begin with?
or making fresh new generic params
How would I go about doing that? I looked into initializing a new GenericParamList
, and the only way I found was using make::generic_param_list
, which is actually what’s in place now (but fails).
I defined a new ast::Fn
method similar to get_or_create_generic_param_list()
that calls syntax_editor_create_generic_param_list
if there are no existing generic params. This uses SyntaxEditor
to initialize the list (since insert_raw()
isn’t available for SyntaxEditor
, I used insert
instead).
Unfortunately, I can’t fully test my updates due to a new error:
---- tests::generated::doctest_introduce_named_generic stdout ----
thread 'tests::generated::doctest_introduce_named_generic' panicked at crates/syntax/src/syntax_editor.rs:55:9:
assertion failed: is_ancestor_or_self(&position.parent(), &self.root)
After tracing, this seems to be related to my syntax_editor_add_generic_param
implementation, though it’s identical to add_generic_param
except it uses SyntaxEditor
instead of ted
.
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.
By the way, I know these new functions would ideally go into a separate file, but right now I’m just focused on getting them working first
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.
Sorry for the really late reply. I've been quite busy 'cause I moved to a new job 😅
Your code that creating fresh new generic param list seems good but as it still panics, I'll add some comments.
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.
Sorry for the really late reply. I've been quite busy 'cause I moved to a new job 😅
No worries at all. Best of luck with your new job
None => { | ||
let position = crate::syntax_editor::Position::after(self.l_angle_token().unwrap()); | ||
let new_param_list = make::generic_param_list(once(new_param.clone())); | ||
editor.insert(position, new_param_list.syntax()); |
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.
This panics on the following assertion;
rust-analyzer/crates/syntax/src/syntax_editor.rs
Lines 54 to 55 in a2bb014
pub fn insert(&mut self, position: Position, element: impl Element) { | |
debug_assert!(is_ancestor_or_self(&position.parent(), &self.root)); |
I think that the panic happens when we are here in the following process;
- First, we insert a new empty generic arg list because the function doesn't have one
- And then we add a new type parameter for impl Trait thing
In 2. we don't have the new empty generic arg list made in 1. in our syntax tree yet (it will be made when the syntax edit finishes) so the position for new type parameter doesn't exist in our syntax tree yet either.
So, I think that we should directly insert a new generic arg list with new type param in 1. without separated step 2.
(Also, the current logic seems problematic beside the above because we are inserting <T>
into <>
, instead of T
)
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.
So just for context, there’s been some discussion about this in Zulip.
In 2. we don't have the new empty generic arg list made in 1. in our syntax tree yet (it will be made when the syntax edit finishes) so the position for new type parameter doesn't exist in our syntax tree yet either.
That was exactly the issue. I updated the code to add a method, ast::Fn::syntax_editor_add_generic_param
, that handles the logic for us. Now, there’s a clear separation between the three cases in this method:
- If there is an existing generic param list and it’s not empty, we replace the entire list with a new one (existing params + the new one).
- If there’s an existing but empty generic param list, we place the new param after the left angle token.
- If there is no generic param list, we create one from scratch and place it in the right position.
This is working fine for all these cases, but the only issue left is the cursor position—it’s off in all cases.
I updated the code to use SyntaxEditor::add_annotation()
as suggested #18483 (comment) , but I'm still facing the cursor issue. After tracing it a bit, it seems that this might be happening because fn_.generic_param_list()
isn’t being updated. If that’s the case, how can I update it?
@@ -61,6 +67,9 @@ pub(crate) fn introduce_named_generic(acc: &mut Assists, ctx: &AssistContext<'_> | |||
edit.add_tabstop_before(cap, generic_param); |
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.
The weird snippet cursor position($0
) thing is caused in here.
As I commented on the first panic comment, our code is changed when the SyntaxEditor::finish
is called.
So here, the last element of generic params is pre-existing one, not our new one. (Sorry for my misleading comment about this weeks ago 😅)
I think that we should change add_tabstop_before
as entried like add_file_edits
and triggered in order
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.
Oh, I was wrong again 😅 Sorry. I think that we should use SyntaxEditor::add_annotation
here (Find its usages for examples)
Signed-off-by: Tarek <[email protected]>
Signed-off-by: Tarek <[email protected]>
Signed-off-by: Tarek <[email protected]>
Signed-off-by: Tarek <[email protected]>
…arameters Signed-off-by: Tarek <[email protected]>
…generic parameters Signed-off-by: Tarek <[email protected]>
b77700f
to
cf94c40
Compare
This PR is part of #15710 and #18285.
Changes Made
SyntaxEditor
#18285 for migration.SyntaxFactory
method,type_param
.add_generic_param
(originally inGenericParamList
) with a new method,syntax_editor_add_generic_param
. This new method is similar but takes aSyntaxEditor
instance instead ofted
to avoid the “immutable tree” error.