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::(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 @@ -1720,6 +1720,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( 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"),