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