Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs53
-rw-r--r--crates/syntax/src/syntax_editor.rs54
-rw-r--r--crates/syntax/src/syntax_editor/edit_algo.rs41
3 files changed, 148 insertions, 0 deletions
diff --git a/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs b/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs
index 61d844928a..f8b9bb68db 100644
--- a/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs
+++ b/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs
@@ -1197,4 +1197,57 @@ fn foo() {
"#,
);
}
+
+ #[test]
+ fn regression_issue_21020() {
+ check_assist(
+ convert_tuple_struct_to_named_struct,
+ r#"
+pub struct S$0(pub ());
+
+trait T {
+ fn id(&self) -> usize;
+}
+
+trait T2 {
+ fn foo(&self) -> usize;
+}
+
+impl T for S {
+ fn id(&self) -> usize {
+ self.0.len()
+ }
+}
+
+impl T2 for S {
+ fn foo(&self) -> usize {
+ self.0.len()
+ }
+}
+ "#,
+ r#"
+pub struct S { pub field1: () }
+
+trait T {
+ fn id(&self) -> usize;
+}
+
+trait T2 {
+ fn foo(&self) -> usize;
+}
+
+impl T for S {
+ fn id(&self) -> usize {
+ self.field1.len()
+ }
+}
+
+impl T2 for S {
+ fn foo(&self) -> usize {
+ self.field1.len()
+ }
+}
+ "#,
+ );
+ }
}
diff --git a/crates/syntax/src/syntax_editor.rs b/crates/syntax/src/syntax_editor.rs
index 0b358878fc..5683d891be 100644
--- a/crates/syntax/src/syntax_editor.rs
+++ b/crates/syntax/src/syntax_editor.rs
@@ -653,4 +653,58 @@ mod tests {
let expect = expect![["fn it() {\n \n}"]];
expect.assert_eq(&edit.new_root.to_string());
}
+
+ #[test]
+ fn test_more_times_replace_node_to_mutable_token() {
+ 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::token(parser::SyntaxKind::UNDERSCORE);
+
+ for arg in arg_list.args() {
+ editor.replace(arg.syntax(), &target_expr);
+ }
+
+ let edit = editor.finish();
+
+ let expect = expect![["(_, _)"]];
+ expect.assert_eq(&edit.new_root.to_string());
+ }
+
+ #[test]
+ fn test_more_times_replace_node_to_mutable() {
+ 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());
+ }
+
+ #[test]
+ fn test_more_times_insert_node_to_mutable() {
+ 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::ext::expr_unit().clone_for_update();
+
+ for arg in arg_list.args() {
+ editor.insert(Position::before(arg.syntax()), target_expr.syntax());
+ }
+
+ let edit = editor.finish();
+
+ let expect = expect![["(()1, ()2)"]];
+ expect.assert_eq(&edit.new_root.to_string());
+ }
}
diff --git a/crates/syntax/src/syntax_editor/edit_algo.rs b/crates/syntax/src/syntax_editor/edit_algo.rs
index 01c1f0d49b..e697d97061 100644
--- a/crates/syntax/src/syntax_editor/edit_algo.rs
+++ b/crates/syntax/src/syntax_editor/edit_algo.rs
@@ -150,6 +150,35 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit {
// Map change targets to the correct syntax nodes
let tree_mutator = TreeMutator::new(&root);
let mut changed_elements = vec![];
+ let mut changed_elements_set = rustc_hash::FxHashSet::default();
+ let mut deduplicate_node = |node_or_token: &mut SyntaxElement| {
+ let node;
+ let node = match node_or_token {
+ SyntaxElement::Token(token) => match token.parent() {
+ None => return,
+ Some(parent) => {
+ node = parent;
+ &node
+ }
+ },
+ SyntaxElement::Node(node) => node,
+ };
+ if changed_elements_set.contains(node) {
+ let new_node = node.clone_subtree().clone_for_update();
+ match node_or_token {
+ SyntaxElement::Node(node) => *node = new_node,
+ SyntaxElement::Token(token) => {
+ *token = new_node
+ .children_with_tokens()
+ .filter_map(SyntaxElement::into_token)
+ .find(|it| it.kind() == token.kind() && it.text() == token.text())
+ .unwrap();
+ }
+ }
+ } else {
+ changed_elements_set.insert(node.clone());
+ }
+ };
for index in independent_changes {
match &mut changes[index as usize] {
@@ -180,6 +209,18 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit {
}
}
+ match &mut changes[index as usize] {
+ Change::Insert(_, element) | Change::Replace(_, Some(element)) => {
+ deduplicate_node(element);
+ }
+ Change::InsertAll(_, elements)
+ | Change::ReplaceWithMany(_, elements)
+ | Change::ReplaceAll(_, elements) => {
+ elements.iter_mut().for_each(&mut deduplicate_node);
+ }
+ Change::Replace(_, None) => (),
+ }
+
// Collect changed elements
match &changes[index as usize] {
Change::Insert(_, element) => changed_elements.push(element.clone()),