ide_assists/handlers/
replace_if_let_with_match.rs

1use std::iter::successors;
2
3use ide_db::{RootDatabase, defs::NameClass, ty_filter::TryEnum};
4use syntax::{
5    AstNode, Edition, SyntaxKind, T, TextRange,
6    ast::{self, HasName, edit::IndentLevel, edit_in_place::Indent, syntax_factory::SyntaxFactory},
7    syntax_editor::SyntaxEditor,
8};
9
10use crate::{
11    AssistContext, AssistId, Assists,
12    utils::{does_pat_match_variant, does_pat_variant_nested_or_literal, unwrap_trivial_block},
13};
14
15// Assist: replace_if_let_with_match
16//
17// Replaces a `if let` expression with a `match` expression.
18//
19// ```
20// enum Action { Move { distance: u32 }, Stop }
21//
22// fn handle(action: Action) {
23//     $0if let Action::Move { distance } = action {
24//         foo(distance)
25//     } else {
26//         bar()
27//     }
28// }
29// ```
30// ->
31// ```
32// enum Action { Move { distance: u32 }, Stop }
33//
34// fn handle(action: Action) {
35//     match action {
36//         Action::Move { distance } => foo(distance),
37//         _ => bar(),
38//     }
39// }
40// ```
41pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
42    let if_expr: ast::IfExpr = ctx.find_node_at_offset()?;
43    let available_range = TextRange::new(
44        if_expr.syntax().text_range().start(),
45        if_expr.then_branch()?.syntax().text_range().start(),
46    );
47    let cursor_in_range = available_range.contains_range(ctx.selection_trimmed());
48    if !cursor_in_range {
49        return None;
50    }
51    let mut else_block = None;
52    let indent = if_expr.indent_level();
53    let if_exprs = successors(Some(if_expr.clone()), |expr| match expr.else_branch()? {
54        ast::ElseBranch::IfExpr(expr) => Some(expr),
55        ast::ElseBranch::Block(block) => {
56            let block = unwrap_trivial_block(block).clone_for_update();
57            block.reindent_to(IndentLevel(1));
58            else_block = Some(block);
59            None
60        }
61    });
62    let scrutinee_to_be_expr = if_expr.condition()?;
63    let scrutinee_to_be_expr = match let_and_guard(&scrutinee_to_be_expr) {
64        (Some(let_expr), _) => let_expr.expr()?,
65        (None, cond) => cond?,
66    };
67
68    let mut pat_seen = false;
69    let mut cond_bodies = Vec::new();
70    for if_expr in if_exprs {
71        let cond = if_expr.condition()?;
72        let (cond, guard) = match let_and_guard(&cond) {
73            (None, guard) => (None, Some(guard?)),
74            (Some(let_), guard) => {
75                let pat = let_.pat()?;
76                let expr = let_.expr()?;
77                if scrutinee_to_be_expr.syntax().text() != expr.syntax().text() {
78                    // Only if all condition expressions are equal we can merge them into a match
79                    return None;
80                }
81                pat_seen = true;
82                (Some(pat), guard)
83            }
84        };
85        if let Some(guard) = &guard {
86            guard.dedent(indent);
87            guard.indent(IndentLevel(1));
88        }
89        let body = if_expr.then_branch()?.clone_for_update();
90        body.indent(IndentLevel(1));
91        cond_bodies.push((cond, guard, body));
92    }
93
94    if !pat_seen && cond_bodies.len() != 1 {
95        // Don't offer turning an if (chain) without patterns into a match,
96        // unless its a simple `if cond { .. } (else { .. })`
97        return None;
98    }
99
100    let let_ = if pat_seen { " let" } else { "" };
101
102    acc.add(
103        AssistId::refactor_rewrite("replace_if_let_with_match"),
104        format!("Replace if{let_} with match"),
105        available_range,
106        move |builder| {
107            let make = SyntaxFactory::with_mappings();
108            let match_expr: ast::Expr = {
109                let else_arm = make_else_arm(ctx, &make, else_block, &cond_bodies);
110                let make_match_arm =
111                    |(pat, guard, body): (_, Option<ast::Expr>, ast::BlockExpr)| {
112                        body.reindent_to(IndentLevel::single());
113                        let body = unwrap_trivial_block(body);
114                        match (pat, guard.map(|it| make.match_guard(it))) {
115                            (Some(pat), guard) => make.match_arm(pat, guard, body),
116                            (None, _) if !pat_seen => {
117                                make.match_arm(make.literal_pat("true").into(), None, body)
118                            }
119                            (None, guard) => {
120                                make.match_arm(make.wildcard_pat().into(), guard, body)
121                            }
122                        }
123                    };
124                let arms = cond_bodies.into_iter().map(make_match_arm).chain([else_arm]);
125                let match_expr = make.expr_match(scrutinee_to_be_expr, make.match_arm_list(arms));
126                match_expr.indent(indent);
127                match_expr.into()
128            };
129
130            let has_preceding_if_expr =
131                if_expr.syntax().parent().is_some_and(|it| ast::IfExpr::can_cast(it.kind()));
132            let expr = if has_preceding_if_expr {
133                // make sure we replace the `else if let ...` with a block so we don't end up with `else expr`
134                match_expr.dedent(indent);
135                match_expr.indent(IndentLevel(1));
136                let block_expr = make.block_expr([], Some(match_expr));
137                block_expr.indent(indent);
138                block_expr.into()
139            } else {
140                match_expr
141            };
142
143            let mut editor = builder.make_editor(if_expr.syntax());
144            editor.replace(if_expr.syntax(), expr.syntax());
145            editor.add_mappings(make.finish_with_mappings());
146            builder.add_file_edits(ctx.vfs_file_id(), editor);
147        },
148    )
149}
150
151fn make_else_arm(
152    ctx: &AssistContext<'_>,
153    make: &SyntaxFactory,
154    else_expr: Option<ast::Expr>,
155    conditionals: &[(Option<ast::Pat>, Option<ast::Expr>, ast::BlockExpr)],
156) -> ast::MatchArm {
157    let (pattern, expr) = if let Some(else_expr) = else_expr {
158        let pattern = match conditionals {
159            [(None, Some(_), _)] => make.literal_pat("false").into(),
160            [(Some(pat), _, _)] => match ctx
161                .sema
162                .type_of_pat(pat)
163                .and_then(|ty| TryEnum::from_ty(&ctx.sema, &ty.adjusted()))
164            {
165                Some(it) => {
166                    if does_pat_match_variant(pat, &it.sad_pattern()) {
167                        it.happy_pattern_wildcard()
168                    } else if does_pat_variant_nested_or_literal(ctx, pat) {
169                        make.wildcard_pat().into()
170                    } else {
171                        it.sad_pattern()
172                    }
173                }
174                None => make.wildcard_pat().into(),
175            },
176            _ => make.wildcard_pat().into(),
177        };
178        (pattern, else_expr)
179    } else {
180        let pattern = match conditionals {
181            [(None, Some(_), _)] => make.literal_pat("false").into(),
182            _ => make.wildcard_pat().into(),
183        };
184        (pattern, make.expr_unit())
185    };
186    make.match_arm(pattern, None, expr)
187}
188
189// Assist: replace_match_with_if_let
190//
191// Replaces a binary `match` with a wildcard pattern with an `if let` expression.
192//
193// ```
194// enum Action { Move { distance: u32 }, Stop }
195//
196// fn handle(action: Action) {
197//     $0match action {
198//         Action::Move { distance } => foo(distance),
199//         _ => bar(),
200//     }
201// }
202// ```
203// ->
204// ```
205// enum Action { Move { distance: u32 }, Stop }
206//
207// fn handle(action: Action) {
208//     if let Action::Move { distance } = action {
209//         foo(distance)
210//     } else {
211//         bar()
212//     }
213// }
214// ```
215pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
216    let match_expr: ast::MatchExpr = ctx.find_node_at_offset()?;
217    let match_arm_list = match_expr.match_arm_list()?;
218    let available_range = TextRange::new(
219        match_expr.syntax().text_range().start(),
220        match_arm_list.syntax().text_range().start(),
221    );
222    let cursor_in_range = available_range.contains_range(ctx.selection_trimmed());
223    if !cursor_in_range {
224        return None;
225    }
226
227    let mut arms = match_arm_list.arms();
228    let (first_arm, second_arm) = (arms.next()?, arms.next()?);
229    if arms.next().is_some() || second_arm.guard().is_some() {
230        return None;
231    }
232    if first_arm.guard().is_some() && ctx.edition() < Edition::Edition2024 {
233        return None;
234    }
235
236    let (if_let_pat, guard, then_expr, else_expr) = pick_pattern_and_expr_order(
237        &ctx.sema,
238        first_arm.pat()?,
239        second_arm.pat()?,
240        first_arm.expr()?,
241        second_arm.expr()?,
242        first_arm.guard(),
243        second_arm.guard(),
244    )?;
245    let scrutinee = match_expr.expr()?;
246    let guard = guard.and_then(|it| it.condition());
247
248    let let_ = match &if_let_pat {
249        ast::Pat::LiteralPat(p)
250            if p.literal()
251                .map(|it| it.token().kind())
252                .is_some_and(|it| it == T![true] || it == T![false]) =>
253        {
254            ""
255        }
256        _ => " let",
257    };
258    acc.add(
259        AssistId::refactor_rewrite("replace_match_with_if_let"),
260        format!("Replace match with if{let_}"),
261        match_expr.syntax().text_range(),
262        move |builder| {
263            let make = SyntaxFactory::with_mappings();
264            let make_block_expr = |expr: ast::Expr| {
265                // Blocks with modifiers (unsafe, async, etc.) are parsed as BlockExpr, but are
266                // formatted without enclosing braces. If we encounter such block exprs,
267                // wrap them in another BlockExpr.
268                match expr {
269                    ast::Expr::BlockExpr(block) if block.modifier().is_none() => block,
270                    expr => {
271                        expr.indent(IndentLevel(1));
272                        make.block_expr([], Some(expr))
273                    }
274                }
275            };
276
277            let condition = match if_let_pat {
278                ast::Pat::LiteralPat(p)
279                    if p.literal().is_some_and(|it| it.token().kind() == T![true]) =>
280                {
281                    scrutinee
282                }
283                ast::Pat::LiteralPat(p)
284                    if p.literal().is_some_and(|it| it.token().kind() == T![false]) =>
285                {
286                    make.expr_prefix(T![!], scrutinee).into()
287                }
288                _ => make.expr_let(if_let_pat, scrutinee).into(),
289            };
290            let condition = if let Some(guard) = guard {
291                make.expr_bin(condition, ast::BinaryOp::LogicOp(ast::LogicOp::And), guard).into()
292            } else {
293                condition
294            };
295            let then_expr = then_expr.clone_for_update();
296            let else_expr = else_expr.clone_for_update();
297            then_expr.reindent_to(IndentLevel::single());
298            else_expr.reindent_to(IndentLevel::single());
299            let then_block = make_block_expr(then_expr);
300            let else_expr = if is_empty_expr(&else_expr) { None } else { Some(else_expr) };
301            let if_let_expr = make.expr_if(
302                condition,
303                then_block,
304                else_expr.map(make_block_expr).map(ast::ElseBranch::Block),
305            );
306            if_let_expr.indent(IndentLevel::from_node(match_expr.syntax()));
307
308            let mut editor = builder.make_editor(match_expr.syntax());
309            editor.replace(match_expr.syntax(), if_let_expr.syntax());
310            editor.add_mappings(make.finish_with_mappings());
311            builder.add_file_edits(ctx.vfs_file_id(), editor);
312        },
313    )
314}
315
316/// Pick the pattern for the if let condition and return the expressions for the `then` body and `else` body in that order.
317fn pick_pattern_and_expr_order(
318    sema: &hir::Semantics<'_, RootDatabase>,
319    pat: ast::Pat,
320    pat2: ast::Pat,
321    expr: ast::Expr,
322    expr2: ast::Expr,
323    guard: Option<ast::MatchGuard>,
324    guard2: Option<ast::MatchGuard>,
325) -> Option<(ast::Pat, Option<ast::MatchGuard>, ast::Expr, ast::Expr)> {
326    if guard.is_some() && guard2.is_some() {
327        return None;
328    }
329    let res = match (pat, pat2) {
330        (ast::Pat::WildcardPat(_), _) => return None,
331        (pat, ast::Pat::WildcardPat(_)) => (pat, guard, expr, expr2),
332        (pat, _) if is_empty_expr(&expr2) => (pat, guard, expr, expr2),
333        (_, pat) if is_empty_expr(&expr) => (pat, guard, expr2, expr),
334        (pat, pat2) => match (binds_name(sema, &pat), binds_name(sema, &pat2)) {
335            (true, true) => return None,
336            (true, false) => (pat, guard, expr, expr2),
337            (false, true) => {
338                // This pattern triggers an invalid transformation.
339                // See issues #11373, #19443
340                if let ast::Pat::IdentPat(_) = pat2 {
341                    return None;
342                }
343                (pat2, guard2, expr2, expr)
344            }
345            _ if is_sad_pat(sema, &pat) => (pat2, guard2, expr2, expr),
346            (false, false) => (pat, guard, expr, expr2),
347        },
348    };
349    Some(res)
350}
351
352fn is_empty_expr(expr: &ast::Expr) -> bool {
353    match expr {
354        ast::Expr::BlockExpr(expr) => match expr.stmt_list() {
355            Some(it) => it.statements().next().is_none() && it.tail_expr().is_none(),
356            None => true,
357        },
358        ast::Expr::TupleExpr(expr) => expr.fields().next().is_none(),
359        _ => false,
360    }
361}
362
363fn binds_name(sema: &hir::Semantics<'_, RootDatabase>, pat: &ast::Pat) -> bool {
364    let binds_name_v = |pat| binds_name(sema, &pat);
365    match pat {
366        ast::Pat::IdentPat(pat) => !matches!(
367            pat.name().and_then(|name| NameClass::classify(sema, &name)),
368            Some(NameClass::ConstReference(_))
369        ),
370        ast::Pat::MacroPat(_) => true,
371        ast::Pat::OrPat(pat) => pat.pats().any(binds_name_v),
372        ast::Pat::SlicePat(pat) => pat.pats().any(binds_name_v),
373        ast::Pat::TuplePat(it) => it.fields().any(binds_name_v),
374        ast::Pat::TupleStructPat(it) => it.fields().any(binds_name_v),
375        ast::Pat::RecordPat(it) => it
376            .record_pat_field_list()
377            .is_some_and(|rpfl| rpfl.fields().flat_map(|rpf| rpf.pat()).any(binds_name_v)),
378        ast::Pat::RefPat(pat) => pat.pat().is_some_and(binds_name_v),
379        ast::Pat::BoxPat(pat) => pat.pat().is_some_and(binds_name_v),
380        ast::Pat::ParenPat(pat) => pat.pat().is_some_and(binds_name_v),
381        _ => false,
382    }
383}
384
385fn is_sad_pat(sema: &hir::Semantics<'_, RootDatabase>, pat: &ast::Pat) -> bool {
386    sema.type_of_pat(pat)
387        .and_then(|ty| TryEnum::from_ty(sema, &ty.adjusted()))
388        .is_some_and(|it| does_pat_match_variant(pat, &it.sad_pattern()))
389}
390
391fn let_and_guard(cond: &ast::Expr) -> (Option<ast::LetExpr>, Option<ast::Expr>) {
392    if let ast::Expr::ParenExpr(expr) = cond
393        && let Some(sub_expr) = expr.expr()
394    {
395        let_and_guard(&sub_expr)
396    } else if let ast::Expr::LetExpr(let_expr) = cond {
397        (Some(let_expr.clone()), None)
398    } else if let ast::Expr::BinExpr(bin_expr) = cond
399        && let Some(ast::Expr::LetExpr(let_expr)) = and_bin_expr_left(bin_expr).lhs()
400    {
401        let new_expr = bin_expr.clone_subtree();
402        let mut edit = SyntaxEditor::new(new_expr.syntax().clone());
403
404        let left_bin = and_bin_expr_left(&new_expr);
405        if let Some(rhs) = left_bin.rhs() {
406            edit.replace(left_bin.syntax(), rhs.syntax());
407        } else {
408            if let Some(next) = left_bin.syntax().next_sibling_or_token()
409                && next.kind() == SyntaxKind::WHITESPACE
410            {
411                edit.delete(next);
412            }
413            edit.delete(left_bin.syntax());
414        }
415
416        let new_expr = edit.finish().new_root().clone();
417        (Some(let_expr), ast::Expr::cast(new_expr))
418    } else {
419        (None, Some(cond.clone()))
420    }
421}
422
423fn and_bin_expr_left(expr: &ast::BinExpr) -> ast::BinExpr {
424    if expr.op_kind() == Some(ast::BinaryOp::LogicOp(ast::LogicOp::And))
425        && let Some(ast::Expr::BinExpr(left)) = expr.lhs()
426    {
427        and_bin_expr_left(&left)
428    } else {
429        expr.clone()
430    }
431}
432
433#[cfg(test)]
434mod tests {
435    use super::*;
436
437    use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
438
439    #[test]
440    fn test_if_let_with_match_inapplicable_for_simple_ifs() {
441        check_assist_not_applicable(
442            replace_if_let_with_match,
443            r#"
444fn main() {
445    if $0true {} else if false {} else {}
446}
447"#,
448        )
449    }
450
451    #[test]
452    fn test_if_with_match_no_else() {
453        check_assist(
454            replace_if_let_with_match,
455            r#"
456pub fn foo(foo: bool) {
457    if foo$0 {
458        self.foo();
459    }
460}
461"#,
462            r#"
463pub fn foo(foo: bool) {
464    match foo {
465        true => {
466            self.foo();
467        }
468        false => (),
469    }
470}
471"#,
472        )
473    }
474
475    #[test]
476    fn test_if_with_match_with_else() {
477        check_assist(
478            replace_if_let_with_match,
479            r#"
480pub fn foo(foo: bool) {
481    if foo$0 {
482        self.foo();
483    } else {
484        self.bar();
485    }
486}
487"#,
488            r#"
489pub fn foo(foo: bool) {
490    match foo {
491        true => {
492            self.foo();
493        }
494        false => {
495            self.bar();
496        }
497    }
498}
499"#,
500        )
501    }
502
503    #[test]
504    fn test_if_with_match_comments() {
505        check_assist(
506            replace_if_let_with_match,
507            r#"
508pub fn foo(foo: i32) {
509    $0if let 1 = foo {
510        // some comment
511        self.foo();
512    } else if let 2 = foo {
513        // some comment 2
514        self.bar()
515    } else {
516        // some comment 3
517        self.baz();
518    }
519}
520"#,
521            r#"
522pub fn foo(foo: i32) {
523    match foo {
524        1 => {
525            // some comment
526            self.foo();
527        }
528        2 => {
529            // some comment 2
530            self.bar()
531        }
532        _ => {
533            // some comment 3
534            self.baz();
535        }
536    }
537}
538"#,
539        )
540    }
541
542    #[test]
543    fn test_if_let_with_match_no_else() {
544        check_assist(
545            replace_if_let_with_match,
546            r#"
547impl VariantData {
548    pub fn foo(&self) {
549        if $0let VariantData::Struct(..) = *self {
550            self.foo();
551        }
552    }
553}
554"#,
555            r#"
556impl VariantData {
557    pub fn foo(&self) {
558        match *self {
559            VariantData::Struct(..) => {
560                self.foo();
561            }
562            _ => (),
563        }
564    }
565}
566"#,
567        )
568    }
569
570    #[test]
571    fn test_if_let_with_match_available_range_left() {
572        check_assist_not_applicable(
573            replace_if_let_with_match,
574            r#"
575impl VariantData {
576    pub fn foo(&self) {
577        $0 if let VariantData::Struct(..) = *self {
578            self.foo();
579        }
580    }
581}
582"#,
583        )
584    }
585
586    #[test]
587    fn test_if_let_with_match_available_range_right() {
588        check_assist_not_applicable(
589            replace_if_let_with_match,
590            r#"
591impl VariantData {
592    pub fn foo(&self) {
593        if let VariantData::Struct(..) = *self {$0
594            self.foo();
595        }
596    }
597}
598"#,
599        )
600    }
601
602    #[test]
603    fn test_if_let_with_match_let_chain() {
604        check_assist(
605            replace_if_let_with_match,
606            r#"
607#![feature(if_let_guard)]
608fn main() {
609    if $0let true = true && let Some(1) = None {} else { other() }
610}
611"#,
612            r#"
613#![feature(if_let_guard)]
614fn main() {
615    match true {
616        true if let Some(1) = None => {}
617        _ => other(),
618    }
619}
620"#,
621        );
622
623        check_assist(
624            replace_if_let_with_match,
625            r#"
626#![feature(if_let_guard)]
627fn main() {
628    if true {
629        $0if let ParenExpr(expr) = cond
630            && let Some(sub_expr) = expr.expr()
631        {
632            branch1(
633                "..."
634            )
635        } else if let LetExpr(let_expr) = cond {
636            branch2(
637                "..."
638            )
639        } else if let BinExpr(bin_expr) = cond
640            && let Some(kind) = bin_expr.op_kind()
641            && let Some(LetExpr(let_expr)) = foo(bin_expr)
642        {
643            branch3()
644        } else {
645            branch4(
646                "..."
647            )
648        }
649    }
650}
651"#,
652            r#"
653#![feature(if_let_guard)]
654fn main() {
655    if true {
656        match cond {
657            ParenExpr(expr) if let Some(sub_expr) = expr.expr() => {
658                branch1(
659                    "..."
660                )
661            }
662            LetExpr(let_expr) => {
663                branch2(
664                    "..."
665                )
666            }
667            BinExpr(bin_expr) if let Some(kind) = bin_expr.op_kind()
668                && let Some(LetExpr(let_expr)) = foo(bin_expr) => branch3(),
669            _ => {
670                branch4(
671                    "..."
672                )
673            }
674        }
675    }
676}
677"#,
678        );
679
680        check_assist(
681            replace_if_let_with_match,
682            r#"
683fn main() {
684    if $0let true = true
685        && true
686        && false
687    {
688        code()
689    } else {
690        other()
691    }
692}
693"#,
694            r#"
695fn main() {
696    match true {
697        true if true
698            && false => code(),
699        _ => other(),
700    }
701}
702"#,
703        );
704    }
705
706    #[test]
707    fn test_if_let_with_match_let_chain_no_else() {
708        check_assist(
709            replace_if_let_with_match,
710            r#"
711#![feature(if_let_guard)]
712fn main() {
713    if $0let true = true && let Some(1) = None {}
714}
715"#,
716            r#"
717#![feature(if_let_guard)]
718fn main() {
719    match true {
720        true if let Some(1) = None => {}
721        _ => (),
722    }
723}
724"#,
725        );
726
727        check_assist(
728            replace_if_let_with_match,
729            r#"
730fn main() {
731    if $0let true = true
732        && true
733        && false
734    {
735        code()
736    }
737}
738"#,
739            r#"
740fn main() {
741    match true {
742        true if true
743            && false => code(),
744        _ => (),
745    }
746}
747"#,
748        );
749    }
750
751    #[test]
752    fn test_if_let_with_match_basic() {
753        check_assist(
754            replace_if_let_with_match,
755            r#"
756impl VariantData {
757    pub fn is_struct(&self) -> bool {
758        if $0let VariantData::Struct(..) = *self {
759            true
760        } else if let VariantData::Tuple(..) = *self {
761            false
762        } else if cond() {
763            true
764        } else {
765            bar(
766                123
767            )
768        }
769    }
770}
771"#,
772            r#"
773impl VariantData {
774    pub fn is_struct(&self) -> bool {
775        match *self {
776            VariantData::Struct(..) => true,
777            VariantData::Tuple(..) => false,
778            _ if cond() => true,
779            _ => {
780                bar(
781                    123
782                )
783            }
784        }
785    }
786}
787"#,
788        )
789    }
790
791    #[test]
792    fn test_if_let_with_match_on_tail_if_let() {
793        check_assist(
794            replace_if_let_with_match,
795            r#"
796impl VariantData {
797    pub fn is_struct(&self) -> bool {
798        if let VariantData::Struct(..) = *self {
799            true
800        } else if let$0 VariantData::Tuple(..) = *self {
801            false
802        } else {
803            false
804        }
805    }
806}
807"#,
808            r#"
809impl VariantData {
810    pub fn is_struct(&self) -> bool {
811        if let VariantData::Struct(..) = *self {
812            true
813        } else {
814            match *self {
815                VariantData::Tuple(..) => false,
816                _ => false,
817            }
818        }
819    }
820}
821"#,
822        )
823    }
824
825    #[test]
826    fn special_case_option() {
827        check_assist(
828            replace_if_let_with_match,
829            r#"
830//- minicore: option
831fn foo(x: Option<i32>) {
832    $0if let Some(x) = x {
833        println!("{}", x)
834    } else {
835        println!("none")
836    }
837}
838"#,
839            r#"
840fn foo(x: Option<i32>) {
841    match x {
842        Some(x) => println!("{}", x),
843        None => println!("none"),
844    }
845}
846"#,
847        );
848    }
849
850    #[test]
851    fn special_case_inverted_option() {
852        check_assist(
853            replace_if_let_with_match,
854            r#"
855//- minicore: option
856fn foo(x: Option<i32>) {
857    $0if let None = x {
858        println!("none")
859    } else {
860        println!("some")
861    }
862}
863"#,
864            r#"
865fn foo(x: Option<i32>) {
866    match x {
867        None => println!("none"),
868        Some(_) => println!("some"),
869    }
870}
871"#,
872        );
873    }
874
875    #[test]
876    fn special_case_result() {
877        check_assist(
878            replace_if_let_with_match,
879            r#"
880//- minicore: result
881fn foo(x: Result<i32, ()>) {
882    $0if let Ok(x) = x {
883        println!("{}", x)
884    } else {
885        println!("none")
886    }
887}
888"#,
889            r#"
890fn foo(x: Result<i32, ()>) {
891    match x {
892        Ok(x) => println!("{}", x),
893        Err(_) => println!("none"),
894    }
895}
896"#,
897        );
898    }
899
900    #[test]
901    fn special_case_inverted_result() {
902        check_assist(
903            replace_if_let_with_match,
904            r#"
905//- minicore: result
906fn foo(x: Result<i32, ()>) {
907    $0if let Err(x) = x {
908        println!("{}", x)
909    } else {
910        println!("ok")
911    }
912}
913"#,
914            r#"
915fn foo(x: Result<i32, ()>) {
916    match x {
917        Err(x) => println!("{}", x),
918        Ok(_) => println!("ok"),
919    }
920}
921"#,
922        );
923    }
924
925    #[test]
926    fn nested_indent() {
927        check_assist(
928            replace_if_let_with_match,
929            r#"
930fn main() {
931    if true {
932        $0if let Ok(rel_path) = path.strip_prefix(root_path) {
933            let rel_path = RelativePathBuf::from_path(rel_path)
934                .ok()?;
935            Some((*id, rel_path))
936        } else {
937            let _ = some_code()
938                .clone();
939            None
940        }
941    }
942}
943"#,
944            r#"
945fn main() {
946    if true {
947        match path.strip_prefix(root_path) {
948            Ok(rel_path) => {
949                let rel_path = RelativePathBuf::from_path(rel_path)
950                    .ok()?;
951                Some((*id, rel_path))
952            }
953            _ => {
954                let _ = some_code()
955                    .clone();
956                None
957            }
958        }
959    }
960}
961"#,
962        );
963
964        check_assist(
965            replace_if_let_with_match,
966            r#"
967fn main() {
968    if true {
969        $0if let Ok(rel_path) = path.strip_prefix(root_path) {
970            Foo {
971                x: 1
972            }
973        } else {
974            Foo {
975                x: 2
976            }
977        }
978    }
979}
980"#,
981            r#"
982fn main() {
983    if true {
984        match path.strip_prefix(root_path) {
985            Ok(rel_path) => {
986                Foo {
987                    x: 1
988                }
989            }
990            _ => {
991                Foo {
992                    x: 2
993                }
994            }
995        }
996    }
997}
998"#,
999        )
1000    }
1001
1002    #[test]
1003    fn test_if_let_with_match_nested_tuple_struct() {
1004        check_assist(
1005            replace_if_let_with_match,
1006            r#"
1007//- minicore: result, option
1008fn foo(x: Result<i32, ()>) {
1009    let bar: Result<_, ()> = Ok(Some(1));
1010    $0if let Ok(Some(_)) = bar {
1011        ()
1012    } else {
1013        ()
1014    }
1015}
1016"#,
1017            r#"
1018fn foo(x: Result<i32, ()>) {
1019    let bar: Result<_, ()> = Ok(Some(1));
1020    match bar {
1021        Ok(Some(_)) => (),
1022        _ => (),
1023    }
1024}
1025"#,
1026        );
1027
1028        check_assist(
1029            replace_if_let_with_match,
1030            r#"
1031//- minicore: result
1032struct MyStruct(i32, i32);
1033fn foo(x: Result<MyStruct, ()>) {
1034    let bar: Result<MyStruct, ()> = Ok(MyStruct(1, 2));
1035    $0if let Ok(MyStruct(a, b)) = bar {
1036        ()
1037    } else {
1038        ()
1039    }
1040}
1041"#,
1042            r#"
1043struct MyStruct(i32, i32);
1044fn foo(x: Result<MyStruct, ()>) {
1045    let bar: Result<MyStruct, ()> = Ok(MyStruct(1, 2));
1046    match bar {
1047        Ok(MyStruct(a, b)) => (),
1048        Err(_) => (),
1049    }
1050}
1051"#,
1052        );
1053    }
1054
1055    #[test]
1056    fn test_if_let_with_match_nested_slice() {
1057        check_assist(
1058            replace_if_let_with_match,
1059            r#"
1060//- minicore: result
1061fn foo(x: Result<&[i32], ()>) {
1062    let foo: Result<&[_], ()> = Ok(&[0, 1, 2]);
1063    $0if let Ok([]) = foo {
1064        ()
1065    } else {
1066        ()
1067    }
1068}
1069        "#,
1070            r#"
1071fn foo(x: Result<&[i32], ()>) {
1072    let foo: Result<&[_], ()> = Ok(&[0, 1, 2]);
1073    match foo {
1074        Ok([]) => (),
1075        _ => (),
1076    }
1077}
1078        "#,
1079        );
1080
1081        check_assist(
1082            replace_if_let_with_match,
1083            r#"
1084//- minicore: result
1085fn foo(x: Result<[&'static str; 2], ()>) {
1086    let foobar: Result<_, ()> = Ok(["foo", "bar"]);
1087    $0if let Ok([_, "bar"]) = foobar {
1088        ()
1089    } else {
1090        ()
1091    }
1092}
1093"#,
1094            r#"
1095fn foo(x: Result<[&'static str; 2], ()>) {
1096    let foobar: Result<_, ()> = Ok(["foo", "bar"]);
1097    match foobar {
1098        Ok([_, "bar"]) => (),
1099        _ => (),
1100    }
1101}
1102"#,
1103        );
1104
1105        check_assist(
1106            replace_if_let_with_match,
1107            r#"
1108//- minicore: result
1109fn foo(x: Result<[&'static str; 2], ()>) {
1110    let foobar: Result<_, ()> = Ok(["foo", "bar"]);
1111    $0if let Ok([..]) = foobar {
1112        ()
1113    } else {
1114        ()
1115    }
1116}
1117"#,
1118            r#"
1119fn foo(x: Result<[&'static str; 2], ()>) {
1120    let foobar: Result<_, ()> = Ok(["foo", "bar"]);
1121    match foobar {
1122        Ok([..]) => (),
1123        Err(_) => (),
1124    }
1125}
1126"#,
1127        );
1128
1129        check_assist(
1130            replace_if_let_with_match,
1131            r#"
1132//- minicore: result
1133fn foo(x: Result<&[&'static str], ()>) {
1134    let foobar: Result<&[&'static str], ()> = Ok(&["foo", "bar"]);
1135    $0if let Ok([a, ..]) = foobar {
1136        ()
1137    } else {
1138        ()
1139    }
1140}
1141"#,
1142            r#"
1143fn foo(x: Result<&[&'static str], ()>) {
1144    let foobar: Result<&[&'static str], ()> = Ok(&["foo", "bar"]);
1145    match foobar {
1146        Ok([a, ..]) => (),
1147        _ => (),
1148    }
1149}
1150"#,
1151        );
1152
1153        check_assist(
1154            replace_if_let_with_match,
1155            r#"
1156//- minicore: result
1157fn foo(x: Result<&[&'static str], ()>) {
1158    let foobar: Result<&[&'static str], ()> = Ok(&["foo", "bar"]);
1159    $0if let Ok([a, .., b, c]) = foobar {
1160        ()
1161    } else {
1162        ()
1163    }
1164}
1165"#,
1166            r#"
1167fn foo(x: Result<&[&'static str], ()>) {
1168    let foobar: Result<&[&'static str], ()> = Ok(&["foo", "bar"]);
1169    match foobar {
1170        Ok([a, .., b, c]) => (),
1171        _ => (),
1172    }
1173}
1174"#,
1175        );
1176
1177        check_assist(
1178            replace_if_let_with_match,
1179            r#"
1180//- minicore: result
1181fn foo(x: Result<Option<[&'static str; 2]>, ()>) {
1182    let foobar: Result<_, ()> = Ok(Some(["foo", "bar"]));
1183    $0if let Ok(Some([_, "bar"])) = foobar {
1184        ()
1185    } else {
1186        ()
1187    }
1188}
1189"#,
1190            r#"
1191fn foo(x: Result<Option<[&'static str; 2]>, ()>) {
1192    let foobar: Result<_, ()> = Ok(Some(["foo", "bar"]));
1193    match foobar {
1194        Ok(Some([_, "bar"])) => (),
1195        _ => (),
1196    }
1197}
1198"#,
1199        );
1200    }
1201
1202    #[test]
1203    fn test_if_let_with_match_nested_literal() {
1204        check_assist(
1205            replace_if_let_with_match,
1206            r#"
1207//- minicore: result
1208fn foo(x: Result<&'static str, ()>) {
1209    let bar: Result<&_, ()> = Ok("bar");
1210    $0if let Ok("foo") = bar {
1211        ()
1212    } else {
1213        ()
1214    }
1215}
1216"#,
1217            r#"
1218fn foo(x: Result<&'static str, ()>) {
1219    let bar: Result<&_, ()> = Ok("bar");
1220    match bar {
1221        Ok("foo") => (),
1222        _ => (),
1223    }
1224}
1225"#,
1226        );
1227    }
1228
1229    #[test]
1230    fn test_if_let_with_match_nested_tuple() {
1231        check_assist(
1232            replace_if_let_with_match,
1233            r#"
1234//- minicore: result
1235fn foo(x: Result<(i32, i32, i32), ()>) {
1236    let bar: Result<(i32, i32, i32), ()> = Ok((1, 2, 3));
1237    $0if let Ok((1, second, third)) = bar {
1238        ()
1239    } else {
1240        ()
1241    }
1242}
1243"#,
1244            r#"
1245fn foo(x: Result<(i32, i32, i32), ()>) {
1246    let bar: Result<(i32, i32, i32), ()> = Ok((1, 2, 3));
1247    match bar {
1248        Ok((1, second, third)) => (),
1249        _ => (),
1250    }
1251}
1252"#,
1253        );
1254
1255        check_assist(
1256            replace_if_let_with_match,
1257            r#"
1258//- minicore: result
1259fn foo(x: Result<(i32, i32, i32), ()>) {
1260    let bar: Result<(i32, i32, i32), ()> = Ok((1, 2, 3));
1261    $0if let Ok((first, second, third)) = bar {
1262        ()
1263    } else {
1264        ()
1265    }
1266}
1267"#,
1268            r#"
1269fn foo(x: Result<(i32, i32, i32), ()>) {
1270    let bar: Result<(i32, i32, i32), ()> = Ok((1, 2, 3));
1271    match bar {
1272        Ok((first, second, third)) => (),
1273        Err(_) => (),
1274    }
1275}
1276"#,
1277        );
1278    }
1279
1280    #[test]
1281    fn test_if_let_with_match_nested_or() {
1282        check_assist(
1283            replace_if_let_with_match,
1284            r#"
1285//- minicore: result
1286fn foo(x: Result<i32, ()>) {
1287    let bar: Result<i32, ()> = Ok(1);
1288    $0if let Ok(1 | 2) = bar {
1289        ()
1290    } else {
1291        ()
1292    }
1293}
1294"#,
1295            r#"
1296fn foo(x: Result<i32, ()>) {
1297    let bar: Result<i32, ()> = Ok(1);
1298    match bar {
1299        Ok(1 | 2) => (),
1300        _ => (),
1301    }
1302}
1303"#,
1304        );
1305
1306        check_assist(
1307            replace_if_let_with_match,
1308            r#"
1309//- minicore: result
1310fn foo(x: Result<(i32, i32), ()>) {
1311    let bar: Result<(i32, i32), ()> = Ok((1, 2));
1312    $0if let Ok((b, a) | (a, b)) = bar {
1313        ()
1314    } else {
1315        ()
1316    }
1317}
1318"#,
1319            r#"
1320fn foo(x: Result<(i32, i32), ()>) {
1321    let bar: Result<(i32, i32), ()> = Ok((1, 2));
1322    match bar {
1323        Ok((b, a) | (a, b)) => (),
1324        Err(_) => (),
1325    }
1326}
1327"#,
1328        );
1329
1330        check_assist(
1331            replace_if_let_with_match,
1332            r#"
1333//- minicore: result
1334fn foo(x: Result<(i32, i32), ()>) {
1335    let bar: Result<(i32, i32), ()> = Ok((1, 2));
1336    $0if let Ok((1, a) | (a, 2)) = bar {
1337        ()
1338    } else {
1339        ()
1340    }
1341}
1342"#,
1343            r#"
1344fn foo(x: Result<(i32, i32), ()>) {
1345    let bar: Result<(i32, i32), ()> = Ok((1, 2));
1346    match bar {
1347        Ok((1, a) | (a, 2)) => (),
1348        _ => (),
1349    }
1350}
1351"#,
1352        );
1353    }
1354
1355    #[test]
1356    fn test_if_let_with_match_nested_range() {
1357        check_assist(
1358            replace_if_let_with_match,
1359            r#"
1360//- minicore: result
1361fn foo(x: Result<i32, ()>) {
1362    let bar: Result<i32, ()> = Ok(1);
1363    $0if let Ok(1..2) = bar {
1364        ()
1365    } else {
1366        ()
1367    }
1368}
1369"#,
1370            r#"
1371fn foo(x: Result<i32, ()>) {
1372    let bar: Result<i32, ()> = Ok(1);
1373    match bar {
1374        Ok(1..2) => (),
1375        _ => (),
1376    }
1377}
1378"#,
1379        );
1380    }
1381
1382    #[test]
1383    fn test_if_let_with_match_nested_paren() {
1384        check_assist(
1385            replace_if_let_with_match,
1386            r#"
1387//- minicore: result
1388fn foo(x: Result<(i32, i32), ()>) {
1389    let bar: Result<(i32, i32), ()> = Ok((1, 1));
1390    $0if let Ok(((1, 2))) = bar {
1391        ()
1392    } else {
1393        ()
1394    }
1395}
1396"#,
1397            r#"
1398fn foo(x: Result<(i32, i32), ()>) {
1399    let bar: Result<(i32, i32), ()> = Ok((1, 1));
1400    match bar {
1401        Ok(((1, 2))) => (),
1402        _ => (),
1403    }
1404}
1405"#,
1406        );
1407
1408        check_assist(
1409            replace_if_let_with_match,
1410            r#"
1411//- minicore: result
1412fn foo(x: Result<(i32, i32), ()>) {
1413    let bar: Result<(i32, i32), ()> = Ok((1, 1));
1414    $0if let Ok(((a, b))) = bar {
1415        ()
1416    } else {
1417        ()
1418    }
1419}
1420"#,
1421            r#"
1422fn foo(x: Result<(i32, i32), ()>) {
1423    let bar: Result<(i32, i32), ()> = Ok((1, 1));
1424    match bar {
1425        Ok(((a, b))) => (),
1426        Err(_) => (),
1427    }
1428}
1429"#,
1430        );
1431    }
1432
1433    #[test]
1434    fn test_if_let_with_match_nested_macro() {
1435        check_assist(
1436            replace_if_let_with_match,
1437            r#"
1438//- minicore: result
1439fn foo(x: Result<i32, ()>) {
1440    macro_rules! is_42 {
1441        () => {
1442            42
1443        };
1444    }
1445
1446    let bar: Result<i32, ()> = Ok(1);
1447    $0if let Ok(is_42!()) = bar {
1448        ()
1449    } else {
1450        ()
1451    }
1452}
1453"#,
1454            r#"
1455fn foo(x: Result<i32, ()>) {
1456    macro_rules! is_42 {
1457        () => {
1458            42
1459        };
1460    }
1461
1462    let bar: Result<i32, ()> = Ok(1);
1463    match bar {
1464        Ok(is_42!()) => (),
1465        _ => (),
1466    }
1467}
1468"#,
1469        );
1470    }
1471
1472    #[test]
1473    fn test_if_let_with_match_nested_path() {
1474        check_assist(
1475            replace_if_let_with_match,
1476            r#"
1477//- minicore: result
1478enum MyEnum {
1479    Foo,
1480    Bar,
1481}
1482
1483fn foo(x: Result<MyEnum, ()>) {
1484    let bar: Result<MyEnum, ()> = Ok(MyEnum::Foo);
1485    $0if let Ok(MyEnum::Foo) = bar {
1486        ()
1487    } else {
1488        ()
1489    }
1490}
1491"#,
1492            r#"
1493enum MyEnum {
1494    Foo,
1495    Bar,
1496}
1497
1498fn foo(x: Result<MyEnum, ()>) {
1499    let bar: Result<MyEnum, ()> = Ok(MyEnum::Foo);
1500    match bar {
1501        Ok(MyEnum::Foo) => (),
1502        _ => (),
1503    }
1504}
1505"#,
1506        );
1507    }
1508
1509    #[test]
1510    fn test_if_let_with_match_nested_record() {
1511        check_assist(
1512            replace_if_let_with_match,
1513            r#"
1514//- minicore: result
1515struct MyStruct {
1516    foo: i32,
1517    bar: i32,
1518}
1519
1520fn foo(x: Result<MyStruct, ()>) {
1521    let bar: Result<MyStruct, ()> = Ok(MyStruct { foo: 1, bar: 2 });
1522    $0if let Ok(MyStruct { foo, bar }) = bar {
1523        ()
1524    } else {
1525        ()
1526    }
1527}
1528"#,
1529            r#"
1530struct MyStruct {
1531    foo: i32,
1532    bar: i32,
1533}
1534
1535fn foo(x: Result<MyStruct, ()>) {
1536    let bar: Result<MyStruct, ()> = Ok(MyStruct { foo: 1, bar: 2 });
1537    match bar {
1538        Ok(MyStruct { foo, bar }) => (),
1539        Err(_) => (),
1540    }
1541}
1542"#,
1543        );
1544
1545        check_assist(
1546            replace_if_let_with_match,
1547            r#"
1548//- minicore: result
1549struct MyStruct {
1550    foo: i32,
1551    bar: i32,
1552}
1553
1554fn foo(x: Result<MyStruct, ()>) {
1555    let bar: Result<MyStruct, ()> = Ok(MyStruct { foo: 1, bar: 2 });
1556    $0if let Ok(MyStruct { foo, bar: 12 }) = bar {
1557        ()
1558    } else {
1559        ()
1560    }
1561}
1562"#,
1563            r#"
1564struct MyStruct {
1565    foo: i32,
1566    bar: i32,
1567}
1568
1569fn foo(x: Result<MyStruct, ()>) {
1570    let bar: Result<MyStruct, ()> = Ok(MyStruct { foo: 1, bar: 2 });
1571    match bar {
1572        Ok(MyStruct { foo, bar: 12 }) => (),
1573        _ => (),
1574    }
1575}
1576"#,
1577        );
1578
1579        check_assist(
1580            replace_if_let_with_match,
1581            r#"
1582//- minicore: result
1583struct MyStruct {
1584    foo: i32,
1585    bar: i32,
1586}
1587
1588fn foo(x: Result<MyStruct, ()>) {
1589    let bar: Result<MyStruct, ()> = Ok(MyStruct { foo: 1, bar: 2 });
1590    $0if let Ok(MyStruct { foo, .. }) = bar {
1591        ()
1592    } else {
1593        ()
1594    }
1595}
1596"#,
1597            r#"
1598struct MyStruct {
1599    foo: i32,
1600    bar: i32,
1601}
1602
1603fn foo(x: Result<MyStruct, ()>) {
1604    let bar: Result<MyStruct, ()> = Ok(MyStruct { foo: 1, bar: 2 });
1605    match bar {
1606        Ok(MyStruct { foo, .. }) => (),
1607        Err(_) => (),
1608    }
1609}
1610"#,
1611        );
1612
1613        check_assist(
1614            replace_if_let_with_match,
1615            r#"
1616//- minicore: result
1617enum MyEnum {
1618    Foo(i32, i32),
1619    Bar { a: i32, b: i32 },
1620}
1621
1622fn foo(x: Result<MyEnum, ()>) {
1623    let bar: Result<MyEnum, ()> = Ok(MyEnum::Foo(1, 2));
1624    $0if let Ok(MyEnum::Bar { a, b }) = bar {
1625        ()
1626    } else {
1627        ()
1628    }
1629}
1630"#,
1631            r#"
1632enum MyEnum {
1633    Foo(i32, i32),
1634    Bar { a: i32, b: i32 },
1635}
1636
1637fn foo(x: Result<MyEnum, ()>) {
1638    let bar: Result<MyEnum, ()> = Ok(MyEnum::Foo(1, 2));
1639    match bar {
1640        Ok(MyEnum::Bar { a, b }) => (),
1641        _ => (),
1642    }
1643}
1644"#,
1645        );
1646    }
1647
1648    #[test]
1649    fn test_if_let_with_match_nested_ident() {
1650        check_assist(
1651            replace_if_let_with_match,
1652            r#"
1653//- minicore: result
1654fn foo(x: Result<i32, ()>) {
1655    let bar: Result<i32, ()> = Ok(1);
1656    $0if let Ok(a @ 1..2) = bar {
1657        ()
1658    } else {
1659        ()
1660    }
1661}
1662"#,
1663            r#"
1664fn foo(x: Result<i32, ()>) {
1665    let bar: Result<i32, ()> = Ok(1);
1666    match bar {
1667        Ok(a @ 1..2) => (),
1668        _ => (),
1669    }
1670}
1671"#,
1672        );
1673
1674        check_assist(
1675            replace_if_let_with_match,
1676            r#"
1677//- minicore: result
1678fn foo(x: Result<i32, ()>) {
1679    let bar: Result<i32, ()> = Ok(1);
1680    $0if let Ok(a) = bar {
1681        ()
1682    } else {
1683        ()
1684    }
1685}
1686"#,
1687            r#"
1688fn foo(x: Result<i32, ()>) {
1689    let bar: Result<i32, ()> = Ok(1);
1690    match bar {
1691        Ok(a) => (),
1692        Err(_) => (),
1693    }
1694}
1695"#,
1696        );
1697
1698        check_assist(
1699            replace_if_let_with_match,
1700            r#"
1701//- minicore: result
1702fn foo(x: Result<i32, ()>) {
1703    let bar: Result<i32, ()> = Ok(1);
1704    $0if let Ok(a @ b @ c @ d) = bar {
1705        ()
1706    } else {
1707        ()
1708    }
1709}
1710"#,
1711            r#"
1712fn foo(x: Result<i32, ()>) {
1713    let bar: Result<i32, ()> = Ok(1);
1714    match bar {
1715        Ok(a @ b @ c @ d) => (),
1716        Err(_) => (),
1717    }
1718}
1719"#,
1720        );
1721    }
1722
1723    #[test]
1724    fn test_replace_match_with_if_let_unwraps_simple_expressions() {
1725        check_assist(
1726            replace_match_with_if_let,
1727            r#"
1728impl VariantData {
1729    pub fn is_struct(&self) -> bool {
1730        $0match *self {
1731            VariantData::Struct(..) => true,
1732            _ => false,
1733        }
1734    }
1735}           "#,
1736            r#"
1737impl VariantData {
1738    pub fn is_struct(&self) -> bool {
1739        if let VariantData::Struct(..) = *self {
1740            true
1741        } else {
1742            false
1743        }
1744    }
1745}           "#,
1746        )
1747    }
1748
1749    #[test]
1750    fn test_replace_match_with_if_let_doesnt_unwrap_multiline_expressions() {
1751        check_assist(
1752            replace_match_with_if_let,
1753            r#"
1754fn foo() {
1755    $0match a {
1756        VariantData::Struct(..) => {
1757            bar(
1758                123
1759            )
1760        }
1761        _ => false,
1762    }
1763}           "#,
1764            r#"
1765fn foo() {
1766    if let VariantData::Struct(..) = a {
1767        bar(
1768            123
1769        )
1770    } else {
1771        false
1772    }
1773}           "#,
1774        )
1775    }
1776
1777    #[test]
1778    fn replace_match_with_if_let_target() {
1779        check_assist_target(
1780            replace_match_with_if_let,
1781            r#"
1782impl VariantData {
1783    pub fn is_struct(&self) -> bool {
1784        $0match *self {
1785            VariantData::Struct(..) => true,
1786            _ => false,
1787        }
1788    }
1789}           "#,
1790            r#"match *self {
1791            VariantData::Struct(..) => true,
1792            _ => false,
1793        }"#,
1794        );
1795    }
1796
1797    #[test]
1798    fn special_case_option_match_to_if_let() {
1799        check_assist(
1800            replace_match_with_if_let,
1801            r#"
1802//- minicore: option
1803fn foo(x: Option<i32>) {
1804    $0match x {
1805        Some(x) => println!("{}", x),
1806        None => println!("none"),
1807    }
1808}
1809"#,
1810            r#"
1811fn foo(x: Option<i32>) {
1812    if let Some(x) = x {
1813        println!("{}", x)
1814    } else {
1815        println!("none")
1816    }
1817}
1818"#,
1819        );
1820    }
1821
1822    #[test]
1823    fn special_case_result_match_to_if_let() {
1824        check_assist(
1825            replace_match_with_if_let,
1826            r#"
1827//- minicore: result
1828fn foo(x: Result<i32, ()>) {
1829    $0match x {
1830        Ok(x) => println!("{}", x),
1831        Err(_) => println!("none"),
1832    }
1833}
1834"#,
1835            r#"
1836fn foo(x: Result<i32, ()>) {
1837    if let Ok(x) = x {
1838        println!("{}", x)
1839    } else {
1840        println!("none")
1841    }
1842}
1843"#,
1844        );
1845    }
1846
1847    #[test]
1848    fn nested_indent_match_to_if_let() {
1849        check_assist(
1850            replace_match_with_if_let,
1851            r#"
1852fn main() {
1853    if true {
1854        $0match path.strip_prefix(root_path) {
1855            Ok(rel_path) => Foo {
1856                x: 2
1857            }
1858            _ => Foo {
1859                x: 3
1860            },
1861        }
1862    }
1863}
1864"#,
1865            r#"
1866fn main() {
1867    if true {
1868        if let Ok(rel_path) = path.strip_prefix(root_path) {
1869            Foo {
1870                x: 2
1871            }
1872        } else {
1873            Foo {
1874                x: 3
1875            }
1876        }
1877    }
1878}
1879"#,
1880        );
1881
1882        check_assist(
1883            replace_match_with_if_let,
1884            r#"
1885fn main() {
1886    if true {
1887        $0match path.strip_prefix(root_path) {
1888            Ok(rel_path) => {
1889                let rel_path = RelativePathBuf::from_path(rel_path)
1890                    .ok()?;
1891                Some((*id, rel_path))
1892            }
1893            _ => {
1894                let _ = some_code()
1895                    .clone();
1896                None
1897            },
1898        }
1899    }
1900}
1901"#,
1902            r#"
1903fn main() {
1904    if true {
1905        if let Ok(rel_path) = path.strip_prefix(root_path) {
1906            let rel_path = RelativePathBuf::from_path(rel_path)
1907                .ok()?;
1908            Some((*id, rel_path))
1909        } else {
1910            let _ = some_code()
1911                .clone();
1912            None
1913        }
1914    }
1915}
1916"#,
1917        );
1918    }
1919
1920    #[test]
1921    fn replace_match_with_if_let_empty_wildcard_expr() {
1922        check_assist(
1923            replace_match_with_if_let,
1924            r#"
1925fn main() {
1926    $0match path.strip_prefix(root_path) {
1927        Ok(rel_path) => println!("{}", rel_path),
1928        _ => (),
1929    }
1930}
1931"#,
1932            r#"
1933fn main() {
1934    if let Ok(rel_path) = path.strip_prefix(root_path) {
1935        println!("{}", rel_path)
1936    }
1937}
1938"#,
1939        )
1940    }
1941
1942    #[test]
1943    fn replace_match_with_if_let_number_body() {
1944        check_assist(
1945            replace_match_with_if_let,
1946            r#"
1947fn main() {
1948    $0match Ok(()) {
1949        Ok(()) => {},
1950        Err(_) => 0,
1951    }
1952}
1953"#,
1954            r#"
1955fn main() {
1956    if let Err(_) = Ok(()) {
1957        0
1958    }
1959}
1960"#,
1961        )
1962    }
1963
1964    #[test]
1965    fn replace_match_with_if_let_exhaustive() {
1966        check_assist(
1967            replace_match_with_if_let,
1968            r#"
1969fn print_source(def_source: ModuleSource) {
1970    match def_so$0urce {
1971        ModuleSource::SourceFile(..) => { println!("source file"); }
1972        ModuleSource::Module(..) => { println!("module"); }
1973    }
1974}
1975"#,
1976            r#"
1977fn print_source(def_source: ModuleSource) {
1978    if let ModuleSource::SourceFile(..) = def_source { println!("source file"); } else { println!("module"); }
1979}
1980"#,
1981        )
1982    }
1983
1984    #[test]
1985    fn replace_match_with_if_let_prefer_name_bind() {
1986        check_assist(
1987            replace_match_with_if_let,
1988            r#"
1989fn foo() {
1990    match $0Foo(0) {
1991        Foo(_) => (),
1992        Bar(bar) => println!("bar {}", bar),
1993    }
1994}
1995"#,
1996            r#"
1997fn foo() {
1998    if let Bar(bar) = Foo(0) {
1999        println!("bar {}", bar)
2000    }
2001}
2002"#,
2003        );
2004        check_assist(
2005            replace_match_with_if_let,
2006            r#"
2007fn foo() {
2008    match $0Foo(0) {
2009        Bar(bar) => println!("bar {}", bar),
2010        Foo(_) => (),
2011    }
2012}
2013"#,
2014            r#"
2015fn foo() {
2016    if let Bar(bar) = Foo(0) {
2017        println!("bar {}", bar)
2018    }
2019}
2020"#,
2021        );
2022    }
2023
2024    #[test]
2025    fn replace_match_with_if_let_prefer_nonempty_body() {
2026        check_assist(
2027            replace_match_with_if_let,
2028            r#"
2029fn foo() {
2030    match $0Ok(0) {
2031        Ok(value) => {},
2032        Err(err) => eprintln!("{}", err),
2033    }
2034}
2035"#,
2036            r#"
2037fn foo() {
2038    if let Err(err) = Ok(0) {
2039        eprintln!("{}", err)
2040    }
2041}
2042"#,
2043        );
2044        check_assist(
2045            replace_match_with_if_let,
2046            r#"
2047fn foo() {
2048    match $0Ok(0) {
2049        Err(err) => eprintln!("{}", err),
2050        Ok(value) => {},
2051    }
2052}
2053"#,
2054            r#"
2055fn foo() {
2056    if let Err(err) = Ok(0) {
2057        eprintln!("{}", err)
2058    }
2059}
2060"#,
2061        );
2062    }
2063
2064    #[test]
2065    fn replace_match_with_if_let_rejects_double_name_bindings() {
2066        check_assist_not_applicable(
2067            replace_match_with_if_let,
2068            r#"
2069fn foo() {
2070    match $0Foo(0) {
2071        Foo(foo) => println!("bar {}", foo),
2072        Bar(bar) => println!("bar {}", bar),
2073    }
2074}
2075"#,
2076        );
2077    }
2078
2079    #[test]
2080    fn test_replace_match_with_if_let_keeps_unsafe_block() {
2081        check_assist(
2082            replace_match_with_if_let,
2083            r#"
2084impl VariantData {
2085    pub fn is_struct(&self) -> bool {
2086        $0match *self {
2087            VariantData::Struct(..) => true,
2088            _ => unsafe { unreachable_unchecked() },
2089        }
2090    }
2091}           "#,
2092            r#"
2093impl VariantData {
2094    pub fn is_struct(&self) -> bool {
2095        if let VariantData::Struct(..) = *self {
2096            true
2097        } else {
2098            unsafe { unreachable_unchecked() }
2099        }
2100    }
2101}           "#,
2102        )
2103    }
2104
2105    #[test]
2106    fn test_replace_match_with_if_let_forces_else() {
2107        check_assist(
2108            replace_match_with_if_let,
2109            r#"
2110fn main() {
2111    match$0 0 {
2112        0 => (),
2113        _ => code(),
2114    }
2115}
2116"#,
2117            r#"
2118fn main() {
2119    if let 0 = 0 {
2120        ()
2121    } else {
2122        code()
2123    }
2124}
2125"#,
2126        )
2127    }
2128
2129    #[test]
2130    fn test_replace_match_with_if_bool() {
2131        check_assist(
2132            replace_match_with_if_let,
2133            r#"
2134fn main() {
2135    match$0 b {
2136        true => (),
2137        _ => code(),
2138    }
2139}
2140"#,
2141            r#"
2142fn main() {
2143    if b {
2144        ()
2145    } else {
2146        code()
2147    }
2148}
2149"#,
2150        );
2151        check_assist(
2152            replace_match_with_if_let,
2153            r#"
2154fn main() {
2155    match$0 b {
2156        false => code(),
2157        true => (),
2158    }
2159}
2160"#,
2161            r#"
2162fn main() {
2163    if !b {
2164        code()
2165    }
2166}
2167"#,
2168        );
2169        check_assist(
2170            replace_match_with_if_let,
2171            r#"
2172fn main() {
2173    match$0 b {
2174        false => (),
2175        true => code(),
2176    }
2177}
2178"#,
2179            r#"
2180fn main() {
2181    if b {
2182        code()
2183    }
2184}
2185"#,
2186        )
2187    }
2188
2189    #[test]
2190    fn test_replace_match_with_if_let_chain() {
2191        check_assist(
2192            replace_match_with_if_let,
2193            r#"
2194fn main() {
2195    match$0 Some(0) {
2196        Some(n) if n % 2 == 0 && n != 6 => (),
2197        _ => code(),
2198    }
2199}
2200"#,
2201            r#"
2202fn main() {
2203    if let Some(n) = Some(0) && n % 2 == 0 && n != 6 {
2204        ()
2205    } else {
2206        code()
2207    }
2208}
2209"#,
2210        )
2211    }
2212
2213    #[test]
2214    fn test_replace_match_with_if_let_not_applicable_pat2_is_ident_pat() {
2215        check_assist_not_applicable(
2216            replace_match_with_if_let,
2217            r"
2218fn test(a: i32) {
2219    match$0 a {
2220        1 => code(),
2221        other => code(other),
2222    }
2223}
2224",
2225        )
2226    }
2227}