Unnamed repository; edit this file 'description' to name the repository.
Auto merge of #18294 - Giga-Bowser:master, r=Veykril
Add wrap/unwrap return type in Option I pretty much just copied over the code and tests for wrapping/unwrapping return types in `Result` and then did a bunch of find and replace changes. I handled unwrapping statements returning `None` by just replacing `None` with the unit type, but I'm open to suggestions for more intuitive behavior here.
bors 2024-10-21
parent dc2a348 · parent 2c82386 · commit 546da8a
-rw-r--r--crates/ide-assists/src/handlers/unwrap_result_return_type.rs1115
-rw-r--r--crates/ide-assists/src/handlers/unwrap_return_type.rs2229
-rw-r--r--crates/ide-assists/src/handlers/wrap_return_type.rs2457
-rw-r--r--crates/ide-assists/src/handlers/wrap_return_type_in_result.rs1268
-rw-r--r--crates/ide-assists/src/lib.rs8
-rw-r--r--crates/ide-assists/src/tests/generated.rs28
6 files changed, 4718 insertions, 2387 deletions
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<i32>$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::<ast::RetType>()?;
- 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<ast::Expr>, 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<T, E>`.
-fn unwrap_result_type(ty: &ast::Type) -> Option<ast::Type> {
- 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<i3$02> {
- 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<dyn Error$0>> {
- Ok(())
-}
-"#,
- r#"
-fn foo() {
-}
-"#,
- );
-
- // Unformatted return type
- check_assist(
- unwrap_result_return_type,
- r#"
-//- minicore: result
-fn foo() -> Result<(), Box<dyn Error$0>>{
- 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<i32, Box<dyn Error$0>> {
- 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<i3$02, String> {
- 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<i32$0> {
- 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<i32> {
- 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<i32, String> {
- 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<i32$0> { 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<i32>$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<i32, String>$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<i32>$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<i$032> {
- 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<i32> {
- 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<i32$0> {
- 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<i32$0> {
- 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<i32$0> {
- 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<i32>$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<i32$0> {
- 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<i32$0> {
- 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<i32$0> {
- 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<i32, String> {
- 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<u32$0> {
- 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<u32$0> {
- 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<i32$0> {
- 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<u32$0> {
- 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<u32$0> {
- 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<u32$0> {
- 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<u3$02> {
- 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<Option<i32$0>, ()> {
- Ok(Some(42))
-}
-"#,
- r#"
-fn foo() -> Option<i32> {
- Some(42)
-}
-"#,
- );
-
- check_assist(
- unwrap_result_return_type,
- r#"
-//- minicore: result, option
-fn foo() -> Result<Option<Result<i32$0, ()>>, ()> {
- Ok(None)
-}
-"#,
- r#"
-fn foo() -> Option<Result<i32, ()>> {
- None
-}
-"#,
- );
-
- check_assist(
- unwrap_result_return_type,
- r#"
-//- minicore: result, option, iterators
-fn foo() -> Result<impl Iterator<Item = i32>$0, ()> {
- Ok(Some(42).into_iter())
-}
-"#,
- r#"
-fn foo() -> impl Iterator<Item = i32> {
- 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<i32>$0 { Some(42i32) }
+// ```
+// ->
+// ```
+// fn foo() -> i32 { 42i32 }
+// ```
+
+// Assist: unwrap_result_return_type
+//
+// Unwrap the function's return type.
+//
+// ```
+// # //- minicore: result
+// fn foo() -> Result<i32>$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::<ast::RetType>()?;
+ 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<hir::Enum> {
+ match self {
+ UnwrapperKind::Option => famous_defs.core_option_Option(),
+ UnwrapperKind::Result => famous_defs.core_result_Result(),
+ }
+ }
+}
+
+fn tail_cb_impl(acc: &mut Vec<ast::Expr>, 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<T>` or `Result<T, E>`.
+fn extract_wrapped_type(ty: &ast::Type) -> Option<ast::Type> {
+ 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<i3$02> {
+ 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<i3$02> {
+ 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<i3$02> {
+ 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<i3$02> {
+ 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<i32$0> {
+ 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<i32> {
+ 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<i32> {
+ 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<i32$0> { 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<i32>$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<i32>$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<i32>$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<i$032> {
+ 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<i32> {
+ 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<i32$0> {
+ 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<i32$0> {
+ 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<i32$0> {
+ 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<i32>$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<i32$0> {
+ 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<i32$0> {
+ 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<i32$0> {
+ 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<i32> {
+ 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<u32$0> {
+ 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<u32$0> {
+ 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<i32$0> {
+ 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<u32$0> {
+ 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<u32$0> {
+ 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<u32$0> {
+ 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<u3$02> {
+ 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<Result<i32$0, ()>> {
+ Some(Ok(42))
+}
+"#,
+ r#"
+fn foo() -> Result<i32, ()> {
+ Ok(42)
+}
+"#,
+ "Unwrap Option return type",
+ );
+
+ check_assist_by_label(
+ unwrap_return_type,
+ r#"
+//- minicore: option, result
+fn foo() -> Option<Result<Option<i32$0>, ()>> {
+ Some(Err())
+}
+"#,
+ r#"
+fn foo() -> Result<Option<i32>, ()> {
+ Err()
+}
+"#,
+ "Unwrap Option return type",
+ );
+
+ check_assist_by_label(
+ unwrap_return_type,
+ r#"
+//- minicore: option, result, iterators
+fn foo() -> Option<impl Iterator<Item = i32>$0> {
+ Some(Some(42).into_iter())
+}
+"#,
+ r#"
+fn foo() -> impl Iterator<Item = i32> {
+ 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<i3$02> {
+ 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<dyn Error$0>> {
+ 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<dyn Error$0>>{
+ 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<i32, Box<dyn Error$0>> {
+ 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<i3$02, String> {
+ 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<i32$0> {
+ 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<i32> {
+ 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<i32, String> {
+ 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<i32$0> { 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<i32>$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<i32, String>$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<i32>$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<i$032> {
+ 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<i32> {
+ 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<i32$0> {
+ 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<i32$0> {
+ 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<i32$0> {
+ 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<i32>$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<i32$0> {
+ 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<i32$0> {
+ 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<i32$0> {
+ 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<i32, String> {
+ 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<u32$0> {
+ 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<u32$0> {
+ 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<i32$0> {
+ 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<u32$0> {
+ 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<u32$0> {
+ 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<u32$0> {
+ 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<u3$02> {
+ 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<Option<i32$0>, ()> {
+ Ok(Some(42))
+}
+"#,
+ r#"
+fn foo() -> Option<i32> {
+ Some(42)
+}
+"#,
+ "Unwrap Result return type",
+ );
+
+ check_assist_by_label(
+ unwrap_return_type,
+ r#"
+//- minicore: result, option
+fn foo() -> Result<Option<Result<i32$0, ()>>, ()> {
+ Ok(None)
+}
+"#,
+ r#"
+fn foo() -> Option<Result<i32, ()>> {
+ None
+}
+"#,
+ "Unwrap Result return type",
+ );
+
+ check_assist_by_label(
+ unwrap_return_type,
+ r#"
+//- minicore: result, option, iterators
+fn foo() -> Result<impl Iterator<Item = i32>$0, ()> {
+ Ok(Some(42).into_iter())
+}
+"#,
+ r#"
+fn foo() -> impl Iterator<Item = i32> {
+ 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<i32> { 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<i32, ${0:_}> { Ok(42i32) }
+// ```
+
+pub(crate) fn wrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+ let ret_type = ctx.find_node_at_offset::<ast::RetType>()?;
+ 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<hir::Enum> {
+ 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<ast::Type> {
+ 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<ast::Expr>, 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<i32> {
+ 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<i32> {
+ 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<i32> {
+ 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<i32$0> {
+ 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<i32$0> {
+ 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<i32$0, String> {
+ 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<i32> {
+ 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<i32> {
+ 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<i32> {
+ 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<i32> { 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<i32> {
+ 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<i32> {
+ 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<i32> {
+ 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<i32> {
+ 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<i32> {
+ 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<i32> {
+ 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<i32> {
+ 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<i32> {
+ 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<i32> {
+ 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<i32> {
+ 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<i32> {
+ 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<i32> {
+ 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<i32> {
+ 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<u32> {
+ 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<u32> {
+ 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<i32> {
+ 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<u32> {
+ 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<u32> {
+ 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<u32> {
+ 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<u32> {
+ 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<T> = core::option::Option<T>;
+
+fn foo() -> i3$02 {
+ return 42i32;
+}
+"#,
+ r#"
+type Option<T> = core::option::Option<T>;
+
+fn foo() -> Option<i32> {
+ return Some(42i32);
+}
+"#,
+ WrapperKind::Option.label(),
+ );
+
+ check_assist_by_label(
+ wrap_return_type,
+ r#"
+//- minicore: option
+type Option2<T> = core::option::Option<T>;
+
+fn foo() -> i3$02 {
+ return 42i32;
+}
+"#,
+ r#"
+type Option2<T> = core::option::Option<T>;
+
+fn foo() -> Option<i32> {
+ 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<T> = core::option::Option<T>;
+}
+
+use some_module::Option;
+
+fn foo() -> i3$02 {
+ return 42i32;
+}
+"#,
+ r#"
+mod some_module {
+ pub type Option<T> = core::option::Option<T>;
+}
+
+use some_module::Option;
+
+fn foo() -> Option<i32> {
+ return Some(42i32);
+}
+"#,
+ WrapperKind::Option.label(),
+ );
+
+ check_assist_by_label(
+ wrap_return_type,
+ r#"
+//- minicore: option
+mod some_module {
+ pub type Option<T> = core::option::Option<T>;
+}
+
+use some_module::*;
+
+fn foo() -> i3$02 {
+ return 42i32;
+}
+"#,
+ r#"
+mod some_module {
+ pub type Option<T> = core::option::Option<T>;
+}
+
+use some_module::*;
+
+fn foo() -> Option<i32> {
+ 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<T> = core::option::Option<T>;
+ 0
+}
+"#,
+ r#"
+fn foo() -> Option<i32> {
+ type Option<T> = core::option::Option<T>;
+ 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<T> = core::option::Option<T>;
+
+fn foo() -> Option<i3$02> {
+ 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<i32, ${0:_}> {
+ 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<i32, ${0:_}> {
+ 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<i32, ${0:_}> {
+ 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<i32$0, String> {
+ 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<i32$0, String> {
+ 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<i32$0, String> {
+ 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<i32, ${0:_}> {
+ 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<i32, ${0:_}> {
+ 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<i32, ${0:_}> {
+ 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<i32, ${0:_}> { 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<i32, ${0:_}> {
+ 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<i32, ${0:_}> {
+ 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<i32, ${0:_}> {
+ 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<i32, ${0:_}> {
+ 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<i32, ${0:_}> {
+ 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<i32, ${0:_}> {
+ 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<i32, ${0:_}> {
+ 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<i32, ${0:_}> {
+ 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<i32, ${0:_}> {
+ 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<i32, ${0:_}> {
+ 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<i32, ${0:_}> {
+ 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<i32, ${0:_}> {
+ 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<i32, ${0:_}> {
+ 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<u32, ${0:_}> {
+ 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<u32, ${0:_}> {
+ 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<i32, ${0:_}> {
+ 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<u32, ${0:_}> {
+ 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<u32, ${0:_}> {
+ 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<u32, ${0:_}> {
+ 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<u32, ${0:_}> {
+ 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<T> = core::result::Result<T, ()>;
+
+fn foo() -> i3$02 {
+ return 42i32;
+}
+"#,
+ r#"
+type Result<T> = core::result::Result<T, ()>;
+
+fn foo() -> Result<i32> {
+ return Ok(42i32);
+}
+"#,
+ WrapperKind::Result.label(),
+ );
+
+ check_assist_by_label(
+ wrap_return_type,
+ r#"
+//- minicore: result
+type Result2<T> = core::result::Result<T, ()>;
+
+fn foo() -> i3$02 {
+ return 42i32;
+}
+"#,
+ r#"
+type Result2<T> = core::result::Result<T, ()>;
+
+fn foo() -> Result<i32, ${0:_}> {
+ 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<T> = core::result::Result<T, ()>;
+}
+
+use some_module::Result;
+
+fn foo() -> i3$02 {
+ return 42i32;
+}
+"#,
+ r#"
+mod some_module {
+ pub type Result<T> = core::result::Result<T, ()>;
+}
+
+use some_module::Result;
+
+fn foo() -> Result<i32> {
+ return Ok(42i32);
+}
+"#,
+ WrapperKind::Result.label(),
+ );
+
+ check_assist_by_label(
+ wrap_return_type,
+ r#"
+//- minicore: result
+mod some_module {
+ pub type Result<T> = core::result::Result<T, ()>;
+}
+
+use some_module::*;
+
+fn foo() -> i3$02 {
+ return 42i32;
+}
+"#,
+ r#"
+mod some_module {
+ pub type Result<T> = core::result::Result<T, ()>;
+}
+
+use some_module::*;
+
+fn foo() -> Result<i32> {
+ 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<T> = core::result::Result<T, ()>;
+ 0
+}
+"#,
+ r#"
+fn foo() -> Result<i32, ${0:_}> {
+ type Result<T> = core::result::Result<T, ()>;
+ 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<T> = core::result::Result<T, ()>;
+
+fn foo() -> Result<i3$02> {
+ 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<T, E> = core::result::Result<T, E>;
+
+fn foo() -> i3$02 {
+ 0
+}
+"#,
+ r#"
+type Result<T, E> = core::result::Result<T, E>;
+
+fn foo() -> Result<i32, ${0:_}> {
+ Ok(0)
+}
+"#,
+ WrapperKind::Result.label(),
+ );
+
+ check_assist_by_label(
+ wrap_return_type,
+ r#"
+//- minicore: result
+type Result<T, E> = core::result::Result<Foo<T, E>, ()>;
+
+fn foo() -> i3$02 {
+ 0
+}
+ "#,
+ r#"
+type Result<T, E> = core::result::Result<Foo<T, E>, ()>;
+
+fn foo() -> Result<i32, ${0:_}> {
+ Ok(0)
+}
+ "#,
+ WrapperKind::Result.label(),
+ );
+
+ check_assist_by_label(
+ wrap_return_type,
+ r#"
+//- minicore: result
+type Result<'a, T, E> = core::result::Result<Foo<T, E>, &'a ()>;
+
+fn foo() -> i3$02 {
+ 0
+}
+ "#,
+ r#"
+type Result<'a, T, E> = core::result::Result<Foo<T, E>, &'a ()>;
+
+fn foo() -> Result<'_, i32, ${0:_}> {
+ Ok(0)
+}
+ "#,
+ WrapperKind::Result.label(),
+ );
+
+ check_assist_by_label(
+ wrap_return_type,
+ r#"
+//- minicore: result
+type Result<T, const N: usize> = core::result::Result<Foo<T>, Bar<N>>;
+
+fn foo() -> i3$02 {
+ 0
+}
+ "#,
+ r#"
+type Result<T, const N: usize> = core::result::Result<Foo<T>, Bar<N>>;
+
+fn foo() -> Result<i32, ${0:_}> {
+ 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<i32, ${0:_}> { Ok(42i32) }
-// ```
-pub(crate) fn wrap_return_type_in_result(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
- let ret_type = ctx.find_node_at_offset::<ast::RetType>()?;
- 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<T, ...> 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<ast::Expr>, 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<i32, ${0:_}> {
- 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<i32, ${0:_}> {
- 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<i32, ${0:_}> {
- 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<i32$0, String> {
- 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<i32$0, String> {
- 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<i32$0, String> {
- 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<i32, ${0:_}> {
- 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<i32, ${0:_}> {
- 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<i32, ${0:_}> {
- 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<i32, ${0:_}> { 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<i32, ${0:_}> {
- 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<i32, ${0:_}> {
- 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<i32, ${0:_}> {
- 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<i32, ${0:_}> {
- 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<i32, ${0:_}> {
- 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<i32, ${0:_}> {
- 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<i32, ${0:_}> {
- 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<i32, ${0:_}> {
- 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<i32, ${0:_}> {
- 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<i32, ${0:_}> {
- 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<i32, ${0:_}> {
- 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<i32, ${0:_}> {
- 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<i32, ${0:_}> {
- 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<u32, ${0:_}> {
- 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<u32, ${0:_}> {
- 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<i32, ${0:_}> {
- 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<u32, ${0:_}> {
- 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<u32, ${0:_}> {
- 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<u32, ${0:_}> {
- 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<u32, ${0:_}> {
- 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<T> = core::result::Result<T, ()>;
-
-fn foo() -> i3$02 {
- return 42i32;
-}
-"#,
- r#"
-type Result<T> = core::result::Result<T, ()>;
-
-fn foo() -> Result<i32> {
- return Ok(42i32);
-}
-"#,
- );
-
- check_assist(
- wrap_return_type_in_result,
- r#"
-//- minicore: result
-type Result2<T> = core::result::Result<T, ()>;
-
-fn foo() -> i3$02 {
- return 42i32;
-}
-"#,
- r#"
-type Result2<T> = core::result::Result<T, ()>;
-
-fn foo() -> Result<i32, ${0:_}> {
- 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<T> = core::result::Result<T, ()>;
-}
-
-use some_module::Result;
-
-fn foo() -> i3$02 {
- return 42i32;
-}
-"#,
- r#"
-mod some_module {
- pub type Result<T> = core::result::Result<T, ()>;
-}
-
-use some_module::Result;
-
-fn foo() -> Result<i32> {
- return Ok(42i32);
-}
-"#,
- );
-
- check_assist(
- wrap_return_type_in_result,
- r#"
-//- minicore: result
-mod some_module {
- pub type Result<T> = core::result::Result<T, ()>;
-}
-
-use some_module::*;
-
-fn foo() -> i3$02 {
- return 42i32;
-}
-"#,
- r#"
-mod some_module {
- pub type Result<T> = core::result::Result<T, ()>;
-}
-
-use some_module::*;
-
-fn foo() -> Result<i32> {
- 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<T> = core::result::Result<T, ()>;
- 0
-}
-"#,
- r#"
-fn foo() -> Result<i32, ${0:_}> {
- type Result<T> = core::result::Result<T, ()>;
- 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<T> = core::result::Result<T, ()>;
-
-fn foo() -> Result<i3$02> {
- 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<T, E> = core::result::Result<T, E>;
-
-fn foo() -> i3$02 {
- 0
-}
-"#,
- r#"
-type Result<T, E> = core::result::Result<T, E>;
-
-fn foo() -> Result<i32, ${0:_}> {
- Ok(0)
-}
-"#,
- );
-
- check_assist(
- wrap_return_type_in_result,
- r#"
-//- minicore: result
-type Result<T, E> = core::result::Result<Foo<T, E>, ()>;
-
-fn foo() -> i3$02 {
- 0
-}
- "#,
- r#"
-type Result<T, E> = core::result::Result<Foo<T, E>, ()>;
-
-fn foo() -> Result<i32, ${0:_}> {
- Ok(0)
-}
- "#,
- );
-
- check_assist(
- wrap_return_type_in_result,
- r#"
-//- minicore: result
-type Result<'a, T, E> = core::result::Result<Foo<T, E>, &'a ()>;
-
-fn foo() -> i3$02 {
- 0
-}
- "#,
- r#"
-type Result<'a, T, E> = core::result::Result<Foo<T, E>, &'a ()>;
-
-fn foo() -> Result<'_, i32, ${0:_}> {
- Ok(0)
-}
- "#,
- );
-
- check_assist(
- wrap_return_type_in_result,
- r#"
-//- minicore: result
-type Result<T, const N: usize> = core::result::Result<Foo<T>, Bar<N>>;
-
-fn foo() -> i3$02 {
- 0
-}
- "#,
- r#"
-type Result<T, const N: usize> = core::result::Result<Foo<T>, Bar<N>>;
-
-fn foo() -> Result<i32, ${0:_}> {
- 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
@@ -3265,6 +3265,20 @@ fn foo() {
}
#[test]
+fn doctest_unwrap_option_return_type() {
+ check_doc_test(
+ "unwrap_option_return_type",
+ r#####"
+//- minicore: option
+fn foo() -> Option<i32>$0 { Some(42i32) }
+"#####,
+ r#####"
+fn foo() -> i32 { 42i32 }
+"#####,
+ )
+}
+
+#[test]
fn doctest_unwrap_result_return_type() {
check_doc_test(
"unwrap_result_return_type",
@@ -3298,6 +3312,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<i32> { Some(42i32) }
+"#####,
+ )
+}
+
+#[test]
fn doctest_wrap_return_type_in_result() {
check_doc_test(
"wrap_return_type_in_result",