From d9df0f43ac669a68dc76466a2f2c21885b5af2dd Mon Sep 17 00:00:00 2001 From: Unreal Hoang Date: Thu, 26 Mar 2020 18:16:10 +0900 Subject: Assist: replace unwrap with match --- crates/ra_assists/src/doc_tests/generated.rs | 24 +++ .../src/handlers/replace_unwrap_with_match.rs | 177 +++++++++++++++++++++ crates/ra_assists/src/lib.rs | 2 + crates/ra_syntax/src/ast/make.rs | 4 + 4 files changed, 207 insertions(+) create mode 100644 crates/ra_assists/src/handlers/replace_unwrap_with_match.rs (limited to 'crates') diff --git a/crates/ra_assists/src/doc_tests/generated.rs b/crates/ra_assists/src/doc_tests/generated.rs index 62dcb3808..543224232 100644 --- a/crates/ra_assists/src/doc_tests/generated.rs +++ b/crates/ra_assists/src/doc_tests/generated.rs @@ -622,6 +622,30 @@ fn process(map: HashMap) {} ) } +#[test] +fn doctest_replace_unwrap_with_match() { + check( + "replace_unwrap_with_match", + r#####" +enum Result { Ok(T), Err(E) } +fn main() { + let x: Result = Result::Ok(92); + let y = x.<|>unwrap(); +} +"#####, + r#####" +enum Result { Ok(T), Err(E) } +fn main() { + let x: Result = Result::Ok(92); + let y = match x { + Ok(a) => a, + _ => unreachable!(), + }; +} +"#####, + ) +} + #[test] fn doctest_split_import() { check( diff --git a/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs b/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs new file mode 100644 index 000000000..62cb7a763 --- /dev/null +++ b/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs @@ -0,0 +1,177 @@ +use std::iter; + +use ra_syntax::{ + ast::{self, make}, + AstNode, +}; + +use crate::{Assist, AssistCtx, AssistId}; +use ast::edit::IndentLevel; + +// Assist: replace_unwrap_with_match +// +// Replaces `unwrap` a `match` expression. Works for Result and Option. +// +// ``` +// enum Result { Ok(T), Err(E) } +// fn main() { +// let x: Result = Result::Ok(92); +// let y = x.<|>unwrap(); +// } +// ``` +// -> +// ``` +// enum Result { Ok(T), Err(E) } +// fn main() { +// let x: Result = Result::Ok(92); +// let y = match x { +// Ok(a) => a, +// _ => unreachable!(), +// }; +// } +// ``` +pub(crate) fn replace_unwrap_with_match(ctx: AssistCtx) -> Option { + let method_call: ast::MethodCallExpr = ctx.find_node_at_offset()?; + let name = method_call.name_ref()?; + if name.text() != "unwrap" { + return None; + } + let caller = method_call.expr()?; + let ty = ctx.sema.type_of_expr(&caller)?; + + let type_name = ty.as_adt()?.name(ctx.sema.db).to_string(); + + 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 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); + + edit.target(method_call.syntax().text_range()); + edit.set_cursor(caller.syntax().text_range().start()); + edit.replace_ast::(method_call.into(), match_expr); + }, + ); + } + } + None +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::helpers::{check_assist, check_assist_target}; + + #[test] + fn test_replace_result_unwrap_with_match() { + check_assist( + replace_unwrap_with_match, + r" +enum Result { Ok(T), Err(E) } +fn i(a: T) -> T { a } +fn main() { + let x: Result = Result::Ok(92); + let y = i(x).<|>unwrap(); +} + ", + r" +enum Result { Ok(T), Err(E) } +fn i(a: T) -> T { a } +fn main() { + let x: Result = Result::Ok(92); + let y = <|>match i(x) { + Ok(a) => a, + _ => unreachable!(), + }; +} + ", + ) + } + + #[test] + fn test_replace_option_unwrap_with_match() { + check_assist( + replace_unwrap_with_match, + r" +enum Option { Some(T), None } +fn i(a: T) -> T { a } +fn main() { + let x = Option::Some(92); + let y = i(x).<|>unwrap(); +} + ", + r" +enum Option { Some(T), None } +fn i(a: T) -> T { a } +fn main() { + let x = Option::Some(92); + let y = <|>match i(x) { + Some(a) => a, + _ => unreachable!(), + }; +} + ", + ); + } + + #[test] + fn test_replace_result_unwrap_with_match_chaining() { + check_assist( + replace_unwrap_with_match, + r" +enum Result { Ok(T), Err(E) } +fn i(a: T) -> T { a } +fn main() { + let x: Result = Result::Ok(92); + let y = i(x).<|>unwrap().count_zeroes(); +} + ", + r" +enum Result { Ok(T), Err(E) } +fn i(a: T) -> T { a } +fn main() { + let x: Result = Result::Ok(92); + let y = <|>match i(x) { + Ok(a) => a, + _ => unreachable!(), + }.count_zeroes(); +} + ", + ) + } + + #[test] + fn replace_unwrap_with_match_target() { + check_assist_target( + replace_unwrap_with_match, + r" +enum Option { Some(T), None } +fn i(a: T) -> T { a } +fn main() { + let x = Option::Some(92); + let y = i(x).<|>unwrap(); +} + ", + r"i(x).unwrap()", + ); + } +} diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index bcc9b3f10..becd5e99d 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs @@ -119,6 +119,7 @@ mod handlers { mod remove_mut; mod replace_if_let_with_match; mod replace_qualified_name_with_use; + mod replace_unwrap_with_match; mod split_import; pub(crate) fn all() -> &'static [AssistHandler] { @@ -154,6 +155,7 @@ mod handlers { remove_mut::remove_mut, replace_if_let_with_match::replace_if_let_with_match, replace_qualified_name_with_use::replace_qualified_name_with_use, + replace_unwrap_with_match::replace_unwrap_with_match, split_import::split_import, ] } diff --git a/crates/ra_syntax/src/ast/make.rs b/crates/ra_syntax/src/ast/make.rs index 1145b69e8..e29600439 100644 --- a/crates/ra_syntax/src/ast/make.rs +++ b/crates/ra_syntax/src/ast/make.rs @@ -250,6 +250,10 @@ pub fn token(kind: SyntaxKind) -> SyntaxToken { .unwrap_or_else(|| panic!("unhandled token: {:?}", kind)) } +pub fn unreachable_macro_call() -> ast::MacroCall { + ast_from_text(&format!("unreachable!()")) +} + fn ast_from_text(text: &str) -> N { let parse = SourceFile::parse(text); let node = parse.tree().syntax().descendants().find_map(N::cast).unwrap(); -- cgit v1.2.3