Skip to content

Conversation

@A4-Tacks
Copy link
Member

@A4-Tacks A4-Tacks commented Nov 11, 2025

Fixes #21020

Example

let arg_list = make::arg_list([make::expr_literal("1").into(), make::expr_literal("2").into()]);
let mut editor = SyntaxEditor::new(arg_list.syntax().clone());
let target_expr = make::expr_literal("3").clone_for_update();

for arg in arg_list.args() {
    editor.replace(arg.syntax(), target_expr.syntax());
}

let edit = editor.finish();
let expect = expect![["(3, 3)"]];
expect.assert_eq(&edit.new_root.to_string());

Before this PR

(, )3

After this PR

(3, 3)

Example
---
```rust
let arg_list = make::arg_list([make::expr_literal("1").into(), make::expr_literal("2").into()]);
let mut editor = SyntaxEditor::new(arg_list.syntax().clone());
let target_expr = make::expr_literal("3").clone_for_update();

for arg in arg_list.args() {
    editor.replace(arg.syntax(), target_expr.syntax());
}

let edit = editor.finish();
let expect = expect![["(3, 3)"]];
expect.assert_eq(&edit.new_root.to_string());
```

**Before this PR**

```text
(, )3
```

**After this PR**

```text
(3, 3)
```
@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Nov 11, 2025
Copy link
Member

Choose a reason for hiding this comment

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

The dedup logic looks correct to me, but I’m not sure whether inserting or replacing with the same syntax node multiple times is part of the intended design. Should we automatically handle this by cloning the duplicated node (as done here), or should we reject duplicates and require the caller to handle it instead?
@DropDemBits, I’d appreciate your thoughts on this 😅

Copy link
Contributor

Choose a reason for hiding this comment

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

Oof, it's been a year but I managed to recollect enough context to form an opinion 😆

The current SyntaxEditor design admits that SyntaxNodes do have a form of identity which at the moment is tracked by SyntaxNodes sharing the same NodeData1. At the same time however, the implementation detail being mutable syntax trees necessarily means that shared nodes get mutated (causing this issue), while using clone_subtree breaks this identity.

For now, I am okay with letting this through to fix this issue, but I've opened #21155 for myself to fix the broader issue.

Footnotes

  1. This doesn't necessarily lock us in to having SyntaxNodes be reference counted, just that they need to carry through some identity token when we need it, e.g. some u32 derived from a global AtomicU32 counter. In fact, lifting the concept of node identity into rowan proper should let us remove SyntaxMappings, annoying as they are!

Copy link
Member

Choose a reason for hiding this comment

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

Huge thanks for your clear explanation!

@ShoyuVanilla ShoyuVanilla added this pull request to the merge queue Nov 28, 2025
SyntaxElement::Node(node) => node,
};
if changed_elements_set.contains(node) {
let new_node = node.clone_subtree().clone_for_update();
Copy link
Contributor

Choose a reason for hiding this comment

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

This unfortunately does break node identity at the moment, but it doesn't seem like there's any test depending on it at the moment. There's also not really a good way to fix this at the moment.

Copy link
Contributor

Choose a reason for hiding this comment

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

We should also be broadcasting source nodes for dependent changes, but that's trickier because we still need to preserve the mapping in that case. Again, not really a good way to fix that at the moment.

Copy link
Member

Choose a reason for hiding this comment

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

Oops, looks like I've merged without enough checks 😅

Copy link
Contributor

Choose a reason for hiding this comment

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

It's fiiiine 😆

Merged via the queue into rust-lang:master with commit 3ee3221 Nov 28, 2025
15 checks passed
@rustbot rustbot removed the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Nov 28, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

convert tuple struct to named struct fails to produce field name in some cases

4 participants