Skip to content

Commit 848e6aa

Browse files
authored
Merge pull request #22081 from A4-Tacks/migrate-loop-iter-for
internal: Migrate `convert_iter_for_each_to_for` assist to SyntaxEditor
2 parents fba71bb + 6dea98c commit 848e6aa

3 files changed

Lines changed: 48 additions & 27 deletions

File tree

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

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
use hir::{Name, sym};
22
use ide_db::famous_defs::FamousDefs;
3-
use stdx::format_to;
43
use syntax::{
54
AstNode,
65
ast::{self, HasArgList, HasLoopBody, edit::AstNodeEdit},
76
};
87

9-
use crate::{AssistContext, AssistId, Assists};
8+
use crate::{AssistContext, AssistId, Assists, utils::wrap_paren};
109

1110
// Assist: convert_iter_for_each_to_for
1211
//
@@ -115,32 +114,40 @@ pub(crate) fn convert_for_loop_with_for_each(
115114
"Replace this for loop with `Iterator::for_each`",
116115
for_loop.syntax().text_range(),
117116
|builder| {
118-
let mut buf = String::new();
117+
let editor = builder.make_editor(for_loop.syntax());
118+
let make = editor.make();
119+
120+
let mut receiver = iterable.clone();
119121

120-
if let Some((expr_behind_ref, method, krate)) =
122+
let iter_method = if let Some((expr_behind_ref, method, krate)) =
121123
is_ref_and_impls_iter_method(&ctx.sema, &iterable)
122124
{
125+
receiver = expr_behind_ref;
123126
// We have either "for x in &col" and col implements a method called iter
124127
// or "for x in &mut col" and col implements a method called iter_mut
125-
format_to!(
126-
buf,
127-
"{expr_behind_ref}.{}()",
128-
method.display(ctx.db(), krate.edition(ctx.db()))
129-
);
130-
} else if let ast::Expr::RangeExpr(..) = iterable {
131-
// range expressions need to be parenthesized for the syntax to be correct
132-
format_to!(buf, "({iterable})");
133-
} else if impls_core_iter(&ctx.sema, &iterable) {
134-
format_to!(buf, "{iterable}");
135-
} else if let ast::Expr::RefExpr(_) = iterable {
136-
format_to!(buf, "({iterable}).into_iter()");
128+
method.display(ctx.db(), krate.edition(ctx.db())).to_string()
137129
} else {
138-
format_to!(buf, "{iterable}.into_iter()");
130+
"into_iter".to_owned()
131+
};
132+
133+
receiver = wrap_paren(receiver, make, ast::prec::ExprPrecedence::Postfix);
134+
135+
if !impls_core_iter(&ctx.sema, &iterable) {
136+
receiver = make
137+
.expr_method_call(receiver, make.name_ref(&iter_method), make.arg_list([]))
138+
.into();
139139
}
140140

141-
format_to!(buf, ".for_each(|{pat}| {body});");
141+
let loop_arg = make.expr_closure([make.untyped_param(pat)], body.into());
142+
let for_each = make.expr_method_call(
143+
receiver,
144+
make.name_ref("for_each"),
145+
make.arg_list([loop_arg.into()]),
146+
);
147+
let for_each = make.expr_stmt(for_each.into());
142148

143-
builder.replace(for_loop.syntax().text_range(), buf)
149+
editor.replace(for_loop.syntax(), for_each.syntax());
150+
builder.add_file_edits(ctx.vfs_file_id(), editor);
144151
},
145152
)
146153
}

crates/ide-db/src/source_change.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -285,11 +285,11 @@ impl SourceChangeBuilder {
285285
SyntaxEditor::new(node.ancestors().last().unwrap_or_else(|| node.clone())).0
286286
}
287287

288-
pub fn add_file_edits(&mut self, file_id: impl Into<FileId>, edit: SyntaxEditor) {
288+
pub fn add_file_edits(&mut self, file_id: impl Into<FileId>, editor: SyntaxEditor) {
289289
match self.file_editors.entry(file_id.into()) {
290-
Entry::Occupied(mut entry) => entry.get_mut().merge(edit),
290+
Entry::Occupied(mut entry) => entry.get_mut().merge(editor),
291291
Entry::Vacant(entry) => {
292-
entry.insert(edit);
292+
entry.insert(editor);
293293
}
294294
}
295295
}

crates/syntax/src/ast/syntax_factory/constructors.rs

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,18 @@ impl SyntaxFactory {
193193
ast
194194
}
195195

196+
pub fn untyped_param(&self, pat: ast::Pat) -> ast::Param {
197+
let ast = make::untyped_param(pat.clone()).clone_for_update();
198+
199+
if let Some(mut mapping) = self.mappings() {
200+
let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
201+
builder.map_node(pat.syntax().clone(), ast.pat().unwrap().syntax().clone());
202+
builder.finish(&mut mapping);
203+
}
204+
205+
ast
206+
}
207+
196208
pub fn ty_fn_ptr<I: Iterator<Item = Param>>(
197209
&self,
198210
is_unsafe: bool,
@@ -1088,13 +1100,15 @@ impl SyntaxFactory {
10881100
let ast = make::expr_closure(args, expr.clone()).clone_for_update();
10891101

10901102
if let Some(mut mapping) = self.mappings() {
1091-
let mut builder = SyntaxMappingBuilder::new(ast.syntax.clone());
1092-
builder.map_children(
1093-
input,
1094-
ast.param_list().unwrap().params().map(|param| param.syntax().clone()),
1095-
);
1103+
let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
10961104
builder.map_node(expr.syntax().clone(), ast.body().unwrap().syntax().clone());
10971105
builder.finish(&mut mapping);
1106+
1107+
let param_list = ast.param_list().unwrap();
1108+
let mut params_builder = SyntaxMappingBuilder::new(param_list.syntax().clone());
1109+
params_builder
1110+
.map_children(input, param_list.params().map(|param| param.syntax().clone()));
1111+
params_builder.finish(&mut mapping);
10981112
}
10991113

11001114
ast

0 commit comments

Comments
 (0)