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

Migrate introduce_named_generic Assist to Use SyntaxFactory #18483

Merged

Conversation

tareknaser
Copy link
Contributor

This PR is part of #15710 and #18285.

Changes Made

  • Followed the steps in Migrate assists to SyntaxEditor #18285 for migration.
  • Added a new SyntaxFactory method, type_param.
  • Replaced add_generic_param (originally in GenericParamList) with a new method, syntax_editor_add_generic_param. This new method is similar but takes a SyntaxEditor instance instead of ted to avoid the “immutable tree” error.

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

Marking this as a draft for now since I’m running into a couple of errors with this assist

  1. The first issue is with cursor positioning:
---- 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.

  1. The second error is related to an immutable tree in rowan:
---- 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();
Copy link
Member

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

Copy link
Member

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

Copy link
Contributor Author

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.

Copy link
Contributor Author

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

Copy link
Member

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.

Copy link
Contributor Author

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());
Copy link
Member

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;

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;

  1. First, we insert a new empty generic arg list because the function doesn't have one
  2. 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)

Copy link
Contributor Author

@tareknaser tareknaser Nov 27, 2024

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?

Update: Fixed in 69b9660. It turns out manually inserting the tokens resolves the issue.

@tareknaser tareknaser force-pushed the syntax_factory_introduce_named_generic branch 2 times, most recently from cf94c40 to 69b9660 Compare December 3, 2024 21:48
@tareknaser tareknaser marked this pull request as ready for review December 3, 2024 21:51
crates/ide-assists/src/handlers/introduce_named_generic.rs Outdated Show resolved Hide resolved
crates/ide-assists/src/handlers/introduce_named_generic.rs Outdated Show resolved Hide resolved
crates/ide-assists/src/handlers/introduce_named_generic.rs Outdated Show resolved Hide resolved
crates/ide-assists/src/handlers/introduce_named_generic.rs Outdated Show resolved Hide resolved
crates/syntax/src/ast/edit_in_place.rs Outdated Show resolved Hide resolved
crates/syntax/src/ast/edit_in_place.rs Outdated Show resolved Hide resolved
Signed-off-by: Tarek <tareknaser360@gmail.com>
Signed-off-by: Tarek <tareknaser360@gmail.com>
Signed-off-by: Tarek <tareknaser360@gmail.com>
Signed-off-by: Tarek <tareknaser360@gmail.com>
…arameters

Signed-off-by: Tarek <tareknaser360@gmail.com>
…generic parameters

Signed-off-by: Tarek <tareknaser360@gmail.com>
Signed-off-by: Tarek <tareknaser360@gmail.com>
Signed-off-by: Tarek <tareknaser360@gmail.com>
Signed-off-by: Tarek <tareknaser360@gmail.com>
@tareknaser tareknaser force-pushed the syntax_factory_introduce_named_generic branch from da2405c to 509a9f8 Compare December 4, 2024 12:50
Signed-off-by: Tarek <tareknaser360@gmail.com>
@tareknaser tareknaser force-pushed the syntax_factory_introduce_named_generic branch from 509a9f8 to 9157761 Compare December 4, 2024 12:51
Signed-off-by: Tarek <tareknaser360@gmail.com>
@tareknaser tareknaser requested a review from darichey December 4, 2024 15:24
Signed-off-by: Tarek <tareknaser360@gmail.com>
Copy link
Contributor

@darichey darichey left a comment

Choose a reason for hiding this comment

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

Awesome! Thanks for your perseverance :)

cc @davidbarsky

@davidbarsky davidbarsky added this pull request to the merge queue Dec 5, 2024
Merged via the queue into rust-lang:master with commit 2fd0654 Dec 5, 2024
9 checks passed
@tareknaser tareknaser deleted the syntax_factory_introduce_named_generic branch December 5, 2024 19:15
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.

5 participants