]> git.lizzy.rs Git - rust.git/blobdiff - crates/ide_assists/src/handlers/replace_if_let_with_match.rs
parameters.split_last()
[rust.git] / crates / ide_assists / src / handlers / replace_if_let_with_match.rs
index 18736533068124e2f6f47cea887f436fb4857455..77909347927802a2702982a2deb6c9f70d8e319e 100644 (file)
@@ -6,13 +6,13 @@
     ast::{
         self,
         edit::{AstNodeEdit, IndentLevel},
-        make, NameOwner,
+        make, HasName,
     },
     AstNode, TextRange,
 };
 
 use crate::{
-    utils::{does_pat_match_variant, unwrap_trivial_block},
+    utils::{does_nested_pattern, does_pat_match_variant, unwrap_trivial_block},
     AssistContext, AssistId, AssistKind, Assists,
 };
 
@@ -48,7 +48,7 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext)
         if_expr.syntax().text_range().start(),
         if_expr.then_branch()?.syntax().text_range().start(),
     );
-    let cursor_in_range = available_range.contains_range(ctx.frange.range);
+    let cursor_in_range = available_range.contains_range(ctx.selection_trimmed());
     if !cursor_in_range {
         return None;
     }
@@ -142,7 +142,9 @@ fn make_else_arm(
         let pattern = match pattern {
             Some((it, pat)) => {
                 if does_pat_match_variant(pat, &it.sad_pattern()) {
-                    it.happy_pattern()
+                    it.happy_pattern_wildcard()
+                } else if does_nested_pattern(pat) {
+                    make::wildcard_pat().into()
                 } else {
                     it.sad_pattern()
                 }
@@ -205,25 +207,23 @@ pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext)
         "Replace match with if let",
         target,
         move |edit| {
+            fn make_block_expr(expr: ast::Expr) -> ast::BlockExpr {
+                // Blocks with modifiers (unsafe, async, etc.) are parsed as BlockExpr, but are
+                // formatted without enclosing braces. If we encounter such block exprs,
+                // wrap them in another BlockExpr.
+                match expr {
+                    ast::Expr::BlockExpr(block) if block.modifier().is_none() => block,
+                    expr => make::block_expr(iter::empty(), Some(expr)),
+                }
+            }
+
             let condition = make::condition(scrutinee, Some(if_let_pat));
-            let then_block = match then_expr.reset_indent() {
-                ast::Expr::BlockExpr(block) => block,
-                expr => make::block_expr(iter::empty(), Some(expr)),
-            };
-            let else_expr = match else_expr {
-                ast::Expr::BlockExpr(block) if block.is_empty() => None,
-                ast::Expr::TupleExpr(tuple) if tuple.fields().next().is_none() => None,
-                expr => Some(expr),
-            };
+            let then_block = make_block_expr(then_expr.reset_indent());
+            let else_expr = if is_empty_expr(&else_expr) { None } else { Some(else_expr) };
             let if_let_expr = make::expr_if(
                 condition,
                 then_block,
-                else_expr
-                    .map(|expr| match expr {
-                        ast::Expr::BlockExpr(block) => block,
-                        expr => (make::block_expr(iter::empty(), Some(expr))),
-                    })
-                    .map(ast::ElseBranch::Block),
+                else_expr.map(make_block_expr).map(ast::ElseBranch::Block),
             )
             .indent(IndentLevel::from_node(match_expr.syntax()));
 
@@ -257,7 +257,10 @@ fn pick_pattern_and_expr_order(
 
 fn is_empty_expr(expr: &ast::Expr) -> bool {
     match expr {
-        ast::Expr::BlockExpr(expr) => expr.is_empty(),
+        ast::Expr::BlockExpr(expr) => match expr.stmt_list() {
+            Some(it) => it.statements().next().is_none() && it.tail_expr().is_none(),
+            None => true,
+        },
         ast::Expr::TupleExpr(expr) => expr.fields().next().is_none(),
         _ => false,
     }
@@ -575,6 +578,33 @@ fn main() {
         )
     }
 
+    #[test]
+    fn nested_type() {
+        check_assist(
+            replace_if_let_with_match,
+            r#"
+//- minicore: result
+fn foo(x: Result<i32, ()>) {
+    let bar: Result<_, ()> = Ok(Some(1));
+    $0if let Ok(Some(_)) = bar {
+        ()
+    } else {
+        ()
+    }
+}
+"#,
+            r#"
+fn foo(x: Result<i32, ()>) {
+    let bar: Result<_, ()> = Ok(Some(1));
+    match bar {
+        Ok(Some(_)) => (),
+        _ => (),
+    }
+}
+"#,
+        );
+    }
+
     #[test]
     fn test_replace_match_with_if_let_unwraps_simple_expressions() {
         check_assist(
@@ -889,4 +919,30 @@ fn foo() {
 "#,
         );
     }
+
+    #[test]
+    fn test_replace_match_with_if_let_keeps_unsafe_block() {
+        check_assist(
+            replace_match_with_if_let,
+            r#"
+impl VariantData {
+    pub fn is_struct(&self) -> bool {
+        $0match *self {
+            VariantData::Struct(..) => true,
+            _ => unsafe { unreachable_unchecked() },
+        }
+    }
+}           "#,
+            r#"
+impl VariantData {
+    pub fn is_struct(&self) -> bool {
+        if let VariantData::Struct(..) = *self {
+            true
+        } else {
+            unsafe { unreachable_unchecked() }
+        }
+    }
+}           "#,
+        )
+    }
 }