diff --git a/crates/ide-assists/src/handlers/unwrap_result_return_type.rs b/crates/ide-assists/src/handlers/unwrap_result_return_type.rs deleted file mode 100644 index a1987247cb..0000000000 --- a/crates/ide-assists/src/handlers/unwrap_result_return_type.rs +++ /dev/null @@ -1,1115 +0,0 @@ -use ide_db::{ - famous_defs::FamousDefs, - syntax_helpers::node_ext::{for_each_tail_expr, walk_expr}, -}; -use itertools::Itertools; -use syntax::{ - ast::{self, Expr, HasGenericArgs}, - match_ast, AstNode, NodeOrToken, SyntaxKind, TextRange, -}; - -use crate::{AssistContext, AssistId, AssistKind, Assists}; - -// Assist: unwrap_result_return_type -// -// Unwrap the function's return type. -// -// ``` -// # //- minicore: result -// fn foo() -> Result$0 { Ok(42i32) } -// ``` -// -> -// ``` -// fn foo() -> i32 { 42i32 } -// ``` -pub(crate) fn unwrap_result_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { - let ret_type = ctx.find_node_at_offset::()?; - let parent = ret_type.syntax().parent()?; - let body = match_ast! { - match parent { - ast::Fn(func) => func.body()?, - ast::ClosureExpr(closure) => match closure.body()? { - Expr::BlockExpr(block) => block, - // closures require a block when a return type is specified - _ => return None, - }, - _ => return None, - } - }; - - let type_ref = &ret_type.ty()?; - let Some(hir::Adt::Enum(ret_enum)) = ctx.sema.resolve_type(type_ref)?.as_adt() else { - return None; - }; - let result_enum = - FamousDefs(&ctx.sema, ctx.sema.scope(type_ref.syntax())?.krate()).core_result_Result()?; - if ret_enum != result_enum { - return None; - } - - let ok_type = unwrap_result_type(type_ref)?; - - acc.add( - AssistId("unwrap_result_return_type", AssistKind::RefactorRewrite), - "Unwrap Result return type", - type_ref.syntax().text_range(), - |builder| { - let body = ast::Expr::BlockExpr(body); - - let mut exprs_to_unwrap = Vec::new(); - let tail_cb = &mut |e: &_| tail_cb_impl(&mut exprs_to_unwrap, e); - walk_expr(&body, &mut |expr| { - if let Expr::ReturnExpr(ret_expr) = expr { - if let Some(ret_expr_arg) = &ret_expr.expr() { - for_each_tail_expr(ret_expr_arg, tail_cb); - } - } - }); - for_each_tail_expr(&body, tail_cb); - - let is_unit_type = is_unit_type(&ok_type); - if is_unit_type { - let mut text_range = ret_type.syntax().text_range(); - - if let Some(NodeOrToken::Token(token)) = ret_type.syntax().next_sibling_or_token() { - if token.kind() == SyntaxKind::WHITESPACE { - text_range = TextRange::new(text_range.start(), token.text_range().end()); - } - } - - builder.delete(text_range); - } else { - builder.replace(type_ref.syntax().text_range(), ok_type.syntax().text()); - } - - for ret_expr_arg in exprs_to_unwrap { - let ret_expr_str = ret_expr_arg.to_string(); - if ret_expr_str.starts_with("Ok(") || ret_expr_str.starts_with("Err(") { - let arg_list = ret_expr_arg.syntax().children().find_map(ast::ArgList::cast); - if let Some(arg_list) = arg_list { - if is_unit_type { - match ret_expr_arg.syntax().prev_sibling_or_token() { - // Useful to delete the entire line without leaving trailing whitespaces - Some(whitespace) => { - let new_range = TextRange::new( - whitespace.text_range().start(), - ret_expr_arg.syntax().text_range().end(), - ); - builder.delete(new_range); - } - None => { - builder.delete(ret_expr_arg.syntax().text_range()); - } - } - } else { - builder.replace( - ret_expr_arg.syntax().text_range(), - arg_list.args().join(", "), - ); - } - } - } - } - }, - ) -} - -fn tail_cb_impl(acc: &mut Vec, e: &ast::Expr) { - match e { - Expr::BreakExpr(break_expr) => { - if let Some(break_expr_arg) = break_expr.expr() { - for_each_tail_expr(&break_expr_arg, &mut |e| tail_cb_impl(acc, e)) - } - } - Expr::ReturnExpr(_) => { - // all return expressions have already been handled by the walk loop - } - e => acc.push(e.clone()), - } -} - -// Tries to extract `T` from `Result`. -fn unwrap_result_type(ty: &ast::Type) -> Option { - let ast::Type::PathType(path_ty) = ty else { - return None; - }; - let path = path_ty.path()?; - let segment = path.first_segment()?; - let generic_arg_list = segment.generic_arg_list()?; - let generic_args: Vec<_> = generic_arg_list.generic_args().collect(); - let ast::GenericArg::TypeArg(ok_type) = generic_args.first()? else { - return None; - }; - ok_type.ty() -} - -fn is_unit_type(ty: &ast::Type) -> bool { - let ast::Type::TupleType(tuple) = ty else { return false }; - tuple.fields().next().is_none() -} - -#[cfg(test)] -mod tests { - use crate::tests::{check_assist, check_assist_not_applicable}; - - use super::*; - - #[test] - fn unwrap_result_return_type_simple() { - check_assist( - unwrap_result_return_type, - r#" -//- minicore: result -fn foo() -> Result { - let test = "test"; - return Ok(42i32); -} -"#, - r#" -fn foo() -> i32 { - let test = "test"; - return 42i32; -} -"#, - ); - } - - #[test] - fn unwrap_result_return_type_unit_type() { - check_assist( - unwrap_result_return_type, - r#" -//- minicore: result -fn foo() -> Result<(), Box> { - Ok(()) -} -"#, - r#" -fn foo() { -} -"#, - ); - - // Unformatted return type - check_assist( - unwrap_result_return_type, - r#" -//- minicore: result -fn foo() -> Result<(), Box>{ - Ok(()) -} -"#, - r#" -fn foo() { -} -"#, - ); - } - - #[test] - fn unwrap_result_return_type_ending_with_parent() { - check_assist( - unwrap_result_return_type, - r#" -//- minicore: result -fn foo() -> Result> { - if true { - Ok(42) - } else { - foo() - } -} -"#, - r#" -fn foo() -> i32 { - if true { - 42 - } else { - foo() - } -} -"#, - ); - } - - #[test] - fn unwrap_return_type_break_split_tail() { - check_assist( - unwrap_result_return_type, - r#" -//- minicore: result -fn foo() -> Result { - loop { - break if true { - Ok(1) - } else { - Ok(0) - }; - } -} -"#, - r#" -fn foo() -> i32 { - loop { - break if true { - 1 - } else { - 0 - }; - } -} -"#, - ); - } - - #[test] - fn unwrap_result_return_type_simple_closure() { - check_assist( - unwrap_result_return_type, - r#" -//- minicore: result -fn foo() { - || -> Result { - let test = "test"; - return Ok(42i32); - }; -} -"#, - r#" -fn foo() { - || -> i32 { - let test = "test"; - return 42i32; - }; -} -"#, - ); - } - - #[test] - fn unwrap_result_return_type_simple_return_type_bad_cursor() { - check_assist_not_applicable( - unwrap_result_return_type, - r#" -//- minicore: result -fn foo() -> i32 { - let test = "test";$0 - return 42i32; -} -"#, - ); - } - - #[test] - fn unwrap_result_return_type_simple_return_type_bad_cursor_closure() { - check_assist_not_applicable( - unwrap_result_return_type, - r#" -//- minicore: result -fn foo() { - || -> i32 { - let test = "test";$0 - return 42i32; - }; -} -"#, - ); - } - - #[test] - fn unwrap_result_return_type_closure_non_block() { - check_assist_not_applicable( - unwrap_result_return_type, - r#" -//- minicore: result -fn foo() { || -> i$032 3; } -"#, - ); - } - - #[test] - fn unwrap_result_return_type_simple_return_type_already_not_result_std() { - check_assist_not_applicable( - unwrap_result_return_type, - r#" -//- minicore: result -fn foo() -> i32$0 { - let test = "test"; - return 42i32; -} -"#, - ); - } - - #[test] - fn unwrap_result_return_type_simple_return_type_already_not_result_closure() { - check_assist_not_applicable( - unwrap_result_return_type, - r#" -//- minicore: result -fn foo() { - || -> i32$0 { - let test = "test"; - return 42i32; - }; -} -"#, - ); - } - - #[test] - fn unwrap_result_return_type_simple_with_tail() { - check_assist( - unwrap_result_return_type, - r#" -//- minicore: result -fn foo() ->$0 Result { - let test = "test"; - Ok(42i32) -} -"#, - r#" -fn foo() -> i32 { - let test = "test"; - 42i32 -} -"#, - ); - } - - #[test] - fn unwrap_result_return_type_simple_with_tail_closure() { - check_assist( - unwrap_result_return_type, - r#" -//- minicore: result -fn foo() { - || ->$0 Result { - let test = "test"; - Ok(42i32) - }; -} -"#, - r#" -fn foo() { - || -> i32 { - let test = "test"; - 42i32 - }; -} -"#, - ); - } - - #[test] - fn unwrap_result_return_type_simple_with_tail_only() { - check_assist( - unwrap_result_return_type, - r#" -//- minicore: result -fn foo() -> Result { Ok(42i32) } -"#, - r#" -fn foo() -> i32 { 42i32 } -"#, - ); - } - - #[test] - fn unwrap_result_return_type_simple_with_tail_block_like() { - check_assist( - unwrap_result_return_type, - r#" -//- minicore: result -fn foo() -> Result$0 { - if true { - Ok(42i32) - } else { - Ok(24i32) - } -} -"#, - r#" -fn foo() -> i32 { - if true { - 42i32 - } else { - 24i32 - } -} -"#, - ); - } - - #[test] - fn unwrap_result_return_type_simple_without_block_closure() { - check_assist( - unwrap_result_return_type, - r#" -//- minicore: result -fn foo() { - || -> Result$0 { - if true { - Ok(42i32) - } else { - Ok(24i32) - } - }; -} -"#, - r#" -fn foo() { - || -> i32 { - if true { - 42i32 - } else { - 24i32 - } - }; -} -"#, - ); - } - - #[test] - fn unwrap_result_return_type_simple_with_nested_if() { - check_assist( - unwrap_result_return_type, - r#" -//- minicore: result -fn foo() -> Result$0 { - if true { - if false { - Ok(1) - } else { - Ok(2) - } - } else { - Ok(24i32) - } -} -"#, - r#" -fn foo() -> i32 { - if true { - if false { - 1 - } else { - 2 - } - } else { - 24i32 - } -} -"#, - ); - } - - #[test] - fn unwrap_result_return_type_simple_with_await() { - check_assist( - unwrap_result_return_type, - r#" -//- minicore: result -async fn foo() -> Result { - if true { - if false { - Ok(1.await) - } else { - Ok(2.await) - } - } else { - Ok(24i32.await) - } -} -"#, - r#" -async fn foo() -> i32 { - if true { - if false { - 1.await - } else { - 2.await - } - } else { - 24i32.await - } -} -"#, - ); - } - - #[test] - fn unwrap_result_return_type_simple_with_array() { - check_assist( - unwrap_result_return_type, - r#" -//- minicore: result -fn foo() -> Result<[i32; 3]$0> { Ok([1, 2, 3]) } -"#, - r#" -fn foo() -> [i32; 3] { [1, 2, 3] } -"#, - ); - } - - #[test] - fn unwrap_result_return_type_simple_with_cast() { - check_assist( - unwrap_result_return_type, - r#" -//- minicore: result -fn foo() -$0> Result { - if true { - if false { - Ok(1 as i32) - } else { - Ok(2 as i32) - } - } else { - Ok(24 as i32) - } -} -"#, - r#" -fn foo() -> i32 { - if true { - if false { - 1 as i32 - } else { - 2 as i32 - } - } else { - 24 as i32 - } -} -"#, - ); - } - - #[test] - fn unwrap_result_return_type_simple_with_tail_block_like_match() { - check_assist( - unwrap_result_return_type, - r#" -//- minicore: result -fn foo() -> Result { - let my_var = 5; - match my_var { - 5 => Ok(42i32), - _ => Ok(24i32), - } -} -"#, - r#" -fn foo() -> i32 { - let my_var = 5; - match my_var { - 5 => 42i32, - _ => 24i32, - } -} -"#, - ); - } - - #[test] - fn unwrap_result_return_type_simple_with_loop_with_tail() { - check_assist( - unwrap_result_return_type, - r#" -//- minicore: result -fn foo() -> Result { - let my_var = 5; - loop { - println!("test"); - 5 - } - Ok(my_var) -} -"#, - r#" -fn foo() -> i32 { - let my_var = 5; - loop { - println!("test"); - 5 - } - my_var -} -"#, - ); - } - - #[test] - fn unwrap_result_return_type_simple_with_loop_in_let_stmt() { - check_assist( - unwrap_result_return_type, - r#" -//- minicore: result -fn foo() -> Result { - let my_var = let x = loop { - break 1; - }; - Ok(my_var) -} -"#, - r#" -fn foo() -> i32 { - let my_var = let x = loop { - break 1; - }; - my_var -} -"#, - ); - } - - #[test] - fn unwrap_result_return_type_simple_with_tail_block_like_match_return_expr() { - check_assist( - unwrap_result_return_type, - r#" -//- minicore: result -fn foo() -> Result$0 { - let my_var = 5; - let res = match my_var { - 5 => 42i32, - _ => return Ok(24i32), - }; - Ok(res) -} -"#, - r#" -fn foo() -> i32 { - let my_var = 5; - let res = match my_var { - 5 => 42i32, - _ => return 24i32, - }; - res -} -"#, - ); - - check_assist( - unwrap_result_return_type, - r#" -//- minicore: result -fn foo() -> Result { - let my_var = 5; - let res = if my_var == 5 { - 42i32 - } else { - return Ok(24i32); - }; - Ok(res) -} -"#, - r#" -fn foo() -> i32 { - let my_var = 5; - let res = if my_var == 5 { - 42i32 - } else { - return 24i32; - }; - res -} -"#, - ); - } - - #[test] - fn unwrap_result_return_type_simple_with_tail_block_like_match_deeper() { - check_assist( - unwrap_result_return_type, - r#" -//- minicore: result -fn foo() -> Result { - let my_var = 5; - match my_var { - 5 => { - if true { - Ok(42i32) - } else { - Ok(25i32) - } - }, - _ => { - let test = "test"; - if test == "test" { - return Ok(bar()); - } - Ok(53i32) - }, - } -} -"#, - r#" -fn foo() -> i32 { - let my_var = 5; - match my_var { - 5 => { - if true { - 42i32 - } else { - 25i32 - } - }, - _ => { - let test = "test"; - if test == "test" { - return bar(); - } - 53i32 - }, - } -} -"#, - ); - } - - #[test] - fn unwrap_result_return_type_simple_with_tail_block_like_early_return() { - check_assist( - unwrap_result_return_type, - r#" -//- minicore: result -fn foo() -> Result { - let test = "test"; - if test == "test" { - return Ok(24i32); - } - Ok(53i32) -} -"#, - r#" -fn foo() -> i32 { - let test = "test"; - if test == "test" { - return 24i32; - } - 53i32 -} -"#, - ); - } - - #[test] - fn wrap_return_in_tail_position() { - check_assist( - unwrap_result_return_type, - r#" -//- minicore: result -fn foo(num: i32) -> $0Result { - return Ok(num) -} -"#, - r#" -fn foo(num: i32) -> i32 { - return num -} -"#, - ); - } - - #[test] - fn unwrap_result_return_type_simple_with_closure() { - check_assist( - unwrap_result_return_type, - r#" -//- minicore: result -fn foo(the_field: u32) -> Result { - let true_closure = || { return true; }; - if the_field < 5 { - let mut i = 0; - if true_closure() { - return Ok(99); - } else { - return Ok(0); - } - } - Ok(the_field) -} -"#, - r#" -fn foo(the_field: u32) -> u32 { - let true_closure = || { return true; }; - if the_field < 5 { - let mut i = 0; - if true_closure() { - return 99; - } else { - return 0; - } - } - the_field -} -"#, - ); - - check_assist( - unwrap_result_return_type, - r#" -//- minicore: result -fn foo(the_field: u32) -> Result { - let true_closure = || { - return true; - }; - if the_field < 5 { - let mut i = 0; - - - if true_closure() { - return Ok(99); - } else { - return Ok(0); - } - } - let t = None; - - Ok(t.unwrap_or_else(|| the_field)) -} -"#, - r#" -fn foo(the_field: u32) -> u32 { - let true_closure = || { - return true; - }; - if the_field < 5 { - let mut i = 0; - - - if true_closure() { - return 99; - } else { - return 0; - } - } - let t = None; - - t.unwrap_or_else(|| the_field) -} -"#, - ); - } - - #[test] - fn unwrap_result_return_type_simple_with_weird_forms() { - check_assist( - unwrap_result_return_type, - r#" -//- minicore: result -fn foo() -> Result { - let test = "test"; - if test == "test" { - return Ok(24i32); - } - let mut i = 0; - loop { - if i == 1 { - break Ok(55); - } - i += 1; - } -} -"#, - r#" -fn foo() -> i32 { - let test = "test"; - if test == "test" { - return 24i32; - } - let mut i = 0; - loop { - if i == 1 { - break 55; - } - i += 1; - } -} -"#, - ); - - check_assist( - unwrap_result_return_type, - r#" -//- minicore: result -fn foo(the_field: u32) -> Result { - if the_field < 5 { - let mut i = 0; - loop { - if i > 5 { - return Ok(55u32); - } - i += 3; - } - match i { - 5 => return Ok(99), - _ => return Ok(0), - }; - } - Ok(the_field) -} -"#, - r#" -fn foo(the_field: u32) -> u32 { - if the_field < 5 { - let mut i = 0; - loop { - if i > 5 { - return 55u32; - } - i += 3; - } - match i { - 5 => return 99, - _ => return 0, - }; - } - the_field -} -"#, - ); - - check_assist( - unwrap_result_return_type, - r#" -//- minicore: result -fn foo(the_field: u32) -> Result { - if the_field < 5 { - let mut i = 0; - match i { - 5 => return Ok(99), - _ => return Ok(0), - } - } - Ok(the_field) -} -"#, - r#" -fn foo(the_field: u32) -> u32 { - if the_field < 5 { - let mut i = 0; - match i { - 5 => return 99, - _ => return 0, - } - } - the_field -} -"#, - ); - - check_assist( - unwrap_result_return_type, - r#" -//- minicore: result -fn foo(the_field: u32) -> Result { - if the_field < 5 { - let mut i = 0; - if i == 5 { - return Ok(99) - } else { - return Ok(0) - } - } - Ok(the_field) -} -"#, - r#" -fn foo(the_field: u32) -> u32 { - if the_field < 5 { - let mut i = 0; - if i == 5 { - return 99 - } else { - return 0 - } - } - the_field -} -"#, - ); - - check_assist( - unwrap_result_return_type, - r#" -//- minicore: result -fn foo(the_field: u32) -> Result { - if the_field < 5 { - let mut i = 0; - if i == 5 { - return Ok(99); - } else { - return Ok(0); - } - } - Ok(the_field) -} -"#, - r#" -fn foo(the_field: u32) -> u32 { - if the_field < 5 { - let mut i = 0; - if i == 5 { - return 99; - } else { - return 0; - } - } - the_field -} -"#, - ); - } - - #[test] - fn unwrap_result_return_type_nested_type() { - check_assist( - unwrap_result_return_type, - r#" -//- minicore: result, option -fn foo() -> Result, ()> { - Ok(Some(42)) -} -"#, - r#" -fn foo() -> Option { - Some(42) -} -"#, - ); - - check_assist( - unwrap_result_return_type, - r#" -//- minicore: result, option -fn foo() -> Result>, ()> { - Ok(None) -} -"#, - r#" -fn foo() -> Option> { - None -} -"#, - ); - - check_assist( - unwrap_result_return_type, - r#" -//- minicore: result, option, iterators -fn foo() -> Result$0, ()> { - Ok(Some(42).into_iter()) -} -"#, - r#" -fn foo() -> impl Iterator { - Some(42).into_iter() -} -"#, - ); - } -} diff --git a/crates/ide-assists/src/handlers/unwrap_return_type.rs b/crates/ide-assists/src/handlers/unwrap_return_type.rs new file mode 100644 index 0000000000..64d5e2c9b8 --- /dev/null +++ b/crates/ide-assists/src/handlers/unwrap_return_type.rs @@ -0,0 +1,2229 @@ +use ide_db::{ + famous_defs::FamousDefs, + syntax_helpers::node_ext::{for_each_tail_expr, walk_expr}, +}; +use itertools::Itertools; +use syntax::{ + ast::{self, Expr, HasGenericArgs}, + match_ast, AstNode, NodeOrToken, SyntaxKind, TextRange, +}; + +use crate::{AssistContext, AssistId, AssistKind, Assists}; + +// Assist: unwrap_option_return_type +// +// Unwrap the function's return type. +// +// ``` +// # //- minicore: option +// fn foo() -> Option$0 { Some(42i32) } +// ``` +// -> +// ``` +// fn foo() -> i32 { 42i32 } +// ``` + +// Assist: unwrap_result_return_type +// +// Unwrap the function's return type. +// +// ``` +// # //- minicore: result +// fn foo() -> Result$0 { Ok(42i32) } +// ``` +// -> +// ``` +// fn foo() -> i32 { 42i32 } +// ``` + +pub(crate) fn unwrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { + let ret_type = ctx.find_node_at_offset::()?; + let parent = ret_type.syntax().parent()?; + let body = match_ast! { + match parent { + ast::Fn(func) => func.body()?, + ast::ClosureExpr(closure) => match closure.body()? { + Expr::BlockExpr(block) => block, + // closures require a block when a return type is specified + _ => return None, + }, + _ => return None, + } + }; + + let type_ref = &ret_type.ty()?; + let Some(hir::Adt::Enum(ret_enum)) = ctx.sema.resolve_type(type_ref)?.as_adt() else { + return None; + }; + + let famous_defs = FamousDefs(&ctx.sema, ctx.sema.scope(type_ref.syntax())?.krate()); + + let kind = UnwrapperKind::ALL + .iter() + .find(|k| matches!(k.core_type(&famous_defs), Some(core_type) if ret_enum == core_type))?; + + let happy_type = extract_wrapped_type(type_ref)?; + + acc.add(kind.assist_id(), kind.label(), type_ref.syntax().text_range(), |builder| { + let body = ast::Expr::BlockExpr(body); + + let mut exprs_to_unwrap = Vec::new(); + let tail_cb = &mut |e: &_| tail_cb_impl(&mut exprs_to_unwrap, e); + walk_expr(&body, &mut |expr| { + if let Expr::ReturnExpr(ret_expr) = expr { + if let Some(ret_expr_arg) = &ret_expr.expr() { + for_each_tail_expr(ret_expr_arg, tail_cb); + } + } + }); + for_each_tail_expr(&body, tail_cb); + + let is_unit_type = is_unit_type(&happy_type); + if is_unit_type { + let mut text_range = ret_type.syntax().text_range(); + + if let Some(NodeOrToken::Token(token)) = ret_type.syntax().next_sibling_or_token() { + if token.kind() == SyntaxKind::WHITESPACE { + text_range = TextRange::new(text_range.start(), token.text_range().end()); + } + } + + builder.delete(text_range); + } else { + builder.replace(type_ref.syntax().text_range(), happy_type.syntax().text()); + } + + for ret_expr_arg in exprs_to_unwrap { + let ret_expr_str = ret_expr_arg.to_string(); + + let needs_replacing = match kind { + UnwrapperKind::Option => ret_expr_str.starts_with("Some("), + UnwrapperKind::Result => { + ret_expr_str.starts_with("Ok(") || ret_expr_str.starts_with("Err(") + } + }; + + if needs_replacing { + let arg_list = ret_expr_arg.syntax().children().find_map(ast::ArgList::cast); + if let Some(arg_list) = arg_list { + if is_unit_type { + match ret_expr_arg.syntax().prev_sibling_or_token() { + // Useful to delete the entire line without leaving trailing whitespaces + Some(whitespace) => { + let new_range = TextRange::new( + whitespace.text_range().start(), + ret_expr_arg.syntax().text_range().end(), + ); + builder.delete(new_range); + } + None => { + builder.delete(ret_expr_arg.syntax().text_range()); + } + } + } else { + builder.replace( + ret_expr_arg.syntax().text_range(), + arg_list.args().join(", "), + ); + } + } + } else if matches!(kind, UnwrapperKind::Option if ret_expr_str == "None") { + builder.replace(ret_expr_arg.syntax().text_range(), "()"); + } + } + }) +} + +enum UnwrapperKind { + Option, + Result, +} + +impl UnwrapperKind { + const ALL: &'static [UnwrapperKind] = &[UnwrapperKind::Option, UnwrapperKind::Result]; + + fn assist_id(&self) -> AssistId { + let s = match self { + UnwrapperKind::Option => "unwrap_option_return_type", + UnwrapperKind::Result => "unwrap_result_return_type", + }; + + AssistId(s, AssistKind::RefactorRewrite) + } + + fn label(&self) -> &'static str { + match self { + UnwrapperKind::Option => "Unwrap Option return type", + UnwrapperKind::Result => "Unwrap Result return type", + } + } + + fn core_type(&self, famous_defs: &FamousDefs<'_, '_>) -> Option { + match self { + UnwrapperKind::Option => famous_defs.core_option_Option(), + UnwrapperKind::Result => famous_defs.core_result_Result(), + } + } +} + +fn tail_cb_impl(acc: &mut Vec, e: &ast::Expr) { + match e { + Expr::BreakExpr(break_expr) => { + if let Some(break_expr_arg) = break_expr.expr() { + for_each_tail_expr(&break_expr_arg, &mut |e| tail_cb_impl(acc, e)) + } + } + Expr::ReturnExpr(_) => { + // all return expressions have already been handled by the walk loop + } + e => acc.push(e.clone()), + } +} + +// Tries to extract `T` from `Option` or `Result`. +fn extract_wrapped_type(ty: &ast::Type) -> Option { + let ast::Type::PathType(path_ty) = ty else { + return None; + }; + let path = path_ty.path()?; + let segment = path.first_segment()?; + let generic_arg_list = segment.generic_arg_list()?; + let generic_args: Vec<_> = generic_arg_list.generic_args().collect(); + let ast::GenericArg::TypeArg(happy_type) = generic_args.first()? else { + return None; + }; + happy_type.ty() +} + +fn is_unit_type(ty: &ast::Type) -> bool { + let ast::Type::TupleType(tuple) = ty else { return false }; + tuple.fields().next().is_none() +} + +#[cfg(test)] +mod tests { + use crate::tests::{check_assist_by_label, check_assist_not_applicable_by_label}; + + use super::*; + + #[test] + fn unwrap_option_return_type_simple() { + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: option +fn foo() -> Option { + let test = "test"; + return Some(42i32); +} +"#, + r#" +fn foo() -> i32 { + let test = "test"; + return 42i32; +} +"#, + "Unwrap Option return type", + ); + } + + #[test] + fn unwrap_option_return_type_unit_type() { + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: option +fn foo() -> Option<()$0> { + Some(()) +} +"#, + r#" +fn foo() { +} +"#, + "Unwrap Option return type", + ); + + // Unformatted return type + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: option +fn foo() -> Option<()$0>{ + Some(()) +} +"#, + r#" +fn foo() { +} +"#, + "Unwrap Option return type", + ); + } + + #[test] + fn unwrap_option_return_type_none() { + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: option +fn foo() -> Option { + if true { + Some(42) + } else { + None + } +} +"#, + r#" +fn foo() -> i32 { + if true { + 42 + } else { + () + } +} +"#, + "Unwrap Option return type", + ); + } + + #[test] + fn unwrap_option_return_type_ending_with_parent() { + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: option +fn foo() -> Option { + if true { + Some(42) + } else { + foo() + } +} +"#, + r#" +fn foo() -> i32 { + if true { + 42 + } else { + foo() + } +} +"#, + "Unwrap Option return type", + ); + } + + #[test] + fn unwrap_option_return_type_break_split_tail() { + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: option +fn foo() -> Option { + loop { + break if true { + Some(1) + } else { + Some(0) + }; + } +} +"#, + r#" +fn foo() -> i32 { + loop { + break if true { + 1 + } else { + 0 + }; + } +} +"#, + "Unwrap Option return type", + ); + } + + #[test] + fn unwrap_option_return_type_simple_closure() { + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: option +fn foo() { + || -> Option { + let test = "test"; + return Some(42i32); + }; +} +"#, + r#" +fn foo() { + || -> i32 { + let test = "test"; + return 42i32; + }; +} +"#, + "Unwrap Option return type", + ); + } + + #[test] + fn unwrap_option_return_type_simple_return_type_bad_cursor() { + check_assist_not_applicable_by_label( + unwrap_return_type, + r#" +//- minicore: option +fn foo() -> i32 { + let test = "test";$0 + return 42i32; +} +"#, + "Unwrap Option return type", + ); + } + + #[test] + fn unwrap_option_return_type_simple_return_type_bad_cursor_closure() { + check_assist_not_applicable_by_label( + unwrap_return_type, + r#" +//- minicore: option +fn foo() { + || -> i32 { + let test = "test";$0 + return 42i32; + }; +} +"#, + "Unwrap Option return type", + ); + } + + #[test] + fn unwrap_option_return_type_closure_non_block() { + check_assist_not_applicable_by_label( + unwrap_return_type, + r#" +//- minicore: option +fn foo() { || -> i$032 3; } +"#, + "Unwrap Option return type", + ); + } + + #[test] + fn unwrap_option_return_type_simple_return_type_already_not_option_std() { + check_assist_not_applicable_by_label( + unwrap_return_type, + r#" +//- minicore: option +fn foo() -> i32$0 { + let test = "test"; + return 42i32; +} +"#, + "Unwrap Option return type", + ); + } + + #[test] + fn unwrap_option_return_type_simple_return_type_already_not_option_closure() { + check_assist_not_applicable_by_label( + unwrap_return_type, + r#" +//- minicore: option +fn foo() { + || -> i32$0 { + let test = "test"; + return 42i32; + }; +} +"#, + "Unwrap Option return type", + ); + } + + #[test] + fn unwrap_option_return_type_simple_with_tail() { + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: option +fn foo() ->$0 Option { + let test = "test"; + Some(42i32) +} +"#, + r#" +fn foo() -> i32 { + let test = "test"; + 42i32 +} +"#, + "Unwrap Option return type", + ); + } + + #[test] + fn unwrap_option_return_type_simple_with_tail_closure() { + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: option +fn foo() { + || ->$0 Option { + let test = "test"; + Some(42i32) + }; +} +"#, + r#" +fn foo() { + || -> i32 { + let test = "test"; + 42i32 + }; +} +"#, + "Unwrap Option return type", + ); + } + + #[test] + fn unwrap_option_return_type_simple_with_tail_only() { + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: option +fn foo() -> Option { Some(42i32) } +"#, + r#" +fn foo() -> i32 { 42i32 } +"#, + "Unwrap Option return type", + ); + } + + #[test] + fn unwrap_option_return_type_simple_with_tail_block_like() { + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: option +fn foo() -> Option$0 { + if true { + Some(42i32) + } else { + Some(24i32) + } +} +"#, + r#" +fn foo() -> i32 { + if true { + 42i32 + } else { + 24i32 + } +} +"#, + "Unwrap Option return type", + ); + } + + #[test] + fn unwrap_option_return_type_simple_without_block_closure() { + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: option +fn foo() { + || -> Option$0 { + if true { + Some(42i32) + } else { + Some(24i32) + } + }; +} +"#, + r#" +fn foo() { + || -> i32 { + if true { + 42i32 + } else { + 24i32 + } + }; +} +"#, + "Unwrap Option return type", + ); + } + + #[test] + fn unwrap_option_return_type_simple_with_nested_if() { + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: option +fn foo() -> Option$0 { + if true { + if false { + Some(1) + } else { + Some(2) + } + } else { + Some(24i32) + } +} +"#, + r#" +fn foo() -> i32 { + if true { + if false { + 1 + } else { + 2 + } + } else { + 24i32 + } +} +"#, + "Unwrap Option return type", + ); + } + + #[test] + fn unwrap_option_return_type_simple_with_await() { + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: option +async fn foo() -> Option { + if true { + if false { + Some(1.await) + } else { + Some(2.await) + } + } else { + Some(24i32.await) + } +} +"#, + r#" +async fn foo() -> i32 { + if true { + if false { + 1.await + } else { + 2.await + } + } else { + 24i32.await + } +} +"#, + "Unwrap Option return type", + ); + } + + #[test] + fn unwrap_option_return_type_simple_with_array() { + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: option +fn foo() -> Option<[i32; 3]$0> { Some([1, 2, 3]) } +"#, + r#" +fn foo() -> [i32; 3] { [1, 2, 3] } +"#, + "Unwrap Option return type", + ); + } + + #[test] + fn unwrap_option_return_type_simple_with_cast() { + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: option +fn foo() -$0> Option { + if true { + if false { + Some(1 as i32) + } else { + Some(2 as i32) + } + } else { + Some(24 as i32) + } +} +"#, + r#" +fn foo() -> i32 { + if true { + if false { + 1 as i32 + } else { + 2 as i32 + } + } else { + 24 as i32 + } +} +"#, + "Unwrap Option return type", + ); + } + + #[test] + fn unwrap_option_return_type_simple_with_tail_block_like_match() { + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: option +fn foo() -> Option { + let my_var = 5; + match my_var { + 5 => Some(42i32), + _ => Some(24i32), + } +} +"#, + r#" +fn foo() -> i32 { + let my_var = 5; + match my_var { + 5 => 42i32, + _ => 24i32, + } +} +"#, + "Unwrap Option return type", + ); + } + + #[test] + fn unwrap_option_return_type_simple_with_loop_with_tail() { + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: option +fn foo() -> Option { + let my_var = 5; + loop { + println!("test"); + 5 + } + Some(my_var) +} +"#, + r#" +fn foo() -> i32 { + let my_var = 5; + loop { + println!("test"); + 5 + } + my_var +} +"#, + "Unwrap Option return type", + ); + } + + #[test] + fn unwrap_option_return_type_simple_with_loop_in_let_stmt() { + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: option +fn foo() -> Option { + let my_var = let x = loop { + break 1; + }; + Some(my_var) +} +"#, + r#" +fn foo() -> i32 { + let my_var = let x = loop { + break 1; + }; + my_var +} +"#, + "Unwrap Option return type", + ); + } + + #[test] + fn unwrap_option_return_type_simple_with_tail_block_like_match_return_expr() { + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: option +fn foo() -> Option$0 { + let my_var = 5; + let res = match my_var { + 5 => 42i32, + _ => return Some(24i32), + }; + Some(res) +} +"#, + r#" +fn foo() -> i32 { + let my_var = 5; + let res = match my_var { + 5 => 42i32, + _ => return 24i32, + }; + res +} +"#, + "Unwrap Option return type", + ); + + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: option +fn foo() -> Option { + let my_var = 5; + let res = if my_var == 5 { + 42i32 + } else { + return Some(24i32); + }; + Some(res) +} +"#, + r#" +fn foo() -> i32 { + let my_var = 5; + let res = if my_var == 5 { + 42i32 + } else { + return 24i32; + }; + res +} +"#, + "Unwrap Option return type", + ); + } + + #[test] + fn unwrap_option_return_type_simple_with_tail_block_like_match_deeper() { + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: option +fn foo() -> Option { + let my_var = 5; + match my_var { + 5 => { + if true { + Some(42i32) + } else { + Some(25i32) + } + }, + _ => { + let test = "test"; + if test == "test" { + return Some(bar()); + } + Some(53i32) + }, + } +} +"#, + r#" +fn foo() -> i32 { + let my_var = 5; + match my_var { + 5 => { + if true { + 42i32 + } else { + 25i32 + } + }, + _ => { + let test = "test"; + if test == "test" { + return bar(); + } + 53i32 + }, + } +} +"#, + "Unwrap Option return type", + ); + } + + #[test] + fn unwrap_option_return_type_simple_with_tail_block_like_early_return() { + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: option +fn foo() -> Option { + let test = "test"; + if test == "test" { + return Some(24i32); + } + Some(53i32) +} +"#, + r#" +fn foo() -> i32 { + let test = "test"; + if test == "test" { + return 24i32; + } + 53i32 +} +"#, + "Unwrap Option return type", + ); + } + + #[test] + fn unwrap_option_return_in_tail_position() { + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: option +fn foo(num: i32) -> $0Option { + return Some(num) +} +"#, + r#" +fn foo(num: i32) -> i32 { + return num +} +"#, + "Unwrap Option return type", + ); + } + + #[test] + fn unwrap_option_return_type_simple_with_closure() { + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: option +fn foo(the_field: u32) -> Option { + let true_closure = || { return true; }; + if the_field < 5 { + let mut i = 0; + if true_closure() { + return Some(99); + } else { + return Some(0); + } + } + Some(the_field) +} +"#, + r#" +fn foo(the_field: u32) -> u32 { + let true_closure = || { return true; }; + if the_field < 5 { + let mut i = 0; + if true_closure() { + return 99; + } else { + return 0; + } + } + the_field +} +"#, + "Unwrap Option return type", + ); + + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: option +fn foo(the_field: u32) -> Option { + let true_closure = || { + return true; + }; + if the_field < 5 { + let mut i = 0; + + + if true_closure() { + return Some(99); + } else { + return Some(0); + } + } + let t = None; + + Some(t.unwrap_or_else(|| the_field)) +} +"#, + r#" +fn foo(the_field: u32) -> u32 { + let true_closure = || { + return true; + }; + if the_field < 5 { + let mut i = 0; + + + if true_closure() { + return 99; + } else { + return 0; + } + } + let t = None; + + t.unwrap_or_else(|| the_field) +} +"#, + "Unwrap Option return type", + ); + } + + #[test] + fn unwrap_option_return_type_simple_with_weird_forms() { + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: option +fn foo() -> Option { + let test = "test"; + if test == "test" { + return Some(24i32); + } + let mut i = 0; + loop { + if i == 1 { + break Some(55); + } + i += 1; + } +} +"#, + r#" +fn foo() -> i32 { + let test = "test"; + if test == "test" { + return 24i32; + } + let mut i = 0; + loop { + if i == 1 { + break 55; + } + i += 1; + } +} +"#, + "Unwrap Option return type", + ); + + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: option +fn foo(the_field: u32) -> Option { + if the_field < 5 { + let mut i = 0; + loop { + if i > 5 { + return Some(55u32); + } + i += 3; + } + match i { + 5 => return Some(99), + _ => return Some(0), + }; + } + Some(the_field) +} +"#, + r#" +fn foo(the_field: u32) -> u32 { + if the_field < 5 { + let mut i = 0; + loop { + if i > 5 { + return 55u32; + } + i += 3; + } + match i { + 5 => return 99, + _ => return 0, + }; + } + the_field +} +"#, + "Unwrap Option return type", + ); + + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: option +fn foo(the_field: u32) -> Option { + if the_field < 5 { + let mut i = 0; + match i { + 5 => return Some(99), + _ => return Some(0), + } + } + Some(the_field) +} +"#, + r#" +fn foo(the_field: u32) -> u32 { + if the_field < 5 { + let mut i = 0; + match i { + 5 => return 99, + _ => return 0, + } + } + the_field +} +"#, + "Unwrap Option return type", + ); + + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: option +fn foo(the_field: u32) -> Option { + if the_field < 5 { + let mut i = 0; + if i == 5 { + return Some(99) + } else { + return Some(0) + } + } + Some(the_field) +} +"#, + r#" +fn foo(the_field: u32) -> u32 { + if the_field < 5 { + let mut i = 0; + if i == 5 { + return 99 + } else { + return 0 + } + } + the_field +} +"#, + "Unwrap Option return type", + ); + + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: option +fn foo(the_field: u32) -> Option { + if the_field < 5 { + let mut i = 0; + if i == 5 { + return Some(99); + } else { + return Some(0); + } + } + Some(the_field) +} +"#, + r#" +fn foo(the_field: u32) -> u32 { + if the_field < 5 { + let mut i = 0; + if i == 5 { + return 99; + } else { + return 0; + } + } + the_field +} +"#, + "Unwrap Option return type", + ); + } + + #[test] + fn unwrap_option_return_type_nested_type() { + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: option, result +fn foo() -> Option> { + Some(Ok(42)) +} +"#, + r#" +fn foo() -> Result { + Ok(42) +} +"#, + "Unwrap Option return type", + ); + + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: option, result +fn foo() -> Option, ()>> { + Some(Err()) +} +"#, + r#" +fn foo() -> Result, ()> { + Err() +} +"#, + "Unwrap Option return type", + ); + + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: option, result, iterators +fn foo() -> Option$0> { + Some(Some(42).into_iter()) +} +"#, + r#" +fn foo() -> impl Iterator { + Some(42).into_iter() +} +"#, + "Unwrap Option return type", + ); + } + + #[test] + fn unwrap_result_return_type_simple() { + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: result +fn foo() -> Result { + let test = "test"; + return Ok(42i32); +} +"#, + r#" +fn foo() -> i32 { + let test = "test"; + return 42i32; +} +"#, + "Unwrap Result return type", + ); + } + + #[test] + fn unwrap_result_return_type_unit_type() { + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: result +fn foo() -> Result<(), Box> { + Ok(()) +} +"#, + r#" +fn foo() { +} +"#, + "Unwrap Result return type", + ); + + // Unformatted return type + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: result +fn foo() -> Result<(), Box>{ + Ok(()) +} +"#, + r#" +fn foo() { +} +"#, + "Unwrap Result return type", + ); + } + + #[test] + fn unwrap_result_return_type_ending_with_parent() { + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: result +fn foo() -> Result> { + if true { + Ok(42) + } else { + foo() + } +} +"#, + r#" +fn foo() -> i32 { + if true { + 42 + } else { + foo() + } +} +"#, + "Unwrap Result return type", + ); + } + + #[test] + fn unwrap_result_return_type_break_split_tail() { + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: result +fn foo() -> Result { + loop { + break if true { + Ok(1) + } else { + Ok(0) + }; + } +} +"#, + r#" +fn foo() -> i32 { + loop { + break if true { + 1 + } else { + 0 + }; + } +} +"#, + "Unwrap Result return type", + ); + } + + #[test] + fn unwrap_result_return_type_simple_closure() { + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: result +fn foo() { + || -> Result { + let test = "test"; + return Ok(42i32); + }; +} +"#, + r#" +fn foo() { + || -> i32 { + let test = "test"; + return 42i32; + }; +} +"#, + "Unwrap Result return type", + ); + } + + #[test] + fn unwrap_result_return_type_simple_return_type_bad_cursor() { + check_assist_not_applicable_by_label( + unwrap_return_type, + r#" +//- minicore: result +fn foo() -> i32 { + let test = "test";$0 + return 42i32; +} +"#, + "Unwrap Result return type", + ); + } + + #[test] + fn unwrap_result_return_type_simple_return_type_bad_cursor_closure() { + check_assist_not_applicable_by_label( + unwrap_return_type, + r#" +//- minicore: result +fn foo() { + || -> i32 { + let test = "test";$0 + return 42i32; + }; +} +"#, + "Unwrap Result return type", + ); + } + + #[test] + fn unwrap_result_return_type_closure_non_block() { + check_assist_not_applicable_by_label( + unwrap_return_type, + r#" +//- minicore: result +fn foo() { || -> i$032 3; } +"#, + "Unwrap Result return type", + ); + } + + #[test] + fn unwrap_result_return_type_simple_return_type_already_not_result_std() { + check_assist_not_applicable_by_label( + unwrap_return_type, + r#" +//- minicore: result +fn foo() -> i32$0 { + let test = "test"; + return 42i32; +} +"#, + "Unwrap Result return type", + ); + } + + #[test] + fn unwrap_result_return_type_simple_return_type_already_not_result_closure() { + check_assist_not_applicable_by_label( + unwrap_return_type, + r#" +//- minicore: result +fn foo() { + || -> i32$0 { + let test = "test"; + return 42i32; + }; +} +"#, + "Unwrap Result return type", + ); + } + + #[test] + fn unwrap_result_return_type_simple_with_tail() { + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: result +fn foo() ->$0 Result { + let test = "test"; + Ok(42i32) +} +"#, + r#" +fn foo() -> i32 { + let test = "test"; + 42i32 +} +"#, + "Unwrap Result return type", + ); + } + + #[test] + fn unwrap_result_return_type_simple_with_tail_closure() { + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: result +fn foo() { + || ->$0 Result { + let test = "test"; + Ok(42i32) + }; +} +"#, + r#" +fn foo() { + || -> i32 { + let test = "test"; + 42i32 + }; +} +"#, + "Unwrap Result return type", + ); + } + + #[test] + fn unwrap_result_return_type_simple_with_tail_only() { + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: result +fn foo() -> Result { Ok(42i32) } +"#, + r#" +fn foo() -> i32 { 42i32 } +"#, + "Unwrap Result return type", + ); + } + + #[test] + fn unwrap_result_return_type_simple_with_tail_block_like() { + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: result +fn foo() -> Result$0 { + if true { + Ok(42i32) + } else { + Ok(24i32) + } +} +"#, + r#" +fn foo() -> i32 { + if true { + 42i32 + } else { + 24i32 + } +} +"#, + "Unwrap Result return type", + ); + } + + #[test] + fn unwrap_result_return_type_simple_without_block_closure() { + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: result +fn foo() { + || -> Result$0 { + if true { + Ok(42i32) + } else { + Ok(24i32) + } + }; +} +"#, + r#" +fn foo() { + || -> i32 { + if true { + 42i32 + } else { + 24i32 + } + }; +} +"#, + "Unwrap Result return type", + ); + } + + #[test] + fn unwrap_result_return_type_simple_with_nested_if() { + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: result +fn foo() -> Result$0 { + if true { + if false { + Ok(1) + } else { + Ok(2) + } + } else { + Ok(24i32) + } +} +"#, + r#" +fn foo() -> i32 { + if true { + if false { + 1 + } else { + 2 + } + } else { + 24i32 + } +} +"#, + "Unwrap Result return type", + ); + } + + #[test] + fn unwrap_result_return_type_simple_with_await() { + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: result +async fn foo() -> Result { + if true { + if false { + Ok(1.await) + } else { + Ok(2.await) + } + } else { + Ok(24i32.await) + } +} +"#, + r#" +async fn foo() -> i32 { + if true { + if false { + 1.await + } else { + 2.await + } + } else { + 24i32.await + } +} +"#, + "Unwrap Result return type", + ); + } + + #[test] + fn unwrap_result_return_type_simple_with_array() { + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: result +fn foo() -> Result<[i32; 3]$0> { Ok([1, 2, 3]) } +"#, + r#" +fn foo() -> [i32; 3] { [1, 2, 3] } +"#, + "Unwrap Result return type", + ); + } + + #[test] + fn unwrap_result_return_type_simple_with_cast() { + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: result +fn foo() -$0> Result { + if true { + if false { + Ok(1 as i32) + } else { + Ok(2 as i32) + } + } else { + Ok(24 as i32) + } +} +"#, + r#" +fn foo() -> i32 { + if true { + if false { + 1 as i32 + } else { + 2 as i32 + } + } else { + 24 as i32 + } +} +"#, + "Unwrap Result return type", + ); + } + + #[test] + fn unwrap_result_return_type_simple_with_tail_block_like_match() { + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: result +fn foo() -> Result { + let my_var = 5; + match my_var { + 5 => Ok(42i32), + _ => Ok(24i32), + } +} +"#, + r#" +fn foo() -> i32 { + let my_var = 5; + match my_var { + 5 => 42i32, + _ => 24i32, + } +} +"#, + "Unwrap Result return type", + ); + } + + #[test] + fn unwrap_result_return_type_simple_with_loop_with_tail() { + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: result +fn foo() -> Result { + let my_var = 5; + loop { + println!("test"); + 5 + } + Ok(my_var) +} +"#, + r#" +fn foo() -> i32 { + let my_var = 5; + loop { + println!("test"); + 5 + } + my_var +} +"#, + "Unwrap Result return type", + ); + } + + #[test] + fn unwrap_result_return_type_simple_with_loop_in_let_stmt() { + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: result +fn foo() -> Result { + let my_var = let x = loop { + break 1; + }; + Ok(my_var) +} +"#, + r#" +fn foo() -> i32 { + let my_var = let x = loop { + break 1; + }; + my_var +} +"#, + "Unwrap Result return type", + ); + } + + #[test] + fn unwrap_result_return_type_simple_with_tail_block_like_match_return_expr() { + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: result +fn foo() -> Result$0 { + let my_var = 5; + let res = match my_var { + 5 => 42i32, + _ => return Ok(24i32), + }; + Ok(res) +} +"#, + r#" +fn foo() -> i32 { + let my_var = 5; + let res = match my_var { + 5 => 42i32, + _ => return 24i32, + }; + res +} +"#, + "Unwrap Result return type", + ); + + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: result +fn foo() -> Result { + let my_var = 5; + let res = if my_var == 5 { + 42i32 + } else { + return Ok(24i32); + }; + Ok(res) +} +"#, + r#" +fn foo() -> i32 { + let my_var = 5; + let res = if my_var == 5 { + 42i32 + } else { + return 24i32; + }; + res +} +"#, + "Unwrap Result return type", + ); + } + + #[test] + fn unwrap_result_return_type_simple_with_tail_block_like_match_deeper() { + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: result +fn foo() -> Result { + let my_var = 5; + match my_var { + 5 => { + if true { + Ok(42i32) + } else { + Ok(25i32) + } + }, + _ => { + let test = "test"; + if test == "test" { + return Ok(bar()); + } + Ok(53i32) + }, + } +} +"#, + r#" +fn foo() -> i32 { + let my_var = 5; + match my_var { + 5 => { + if true { + 42i32 + } else { + 25i32 + } + }, + _ => { + let test = "test"; + if test == "test" { + return bar(); + } + 53i32 + }, + } +} +"#, + "Unwrap Result return type", + ); + } + + #[test] + fn unwrap_result_return_type_simple_with_tail_block_like_early_return() { + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: result +fn foo() -> Result { + let test = "test"; + if test == "test" { + return Ok(24i32); + } + Ok(53i32) +} +"#, + r#" +fn foo() -> i32 { + let test = "test"; + if test == "test" { + return 24i32; + } + 53i32 +} +"#, + "Unwrap Result return type", + ); + } + + #[test] + fn unwrap_result_return_in_tail_position() { + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: result +fn foo(num: i32) -> $0Result { + return Ok(num) +} +"#, + r#" +fn foo(num: i32) -> i32 { + return num +} +"#, + "Unwrap Result return type", + ); + } + + #[test] + fn unwrap_result_return_type_simple_with_closure() { + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: result +fn foo(the_field: u32) -> Result { + let true_closure = || { return true; }; + if the_field < 5 { + let mut i = 0; + if true_closure() { + return Ok(99); + } else { + return Ok(0); + } + } + Ok(the_field) +} +"#, + r#" +fn foo(the_field: u32) -> u32 { + let true_closure = || { return true; }; + if the_field < 5 { + let mut i = 0; + if true_closure() { + return 99; + } else { + return 0; + } + } + the_field +} +"#, + "Unwrap Result return type", + ); + + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: result +fn foo(the_field: u32) -> Result { + let true_closure = || { + return true; + }; + if the_field < 5 { + let mut i = 0; + + + if true_closure() { + return Ok(99); + } else { + return Ok(0); + } + } + let t = None; + + Ok(t.unwrap_or_else(|| the_field)) +} +"#, + r#" +fn foo(the_field: u32) -> u32 { + let true_closure = || { + return true; + }; + if the_field < 5 { + let mut i = 0; + + + if true_closure() { + return 99; + } else { + return 0; + } + } + let t = None; + + t.unwrap_or_else(|| the_field) +} +"#, + "Unwrap Result return type", + ); + } + + #[test] + fn unwrap_result_return_type_simple_with_weird_forms() { + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: result +fn foo() -> Result { + let test = "test"; + if test == "test" { + return Ok(24i32); + } + let mut i = 0; + loop { + if i == 1 { + break Ok(55); + } + i += 1; + } +} +"#, + r#" +fn foo() -> i32 { + let test = "test"; + if test == "test" { + return 24i32; + } + let mut i = 0; + loop { + if i == 1 { + break 55; + } + i += 1; + } +} +"#, + "Unwrap Result return type", + ); + + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: result +fn foo(the_field: u32) -> Result { + if the_field < 5 { + let mut i = 0; + loop { + if i > 5 { + return Ok(55u32); + } + i += 3; + } + match i { + 5 => return Ok(99), + _ => return Ok(0), + }; + } + Ok(the_field) +} +"#, + r#" +fn foo(the_field: u32) -> u32 { + if the_field < 5 { + let mut i = 0; + loop { + if i > 5 { + return 55u32; + } + i += 3; + } + match i { + 5 => return 99, + _ => return 0, + }; + } + the_field +} +"#, + "Unwrap Result return type", + ); + + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: result +fn foo(the_field: u32) -> Result { + if the_field < 5 { + let mut i = 0; + match i { + 5 => return Ok(99), + _ => return Ok(0), + } + } + Ok(the_field) +} +"#, + r#" +fn foo(the_field: u32) -> u32 { + if the_field < 5 { + let mut i = 0; + match i { + 5 => return 99, + _ => return 0, + } + } + the_field +} +"#, + "Unwrap Result return type", + ); + + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: result +fn foo(the_field: u32) -> Result { + if the_field < 5 { + let mut i = 0; + if i == 5 { + return Ok(99) + } else { + return Ok(0) + } + } + Ok(the_field) +} +"#, + r#" +fn foo(the_field: u32) -> u32 { + if the_field < 5 { + let mut i = 0; + if i == 5 { + return 99 + } else { + return 0 + } + } + the_field +} +"#, + "Unwrap Result return type", + ); + + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: result +fn foo(the_field: u32) -> Result { + if the_field < 5 { + let mut i = 0; + if i == 5 { + return Ok(99); + } else { + return Ok(0); + } + } + Ok(the_field) +} +"#, + r#" +fn foo(the_field: u32) -> u32 { + if the_field < 5 { + let mut i = 0; + if i == 5 { + return 99; + } else { + return 0; + } + } + the_field +} +"#, + "Unwrap Result return type", + ); + } + + #[test] + fn unwrap_result_return_type_nested_type() { + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: result, option +fn foo() -> Result, ()> { + Ok(Some(42)) +} +"#, + r#" +fn foo() -> Option { + Some(42) +} +"#, + "Unwrap Result return type", + ); + + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: result, option +fn foo() -> Result>, ()> { + Ok(None) +} +"#, + r#" +fn foo() -> Option> { + None +} +"#, + "Unwrap Result return type", + ); + + check_assist_by_label( + unwrap_return_type, + r#" +//- minicore: result, option, iterators +fn foo() -> Result$0, ()> { + Ok(Some(42).into_iter()) +} +"#, + r#" +fn foo() -> impl Iterator { + Some(42).into_iter() +} +"#, + "Unwrap Result return type", + ); + } +} diff --git a/crates/ide-assists/src/handlers/wrap_return_type.rs b/crates/ide-assists/src/handlers/wrap_return_type.rs new file mode 100644 index 0000000000..2d918a5b1c --- /dev/null +++ b/crates/ide-assists/src/handlers/wrap_return_type.rs @@ -0,0 +1,2457 @@ +use std::iter; + +use hir::HasSource; +use ide_db::{ + assists::GroupLabel, + famous_defs::FamousDefs, + syntax_helpers::node_ext::{for_each_tail_expr, walk_expr}, +}; +use itertools::Itertools; +use syntax::{ + ast::{self, make, Expr, HasGenericParams}, + match_ast, ted, AstNode, ToSmolStr, +}; + +use crate::{AssistContext, AssistId, AssistKind, Assists}; + +// Assist: wrap_return_type_in_option +// +// Wrap the function's return type into Option. +// +// ``` +// # //- minicore: option +// fn foo() -> i32$0 { 42i32 } +// ``` +// -> +// ``` +// fn foo() -> Option { Some(42i32) } +// ``` + +// Assist: wrap_return_type_in_result +// +// Wrap the function's return type into Result. +// +// ``` +// # //- minicore: result +// fn foo() -> i32$0 { 42i32 } +// ``` +// -> +// ``` +// fn foo() -> Result { Ok(42i32) } +// ``` + +pub(crate) fn wrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { + let ret_type = ctx.find_node_at_offset::()?; + let parent = ret_type.syntax().parent()?; + let body = match_ast! { + match parent { + ast::Fn(func) => func.body()?, + ast::ClosureExpr(closure) => match closure.body()? { + Expr::BlockExpr(block) => block, + // closures require a block when a return type is specified + _ => return None, + }, + _ => return None, + } + }; + + let type_ref = &ret_type.ty()?; + let ty = ctx.sema.resolve_type(type_ref)?.as_adt(); + let famous_defs = FamousDefs(&ctx.sema, ctx.sema.scope(type_ref.syntax())?.krate()); + + for kind in WrapperKind::ALL { + let Some(core_wrapper) = kind.core_type(&famous_defs) else { + continue; + }; + + if matches!(ty, Some(hir::Adt::Enum(ret_type)) if ret_type == core_wrapper) { + // The return type is already wrapped + cov_mark::hit!(wrap_return_type_simple_return_type_already_wrapped); + continue; + } + + acc.add_group( + &GroupLabel("Wrap return type in...".into()), + kind.assist_id(), + kind.label(), + type_ref.syntax().text_range(), + |edit| { + let alias = wrapper_alias(ctx, &core_wrapper, type_ref, kind.symbol()); + let new_return_ty = + alias.unwrap_or_else(|| kind.wrap_type(type_ref)).clone_for_update(); + + let body = edit.make_mut(ast::Expr::BlockExpr(body.clone())); + + let mut exprs_to_wrap = Vec::new(); + let tail_cb = &mut |e: &_| tail_cb_impl(&mut exprs_to_wrap, e); + walk_expr(&body, &mut |expr| { + if let Expr::ReturnExpr(ret_expr) = expr { + if let Some(ret_expr_arg) = &ret_expr.expr() { + for_each_tail_expr(ret_expr_arg, tail_cb); + } + } + }); + for_each_tail_expr(&body, tail_cb); + + for ret_expr_arg in exprs_to_wrap { + let happy_wrapped = make::expr_call( + make::expr_path(make::ext::ident_path(kind.happy_ident())), + make::arg_list(iter::once(ret_expr_arg.clone())), + ) + .clone_for_update(); + ted::replace(ret_expr_arg.syntax(), happy_wrapped.syntax()); + } + + let old_return_ty = edit.make_mut(type_ref.clone()); + ted::replace(old_return_ty.syntax(), new_return_ty.syntax()); + + if let WrapperKind::Result = kind { + // Add a placeholder snippet at the first generic argument that doesn't equal the return type. + // This is normally the error type, but that may not be the case when we inserted a type alias. + let args = + new_return_ty.syntax().descendants().find_map(ast::GenericArgList::cast); + let error_type_arg = args.and_then(|list| { + list.generic_args().find(|arg| match arg { + ast::GenericArg::TypeArg(_) => { + arg.syntax().text() != type_ref.syntax().text() + } + ast::GenericArg::LifetimeArg(_) => false, + _ => true, + }) + }); + if let Some(error_type_arg) = error_type_arg { + if let Some(cap) = ctx.config.snippet_cap { + edit.add_placeholder_snippet(cap, error_type_arg); + } + } + } + }, + ); + } + + Some(()) +} + +enum WrapperKind { + Option, + Result, +} + +impl WrapperKind { + const ALL: &'static [WrapperKind] = &[WrapperKind::Option, WrapperKind::Result]; + + fn assist_id(&self) -> AssistId { + let s = match self { + WrapperKind::Option => "wrap_return_type_in_option", + WrapperKind::Result => "wrap_return_type_in_result", + }; + + AssistId(s, AssistKind::RefactorRewrite) + } + + fn label(&self) -> &'static str { + match self { + WrapperKind::Option => "Wrap return type in Option", + WrapperKind::Result => "Wrap return type in Result", + } + } + + fn happy_ident(&self) -> &'static str { + match self { + WrapperKind::Option => "Some", + WrapperKind::Result => "Ok", + } + } + + fn core_type(&self, famous_defs: &FamousDefs<'_, '_>) -> Option { + match self { + WrapperKind::Option => famous_defs.core_option_Option(), + WrapperKind::Result => famous_defs.core_result_Result(), + } + } + + fn symbol(&self) -> hir::Symbol { + match self { + WrapperKind::Option => hir::sym::Option.clone(), + WrapperKind::Result => hir::sym::Result.clone(), + } + } + + fn wrap_type(&self, type_ref: &ast::Type) -> ast::Type { + match self { + WrapperKind::Option => make::ext::ty_option(type_ref.clone()), + WrapperKind::Result => make::ext::ty_result(type_ref.clone(), make::ty_placeholder()), + } + } +} + +// Try to find an wrapper type alias in the current scope (shadowing the default). +fn wrapper_alias( + ctx: &AssistContext<'_>, + core_wrapper: &hir::Enum, + ret_type: &ast::Type, + wrapper: hir::Symbol, +) -> Option { + let wrapper_path = hir::ModPath::from_segments( + hir::PathKind::Plain, + iter::once(hir::Name::new_symbol_root(wrapper)), + ); + + ctx.sema.resolve_mod_path(ret_type.syntax(), &wrapper_path).and_then(|def| { + def.filter_map(|def| match def.as_module_def()? { + hir::ModuleDef::TypeAlias(alias) => { + let enum_ty = alias.ty(ctx.db()).as_adt()?.as_enum()?; + (&enum_ty == core_wrapper).then_some(alias) + } + _ => None, + }) + .find_map(|alias| { + let mut inserted_ret_type = false; + let generic_params = alias + .source(ctx.db())? + .value + .generic_param_list()? + .generic_params() + .map(|param| match param { + // Replace the very first type parameter with the functions return type. + ast::GenericParam::TypeParam(_) if !inserted_ret_type => { + inserted_ret_type = true; + ret_type.to_smolstr() + } + ast::GenericParam::LifetimeParam(_) => make::lifetime("'_").to_smolstr(), + _ => make::ty_placeholder().to_smolstr(), + }) + .join(", "); + + let name = alias.name(ctx.db()); + let name = name.as_str(); + Some(make::ty(&format!("{name}<{generic_params}>"))) + }) + }) +} + +fn tail_cb_impl(acc: &mut Vec, e: &ast::Expr) { + match e { + Expr::BreakExpr(break_expr) => { + if let Some(break_expr_arg) = break_expr.expr() { + for_each_tail_expr(&break_expr_arg, &mut |e| tail_cb_impl(acc, e)) + } + } + Expr::ReturnExpr(_) => { + // all return expressions have already been handled by the walk loop + } + e => acc.push(e.clone()), + } +} + +#[cfg(test)] +mod tests { + use crate::tests::{check_assist_by_label, check_assist_not_applicable_by_label}; + + use super::*; + + #[test] + fn wrap_return_type_in_option_simple() { + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: option +fn foo() -> i3$02 { + let test = "test"; + return 42i32; +} +"#, + r#" +fn foo() -> Option { + let test = "test"; + return Some(42i32); +} +"#, + WrapperKind::Option.label(), + ); + } + + #[test] + fn wrap_return_type_in_option_break_split_tail() { + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: option +fn foo() -> i3$02 { + loop { + break if true { + 1 + } else { + 0 + }; + } +} +"#, + r#" +fn foo() -> Option { + loop { + break if true { + Some(1) + } else { + Some(0) + }; + } +} +"#, + WrapperKind::Option.label(), + ); + } + + #[test] + fn wrap_return_type_in_option_simple_closure() { + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: option +fn foo() { + || -> i32$0 { + let test = "test"; + return 42i32; + }; +} +"#, + r#" +fn foo() { + || -> Option { + let test = "test"; + return Some(42i32); + }; +} +"#, + WrapperKind::Option.label(), + ); + } + + #[test] + fn wrap_return_type_in_option_simple_return_type_bad_cursor() { + check_assist_not_applicable_by_label( + wrap_return_type, + r#" +//- minicore: option +fn foo() -> i32 { + let test = "test";$0 + return 42i32; +} +"#, + WrapperKind::Option.label(), + ); + } + + #[test] + fn wrap_return_type_in_option_simple_return_type_bad_cursor_closure() { + check_assist_not_applicable_by_label( + wrap_return_type, + r#" +//- minicore: option +fn foo() { + || -> i32 { + let test = "test";$0 + return 42i32; + }; +} +"#, + WrapperKind::Option.label(), + ); + } + + #[test] + fn wrap_return_type_in_option_closure_non_block() { + check_assist_not_applicable_by_label( + wrap_return_type, + r#" +//- minicore: option +fn foo() { || -> i$032 3; } +"#, + WrapperKind::Option.label(), + ); + } + + #[test] + fn wrap_return_type_in_option_simple_return_type_already_option_std() { + check_assist_not_applicable_by_label( + wrap_return_type, + r#" +//- minicore: option +fn foo() -> core::option::Option { + let test = "test"; + return 42i32; +} +"#, + WrapperKind::Option.label(), + ); + } + + #[test] + fn wrap_return_type_in_option_simple_return_type_already_option() { + cov_mark::check!(wrap_return_type_simple_return_type_already_wrapped); + check_assist_not_applicable_by_label( + wrap_return_type, + r#" +//- minicore: option +fn foo() -> Option { + let test = "test"; + return 42i32; +} +"#, + WrapperKind::Option.label(), + ); + } + + #[test] + fn wrap_return_type_in_option_simple_return_type_already_option_closure() { + check_assist_not_applicable_by_label( + wrap_return_type, + r#" +//- minicore: option +fn foo() { + || -> Option { + let test = "test"; + return 42i32; + }; +} +"#, + WrapperKind::Option.label(), + ); + } + + #[test] + fn wrap_return_type_in_option_simple_with_cursor() { + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: option +fn foo() -> $0i32 { + let test = "test"; + return 42i32; +} +"#, + r#" +fn foo() -> Option { + let test = "test"; + return Some(42i32); +} +"#, + WrapperKind::Option.label(), + ); + } + + #[test] + fn wrap_return_type_in_option_simple_with_tail() { + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: option +fn foo() ->$0 i32 { + let test = "test"; + 42i32 +} +"#, + r#" +fn foo() -> Option { + let test = "test"; + Some(42i32) +} +"#, + WrapperKind::Option.label(), + ); + } + + #[test] + fn wrap_return_type_in_option_simple_with_tail_closure() { + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: option +fn foo() { + || ->$0 i32 { + let test = "test"; + 42i32 + }; +} +"#, + r#" +fn foo() { + || -> Option { + let test = "test"; + Some(42i32) + }; +} +"#, + WrapperKind::Option.label(), + ); + } + + #[test] + fn wrap_return_type_in_option_simple_with_tail_only() { + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: option +fn foo() -> i32$0 { 42i32 } +"#, + r#" +fn foo() -> Option { Some(42i32) } +"#, + WrapperKind::Option.label(), + ); + } + + #[test] + fn wrap_return_type_in_option_simple_with_tail_block_like() { + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: option +fn foo() -> i32$0 { + if true { + 42i32 + } else { + 24i32 + } +} +"#, + r#" +fn foo() -> Option { + if true { + Some(42i32) + } else { + Some(24i32) + } +} +"#, + WrapperKind::Option.label(), + ); + } + + #[test] + fn wrap_return_type_in_option_simple_without_block_closure() { + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: option +fn foo() { + || -> i32$0 { + if true { + 42i32 + } else { + 24i32 + } + }; +} +"#, + r#" +fn foo() { + || -> Option { + if true { + Some(42i32) + } else { + Some(24i32) + } + }; +} +"#, + WrapperKind::Option.label(), + ); + } + + #[test] + fn wrap_return_type_in_option_simple_with_nested_if() { + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: option +fn foo() -> i32$0 { + if true { + if false { + 1 + } else { + 2 + } + } else { + 24i32 + } +} +"#, + r#" +fn foo() -> Option { + if true { + if false { + Some(1) + } else { + Some(2) + } + } else { + Some(24i32) + } +} +"#, + WrapperKind::Option.label(), + ); + } + + #[test] + fn wrap_return_type_in_option_simple_with_await() { + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: option +async fn foo() -> i$032 { + if true { + if false { + 1.await + } else { + 2.await + } + } else { + 24i32.await + } +} +"#, + r#" +async fn foo() -> Option { + if true { + if false { + Some(1.await) + } else { + Some(2.await) + } + } else { + Some(24i32.await) + } +} +"#, + WrapperKind::Option.label(), + ); + } + + #[test] + fn wrap_return_type_in_option_simple_with_array() { + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: option +fn foo() -> [i32;$0 3] { [1, 2, 3] } +"#, + r#" +fn foo() -> Option<[i32; 3]> { Some([1, 2, 3]) } +"#, + WrapperKind::Option.label(), + ); + } + + #[test] + fn wrap_return_type_in_option_simple_with_cast() { + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: option +fn foo() -$0> i32 { + if true { + if false { + 1 as i32 + } else { + 2 as i32 + } + } else { + 24 as i32 + } +} +"#, + r#" +fn foo() -> Option { + if true { + if false { + Some(1 as i32) + } else { + Some(2 as i32) + } + } else { + Some(24 as i32) + } +} +"#, + WrapperKind::Option.label(), + ); + } + + #[test] + fn wrap_return_type_in_option_simple_with_tail_block_like_match() { + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: option +fn foo() -> i32$0 { + let my_var = 5; + match my_var { + 5 => 42i32, + _ => 24i32, + } +} +"#, + r#" +fn foo() -> Option { + let my_var = 5; + match my_var { + 5 => Some(42i32), + _ => Some(24i32), + } +} +"#, + WrapperKind::Option.label(), + ); + } + + #[test] + fn wrap_return_type_in_option_simple_with_loop_with_tail() { + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: option +fn foo() -> i32$0 { + let my_var = 5; + loop { + println!("test"); + 5 + } + my_var +} +"#, + r#" +fn foo() -> Option { + let my_var = 5; + loop { + println!("test"); + 5 + } + Some(my_var) +} +"#, + WrapperKind::Option.label(), + ); + } + + #[test] + fn wrap_return_type_in_option_simple_with_loop_in_let_stmt() { + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: option +fn foo() -> i32$0 { + let my_var = let x = loop { + break 1; + }; + my_var +} +"#, + r#" +fn foo() -> Option { + let my_var = let x = loop { + break 1; + }; + Some(my_var) +} +"#, + WrapperKind::Option.label(), + ); + } + + #[test] + fn wrap_return_type_in_option_simple_with_tail_block_like_match_return_expr() { + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: option +fn foo() -> i32$0 { + let my_var = 5; + let res = match my_var { + 5 => 42i32, + _ => return 24i32, + }; + res +} +"#, + r#" +fn foo() -> Option { + let my_var = 5; + let res = match my_var { + 5 => 42i32, + _ => return Some(24i32), + }; + Some(res) +} +"#, + WrapperKind::Option.label(), + ); + + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: option +fn foo() -> i32$0 { + let my_var = 5; + let res = if my_var == 5 { + 42i32 + } else { + return 24i32; + }; + res +} +"#, + r#" +fn foo() -> Option { + let my_var = 5; + let res = if my_var == 5 { + 42i32 + } else { + return Some(24i32); + }; + Some(res) +} +"#, + WrapperKind::Option.label(), + ); + } + + #[test] + fn wrap_return_type_in_option_simple_with_tail_block_like_match_deeper() { + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: option +fn foo() -> i32$0 { + let my_var = 5; + match my_var { + 5 => { + if true { + 42i32 + } else { + 25i32 + } + }, + _ => { + let test = "test"; + if test == "test" { + return bar(); + } + 53i32 + }, + } +} +"#, + r#" +fn foo() -> Option { + let my_var = 5; + match my_var { + 5 => { + if true { + Some(42i32) + } else { + Some(25i32) + } + }, + _ => { + let test = "test"; + if test == "test" { + return Some(bar()); + } + Some(53i32) + }, + } +} +"#, + WrapperKind::Option.label(), + ); + } + + #[test] + fn wrap_return_type_in_option_simple_with_tail_block_like_early_return() { + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: option +fn foo() -> i$032 { + let test = "test"; + if test == "test" { + return 24i32; + } + 53i32 +} +"#, + r#" +fn foo() -> Option { + let test = "test"; + if test == "test" { + return Some(24i32); + } + Some(53i32) +} +"#, + WrapperKind::Option.label(), + ); + } + + #[test] + fn wrap_return_in_option_tail_position() { + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: option +fn foo(num: i32) -> $0i32 { + return num +} +"#, + r#" +fn foo(num: i32) -> Option { + return Some(num) +} +"#, + WrapperKind::Option.label(), + ); + } + + #[test] + fn wrap_return_type_in_option_simple_with_closure() { + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: option +fn foo(the_field: u32) ->$0 u32 { + let true_closure = || { return true; }; + if the_field < 5 { + let mut i = 0; + if true_closure() { + return 99; + } else { + return 0; + } + } + the_field +} +"#, + r#" +fn foo(the_field: u32) -> Option { + let true_closure = || { return true; }; + if the_field < 5 { + let mut i = 0; + if true_closure() { + return Some(99); + } else { + return Some(0); + } + } + Some(the_field) +} +"#, + WrapperKind::Option.label(), + ); + + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: option +fn foo(the_field: u32) -> u32$0 { + let true_closure = || { + return true; + }; + if the_field < 5 { + let mut i = 0; + + + if true_closure() { + return 99; + } else { + return 0; + } + } + let t = None; + + t.unwrap_or_else(|| the_field) +} +"#, + r#" +fn foo(the_field: u32) -> Option { + let true_closure = || { + return true; + }; + if the_field < 5 { + let mut i = 0; + + + if true_closure() { + return Some(99); + } else { + return Some(0); + } + } + let t = None; + + Some(t.unwrap_or_else(|| the_field)) +} +"#, + WrapperKind::Option.label(), + ); + } + + #[test] + fn wrap_return_type_in_option_simple_with_weird_forms() { + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: option +fn foo() -> i32$0 { + let test = "test"; + if test == "test" { + return 24i32; + } + let mut i = 0; + loop { + if i == 1 { + break 55; + } + i += 1; + } +} +"#, + r#" +fn foo() -> Option { + let test = "test"; + if test == "test" { + return Some(24i32); + } + let mut i = 0; + loop { + if i == 1 { + break Some(55); + } + i += 1; + } +} +"#, + WrapperKind::Option.label(), + ); + + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: option +fn foo(the_field: u32) -> u32$0 { + if the_field < 5 { + let mut i = 0; + loop { + if i > 5 { + return 55u32; + } + i += 3; + } + match i { + 5 => return 99, + _ => return 0, + }; + } + the_field +} +"#, + r#" +fn foo(the_field: u32) -> Option { + if the_field < 5 { + let mut i = 0; + loop { + if i > 5 { + return Some(55u32); + } + i += 3; + } + match i { + 5 => return Some(99), + _ => return Some(0), + }; + } + Some(the_field) +} +"#, + WrapperKind::Option.label(), + ); + + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: option +fn foo(the_field: u32) -> u3$02 { + if the_field < 5 { + let mut i = 0; + match i { + 5 => return 99, + _ => return 0, + } + } + the_field +} +"#, + r#" +fn foo(the_field: u32) -> Option { + if the_field < 5 { + let mut i = 0; + match i { + 5 => return Some(99), + _ => return Some(0), + } + } + Some(the_field) +} +"#, + WrapperKind::Option.label(), + ); + + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: option +fn foo(the_field: u32) -> u32$0 { + if the_field < 5 { + let mut i = 0; + if i == 5 { + return 99 + } else { + return 0 + } + } + the_field +} +"#, + r#" +fn foo(the_field: u32) -> Option { + if the_field < 5 { + let mut i = 0; + if i == 5 { + return Some(99) + } else { + return Some(0) + } + } + Some(the_field) +} +"#, + WrapperKind::Option.label(), + ); + + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: option +fn foo(the_field: u32) -> $0u32 { + if the_field < 5 { + let mut i = 0; + if i == 5 { + return 99; + } else { + return 0; + } + } + the_field +} +"#, + r#" +fn foo(the_field: u32) -> Option { + if the_field < 5 { + let mut i = 0; + if i == 5 { + return Some(99); + } else { + return Some(0); + } + } + Some(the_field) +} +"#, + WrapperKind::Option.label(), + ); + } + + #[test] + fn wrap_return_type_in_local_option_type() { + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: option +type Option = core::option::Option; + +fn foo() -> i3$02 { + return 42i32; +} +"#, + r#" +type Option = core::option::Option; + +fn foo() -> Option { + return Some(42i32); +} +"#, + WrapperKind::Option.label(), + ); + + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: option +type Option2 = core::option::Option; + +fn foo() -> i3$02 { + return 42i32; +} +"#, + r#" +type Option2 = core::option::Option; + +fn foo() -> Option { + return Some(42i32); +} +"#, + WrapperKind::Option.label(), + ); + } + + #[test] + fn wrap_return_type_in_imported_local_option_type() { + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: option +mod some_module { + pub type Option = core::option::Option; +} + +use some_module::Option; + +fn foo() -> i3$02 { + return 42i32; +} +"#, + r#" +mod some_module { + pub type Option = core::option::Option; +} + +use some_module::Option; + +fn foo() -> Option { + return Some(42i32); +} +"#, + WrapperKind::Option.label(), + ); + + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: option +mod some_module { + pub type Option = core::option::Option; +} + +use some_module::*; + +fn foo() -> i3$02 { + return 42i32; +} +"#, + r#" +mod some_module { + pub type Option = core::option::Option; +} + +use some_module::*; + +fn foo() -> Option { + return Some(42i32); +} +"#, + WrapperKind::Option.label(), + ); + } + + #[test] + fn wrap_return_type_in_local_option_type_from_function_body() { + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: option +fn foo() -> i3$02 { + type Option = core::option::Option; + 0 +} +"#, + r#" +fn foo() -> Option { + type Option = core::option::Option; + Some(0) +} +"#, + WrapperKind::Option.label(), + ); + } + + #[test] + fn wrap_return_type_in_local_option_type_already_using_alias() { + check_assist_not_applicable_by_label( + wrap_return_type, + r#" +//- minicore: option +pub type Option = core::option::Option; + +fn foo() -> Option { + return Some(42i32); +} +"#, + WrapperKind::Option.label(), + ); + } + + #[test] + fn wrap_return_type_in_result_simple() { + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: result +fn foo() -> i3$02 { + let test = "test"; + return 42i32; +} +"#, + r#" +fn foo() -> Result { + let test = "test"; + return Ok(42i32); +} +"#, + WrapperKind::Result.label(), + ); + } + + #[test] + fn wrap_return_type_in_result_break_split_tail() { + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: result +fn foo() -> i3$02 { + loop { + break if true { + 1 + } else { + 0 + }; + } +} +"#, + r#" +fn foo() -> Result { + loop { + break if true { + Ok(1) + } else { + Ok(0) + }; + } +} +"#, + WrapperKind::Result.label(), + ); + } + + #[test] + fn wrap_return_type_in_result_simple_closure() { + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: result +fn foo() { + || -> i32$0 { + let test = "test"; + return 42i32; + }; +} +"#, + r#" +fn foo() { + || -> Result { + let test = "test"; + return Ok(42i32); + }; +} +"#, + WrapperKind::Result.label(), + ); + } + + #[test] + fn wrap_return_type_in_result_simple_return_type_bad_cursor() { + check_assist_not_applicable_by_label( + wrap_return_type, + r#" +//- minicore: result +fn foo() -> i32 { + let test = "test";$0 + return 42i32; +} +"#, + WrapperKind::Result.label(), + ); + } + + #[test] + fn wrap_return_type_in_result_simple_return_type_bad_cursor_closure() { + check_assist_not_applicable_by_label( + wrap_return_type, + r#" +//- minicore: result +fn foo() { + || -> i32 { + let test = "test";$0 + return 42i32; + }; +} +"#, + WrapperKind::Result.label(), + ); + } + + #[test] + fn wrap_return_type_in_result_closure_non_block() { + check_assist_not_applicable_by_label( + wrap_return_type, + r#" +//- minicore: result +fn foo() { || -> i$032 3; } +"#, + WrapperKind::Result.label(), + ); + } + + #[test] + fn wrap_return_type_in_result_simple_return_type_already_result_std() { + check_assist_not_applicable_by_label( + wrap_return_type, + r#" +//- minicore: result +fn foo() -> core::result::Result { + let test = "test"; + return 42i32; +} +"#, + WrapperKind::Result.label(), + ); + } + + #[test] + fn wrap_return_type_in_result_simple_return_type_already_result() { + cov_mark::check!(wrap_return_type_simple_return_type_already_wrapped); + check_assist_not_applicable_by_label( + wrap_return_type, + r#" +//- minicore: result +fn foo() -> Result { + let test = "test"; + return 42i32; +} +"#, + WrapperKind::Result.label(), + ); + } + + #[test] + fn wrap_return_type_in_result_simple_return_type_already_result_closure() { + check_assist_not_applicable_by_label( + wrap_return_type, + r#" +//- minicore: result +fn foo() { + || -> Result { + let test = "test"; + return 42i32; + }; +} +"#, + WrapperKind::Result.label(), + ); + } + + #[test] + fn wrap_return_type_in_result_simple_with_cursor() { + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: result +fn foo() -> $0i32 { + let test = "test"; + return 42i32; +} +"#, + r#" +fn foo() -> Result { + let test = "test"; + return Ok(42i32); +} +"#, + WrapperKind::Result.label(), + ); + } + + #[test] + fn wrap_return_type_in_result_simple_with_tail() { + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: result +fn foo() ->$0 i32 { + let test = "test"; + 42i32 +} +"#, + r#" +fn foo() -> Result { + let test = "test"; + Ok(42i32) +} +"#, + WrapperKind::Result.label(), + ); + } + + #[test] + fn wrap_return_type_in_result_simple_with_tail_closure() { + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: result +fn foo() { + || ->$0 i32 { + let test = "test"; + 42i32 + }; +} +"#, + r#" +fn foo() { + || -> Result { + let test = "test"; + Ok(42i32) + }; +} +"#, + WrapperKind::Result.label(), + ); + } + + #[test] + fn wrap_return_type_in_result_simple_with_tail_only() { + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: result +fn foo() -> i32$0 { 42i32 } +"#, + r#" +fn foo() -> Result { Ok(42i32) } +"#, + WrapperKind::Result.label(), + ); + } + + #[test] + fn wrap_return_type_in_result_simple_with_tail_block_like() { + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: result +fn foo() -> i32$0 { + if true { + 42i32 + } else { + 24i32 + } +} +"#, + r#" +fn foo() -> Result { + if true { + Ok(42i32) + } else { + Ok(24i32) + } +} +"#, + WrapperKind::Result.label(), + ); + } + + #[test] + fn wrap_return_type_in_result_simple_without_block_closure() { + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: result +fn foo() { + || -> i32$0 { + if true { + 42i32 + } else { + 24i32 + } + }; +} +"#, + r#" +fn foo() { + || -> Result { + if true { + Ok(42i32) + } else { + Ok(24i32) + } + }; +} +"#, + WrapperKind::Result.label(), + ); + } + + #[test] + fn wrap_return_type_in_result_simple_with_nested_if() { + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: result +fn foo() -> i32$0 { + if true { + if false { + 1 + } else { + 2 + } + } else { + 24i32 + } +} +"#, + r#" +fn foo() -> Result { + if true { + if false { + Ok(1) + } else { + Ok(2) + } + } else { + Ok(24i32) + } +} +"#, + WrapperKind::Result.label(), + ); + } + + #[test] + fn wrap_return_type_in_result_simple_with_await() { + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: result +async fn foo() -> i$032 { + if true { + if false { + 1.await + } else { + 2.await + } + } else { + 24i32.await + } +} +"#, + r#" +async fn foo() -> Result { + if true { + if false { + Ok(1.await) + } else { + Ok(2.await) + } + } else { + Ok(24i32.await) + } +} +"#, + WrapperKind::Result.label(), + ); + } + + #[test] + fn wrap_return_type_in_result_simple_with_array() { + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: result +fn foo() -> [i32;$0 3] { [1, 2, 3] } +"#, + r#" +fn foo() -> Result<[i32; 3], ${0:_}> { Ok([1, 2, 3]) } +"#, + WrapperKind::Result.label(), + ); + } + + #[test] + fn wrap_return_type_in_result_simple_with_cast() { + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: result +fn foo() -$0> i32 { + if true { + if false { + 1 as i32 + } else { + 2 as i32 + } + } else { + 24 as i32 + } +} +"#, + r#" +fn foo() -> Result { + if true { + if false { + Ok(1 as i32) + } else { + Ok(2 as i32) + } + } else { + Ok(24 as i32) + } +} +"#, + WrapperKind::Result.label(), + ); + } + + #[test] + fn wrap_return_type_in_result_simple_with_tail_block_like_match() { + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: result +fn foo() -> i32$0 { + let my_var = 5; + match my_var { + 5 => 42i32, + _ => 24i32, + } +} +"#, + r#" +fn foo() -> Result { + let my_var = 5; + match my_var { + 5 => Ok(42i32), + _ => Ok(24i32), + } +} +"#, + WrapperKind::Result.label(), + ); + } + + #[test] + fn wrap_return_type_in_result_simple_with_loop_with_tail() { + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: result +fn foo() -> i32$0 { + let my_var = 5; + loop { + println!("test"); + 5 + } + my_var +} +"#, + r#" +fn foo() -> Result { + let my_var = 5; + loop { + println!("test"); + 5 + } + Ok(my_var) +} +"#, + WrapperKind::Result.label(), + ); + } + + #[test] + fn wrap_return_type_in_result_simple_with_loop_in_let_stmt() { + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: result +fn foo() -> i32$0 { + let my_var = let x = loop { + break 1; + }; + my_var +} +"#, + r#" +fn foo() -> Result { + let my_var = let x = loop { + break 1; + }; + Ok(my_var) +} +"#, + WrapperKind::Result.label(), + ); + } + + #[test] + fn wrap_return_type_in_result_simple_with_tail_block_like_match_return_expr() { + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: result +fn foo() -> i32$0 { + let my_var = 5; + let res = match my_var { + 5 => 42i32, + _ => return 24i32, + }; + res +} +"#, + r#" +fn foo() -> Result { + let my_var = 5; + let res = match my_var { + 5 => 42i32, + _ => return Ok(24i32), + }; + Ok(res) +} +"#, + WrapperKind::Result.label(), + ); + + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: result +fn foo() -> i32$0 { + let my_var = 5; + let res = if my_var == 5 { + 42i32 + } else { + return 24i32; + }; + res +} +"#, + r#" +fn foo() -> Result { + let my_var = 5; + let res = if my_var == 5 { + 42i32 + } else { + return Ok(24i32); + }; + Ok(res) +} +"#, + WrapperKind::Result.label(), + ); + } + + #[test] + fn wrap_return_type_in_result_simple_with_tail_block_like_match_deeper() { + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: result +fn foo() -> i32$0 { + let my_var = 5; + match my_var { + 5 => { + if true { + 42i32 + } else { + 25i32 + } + }, + _ => { + let test = "test"; + if test == "test" { + return bar(); + } + 53i32 + }, + } +} +"#, + r#" +fn foo() -> Result { + let my_var = 5; + match my_var { + 5 => { + if true { + Ok(42i32) + } else { + Ok(25i32) + } + }, + _ => { + let test = "test"; + if test == "test" { + return Ok(bar()); + } + Ok(53i32) + }, + } +} +"#, + WrapperKind::Result.label(), + ); + } + + #[test] + fn wrap_return_type_in_result_simple_with_tail_block_like_early_return() { + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: result +fn foo() -> i$032 { + let test = "test"; + if test == "test" { + return 24i32; + } + 53i32 +} +"#, + r#" +fn foo() -> Result { + let test = "test"; + if test == "test" { + return Ok(24i32); + } + Ok(53i32) +} +"#, + WrapperKind::Result.label(), + ); + } + + #[test] + fn wrap_return_in_result_tail_position() { + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: result +fn foo(num: i32) -> $0i32 { + return num +} +"#, + r#" +fn foo(num: i32) -> Result { + return Ok(num) +} +"#, + WrapperKind::Result.label(), + ); + } + + #[test] + fn wrap_return_type_in_result_simple_with_closure() { + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: result +fn foo(the_field: u32) ->$0 u32 { + let true_closure = || { return true; }; + if the_field < 5 { + let mut i = 0; + if true_closure() { + return 99; + } else { + return 0; + } + } + the_field +} +"#, + r#" +fn foo(the_field: u32) -> Result { + let true_closure = || { return true; }; + if the_field < 5 { + let mut i = 0; + if true_closure() { + return Ok(99); + } else { + return Ok(0); + } + } + Ok(the_field) +} +"#, + WrapperKind::Result.label(), + ); + + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: result +fn foo(the_field: u32) -> u32$0 { + let true_closure = || { + return true; + }; + if the_field < 5 { + let mut i = 0; + + + if true_closure() { + return 99; + } else { + return 0; + } + } + let t = None; + + t.unwrap_or_else(|| the_field) +} +"#, + r#" +fn foo(the_field: u32) -> Result { + let true_closure = || { + return true; + }; + if the_field < 5 { + let mut i = 0; + + + if true_closure() { + return Ok(99); + } else { + return Ok(0); + } + } + let t = None; + + Ok(t.unwrap_or_else(|| the_field)) +} +"#, + WrapperKind::Result.label(), + ); + } + + #[test] + fn wrap_return_type_in_result_simple_with_weird_forms() { + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: result +fn foo() -> i32$0 { + let test = "test"; + if test == "test" { + return 24i32; + } + let mut i = 0; + loop { + if i == 1 { + break 55; + } + i += 1; + } +} +"#, + r#" +fn foo() -> Result { + let test = "test"; + if test == "test" { + return Ok(24i32); + } + let mut i = 0; + loop { + if i == 1 { + break Ok(55); + } + i += 1; + } +} +"#, + WrapperKind::Result.label(), + ); + + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: result +fn foo(the_field: u32) -> u32$0 { + if the_field < 5 { + let mut i = 0; + loop { + if i > 5 { + return 55u32; + } + i += 3; + } + match i { + 5 => return 99, + _ => return 0, + }; + } + the_field +} +"#, + r#" +fn foo(the_field: u32) -> Result { + if the_field < 5 { + let mut i = 0; + loop { + if i > 5 { + return Ok(55u32); + } + i += 3; + } + match i { + 5 => return Ok(99), + _ => return Ok(0), + }; + } + Ok(the_field) +} +"#, + WrapperKind::Result.label(), + ); + + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: result +fn foo(the_field: u32) -> u3$02 { + if the_field < 5 { + let mut i = 0; + match i { + 5 => return 99, + _ => return 0, + } + } + the_field +} +"#, + r#" +fn foo(the_field: u32) -> Result { + if the_field < 5 { + let mut i = 0; + match i { + 5 => return Ok(99), + _ => return Ok(0), + } + } + Ok(the_field) +} +"#, + WrapperKind::Result.label(), + ); + + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: result +fn foo(the_field: u32) -> u32$0 { + if the_field < 5 { + let mut i = 0; + if i == 5 { + return 99 + } else { + return 0 + } + } + the_field +} +"#, + r#" +fn foo(the_field: u32) -> Result { + if the_field < 5 { + let mut i = 0; + if i == 5 { + return Ok(99) + } else { + return Ok(0) + } + } + Ok(the_field) +} +"#, + WrapperKind::Result.label(), + ); + + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: result +fn foo(the_field: u32) -> $0u32 { + if the_field < 5 { + let mut i = 0; + if i == 5 { + return 99; + } else { + return 0; + } + } + the_field +} +"#, + r#" +fn foo(the_field: u32) -> Result { + if the_field < 5 { + let mut i = 0; + if i == 5 { + return Ok(99); + } else { + return Ok(0); + } + } + Ok(the_field) +} +"#, + WrapperKind::Result.label(), + ); + } + + #[test] + fn wrap_return_type_in_local_result_type() { + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: result +type Result = core::result::Result; + +fn foo() -> i3$02 { + return 42i32; +} +"#, + r#" +type Result = core::result::Result; + +fn foo() -> Result { + return Ok(42i32); +} +"#, + WrapperKind::Result.label(), + ); + + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: result +type Result2 = core::result::Result; + +fn foo() -> i3$02 { + return 42i32; +} +"#, + r#" +type Result2 = core::result::Result; + +fn foo() -> Result { + return Ok(42i32); +} +"#, + WrapperKind::Result.label(), + ); + } + + #[test] + fn wrap_return_type_in_imported_local_result_type() { + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: result +mod some_module { + pub type Result = core::result::Result; +} + +use some_module::Result; + +fn foo() -> i3$02 { + return 42i32; +} +"#, + r#" +mod some_module { + pub type Result = core::result::Result; +} + +use some_module::Result; + +fn foo() -> Result { + return Ok(42i32); +} +"#, + WrapperKind::Result.label(), + ); + + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: result +mod some_module { + pub type Result = core::result::Result; +} + +use some_module::*; + +fn foo() -> i3$02 { + return 42i32; +} +"#, + r#" +mod some_module { + pub type Result = core::result::Result; +} + +use some_module::*; + +fn foo() -> Result { + return Ok(42i32); +} +"#, + WrapperKind::Result.label(), + ); + } + + #[test] + fn wrap_return_type_in_local_result_type_from_function_body() { + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: result +fn foo() -> i3$02 { + type Result = core::result::Result; + 0 +} +"#, + r#" +fn foo() -> Result { + type Result = core::result::Result; + Ok(0) +} +"#, + WrapperKind::Result.label(), + ); + } + + #[test] + fn wrap_return_type_in_local_result_type_already_using_alias() { + check_assist_not_applicable_by_label( + wrap_return_type, + r#" +//- minicore: result +pub type Result = core::result::Result; + +fn foo() -> Result { + return Ok(42i32); +} +"#, + WrapperKind::Result.label(), + ); + } + + #[test] + fn wrap_return_type_in_local_result_type_multiple_generics() { + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: result +type Result = core::result::Result; + +fn foo() -> i3$02 { + 0 +} +"#, + r#" +type Result = core::result::Result; + +fn foo() -> Result { + Ok(0) +} +"#, + WrapperKind::Result.label(), + ); + + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: result +type Result = core::result::Result, ()>; + +fn foo() -> i3$02 { + 0 +} + "#, + r#" +type Result = core::result::Result, ()>; + +fn foo() -> Result { + Ok(0) +} + "#, + WrapperKind::Result.label(), + ); + + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: result +type Result<'a, T, E> = core::result::Result, &'a ()>; + +fn foo() -> i3$02 { + 0 +} + "#, + r#" +type Result<'a, T, E> = core::result::Result, &'a ()>; + +fn foo() -> Result<'_, i32, ${0:_}> { + Ok(0) +} + "#, + WrapperKind::Result.label(), + ); + + check_assist_by_label( + wrap_return_type, + r#" +//- minicore: result +type Result = core::result::Result, Bar>; + +fn foo() -> i3$02 { + 0 +} + "#, + r#" +type Result = core::result::Result, Bar>; + +fn foo() -> Result { + Ok(0) +} + "#, + WrapperKind::Result.label(), + ); + } +} diff --git a/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs b/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs deleted file mode 100644 index 8f0e9b4fe0..0000000000 --- a/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs +++ /dev/null @@ -1,1268 +0,0 @@ -use std::iter; - -use hir::HasSource; -use ide_db::{ - famous_defs::FamousDefs, - syntax_helpers::node_ext::{for_each_tail_expr, walk_expr}, -}; -use itertools::Itertools; -use syntax::{ - ast::{self, make, Expr, HasGenericParams}, - match_ast, ted, AstNode, ToSmolStr, -}; - -use crate::{AssistContext, AssistId, AssistKind, Assists}; - -// Assist: wrap_return_type_in_result -// -// Wrap the function's return type into Result. -// -// ``` -// # //- minicore: result -// fn foo() -> i32$0 { 42i32 } -// ``` -// -> -// ``` -// fn foo() -> Result { Ok(42i32) } -// ``` -pub(crate) fn wrap_return_type_in_result(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { - let ret_type = ctx.find_node_at_offset::()?; - let parent = ret_type.syntax().parent()?; - let body = match_ast! { - match parent { - ast::Fn(func) => func.body()?, - ast::ClosureExpr(closure) => match closure.body()? { - Expr::BlockExpr(block) => block, - // closures require a block when a return type is specified - _ => return None, - }, - _ => return None, - } - }; - - let type_ref = &ret_type.ty()?; - let core_result = - FamousDefs(&ctx.sema, ctx.sema.scope(type_ref.syntax())?.krate()).core_result_Result()?; - - let ty = ctx.sema.resolve_type(type_ref)?.as_adt(); - if matches!(ty, Some(hir::Adt::Enum(ret_type)) if ret_type == core_result) { - // The return type is already wrapped in a Result - cov_mark::hit!(wrap_return_type_in_result_simple_return_type_already_result); - return None; - } - - acc.add( - AssistId("wrap_return_type_in_result", AssistKind::RefactorRewrite), - "Wrap return type in Result", - type_ref.syntax().text_range(), - |edit| { - let new_result_ty = result_type(ctx, &core_result, type_ref).clone_for_update(); - let body = edit.make_mut(ast::Expr::BlockExpr(body)); - - let mut exprs_to_wrap = Vec::new(); - let tail_cb = &mut |e: &_| tail_cb_impl(&mut exprs_to_wrap, e); - walk_expr(&body, &mut |expr| { - if let Expr::ReturnExpr(ret_expr) = expr { - if let Some(ret_expr_arg) = &ret_expr.expr() { - for_each_tail_expr(ret_expr_arg, tail_cb); - } - } - }); - for_each_tail_expr(&body, tail_cb); - - for ret_expr_arg in exprs_to_wrap { - let ok_wrapped = make::expr_call( - make::expr_path(make::ext::ident_path("Ok")), - make::arg_list(iter::once(ret_expr_arg.clone())), - ) - .clone_for_update(); - ted::replace(ret_expr_arg.syntax(), ok_wrapped.syntax()); - } - - let old_result_ty = edit.make_mut(type_ref.clone()); - ted::replace(old_result_ty.syntax(), new_result_ty.syntax()); - - // Add a placeholder snippet at the first generic argument that doesn't equal the return type. - // This is normally the error type, but that may not be the case when we inserted a type alias. - let args = new_result_ty.syntax().descendants().find_map(ast::GenericArgList::cast); - let error_type_arg = args.and_then(|list| { - list.generic_args().find(|arg| match arg { - ast::GenericArg::TypeArg(_) => arg.syntax().text() != type_ref.syntax().text(), - ast::GenericArg::LifetimeArg(_) => false, - _ => true, - }) - }); - if let Some(error_type_arg) = error_type_arg { - if let Some(cap) = ctx.config.snippet_cap { - edit.add_placeholder_snippet(cap, error_type_arg); - } - } - }, - ) -} - -fn result_type( - ctx: &AssistContext<'_>, - core_result: &hir::Enum, - ret_type: &ast::Type, -) -> ast::Type { - // Try to find a Result type alias in the current scope (shadowing the default). - let result_path = hir::ModPath::from_segments( - hir::PathKind::Plain, - iter::once(hir::Name::new_symbol_root(hir::sym::Result.clone())), - ); - let alias = ctx.sema.resolve_mod_path(ret_type.syntax(), &result_path).and_then(|def| { - def.filter_map(|def| match def.as_module_def()? { - hir::ModuleDef::TypeAlias(alias) => { - let enum_ty = alias.ty(ctx.db()).as_adt()?.as_enum()?; - (&enum_ty == core_result).then_some(alias) - } - _ => None, - }) - .find_map(|alias| { - let mut inserted_ret_type = false; - let generic_params = alias - .source(ctx.db())? - .value - .generic_param_list()? - .generic_params() - .map(|param| match param { - // Replace the very first type parameter with the functions return type. - ast::GenericParam::TypeParam(_) if !inserted_ret_type => { - inserted_ret_type = true; - ret_type.to_smolstr() - } - ast::GenericParam::LifetimeParam(_) => make::lifetime("'_").to_smolstr(), - _ => make::ty_placeholder().to_smolstr(), - }) - .join(", "); - - let name = alias.name(ctx.db()); - let name = name.as_str(); - Some(make::ty(&format!("{name}<{generic_params}>"))) - }) - }); - // If there is no applicable alias in scope use the default Result type. - alias.unwrap_or_else(|| make::ext::ty_result(ret_type.clone(), make::ty_placeholder())) -} - -fn tail_cb_impl(acc: &mut Vec, e: &ast::Expr) { - match e { - Expr::BreakExpr(break_expr) => { - if let Some(break_expr_arg) = break_expr.expr() { - for_each_tail_expr(&break_expr_arg, &mut |e| tail_cb_impl(acc, e)) - } - } - Expr::ReturnExpr(_) => { - // all return expressions have already been handled by the walk loop - } - e => acc.push(e.clone()), - } -} - -#[cfg(test)] -mod tests { - use crate::tests::{check_assist, check_assist_not_applicable}; - - use super::*; - - #[test] - fn wrap_return_type_in_result_simple() { - check_assist( - wrap_return_type_in_result, - r#" -//- minicore: result -fn foo() -> i3$02 { - let test = "test"; - return 42i32; -} -"#, - r#" -fn foo() -> Result { - let test = "test"; - return Ok(42i32); -} -"#, - ); - } - - #[test] - fn wrap_return_type_break_split_tail() { - check_assist( - wrap_return_type_in_result, - r#" -//- minicore: result -fn foo() -> i3$02 { - loop { - break if true { - 1 - } else { - 0 - }; - } -} -"#, - r#" -fn foo() -> Result { - loop { - break if true { - Ok(1) - } else { - Ok(0) - }; - } -} -"#, - ); - } - - #[test] - fn wrap_return_type_in_result_simple_closure() { - check_assist( - wrap_return_type_in_result, - r#" -//- minicore: result -fn foo() { - || -> i32$0 { - let test = "test"; - return 42i32; - }; -} -"#, - r#" -fn foo() { - || -> Result { - let test = "test"; - return Ok(42i32); - }; -} -"#, - ); - } - - #[test] - fn wrap_return_type_in_result_simple_return_type_bad_cursor() { - check_assist_not_applicable( - wrap_return_type_in_result, - r#" -//- minicore: result -fn foo() -> i32 { - let test = "test";$0 - return 42i32; -} -"#, - ); - } - - #[test] - fn wrap_return_type_in_result_simple_return_type_bad_cursor_closure() { - check_assist_not_applicable( - wrap_return_type_in_result, - r#" -//- minicore: result -fn foo() { - || -> i32 { - let test = "test";$0 - return 42i32; - }; -} -"#, - ); - } - - #[test] - fn wrap_return_type_in_result_closure_non_block() { - check_assist_not_applicable( - wrap_return_type_in_result, - r#" -//- minicore: result -fn foo() { || -> i$032 3; } -"#, - ); - } - - #[test] - fn wrap_return_type_in_result_simple_return_type_already_result_std() { - check_assist_not_applicable( - wrap_return_type_in_result, - r#" -//- minicore: result -fn foo() -> core::result::Result { - let test = "test"; - return 42i32; -} -"#, - ); - } - - #[test] - fn wrap_return_type_in_result_simple_return_type_already_result() { - cov_mark::check!(wrap_return_type_in_result_simple_return_type_already_result); - check_assist_not_applicable( - wrap_return_type_in_result, - r#" -//- minicore: result -fn foo() -> Result { - let test = "test"; - return 42i32; -} -"#, - ); - } - - #[test] - fn wrap_return_type_in_result_simple_return_type_already_result_closure() { - check_assist_not_applicable( - wrap_return_type_in_result, - r#" -//- minicore: result -fn foo() { - || -> Result { - let test = "test"; - return 42i32; - }; -} -"#, - ); - } - - #[test] - fn wrap_return_type_in_result_simple_with_cursor() { - check_assist( - wrap_return_type_in_result, - r#" -//- minicore: result -fn foo() -> $0i32 { - let test = "test"; - return 42i32; -} -"#, - r#" -fn foo() -> Result { - let test = "test"; - return Ok(42i32); -} -"#, - ); - } - - #[test] - fn wrap_return_type_in_result_simple_with_tail() { - check_assist( - wrap_return_type_in_result, - r#" -//- minicore: result -fn foo() ->$0 i32 { - let test = "test"; - 42i32 -} -"#, - r#" -fn foo() -> Result { - let test = "test"; - Ok(42i32) -} -"#, - ); - } - - #[test] - fn wrap_return_type_in_result_simple_with_tail_closure() { - check_assist( - wrap_return_type_in_result, - r#" -//- minicore: result -fn foo() { - || ->$0 i32 { - let test = "test"; - 42i32 - }; -} -"#, - r#" -fn foo() { - || -> Result { - let test = "test"; - Ok(42i32) - }; -} -"#, - ); - } - - #[test] - fn wrap_return_type_in_result_simple_with_tail_only() { - check_assist( - wrap_return_type_in_result, - r#" -//- minicore: result -fn foo() -> i32$0 { 42i32 } -"#, - r#" -fn foo() -> Result { Ok(42i32) } -"#, - ); - } - - #[test] - fn wrap_return_type_in_result_simple_with_tail_block_like() { - check_assist( - wrap_return_type_in_result, - r#" -//- minicore: result -fn foo() -> i32$0 { - if true { - 42i32 - } else { - 24i32 - } -} -"#, - r#" -fn foo() -> Result { - if true { - Ok(42i32) - } else { - Ok(24i32) - } -} -"#, - ); - } - - #[test] - fn wrap_return_type_in_result_simple_without_block_closure() { - check_assist( - wrap_return_type_in_result, - r#" -//- minicore: result -fn foo() { - || -> i32$0 { - if true { - 42i32 - } else { - 24i32 - } - }; -} -"#, - r#" -fn foo() { - || -> Result { - if true { - Ok(42i32) - } else { - Ok(24i32) - } - }; -} -"#, - ); - } - - #[test] - fn wrap_return_type_in_result_simple_with_nested_if() { - check_assist( - wrap_return_type_in_result, - r#" -//- minicore: result -fn foo() -> i32$0 { - if true { - if false { - 1 - } else { - 2 - } - } else { - 24i32 - } -} -"#, - r#" -fn foo() -> Result { - if true { - if false { - Ok(1) - } else { - Ok(2) - } - } else { - Ok(24i32) - } -} -"#, - ); - } - - #[test] - fn wrap_return_type_in_result_simple_with_await() { - check_assist( - wrap_return_type_in_result, - r#" -//- minicore: result -async fn foo() -> i$032 { - if true { - if false { - 1.await - } else { - 2.await - } - } else { - 24i32.await - } -} -"#, - r#" -async fn foo() -> Result { - if true { - if false { - Ok(1.await) - } else { - Ok(2.await) - } - } else { - Ok(24i32.await) - } -} -"#, - ); - } - - #[test] - fn wrap_return_type_in_result_simple_with_array() { - check_assist( - wrap_return_type_in_result, - r#" -//- minicore: result -fn foo() -> [i32;$0 3] { [1, 2, 3] } -"#, - r#" -fn foo() -> Result<[i32; 3], ${0:_}> { Ok([1, 2, 3]) } -"#, - ); - } - - #[test] - fn wrap_return_type_in_result_simple_with_cast() { - check_assist( - wrap_return_type_in_result, - r#" -//- minicore: result -fn foo() -$0> i32 { - if true { - if false { - 1 as i32 - } else { - 2 as i32 - } - } else { - 24 as i32 - } -} -"#, - r#" -fn foo() -> Result { - if true { - if false { - Ok(1 as i32) - } else { - Ok(2 as i32) - } - } else { - Ok(24 as i32) - } -} -"#, - ); - } - - #[test] - fn wrap_return_type_in_result_simple_with_tail_block_like_match() { - check_assist( - wrap_return_type_in_result, - r#" -//- minicore: result -fn foo() -> i32$0 { - let my_var = 5; - match my_var { - 5 => 42i32, - _ => 24i32, - } -} -"#, - r#" -fn foo() -> Result { - let my_var = 5; - match my_var { - 5 => Ok(42i32), - _ => Ok(24i32), - } -} -"#, - ); - } - - #[test] - fn wrap_return_type_in_result_simple_with_loop_with_tail() { - check_assist( - wrap_return_type_in_result, - r#" -//- minicore: result -fn foo() -> i32$0 { - let my_var = 5; - loop { - println!("test"); - 5 - } - my_var -} -"#, - r#" -fn foo() -> Result { - let my_var = 5; - loop { - println!("test"); - 5 - } - Ok(my_var) -} -"#, - ); - } - - #[test] - fn wrap_return_type_in_result_simple_with_loop_in_let_stmt() { - check_assist( - wrap_return_type_in_result, - r#" -//- minicore: result -fn foo() -> i32$0 { - let my_var = let x = loop { - break 1; - }; - my_var -} -"#, - r#" -fn foo() -> Result { - let my_var = let x = loop { - break 1; - }; - Ok(my_var) -} -"#, - ); - } - - #[test] - fn wrap_return_type_in_result_simple_with_tail_block_like_match_return_expr() { - check_assist( - wrap_return_type_in_result, - r#" -//- minicore: result -fn foo() -> i32$0 { - let my_var = 5; - let res = match my_var { - 5 => 42i32, - _ => return 24i32, - }; - res -} -"#, - r#" -fn foo() -> Result { - let my_var = 5; - let res = match my_var { - 5 => 42i32, - _ => return Ok(24i32), - }; - Ok(res) -} -"#, - ); - - check_assist( - wrap_return_type_in_result, - r#" -//- minicore: result -fn foo() -> i32$0 { - let my_var = 5; - let res = if my_var == 5 { - 42i32 - } else { - return 24i32; - }; - res -} -"#, - r#" -fn foo() -> Result { - let my_var = 5; - let res = if my_var == 5 { - 42i32 - } else { - return Ok(24i32); - }; - Ok(res) -} -"#, - ); - } - - #[test] - fn wrap_return_type_in_result_simple_with_tail_block_like_match_deeper() { - check_assist( - wrap_return_type_in_result, - r#" -//- minicore: result -fn foo() -> i32$0 { - let my_var = 5; - match my_var { - 5 => { - if true { - 42i32 - } else { - 25i32 - } - }, - _ => { - let test = "test"; - if test == "test" { - return bar(); - } - 53i32 - }, - } -} -"#, - r#" -fn foo() -> Result { - let my_var = 5; - match my_var { - 5 => { - if true { - Ok(42i32) - } else { - Ok(25i32) - } - }, - _ => { - let test = "test"; - if test == "test" { - return Ok(bar()); - } - Ok(53i32) - }, - } -} -"#, - ); - } - - #[test] - fn wrap_return_type_in_result_simple_with_tail_block_like_early_return() { - check_assist( - wrap_return_type_in_result, - r#" -//- minicore: result -fn foo() -> i$032 { - let test = "test"; - if test == "test" { - return 24i32; - } - 53i32 -} -"#, - r#" -fn foo() -> Result { - let test = "test"; - if test == "test" { - return Ok(24i32); - } - Ok(53i32) -} -"#, - ); - } - - #[test] - fn wrap_return_in_tail_position() { - check_assist( - wrap_return_type_in_result, - r#" -//- minicore: result -fn foo(num: i32) -> $0i32 { - return num -} -"#, - r#" -fn foo(num: i32) -> Result { - return Ok(num) -} -"#, - ); - } - - #[test] - fn wrap_return_type_in_result_simple_with_closure() { - check_assist( - wrap_return_type_in_result, - r#" -//- minicore: result -fn foo(the_field: u32) ->$0 u32 { - let true_closure = || { return true; }; - if the_field < 5 { - let mut i = 0; - if true_closure() { - return 99; - } else { - return 0; - } - } - the_field -} -"#, - r#" -fn foo(the_field: u32) -> Result { - let true_closure = || { return true; }; - if the_field < 5 { - let mut i = 0; - if true_closure() { - return Ok(99); - } else { - return Ok(0); - } - } - Ok(the_field) -} -"#, - ); - - check_assist( - wrap_return_type_in_result, - r#" -//- minicore: result -fn foo(the_field: u32) -> u32$0 { - let true_closure = || { - return true; - }; - if the_field < 5 { - let mut i = 0; - - - if true_closure() { - return 99; - } else { - return 0; - } - } - let t = None; - - t.unwrap_or_else(|| the_field) -} -"#, - r#" -fn foo(the_field: u32) -> Result { - let true_closure = || { - return true; - }; - if the_field < 5 { - let mut i = 0; - - - if true_closure() { - return Ok(99); - } else { - return Ok(0); - } - } - let t = None; - - Ok(t.unwrap_or_else(|| the_field)) -} -"#, - ); - } - - #[test] - fn wrap_return_type_in_result_simple_with_weird_forms() { - check_assist( - wrap_return_type_in_result, - r#" -//- minicore: result -fn foo() -> i32$0 { - let test = "test"; - if test == "test" { - return 24i32; - } - let mut i = 0; - loop { - if i == 1 { - break 55; - } - i += 1; - } -} -"#, - r#" -fn foo() -> Result { - let test = "test"; - if test == "test" { - return Ok(24i32); - } - let mut i = 0; - loop { - if i == 1 { - break Ok(55); - } - i += 1; - } -} -"#, - ); - - check_assist( - wrap_return_type_in_result, - r#" -//- minicore: result -fn foo(the_field: u32) -> u32$0 { - if the_field < 5 { - let mut i = 0; - loop { - if i > 5 { - return 55u32; - } - i += 3; - } - match i { - 5 => return 99, - _ => return 0, - }; - } - the_field -} -"#, - r#" -fn foo(the_field: u32) -> Result { - if the_field < 5 { - let mut i = 0; - loop { - if i > 5 { - return Ok(55u32); - } - i += 3; - } - match i { - 5 => return Ok(99), - _ => return Ok(0), - }; - } - Ok(the_field) -} -"#, - ); - - check_assist( - wrap_return_type_in_result, - r#" -//- minicore: result -fn foo(the_field: u32) -> u3$02 { - if the_field < 5 { - let mut i = 0; - match i { - 5 => return 99, - _ => return 0, - } - } - the_field -} -"#, - r#" -fn foo(the_field: u32) -> Result { - if the_field < 5 { - let mut i = 0; - match i { - 5 => return Ok(99), - _ => return Ok(0), - } - } - Ok(the_field) -} -"#, - ); - - check_assist( - wrap_return_type_in_result, - r#" -//- minicore: result -fn foo(the_field: u32) -> u32$0 { - if the_field < 5 { - let mut i = 0; - if i == 5 { - return 99 - } else { - return 0 - } - } - the_field -} -"#, - r#" -fn foo(the_field: u32) -> Result { - if the_field < 5 { - let mut i = 0; - if i == 5 { - return Ok(99) - } else { - return Ok(0) - } - } - Ok(the_field) -} -"#, - ); - - check_assist( - wrap_return_type_in_result, - r#" -//- minicore: result -fn foo(the_field: u32) -> $0u32 { - if the_field < 5 { - let mut i = 0; - if i == 5 { - return 99; - } else { - return 0; - } - } - the_field -} -"#, - r#" -fn foo(the_field: u32) -> Result { - if the_field < 5 { - let mut i = 0; - if i == 5 { - return Ok(99); - } else { - return Ok(0); - } - } - Ok(the_field) -} -"#, - ); - } - - #[test] - fn wrap_return_type_in_local_result_type() { - check_assist( - wrap_return_type_in_result, - r#" -//- minicore: result -type Result = core::result::Result; - -fn foo() -> i3$02 { - return 42i32; -} -"#, - r#" -type Result = core::result::Result; - -fn foo() -> Result { - return Ok(42i32); -} -"#, - ); - - check_assist( - wrap_return_type_in_result, - r#" -//- minicore: result -type Result2 = core::result::Result; - -fn foo() -> i3$02 { - return 42i32; -} -"#, - r#" -type Result2 = core::result::Result; - -fn foo() -> Result { - return Ok(42i32); -} -"#, - ); - } - - #[test] - fn wrap_return_type_in_imported_local_result_type() { - check_assist( - wrap_return_type_in_result, - r#" -//- minicore: result -mod some_module { - pub type Result = core::result::Result; -} - -use some_module::Result; - -fn foo() -> i3$02 { - return 42i32; -} -"#, - r#" -mod some_module { - pub type Result = core::result::Result; -} - -use some_module::Result; - -fn foo() -> Result { - return Ok(42i32); -} -"#, - ); - - check_assist( - wrap_return_type_in_result, - r#" -//- minicore: result -mod some_module { - pub type Result = core::result::Result; -} - -use some_module::*; - -fn foo() -> i3$02 { - return 42i32; -} -"#, - r#" -mod some_module { - pub type Result = core::result::Result; -} - -use some_module::*; - -fn foo() -> Result { - return Ok(42i32); -} -"#, - ); - } - - #[test] - fn wrap_return_type_in_local_result_type_from_function_body() { - check_assist( - wrap_return_type_in_result, - r#" -//- minicore: result -fn foo() -> i3$02 { - type Result = core::result::Result; - 0 -} -"#, - r#" -fn foo() -> Result { - type Result = core::result::Result; - Ok(0) -} -"#, - ); - } - - #[test] - fn wrap_return_type_in_local_result_type_already_using_alias() { - check_assist_not_applicable( - wrap_return_type_in_result, - r#" -//- minicore: result -pub type Result = core::result::Result; - -fn foo() -> Result { - return Ok(42i32); -} -"#, - ); - } - - #[test] - fn wrap_return_type_in_local_result_type_multiple_generics() { - check_assist( - wrap_return_type_in_result, - r#" -//- minicore: result -type Result = core::result::Result; - -fn foo() -> i3$02 { - 0 -} -"#, - r#" -type Result = core::result::Result; - -fn foo() -> Result { - Ok(0) -} -"#, - ); - - check_assist( - wrap_return_type_in_result, - r#" -//- minicore: result -type Result = core::result::Result, ()>; - -fn foo() -> i3$02 { - 0 -} - "#, - r#" -type Result = core::result::Result, ()>; - -fn foo() -> Result { - Ok(0) -} - "#, - ); - - check_assist( - wrap_return_type_in_result, - r#" -//- minicore: result -type Result<'a, T, E> = core::result::Result, &'a ()>; - -fn foo() -> i3$02 { - 0 -} - "#, - r#" -type Result<'a, T, E> = core::result::Result, &'a ()>; - -fn foo() -> Result<'_, i32, ${0:_}> { - Ok(0) -} - "#, - ); - - check_assist( - wrap_return_type_in_result, - r#" -//- minicore: result -type Result = core::result::Result, Bar>; - -fn foo() -> i3$02 { - 0 -} - "#, - r#" -type Result = core::result::Result, Bar>; - -fn foo() -> Result { - Ok(0) -} - "#, - ); - } -} diff --git a/crates/ide-assists/src/lib.rs b/crates/ide-assists/src/lib.rs index c98655b423..22620816d5 100644 --- a/crates/ide-assists/src/lib.rs +++ b/crates/ide-assists/src/lib.rs @@ -223,9 +223,9 @@ mod handlers { mod unnecessary_async; mod unqualify_method_call; mod unwrap_block; - mod unwrap_result_return_type; + mod unwrap_return_type; mod unwrap_tuple; - mod wrap_return_type_in_result; + mod wrap_return_type; mod wrap_unwrap_cfg_attr; pub(crate) fn all() -> &'static [Handler] { @@ -355,10 +355,10 @@ mod handlers { unmerge_use::unmerge_use, unnecessary_async::unnecessary_async, unwrap_block::unwrap_block, - unwrap_result_return_type::unwrap_result_return_type, + unwrap_return_type::unwrap_return_type, unwrap_tuple::unwrap_tuple, unqualify_method_call::unqualify_method_call, - wrap_return_type_in_result::wrap_return_type_in_result, + wrap_return_type::wrap_return_type, wrap_unwrap_cfg_attr::wrap_unwrap_cfg_attr, // These are manually sorted for better priorities. By default, diff --git a/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs index 48e12a8107..933d45d750 100644 --- a/crates/ide-assists/src/tests/generated.rs +++ b/crates/ide-assists/src/tests/generated.rs @@ -3264,6 +3264,20 @@ fn foo() { ) } +#[test] +fn doctest_unwrap_option_return_type() { + check_doc_test( + "unwrap_option_return_type", + r#####" +//- minicore: option +fn foo() -> Option$0 { Some(42i32) } +"#####, + r#####" +fn foo() -> i32 { 42i32 } +"#####, + ) +} + #[test] fn doctest_unwrap_result_return_type() { check_doc_test( @@ -3297,6 +3311,20 @@ fn main() { ) } +#[test] +fn doctest_wrap_return_type_in_option() { + check_doc_test( + "wrap_return_type_in_option", + r#####" +//- minicore: option +fn foo() -> i32$0 { 42i32 } +"#####, + r#####" +fn foo() -> Option { Some(42i32) } +"#####, + ) +} + #[test] fn doctest_wrap_return_type_in_result() { check_doc_test(