aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorLuiz Carlos Mourão Paes de Carvalho <[email protected]>2021-03-10 01:58:17 +0000
committerLuiz Carlos Mourão Paes de Carvalho <[email protected]>2021-03-10 01:58:17 +0000
commiteea21490e06807960269844ab0f50b1873e1c78b (patch)
tree357e6961f4fae83e3fe819c758f0380429ed980a /crates
parent21913d0fdb848445a908021dbcd4c3accf2ca0a5 (diff)
feat: add assist to conver for_each into for loops
Diffstat (limited to 'crates')
-rw-r--r--crates/ide_assists/src/handlers/convert_iter_for_each_to_for.rs138
-rw-r--r--crates/ide_assists/src/lib.rs2
2 files changed, 140 insertions, 0 deletions
diff --git a/crates/ide_assists/src/handlers/convert_iter_for_each_to_for.rs b/crates/ide_assists/src/handlers/convert_iter_for_each_to_for.rs
new file mode 100644
index 000000000..3220f2f46
--- /dev/null
+++ b/crates/ide_assists/src/handlers/convert_iter_for_each_to_for.rs
@@ -0,0 +1,138 @@
1use ide_db::helpers::FamousDefs;
2use stdx::format_to;
3use syntax::{AstNode, ast::{self, ArgListOwner}};
4
5use crate::{AssistContext, AssistId, AssistKind, Assists};
6
7/// Assist: convert_iter_for_each_to_for
8//
9/// Converts an Iterator::for_each function into a for loop.
10///
11/// ```rust
12/// fn main() {
13/// let vec = vec![(1, 2), (2, 3), (3, 4)];
14/// x.iter().for_each(|(x, y)| {
15/// println!("x: {}, y: {}", x, y);
16/// })
17/// }
18/// ```
19/// ->
20/// ```rust
21/// fn main() {
22/// let vec = vec![(1, 2), (2, 3), (3, 4)];
23/// for (x, y) in x.iter() {
24/// println!("x: {}, y: {}", x, y);
25/// });
26/// }
27/// ```
28pub(crate) fn convert_iter_for_each_to_for(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
29 let closure;
30
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,
35 _ => { return None; }
36 };
37
38 expr
39 },
40 ast::Expr::ClosureExpr(expr) => {
41 closure = expr;
42 ast::MethodCallExpr::cast(closure.syntax().ancestors().nth(2)?)?
43 },
44 _ => { return None; }
45 };
46
47 let (total_expr, parent) = validate_method_call_expr(&ctx.sema, total_expr)?;
48
49 let param_list = closure.param_list()?;
50 let param = param_list.params().next()?;
51 let body = closure.body()?;
52
53 acc.add(
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(),
57 |builder| {
58 let mut buf = String::new();
59
60 format_to!(buf, "for {} in {} ", param, parent);
61
62 match body {
63 ast::Expr::BlockExpr(body) => format_to!(buf, "{}", body),
64 _ => format_to!(buf, "{{\n{}\n}}", body)
65 }
66
67 builder.replace(total_expr.syntax().text_range(), buf)
68 },
69 )
70}
71
72fn 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" {
77 return None;
78 }
79
80 let expr = ast::Expr::MethodCallExpr(expr);
81 let parent = ast::Expr::cast(expr.syntax().first_child()?)?;
82
83 let it_type = sema.type_of_expr(&parent)?;
84 let module = sema.scope(parent.syntax()).module()?;
85 let krate = module.krate();
86
87 let iter_trait = FamousDefs(sema, Some(krate)).core_iter_Iterator()?;
88 it_type.impls_trait(sema.db, iter_trait, &[]).then(|| (expr, parent))
89}
90
91#[cfg(test)]
92mod tests {
93 use crate::tests::check_assist;
94
95 use super::*;
96
97 #[test]
98 fn test_for_each_in_method() {
99 check_assist(
100 convert_iter_for_each_to_for,
101 r"
102fn main() {
103 let x = vec![(1, 1), (2, 2), (3, 3), (4, 4)];
104 x.iter().$0for_each(|(x, y)| {
105 dbg!(x, y)
106 });
107}",
108 r"
109fn main() {
110 let x = vec![(1, 1), (2, 2), (3, 3), (4, 4)];
111 for (x, y) in x.iter() {
112 dbg!(x, y)
113 };
114}",
115 )
116 }
117
118 #[test]
119 fn test_for_each_in_closure() {
120 check_assist(
121 convert_iter_for_each_to_for,
122 r"
123fn main() {
124 let x = vec![(1, 1), (2, 2), (3, 3), (4, 4)];
125 x.iter().for_each($0|(x, y)| {
126 dbg!(x, y)
127 });
128}",
129 r"
130fn main() {
131 let x = vec![(1, 1), (2, 2), (3, 3), (4, 4)];
132 for (x, y) in x.iter() {
133 dbg!(x, y)
134 };
135}",
136 )
137 }
138} \ No newline at end of file
diff --git a/crates/ide_assists/src/lib.rs b/crates/ide_assists/src/lib.rs
index ea62d5f5d..f1aab74d4 100644
--- a/crates/ide_assists/src/lib.rs
+++ b/crates/ide_assists/src/lib.rs
@@ -116,6 +116,7 @@ mod handlers {
116 mod change_visibility; 116 mod change_visibility;
117 mod convert_integer_literal; 117 mod convert_integer_literal;
118 mod convert_comment_block; 118 mod convert_comment_block;
119 mod convert_iter_for_each_to_for;
119 mod early_return; 120 mod early_return;
120 mod expand_glob_import; 121 mod expand_glob_import;
121 mod extract_function; 122 mod extract_function;
@@ -181,6 +182,7 @@ mod handlers {
181 change_visibility::change_visibility, 182 change_visibility::change_visibility,
182 convert_integer_literal::convert_integer_literal, 183 convert_integer_literal::convert_integer_literal,
183 convert_comment_block::convert_comment_block, 184 convert_comment_block::convert_comment_block,
185 convert_iter_for_each_to_for::convert_iter_for_each_to_for,
184 early_return::convert_to_guarded_return, 186 early_return::convert_to_guarded_return,
185 expand_glob_import::expand_glob_import, 187 expand_glob_import::expand_glob_import,
186 extract_struct_from_enum_variant::extract_struct_from_enum_variant, 188 extract_struct_from_enum_variant::extract_struct_from_enum_variant,