about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2020-04-29 10:01:26 +0000
committerGitHub <noreply@github.com>2020-04-29 10:01:26 +0000
commitc3dfeba165e4d13821bba34d21cdc5242ea2fa71 (patch)
treed9d396b0364238c68d669bd65446deea922dda24
parentb1408271a35372c5e525296de985c266e63d9907 (diff)
parent7c3c289dab46d1dbe9196549c81307f291e631f7 (diff)
downloadrust-c3dfeba165e4d13821bba34d21cdc5242ea2fa71.tar.gz
rust-c3dfeba165e4d13821bba34d21cdc5242ea2fa71.zip
Merge #4204
4204: Use specific pattern when translating if-let-else to match r=matklad a=matklad

We *probably* should actually use the same machinery here, as we do
for fill match arms, but just special-casing options and results seems
to be a good first step.



bors r+
🤖

Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
-rw-r--r--crates/ra_assists/src/handlers/replace_if_let_with_match.rs78
-rw-r--r--crates/ra_assists/src/handlers/replace_let_with_if_let.rs16
-rw-r--r--crates/ra_assists/src/handlers/replace_unwrap_with_match.rs52
-rw-r--r--crates/ra_assists/src/utils.rs52
4 files changed, 144 insertions, 54 deletions
diff --git a/crates/ra_assists/src/handlers/replace_if_let_with_match.rs b/crates/ra_assists/src/handlers/replace_if_let_with_match.rs
index 0a0a88f3dcf..9841f6980bc 100644
--- a/crates/ra_assists/src/handlers/replace_if_let_with_match.rs
+++ b/crates/ra_assists/src/handlers/replace_if_let_with_match.rs
@@ -1,11 +1,10 @@
 use ra_fmt::unwrap_trivial_block;
 use ra_syntax::{
-    ast::{self, make},
+    ast::{self, edit::IndentLevel, make},
     AstNode,
 };
 
-use crate::{Assist, AssistCtx, AssistId};
-use ast::edit::IndentLevel;
+use crate::{utils::TryEnum, Assist, AssistCtx, AssistId};
 
 // Assist: replace_if_let_with_match
 //
@@ -44,15 +43,21 @@ pub(crate) fn replace_if_let_with_match(ctx: AssistCtx) -> Option<Assist> {
         ast::ElseBranch::IfExpr(_) => return None,
     };
 
