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