3 use ide_db::helpers::{for_each_tail_expr, FamousDefs};
5 ast::{self, make, Expr},
9 use crate::{AssistContext, AssistId, AssistKind, Assists};
11 // Assist: wrap_return_type_in_result
13 // Wrap the function's return type into Result.
16 // # //- minicore: result
17 // fn foo() -> i32$0 { 42i32 }
21 // fn foo() -> Result<i32, ${0:_}> { Ok(42i32) }
23 pub(crate) fn wrap_return_type_in_result(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
24 let ret_type = ctx.find_node_at_offset::<ast::RetType>()?;
25 let parent = ret_type.syntax().parent()?;
26 let body = match_ast! {
28 ast::Fn(func) => func.body()?,
29 ast::ClosureExpr(closure) => match closure.body()? {
30 Expr::BlockExpr(block) => block,
31 // closures require a block when a return type is specified
38 let type_ref = &ret_type.ty()?;
39 let ty = ctx.sema.resolve_type(type_ref).and_then(|ty| ty.as_adt());
41 FamousDefs(&ctx.sema, ctx.sema.scope(type_ref.syntax()).krate()).core_result_Result()?;
43 if matches!(ty, Some(hir::Adt::Enum(ret_type)) if ret_type == result_enum) {
44 cov_mark::hit!(wrap_return_type_in_result_simple_return_type_already_result);
49 AssistId("wrap_return_type_in_result", AssistKind::RefactorRewrite),
50 "Wrap return type in Result",
51 type_ref.syntax().text_range(),
53 let body = ast::Expr::BlockExpr(body);
55 let mut exprs_to_wrap = Vec::new();
56 let tail_cb = &mut |e: &_| tail_cb_impl(&mut exprs_to_wrap, e);
57 body.walk(&mut |expr| {
58 if let Expr::ReturnExpr(ret_expr) = expr {
59 if let Some(ret_expr_arg) = &ret_expr.expr() {
60 for_each_tail_expr(ret_expr_arg, tail_cb);
64 for_each_tail_expr(&body, tail_cb);
66 for ret_expr_arg in exprs_to_wrap {
67 let ok_wrapped = make::expr_call(
68 make::expr_path(make::ext::ident_path("Ok")),
69 make::arg_list(iter::once(ret_expr_arg.clone())),
71 builder.replace_ast(ret_expr_arg, ok_wrapped);
74 match ctx.config.snippet_cap {
76 let snippet = format!("Result<{}, ${{0:_}}>", type_ref);
77 builder.replace_snippet(cap, type_ref.syntax().text_range(), snippet)
80 .replace(type_ref.syntax().text_range(), format!("Result<{}, _>", type_ref)),
86 fn tail_cb_impl(acc: &mut Vec<ast::Expr>, e: &ast::Expr) {
88 Expr::BreakExpr(break_expr) => {
89 if let Some(break_expr_arg) = break_expr.expr() {
90 for_each_tail_expr(&break_expr_arg, &mut |e| tail_cb_impl(acc, e))
93 Expr::ReturnExpr(ret_expr) => {
94 if let Some(ret_expr_arg) = &ret_expr.expr() {
95 for_each_tail_expr(ret_expr_arg, &mut |e| tail_cb_impl(acc, e));
98 e => acc.push(e.clone()),
104 use crate::tests::{check_assist, check_assist_not_applicable};
109 fn wrap_return_type_in_result_simple() {
111 wrap_return_type_in_result,
120 fn foo() -> Result<i32, ${0:_}> {
129 fn wrap_return_type_break_split_tail() {
131 wrap_return_type_in_result,
145 fn foo() -> Result<i32, ${0:_}> {
159 fn wrap_return_type_in_result_simple_closure() {
161 wrap_return_type_in_result,
173 || -> Result<i32, ${0:_}> {
183 fn wrap_return_type_in_result_simple_return_type_bad_cursor() {
184 check_assist_not_applicable(
185 wrap_return_type_in_result,
197 fn wrap_return_type_in_result_simple_return_type_bad_cursor_closure() {
198 check_assist_not_applicable(
199 wrap_return_type_in_result,
213 fn wrap_return_type_in_result_closure_non_block() {
214 check_assist_not_applicable(
215 wrap_return_type_in_result,
218 fn foo() { || -> i$032 3; }
224 fn wrap_return_type_in_result_simple_return_type_already_result_std() {
225 check_assist_not_applicable(
226 wrap_return_type_in_result,
229 fn foo() -> core::result::Result<i32$0, String> {
238 fn wrap_return_type_in_result_simple_return_type_already_result() {
239 cov_mark::check!(wrap_return_type_in_result_simple_return_type_already_result);
240 check_assist_not_applicable(
241 wrap_return_type_in_result,
244 fn foo() -> Result<i32$0, String> {
253 fn wrap_return_type_in_result_simple_return_type_already_result_closure() {
254 check_assist_not_applicable(
255 wrap_return_type_in_result,
259 || -> Result<i32$0, String> {
269 fn wrap_return_type_in_result_simple_with_cursor() {
271 wrap_return_type_in_result,
280 fn foo() -> Result<i32, ${0:_}> {
289 fn wrap_return_type_in_result_simple_with_tail() {
291 wrap_return_type_in_result,
300 fn foo() -> Result<i32, ${0:_}> {
309 fn wrap_return_type_in_result_simple_with_tail_closure() {
311 wrap_return_type_in_result,
323 || -> Result<i32, ${0:_}> {
333 fn wrap_return_type_in_result_simple_with_tail_only() {
335 wrap_return_type_in_result,
338 fn foo() -> i32$0 { 42i32 }
341 fn foo() -> Result<i32, ${0:_}> { Ok(42i32) }
347 fn wrap_return_type_in_result_simple_with_tail_block_like() {
349 wrap_return_type_in_result,
361 fn foo() -> Result<i32, ${0:_}> {
373 fn wrap_return_type_in_result_simple_without_block_closure() {
375 wrap_return_type_in_result,
390 || -> Result<i32, ${0:_}> {
403 fn wrap_return_type_in_result_simple_with_nested_if() {
405 wrap_return_type_in_result,
421 fn foo() -> Result<i32, ${0:_}> {
437 fn wrap_return_type_in_result_simple_with_await() {
439 wrap_return_type_in_result,
442 async fn foo() -> i$032 {
455 async fn foo() -> Result<i32, ${0:_}> {
471 fn wrap_return_type_in_result_simple_with_array() {
473 wrap_return_type_in_result,
476 fn foo() -> [i32;$0 3] { [1, 2, 3] }
479 fn foo() -> Result<[i32; 3], ${0:_}> { Ok([1, 2, 3]) }
485 fn wrap_return_type_in_result_simple_with_cast() {
487 wrap_return_type_in_result,
503 fn foo() -> Result<i32, ${0:_}> {
519 fn wrap_return_type_in_result_simple_with_tail_block_like_match() {
521 wrap_return_type_in_result,
533 fn foo() -> Result<i32, ${0:_}> {
545 fn wrap_return_type_in_result_simple_with_loop_with_tail() {
547 wrap_return_type_in_result,
560 fn foo() -> Result<i32, ${0:_}> {
573 fn wrap_return_type_in_result_simple_with_loop_in_let_stmt() {
575 wrap_return_type_in_result,
579 let my_var = let x = loop {
586 fn foo() -> Result<i32, ${0:_}> {
587 let my_var = let x = loop {
597 fn wrap_return_type_in_result_simple_with_tail_block_like_match_return_expr() {
599 wrap_return_type_in_result,
604 let res = match my_var {
612 fn foo() -> Result<i32, ${0:_}> {
614 let res = match my_var {
616 _ => return Ok(24i32),
624 wrap_return_type_in_result,
629 let res = if my_var == 5 {
638 fn foo() -> Result<i32, ${0:_}> {
640 let res = if my_var == 5 {
652 fn wrap_return_type_in_result_simple_with_tail_block_like_match_deeper() {
654 wrap_return_type_in_result,
678 fn foo() -> Result<i32, ${0:_}> {
702 fn wrap_return_type_in_result_simple_with_tail_block_like_early_return() {
704 wrap_return_type_in_result,
716 fn foo() -> Result<i32, ${0:_}> {
728 fn wrap_return_type_in_result_simple_with_closure() {
730 wrap_return_type_in_result,
733 fn foo(the_field: u32) ->$0 u32 {
734 let true_closure = || { return true; };
747 fn foo(the_field: u32) -> Result<u32, ${0:_}> {
748 let true_closure = || { return true; };
763 wrap_return_type_in_result,
766 fn foo(the_field: u32) -> u32$0 {
767 let true_closure = || {
782 t.unwrap_or_else(|| the_field)
786 fn foo(the_field: u32) -> Result<u32, ${0:_}> {
787 let true_closure = || {
802 Ok(t.unwrap_or_else(|| the_field))
809 fn wrap_return_type_in_result_simple_with_weird_forms() {
811 wrap_return_type_in_result,
829 fn foo() -> Result<i32, ${0:_}> {
846 wrap_return_type_in_result,
849 fn foo(the_field: u32) -> u32$0 {
867 fn foo(the_field: u32) -> Result<u32, ${0:_}> {
887 wrap_return_type_in_result,
890 fn foo(the_field: u32) -> u3$02 {
902 fn foo(the_field: u32) -> Result<u32, ${0:_}> {
916 wrap_return_type_in_result,
919 fn foo(the_field: u32) -> u32$0 {
932 fn foo(the_field: u32) -> Result<u32, ${0:_}> {
947 wrap_return_type_in_result,
950 fn foo(the_field: u32) -> $0u32 {
963 fn foo(the_field: u32) -> Result<u32, ${0:_}> {