2 use ide_db::famous_defs::FamousDefs;
5 ast::{self, edit_in_place::Indent, make, HasArgList, HasLoopBody},
9 use crate::{AssistContext, AssistId, AssistKind, Assists};
11 // Assist: convert_iter_for_each_to_for
13 // Converts an Iterator::for_each function into a for loop.
16 // # //- minicore: iterators
19 // let iter = iter::repeat((9, 2));
20 // iter.for_each$0(|(x, y)| {
21 // println!("x: {}, y: {}", x, y);
29 // let iter = iter::repeat((9, 2));
30 // for (x, y) in iter {
31 // println!("x: {}, y: {}", x, y);
35 pub(crate) fn convert_iter_for_each_to_for(
37 ctx: &AssistContext<'_>,
39 let method = ctx.find_node_at_offset::<ast::MethodCallExpr>()?;
41 let closure = match method.arg_list()?.args().next()? {
42 ast::Expr::ClosureExpr(expr) => expr,
46 let (method, receiver) = validate_method_call_expr(ctx, method)?;
48 let param_list = closure.param_list()?;
49 let param = param_list.params().next()?.pat()?;
50 let body = closure.body()?;
52 let stmt = method.syntax().parent().and_then(ast::ExprStmt::cast);
53 let range = stmt.as_ref().map_or(method.syntax(), AstNode::syntax).text_range();
56 AssistId("convert_iter_for_each_to_for", AssistKind::RefactorRewrite),
57 "Replace this `Iterator::for_each` with a for loop",
61 stmt.as_ref().map_or_else(|| method.indent_level(), ast::ExprStmt::indent_level);
63 let block = match body {
64 ast::Expr::BlockExpr(block) => block,
65 _ => make::block_expr(Vec::new(), Some(body)),
68 block.reindent_to(indent);
70 let expr_for_loop = make::expr_for_loop(param, receiver, block);
71 builder.replace(range, expr_for_loop.to_string())
76 // Assist: convert_for_loop_with_for_each
78 // Converts a for loop into a for_each loop on the Iterator.
82 // let x = vec![1, 2, 3];
91 // let x = vec![1, 2, 3];
92 // x.into_iter().for_each(|v| {
97 pub(crate) fn convert_for_loop_with_for_each(
99 ctx: &AssistContext<'_>,
101 let for_loop = ctx.find_node_at_offset::<ast::ForExpr>()?;
102 let iterable = for_loop.iterable()?;
103 let pat = for_loop.pat()?;
104 let body = for_loop.loop_body()?;
105 if body.syntax().text_range().start() < ctx.offset() {
106 cov_mark::hit!(not_available_in_body);
111 AssistId("convert_for_loop_with_for_each", AssistKind::RefactorRewrite),
112 "Replace this for loop with `Iterator::for_each`",
113 for_loop.syntax().text_range(),
115 let mut buf = String::new();
117 if let Some((expr_behind_ref, method)) =
118 is_ref_and_impls_iter_method(&ctx.sema, &iterable)
120 // We have either "for x in &col" and col implements a method called iter
121 // or "for x in &mut col" and col implements a method called iter_mut
122 format_to!(buf, "{}.{}()", expr_behind_ref, method);
123 } else if let ast::Expr::RangeExpr(..) = iterable {
124 // range expressions need to be parenthesized for the syntax to be correct
125 format_to!(buf, "({})", iterable);
126 } else if impls_core_iter(&ctx.sema, &iterable) {
127 format_to!(buf, "{}", iterable);
128 } else if let ast::Expr::RefExpr(_) = iterable {
129 format_to!(buf, "({}).into_iter()", iterable);
131 format_to!(buf, "{}.into_iter()", iterable);
134 format_to!(buf, ".for_each(|{}| {});", pat, body);
136 builder.replace(for_loop.syntax().text_range(), buf)
141 /// If iterable is a reference where the expression behind the reference implements a method
142 /// returning an Iterator called iter or iter_mut (depending on the type of reference) then return
143 /// the expression behind the reference and the method name
144 fn is_ref_and_impls_iter_method(
145 sema: &hir::Semantics<'_, ide_db::RootDatabase>,
146 iterable: &ast::Expr,
147 ) -> Option<(ast::Expr, hir::Name)> {
148 let ref_expr = match iterable {
149 ast::Expr::RefExpr(r) => r,
152 let wanted_method = if ref_expr.mut_token().is_some() { known::iter_mut } else { known::iter };
153 let expr_behind_ref = ref_expr.expr()?;
154 let ty = sema.type_of_expr(&expr_behind_ref)?.adjusted();
155 let scope = sema.scope(iterable.syntax())?;
156 let krate = scope.krate();
157 let iter_trait = FamousDefs(sema, krate).core_iter_Iterator()?;
159 let has_wanted_method = ty
160 .iterate_method_candidates(
163 &scope.visible_traits().0,
165 Some(&wanted_method),
167 if func.ret_type(sema.db).impls_trait(sema.db, iter_trait, &[]) {
174 if !has_wanted_method {
178 Some((expr_behind_ref, wanted_method))
181 /// Whether iterable implements core::Iterator
182 fn impls_core_iter(sema: &hir::Semantics<'_, ide_db::RootDatabase>, iterable: &ast::Expr) -> bool {
184 let it_typ = sema.type_of_expr(iterable)?.adjusted();
186 let module = sema.scope(iterable.syntax())?.module();
188 let krate = module.krate();
189 let iter_trait = FamousDefs(sema, krate).core_iter_Iterator()?;
190 cov_mark::hit!(test_already_impls_iterator);
191 Some(it_typ.impls_trait(sema.db, iter_trait, &[]))
196 fn validate_method_call_expr(
197 ctx: &AssistContext<'_>,
198 expr: ast::MethodCallExpr,
199 ) -> Option<(ast::Expr, ast::Expr)> {
200 let name_ref = expr.name_ref()?;
201 if !name_ref.syntax().text_range().contains_range(ctx.selection_trimmed()) {
202 cov_mark::hit!(test_for_each_not_applicable_invalid_cursor_pos);
205 if name_ref.text() != "for_each" {
209 let sema = &ctx.sema;
211 let receiver = expr.receiver()?;
212 let expr = ast::Expr::MethodCallExpr(expr);
214 let it_type = sema.type_of_expr(&receiver)?.adjusted();
215 let module = sema.scope(receiver.syntax())?.module();
216 let krate = module.krate();
218 let iter_trait = FamousDefs(sema, krate).core_iter_Iterator()?;
219 it_type.impls_trait(sema.db, iter_trait, &[]).then(|| (expr, receiver))
224 use crate::tests::{check_assist, check_assist_not_applicable};
229 fn test_for_each_in_method_stmt() {
231 convert_iter_for_each_to_for,
233 //- minicore: iterators
235 let it = core::iter::repeat(92);
236 it.$0for_each(|(x, y)| {
237 println!("x: {}, y: {}", x, y);
243 let it = core::iter::repeat(92);
245 println!("x: {}, y: {}", x, y);
253 fn test_for_each_in_method() {
255 convert_iter_for_each_to_for,
257 //- minicore: iterators
259 let it = core::iter::repeat(92);
260 it.$0for_each(|(x, y)| {
261 println!("x: {}, y: {}", x, y);
267 let it = core::iter::repeat(92);
269 println!("x: {}, y: {}", x, y);
277 fn test_for_each_without_braces_stmt() {
279 convert_iter_for_each_to_for,
281 //- minicore: iterators
283 let it = core::iter::repeat(92);
284 it.$0for_each(|(x, y)| println!("x: {}, y: {}", x, y));
289 let it = core::iter::repeat(92);
291 println!("x: {}, y: {}", x, y)
299 fn test_for_each_not_applicable() {
300 check_assist_not_applicable(
301 convert_iter_for_each_to_for,
303 //- minicore: iterators
305 ().$0for_each(|x| println!("{}", x));
311 fn test_for_each_not_applicable_invalid_cursor_pos() {
312 cov_mark::check!(test_for_each_not_applicable_invalid_cursor_pos);
313 check_assist_not_applicable(
314 convert_iter_for_each_to_for,
316 //- minicore: iterators
318 core::iter::repeat(92).for_each(|(x, y)| $0println!("x: {}, y: {}", x, y));
324 fn each_to_for_not_for() {
325 check_assist_not_applicable(
326 convert_for_loop_with_for_each,
328 let mut x = vec![1, 2, 3];
329 x.iter_mut().$0for_each(|v| *v *= 2);
335 fn each_to_for_simple_for() {
337 convert_for_loop_with_for_each,
340 let x = vec![1, 2, 3];
347 let x = vec![1, 2, 3];
348 x.into_iter().for_each(|v| {
356 fn each_to_for_for_in_range() {
358 convert_for_loop_with_for_each,
360 //- minicore: range, iterators
361 impl<T> core::iter::Iterator for core::ops::Range<T> {
364 fn next(&mut self) -> Option<Self::Item> {
375 impl<T> core::iter::Iterator for core::ops::Range<T> {
378 fn next(&mut self) -> Option<Self::Item> {
384 (0..92).for_each(|x| {
392 fn each_to_for_not_available_in_body() {
393 cov_mark::check!(not_available_in_body);
394 check_assist_not_applicable(
395 convert_for_loop_with_for_each,
398 let x = vec![1, 2, 3];
407 fn each_to_for_for_borrowed() {
409 convert_for_loop_with_for_each,
411 //- minicore: iterators
412 use core::iter::{Repeat, repeat};
416 fn iter(&self) -> Repeat<i32> { repeat(92) }
417 fn iter_mut(&mut self) -> Repeat<i32> { repeat(92) }
428 use core::iter::{Repeat, repeat};
432 fn iter(&self) -> Repeat<i32> { repeat(92) }
433 fn iter_mut(&mut self) -> Repeat<i32> { repeat(92) }
438 x.iter().for_each(|v| {
447 fn each_to_for_for_borrowed_no_iter_method() {
449 convert_for_loop_with_for_each,
453 let x = NoIterMethod;
462 let x = NoIterMethod;
463 (&x).into_iter().for_each(|v| {
472 fn each_to_for_for_borrowed_mut() {
474 convert_for_loop_with_for_each,
476 //- minicore: iterators
477 use core::iter::{Repeat, repeat};
481 fn iter(&self) -> Repeat<i32> { repeat(92) }
482 fn iter_mut(&mut self) -> Repeat<i32> { repeat(92) }
493 use core::iter::{Repeat, repeat};
497 fn iter(&self) -> Repeat<i32> { repeat(92) }
498 fn iter_mut(&mut self) -> Repeat<i32> { repeat(92) }
503 x.iter_mut().for_each(|v| {
512 fn each_to_for_for_borrowed_mut_behind_var() {
514 convert_for_loop_with_for_each,
517 let x = vec![1, 2, 3];
525 let x = vec![1, 2, 3];
527 y.into_iter().for_each(|v| {
535 fn each_to_for_already_impls_iterator() {
536 cov_mark::check!(test_already_impls_iterator);
538 convert_for_loop_with_for_each,
540 //- minicore: iterators
542 for$0 a in core::iter::repeat(92).take(1) {
549 core::iter::repeat(92).take(1).for_each(|a| {