aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorkrk <[email protected]>2019-10-31 20:10:58 +0000
committerkrk <[email protected]>2019-10-31 20:10:58 +0000
commit4a4d9f7a90dc0605992c4f774a8d9a1323ad6d1e (patch)
treee18fbba37ec5270c6fc740c0fc382fa08968cfcc
parent998088876d91b7602068f8209a61918d4a8a8fe7 (diff)
Handle IfLet in convert_to_guarded_return.
-rw-r--r--crates/ra_assists/src/assists/early_return.rs183
-rw-r--r--crates/ra_syntax/src/ast/make.rs16
2 files changed, 171 insertions, 28 deletions
diff --git a/crates/ra_assists/src/assists/early_return.rs b/crates/ra_assists/src/assists/early_return.rs
index ad6c5695a..827170f8f 100644
--- a/crates/ra_assists/src/assists/early_return.rs
+++ b/crates/ra_assists/src/assists/early_return.rs
@@ -37,7 +37,9 @@ use crate::{
37// ``` 37// ```
38pub(crate) fn convert_to_guarded_return(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 38pub(crate) fn convert_to_guarded_return(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
39 let if_expr: ast::IfExpr = ctx.find_node_at_offset()?; 39 let if_expr: ast::IfExpr = ctx.find_node_at_offset()?;
40 let expr = if_expr.condition()?.expr()?; 40 let cond = if_expr.condition()?;
41 let pat = &cond.pat();
42 let expr = cond.expr()?;
41 let then_block = if_expr.then_branch()?.block()?; 43 let then_block = if_expr.then_branch()?.block()?;
42 if if_expr.else_branch().is_some() { 44 if if_expr.else_branch().is_some() {
43 return None; 45 return None;
@@ -63,8 +65,8 @@ pub(crate) fn convert_to_guarded_return(ctx: AssistCtx<impl HirDatabase>) -> Opt
63 let parent_container = parent_block.syntax().parent()?.parent()?; 65 let parent_container = parent_block.syntax().parent()?.parent()?;
64 66
65 let early_expression = match parent_container.kind() { 67 let early_expression = match parent_container.kind() {
66 WHILE_EXPR | LOOP_EXPR => Some("continue;"), 68 WHILE_EXPR | LOOP_EXPR => Some("continue"),
67 FN_DEF => Some("return;"), 69 FN_DEF => Some("return"),
68 _ => None, 70 _ => None,
69 }?; 71 }?;
70 72
@@ -77,31 +79,67 @@ pub(crate) fn convert_to_guarded_return(ctx: AssistCtx<impl HirDatabase>) -> Opt
77 79
78 ctx.add_assist(AssistId("convert_to_guarded_return"), "convert to guarded return", |edit| { 80 ctx.add_assist(AssistId("convert_to_guarded_return"), "convert to guarded return", |edit| {
79 let if_indent_level = IndentLevel::from_node(&if_expr.syntax()); 81 let if_indent_level = IndentLevel::from_node(&if_expr.syntax());
80 let new_if_expr = 82 let new_block = match pat {
81 if_indent_level.increase_indent(make::if_expression(&expr, early_expression)); 83 None => {
82 let then_block_items = IndentLevel::from(1).decrease_indent(then_block.clone()); 84 // If.
83 let end_of_then = then_block_items.syntax().last_child_or_token().unwrap(); 85 let early_expression = &(early_expression.to_owned() + ";");
84 let end_of_then = 86 let new_if_expr =
85 if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) { 87 if_indent_level.increase_indent(make::if_expression(&expr, early_expression));
86 end_of_then.prev_sibling_or_token().unwrap() 88 let then_block_items = IndentLevel::from(1).decrease_indent(then_block.clone());
87 } else { 89 let end_of_then = then_block_items.syntax().last_child_or_token().unwrap();
88 end_of_then 90 let end_of_then =
89 }; 91 if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) {
90 let mut new_if_and_then_statements = new_if_expr.syntax().children_with_tokens().chain( 92 end_of_then.prev_sibling_or_token().unwrap()
91 then_block_items 93 } else {
92 .syntax() 94 end_of_then
93 .children_with_tokens() 95 };
94 .skip(1) 96 let mut new_if_and_then_statements =
95 .take_while(|i| *i != end_of_then), 97 new_if_expr.syntax().children_with_tokens().chain(
96 ); 98 then_block_items
97 let new_block = replace_children( 99 .syntax()
98 &parent_block.syntax(), 100 .children_with_tokens()
99 RangeInclusive::new( 101 .skip(1)
100 if_expr.clone().syntax().clone().into(), 102 .take_while(|i| *i != end_of_then),
101 if_expr.syntax().clone().into(), 103 );
102 ), 104 replace_children(
103 &mut new_if_and_then_statements, 105 &parent_block.syntax(),
104 ); 106 RangeInclusive::new(
107 if_expr.clone().syntax().clone().into(),
108 if_expr.syntax().clone().into(),
109 ),
110 &mut new_if_and_then_statements,
111 )
112 }
113 _ => {
114 // If-let.
115 let new_match_expr =
116 if_indent_level.increase_indent(make::let_match_early(expr, early_expression));
117 let then_block_items = IndentLevel::from(1).decrease_indent(then_block.clone());
118 let end_of_then = then_block_items.syntax().last_child_or_token().unwrap();
119 let end_of_then =
120 if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) {
121 end_of_then.prev_sibling_or_token().unwrap()
122 } else {
123 end_of_then
124 };
125 let mut then_statements = new_match_expr.syntax().children_with_tokens().chain(
126 then_block_items
127 .syntax()
128 .children_with_tokens()
129 .skip(1)
130 .take_while(|i| *i != end_of_then),
131 );
132 let new_block = replace_children(
133 &parent_block.syntax(),
134 RangeInclusive::new(
135 if_expr.clone().syntax().clone().into(),
136 if_expr.syntax().clone().into(),
137 ),
138 &mut then_statements,
139 );
140 new_block
141 }
142 };
105 edit.target(if_expr.syntax().text_range()); 143 edit.target(if_expr.syntax().text_range());
106 edit.replace_ast(parent_block, ast::Block::cast(new_block).unwrap()); 144 edit.replace_ast(parent_block, ast::Block::cast(new_block).unwrap());
107 edit.set_cursor(cursor_position); 145 edit.set_cursor(cursor_position);
@@ -144,6 +182,37 @@ mod tests {
144 } 182 }
145 183
146 #[test] 184 #[test]
185 fn convert_let_inside_fn() {
186 check_assist(
187 convert_to_guarded_return,
188 r#"
189 fn main(n: Option<String>) {
190 bar();
191 if<|> let Some(n) = n {
192 foo(n);
193
194 //comment
195 bar();
196 }
197 }
198 "#,
199 r#"
200 fn main(n: Option<String>) {
201 bar();
202 le<|>t n = match n {
203 Some(it) => it,
204 None => return,
205 };
206 foo(n);
207
208 //comment
209 bar();
210 }
211 "#,
212 );
213 }
214
215 #[test]
147 fn convert_inside_while() { 216 fn convert_inside_while() {
148 check_assist( 217 check_assist(
149 convert_to_guarded_return, 218 convert_to_guarded_return,
@@ -172,6 +241,35 @@ mod tests {
172 } 241 }
173 242
174 #[test] 243 #[test]
244 fn convert_let_inside_while() {
245 check_assist(
246 convert_to_guarded_return,
247 r#"
248 fn main() {
249 while true {
250 if<|> let Some(n) = n {
251 foo(n);
252 bar();
253 }
254 }
255 }
256 "#,
257 r#"
258 fn main() {
259 while true {
260 le<|>t n = match n {
261 Some(it) => it,
262 None => continue,
263 };
264 foo(n);
265 bar();
266 }
267 }
268 "#,
269 );
270 }
271
272 #[test]
175 fn convert_inside_loop() { 273 fn convert_inside_loop() {
176 check_assist( 274 check_assist(
177 convert_to_guarded_return, 275 convert_to_guarded_return,
@@ -200,6 +298,35 @@ mod tests {
200 } 298 }
201 299
202 #[test] 300 #[test]
301 fn convert_let_inside_loop() {
302 check_assist(
303 convert_to_guarded_return,
304 r#"
305 fn main() {
306 loop {
307 if<|> let Some(n) = n {
308 foo(n);
309 bar();
310 }
311 }
312 }
313 "#,
314 r#"
315 fn main() {
316 loop {
317 le<|>t n = match n {
318 Some(it) => it,
319 None => continue,
320 };
321 foo(n);
322 bar();
323 }
324 }
325 "#,
326 );
327 }
328
329 #[test]
203 fn ignore_already_converted_if() { 330 fn ignore_already_converted_if() {
204 check_assist_not_applicable( 331 check_assist_not_applicable(
205 convert_to_guarded_return, 332 convert_to_guarded_return,
diff --git a/crates/ra_syntax/src/ast/make.rs b/crates/ra_syntax/src/ast/make.rs
index 3d5f18bfa..979819d4b 100644
--- a/crates/ra_syntax/src/ast/make.rs
+++ b/crates/ra_syntax/src/ast/make.rs
@@ -110,6 +110,22 @@ pub fn match_arm_list(arms: impl Iterator<Item = ast::MatchArm>) -> ast::MatchAr
110 } 110 }
111} 111}
112 112
113pub fn let_match_early(expr: ast::Expr, early_expression: &str) -> ast::LetStmt {
114 return from_text(&format!(
115 r#"let {} = match {} {{
116 Some(it) => it,
117 None => {},
118}};"#,
119 expr.syntax().text(),
120 expr.syntax().text(),
121 early_expression
122 ));
123
124 fn from_text(text: &str) -> ast::LetStmt {
125 ast_from_text(&format!("fn f() {{ {} }}", text))
126 }
127}
128
113pub fn where_pred(path: ast::Path, bounds: impl Iterator<Item = ast::TypeBound>) -> ast::WherePred { 129pub fn where_pred(path: ast::Path, bounds: impl Iterator<Item = ast::TypeBound>) -> ast::WherePred {
114 let bounds = bounds.map(|b| b.syntax().to_string()).join(" + "); 130 let bounds = bounds.map(|b| b.syntax().to_string()).join(" + ");
115 return from_text(&format!("{}: {}", path.syntax(), bounds)); 131 return from_text(&format!("{}: {}", path.syntax(), bounds));