]> git.lizzy.rs Git - rust.git/blob - crates/ide_assists/src/handlers/replace_if_let_with_match.rs
Merge #11424
[rust.git] / 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     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,
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 {
100         // Don't offer turning an if (chain) without patterns into a match
101         return None;
102     }
103
104     acc.add(
105         AssistId("replace_if_let_with_match", AssistKind::RefactorRewrite),
106         "Replace if let with match",
107         available_range,
108         move |edit| {
109             let match_expr = {
110                 let else_arm = make_else_arm(ctx, else_block, &cond_bodies);
111                 let make_match_arm = |(pat, body): (_, ast::BlockExpr)| {
112                     let body = body.reset_indent().indent(IndentLevel(1));
113                     match pat {
114                         Either::Left(pat) => {
115                             make::match_arm(iter::once(pat), None, unwrap_trivial_block(body))
116                         }
117                         Either::Right(expr) => make::match_arm(
118                             iter::once(make::wildcard_pat().into()),
119                             Some(expr),
120                             unwrap_trivial_block(body),
121                         ),
122                     }
123                 };
124                 let arms = cond_bodies.into_iter().map(make_match_arm).chain(iter::once(else_arm));
125                 let match_expr = make::expr_match(scrutinee_to_be_expr, make::match_arm_list(arms));
126                 match_expr.indent(IndentLevel::from_node(if_expr.syntax()))
127             };
128
129             let has_preceding_if_expr =
130                 if_expr.syntax().parent().map_or(false, |it| ast::IfExpr::can_cast(it.kind()));
131             let expr = if has_preceding_if_expr {
132                 // make sure we replace the `else if let ...` with a block so we don't end up with `else expr`
133                 make::block_expr(None, Some(match_expr)).into()
134             } else {
135                 match_expr
136             };
137             edit.replace_ast::<ast::Expr>(if_expr.into(), expr);
138         },
139     )
140 }
141
142 fn make_else_arm(
143     ctx: &AssistContext,
144     else_block: Option<ast::BlockExpr>,
145     conditionals: &[(Either<ast::Pat, ast::Expr>, ast::BlockExpr)],
146 ) -> ast::MatchArm {
147     if let Some(else_block) = else_block {
148         let pattern = if let [(Either::Left(pat), _)] = conditionals {
149             ctx.sema
150                 .type_of_pat(pat)
151                 .and_then(|ty| TryEnum::from_ty(&ctx.sema, &ty.adjusted()))
152                 .zip(Some(pat))
153         } else {
154             None
155         };
156         let pattern = match pattern {
157             Some((it, pat)) => {
158                 if does_pat_match_variant(pat, &it.sad_pattern()) {
159                     it.happy_pattern_wildcard()
160                 } else if does_nested_pattern(pat) {
161                     make::wildcard_pat().into()
162                 } else {
163                     it.sad_pattern()
164                 }
165             }
166             None => make::wildcard_pat().into(),
167         };
168         make::match_arm(iter::once(pattern), None, unwrap_trivial_block(else_block))
169     } else {
170         make::match_arm(iter::once(make::wildcard_pat().into()), None, make::expr_unit())
171     }
172 }
173
174 // Assist: replace_match_with_if_let
175 //
176 // Replaces a binary `match` with a wildcard pattern and no guards with an `if let` expression.
177 //
178 // ```
179 // enum Action { Move { distance: u32 }, Stop }
180 //
181 // fn handle(action: Action) {
182 //     $0match action {
183 //         Action::Move { distance } => foo(distance),
184 //         _ => bar(),
185 //     }
186 // }
187 // ```
188 // ->
189 // ```
190 // enum Action { Move { distance: u32 }, Stop }
191 //
192 // fn handle(action: Action) {
193 //     if let Action::Move { distance } = action {
194 //         foo(distance)
195 //     } else {
196 //         bar()
197 //     }
198 // }
199 // ```
200 pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
201     let match_expr: ast::MatchExpr = ctx.find_node_at_offset()?;
202
203     let mut arms = match_expr.match_arm_list()?.arms();
204     let (first_arm, second_arm) = (arms.next()?, arms.next()?);
205     if arms.next().is_some() || first_arm.guard().is_some() || second_arm.guard().is_some() {
206         return None;
207     }
208
209     let (if_let_pat, then_expr, else_expr) = pick_pattern_and_expr_order(
210         &ctx.sema,
211         first_arm.pat()?,
212         second_arm.pat()?,
213         first_arm.expr()?,
214         second_arm.expr()?,
215     )?;
216     let scrutinee = match_expr.expr()?;
217
218     let target = match_expr.syntax().text_range();
219     acc.add(
220         AssistId("replace_match_with_if_let", AssistKind::RefactorRewrite),
221         "Replace match with if let",
222         target,
223         move |edit| {
224             fn make_block_expr(expr: ast::Expr) -> ast::BlockExpr {
225                 // Blocks with modifiers (unsafe, async, etc.) are parsed as BlockExpr, but are
226                 // formatted without enclosing braces. If we encounter such block exprs,
227                 // wrap them in another BlockExpr.
228                 match expr {
229                     ast::Expr::BlockExpr(block) if block.modifier().is_none() => block,
230                     expr => make::block_expr(iter::empty(), Some(expr)),
231                 }
232             }
233
234             let condition = make::expr_let(if_let_pat, scrutinee);
235             let then_block = make_block_expr(then_expr.reset_indent());
236             let else_expr = if is_empty_expr(&else_expr) { None } else { Some(else_expr) };
237             let if_let_expr = make::expr_if(
238                 condition.into(),
239                 then_block,
240                 else_expr.map(make_block_expr).map(ast::ElseBranch::Block),
241             )
242             .indent(IndentLevel::from_node(match_expr.syntax()));
243
244             edit.replace_ast::<ast::Expr>(match_expr.into(), if_let_expr);
245         },
246     )
247 }
248
249 /// Pick the pattern for the if let condition and return the expressions for the `then` body and `else` body in that order.
250 fn pick_pattern_and_expr_order(
251     sema: &hir::Semantics<RootDatabase>,
252     pat: ast::Pat,
253     pat2: ast::Pat,
254     expr: ast::Expr,
255     expr2: ast::Expr,
256 ) -> Option<(ast::Pat, ast::Expr, ast::Expr)> {
257     let res = match (pat, pat2) {
258         (ast::Pat::WildcardPat(_), _) => return None,
259         (pat, _) if is_empty_expr(&expr2) => (pat, expr, expr2),
260         (_, pat) if is_empty_expr(&expr) => (pat, expr2, expr),
261         (pat, pat2) => match (binds_name(sema, &pat), binds_name(sema, &pat2)) {
262             (true, true) => return None,
263             (true, false) => (pat, expr, expr2),
264             (false, true) => (pat2, expr2, expr),
265             _ if is_sad_pat(sema, &pat) => (pat2, expr2, expr),
266             (false, false) => (pat, expr, expr2),
267         },
268     };
269     Some(res)
270 }
271
272 fn is_empty_expr(expr: &ast::Expr) -> bool {
273     match expr {
274         ast::Expr::BlockExpr(expr) => match expr.stmt_list() {
275             Some(it) => it.statements().next().is_none() && it.tail_expr().is_none(),
276             None => true,
277         },
278         ast::Expr::TupleExpr(expr) => expr.fields().next().is_none(),
279         _ => false,
280     }
281 }
282
283 fn binds_name(sema: &hir::Semantics<RootDatabase>, pat: &ast::Pat) -> bool {
284     let binds_name_v = |pat| binds_name(sema, &pat);
285     match pat {
286         ast::Pat::IdentPat(pat) => !matches!(
287             pat.name().and_then(|name| NameClass::classify(sema, &name)),
288             Some(NameClass::ConstReference(_))
289         ),
290         ast::Pat::MacroPat(_) => true,
291         ast::Pat::OrPat(pat) => pat.pats().any(binds_name_v),
292         ast::Pat::SlicePat(pat) => pat.pats().any(binds_name_v),
293         ast::Pat::TuplePat(it) => it.fields().any(binds_name_v),
294         ast::Pat::TupleStructPat(it) => it.fields().any(binds_name_v),
295         ast::Pat::RecordPat(it) => it
296             .record_pat_field_list()
297             .map_or(false, |rpfl| rpfl.fields().flat_map(|rpf| rpf.pat()).any(binds_name_v)),
298         ast::Pat::RefPat(pat) => pat.pat().map_or(false, binds_name_v),
299         ast::Pat::BoxPat(pat) => pat.pat().map_or(false, binds_name_v),
300         ast::Pat::ParenPat(pat) => pat.pat().map_or(false, binds_name_v),
301         _ => false,
302     }
303 }
304
305 fn is_sad_pat(sema: &hir::Semantics<RootDatabase>, pat: &ast::Pat) -> bool {
306     sema.type_of_pat(pat)
307         .and_then(|ty| TryEnum::from_ty(sema, &ty.adjusted()))
308         .map_or(false, |it| does_pat_match_variant(pat, &it.sad_pattern()))
309 }
310
311 #[cfg(test)]
312 mod tests {
313     use super::*;
314
315     use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
316
317     #[test]
318     fn test_if_let_with_match_unapplicable_for_simple_ifs() {
319         check_assist_not_applicable(
320             replace_if_let_with_match,
321             r#"
322 fn main() {
323     if $0true {} else if false {} else {}
324 }
325 "#,
326         )
327     }
328
329     #[test]
330     fn test_if_let_with_match_no_else() {
331         check_assist(
332             replace_if_let_with_match,
333             r#"
334 impl VariantData {
335     pub fn foo(&self) {
336         if $0let VariantData::Struct(..) = *self {
337             self.foo();
338         }
339     }
340 }
341 "#,
342             r#"
343 impl VariantData {
344     pub fn foo(&self) {
345         match *self {
346             VariantData::Struct(..) => {
347                 self.foo();
348             }
349             _ => (),
350         }
351     }
352 }
353 "#,
354         )
355     }
356
357     #[test]
358     fn test_if_let_with_match_available_range_left() {
359         check_assist_not_applicable(
360             replace_if_let_with_match,
361             r#"
362 impl VariantData {
363     pub fn foo(&self) {
364         $0 if let VariantData::Struct(..) = *self {
365             self.foo();
366         }
367     }
368 }
369 "#,
370         )
371     }
372
373     #[test]
374     fn test_if_let_with_match_available_range_right() {
375         check_assist_not_applicable(
376             replace_if_let_with_match,
377             r#"
378 impl VariantData {
379     pub fn foo(&self) {
380         if let VariantData::Struct(..) = *self {$0
381             self.foo();
382         }
383     }
384 }
385 "#,
386         )
387     }
388
389     #[test]
390     fn test_if_let_with_match_let_chain() {
391         check_assist_not_applicable(
392             replace_if_let_with_match,
393             r#"
394 fn main() {
395     if $0let true = true && let Some(1) = None {}
396 }
397 "#,
398         )
399     }
400
401     #[test]
402     fn test_if_let_with_match_basic() {
403         check_assist(
404             replace_if_let_with_match,
405             r#"
406 impl VariantData {
407     pub fn is_struct(&self) -> bool {
408         if $0let VariantData::Struct(..) = *self {
409             true
410         } else if let VariantData::Tuple(..) = *self {
411             false
412         } else if cond() {
413             true
414         } else {
415             bar(
416                 123
417             )
418         }
419     }
420 }
421 "#,
422             r#"
423 impl VariantData {
424     pub fn is_struct(&self) -> bool {
425         match *self {
426             VariantData::Struct(..) => true,
427             VariantData::Tuple(..) => false,
428             _ if cond() => true,
429             _ => {
430                     bar(
431                         123
432                     )
433                 }
434         }
435     }
436 }
437 "#,
438         )
439     }
440
441     #[test]
442     fn test_if_let_with_match_on_tail_if_let() {
443         check_assist(
444             replace_if_let_with_match,
445             r#"
446 impl VariantData {
447     pub fn is_struct(&self) -> bool {
448         if let VariantData::Struct(..) = *self {
449             true
450         } else if let$0 VariantData::Tuple(..) = *self {
451             false
452         } else {
453             false
454         }
455     }
456 }
457 "#,
458             r#"
459 impl VariantData {
460     pub fn is_struct(&self) -> bool {
461         if let VariantData::Struct(..) = *self {
462             true
463         } else {
464     match *self {
465             VariantData::Tuple(..) => false,
466             _ => false,
467         }
468 }
469     }
470 }
471 "#,
472         )
473     }
474
475     #[test]
476     fn special_case_option() {
477         check_assist(
478             replace_if_let_with_match,
479             r#"
480 //- minicore: option
481 fn foo(x: Option<i32>) {
482     $0if let Some(x) = x {
483         println!("{}", x)
484     } else {
485         println!("none")
486     }
487 }
488 "#,
489             r#"
490 fn foo(x: Option<i32>) {
491     match x {
492         Some(x) => println!("{}", x),
493         None => println!("none"),
494     }
495 }
496 "#,
497         );
498     }
499
500     #[test]
501     fn special_case_inverted_option() {
502         check_assist(
503             replace_if_let_with_match,
504             r#"
505 //- minicore: option
506 fn foo(x: Option<i32>) {
507     $0if let None = x {
508         println!("none")
509     } else {
510         println!("some")
511     }
512 }
513 "#,
514             r#"
515 fn foo(x: Option<i32>) {
516     match x {
517         None => println!("none"),
518         Some(_) => println!("some"),
519     }
520 }
521 "#,
522         );
523     }
524
525     #[test]
526     fn special_case_result() {
527         check_assist(
528             replace_if_let_with_match,
529             r#"
530 //- minicore: result
531 fn foo(x: Result<i32, ()>) {
532     $0if let Ok(x) = x {
533         println!("{}", x)
534     } else {
535         println!("none")
536     }
537 }
538 "#,
539             r#"
540 fn foo(x: Result<i32, ()>) {
541     match x {
542         Ok(x) => println!("{}", x),
543         Err(_) => println!("none"),
544     }
545 }
546 "#,
547         );
548     }
549
550     #[test]
551     fn special_case_inverted_result() {
552         check_assist(
553             replace_if_let_with_match,
554             r#"
555 //- minicore: result
556 fn foo(x: Result<i32, ()>) {
557     $0if let Err(x) = x {
558         println!("{}", x)
559     } else {
560         println!("ok")
561     }
562 }
563 "#,
564             r#"
565 fn foo(x: Result<i32, ()>) {
566     match x {
567         Err(x) => println!("{}", x),
568         Ok(_) => println!("ok"),
569     }
570 }
571 "#,
572         );
573     }
574
575     #[test]
576     fn nested_indent() {
577         check_assist(
578             replace_if_let_with_match,
579             r#"
580 fn main() {
581     if true {
582         $0if let Ok(rel_path) = path.strip_prefix(root_path) {
583             let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
584             Some((*id, rel_path))
585         } else {
586             None
587         }
588     }
589 }
590 "#,
591             r#"
592 fn main() {
593     if true {
594         match path.strip_prefix(root_path) {
595             Ok(rel_path) => {
596                 let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
597                 Some((*id, rel_path))
598             }
599             _ => None,
600         }
601     }
602 }
603 "#,
604         )
605     }
606
607     #[test]
608     fn nested_type() {
609         check_assist(
610             replace_if_let_with_match,
611             r#"
612 //- minicore: result
613 fn foo(x: Result<i32, ()>) {
614     let bar: Result<_, ()> = Ok(Some(1));
615     $0if let Ok(Some(_)) = bar {
616         ()
617     } else {
618         ()
619     }
620 }
621 "#,
622             r#"
623 fn foo(x: Result<i32, ()>) {
624     let bar: Result<_, ()> = Ok(Some(1));
625     match bar {
626         Ok(Some(_)) => (),
627         _ => (),
628     }
629 }
630 "#,
631         );
632     }
633
634     #[test]
635     fn test_replace_match_with_if_let_unwraps_simple_expressions() {
636         check_assist(
637             replace_match_with_if_let,
638             r#"
639 impl VariantData {
640     pub fn is_struct(&self) -> bool {
641         $0match *self {
642             VariantData::Struct(..) => true,
643             _ => false,
644         }
645     }
646 }           "#,
647             r#"
648 impl VariantData {
649     pub fn is_struct(&self) -> bool {
650         if let VariantData::Struct(..) = *self {
651             true
652         } else {
653             false
654         }
655     }
656 }           "#,
657         )
658     }
659
660     #[test]
661     fn test_replace_match_with_if_let_doesnt_unwrap_multiline_expressions() {
662         check_assist(
663             replace_match_with_if_let,
664             r#"
665 fn foo() {
666     $0match a {
667         VariantData::Struct(..) => {
668             bar(
669                 123
670             )
671         }
672         _ => false,
673     }
674 }           "#,
675             r#"
676 fn foo() {
677     if let VariantData::Struct(..) = a {
678         bar(
679             123
680         )
681     } else {
682         false
683     }
684 }           "#,
685         )
686     }
687
688     #[test]
689     fn replace_match_with_if_let_target() {
690         check_assist_target(
691             replace_match_with_if_let,
692             r#"
693 impl VariantData {
694     pub fn is_struct(&self) -> bool {
695         $0match *self {
696             VariantData::Struct(..) => true,
697             _ => false,
698         }
699     }
700 }           "#,
701             r#"match *self {
702             VariantData::Struct(..) => true,
703             _ => false,
704         }"#,
705         );
706     }
707
708     #[test]
709     fn special_case_option_match_to_if_let() {
710         check_assist(
711             replace_match_with_if_let,
712             r#"
713 //- minicore: option
714 fn foo(x: Option<i32>) {
715     $0match x {
716         Some(x) => println!("{}", x),
717         None => println!("none"),
718     }
719 }
720 "#,
721             r#"
722 fn foo(x: Option<i32>) {
723     if let Some(x) = x {
724         println!("{}", x)
725     } else {
726         println!("none")
727     }
728 }
729 "#,
730         );
731     }
732
733     #[test]
734     fn special_case_result_match_to_if_let() {
735         check_assist(
736             replace_match_with_if_let,
737             r#"
738 //- minicore: result
739 fn foo(x: Result<i32, ()>) {
740     $0match x {
741         Ok(x) => println!("{}", x),
742         Err(_) => println!("none"),
743     }
744 }
745 "#,
746             r#"
747 fn foo(x: Result<i32, ()>) {
748     if let Ok(x) = x {
749         println!("{}", x)
750     } else {
751         println!("none")
752     }
753 }
754 "#,
755         );
756     }
757
758     #[test]
759     fn nested_indent_match_to_if_let() {
760         check_assist(
761             replace_match_with_if_let,
762             r#"
763 fn main() {
764     if true {
765         $0match path.strip_prefix(root_path) {
766             Ok(rel_path) => {
767                 let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
768                 Some((*id, rel_path))
769             }
770             _ => None,
771         }
772     }
773 }
774 "#,
775             r#"
776 fn main() {
777     if true {
778         if let Ok(rel_path) = path.strip_prefix(root_path) {
779             let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
780             Some((*id, rel_path))
781         } else {
782             None
783         }
784     }
785 }
786 "#,
787         )
788     }
789
790     #[test]
791     fn replace_match_with_if_let_empty_wildcard_expr() {
792         check_assist(
793             replace_match_with_if_let,
794             r#"
795 fn main() {
796     $0match path.strip_prefix(root_path) {
797         Ok(rel_path) => println!("{}", rel_path),
798         _ => (),
799     }
800 }
801 "#,
802             r#"
803 fn main() {
804     if let Ok(rel_path) = path.strip_prefix(root_path) {
805         println!("{}", rel_path)
806     }
807 }
808 "#,
809         )
810     }
811
812     #[test]
813     fn replace_match_with_if_let_number_body() {
814         check_assist(
815             replace_match_with_if_let,
816             r#"
817 fn main() {
818     $0match Ok(()) {
819         Ok(()) => {},
820         Err(_) => 0,
821     }
822 }
823 "#,
824             r#"
825 fn main() {
826     if let Err(_) = Ok(()) {
827         0
828     }
829 }
830 "#,
831         )
832     }
833
834     #[test]
835     fn replace_match_with_if_let_exhaustive() {
836         check_assist(
837             replace_match_with_if_let,
838             r#"
839 fn print_source(def_source: ModuleSource) {
840     match def_so$0urce {
841         ModuleSource::SourceFile(..) => { println!("source file"); }
842         ModuleSource::Module(..) => { println!("module"); }
843     }
844 }
845 "#,
846             r#"
847 fn print_source(def_source: ModuleSource) {
848     if let ModuleSource::SourceFile(..) = def_source { println!("source file"); } else { println!("module"); }
849 }
850 "#,
851         )
852     }
853
854     #[test]
855     fn replace_match_with_if_let_prefer_name_bind() {
856         check_assist(
857             replace_match_with_if_let,
858             r#"
859 fn foo() {
860     match $0Foo(0) {
861         Foo(_) => (),
862         Bar(bar) => println!("bar {}", bar),
863     }
864 }
865 "#,
866             r#"
867 fn foo() {
868     if let Bar(bar) = Foo(0) {
869         println!("bar {}", bar)
870     }
871 }
872 "#,
873         );
874         check_assist(
875             replace_match_with_if_let,
876             r#"
877 fn foo() {
878     match $0Foo(0) {
879         Bar(bar) => println!("bar {}", bar),
880         Foo(_) => (),
881     }
882 }
883 "#,
884             r#"
885 fn foo() {
886     if let Bar(bar) = Foo(0) {
887         println!("bar {}", bar)
888     }
889 }
890 "#,
891         );
892     }
893
894     #[test]
895     fn replace_match_with_if_let_prefer_nonempty_body() {
896         check_assist(
897             replace_match_with_if_let,
898             r#"
899 fn foo() {
900     match $0Ok(0) {
901         Ok(value) => {},
902         Err(err) => eprintln!("{}", err),
903     }
904 }
905 "#,
906             r#"
907 fn foo() {
908     if let Err(err) = Ok(0) {
909         eprintln!("{}", err)
910     }
911 }
912 "#,
913         );
914         check_assist(
915             replace_match_with_if_let,
916             r#"
917 fn foo() {
918     match $0Ok(0) {
919         Err(err) => eprintln!("{}", err),
920         Ok(value) => {},
921     }
922 }
923 "#,
924             r#"
925 fn foo() {
926     if let Err(err) = Ok(0) {
927         eprintln!("{}", err)
928     }
929 }
930 "#,
931         );
932     }
933
934     #[test]
935     fn replace_match_with_if_let_rejects_double_name_bindings() {
936         check_assist_not_applicable(
937             replace_match_with_if_let,
938             r#"
939 fn foo() {
940     match $0Foo(0) {
941         Foo(foo) => println!("bar {}", foo),
942         Bar(bar) => println!("bar {}", bar),
943     }
944 }
945 "#,
946         );
947     }
948
949     #[test]
950     fn test_replace_match_with_if_let_keeps_unsafe_block() {
951         check_assist(
952             replace_match_with_if_let,
953             r#"
954 impl VariantData {
955     pub fn is_struct(&self) -> bool {
956         $0match *self {
957             VariantData::Struct(..) => true,
958             _ => unsafe { unreachable_unchecked() },
959         }
960     }
961 }           "#,
962             r#"
963 impl VariantData {
964     pub fn is_struct(&self) -> bool {
965         if let VariantData::Struct(..) = *self {
966             true
967         } else {
968             unsafe { unreachable_unchecked() }
969         }
970     }
971 }           "#,
972         )
973     }
974 }