1 use ide_db::helpers::{for_each_tail_expr, node_ext::walk_expr, FamousDefs};
2 use itertools::Itertools;
5 match_ast, AstNode, TextRange, TextSize,
8 use crate::{AssistContext, AssistId, AssistKind, Assists};
10 // Assist: unwrap_result_return_type
12 // Unwrap the function's return type.
15 // # //- minicore: result
16 // fn foo() -> Result<i32>$0 { Ok(42i32) }
20 // fn foo() -> i32 { 42i32 }
22 pub(crate) fn unwrap_result_return_type(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 body = match_ast! {
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
37 let type_ref = &ret_type.ty()?;
38 let ty = ctx.sema.resolve_type(type_ref).and_then(|ty| ty.as_adt());
40 FamousDefs(&ctx.sema, ctx.sema.scope(type_ref.syntax()).krate()).core_result_Result()?;
42 if !matches!(ty, Some(hir::Adt::Enum(ret_type)) if ret_type == result_enum) {
47 AssistId("unwrap_result_return_type", AssistKind::RefactorRewrite),
48 "Unwrap Result return type",
49 type_ref.syntax().text_range(),
51 let body = ast::Expr::BlockExpr(body);
53 let mut exprs_to_unwrap = Vec::new();
54 let tail_cb = &mut |e: &_| tail_cb_impl(&mut exprs_to_unwrap, e);
55 walk_expr(&body, &mut |expr| {
56 if let Expr::ReturnExpr(ret_expr) = expr {
57 if let Some(ret_expr_arg) = &ret_expr.expr() {
58 for_each_tail_expr(ret_expr_arg, tail_cb);
62 for_each_tail_expr(&body, tail_cb);
64 let mut is_unit_type = false;
65 if let Some((_, inner_type)) = type_ref.to_string().split_once('<') {
66 let inner_type = match inner_type.split_once(',') {
67 Some((success_inner_type, _)) => success_inner_type,
70 let new_ret_type = inner_type.strip_suffix('>').unwrap_or(inner_type);
71 if new_ret_type == "()" {
73 let text_range = TextRange::new(
74 ret_type.syntax().text_range().start(),
75 ret_type.syntax().text_range().end() + TextSize::from(1u32),
77 builder.delete(text_range)
80 type_ref.syntax().text_range(),
81 inner_type.strip_suffix('>').unwrap_or(inner_type),
86 for ret_expr_arg in exprs_to_unwrap {
87 let ret_expr_str = ret_expr_arg.to_string();
88 if ret_expr_str.starts_with("Ok(") || ret_expr_str.starts_with("Err(") {
89 let arg_list = ret_expr_arg.syntax().children().find_map(ast::ArgList::cast);
90 if let Some(arg_list) = arg_list {
92 match ret_expr_arg.syntax().prev_sibling_or_token() {
93 // Useful to delete the entire line without leaving trailing whitespaces
95 let new_range = TextRange::new(
96 whitespace.text_range().start(),
97 ret_expr_arg.syntax().text_range().end(),
99 builder.delete(new_range);
102 builder.delete(ret_expr_arg.syntax().text_range());
107 ret_expr_arg.syntax().text_range(),
108 arg_list.args().join(", "),
118 fn tail_cb_impl(acc: &mut Vec<ast::Expr>, e: &ast::Expr) {
120 Expr::BreakExpr(break_expr) => {
121 if let Some(break_expr_arg) = break_expr.expr() {
122 for_each_tail_expr(&break_expr_arg, &mut |e| tail_cb_impl(acc, e))
125 Expr::ReturnExpr(ret_expr) => {
126 if let Some(ret_expr_arg) = &ret_expr.expr() {
127 for_each_tail_expr(ret_expr_arg, &mut |e| tail_cb_impl(acc, e));
130 e => acc.push(e.clone()),
136 use crate::tests::{check_assist, check_assist_not_applicable};
141 fn unwrap_result_return_type_simple() {
143 unwrap_result_return_type,
146 fn foo() -> Result<i3$02> {
161 fn unwrap_result_return_type_unit_type() {
163 unwrap_result_return_type,
166 fn foo() -> Result<(), Box<dyn Error$0>> {
178 fn unwrap_result_return_type_ending_with_parent() {
180 unwrap_result_return_type,
183 fn foo() -> Result<i32, Box<dyn Error$0>> {
204 fn unwrap_return_type_break_split_tail() {
206 unwrap_result_return_type,
209 fn foo() -> Result<i3$02, String> {
234 fn unwrap_result_return_type_simple_closure() {
236 unwrap_result_return_type,
240 || -> Result<i32$0> {
258 fn unwrap_result_return_type_simple_return_type_bad_cursor() {
259 check_assist_not_applicable(
260 unwrap_result_return_type,
272 fn unwrap_result_return_type_simple_return_type_bad_cursor_closure() {
273 check_assist_not_applicable(
274 unwrap_result_return_type,
288 fn unwrap_result_return_type_closure_non_block() {
289 check_assist_not_applicable(
290 unwrap_result_return_type,
293 fn foo() { || -> i$032 3; }
299 fn unwrap_result_return_type_simple_return_type_already_not_result_std() {
300 check_assist_not_applicable(
301 unwrap_result_return_type,
313 fn unwrap_result_return_type_simple_return_type_already_not_result_closure() {
314 check_assist_not_applicable(
315 unwrap_result_return_type,
329 fn unwrap_result_return_type_simple_with_tail() {
331 unwrap_result_return_type,
334 fn foo() ->$0 Result<i32> {
349 fn unwrap_result_return_type_simple_with_tail_closure() {
351 unwrap_result_return_type,
355 || ->$0 Result<i32, String> {
373 fn unwrap_result_return_type_simple_with_tail_only() {
375 unwrap_result_return_type,
378 fn foo() -> Result<i32$0> { Ok(42i32) }
381 fn foo() -> i32 { 42i32 }
387 fn unwrap_result_return_type_simple_with_tail_block_like() {
389 unwrap_result_return_type,
392 fn foo() -> Result<i32>$0 {
413 fn unwrap_result_return_type_simple_without_block_closure() {
415 unwrap_result_return_type,
419 || -> Result<i32, String>$0 {
443 fn unwrap_result_return_type_simple_with_nested_if() {
445 unwrap_result_return_type,
448 fn foo() -> Result<i32>$0 {
477 fn unwrap_result_return_type_simple_with_await() {
479 unwrap_result_return_type,
482 async fn foo() -> Result<i$032> {
495 async fn foo() -> i32 {
511 fn unwrap_result_return_type_simple_with_array() {
513 unwrap_result_return_type,
516 fn foo() -> Result<[i32; 3]$0> { Ok([1, 2, 3]) }
519 fn foo() -> [i32; 3] { [1, 2, 3] }
525 fn unwrap_result_return_type_simple_with_cast() {
527 unwrap_result_return_type,
530 fn foo() -$0> Result<i32> {
559 fn unwrap_result_return_type_simple_with_tail_block_like_match() {
561 unwrap_result_return_type,
564 fn foo() -> Result<i32$0> {
585 fn unwrap_result_return_type_simple_with_loop_with_tail() {
587 unwrap_result_return_type,
590 fn foo() -> Result<i32$0> {
613 fn unwrap_result_return_type_simple_with_loop_in_let_stmt() {
615 unwrap_result_return_type,
618 fn foo() -> Result<i32$0> {
619 let my_var = let x = loop {
627 let my_var = let x = loop {
637 fn unwrap_result_return_type_simple_with_tail_block_like_match_return_expr() {
639 unwrap_result_return_type,
642 fn foo() -> Result<i32>$0 {
644 let res = match my_var {
646 _ => return Ok(24i32),
654 let res = match my_var {
664 unwrap_result_return_type,
667 fn foo() -> Result<i32$0> {
669 let res = if my_var == 5 {
680 let res = if my_var == 5 {
692 fn unwrap_result_return_type_simple_with_tail_block_like_match_deeper() {
694 unwrap_result_return_type,
697 fn foo() -> Result<i32$0> {
742 fn unwrap_result_return_type_simple_with_tail_block_like_early_return() {
744 unwrap_result_return_type,
747 fn foo() -> Result<i32$0> {
768 fn unwrap_result_return_type_simple_with_closure() {
770 unwrap_result_return_type,
773 fn foo(the_field: u32) -> Result<u32$0> {
774 let true_closure = || { return true; };
787 fn foo(the_field: u32) -> u32 {
788 let true_closure = || { return true; };
803 unwrap_result_return_type,
806 fn foo(the_field: u32) -> Result<u32$0> {
807 let true_closure = || {
822 Ok(t.unwrap_or_else(|| the_field))
826 fn foo(the_field: u32) -> u32 {
827 let true_closure = || {
842 t.unwrap_or_else(|| the_field)
849 fn unwrap_result_return_type_simple_with_weird_forms() {
851 unwrap_result_return_type,
854 fn foo() -> Result<i32$0> {
886 unwrap_result_return_type,
889 fn foo(the_field: u32) -> Result<u32$0> {
907 fn foo(the_field: u32) -> u32 {
927 unwrap_result_return_type,
930 fn foo(the_field: u32) -> Result<u32$0> {
942 fn foo(the_field: u32) -> u32 {
956 unwrap_result_return_type,
959 fn foo(the_field: u32) -> Result<u32$0> {
972 fn foo(the_field: u32) -> u32 {
987 unwrap_result_return_type,
990 fn foo(the_field: u32) -> Result<u3$02> {
1003 fn foo(the_field: u32) -> u32 {