2 use ide_db::helpers::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(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
36 let method = ctx.find_node_at_offset::<ast::MethodCallExpr>()?;
38 let closure = match method.arg_list()?.args().next()? {
39 ast::Expr::ClosureExpr(expr) => expr,
43 let (method, receiver) = validate_method_call_expr(ctx, method)?;
45 let param_list = closure.param_list()?;
46 let param = param_list.params().next()?.pat()?;
47 let body = closure.body()?;
49 let stmt = method.syntax().parent().and_then(ast::ExprStmt::cast);
50 let range = stmt.as_ref().map_or(method.syntax(), AstNode::syntax).text_range();
53 AssistId("convert_iter_for_each_to_for", AssistKind::RefactorRewrite),
54 "Replace this `Iterator::for_each` with a for loop",
58 stmt.as_ref().map_or_else(|| method.indent_level(), ast::ExprStmt::indent_level);
60 let block = match body {
61 ast::Expr::BlockExpr(block) => block,
62 _ => make::block_expr(Vec::new(), Some(body)),
65 block.reindent_to(indent);
67 let expr_for_loop = make::expr_for_loop(param, receiver, block);
68 builder.replace(range, expr_for_loop.to_string())
73 // Assist: convert_for_loop_with_for_each
75 // Converts a for loop into a for_each loop on the Iterator.
79 // let x = vec![1, 2, 3];
88 // let x = vec![1, 2, 3];
89 // x.into_iter().for_each(|v| {
94 pub(crate) fn convert_for_loop_with_for_each(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
95 let for_loop = ctx.find_node_at_offset::<ast::ForExpr>()?;
96 let iterable = for_loop.iterable()?;
97 let pat = for_loop.pat()?;
98 let body = for_loop.loop_body()?;
99 if body.syntax().text_range().start() < ctx.offset() {
100 cov_mark::hit!(not_available_in_body);
105 AssistId("convert_for_loop_with_for_each", AssistKind::RefactorRewrite),
106 "Replace this for loop with `Iterator::for_each`",
107 for_loop.syntax().text_range(),
109 let mut buf = String::new();
111 if let Some((expr_behind_ref, method)) =
112 is_ref_and_impls_iter_method(&ctx.sema, &iterable)
114 // We have either "for x in &col" and col implements a method called iter
115 // or "for x in &mut col" and col implements a method called iter_mut
116 format_to!(buf, "{}.{}()", expr_behind_ref, method);
117 } else if let ast::Expr::RangeExpr(..) = iterable {
118 // range expressions need to be parenthesized for the syntax to be correct
119 format_to!(buf, "({})", iterable);
120 } else if impls_core_iter(&ctx.sema, &iterable) {
121 format_to!(buf, "{}", iterable);
122 } else if let ast::Expr::RefExpr(_) = iterable {
123 format_to!(buf, "({}).into_iter()", iterable);
125 format_to!(buf, "{}.into_iter()", iterable);
128 format_to!(buf, ".for_each(|{}| {});", pat, body);
130 builder.replace(for_loop.syntax().text_range(), buf)
135 /// If iterable is a reference where the expression behind the reference implements a method
136 /// returning an Iterator called iter or iter_mut (depending on the type of reference) then return
137 /// the expression behind the reference and the method name
138 fn is_ref_and_impls_iter_method(
139 sema: &hir::Semantics<ide_db::RootDatabase>,
140 iterable: &ast::Expr,
141 ) -> Option<(ast::Expr, hir::Name)> {
142 let ref_expr = match iterable {
143 ast::Expr::RefExpr(r) => r,
146 let wanted_method = if ref_expr.mut_token().is_some() { known::iter_mut } else { known::iter };
147 let expr_behind_ref = ref_expr.expr()?;
148 let ty = sema.type_of_expr(&expr_behind_ref)?.adjusted();
149 let scope = sema.scope(iterable.syntax());
150 let krate = scope.module()?.krate();
151 let traits_in_scope = scope.visible_traits();
152 let iter_trait = FamousDefs(sema, Some(krate)).core_iter_Iterator()?;
154 let has_wanted_method = ty
155 .iterate_method_candidates(
159 Some(&wanted_method),
161 if func.ret_type(sema.db).impls_trait(sema.db, iter_trait, &[]) {
168 if !has_wanted_method {
172 Some((expr_behind_ref, wanted_method))
175 /// Whether iterable implements core::Iterator
176 fn impls_core_iter(sema: &hir::Semantics<ide_db::RootDatabase>, iterable: &ast::Expr) -> bool {
177 let it_typ = match sema.type_of_expr(iterable) {
178 Some(it) => it.adjusted(),
179 None => return false,
182 let module = match sema.scope(iterable.syntax()).module() {
184 None => return false,
187 let krate = module.krate();
188 match FamousDefs(sema, Some(krate)).core_iter_Iterator() {
189 Some(iter_trait) => {
190 cov_mark::hit!(test_already_impls_iterator);
191 it_typ.impls_trait(sema.db, iter_trait, &[])
197 fn validate_method_call_expr(
199 expr: ast::MethodCallExpr,
200 ) -> Option<(ast::Expr, ast::Expr)> {
201 let name_ref = expr.name_ref()?;
202 if !name_ref.syntax().text_range().contains_range(ctx.selection_trimmed()) {
203 cov_mark::hit!(test_for_each_not_applicable_invalid_cursor_pos);
206 if name_ref.text() != "for_each" {
210 let sema = &ctx.sema;
212 let receiver = expr.receiver()?;
213 let expr = ast::Expr::MethodCallExpr(expr);
215 let it_type = sema.type_of_expr(&receiver)?.adjusted();
216 let module = sema.scope(receiver.syntax()).module()?;
217 let krate = module.krate();
219 let iter_trait = FamousDefs(sema, Some(krate)).core_iter_Iterator()?;
220 it_type.impls_trait(sema.db, iter_trait, &[]).then(|| (expr, receiver))
225 use crate::tests::{check_assist, check_assist_not_applicable};
230 fn test_for_each_in_method_stmt() {
232 convert_iter_for_each_to_for,
234 //- minicore: iterators
236 let it = core::iter::repeat(92);
237 it.$0for_each(|(x, y)| {
238 println!("x: {}, y: {}", x, y);
244 let it = core::iter::repeat(92);
246 println!("x: {}, y: {}", x, y);
254 fn test_for_each_in_method() {
256 convert_iter_for_each_to_for,
258 //- minicore: iterators
260 let it = core::iter::repeat(92);
261 it.$0for_each(|(x, y)| {
262 println!("x: {}, y: {}", x, y);
268 let it = core::iter::repeat(92);
270 println!("x: {}, y: {}", x, y);
278 fn test_for_each_without_braces_stmt() {
280 convert_iter_for_each_to_for,
282 //- minicore: iterators
284 let it = core::iter::repeat(92);
285 it.$0for_each(|(x, y)| println!("x: {}, y: {}", x, y));
290 let it = core::iter::repeat(92);
292 println!("x: {}, y: {}", x, y)
300 fn test_for_each_not_applicable() {
301 check_assist_not_applicable(
302 convert_iter_for_each_to_for,
304 //- minicore: iterators
306 ().$0for_each(|x| println!("{}", x));
312 fn test_for_each_not_applicable_invalid_cursor_pos() {
313 cov_mark::check!(test_for_each_not_applicable_invalid_cursor_pos);
314 check_assist_not_applicable(
315 convert_iter_for_each_to_for,
317 //- minicore: iterators
319 core::iter::repeat(92).for_each(|(x, y)| $0println!("x: {}, y: {}", x, y));
325 fn each_to_for_not_for() {
326 check_assist_not_applicable(
327 convert_for_loop_with_for_each,
329 let mut x = vec![1, 2, 3];
330 x.iter_mut().$0for_each(|v| *v *= 2);
336 fn each_to_for_simple_for() {
338 convert_for_loop_with_for_each,
341 let x = vec![1, 2, 3];
348 let x = vec![1, 2, 3];
349 x.into_iter().for_each(|v| {
357 fn each_to_for_for_in_range() {
359 convert_for_loop_with_for_each,
361 //- minicore: range, iterators
362 impl<T> core::iter::Iterator for core::ops::Range<T> {
365 fn next(&mut self) -> Option<Self::Item> {
376 impl<T> core::iter::Iterator for core::ops::Range<T> {
379 fn next(&mut self) -> Option<Self::Item> {
385 (0..92).for_each(|x| {
393 fn each_to_for_not_available_in_body() {
394 cov_mark::check!(not_available_in_body);
395 check_assist_not_applicable(
396 convert_for_loop_with_for_each,
399 let x = vec![1, 2, 3];
408 fn each_to_for_for_borrowed() {
410 convert_for_loop_with_for_each,
412 //- minicore: iterators
413 use core::iter::{Repeat, repeat};
417 fn iter(&self) -> Repeat<i32> { repeat(92) }
418 fn iter_mut(&mut self) -> Repeat<i32> { repeat(92) }
429 use core::iter::{Repeat, repeat};
433 fn iter(&self) -> Repeat<i32> { repeat(92) }
434 fn iter_mut(&mut self) -> Repeat<i32> { repeat(92) }
439 x.iter().for_each(|v| {
448 fn each_to_for_for_borrowed_no_iter_method() {
450 convert_for_loop_with_for_each,
454 let x = NoIterMethod;
463 let x = NoIterMethod;
464 (&x).into_iter().for_each(|v| {
473 fn each_to_for_for_borrowed_mut() {
475 convert_for_loop_with_for_each,
477 //- minicore: iterators
478 use core::iter::{Repeat, repeat};
482 fn iter(&self) -> Repeat<i32> { repeat(92) }
483 fn iter_mut(&mut self) -> Repeat<i32> { repeat(92) }
494 use core::iter::{Repeat, repeat};
498 fn iter(&self) -> Repeat<i32> { repeat(92) }
499 fn iter_mut(&mut self) -> Repeat<i32> { repeat(92) }
504 x.iter_mut().for_each(|v| {
513 fn each_to_for_for_borrowed_mut_behind_var() {
515 convert_for_loop_with_for_each,
518 let x = vec![1, 2, 3];
526 let x = vec![1, 2, 3];
528 y.into_iter().for_each(|v| {
536 fn each_to_for_already_impls_iterator() {
537 cov_mark::check!(test_already_impls_iterator);
539 convert_for_loop_with_for_each,
541 //- minicore: iterators
543 for$0 a in core::iter::repeat(92).take(1) {
550 core::iter::repeat(92).take(1).for_each(|a| {