aboutsummaryrefslogtreecommitdiff
path: root/crates/assists
diff options
context:
space:
mode:
Diffstat (limited to 'crates/assists')
-rw-r--r--crates/assists/src/handlers/change_return_type_to_result.rs1091
-rw-r--r--crates/assists/src/handlers/replace_derive_with_manual_impl.rs (renamed from crates/assists/src/handlers/add_custom_impl.rs)103
-rw-r--r--crates/assists/src/handlers/wrap_return_type_in_result.rs1158
-rw-r--r--crates/assists/src/lib.rs8
-rw-r--r--crates/assists/src/tests/generated.rs68
5 files changed, 1256 insertions, 1172 deletions
diff --git a/crates/assists/src/handlers/change_return_type_to_result.rs b/crates/assists/src/handlers/change_return_type_to_result.rs
deleted file mode 100644
index 76f33a5b6..000000000
--- a/crates/assists/src/handlers/change_return_type_to_result.rs
+++ /dev/null
@@ -1,1091 +0,0 @@
1use std::iter;
2
3use syntax::{
4 ast::{self, make, BlockExpr, Expr, LoopBodyOwner},
5 match_ast, AstNode, SyntaxNode,
6};
7use test_utils::mark;
8
9use crate::{AssistContext, AssistId, AssistKind, Assists};
10
11// Assist: change_return_type_to_result
12//
13// Change the function's return type to Result.
14//
15// ```
16// fn foo() -> i32<|> { 42i32 }
17// ```
18// ->
19// ```
20// fn foo() -> Result<i32, ${0:_}> { Ok(42i32) }
21// ```
22pub(crate) fn change_return_type_to_result(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
23 let ret_type = ctx.find_node_at_offset::<ast::RetType>()?;
24 let parent = ret_type.syntax().parent()?;
25 let block_expr = match_ast! {
26 match parent {
27 ast::Fn(func) => func.body()?,
28 ast::ClosureExpr(closure) => match closure.body()? {
29 Expr::BlockExpr(block) => block,
30 // closures require a block when a return type is specified
31 _ => return None,
32 },
33 _ => return None,
34 }
35 };
36
37 let type_ref = &ret_type.ty()?;
38 let ret_type_str = type_ref.syntax().text().to_string();
39 let first_part_ret_type = ret_type_str.splitn(2, '<').next();
40 if let Some(ret_type_first_part) = first_part_ret_type {
41 if ret_type_first_part.ends_with("Result") {
42 mark::hit!(change_return_type_to_result_simple_return_type_already_result);
43 return None;
44 }
45 }
46
47 acc.add(
48 AssistId("change_return_type_to_result", AssistKind::RefactorRewrite),
49 "Wrap return type in Result",
50 type_ref.syntax().text_range(),
51 |builder| {
52 let mut tail_return_expr_collector = TailReturnCollector::new();
53 tail_return_expr_collector.collect_jump_exprs(&block_expr, false);
54 tail_return_expr_collector.collect_tail_exprs(&block_expr);
55
56 for ret_expr_arg in tail_return_expr_collector.exprs_to_wrap {
57 let ok_wrapped = make::expr_call(
58 make::expr_path(make::path_unqualified(make::path_segment(make::name_ref(
59 "Ok",
60 )))),
61 make::arg_list(iter::once(ret_expr_arg.clone())),
62 );
63 builder.replace_ast(ret_expr_arg, ok_wrapped);
64 }
65
66 match ctx.config.snippet_cap {
67 Some(cap) => {
68 let snippet = format!("Result<{}, ${{0:_}}>", type_ref);
69 builder.replace_snippet(cap, type_ref.syntax().text_range(), snippet)
70 }
71 None => builder
72 .replace(type_ref.syntax().text_range(), format!("Result<{}, _>", type_ref)),
73 }
74 },
75 )
76}
77
78struct TailReturnCollector {
79 exprs_to_wrap: Vec<ast::Expr>,
80}
81
82impl TailReturnCollector {
83 fn new() -> Self {
84 Self { exprs_to_wrap: vec![] }
85 }
86 /// Collect all`return` expression
87 fn collect_jump_exprs(&mut self, block_expr: &BlockExpr, collect_break: bool) {
88 let statements = block_expr.statements();
89 for stmt in statements {
90 let expr = match &stmt {
91 ast::Stmt::ExprStmt(stmt) => stmt.expr(),
92 ast::Stmt::LetStmt(stmt) => stmt.initializer(),
93 ast::Stmt::Item(_) => continue,
94 };
95 if let Some(expr) = &expr {
96 self.handle_exprs(expr, collect_break);
97 }
98 }
99
100 // Browse tail expressions for each block
101 if let Some(expr) = block_expr.expr() {
102 if let Some(last_exprs) = get_tail_expr_from_block(&expr) {
103 for last_expr in last_exprs {
104 let last_expr = match last_expr {
105 NodeType::Node(expr) => expr,
106 NodeType::Leaf(expr) => expr.syntax().clone(),
107 };
108
109 if let Some(last_expr) = Expr::cast(last_expr.clone()) {
110 self.handle_exprs(&last_expr, collect_break);
111 } else if let Some(expr_stmt) = ast::Stmt::cast(last_expr) {
112 let expr_stmt = match &expr_stmt {
113 ast::Stmt::ExprStmt(stmt) => stmt.expr(),
114 ast::Stmt::LetStmt(stmt) => stmt.initializer(),
115 ast::Stmt::Item(_) => None,
116 };
117 if let Some(expr) = &expr_stmt {
118 self.handle_exprs(expr, collect_break);
119 }
120 }
121 }
122 }
123 }
124 }
125
126 fn handle_exprs(&mut self, expr: &Expr, collect_break: bool) {
127 match expr {
128 Expr::BlockExpr(block_expr) => {
129 self.collect_jump_exprs(&block_expr, collect_break);
130 }
131 Expr::ReturnExpr(ret_expr) => {
132 if let Some(ret_expr_arg) = &ret_expr.expr() {
133 self.exprs_to_wrap.push(ret_expr_arg.clone());
134 }
135 }
136 Expr::BreakExpr(break_expr) if collect_break => {
137 if let Some(break_expr_arg) = &break_expr.expr() {
138 self.exprs_to_wrap.push(break_expr_arg.clone());
139 }
140 }
141 Expr::IfExpr(if_expr) => {
142 for block in if_expr.blocks() {
143 self.collect_jump_exprs(&block, collect_break);
144 }
145 }
146 Expr::LoopExpr(loop_expr) => {
147 if let Some(block_expr) = loop_expr.loop_body() {
148 self.collect_jump_exprs(&block_expr, collect_break);
149 }
150 }
151 Expr::ForExpr(for_expr) => {
152 if let Some(block_expr) = for_expr.loop_body() {
153 self.collect_jump_exprs(&block_expr, collect_break);
154 }
155 }
156 Expr::WhileExpr(while_expr) => {
157 if let Some(block_expr) = while_expr.loop_body() {
158 self.collect_jump_exprs(&block_expr, collect_break);
159 }
160 }
161 Expr::MatchExpr(match_expr) => {
162 if let Some(arm_list) = match_expr.match_arm_list() {
163 arm_list.arms().filter_map(|match_arm| match_arm.expr()).for_each(|expr| {
164 self.handle_exprs(&expr, collect_break);
165 });
166 }
167 }
168 _ => {}
169 }
170 }
171
172 fn collect_tail_exprs(&mut self, block: &BlockExpr) {
173 if let Some(expr) = block.expr() {
174 self.handle_exprs(&expr, true);
175 self.fetch_tail_exprs(&expr);
176 }
177 }
178
179 fn fetch_tail_exprs(&mut self, expr: &Expr) {
180 if let Some(exprs) = get_tail_expr_from_block(expr) {
181 for node_type in &exprs {
182 match node_type {
183 NodeType::Leaf(expr) => {
184 self.exprs_to_wrap.push(expr.clone());
185 }
186 NodeType::Node(expr) => {
187 if let Some(last_expr) = Expr::cast(expr.clone()) {
188 self.fetch_tail_exprs(&last_expr);
189 }
190 }
191 }
192 }
193 }
194 }
195}
196
197#[derive(Debug)]
198enum NodeType {
199 Leaf(ast::Expr),
200 Node(SyntaxNode),
201}
202
203/// Get a tail expression inside a block
204fn get_tail_expr_from_block(expr: &Expr) -> Option<Vec<NodeType>> {
205 match expr {
206 Expr::IfExpr(if_expr) => {
207 let mut nodes = vec![];
208 for block in if_expr.blocks() {
209 if let Some(block_expr) = block.expr() {
210 if let Some(tail_exprs) = get_tail_expr_from_block(&block_expr) {
211 nodes.extend(tail_exprs);
212 }
213 } else if let Some(last_expr) = block.syntax().last_child() {
214 nodes.push(NodeType::Node(last_expr));
215 } else {
216 nodes.push(NodeType::Node(block.syntax().clone()));
217 }
218 }
219 Some(nodes)
220 }
221 Expr::LoopExpr(loop_expr) => {
222 loop_expr.syntax().last_child().map(|lc| vec![NodeType::Node(lc)])
223 }
224 Expr::ForExpr(for_expr) => {
225 for_expr.syntax().last_child().map(|lc| vec![NodeType::Node(lc)])
226 }
227 Expr::WhileExpr(while_expr) => {
228 while_expr.syntax().last_child().map(|lc| vec![NodeType::Node(lc)])
229 }
230 Expr::BlockExpr(block_expr) => {
231 block_expr.expr().map(|lc| vec![NodeType::Node(lc.syntax().clone())])
232 }
233 Expr::MatchExpr(match_expr) => {
234 let arm_list = match_expr.match_arm_list()?;
235 let arms: Vec<NodeType> = arm_list
236 .arms()
237 .filter_map(|match_arm| match_arm.expr())
238 .map(|expr| match expr {
239 Expr::ReturnExpr(ret_expr) => NodeType::Node(ret_expr.syntax().clone()),
240 Expr::BreakExpr(break_expr) => NodeType::Node(break_expr.syntax().clone()),
241 _ => match expr.syntax().last_child() {
242 Some(last_expr) => NodeType::Node(last_expr),
243 None => NodeType::Node(expr.syntax().clone()),
244 },
245 })
246 .collect();
247
248 Some(arms)
249 }
250 Expr::BreakExpr(expr) => expr.expr().map(|e| vec![NodeType::Leaf(e)]),
251 Expr::ReturnExpr(ret_expr) => Some(vec![NodeType::Node(ret_expr.syntax().clone())]),
252
253 Expr::CallExpr(_)
254 | Expr::Literal(_)
255 | Expr::TupleExpr(_)
256 | Expr::ArrayExpr(_)
257 | Expr::ParenExpr(_)
258 | Expr::PathExpr(_)
259 | Expr::RecordExpr(_)
260 | Expr::IndexExpr(_)
261 | Expr::MethodCallExpr(_)
262 | Expr::AwaitExpr(_)
263 | Expr::CastExpr(_)
264 | Expr::RefExpr(_)
265 | Expr::PrefixExpr(_)
266 | Expr::RangeExpr(_)
267 | Expr::BinExpr(_)
268 | Expr::MacroCall(_)
269 | Expr::BoxExpr(_) => Some(vec![NodeType::Leaf(expr.clone())]),
270 _ => None,
271 }
272}
273
274#[cfg(test)]
275mod tests {
276 use crate::tests::{check_assist, check_assist_not_applicable};
277
278 use super::*;
279
280 #[test]
281 fn change_return_type_to_result_simple() {
282 check_assist(
283 change_return_type_to_result,
284 r#"fn foo() -> i3<|>2 {
285 let test = "test";
286 return 42i32;
287 }"#,
288 r#"fn foo() -> Result<i32, ${0:_}> {
289 let test = "test";
290 return Ok(42i32);
291 }"#,
292 );
293 }
294
295 #[test]
296 fn change_return_type_to_result_simple_closure() {
297 check_assist(
298 change_return_type_to_result,
299 r#"fn foo() {
300 || -> i32<|> {
301 let test = "test";
302 return 42i32;
303 };
304 }"#,
305 r#"fn foo() {
306 || -> Result<i32, ${0:_}> {
307 let test = "test";
308 return Ok(42i32);
309 };
310 }"#,
311 );
312 }
313
314 #[test]
315 fn change_return_type_to_result_simple_return_type_bad_cursor() {
316 check_assist_not_applicable(
317 change_return_type_to_result,
318 r#"fn foo() -> i32 {
319 let test = "test";<|>
320 return 42i32;
321 }"#,
322 );
323 }
324
325 #[test]
326 fn change_return_type_to_result_simple_return_type_bad_cursor_closure() {
327 check_assist_not_applicable(
328 change_return_type_to_result,
329 r#"fn foo() {
330 || -> i32 {
331 let test = "test";<|>
332 return 42i32;
333 };
334 }"#,
335 );
336 }
337
338 #[test]
339 fn change_return_type_to_result_closure_non_block() {
340 check_assist_not_applicable(
341 change_return_type_to_result,
342 r#"fn foo() {
343 || -> i<|>32 3;
344 }"#,
345 );
346 }
347
348 #[test]
349 fn change_return_type_to_result_simple_return_type_already_result_std() {
350 check_assist_not_applicable(
351 change_return_type_to_result,
352 r#"fn foo() -> std::result::Result<i32<|>, String> {
353 let test = "test";
354 return 42i32;
355 }"#,
356 );
357 }
358
359 #[test]
360 fn change_return_type_to_result_simple_return_type_already_result() {
361 mark::check!(change_return_type_to_result_simple_return_type_already_result);
362 check_assist_not_applicable(
363 change_return_type_to_result,
364 r#"fn foo() -> Result<i32<|>, String> {
365 let test = "test";
366 return 42i32;
367 }"#,
368 );
369 }
370
371 #[test]
372 fn change_return_type_to_result_simple_return_type_already_result_closure() {
373 check_assist_not_applicable(
374 change_return_type_to_result,
375 r#"fn foo() {
376 || -> Result<i32<|>, String> {
377 let test = "test";
378 return 42i32;
379 };
380 }"#,
381 );
382 }
383
384 #[test]
385 fn change_return_type_to_result_simple_with_cursor() {
386 check_assist(
387 change_return_type_to_result,
388 r#"fn foo() -> <|>i32 {
389 let test = "test";
390 return 42i32;
391 }"#,
392 r#"fn foo() -> Result<i32, ${0:_}> {
393 let test = "test";
394 return Ok(42i32);
395 }"#,
396 );
397 }
398
399 #[test]
400 fn change_return_type_to_result_simple_with_tail() {
401 check_assist(
402 change_return_type_to_result,
403 r#"fn foo() -><|> i32 {
404 let test = "test";
405 42i32
406 }"#,
407 r#"fn foo() -> Result<i32, ${0:_}> {
408 let test = "test";
409 Ok(42i32)
410 }"#,
411 );
412 }
413
414 #[test]
415 fn change_return_type_to_result_simple_with_tail_closure() {
416 check_assist(
417 change_return_type_to_result,
418 r#"fn foo() {
419 || -><|> i32 {
420 let test = "test";
421 42i32
422 };
423 }"#,
424 r#"fn foo() {
425 || -> Result<i32, ${0:_}> {
426 let test = "test";
427 Ok(42i32)
428 };
429 }"#,
430 );
431 }
432
433 #[test]
434 fn change_return_type_to_result_simple_with_tail_only() {
435 check_assist(
436 change_return_type_to_result,
437 r#"fn foo() -> i32<|> {
438 42i32
439 }"#,
440 r#"fn foo() -> Result<i32, ${0:_}> {
441 Ok(42i32)
442 }"#,
443 );
444 }
445
446 #[test]
447 fn change_return_type_to_result_simple_with_tail_block_like() {
448 check_assist(
449 change_return_type_to_result,
450 r#"fn foo() -> i32<|> {
451 if true {
452 42i32
453 } else {
454 24i32
455 }
456 }"#,
457 r#"fn foo() -> Result<i32, ${0:_}> {
458 if true {
459 Ok(42i32)
460 } else {
461 Ok(24i32)
462 }
463 }"#,
464 );
465 }
466
467 #[test]
468 fn change_return_type_to_result_simple_without_block_closure() {
469 check_assist(
470 change_return_type_to_result,
471 r#"fn foo() {
472 || -> i32<|> {
473 if true {
474 42i32
475 } else {
476 24i32
477 }
478 };
479 }"#,
480 r#"fn foo() {
481 || -> Result<i32, ${0:_}> {
482 if true {
483 Ok(42i32)
484 } else {
485 Ok(24i32)
486 }
487 };
488 }"#,
489 );
490 }
491
492 #[test]
493 fn change_return_type_to_result_simple_with_nested_if() {
494 check_assist(
495 change_return_type_to_result,
496 r#"fn foo() -> i32<|> {
497 if true {
498 if false {
499 1
500 } else {
501 2
502 }
503 } else {
504 24i32
505 }
506 }"#,
507 r#"fn foo() -> Result<i32, ${0:_}> {
508 if true {
509 if false {
510 Ok(1)
511 } else {
512 Ok(2)
513 }
514 } else {
515 Ok(24i32)
516 }
517 }"#,
518 );
519 }
520
521 #[test]
522 fn change_return_type_to_result_simple_with_await() {
523 check_assist(
524 change_return_type_to_result,
525 r#"async fn foo() -> i<|>32 {
526 if true {
527 if false {
528 1.await
529 } else {
530 2.await
531 }
532 } else {
533 24i32.await
534 }
535 }"#,
536 r#"async fn foo() -> Result<i32, ${0:_}> {
537 if true {
538 if false {
539 Ok(1.await)
540 } else {
541 Ok(2.await)
542 }
543 } else {
544 Ok(24i32.await)
545 }
546 }"#,
547 );
548 }
549
550 #[test]
551 fn change_return_type_to_result_simple_with_array() {
552 check_assist(
553 change_return_type_to_result,
554 r#"fn foo() -> [i32;<|> 3] {
555 [1, 2, 3]
556 }"#,
557 r#"fn foo() -> Result<[i32; 3], ${0:_}> {
558 Ok([1, 2, 3])
559 }"#,
560 );
561 }
562
563 #[test]
564 fn change_return_type_to_result_simple_with_cast() {
565 check_assist(
566 change_return_type_to_result,
567 r#"fn foo() -<|>> i32 {
568 if true {
569 if false {
570 1 as i32
571 } else {
572 2 as i32
573 }
574 } else {
575 24 as i32
576 }
577 }"#,
578 r#"fn foo() -> Result<i32, ${0:_}> {
579 if true {
580 if false {
581 Ok(1 as i32)
582 } else {
583 Ok(2 as i32)
584 }
585 } else {
586 Ok(24 as i32)
587 }
588 }"#,
589 );
590 }
591
592 #[test]
593 fn change_return_type_to_result_simple_with_tail_block_like_match() {
594 check_assist(
595 change_return_type_to_result,
596 r#"fn foo() -> i32<|> {
597 let my_var = 5;
598 match my_var {
599 5 => 42i32,
600 _ => 24i32,
601 }
602 }"#,
603 r#"fn foo() -> Result<i32, ${0:_}> {
604 let my_var = 5;
605 match my_var {
606 5 => Ok(42i32),
607 _ => Ok(24i32),
608 }
609 }"#,
610 );
611 }
612
613 #[test]
614 fn change_return_type_to_result_simple_with_loop_with_tail() {
615 check_assist(
616 change_return_type_to_result,
617 r#"fn foo() -> i32<|> {
618 let my_var = 5;
619 loop {
620 println!("test");
621 5
622 }
623
624 my_var
625 }"#,
626 r#"fn foo() -> Result<i32, ${0:_}> {
627 let my_var = 5;
628 loop {
629 println!("test");
630 5
631 }
632
633 Ok(my_var)
634 }"#,
635 );
636 }
637
638 #[test]
639 fn change_return_type_to_result_simple_with_loop_in_let_stmt() {
640 check_assist(
641 change_return_type_to_result,
642 r#"fn foo() -> i32<|> {
643 let my_var = let x = loop {
644 break 1;
645 };
646
647 my_var
648 }"#,
649 r#"fn foo() -> Result<i32, ${0:_}> {
650 let my_var = let x = loop {
651 break 1;
652 };
653
654 Ok(my_var)
655 }"#,
656 );
657 }
658
659 #[test]
660 fn change_return_type_to_result_simple_with_tail_block_like_match_return_expr() {
661 check_assist(
662 change_return_type_to_result,
663 r#"fn foo() -> i32<|> {
664 let my_var = 5;
665 let res = match my_var {
666 5 => 42i32,
667 _ => return 24i32,
668 };
669
670 res
671 }"#,
672 r#"fn foo() -> Result<i32, ${0:_}> {
673 let my_var = 5;
674 let res = match my_var {
675 5 => 42i32,
676 _ => return Ok(24i32),
677 };
678
679 Ok(res)
680 }"#,
681 );
682
683 check_assist(
684 change_return_type_to_result,
685 r#"fn foo() -> i32<|> {
686 let my_var = 5;
687 let res = if my_var == 5 {
688 42i32
689 } else {
690 return 24i32;
691 };
692
693 res
694 }"#,
695 r#"fn foo() -> Result<i32, ${0:_}> {
696 let my_var = 5;
697 let res = if my_var == 5 {
698 42i32
699 } else {
700 return Ok(24i32);
701 };
702
703 Ok(res)
704 }"#,
705 );
706 }
707
708 #[test]
709 fn change_return_type_to_result_simple_with_tail_block_like_match_deeper() {
710 check_assist(
711 change_return_type_to_result,
712 r#"fn foo() -> i32<|> {
713 let my_var = 5;
714 match my_var {
715 5 => {
716 if true {
717 42i32
718 } else {
719 25i32
720 }
721 },
722 _ => {
723 let test = "test";
724 if test == "test" {
725 return bar();
726 }
727 53i32
728 },
729 }
730 }"#,
731 r#"fn foo() -> Result<i32, ${0:_}> {
732 let my_var = 5;
733 match my_var {
734 5 => {
735 if true {
736 Ok(42i32)
737 } else {
738 Ok(25i32)
739 }
740 },
741 _ => {
742 let test = "test";
743 if test == "test" {
744 return Ok(bar());
745 }
746 Ok(53i32)
747 },
748 }
749 }"#,
750 );
751 }
752
753 #[test]
754 fn change_return_type_to_result_simple_with_tail_block_like_early_return() {
755 check_assist(
756 change_return_type_to_result,
757 r#"fn foo() -> i<|>32 {
758 let test = "test";
759 if test == "test" {
760 return 24i32;
761 }
762 53i32
763 }"#,
764 r#"fn foo() -> Result<i32, ${0:_}> {
765 let test = "test";
766 if test == "test" {
767 return Ok(24i32);
768 }
769 Ok(53i32)
770 }"#,
771 );
772 }
773
774 #[test]
775 fn change_return_type_to_result_simple_with_closure() {
776 check_assist(
777 change_return_type_to_result,
778 r#"fn foo(the_field: u32) -><|> u32 {
779 let true_closure = || {
780 return true;
781 };
782 if the_field < 5 {
783 let mut i = 0;
784
785
786 if true_closure() {
787 return 99;
788 } else {
789 return 0;
790 }
791 }
792
793 the_field
794 }"#,
795 r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> {
796 let true_closure = || {
797 return true;
798 };
799 if the_field < 5 {
800 let mut i = 0;
801
802
803 if true_closure() {
804 return Ok(99);
805 } else {
806 return Ok(0);
807 }
808 }
809
810 Ok(the_field)
811 }"#,
812 );
813
814 check_assist(
815 change_return_type_to_result,
816 r#"fn foo(the_field: u32) -> u32<|> {
817 let true_closure = || {
818 return true;
819 };
820 if the_field < 5 {
821 let mut i = 0;
822
823
824 if true_closure() {
825 return 99;
826 } else {
827 return 0;
828 }
829 }
830 let t = None;
831
832 t.unwrap_or_else(|| the_field)
833 }"#,
834 r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> {
835 let true_closure = || {
836 return true;
837 };
838 if the_field < 5 {
839 let mut i = 0;
840
841
842 if true_closure() {
843 return Ok(99);
844 } else {
845 return Ok(0);
846 }
847 }
848 let t = None;
849
850 Ok(t.unwrap_or_else(|| the_field))
851 }"#,
852 );
853 }
854
855 #[test]
856 fn change_return_type_to_result_simple_with_weird_forms() {
857 check_assist(
858 change_return_type_to_result,
859 r#"fn foo() -> i32<|> {
860 let test = "test";
861 if test == "test" {
862 return 24i32;
863 }
864 let mut i = 0;
865 loop {
866 if i == 1 {
867 break 55;
868 }
869 i += 1;
870 }
871 }"#,
872 r#"fn foo() -> Result<i32, ${0:_}> {
873 let test = "test";
874 if test == "test" {
875 return Ok(24i32);
876 }
877 let mut i = 0;
878 loop {
879 if i == 1 {
880 break Ok(55);
881 }
882 i += 1;
883 }
884 }"#,
885 );
886
887 check_assist(
888 change_return_type_to_result,
889 r#"fn foo() -> i32<|> {
890 let test = "test";
891 if test == "test" {
892 return 24i32;
893 }
894 let mut i = 0;
895 loop {
896 loop {
897 if i == 1 {
898 break 55;
899 }
900 i += 1;
901 }
902 }
903 }"#,
904 r#"fn foo() -> Result<i32, ${0:_}> {
905 let test = "test";
906 if test == "test" {
907 return Ok(24i32);
908 }
909 let mut i = 0;
910 loop {
911 loop {
912 if i == 1 {
913 break Ok(55);
914 }
915 i += 1;
916 }
917 }
918 }"#,
919 );
920
921 check_assist(
922 change_return_type_to_result,
923 r#"fn foo() -> i3<|>2 {
924 let test = "test";
925 let other = 5;
926 if test == "test" {
927 let res = match other {
928 5 => 43,
929 _ => return 56,
930 };
931 }
932 let mut i = 0;
933 loop {
934 loop {
935 if i == 1 {
936 break 55;
937 }
938 i += 1;
939 }
940 }
941 }"#,
942 r#"fn foo() -> Result<i32, ${0:_}> {
943 let test = "test";
944 let other = 5;
945 if test == "test" {
946 let res = match other {
947 5 => 43,
948 _ => return Ok(56),
949 };
950 }
951 let mut i = 0;
952 loop {
953 loop {
954 if i == 1 {
955 break Ok(55);
956 }
957 i += 1;
958 }
959 }
960 }"#,
961 );
962
963 check_assist(
964 change_return_type_to_result,
965 r#"fn foo(the_field: u32) -> u32<|> {
966 if the_field < 5 {
967 let mut i = 0;
968 loop {
969 if i > 5 {
970 return 55u32;
971 }
972 i += 3;
973 }
974
975 match i {
976 5 => return 99,
977 _ => return 0,
978 };
979 }
980
981 the_field
982 }"#,
983 r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> {
984 if the_field < 5 {
985 let mut i = 0;
986 loop {
987 if i > 5 {
988 return Ok(55u32);
989 }
990 i += 3;
991 }
992
993 match i {
994 5 => return Ok(99),
995 _ => return Ok(0),
996 };
997 }
998
999 Ok(the_field)
1000 }"#,
1001 );
1002
1003 check_assist(
1004 change_return_type_to_result,
1005 r#"fn foo(the_field: u32) -> u3<|>2 {
1006 if the_field < 5 {
1007 let mut i = 0;
1008
1009 match i {
1010 5 => return 99,
1011 _ => return 0,
1012 }
1013 }
1014
1015 the_field
1016 }"#,
1017 r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> {
1018 if the_field < 5 {
1019 let mut i = 0;
1020
1021 match i {
1022 5 => return Ok(99),
1023 _ => return Ok(0),
1024 }
1025 }
1026
1027 Ok(the_field)
1028 }"#,
1029 );
1030
1031 check_assist(
1032 change_return_type_to_result,
1033 r#"fn foo(the_field: u32) -> u32<|> {
1034 if the_field < 5 {
1035 let mut i = 0;
1036
1037 if i == 5 {
1038 return 99
1039 } else {
1040 return 0
1041 }
1042 }
1043
1044 the_field
1045 }"#,
1046 r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> {
1047 if the_field < 5 {
1048 let mut i = 0;
1049
1050 if i == 5 {
1051 return Ok(99)
1052 } else {
1053 return Ok(0)
1054 }
1055 }
1056
1057 Ok(the_field)
1058 }"#,
1059 );
1060
1061 check_assist(
1062 change_return_type_to_result,
1063 r#"fn foo(the_field: u32) -> <|>u32 {
1064 if the_field < 5 {
1065 let mut i = 0;
1066
1067 if i == 5 {
1068 return 99;
1069 } else {
1070 return 0;
1071 }
1072 }
1073
1074 the_field
1075 }"#,
1076 r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> {
1077 if the_field < 5 {
1078 let mut i = 0;
1079
1080 if i == 5 {
1081 return Ok(99);
1082 } else {
1083 return Ok(0);
1084 }
1085 }
1086
1087 Ok(the_field)
1088 }"#,
1089 );
1090 }
1091}
diff --git a/crates/assists/src/handlers/add_custom_impl.rs b/crates/assists/src/handlers/replace_derive_with_manual_impl.rs
index c13493fd8..82625516c 100644
--- a/crates/assists/src/handlers/add_custom_impl.rs
+++ b/crates/assists/src/handlers/replace_derive_with_manual_impl.rs
@@ -16,24 +16,31 @@ use crate::{
16 AssistId, AssistKind, 16 AssistId, AssistKind,
17}; 17};
18 18
19// Assist: add_custom_impl 19// Assist: replace_derive_with_manual_impl
20// 20//
21// Adds impl block for derived trait. 21// Converts a `derive` impl into a manual one.
22// 22//
23// ``` 23// ```
24// # trait Debug { fn fmt(&self, f: &mut Formatter) -> Result<()>; }
24// #[derive(Deb<|>ug, Display)] 25// #[derive(Deb<|>ug, Display)]
25// struct S; 26// struct S;
26// ``` 27// ```
27// -> 28// ->
28// ``` 29// ```
30// # trait Debug { fn fmt(&self, f: &mut Formatter) -> Result<()>; }
29// #[derive(Display)] 31// #[derive(Display)]
30// struct S; 32// struct S;
31// 33//
32// impl Debug for S { 34// impl Debug for S {
33// $0 35// fn fmt(&self, f: &mut Formatter) -> Result<()> {
36// ${0:todo!()}
37// }
34// } 38// }
35// ``` 39// ```
36pub(crate) fn add_custom_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 40pub(crate) fn replace_derive_with_manual_impl(
41 acc: &mut Assists,
42 ctx: &AssistContext,
43) -> Option<()> {
37 let attr = ctx.find_node_at_offset::<ast::Attr>()?; 44 let attr = ctx.find_node_at_offset::<ast::Attr>()?;
38 45
39 let attr_name = attr 46 let attr_name = attr
@@ -90,43 +97,49 @@ fn add_assist(
90) -> Option<()> { 97) -> Option<()> {
91 let target = attr.syntax().text_range(); 98 let target = attr.syntax().text_range();
92 let input = attr.token_tree()?; 99 let input = attr.token_tree()?;
93 let label = format!("Add custom impl `{}` for `{}`", trait_path, annotated_name); 100 let label = format!("Convert to manual `impl {} for {}`", trait_path, annotated_name);
94 let trait_name = trait_path.segment().and_then(|seg| seg.name_ref())?; 101 let trait_name = trait_path.segment().and_then(|seg| seg.name_ref())?;
95 102
96 acc.add(AssistId("add_custom_impl", AssistKind::Refactor), label, target, |builder| { 103 acc.add(
97 let impl_def_with_items = 104 AssistId("replace_derive_with_manual_impl", AssistKind::Refactor),
98 impl_def_from_trait(&ctx.sema, annotated_name, trait_, trait_path); 105 label,
99 update_attribute(builder, &input, &trait_name, &attr); 106 target,
100 match (ctx.config.snippet_cap, impl_def_with_items) { 107 |builder| {
101 (None, _) => builder.insert( 108 let impl_def_with_items =
102 insert_pos, 109 impl_def_from_trait(&ctx.sema, annotated_name, trait_, trait_path);
103 format!("\n\nimpl {} for {} {{\n\n}}", trait_path, annotated_name), 110 update_attribute(builder, &input, &trait_name, &attr);
104 ), 111 match (ctx.config.snippet_cap, impl_def_with_items) {
105 (Some(cap), None) => builder.insert_snippet( 112 (None, _) => builder.insert(
106 cap, 113 insert_pos,
107 insert_pos, 114 format!("\n\nimpl {} for {} {{\n\n}}", trait_path, annotated_name),
108 format!("\n\nimpl {} for {} {{\n $0\n}}", trait_path, annotated_name), 115 ),
109 ), 116 (Some(cap), None) => builder.insert_snippet(
110 (Some(cap), Some((impl_def, first_assoc_item))) => { 117 cap,
111 let mut cursor = Cursor::Before(first_assoc_item.syntax()); 118 insert_pos,
112 let placeholder; 119 format!("\n\nimpl {} for {} {{\n $0\n}}", trait_path, annotated_name),
113 if let ast::AssocItem::Fn(ref func) = first_assoc_item { 120 ),
114 if let Some(m) = func.syntax().descendants().find_map(ast::MacroCall::cast) { 121 (Some(cap), Some((impl_def, first_assoc_item))) => {
115 if m.syntax().text() == "todo!()" { 122 let mut cursor = Cursor::Before(first_assoc_item.syntax());
116 placeholder = m; 123 let placeholder;
117 cursor = Cursor::Replace(placeholder.syntax()); 124 if let ast::AssocItem::Fn(ref func) = first_assoc_item {
125 if let Some(m) = func.syntax().descendants().find_map(ast::MacroCall::cast)
126 {
127 if m.syntax().text() == "todo!()" {
128 placeholder = m;
129 cursor = Cursor::Replace(placeholder.syntax());
130 }
118 } 131 }
119 } 132 }
120 }
121 133
122 builder.insert_snippet( 134 builder.insert_snippet(
123 cap, 135 cap,
124 insert_pos, 136 insert_pos,
125 format!("\n\n{}", render_snippet(cap, impl_def.syntax(), cursor)), 137 format!("\n\n{}", render_snippet(cap, impl_def.syntax(), cursor)),
126 ) 138 )
127 } 139 }
128 }; 140 };
129 }) 141 },
142 )
130} 143}
131 144
132fn impl_def_from_trait( 145fn impl_def_from_trait(
@@ -192,7 +205,7 @@ mod tests {
192 #[test] 205 #[test]
193 fn add_custom_impl_debug() { 206 fn add_custom_impl_debug() {
194 check_assist( 207 check_assist(
195 add_custom_impl, 208 replace_derive_with_manual_impl,
196 " 209 "
197mod fmt { 210mod fmt {
198 pub struct Error; 211 pub struct Error;
@@ -233,7 +246,7 @@ impl fmt::Debug for Foo {
233 #[test] 246 #[test]
234 fn add_custom_impl_all() { 247 fn add_custom_impl_all() {
235 check_assist( 248 check_assist(
236 add_custom_impl, 249 replace_derive_with_manual_impl,
237 " 250 "
238mod foo { 251mod foo {
239 pub trait Bar { 252 pub trait Bar {
@@ -282,7 +295,7 @@ impl foo::Bar for Foo {
282 #[test] 295 #[test]
283 fn add_custom_impl_for_unique_input() { 296 fn add_custom_impl_for_unique_input() {
284 check_assist( 297 check_assist(
285 add_custom_impl, 298 replace_derive_with_manual_impl,
286 " 299 "
287#[derive(Debu<|>g)] 300#[derive(Debu<|>g)]
288struct Foo { 301struct Foo {
@@ -304,7 +317,7 @@ impl Debug for Foo {
304 #[test] 317 #[test]
305 fn add_custom_impl_for_with_visibility_modifier() { 318 fn add_custom_impl_for_with_visibility_modifier() {
306 check_assist( 319 check_assist(
307 add_custom_impl, 320 replace_derive_with_manual_impl,
308 " 321 "
309#[derive(Debug<|>)] 322#[derive(Debug<|>)]
310pub struct Foo { 323pub struct Foo {
@@ -326,7 +339,7 @@ impl Debug for Foo {
326 #[test] 339 #[test]
327 fn add_custom_impl_when_multiple_inputs() { 340 fn add_custom_impl_when_multiple_inputs() {
328 check_assist( 341 check_assist(
329 add_custom_impl, 342 replace_derive_with_manual_impl,
330 " 343 "
331#[derive(Display, Debug<|>, Serialize)] 344#[derive(Display, Debug<|>, Serialize)]
332struct Foo {} 345struct Foo {}
@@ -345,7 +358,7 @@ impl Debug for Foo {
345 #[test] 358 #[test]
346 fn test_ignore_derive_macro_without_input() { 359 fn test_ignore_derive_macro_without_input() {
347 check_assist_not_applicable( 360 check_assist_not_applicable(
348 add_custom_impl, 361 replace_derive_with_manual_impl,
349 " 362 "
350#[derive(<|>)] 363#[derive(<|>)]
351struct Foo {} 364struct Foo {}
@@ -356,7 +369,7 @@ struct Foo {}
356 #[test] 369 #[test]
357 fn test_ignore_if_cursor_on_param() { 370 fn test_ignore_if_cursor_on_param() {
358 check_assist_not_applicable( 371 check_assist_not_applicable(
359 add_custom_impl, 372 replace_derive_with_manual_impl,
360 " 373 "
361#[derive<|>(Debug)] 374#[derive<|>(Debug)]
362struct Foo {} 375struct Foo {}
@@ -364,7 +377,7 @@ struct Foo {}
364 ); 377 );
365 378
366 check_assist_not_applicable( 379 check_assist_not_applicable(
367 add_custom_impl, 380 replace_derive_with_manual_impl,
368 " 381 "
369#[derive(Debug)<|>] 382#[derive(Debug)<|>]
370struct Foo {} 383struct Foo {}
@@ -375,7 +388,7 @@ struct Foo {}
375 #[test] 388 #[test]
376 fn test_ignore_if_not_derive() { 389 fn test_ignore_if_not_derive() {
377 check_assist_not_applicable( 390 check_assist_not_applicable(
378 add_custom_impl, 391 replace_derive_with_manual_impl,
379 " 392 "
380#[allow(non_camel_<|>case_types)] 393#[allow(non_camel_<|>case_types)]
381struct Foo {} 394struct Foo {}
diff --git a/crates/assists/src/handlers/wrap_return_type_in_result.rs b/crates/assists/src/handlers/wrap_return_type_in_result.rs
new file mode 100644
index 000000000..59e5debb1
--- /dev/null
+++ b/crates/assists/src/handlers/wrap_return_type_in_result.rs
@@ -0,0 +1,1158 @@
1use std::iter;
2
3use syntax::{
4 ast::{self, make, BlockExpr, Expr, LoopBodyOwner},
5 match_ast, AstNode, SyntaxNode,
6};
7use test_utils::mark;
8
9use crate::{AssistContext, AssistId, AssistKind, Assists};
10
11// Assist: wrap_return_type_in_result
12//
13// Wrap the function's return type into Result.
14//
15// ```
16// fn foo() -> i32<|> { 42i32 }
17// ```
18// ->
19// ```
20// fn foo() -> Result<i32, ${0:_}> { Ok(42i32) }
21// ```
22pub(crate) fn wrap_return_type_in_result(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
23 let ret_type = ctx.find_node_at_offset::<ast::RetType>()?;
24 let parent = ret_type.syntax().parent()?;
25 let block_expr = match_ast! {
26 match parent {
27 ast::Fn(func) => func.body()?,
28 ast::ClosureExpr(closure) => match closure.body()? {
29 Expr::BlockExpr(block) => block,
30 // closures require a block when a return type is specified
31 _ => return None,
32 },
33 _ => return None,
34 }
35 };
36
37 let type_ref = &ret_type.ty()?;
38 let ret_type_str = type_ref.syntax().text().to_string();
39 let first_part_ret_type = ret_type_str.splitn(2, '<').next();
40 if let Some(ret_type_first_part) = first_part_ret_type {
41 if ret_type_first_part.ends_with("Result") {
42 mark::hit!(wrap_return_type_in_result_simple_return_type_already_result);
43 return None;
44 }
45 }
46
47 acc.add(
48 AssistId("wrap_return_type_in_result", AssistKind::RefactorRewrite),
49 "Wrap return type in Result",
50 type_ref.syntax().text_range(),
51 |builder| {
52 let mut tail_return_expr_collector = TailReturnCollector::new();
53 tail_return_expr_collector.collect_jump_exprs(&block_expr, false);
54 tail_return_expr_collector.collect_tail_exprs(&block_expr);
55
56 for ret_expr_arg in tail_return_expr_collector.exprs_to_wrap {
57 let ok_wrapped = make::expr_call(
58 make::expr_path(make::path_unqualified(make::path_segment(make::name_ref(
59 "Ok",
60 )))),
61 make::arg_list(iter::once(ret_expr_arg.clone())),
62 );
63 builder.replace_ast(ret_expr_arg, ok_wrapped);
64 }
65
66 match ctx.config.snippet_cap {
67 Some(cap) => {
68 let snippet = format!("Result<{}, ${{0:_}}>", type_ref);
69 builder.replace_snippet(cap, type_ref.syntax().text_range(), snippet)
70 }
71 None => builder
72 .replace(type_ref.syntax().text_range(), format!("Result<{}, _>", type_ref)),
73 }
74 },
75 )
76}
77
78struct TailReturnCollector {
79 exprs_to_wrap: Vec<ast::Expr>,
80}
81
82impl TailReturnCollector {
83 fn new() -> Self {
84 Self { exprs_to_wrap: vec![] }
85 }
86 /// Collect all`return` expression
87 fn collect_jump_exprs(&mut self, block_expr: &BlockExpr, collect_break: bool) {
88 let statements = block_expr.statements();
89 for stmt in statements {
90 let expr = match &stmt {
91 ast::Stmt::ExprStmt(stmt) => stmt.expr(),
92 ast::Stmt::LetStmt(stmt) => stmt.initializer(),
93 ast::Stmt::Item(_) => continue,
94 };
95 if let Some(expr) = &expr {
96 self.handle_exprs(expr, collect_break);
97 }
98 }
99
100 // Browse tail expressions for each block
101 if let Some(expr) = block_expr.expr() {
102 if let Some(last_exprs) = get_tail_expr_from_block(&expr) {
103 for last_expr in last_exprs {
104 let last_expr = match last_expr {
105 NodeType::Node(expr) => expr,
106 NodeType::Leaf(expr) => expr.syntax().clone(),
107 };
108
109 if let Some(last_expr) = Expr::cast(last_expr.clone()) {
110 self.handle_exprs(&last_expr, collect_break);
111 } else if let Some(expr_stmt) = ast::Stmt::cast(last_expr) {
112 let expr_stmt = match &expr_stmt {
113 ast::Stmt::ExprStmt(stmt) => stmt.expr(),
114 ast::Stmt::LetStmt(stmt) => stmt.initializer(),
115 ast::Stmt::Item(_) => None,
116 };
117 if let Some(expr) = &expr_stmt {
118 self.handle_exprs(expr, collect_break);
119 }
120 }
121 }
122 }
123 }
124 }
125
126 fn handle_exprs(&mut self, expr: &Expr, collect_break: bool) {
127 match expr {
128 Expr::BlockExpr(block_expr) => {
129 self.collect_jump_exprs(&block_expr, collect_break);
130 }
131 Expr::ReturnExpr(ret_expr) => {
132 if let Some(ret_expr_arg) = &ret_expr.expr() {
133 self.exprs_to_wrap.push(ret_expr_arg.clone());
134 }
135 }
136 Expr::BreakExpr(break_expr) if collect_break => {
137 if let Some(break_expr_arg) = &break_expr.expr() {
138 self.exprs_to_wrap.push(break_expr_arg.clone());
139 }
140 }
141 Expr::IfExpr(if_expr) => {
142 for block in if_expr.blocks() {
143 self.collect_jump_exprs(&block, collect_break);
144 }
145 }
146 Expr::LoopExpr(loop_expr) => {
147 if let Some(block_expr) = loop_expr.loop_body() {
148 self.collect_jump_exprs(&block_expr, collect_break);
149 }
150 }
151 Expr::ForExpr(for_expr) => {
152 if let Some(block_expr) = for_expr.loop_body() {
153 self.collect_jump_exprs(&block_expr, collect_break);
154 }
155 }
156 Expr::WhileExpr(while_expr) => {
157 if let Some(block_expr) = while_expr.loop_body() {
158 self.collect_jump_exprs(&block_expr, collect_break);
159 }
160 }
161 Expr::MatchExpr(match_expr) => {
162 if let Some(arm_list) = match_expr.match_arm_list() {
163 arm_list.arms().filter_map(|match_arm| match_arm.expr()).for_each(|expr| {
164 self.handle_exprs(&expr, collect_break);
165 });
166 }
167 }
168 _ => {}
169 }
170 }
171
172 fn collect_tail_exprs(&mut self, block: &BlockExpr) {
173 if let Some(expr) = block.expr() {
174 self.handle_exprs(&expr, true);
175 self.fetch_tail_exprs(&expr);
176 }
177 }
178
179 fn fetch_tail_exprs(&mut self, expr: &Expr) {
180 if let Some(exprs) = get_tail_expr_from_block(expr) {
181 for node_type in &exprs {
182 match node_type {
183 NodeType::Leaf(expr) => {
184 self.exprs_to_wrap.push(expr.clone());
185 }
186 NodeType::Node(expr) => {
187 if let Some(last_expr) = Expr::cast(expr.clone()) {
188 self.fetch_tail_exprs(&last_expr);
189 }
190 }
191 }
192 }
193 }
194 }
195}
196
197#[derive(Debug)]
198enum NodeType {
199 Leaf(ast::Expr),
200 Node(SyntaxNode),
201}
202
203/// Get a tail expression inside a block
204fn get_tail_expr_from_block(expr: &Expr) -> Option<Vec<NodeType>> {
205 match expr {
206 Expr::IfExpr(if_expr) => {
207 let mut nodes = vec![];
208 for block in if_expr.blocks() {
209 if let Some(block_expr) = block.expr() {
210 if let Some(tail_exprs) = get_tail_expr_from_block(&block_expr) {
211 nodes.extend(tail_exprs);
212 }
213 } else if let Some(last_expr) = block.syntax().last_child() {
214 nodes.push(NodeType::Node(last_expr));
215 } else {
216 nodes.push(NodeType::Node(block.syntax().clone()));
217 }
218 }
219 Some(nodes)
220 }
221 Expr::LoopExpr(loop_expr) => {
222 loop_expr.syntax().last_child().map(|lc| vec![NodeType::Node(lc)])
223 }
224 Expr::ForExpr(for_expr) => {
225 for_expr.syntax().last_child().map(|lc| vec![NodeType::Node(lc)])
226 }
227 Expr::WhileExpr(while_expr) => {
228 while_expr.syntax().last_child().map(|lc| vec![NodeType::Node(lc)])
229 }
230 Expr::BlockExpr(block_expr) => {
231 block_expr.expr().map(|lc| vec![NodeType::Node(lc.syntax().clone())])
232 }
233 Expr::MatchExpr(match_expr) => {
234 let arm_list = match_expr.match_arm_list()?;
235 let arms: Vec<NodeType> = arm_list
236 .arms()
237 .filter_map(|match_arm| match_arm.expr())
238 .map(|expr| match expr {
239 Expr::ReturnExpr(ret_expr) => NodeType::Node(ret_expr.syntax().clone()),
240 Expr::BreakExpr(break_expr) => NodeType::Node(break_expr.syntax().clone()),
241 _ => match expr.syntax().last_child() {
242 Some(last_expr) => NodeType::Node(last_expr),
243 None => NodeType::Node(expr.syntax().clone()),
244 },
245 })
246 .collect();
247
248 Some(arms)
249 }
250 Expr::BreakExpr(expr) => expr.expr().map(|e| vec![NodeType::Leaf(e)]),
251 Expr::ReturnExpr(ret_expr) => Some(vec![NodeType::Node(ret_expr.syntax().clone())]),
252
253 Expr::CallExpr(_)
254 | Expr::Literal(_)
255 | Expr::TupleExpr(_)
256 | Expr::ArrayExpr(_)
257 | Expr::ParenExpr(_)
258 | Expr::PathExpr(_)
259 | Expr::RecordExpr(_)
260 | Expr::IndexExpr(_)
261 | Expr::MethodCallExpr(_)
262 | Expr::AwaitExpr(_)
263 | Expr::CastExpr(_)
264 | Expr::RefExpr(_)
265 | Expr::PrefixExpr(_)
266 | Expr::RangeExpr(_)
267 | Expr::BinExpr(_)
268 | Expr::MacroCall(_)
269 | Expr::BoxExpr(_) => Some(vec![NodeType::Leaf(expr.clone())]),
270 _ => None,
271 }
272}
273
274#[cfg(test)]
275mod tests {
276 use crate::tests::{check_assist, check_assist_not_applicable};
277
278 use super::*;
279
280 #[test]
281 fn wrap_return_type_in_result_simple() {
282 check_assist(
283 wrap_return_type_in_result,
284 r#"
285fn foo() -> i3<|>2 {
286 let test = "test";
287 return 42i32;
288}
289"#,
290 r#"
291fn foo() -> Result<i32, ${0:_}> {
292 let test = "test";
293 return Ok(42i32);
294}
295"#,
296 );
297 }
298
299 #[test]
300 fn wrap_return_type_in_result_simple_closure() {
301 check_assist(
302 wrap_return_type_in_result,
303 r#"
304fn foo() {
305 || -> i32<|> {
306 let test = "test";
307 return 42i32;
308 };
309}
310"#,
311 r#"
312fn foo() {
313 || -> Result<i32, ${0:_}> {
314 let test = "test";
315 return Ok(42i32);
316 };
317}
318"#,
319 );
320 }
321
322 #[test]
323 fn wrap_return_type_in_result_simple_return_type_bad_cursor() {
324 check_assist_not_applicable(
325 wrap_return_type_in_result,
326 r#"
327fn foo() -> i32 {
328 let test = "test";<|>
329 return 42i32;
330}
331"#,
332 );
333 }
334
335 #[test]
336 fn wrap_return_type_in_result_simple_return_type_bad_cursor_closure() {
337 check_assist_not_applicable(
338 wrap_return_type_in_result,
339 r#"
340fn foo() {
341 || -> i32 {
342 let test = "test";<|>
343 return 42i32;
344 };
345}
346"#,
347 );
348 }
349
350 #[test]
351 fn wrap_return_type_in_result_closure_non_block() {
352 check_assist_not_applicable(wrap_return_type_in_result, r#"fn foo() { || -> i<|>32 3; }"#);
353 }
354
355 #[test]
356 fn wrap_return_type_in_result_simple_return_type_already_result_std() {
357 check_assist_not_applicable(
358 wrap_return_type_in_result,
359 r#"
360fn foo() -> std::result::Result<i32<|>, String> {
361 let test = "test";
362 return 42i32;
363}
364"#,
365 );
366 }
367
368 #[test]
369 fn wrap_return_type_in_result_simple_return_type_already_result() {
370 mark::check!(wrap_return_type_in_result_simple_return_type_already_result);
371 check_assist_not_applicable(
372 wrap_return_type_in_result,
373 r#"
374fn foo() -> Result<i32<|>, String> {
375 let test = "test";
376 return 42i32;
377}
378"#,
379 );
380 }
381
382 #[test]
383 fn wrap_return_type_in_result_simple_return_type_already_result_closure() {
384 check_assist_not_applicable(
385 wrap_return_type_in_result,
386 r#"
387fn foo() {
388 || -> Result<i32<|>, String> {
389 let test = "test";
390 return 42i32;
391 };
392}
393"#,
394 );
395 }
396
397 #[test]
398 fn wrap_return_type_in_result_simple_with_cursor() {
399 check_assist(
400 wrap_return_type_in_result,
401 r#"
402fn foo() -> <|>i32 {
403 let test = "test";
404 return 42i32;
405}
406"#,
407 r#"
408fn foo() -> Result<i32, ${0:_}> {
409 let test = "test";
410 return Ok(42i32);
411}
412"#,
413 );
414 }
415
416 #[test]
417 fn wrap_return_type_in_result_simple_with_tail() {
418 check_assist(
419 wrap_return_type_in_result,
420 r#"
421fn foo() -><|> i32 {
422 let test = "test";
423 42i32
424}
425"#,
426 r#"
427fn foo() -> Result<i32, ${0:_}> {
428 let test = "test";
429 Ok(42i32)
430}
431"#,
432 );
433 }
434
435 #[test]
436 fn wrap_return_type_in_result_simple_with_tail_closure() {
437 check_assist(
438 wrap_return_type_in_result,
439 r#"
440fn foo() {
441 || -><|> i32 {
442 let test = "test";
443 42i32
444 };
445}
446"#,
447 r#"
448fn foo() {
449 || -> Result<i32, ${0:_}> {
450 let test = "test";
451 Ok(42i32)
452 };
453}
454"#,
455 );
456 }
457
458 #[test]
459 fn wrap_return_type_in_result_simple_with_tail_only() {
460 check_assist(
461 wrap_return_type_in_result,
462 r#"fn foo() -> i32<|> { 42i32 }"#,
463 r#"fn foo() -> Result<i32, ${0:_}> { Ok(42i32) }"#,
464 );
465 }
466
467 #[test]
468 fn wrap_return_type_in_result_simple_with_tail_block_like() {
469 check_assist(
470 wrap_return_type_in_result,
471 r#"
472fn foo() -> i32<|> {
473 if true {
474 42i32
475 } else {
476 24i32
477 }
478}
479"#,
480 r#"
481fn foo() -> Result<i32, ${0:_}> {
482 if true {
483 Ok(42i32)
484 } else {
485 Ok(24i32)
486 }
487}
488"#,
489 );
490 }
491
492 #[test]
493 fn wrap_return_type_in_result_simple_without_block_closure() {
494 check_assist(
495 wrap_return_type_in_result,
496 r#"
497fn foo() {
498 || -> i32<|> {
499 if true {
500 42i32
501 } else {
502 24i32
503 }
504 };
505}
506"#,
507 r#"
508fn foo() {
509 || -> Result<i32, ${0:_}> {
510 if true {
511 Ok(42i32)
512 } else {
513 Ok(24i32)
514 }
515 };
516}
517"#,
518 );
519 }
520
521 #[test]
522 fn wrap_return_type_in_result_simple_with_nested_if() {
523 check_assist(
524 wrap_return_type_in_result,
525 r#"
526fn foo() -> i32<|> {
527 if true {
528 if false {
529 1
530 } else {
531 2
532 }
533 } else {
534 24i32
535 }
536}
537"#,
538 r#"
539fn foo() -> Result<i32, ${0:_}> {
540 if true {
541 if false {
542 Ok(1)
543 } else {
544 Ok(2)
545 }
546 } else {
547 Ok(24i32)
548 }
549}
550"#,
551 );
552 }
553
554 #[test]
555 fn wrap_return_type_in_result_simple_with_await() {
556 check_assist(
557 wrap_return_type_in_result,
558 r#"
559async fn foo() -> i<|>32 {
560 if true {
561 if false {
562 1.await
563 } else {
564 2.await
565 }
566 } else {
567 24i32.await
568 }
569}
570"#,
571 r#"
572async fn foo() -> Result<i32, ${0:_}> {
573 if true {
574 if false {
575 Ok(1.await)
576 } else {
577 Ok(2.await)
578 }
579 } else {
580 Ok(24i32.await)
581 }
582}
583"#,
584 );
585 }
586
587 #[test]
588 fn wrap_return_type_in_result_simple_with_array() {
589 check_assist(
590 wrap_return_type_in_result,
591 r#"fn foo() -> [i32;<|> 3] { [1, 2, 3] }"#,
592 r#"fn foo() -> Result<[i32; 3], ${0:_}> { Ok([1, 2, 3]) }"#,
593 );
594 }
595
596 #[test]
597 fn wrap_return_type_in_result_simple_with_cast() {
598 check_assist(
599 wrap_return_type_in_result,
600 r#"
601fn foo() -<|>> i32 {
602 if true {
603 if false {
604 1 as i32
605 } else {
606 2 as i32
607 }
608 } else {
609 24 as i32
610 }
611}
612"#,
613 r#"
614fn foo() -> Result<i32, ${0:_}> {
615 if true {
616 if false {
617 Ok(1 as i32)
618 } else {
619 Ok(2 as i32)
620 }
621 } else {
622 Ok(24 as i32)
623 }
624}
625"#,
626 );
627 }
628
629 #[test]
630 fn wrap_return_type_in_result_simple_with_tail_block_like_match() {
631 check_assist(
632 wrap_return_type_in_result,
633 r#"
634fn foo() -> i32<|> {
635 let my_var = 5;
636 match my_var {
637 5 => 42i32,
638 _ => 24i32,
639 }
640}
641"#,
642 r#"
643fn foo() -> Result<i32, ${0:_}> {
644 let my_var = 5;
645 match my_var {
646 5 => Ok(42i32),
647 _ => Ok(24i32),
648 }
649}
650"#,
651 );
652 }
653
654 #[test]
655 fn wrap_return_type_in_result_simple_with_loop_with_tail() {
656 check_assist(
657 wrap_return_type_in_result,
658 r#"
659fn foo() -> i32<|> {
660 let my_var = 5;
661 loop {
662 println!("test");
663 5
664 }
665 my_var
666}
667"#,
668 r#"
669fn foo() -> Result<i32, ${0:_}> {
670 let my_var = 5;
671 loop {
672 println!("test");
673 5
674 }
675 Ok(my_var)
676}
677"#,
678 );
679 }
680
681 #[test]
682 fn wrap_return_type_in_result_simple_with_loop_in_let_stmt() {
683 check_assist(
684 wrap_return_type_in_result,
685 r#"
686fn foo() -> i32<|> {
687 let my_var = let x = loop {
688 break 1;
689 };
690 my_var
691}
692"#,
693 r#"
694fn foo() -> Result<i32, ${0:_}> {
695 let my_var = let x = loop {
696 break 1;
697 };
698 Ok(my_var)
699}
700"#,
701 );
702 }
703
704 #[test]
705 fn wrap_return_type_in_result_simple_with_tail_block_like_match_return_expr() {
706 check_assist(
707 wrap_return_type_in_result,
708 r#"
709fn foo() -> i32<|> {
710 let my_var = 5;
711 let res = match my_var {
712 5 => 42i32,
713 _ => return 24i32,
714 };
715 res
716}
717"#,
718 r#"
719fn foo() -> Result<i32, ${0:_}> {
720 let my_var = 5;
721 let res = match my_var {
722 5 => 42i32,
723 _ => return Ok(24i32),
724 };
725 Ok(res)
726}
727"#,
728 );
729
730 check_assist(
731 wrap_return_type_in_result,
732 r#"
733fn foo() -> i32<|> {
734 let my_var = 5;
735 let res = if my_var == 5 {
736 42i32
737 } else {
738 return 24i32;
739 };
740 res
741}
742"#,
743 r#"
744fn foo() -> Result<i32, ${0:_}> {
745 let my_var = 5;
746 let res = if my_var == 5 {
747 42i32
748 } else {
749 return Ok(24i32);
750 };
751 Ok(res)
752}
753"#,
754 );
755 }
756
757 #[test]
758 fn wrap_return_type_in_result_simple_with_tail_block_like_match_deeper() {
759 check_assist(
760 wrap_return_type_in_result,
761 r#"
762fn foo() -> i32<|> {
763 let my_var = 5;
764 match my_var {
765 5 => {
766 if true {
767 42i32
768 } else {
769 25i32
770 }
771 },
772 _ => {
773 let test = "test";
774 if test == "test" {
775 return bar();
776 }
777 53i32
778 },
779 }
780}
781"#,
782 r#"
783fn foo() -> Result<i32, ${0:_}> {
784 let my_var = 5;
785 match my_var {
786 5 => {
787 if true {
788 Ok(42i32)
789 } else {
790 Ok(25i32)
791 }
792 },
793 _ => {
794 let test = "test";
795 if test == "test" {
796 return Ok(bar());
797 }
798 Ok(53i32)
799 },
800 }
801}
802"#,
803 );
804 }
805
806 #[test]
807 fn wrap_return_type_in_result_simple_with_tail_block_like_early_return() {
808 check_assist(
809 wrap_return_type_in_result,
810 r#"
811fn foo() -> i<|>32 {
812 let test = "test";
813 if test == "test" {
814 return 24i32;
815 }
816 53i32
817}
818"#,
819 r#"
820fn foo() -> Result<i32, ${0:_}> {
821 let test = "test";
822 if test == "test" {
823 return Ok(24i32);
824 }
825 Ok(53i32)
826}
827"#,
828 );
829 }
830
831 #[test]
832 fn wrap_return_type_in_result_simple_with_closure() {
833 check_assist(
834 wrap_return_type_in_result,
835 r#"
836fn foo(the_field: u32) -><|> u32 {
837 let true_closure = || { return true; };
838 if the_field < 5 {
839 let mut i = 0;
840 if true_closure() {
841 return 99;
842 } else {
843 return 0;
844 }
845 }
846 the_field
847}
848"#,
849 r#"
850fn foo(the_field: u32) -> Result<u32, ${0:_}> {
851 let true_closure = || { return true; };
852 if the_field < 5 {
853 let mut i = 0;
854 if true_closure() {
855 return Ok(99);
856 } else {
857 return Ok(0);
858 }
859 }
860 Ok(the_field)
861}
862"#,
863 );
864
865 check_assist(
866 wrap_return_type_in_result,
867 r#"
868 fn foo(the_field: u32) -> u32<|> {
869 let true_closure = || {
870 return true;
871 };
872 if the_field < 5 {
873 let mut i = 0;
874
875
876 if true_closure() {
877 return 99;
878 } else {
879 return 0;
880 }
881 }
882 let t = None;
883
884 t.unwrap_or_else(|| the_field)
885 }
886 "#,
887 r#"
888 fn foo(the_field: u32) -> Result<u32, ${0:_}> {
889 let true_closure = || {
890 return true;
891 };
892 if the_field < 5 {
893 let mut i = 0;
894
895
896 if true_closure() {
897 return Ok(99);
898 } else {
899 return Ok(0);
900 }
901 }
902 let t = None;
903
904 Ok(t.unwrap_or_else(|| the_field))
905 }
906 "#,
907 );
908 }
909
910 #[test]
911 fn wrap_return_type_in_result_simple_with_weird_forms() {
912 check_assist(
913 wrap_return_type_in_result,
914 r#"
915fn foo() -> i32<|> {
916 let test = "test";
917 if test == "test" {
918 return 24i32;
919 }
920 let mut i = 0;
921 loop {
922 if i == 1 {
923 break 55;
924 }
925 i += 1;
926 }
927}
928"#,
929 r#"
930fn foo() -> Result<i32, ${0:_}> {
931 let test = "test";
932 if test == "test" {
933 return Ok(24i32);
934 }
935 let mut i = 0;
936 loop {
937 if i == 1 {
938 break Ok(55);
939 }
940 i += 1;
941 }
942}
943"#,
944 );
945
946 check_assist(
947 wrap_return_type_in_result,
948 r#"
949fn foo() -> i32<|> {
950 let test = "test";
951 if test == "test" {
952 return 24i32;
953 }
954 let mut i = 0;
955 loop {
956 loop {
957 if i == 1 {
958 break 55;
959 }
960 i += 1;
961 }
962 }
963}
964"#,
965 r#"
966fn foo() -> Result<i32, ${0:_}> {
967 let test = "test";
968 if test == "test" {
969 return Ok(24i32);
970 }
971 let mut i = 0;
972 loop {
973 loop {
974 if i == 1 {
975 break Ok(55);
976 }
977 i += 1;
978 }
979 }
980}
981"#,
982 );
983
984 check_assist(
985 wrap_return_type_in_result,
986 r#"
987fn foo() -> i3<|>2 {
988 let test = "test";
989 let other = 5;
990 if test == "test" {
991 let res = match other {
992 5 => 43,
993 _ => return 56,
994 };
995 }
996 let mut i = 0;
997 loop {
998 loop {
999 if i == 1 {
1000 break 55;
1001 }
1002 i += 1;
1003 }
1004 }
1005}
1006"#,
1007 r#"
1008fn foo() -> Result<i32, ${0:_}> {
1009 let test = "test";
1010 let other = 5;
1011 if test == "test" {
1012 let res = match other {
1013 5 => 43,
1014 _ => return Ok(56),
1015 };
1016 }
1017 let mut i = 0;
1018 loop {
1019 loop {
1020 if i == 1 {
1021 break Ok(55);
1022 }
1023 i += 1;
1024 }
1025 }
1026}
1027"#,
1028 );
1029
1030 check_assist(
1031 wrap_return_type_in_result,
1032 r#"
1033fn foo(the_field: u32) -> u32<|> {
1034 if the_field < 5 {
1035 let mut i = 0;
1036 loop {
1037 if i > 5 {
1038 return 55u32;
1039 }
1040 i += 3;
1041 }
1042 match i {
1043 5 => return 99,
1044 _ => return 0,
1045 };
1046 }
1047 the_field
1048}
1049"#,
1050 r#"
1051fn foo(the_field: u32) -> Result<u32, ${0:_}> {
1052 if the_field < 5 {
1053 let mut i = 0;
1054 loop {
1055 if i > 5 {
1056 return Ok(55u32);
1057 }
1058 i += 3;
1059 }
1060 match i {
1061 5 => return Ok(99),
1062 _ => return Ok(0),
1063 };
1064 }
1065 Ok(the_field)
1066}
1067"#,
1068 );
1069
1070 check_assist(
1071 wrap_return_type_in_result,
1072 r#"
1073fn foo(the_field: u32) -> u3<|>2 {
1074 if the_field < 5 {
1075 let mut i = 0;
1076 match i {
1077 5 => return 99,
1078 _ => return 0,
1079 }
1080 }
1081 the_field
1082}
1083"#,
1084 r#"
1085fn foo(the_field: u32) -> Result<u32, ${0:_}> {
1086 if the_field < 5 {
1087 let mut i = 0;
1088 match i {
1089 5 => return Ok(99),
1090 _ => return Ok(0),
1091 }
1092 }
1093 Ok(the_field)
1094}
1095"#,
1096 );
1097
1098 check_assist(
1099 wrap_return_type_in_result,
1100 r#"
1101fn foo(the_field: u32) -> u32<|> {
1102 if the_field < 5 {
1103 let mut i = 0;
1104 if i == 5 {
1105 return 99
1106 } else {
1107 return 0
1108 }
1109 }
1110 the_field
1111}
1112"#,
1113 r#"
1114fn foo(the_field: u32) -> Result<u32, ${0:_}> {
1115 if the_field < 5 {
1116 let mut i = 0;
1117 if i == 5 {
1118 return Ok(99)
1119 } else {
1120 return Ok(0)
1121 }
1122 }
1123 Ok(the_field)
1124}
1125"#,
1126 );
1127
1128 check_assist(
1129 wrap_return_type_in_result,
1130 r#"
1131fn foo(the_field: u32) -> <|>u32 {
1132 if the_field < 5 {
1133 let mut i = 0;
1134 if i == 5 {
1135 return 99;
1136 } else {
1137 return 0;
1138 }
1139 }
1140 the_field
1141}
1142"#,
1143 r#"
1144fn foo(the_field: u32) -> Result<u32, ${0:_}> {
1145 if the_field < 5 {
1146 let mut i = 0;
1147 if i == 5 {
1148 return Ok(99);
1149 } else {
1150 return Ok(0);
1151 }
1152 }
1153 Ok(the_field)
1154}
1155"#,
1156 );
1157 }
1158}
diff --git a/crates/assists/src/lib.rs b/crates/assists/src/lib.rs
index af88b3437..e8d81b33d 100644
--- a/crates/assists/src/lib.rs
+++ b/crates/assists/src/lib.rs
@@ -120,13 +120,11 @@ mod handlers {
120 120
121 pub(crate) type Handler = fn(&mut Assists, &AssistContext) -> Option<()>; 121 pub(crate) type Handler = fn(&mut Assists, &AssistContext) -> Option<()>;
122 122
123 mod add_custom_impl;
124 mod add_explicit_type; 123 mod add_explicit_type;
125 mod add_missing_impl_members; 124 mod add_missing_impl_members;
126 mod add_turbo_fish; 125 mod add_turbo_fish;
127 mod apply_demorgan; 126 mod apply_demorgan;
128 mod auto_import; 127 mod auto_import;
129 mod change_return_type_to_result;
130 mod change_visibility; 128 mod change_visibility;
131 mod convert_integer_literal; 129 mod convert_integer_literal;
132 mod early_return; 130 mod early_return;
@@ -157,6 +155,7 @@ mod handlers {
157 mod remove_mut; 155 mod remove_mut;
158 mod remove_unused_param; 156 mod remove_unused_param;
159 mod reorder_fields; 157 mod reorder_fields;
158 mod replace_derive_with_manual_impl;
160 mod replace_if_let_with_match; 159 mod replace_if_let_with_match;
161 mod replace_impl_trait_with_generic; 160 mod replace_impl_trait_with_generic;
162 mod replace_let_with_if_let; 161 mod replace_let_with_if_let;
@@ -165,16 +164,15 @@ mod handlers {
165 mod replace_unwrap_with_match; 164 mod replace_unwrap_with_match;
166 mod split_import; 165 mod split_import;
167 mod unwrap_block; 166 mod unwrap_block;
167 mod wrap_return_type_in_result;
168 168
169 pub(crate) fn all() -> &'static [Handler] { 169 pub(crate) fn all() -> &'static [Handler] {
170 &[ 170 &[
171 // These are alphabetic for the foolish consistency 171 // These are alphabetic for the foolish consistency
172 add_custom_impl::add_custom_impl,
173 add_explicit_type::add_explicit_type, 172 add_explicit_type::add_explicit_type,
174 add_turbo_fish::add_turbo_fish, 173 add_turbo_fish::add_turbo_fish,
175 apply_demorgan::apply_demorgan, 174 apply_demorgan::apply_demorgan,
176 auto_import::auto_import, 175 auto_import::auto_import,
177 change_return_type_to_result::change_return_type_to_result,
178 change_visibility::change_visibility, 176 change_visibility::change_visibility,
179 convert_integer_literal::convert_integer_literal, 177 convert_integer_literal::convert_integer_literal,
180 early_return::convert_to_guarded_return, 178 early_return::convert_to_guarded_return,
@@ -208,6 +206,7 @@ mod handlers {
208 remove_mut::remove_mut, 206 remove_mut::remove_mut,
209 remove_unused_param::remove_unused_param, 207 remove_unused_param::remove_unused_param,
210 reorder_fields::reorder_fields, 208 reorder_fields::reorder_fields,
209 replace_derive_with_manual_impl::replace_derive_with_manual_impl,
211 replace_if_let_with_match::replace_if_let_with_match, 210 replace_if_let_with_match::replace_if_let_with_match,
212 replace_impl_trait_with_generic::replace_impl_trait_with_generic, 211 replace_impl_trait_with_generic::replace_impl_trait_with_generic,
213 replace_let_with_if_let::replace_let_with_if_let, 212 replace_let_with_if_let::replace_let_with_if_let,
@@ -215,6 +214,7 @@ mod handlers {
215 replace_unwrap_with_match::replace_unwrap_with_match, 214 replace_unwrap_with_match::replace_unwrap_with_match,
216 split_import::split_import, 215 split_import::split_import,
217 unwrap_block::unwrap_block, 216 unwrap_block::unwrap_block,
217 wrap_return_type_in_result::wrap_return_type_in_result,
218 // These are manually sorted for better priorities 218 // These are manually sorted for better priorities
219 add_missing_impl_members::add_missing_impl_members, 219 add_missing_impl_members::add_missing_impl_members,
220 add_missing_impl_members::add_missing_default_members, 220 add_missing_impl_members::add_missing_default_members,
diff --git a/crates/assists/src/tests/generated.rs b/crates/assists/src/tests/generated.rs
index 168e1626a..dbf4f21aa 100644
--- a/crates/assists/src/tests/generated.rs
+++ b/crates/assists/src/tests/generated.rs
@@ -3,25 +3,6 @@
3use super::check_doc_test; 3use super::check_doc_test;
4 4
5#[test] 5#[test]
6fn doctest_add_custom_impl() {
7 check_doc_test(
8 "add_custom_impl",
9 r#####"
10#[derive(Deb<|>ug, Display)]
11struct S;
12"#####,
13 r#####"
14#[derive(Display)]
15struct S;
16
17impl Debug for S {
18 $0
19}
20"#####,
21 )
22}
23
24#[test]
25fn doctest_add_explicit_type() { 6fn doctest_add_explicit_type() {
26 check_doc_test( 7 check_doc_test(
27 "add_explicit_type", 8 "add_explicit_type",
@@ -178,19 +159,6 @@ pub mod std { pub mod collections { pub struct HashMap { } } }
178} 159}
179 160
180#[test] 161#[test]
181fn doctest_change_return_type_to_result() {
182 check_doc_test(
183 "change_return_type_to_result",
184 r#####"
185fn foo() -> i32<|> { 42i32 }
186"#####,
187 r#####"
188fn foo() -> Result<i32, ${0:_}> { Ok(42i32) }
189"#####,
190 )
191}
192
193#[test]
194fn doctest_change_visibility() { 162fn doctest_change_visibility() {
195 check_doc_test( 163 check_doc_test(
196 "change_visibility", 164 "change_visibility",
@@ -832,6 +800,29 @@ const test: Foo = Foo {foo: 1, bar: 0}
832} 800}
833 801
834#[test] 802#[test]
803fn doctest_replace_derive_with_manual_impl() {
804 check_doc_test(
805 "replace_derive_with_manual_impl",
806 r#####"
807trait Debug { fn fmt(&self, f: &mut Formatter) -> Result<()>; }
808#[derive(Deb<|>ug, Display)]
809struct S;
810"#####,
811 r#####"
812trait Debug { fn fmt(&self, f: &mut Formatter) -> Result<()>; }
813#[derive(Display)]
814struct S;
815
816impl Debug for S {
817 fn fmt(&self, f: &mut Formatter) -> Result<()> {
818 ${0:todo!()}
819 }
820}
821"#####,
822 )
823}
824
825#[test]
835fn doctest_replace_if_let_with_match() { 826fn doctest_replace_if_let_with_match() {
836 check_doc_test( 827 check_doc_test(
837 "replace_if_let_with_match", 828 "replace_if_let_with_match",
@@ -985,3 +976,16 @@ fn foo() {
985"#####, 976"#####,
986 ) 977 )
987} 978}
979
980#[test]
981fn doctest_wrap_return_type_in_result() {
982 check_doc_test(
983 "wrap_return_type_in_result",
984 r#####"
985fn foo() -> i32<|> { 42i32 }
986"#####,
987 r#####"
988fn foo() -> Result<i32, ${0:_}> { Ok(42i32) }
989"#####,
990 )
991}