aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2020-03-27 11:12:17 +0000
committerAleksey Kladov <[email protected]>2020-03-27 11:15:46 +0000
commit91e482b46d43a24cd0a48ea1119b93105140cff2 (patch)
tree3fd91a247ca1a9297884139d135aca4763d02da8 /crates
parentcbb53cf55ca350bbcada5fc759b0119d932e879d (diff)
Replace if with if-let
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_assists/src/doc_tests/generated.rs26
-rw-r--r--crates/ra_assists/src/handlers/replace_let_with_if_let.rs108
-rw-r--r--crates/ra_assists/src/lib.rs2
-rw-r--r--crates/ra_syntax/src/ast/edit.rs11
-rw-r--r--crates/ra_syntax/src/ast/make.rs5
5 files changed, 147 insertions, 5 deletions
diff --git a/crates/ra_assists/src/doc_tests/generated.rs b/crates/ra_assists/src/doc_tests/generated.rs
index 62dcb3808..c0486a8a9 100644
--- a/crates/ra_assists/src/doc_tests/generated.rs
+++ b/crates/ra_assists/src/doc_tests/generated.rs
@@ -608,6 +608,32 @@ fn handle(action: Action) {
608} 608}
609 609
610#[test] 610#[test]
611fn doctest_replace_let_with_if_let() {
612 check(
613 "replace_let_with_if_let",
614 r#####"
615enum Option<T> { Some(T), None }
616
617fn main(action: Action) {
618 <|>let x = compute();
619}
620
621fn compute() -> Option<i32> { None }
622"#####,
623 r#####"
624enum Option<T> { Some(T), None }
625
626fn main(action: Action) {
627 if let Some(x) = compute() {
628 }
629}
630
631fn compute() -> Option<i32> { None }
632"#####,
633 )
634}
635
636#[test]
611fn doctest_replace_qualified_name_with_use() { 637fn doctest_replace_qualified_name_with_use() {
612 check( 638 check(
613 "replace_qualified_name_with_use", 639 "replace_qualified_name_with_use",
diff --git a/crates/ra_assists/src/handlers/replace_let_with_if_let.rs b/crates/ra_assists/src/handlers/replace_let_with_if_let.rs
new file mode 100644
index 000000000..10e41f97e
--- /dev/null
+++ b/crates/ra_assists/src/handlers/replace_let_with_if_let.rs
@@ -0,0 +1,108 @@
1use hir::Adt;
2use ra_syntax::{
3 ast::{self, make},
4 AstNode, T,
5};
6
7use crate::{
8 assist_ctx::{Assist, AssistCtx},
9 AssistId,
10};
11use ast::edit::{AstNodeEdit, IndentLevel};
12use std::iter::once;
13
14// Assist: replace_let_with_if_let
15//
16// Replaces `if let` with an else branch with a `match` expression.
17//
18// ```
19// # enum Option<T> { Some(T), None }
20//
21// fn main(action: Action) {
22// <|>let x = compute();
23// }
24//
25// fn compute() -> Option<i32> { None }
26// ```
27// ->
28// ```
29// # enum Option<T> { Some(T), None }
30//
31// fn main(action: Action) {
32// if let Some(x) = compute() {
33// }
34// }
35//
36// fn compute() -> Option<i32> { None }
37// ```
38pub(crate) fn replace_let_with_if_let(ctx: AssistCtx) -> Option<Assist> {
39 let let_kw = ctx.find_token_at_offset(T![let])?;
40 let let_stmt = let_kw.ancestors().find_map(ast::LetStmt::cast)?;
41 let init = let_stmt.initializer()?;
42 let original_pat = let_stmt.pat()?;
43 let ty = ctx.sema.type_of_expr(&init)?;
44 let enum_ = match ty.as_adt() {
45 Some(Adt::Enum(it)) => it,
46 _ => return None,
47 };
48 let happy_case =
49 [("Result", "Ok"), ("Option", "Some")].iter().find_map(|(known_type, happy_case)| {
50 if &enum_.name(ctx.db).to_string() == known_type {
51 return Some(happy_case);
52 }
53 None
54 });
55
56 ctx.add_assist(AssistId("replace_let_with_if_let"), "Replace with if-let", |edit| {
57 let with_placeholder: ast::Pat = match happy_case {
58 None => make::placeholder_pat().into(),
59 Some(var_name) => make::tuple_struct_pat(
60 make::path_unqualified(make::path_segment(make::name_ref(var_name))),
61 once(make::placeholder_pat().into()),
62 )
63 .into(),
64 };
65 let block =
66 IndentLevel::from_node(let_stmt.syntax()).increase_indent(make::block_expr(None, None));
67 let if_ = make::expr_if(make::condition(init, Some(with_placeholder)), block);
68 let stmt = make::expr_stmt(if_);
69
70 let placeholder = stmt.syntax().descendants().find_map(ast::PlaceholderPat::cast).unwrap();
71 let target_offset =
72 let_stmt.syntax().text_range().start() + placeholder.syntax().text_range().start();
73 let stmt = stmt.replace_descendant(placeholder.into(), original_pat);
74
75 edit.replace_ast(ast::Stmt::from(let_stmt), ast::Stmt::from(stmt));
76 edit.target(let_kw.text_range());
77 edit.set_cursor(target_offset);
78 })
79}
80
81#[cfg(test)]
82mod tests {
83 use crate::helpers::check_assist;
84
85 use super::*;
86
87 #[test]
88 fn replace_let_unknown_enum() {
89 check_assist(
90 replace_let_with_if_let,
91 r"
92enum E<T> { X(T), Y(T) }
93
94fn main() {
95 <|>let x = E::X(92);
96}
97 ",
98 r"
99enum E<T> { X(T), Y(T) }
100
101fn main() {
102 if let <|>x = E::X(92) {
103 }
104}
105 ",
106 )
107 }
108}
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs
index bcc9b3f10..67a58fc1f 100644
--- a/crates/ra_assists/src/lib.rs
+++ b/crates/ra_assists/src/lib.rs
@@ -118,6 +118,7 @@ mod handlers {
118 mod remove_dbg; 118 mod remove_dbg;
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_let_with_if_let;
121 mod replace_qualified_name_with_use; 122 mod replace_qualified_name_with_use;
122 mod split_import; 123 mod split_import;
123 124
@@ -153,6 +154,7 @@ mod handlers {
153 remove_dbg::remove_dbg, 154 remove_dbg::remove_dbg,
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,
157 replace_let_with_if_let::replace_let_with_if_let,
156 replace_qualified_name_with_use::replace_qualified_name_with_use, 158 replace_qualified_name_with_use::replace_qualified_name_with_use,
157 split_import::split_import, 159 split_import::split_import,
158 ] 160 ]
diff --git a/crates/ra_syntax/src/ast/edit.rs b/crates/ra_syntax/src/ast/edit.rs
index f74c9f9c6..bdaecdc43 100644
--- a/crates/ra_syntax/src/ast/edit.rs
+++ b/crates/ra_syntax/src/ast/edit.rs
@@ -251,7 +251,7 @@ impl ast::UseItem {
251 #[must_use] 251 #[must_use]
252 pub fn with_use_tree(&self, use_tree: ast::UseTree) -> ast::UseItem { 252 pub fn with_use_tree(&self, use_tree: ast::UseTree) -> ast::UseItem {
253 if let Some(old) = self.use_tree() { 253 if let Some(old) = self.use_tree() {
254 return self.replace_descendants(iter::once((old, use_tree))); 254 return self.replace_descendant(old, use_tree);
255 } 255 }
256 self.clone() 256 self.clone()
257 } 257 }
@@ -283,7 +283,7 @@ impl ast::UseTree {
283 #[must_use] 283 #[must_use]
284 pub fn with_path(&self, path: ast::Path) -> ast::UseTree { 284 pub fn with_path(&self, path: ast::Path) -> ast::UseTree {
285 if let Some(old) = self.path() { 285 if let Some(old) = self.path() {
286 return self.replace_descendants(iter::once((old, path))); 286 return self.replace_descendant(old, path);
287 } 287 }
288 self.clone() 288 self.clone()
289 } 289 }
@@ -291,7 +291,7 @@ impl ast::UseTree {
291 #[must_use] 291 #[must_use]
292 pub fn with_use_tree_list(&self, use_tree_list: ast::UseTreeList) -> ast::UseTree { 292 pub fn with_use_tree_list(&self, use_tree_list: ast::UseTreeList) -> ast::UseTree {
293 if let Some(old) = self.use_tree_list() { 293 if let Some(old) = self.use_tree_list() {
294 return self.replace_descendants(iter::once((old, use_tree_list))); 294 return self.replace_descendant(old, use_tree_list);
295 } 295 }
296 self.clone() 296 self.clone()
297 } 297 }
@@ -466,6 +466,11 @@ pub trait AstNodeEdit: AstNode + Sized {
466 } 466 }
467 467
468 #[must_use] 468 #[must_use]
469 fn replace_descendant<D: AstNode>(&self, old: D, new: D) -> Self {
470 self.replace_descendants(iter::once((old, new)))
471 }
472
473 #[must_use]
469 fn replace_descendants<D: AstNode>( 474 fn replace_descendants<D: AstNode>(
470 &self, 475 &self,
471 replacement_map: impl IntoIterator<Item = (D, D)>, 476 replacement_map: impl IntoIterator<Item = (D, D)>,
diff --git a/crates/ra_syntax/src/ast/make.rs b/crates/ra_syntax/src/ast/make.rs
index 6aee39203..c818bba55 100644
--- a/crates/ra_syntax/src/ast/make.rs
+++ b/crates/ra_syntax/src/ast/make.rs
@@ -127,7 +127,7 @@ pub fn condition(expr: ast::Expr, pattern: Option<ast::Pat>) -> ast::Condition {
127 match pattern { 127 match pattern {
128 None => ast_from_text(&format!("const _: () = while {} {{}};", expr)), 128 None => ast_from_text(&format!("const _: () = while {} {{}};", expr)),
129 Some(pattern) => { 129 Some(pattern) => {
130 ast_from_text(&format!("const _: () = while {} = {} {{}};", pattern, expr)) 130 ast_from_text(&format!("const _: () = while let {} = {} {{}};", pattern, expr))
131 } 131 }
132 } 132 }
133} 133}
@@ -245,7 +245,8 @@ pub fn let_stmt(pattern: ast::Pat, initializer: Option<ast::Expr>) -> ast::LetSt
245 ast_from_text(&format!("fn f() {{ {} }}", text)) 245 ast_from_text(&format!("fn f() {{ {} }}", text))
246} 246}
247pub fn expr_stmt(expr: ast::Expr) -> ast::ExprStmt { 247pub fn expr_stmt(expr: ast::Expr) -> ast::ExprStmt {
248 ast_from_text(&format!("fn f() {{ {}; }}", expr)) 248 let semi = if expr.is_block_like() { "" } else { ";" };
249 ast_from_text(&format!("fn f() {{ {}{} (); }}", expr, semi))
249} 250}
250 251
251pub fn token(kind: SyntaxKind) -> SyntaxToken { 252pub fn token(kind: SyntaxKind) -> SyntaxToken {