aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists/src/assists
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_assists/src/assists')
-rw-r--r--crates/ra_assists/src/assists/early_return.rs128
1 files changed, 88 insertions, 40 deletions
diff --git a/crates/ra_assists/src/assists/early_return.rs b/crates/ra_assists/src/assists/early_return.rs
index 570a07a20..264412526 100644
--- a/crates/ra_assists/src/assists/early_return.rs
+++ b/crates/ra_assists/src/assists/early_return.rs
@@ -1,4 +1,4 @@
1use std::ops::RangeInclusive; 1use std::{iter::once, ops::RangeInclusive};
2 2
3use hir::db::HirDatabase; 3use hir::db::HirDatabase;
4use ra_syntax::{ 4use ra_syntax::{
@@ -38,27 +38,30 @@ use crate::{
38// ``` 38// ```
39pub(crate) fn convert_to_guarded_return(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 39pub(crate) fn convert_to_guarded_return(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
40 let if_expr: ast::IfExpr = ctx.find_node_at_offset()?; 40 let if_expr: ast::IfExpr = ctx.find_node_at_offset()?;
41 if if_expr.else_branch().is_some() {
42 return None;
43 }
44
41 let cond = if_expr.condition()?; 45 let cond = if_expr.condition()?;
42 let mut if_let_ident: Option<String> = None;
43 46
44 // Check if there is an IfLet that we can handle. 47 // Check if there is an IfLet that we can handle.
45 match cond.pat() { 48 let if_let_pat = match cond.pat() {
46 None => {} // No IfLet, supported. 49 None => None, // No IfLet, supported.
47 Some(TupleStructPat(ref pat)) if pat.args().count() == 1usize => match &pat.path() { 50 Some(TupleStructPat(pat)) if pat.args().count() == 1 => {
48 Some(p) => match p.qualifier() { 51 let path = pat.path()?;
49 None => if_let_ident = Some(p.syntax().text().to_string()), 52 match path.qualifier() {
50 _ => return None, 53 None => {
51 }, 54 let bound_ident = pat.args().next().unwrap();
52 _ => return None, 55 Some((path, bound_ident))
53 }, 56 }
54 _ => return None, // Unsupported IfLet. 57 Some(_) => return None,
58 }
59 }
60 Some(_) => return None, // Unsupported IfLet.
55 }; 61 };
56 62
57 let expr = cond.expr()?; 63 let cond_expr = cond.expr()?;
58 let then_block = if_expr.then_branch()?.block()?; 64 let then_block = if_expr.then_branch()?.block()?;
59 if if_expr.else_branch().is_some() {
60 return None;
61 }
62 65
63 let parent_block = if_expr.syntax().parent()?.ancestors().find_map(ast::Block::cast)?; 66 let parent_block = if_expr.syntax().parent()?.ancestors().find_map(ast::Block::cast)?;
64 67
@@ -79,11 +82,11 @@ pub(crate) fn convert_to_guarded_return(ctx: AssistCtx<impl HirDatabase>) -> Opt
79 82
80 let parent_container = parent_block.syntax().parent()?.parent()?; 83 let parent_container = parent_block.syntax().parent()?.parent()?;
81 84
82 let early_expression = match parent_container.kind() { 85 let early_expression: ast::Expr = match parent_container.kind() {
83 WHILE_EXPR | LOOP_EXPR => Some("continue"), 86 WHILE_EXPR | LOOP_EXPR => make::expr_continue().into(),
84 FN_DEF => Some("return"), 87 FN_DEF => make::expr_return().into(),
85 _ => None, 88 _ => return None,
86 }?; 89 };
87 90
88 if then_block.syntax().first_child_or_token().map(|t| t.kind() == L_CURLY).is_none() { 91 if then_block.syntax().first_child_or_token().map(|t| t.kind() == L_CURLY).is_none() {
89 return None; 92 return None;
@@ -94,22 +97,43 @@ pub(crate) fn convert_to_guarded_return(ctx: AssistCtx<impl HirDatabase>) -> Opt
94 97
95 ctx.add_assist(AssistId("convert_to_guarded_return"), "convert to guarded return", |edit| { 98 ctx.add_assist(AssistId("convert_to_guarded_return"), "convert to guarded return", |edit| {
96 let if_indent_level = IndentLevel::from_node(&if_expr.syntax()); 99 let if_indent_level = IndentLevel::from_node(&if_expr.syntax());
97 let new_block = match if_let_ident { 100 let new_block = match if_let_pat {
98 None => { 101 None => {
99 // If. 102 // If.
100 let early_expression = &(early_expression.to_owned() + ";"); 103 let early_expression = &(early_expression.syntax().to_string() + ";");
101 let new_expr = 104 let new_expr = if_indent_level
102 if_indent_level.increase_indent(make::if_expression(&expr, early_expression)); 105 .increase_indent(make::if_expression(&cond_expr, early_expression));
103 replace(new_expr, &then_block, &parent_block, &if_expr) 106 replace(new_expr.syntax(), &then_block, &parent_block, &if_expr)
104 } 107 }
105 Some(if_let_ident) => { 108 Some((path, bound_ident)) => {
106 // If-let. 109 // If-let.
107 let new_expr = if_indent_level.increase_indent(make::let_match_early( 110 let match_expr = {
108 expr, 111 let happy_arm = make::match_arm(
109 &if_let_ident, 112 once(
110 early_expression, 113 make::tuple_struct_pat(
111 )); 114 path,
112 replace(new_expr, &then_block, &parent_block, &if_expr) 115 once(make::bind_pat(make::name("it")).into()),
116 )
117 .into(),
118 ),
119 make::expr_path(make::path_from_name_ref(make::name_ref("it"))).into(),
120 );
121
122 let sad_arm = make::match_arm(
123 // FIXME: would be cool to use `None` or `Err(_)` if appropriate
124 once(make::placeholder_pat().into()),
125 early_expression.into(),
126 );
127
128 make::expr_match(cond_expr, make::match_arm_list(vec![happy_arm, sad_arm]))
129 };
130
131 let let_stmt = make::let_stmt(
132 make::bind_pat(make::name(&bound_ident.syntax().to_string())).into(),
133 Some(match_expr.into()),
134 );
135 let let_stmt = if_indent_level.increase_indent(let_stmt);
136 replace(let_stmt.syntax(), &then_block, &parent_block, &if_expr)
113 } 137 }
114 }; 138 };
115 edit.target(if_expr.syntax().text_range()); 139 edit.target(if_expr.syntax().text_range());
@@ -117,7 +141,7 @@ pub(crate) fn convert_to_guarded_return(ctx: AssistCtx<impl HirDatabase>) -> Opt
117 edit.set_cursor(cursor_position); 141 edit.set_cursor(cursor_position);
118 142
119 fn replace( 143 fn replace(
120 new_expr: impl AstNode, 144 new_expr: &SyntaxNode,
121 then_block: &Block, 145 then_block: &Block,
122 parent_block: &Block, 146 parent_block: &Block,
123 if_expr: &ast::IfExpr, 147 if_expr: &ast::IfExpr,
@@ -130,7 +154,7 @@ pub(crate) fn convert_to_guarded_return(ctx: AssistCtx<impl HirDatabase>) -> Opt
130 } else { 154 } else {
131 end_of_then 155 end_of_then
132 }; 156 };
133 let mut then_statements = new_expr.syntax().children_with_tokens().chain( 157 let mut then_statements = new_expr.children_with_tokens().chain(
134 then_block_items 158 then_block_items
135 .syntax() 159 .syntax()
136 .children_with_tokens() 160 .children_with_tokens()
@@ -151,9 +175,10 @@ pub(crate) fn convert_to_guarded_return(ctx: AssistCtx<impl HirDatabase>) -> Opt
151 175
152#[cfg(test)] 176#[cfg(test)]
153mod tests { 177mod tests {
154 use super::*;
155 use crate::helpers::{check_assist, check_assist_not_applicable}; 178 use crate::helpers::{check_assist, check_assist_not_applicable};
156 179
180 use super::*;
181
157 #[test] 182 #[test]
158 fn convert_inside_fn() { 183 fn convert_inside_fn() {
159 check_assist( 184 check_assist(
@@ -204,7 +229,7 @@ mod tests {
204 bar(); 229 bar();
205 le<|>t n = match n { 230 le<|>t n = match n {
206 Some(it) => it, 231 Some(it) => it,
207 None => return, 232 _ => return,
208 }; 233 };
209 foo(n); 234 foo(n);
210 235
@@ -216,6 +241,29 @@ mod tests {
216 } 241 }
217 242
218 #[test] 243 #[test]
244 fn convert_if_let_result() {
245 check_assist(
246 convert_to_guarded_return,
247 r#"
248 fn main() {
249 if<|> let Ok(x) = Err(92) {
250 foo(x);
251 }
252 }
253 "#,
254 r#"
255 fn main() {
256 le<|>t x = match Err(92) {
257 Ok(it) => it,
258 _ => return,
259 };
260 foo(x);
261 }
262 "#,
263 );
264 }
265
266 #[test]
219 fn convert_let_ok_inside_fn() { 267 fn convert_let_ok_inside_fn() {
220 check_assist( 268 check_assist(
221 convert_to_guarded_return, 269 convert_to_guarded_return,
@@ -235,7 +283,7 @@ mod tests {
235 bar(); 283 bar();
236 le<|>t n = match n { 284 le<|>t n = match n {
237 Ok(it) => it, 285 Ok(it) => it,
238 None => return, 286 _ => return,
239 }; 287 };
240 foo(n); 288 foo(n);
241 289
@@ -293,7 +341,7 @@ mod tests {
293 while true { 341 while true {
294 le<|>t n = match n { 342 le<|>t n = match n {
295 Some(it) => it, 343 Some(it) => it,
296 None => continue, 344 _ => continue,
297 }; 345 };
298 foo(n); 346 foo(n);
299 bar(); 347 bar();
@@ -350,7 +398,7 @@ mod tests {
350 loop { 398 loop {
351 le<|>t n = match n { 399 le<|>t n = match n {
352 Some(it) => it, 400 Some(it) => it,
353 None => continue, 401 _ => continue,
354 }; 402 };
355 foo(n); 403 foo(n);
356 bar(); 404 bar();