1 use ide_db::helpers::FamousDefs;
3 use syntax::{AstNode, ast::{self, ArgListOwner}};
5 use crate::{AssistContext, AssistId, AssistKind, Assists};
7 /// Assist: convert_iter_for_each_to_for
9 /// Converts an Iterator::for_each function into a for loop.
13 /// let vec = vec![(1, 2), (2, 3), (3, 4)];
14 /// x.iter().for_each(|(x, y)| {
15 /// println!("x: {}, y: {}", x, y);
22 /// let vec = vec![(1, 2), (2, 3), (3, 4)];
23 /// for (x, y) in x.iter() {
24 /// println!("x: {}, y: {}", x, y);
28 pub(crate) fn convert_iter_for_each_to_for(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
31 let total_expr = match ctx.find_node_at_offset::<ast::Expr>()? {
32 ast::Expr::MethodCallExpr(expr) => {
33 closure = match expr.arg_list()?.args().next()? {
34 ast::Expr::ClosureExpr(expr) => expr,
40 ast::Expr::ClosureExpr(expr) => {
42 ast::MethodCallExpr::cast(closure.syntax().ancestors().nth(2)?)?
47 let (total_expr, parent) = validate_method_call_expr(&ctx.sema, total_expr)?;
49 let param_list = closure.param_list()?;
50 let param = param_list.params().next()?;
51 let body = closure.body()?;
54 AssistId("convert_iter_for_each_to_for", AssistKind::RefactorRewrite),
55 "Replace this `Iterator::for_each` with a for loop",
56 total_expr.syntax().text_range(),
58 let mut buf = String::new();
60 format_to!(buf, "for {} in {} ", param, parent);
63 ast::Expr::BlockExpr(body) => format_to!(buf, "{}", body),
64 _ => format_to!(buf, "{{\n{}\n}}", body)
67 builder.replace(total_expr.syntax().text_range(), buf)
72 fn validate_method_call_expr(
73 sema: &hir::Semantics<ide_db::RootDatabase>,
74 expr: ast::MethodCallExpr,
75 ) -> Option<(ast::Expr, ast::Expr)> {
76 if expr.name_ref()?.text() != "for_each" {
80 let expr = ast::Expr::MethodCallExpr(expr);
81 let parent = ast::Expr::cast(expr.syntax().first_child()?)?;
83 let it_type = sema.type_of_expr(&parent)?;
84 let module = sema.scope(parent.syntax()).module()?;
85 let krate = module.krate();
87 let iter_trait = FamousDefs(sema, Some(krate)).core_iter_Iterator()?;
88 it_type.impls_trait(sema.db, iter_trait, &[]).then(|| (expr, parent))
93 use crate::tests::check_assist;
98 fn test_for_each_in_method() {
100 convert_iter_for_each_to_for,
103 let x = vec![(1, 1), (2, 2), (3, 3), (4, 4)];
104 x.iter().$0for_each(|(x, y)| {
110 let x = vec![(1, 1), (2, 2), (3, 3), (4, 4)];
111 for (x, y) in x.iter() {
119 fn test_for_each_in_closure() {
121 convert_iter_for_each_to_for,
124 let x = vec![(1, 1), (2, 2), (3, 3), (4, 4)];
125 x.iter().for_each($0|(x, y)| {
131 let x = vec![(1, 1), (2, 2), (3, 3), (4, 4)];
132 for (x, y) in x.iter() {