about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2021-10-06 20:45:14 +0000
committerGitHub <noreply@github.com>2021-10-06 20:45:14 +0000
commit12d33eafaf3da93072a53769ac01cfd37d05ec1e (patch)
tree63087996d522b1c786f64aa8f8cabc11fc1791df
parenteb60836a39bb7cd7f583b18f19cbfcdf36073880 (diff)
parent1161fa45af0444166df8382ae4b0c9c6407e585b (diff)
downloadrust-12d33eafaf3da93072a53769ac01cfd37d05ec1e.tar.gz
rust-12d33eafaf3da93072a53769ac01cfd37d05ec1e.zip
Merge #10476
10476: feat: Add `replace_try_expr_with_match` assist r=Veykril a=Crauzer

Adds the replace_try_expr_with_match assist
#10424

Co-authored-by: crauzer <filip.quitko@gmail.com>
-rw-r--r--crates/ide_assists/src/handlers/replace_if_let_with_match.rs2
-rw-r--r--crates/ide_assists/src/handlers/replace_try_expr_with_match.rs148
-rw-r--r--crates/ide_assists/src/lib.rs2
-rw-r--r--crates/ide_assists/src/tests/generated.rs21
-rw-r--r--crates/ide_db/src/ty_filter.rs15
5 files changed, 185 insertions, 3 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 22675fbee0f..8aad3e2f52b 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
@@ -142,7 +142,7 @@ 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 {
                     it.sad_pattern()
                 }
