1 use ide_db::helpers::FamousDefs;
3 ast::{self, edit::AstNodeEdit, 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 // # //- /lib.rs crate:core
15 // # pub mod iter { pub mod traits { pub mod iterator { pub trait Iterator {} } } }
16 // # pub struct SomeIter;
17 // # impl self::iter::traits::iterator::Iterator for SomeIter {}
18 // # //- /lib.rs crate:main deps:core
19 // # use core::SomeIter;
21 // let iter = SomeIter;
22 // iter.for_each$0(|(x, y)| {
23 // println!("x: {}, y: {}", x, y);
29 // # use core::SomeIter;
31 // let iter = SomeIter;
32 // for (x, y) in iter {
33 // println!("x: {}, y: {}", x, y);
38 pub(crate) fn convert_iter_for_each_to_for(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
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 syntax = stmt.as_ref().map_or(method.syntax(), |stmt| stmt.syntax());
56 AssistId("convert_iter_for_each_to_for", AssistKind::RefactorRewrite),
57 "Replace this `Iterator::for_each` with a for loop",
60 let indent = stmt.as_ref().map_or(method.indent_level(), |stmt| stmt.indent_level());
62 let block = match body {
63 ast::Expr::BlockExpr(block) => block,
64 _ => make::block_expr(Vec::new(), Some(body)),
69 let expr_for_loop = make::expr_for_loop(param, receiver, block);
70 builder.replace(syntax.text_range(), expr_for_loop.syntax().text())
75 fn validate_method_call_expr(
77 expr: ast::MethodCallExpr,
78 ) -> Option<(ast::Expr, ast::Expr)> {
79 let name_ref = expr.name_ref()?;
80 if name_ref.syntax().text_range().intersect(ctx.frange.range).is_none() {
81 cov_mark::hit!(test_for_each_not_applicable_invalid_cursor_pos);
84 if name_ref.text() != "for_each" {
90 let receiver = expr.receiver()?;
91 let expr = ast::Expr::MethodCallExpr(expr);
93 let it_type = sema.type_of_expr(&receiver)?;
94 let module = sema.scope(receiver.syntax()).module()?;
95 let krate = module.krate();
97 let iter_trait = FamousDefs(sema, Some(krate)).core_iter_Iterator()?;
98 it_type.impls_trait(sema.db, iter_trait, &[]).then(|| (expr, receiver))
103 use crate::tests::{check_assist, check_assist_not_applicable};
108 fn test_for_each_in_method_stmt() {
110 convert_iter_for_each_to_for,
112 //- minicore: iterators
114 let it = core::iter::repeat(92);
115 it.$0for_each(|(x, y)| {
116 println!("x: {}, y: {}", x, y);
122 let it = core::iter::repeat(92);
124 println!("x: {}, y: {}", x, y);
132 fn test_for_each_in_method() {
134 convert_iter_for_each_to_for,
136 //- minicore: iterators
138 let it = core::iter::repeat(92);
139 it.$0for_each(|(x, y)| {
140 println!("x: {}, y: {}", x, y);
146 let it = core::iter::repeat(92);
148 println!("x: {}, y: {}", x, y);
156 fn test_for_each_without_braces_stmt() {
158 convert_iter_for_each_to_for,
160 //- minicore: iterators
162 let it = core::iter::repeat(92);
163 it.$0for_each(|(x, y)| println!("x: {}, y: {}", x, y));
168 let it = core::iter::repeat(92);
170 println!("x: {}, y: {}", x, y)
178 fn test_for_each_not_applicable() {
179 check_assist_not_applicable(
180 convert_iter_for_each_to_for,
182 //- minicore: iterators
184 ().$0for_each(|x| println!("{}", x));
190 fn test_for_each_not_applicable_invalid_cursor_pos() {
191 cov_mark::check!(test_for_each_not_applicable_invalid_cursor_pos);
192 check_assist_not_applicable(
193 convert_iter_for_each_to_for,
195 //- minicore: iterators
197 core::iter::repeat(92).for_each(|(x, y)| $0println!("x: {}, y: {}", x, y));