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