aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_assists/src/doc_tests/generated.rs24
-rw-r--r--crates/ra_assists/src/handlers/replace_unwrap_with_match.rs177
-rw-r--r--crates/ra_assists/src/lib.rs2
-rw-r--r--crates/ra_syntax/src/ast/make.rs4
-rw-r--r--docs/user/assists.md23
5 files changed, 230 insertions, 0 deletions
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
@@ -623,6 +623,30 @@ fn process(map: HashMap<String, String>) {}
623} 623}
624 624
625#[test] 625#[test]
626fn doctest_replace_unwrap_with_match() {
627 check(
628 "replace_unwrap_with_match",
629 r#####"
630enum Result<T, E> { Ok(T), Err(E) }
631fn main() {
632 let x: Result<i32, i32> = Result::Ok(92);
633 let y = x.<|>unwrap();
634}
635"#####,
636 r#####"
637enum Result<T, E> { Ok(T), Err(E) }
638fn main() {
639 let x: Result<i32, i32> = Result::Ok(92);
640 let y = match x {
641 Ok(a) => a,
642 _ => unreachable!(),
643 };
644}
645"#####,
646 )
647}
648
649#[test]
626fn doctest_split_import() { 650fn doctest_split_import() {
627 check( 651 check(
628 "split_import", 652 "split_import",
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 @@
1use std::iter;
2
3use ra_syntax::{
4 ast::{self, make},
5 AstNode,
6};
7
8use crate::{Assist, AssistCtx, AssistId};
9use 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// ```
33pub(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)]
80mod 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"
89enum Result<T, E> { Ok(T), Err(E) }
90fn i<T>(a: T) -> T { a }
91fn main() {
92 let x: Result<i32, i32> = Result::Ok(92);
93 let y = i(x).<|>unwrap();
94}
95 ",
96 r"
97enum Result<T, E> { Ok(T), Err(E) }
98fn i<T>(a: T) -> T { a }
99fn 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"
115enum Option<T> { Some(T), None }
116fn i<T>(a: T) -> T { a }
117fn main() {
118 let x = Option::Some(92);
119 let y = i(x).<|>unwrap();
120}
121 ",
122 r"
123enum Option<T> { Some(T), None }
124fn i<T>(a: T) -> T { a }
125fn 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"
141enum Result<T, E> { Ok(T), Err(E) }
142fn i<T>(a: T) -> T { a }
143fn main() {
144 let x: Result<i32, i32> = Result::Ok(92);
145 let y = i(x).<|>unwrap().count_zeroes();
146}
147 ",
148 r"
149enum Result<T, E> { Ok(T), Err(E) }
150fn i<T>(a: T) -> T { a }
151fn 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"
167enum Option<T> { Some(T), None }
168fn i<T>(a: T) -> T { a }
169fn main() {
170 let x = Option::Some(92);
171 let y = i(x).<|>unwrap();
172}
173 ",
174 r"i(x).unwrap()",
175 );
176 }
177}
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 {
119 mod remove_mut; 119 mod remove_mut;
120 mod replace_if_let_with_match; 120 mod replace_if_let_with_match;
121 mod replace_qualified_name_with_use; 121 mod replace_qualified_name_with_use;
122 mod replace_unwrap_with_match;
122 mod split_import; 123 mod split_import;
123 124
124 pub(crate) fn all() -> &'static [AssistHandler] { 125 pub(crate) fn all() -> &'static [AssistHandler] {
@@ -154,6 +155,7 @@ mod handlers {
154 remove_mut::remove_mut, 155 remove_mut::remove_mut,
155 replace_if_let_with_match::replace_if_let_with_match, 156 replace_if_let_with_match::replace_if_let_with_match,
156 replace_qualified_name_with_use::replace_qualified_name_with_use, 157 replace_qualified_name_with_use::replace_qualified_name_with_use,
158 replace_unwrap_with_match::replace_unwrap_with_match,
157 split_import::split_import, 159 split_import::split_import,
158 ] 160 ]
159 } 161 }
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 {
250 .unwrap_or_else(|| panic!("unhandled token: {:?}", kind)) 250 .unwrap_or_else(|| panic!("unhandled token: {:?}", kind))
251} 251}
252 252
253pub fn unreachable_macro_call() -> ast::MacroCall {
254 ast_from_text(&format!("unreachable!()"))
255}
256
253fn ast_from_text<N: AstNode>(text: &str) -> N { 257fn ast_from_text<N: AstNode>(text: &str) -> N {
254 let parse = SourceFile::parse(text); 258 let parse = SourceFile::parse(text);
255 let node = parse.tree().syntax().descendants().find_map(N::cast).unwrap(); 259 let node = parse.tree().syntax().descendants().find_map(N::cast).unwrap();
diff --git a/docs/user/assists.md b/docs/user/assists.md
index f3ce6b0e0..b2568a954 100644
--- a/docs/user/assists.md
+++ b/docs/user/assists.md
@@ -597,6 +597,29 @@ use std::collections::HashMap;
597fn process(map: HashMap<String, String>) {} 597fn process(map: HashMap<String, String>) {}
598``` 598```
599 599
600## `replace_unwrap_with_match`
601
602Replaces `unwrap` a `match` expression. Works for Result and Option.
603
604```rust
605// BEFORE
606enum Result<T, E> { Ok(T), Err(E) }
607fn main() {
608 let x: Result<i32, i32> = Result::Ok(92);
609 let y = x.┃unwrap();
610}
611
612// AFTER
613enum Result<T, E> { Ok(T), Err(E) }
614fn main() {
615 let x: Result<i32, i32> = Result::Ok(92);
616 let y = match x {
617 Ok(a) => a,
618 _ => unreachable!(),
619 };
620}
621```
622
600## `split_import` 623## `split_import`
601 624
602Wraps the tail of import into braces. 625Wraps the tail of import into braces.