Skip to main content

ide_assists/handlers/
replace_if_let_with_match.rs

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