]> git.proxmox.com Git - rustc.git/blob - src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs
457559656a42b29bc10306f7d7975556ec6a93c8
[rustc.git] / src / tools / rust-analyzer / crates / ide-assists / src / handlers / replace_if_let_with_match.rs
1 use std::iter::{self, successors};
2
3 use either::Either;
4 use ide_db::{
5 defs::NameClass,
6 syntax_helpers::node_ext::{is_pattern_cond, single_let},
7 ty_filter::TryEnum,
8 RootDatabase,
9 };
10 use syntax::{
11 ast::{
12 self,
13 edit::{AstNodeEdit, IndentLevel},
14 make, HasName,
15 },
16 AstNode, TextRange, T,
17 };
18
19 use crate::{
20 utils::{does_nested_pattern, does_pat_match_variant, unwrap_trivial_block},
21 AssistContext, AssistId, AssistKind, Assists,
22 };
23
24 // Assist: replace_if_let_with_match
25 //
26 // Replaces a `if let` expression with a `match` expression.
27 //
28 // ```
29 // enum Action { Move { distance: u32 }, Stop }
30 //
31 // fn handle(action: Action) {
32 // $0if let Action::Move { distance } = action {
33 // foo(distance)
34 // } else {
35 // bar()
36 // }
37 // }
38 // ```
39 // ->
40 // ```
41 // enum Action { Move { distance: u32 }, Stop }
42 //
43 // fn handle(action: Action) {
44 // match action {
45 // Action::Move { distance } => foo(distance),
46 // _ => bar(),
47 // }
48 // }
49 // ```
50 pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
51 let if_expr: ast::IfExpr = ctx.find_node_at_offset()?;
52 let available_range = TextRange::new(
53 if_expr.syntax().text_range().start(),
54 if_expr.then_branch()?.syntax().text_range().start(),
55 );
56 let cursor_in_range = available_range.contains_range(ctx.selection_trimmed());
57 if !cursor_in_range {
58 return None;
59 }
60 let mut else_block = None;
61 let if_exprs = successors(Some(if_expr.clone()), |expr| match expr.else_branch()? {
62 ast::ElseBranch::IfExpr(expr) => Some(expr),
63 ast::ElseBranch::Block(block) => {
64 else_block = Some(block);
65 None
66 }
67 });
68 let scrutinee_to_be_expr = if_expr.condition()?;
69 let scrutinee_to_be_expr = match single_let(scrutinee_to_be_expr.clone()) {
70 Some(cond) => cond.expr()?,
71 None => scrutinee_to_be_expr,
72 };
73
74 let mut pat_seen = false;
75 let mut cond_bodies = Vec::new();
76 for if_expr in if_exprs {
77 let cond = if_expr.condition()?;
78 let cond = match single_let(cond.clone()) {
79 Some(let_) => {
80 let pat = let_.pat()?;
81 let expr = let_.expr()?;
82 // FIXME: If one `let` is wrapped in parentheses and the second is not,
83 // we'll exit here.
84 if scrutinee_to_be_expr.syntax().text() != expr.syntax().text() {
85 // Only if all condition expressions are equal we can merge them into a match
86 return None;
87 }
88 pat_seen = true;
89 Either::Left(pat)
90 }
91 // Multiple `let`, unsupported.
92 None if is_pattern_cond(cond.clone()) => return None,
93 None => Either::Right(cond),
94 };
95 let body = if_expr.then_branch()?;
96 cond_bodies.push((cond, body));
97 }
98
99 if !pat_seen && cond_bodies.len() != 1 {
100 // Don't offer turning an if (chain) without patterns into a match,
101 // unless its a simple `if cond { .. } (else { .. })`
102 return None;
103 }
104
105 acc.add(
106 AssistId("replace_if_let_with_match", AssistKind::RefactorRewrite),
107 "Replace if let with match",
108 available_range,
109 move |edit| {
110 let match_expr = {
111 let else_arm = make_else_arm(ctx, else_block, &cond_bodies);
112 let make_match_arm = |(pat, body): (_, ast::BlockExpr)| {
113 let body = body.reset_indent().indent(IndentLevel(1));
114 match pat {
115 Either::Left(pat) => {
116 make::match_arm(iter::once(pat), None, unwrap_trivial_block(body))
117 }
118 Either::Right(_) if !pat_seen => make::match_arm(
119 iter::once(make::literal_pat("true").into()),
120 None,
121 unwrap_trivial_block(body),
122 ),
123 Either::Right(expr) => make::match_arm(
124 iter::once(make::wildcard_pat().into()),
125 Some(expr),
126 unwrap_trivial_block(body),
127 ),
128 }
129 };
130 let arms = cond_bodies.into_iter().map(make_match_arm).chain(iter::once(else_arm));
131 let match_expr = make::expr_match(scrutinee_to_be_expr, make::match_arm_list(arms));
132 match_expr.indent(IndentLevel::from_node(if_expr.syntax()))
133 };
134
135 let has_preceding_if_expr =
136 if_expr.syntax().parent().map_or(false, |it| ast::IfExpr::can_cast(it.kind()));
137 let expr = if has_preceding_if_expr {
138 // make sure we replace the `else if let ...` with a block so we don't end up with `else expr`
139 make::block_expr(None, Some(match_expr)).into()
140 } else {
141 match_expr
142 };
143 edit.replace_ast::<ast::Expr>(if_expr.into(), expr);
144 },
145 )
146 }
147
148 fn make_else_arm(
149 ctx: &AssistContext<'_>,
150 else_block: Option<ast::BlockExpr>,
151 conditionals: &[(Either<ast::Pat, ast::Expr>, ast::BlockExpr)],
152 ) -> ast::MatchArm {
153 let (pattern, expr) = if let Some(else_block) = else_block {
154 let pattern = match conditionals {
155 [(Either::Right(_), _)] => make::literal_pat("false").into(),
156 [(Either::Left(pat), _)] => match ctx
157 .sema
158 .type_of_pat(pat)
159 .and_then(|ty| TryEnum::from_ty(&ctx.sema, &ty.adjusted()))
160 {
161 Some(it) => {
162 if does_pat_match_variant(pat, &it.sad_pattern()) {
163 it.happy_pattern_wildcard()
164 } else if does_nested_pattern(pat) {
165 make::wildcard_pat().into()
166 } else {
167 it.sad_pattern()
168 }
169 }
170 None => make::wildcard_pat().into(),
171 },
172 _ => make::wildcard_pat().into(),
173 };
174 (pattern, unwrap_trivial_block(else_block))
175 } else {
176 let pattern = match conditionals {
177 [(Either::Right(_), _)] => make::literal_pat("false").into(),
178 _ => make::wildcard_pat().into(),
179 };
180 (pattern, make::expr_unit())
181 };
182 make::match_arm(iter::once(pattern), None, expr)
183 }
184
185 // Assist: replace_match_with_if_let
186 //
187 // Replaces a binary `match` with a wildcard pattern and no guards with an `if let` expression.
188 //
189 // ```
190 // enum Action { Move { distance: u32 }, Stop }
191 //
192 // fn handle(action: Action) {
193 // $0match action {
194 // Action::Move { distance } => foo(distance),
195 // _ => bar(),
196 // }
197 // }
198 // ```
199 // ->
200 // ```
201 // enum Action { Move { distance: u32 }, Stop }
202 //
203 // fn handle(action: Action) {
204 // if let Action::Move { distance } = action {
205 // foo(distance)
206 // } else {
207 // bar()
208 // }
209 // }
210 // ```
211 pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
212 let match_expr: ast::MatchExpr = ctx.find_node_at_offset()?;
213
214 let mut arms = match_expr.match_arm_list()?.arms();
215 let (first_arm, second_arm) = (arms.next()?, arms.next()?);
216 if arms.next().is_some() || first_arm.guard().is_some() || second_arm.guard().is_some() {
217 return None;
218 }
219
220 let (if_let_pat, then_expr, else_expr) = pick_pattern_and_expr_order(
221 &ctx.sema,
222 first_arm.pat()?,
223 second_arm.pat()?,
224 first_arm.expr()?,
225 second_arm.expr()?,
226 )?;
227 let scrutinee = match_expr.expr()?;
228
229 let target = match_expr.syntax().text_range();
230 acc.add(
231 AssistId("replace_match_with_if_let", AssistKind::RefactorRewrite),
232 "Replace match with if let",
233 target,
234 move |edit| {
235 fn make_block_expr(expr: ast::Expr) -> ast::BlockExpr {
236 // Blocks with modifiers (unsafe, async, etc.) are parsed as BlockExpr, but are
237 // formatted without enclosing braces. If we encounter such block exprs,
238 // wrap them in another BlockExpr.
239 match expr {
240 ast::Expr::BlockExpr(block) if block.modifier().is_none() => block,
241 expr => make::block_expr(iter::empty(), Some(expr)),
242 }
243 }
244
245 let condition = match if_let_pat {
246 ast::Pat::LiteralPat(p)
247 if p.literal().map_or(false, |it| it.token().kind() == T![true]) =>
248 {
249 scrutinee
250 }
251 ast::Pat::LiteralPat(p)
252 if p.literal().map_or(false, |it| it.token().kind() == T![false]) =>
253 {
254 make::expr_prefix(T![!], scrutinee)
255 }
256 _ => make::expr_let(if_let_pat, scrutinee).into(),
257 };
258 let then_block = make_block_expr(then_expr.reset_indent());
259 let else_expr = if is_empty_expr(&else_expr) { None } else { Some(else_expr) };
260 let if_let_expr = make::expr_if(
261 condition.into(),
262 then_block,
263 else_expr.map(make_block_expr).map(ast::ElseBranch::Block),
264 )
265 .indent(IndentLevel::from_node(match_expr.syntax()));
266
267 edit.replace_ast::<ast::Expr>(match_expr.into(), if_let_expr);
268 },
269 )
270 }
271
272 /// Pick the pattern for the if let condition and return the expressions for the `then` body and `else` body in that order.
273 fn pick_pattern_and_expr_order(
274 sema: &hir::Semantics<'_, RootDatabase>,
275 pat: ast::Pat,
276 pat2: ast::Pat,
277 expr: ast::Expr,
278 expr2: ast::Expr,
279 ) -> Option<(ast::Pat, ast::Expr, ast::Expr)> {
280 let res = match (pat, pat2) {
281 (ast::Pat::WildcardPat(_), _) => return None,
282 (pat, ast::Pat::WildcardPat(_)) => (pat, expr, expr2),
283 (pat, _) if is_empty_expr(&expr2) => (pat, expr, expr2),
284 (_, pat) if is_empty_expr(&expr) => (pat, expr2, expr),
285 (pat, pat2) => match (binds_name(sema, &pat), binds_name(sema, &pat2)) {
286 (true, true) => return None,
287 (true, false) => (pat, expr, expr2),
288 (false, true) => (pat2, expr2, expr),
289 _ if is_sad_pat(sema, &pat) => (pat2, expr2, expr),
290 (false, false) => (pat, expr, expr2),
291 },
292 };
293 Some(res)
294 }
295
296 fn is_empty_expr(expr: &ast::Expr) -> bool {
297 match expr {
298 ast::Expr::BlockExpr(expr) => match expr.stmt_list() {
299 Some(it) => it.statements().next().is_none() && it.tail_expr().is_none(),
300 None => true,
301 },
302 ast::Expr::TupleExpr(expr) => expr.fields().next().is_none(),
303 _ => false,
304 }
305 }
306
307 fn binds_name(sema: &hir::Semantics<'_, RootDatabase>, pat: &ast::Pat) -> bool {
308 let binds_name_v = |pat| binds_name(sema, &pat);
309 match pat {
310 ast::Pat::IdentPat(pat) => !matches!(
311 pat.name().and_then(|name| NameClass::classify(sema, &name)),
312 Some(NameClass::ConstReference(_))
313 ),
314 ast::Pat::MacroPat(_) => true,
315 ast::Pat::OrPat(pat) => pat.pats().any(binds_name_v),
316 ast::Pat::SlicePat(pat) => pat.pats().any(binds_name_v),
317 ast::Pat::TuplePat(it) => it.fields().any(binds_name_v),
318 ast::Pat::TupleStructPat(it) => it.fields().any(binds_name_v),
319 ast::Pat::RecordPat(it) => it
320 .record_pat_field_list()
321 .map_or(false, |rpfl| rpfl.fields().flat_map(|rpf| rpf.pat()).any(binds_name_v)),
322 ast::Pat::RefPat(pat) => pat.pat().map_or(false, binds_name_v),
323 ast::Pat::BoxPat(pat) => pat.pat().map_or(false, binds_name_v),
324 ast::Pat::ParenPat(pat) => pat.pat().map_or(false, binds_name_v),
325 _ => false,
326 }
327 }
328
329 fn is_sad_pat(sema: &hir::Semantics<'_, RootDatabase>, pat: &ast::Pat) -> bool {
330 sema.type_of_pat(pat)
331 .and_then(|ty| TryEnum::from_ty(sema, &ty.adjusted()))
332 .map_or(false, |it| does_pat_match_variant(pat, &it.sad_pattern()))
333 }
334
335 #[cfg(test)]
336 mod tests {
337 use super::*;
338
339 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
340
341 #[test]
342 fn test_if_let_with_match_unapplicable_for_simple_ifs() {
343 check_assist_not_applicable(
344 replace_if_let_with_match,
345 r#"
346 fn main() {
347 if $0true {} else if false {} else {}
348 }
349 "#,
350 )
351 }
352
353 #[test]
354 fn test_if_with_match_no_else() {
355 check_assist(
356 replace_if_let_with_match,
357 r#"
358 pub fn foo(foo: bool) {
359 if foo$0 {
360 self.foo();
361 }
362 }
363 "#,
364 r#"
365 pub fn foo(foo: bool) {
366 match foo {
367 true => {
368 self.foo();
369 }
370 false => (),
371 }
372 }
373 "#,
374 )
375 }
376
377 #[test]
378 fn test_if_with_match_with_else() {
379 check_assist(
380 replace_if_let_with_match,
381 r#"
382 pub fn foo(foo: bool) {
383 if foo$0 {
384 self.foo();
385 } else {
386 self.bar();
387 }
388 }
389 "#,
390 r#"
391 pub fn foo(foo: bool) {
392 match foo {
393 true => {
394 self.foo();
395 }
396 false => {
397 self.bar();
398 }
399 }
400 }
401 "#,
402 )
403 }
404
405 #[test]
406 fn test_if_let_with_match_no_else() {
407 check_assist(
408 replace_if_let_with_match,
409 r#"
410 impl VariantData {
411 pub fn foo(&self) {
412 if $0let VariantData::Struct(..) = *self {
413 self.foo();
414 }
415 }
416 }
417 "#,
418 r#"
419 impl VariantData {
420 pub fn foo(&self) {
421 match *self {
422 VariantData::Struct(..) => {
423 self.foo();
424 }
425 _ => (),
426 }
427 }
428 }
429 "#,
430 )
431 }
432
433 #[test]
434 fn test_if_let_with_match_available_range_left() {
435 check_assist_not_applicable(
436 replace_if_let_with_match,
437 r#"
438 impl VariantData {
439 pub fn foo(&self) {
440 $0 if let VariantData::Struct(..) = *self {
441 self.foo();
442 }
443 }
444 }
445 "#,
446 )
447 }
448
449 #[test]
450 fn test_if_let_with_match_available_range_right() {
451 check_assist_not_applicable(
452 replace_if_let_with_match,
453 r#"
454 impl VariantData {
455 pub fn foo(&self) {
456 if let VariantData::Struct(..) = *self {$0
457 self.foo();
458 }
459 }
460 }
461 "#,
462 )
463 }
464
465 #[test]
466 fn test_if_let_with_match_let_chain() {
467 check_assist_not_applicable(
468 replace_if_let_with_match,
469 r#"
470 fn main() {
471 if $0let true = true && let Some(1) = None {}
472 }
473 "#,
474 )
475 }
476
477 #[test]
478 fn test_if_let_with_match_basic() {
479 check_assist(
480 replace_if_let_with_match,
481 r#"
482 impl VariantData {
483 pub fn is_struct(&self) -> bool {
484 if $0let VariantData::Struct(..) = *self {
485 true
486 } else if let VariantData::Tuple(..) = *self {
487 false
488 } else if cond() {
489 true
490 } else {
491 bar(
492 123
493 )
494 }
495 }
496 }
497 "#,
498 r#"
499 impl VariantData {
500 pub fn is_struct(&self) -> bool {
501 match *self {
502 VariantData::Struct(..) => true,
503 VariantData::Tuple(..) => false,
504 _ if cond() => true,
505 _ => {
506 bar(
507 123
508 )
509 }
510 }
511 }
512 }
513 "#,
514 )
515 }
516
517 #[test]
518 fn test_if_let_with_match_on_tail_if_let() {
519 check_assist(
520 replace_if_let_with_match,
521 r#"
522 impl VariantData {
523 pub fn is_struct(&self) -> bool {
524 if let VariantData::Struct(..) = *self {
525 true
526 } else if let$0 VariantData::Tuple(..) = *self {
527 false
528 } else {
529 false
530 }
531 }
532 }
533 "#,
534 r#"
535 impl VariantData {
536 pub fn is_struct(&self) -> bool {
537 if let VariantData::Struct(..) = *self {
538 true
539 } else {
540 match *self {
541 VariantData::Tuple(..) => false,
542 _ => false,
543 }
544 }
545 }
546 }
547 "#,
548 )
549 }
550
551 #[test]
552 fn special_case_option() {
553 check_assist(
554 replace_if_let_with_match,
555 r#"
556 //- minicore: option
557 fn foo(x: Option<i32>) {
558 $0if let Some(x) = x {
559 println!("{}", x)
560 } else {
561 println!("none")
562 }
563 }
564 "#,
565 r#"
566 fn foo(x: Option<i32>) {
567 match x {
568 Some(x) => println!("{}", x),
569 None => println!("none"),
570 }
571 }
572 "#,
573 );
574 }
575
576 #[test]
577 fn special_case_inverted_option() {
578 check_assist(
579 replace_if_let_with_match,
580 r#"
581 //- minicore: option
582 fn foo(x: Option<i32>) {
583 $0if let None = x {
584 println!("none")
585 } else {
586 println!("some")
587 }
588 }
589 "#,
590 r#"
591 fn foo(x: Option<i32>) {
592 match x {
593 None => println!("none"),
594 Some(_) => println!("some"),
595 }
596 }
597 "#,
598 );
599 }
600
601 #[test]
602 fn special_case_result() {
603 check_assist(
604 replace_if_let_with_match,
605 r#"
606 //- minicore: result
607 fn foo(x: Result<i32, ()>) {
608 $0if let Ok(x) = x {
609 println!("{}", x)
610 } else {
611 println!("none")
612 }
613 }
614 "#,
615 r#"
616 fn foo(x: Result<i32, ()>) {
617 match x {
618 Ok(x) => println!("{}", x),
619 Err(_) => println!("none"),
620 }
621 }
622 "#,
623 );
624 }
625
626 #[test]
627 fn special_case_inverted_result() {
628 check_assist(
629 replace_if_let_with_match,
630 r#"
631 //- minicore: result
632 fn foo(x: Result<i32, ()>) {
633 $0if let Err(x) = x {
634 println!("{}", x)
635 } else {
636 println!("ok")
637 }
638 }
639 "#,
640 r#"
641 fn foo(x: Result<i32, ()>) {
642 match x {
643 Err(x) => println!("{}", x),
644 Ok(_) => println!("ok"),
645 }
646 }
647 "#,
648 );
649 }
650
651 #[test]
652 fn nested_indent() {
653 check_assist(
654 replace_if_let_with_match,
655 r#"
656 fn main() {
657 if true {
658 $0if let Ok(rel_path) = path.strip_prefix(root_path) {
659 let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
660 Some((*id, rel_path))
661 } else {
662 None
663 }
664 }
665 }
666 "#,
667 r#"
668 fn main() {
669 if true {
670 match path.strip_prefix(root_path) {
671 Ok(rel_path) => {
672 let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
673 Some((*id, rel_path))
674 }
675 _ => None,
676 }
677 }
678 }
679 "#,
680 )
681 }
682
683 #[test]
684 fn nested_type() {
685 check_assist(
686 replace_if_let_with_match,
687 r#"
688 //- minicore: result
689 fn foo(x: Result<i32, ()>) {
690 let bar: Result<_, ()> = Ok(Some(1));
691 $0if let Ok(Some(_)) = bar {
692 ()
693 } else {
694 ()
695 }
696 }
697 "#,
698 r#"
699 fn foo(x: Result<i32, ()>) {
700 let bar: Result<_, ()> = Ok(Some(1));
701 match bar {
702 Ok(Some(_)) => (),
703 _ => (),
704 }
705 }
706 "#,
707 );
708 }
709
710 #[test]
711 fn test_replace_match_with_if_let_unwraps_simple_expressions() {
712 check_assist(
713 replace_match_with_if_let,
714 r#"
715 impl VariantData {
716 pub fn is_struct(&self) -> bool {
717 $0match *self {
718 VariantData::Struct(..) => true,
719 _ => false,
720 }
721 }
722 } "#,
723 r#"
724 impl VariantData {
725 pub fn is_struct(&self) -> bool {
726 if let VariantData::Struct(..) = *self {
727 true
728 } else {
729 false
730 }
731 }
732 } "#,
733 )
734 }
735
736 #[test]
737 fn test_replace_match_with_if_let_doesnt_unwrap_multiline_expressions() {
738 check_assist(
739 replace_match_with_if_let,
740 r#"
741 fn foo() {
742 $0match a {
743 VariantData::Struct(..) => {
744 bar(
745 123
746 )
747 }
748 _ => false,
749 }
750 } "#,
751 r#"
752 fn foo() {
753 if let VariantData::Struct(..) = a {
754 bar(
755 123
756 )
757 } else {
758 false
759 }
760 } "#,
761 )
762 }
763
764 #[test]
765 fn replace_match_with_if_let_target() {
766 check_assist_target(
767 replace_match_with_if_let,
768 r#"
769 impl VariantData {
770 pub fn is_struct(&self) -> bool {
771 $0match *self {
772 VariantData::Struct(..) => true,
773 _ => false,
774 }
775 }
776 } "#,
777 r#"match *self {
778 VariantData::Struct(..) => true,
779 _ => false,
780 }"#,
781 );
782 }
783
784 #[test]
785 fn special_case_option_match_to_if_let() {
786 check_assist(
787 replace_match_with_if_let,
788 r#"
789 //- minicore: option
790 fn foo(x: Option<i32>) {
791 $0match x {
792 Some(x) => println!("{}", x),
793 None => println!("none"),
794 }
795 }
796 "#,
797 r#"
798 fn foo(x: Option<i32>) {
799 if let Some(x) = x {
800 println!("{}", x)
801 } else {
802 println!("none")
803 }
804 }
805 "#,
806 );
807 }
808
809 #[test]
810 fn special_case_result_match_to_if_let() {
811 check_assist(
812 replace_match_with_if_let,
813 r#"
814 //- minicore: result
815 fn foo(x: Result<i32, ()>) {
816 $0match x {
817 Ok(x) => println!("{}", x),
818 Err(_) => println!("none"),
819 }
820 }
821 "#,
822 r#"
823 fn foo(x: Result<i32, ()>) {
824 if let Ok(x) = x {
825 println!("{}", x)
826 } else {
827 println!("none")
828 }
829 }
830 "#,
831 );
832 }
833
834 #[test]
835 fn nested_indent_match_to_if_let() {
836 check_assist(
837 replace_match_with_if_let,
838 r#"
839 fn main() {
840 if true {
841 $0match path.strip_prefix(root_path) {
842 Ok(rel_path) => {
843 let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
844 Some((*id, rel_path))
845 }
846 _ => None,
847 }
848 }
849 }
850 "#,
851 r#"
852 fn main() {
853 if true {
854 if let Ok(rel_path) = path.strip_prefix(root_path) {
855 let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
856 Some((*id, rel_path))
857 } else {
858 None
859 }
860 }
861 }
862 "#,
863 )
864 }
865
866 #[test]
867 fn replace_match_with_if_let_empty_wildcard_expr() {
868 check_assist(
869 replace_match_with_if_let,
870 r#"
871 fn main() {
872 $0match path.strip_prefix(root_path) {
873 Ok(rel_path) => println!("{}", rel_path),
874 _ => (),
875 }
876 }
877 "#,
878 r#"
879 fn main() {
880 if let Ok(rel_path) = path.strip_prefix(root_path) {
881 println!("{}", rel_path)
882 }
883 }
884 "#,
885 )
886 }
887
888 #[test]
889 fn replace_match_with_if_let_number_body() {
890 check_assist(
891 replace_match_with_if_let,
892 r#"
893 fn main() {
894 $0match Ok(()) {
895 Ok(()) => {},
896 Err(_) => 0,
897 }
898 }
899 "#,
900 r#"
901 fn main() {
902 if let Err(_) = Ok(()) {
903 0
904 }
905 }
906 "#,
907 )
908 }
909
910 #[test]
911 fn replace_match_with_if_let_exhaustive() {
912 check_assist(
913 replace_match_with_if_let,
914 r#"
915 fn print_source(def_source: ModuleSource) {
916 match def_so$0urce {
917 ModuleSource::SourceFile(..) => { println!("source file"); }
918 ModuleSource::Module(..) => { println!("module"); }
919 }
920 }
921 "#,
922 r#"
923 fn print_source(def_source: ModuleSource) {
924 if let ModuleSource::SourceFile(..) = def_source { println!("source file"); } else { println!("module"); }
925 }
926 "#,
927 )
928 }
929
930 #[test]
931 fn replace_match_with_if_let_prefer_name_bind() {
932 check_assist(
933 replace_match_with_if_let,
934 r#"
935 fn foo() {
936 match $0Foo(0) {
937 Foo(_) => (),
938 Bar(bar) => println!("bar {}", bar),
939 }
940 }
941 "#,
942 r#"
943 fn foo() {
944 if let Bar(bar) = Foo(0) {
945 println!("bar {}", bar)
946 }
947 }
948 "#,
949 );
950 check_assist(
951 replace_match_with_if_let,
952 r#"
953 fn foo() {
954 match $0Foo(0) {
955 Bar(bar) => println!("bar {}", bar),
956 Foo(_) => (),
957 }
958 }
959 "#,
960 r#"
961 fn foo() {
962 if let Bar(bar) = Foo(0) {
963 println!("bar {}", bar)
964 }
965 }
966 "#,
967 );
968 }
969
970 #[test]
971 fn replace_match_with_if_let_prefer_nonempty_body() {
972 check_assist(
973 replace_match_with_if_let,
974 r#"
975 fn foo() {
976 match $0Ok(0) {
977 Ok(value) => {},
978 Err(err) => eprintln!("{}", err),
979 }
980 }
981 "#,
982 r#"
983 fn foo() {
984 if let Err(err) = Ok(0) {
985 eprintln!("{}", err)
986 }
987 }
988 "#,
989 );
990 check_assist(
991 replace_match_with_if_let,
992 r#"
993 fn foo() {
994 match $0Ok(0) {
995 Err(err) => eprintln!("{}", err),
996 Ok(value) => {},
997 }
998 }
999 "#,
1000 r#"
1001 fn foo() {
1002 if let Err(err) = Ok(0) {
1003 eprintln!("{}", err)
1004 }
1005 }
1006 "#,
1007 );
1008 }
1009
1010 #[test]
1011 fn replace_match_with_if_let_rejects_double_name_bindings() {
1012 check_assist_not_applicable(
1013 replace_match_with_if_let,
1014 r#"
1015 fn foo() {
1016 match $0Foo(0) {
1017 Foo(foo) => println!("bar {}", foo),
1018 Bar(bar) => println!("bar {}", bar),
1019 }
1020 }
1021 "#,
1022 );
1023 }
1024
1025 #[test]
1026 fn test_replace_match_with_if_let_keeps_unsafe_block() {
1027 check_assist(
1028 replace_match_with_if_let,
1029 r#"
1030 impl VariantData {
1031 pub fn is_struct(&self) -> bool {
1032 $0match *self {
1033 VariantData::Struct(..) => true,
1034 _ => unsafe { unreachable_unchecked() },
1035 }
1036 }
1037 } "#,
1038 r#"
1039 impl VariantData {
1040 pub fn is_struct(&self) -> bool {
1041 if let VariantData::Struct(..) = *self {
1042 true
1043 } else {
1044 unsafe { unreachable_unchecked() }
1045 }
1046 }
1047 } "#,
1048 )
1049 }
1050
1051 #[test]
1052 fn test_replace_match_with_if_let_forces_else() {
1053 check_assist(
1054 replace_match_with_if_let,
1055 r#"
1056 fn main() {
1057 match$0 0 {
1058 0 => (),
1059 _ => code(),
1060 }
1061 }
1062 "#,
1063 r#"
1064 fn main() {
1065 if let 0 = 0 {
1066 ()
1067 } else {
1068 code()
1069 }
1070 }
1071 "#,
1072 )
1073 }
1074
1075 #[test]
1076 fn test_replace_match_with_if_bool() {
1077 check_assist(
1078 replace_match_with_if_let,
1079 r#"
1080 fn main() {
1081 match$0 b {
1082 true => (),
1083 _ => code(),
1084 }
1085 }
1086 "#,
1087 r#"
1088 fn main() {
1089 if b {
1090 ()
1091 } else {
1092 code()
1093 }
1094 }
1095 "#,
1096 );
1097 check_assist(
1098 replace_match_with_if_let,
1099 r#"
1100 fn main() {
1101 match$0 b {
1102 false => code(),
1103 true => (),
1104 }
1105 }
1106 "#,
1107 r#"
1108 fn main() {
1109 if !b {
1110 code()
1111 }
1112 }
1113 "#,
1114 );
1115 check_assist(
1116 replace_match_with_if_let,
1117 r#"
1118 fn main() {
1119 match$0 b {
1120 false => (),
1121 true => code(),
1122 }
1123 }
1124 "#,
1125 r#"
1126 fn main() {
1127 if b {
1128 code()
1129 }
1130 }
1131 "#,
1132 )
1133 }
1134 }