Skip to main content

ra_ap_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::{
7        self, HasName,
8        edit::{AstNodeEdit, IndentLevel},
9        syntax_factory::SyntaxFactory,
10    },
11    syntax_editor::SyntaxEditor,
12};
13
14use crate::{
15    AssistContext, AssistId, Assists,
16    utils::{does_pat_match_variant, does_pat_variant_nested_or_literal, unwrap_trivial_block},
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// ```
45pub(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 indent = if_expr.indent_level();
57    let if_exprs = successors(Some(if_expr.clone()), |expr| match expr.else_branch()? {
58        ast::ElseBranch::IfExpr(expr) => Some(expr),
59        ast::ElseBranch::Block(block) => {
60            let block = unwrap_trivial_block(block);
61            else_block = Some(block.reset_indent().indent(IndentLevel(1)));
62            None
63        }
64    });
65    let scrutinee_to_be_expr = if_expr.condition()?;
66    let scrutinee_to_be_expr = match let_and_guard(&scrutinee_to_be_expr) {
67        (Some(let_expr), _) => let_expr.expr()?,
68        (None, cond) => cond?,
69    };
70
71    let mut pat_seen = false;
72    let mut cond_bodies = Vec::new();
73    for if_expr in if_exprs {
74        let cond = if_expr.condition()?;
75        let (cond, guard) = match let_and_guard(&cond) {
76            (None, guard) => (None, Some(guard?)),
77            (Some(let_), guard) => {
78                let pat = let_.pat()?;
79                let expr = let_.expr()?;
80                if scrutinee_to_be_expr.syntax().text() != expr.syntax().text() {
81                    // Only if all condition expressions are equal we can merge them into a match
82                    return None;
83                }
84                pat_seen = true;
85                (Some(pat), guard)
86            }
87        };
88        let guard = if let Some(guard) = &guard {
89            Some(guard.dedent(indent).indent(IndentLevel(1)))
90        } else {
91            guard
92        };
93
94        let body = if_expr.then_branch()?.indent(IndentLevel(1));
95        cond_bodies.push((cond, guard, body));
96    }
97
98    if !pat_seen && cond_bodies.len() != 1 {
99        // Don't offer turning an if (chain) without patterns into a match,
100        // unless its a simple `if cond { .. } (else { .. })`
101        return None;
102    }
103
104    let let_ = if pat_seen { " let" } else { "" };
105
106    acc.add(
107        AssistId::refactor_rewrite("replace_if_let_with_match"),
108        format!("Replace if{let_} with match"),
109        available_range,
110        move |builder| {
111            let make = SyntaxFactory::with_mappings();
112            let match_expr: ast::Expr = {
113                let else_arm = make_else_arm(ctx, &make, else_block, &cond_bodies);
114                let make_match_arm =
115                    |(pat, guard, body): (_, Option<ast::Expr>, ast::BlockExpr)| {
116                        // Dedent from original position, then indent for match arm
117                        let body = body.dedent(indent);
118                        let body = unwrap_trivial_block(body);
119                        match (pat, guard.map(|it| make.match_guard(it))) {
120                            (Some(pat), guard) => make.match_arm(pat, guard, body),
121                            (None, _) if !pat_seen => {
122                                make.match_arm(make.literal_pat("true").into(), None, body)
123                            }
124                            (None, guard) => {
125                                make.match_arm(make.wildcard_pat().into(), guard, body)
126                            }
127                        }
128                    };
129                let arms = cond_bodies.into_iter().map(make_match_arm).chain([else_arm]);
130                let expr = scrutinee_to_be_expr.reset_indent();
131                let match_expr = make.expr_match(expr, make.match_arm_list(arms)).indent(indent);
132                match_expr.into()
133            };
134
135            let has_preceding_if_expr =
136                if_expr.syntax().parent().is_some_and(|it| ast::IfExpr::can_cast(it.kind()));
137            let expr = if has_preceding_if_expr {
138                // make sure we replace the `else if let ...` with a block so we don't end up with `else expr`
139                let block_expr = make
140                    .block_expr([], Some(match_expr.dedent(indent).indent(IndentLevel(1))))
141                    .indent(indent);
142                block_expr.into()
143            } else {
144                match_expr
145            };
146
147            let mut editor = builder.make_editor(if_expr.syntax());
148            editor.replace(if_expr.syntax(), expr.syntax());
149            editor.add_mappings(make.finish_with_mappings());
150            builder.add_file_edits(ctx.vfs_file_id(), editor);
151        },
152    )
153}
154
155fn make_else_arm(
156    ctx: &AssistContext<'_>,
157    make: &SyntaxFactory,
158    else_expr: Option<ast::Expr>,
159    conditionals: &[(Option<ast::Pat>, Option<ast::Expr>, ast::BlockExpr)],
160) -> ast::MatchArm {
161    let (pattern, expr) = if let Some(else_expr) = else_expr {
162        let pattern = match conditionals {
163            [(None, Some(_), _)] => make.literal_pat("false").into(),
164            [(Some(pat), _, _)] => match ctx
165                .sema
166                .type_of_pat(pat)
167                .and_then(|ty| TryEnum::from_ty(&ctx.sema, &ty.adjusted()))
168            {
169                Some(it) => {
170                    if does_pat_match_variant(pat, &it.sad_pattern()) {
171                        it.happy_pattern_wildcard()
172                    } else if does_pat_variant_nested_or_literal(ctx, pat) {
173                        make.wildcard_pat().into()
174                    } else {
175                        it.sad_pattern()
176                    }
177                }
178                None => make.wildcard_pat().into(),
179            },
180            _ => make.wildcard_pat().into(),
181        };
182        (pattern, else_expr)
183    } else {
184        let pattern = match conditionals {
185            [(None, Some(_), _)] => make.literal_pat("false").into(),
186            _ => make.wildcard_pat().into(),
187        };
188        (pattern, make.expr_unit())
189    };
190    make.match_arm(pattern, None, expr)
191}
192
193// Assist: replace_match_with_if_let
194//
195// Replaces a binary `match` with a wildcard pattern with an `if let` expression.
196//
197// ```
198// enum Action { Move { distance: u32 }, Stop }
199//
200// fn handle(action: Action) {
201//     $0match action {
202//         Action::Move { distance } => foo(distance),
203//         _ => bar(),
204//     }
205// }
206// ```
207// ->
208// ```
209// enum Action { Move { distance: u32 }, Stop }
210//
211// fn handle(action: Action) {
212//     if let Action::Move { distance } = action {
213//         foo(distance)
214//     } else {
215//         bar()
216//     }
217// }
218// ```
219pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
220    let match_expr: ast::MatchExpr = ctx.find_node_at_offset()?;
221    let match_arm_list = match_expr.match_arm_list()?;
222    let available_range = TextRange::new(
223        match_expr.syntax().text_range().start(),
224        match_arm_list.syntax().text_range().start(),
225    );
226    let cursor_in_range = available_range.contains_range(ctx.selection_trimmed());
227    if !cursor_in_range {
228        return None;
229    }
230
231    let mut arms = match_arm_list.arms();
232    let (first_arm, second_arm) = (arms.next()?, arms.next()?);
233    if arms.next().is_some() || second_arm.guard().is_some() {
234        return None;
235    }
236    if first_arm.guard().is_some() && ctx.edition() < Edition::Edition2024 {
237        return None;
238    }
239
240    let (if_let_pat, guard, then_expr, else_expr) = pick_pattern_and_expr_order(
241        &ctx.sema,
242        first_arm.pat()?,
243        second_arm.pat()?,
244        first_arm.expr()?,
245        second_arm.expr()?,
246        first_arm.guard(),
247        second_arm.guard(),
248    )?;
249    let scrutinee = match_expr.expr()?.reset_indent();
250    let guard = guard.and_then(|it| it.condition());
251
252    let let_ = match &if_let_pat {
253        ast::Pat::LiteralPat(p)
254            if p.literal()
255                .map(|it| it.token().kind())
256                .is_some_and(|it| it == T![true] || it == T![false]) =>
257        {
258            ""
259        }
260        _ => " let",
261    };
262    acc.add(
263        AssistId::refactor_rewrite("replace_match_with_if_let"),
264        format!("Replace match with if{let_}"),
265        match_expr.syntax().text_range(),
266        move |builder| {
267            let make = SyntaxFactory::with_mappings();
268            let make_block_expr = |expr: ast::Expr| {
269                // Blocks with modifiers (unsafe, async, etc.) are parsed as BlockExpr, but are
270                // formatted without enclosing braces. If we encounter such block exprs,
271                // wrap them in another BlockExpr.
272                match expr {
273                    ast::Expr::BlockExpr(block) if block.modifier().is_none() => block,
274                    expr => make.block_expr([], Some(expr.indent(IndentLevel(1)))),
275                }
276            };
277
278            let condition = match if_let_pat {
279                ast::Pat::LiteralPat(p)
280                    if p.literal().is_some_and(|it| it.token().kind() == T![true]) =>
281                {
282                    scrutinee
283                }
284                ast::Pat::LiteralPat(p)
285                    if p.literal().is_some_and(|it| it.token().kind() == T![false]) =>
286                {
287                    make.expr_prefix(T![!], scrutinee).into()
288                }
289                _ => make.expr_let(if_let_pat, scrutinee).into(),
290            };
291            let condition = if let Some(guard) = guard {
292                make.expr_bin(condition, ast::BinaryOp::LogicOp(ast::LogicOp::And), guard).into()
293            } else {
294                condition
295            };
296            let then_expr = then_expr.reset_indent();
297            let else_expr = else_expr.reset_indent();
298            let then_block = make_block_expr(then_expr);
299            let else_expr = if is_empty_expr(&else_expr) { None } else { Some(else_expr) };
300            let if_let_expr = make
301                .expr_if(
302                    condition,
303                    then_block,
304                    else_expr.map(make_block_expr).map(ast::ElseBranch::Block),
305                )
306                .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_option_ref() {
852        check_assist(
853            replace_if_let_with_match,
854            r#"
855//- minicore: option
856fn foo(x: &Option<i32>) {
857    $0if let Some(x) = x {
858        println!("{}", x)
859    } else {
860        println!("none")
861    }
862}
863"#,
864            r#"
865fn foo(x: &Option<i32>) {
866    match x {
867        Some(x) => println!("{}", x),
868        None => println!("none"),
869    }
870}
871"#,
872        );
873    }
874
875    #[test]
876    fn special_case_inverted_option() {
877        check_assist(
878            replace_if_let_with_match,
879            r#"
880//- minicore: option
881fn foo(x: Option<i32>) {
882    $0if let None = x {
883        println!("none")
884    } else {
885        println!("some")
886    }
887}
888"#,
889            r#"
890fn foo(x: Option<i32>) {
891    match x {
892        None => println!("none"),
893        Some(_) => println!("some"),
894    }
895}
896"#,
897        );
898    }
899
900    #[test]
901    fn special_case_result() {
902        check_assist(
903            replace_if_let_with_match,
904            r#"
905//- minicore: result
906fn foo(x: Result<i32, ()>) {
907    $0if let Ok(x) = x {
908        println!("{}", x)
909    } else {
910        println!("none")
911    }
912}
913"#,
914            r#"
915fn foo(x: Result<i32, ()>) {
916    match x {
917        Ok(x) => println!("{}", x),
918        Err(_) => println!("none"),
919    }
920}
921"#,
922        );
923    }
924
925    #[test]
926    fn special_case_inverted_result() {
927        check_assist(
928            replace_if_let_with_match,
929            r#"
930//- minicore: result
931fn foo(x: Result<i32, ()>) {
932    $0if let Err(x) = x {
933        println!("{}", x)
934    } else {
935        println!("ok")
936    }
937}
938"#,
939            r#"
940fn foo(x: Result<i32, ()>) {
941    match x {
942        Err(x) => println!("{}", x),
943        Ok(_) => println!("ok"),
944    }
945}
946"#,
947        );
948    }
949
950    #[test]
951    fn nested_indent() {
952        check_assist(
953            replace_if_let_with_match,
954            r#"
955fn main() {
956    if true {
957        $0if let Ok(rel_path) = path.strip_prefix(root_path)
958            .and(x)
959        {
960            let rel_path = RelativePathBuf::from_path(rel_path)
961                .ok()?;
962            Some((*id, rel_path))
963        } else {
964            let _ = some_code()
965                .clone();
966            None
967        }
968    }
969}
970"#,
971            r#"
972fn main() {
973    if true {
974        match path.strip_prefix(root_path)
975            .and(x)
976        {
977            Ok(rel_path) => {
978                let rel_path = RelativePathBuf::from_path(rel_path)
979                    .ok()?;
980                Some((*id, rel_path))
981            }
982            _ => {
983                let _ = some_code()
984                    .clone();
985                None
986            }
987        }
988    }
989}
990"#,
991        );
992
993        check_assist(
994            replace_if_let_with_match,
995            r#"
996fn main() {
997    if true {
998        $0if let Ok(rel_path) = path.strip_prefix(root_path)
999            .and(x)
1000        {
1001            Foo {
1002                x: 1
1003            }
1004        } else {
1005            Foo {
1006                x: 2
1007            }
1008        }
1009    }
1010}
1011"#,
1012            r#"
1013fn main() {
1014    if true {
1015        match path.strip_prefix(root_path)
1016            .and(x)
1017        {
1018            Ok(rel_path) => {
1019                Foo {
1020                    x: 1
1021                }
1022            }
1023            _ => {
1024                Foo {
1025                    x: 2
1026                }
1027            }
1028        }
1029    }
1030}
1031"#,
1032        );
1033
1034        check_assist(
1035            replace_if_let_with_match,
1036            r#"
1037fn main() {
1038    if true {
1039        $0if true
1040            && false
1041        {
1042            foo()
1043        }
1044    }
1045}
1046"#,
1047            r#"
1048fn main() {
1049    if true {
1050        match true
1051            && false
1052        {
1053            true => foo(),
1054            false => (),
1055        }
1056    }
1057}
1058"#,
1059        );
1060    }
1061
1062    #[test]
1063    fn test_if_let_with_match_nested_tuple_struct() {
1064        check_assist(
1065            replace_if_let_with_match,
1066            r#"
1067//- minicore: result, option
1068fn foo(x: Result<i32, ()>) {
1069    let bar: Result<_, ()> = Ok(Some(1));
1070    $0if let Ok(Some(_)) = bar {
1071        ()
1072    } else {
1073        ()
1074    }
1075}
1076"#,
1077            r#"
1078fn foo(x: Result<i32, ()>) {
1079    let bar: Result<_, ()> = Ok(Some(1));
1080    match bar {
1081        Ok(Some(_)) => (),
1082        _ => (),
1083    }
1084}
1085"#,
1086        );
1087
1088        check_assist(
1089            replace_if_let_with_match,
1090            r#"
1091//- minicore: result
1092struct MyStruct(i32, i32);
1093fn foo(x: Result<MyStruct, ()>) {
1094    let bar: Result<MyStruct, ()> = Ok(MyStruct(1, 2));
1095    $0if let Ok(MyStruct(a, b)) = bar {
1096        ()
1097    } else {
1098        ()
1099    }
1100}
1101"#,
1102            r#"
1103struct MyStruct(i32, i32);
1104fn foo(x: Result<MyStruct, ()>) {
1105    let bar: Result<MyStruct, ()> = Ok(MyStruct(1, 2));
1106    match bar {
1107        Ok(MyStruct(a, b)) => (),
1108        Err(_) => (),
1109    }
1110}
1111"#,
1112        );
1113    }
1114
1115    #[test]
1116    fn test_if_let_with_match_nested_slice() {
1117        check_assist(
1118            replace_if_let_with_match,
1119            r#"
1120//- minicore: result
1121fn foo(x: Result<&[i32], ()>) {
1122    let foo: Result<&[_], ()> = Ok(&[0, 1, 2]);
1123    $0if let Ok([]) = foo {
1124        ()
1125    } else {
1126        ()
1127    }
1128}
1129        "#,
1130            r#"
1131fn foo(x: Result<&[i32], ()>) {
1132    let foo: Result<&[_], ()> = Ok(&[0, 1, 2]);
1133    match foo {
1134        Ok([]) => (),
1135        _ => (),
1136    }
1137}
1138        "#,
1139        );
1140
1141        check_assist(
1142            replace_if_let_with_match,
1143            r#"
1144//- minicore: result
1145fn foo(x: Result<[&'static str; 2], ()>) {
1146    let foobar: Result<_, ()> = Ok(["foo", "bar"]);
1147    $0if let Ok([_, "bar"]) = foobar {
1148        ()
1149    } else {
1150        ()
1151    }
1152}
1153"#,
1154            r#"
1155fn foo(x: Result<[&'static str; 2], ()>) {
1156    let foobar: Result<_, ()> = Ok(["foo", "bar"]);
1157    match foobar {
1158        Ok([_, "bar"]) => (),
1159        _ => (),
1160    }
1161}
1162"#,
1163        );
1164
1165        check_assist(
1166            replace_if_let_with_match,
1167            r#"
1168//- minicore: result
1169fn foo(x: Result<[&'static str; 2], ()>) {
1170    let foobar: Result<_, ()> = Ok(["foo", "bar"]);
1171    $0if let Ok([..]) = foobar {
1172        ()
1173    } else {
1174        ()
1175    }
1176}
1177"#,
1178            r#"
1179fn foo(x: Result<[&'static str; 2], ()>) {
1180    let foobar: Result<_, ()> = Ok(["foo", "bar"]);
1181    match foobar {
1182        Ok([..]) => (),
1183        Err(_) => (),
1184    }
1185}
1186"#,
1187        );
1188
1189        check_assist(
1190            replace_if_let_with_match,
1191            r#"
1192//- minicore: result
1193fn foo(x: Result<&[&'static str], ()>) {
1194    let foobar: Result<&[&'static str], ()> = Ok(&["foo", "bar"]);
1195    $0if let Ok([a, ..]) = foobar {
1196        ()
1197    } else {
1198        ()
1199    }
1200}
1201"#,
1202            r#"
1203fn foo(x: Result<&[&'static str], ()>) {
1204    let foobar: Result<&[&'static str], ()> = Ok(&["foo", "bar"]);
1205    match foobar {
1206        Ok([a, ..]) => (),
1207        _ => (),
1208    }
1209}
1210"#,
1211        );
1212
1213        check_assist(
1214            replace_if_let_with_match,
1215            r#"
1216//- minicore: result
1217fn foo(x: Result<&[&'static str], ()>) {
1218    let foobar: Result<&[&'static str], ()> = Ok(&["foo", "bar"]);
1219    $0if let Ok([a, .., b, c]) = foobar {
1220        ()
1221    } else {
1222        ()
1223    }
1224}
1225"#,
1226            r#"
1227fn foo(x: Result<&[&'static str], ()>) {
1228    let foobar: Result<&[&'static str], ()> = Ok(&["foo", "bar"]);
1229    match foobar {
1230        Ok([a, .., b, c]) => (),
1231        _ => (),
1232    }
1233}
1234"#,
1235        );
1236
1237        check_assist(
1238            replace_if_let_with_match,
1239            r#"
1240//- minicore: result
1241fn foo(x: Result<Option<[&'static str; 2]>, ()>) {
1242    let foobar: Result<_, ()> = Ok(Some(["foo", "bar"]));
1243    $0if let Ok(Some([_, "bar"])) = foobar {
1244        ()
1245    } else {
1246        ()
1247    }
1248}
1249"#,
1250            r#"
1251fn foo(x: Result<Option<[&'static str; 2]>, ()>) {
1252    let foobar: Result<_, ()> = Ok(Some(["foo", "bar"]));
1253    match foobar {
1254        Ok(Some([_, "bar"])) => (),
1255        _ => (),
1256    }
1257}
1258"#,
1259        );
1260    }
1261
1262    #[test]
1263    fn test_if_let_with_match_nested_literal() {
1264        check_assist(
1265            replace_if_let_with_match,
1266            r#"
1267//- minicore: result
1268fn foo(x: Result<&'static str, ()>) {
1269    let bar: Result<&_, ()> = Ok("bar");
1270    $0if let Ok("foo") = bar {
1271        ()
1272    } else {
1273        ()
1274    }
1275}
1276"#,
1277            r#"
1278fn foo(x: Result<&'static str, ()>) {
1279    let bar: Result<&_, ()> = Ok("bar");
1280    match bar {
1281        Ok("foo") => (),
1282        _ => (),
1283    }
1284}
1285"#,
1286        );
1287    }
1288
1289    #[test]
1290    fn test_if_let_with_match_nested_tuple() {
1291        check_assist(
1292            replace_if_let_with_match,
1293            r#"
1294//- minicore: result
1295fn foo(x: Result<(i32, i32, i32), ()>) {
1296    let bar: Result<(i32, i32, i32), ()> = Ok((1, 2, 3));
1297    $0if let Ok((1, second, third)) = bar {
1298        ()
1299    } else {
1300        ()
1301    }
1302}
1303"#,
1304            r#"
1305fn foo(x: Result<(i32, i32, i32), ()>) {
1306    let bar: Result<(i32, i32, i32), ()> = Ok((1, 2, 3));
1307    match bar {
1308        Ok((1, second, third)) => (),
1309        _ => (),
1310    }
1311}
1312"#,
1313        );
1314
1315        check_assist(
1316            replace_if_let_with_match,
1317            r#"
1318//- minicore: result
1319fn foo(x: Result<(i32, i32, i32), ()>) {
1320    let bar: Result<(i32, i32, i32), ()> = Ok((1, 2, 3));
1321    $0if let Ok((first, second, third)) = bar {
1322        ()
1323    } else {
1324        ()
1325    }
1326}
1327"#,
1328            r#"
1329fn foo(x: Result<(i32, i32, i32), ()>) {
1330    let bar: Result<(i32, i32, i32), ()> = Ok((1, 2, 3));
1331    match bar {
1332        Ok((first, second, third)) => (),
1333        Err(_) => (),
1334    }
1335}
1336"#,
1337        );
1338    }
1339
1340    #[test]
1341    fn test_if_let_with_match_nested_or() {
1342        check_assist(
1343            replace_if_let_with_match,
1344            r#"
1345//- minicore: result
1346fn foo(x: Result<i32, ()>) {
1347    let bar: Result<i32, ()> = Ok(1);
1348    $0if let Ok(1 | 2) = bar {
1349        ()
1350    } else {
1351        ()
1352    }
1353}
1354"#,
1355            r#"
1356fn foo(x: Result<i32, ()>) {
1357    let bar: Result<i32, ()> = Ok(1);
1358    match bar {
1359        Ok(1 | 2) => (),
1360        _ => (),
1361    }
1362}
1363"#,
1364        );
1365
1366        check_assist(
1367            replace_if_let_with_match,
1368            r#"
1369//- minicore: result
1370fn foo(x: Result<(i32, i32), ()>) {
1371    let bar: Result<(i32, i32), ()> = Ok((1, 2));
1372    $0if let Ok((b, a) | (a, b)) = bar {
1373        ()
1374    } else {
1375        ()
1376    }
1377}
1378"#,
1379            r#"
1380fn foo(x: Result<(i32, i32), ()>) {
1381    let bar: Result<(i32, i32), ()> = Ok((1, 2));
1382    match bar {
1383        Ok((b, a) | (a, b)) => (),
1384        Err(_) => (),
1385    }
1386}
1387"#,
1388        );
1389
1390        check_assist(
1391            replace_if_let_with_match,
1392            r#"
1393//- minicore: result
1394fn foo(x: Result<(i32, i32), ()>) {
1395    let bar: Result<(i32, i32), ()> = Ok((1, 2));
1396    $0if let Ok((1, a) | (a, 2)) = bar {
1397        ()
1398    } else {
1399        ()
1400    }
1401}
1402"#,
1403            r#"
1404fn foo(x: Result<(i32, i32), ()>) {
1405    let bar: Result<(i32, i32), ()> = Ok((1, 2));
1406    match bar {
1407        Ok((1, a) | (a, 2)) => (),
1408        _ => (),
1409    }
1410}
1411"#,
1412        );
1413    }
1414
1415    #[test]
1416    fn test_if_let_with_match_nested_range() {
1417        check_assist(
1418            replace_if_let_with_match,
1419            r#"
1420//- minicore: result
1421fn foo(x: Result<i32, ()>) {
1422    let bar: Result<i32, ()> = Ok(1);
1423    $0if let Ok(1..2) = bar {
1424        ()
1425    } else {
1426        ()
1427    }
1428}
1429"#,
1430            r#"
1431fn foo(x: Result<i32, ()>) {
1432    let bar: Result<i32, ()> = Ok(1);
1433    match bar {
1434        Ok(1..2) => (),
1435        _ => (),
1436    }
1437}
1438"#,
1439        );
1440    }
1441
1442    #[test]
1443    fn test_if_let_with_match_nested_paren() {
1444        check_assist(
1445            replace_if_let_with_match,
1446            r#"
1447//- minicore: result
1448fn foo(x: Result<(i32, i32), ()>) {
1449    let bar: Result<(i32, i32), ()> = Ok((1, 1));
1450    $0if let Ok(((1, 2))) = bar {
1451        ()
1452    } else {
1453        ()
1454    }
1455}
1456"#,
1457            r#"
1458fn foo(x: Result<(i32, i32), ()>) {
1459    let bar: Result<(i32, i32), ()> = Ok((1, 1));
1460    match bar {
1461        Ok(((1, 2))) => (),
1462        _ => (),
1463    }
1464}
1465"#,
1466        );
1467
1468        check_assist(
1469            replace_if_let_with_match,
1470            r#"
1471//- minicore: result
1472fn foo(x: Result<(i32, i32), ()>) {
1473    let bar: Result<(i32, i32), ()> = Ok((1, 1));
1474    $0if let Ok(((a, b))) = bar {
1475        ()
1476    } else {
1477        ()
1478    }
1479}
1480"#,
1481            r#"
1482fn foo(x: Result<(i32, i32), ()>) {
1483    let bar: Result<(i32, i32), ()> = Ok((1, 1));
1484    match bar {
1485        Ok(((a, b))) => (),
1486        Err(_) => (),
1487    }
1488}
1489"#,
1490        );
1491    }
1492
1493    #[test]
1494    fn test_if_let_with_match_nested_macro() {
1495        check_assist(
1496            replace_if_let_with_match,
1497            r#"
1498//- minicore: result
1499fn foo(x: Result<i32, ()>) {
1500    macro_rules! is_42 {
1501        () => {
1502            42
1503        };
1504    }
1505
1506    let bar: Result<i32, ()> = Ok(1);
1507    $0if let Ok(is_42!()) = bar {
1508        ()
1509    } else {
1510        ()
1511    }
1512}
1513"#,
1514            r#"
1515fn foo(x: Result<i32, ()>) {
1516    macro_rules! is_42 {
1517        () => {
1518            42
1519        };
1520    }
1521
1522    let bar: Result<i32, ()> = Ok(1);
1523    match bar {
1524        Ok(is_42!()) => (),
1525        _ => (),
1526    }
1527}
1528"#,
1529        );
1530    }
1531
1532    #[test]
1533    fn test_if_let_with_match_nested_path() {
1534        check_assist(
1535            replace_if_let_with_match,
1536            r#"
1537//- minicore: result
1538enum MyEnum {
1539    Foo,
1540    Bar,
1541}
1542
1543fn foo(x: Result<MyEnum, ()>) {
1544    let bar: Result<MyEnum, ()> = Ok(MyEnum::Foo);
1545    $0if let Ok(MyEnum::Foo) = bar {
1546        ()
1547    } else {
1548        ()
1549    }
1550}
1551"#,
1552            r#"
1553enum MyEnum {
1554    Foo,
1555    Bar,
1556}
1557
1558fn foo(x: Result<MyEnum, ()>) {
1559    let bar: Result<MyEnum, ()> = Ok(MyEnum::Foo);
1560    match bar {
1561        Ok(MyEnum::Foo) => (),
1562        _ => (),
1563    }
1564}
1565"#,
1566        );
1567    }
1568
1569    #[test]
1570    fn test_if_let_with_match_nested_record() {
1571        check_assist(
1572            replace_if_let_with_match,
1573            r#"
1574//- minicore: result
1575struct MyStruct {
1576    foo: i32,
1577    bar: i32,
1578}
1579
1580fn foo(x: Result<MyStruct, ()>) {
1581    let bar: Result<MyStruct, ()> = Ok(MyStruct { foo: 1, bar: 2 });
1582    $0if let Ok(MyStruct { foo, bar }) = bar {
1583        ()
1584    } else {
1585        ()
1586    }
1587}
1588"#,
1589            r#"
1590struct MyStruct {
1591    foo: i32,
1592    bar: i32,
1593}
1594
1595fn foo(x: Result<MyStruct, ()>) {
1596    let bar: Result<MyStruct, ()> = Ok(MyStruct { foo: 1, bar: 2 });
1597    match bar {
1598        Ok(MyStruct { foo, bar }) => (),
1599        Err(_) => (),
1600    }
1601}
1602"#,
1603        );
1604
1605        check_assist(
1606            replace_if_let_with_match,
1607            r#"
1608//- minicore: result
1609struct MyStruct {
1610    foo: i32,
1611    bar: i32,
1612}
1613
1614fn foo(x: Result<MyStruct, ()>) {
1615    let bar: Result<MyStruct, ()> = Ok(MyStruct { foo: 1, bar: 2 });
1616    $0if let Ok(MyStruct { foo, bar: 12 }) = bar {
1617        ()
1618    } else {
1619        ()
1620    }
1621}
1622"#,
1623            r#"
1624struct MyStruct {
1625    foo: i32,
1626    bar: i32,
1627}
1628
1629fn foo(x: Result<MyStruct, ()>) {
1630    let bar: Result<MyStruct, ()> = Ok(MyStruct { foo: 1, bar: 2 });
1631    match bar {
1632        Ok(MyStruct { foo, bar: 12 }) => (),
1633        _ => (),
1634    }
1635}
1636"#,
1637        );
1638
1639        check_assist(
1640            replace_if_let_with_match,
1641            r#"
1642//- minicore: result
1643struct MyStruct {
1644    foo: i32,
1645    bar: i32,
1646}
1647
1648fn foo(x: Result<MyStruct, ()>) {
1649    let bar: Result<MyStruct, ()> = Ok(MyStruct { foo: 1, bar: 2 });
1650    $0if let Ok(MyStruct { foo, .. }) = bar {
1651        ()
1652    } else {
1653        ()
1654    }
1655}
1656"#,
1657            r#"
1658struct MyStruct {
1659    foo: i32,
1660    bar: i32,
1661}
1662
1663fn foo(x: Result<MyStruct, ()>) {
1664    let bar: Result<MyStruct, ()> = Ok(MyStruct { foo: 1, bar: 2 });
1665    match bar {
1666        Ok(MyStruct { foo, .. }) => (),
1667        Err(_) => (),
1668    }
1669}
1670"#,
1671        );
1672
1673        check_assist(
1674            replace_if_let_with_match,
1675            r#"
1676//- minicore: result
1677enum MyEnum {
1678    Foo(i32, i32),
1679    Bar { a: i32, b: i32 },
1680}
1681
1682fn foo(x: Result<MyEnum, ()>) {
1683    let bar: Result<MyEnum, ()> = Ok(MyEnum::Foo(1, 2));
1684    $0if let Ok(MyEnum::Bar { a, b }) = bar {
1685        ()
1686    } else {
1687        ()
1688    }
1689}
1690"#,
1691            r#"
1692enum MyEnum {
1693    Foo(i32, i32),
1694    Bar { a: i32, b: i32 },
1695}
1696
1697fn foo(x: Result<MyEnum, ()>) {
1698    let bar: Result<MyEnum, ()> = Ok(MyEnum::Foo(1, 2));
1699    match bar {
1700        Ok(MyEnum::Bar { a, b }) => (),
1701        _ => (),
1702    }
1703}
1704"#,
1705        );
1706    }
1707
1708    #[test]
1709    fn test_if_let_with_match_nested_ident() {
1710        check_assist(
1711            replace_if_let_with_match,
1712            r#"
1713//- minicore: result
1714fn foo(x: Result<i32, ()>) {
1715    let bar: Result<i32, ()> = Ok(1);
1716    $0if let Ok(a @ 1..2) = bar {
1717        ()
1718    } else {
1719        ()
1720    }
1721}
1722"#,
1723            r#"
1724fn foo(x: Result<i32, ()>) {
1725    let bar: Result<i32, ()> = Ok(1);
1726    match bar {
1727        Ok(a @ 1..2) => (),
1728        _ => (),
1729    }
1730}
1731"#,
1732        );
1733
1734        check_assist(
1735            replace_if_let_with_match,
1736            r#"
1737//- minicore: result
1738fn foo(x: Result<i32, ()>) {
1739    let bar: Result<i32, ()> = Ok(1);
1740    $0if let Ok(a) = bar {
1741        ()
1742    } else {
1743        ()
1744    }
1745}
1746"#,
1747            r#"
1748fn foo(x: Result<i32, ()>) {
1749    let bar: Result<i32, ()> = Ok(1);
1750    match bar {
1751        Ok(a) => (),
1752        Err(_) => (),
1753    }
1754}
1755"#,
1756        );
1757
1758        check_assist(
1759            replace_if_let_with_match,
1760            r#"
1761//- minicore: result
1762fn foo(x: Result<i32, ()>) {
1763    let bar: Result<i32, ()> = Ok(1);
1764    $0if let Ok(a @ b @ c @ d) = bar {
1765        ()
1766    } else {
1767        ()
1768    }
1769}
1770"#,
1771            r#"
1772fn foo(x: Result<i32, ()>) {
1773    let bar: Result<i32, ()> = Ok(1);
1774    match bar {
1775        Ok(a @ b @ c @ d) => (),
1776        Err(_) => (),
1777    }
1778}
1779"#,
1780        );
1781    }
1782
1783    #[test]
1784    fn test_replace_match_with_if_let_unwraps_simple_expressions() {
1785        check_assist(
1786            replace_match_with_if_let,
1787            r#"
1788impl VariantData {
1789    pub fn is_struct(&self) -> bool {
1790        $0match *self {
1791            VariantData::Struct(..) => true,
1792            _ => false,
1793        }
1794    }
1795}           "#,
1796            r#"
1797impl VariantData {
1798    pub fn is_struct(&self) -> bool {
1799        if let VariantData::Struct(..) = *self {
1800            true
1801        } else {
1802            false
1803        }
1804    }
1805}           "#,
1806        )
1807    }
1808
1809    #[test]
1810    fn test_replace_match_with_if_let_doesnt_unwrap_multiline_expressions() {
1811        check_assist(
1812            replace_match_with_if_let,
1813            r#"
1814fn foo() {
1815    $0match a {
1816        VariantData::Struct(..) => {
1817            bar(
1818                123
1819            )
1820        }
1821        _ => false,
1822    }
1823}           "#,
1824            r#"
1825fn foo() {
1826    if let VariantData::Struct(..) = a {
1827        bar(
1828            123
1829        )
1830    } else {
1831        false
1832    }
1833}           "#,
1834        )
1835    }
1836
1837    #[test]
1838    fn replace_match_with_if_let_target() {
1839        check_assist_target(
1840            replace_match_with_if_let,
1841            r#"
1842impl VariantData {
1843    pub fn is_struct(&self) -> bool {
1844        $0match *self {
1845            VariantData::Struct(..) => true,
1846            _ => false,
1847        }
1848    }
1849}           "#,
1850            r#"match *self {
1851            VariantData::Struct(..) => true,
1852            _ => false,
1853        }"#,
1854        );
1855    }
1856
1857    #[test]
1858    fn special_case_option_match_to_if_let() {
1859        check_assist(
1860            replace_match_with_if_let,
1861            r#"
1862//- minicore: option
1863fn foo(x: Option<i32>) {
1864    $0match x {
1865        Some(x) => println!("{}", x),
1866        None => println!("none"),
1867    }
1868}
1869"#,
1870            r#"
1871fn foo(x: Option<i32>) {
1872    if let Some(x) = x {
1873        println!("{}", x)
1874    } else {
1875        println!("none")
1876    }
1877}
1878"#,
1879        );
1880    }
1881
1882    #[test]
1883    fn special_case_result_match_to_if_let() {
1884        check_assist(
1885            replace_match_with_if_let,
1886            r#"
1887//- minicore: result
1888fn foo(x: Result<i32, ()>) {
1889    $0match x {
1890        Ok(x) => println!("{}", x),
1891        Err(_) => println!("none"),
1892    }
1893}
1894"#,
1895            r#"
1896fn foo(x: Result<i32, ()>) {
1897    if let Ok(x) = x {
1898        println!("{}", x)
1899    } else {
1900        println!("none")
1901    }
1902}
1903"#,
1904        );
1905    }
1906
1907    #[test]
1908    fn nested_indent_match_to_if_let() {
1909        check_assist(
1910            replace_match_with_if_let,
1911            r#"
1912fn main() {
1913    if true {
1914        $0match path.strip_prefix(root_path)
1915            .and(x)
1916        {
1917            Ok(rel_path) => Foo {
1918                x: 2
1919            }
1920            _ => Foo {
1921                x: 3
1922            },
1923        }
1924    }
1925}
1926"#,
1927            r#"
1928fn main() {
1929    if true {
1930        if let Ok(rel_path) = path.strip_prefix(root_path)
1931            .and(x)
1932        {
1933            Foo {
1934                x: 2
1935            }
1936        } else {
1937            Foo {
1938                x: 3
1939            }
1940        }
1941    }
1942}
1943"#,
1944        );
1945
1946        check_assist(
1947            replace_match_with_if_let,
1948            r#"
1949fn main() {
1950    if true {
1951        $0match path.strip_prefix(root_path)
1952            .and(x)
1953        {
1954            Ok(rel_path) => {
1955                let rel_path = RelativePathBuf::from_path(rel_path)
1956                    .ok()?;
1957                Some((*id, rel_path))
1958            }
1959            _ => {
1960                let _ = some_code()
1961                    .clone();
1962                None
1963            },
1964        }
1965    }
1966}
1967"#,
1968            r#"
1969fn main() {
1970    if true {
1971        if let Ok(rel_path) = path.strip_prefix(root_path)
1972            .and(x)
1973        {
1974            let rel_path = RelativePathBuf::from_path(rel_path)
1975                .ok()?;
1976            Some((*id, rel_path))
1977        } else {
1978            let _ = some_code()
1979                .clone();
1980            None
1981        }
1982    }
1983}
1984"#,
1985        );
1986    }
1987
1988    #[test]
1989    fn replace_match_with_if_let_empty_wildcard_expr() {
1990        check_assist(
1991            replace_match_with_if_let,
1992            r#"
1993fn main() {
1994    $0match path.strip_prefix(root_path) {
1995        Ok(rel_path) => println!("{}", rel_path),
1996        _ => (),
1997    }
1998}
1999"#,
2000            r#"
2001fn main() {
2002    if let Ok(rel_path) = path.strip_prefix(root_path) {
2003        println!("{}", rel_path)
2004    }
2005}
2006"#,
2007        )
2008    }
2009
2010    #[test]
2011    fn replace_match_with_if_let_number_body() {
2012        check_assist(
2013            replace_match_with_if_let,
2014            r#"
2015fn main() {
2016    $0match Ok(()) {
2017        Ok(()) => {},
2018        Err(_) => 0,
2019    }
2020}
2021"#,
2022            r#"
2023fn main() {
2024    if let Err(_) = Ok(()) {
2025        0
2026    }
2027}
2028"#,
2029        )
2030    }
2031
2032    #[test]
2033    fn replace_match_with_if_let_exhaustive() {
2034        check_assist(
2035            replace_match_with_if_let,
2036            r#"
2037fn print_source(def_source: ModuleSource) {
2038    match def_so$0urce {
2039        ModuleSource::SourceFile(..) => { println!("source file"); }
2040        ModuleSource::Module(..) => { println!("module"); }
2041    }
2042}
2043"#,
2044            r#"
2045fn print_source(def_source: ModuleSource) {
2046    if let ModuleSource::SourceFile(..) = def_source { println!("source file"); } else { println!("module"); }
2047}
2048"#,
2049        )
2050    }
2051
2052    #[test]
2053    fn replace_match_with_if_let_prefer_name_bind() {
2054        check_assist(
2055            replace_match_with_if_let,
2056            r#"
2057fn foo() {
2058    match $0Foo(0) {
2059        Foo(_) => (),
2060        Bar(bar) => println!("bar {}", bar),
2061    }
2062}
2063"#,
2064            r#"
2065fn foo() {
2066    if let Bar(bar) = Foo(0) {
2067        println!("bar {}", bar)
2068    }
2069}
2070"#,
2071        );
2072        check_assist(
2073            replace_match_with_if_let,
2074            r#"
2075fn foo() {
2076    match $0Foo(0) {
2077        Bar(bar) => println!("bar {}", bar),
2078        Foo(_) => (),
2079    }
2080}
2081"#,
2082            r#"
2083fn foo() {
2084    if let Bar(bar) = Foo(0) {
2085        println!("bar {}", bar)
2086    }
2087}
2088"#,
2089        );
2090    }
2091
2092    #[test]
2093    fn replace_match_with_if_let_prefer_nonempty_body() {
2094        check_assist(
2095            replace_match_with_if_let,
2096            r#"
2097fn foo() {
2098    match $0Ok(0) {
2099        Ok(value) => {},
2100        Err(err) => eprintln!("{}", err),
2101    }
2102}
2103"#,
2104            r#"
2105fn foo() {
2106    if let Err(err) = Ok(0) {
2107        eprintln!("{}", err)
2108    }
2109}
2110"#,
2111        );
2112        check_assist(
2113            replace_match_with_if_let,
2114            r#"
2115fn foo() {
2116    match $0Ok(0) {
2117        Err(err) => eprintln!("{}", err),
2118        Ok(value) => {},
2119    }
2120}
2121"#,
2122            r#"
2123fn foo() {
2124    if let Err(err) = Ok(0) {
2125        eprintln!("{}", err)
2126    }
2127}
2128"#,
2129        );
2130    }
2131
2132    #[test]
2133    fn replace_match_with_if_let_rejects_double_name_bindings() {
2134        check_assist_not_applicable(
2135            replace_match_with_if_let,
2136            r#"
2137fn foo() {
2138    match $0Foo(0) {
2139        Foo(foo) => println!("bar {}", foo),
2140        Bar(bar) => println!("bar {}", bar),
2141    }
2142}
2143"#,
2144        );
2145    }
2146
2147    #[test]
2148    fn test_replace_match_with_if_let_keeps_unsafe_block() {
2149        check_assist(
2150            replace_match_with_if_let,
2151            r#"
2152impl VariantData {
2153    pub fn is_struct(&self) -> bool {
2154        $0match *self {
2155            VariantData::Struct(..) => true,
2156            _ => unsafe { unreachable_unchecked() },
2157        }
2158    }
2159}           "#,
2160            r#"
2161impl VariantData {
2162    pub fn is_struct(&self) -> bool {
2163        if let VariantData::Struct(..) = *self {
2164            true
2165        } else {
2166            unsafe { unreachable_unchecked() }
2167        }
2168    }
2169}           "#,
2170        )
2171    }
2172
2173    #[test]
2174    fn test_replace_match_with_if_let_forces_else() {
2175        check_assist(
2176            replace_match_with_if_let,
2177            r#"
2178fn main() {
2179    match$0 0 {
2180        0 => (),
2181        _ => code(),
2182    }
2183}
2184"#,
2185            r#"
2186fn main() {
2187    if let 0 = 0 {
2188        ()
2189    } else {
2190        code()
2191    }
2192}
2193"#,
2194        )
2195    }
2196
2197    #[test]
2198    fn test_replace_match_with_if_bool() {
2199        check_assist(
2200            replace_match_with_if_let,
2201            r#"
2202fn main() {
2203    match$0 b {
2204        true => (),
2205        _ => code(),
2206    }
2207}
2208"#,
2209            r#"
2210fn main() {
2211    if b {
2212        ()
2213    } else {
2214        code()
2215    }
2216}
2217"#,
2218        );
2219        check_assist(
2220            replace_match_with_if_let,
2221            r#"
2222fn main() {
2223    match$0 b {
2224        false => code(),
2225        true => (),
2226    }
2227}
2228"#,
2229            r#"
2230fn main() {
2231    if !b {
2232        code()
2233    }
2234}
2235"#,
2236        );
2237        check_assist(
2238            replace_match_with_if_let,
2239            r#"
2240fn main() {
2241    match$0 b {
2242        false => (),
2243        true => code(),
2244    }
2245}
2246"#,
2247            r#"
2248fn main() {
2249    if b {
2250        code()
2251    }
2252}
2253"#,
2254        )
2255    }
2256
2257    #[test]
2258    fn test_replace_match_with_if_let_chain() {
2259        check_assist(
2260            replace_match_with_if_let,
2261            r#"
2262fn main() {
2263    match$0 Some(0) {
2264        Some(n) if n % 2 == 0 && n != 6 => (),
2265        _ => code(),
2266    }
2267}
2268"#,
2269            r#"
2270fn main() {
2271    if let Some(n) = Some(0) && n % 2 == 0 && n != 6 {
2272        ()
2273    } else {
2274        code()
2275    }
2276}
2277"#,
2278        )
2279    }
2280
2281    #[test]
2282    fn test_replace_match_with_if_let_not_applicable_pat2_is_ident_pat() {
2283        check_assist_not_applicable(
2284            replace_match_with_if_let,
2285            r"
2286fn test(a: i32) {
2287    match$0 a {
2288        1 => code(),
2289        other => code(other),
2290    }
2291}
2292",
2293        )
2294    }
2295}