diff --git a/crates/ide_assists/src/handlers/replace_try_expr_with_match.rs b/crates/ide_assists/src/handlers/replace_try_expr_with_match.rs
new file mode 100644
index 00000000000..b8ec83cadd6
--- /dev/null
+++ b/crates/ide_assists/src/handlers/replace_try_expr_with_match.rs
@@ -0,0 +1,148 @@
+use std::iter;
+
+use ide_db::{
+    assists::{AssistId, AssistKind},
+    ty_filter::TryEnum,
+};
+use syntax::{
+    ast::{
+        self,
+        edit::{AstNodeEdit, IndentLevel},
+        make,
+    },
+    AstNode, T,
+};
+
+use crate::assist_context::{AssistContext, Assists};
+
+// Assist: replace_try_expr_with_match
+//
+// Replaces a `try` expression with a `match` expression.
+//
+// ```
+// # //- minicore:option
+// fn handle() {
+//     let pat = Some(true)$0?;
+// }
+// ```
+// ->
+// ```
+// fn handle() {
+//     let pat = match Some(true) {
+//         Some(it) => it,
+//         None => return None,
+//     };
+// }
+// ```
+pub(crate) fn replace_try_expr_with_match(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
+    let qm_kw = ctx.find_token_syntax_at_offset(T![?])?;
+    let qm_kw_parent = qm_kw.parent().and_then(ast::TryExpr::cast)?;
+
+    let expr = qm_kw_parent.expr()?;
+    let expr_type_info = ctx.sema.type_of_expr(&expr)?;
+
+    let try_enum = TryEnum::from_ty(&ctx.sema, &expr_type_info.original)?;
+
+    let target = qm_kw_parent.syntax().text_range();
+    acc.add(
+        AssistId("replace_try_expr_with_match", AssistKind::RefactorRewrite),
+        "Replace try expression with match",
+        target,
+        |edit| {
+            let sad_pat = match try_enum {
+                TryEnum::Option => make::path_pat(make::ext::ident_path("None")),
+                TryEnum::Result => make::tuple_struct_pat(
+                    make::ext::ident_path("Err"),
+                    iter::once(make::path_pat(make::ext::ident_path("err"))),
+                )
+                .into(),
+            };
+            let sad_expr = match try_enum {
+                TryEnum::Option => {
+                    make::expr_return(Some(make::expr_path(make::ext::ident_path("None"))))
+                }
+                TryEnum::Result => make::expr_return(Some(make::expr_call(
+                    make::expr_path(make::ext::ident_path("Err")),
+                    make::arg_list(iter::once(make::expr_path(make::ext::ident_path("err")))),
+                ))),
+            };
+
+            let happy_arm = make::match_arm(
+                iter::once(
+                    try_enum.happy_pattern(make::ident_pat(false, false, make::name("it")).into()),
+                ),
+                None,
+                make::expr_path(make::ext::ident_path("it")),
+            );
+            let sad_arm = make::match_arm(iter::once(sad_pat), None, sad_expr);
+
+            let match_arms = [happy_arm, sad_arm];
+            let match_arm_list = make::match_arm_list(std::array::IntoIter::new(match_arms));
+
+            let expr_match = make::expr_match(expr, match_arm_list)
+                .indent(IndentLevel::from_node(qm_kw_parent.syntax()));
+            edit.replace_ast::<ast::Expr>(qm_kw_parent.into(), expr_match);
+        },
+    )
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    use crate::tests::{check_assist, check_assist_not_applicable};
+
+    #[test]
+    fn test_replace_try_expr_with_match_not_applicable() {
+        check_assist_not_applicable(
+            replace_try_expr_with_match,
+            r#"
+                fn test() {
+                    let pat: u32 = 25$0;
+                }
+            "#,
+        );
+    }
+
+    #[test]
+    fn test_replace_try_expr_with_match_option() {
+        check_assist(
+            replace_try_expr_with_match,
+            r#"
+//- minicore:option
+fn test() {
+    let pat = Some(true)$0?;
+}
+            "#,
+            r#"
+fn test() {
+    let pat = match Some(true) {
+        Some(it) => it,
+        None => return None,
+    };
+}
+            "#,
+        );
+    }
+
+    #[test]
+    fn test_replace_try_expr_with_match_result() {
+        check_assist(
+            replace_try_expr_with_match,
+            r#"
+//- minicore:result
+fn test() {
+    let pat = Ok(true)$0?;
+}
+            "#,
+            r#"
+fn test() {
+    let pat = match Ok(true) {
+        Ok(it) => it,
+        Err(err) => return Err(err),
+    };
+}
+            "#,
+        );
+    }
+}
diff --git a/crates/ide_assists/src/lib.rs b/crates/ide_assists/src/lib.rs
index bec51c508b0..a0629afb1dd 100644
--- a/crates/ide_assists/src/lib.rs
+++ b/crates/ide_assists/src/lib.rs
@@ -164,6 +164,7 @@ mod handlers {
     mod remove_unused_param;
     mod reorder_fields;
     mod reorder_impl;
+    mod replace_try_expr_with_match;
     mod replace_derive_with_manual_impl;
     mod replace_if_let_with_match;
     mod introduce_named_generic;
@@ -243,6 +244,7 @@ mod handlers {
             remove_unused_param::remove_unused_param,
             reorder_fields::reorder_fields,
             reorder_impl::reorder_impl,
+            replace_try_expr_with_match::replace_try_expr_with_match,
             replace_derive_with_manual_impl::replace_derive_with_manual_impl,
             replace_if_let_with_match::replace_if_let_with_match,
             replace_if_let_with_match::replace_match_with_if_let,
diff --git a/crates/ide_assists/src/tests/generated.rs b/crates/ide_assists/src/tests/generated.rs
index 1daf96b827b..62c44d70e46 100644
--- a/crates/ide_assists/src/tests/generated.rs
+++ b/crates/ide_assists/src/tests/generated.rs
@@ -1721,6 +1721,27 @@ fn main() {
 }
 
 #[test]
+fn doctest_replace_try_expr_with_match() {
+    check_doc_test(
+        "replace_try_expr_with_match",
+        r#####"
+//- minicore:option
+fn handle() {
+    let pat = Some(true)$0?;
+}
+"#####,
+        r#####"
+fn handle() {
+    let pat = match Some(true) {
+        Some(it) => it,
+        None => return None,
+    };
+}
+"#####,
+    )
+}
+
+#[test]
 fn doctest_sort_items() {
     check_doc_test(
         "sort_items",
diff --git a/crates/ide_db/src/ty_filter.rs b/crates/ide_db/src/ty_filter.rs
index 370d0df0076..28c01d3173d 100644
--- a/crates/ide_db/src/ty_filter.rs
+++ b/crates/ide_db/src/ty_filter.rs
@@ -5,7 +5,7 @@
 use std::iter;
 
 use hir::Semantics;
-use syntax::ast::{self, make};
+use syntax::ast::{self, make, Pat};
 
 use crate::RootDatabase;
 
@@ -51,7 +51,18 @@ impl TryEnum {
         }
     }
 
-    pub fn happy_pattern(self) -> ast::Pat {
+    pub fn happy_pattern(self, pat: Pat) -> ast::Pat {
+        match self {
+            TryEnum::Result => {
+                make::tuple_struct_pat(make::ext::ident_path("Ok"), iter::once(pat)).into()
+            }
+            TryEnum::Option => {
+                make::tuple_struct_pat(make::ext::ident_path("Some"), iter::once(pat)).into()
+            }
+        }
+    }
+
+    pub fn happy_pattern_wildcard(self) -> ast::Pat {
         match self {
             TryEnum::Result => make::tuple_struct_pat(
                 make::ext::ident_path("Ok"),