1use syntax::T;
2use syntax::ast::RangeItem;
3use syntax::ast::edit::AstNodeEdit;
4use syntax::ast::{self, AstNode, HasName, LetStmt, Pat};
5use syntax::syntax_editor::SyntaxEditor;
6
7use crate::{AssistContext, AssistId, Assists};
8
9pub(crate) fn convert_let_else_to_match(
28 acc: &mut Assists,
29 ctx: &AssistContext<'_, '_>,
30) -> Option<()> {
31 let (editor, _) = SyntaxEditor::new(ctx.source_file().syntax().clone());
32 let let_stmt = ctx
34 .find_token_syntax_at_offset(T![else])
35 .and_then(|it| it.parent()?.parent())
36 .or_else(|| ctx.find_token_syntax_at_offset(T![let])?.parent())?;
37 let let_stmt = LetStmt::cast(let_stmt)?;
38 let else_block = let_stmt.let_else()?.block_expr()?;
39 let else_expr = if else_block.statements().next().is_none()
40 && let Some(tail_expr) = else_block.tail_expr()
41 {
42 tail_expr.reset_indent()
43 } else {
44 else_block.reset_indent().into()
45 };
46 let init = let_stmt.initializer()?;
47 if let_stmt.ty().is_some() {
49 return None;
50 }
51 let pat = let_stmt.pat()?;
52 let mut idents = Vec::default();
53 let pat_without_mut = remove_mut_and_collect_idents(&editor, &pat, &mut idents)?;
54 let bindings = idents
55 .into_iter()
56 .filter_map(|ref pat| {
57 if ctx.sema.resolve_bind_pat_to_const(pat).is_none() {
59 Some((pat.name()?, pat.ref_token().is_none() && pat.mut_token().is_some()))
60 } else {
61 None
62 }
63 })
64 .collect::<Vec<_>>();
65
66 acc.add(
67 AssistId::refactor_rewrite("convert_let_else_to_match"),
68 if bindings.is_empty() {
69 "Convert let-else to match"
70 } else {
71 "Convert let-else to let and match"
72 },
73 let_stmt.syntax().text_range(),
74 |builder| {
75 let make = editor.make();
76 let binding_paths = bindings
77 .iter()
78 .map(|(name, _)| make.expr_path(make.ident_path(&name.to_string())))
79 .collect::<Vec<_>>();
80
81 let binding_arm = make.match_arm(
82 pat_without_mut,
83 None,
84 match binding_paths.len() {
90 0 => make.expr_empty_block().into(),
91
92 1 => binding_paths[0].clone(),
93 _ => make.expr_tuple(binding_paths).into(),
94 },
95 );
96 let else_arm = make.match_arm(make.wildcard_pat().into(), None, else_expr);
97 let arms = [binding_arm, else_arm].map(|arm| arm.indent(1.into()));
98 let match_ = make.expr_match(init, make.match_arm_list(arms));
99 let match_ = match_.indent(let_stmt.indent_level());
100
101 if bindings.is_empty() {
102 editor.replace(let_stmt.syntax(), match_.syntax());
103 } else {
104 let ident_pats = bindings
105 .into_iter()
106 .map(|(name, is_mut)| make.ident_pat(false, is_mut, name).into())
107 .collect::<Vec<Pat>>();
108 let new_let_stmt = make.let_stmt(
109 if ident_pats.len() == 1 {
110 ident_pats[0].clone()
111 } else {
112 make.tuple_pat(ident_pats).into()
113 },
114 None,
115 Some(match_.into()),
116 );
117 editor.replace(let_stmt.syntax(), new_let_stmt.syntax());
118 }
119 builder.add_file_edits(ctx.vfs_file_id(), editor);
120 },
121 )
122}
123
124fn remove_mut_and_collect_idents(
125 editor: &SyntaxEditor,
126 pat: &ast::Pat,
127 acc: &mut Vec<ast::IdentPat>,
128) -> Option<ast::Pat> {
129 let make = editor.make();
130 Some(match pat {
131 ast::Pat::IdentPat(p) => {
132 acc.push(p.clone());
133 let non_mut_pat = make.ident_pat(
134 p.ref_token().is_some(),
135 p.ref_token().is_some() && p.mut_token().is_some(),
136 p.name()?,
137 );
138 let non_mut_pat = if let Some(inner) = p.pat() {
139 non_mut_pat.set_pat(remove_mut_and_collect_idents(editor, &inner, acc), editor)
140 } else {
141 non_mut_pat
142 };
143 non_mut_pat.into()
144 }
145 ast::Pat::BoxPat(p) => {
146 let pat = remove_mut_and_collect_idents(editor, &p.pat()?, acc)?;
147 make.box_pat(pat).into()
148 }
149 ast::Pat::DerefPat(p) => {
150 let pat = remove_mut_and_collect_idents(editor, &p.pat()?, acc)?;
151 make.deref_pat(pat)
152 }
153 ast::Pat::OrPat(p) => {
154 let pats = p
155 .pats()
156 .map(|pat| remove_mut_and_collect_idents(editor, &pat, acc))
157 .collect::<Option<Vec<_>>>()?;
158 make.or_pat(pats, p.leading_pipe().is_some()).into()
159 }
160 ast::Pat::ParenPat(p) => {
161 let pat = remove_mut_and_collect_idents(editor, &p.pat()?, acc)?;
162 make.paren_pat(pat).into()
163 }
164 ast::Pat::RangePat(p) => {
165 let start = if let Some(start) = p.start() {
166 Some(remove_mut_and_collect_idents(editor, &start, acc)?)
167 } else {
168 None
169 };
170 let end = if let Some(end) = p.end() {
171 Some(remove_mut_and_collect_idents(editor, &end, acc)?)
172 } else {
173 None
174 };
175 make.range_pat(start, end).into()
176 }
177 ast::Pat::RecordPat(p) => {
178 let fields = p
179 .record_pat_field_list()?
180 .fields()
181 .map(|field| {
182 remove_mut_and_collect_idents(editor, &field.pat()?, acc).map(|pat| {
183 if let Some(name_ref) = field.name_ref() {
184 make.record_pat_field(name_ref, pat)
185 } else {
186 make.record_pat_field_shorthand(pat)
187 }
188 })
189 })
190 .collect::<Option<Vec<_>>>()?;
191 make.record_pat_with_fields(
192 p.path()?,
193 make.record_pat_field_list(fields, p.record_pat_field_list()?.rest_pat()),
194 )
195 .into()
196 }
197 ast::Pat::RefPat(p) => {
198 let inner = p.pat()?;
199 if let ast::Pat::IdentPat(ident) = inner {
200 acc.push(ident);
201 p.clone().into()
202 } else {
203 let pat = remove_mut_and_collect_idents(editor, &inner, acc)?;
204 make.ref_pat(pat).into()
205 }
206 }
207 ast::Pat::SlicePat(p) => {
208 let pats = p
209 .pats()
210 .map(|pat| remove_mut_and_collect_idents(editor, &pat, acc))
211 .collect::<Option<Vec<_>>>()?;
212 make.slice_pat(pats).into()
213 }
214 ast::Pat::TuplePat(p) => {
215 let pats = p
216 .fields()
217 .map(|field| remove_mut_and_collect_idents(editor, &field, acc))
218 .collect::<Option<Vec<_>>>()?;
219 make.tuple_pat(pats).into()
220 }
221 ast::Pat::TupleStructPat(p) => {
222 let fields = p
223 .fields()
224 .map(|field| remove_mut_and_collect_idents(editor, &field, acc))
225 .collect::<Option<Vec<_>>>()?;
226 make.tuple_struct_pat(p.path()?, fields).into()
227 }
228 ast::Pat::RestPat(_)
229 | ast::Pat::LiteralPat(_)
230 | ast::Pat::PathPat(_)
231 | ast::Pat::WildcardPat(_)
232 | ast::Pat::NotNull(_)
233 | ast::Pat::ConstBlockPat(_) => pat.clone(),
234 ast::Pat::MacroPat(_) => return None,
236 })
237}
238
239#[cfg(test)]
240mod tests {
241 use super::*;
242
243 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
244
245 #[test]
246 fn convert_let_else_to_match_no_type_let() {
247 check_assist_not_applicable(
248 convert_let_else_to_match,
249 r#"
250fn main() {
251 let 1: u32 = v.iter().sum() else$0 { return };
252}"#,
253 );
254 }
255
256 #[test]
257 fn convert_let_else_to_match_on_else() {
258 check_assist_not_applicable(
259 convert_let_else_to_match,
260 r#"
261fn main() {
262 let Ok(x) = f() else {$0 return };
263}
264 "#,
265 );
266 }
267
268 #[test]
269 fn convert_let_else_to_match_no_macropat() {
270 check_assist_not_applicable(
271 convert_let_else_to_match,
272 r#"
273fn main() {
274 let m!() = g() else$0 { return };
275}
276 "#,
277 );
278 }
279
280 #[test]
281 fn convert_let_else_to_match_target() {
282 check_assist_target(
283 convert_let_else_to_match,
284 r"
285fn main() {
286 let Ok(x) = f() else$0 { continue };
287}",
288 "let Ok(x) = f() else { continue };",
289 );
290 }
291
292 #[test]
293 fn convert_let_else_to_match_basic() {
294 check_assist(
295 convert_let_else_to_match,
296 r"
297fn main() {
298 let Ok(x) = f() else$0 { continue };
299}",
300 r"
301fn main() {
302 let x = match f() {
303 Ok(x) => x,
304 _ => continue,
305 };
306}",
307 );
308 }
309
310 #[test]
311 fn convert_let_else_to_match_with_empty_else_block() {
312 check_assist(
313 convert_let_else_to_match,
314 r"
315fn main() {
316 let Ok(x) = f() else$0 {};
317}",
318 r"
319fn main() {
320 let x = match f() {
321 Ok(x) => x,
322 _ => {}
323 };
324}",
325 );
326 }
327
328 #[test]
329 fn convert_let_else_to_match_with_some_indent() {
330 check_assist(
331 convert_let_else_to_match,
332 r#"
333mod indent {
334 fn main() {
335 let Ok(x) = f() else$0 {
336 log();
337 unreachable!(
338 "..."
339 );
340 };
341 }
342}"#,
343 r#"
344mod indent {
345 fn main() {
346 let x = match f() {
347 Ok(x) => x,
348 _ => {
349 log();
350 unreachable!(
351 "..."
352 );
353 }
354 };
355 }
356}"#,
357 );
358
359 check_assist(
360 convert_let_else_to_match,
361 r#"
362mod indent {
363 fn main() {
364 let Ok(x) = f() else$0 {
365 unreachable!(
366 "..."
367 )
368 };
369 }
370}"#,
371 r#"
372mod indent {
373 fn main() {
374 let x = match f() {
375 Ok(x) => x,
376 _ => unreachable!(
377 "..."
378 ),
379 };
380 }
381}"#,
382 );
383 }
384
385 #[test]
386 fn convert_let_else_to_match_const_ref() {
387 check_assist(
388 convert_let_else_to_match,
389 r"
390enum Option<T> {
391 Some(T),
392 None,
393}
394use Option::*;
395fn main() {
396 let None = f() el$0se { continue };
397}",
398 r"
399enum Option<T> {
400 Some(T),
401 None,
402}
403use Option::*;
404fn main() {
405 match f() {
406 None => {}
407 _ => continue,
408 }
409}",
410 );
411 }
412
413 #[test]
414 fn convert_let_else_to_match_const_ref_const() {
415 check_assist(
416 convert_let_else_to_match,
417 r"
418const NEG1: i32 = -1;
419fn main() {
420 let NEG1 = f() el$0se { continue };
421}",
422 r"
423const NEG1: i32 = -1;
424fn main() {
425 match f() {
426 NEG1 => {}
427 _ => continue,
428 }
429}",
430 );
431 }
432
433 #[test]
434 fn convert_let_else_to_match_mut() {
435 check_assist(
436 convert_let_else_to_match,
437 r"
438fn main() {
439 let Ok(mut x) = f() el$0se { continue };
440}",
441 r"
442fn main() {
443 let mut x = match f() {
444 Ok(x) => x,
445 _ => continue,
446 };
447}",
448 );
449 }
450
451 #[test]
452 fn convert_let_else_to_match_multi_binders() {
453 check_assist(
454 convert_let_else_to_match,
455 r#"
456fn main() {
457 let ControlFlow::Break((x, "tag", y, ..)) = f() else$0 { g(); return };
458}"#,
459 r#"
460fn main() {
461 let (x, y) = match f() {
462 ControlFlow::Break((x, "tag", y, ..)) => (x, y),
463 _ => { g(); return }
464 };
465}"#,
466 );
467 }
468
469 #[test]
470 fn convert_let_else_to_match_slice() {
471 check_assist(
472 convert_let_else_to_match,
473 r#"
474fn main() {
475 let [one, 1001, other] = f() else$0 { break };
476}"#,
477 r#"
478fn main() {
479 let (one, other) = match f() {
480 [one, 1001, other] => (one, other),
481 _ => break,
482 };
483}"#,
484 );
485 }
486
487 #[test]
488 fn convert_let_else_to_match_struct() {
489 check_assist(
490 convert_let_else_to_match,
491 r#"
492fn main() {
493 let [Struct { inner: Some(it) }, 1001, other] = f() else$0 { break };
494}"#,
495 r#"
496fn main() {
497 let (it, other) = match f() {
498 [Struct { inner: Some(it) }, 1001, other] => (it, other),
499 _ => break,
500 };
501}"#,
502 );
503 }
504
505 #[test]
506 fn convert_let_else_to_match_struct_ident_pat() {
507 check_assist(
508 convert_let_else_to_match,
509 r#"
510fn main() {
511 let [Struct { inner }, 1001, other] = f() else$0 { break };
512}"#,
513 r#"
514fn main() {
515 let (inner, other) = match f() {
516 [Struct { inner }, 1001, other] => (inner, other),
517 _ => break,
518 };
519}"#,
520 );
521 }
522
523 #[test]
524 fn convert_let_else_to_match_no_binder() {
525 check_assist(
526 convert_let_else_to_match,
527 r#"
528fn main() {
529 let (8 | 9) = f() else$0 { panic!() };
530}"#,
531 r#"
532fn main() {
533 match f() {
534 (8 | 9) => {}
535 _ => panic!(),
536 }
537}"#,
538 );
539 }
540
541 #[test]
542 fn convert_let_else_to_match_range() {
543 check_assist(
544 convert_let_else_to_match,
545 r#"
546fn main() {
547 let 1.. = f() e$0lse { return };
548}"#,
549 r#"
550fn main() {
551 match f() {
552 1.. => {}
553 _ => return,
554 }
555}"#,
556 );
557 }
558
559 #[test]
560 fn convert_let_else_to_match_refpat() {
561 check_assist(
562 convert_let_else_to_match,
563 r#"
564fn main() {
565 let Ok(&mut x) = f(&mut 0) else$0 { return };
566}"#,
567 r#"
568fn main() {
569 let x = match f(&mut 0) {
570 Ok(&mut x) => x,
571 _ => return,
572 };
573}"#,
574 );
575 }
576
577 #[test]
578 fn convert_let_else_to_match_refmut() {
579 check_assist(
580 convert_let_else_to_match,
581 r#"
582fn main() {
583 let Ok(ref mut x) = f() else$0 { return };
584}"#,
585 r#"
586fn main() {
587 let x = match f() {
588 Ok(ref mut x) => x,
589 _ => return,
590 };
591}"#,
592 );
593 }
594
595 #[test]
596 fn convert_let_else_to_match_atpat() {
597 check_assist(
598 convert_let_else_to_match,
599 r#"
600fn main() {
601 let out @ Ok(ins) = f() else$0 { return };
602}"#,
603 r#"
604fn main() {
605 let (out, ins) = match f() {
606 out @ Ok(ins) => (out, ins),
607 _ => return,
608 };
609}"#,
610 );
611 }
612
613 #[test]
614 fn convert_let_else_to_match_complex_init() {
615 check_assist(
616 convert_let_else_to_match,
617 r#"
618fn main() {
619 let v = vec![1, 2, 3];
620 let &[mut x, y, ..] = &v.iter().collect::<Vec<_>>()[..] else$0 { return };
621}"#,
622 r#"
623fn main() {
624 let v = vec![1, 2, 3];
625 let (mut x, y) = match &v.iter().collect::<Vec<_>>()[..] {
626 &[x, y, ..] => (x, y),
627 _ => return,
628 };
629}"#,
630 );
631 }
632}