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

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from

Conversation

tareknaser
Copy link

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
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
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
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

let position = crate::syntax_editor::Position::after(last_param.syntax());
let elements = vec![
make::token(T![,]).into(),
make::tokens::single_space().into(),
Copy link
Member

@ShoyuVanilla ShoyuVanilla Nov 7, 2024

Choose a reason for hiding this comment

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

We should not use make:: in new assists but should create a corresponding method in impl SyntaxFactory in here;


and call it like make.token(..) (See Bee's migration guide here) edit: nevermind, this seems okay 😅

And I think that this function shouldn't be here since we are going to remove all mutable syntax tree things and "edit in place" is kinda mutation. We should "create new" rather than "modify existing".

About the cursor position errors, I noticed that the cursor is on the n - 1 indexed generic parameter element position when we are inserting n indexed one as new. (and If we are inserting the 0 indexed one, the cursor is missing 🤔) I guess there might be some unexpected interaction between here and the SyntaxEditor::replace but as this might be moved to somewhere, let's see if this still happens then.

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.

3 participants