about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2021-10-14 22:41:06 +0000
committerGitHub <noreply@github.com>2021-10-14 22:41:06 +0000
commit3a79af7e277839f803bd0304f28dbe4971ffdad6 (patch)
tree457e5a6a94502727d645ee7987c807636041caef
parentbfb8f73fb3f8a1a7fadfa31bda98a17549a91093 (diff)
parentbd9bab87ed72779c3bf407137928b75555f5d2a9 (diff)
downloadrust-3a79af7e277839f803bd0304f28dbe4971ffdad6.tar.gz
rust-3a79af7e277839f803bd0304f28dbe4971ffdad6.zip
Merge #10491
10491: Support nested type on replace if let with match r=k-nasa a=k-nasa

## Why

close: https://github.com/rust-analyzer/rust-analyzer/issues/8690

Now, Replacing if-let with match cant't output exhaustive patterns code.
This was because the `else` conversion used specific types (ex. Option, Result) instead of wildcards.

I thought it was more of a problem to generate non-exhaustive patterns than the benefits of using the concrete one.

How about using wildcards in `else`? 
Is this change policy acceptable?

## What

- using wildcards on `make_else_arm`
- Change test cases

Co-authored-by: k-nasa <htilcs1115@gmail.com>
-rw-r--r--crates/ide_assists/src/handlers/replace_if_let_with_match.rs31
-rw-r--r--crates/ide_assists/src/utils.rs41
2 files changed, 71 insertions, 1 deletions
diff --git a/crates/ide_assists/src/handlers/replace_if_let_with_match.rs b/crates/ide_assists/src/handlers/replace_if_let_with_match.rs
index 4d5262646c8..47d1fbe144a 100644
--- a/crates/ide_assists/src/handlers/replace_if_let_with_match.rs
+++ b/crates/ide_assists/src/handlers/replace_if_let_with_match.rs
@@ -12,7 +12,7 @@ use syntax::{
 };
 
 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,
 };
 
@@ -143,6 +143,8 @@ fn make_else_arm(
             Some((it, pat)) => {
                 if does_pat_match_variant(pat, &it.sad_pattern()) {
                     it.happy_pattern_wildcard()
+                } else if does_nested_pattern(pat) {
+                    make::wildcard_pat().into()
                 } else {
                     it.sad_pattern()
                 }
@@ -575,6 +577,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(
             replace_match_with_if_let,
diff --git a/crates/ide_assists/src/utils.rs b/crates/ide_assists/src/utils.rs
index c1092b97c22..6866186d342 100644
--- a/crates/ide_assists/src/utils.rs
+++ b/crates/ide_assists/src/utils.rs
@@ -285,6 +285,47 @@ pub(crate) fn does_pat_match_variant(pat: &ast::Pat, var: &ast::Pat) -> bool {
     pat_head == var_head
 }
 
+pub(crate) fn does_nested_pattern(pat: &ast::Pat) -> bool {
+    let depth = calc_depth(pat, 0);
+
+    if 1 < depth {
+        return true;
+    }
+    false
+}
+
+fn calc_depth(pat: &ast::Pat, depth: usize) -> usize {
+    match pat {
+        ast::Pat::IdentPat(_)
+        | ast::Pat::BoxPat(_)
+        | ast::Pat::RestPat(_)
+        | ast::Pat::LiteralPat(_)
+        | ast::Pat::MacroPat(_)
+        | ast::Pat::OrPat(_)
+        | ast::Pat::ParenPat(_)
+        | ast::Pat::PathPat(_)
+        | ast::Pat::WildcardPat(_)
+        | ast::Pat::RangePat(_)
+        | ast::Pat::RecordPat(_)
+        | ast::Pat::RefPat(_)
+        | ast::Pat::SlicePat(_)
+        | ast::Pat::TuplePat(_)
+        | ast::Pat::ConstBlockPat(_) => depth,
+
+        // FIXME: Other patterns may also be nested. Currently it simply supports only `TupleStructPat`
+        ast::Pat::TupleStructPat(pat) => {
+            let mut max_depth = depth;
+            for p in pat.fields() {
+                let d = calc_depth(&p, depth + 1);
+                if d > max_depth {
+                    max_depth = d
+                }
+            }
+            max_depth
+        }
+    }
+}
+
 // Uses a syntax-driven approach to find any impl blocks for the struct that
 // exist within the module/file
 //