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