From fc34403018079ea053f26d0a31b7517053c7dd8c Mon Sep 17 00:00:00 2001 From: Aleksey Kladov <aleksey.kladov@gmail.com> Date: Thu, 13 Aug 2020 17:33:38 +0200 Subject: Rename ra_assists -> assists --- .../src/handlers/replace_unwrap_with_match.rs | 187 +++++++++++++++++++++ 1 file changed, 187 insertions(+) create mode 100644 crates/assists/src/handlers/replace_unwrap_with_match.rs (limited to 'crates/assists/src/handlers/replace_unwrap_with_match.rs') diff --git a/crates/assists/src/handlers/replace_unwrap_with_match.rs b/crates/assists/src/handlers/replace_unwrap_with_match.rs new file mode 100644 index 000000000..9705f11b7 --- /dev/null +++ b/crates/assists/src/handlers/replace_unwrap_with_match.rs @@ -0,0 +1,187 @@ +use std::iter; + +use syntax::{ + ast::{ + self, + edit::{AstNodeEdit, IndentLevel}, + make, + }, + AstNode, +}; + +use crate::{ + utils::{render_snippet, Cursor, TryEnum}, + AssistContext, AssistId, AssistKind, Assists, +}; + +// Assist: replace_unwrap_with_match +// +// Replaces `unwrap` a `match` expression. Works for Result and Option. +// +// ``` +// enum Result<T, E> { Ok(T), Err(E) } +// fn main() { +// let x: Result<i32, i32> = Result::Ok(92); +// let y = x.<|>unwrap(); +// } +// ``` +// -> +// ``` +// enum Result<T, E> { Ok(T), Err(E) } +// fn main() { +// let x: Result<i32, i32> = Result::Ok(92); +// let y = match x { +// Ok(a) => a, +// $0_ => unreachable!(), +// }; +// } +// ``` +pub(crate) fn replace_unwrap_with_match(acc: &mut Assists, ctx: &AssistContext) -> 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 happy_variant = TryEnum::from_ty(&ctx.sema, &ty)?.happy_case(); + let target = method_call.syntax().text_range(); + acc.add( + AssistId("replace_unwrap_with_match", AssistKind::RefactorRewrite), + "Replace unwrap with match", + target, + |builder| { + let ok_path = make::path_unqualified(make::path_segment(make::name_ref(happy_variant))); + let it = make::ident_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::expr_unreachable(); + let err_arm = + make::match_arm(iter::once(make::wildcard_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) + .indent(IndentLevel::from_node(method_call.syntax())); + + let range = method_call.syntax().text_range(); + match ctx.config.snippet_cap { + Some(cap) => { + let err_arm = match_expr + .syntax() + .descendants() + .filter_map(ast::MatchArm::cast) + .last() + .unwrap(); + let snippet = + render_snippet(cap, match_expr.syntax(), Cursor::Before(err_arm.syntax())); + builder.replace_snippet(cap, range, snippet) + } + None => builder.replace(range, match_expr.to_string()), + } + }, + ) +} + +#[cfg(test)] +mod tests { + use crate::tests::{check_assist, check_assist_target}; + + use super::*; + + #[test] + fn test_replace_result_unwrap_with_match() { + check_assist( + replace_unwrap_with_match, + r" +enum Result<T, E> { Ok(T), Err(E) } +fn i<T>(a: T) -> T { a } +fn main() { + let x: Result<i32, i32> = Result::Ok(92); + let y = i(x).<|>unwrap(); +} + ", + r" +enum Result<T, E> { Ok(T), Err(E) } +fn i<T>(a: T) -> T { a } +fn main() { + let x: Result<i32, i32> = Result::Ok(92); + let y = match i(x) { + Ok(a) => a, + $0_ => unreachable!(), + }; +} + ", + ) + } + + #[test] + fn test_replace_option_unwrap_with_match() { + check_assist( + replace_unwrap_with_match, + r" +enum Option<T> { Some(T), None } +fn i<T>(a: T) -> T { a } +fn main() { + let x = Option::Some(92); + let y = i(x).<|>unwrap(); +} + ", + r" +enum Option<T> { Some(T), None } +fn i<T>(a: T) -> T { a } +fn main() { + let x = Option::Some(92); + let y = match i(x) { + Some(a) => a, + $0_ => unreachable!(), + }; +} + ", + ); + } + + #[test] + fn test_replace_result_unwrap_with_match_chaining() { + check_assist( + replace_unwrap_with_match, + r" +enum Result<T, E> { Ok(T), Err(E) } +fn i<T>(a: T) -> T { a } +fn main() { + let x: Result<i32, i32> = Result::Ok(92); + let y = i(x).<|>unwrap().count_zeroes(); +} + ", + r" +enum Result<T, E> { Ok(T), Err(E) } +fn i<T>(a: T) -> T { a } +fn main() { + let x: Result<i32, i32> = Result::Ok(92); + let y = match i(x) { + Ok(a) => a, + $0_ => unreachable!(), + }.count_zeroes(); +} + ", + ) + } + + #[test] + fn replace_unwrap_with_match_target() { + check_assist_target( + replace_unwrap_with_match, + r" +enum Option<T> { Some(T), None } +fn i<T>(a: T) -> T { a } +fn main() { + let x = Option::Some(92); + let y = i(x).<|>unwrap(); +} + ", + r"i(x).unwrap()", + ); + } +} -- cgit v1.2.3