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
24pub(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 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 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 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 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
203pub(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 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
329fn 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 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 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 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 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}