aboutsummaryrefslogtreecommitdiff
path: root/crates/assists/src/handlers/replace_unwrap_with_match.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/assists/src/handlers/replace_unwrap_with_match.rs')
-rw-r--r--crates/assists/src/handlers/replace_unwrap_with_match.rs187
1 files changed, 187 insertions, 0 deletions
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 @@
1use std::iter;
2
3use syntax::{
4 ast::{
5 self,
6 edit::{AstNodeEdit, IndentLevel},
7 make,
8 },
9 AstNode,
10};
11
12use crate::{
13 utils::{render_snippet, Cursor, TryEnum},
14 AssistContext, AssistId, AssistKind, Assists,
15};
16
17// Assist: replace_unwrap_with_match
18//
19// Replaces `unwrap` a `match` expression. Works for Result and Option.
20//
21// ```
22// enum Result<T, E> { Ok(T), Err(E) }
23// fn main() {
24// let x: Result<i32, i32> = Result::Ok(92);
25// let y = x.<|>unwrap();
26// }
27// ```
28// ->
29// ```
30// enum Result<T, E> { Ok(T), Err(E) }
31// fn main() {
32// let x: Result<i32, i32> = Result::Ok(92);
33// let y = match x {
34// Ok(a) => a,
35// $0_ => unreachable!(),
36// };
37// }
38// ```
39pub(crate) fn replace_unwrap_with_match(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
40 let method_call: ast::MethodCallExpr = ctx.find_node_at_offset()?;
41 let name = method_call.name_ref()?;
42 if name.text() != "unwrap" {
43 return None;
44 }
45 let caller = method_call.expr()?;
46 let ty = ctx.sema.type_of_expr(&caller)?;
47 let happy_variant = TryEnum::from_ty(&ctx.sema, &ty)?.happy_case();
48 let target = method_call.syntax().text_range();
49 acc.add(
50 AssistId("replace_unwrap_with_match", AssistKind::RefactorRewrite),
51 "Replace unwrap with match",
52 target,
53 |builder| {
54 let ok_path = make::path_unqualified(make::path_segment(make::name_ref(happy_variant)));
55 let it = make::ident_pat(make::name("a")).into();
56 let ok_tuple = make::tuple_struct_pat(ok_path, iter::once(it)).into();
57
58 let bind_path = make::path_unqualified(make::path_segment(make::name_ref("a")));
59 let ok_arm = make::match_arm(iter::once(ok_tuple), make::expr_path(bind_path));
60
61 let unreachable_call = make::expr_unreachable();
62 let err_arm =
63 make::match_arm(iter::once(make::wildcard_pat().into()), unreachable_call);
64
65 let match_arm_list = make::match_arm_list(vec![ok_arm, err_arm]);
66 let match_expr = make::expr_match(caller.clone(), match_arm_list)
67 .indent(IndentLevel::from_node(method_call.syntax()));
68
69 let range = method_call.syntax().text_range();
70 match ctx.config.snippet_cap {
71 Some(cap) => {
72 let err_arm = match_expr
73 .syntax()
74 .descendants()
75 .filter_map(ast::MatchArm::cast)
76 .last()
77 .unwrap();
78 let snippet =
79 render_snippet(cap, match_expr.syntax(), Cursor::Before(err_arm.syntax()));
80 builder.replace_snippet(cap, range, snippet)
81 }
82 None => builder.replace(range, match_expr.to_string()),
83 }
84 },
85 )
86}
87
88#[cfg(test)]
89mod tests {
90 use crate::tests::{check_assist, check_assist_target};
91
92 use super::*;
93
94 #[test]
95 fn test_replace_result_unwrap_with_match() {
96 check_assist(
97 replace_unwrap_with_match,
98 r"
99enum Result<T, E> { Ok(T), Err(E) }
100fn i<T>(a: T) -> T { a }
101fn main() {
102 let x: Result<i32, i32> = Result::Ok(92);
103 let y = i(x).<|>unwrap();
104}
105 ",
106 r"
107enum Result<T, E> { Ok(T), Err(E) }
108fn i<T>(a: T) -> T { a }
109fn main() {
110 let x: Result<i32, i32> = Result::Ok(92);
111 let y = match i(x) {
112 Ok(a) => a,
113 $0_ => unreachable!(),
114 };
115}
116 ",
117 )
118 }
119
120 #[test]
121 fn test_replace_option_unwrap_with_match() {
122 check_assist(
123 replace_unwrap_with_match,
124 r"
125enum Option<T> { Some(T), None }
126fn i<T>(a: T) -> T { a }
127fn main() {
128 let x = Option::Some(92);
129 let y = i(x).<|>unwrap();
130}
131 ",
132 r"
133enum Option<T> { Some(T), None }
134fn i<T>(a: T) -> T { a }
135fn main() {
136 let x = Option::Some(92);
137 let y = match i(x) {
138 Some(a) => a,
139 $0_ => unreachable!(),
140 };
141}
142 ",
143 );
144 }
145
146 #[test]
147 fn test_replace_result_unwrap_with_match_chaining() {
148 check_assist(
149 replace_unwrap_with_match,
150 r"
151enum Result<T, E> { Ok(T), Err(E) }
152fn i<T>(a: T) -> T { a }
153fn main() {
154 let x: Result<i32, i32> = Result::Ok(92);
155 let y = i(x).<|>unwrap().count_zeroes();
156}
157 ",
158 r"
159enum Result<T, E> { Ok(T), Err(E) }
160fn i<T>(a: T) -> T { a }
161fn main() {
162 let x: Result<i32, i32> = Result::Ok(92);
163 let y = match i(x) {
164 Ok(a) => a,
165 $0_ => unreachable!(),
166 }.count_zeroes();
167}
168 ",
169 )
170 }
171
172 #[test]
173 fn replace_unwrap_with_match_target() {
174 check_assist_target(
175 replace_unwrap_with_match,
176 r"
177enum Option<T> { Some(T), None }
178fn i<T>(a: T) -> T { a }
179fn main() {
180 let x = Option::Some(92);
181 let y = i(x).<|>unwrap();
182}
183 ",
184 r"i(x).unwrap()",
185 );
186 }
187}