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(
160 Some(&wanted_method),
162 if func.ret_type(sema.db).impls_trait(sema.db, iter_trait, &[]) {
169 if !has_wanted_method {
173 Some((expr_behind_ref, wanted_method))
176 /// Whether iterable implements core::Iterator
177 fn impls_core_iter(sema: &hir::Semantics<ide_db::RootDatabase>, iterable: &ast::Expr) -> bool {
178 let it_typ = match sema.type_of_expr(iterable) {
179 Some(it) => it.adjusted(),
180 None => return false,
183 let module = match sema.scope(iterable.syntax()).module() {
185 None => return false,
188 let krate = module.krate();
189 match FamousDefs(sema, Some(krate)).core_iter_Iterator() {
190 Some(iter_trait) => {
191 cov_mark::hit!(test_already_impls_iterator);
192 it_typ.impls_trait(sema.db, iter_trait, &[])
198 fn validate_method_call_expr(
200 expr: ast::MethodCallExpr,
201 ) -> Option<(ast::Expr, ast::Expr)> {
202 let name_ref = expr.name_ref()?;
203 if !name_ref.syntax().text_range().contains_range(ctx.selection_trimmed()) {
204 cov_mark::hit!(test_for_each_not_applicable_invalid_cursor_pos);
207 if name_ref.text() != "for_each" {
211 let sema = &ctx.sema;
213 let receiver = expr.receiver()?;
214 let expr = ast::Expr::MethodCallExpr(expr);
216 let it_type = sema.type_of_expr(&receiver)?.adjusted();
217 let module = sema.scope(receiver.syntax()).module()?;
218 let krate = module.krate();
220 let iter_trait = FamousDefs(sema, Some(krate)).core_iter_Iterator()?;
221 it_type.impls_trait(sema.db, iter_trait, &[]).then(|| (expr, receiver))
226 use crate::tests::{check_assist, check_assist_not_applicable};
231 fn test_for_each_in_method_stmt() {
233 convert_iter_for_each_to_for,
235 //- minicore: iterators
237 let it = core::iter::repeat(92);
238 it.$0for_each(|(x, y)| {
239 println!("x: {}, y: {}", x, y);
245 let it = core::iter::repeat(92);
247 println!("x: {}, y: {}", x, y);
255 fn test_for_each_in_method() {
257 convert_iter_for_each_to_for,
259 //- minicore: iterators
261 let it = core::iter::repeat(92);
262 it.$0for_each(|(x, y)| {
263 println!("x: {}, y: {}", x, y);
269 let it = core::iter::repeat(92);
271 println!("x: {}, y: {}", x, y);
279 fn test_for_each_without_braces_stmt() {
281 convert_iter_for_each_to_for,
283 //- minicore: iterators
285 let it = core::iter::repeat(92);
286 it.$0for_each(|(x, y)| println!("x: {}, y: {}", x, y));
291 let it = core::iter::repeat(92);
293 println!("x: {}, y: {}", x, y)
301 fn test_for_each_not_applicable() {
302 check_assist_not_applicable(
303 convert_iter_for_each_to_for,
305 //- minicore: iterators
307 ().$0for_each(|x| println!("{}", x));
313 fn test_for_each_not_applicable_invalid_cursor_pos() {
314 cov_mark::check!(test_for_each_not_applicable_invalid_cursor_pos);
315 check_assist_not_applicable(
316 convert_iter_for_each_to_for,
318 //- minicore: iterators
320 core::iter::repeat(92).for_each(|(x, y)| $0println!("x: {}, y: {}", x, y));
326 fn each_to_for_not_for() {
327 check_assist_not_applicable(
328 convert_for_loop_with_for_each,
330 let mut x = vec![1, 2, 3];
331 x.iter_mut().$0for_each(|v| *v *= 2);
337 fn each_to_for_simple_for() {
339 convert_for_loop_with_for_each,
342 let x = vec![1, 2, 3];
349 let x = vec![1, 2, 3];
350 x.into_iter().for_each(|v| {
358 fn each_to_for_for_in_range() {
360 convert_for_loop_with_for_each,
362 //- minicore: range, iterators
363 impl<T> core::iter::Iterator for core::ops::Range<T> {
366 fn next(&mut self) -> Option<Self::Item> {
377 impl<T> core::iter::Iterator for core::ops::Range<T> {
380 fn next(&mut self) -> Option<Self::Item> {
386 (0..92).for_each(|x| {
394 fn each_to_for_not_available_in_body() {
395 cov_mark::check!(not_available_in_body);
396 check_assist_not_applicable(
397 convert_for_loop_with_for_each,
400 let x = vec![1, 2, 3];
409 fn each_to_for_for_borrowed() {
411 convert_for_loop_with_for_each,
413 //- minicore: iterators
414 use core::iter::{Repeat, repeat};
418 fn iter(&self) -> Repeat<i32> { repeat(92) }
419 fn iter_mut(&mut self) -> Repeat<i32> { repeat(92) }
430 use core::iter::{Repeat, repeat};
434 fn iter(&self) -> Repeat<i32> { repeat(92) }
435 fn iter_mut(&mut self) -> Repeat<i32> { repeat(92) }
440 x.iter().for_each(|v| {
449 fn each_to_for_for_borrowed_no_iter_method() {
451 convert_for_loop_with_for_each,
455 let x = NoIterMethod;
464 let x = NoIterMethod;
465 (&x).into_iter().for_each(|v| {
474 fn each_to_for_for_borrowed_mut() {
476 convert_for_loop_with_for_each,
478 //- minicore: iterators
479 use core::iter::{Repeat, repeat};
483 fn iter(&self) -> Repeat<i32> { repeat(92) }
484 fn iter_mut(&mut self) -> Repeat<i32> { repeat(92) }
495 use core::iter::{Repeat, repeat};
499 fn iter(&self) -> Repeat<i32> { repeat(92) }
500 fn iter_mut(&mut self) -> Repeat<i32> { repeat(92) }
505 x.iter_mut().for_each(|v| {
514 fn each_to_for_for_borrowed_mut_behind_var() {
516 convert_for_loop_with_for_each,
519 let x = vec![1, 2, 3];
527 let x = vec![1, 2, 3];
529 y.into_iter().for_each(|v| {
537 fn each_to_for_already_impls_iterator() {
538 cov_mark::check!(test_already_impls_iterator);
540 convert_for_loop_with_for_each,
542 //- minicore: iterators
544 for$0 a in core::iter::repeat(92).take(1) {
551 core::iter::repeat(92).take(1).for_each(|a| {