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