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.
15 // let vec = vec![(1, 2), (2, 3), (3, 4)];
16 // x.iter().for_each(|(x, y)| {
17 // println!("x: {}, y: {}", x, y);
24 // let vec = vec![(1, 2), (2, 3), (3, 4)];
25 // for (x, y) in x.iter() {
26 // println!("x: {}, y: {}", x, y);
30 pub(crate) fn convert_iter_for_each_to_for(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
31 let method = ctx.find_node_at_offset::<ast::MethodCallExpr>()?;
32 let stmt = method.syntax().parent().and_then(ast::ExprStmt::cast);
34 let closure = match method.arg_list()?.args().next()? {
35 ast::Expr::ClosureExpr(expr) => expr,
39 let (method, receiver) = validate_method_call_expr(&ctx.sema, method)?;
41 let param_list = closure.param_list()?;
42 let param = param_list.params().next()?.pat()?;
43 let body = closure.body()?;
45 let syntax = stmt.as_ref().map_or(method.syntax(), |stmt| stmt.syntax());
48 AssistId("convert_iter_for_each_to_for", AssistKind::RefactorRewrite),
49 "Replace this `Iterator::for_each` with a for loop",
52 let indent = stmt.as_ref().map_or(method.indent_level(), |stmt| stmt.indent_level());
54 let block = match body {
55 ast::Expr::BlockExpr(block) => block,
56 _ => make::block_expr(Vec::new(), Some(body)),
61 let expr_for_loop = make::expr_for_loop(param, receiver, block);
62 builder.replace(syntax.text_range(), expr_for_loop.syntax().text())
67 fn validate_method_call_expr(
68 sema: &hir::Semantics<ide_db::RootDatabase>,
69 expr: ast::MethodCallExpr,
70 ) -> Option<(ast::Expr, ast::Expr)> {
71 if expr.name_ref()?.text() != "for_each" {
75 let receiver = expr.receiver()?;
76 let expr = ast::Expr::MethodCallExpr(expr);
78 let it_type = sema.type_of_expr(&receiver)?;
79 let module = sema.scope(receiver.syntax()).module()?;
80 let krate = module.krate();
82 let iter_trait = FamousDefs(sema, Some(krate)).core_iter_Iterator()?;
83 it_type.impls_trait(sema.db, iter_trait, &[]).then(|| (expr, receiver))
88 use crate::tests::{check_assist, check_assist_not_applicable};
92 const EMPTY_ITER_FIXTURE: &'static str = r"
93 //- /lib.rs deps:core crate:empty_iter
95 impl Iterator for EmptyIter {
97 fn next(&mut self) -> Option<Self::Item> { None }
101 pub fn iter(&self) -> EmptyIter { EmptyIter }
105 fn check_assist_with_fixtures(before: &str, after: &str) {
106 let before = &format!(
107 "//- /main.rs crate:main deps:core,empty_iter{}{}{}",
112 check_assist(convert_iter_for_each_to_for, before, after);
116 fn test_for_each_in_method_stmt() {
117 check_assist_with_fixtures(
122 x.iter().$0for_each(|(x, y)| {
123 println!("x: {}, y: {}", x, y);
130 for (x, y) in x.iter() {
131 println!("x: {}, y: {}", x, y);
139 fn test_for_each_in_method() {
140 check_assist_with_fixtures(
145 x.iter().$0for_each(|(x, y)| {
146 println!("x: {}, y: {}", x, y);
153 for (x, y) in x.iter() {
154 println!("x: {}, y: {}", x, y);
162 fn test_for_each_in_iter_stmt() {
163 check_assist_with_fixtures(
167 let x = Empty.iter();
168 x.$0for_each(|(x, y)| {
169 println!("x: {}, y: {}", x, y);
175 let x = Empty.iter();
177 println!("x: {}, y: {}", x, y);
185 fn test_for_each_without_braces_stmt() {
186 check_assist_with_fixtures(
191 x.iter().$0for_each(|(x, y)| println!("x: {}, y: {}", x, y));
197 for (x, y) in x.iter() {
198 println!("x: {}, y: {}", x, y)
205 fn test_for_each_not_applicable() {
206 check_assist_not_applicable(
207 convert_iter_for_each_to_for,
210 value.$0for_each(|x| println!("{}", x));