Skip to content

Commit 73ca1d4

Browse files
Merge pull request #22310 from Shourya742/2026-05-07-remove-make-mut
Remove make mut
2 parents 804bcd8 + f627fc7 commit 73ca1d4

7 files changed

Lines changed: 103 additions & 117 deletions

File tree

crates/ide-assists/src/handlers/generate_delegate_methods.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,6 @@ pub(crate) fn generate_delegate_methods(
213213
let impl_def = impl_def.indent(indent);
214214

215215
// Insert the impl block.
216-
let strukt = edit.make_mut(strukt.clone());
217216
editor.insert_all(
218217
Position::after(strukt.syntax()),
219218
vec![

crates/ide-assists/src/handlers/remove_unused_imports.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -111,19 +111,24 @@ pub(crate) fn remove_unused_imports(acc: &mut Assists, ctx: &AssistContext<'_, '
111111
is_path_per_ns_unused_in_scope(ctx, &u, scope, &res).then_some(u)
112112
}
113113
})
114-
.peekable();
114+
.collect::<Vec<_>>();
115115

116-
// Peek so we terminate early if an unused use is found. Only do the rest of the work if the user selects the assist.
117-
if unused.peek().is_some() {
116+
// Terminate early unless an unused use is found. Only do the rest of the work if the user selects the assist.
117+
if !unused.is_empty() {
118118
acc.add(
119119
AssistId::quick_fix("remove_unused_imports"),
120120
"Remove all unused imports",
121121
selected_el.text_range(),
122122
|builder| {
123-
let unused: Vec<ast::UseTree> = unused.map(|x| builder.make_mut(x)).collect();
124-
for node in unused {
125-
node.remove_recursive();
123+
let editor = builder.make_editor(&selected_el);
124+
unused.sort_by_key(|use_tree| use_tree.syntax().text_range().start());
125+
for node in &unused {
126+
editor.delete(node.syntax());
126127
}
128+
for node in unused.iter().cloned() {
129+
node.remove_recursive(&editor);
130+
}
131+
builder.add_file_edits(ctx.vfs_file_id(), editor);
127132
},
128133
)
129134
} else {

crates/ide-db/src/imports/merge_imports.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -256,10 +256,7 @@ pub fn try_normalize_import(use_item: &ast::Use, style: NormalizationStyle) -> O
256256
Some(use_item)
257257
}
258258

259-
pub fn try_normalize_use_tree_mut(
260-
use_tree: &ast::UseTree,
261-
style: NormalizationStyle,
262-
) -> Option<()> {
259+
fn try_normalize_use_tree_mut(use_tree: &ast::UseTree, style: NormalizationStyle) -> Option<()> {
263260
if style == NormalizationStyle::One {
264261
let mut modified = false;
265262
modified |= use_tree.wrap_in_tree_list().is_some();

crates/ide-db/src/source_change.rs

Lines changed: 1 addition & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
66
use std::{collections::hash_map::Entry, fmt, iter, mem};
77

8-
use crate::imports::insert_use::{ImportScope, ImportScopeKind};
98
use crate::text_edit::{TextEdit, TextEditBuilder};
109
use crate::{SnippetCap, assists::Command, syntax_helpers::tree_diff::diff};
1110
use base_db::AnchoredPathBuf;
@@ -16,7 +15,7 @@ use rustc_hash::FxHashMap;
1615
use span::FileId;
1716
use stdx::never;
1817
use syntax::{
19-
AstNode, SyntaxElement, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange, TextSize,
18+
AstNode, SyntaxElement, SyntaxNode, SyntaxToken, TextRange, TextSize,
2019
syntax_editor::{SyntaxAnnotation, SyntaxEditor},
2120
};
2221

@@ -245,23 +244,6 @@ pub struct SnippetBuilder {
245244
places: Vec<PlaceSnippet>,
246245
}
247246

248-
impl TreeMutator {
249-
fn new(immutable: &SyntaxNode) -> TreeMutator {
250-
let immutable = immutable.ancestors().last().unwrap();
251-
let mutable_clone = immutable.clone_for_update();
252-
TreeMutator { immutable, mutable_clone }
253-
}
254-
255-
fn make_mut<N: AstNode>(&self, node: &N) -> N {
256-
N::cast(self.make_syntax_mut(node.syntax())).unwrap()
257-
}
258-
259-
fn make_syntax_mut(&self, node: &SyntaxNode) -> SyntaxNode {
260-
let ptr = SyntaxNodePtr::new(node);
261-
ptr.to_node(&self.mutable_clone)
262-
}
263-
}
264-
265247
impl SourceChangeBuilder {
266248
pub fn new(file_id: impl Into<FileId>) -> SourceChangeBuilder {
267249
SourceChangeBuilder {
@@ -366,34 +348,6 @@ impl SourceChangeBuilder {
366348
}
367349
}
368350

369-
pub fn make_mut<N: AstNode>(&mut self, node: N) -> N {
370-
self.mutated_tree.get_or_insert_with(|| TreeMutator::new(node.syntax())).make_mut(&node)
371-
}
372-
373-
pub fn make_import_scope_mut(&mut self, scope: ImportScope) -> ImportScope {
374-
ImportScope {
375-
kind: match scope.kind.clone() {
376-
ImportScopeKind::File(it) => ImportScopeKind::File(self.make_mut(it)),
377-
ImportScopeKind::Module(it) => ImportScopeKind::Module(self.make_mut(it)),
378-
ImportScopeKind::Block(it) => ImportScopeKind::Block(self.make_mut(it)),
379-
},
380-
required_cfgs: scope.required_cfgs.iter().map(|it| self.make_mut(it.clone())).collect(),
381-
}
382-
}
383-
/// Returns a copy of the `node`, suitable for mutation.
384-
///
385-
/// Syntax trees in rust-analyzer are typically immutable, and mutating
386-
/// operations panic at runtime. However, it is possible to make a copy of
387-
/// the tree and mutate the copy freely. Mutation is based on interior
388-
/// mutability, and different nodes in the same tree see the same mutations.
389-
///
390-
/// The typical pattern for an assist is to find specific nodes in the read
391-
/// phase, and then get their mutable counterparts using `make_mut` in the
392-
/// mutable state.
393-
pub fn make_syntax_mut(&mut self, node: SyntaxNode) -> SyntaxNode {
394-
self.mutated_tree.get_or_insert_with(|| TreeMutator::new(&node)).make_syntax_mut(&node)
395-
}
396-
397351
/// Remove specified `range` of text.
398352
pub fn delete(&mut self, range: TextRange) {
399353
self.edit.delete(range)
@@ -434,12 +388,6 @@ impl SourceChangeBuilder {
434388
self.add_snippet(PlaceSnippet::Before(node.syntax().clone().into()));
435389
}
436390

437-
/// Adds a tabstop snippet to place the cursor after `node`
438-
pub fn add_tabstop_after(&mut self, _cap: SnippetCap, node: impl AstNode) {
439-
assert!(node.syntax().parent().is_some());
440-
self.add_snippet(PlaceSnippet::After(node.syntax().clone().into()));
441-
}
442-
443391
/// Adds a tabstop snippet to place the cursor before `token`
444392
pub fn add_tabstop_before_token(&mut self, _cap: SnippetCap, token: SyntaxToken) {
445393
assert!(token.parent().is_some());
@@ -458,23 +406,6 @@ impl SourceChangeBuilder {
458406
self.add_snippet(PlaceSnippet::Over(node.syntax().clone().into()))
459407
}
460408

461-
/// Adds a snippet to move the cursor selected over `token`
462-
pub fn add_placeholder_snippet_token(&mut self, _cap: SnippetCap, token: SyntaxToken) {
463-
assert!(token.parent().is_some());
464-
self.add_snippet(PlaceSnippet::Over(token.into()))
465-
}
466-
467-
/// Adds a snippet to move the cursor selected over `nodes`
468-
///
469-
/// This allows for renaming newly generated items without having to go
470-
/// through a separate rename step.
471-
pub fn add_placeholder_snippet_group(&mut self, _cap: SnippetCap, nodes: Vec<SyntaxNode>) {
472-
assert!(nodes.iter().all(|node| node.parent().is_some()));
473-
self.add_snippet(PlaceSnippet::OverGroup(
474-
nodes.into_iter().map(|node| node.into()).collect(),
475-
))
476-
}
477-
478409
fn add_snippet(&mut self, snippet: PlaceSnippet) {
479410
let snippet_builder = self.snippet_builder.get_or_insert(SnippetBuilder { places: vec![] });
480411
snippet_builder.places.push(snippet);
@@ -553,9 +484,6 @@ enum PlaceSnippet {
553484
After(SyntaxElement),
554485
/// Place a placeholder snippet in place of the element
555486
Over(SyntaxElement),
556-
/// Place a group of placeholder snippets which are linked together
557-
/// in place of the elements
558-
OverGroup(Vec<SyntaxElement>),
559487
}
560488

561489
impl PlaceSnippet {
@@ -564,9 +492,6 @@ impl PlaceSnippet {
564492
PlaceSnippet::Before(it) => vec![Snippet::Tabstop(it.text_range().start())],
565493
PlaceSnippet::After(it) => vec![Snippet::Tabstop(it.text_range().end())],
566494
PlaceSnippet::Over(it) => vec![Snippet::Placeholder(it.text_range())],
567-
PlaceSnippet::OverGroup(it) => {
568-
vec![Snippet::PlaceholderGroup(it.into_iter().map(|it| it.text_range()).collect())]
569-
}
570495
}
571496
}
572497
}

crates/syntax/src/ast/edit_in_place.rs

Lines changed: 45 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -56,24 +56,41 @@ impl Removable for ast::UseTree {
5656
}
5757

5858
impl ast::UseTree {
59+
/// Editor variant of UseTree remove
60+
fn remove_with_editor(&self, editor: &SyntaxEditor) {
61+
for dir in [Direction::Next, Direction::Prev] {
62+
if let Some(next_use_tree) = neighbor(self, dir) {
63+
let separators = self
64+
.syntax()
65+
.siblings_with_tokens(dir)
66+
.skip(1)
67+
.take_while(|it| it.as_node() != Some(next_use_tree.syntax()));
68+
for separator in separators {
69+
editor.delete(separator);
70+
}
71+
break;
72+
}
73+
}
74+
editor.delete(self.syntax());
75+
}
76+
5977
/// Deletes the usetree node represented by the input. Recursively removes parents, including use nodes that become empty.
60-
pub fn remove_recursive(self) {
78+
pub fn remove_recursive(self, editor: &SyntaxEditor) {
6179
let parent = self.syntax().parent();
6280

63-
self.remove();
64-
6581
if let Some(u) = parent.clone().and_then(ast::Use::cast) {
66-
if u.use_tree().is_none() {
67-
u.remove();
68-
}
82+
u.remove(editor);
6983
} else if let Some(u) = parent.and_then(ast::UseTreeList::cast) {
70-
if u.use_trees().next().is_none() {
71-
let parent = u.syntax().parent().and_then(ast::UseTree::cast);
72-
if let Some(u) = parent {
73-
u.remove_recursive();
74-
}
84+
if u.use_trees().nth(1).is_none()
85+
|| u.use_trees().all(|use_tree| {
86+
use_tree.syntax() == self.syntax() || editor.deleted(use_tree.syntax())
87+
})
88+
{
89+
u.parent_use_tree().remove_recursive(editor);
90+
return;
7591
}
76-
u.remove_unnecessary_braces();
92+
self.remove_with_editor(editor);
93+
u.remove_unnecessary_braces(editor);
7794
}
7895
}
7996

@@ -224,8 +241,9 @@ impl ast::UseTreeList {
224241
}
225242
}
226243

227-
impl Removable for ast::Use {
228-
fn remove(&self) {
244+
impl ast::Use {
245+
fn remove(&self, editor: &SyntaxEditor) {
246+
let make = editor.make();
229247
let next_ws = self
230248
.syntax()
231249
.next_sibling_or_token()
@@ -234,10 +252,17 @@ impl Removable for ast::Use {
234252
if let Some(next_ws) = next_ws {
235253
let ws_text = next_ws.syntax().text();
236254
if let Some(rest) = ws_text.strip_prefix('\n') {
237-
if rest.is_empty() {
238-
ted::remove(next_ws.syntax());
255+
let next_use_removed = next_ws
256+
.syntax()
257+
.next_sibling_or_token()
258+
.and_then(|it| it.into_node())
259+
.and_then(ast::Use::cast)
260+
.and_then(|use_| use_.use_tree())
261+
.is_some_and(|use_tree| editor.deleted(use_tree.syntax()));
262+
if rest.is_empty() || next_use_removed {
263+
editor.delete(next_ws.syntax());
239264
} else {
240-
ted::replace(next_ws.syntax(), make::tokens::whitespace(rest));
265+
editor.replace(next_ws.syntax(), make.whitespace(rest));
241266
}
242267
}
243268
}
@@ -251,13 +276,13 @@ impl Removable for ast::Use {
251276
let prev_newline = ws_text.rfind('\n').map(|x| x + 1).unwrap_or(0);
252277
let rest = &ws_text[0..prev_newline];
253278
if rest.is_empty() {
254-
ted::remove(prev_ws.syntax());
279+
editor.delete(prev_ws.syntax());
255280
} else {
256-
ted::replace(prev_ws.syntax(), make::tokens::whitespace(rest));
281+
editor.replace(prev_ws.syntax(), make.whitespace(rest));
257282
}
258283
}
259284

260-
ted::remove(self.syntax());
285+
editor.delete(self.syntax());
261286
}
262287
}
263288

crates/syntax/src/ast/node_ext.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use crate::{
1616
self, AstNode, AstToken, HasAttrs, HasGenericArgs, HasGenericParams, HasName,
1717
HasTypeBounds, SyntaxNode, support,
1818
},
19-
ted,
19+
syntax_editor::SyntaxEditor,
2020
};
2121

2222
use super::{GenericParam, RangeItem, RangeOp};
@@ -454,11 +454,12 @@ impl ast::UseTreeList {
454454
}
455455

456456
/// Remove the unnecessary braces in current `UseTreeList`
457-
pub fn remove_unnecessary_braces(mut self) {
457+
pub fn remove_unnecessary_braces(mut self, editor: &SyntaxEditor) {
458458
// Returns true iff there is a single subtree and it is not the self keyword. The braces in
459459
// `use x::{self};` are necessary and so we should not remove them.
460460
let has_single_subtree_that_is_not_self = |u: &ast::UseTreeList| {
461-
if let Some((single_subtree,)) = u.use_trees().collect_tuple() {
461+
let use_trees = u.use_trees().filter(|use_tree| !editor.deleted(use_tree.syntax()));
462+
if let Some((single_subtree,)) = use_trees.collect_tuple() {
462463
// We have a single subtree, check whether it is self.
463464

464465
let is_self = single_subtree.path().as_ref().is_some_and(|path| {
@@ -476,12 +477,12 @@ impl ast::UseTreeList {
476477
let remove_brace_in_use_tree_list = |u: &ast::UseTreeList| {
477478
if has_single_subtree_that_is_not_self(u) {
478479
if let Some(a) = u.l_curly_token() {
479-
ted::remove(a)
480+
editor.delete(a)
480481
}
481482
if let Some(a) = u.r_curly_token() {
482-
ted::remove(a)
483+
editor.delete(a)
483484
}
484-
u.comma().for_each(ted::remove);
485+
u.comma().for_each(|u| editor.delete(u));
485486
}
486487
};
487488

crates/syntax/src/syntax_editor.rs

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,19 @@ impl SyntaxEditor {
133133
!matches!(&element, SyntaxElement::Node(node) if node == &self.root),
134134
"should not delete root node"
135135
);
136-
self.changes.borrow_mut().push(Change::Replace(element.syntax_element(), None));
136+
let mut changes = self.changes.borrow_mut();
137+
for change in changes.iter_mut() {
138+
if let Change::Replace(existing, replacement) = change
139+
&& *existing == element
140+
{
141+
if replacement.is_none() {
142+
return;
143+
}
144+
*replacement = None;
145+
return;
146+
}
147+
}
148+
changes.push(Change::Replace(element, None));
137149
}
138150

139151
pub fn delete_all(&self, range: RangeInclusive<SyntaxElement>) {
@@ -149,9 +161,23 @@ impl SyntaxEditor {
149161
pub fn replace(&self, old: impl Element, new: impl Element) {
150162
let old = old.syntax_element();
151163
debug_assert!(is_ancestor_or_self_of_element(&old, &self.root));
152-
self.changes
153-
.borrow_mut()
154-
.push(Change::Replace(old.syntax_element(), Some(new.syntax_element())));
164+
let new = new.syntax_element();
165+
let mut changes = self.changes.borrow_mut();
166+
for change in changes.iter_mut() {
167+
if let Change::Replace(existing, replacement) = change
168+
&& *existing == old
169+
{
170+
match replacement {
171+
None => return,
172+
Some(existing_new) if *existing_new == new => return,
173+
Some(existing_new) => {
174+
*existing_new = new;
175+
return;
176+
}
177+
}
178+
}
179+
}
180+
changes.push(Change::Replace(old, Some(new)));
155181
}
156182

157183
pub fn replace_with_many(&self, old: impl Element, new: Vec<SyntaxElement>) {
@@ -177,6 +203,14 @@ impl SyntaxEditor {
177203
pub fn finish(self) -> SyntaxEdit {
178204
edit_algo::apply_edits(self)
179205
}
206+
207+
pub fn deleted(&self, element: impl Element) -> bool {
208+
let element = element.syntax_element();
209+
self.changes
210+
.borrow()
211+
.iter()
212+
.any(|change| matches!(change, Change::Replace(existing, None) if *existing == element))
213+
}
180214
}
181215

182216
/// Represents a completed [`SyntaxEditor`] operation.

0 commit comments

Comments
 (0)