1use hir::{Name, sym};
2use ide_db::{famous_defs::FamousDefs, syntax_helpers::suggest_name};
3use syntax::{
4 AstNode,
5 ast::{self, HasAttrs, HasLoopBody, edit::IndentLevel},
6 syntax_editor::Position,
7};
8
9use crate::{AssistContext, AssistId, Assists};
10
11pub(crate) fn convert_for_loop_to_while_let(
34 acc: &mut Assists,
35 ctx: &AssistContext<'_, '_>,
36) -> Option<()> {
37 let for_loop = ctx.find_node_at_offset::<ast::ForExpr>()?;
38 let iterable = for_loop.iterable()?;
39 let pat = for_loop.pat()?;
40 let body = for_loop.loop_body()?;
41 if body.syntax().text_range().start() < ctx.offset() {
42 cov_mark::hit!(not_available_in_body);
43 return None;
44 }
45
46 acc.add(
47 AssistId::refactor_rewrite("convert_for_loop_to_while_let"),
48 "Replace this for loop with `while let`",
49 for_loop.syntax().text_range(),
50 |builder| {
51 let editor = builder.make_editor(for_loop.syntax());
52 let make = editor.make();
53
54 let (iterable, method) = if impls_core_iter(&ctx.sema, &iterable) {
55 (iterable, None)
56 } else if let Some((expr, method)) = is_ref_and_impls_iter_method(&ctx.sema, &iterable)
57 {
58 (expr, Some(make.name_ref(method.as_str())))
59 } else if let ast::Expr::RefExpr(_) = iterable {
60 (make.expr_paren(iterable).into(), Some(make.name_ref("into_iter")))
61 } else {
62 (iterable, Some(make.name_ref("into_iter")))
63 };
64
65 let iterable = if let Some(method) = method {
66 make.expr_method_call(iterable, method, make.arg_list([])).into()
67 } else {
68 iterable
69 };
70
71 let mut new_name = suggest_name::NameGenerator::new_from_scope_locals(
72 ctx.sema.scope(for_loop.syntax()),
73 );
74 let tmp_var = new_name.suggest_name("iter");
75
76 let mut_expr = make.let_stmt(
77 make.ident_pat(false, true, make.name(&tmp_var)).into(),
78 None,
79 Some(iterable),
80 );
81 let indent = IndentLevel::from_node(for_loop.syntax());
82
83 if let Some(label) = for_loop.label() {
84 let label = label.syntax();
85 editor.insert(Position::before(for_loop.syntax()), make.whitespace(" "));
86 editor.insert(Position::before(for_loop.syntax()), label);
87 }
88 crate::utils::insert_attributes(for_loop.syntax(), &editor, for_loop.attrs());
89
90 editor.insert(
91 Position::before(for_loop.syntax()),
92 make.whitespace(format!("\n{indent}").as_str()),
93 );
94 editor.insert(Position::before(for_loop.syntax()), mut_expr.syntax());
95
96 let opt_pat = make.tuple_struct_pat(make.ident_path("Some"), [pat]);
97 let iter_next_expr = make.expr_method_call(
98 make.expr_path(make.ident_path(&tmp_var)),
99 make.name_ref("next"),
100 make.arg_list([]),
101 );
102 let cond = make.expr_let(opt_pat.into(), iter_next_expr.into());
103
104 let while_loop = make.expr_while_loop(cond.into(), body);
105
106 editor.replace(for_loop.syntax(), while_loop.syntax());
107
108 builder.add_file_edits(ctx.vfs_file_id(), editor);
109 },
110 )
111}
112
113fn is_ref_and_impls_iter_method(
117 sema: &hir::Semantics<'_, ide_db::RootDatabase>,
118 iterable: &ast::Expr,
119) -> Option<(ast::Expr, hir::Name)> {
120 let ref_expr = match iterable {
121 ast::Expr::RefExpr(r) => r,
122 _ => return None,
123 };
124 let wanted_method = Name::new_symbol_root(if ref_expr.mut_token().is_some() {
125 sym::iter_mut
126 } else {
127 sym::iter
128 });
129 let expr_behind_ref = ref_expr.expr()?;
130 let ty = sema.type_of_expr(&expr_behind_ref)?.adjusted();
131 let scope = sema.scope(iterable.syntax())?;
132 let krate = scope.krate();
133 let iter_trait = FamousDefs(sema, krate).core_iter_Iterator()?;
134
135 let has_wanted_method = ty
136 .iterate_method_candidates(sema.db, &scope, Some(&wanted_method), |func| {
137 if func.ret_type(sema.db).impls_trait(sema.db, iter_trait, &[]) {
138 return Some(());
139 }
140 None
141 })
142 .is_some();
143 if !has_wanted_method {
144 return None;
145 }
146
147 Some((expr_behind_ref, wanted_method))
148}
149
150fn impls_core_iter(sema: &hir::Semantics<'_, ide_db::RootDatabase>, iterable: &ast::Expr) -> bool {
152 (|| {
153 let it_typ = sema.type_of_expr(iterable)?.adjusted();
154
155 let module = sema.scope(iterable.syntax())?.module();
156
157 let krate = module.krate(sema.db);
158 let iter_trait = FamousDefs(sema, krate).core_iter_Iterator()?;
159 cov_mark::hit!(test_already_impls_iterator);
160 Some(it_typ.impls_trait(sema.db, iter_trait, &[]))
161 })()
162 .unwrap_or(false)
163}
164
165#[cfg(test)]
166mod tests {
167 use crate::tests::{check_assist, check_assist_not_applicable};
168
169 use super::*;
170
171 #[test]
172 fn each_to_for_simple_for() {
173 check_assist(
174 convert_for_loop_to_while_let,
175 r"
176fn main() {
177 let mut x = vec![1, 2, 3];
178 for $0v in x {
179 v *= 2;
180 };
181}",
182 r"
183fn main() {
184 let mut x = vec![1, 2, 3];
185 let mut iter = x.into_iter();
186 while let Some(v) = iter.next() {
187 v *= 2;
188 };
189}",
190 )
191 }
192
193 #[test]
194 fn each_to_for_with_label() {
195 check_assist(
196 convert_for_loop_to_while_let,
197 r"
198fn main() {
199 let mut x = vec![1, 2, 3];
200 'a: for $0v in x {
201 v *= 2;
202 break 'a;
203 };
204}",
205 r"
206fn main() {
207 let mut x = vec![1, 2, 3];
208 let mut iter = x.into_iter();
209 'a: while let Some(v) = iter.next() {
210 v *= 2;
211 break 'a;
212 };
213}",
214 )
215 }
216
217 #[test]
218 fn each_to_for_with_attributes() {
219 check_assist(
220 convert_for_loop_to_while_let,
221 r"
222fn main() {
223 let mut x = vec![1, 2, 3];
224 #[allow(unused)]
225 #[deny(unsafe_code)]
226 for $0v in x {
227 v *= 2;
228 };
229}",
230 r"
231fn main() {
232 let mut x = vec![1, 2, 3];
233 let mut iter = x.into_iter();
234 #[allow(unused)]
235 #[deny(unsafe_code)]
236 while let Some(v) = iter.next() {
237 v *= 2;
238 };
239}",
240 )
241 }
242
243 #[test]
244 fn each_to_for_for_in_range() {
245 check_assist(
246 convert_for_loop_to_while_let,
247 r#"
248//- minicore: range, iterators
249impl<T> core::iter::Iterator for core::ops::Range<T> {
250 type Item = T;
251
252 fn next(&mut self) -> Option<Self::Item> {
253 None
254 }
255}
256
257fn main() {
258 for $0x in 0..92 {
259 print!("{}", x);
260 }
261}"#,
262 r#"
263impl<T> core::iter::Iterator for core::ops::Range<T> {
264 type Item = T;
265
266 fn next(&mut self) -> Option<Self::Item> {
267 None
268 }
269}
270
271fn main() {
272 let mut iter = 0..92;
273 while let Some(x) = iter.next() {
274 print!("{}", x);
275 }
276}"#,
277 )
278 }
279
280 #[test]
281 fn each_to_for_not_available_in_body() {
282 cov_mark::check!(not_available_in_body);
283 check_assist_not_applicable(
284 convert_for_loop_to_while_let,
285 r"
286fn main() {
287 let mut x = vec![1, 2, 3];
288 for v in x {
289 $0v *= 2;
290 }
291}",
292 )
293 }
294
295 #[test]
296 fn each_to_for_for_borrowed() {
297 check_assist(
298 convert_for_loop_to_while_let,
299 r#"
300//- minicore: iterators
301use core::iter::{Repeat, repeat};
302
303struct S;
304impl S {
305 fn iter(&self) -> Repeat<i32> { repeat(92) }
306 fn iter_mut(&mut self) -> Repeat<i32> { repeat(92) }
307}
308
309fn main() {
310 let x = S;
311 for $0v in &x {
312 let a = v * 2;
313 }
314}
315"#,
316 r#"
317use core::iter::{Repeat, repeat};
318
319struct S;
320impl S {
321 fn iter(&self) -> Repeat<i32> { repeat(92) }
322 fn iter_mut(&mut self) -> Repeat<i32> { repeat(92) }
323}
324
325fn main() {
326 let x = S;
327 let mut iter = x.iter();
328 while let Some(v) = iter.next() {
329 let a = v * 2;
330 }
331}
332"#,
333 )
334 }
335
336 #[test]
337 fn each_to_for_for_borrowed_no_iter_method() {
338 check_assist(
339 convert_for_loop_to_while_let,
340 r"
341struct NoIterMethod;
342fn main() {
343 let x = NoIterMethod;
344 for $0v in &x {
345 let a = v * 2;
346 }
347}
348",
349 r"
350struct NoIterMethod;
351fn main() {
352 let x = NoIterMethod;
353 let mut iter = (&x).into_iter();
354 while let Some(v) = iter.next() {
355 let a = v * 2;
356 }
357}
358",
359 )
360 }
361
362 #[test]
363 fn each_to_for_for_borrowed_no_iter_method_mut() {
364 check_assist(
365 convert_for_loop_to_while_let,
366 r"
367struct NoIterMethod;
368fn main() {
369 let x = NoIterMethod;
370 for $0v in &mut x {
371 let a = v * 2;
372 }
373}
374",
375 r"
376struct NoIterMethod;
377fn main() {
378 let x = NoIterMethod;
379 let mut iter = (&mut x).into_iter();
380 while let Some(v) = iter.next() {
381 let a = v * 2;
382 }
383}
384",
385 )
386 }
387
388 #[test]
389 fn each_to_for_for_borrowed_mut() {
390 check_assist(
391 convert_for_loop_to_while_let,
392 r#"
393//- minicore: iterators
394use core::iter::{Repeat, repeat};
395
396struct S;
397impl S {
398 fn iter(&self) -> Repeat<i32> { repeat(92) }
399 fn iter_mut(&mut self) -> Repeat<i32> { repeat(92) }
400}
401
402fn main() {
403 let x = S;
404 for $0v in &mut x {
405 let a = v * 2;
406 }
407}
408"#,
409 r#"
410use core::iter::{Repeat, repeat};
411
412struct S;
413impl S {
414 fn iter(&self) -> Repeat<i32> { repeat(92) }
415 fn iter_mut(&mut self) -> Repeat<i32> { repeat(92) }
416}
417
418fn main() {
419 let x = S;
420 let mut iter = x.iter_mut();
421 while let Some(v) = iter.next() {
422 let a = v * 2;
423 }
424}
425"#,
426 )
427 }
428
429 #[test]
430 fn each_to_for_for_borrowed_mut_behind_var() {
431 check_assist(
432 convert_for_loop_to_while_let,
433 r"
434fn main() {
435 let mut x = vec![1, 2, 3];
436 let y = &mut x;
437 for $0v in y {
438 *v *= 2;
439 }
440}",
441 r"
442fn main() {
443 let mut x = vec![1, 2, 3];
444 let y = &mut x;
445 let mut iter = y.into_iter();
446 while let Some(v) = iter.next() {
447 *v *= 2;
448 }
449}",
450 )
451 }
452
453 #[test]
454 fn each_to_for_already_impls_iterator() {
455 cov_mark::check!(test_already_impls_iterator);
456 check_assist(
457 convert_for_loop_to_while_let,
458 r#"
459//- minicore: iterators
460fn main() {
461 for$0 a in core::iter::repeat(92).take(1) {
462 println!("{}", a);
463 }
464}
465"#,
466 r#"
467fn main() {
468 let mut iter = core::iter::repeat(92).take(1);
469 while let Some(a) = iter.next() {
470 println!("{}", a);
471 }
472}
473"#,
474 );
475 }
476}