1 use ide_db::helpers::FamousDefs;
3 ast::{self, edit_in_place::Indent, make, ArgListOwner},
7 use crate::{AssistContext, AssistId, AssistKind, Assists};
9 // Assist: convert_iter_for_each_to_for
11 // Converts an Iterator::for_each function into a for loop.
14 // # //- minicore: iterators
17 // let iter = iter::repeat((9, 2));
18 // iter.for_each$0(|(x, y)| {
19 // println!("x: {}, y: {}", x, y);
27 // let iter = iter::repeat((9, 2));
28 // for (x, y) in iter {
29 // println!("x: {}, y: {}", x, y);
34 pub(crate) fn convert_iter_for_each_to_for(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
35 let method = ctx.find_node_at_offset::<ast::MethodCallExpr>()?;
37 let closure = match method.arg_list()?.args().next()? {
38 ast::Expr::ClosureExpr(expr) => expr,
42 let (method, receiver) = validate_method_call_expr(ctx, method)?;
44 let param_list = closure.param_list()?;
45 let param = param_list.params().next()?.pat()?;
46 let body = closure.body()?;
48 let stmt = method.syntax().parent().and_then(ast::ExprStmt::cast);
49 let range = stmt.as_ref().map_or(method.syntax(), AstNode::syntax).text_range();
52 AssistId("convert_iter_for_each_to_for", AssistKind::RefactorRewrite),
53 "Replace this `Iterator::for_each` with a for loop",
57 stmt.as_ref().map_or_else(|| method.indent_level(), ast::ExprStmt::indent_level);
59 let block = match body {
60 ast::Expr::BlockExpr(block) => block,
61 _ => make::block_expr(Vec::new(), Some(body)),
64 block.reindent_to(indent);
66 let expr_for_loop = make::expr_for_loop(param, receiver, block);
67 builder.replace(range, expr_for_loop.to_string())
72 fn validate_method_call_expr(
74 expr: ast::MethodCallExpr,
75 ) -> Option<(ast::Expr, ast::Expr)> {
76 let name_ref = expr.name_ref()?;
77 if name_ref.syntax().text_range().intersect(ctx.frange.range).is_none() {
78 cov_mark::hit!(test_for_each_not_applicable_invalid_cursor_pos);
81 if name_ref.text() != "for_each" {
87 let receiver = expr.receiver()?;
88 let expr = ast::Expr::MethodCallExpr(expr);
90 let it_type = sema.type_of_expr(&receiver)?.adjusted();
91 let module = sema.scope(receiver.syntax()).module()?;
92 let krate = module.krate();
94 let iter_trait = FamousDefs(sema, Some(krate)).core_iter_Iterator()?;
95 it_type.impls_trait(sema.db, iter_trait, &[]).then(|| (expr, receiver))
100 use crate::tests::{check_assist, check_assist_not_applicable};
105 fn test_for_each_in_method_stmt() {
107 convert_iter_for_each_to_for,
109 //- minicore: iterators
111 let it = core::iter::repeat(92);
112 it.$0for_each(|(x, y)| {
113 println!("x: {}, y: {}", x, y);
119 let it = core::iter::repeat(92);
121 println!("x: {}, y: {}", x, y);
129 fn test_for_each_in_method() {
131 convert_iter_for_each_to_for,
133 //- minicore: iterators
135 let it = core::iter::repeat(92);
136 it.$0for_each(|(x, y)| {
137 println!("x: {}, y: {}", x, y);
143 let it = core::iter::repeat(92);
145 println!("x: {}, y: {}", x, y);
153 fn test_for_each_without_braces_stmt() {
155 convert_iter_for_each_to_for,
157 //- minicore: iterators
159 let it = core::iter::repeat(92);
160 it.$0for_each(|(x, y)| println!("x: {}, y: {}", x, y));
165 let it = core::iter::repeat(92);
167 println!("x: {}, y: {}", x, y)
175 fn test_for_each_not_applicable() {
176 check_assist_not_applicable(
177 convert_iter_for_each_to_for,
179 //- minicore: iterators
181 ().$0for_each(|x| println!("{}", x));
187 fn test_for_each_not_applicable_invalid_cursor_pos() {
188 cov_mark::check!(test_for_each_not_applicable_invalid_cursor_pos);
189 check_assist_not_applicable(
190 convert_iter_for_each_to_for,
192 //- minicore: iterators
194 core::iter::repeat(92).for_each(|(x, y)| $0println!("x: {}, y: {}", x, y));