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