• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use hir::Semantics;
2 use ide_db::RootDatabase;
3 use syntax::ast::{edit::AstNodeEdit, AstNode, HasName, LetStmt, Name, Pat};
4 use syntax::T;
5 
6 use crate::{AssistContext, AssistId, AssistKind, Assists};
7 
8 // Assist: convert_let_else_to_match
9 //
10 // Converts let-else statement to let statement and match expression.
11 //
12 // ```
13 // fn main() {
14 //     let Ok(mut x) = f() else$0 { return };
15 // }
16 // ```
17 // ->
18 // ```
19 // fn main() {
20 //     let mut x = match f() {
21 //         Ok(x) => x,
22 //         _ => return,
23 //     };
24 // }
25 // ```
convert_let_else_to_match(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()>26 pub(crate) fn convert_let_else_to_match(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
27     // should focus on else token to trigger
28     let let_stmt = ctx
29         .find_token_syntax_at_offset(T![else])
30         .and_then(|it| it.parent()?.parent())
31         .or_else(|| ctx.find_token_syntax_at_offset(T![let])?.parent())?;
32     let let_stmt = LetStmt::cast(let_stmt)?;
33     let let_else_block = let_stmt.let_else()?.block_expr()?;
34     let let_init = let_stmt.initializer()?;
35     if let_stmt.ty().is_some() {
36         // don't support let with type annotation
37         return None;
38     }
39     let pat = let_stmt.pat()?;
40     let mut binders = Vec::new();
41     binders_in_pat(&mut binders, &pat, &ctx.sema)?;
42 
43     let target = let_stmt.syntax().text_range();
44     acc.add(
45         AssistId("convert_let_else_to_match", AssistKind::RefactorRewrite),
46         "Convert let-else to let and match",
47         target,
48         |edit| {
49             let indent_level = let_stmt.indent_level().0 as usize;
50             let indent = "    ".repeat(indent_level);
51             let indent1 = "    ".repeat(indent_level + 1);
52 
53             let binders_str = binders_to_str(&binders, false);
54             let binders_str_mut = binders_to_str(&binders, true);
55 
56             let init_expr = let_init.syntax().text();
57             let mut pat_no_mut = pat.syntax().text().to_string();
58             // remove the mut from the pattern
59             for (b, ismut) in binders.iter() {
60                 if *ismut {
61                     pat_no_mut = pat_no_mut.replace(&format!("mut {b}"), &b.to_string());
62                 }
63             }
64 
65             let only_expr = let_else_block.statements().next().is_none();
66             let branch2 = match &let_else_block.tail_expr() {
67                 Some(tail) if only_expr => format!("{tail},"),
68                 _ => let_else_block.syntax().text().to_string(),
69             };
70             let replace = if binders.is_empty() {
71                 format!(
72                     "match {init_expr} {{
73 {indent1}{pat_no_mut} => {binders_str}
74 {indent1}_ => {branch2}
75 {indent}}}"
76                 )
77             } else {
78                 format!(
79                     "let {binders_str_mut} = match {init_expr} {{
80 {indent1}{pat_no_mut} => {binders_str},
81 {indent1}_ => {branch2}
82 {indent}}};"
83                 )
84             };
85             edit.replace(target, replace);
86         },
87     )
88 }
89 
90 /// Gets a list of binders in a pattern, and whether they are mut.
binders_in_pat( acc: &mut Vec<(Name, bool)>, pat: &Pat, sem: &Semantics<'_, RootDatabase>, ) -> Option<()>91 fn binders_in_pat(
92     acc: &mut Vec<(Name, bool)>,
93     pat: &Pat,
94     sem: &Semantics<'_, RootDatabase>,
95 ) -> Option<()> {
96     use Pat::*;
97     match pat {
98         IdentPat(p) => {
99             let ident = p.name()?;
100             let ismut = p.ref_token().is_none() && p.mut_token().is_some();
101             // check for const reference
102             if sem.resolve_bind_pat_to_const(p).is_none() {
103                 acc.push((ident, ismut));
104             }
105             if let Some(inner) = p.pat() {
106                 binders_in_pat(acc, &inner, sem)?;
107             }
108             Some(())
109         }
110         BoxPat(p) => p.pat().and_then(|p| binders_in_pat(acc, &p, sem)),
111         RestPat(_) | LiteralPat(_) | PathPat(_) | WildcardPat(_) | ConstBlockPat(_) => Some(()),
112         OrPat(p) => {
113             for p in p.pats() {
114                 binders_in_pat(acc, &p, sem)?;
115             }
116             Some(())
117         }
118         ParenPat(p) => p.pat().and_then(|p| binders_in_pat(acc, &p, sem)),
119         RangePat(p) => {
120             if let Some(st) = p.start() {
121                 binders_in_pat(acc, &st, sem)?
122             }
123             if let Some(ed) = p.end() {
124                 binders_in_pat(acc, &ed, sem)?
125             }
126             Some(())
127         }
128         RecordPat(p) => {
129             for f in p.record_pat_field_list()?.fields() {
130                 let pat = f.pat()?;
131                 binders_in_pat(acc, &pat, sem)?;
132             }
133             Some(())
134         }
135         RefPat(p) => p.pat().and_then(|p| binders_in_pat(acc, &p, sem)),
136         SlicePat(p) => {
137             for p in p.pats() {
138                 binders_in_pat(acc, &p, sem)?;
139             }
140             Some(())
141         }
142         TuplePat(p) => {
143             for p in p.fields() {
144                 binders_in_pat(acc, &p, sem)?;
145             }
146             Some(())
147         }
148         TupleStructPat(p) => {
149             for p in p.fields() {
150                 binders_in_pat(acc, &p, sem)?;
151             }
152             Some(())
153         }
154         // don't support macro pat yet
155         MacroPat(_) => None,
156     }
157 }
158 
binders_to_str(binders: &[(Name, bool)], addmut: bool) -> String159 fn binders_to_str(binders: &[(Name, bool)], addmut: bool) -> String {
160     let vars = binders
161         .iter()
162         .map(
163             |(ident, ismut)| {
164                 if *ismut && addmut {
165                     format!("mut {ident}")
166                 } else {
167                     ident.to_string()
168                 }
169             },
170         )
171         .collect::<Vec<_>>()
172         .join(", ");
173     if binders.is_empty() {
174         String::from("{}")
175     } else if binders.len() == 1 {
176         vars
177     } else {
178         format!("({vars})")
179     }
180 }
181 
182 #[cfg(test)]
183 mod tests {
184     use super::*;
185 
186     use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
187 
188     #[test]
convert_let_else_to_match_no_type_let()189     fn convert_let_else_to_match_no_type_let() {
190         check_assist_not_applicable(
191             convert_let_else_to_match,
192             r#"
193 fn main() {
194     let 1: u32 = v.iter().sum() else$0 { return };
195 }"#,
196         );
197     }
198 
199     #[test]
convert_let_else_to_match_on_else()200     fn convert_let_else_to_match_on_else() {
201         check_assist_not_applicable(
202             convert_let_else_to_match,
203             r#"
204 fn main() {
205     let Ok(x) = f() else {$0 return };
206 }
207             "#,
208         );
209     }
210 
211     #[test]
convert_let_else_to_match_no_macropat()212     fn convert_let_else_to_match_no_macropat() {
213         check_assist_not_applicable(
214             convert_let_else_to_match,
215             r#"
216 fn main() {
217     let m!() = g() else$0 { return };
218 }
219             "#,
220         );
221     }
222 
223     #[test]
convert_let_else_to_match_target()224     fn convert_let_else_to_match_target() {
225         check_assist_target(
226             convert_let_else_to_match,
227             r"
228 fn main() {
229     let Ok(x) = f() else$0 { continue };
230 }",
231             "let Ok(x) = f() else { continue };",
232         );
233     }
234 
235     #[test]
convert_let_else_to_match_basic()236     fn convert_let_else_to_match_basic() {
237         check_assist(
238             convert_let_else_to_match,
239             r"
240 fn main() {
241     let Ok(x) = f() else$0 { continue };
242 }",
243             r"
244 fn main() {
245     let x = match f() {
246         Ok(x) => x,
247         _ => continue,
248     };
249 }",
250         );
251     }
252 
253     #[test]
convert_let_else_to_match_const_ref()254     fn convert_let_else_to_match_const_ref() {
255         check_assist(
256             convert_let_else_to_match,
257             r"
258 enum Option<T> {
259     Some(T),
260     None,
261 }
262 use Option::*;
263 fn main() {
264     let None = f() el$0se { continue };
265 }",
266             r"
267 enum Option<T> {
268     Some(T),
269     None,
270 }
271 use Option::*;
272 fn main() {
273     match f() {
274         None => {}
275         _ => continue,
276     }
277 }",
278         );
279     }
280 
281     #[test]
convert_let_else_to_match_const_ref_const()282     fn convert_let_else_to_match_const_ref_const() {
283         check_assist(
284             convert_let_else_to_match,
285             r"
286 const NEG1: i32 = -1;
287 fn main() {
288     let NEG1 = f() el$0se { continue };
289 }",
290             r"
291 const NEG1: i32 = -1;
292 fn main() {
293     match f() {
294         NEG1 => {}
295         _ => continue,
296     }
297 }",
298         );
299     }
300 
301     #[test]
convert_let_else_to_match_mut()302     fn convert_let_else_to_match_mut() {
303         check_assist(
304             convert_let_else_to_match,
305             r"
306 fn main() {
307     let Ok(mut x) = f() el$0se { continue };
308 }",
309             r"
310 fn main() {
311     let mut x = match f() {
312         Ok(x) => x,
313         _ => continue,
314     };
315 }",
316         );
317     }
318 
319     #[test]
convert_let_else_to_match_multi_binders()320     fn convert_let_else_to_match_multi_binders() {
321         check_assist(
322             convert_let_else_to_match,
323             r#"
324 fn main() {
325     let ControlFlow::Break((x, "tag", y, ..)) = f() else$0 { g(); return };
326 }"#,
327             r#"
328 fn main() {
329     let (x, y) = match f() {
330         ControlFlow::Break((x, "tag", y, ..)) => (x, y),
331         _ => { g(); return }
332     };
333 }"#,
334         );
335     }
336 
337     #[test]
convert_let_else_to_match_slice()338     fn convert_let_else_to_match_slice() {
339         check_assist(
340             convert_let_else_to_match,
341             r#"
342 fn main() {
343     let [one, 1001, other] = f() else$0 { break };
344 }"#,
345             r#"
346 fn main() {
347     let (one, other) = match f() {
348         [one, 1001, other] => (one, other),
349         _ => break,
350     };
351 }"#,
352         );
353     }
354 
355     #[test]
convert_let_else_to_match_struct()356     fn convert_let_else_to_match_struct() {
357         check_assist(
358             convert_let_else_to_match,
359             r#"
360 fn main() {
361     let [Struct { inner: Some(it) }, 1001, other] = f() else$0 { break };
362 }"#,
363             r#"
364 fn main() {
365     let (it, other) = match f() {
366         [Struct { inner: Some(it) }, 1001, other] => (it, other),
367         _ => break,
368     };
369 }"#,
370         );
371     }
372 
373     #[test]
convert_let_else_to_match_struct_ident_pat()374     fn convert_let_else_to_match_struct_ident_pat() {
375         check_assist(
376             convert_let_else_to_match,
377             r#"
378 fn main() {
379     let [Struct { inner }, 1001, other] = f() else$0 { break };
380 }"#,
381             r#"
382 fn main() {
383     let (inner, other) = match f() {
384         [Struct { inner }, 1001, other] => (inner, other),
385         _ => break,
386     };
387 }"#,
388         );
389     }
390 
391     #[test]
convert_let_else_to_match_no_binder()392     fn convert_let_else_to_match_no_binder() {
393         check_assist(
394             convert_let_else_to_match,
395             r#"
396 fn main() {
397     let (8 | 9) = f() else$0 { panic!() };
398 }"#,
399             r#"
400 fn main() {
401     match f() {
402         (8 | 9) => {}
403         _ => panic!(),
404     }
405 }"#,
406         );
407     }
408 
409     #[test]
convert_let_else_to_match_range()410     fn convert_let_else_to_match_range() {
411         check_assist(
412             convert_let_else_to_match,
413             r#"
414 fn main() {
415     let 1.. = f() e$0lse { return };
416 }"#,
417             r#"
418 fn main() {
419     match f() {
420         1.. => {}
421         _ => return,
422     }
423 }"#,
424         );
425     }
426 
427     #[test]
convert_let_else_to_match_refpat()428     fn convert_let_else_to_match_refpat() {
429         check_assist(
430             convert_let_else_to_match,
431             r#"
432 fn main() {
433     let Ok(&mut x) = f(&mut 0) else$0 { return };
434 }"#,
435             r#"
436 fn main() {
437     let x = match f(&mut 0) {
438         Ok(&mut x) => x,
439         _ => return,
440     };
441 }"#,
442         );
443     }
444 
445     #[test]
convert_let_else_to_match_refmut()446     fn convert_let_else_to_match_refmut() {
447         check_assist(
448             convert_let_else_to_match,
449             r#"
450 fn main() {
451     let Ok(ref mut x) = f() else$0 { return };
452 }"#,
453             r#"
454 fn main() {
455     let x = match f() {
456         Ok(ref mut x) => x,
457         _ => return,
458     };
459 }"#,
460         );
461     }
462 
463     #[test]
convert_let_else_to_match_atpat()464     fn convert_let_else_to_match_atpat() {
465         check_assist(
466             convert_let_else_to_match,
467             r#"
468 fn main() {
469     let out @ Ok(ins) = f() else$0 { return };
470 }"#,
471             r#"
472 fn main() {
473     let (out, ins) = match f() {
474         out @ Ok(ins) => (out, ins),
475         _ => return,
476     };
477 }"#,
478         );
479     }
480 
481     #[test]
convert_let_else_to_match_complex_init()482     fn convert_let_else_to_match_complex_init() {
483         check_assist(
484             convert_let_else_to_match,
485             r#"
486 fn main() {
487     let v = vec![1, 2, 3];
488     let &[mut x, y, ..] = &v.iter().collect::<Vec<_>>()[..] else$0 { return };
489 }"#,
490             r#"
491 fn main() {
492     let v = vec![1, 2, 3];
493     let (mut x, y) = match &v.iter().collect::<Vec<_>>()[..] {
494         &[x, y, ..] => (x, y),
495         _ => return,
496     };
497 }"#,
498         );
499     }
500 }
501