diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2020-05-06 17:04:47 +0100 |
---|---|---|
committer | GitHub <[email protected]> | 2020-05-06 17:04:47 +0100 |
commit | fbc8bd3fdbbeed199901d6f387ab57d82aac6e04 (patch) | |
tree | 1817e29c0da2455f898d611e38914ff14cace26c | |
parent | e99447ffbf77b17674dc047e5a9f5aff9480ed1c (diff) | |
parent | 51c02ab84f6b88ba39e2d0a3ed22bea51114b05a (diff) |
Merge #4043
4043: add Ok wrapping assist #3907 r=matklad a=bnjjj
About issue #3907
close #3907
Co-authored-by: Benjamin Coenen <[email protected]>
-rw-r--r-- | crates/ra_assists/src/handlers/change_return_type_to_result.rs | 971 | ||||
-rw-r--r-- | crates/ra_assists/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/ra_assists/src/tests/generated.rs | 13 | ||||
-rw-r--r-- | docs/user/assists.md | 12 |
4 files changed, 998 insertions, 0 deletions
diff --git a/crates/ra_assists/src/handlers/change_return_type_to_result.rs b/crates/ra_assists/src/handlers/change_return_type_to_result.rs new file mode 100644 index 000000000..1e8d986cd --- /dev/null +++ b/crates/ra_assists/src/handlers/change_return_type_to_result.rs | |||
@@ -0,0 +1,971 @@ | |||
1 | use ra_syntax::{ | ||
2 | ast, AstNode, | ||
3 | SyntaxKind::{COMMENT, WHITESPACE}, | ||
4 | SyntaxNode, TextSize, | ||
5 | }; | ||
6 | |||
7 | use crate::{Assist, AssistCtx, AssistId}; | ||
8 | use ast::{BlockExpr, Expr, LoopBodyOwner}; | ||
9 | |||
10 | // Assist: change_return_type_to_result | ||
11 | // | ||
12 | // Change the function's return type to Result. | ||
13 | // | ||
14 | // ``` | ||
15 | // fn foo() -> i32<|> { 42i32 } | ||
16 | // ``` | ||
17 | // -> | ||
18 | // ``` | ||
19 | // fn foo() -> Result<i32, > { Ok(42i32) } | ||
20 | // ``` | ||
21 | pub(crate) fn change_return_type_to_result(ctx: AssistCtx) -> Option<Assist> { | ||
22 | let fn_def = ctx.find_node_at_offset::<ast::FnDef>(); | ||
23 | let fn_def = &mut fn_def?; | ||
24 | let ret_type = &fn_def.ret_type()?.type_ref()?; | ||
25 | if ret_type.syntax().text().to_string().starts_with("Result<") { | ||
26 | return None; | ||
27 | } | ||
28 | |||
29 | let block_expr = &fn_def.body()?; | ||
30 | let cursor_in_ret_type = | ||
31 | fn_def.ret_type()?.syntax().text_range().contains_range(ctx.frange.range); | ||
32 | if !cursor_in_ret_type { | ||
33 | return None; | ||
34 | } | ||
35 | |||
36 | ctx.add_assist( | ||
37 | AssistId("change_return_type_to_result"), | ||
38 | "Change return type to Result", | ||
39 | ret_type.syntax().text_range(), | ||
40 | |edit| { | ||
41 | let mut tail_return_expr_collector = TailReturnCollector::new(); | ||
42 | tail_return_expr_collector.collect_jump_exprs(block_expr, false); | ||
43 | tail_return_expr_collector.collect_tail_exprs(block_expr); | ||
44 | |||
45 | for ret_expr_arg in tail_return_expr_collector.exprs_to_wrap { | ||
46 | edit.replace_node_and_indent(&ret_expr_arg, format!("Ok({})", ret_expr_arg)); | ||
47 | } | ||
48 | edit.replace_node_and_indent(ret_type.syntax(), format!("Result<{}, >", ret_type)); | ||
49 | |||
50 | if let Some(node_start) = result_insertion_offset(&ret_type) { | ||
51 | edit.set_cursor(node_start + TextSize::of(&format!("Result<{}, ", ret_type))); | ||
52 | } | ||
53 | }, | ||
54 | ) | ||
55 | } | ||
56 | |||
57 | struct TailReturnCollector { | ||
58 | exprs_to_wrap: Vec<SyntaxNode>, | ||
59 | } | ||
60 | |||
61 | impl TailReturnCollector { | ||
62 | fn new() -> Self { | ||
63 | Self { exprs_to_wrap: vec![] } | ||
64 | } | ||
65 | /// Collect all`return` expression | ||
66 | fn collect_jump_exprs(&mut self, block_expr: &BlockExpr, collect_break: bool) { | ||
67 | let statements = block_expr.statements(); | ||
68 | for stmt in statements { | ||
69 | let expr = match &stmt { | ||
70 | ast::Stmt::ExprStmt(stmt) => stmt.expr(), | ||
71 | ast::Stmt::LetStmt(stmt) => stmt.initializer(), | ||
72 | }; | ||
73 | if let Some(expr) = &expr { | ||
74 | self.handle_exprs(expr, collect_break); | ||
75 | } | ||
76 | } | ||
77 | |||
78 | // Browse tail expressions for each block | ||
79 | if let Some(expr) = block_expr.expr() { | ||
80 | if let Some(last_exprs) = get_tail_expr_from_block(&expr) { | ||
81 | for last_expr in last_exprs { | ||
82 | let last_expr = match last_expr { | ||
83 | NodeType::Node(expr) | NodeType::Leaf(expr) => expr, | ||
84 | }; | ||
85 | |||
86 | if let Some(last_expr) = Expr::cast(last_expr.clone()) { | ||
87 | self.handle_exprs(&last_expr, collect_break); | ||
88 | } else if let Some(expr_stmt) = ast::Stmt::cast(last_expr) { | ||
89 | let expr_stmt = match &expr_stmt { | ||
90 | ast::Stmt::ExprStmt(stmt) => stmt.expr(), | ||
91 | ast::Stmt::LetStmt(stmt) => stmt.initializer(), | ||
92 | }; | ||
93 | if let Some(expr) = &expr_stmt { | ||
94 | self.handle_exprs(expr, collect_break); | ||
95 | } | ||
96 | } | ||
97 | } | ||
98 | } | ||
99 | } | ||
100 | } | ||
101 | |||
102 | fn handle_exprs(&mut self, expr: &Expr, collect_break: bool) { | ||
103 | match expr { | ||
104 | Expr::BlockExpr(block_expr) => { | ||
105 | self.collect_jump_exprs(&block_expr, collect_break); | ||
106 | } | ||
107 | Expr::ReturnExpr(ret_expr) => { | ||
108 | if let Some(ret_expr_arg) = &ret_expr.expr() { | ||
109 | self.exprs_to_wrap.push(ret_expr_arg.syntax().clone()); | ||
110 | } | ||
111 | } | ||
112 | Expr::BreakExpr(break_expr) if collect_break => { | ||
113 | if let Some(break_expr_arg) = &break_expr.expr() { | ||
114 | self.exprs_to_wrap.push(break_expr_arg.syntax().clone()); | ||
115 | } | ||
116 | } | ||
117 | Expr::IfExpr(if_expr) => { | ||
118 | for block in if_expr.blocks() { | ||
119 | self.collect_jump_exprs(&block, collect_break); | ||
120 | } | ||
121 | } | ||
122 | Expr::LoopExpr(loop_expr) => { | ||
123 | if let Some(block_expr) = loop_expr.loop_body() { | ||
124 | self.collect_jump_exprs(&block_expr, collect_break); | ||
125 | } | ||
126 | } | ||
127 | Expr::ForExpr(for_expr) => { | ||
128 | if let Some(block_expr) = for_expr.loop_body() { | ||
129 | self.collect_jump_exprs(&block_expr, collect_break); | ||
130 | } | ||
131 | } | ||
132 | Expr::WhileExpr(while_expr) => { | ||
133 | if let Some(block_expr) = while_expr.loop_body() { | ||
134 | self.collect_jump_exprs(&block_expr, collect_break); | ||
135 | } | ||
136 | } | ||
137 | Expr::MatchExpr(match_expr) => { | ||
138 | if let Some(arm_list) = match_expr.match_arm_list() { | ||
139 | arm_list.arms().filter_map(|match_arm| match_arm.expr()).for_each(|expr| { | ||
140 | self.handle_exprs(&expr, collect_break); | ||
141 | }); | ||
142 | } | ||
143 | } | ||
144 | _ => {} | ||
145 | } | ||
146 | } | ||
147 | |||
148 | fn collect_tail_exprs(&mut self, block: &BlockExpr) { | ||
149 | if let Some(expr) = block.expr() { | ||
150 | self.handle_exprs(&expr, true); | ||
151 | self.fetch_tail_exprs(&expr); | ||
152 | } | ||
153 | } | ||
154 | |||
155 | fn fetch_tail_exprs(&mut self, expr: &Expr) { | ||
156 | if let Some(exprs) = get_tail_expr_from_block(expr) { | ||
157 | for node_type in &exprs { | ||
158 | match node_type { | ||
159 | NodeType::Leaf(expr) => { | ||
160 | self.exprs_to_wrap.push(expr.clone()); | ||
161 | } | ||
162 | NodeType::Node(expr) => match &Expr::cast(expr.clone()) { | ||
163 | Some(last_expr) => { | ||
164 | self.fetch_tail_exprs(last_expr); | ||
165 | } | ||
166 | None => { | ||
167 | self.exprs_to_wrap.push(expr.clone()); | ||
168 | } | ||
169 | }, | ||
170 | } | ||
171 | } | ||
172 | } | ||
173 | } | ||
174 | } | ||
175 | |||
176 | #[derive(Debug)] | ||
177 | enum NodeType { | ||
178 | Leaf(SyntaxNode), | ||
179 | Node(SyntaxNode), | ||
180 | } | ||
181 | |||
182 | /// Get a tail expression inside a block | ||
183 | fn get_tail_expr_from_block(expr: &Expr) -> Option<Vec<NodeType>> { | ||
184 | match expr { | ||
185 | Expr::IfExpr(if_expr) => { | ||
186 | let mut nodes = vec![]; | ||
187 | for block in if_expr.blocks() { | ||
188 | if let Some(block_expr) = block.expr() { | ||
189 | if let Some(tail_exprs) = get_tail_expr_from_block(&block_expr) { | ||
190 | nodes.extend(tail_exprs); | ||
191 | } | ||
192 | } else if let Some(last_expr) = block.syntax().last_child() { | ||
193 | nodes.push(NodeType::Node(last_expr)); | ||
194 | } else { | ||
195 | nodes.push(NodeType::Node(block.syntax().clone())); | ||
196 | } | ||
197 | } | ||
198 | Some(nodes) | ||
199 | } | ||
200 | Expr::LoopExpr(loop_expr) => { | ||
201 | loop_expr.syntax().last_child().map(|lc| vec![NodeType::Node(lc)]) | ||
202 | } | ||
203 | Expr::ForExpr(for_expr) => { | ||
204 | for_expr.syntax().last_child().map(|lc| vec![NodeType::Node(lc)]) | ||
205 | } | ||
206 | Expr::WhileExpr(while_expr) => { | ||
207 | while_expr.syntax().last_child().map(|lc| vec![NodeType::Node(lc)]) | ||
208 | } | ||
209 | Expr::BlockExpr(block_expr) => { | ||
210 | block_expr.expr().map(|lc| vec![NodeType::Node(lc.syntax().clone())]) | ||
211 | } | ||
212 | Expr::MatchExpr(match_expr) => { | ||
213 | let arm_list = match_expr.match_arm_list()?; | ||
214 | let arms: Vec<NodeType> = arm_list | ||
215 | .arms() | ||
216 | .filter_map(|match_arm| match_arm.expr()) | ||
217 | .map(|expr| match expr { | ||
218 | Expr::ReturnExpr(ret_expr) => NodeType::Node(ret_expr.syntax().clone()), | ||
219 | Expr::BreakExpr(break_expr) => NodeType::Node(break_expr.syntax().clone()), | ||
220 | _ => match expr.syntax().last_child() { | ||
221 | Some(last_expr) => NodeType::Node(last_expr), | ||
222 | None => NodeType::Node(expr.syntax().clone()), | ||
223 | }, | ||
224 | }) | ||
225 | .collect(); | ||
226 | |||
227 | Some(arms) | ||
228 | } | ||
229 | Expr::BreakExpr(expr) => expr.expr().map(|e| vec![NodeType::Leaf(e.syntax().clone())]), | ||
230 | Expr::ReturnExpr(ret_expr) => Some(vec![NodeType::Node(ret_expr.syntax().clone())]), | ||
231 | Expr::CallExpr(call_expr) => Some(vec![NodeType::Leaf(call_expr.syntax().clone())]), | ||
232 | Expr::Literal(lit_expr) => Some(vec![NodeType::Leaf(lit_expr.syntax().clone())]), | ||
233 | Expr::TupleExpr(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]), | ||
234 | Expr::ArrayExpr(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]), | ||
235 | Expr::ParenExpr(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]), | ||
236 | Expr::PathExpr(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]), | ||
237 | Expr::Label(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]), | ||
238 | Expr::RecordLit(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]), | ||
239 | Expr::IndexExpr(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]), | ||
240 | Expr::MethodCallExpr(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]), | ||
241 | Expr::AwaitExpr(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]), | ||
242 | Expr::CastExpr(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]), | ||
243 | Expr::RefExpr(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]), | ||
244 | Expr::PrefixExpr(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]), | ||
245 | Expr::RangeExpr(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]), | ||
246 | Expr::BinExpr(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]), | ||
247 | Expr::MacroCall(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]), | ||
248 | Expr::BoxExpr(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]), | ||
249 | _ => None, | ||
250 | } | ||
251 | } | ||
252 | |||
253 | fn result_insertion_offset(ret_type: &ast::TypeRef) -> Option<TextSize> { | ||
254 | let non_ws_child = ret_type | ||
255 | .syntax() | ||
256 | .children_with_tokens() | ||
257 | .find(|it| it.kind() != COMMENT && it.kind() != WHITESPACE)?; | ||
258 | Some(non_ws_child.text_range().start()) | ||
259 | } | ||
260 | |||
261 | #[cfg(test)] | ||
262 | mod tests { | ||
263 | |||
264 | use crate::tests::{check_assist, check_assist_not_applicable}; | ||
265 | |||
266 | use super::*; | ||
267 | |||
268 | #[test] | ||
269 | fn change_return_type_to_result_simple() { | ||
270 | check_assist( | ||
271 | change_return_type_to_result, | ||
272 | r#"fn foo() -> i3<|>2 { | ||
273 | let test = "test"; | ||
274 | return 42i32; | ||
275 | }"#, | ||
276 | r#"fn foo() -> Result<i32, <|>> { | ||
277 | let test = "test"; | ||
278 | return Ok(42i32); | ||
279 | }"#, | ||
280 | ); | ||
281 | } | ||
282 | |||
283 | #[test] | ||
284 | fn change_return_type_to_result_simple_return_type() { | ||
285 | check_assist( | ||
286 | change_return_type_to_result, | ||
287 | r#"fn foo() -> i32<|> { | ||
288 | let test = "test"; | ||
289 | return 42i32; | ||
290 | }"#, | ||
291 | r#"fn foo() -> Result<i32, <|>> { | ||
292 | let test = "test"; | ||
293 | return Ok(42i32); | ||
294 | }"#, | ||
295 | ); | ||
296 | } | ||
297 | |||
298 | #[test] | ||
299 | fn change_return_type_to_result_simple_return_type_bad_cursor() { | ||
300 | check_assist_not_applicable( | ||
301 | change_return_type_to_result, | ||
302 | r#"fn foo() -> i32 { | ||
303 | let test = "test";<|> | ||
304 | return 42i32; | ||
305 | }"#, | ||
306 | ); | ||
307 | } | ||
308 | |||
309 | #[test] | ||
310 | fn change_return_type_to_result_simple_with_cursor() { | ||
311 | check_assist( | ||
312 | change_return_type_to_result, | ||
313 | r#"fn foo() -> <|>i32 { | ||
314 | let test = "test"; | ||
315 | return 42i32; | ||
316 | }"#, | ||
317 | r#"fn foo() -> Result<i32, <|>> { | ||
318 | let test = "test"; | ||
319 | return Ok(42i32); | ||
320 | }"#, | ||
321 | ); | ||
322 | } | ||
323 | |||
324 | #[test] | ||
325 | fn change_return_type_to_result_simple_with_tail() { | ||
326 | check_assist( | ||
327 | change_return_type_to_result, | ||
328 | r#"fn foo() -><|> i32 { | ||
329 | let test = "test"; | ||
330 | 42i32 | ||
331 | }"#, | ||
332 | r#"fn foo() -> Result<i32, <|>> { | ||
333 | let test = "test"; | ||
334 | Ok(42i32) | ||
335 | }"#, | ||
336 | ); | ||
337 | } | ||
338 | |||
339 | #[test] | ||
340 | fn change_return_type_to_result_simple_with_tail_only() { | ||
341 | check_assist( | ||
342 | change_return_type_to_result, | ||
343 | r#"fn foo() -> i32<|> { | ||
344 | 42i32 | ||
345 | }"#, | ||
346 | r#"fn foo() -> Result<i32, <|>> { | ||
347 | Ok(42i32) | ||
348 | }"#, | ||
349 | ); | ||
350 | } | ||
351 | #[test] | ||
352 | fn change_return_type_to_result_simple_with_tail_block_like() { | ||
353 | check_assist( | ||
354 | change_return_type_to_result, | ||
355 | r#"fn foo() -> i32<|> { | ||
356 | if true { | ||
357 | 42i32 | ||
358 | } else { | ||
359 | 24i32 | ||
360 | } | ||
361 | }"#, | ||
362 | r#"fn foo() -> Result<i32, <|>> { | ||
363 | if true { | ||
364 | Ok(42i32) | ||
365 | } else { | ||
366 | Ok(24i32) | ||
367 | } | ||
368 | }"#, | ||
369 | ); | ||
370 | } | ||
371 | |||
372 | #[test] | ||
373 | fn change_return_type_to_result_simple_with_nested_if() { | ||
374 | check_assist( | ||
375 | change_return_type_to_result, | ||
376 | r#"fn foo() -> i32<|> { | ||
377 | if true { | ||
378 | if false { | ||
379 | 1 | ||
380 | } else { | ||
381 | 2 | ||
382 | } | ||
383 | } else { | ||
384 | 24i32 | ||
385 | } | ||
386 | }"#, | ||
387 | r#"fn foo() -> Result<i32, <|>> { | ||
388 | if true { | ||
389 | if false { | ||
390 | Ok(1) | ||
391 | } else { | ||
392 | Ok(2) | ||
393 | } | ||
394 | } else { | ||
395 | Ok(24i32) | ||
396 | } | ||
397 | }"#, | ||
398 | ); | ||
399 | } | ||
400 | |||
401 | #[test] | ||
402 | fn change_return_type_to_result_simple_with_await() { | ||
403 | check_assist( | ||
404 | change_return_type_to_result, | ||
405 | r#"async fn foo() -> i<|>32 { | ||
406 | if true { | ||
407 | if false { | ||
408 | 1.await | ||
409 | } else { | ||
410 | 2.await | ||
411 | } | ||
412 | } else { | ||
413 | 24i32.await | ||
414 | } | ||
415 | }"#, | ||
416 | r#"async fn foo() -> Result<i32, <|>> { | ||
417 | if true { | ||
418 | if false { | ||
419 | Ok(1.await) | ||
420 | } else { | ||
421 | Ok(2.await) | ||
422 | } | ||
423 | } else { | ||
424 | Ok(24i32.await) | ||
425 | } | ||
426 | }"#, | ||
427 | ); | ||
428 | } | ||
429 | |||
430 | #[test] | ||
431 | fn change_return_type_to_result_simple_with_array() { | ||
432 | check_assist( | ||
433 | change_return_type_to_result, | ||
434 | r#"fn foo() -> [i32;<|> 3] { | ||
435 | [1, 2, 3] | ||
436 | }"#, | ||
437 | r#"fn foo() -> Result<[i32; 3], <|>> { | ||
438 | Ok([1, 2, 3]) | ||
439 | }"#, | ||
440 | ); | ||
441 | } | ||
442 | |||
443 | #[test] | ||
444 | fn change_return_type_to_result_simple_with_cast() { | ||
445 | check_assist( | ||
446 | change_return_type_to_result, | ||
447 | r#"fn foo() -<|>> i32 { | ||
448 | if true { | ||
449 | if false { | ||
450 | 1 as i32 | ||
451 | } else { | ||
452 | 2 as i32 | ||
453 | } | ||
454 | } else { | ||
455 | 24 as i32 | ||
456 | } | ||
457 | }"#, | ||
458 | r#"fn foo() -> Result<i32, <|>> { | ||
459 | if true { | ||
460 | if false { | ||
461 | Ok(1 as i32) | ||
462 | } else { | ||
463 | Ok(2 as i32) | ||
464 | } | ||
465 | } else { | ||
466 | Ok(24 as i32) | ||
467 | } | ||
468 | }"#, | ||
469 | ); | ||
470 | } | ||
471 | |||
472 | #[test] | ||
473 | fn change_return_type_to_result_simple_with_tail_block_like_match() { | ||
474 | check_assist( | ||
475 | change_return_type_to_result, | ||
476 | r#"fn foo() -> i32<|> { | ||
477 | let my_var = 5; | ||
478 | match my_var { | ||
479 | 5 => 42i32, | ||
480 | _ => 24i32, | ||
481 | } | ||
482 | }"#, | ||
483 | r#"fn foo() -> Result<i32, <|>> { | ||
484 | let my_var = 5; | ||
485 | match my_var { | ||
486 | 5 => Ok(42i32), | ||
487 | _ => Ok(24i32), | ||
488 | } | ||
489 | }"#, | ||
490 | ); | ||
491 | } | ||
492 | |||
493 | #[test] | ||
494 | fn change_return_type_to_result_simple_with_loop_with_tail() { | ||
495 | check_assist( | ||
496 | change_return_type_to_result, | ||
497 | r#"fn foo() -> i32<|> { | ||
498 | let my_var = 5; | ||
499 | loop { | ||
500 | println!("test"); | ||
501 | 5 | ||
502 | } | ||
503 | |||
504 | my_var | ||
505 | }"#, | ||
506 | r#"fn foo() -> Result<i32, <|>> { | ||
507 | let my_var = 5; | ||
508 | loop { | ||
509 | println!("test"); | ||
510 | 5 | ||
511 | } | ||
512 | |||
513 | Ok(my_var) | ||
514 | }"#, | ||
515 | ); | ||
516 | } | ||
517 | |||
518 | #[test] | ||
519 | fn change_return_type_to_result_simple_with_loop_in_let_stmt() { | ||
520 | check_assist( | ||
521 | change_return_type_to_result, | ||
522 | r#"fn foo() -> i32<|> { | ||
523 | let my_var = let x = loop { | ||
524 | break 1; | ||
525 | }; | ||
526 | |||
527 | my_var | ||
528 | }"#, | ||
529 | r#"fn foo() -> Result<i32, <|>> { | ||
530 | let my_var = let x = loop { | ||
531 | break 1; | ||
532 | }; | ||
533 | |||
534 | Ok(my_var) | ||
535 | }"#, | ||
536 | ); | ||
537 | } | ||
538 | |||
539 | #[test] | ||
540 | fn change_return_type_to_result_simple_with_tail_block_like_match_return_expr() { | ||
541 | check_assist( | ||
542 | change_return_type_to_result, | ||
543 | r#"fn foo() -> i32<|> { | ||
544 | let my_var = 5; | ||
545 | let res = match my_var { | ||
546 | 5 => 42i32, | ||
547 | _ => return 24i32, | ||
548 | }; | ||
549 | |||
550 | res | ||
551 | }"#, | ||
552 | r#"fn foo() -> Result<i32, <|>> { | ||
553 | let my_var = 5; | ||
554 | let res = match my_var { | ||
555 | 5 => 42i32, | ||
556 | _ => return Ok(24i32), | ||
557 | }; | ||
558 | |||
559 | Ok(res) | ||
560 | }"#, | ||
561 | ); | ||
562 | |||
563 | check_assist( | ||
564 | change_return_type_to_result, | ||
565 | r#"fn foo() -> i32<|> { | ||
566 | let my_var = 5; | ||
567 | let res = if my_var == 5 { | ||
568 | 42i32 | ||
569 | } else { | ||
570 | return 24i32; | ||
571 | }; | ||
572 | |||
573 | res | ||
574 | }"#, | ||
575 | r#"fn foo() -> Result<i32, <|>> { | ||
576 | let my_var = 5; | ||
577 | let res = if my_var == 5 { | ||
578 | 42i32 | ||
579 | } else { | ||
580 | return Ok(24i32); | ||
581 | }; | ||
582 | |||
583 | Ok(res) | ||
584 | }"#, | ||
585 | ); | ||
586 | } | ||
587 | |||
588 | #[test] | ||
589 | fn change_return_type_to_result_simple_with_tail_block_like_match_deeper() { | ||
590 | check_assist( | ||
591 | change_return_type_to_result, | ||
592 | r#"fn foo() -> i32<|> { | ||
593 | let my_var = 5; | ||
594 | match my_var { | ||
595 | 5 => { | ||
596 | if true { | ||
597 | 42i32 | ||
598 | } else { | ||
599 | 25i32 | ||
600 | } | ||
601 | }, | ||
602 | _ => { | ||
603 | let test = "test"; | ||
604 | if test == "test" { | ||
605 | return bar(); | ||
606 | } | ||
607 | 53i32 | ||
608 | }, | ||
609 | } | ||
610 | }"#, | ||
611 | r#"fn foo() -> Result<i32, <|>> { | ||
612 | let my_var = 5; | ||
613 | match my_var { | ||
614 | 5 => { | ||
615 | if true { | ||
616 | Ok(42i32) | ||
617 | } else { | ||
618 | Ok(25i32) | ||
619 | } | ||
620 | }, | ||
621 | _ => { | ||
622 | let test = "test"; | ||
623 | if test == "test" { | ||
624 | return Ok(bar()); | ||
625 | } | ||
626 | Ok(53i32) | ||
627 | }, | ||
628 | } | ||
629 | }"#, | ||
630 | ); | ||
631 | } | ||
632 | |||
633 | #[test] | ||
634 | fn change_return_type_to_result_simple_with_tail_block_like_early_return() { | ||
635 | check_assist( | ||
636 | change_return_type_to_result, | ||
637 | r#"fn foo() -> i<|>32 { | ||
638 | let test = "test"; | ||
639 | if test == "test" { | ||
640 | return 24i32; | ||
641 | } | ||
642 | 53i32 | ||
643 | }"#, | ||
644 | r#"fn foo() -> Result<i32, <|>> { | ||
645 | let test = "test"; | ||
646 | if test == "test" { | ||
647 | return Ok(24i32); | ||
648 | } | ||
649 | Ok(53i32) | ||
650 | }"#, | ||
651 | ); | ||
652 | } | ||
653 | |||
654 | #[test] | ||
655 | fn change_return_type_to_result_simple_with_closure() { | ||
656 | check_assist( | ||
657 | change_return_type_to_result, | ||
658 | r#"fn foo(the_field: u32) -><|> u32 { | ||
659 | let true_closure = || { | ||
660 | return true; | ||
661 | }; | ||
662 | if the_field < 5 { | ||
663 | let mut i = 0; | ||
664 | |||
665 | |||
666 | if true_closure() { | ||
667 | return 99; | ||
668 | } else { | ||
669 | return 0; | ||
670 | } | ||
671 | } | ||
672 | |||
673 | the_field | ||
674 | }"#, | ||
675 | r#"fn foo(the_field: u32) -> Result<u32, <|>> { | ||
676 | let true_closure = || { | ||
677 | return true; | ||
678 | }; | ||
679 | if the_field < 5 { | ||
680 | let mut i = 0; | ||
681 | |||
682 | |||
683 | if true_closure() { | ||
684 | return Ok(99); | ||
685 | } else { | ||
686 | return Ok(0); | ||
687 | } | ||
688 | } | ||
689 | |||
690 | Ok(the_field) | ||
691 | }"#, | ||
692 | ); | ||
693 | |||
694 | check_assist( | ||
695 | change_return_type_to_result, | ||
696 | r#"fn foo(the_field: u32) -> u32<|> { | ||
697 | let true_closure = || { | ||
698 | return true; | ||
699 | }; | ||
700 | if the_field < 5 { | ||
701 | let mut i = 0; | ||
702 | |||
703 | |||
704 | if true_closure() { | ||
705 | return 99; | ||
706 | } else { | ||
707 | return 0; | ||
708 | } | ||
709 | } | ||
710 | let t = None; | ||
711 | |||
712 | t.unwrap_or_else(|| the_field) | ||
713 | }"#, | ||
714 | r#"fn foo(the_field: u32) -> Result<u32, <|>> { | ||
715 | let true_closure = || { | ||
716 | return true; | ||
717 | }; | ||
718 | if the_field < 5 { | ||
719 | let mut i = 0; | ||
720 | |||
721 | |||
722 | if true_closure() { | ||
723 | return Ok(99); | ||
724 | } else { | ||
725 | return Ok(0); | ||
726 | } | ||
727 | } | ||
728 | let t = None; | ||
729 | |||
730 | Ok(t.unwrap_or_else(|| the_field)) | ||
731 | }"#, | ||
732 | ); | ||
733 | } | ||
734 | |||
735 | #[test] | ||
736 | fn change_return_type_to_result_simple_with_weird_forms() { | ||
737 | check_assist( | ||
738 | change_return_type_to_result, | ||
739 | r#"fn foo() -> i32<|> { | ||
740 | let test = "test"; | ||
741 | if test == "test" { | ||
742 | return 24i32; | ||
743 | } | ||
744 | let mut i = 0; | ||
745 | loop { | ||
746 | if i == 1 { | ||
747 | break 55; | ||
748 | } | ||
749 | i += 1; | ||
750 | } | ||
751 | }"#, | ||
752 | r#"fn foo() -> Result<i32, <|>> { | ||
753 | let test = "test"; | ||
754 | if test == "test" { | ||
755 | return Ok(24i32); | ||
756 | } | ||
757 | let mut i = 0; | ||
758 | loop { | ||
759 | if i == 1 { | ||
760 | break Ok(55); | ||
761 | } | ||
762 | i += 1; | ||
763 | } | ||
764 | }"#, | ||
765 | ); | ||
766 | |||
767 | check_assist( | ||
768 | change_return_type_to_result, | ||
769 | r#"fn foo() -> i32<|> { | ||
770 | let test = "test"; | ||
771 | if test == "test" { | ||
772 | return 24i32; | ||
773 | } | ||
774 | let mut i = 0; | ||
775 | loop { | ||
776 | loop { | ||
777 | if i == 1 { | ||
778 | break 55; | ||
779 | } | ||
780 | i += 1; | ||
781 | } | ||
782 | } | ||
783 | }"#, | ||
784 | r#"fn foo() -> Result<i32, <|>> { | ||
785 | let test = "test"; | ||
786 | if test == "test" { | ||
787 | return Ok(24i32); | ||
788 | } | ||
789 | let mut i = 0; | ||
790 | loop { | ||
791 | loop { | ||
792 | if i == 1 { | ||
793 | break Ok(55); | ||
794 | } | ||
795 | i += 1; | ||
796 | } | ||
797 | } | ||
798 | }"#, | ||
799 | ); | ||
800 | |||
801 | check_assist( | ||
802 | change_return_type_to_result, | ||
803 | r#"fn foo() -> i3<|>2 { | ||
804 | let test = "test"; | ||
805 | let other = 5; | ||
806 | if test == "test" { | ||
807 | let res = match other { | ||
808 | 5 => 43, | ||
809 | _ => return 56, | ||
810 | }; | ||
811 | } | ||
812 | let mut i = 0; | ||
813 | loop { | ||
814 | loop { | ||
815 | if i == 1 { | ||
816 | break 55; | ||
817 | } | ||
818 | i += 1; | ||
819 | } | ||
820 | } | ||
821 | }"#, | ||
822 | r#"fn foo() -> Result<i32, <|>> { | ||
823 | let test = "test"; | ||
824 | let other = 5; | ||
825 | if test == "test" { | ||
826 | let res = match other { | ||
827 | 5 => 43, | ||
828 | _ => return Ok(56), | ||
829 | }; | ||
830 | } | ||
831 | let mut i = 0; | ||
832 | loop { | ||
833 | loop { | ||
834 | if i == 1 { | ||
835 | break Ok(55); | ||
836 | } | ||
837 | i += 1; | ||
838 | } | ||
839 | } | ||
840 | }"#, | ||
841 | ); | ||
842 | |||
843 | check_assist( | ||
844 | change_return_type_to_result, | ||
845 | r#"fn foo(the_field: u32) -> u32<|> { | ||
846 | if the_field < 5 { | ||
847 | let mut i = 0; | ||
848 | loop { | ||
849 | if i > 5 { | ||
850 | return 55u32; | ||
851 | } | ||
852 | i += 3; | ||
853 | } | ||
854 | |||
855 | match i { | ||
856 | 5 => return 99, | ||
857 | _ => return 0, | ||
858 | }; | ||
859 | } | ||
860 | |||
861 | the_field | ||
862 | }"#, | ||
863 | r#"fn foo(the_field: u32) -> Result<u32, <|>> { | ||
864 | if the_field < 5 { | ||
865 | let mut i = 0; | ||
866 | loop { | ||
867 | if i > 5 { | ||
868 | return Ok(55u32); | ||
869 | } | ||
870 | i += 3; | ||
871 | } | ||
872 | |||
873 | match i { | ||
874 | 5 => return Ok(99), | ||
875 | _ => return Ok(0), | ||
876 | }; | ||
877 | } | ||
878 | |||
879 | Ok(the_field) | ||
880 | }"#, | ||
881 | ); | ||
882 | |||
883 | check_assist( | ||
884 | change_return_type_to_result, | ||
885 | r#"fn foo(the_field: u32) -> u3<|>2 { | ||
886 | if the_field < 5 { | ||
887 | let mut i = 0; | ||
888 | |||
889 | match i { | ||
890 | 5 => return 99, | ||
891 | _ => return 0, | ||
892 | } | ||
893 | } | ||
894 | |||
895 | the_field | ||
896 | }"#, | ||
897 | r#"fn foo(the_field: u32) -> Result<u32, <|>> { | ||
898 | if the_field < 5 { | ||
899 | let mut i = 0; | ||
900 | |||
901 | match i { | ||
902 | 5 => return Ok(99), | ||
903 | _ => return Ok(0), | ||
904 | } | ||
905 | } | ||
906 | |||
907 | Ok(the_field) | ||
908 | }"#, | ||
909 | ); | ||
910 | |||
911 | check_assist( | ||
912 | change_return_type_to_result, | ||
913 | r#"fn foo(the_field: u32) -> u32<|> { | ||
914 | if the_field < 5 { | ||
915 | let mut i = 0; | ||
916 | |||
917 | if i == 5 { | ||
918 | return 99 | ||
919 | } else { | ||
920 | return 0 | ||
921 | } | ||
922 | } | ||
923 | |||
924 | the_field | ||
925 | }"#, | ||
926 | r#"fn foo(the_field: u32) -> Result<u32, <|>> { | ||
927 | if the_field < 5 { | ||
928 | let mut i = 0; | ||
929 | |||
930 | if i == 5 { | ||
931 | return Ok(99) | ||
932 | } else { | ||
933 | return Ok(0) | ||
934 | } | ||
935 | } | ||
936 | |||
937 | Ok(the_field) | ||
938 | }"#, | ||
939 | ); | ||
940 | |||
941 | check_assist( | ||
942 | change_return_type_to_result, | ||
943 | r#"fn foo(the_field: u32) -> <|>u32 { | ||
944 | if the_field < 5 { | ||
945 | let mut i = 0; | ||
946 | |||
947 | if i == 5 { | ||
948 | return 99; | ||
949 | } else { | ||
950 | return 0; | ||
951 | } | ||
952 | } | ||
953 | |||
954 | the_field | ||
955 | }"#, | ||
956 | r#"fn foo(the_field: u32) -> Result<u32, <|>> { | ||
957 | if the_field < 5 { | ||
958 | let mut i = 0; | ||
959 | |||
960 | if i == 5 { | ||
961 | return Ok(99); | ||
962 | } else { | ||
963 | return Ok(0); | ||
964 | } | ||
965 | } | ||
966 | |||
967 | Ok(the_field) | ||
968 | }"#, | ||
969 | ); | ||
970 | } | ||
971 | } | ||
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index 13ea45ec7..0473fd8c2 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs | |||
@@ -129,6 +129,7 @@ mod handlers { | |||
129 | mod replace_qualified_name_with_use; | 129 | mod replace_qualified_name_with_use; |
130 | mod replace_unwrap_with_match; | 130 | mod replace_unwrap_with_match; |
131 | mod split_import; | 131 | mod split_import; |
132 | mod change_return_type_to_result; | ||
132 | mod add_from_impl_for_enum; | 133 | mod add_from_impl_for_enum; |
133 | mod reorder_fields; | 134 | mod reorder_fields; |
134 | mod unwrap_block; | 135 | mod unwrap_block; |
@@ -145,6 +146,7 @@ mod handlers { | |||
145 | add_new::add_new, | 146 | add_new::add_new, |
146 | apply_demorgan::apply_demorgan, | 147 | apply_demorgan::apply_demorgan, |
147 | auto_import::auto_import, | 148 | auto_import::auto_import, |
149 | change_return_type_to_result::change_return_type_to_result, | ||
148 | change_visibility::change_visibility, | 150 | change_visibility::change_visibility, |
149 | early_return::convert_to_guarded_return, | 151 | early_return::convert_to_guarded_return, |
150 | fill_match_arms::fill_match_arms, | 152 | fill_match_arms::fill_match_arms, |
diff --git a/crates/ra_assists/src/tests/generated.rs b/crates/ra_assists/src/tests/generated.rs index 7d35fa284..972dbd251 100644 --- a/crates/ra_assists/src/tests/generated.rs +++ b/crates/ra_assists/src/tests/generated.rs | |||
@@ -250,6 +250,19 @@ pub mod std { pub mod collections { pub struct HashMap { } } } | |||
250 | } | 250 | } |
251 | 251 | ||
252 | #[test] | 252 | #[test] |
253 | fn doctest_change_return_type_to_result() { | ||
254 | check_doc_test( | ||
255 | "change_return_type_to_result", | ||
256 | r#####" | ||
257 | fn foo() -> i32<|> { 42i32 } | ||
258 | "#####, | ||
259 | r#####" | ||
260 | fn foo() -> Result<i32, > { Ok(42i32) } | ||
261 | "#####, | ||
262 | ) | ||
263 | } | ||
264 | |||
265 | #[test] | ||
253 | fn doctest_change_visibility() { | 266 | fn doctest_change_visibility() { |
254 | check_doc_test( | 267 | check_doc_test( |
255 | "change_visibility", | 268 | "change_visibility", |
diff --git a/docs/user/assists.md b/docs/user/assists.md index ee515949e..692fd4f52 100644 --- a/docs/user/assists.md +++ b/docs/user/assists.md | |||
@@ -241,6 +241,18 @@ fn main() { | |||
241 | } | 241 | } |
242 | ``` | 242 | ``` |
243 | 243 | ||
244 | ## `change_return_type_to_result` | ||
245 | |||
246 | Change the function's return type to Result. | ||
247 | |||
248 | ```rust | ||
249 | // BEFORE | ||
250 | fn foo() -> i32┃ { 42i32 } | ||
251 | |||
252 | // AFTER | ||
253 | fn foo() -> Result<i32, > { Ok(42i32) } | ||
254 | ``` | ||
255 | |||
244 | ## `change_visibility` | 256 | ## `change_visibility` |
245 | 257 | ||
246 | Adds or changes existing visibility specifier. | 258 | Adds or changes existing visibility specifier. |