diff options
-rw-r--r-- | crates/ide_assists/src/handlers/convert_for_to_iter_for_each.rs | 225 | ||||
-rw-r--r-- | crates/ide_assists/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/ide_assists/src/tests/generated.rs | 23 |
3 files changed, 250 insertions, 0 deletions
diff --git a/crates/ide_assists/src/handlers/convert_for_to_iter_for_each.rs b/crates/ide_assists/src/handlers/convert_for_to_iter_for_each.rs new file mode 100644 index 000000000..d858474c6 --- /dev/null +++ b/crates/ide_assists/src/handlers/convert_for_to_iter_for_each.rs | |||
@@ -0,0 +1,225 @@ | |||
1 | use ast::LoopBodyOwner; | ||
2 | use ide_db::helpers::FamousDefs; | ||
3 | use stdx::format_to; | ||
4 | use syntax::{ast, AstNode}; | ||
5 | |||
6 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | ||
7 | |||
8 | // Assist: convert_for_to_iter_for_each | ||
9 | // | ||
10 | // Converts a for loop into a for_each loop on the Iterator. | ||
11 | // | ||
12 | // ``` | ||
13 | // fn main() { | ||
14 | // let x = vec![1, 2, 3]; | ||
15 | // for $0v in x { | ||
16 | // let y = v * 2; | ||
17 | // } | ||
18 | // } | ||
19 | // ``` | ||
20 | // -> | ||
21 | // ``` | ||
22 | // fn main() { | ||
23 | // let x = vec![1, 2, 3]; | ||
24 | // x.into_iter().for_each(|v| { | ||
25 | // let y = v * 2; | ||
26 | // }); | ||
27 | // } | ||
28 | // ``` | ||
29 | pub(crate) fn convert_for_to_iter_for_each(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
30 | let for_loop = ctx.find_node_at_offset::<ast::ForExpr>()?; | ||
31 | let iterable = for_loop.iterable()?; | ||
32 | let pat = for_loop.pat()?; | ||
33 | let body = for_loop.loop_body()?; | ||
34 | |||
35 | let mut buf = String::new(); | ||
36 | |||
37 | if impls_core_iter(&ctx.sema, &iterable) { | ||
38 | buf += &iterable.to_string(); | ||
39 | } else { | ||
40 | match iterable { | ||
41 | ast::Expr::RefExpr(r) => { | ||
42 | if r.mut_token().is_some() { | ||
43 | format_to!(buf, "{}.iter_mut()", r.expr()?); | ||
44 | } else { | ||
45 | format_to!(buf, "{}.iter()", r.expr()?); | ||
46 | } | ||
47 | } | ||
48 | _ => format_to!(buf, "{}.into_iter()", iterable), | ||
49 | } | ||
50 | } | ||
51 | |||
52 | format_to!(buf, ".for_each(|{}| {});", pat, body); | ||
53 | |||
54 | acc.add( | ||
55 | AssistId("convert_for_to_iter_for_each", AssistKind::RefactorRewrite), | ||
56 | "Convert a for loop into an Iterator::for_each", | ||
57 | for_loop.syntax().text_range(), | ||
58 | |builder| builder.replace(for_loop.syntax().text_range(), buf), | ||
59 | ) | ||
60 | } | ||
61 | |||
62 | fn impls_core_iter(sema: &hir::Semantics<ide_db::RootDatabase>, iterable: &ast::Expr) -> bool { | ||
63 | let it_typ = if let Some(i) = sema.type_of_expr(iterable) { | ||
64 | i | ||
65 | } else { | ||
66 | return false; | ||
67 | }; | ||
68 | let module = if let Some(m) = sema.scope(iterable.syntax()).module() { | ||
69 | m | ||
70 | } else { | ||
71 | return false; | ||
72 | }; | ||
73 | let krate = module.krate(); | ||
74 | if let Some(iter_trait) = FamousDefs(sema, Some(krate)).core_iter_Iterator() { | ||
75 | return it_typ.impls_trait(sema.db, iter_trait, &[]); | ||
76 | } | ||
77 | false | ||
78 | } | ||
79 | |||
80 | #[cfg(test)] | ||
81 | mod tests { | ||
82 | use crate::tests::{check_assist, check_assist_not_applicable}; | ||
83 | |||
84 | use super::*; | ||
85 | |||
86 | const EMPTY_ITER_FIXTURE: &'static str = r" | ||
87 | //- /lib.rs deps:core crate:empty_iter | ||
88 | pub struct EmptyIter; | ||
89 | impl Iterator for EmptyIter { | ||
90 | type Item = usize; | ||
91 | fn next(&mut self) -> Option<Self::Item> { None } | ||
92 | } | ||
93 | |||
94 | pub struct Empty; | ||
95 | impl Empty { | ||
96 | pub fn iter(&self) -> EmptyIter { EmptyIter } | ||
97 | } | ||
98 | "; | ||
99 | |||
100 | #[test] | ||
101 | fn test_not_for() { | ||
102 | check_assist_not_applicable( | ||
103 | convert_for_to_iter_for_each, | ||
104 | r" | ||
105 | let mut x = vec![1, 2, 3]; | ||
106 | x.iter_mut().$0for_each(|v| *v *= 2); | ||
107 | ", | ||
108 | ) | ||
109 | } | ||
110 | |||
111 | #[test] | ||
112 | fn test_simple_for() { | ||
113 | check_assist( | ||
114 | convert_for_to_iter_for_each, | ||
115 | r" | ||
116 | fn main() { | ||
117 | let x = vec![1, 2, 3]; | ||
118 | for $0v in x { | ||
119 | v *= 2; | ||
120 | } | ||
121 | }", | ||
122 | r" | ||
123 | fn main() { | ||
124 | let x = vec![1, 2, 3]; | ||
125 | x.into_iter().for_each(|v| { | ||
126 | v *= 2; | ||
127 | }); | ||
128 | }", | ||
129 | ) | ||
130 | } | ||
131 | |||
132 | #[test] | ||
133 | fn test_for_borrowed() { | ||
134 | check_assist( | ||
135 | convert_for_to_iter_for_each, | ||
136 | r" | ||
137 | fn main() { | ||
138 | let x = vec![1, 2, 3]; | ||
139 | for $0v in &x { | ||
140 | let a = v * 2; | ||
141 | } | ||
142 | }", | ||
143 | r" | ||
144 | fn main() { | ||
145 | let x = vec![1, 2, 3]; | ||
146 | x.iter().for_each(|v| { | ||
147 | let a = v * 2; | ||
148 | }); | ||
149 | }", | ||
150 | ) | ||
151 | } | ||
152 | |||
153 | #[test] | ||
154 | fn test_for_borrowed_mut() { | ||
155 | check_assist( | ||
156 | convert_for_to_iter_for_each, | ||
157 | r" | ||
158 | fn main() { | ||
159 | let x = vec![1, 2, 3]; | ||
160 | for $0v in &mut x { | ||
161 | *v *= 2; | ||
162 | } | ||
163 | }", | ||
164 | r" | ||
165 | fn main() { | ||
166 | let x = vec![1, 2, 3]; | ||
167 | x.iter_mut().for_each(|v| { | ||
168 | *v *= 2; | ||
169 | }); | ||
170 | }", | ||
171 | ) | ||
172 | } | ||
173 | |||
174 | #[test] | ||
175 | fn test_for_borrowed_mut_behind_var() { | ||
176 | check_assist( | ||
177 | convert_for_to_iter_for_each, | ||
178 | r" | ||
179 | fn main() { | ||
180 | let x = vec![1, 2, 3]; | ||
181 | let y = &mut x; | ||
182 | for $0v in y { | ||
183 | *v *= 2; | ||
184 | } | ||
185 | }", | ||
186 | r" | ||
187 | fn main() { | ||
188 | let x = vec![1, 2, 3]; | ||
189 | let y = &mut x; | ||
190 | y.into_iter().for_each(|v| { | ||
191 | *v *= 2; | ||
192 | }); | ||
193 | }", | ||
194 | ) | ||
195 | } | ||
196 | |||
197 | #[test] | ||
198 | fn test_take() { | ||
199 | let before = r#" | ||
200 | use empty_iter::*; | ||
201 | fn main() { | ||
202 | let x = Empty; | ||
203 | for$0 a in x.iter().take(1) { | ||
204 | println!("{}", a); | ||
205 | } | ||
206 | } | ||
207 | "#; | ||
208 | let after = r#" | ||
209 | use empty_iter::*; | ||
210 | fn main() { | ||
211 | let x = Empty; | ||
212 | x.iter().take(1).for_each(|a| { | ||
213 | println!("{}", a); | ||
214 | }); | ||
215 | } | ||
216 | "#; | ||
217 | let before = &format!( | ||
218 | "//- /main.rs crate:main deps:core,empty_iter{}{}{}", | ||
219 | before, | ||
220 | FamousDefs::FIXTURE, | ||
221 | EMPTY_ITER_FIXTURE | ||
222 | ); | ||
223 | check_assist(convert_for_to_iter_for_each, before, after); | ||
224 | } | ||
225 | } | ||
diff --git a/crates/ide_assists/src/lib.rs b/crates/ide_assists/src/lib.rs index 7067cf8b6..f4c7e6fbf 100644 --- a/crates/ide_assists/src/lib.rs +++ b/crates/ide_assists/src/lib.rs | |||
@@ -114,6 +114,7 @@ mod handlers { | |||
114 | mod apply_demorgan; | 114 | mod apply_demorgan; |
115 | mod auto_import; | 115 | mod auto_import; |
116 | mod change_visibility; | 116 | mod change_visibility; |
117 | mod convert_for_to_iter_for_each; | ||
117 | mod convert_integer_literal; | 118 | mod convert_integer_literal; |
118 | mod early_return; | 119 | mod early_return; |
119 | mod expand_glob_import; | 120 | mod expand_glob_import; |
@@ -175,6 +176,7 @@ mod handlers { | |||
175 | apply_demorgan::apply_demorgan, | 176 | apply_demorgan::apply_demorgan, |
176 | auto_import::auto_import, | 177 | auto_import::auto_import, |
177 | change_visibility::change_visibility, | 178 | change_visibility::change_visibility, |
179 | convert_for_to_iter_for_each::convert_for_to_iter_for_each, | ||
178 | convert_integer_literal::convert_integer_literal, | 180 | convert_integer_literal::convert_integer_literal, |
179 | early_return::convert_to_guarded_return, | 181 | early_return::convert_to_guarded_return, |
180 | expand_glob_import::expand_glob_import, | 182 | expand_glob_import::expand_glob_import, |
diff --git a/crates/ide_assists/src/tests/generated.rs b/crates/ide_assists/src/tests/generated.rs index 0516deaff..100c3b7fe 100644 --- a/crates/ide_assists/src/tests/generated.rs +++ b/crates/ide_assists/src/tests/generated.rs | |||
@@ -193,6 +193,29 @@ pub(crate) fn frobnicate() {} | |||
193 | } | 193 | } |
194 | 194 | ||
195 | #[test] | 195 | #[test] |
196 | fn doctest_convert_for_to_iter_for_each() { | ||
197 | check_doc_test( | ||
198 | "convert_for_to_iter_for_each", | ||
199 | r#####" | ||
200 | fn main() { | ||
201 | let x = vec![1, 2, 3]; | ||
202 | for $0v in x { | ||
203 | let y = v * 2; | ||
204 | } | ||
205 | } | ||
206 | "#####, | ||
207 | r#####" | ||
208 | fn main() { | ||
209 | let x = vec![1, 2, 3]; | ||
210 | x.into_iter().for_each(|v| { | ||
211 | let y = v * 2; | ||
212 | }); | ||
213 | } | ||
214 | "#####, | ||
215 | ) | ||
216 | } | ||
217 | |||
218 | #[test] | ||
196 | fn doctest_convert_integer_literal() { | 219 | fn doctest_convert_integer_literal() { |
197 | check_doc_test( | 220 | check_doc_test( |
198 | "convert_integer_literal", | 221 | "convert_integer_literal", |