Skip to content

Commit 728fbf8

Browse files
committed
internal: Migrate convert_iter_for_each_to_for assist to SyntaxEditor
1 parent 67d17fa commit 728fbf8

2 files changed

Lines changed: 38 additions & 19 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/syntax/src/ast/syntax_factory/constructors.rs

Lines changed: 12 additions & 0 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,

0 commit comments

Comments
 (0)