-    ctx.add_assist(AssistId("replace_if_let_with_match"), "Replace with match", |edit| {
+    let sema = ctx.sema;
+    ctx.add_assist(AssistId("replace_if_let_with_match"), "Replace with match", move |edit| {
         let match_expr = {
             let then_arm = {
                 let then_expr = unwrap_trivial_block(then_block);
-                make::match_arm(vec![pat], then_expr)
+                make::match_arm(vec![pat.clone()], then_expr)
             };
             let else_arm = {
+                let pattern = sema
+                    .type_of_pat(&pat)
+                    .and_then(|ty| TryEnum::from_ty(sema, &ty))
+                    .map(|it| it.sad_pattern())
+                    .unwrap_or_else(|| make::placeholder_pat().into());
                 let else_expr = unwrap_trivial_block(else_block);
-                make::match_arm(vec![make::placeholder_pat().into()], else_expr)
+                make::match_arm(vec![pattern], else_expr)
             };
             make::expr_match(expr, make::match_arm_list(vec![then_arm, else_arm]))
         };
@@ -68,6 +73,7 @@ pub(crate) fn replace_if_let_with_match(ctx: AssistCtx) -> Option<Assist> {
 #[cfg(test)]
 mod tests {
     use super::*;
+
     use crate::helpers::{check_assist, check_assist_target};
 
     #[test]
@@ -145,4 +151,64 @@ impl VariantData {
         }",
         );
     }
+
+    #[test]
+    fn special_case_option() {
+        check_assist(
+            replace_if_let_with_match,
+            r#"
+enum Option<T> { Some(T), None }
+use Option::*;
+
+fn foo(x: Option<i32>) {
+    <|>if let Some(x) = x {
+        println!("{}", x)
+    } else {
+        println!("none")
+    }
+}
+           "#,
+            r#"
+enum Option<T> { Some(T), None }
+use Option::*;
+
+fn foo(x: Option<i32>) {
+    <|>match x {
+        Some(x) => println!("{}", x),
+        None => println!("none"),
+    }
+}
+           "#,
+        );
+    }
+
+    #[test]
+    fn special_case_result() {
+        check_assist(
+            replace_if_let_with_match,
+            r#"
+enum Result<T, E> { Ok(T), Err(E) }
+use Result::*;
+
+fn foo(x: Result<i32, ()>) {
+    <|>if let Ok(x) = x {
+        println!("{}", x)
+    } else {
+        println!("none")
+    }
+}
+           "#,
+            r#"
+enum Result<T, E> { Ok(T), Err(E) }
+use Result::*;
+
+fn foo(x: Result<i32, ()>) {
+    <|>match x {
+        Ok(x) => println!("{}", x),
+        Err(_) => println!("none"),
+    }
+}
+           "#,
+        );
+    }
 }
diff --git a/crates/ra_assists/src/handlers/replace_let_with_if_let.rs b/crates/ra_assists/src/handlers/replace_let_with_if_let.rs
index bdbaae38983..0cf23b754e8 100644
--- a/crates/ra_assists/src/handlers/replace_let_with_if_let.rs
+++ b/crates/ra_assists/src/handlers/replace_let_with_if_let.rs
@@ -1,6 +1,5 @@
 use std::iter::once;
 
-use hir::Adt;
 use ra_syntax::{
     ast::{
         self,
@@ -12,6 +11,7 @@ use ra_syntax::{
 
 use crate::{
     assist_ctx::{Assist, AssistCtx},
+    utils::TryEnum,
     AssistId,
 };
 
@@ -45,20 +45,10 @@ pub(crate) fn replace_let_with_if_let(ctx: AssistCtx) -> Option<Assist> {
     let init = let_stmt.initializer()?;
     let original_pat = let_stmt.pat()?;
     let ty = ctx.sema.type_of_expr(&init)?;
-    let enum_ = match ty.as_adt() {
-        Some(Adt::Enum(it)) => it,
-        _ => return None,
-    };
-    let happy_case =
-        [("Result", "Ok"), ("Option", "Some")].iter().find_map(|(known_type, happy_case)| {
-            if &enum_.name(ctx.db).to_string() == known_type {
-                return Some(happy_case);
-            }
-            None
-        });
+    let happy_variant = TryEnum::from_ty(ctx.sema, &ty).map(|it| it.happy_case());
 
     ctx.add_assist(AssistId("replace_let_with_if_let"), "Replace with if-let", |edit| {
-        let with_placeholder: ast::Pat = match happy_case {
+        let with_placeholder: ast::Pat = match happy_variant {
             None => make::placeholder_pat().into(),
             Some(var_name) => make::tuple_struct_pat(
                 make::path_unqualified(make::path_segment(make::name_ref(var_name))),
diff --git a/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs b/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs
index 62cb7a7631a..62d4ea52202 100644
--- a/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs
+++ b/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs
@@ -1,12 +1,11 @@
 use std::iter;
 
 use ra_syntax::{
-    ast::{self, make},
+    ast::{self, edit::IndentLevel, make},
     AstNode,
 };
 
-use crate::{Assist, AssistCtx, AssistId};
-use ast::edit::IndentLevel;
+use crate::{utils::TryEnum, Assist, AssistCtx, AssistId};
 
 // Assist: replace_unwrap_with_match
 //
@@ -38,42 +37,27 @@ pub(crate) fn replace_unwrap_with_match(ctx: AssistCtx) -> Option<Assist> {
     }
     let caller = method_call.expr()?;
     let ty = ctx.sema.type_of_expr(&caller)?;
+    let happy_variant = TryEnum::from_ty(ctx.sema, &ty)?.happy_case();
 
-    let type_name = ty.as_adt()?.name(ctx.sema.db).to_string();
+    ctx.add_assist(AssistId("replace_unwrap_with_match"), "Replace unwrap with match", |edit| {
+        let ok_path = make::path_unqualified(make::path_segment(make::name_ref(happy_variant)));
+        let it = make::bind_pat(make::name("a")).into();
+        let ok_tuple = make::tuple_struct_pat(ok_path, iter::once(it)).into();
 
-    for (unwrap_type, variant_name) in [("Result", "Ok"), ("Option", "Some")].iter() {
-        if &type_name == unwrap_type {
-            return ctx.add_assist(
-                AssistId("replace_unwrap_with_match"),
-                "Replace unwrap with match",
-                |edit| {
-                    let ok_path =
-                        make::path_unqualified(make::path_segment(make::name_ref(variant_name)));
-                    let it = make::bind_pat(make::name("a")).into();
-                    let ok_tuple = make::tuple_struct_pat(ok_path, iter::once(it)).into();
+        let bind_path = make::path_unqualified(make::path_segment(make::name_ref("a")));
+        let ok_arm = make::match_arm(iter::once(ok_tuple), make::expr_path(bind_path));
 
-                    let bind_path = make::path_unqualified(make::path_segment(make::name_ref("a")));
-                    let ok_arm = make::match_arm(iter::once(ok_tuple), make::expr_path(bind_path));
+        let unreachable_call = make::unreachable_macro_call().into();
+        let err_arm = make::match_arm(iter::once(make::placeholder_pat().into()), unreachable_call);
 
-                    let unreachable_call = make::unreachable_macro_call().into();
-                    let err_arm = make::match_arm(
-                        iter::once(make::placeholder_pat().into()),
-                        unreachable_call,
-                    );
+        let match_arm_list = make::match_arm_list(vec![ok_arm, err_arm]);
+        let match_expr = make::expr_match(caller.clone(), match_arm_list);
+        let match_expr = IndentLevel::from_node(method_call.syntax()).increase_indent(match_expr);
 
-                    let match_arm_list = make::match_arm_list(vec![ok_arm, err_arm]);
-                    let match_expr = make::expr_match(caller.clone(), match_arm_list);
-                    let match_expr =
-                        IndentLevel::from_node(method_call.syntax()).increase_indent(match_expr);
-
-                    edit.target(method_call.syntax().text_range());
-                    edit.set_cursor(caller.syntax().text_range().start());
-                    edit.replace_ast::<ast::Expr>(method_call.into(), match_expr);
-                },
-            );
-        }
-    }
-    None
+        edit.target(method_call.syntax().text_range());
+        edit.set_cursor(caller.syntax().text_range().start());
+        edit.replace_ast::<ast::Expr>(method_call.into(), match_expr);
+    })
 }
 
 #[cfg(test)]
diff --git a/crates/ra_assists/src/utils.rs b/crates/ra_assists/src/utils.rs
index 3d6c59bda91..61f8bd1dcac 100644
--- a/crates/ra_assists/src/utils.rs
+++ b/crates/ra_assists/src/utils.rs
@@ -1,7 +1,9 @@
 //! Assorted functions shared by several assists.
 pub(crate) mod insert_use;
 
-use hir::Semantics;
+use std::iter;
+
+use hir::{Adt, Semantics, Type};
 use ra_ide_db::RootDatabase;
 use ra_syntax::{
     ast::{self, make, NameOwner},
@@ -99,3 +101,51 @@ fn invert_special_case(expr: &ast::Expr) -> Option<ast::Expr> {
         _ => None,
     }
 }
+
+#[derive(Clone, Copy)]
+pub(crate) enum TryEnum {
+    Result,
+    Option,
+}
+
+impl TryEnum {
+    const ALL: [TryEnum; 2] = [TryEnum::Option, TryEnum::Result];
+
+    pub(crate) fn from_ty(sema: &Semantics<RootDatabase>, ty: &Type) -> Option<TryEnum> {
+        let enum_ = match ty.as_adt() {
+            Some(Adt::Enum(it)) => it,
+            _ => return None,
+        };
+        TryEnum::ALL.iter().find_map(|&var| {
+            if &enum_.name(sema.db).to_string() == var.type_name() {
+                return Some(var);
+            }
+            None
+        })
+    }
+
+    pub(crate) fn happy_case(self) -> &'static str {
+        match self {
+            TryEnum::Result => "Ok",
+            TryEnum::Option => "Some",
+        }
+    }
+
+    pub(crate) fn sad_pattern(self) -> ast::Pat {
+        match self {
+            TryEnum::Result => make::tuple_struct_pat(
+                make::path_unqualified(make::path_segment(make::name_ref("Err"))),
+                iter::once(make::placeholder_pat().into()),
+            )
+            .into(),
+            TryEnum::Option => make::bind_pat(make::name("None")).into(),
+        }
+    }
+
+    fn type_name(self) -> &'static str {
+        match self {
+            TryEnum::Result => "Result",
+            TryEnum::Option => "Option",
+        }
+    }
+}