aboutsummaryrefslogtreecommitdiff
path: root/crates/assists/src/handlers
diff options
context:
space:
mode:
Diffstat (limited to 'crates/assists/src/handlers')
-rw-r--r--crates/assists/src/handlers/add_custom_impl.rs284
-rw-r--r--crates/assists/src/handlers/add_missing_impl_members.rs92
-rw-r--r--crates/assists/src/handlers/auto_import.rs3
-rw-r--r--crates/assists/src/handlers/change_return_type_to_result.rs1091
-rw-r--r--crates/assists/src/handlers/expand_glob_import.rs76
-rw-r--r--crates/assists/src/handlers/extract_struct_from_enum_variant.rs180
-rw-r--r--crates/assists/src/handlers/ignore_test.rs103
-rw-r--r--crates/assists/src/handlers/remove_dbg.rs71
-rw-r--r--crates/assists/src/handlers/remove_unused_param.rs83
-rw-r--r--crates/assists/src/handlers/reorder_fields.rs4
-rw-r--r--crates/assists/src/handlers/replace_derive_with_manual_impl.rs400
-rw-r--r--crates/assists/src/handlers/replace_qualified_name_with_use.rs2
-rw-r--r--crates/assists/src/handlers/unwrap_block.rs613
-rw-r--r--crates/assists/src/handlers/wrap_return_type_in_result.rs1158
14 files changed, 2370 insertions, 1790 deletions
diff --git a/crates/assists/src/handlers/add_custom_impl.rs b/crates/assists/src/handlers/add_custom_impl.rs
deleted file mode 100644
index 669dd9b21..000000000
--- a/crates/assists/src/handlers/add_custom_impl.rs
+++ /dev/null
@@ -1,284 +0,0 @@
1use ide_db::imports_locator;
2use itertools::Itertools;
3use syntax::{
4 ast::{self, make, AstNode},
5 Direction, SmolStr,
6 SyntaxKind::{IDENT, WHITESPACE},
7 TextRange, TextSize,
8};
9
10use crate::{
11 assist_config::SnippetCap,
12 assist_context::{AssistBuilder, AssistContext, Assists},
13 utils::mod_path_to_ast,
14 AssistId, AssistKind,
15};
16
17// Assist: add_custom_impl
18//
19// Adds impl block for derived trait.
20//
21// ```
22// #[derive(Deb<|>ug, Display)]
23// struct S;
24// ```
25// ->
26// ```
27// #[derive(Display)]
28// struct S;
29//
30// impl Debug for S {
31// $0
32// }
33// ```
34pub(crate) fn add_custom_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
35 let attr = ctx.find_node_at_offset::<ast::Attr>()?;
36
37 let attr_name = attr
38 .syntax()
39 .descendants_with_tokens()
40 .filter(|t| t.kind() == IDENT)
41 .find_map(syntax::NodeOrToken::into_token)
42 .filter(|t| t.text() == "derive")?
43 .text()
44 .clone();
45
46 let trait_token =
47 ctx.token_at_offset().find(|t| t.kind() == IDENT && *t.text() != attr_name)?;
48 let trait_path = make::path_unqualified(make::path_segment(make::name_ref(trait_token.text())));
49
50 let annotated = attr.syntax().siblings(Direction::Next).find_map(ast::Name::cast)?;
51 let annotated_name = annotated.syntax().text().to_string();
52 let insert_pos = annotated.syntax().parent()?.text_range().end();
53
54 let current_module = ctx.sema.scope(annotated.syntax()).module()?;
55 let current_crate = current_module.krate();
56
57 let found_traits = imports_locator::find_imports(&ctx.sema, current_crate, trait_token.text())
58 .into_iter()
59 .filter_map(|candidate: either::Either<hir::ModuleDef, hir::MacroDef>| match candidate {
60 either::Either::Left(hir::ModuleDef::Trait(trait_)) => Some(trait_),
61 _ => None,
62 })
63 .flat_map(|trait_| {
64 current_module
65 .find_use_path(ctx.sema.db, hir::ModuleDef::Trait(trait_))
66 .as_ref()
67 .map(mod_path_to_ast)
68 .zip(Some(trait_))
69 });
70
71 let mut no_traits_found = true;
72 for (trait_path, _trait) in found_traits.inspect(|_| no_traits_found = false) {
73 add_assist(acc, ctx.config.snippet_cap, &attr, &trait_path, &annotated_name, insert_pos)?;
74 }
75 if no_traits_found {
76 add_assist(acc, ctx.config.snippet_cap, &attr, &trait_path, &annotated_name, insert_pos)?;
77 }
78 Some(())
79}
80
81fn add_assist(
82 acc: &mut Assists,
83 snippet_cap: Option<SnippetCap>,
84 attr: &ast::Attr,
85 trait_path: &ast::Path,
86 annotated_name: &str,
87 insert_pos: TextSize,
88) -> Option<()> {
89 let target = attr.syntax().text_range();
90 let input = attr.token_tree()?;
91 let label = format!("Add custom impl `{}` for `{}`", trait_path, annotated_name);
92 let trait_name = trait_path.segment().and_then(|seg| seg.name_ref())?;
93
94 acc.add(AssistId("add_custom_impl", AssistKind::Refactor), label, target, |builder| {
95 update_attribute(builder, &input, &trait_name, &attr);
96 match snippet_cap {
97 Some(cap) => {
98 builder.insert_snippet(
99 cap,
100 insert_pos,
101 format!("\n\nimpl {} for {} {{\n $0\n}}", trait_path, annotated_name),
102 );
103 }
104 None => {
105 builder.insert(
106 insert_pos,
107 format!("\n\nimpl {} for {} {{\n\n}}", trait_path, annotated_name),
108 );
109 }
110 }
111 })
112}
113
114fn update_attribute(
115 builder: &mut AssistBuilder,
116 input: &ast::TokenTree,
117 trait_name: &ast::NameRef,
118 attr: &ast::Attr,
119) {
120 let new_attr_input = input
121 .syntax()
122 .descendants_with_tokens()
123 .filter(|t| t.kind() == IDENT)
124 .filter_map(|t| t.into_token().map(|t| t.text().clone()))
125 .filter(|t| t != trait_name.text())
126 .collect::<Vec<SmolStr>>();
127 let has_more_derives = !new_attr_input.is_empty();
128
129 if has_more_derives {
130 let new_attr_input = format!("({})", new_attr_input.iter().format(", "));
131 builder.replace(input.syntax().text_range(), new_attr_input);
132 } else {
133 let attr_range = attr.syntax().text_range();
134 builder.delete(attr_range);
135
136 let line_break_range = attr
137 .syntax()
138 .next_sibling_or_token()
139 .filter(|t| t.kind() == WHITESPACE)
140 .map(|t| t.text_range())
141 .unwrap_or_else(|| TextRange::new(TextSize::from(0), TextSize::from(0)));
142 builder.delete(line_break_range);
143 }
144}
145
146#[cfg(test)]
147mod tests {
148 use crate::tests::{check_assist, check_assist_not_applicable};
149
150 use super::*;
151
152 #[test]
153 fn add_custom_impl_qualified() {
154 check_assist(
155 add_custom_impl,
156 "
157mod fmt {
158 pub trait Debug {}
159}
160
161#[derive(Debu<|>g)]
162struct Foo {
163 bar: String,
164}
165",
166 "
167mod fmt {
168 pub trait Debug {}
169}
170
171struct Foo {
172 bar: String,
173}
174
175impl fmt::Debug for Foo {
176 $0
177}
178",
179 )
180 }
181 #[test]
182 fn add_custom_impl_for_unique_input() {
183 check_assist(
184 add_custom_impl,
185 "
186#[derive(Debu<|>g)]
187struct Foo {
188 bar: String,
189}
190 ",
191 "
192struct Foo {
193 bar: String,
194}
195
196impl Debug for Foo {
197 $0
198}
199 ",
200 )
201 }
202
203 #[test]
204 fn add_custom_impl_for_with_visibility_modifier() {
205 check_assist(
206 add_custom_impl,
207 "
208#[derive(Debug<|>)]
209pub struct Foo {
210 bar: String,
211}
212 ",
213 "
214pub struct Foo {
215 bar: String,
216}
217
218impl Debug for Foo {
219 $0
220}
221 ",
222 )
223 }
224
225 #[test]
226 fn add_custom_impl_when_multiple_inputs() {
227 check_assist(
228 add_custom_impl,
229 "
230#[derive(Display, Debug<|>, Serialize)]
231struct Foo {}
232 ",
233 "
234#[derive(Display, Serialize)]
235struct Foo {}
236
237impl Debug for Foo {
238 $0
239}
240 ",
241 )
242 }
243
244 #[test]
245 fn test_ignore_derive_macro_without_input() {
246 check_assist_not_applicable(
247 add_custom_impl,
248 "
249#[derive(<|>)]
250struct Foo {}
251 ",
252 )
253 }
254
255 #[test]
256 fn test_ignore_if_cursor_on_param() {
257 check_assist_not_applicable(
258 add_custom_impl,
259 "
260#[derive<|>(Debug)]
261struct Foo {}
262 ",
263 );
264
265 check_assist_not_applicable(
266 add_custom_impl,
267 "
268#[derive(Debug)<|>]
269struct Foo {}
270 ",
271 )
272 }
273
274 #[test]
275 fn test_ignore_if_not_derive() {
276 check_assist_not_applicable(
277 add_custom_impl,
278 "
279#[allow(non_camel_<|>case_types)]
280struct Foo {}
281 ",
282 )
283 }
284}
diff --git a/crates/assists/src/handlers/add_missing_impl_members.rs b/crates/assists/src/handlers/add_missing_impl_members.rs
index b82fb30ad..bbb71e261 100644
--- a/crates/assists/src/handlers/add_missing_impl_members.rs
+++ b/crates/assists/src/handlers/add_missing_impl_members.rs
@@ -1,27 +1,14 @@
1use hir::HasSource; 1use ide_db::traits::resolve_target_trait;
2use ide_db::traits::{get_missing_assoc_items, resolve_target_trait}; 2use syntax::ast::{self, AstNode};
3use syntax::{
4 ast::{
5 self,
6 edit::{self, AstNodeEdit, IndentLevel},
7 make, AstNode, NameOwner,
8 },
9 SmolStr,
10};
11 3
12use crate::{ 4use crate::{
13 assist_context::{AssistContext, Assists}, 5 assist_context::{AssistContext, Assists},
14 ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams}, 6 utils::add_trait_assoc_items_to_impl,
15 utils::{render_snippet, Cursor}, 7 utils::DefaultMethods,
8 utils::{filter_assoc_items, render_snippet, Cursor},
16 AssistId, AssistKind, 9 AssistId, AssistKind,
17}; 10};
18 11
19#[derive(PartialEq)]
20enum AddMissingImplMembersMode {
21 DefaultMethodsOnly,
22 NoDefaultMethods,
23}
24
25// Assist: add_impl_missing_members 12// Assist: add_impl_missing_members
26// 13//
27// Adds scaffold for required impl members. 14// Adds scaffold for required impl members.
@@ -55,7 +42,7 @@ pub(crate) fn add_missing_impl_members(acc: &mut Assists, ctx: &AssistContext) -
55 add_missing_impl_members_inner( 42 add_missing_impl_members_inner(
56 acc, 43 acc,
57 ctx, 44 ctx,
58 AddMissingImplMembersMode::NoDefaultMethods, 45 DefaultMethods::No,
59 "add_impl_missing_members", 46 "add_impl_missing_members",
60 "Implement missing members", 47 "Implement missing members",
61 ) 48 )
@@ -97,7 +84,7 @@ pub(crate) fn add_missing_default_members(acc: &mut Assists, ctx: &AssistContext
97 add_missing_impl_members_inner( 84 add_missing_impl_members_inner(
98 acc, 85 acc,
99 ctx, 86 ctx,
100 AddMissingImplMembersMode::DefaultMethodsOnly, 87 DefaultMethods::Only,
101 "add_impl_default_members", 88 "add_impl_default_members",
102 "Implement default members", 89 "Implement default members",
103 ) 90 )
@@ -106,7 +93,7 @@ pub(crate) fn add_missing_default_members(acc: &mut Assists, ctx: &AssistContext
106fn add_missing_impl_members_inner( 93fn add_missing_impl_members_inner(
107 acc: &mut Assists, 94 acc: &mut Assists,
108 ctx: &AssistContext, 95 ctx: &AssistContext,
109 mode: AddMissingImplMembersMode, 96 mode: DefaultMethods,
110 assist_id: &'static str, 97 assist_id: &'static str,
111 label: &'static str, 98 label: &'static str,
112) -> Option<()> { 99) -> Option<()> {
@@ -114,32 +101,11 @@ fn add_missing_impl_members_inner(
114 let impl_def = ctx.find_node_at_offset::<ast::Impl>()?; 101 let impl_def = ctx.find_node_at_offset::<ast::Impl>()?;
115 let trait_ = resolve_target_trait(&ctx.sema, &impl_def)?; 102 let trait_ = resolve_target_trait(&ctx.sema, &impl_def)?;
116 103
117 let def_name = |item: &ast::AssocItem| -> Option<SmolStr> { 104 let missing_items = filter_assoc_items(
118 match item { 105 ctx.db(),
119 ast::AssocItem::Fn(def) => def.name(), 106 &ide_db::traits::get_missing_assoc_items(&ctx.sema, &impl_def),
120 ast::AssocItem::TypeAlias(def) => def.name(), 107 mode,
121 ast::AssocItem::Const(def) => def.name(), 108 );
122 ast::AssocItem::MacroCall(_) => None,
123 }
124 .map(|it| it.text().clone())
125 };
126
127 let missing_items = get_missing_assoc_items(&ctx.sema, &impl_def)
128 .iter()
129 .map(|i| match i {
130 hir::AssocItem::Function(i) => ast::AssocItem::Fn(i.source(ctx.db()).value),
131 hir::AssocItem::TypeAlias(i) => ast::AssocItem::TypeAlias(i.source(ctx.db()).value),
132 hir::AssocItem::Const(i) => ast::AssocItem::Const(i.source(ctx.db()).value),
133 })
134 .filter(|t| def_name(&t).is_some())
135 .filter(|t| match t {
136 ast::AssocItem::Fn(def) => match mode {
137 AddMissingImplMembersMode::DefaultMethodsOnly => def.body().is_some(),
138 AddMissingImplMembersMode::NoDefaultMethods => def.body().is_none(),
139 },
140 _ => mode == AddMissingImplMembersMode::NoDefaultMethods,
141 })
142 .collect::<Vec<_>>();
143 109
144 if missing_items.is_empty() { 110 if missing_items.is_empty() {
145 return None; 111 return None;
@@ -147,29 +113,9 @@ fn add_missing_impl_members_inner(
147 113
148 let target = impl_def.syntax().text_range(); 114 let target = impl_def.syntax().text_range();
149 acc.add(AssistId(assist_id, AssistKind::QuickFix), label, target, |builder| { 115 acc.add(AssistId(assist_id, AssistKind::QuickFix), label, target, |builder| {
150 let impl_item_list = impl_def.assoc_item_list().unwrap_or_else(make::assoc_item_list);
151
152 let n_existing_items = impl_item_list.assoc_items().count();
153 let source_scope = ctx.sema.scope_for_def(trait_);
154 let target_scope = ctx.sema.scope(impl_def.syntax()); 116 let target_scope = ctx.sema.scope(impl_def.syntax());
155 let ast_transform = QualifyPaths::new(&target_scope, &source_scope) 117 let (new_impl_def, first_new_item) =
156 .or(SubstituteTypeParams::for_trait_impl(&source_scope, trait_, impl_def.clone())); 118 add_trait_assoc_items_to_impl(&ctx.sema, missing_items, trait_, impl_def, target_scope);
157
158 let items = missing_items
159 .into_iter()
160 .map(|it| ast_transform::apply(&*ast_transform, it))
161 .map(|it| match it {
162 ast::AssocItem::Fn(def) => ast::AssocItem::Fn(add_body(def)),
163 ast::AssocItem::TypeAlias(def) => ast::AssocItem::TypeAlias(def.remove_bounds()),
164 _ => it,
165 })
166 .map(|it| edit::remove_attrs_and_docs(&it));
167
168 let new_impl_item_list = impl_item_list.append_items(items);
169 let new_impl_def = impl_def.with_assoc_item_list(new_impl_item_list);
170 let first_new_item =
171 new_impl_def.assoc_item_list().unwrap().assoc_items().nth(n_existing_items).unwrap();
172
173 match ctx.config.snippet_cap { 119 match ctx.config.snippet_cap {
174 None => builder.replace(target, new_impl_def.to_string()), 120 None => builder.replace(target, new_impl_def.to_string()),
175 Some(cap) => { 121 Some(cap) => {
@@ -193,14 +139,6 @@ fn add_missing_impl_members_inner(
193 }) 139 })
194} 140}
195 141
196fn add_body(fn_def: ast::Fn) -> ast::Fn {
197 if fn_def.body().is_some() {
198 return fn_def;
199 }
200 let body = make::block_expr(None, Some(make::expr_todo())).indent(IndentLevel(1));
201 fn_def.with_body(body)
202}
203
204#[cfg(test)] 142#[cfg(test)]
205mod tests { 143mod tests {
206 use crate::tests::{check_assist, check_assist_not_applicable}; 144 use crate::tests::{check_assist, check_assist_not_applicable};
diff --git a/crates/assists/src/handlers/auto_import.rs b/crates/assists/src/handlers/auto_import.rs
index 37dd61266..d665837a2 100644
--- a/crates/assists/src/handlers/auto_import.rs
+++ b/crates/assists/src/handlers/auto_import.rs
@@ -98,7 +98,8 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
98 98
99 let range = ctx.sema.original_range(import_assets.syntax_under_caret()).range; 99 let range = ctx.sema.original_range(import_assets.syntax_under_caret()).range;
100 let group = import_group_message(import_assets.import_candidate()); 100 let group = import_group_message(import_assets.import_candidate());
101 let scope = ImportScope::find_insert_use_container(import_assets.syntax_under_caret(), ctx)?; 101 let scope =
102 ImportScope::find_insert_use_container(import_assets.syntax_under_caret(), &ctx.sema)?;
102 for (import, _) in proposed_imports { 103 for (import, _) in proposed_imports {
103 acc.add_group( 104 acc.add_group(
104 &group, 105 &group,
diff --git a/crates/assists/src/handlers/change_return_type_to_result.rs b/crates/assists/src/handlers/change_return_type_to_result.rs
deleted file mode 100644
index 76f33a5b6..000000000
--- a/crates/assists/src/handlers/change_return_type_to_result.rs
+++ /dev/null
@@ -1,1091 +0,0 @@
1use std::iter;
2
3use syntax::{
4 ast::{self, make, BlockExpr, Expr, LoopBodyOwner},
5 match_ast, AstNode, SyntaxNode,
6};
7use test_utils::mark;
8
9use crate::{AssistContext, AssistId, AssistKind, Assists};
10
11// Assist: change_return_type_to_result
12//
13// Change the function's return type to Result.
14//
15// ```
16// fn foo() -> i32<|> { 42i32 }
17// ```
18// ->
19// ```
20// fn foo() -> Result<i32, ${0:_}> { Ok(42i32) }
21// ```
22pub(crate) fn change_return_type_to_result(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
23 let ret_type = ctx.find_node_at_offset::<ast::RetType>()?;
24 let parent = ret_type.syntax().parent()?;
25 let block_expr = match_ast! {
26 match parent {
27 ast::Fn(func) => func.body()?,
28 ast::ClosureExpr(closure) => match closure.body()? {
29 Expr::BlockExpr(block) => block,
30 // closures require a block when a return type is specified
31 _ => return None,
32 },
33 _ => return None,
34 }
35 };
36
37 let type_ref = &ret_type.ty()?;
38 let ret_type_str = type_ref.syntax().text().to_string();
39 let first_part_ret_type = ret_type_str.splitn(2, '<').next();
40 if let Some(ret_type_first_part) = first_part_ret_type {
41 if ret_type_first_part.ends_with("Result") {
42 mark::hit!(change_return_type_to_result_simple_return_type_already_result);
43 return None;
44 }
45 }
46
47 acc.add(
48 AssistId("change_return_type_to_result", AssistKind::RefactorRewrite),
49 "Wrap return type in Result",
50 type_ref.syntax().text_range(),
51 |builder| {
52 let mut tail_return_expr_collector = TailReturnCollector::new();
53 tail_return_expr_collector.collect_jump_exprs(&block_expr, false);
54 tail_return_expr_collector.collect_tail_exprs(&block_expr);
55
56 for ret_expr_arg in tail_return_expr_collector.exprs_to_wrap {
57 let ok_wrapped = make::expr_call(
58 make::expr_path(make::path_unqualified(make::path_segment(make::name_ref(
59 "Ok",
60 )))),
61 make::arg_list(iter::once(ret_expr_arg.clone())),
62 );
63 builder.replace_ast(ret_expr_arg, ok_wrapped);
64 }
65
66 match ctx.config.snippet_cap {
67 Some(cap) => {
68 let snippet = format!("Result<{}, ${{0:_}}>", type_ref);
69 builder.replace_snippet(cap, type_ref.syntax().text_range(), snippet)
70 }
71 None => builder
72 .replace(type_ref.syntax().text_range(), format!("Result<{}, _>", type_ref)),
73 }
74 },
75 )
76}
77
78struct TailReturnCollector {
79 exprs_to_wrap: Vec<ast::Expr>,
80}
81
82impl TailReturnCollector {
83 fn new() -> Self {
84 Self { exprs_to_wrap: vec![] }
85 }
86 /// Collect all`return` expression
87 fn collect_jump_exprs(&mut self, block_expr: &BlockExpr, collect_break: bool) {
88 let statements = block_expr.statements();
89 for stmt in statements {
90 let expr = match &stmt {
91 ast::Stmt::ExprStmt(stmt) => stmt.expr(),
92 ast::Stmt::LetStmt(stmt) => stmt.initializer(),
93 ast::Stmt::Item(_) => continue,
94 };
95 if let Some(expr) = &expr {
96 self.handle_exprs(expr, collect_break);
97 }
98 }
99
100 // Browse tail expressions for each block
101 if let Some(expr) = block_expr.expr() {
102 if let Some(last_exprs) = get_tail_expr_from_block(&expr) {
103 for last_expr in last_exprs {
104 let last_expr = match last_expr {
105 NodeType::Node(expr) => expr,
106 NodeType::Leaf(expr) => expr.syntax().clone(),
107 };
108
109 if let Some(last_expr) = Expr::cast(last_expr.clone()) {
110 self.handle_exprs(&last_expr, collect_break);
111 } else if let Some(expr_stmt) = ast::Stmt::cast(last_expr) {
112 let expr_stmt = match &expr_stmt {
113 ast::Stmt::ExprStmt(stmt) => stmt.expr(),
114 ast::Stmt::LetStmt(stmt) => stmt.initializer(),
115 ast::Stmt::Item(_) => None,
116 };
117 if let Some(expr) = &expr_stmt {
118 self.handle_exprs(expr, collect_break);
119 }
120 }
121 }
122 }
123 }
124 }
125
126 fn handle_exprs(&mut self, expr: &Expr, collect_break: bool) {
127 match expr {
128 Expr::BlockExpr(block_expr) => {
129 self.collect_jump_exprs(&block_expr, collect_break);
130 }
131 Expr::ReturnExpr(ret_expr) => {
132 if let Some(ret_expr_arg) = &ret_expr.expr() {
133 self.exprs_to_wrap.push(ret_expr_arg.clone());
134 }
135 }
136 Expr::BreakExpr(break_expr) if collect_break => {
137 if let Some(break_expr_arg) = &break_expr.expr() {
138 self.exprs_to_wrap.push(break_expr_arg.clone());
139 }
140 }
141 Expr::IfExpr(if_expr) => {
142 for block in if_expr.blocks() {
143 self.collect_jump_exprs(&block, collect_break);
144 }
145 }
146 Expr::LoopExpr(loop_expr) => {
147 if let Some(block_expr) = loop_expr.loop_body() {
148 self.collect_jump_exprs(&block_expr, collect_break);
149 }
150 }
151 Expr::ForExpr(for_expr) => {
152 if let Some(block_expr) = for_expr.loop_body() {
153 self.collect_jump_exprs(&block_expr, collect_break);
154 }
155 }
156 Expr::WhileExpr(while_expr) => {
157 if let Some(block_expr) = while_expr.loop_body() {
158 self.collect_jump_exprs(&block_expr, collect_break);
159 }
160 }
161 Expr::MatchExpr(match_expr) => {
162 if let Some(arm_list) = match_expr.match_arm_list() {
163 arm_list.arms().filter_map(|match_arm| match_arm.expr()).for_each(|expr| {
164 self.handle_exprs(&expr, collect_break);
165 });
166 }
167 }
168 _ => {}
169 }
170 }
171
172 fn collect_tail_exprs(&mut self, block: &BlockExpr) {
173 if let Some(expr) = block.expr() {
174 self.handle_exprs(&expr, true);
175 self.fetch_tail_exprs(&expr);
176 }
177 }
178
179 fn fetch_tail_exprs(&mut self, expr: &Expr) {
180 if let Some(exprs) = get_tail_expr_from_block(expr) {
181 for node_type in &exprs {
182 match node_type {
183 NodeType::Leaf(expr) => {
184 self.exprs_to_wrap.push(expr.clone());
185 }
186 NodeType::Node(expr) => {
187 if let Some(last_expr) = Expr::cast(expr.clone()) {
188 self.fetch_tail_exprs(&last_expr);
189 }
190 }
191 }
192 }
193 }
194 }
195}
196
197#[derive(Debug)]
198enum NodeType {
199 Leaf(ast::Expr),
200 Node(SyntaxNode),
201}
202
203/// Get a tail expression inside a block
204fn get_tail_expr_from_block(expr: &Expr) -> Option<Vec<NodeType>> {
205 match expr {
206 Expr::IfExpr(if_expr) => {
207 let mut nodes = vec![];
208 for block in if_expr.blocks() {
209 if let Some(block_expr) = block.expr() {
210 if let Some(tail_exprs) = get_tail_expr_from_block(&block_expr) {
211 nodes.extend(tail_exprs);
212 }
213 } else if let Some(last_expr) = block.syntax().last_child() {
214 nodes.push(NodeType::Node(last_expr));
215 } else {
216 nodes.push(NodeType::Node(block.syntax().clone()));
217 }
218 }
219 Some(nodes)
220 }
221 Expr::LoopExpr(loop_expr) => {
222 loop_expr.syntax().last_child().map(|lc| vec![NodeType::Node(lc)])
223 }
224 Expr::ForExpr(for_expr) => {
225 for_expr.syntax().last_child().map(|lc| vec![NodeType::Node(lc)])
226 }
227 Expr::WhileExpr(while_expr) => {
228 while_expr.syntax().last_child().map(|lc| vec![NodeType::Node(lc)])
229 }
230 Expr::BlockExpr(block_expr) => {
231 block_expr.expr().map(|lc| vec![NodeType::Node(lc.syntax().clone())])
232 }
233 Expr::MatchExpr(match_expr) => {
234 let arm_list = match_expr.match_arm_list()?;
235 let arms: Vec<NodeType> = arm_list
236 .arms()
237 .filter_map(|match_arm| match_arm.expr())
238 .map(|expr| match expr {
239 Expr::ReturnExpr(ret_expr) => NodeType::Node(ret_expr.syntax().clone()),
240 Expr::BreakExpr(break_expr) => NodeType::Node(break_expr.syntax().clone()),
241 _ => match expr.syntax().last_child() {
242 Some(last_expr) => NodeType::Node(last_expr),
243 None => NodeType::Node(expr.syntax().clone()),
244 },
245 })
246 .collect();
247
248 Some(arms)
249 }
250 Expr::BreakExpr(expr) => expr.expr().map(|e| vec![NodeType::Leaf(e)]),
251 Expr::ReturnExpr(ret_expr) => Some(vec![NodeType::Node(ret_expr.syntax().clone())]),
252
253 Expr::CallExpr(_)
254 | Expr::Literal(_)
255 | Expr::TupleExpr(_)
256 | Expr::ArrayExpr(_)
257 | Expr::ParenExpr(_)
258 | Expr::PathExpr(_)
259 | Expr::RecordExpr(_)
260 | Expr::IndexExpr(_)
261 | Expr::MethodCallExpr(_)
262 | Expr::AwaitExpr(_)
263 | Expr::CastExpr(_)
264 | Expr::RefExpr(_)
265 | Expr::PrefixExpr(_)
266 | Expr::RangeExpr(_)
267 | Expr::BinExpr(_)
268 | Expr::MacroCall(_)
269 | Expr::BoxExpr(_) => Some(vec![NodeType::Leaf(expr.clone())]),
270 _ => None,
271 }
272}
273
274#[cfg(test)]
275mod tests {
276 use crate::tests::{check_assist, check_assist_not_applicable};
277
278 use super::*;
279
280 #[test]
281 fn change_return_type_to_result_simple() {
282 check_assist(
283 change_return_type_to_result,
284 r#"fn foo() -> i3<|>2 {
285 let test = "test";
286 return 42i32;
287 }"#,
288 r#"fn foo() -> Result<i32, ${0:_}> {
289 let test = "test";
290 return Ok(42i32);
291 }"#,
292 );
293 }
294
295 #[test]
296 fn change_return_type_to_result_simple_closure() {
297 check_assist(
298 change_return_type_to_result,
299 r#"fn foo() {
300 || -> i32<|> {
301 let test = "test";
302 return 42i32;
303 };
304 }"#,
305 r#"fn foo() {
306 || -> Result<i32, ${0:_}> {
307 let test = "test";
308 return Ok(42i32);
309 };
310 }"#,
311 );
312 }
313
314 #[test]
315 fn change_return_type_to_result_simple_return_type_bad_cursor() {
316 check_assist_not_applicable(
317 change_return_type_to_result,
318 r#"fn foo() -> i32 {
319 let test = "test";<|>
320 return 42i32;
321 }"#,
322 );
323 }
324
325 #[test]
326 fn change_return_type_to_result_simple_return_type_bad_cursor_closure() {
327 check_assist_not_applicable(
328 change_return_type_to_result,
329 r#"fn foo() {
330 || -> i32 {
331 let test = "test";<|>
332 return 42i32;
333 };
334 }"#,
335 );
336 }
337
338 #[test]
339 fn change_return_type_to_result_closure_non_block() {
340 check_assist_not_applicable(
341 change_return_type_to_result,
342 r#"fn foo() {
343 || -> i<|>32 3;
344 }"#,
345 );
346 }
347
348 #[test]
349 fn change_return_type_to_result_simple_return_type_already_result_std() {
350 check_assist_not_applicable(
351 change_return_type_to_result,
352 r#"fn foo() -> std::result::Result<i32<|>, String> {
353 let test = "test";
354 return 42i32;
355 }"#,
356 );
357 }
358
359 #[test]
360 fn change_return_type_to_result_simple_return_type_already_result() {
361 mark::check!(change_return_type_to_result_simple_return_type_already_result);
362 check_assist_not_applicable(
363 change_return_type_to_result,
364 r#"fn foo() -> Result<i32<|>, String> {
365 let test = "test";
366 return 42i32;
367 }"#,
368 );
369 }
370
371 #[test]
372 fn change_return_type_to_result_simple_return_type_already_result_closure() {
373 check_assist_not_applicable(
374 change_return_type_to_result,
375 r#"fn foo() {
376 || -> Result<i32<|>, String> {
377 let test = "test";
378 return 42i32;
379 };
380 }"#,
381 );
382 }
383
384 #[test]
385 fn change_return_type_to_result_simple_with_cursor() {
386 check_assist(
387 change_return_type_to_result,
388 r#"fn foo() -> <|>i32 {
389 let test = "test";
390 return 42i32;
391 }"#,
392 r#"fn foo() -> Result<i32, ${0:_}> {
393 let test = "test";
394 return Ok(42i32);
395 }"#,
396 );
397 }
398
399 #[test]
400 fn change_return_type_to_result_simple_with_tail() {
401 check_assist(
402 change_return_type_to_result,
403 r#"fn foo() -><|> i32 {
404 let test = "test";
405 42i32
406 }"#,
407 r#"fn foo() -> Result<i32, ${0:_}> {
408 let test = "test";
409 Ok(42i32)
410 }"#,
411 );
412 }
413
414 #[test]
415 fn change_return_type_to_result_simple_with_tail_closure() {
416 check_assist(
417 change_return_type_to_result,
418 r#"fn foo() {
419 || -><|> i32 {
420 let test = "test";
421 42i32
422 };
423 }"#,
424 r#"fn foo() {
425 || -> Result<i32, ${0:_}> {
426 let test = "test";
427 Ok(42i32)
428 };
429 }"#,
430 );
431 }
432
433 #[test]
434 fn change_return_type_to_result_simple_with_tail_only() {
435 check_assist(
436 change_return_type_to_result,
437 r#"fn foo() -> i32<|> {
438 42i32
439 }"#,
440 r#"fn foo() -> Result<i32, ${0:_}> {
441 Ok(42i32)
442 }"#,
443 );
444 }
445
446 #[test]
447 fn change_return_type_to_result_simple_with_tail_block_like() {
448 check_assist(
449 change_return_type_to_result,
450 r#"fn foo() -> i32<|> {
451 if true {
452 42i32
453 } else {
454 24i32
455 }
456 }"#,
457 r#"fn foo() -> Result<i32, ${0:_}> {
458 if true {
459 Ok(42i32)
460 } else {
461 Ok(24i32)
462 }
463 }"#,
464 );
465 }
466
467 #[test]
468 fn change_return_type_to_result_simple_without_block_closure() {
469 check_assist(
470 change_return_type_to_result,
471 r#"fn foo() {
472 || -> i32<|> {
473 if true {
474 42i32
475 } else {
476 24i32
477 }
478 };
479 }"#,
480 r#"fn foo() {
481 || -> Result<i32, ${0:_}> {
482 if true {
483 Ok(42i32)
484 } else {
485 Ok(24i32)
486 }
487 };
488 }"#,
489 );
490 }
491
492 #[test]
493 fn change_return_type_to_result_simple_with_nested_if() {
494 check_assist(
495 change_return_type_to_result,
496 r#"fn foo() -> i32<|> {
497 if true {
498 if false {
499 1
500 } else {
501 2
502 }
503 } else {
504 24i32
505 }
506 }"#,
507 r#"fn foo() -> Result<i32, ${0:_}> {
508 if true {
509 if false {
510 Ok(1)
511 } else {
512 Ok(2)
513 }
514 } else {
515 Ok(24i32)
516 }
517 }"#,
518 );
519 }
520
521 #[test]
522 fn change_return_type_to_result_simple_with_await() {
523 check_assist(
524 change_return_type_to_result,
525 r#"async fn foo() -> i<|>32 {
526 if true {
527 if false {
528 1.await
529 } else {
530 2.await
531 }
532 } else {
533 24i32.await
534 }
535 }"#,
536 r#"async fn foo() -> Result<i32, ${0:_}> {
537 if true {
538 if false {
539 Ok(1.await)
540 } else {
541 Ok(2.await)
542 }
543 } else {
544 Ok(24i32.await)
545 }
546 }"#,
547 );
548 }
549
550 #[test]
551 fn change_return_type_to_result_simple_with_array() {
552 check_assist(
553 change_return_type_to_result,
554 r#"fn foo() -> [i32;<|> 3] {
555 [1, 2, 3]
556 }"#,
557 r#"fn foo() -> Result<[i32; 3], ${0:_}> {
558 Ok([1, 2, 3])
559 }"#,
560 );
561 }
562
563 #[test]
564 fn change_return_type_to_result_simple_with_cast() {
565 check_assist(
566 change_return_type_to_result,
567 r#"fn foo() -<|>> i32 {
568 if true {
569 if false {
570 1 as i32
571 } else {
572 2 as i32
573 }
574 } else {
575 24 as i32
576 }
577 }"#,
578 r#"fn foo() -> Result<i32, ${0:_}> {
579 if true {
580 if false {
581 Ok(1 as i32)
582 } else {
583 Ok(2 as i32)
584 }
585 } else {
586 Ok(24 as i32)
587 }
588 }"#,
589 );
590 }
591
592 #[test]
593 fn change_return_type_to_result_simple_with_tail_block_like_match() {
594 check_assist(
595 change_return_type_to_result,
596 r#"fn foo() -> i32<|> {
597 let my_var = 5;
598 match my_var {
599 5 => 42i32,
600 _ => 24i32,
601 }
602 }"#,
603 r#"fn foo() -> Result<i32, ${0:_}> {
604 let my_var = 5;
605 match my_var {
606 5 => Ok(42i32),
607 _ => Ok(24i32),
608 }
609 }"#,
610 );
611 }
612
613 #[test]
614 fn change_return_type_to_result_simple_with_loop_with_tail() {
615 check_assist(
616 change_return_type_to_result,
617 r#"fn foo() -> i32<|> {
618 let my_var = 5;
619 loop {
620 println!("test");
621 5
622 }
623
624 my_var
625 }"#,
626 r#"fn foo() -> Result<i32, ${0:_}> {
627 let my_var = 5;
628 loop {
629 println!("test");
630 5
631 }
632
633 Ok(my_var)
634 }"#,
635 );
636 }
637
638 #[test]
639 fn change_return_type_to_result_simple_with_loop_in_let_stmt() {
640 check_assist(
641 change_return_type_to_result,
642 r#"fn foo() -> i32<|> {
643 let my_var = let x = loop {
644 break 1;
645 };
646
647 my_var
648 }"#,
649 r#"fn foo() -> Result<i32, ${0:_}> {
650 let my_var = let x = loop {
651 break 1;
652 };
653
654 Ok(my_var)
655 }"#,
656 );
657 }
658
659 #[test]
660 fn change_return_type_to_result_simple_with_tail_block_like_match_return_expr() {
661 check_assist(
662 change_return_type_to_result,
663 r#"fn foo() -> i32<|> {
664 let my_var = 5;
665 let res = match my_var {
666 5 => 42i32,
667 _ => return 24i32,
668 };
669
670 res
671 }"#,
672 r#"fn foo() -> Result<i32, ${0:_}> {
673 let my_var = 5;
674 let res = match my_var {
675 5 => 42i32,
676 _ => return Ok(24i32),
677 };
678
679 Ok(res)
680 }"#,
681 );
682
683 check_assist(
684 change_return_type_to_result,
685 r#"fn foo() -> i32<|> {
686 let my_var = 5;
687 let res = if my_var == 5 {
688 42i32
689 } else {
690 return 24i32;
691 };
692
693 res
694 }"#,
695 r#"fn foo() -> Result<i32, ${0:_}> {
696 let my_var = 5;
697 let res = if my_var == 5 {
698 42i32
699 } else {
700 return Ok(24i32);
701 };
702
703 Ok(res)
704 }"#,
705 );
706 }
707
708 #[test]
709 fn change_return_type_to_result_simple_with_tail_block_like_match_deeper() {
710 check_assist(
711 change_return_type_to_result,
712 r#"fn foo() -> i32<|> {
713 let my_var = 5;
714 match my_var {
715 5 => {
716 if true {
717 42i32
718 } else {
719 25i32
720 }
721 },
722 _ => {
723 let test = "test";
724 if test == "test" {
725 return bar();
726 }
727 53i32
728 },
729 }
730 }"#,
731 r#"fn foo() -> Result<i32, ${0:_}> {
732 let my_var = 5;
733 match my_var {
734 5 => {
735 if true {
736 Ok(42i32)
737 } else {
738 Ok(25i32)
739 }
740 },
741 _ => {
742 let test = "test";
743 if test == "test" {
744 return Ok(bar());
745 }
746 Ok(53i32)
747 },
748 }
749 }"#,
750 );
751 }
752
753 #[test]
754 fn change_return_type_to_result_simple_with_tail_block_like_early_return() {
755 check_assist(
756 change_return_type_to_result,
757 r#"fn foo() -> i<|>32 {
758 let test = "test";
759 if test == "test" {
760 return 24i32;
761 }
762 53i32
763 }"#,
764 r#"fn foo() -> Result<i32, ${0:_}> {
765 let test = "test";
766 if test == "test" {
767 return Ok(24i32);
768 }
769 Ok(53i32)
770 }"#,
771 );
772 }
773
774 #[test]
775 fn change_return_type_to_result_simple_with_closure() {
776 check_assist(
777 change_return_type_to_result,
778 r#"fn foo(the_field: u32) -><|> u32 {
779 let true_closure = || {
780 return true;
781 };
782 if the_field < 5 {
783 let mut i = 0;
784
785
786 if true_closure() {
787 return 99;
788 } else {
789 return 0;
790 }
791 }
792
793 the_field
794 }"#,
795 r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> {
796 let true_closure = || {
797 return true;
798 };
799 if the_field < 5 {
800 let mut i = 0;
801
802
803 if true_closure() {
804 return Ok(99);
805 } else {
806 return Ok(0);
807 }
808 }
809
810 Ok(the_field)
811 }"#,
812 );
813
814 check_assist(
815 change_return_type_to_result,
816 r#"fn foo(the_field: u32) -> u32<|> {
817 let true_closure = || {
818 return true;
819 };
820 if the_field < 5 {
821 let mut i = 0;
822
823
824 if true_closure() {
825 return 99;
826 } else {
827 return 0;
828 }
829 }
830 let t = None;
831
832 t.unwrap_or_else(|| the_field)
833 }"#,
834 r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> {
835 let true_closure = || {
836 return true;
837 };
838 if the_field < 5 {
839 let mut i = 0;
840
841
842 if true_closure() {
843 return Ok(99);
844 } else {
845 return Ok(0);
846 }
847 }
848 let t = None;
849
850 Ok(t.unwrap_or_else(|| the_field))
851 }"#,
852 );
853 }
854
855 #[test]
856 fn change_return_type_to_result_simple_with_weird_forms() {
857 check_assist(
858 change_return_type_to_result,
859 r#"fn foo() -> i32<|> {
860 let test = "test";
861 if test == "test" {
862 return 24i32;
863 }
864 let mut i = 0;
865 loop {
866 if i == 1 {
867 break 55;
868 }
869 i += 1;
870 }
871 }"#,
872 r#"fn foo() -> Result<i32, ${0:_}> {
873 let test = "test";
874 if test == "test" {
875 return Ok(24i32);
876 }
877 let mut i = 0;
878 loop {
879 if i == 1 {
880 break Ok(55);
881 }
882 i += 1;
883 }
884 }"#,
885 );
886
887 check_assist(
888 change_return_type_to_result,
889 r#"fn foo() -> i32<|> {
890 let test = "test";
891 if test == "test" {
892 return 24i32;
893 }
894 let mut i = 0;
895 loop {
896 loop {
897 if i == 1 {
898 break 55;
899 }
900 i += 1;
901 }
902 }
903 }"#,
904 r#"fn foo() -> Result<i32, ${0:_}> {
905 let test = "test";
906 if test == "test" {
907 return Ok(24i32);
908 }
909 let mut i = 0;
910 loop {
911 loop {
912 if i == 1 {
913 break Ok(55);
914 }
915 i += 1;
916 }
917 }
918 }"#,
919 );
920
921 check_assist(
922 change_return_type_to_result,
923 r#"fn foo() -> i3<|>2 {
924 let test = "test";
925 let other = 5;
926 if test == "test" {
927 let res = match other {
928 5 => 43,
929 _ => return 56,
930 };
931 }
932 let mut i = 0;
933 loop {
934 loop {
935 if i == 1 {
936 break 55;
937 }
938 i += 1;
939 }
940 }
941 }"#,
942 r#"fn foo() -> Result<i32, ${0:_}> {
943 let test = "test";
944 let other = 5;
945 if test == "test" {
946 let res = match other {
947 5 => 43,
948 _ => return Ok(56),
949 };
950 }
951 let mut i = 0;
952 loop {
953 loop {
954 if i == 1 {
955 break Ok(55);
956 }
957 i += 1;
958 }
959 }
960 }"#,
961 );
962
963 check_assist(
964 change_return_type_to_result,
965 r#"fn foo(the_field: u32) -> u32<|> {
966 if the_field < 5 {
967 let mut i = 0;
968 loop {
969 if i > 5 {
970 return 55u32;
971 }
972 i += 3;
973 }
974
975 match i {
976 5 => return 99,
977 _ => return 0,
978 };
979 }
980
981 the_field
982 }"#,
983 r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> {
984 if the_field < 5 {
985 let mut i = 0;
986 loop {
987 if i > 5 {
988 return Ok(55u32);
989 }
990 i += 3;
991 }
992
993 match i {
994 5 => return Ok(99),
995 _ => return Ok(0),
996 };
997 }
998
999 Ok(the_field)
1000 }"#,
1001 );
1002
1003 check_assist(
1004 change_return_type_to_result,
1005 r#"fn foo(the_field: u32) -> u3<|>2 {
1006 if the_field < 5 {
1007 let mut i = 0;
1008
1009 match i {
1010 5 => return 99,
1011 _ => return 0,
1012 }
1013 }
1014
1015 the_field
1016 }"#,
1017 r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> {
1018 if the_field < 5 {
1019 let mut i = 0;
1020
1021 match i {
1022 5 => return Ok(99),
1023 _ => return Ok(0),
1024 }
1025 }
1026
1027 Ok(the_field)
1028 }"#,
1029 );
1030
1031 check_assist(
1032 change_return_type_to_result,
1033 r#"fn foo(the_field: u32) -> u32<|> {
1034 if the_field < 5 {
1035 let mut i = 0;
1036
1037 if i == 5 {
1038 return 99
1039 } else {
1040 return 0
1041 }
1042 }
1043
1044 the_field
1045 }"#,
1046 r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> {
1047 if the_field < 5 {
1048 let mut i = 0;
1049
1050 if i == 5 {
1051 return Ok(99)
1052 } else {
1053 return Ok(0)
1054 }
1055 }
1056
1057 Ok(the_field)
1058 }"#,
1059 );
1060
1061 check_assist(
1062 change_return_type_to_result,
1063 r#"fn foo(the_field: u32) -> <|>u32 {
1064 if the_field < 5 {
1065 let mut i = 0;
1066
1067 if i == 5 {
1068 return 99;
1069 } else {
1070 return 0;
1071 }
1072 }
1073
1074 the_field
1075 }"#,
1076 r#"fn foo(the_field: u32) -> Result<u32, ${0:_}> {
1077 if the_field < 5 {
1078 let mut i = 0;
1079
1080 if i == 5 {
1081 return Ok(99);
1082 } else {
1083 return Ok(0);
1084 }
1085 }
1086
1087 Ok(the_field)
1088 }"#,
1089 );
1090 }
1091}
diff --git a/crates/assists/src/handlers/expand_glob_import.rs b/crates/assists/src/handlers/expand_glob_import.rs
index 853266395..f51a9a4ad 100644
--- a/crates/assists/src/handlers/expand_glob_import.rs
+++ b/crates/assists/src/handlers/expand_glob_import.rs
@@ -5,13 +5,13 @@ use ide_db::{
5 search::SearchScope, 5 search::SearchScope,
6}; 6};
7use syntax::{ 7use syntax::{
8 algo, 8 algo::SyntaxRewriter,
9 ast::{self, make}, 9 ast::{self, make},
10 AstNode, Direction, SyntaxNode, SyntaxToken, T, 10 AstNode, Direction, SyntaxNode, SyntaxToken, T,
11}; 11};
12 12
13use crate::{ 13use crate::{
14 assist_context::{AssistBuilder, AssistContext, Assists}, 14 assist_context::{AssistContext, Assists},
15 AssistId, AssistKind, 15 AssistId, AssistKind,
16}; 16};
17 17
@@ -61,7 +61,9 @@ pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext) -> Opti
61 "Expand glob import", 61 "Expand glob import",
62 target.text_range(), 62 target.text_range(),
63 |builder| { 63 |builder| {
64 replace_ast(builder, parent, mod_path, names_to_import); 64 let mut rewriter = SyntaxRewriter::default();
65 replace_ast(&mut rewriter, parent, mod_path, names_to_import);
66 builder.rewrite(rewriter);
65 }, 67 },
66 ) 68 )
67} 69}
@@ -236,7 +238,7 @@ fn find_names_to_import(
236} 238}
237 239
238fn replace_ast( 240fn replace_ast(
239 builder: &mut AssistBuilder, 241 rewriter: &mut SyntaxRewriter,
240 parent: Either<ast::UseTree, ast::UseTreeList>, 242 parent: Either<ast::UseTree, ast::UseTreeList>,
241 path: ast::Path, 243 path: ast::Path,
242 names_to_import: Vec<Name>, 244 names_to_import: Vec<Name>,
@@ -264,32 +266,21 @@ fn replace_ast(
264 match use_trees.as_slice() { 266 match use_trees.as_slice() {
265 [name] => { 267 [name] => {
266 if let Some(end_path) = name.path() { 268 if let Some(end_path) = name.path() {
267 let replacement = 269 rewriter.replace_ast(
268 make::use_tree(make::path_concat(path, end_path), None, None, false); 270 &parent.left_or_else(|tl| tl.parent_use_tree()),
269 271 &make::use_tree(make::path_concat(path, end_path), None, None, false),
270 algo::diff( 272 );
271 &parent.either(|n| n.syntax().clone(), |n| n.syntax().clone()),
272 replacement.syntax(),
273 )
274 .into_text_edit(builder.text_edit_builder());
275 } 273 }
276 } 274 }
277 names => { 275 names => match &parent {
278 let replacement = match parent { 276 Either::Left(parent) => rewriter.replace_ast(
279 Either::Left(_) => { 277 parent,
280 make::use_tree(path, Some(make::use_tree_list(names.to_owned())), None, false) 278 &make::use_tree(path, Some(make::use_tree_list(names.to_owned())), None, false),
281 .syntax() 279 ),
282 .clone() 280 Either::Right(parent) => {
283 } 281 rewriter.replace_ast(parent, &make::use_tree_list(names.to_owned()))
284 Either::Right(_) => make::use_tree_list(names.to_owned()).syntax().clone(), 282 }
285 }; 283 },
286
287 algo::diff(
288 &parent.either(|n| n.syntax().clone(), |n| n.syntax().clone()),
289 &replacement,
290 )
291 .into_text_edit(builder.text_edit_builder());
292 }
293 }; 284 };
294} 285}
295 286
@@ -884,4 +875,33 @@ fn qux(baz: Baz) {}
884 ", 875 ",
885 ) 876 )
886 } 877 }
878
879 #[test]
880 fn expanding_glob_import_single_nested_glob_only() {
881 check_assist(
882 expand_glob_import,
883 r"
884mod foo {
885 pub struct Bar;
886}
887
888use foo::{*<|>};
889
890struct Baz {
891 bar: Bar
892}
893",
894 r"
895mod foo {
896 pub struct Bar;
897}
898
899use foo::Bar;
900
901struct Baz {
902 bar: Bar
903}
904",
905 );
906 }
887} 907}
diff --git a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
index 14209b771..cac77c49b 100644
--- a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
+++ b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs
@@ -5,10 +5,9 @@ use hir::{AsName, EnumVariant, Module, ModuleDef, Name};
5use ide_db::{defs::Definition, search::Reference, RootDatabase}; 5use ide_db::{defs::Definition, search::Reference, RootDatabase};
6use rustc_hash::{FxHashMap, FxHashSet}; 6use rustc_hash::{FxHashMap, FxHashSet};
7use syntax::{ 7use syntax::{
8 algo::find_node_at_offset, 8 algo::{find_node_at_offset, SyntaxRewriter},
9 algo::SyntaxRewriter, 9 ast::{self, edit::IndentLevel, make, AstNode, NameOwner, VisibilityOwner},
10 ast::{self, edit::IndentLevel, make, ArgListOwner, AstNode, NameOwner, VisibilityOwner}, 10 SourceFile, SyntaxElement, SyntaxNode, T,
11 SourceFile, SyntaxElement,
12}; 11};
13 12
14use crate::{ 13use crate::{
@@ -130,18 +129,21 @@ fn existing_definition(db: &RootDatabase, variant_name: &ast::Name, variant: &En
130fn insert_import( 129fn insert_import(
131 ctx: &AssistContext, 130 ctx: &AssistContext,
132 rewriter: &mut SyntaxRewriter, 131 rewriter: &mut SyntaxRewriter,
133 path: &ast::PathExpr, 132 scope_node: &SyntaxNode,
134 module: &Module, 133 module: &Module,
135 enum_module_def: &ModuleDef, 134 enum_module_def: &ModuleDef,
136 variant_hir_name: &Name, 135 variant_hir_name: &Name,
137) -> Option<()> { 136) -> Option<()> {
138 let db = ctx.db(); 137 let db = ctx.db();
139 let mod_path = module.find_use_path(db, enum_module_def.clone()); 138 let mod_path = module.find_use_path_prefixed(
139 db,
140 enum_module_def.clone(),
141 ctx.config.insert_use.prefix_kind,
142 );
140 if let Some(mut mod_path) = mod_path { 143 if let Some(mut mod_path) = mod_path {
141 mod_path.segments.pop(); 144 mod_path.segments.pop();
142 mod_path.segments.push(variant_hir_name.clone()); 145 mod_path.segments.push(variant_hir_name.clone());
143 let scope = ImportScope::find_insert_use_container(path.syntax(), ctx)?; 146 let scope = ImportScope::find_insert_use_container(scope_node, &ctx.sema)?;
144
145 *rewriter += insert_use(&scope, mod_path_to_ast(&mod_path), ctx.config.insert_use.merge); 147 *rewriter += insert_use(&scope, mod_path_to_ast(&mod_path), ctx.config.insert_use.merge);
146 } 148 }
147 Some(()) 149 Some(())
@@ -204,27 +206,31 @@ fn update_reference(
204 variant_hir_name: &Name, 206 variant_hir_name: &Name,
205 visited_modules_set: &mut FxHashSet<Module>, 207 visited_modules_set: &mut FxHashSet<Module>,
206) -> Option<()> { 208) -> Option<()> {
207 let path_expr: ast::PathExpr = find_node_at_offset::<ast::PathExpr>( 209 let offset = reference.file_range.range.start();
208 source_file.syntax(), 210 let (segment, expr) = if let Some(path_expr) =
209 reference.file_range.range.start(), 211 find_node_at_offset::<ast::PathExpr>(source_file.syntax(), offset)
210 )?; 212 {
211 let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?; 213 // tuple variant
212 let list = call.arg_list()?; 214 (path_expr.path()?.segment()?, path_expr.syntax().parent()?.clone())
213 let segment = path_expr.path()?.segment()?; 215 } else if let Some(record_expr) =
214 let module = ctx.sema.scope(&path_expr.syntax()).module()?; 216 find_node_at_offset::<ast::RecordExpr>(source_file.syntax(), offset)
217 {
218 // record variant
219 (record_expr.path()?.segment()?, record_expr.syntax().clone())
220 } else {
221 return None;
222 };
223
224 let module = ctx.sema.scope(&expr).module()?;
215 if !visited_modules_set.contains(&module) { 225 if !visited_modules_set.contains(&module) {
216 if insert_import(ctx, rewriter, &path_expr, &module, enum_module_def, variant_hir_name) 226 if insert_import(ctx, rewriter, &expr, &module, enum_module_def, variant_hir_name).is_some()
217 .is_some()
218 { 227 {
219 visited_modules_set.insert(module); 228 visited_modules_set.insert(module);
220 } 229 }
221 } 230 }
222 231 rewriter.insert_after(segment.syntax(), &make::token(T!['(']));
223 let lparen = syntax::SyntaxElement::from(list.l_paren_token()?); 232 rewriter.insert_after(segment.syntax(), segment.syntax());
224 let rparen = syntax::SyntaxElement::from(list.r_paren_token()?); 233 rewriter.insert_after(&expr, &make::token(T![')']));
225 rewriter.insert_after(&lparen, segment.syntax());
226 rewriter.insert_after(&lparen, &lparen);
227 rewriter.insert_before(&rparen, &rparen);
228 Some(()) 234 Some(())
229} 235}
230 236
@@ -320,7 +326,7 @@ fn another_fn() {
320 r#"use my_mod::my_other_mod::MyField; 326 r#"use my_mod::my_other_mod::MyField;
321 327
322mod my_mod { 328mod my_mod {
323 use my_other_mod::MyField; 329 use self::my_other_mod::MyField;
324 330
325 fn another_fn() { 331 fn another_fn() {
326 let m = my_other_mod::MyEnum::MyField(MyField(1, 1)); 332 let m = my_other_mod::MyEnum::MyField(MyField(1, 1));
@@ -345,6 +351,130 @@ fn another_fn() {
345 ); 351 );
346 } 352 }
347 353
354 #[test]
355 fn extract_record_fix_references() {
356 check_assist(
357 extract_struct_from_enum_variant,
358 r#"
359enum E {
360 <|>V { i: i32, j: i32 }
361}
362
363fn f() {
364 let e = E::V { i: 9, j: 2 };
365}
366"#,
367 r#"
368struct V{ pub i: i32, pub j: i32 }
369
370enum E {
371 V(V)
372}
373
374fn f() {
375 let e = E::V(V { i: 9, j: 2 });
376}
377"#,
378 )
379 }
380
381 #[test]
382 fn test_several_files() {
383 check_assist(
384 extract_struct_from_enum_variant,
385 r#"
386//- /main.rs
387enum E {
388 <|>V(i32, i32)
389}
390mod foo;
391
392//- /foo.rs
393use crate::E;
394fn f() {
395 let e = E::V(9, 2);
396}
397"#,
398 r#"
399//- /main.rs
400struct V(pub i32, pub i32);
401
402enum E {
403 V(V)
404}
405mod foo;
406
407//- /foo.rs
408use crate::{E, V};
409fn f() {
410 let e = E::V(V(9, 2));
411}
412"#,
413 )
414 }
415
416 #[test]
417 fn test_several_files_record() {
418 check_assist(
419 extract_struct_from_enum_variant,
420 r#"
421//- /main.rs
422enum E {
423 <|>V { i: i32, j: i32 }
424}
425mod foo;
426
427//- /foo.rs
428use crate::E;
429fn f() {
430 let e = E::V { i: 9, j: 2 };
431}
432"#,
433 r#"
434//- /main.rs
435struct V{ pub i: i32, pub j: i32 }
436
437enum E {
438 V(V)
439}
440mod foo;
441
442//- /foo.rs
443use crate::{E, V};
444fn f() {
445 let e = E::V(V { i: 9, j: 2 });
446}
447"#,
448 )
449 }
450
451 #[test]
452 fn test_extract_struct_record_nested_call_exp() {
453 check_assist(
454 extract_struct_from_enum_variant,
455 r#"
456enum A { <|>One { a: u32, b: u32 } }
457
458struct B(A);
459
460fn foo() {
461 let _ = B(A::One { a: 1, b: 2 });
462}
463"#,
464 r#"
465struct One{ pub a: u32, pub b: u32 }
466
467enum A { One(One) }
468
469struct B(A);
470
471fn foo() {
472 let _ = B(A::One(One { a: 1, b: 2 }));
473}
474"#,
475 );
476 }
477
348 fn check_not_applicable(ra_fixture: &str) { 478 fn check_not_applicable(ra_fixture: &str) {
349 let fixture = 479 let fixture =
350 format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE); 480 format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE);
diff --git a/crates/assists/src/handlers/ignore_test.rs b/crates/assists/src/handlers/ignore_test.rs
new file mode 100644
index 000000000..5096a0005
--- /dev/null
+++ b/crates/assists/src/handlers/ignore_test.rs
@@ -0,0 +1,103 @@
1use syntax::{
2 ast::{self, AttrsOwner},
3 AstNode, AstToken,
4};
5
6use crate::{utils::test_related_attribute, AssistContext, AssistId, AssistKind, Assists};
7
8// Assist: ignore_test
9//
10// Adds `#[ignore]` attribute to the test.
11//
12// ```
13// <|>#[test]
14// fn arithmetics {
15// assert_eq!(2 + 2, 5);
16// }
17// ```
18// ->
19// ```
20// #[test]
21// #[ignore]
22// fn arithmetics {
23// assert_eq!(2 + 2, 5);
24// }
25// ```
26pub(crate) fn ignore_test(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
27 let attr: ast::Attr = ctx.find_node_at_offset()?;
28 let func = attr.syntax().parent().and_then(ast::Fn::cast)?;
29 let attr = test_related_attribute(&func)?;
30
31 match has_ignore_attribute(&func) {
32 None => acc.add(
33 AssistId("ignore_test", AssistKind::None),
34 "Ignore this test",
35 attr.syntax().text_range(),
36 |builder| builder.insert(attr.syntax().text_range().end(), &format!("\n#[ignore]")),
37 ),
38 Some(ignore_attr) => acc.add(
39 AssistId("unignore_test", AssistKind::None),
40 "Re-enable this test",
41 ignore_attr.syntax().text_range(),
42 |builder| {
43 builder.delete(ignore_attr.syntax().text_range());
44 let whitespace = ignore_attr
45 .syntax()
46 .next_sibling_or_token()
47 .and_then(|x| x.into_token())
48 .and_then(ast::Whitespace::cast);
49 if let Some(whitespace) = whitespace {
50 builder.delete(whitespace.syntax().text_range());
51 }
52 },
53 ),
54 }
55}
56
57fn has_ignore_attribute(fn_def: &ast::Fn) -> Option<ast::Attr> {
58 fn_def.attrs().find_map(|attr| {
59 if attr.path()?.syntax().text() == "ignore" {
60 Some(attr)
61 } else {
62 None
63 }
64 })
65}
66
67#[cfg(test)]
68mod tests {
69 use super::ignore_test;
70 use crate::tests::check_assist;
71
72 #[test]
73 fn test_base_case() {
74 check_assist(
75 ignore_test,
76 r#"
77 #[test<|>]
78 fn test() {}
79 "#,
80 r#"
81 #[test]
82 #[ignore]
83 fn test() {}
84 "#,
85 )
86 }
87
88 #[test]
89 fn test_unignore() {
90 check_assist(
91 ignore_test,
92 r#"
93 #[test<|>]
94 #[ignore]
95 fn test() {}
96 "#,
97 r#"
98 #[test]
99 fn test() {}
100 "#,
101 )
102 }
103}
diff --git a/crates/assists/src/handlers/remove_dbg.rs b/crates/assists/src/handlers/remove_dbg.rs
index 9731344b8..eae6367c1 100644
--- a/crates/assists/src/handlers/remove_dbg.rs
+++ b/crates/assists/src/handlers/remove_dbg.rs
@@ -1,6 +1,6 @@
1use syntax::{ 1use syntax::{
2 ast::{self, AstNode}, 2 ast::{self, AstNode},
3 SyntaxElement, SyntaxKind, TextRange, TextSize, T, 3 match_ast, SyntaxElement, SyntaxKind, TextRange, TextSize, T,
4}; 4};
5 5
6use crate::{AssistContext, AssistId, AssistKind, Assists}; 6use crate::{AssistContext, AssistId, AssistKind, Assists};
@@ -49,12 +49,29 @@ fn adjusted_macro_contents(macro_call: &ast::MacroCall) -> Option<String> {
49 macro_text_with_brackets.len() - TextSize::of(')'), 49 macro_text_with_brackets.len() - TextSize::of(')'),
50 )); 50 ));
51 51
52 let is_leaf = macro_call.syntax().next_sibling().is_none(); 52 Some(
53 Some(if !is_leaf && needs_parentheses_around_macro_contents(contents) { 53 if !is_leaf_or_control_flow_expr(macro_call)
54 format!("({})", macro_text_in_brackets) 54 && needs_parentheses_around_macro_contents(contents)
55 } else { 55 {
56 macro_text_in_brackets.to_string() 56 format!("({})", macro_text_in_brackets)
57 }) 57 } else {
58 macro_text_in_brackets.to_string()
59 },
60 )
61}
62
63fn is_leaf_or_control_flow_expr(macro_call: &ast::MacroCall) -> bool {
64 macro_call.syntax().next_sibling().is_none()
65 || match macro_call.syntax().parent() {
66 Some(parent) => match_ast! {
67 match parent {
68 ast::Condition(_it) => true,
69 ast::MatchExpr(_it) => true,
70 _ => false,
71 }
72 },
73 None => false,
74 }
58} 75}
59 76
60/// Verifies that the given macro_call actually matches the given name 77/// Verifies that the given macro_call actually matches the given name
@@ -361,4 +378,44 @@ fn main() {
361 r#"let res = (foo..=bar).foo();"#, 378 r#"let res = (foo..=bar).foo();"#,
362 ); 379 );
363 } 380 }
381
382 #[test]
383 fn test_remove_dbg_followed_by_block() {
384 check_assist(
385 remove_dbg,
386 r#"fn foo() {
387 if <|>dbg!(x || y) {}
388}"#,
389 r#"fn foo() {
390 if x || y {}
391}"#,
392 );
393 check_assist(
394 remove_dbg,
395 r#"fn foo() {
396 while let foo = <|>dbg!(&x) {}
397}"#,
398 r#"fn foo() {
399 while let foo = &x {}
400}"#,
401 );
402 check_assist(
403 remove_dbg,
404 r#"fn foo() {
405 if let foo = <|>dbg!(&x) {}
406}"#,
407 r#"fn foo() {
408 if let foo = &x {}
409}"#,
410 );
411 check_assist(
412 remove_dbg,
413 r#"fn foo() {
414 match <|>dbg!(&x) {}
415}"#,
416 r#"fn foo() {
417 match &x {}
418}"#,
419 );
420 }
364} 421}
diff --git a/crates/assists/src/handlers/remove_unused_param.rs b/crates/assists/src/handlers/remove_unused_param.rs
index 5fccca54b..1ff5e92b0 100644
--- a/crates/assists/src/handlers/remove_unused_param.rs
+++ b/crates/assists/src/handlers/remove_unused_param.rs
@@ -73,7 +73,8 @@ fn process_usage(
73 let source_file = ctx.sema.parse(usage.file_range.file_id); 73 let source_file = ctx.sema.parse(usage.file_range.file_id);
74 let call_expr: ast::CallExpr = 74 let call_expr: ast::CallExpr =
75 find_node_at_range(source_file.syntax(), usage.file_range.range)?; 75 find_node_at_range(source_file.syntax(), usage.file_range.range)?;
76 if call_expr.expr()?.syntax().text_range() != usage.file_range.range { 76 let call_expr_range = call_expr.expr()?.syntax().text_range();
77 if !call_expr_range.contains_range(usage.file_range.range) {
77 return None; 78 return None;
78 } 79 }
79 let arg = call_expr.arg_list()?.args().nth(arg_to_remove)?; 80 let arg = call_expr.arg_list()?.args().nth(arg_to_remove)?;
@@ -118,6 +119,53 @@ fn b() { foo(9, ) }
118 } 119 }
119 120
120 #[test] 121 #[test]
122 fn remove_unused_qualified_call() {
123 check_assist(
124 remove_unused_param,
125 r#"
126mod bar { pub fn foo(x: i32, <|>y: i32) { x; } }
127fn b() { bar::foo(9, 2) }
128"#,
129 r#"
130mod bar { pub fn foo(x: i32) { x; } }
131fn b() { bar::foo(9) }
132"#,
133 );
134 }
135
136 #[test]
137 fn remove_unused_turbofished_func() {
138 check_assist(
139 remove_unused_param,
140 r#"
141pub fn foo<T>(x: T, <|>y: i32) { x; }
142fn b() { foo::<i32>(9, 2) }
143"#,
144 r#"
145pub fn foo<T>(x: T) { x; }
146fn b() { foo::<i32>(9) }
147"#,
148 );
149 }
150
151 #[test]
152 fn remove_unused_generic_unused_param_func() {
153 check_assist(
154 remove_unused_param,
155 r#"
156pub fn foo<T>(x: i32, <|>y: T) { x; }
157fn b() { foo::<i32>(9, 2) }
158fn b2() { foo(9, 2) }
159"#,
160 r#"
161pub fn foo<T>(x: i32) { x; }
162fn b() { foo::<i32>(9) }
163fn b2() { foo(9) }
164"#,
165 );
166 }
167
168 #[test]
121 fn keep_used() { 169 fn keep_used() {
122 mark::check!(keep_used); 170 mark::check!(keep_used);
123 check_assist_not_applicable( 171 check_assist_not_applicable(
@@ -128,4 +176,37 @@ fn main() { foo(9, 2) }
128"#, 176"#,
129 ); 177 );
130 } 178 }
179
180 #[test]
181 fn remove_across_files() {
182 check_assist(
183 remove_unused_param,
184 r#"
185//- /main.rs
186fn foo(x: i32, <|>y: i32) { x; }
187
188mod foo;
189
190//- /foo.rs
191use super::foo;
192
193fn bar() {
194 let _ = foo(1, 2);
195}
196"#,
197 r#"
198//- /main.rs
199fn foo(x: i32) { x; }
200
201mod foo;
202
203//- /foo.rs
204use super::foo;
205
206fn bar() {
207 let _ = foo(1);
208}
209"#,
210 )
211 }
131} 212}
diff --git a/crates/assists/src/handlers/reorder_fields.rs b/crates/assists/src/handlers/reorder_fields.rs
index 527f457a7..7c0f0f44e 100644
--- a/crates/assists/src/handlers/reorder_fields.rs
+++ b/crates/assists/src/handlers/reorder_fields.rs
@@ -47,9 +47,11 @@ fn reorder<R: AstNode>(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
47 "Reorder record fields", 47 "Reorder record fields",
48 target, 48 target,
49 |edit| { 49 |edit| {
50 let mut rewriter = algo::SyntaxRewriter::default();
50 for (old, new) in fields.iter().zip(&sorted_fields) { 51 for (old, new) in fields.iter().zip(&sorted_fields) {
51 algo::diff(old, new).into_text_edit(edit.text_edit_builder()); 52 rewriter.replace(old, new);
52 } 53 }
54 edit.rewrite(rewriter);
53 }, 55 },
54 ) 56 )
55} 57}
diff --git a/crates/assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/assists/src/handlers/replace_derive_with_manual_impl.rs
new file mode 100644
index 000000000..453a6cebf
--- /dev/null
+++ b/crates/assists/src/handlers/replace_derive_with_manual_impl.rs
@@ -0,0 +1,400 @@
1use ide_db::imports_locator;
2use itertools::Itertools;
3use syntax::{
4 ast::{self, make, AstNode},
5 Direction, SmolStr,
6 SyntaxKind::{IDENT, WHITESPACE},
7 TextSize,
8};
9
10use crate::{
11 assist_context::{AssistBuilder, AssistContext, Assists},
12 utils::{
13 add_trait_assoc_items_to_impl, filter_assoc_items, mod_path_to_ast, render_snippet, Cursor,
14 DefaultMethods,
15 },
16 AssistId, AssistKind,
17};
18
19// Assist: replace_derive_with_manual_impl
20//
21// Converts a `derive` impl into a manual one.
22//
23// ```
24// # trait Debug { fn fmt(&self, f: &mut Formatter) -> Result<()>; }
25// #[derive(Deb<|>ug, Display)]
26// struct S;
27// ```
28// ->
29// ```
30// # trait Debug { fn fmt(&self, f: &mut Formatter) -> Result<()>; }
31// #[derive(Display)]
32// struct S;
33//
34// impl Debug for S {
35// fn fmt(&self, f: &mut Formatter) -> Result<()> {
36// ${0:todo!()}
37// }
38// }
39// ```
40pub(crate) fn replace_derive_with_manual_impl(
41 acc: &mut Assists,
42 ctx: &AssistContext,
43) -> Option<()> {
44 let attr = ctx.find_node_at_offset::<ast::Attr>()?;
45
46 let attr_name = attr
47 .syntax()
48 .descendants_with_tokens()
49 .filter(|t| t.kind() == IDENT)
50 .find_map(syntax::NodeOrToken::into_token)
51 .filter(|t| t.text() == "derive")?
52 .text()
53 .clone();
54
55 let trait_token =
56 ctx.token_at_offset().find(|t| t.kind() == IDENT && *t.text() != attr_name)?;
57 let trait_path = make::path_unqualified(make::path_segment(make::name_ref(trait_token.text())));
58
59 let annotated_name = attr.syntax().siblings(Direction::Next).find_map(ast::Name::cast)?;
60 let insert_pos = annotated_name.syntax().parent()?.text_range().end();
61
62 let current_module = ctx.sema.scope(annotated_name.syntax()).module()?;
63 let current_crate = current_module.krate();
64
65 let found_traits =
66 imports_locator::find_exact_imports(&ctx.sema, current_crate, trait_token.text())
67 .filter_map(
68 |candidate: either::Either<hir::ModuleDef, hir::MacroDef>| match candidate {
69 either::Either::Left(hir::ModuleDef::Trait(trait_)) => Some(trait_),
70 _ => None,
71 },
72 )
73 .flat_map(|trait_| {
74 current_module
75 .find_use_path(ctx.sema.db, hir::ModuleDef::Trait(trait_))
76 .as_ref()
77 .map(mod_path_to_ast)
78 .zip(Some(trait_))
79 });
80
81 let mut no_traits_found = true;
82 for (trait_path, trait_) in found_traits.inspect(|_| no_traits_found = false) {
83 add_assist(acc, ctx, &attr, &trait_path, Some(trait_), &annotated_name, insert_pos)?;
84 }
85 if no_traits_found {
86 add_assist(acc, ctx, &attr, &trait_path, None, &annotated_name, insert_pos)?;
87 }
88 Some(())
89}
90
91fn add_assist(
92 acc: &mut Assists,
93 ctx: &AssistContext,
94 attr: &ast::Attr,
95 trait_path: &ast::Path,
96 trait_: Option<hir::Trait>,
97 annotated_name: &ast::Name,
98 insert_pos: TextSize,
99) -> Option<()> {
100 let target = attr.syntax().text_range();
101 let input = attr.token_tree()?;
102 let label = format!("Convert to manual `impl {} for {}`", trait_path, annotated_name);
103 let trait_name = trait_path.segment().and_then(|seg| seg.name_ref())?;
104
105 acc.add(
106 AssistId("replace_derive_with_manual_impl", AssistKind::Refactor),
107 label,
108 target,
109 |builder| {
110 let impl_def_with_items =
111 impl_def_from_trait(&ctx.sema, annotated_name, trait_, trait_path);
112 update_attribute(builder, &input, &trait_name, &attr);
113 match (ctx.config.snippet_cap, impl_def_with_items) {
114 (None, _) => builder.insert(
115 insert_pos,
116 format!("\n\nimpl {} for {} {{\n\n}}", trait_path, annotated_name),
117 ),
118 (Some(cap), None) => builder.insert_snippet(
119 cap,
120 insert_pos,
121 format!("\n\nimpl {} for {} {{\n $0\n}}", trait_path, annotated_name),
122 ),
123 (Some(cap), Some((impl_def, first_assoc_item))) => {
124 let mut cursor = Cursor::Before(first_assoc_item.syntax());
125 let placeholder;
126 if let ast::AssocItem::Fn(ref func) = first_assoc_item {
127 if let Some(m) = func.syntax().descendants().find_map(ast::MacroCall::cast)
128 {
129 if m.syntax().text() == "todo!()" {
130 placeholder = m;
131 cursor = Cursor::Replace(placeholder.syntax());
132 }
133 }
134 }
135
136 builder.insert_snippet(
137 cap,
138 insert_pos,
139 format!("\n\n{}", render_snippet(cap, impl_def.syntax(), cursor)),
140 )
141 }
142 };
143 },
144 )
145}
146
147fn impl_def_from_trait(
148 sema: &hir::Semantics<ide_db::RootDatabase>,
149 annotated_name: &ast::Name,
150 trait_: Option<hir::Trait>,
151 trait_path: &ast::Path,
152) -> Option<(ast::Impl, ast::AssocItem)> {
153 let trait_ = trait_?;
154 let target_scope = sema.scope(annotated_name.syntax());
155 let trait_items = filter_assoc_items(sema.db, &trait_.items(sema.db), DefaultMethods::No);
156 if trait_items.is_empty() {
157 return None;
158 }
159 let impl_def = make::impl_trait(
160 trait_path.clone(),
161 make::path_unqualified(make::path_segment(make::name_ref(annotated_name.text()))),
162 );
163 let (impl_def, first_assoc_item) =
164 add_trait_assoc_items_to_impl(sema, trait_items, trait_, impl_def, target_scope);
165 Some((impl_def, first_assoc_item))
166}
167
168fn update_attribute(
169 builder: &mut AssistBuilder,
170 input: &ast::TokenTree,
171 trait_name: &ast::NameRef,
172 attr: &ast::Attr,
173) {
174 let new_attr_input = input
175 .syntax()
176 .descendants_with_tokens()
177 .filter(|t| t.kind() == IDENT)
178 .filter_map(|t| t.into_token().map(|t| t.text().clone()))
179 .filter(|t| t != trait_name.text())
180 .collect::<Vec<SmolStr>>();
181 let has_more_derives = !new_attr_input.is_empty();
182
183 if has_more_derives {
184 let new_attr_input = format!("({})", new_attr_input.iter().format(", "));
185 builder.replace(input.syntax().text_range(), new_attr_input);
186 } else {
187 let attr_range = attr.syntax().text_range();
188 builder.delete(attr_range);
189
190 if let Some(line_break_range) = attr
191 .syntax()
192 .next_sibling_or_token()
193 .filter(|t| t.kind() == WHITESPACE)
194 .map(|t| t.text_range())
195 {
196 builder.delete(line_break_range);
197 }
198 }
199}
200
201#[cfg(test)]
202mod tests {
203 use crate::tests::{check_assist, check_assist_not_applicable};
204
205 use super::*;
206
207 #[test]
208 fn add_custom_impl_debug() {
209 check_assist(
210 replace_derive_with_manual_impl,
211 "
212mod fmt {
213 pub struct Error;
214 pub type Result = Result<(), Error>;
215 pub struct Formatter<'a>;
216 pub trait Debug {
217 fn fmt(&self, f: &mut Formatter<'_>) -> Result;
218 }
219}
220
221#[derive(Debu<|>g)]
222struct Foo {
223 bar: String,
224}
225",
226 "
227mod fmt {
228 pub struct Error;
229 pub type Result = Result<(), Error>;
230 pub struct Formatter<'a>;
231 pub trait Debug {
232 fn fmt(&self, f: &mut Formatter<'_>) -> Result;
233 }
234}
235
236struct Foo {
237 bar: String,
238}
239
240impl fmt::Debug for Foo {
241 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
242 ${0:todo!()}
243 }
244}
245",
246 )
247 }
248 #[test]
249 fn add_custom_impl_all() {
250 check_assist(
251 replace_derive_with_manual_impl,
252 "
253mod foo {
254 pub trait Bar {
255 type Qux;
256 const Baz: usize = 42;
257 const Fez: usize;
258 fn foo();
259 fn bar() {}
260 }
261}
262
263#[derive(<|>Bar)]
264struct Foo {
265 bar: String,
266}
267",
268 "
269mod foo {
270 pub trait Bar {
271 type Qux;
272 const Baz: usize = 42;
273 const Fez: usize;
274 fn foo();
275 fn bar() {}
276 }
277}
278
279struct Foo {
280 bar: String,
281}
282
283impl foo::Bar for Foo {
284 $0type Qux;
285
286 const Baz: usize = 42;
287
288 const Fez: usize;
289
290 fn foo() {
291 todo!()
292 }
293}
294",
295 )
296 }
297 #[test]
298 fn add_custom_impl_for_unique_input() {
299 check_assist(
300 replace_derive_with_manual_impl,
301 "
302#[derive(Debu<|>g)]
303struct Foo {
304 bar: String,
305}
306 ",
307 "
308struct Foo {
309 bar: String,
310}
311
312impl Debug for Foo {
313 $0
314}
315 ",
316 )
317 }
318
319 #[test]
320 fn add_custom_impl_for_with_visibility_modifier() {
321 check_assist(
322 replace_derive_with_manual_impl,
323 "
324#[derive(Debug<|>)]
325pub struct Foo {
326 bar: String,
327}
328 ",
329 "
330pub struct Foo {
331 bar: String,
332}
333
334impl Debug for Foo {
335 $0
336}
337 ",
338 )
339 }
340
341 #[test]
342 fn add_custom_impl_when_multiple_inputs() {
343 check_assist(
344 replace_derive_with_manual_impl,
345 "
346#[derive(Display, Debug<|>, Serialize)]
347struct Foo {}
348 ",
349 "
350#[derive(Display, Serialize)]
351struct Foo {}
352
353impl Debug for Foo {
354 $0
355}
356 ",
357 )
358 }
359
360 #[test]
361 fn test_ignore_derive_macro_without_input() {
362 check_assist_not_applicable(
363 replace_derive_with_manual_impl,
364 "
365#[derive(<|>)]
366struct Foo {}
367 ",
368 )
369 }
370
371 #[test]
372 fn test_ignore_if_cursor_on_param() {
373 check_assist_not_applicable(
374 replace_derive_with_manual_impl,
375 "
376#[derive<|>(Debug)]
377struct Foo {}
378 ",
379 );
380
381 check_assist_not_applicable(
382 replace_derive_with_manual_impl,
383 "
384#[derive(Debug)<|>]
385struct Foo {}
386 ",
387 )
388 }
389
390 #[test]
391 fn test_ignore_if_not_derive() {
392 check_assist_not_applicable(
393 replace_derive_with_manual_impl,
394 "
395#[allow(non_camel_<|>case_types)]
396struct Foo {}
397 ",
398 )
399 }
400}
diff --git a/crates/assists/src/handlers/replace_qualified_name_with_use.rs b/crates/assists/src/handlers/replace_qualified_name_with_use.rs
index d7e1d9580..a66db9ae3 100644
--- a/crates/assists/src/handlers/replace_qualified_name_with_use.rs
+++ b/crates/assists/src/handlers/replace_qualified_name_with_use.rs
@@ -34,7 +34,7 @@ pub(crate) fn replace_qualified_name_with_use(
34 } 34 }
35 35
36 let target = path.syntax().text_range(); 36 let target = path.syntax().text_range();
37 let scope = ImportScope::find_insert_use_container(path.syntax(), ctx)?; 37 let scope = ImportScope::find_insert_use_container(path.syntax(), &ctx.sema)?;
38 let syntax = scope.as_syntax_node(); 38 let syntax = scope.as_syntax_node();
39 acc.add( 39 acc.add(
40 AssistId("replace_qualified_name_with_use", AssistKind::RefactorRewrite), 40 AssistId("replace_qualified_name_with_use", AssistKind::RefactorRewrite),
diff --git a/crates/assists/src/handlers/unwrap_block.rs b/crates/assists/src/handlers/unwrap_block.rs
index 36ef871b9..676db7137 100644
--- a/crates/assists/src/handlers/unwrap_block.rs
+++ b/crates/assists/src/handlers/unwrap_block.rs
@@ -3,7 +3,7 @@ use syntax::{
3 self, 3 self,
4 edit::{AstNodeEdit, IndentLevel}, 4 edit::{AstNodeEdit, IndentLevel},
5 }, 5 },
6 AstNode, TextRange, T, 6 AstNode, SyntaxKind, TextRange, T,
7}; 7};
8 8
9use crate::{utils::unwrap_trivial_block, AssistContext, AssistId, AssistKind, Assists}; 9use crate::{utils::unwrap_trivial_block, AssistContext, AssistId, AssistKind, Assists};
@@ -31,11 +31,21 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
31 31
32 let l_curly_token = ctx.find_token_syntax_at_offset(T!['{'])?; 32 let l_curly_token = ctx.find_token_syntax_at_offset(T!['{'])?;
33 let mut block = ast::BlockExpr::cast(l_curly_token.parent())?; 33 let mut block = ast::BlockExpr::cast(l_curly_token.parent())?;
34 let target = block.syntax().text_range();
34 let mut parent = block.syntax().parent()?; 35 let mut parent = block.syntax().parent()?;
35 if ast::MatchArm::can_cast(parent.kind()) { 36 if ast::MatchArm::can_cast(parent.kind()) {
36 parent = parent.ancestors().find(|it| ast::MatchExpr::can_cast(it.kind()))? 37 parent = parent.ancestors().find(|it| ast::MatchExpr::can_cast(it.kind()))?
37 } 38 }
38 39
40 if matches!(parent.kind(), SyntaxKind::BLOCK_EXPR | SyntaxKind::EXPR_STMT) {
41 return acc.add(assist_id, assist_label, target, |builder| {
42 builder.replace(
43 block.syntax().text_range(),
44 update_expr_string(block.to_string(), &[' ', '{', '\n']),
45 );
46 });
47 }
48
39 let parent = ast::Expr::cast(parent)?; 49 let parent = ast::Expr::cast(parent)?;
40 50
41 match parent.clone() { 51 match parent.clone() {
@@ -48,7 +58,6 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
48 // For `else if` blocks 58 // For `else if` blocks
49 let ancestor_then_branch = ancestor.then_branch()?; 59 let ancestor_then_branch = ancestor.then_branch()?;
50 60
51 let target = then_branch.syntax().text_range();
52 return acc.add(assist_id, assist_label, target, |edit| { 61 return acc.add(assist_id, assist_label, target, |edit| {
53 let range_to_del_else_if = TextRange::new( 62 let range_to_del_else_if = TextRange::new(
54 ancestor_then_branch.syntax().text_range().end(), 63 ancestor_then_branch.syntax().text_range().end(),
@@ -68,7 +77,6 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
68 }); 77 });
69 } 78 }
70 } else { 79 } else {
71 let target = block.syntax().text_range();
72 return acc.add(assist_id, assist_label, target, |edit| { 80 return acc.add(assist_id, assist_label, target, |edit| {
73 let range_to_del = TextRange::new( 81 let range_to_del = TextRange::new(
74 then_branch.syntax().text_range().end(), 82 then_branch.syntax().text_range().end(),
@@ -84,7 +92,6 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
84 }; 92 };
85 93
86 let unwrapped = unwrap_trivial_block(block); 94 let unwrapped = unwrap_trivial_block(block);
87 let target = unwrapped.syntax().text_range();
88 acc.add(assist_id, assist_label, target, |builder| { 95 acc.add(assist_id, assist_label, target, |builder| {
89 builder.replace( 96 builder.replace(
90 parent.syntax().text_range(), 97 parent.syntax().text_range(),
@@ -112,31 +119,89 @@ mod tests {
112 use super::*; 119 use super::*;
113 120
114 #[test] 121 #[test]
122 fn unwrap_tail_expr_block() {
123 check_assist(
124 unwrap_block,
125 r#"
126fn main() {
127 <|>{
128 92
129 }
130}
131"#,
132 r#"
133fn main() {
134 92
135}
136"#,
137 )
138 }
139
140 #[test]
141 fn unwrap_stmt_expr_block() {
142 check_assist(
143 unwrap_block,
144 r#"
145fn main() {
146 <|>{
147 92;
148 }
149 ()
150}
151"#,
152 r#"
153fn main() {
154 92;
155 ()
156}
157"#,
158 );
159 // Pedantically, we should add an `;` here...
160 check_assist(
161 unwrap_block,
162 r#"
163fn main() {
164 <|>{
165 92
166 }
167 ()
168}
169"#,
170 r#"
171fn main() {
172 92
173 ()
174}
175"#,
176 );
177 }
178
179 #[test]
115 fn simple_if() { 180 fn simple_if() {
116 check_assist( 181 check_assist(
117 unwrap_block, 182 unwrap_block,
118 r#" 183 r#"
119 fn main() { 184fn main() {
120 bar(); 185 bar();
121 if true {<|> 186 if true {<|>
122 foo(); 187 foo();
123 188
124 //comment 189 //comment
125 bar(); 190 bar();
126 } else { 191 } else {
127 println!("bar"); 192 println!("bar");
128 } 193 }
129 } 194}
130 "#, 195"#,
131 r#" 196 r#"
132 fn main() { 197fn main() {
133 bar(); 198 bar();
134 foo(); 199 foo();
135 200
136 //comment 201 //comment
137 bar(); 202 bar();
138 } 203}
139 "#, 204"#,
140 ); 205 );
141 } 206 }
142 207
@@ -145,30 +210,30 @@ mod tests {
145 check_assist( 210 check_assist(
146 unwrap_block, 211 unwrap_block,
147 r#" 212 r#"
148 fn main() { 213fn main() {
149 bar(); 214 bar();
150 if true { 215 if true {
151 foo(); 216 foo();
152 217
153 //comment 218 //comment
154 bar(); 219 bar();
155 } else {<|> 220 } else {<|>
156 println!("bar"); 221 println!("bar");
157 } 222 }
158 } 223}
159 "#, 224"#,
160 r#" 225 r#"
161 fn main() { 226fn main() {
162 bar(); 227 bar();
163 if true { 228 if true {
164 foo(); 229 foo();
165 230
166 //comment 231 //comment
167 bar(); 232 bar();
168 } 233 }
169 println!("bar"); 234 println!("bar");
170 } 235}
171 "#, 236"#,
172 ); 237 );
173 } 238 }
174 239
@@ -177,32 +242,32 @@ mod tests {
177 check_assist( 242 check_assist(
178 unwrap_block, 243 unwrap_block,
179 r#" 244 r#"
180 fn main() { 245fn main() {
181 //bar(); 246 //bar();
182 if true { 247 if true {
183 println!("true"); 248 println!("true");
184 249
185 //comment 250 //comment
186 //bar(); 251 //bar();
187 } else if false {<|> 252 } else if false {<|>
188 println!("bar"); 253 println!("bar");
189 } else { 254 } else {
190 println!("foo"); 255 println!("foo");
191 } 256 }
192 } 257}
193 "#, 258"#,
194 r#" 259 r#"
195 fn main() { 260fn main() {
196 //bar(); 261 //bar();
197 if true { 262 if true {
198 println!("true"); 263 println!("true");
199 264
200 //comment 265 //comment
201 //bar(); 266 //bar();
202 } 267 }
203 println!("bar"); 268 println!("bar");
204 } 269}
205 "#, 270"#,
206 ); 271 );
207 } 272 }
208 273
@@ -211,34 +276,34 @@ mod tests {
211 check_assist( 276 check_assist(
212 unwrap_block, 277 unwrap_block,
213 r#" 278 r#"
214 fn main() { 279fn main() {
215 //bar(); 280 //bar();
216 if true { 281 if true {
217 println!("true"); 282 println!("true");
218 283
219 //comment 284 //comment
220 //bar(); 285 //bar();
221 } else if false { 286 } else if false {
222 println!("bar"); 287 println!("bar");
223 } else if true {<|> 288 } else if true {<|>
224 println!("foo"); 289 println!("foo");
225 } 290 }
226 } 291}
227 "#, 292"#,
228 r#" 293 r#"
229 fn main() { 294fn main() {
230 //bar(); 295 //bar();
231 if true { 296 if true {
232 println!("true"); 297 println!("true");
233 298
234 //comment 299 //comment
235 //bar(); 300 //bar();
236 } else if false { 301 } else if false {
237 println!("bar"); 302 println!("bar");
238 } 303 }
239 println!("foo"); 304 println!("foo");
240 } 305}
241 "#, 306"#,
242 ); 307 );
243 } 308 }
244 309
@@ -247,38 +312,38 @@ mod tests {
247 check_assist( 312 check_assist(
248 unwrap_block, 313 unwrap_block,
249 r#" 314 r#"
250 fn main() { 315fn main() {
251 //bar(); 316 //bar();
252 if true { 317 if true {
253 println!("true"); 318 println!("true");
254 319
255 //comment 320 //comment
256 //bar(); 321 //bar();
257 } else if false { 322 } else if false {
258 println!("bar"); 323 println!("bar");
259 } else if true { 324 } else if true {
260 println!("foo"); 325 println!("foo");
261 } else {<|> 326 } else {<|>
262 println!("else"); 327 println!("else");
263 } 328 }
264 } 329}
265 "#, 330"#,
266 r#" 331 r#"
267 fn main() { 332fn main() {
268 //bar(); 333 //bar();
269 if true { 334 if true {
270 println!("true"); 335 println!("true");
271 336
272 //comment 337 //comment
273 //bar(); 338 //bar();
274 } else if false { 339 } else if false {
275 println!("bar"); 340 println!("bar");
276 } else if true { 341 } else if true {
277 println!("foo"); 342 println!("foo");
278 } 343 }
279 println!("else"); 344 println!("else");
280 } 345}
281 "#, 346"#,
282 ); 347 );
283 } 348 }
284 349
@@ -287,36 +352,36 @@ mod tests {
287 check_assist( 352 check_assist(
288 unwrap_block, 353 unwrap_block,
289 r#" 354 r#"
290 fn main() { 355fn main() {
291 //bar(); 356 //bar();
292 if true { 357 if true {
293 println!("true"); 358 println!("true");
294 359
295 //comment 360 //comment
296 //bar(); 361 //bar();
297 } else if false { 362 } else if false {
298 println!("bar"); 363 println!("bar");
299 } else if true {<|> 364 } else if true {<|>
300 println!("foo"); 365 println!("foo");
301 } else { 366 } else {
302 println!("else"); 367 println!("else");
303 } 368 }
304 } 369}
305 "#, 370"#,
306 r#" 371 r#"
307 fn main() { 372fn main() {
308 //bar(); 373 //bar();
309 if true { 374 if true {
310 println!("true"); 375 println!("true");
311 376
312 //comment 377 //comment
313 //bar(); 378 //bar();
314 } else if false { 379 } else if false {
315 println!("bar"); 380 println!("bar");
316 } 381 }
317 println!("foo"); 382 println!("foo");
318 } 383}
319 "#, 384"#,
320 ); 385 );
321 } 386 }
322 387
@@ -325,18 +390,18 @@ mod tests {
325 check_assist_not_applicable( 390 check_assist_not_applicable(
326 unwrap_block, 391 unwrap_block,
327 r#" 392 r#"
328 fn main() { 393fn main() {
329 bar();<|> 394 bar();<|>
330 if true { 395 if true {
331 foo(); 396 foo();
332 397
333 //comment 398 //comment
334 bar(); 399 bar();
335 } else { 400 } else {
336 println!("bar"); 401 println!("bar");
337 } 402 }
338 } 403}
339 "#, 404"#,
340 ); 405 );
341 } 406 }
342 407
@@ -345,31 +410,31 @@ mod tests {
345 check_assist( 410 check_assist(
346 unwrap_block, 411 unwrap_block,
347 r#" 412 r#"
348 fn main() { 413fn main() {
349 for i in 0..5 {<|> 414 for i in 0..5 {<|>
350 if true { 415 if true {
351 foo(); 416 foo();
352 417
353 //comment 418 //comment
354 bar(); 419 bar();
355 } else { 420 } else {
356 println!("bar"); 421 println!("bar");
357 } 422 }
358 } 423 }
359 } 424}
360 "#, 425"#,
361 r#" 426 r#"
362 fn main() { 427fn main() {
363 if true { 428 if true {
364 foo(); 429 foo();
365 430
366 //comment 431 //comment
367 bar(); 432 bar();
368 } else { 433 } else {
369 println!("bar"); 434 println!("bar");
370 } 435 }
371 } 436}
372 "#, 437"#,
373 ); 438 );
374 } 439 }
375 440
@@ -378,29 +443,29 @@ mod tests {
378 check_assist( 443 check_assist(
379 unwrap_block, 444 unwrap_block,
380 r#" 445 r#"
381 fn main() { 446fn main() {
382 for i in 0..5 { 447 for i in 0..5 {
383 if true {<|> 448 if true {<|>
384 foo(); 449 foo();
385 450
386 //comment 451 //comment
387 bar(); 452 bar();
388 } else { 453 } else {
389 println!("bar"); 454 println!("bar");
390 } 455 }
391 } 456 }
392 } 457}
393 "#, 458"#,
394 r#" 459 r#"
395 fn main() { 460fn main() {
396 for i in 0..5 { 461 for i in 0..5 {
397 foo(); 462 foo();
398 463
399 //comment 464 //comment
400 bar(); 465 bar();
401 } 466 }
402 } 467}
403 "#, 468"#,
404 ); 469 );
405 } 470 }
406 471
@@ -409,31 +474,31 @@ mod tests {
409 check_assist( 474 check_assist(
410 unwrap_block, 475 unwrap_block,
411 r#" 476 r#"
412 fn main() { 477fn main() {
413 loop {<|> 478 loop {<|>
414 if true { 479 if true {
415 foo(); 480 foo();
416 481
417 //comment 482 //comment
418 bar(); 483 bar();
419 } else { 484 } else {
420 println!("bar"); 485 println!("bar");
421 } 486 }
422 } 487 }
423 } 488}
424 "#, 489"#,
425 r#" 490 r#"
426 fn main() { 491fn main() {
427 if true { 492 if true {
428 foo(); 493 foo();
429 494
430 //comment 495 //comment
431 bar(); 496 bar();
432 } else { 497 } else {
433 println!("bar"); 498 println!("bar");
434 } 499 }
435 } 500}
436 "#, 501"#,
437 ); 502 );
438 } 503 }
439 504
@@ -442,31 +507,31 @@ mod tests {
442 check_assist( 507 check_assist(
443 unwrap_block, 508 unwrap_block,
444 r#" 509 r#"
445 fn main() { 510fn main() {
446 while true {<|> 511 while true {<|>
447 if true { 512 if true {
448 foo(); 513 foo();
449 514
450 //comment 515 //comment
451 bar(); 516 bar();
452 } else { 517 } else {
453 println!("bar"); 518 println!("bar");
454 } 519 }
455 } 520 }
456 } 521}
457 "#, 522"#,
458 r#" 523 r#"
459 fn main() { 524fn main() {
460 if true { 525 if true {
461 foo(); 526 foo();
462 527
463 //comment 528 //comment
464 bar(); 529 bar();
465 } else { 530 } else {
466 println!("bar"); 531 println!("bar");
467 } 532 }
468 } 533}
469 "#, 534"#,
470 ); 535 );
471 } 536 }
472 537
@@ -499,19 +564,19 @@ fn main() {
499 check_assist_not_applicable( 564 check_assist_not_applicable(
500 unwrap_block, 565 unwrap_block,
501 r#" 566 r#"
502 fn main() { 567fn main() {
503 while true { 568 while true {
504 if true { 569 if true {
505 foo();<|> 570 foo();<|>
506 571
507 //comment 572 //comment
508 bar(); 573 bar();
509 } else { 574 } else {
510 println!("bar"); 575 println!("bar");
511 } 576 }
512 } 577 }
513 } 578}
514 "#, 579"#,
515 ); 580 );
516 } 581 }
517} 582}
diff --git a/crates/assists/src/handlers/wrap_return_type_in_result.rs b/crates/assists/src/handlers/wrap_return_type_in_result.rs
new file mode 100644
index 000000000..59e5debb1
--- /dev/null
+++ b/crates/assists/src/handlers/wrap_return_type_in_result.rs
@@ -0,0 +1,1158 @@
1use std::iter;
2
3use syntax::{
4 ast::{self, make, BlockExpr, Expr, LoopBodyOwner},
5 match_ast, AstNode, SyntaxNode,
6};
7use test_utils::mark;
8
9use crate::{AssistContext, AssistId, AssistKind, Assists};
10
11// Assist: wrap_return_type_in_result
12//
13// Wrap the function's return type into Result.
14//
15// ```
16// fn foo() -> i32<|> { 42i32 }
17// ```
18// ->
19// ```
20// fn foo() -> Result<i32, ${0:_}> { Ok(42i32) }
21// ```
22pub(crate) fn wrap_return_type_in_result(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
23 let ret_type = ctx.find_node_at_offset::<ast::RetType>()?;
24 let parent = ret_type.syntax().parent()?;
25 let block_expr = match_ast! {
26 match parent {
27 ast::Fn(func) => func.body()?,
28 ast::ClosureExpr(closure) => match closure.body()? {
29 Expr::BlockExpr(block) => block,
30 // closures require a block when a return type is specified
31 _ => return None,
32 },
33 _ => return None,
34 }
35 };
36
37 let type_ref = &ret_type.ty()?;
38 let ret_type_str = type_ref.syntax().text().to_string();
39 let first_part_ret_type = ret_type_str.splitn(2, '<').next();
40 if let Some(ret_type_first_part) = first_part_ret_type {
41 if ret_type_first_part.ends_with("Result") {
42 mark::hit!(wrap_return_type_in_result_simple_return_type_already_result);
43 return None;
44 }
45 }
46
47 acc.add(
48 AssistId("wrap_return_type_in_result", AssistKind::RefactorRewrite),
49 "Wrap return type in Result",
50 type_ref.syntax().text_range(),
51 |builder| {
52 let mut tail_return_expr_collector = TailReturnCollector::new();
53 tail_return_expr_collector.collect_jump_exprs(&block_expr, false);
54 tail_return_expr_collector.collect_tail_exprs(&block_expr);
55
56 for ret_expr_arg in tail_return_expr_collector.exprs_to_wrap {
57 let ok_wrapped = make::expr_call(
58 make::expr_path(make::path_unqualified(make::path_segment(make::name_ref(
59 "Ok",
60 )))),
61 make::arg_list(iter::once(ret_expr_arg.clone())),
62 );
63 builder.replace_ast(ret_expr_arg, ok_wrapped);
64 }
65
66 match ctx.config.snippet_cap {
67 Some(cap) => {
68 let snippet = format!("Result<{}, ${{0:_}}>", type_ref);
69 builder.replace_snippet(cap, type_ref.syntax().text_range(), snippet)
70 }
71 None => builder
72 .replace(type_ref.syntax().text_range(), format!("Result<{}, _>", type_ref)),
73 }
74 },
75 )
76}
77
78struct TailReturnCollector {
79 exprs_to_wrap: Vec<ast::Expr>,
80}
81
82impl TailReturnCollector {
83 fn new() -> Self {
84 Self { exprs_to_wrap: vec![] }
85 }
86 /// Collect all`return` expression
87 fn collect_jump_exprs(&mut self, block_expr: &BlockExpr, collect_break: bool) {
88 let statements = block_expr.statements();
89 for stmt in statements {
90 let expr = match &stmt {
91 ast::Stmt::ExprStmt(stmt) => stmt.expr(),
92 ast::Stmt::LetStmt(stmt) => stmt.initializer(),
93 ast::Stmt::Item(_) => continue,
94 };
95 if let Some(expr) = &expr {
96 self.handle_exprs(expr, collect_break);
97 }
98 }
99
100 // Browse tail expressions for each block
101 if let Some(expr) = block_expr.expr() {
102 if let Some(last_exprs) = get_tail_expr_from_block(&expr) {
103 for last_expr in last_exprs {
104 let last_expr = match last_expr {
105 NodeType::Node(expr) => expr,
106 NodeType::Leaf(expr) => expr.syntax().clone(),
107 };
108
109 if let Some(last_expr) = Expr::cast(last_expr.clone()) {
110 self.handle_exprs(&last_expr, collect_break);
111 } else if let Some(expr_stmt) = ast::Stmt::cast(last_expr) {
112 let expr_stmt = match &expr_stmt {
113 ast::Stmt::ExprStmt(stmt) => stmt.expr(),
114 ast::Stmt::LetStmt(stmt) => stmt.initializer(),
115 ast::Stmt::Item(_) => None,
116 };
117 if let Some(expr) = &expr_stmt {
118 self.handle_exprs(expr, collect_break);
119 }
120 }
121 }
122 }
123 }
124 }
125
126 fn handle_exprs(&mut self, expr: &Expr, collect_break: bool) {
127 match expr {
128 Expr::BlockExpr(block_expr) => {
129 self.collect_jump_exprs(&block_expr, collect_break);
130 }
131 Expr::ReturnExpr(ret_expr) => {
132 if let Some(ret_expr_arg) = &ret_expr.expr() {
133 self.exprs_to_wrap.push(ret_expr_arg.clone());
134 }
135 }
136 Expr::BreakExpr(break_expr) if collect_break => {
137 if let Some(break_expr_arg) = &break_expr.expr() {
138 self.exprs_to_wrap.push(break_expr_arg.clone());
139 }
140 }
141 Expr::IfExpr(if_expr) => {
142 for block in if_expr.blocks() {
143 self.collect_jump_exprs(&block, collect_break);
144 }
145 }
146 Expr::LoopExpr(loop_expr) => {
147 if let Some(block_expr) = loop_expr.loop_body() {
148 self.collect_jump_exprs(&block_expr, collect_break);
149 }
150 }
151 Expr::ForExpr(for_expr) => {
152 if let Some(block_expr) = for_expr.loop_body() {
153 self.collect_jump_exprs(&block_expr, collect_break);
154 }
155 }
156 Expr::WhileExpr(while_expr) => {
157 if let Some(block_expr) = while_expr.loop_body() {
158 self.collect_jump_exprs(&block_expr, collect_break);
159 }
160 }
161 Expr::MatchExpr(match_expr) => {
162 if let Some(arm_list) = match_expr.match_arm_list() {
163 arm_list.arms().filter_map(|match_arm| match_arm.expr()).for_each(|expr| {
164 self.handle_exprs(&expr, collect_break);
165 });
166 }
167 }
168 _ => {}
169 }
170 }
171
172 fn collect_tail_exprs(&mut self, block: &BlockExpr) {
173 if let Some(expr) = block.expr() {
174 self.handle_exprs(&expr, true);
175 self.fetch_tail_exprs(&expr);
176 }
177 }
178
179 fn fetch_tail_exprs(&mut self, expr: &Expr) {
180 if let Some(exprs) = get_tail_expr_from_block(expr) {
181 for node_type in &exprs {
182 match node_type {
183 NodeType::Leaf(expr) => {
184 self.exprs_to_wrap.push(expr.clone());
185 }
186 NodeType::Node(expr) => {
187 if let Some(last_expr) = Expr::cast(expr.clone()) {
188 self.fetch_tail_exprs(&last_expr);
189 }
190 }
191 }
192 }
193 }
194 }
195}
196
197#[derive(Debug)]
198enum NodeType {
199 Leaf(ast::Expr),
200 Node(SyntaxNode),
201}
202
203/// Get a tail expression inside a block
204fn get_tail_expr_from_block(expr: &Expr) -> Option<Vec<NodeType>> {
205 match expr {
206 Expr::IfExpr(if_expr) => {
207 let mut nodes = vec![];
208 for block in if_expr.blocks() {
209 if let Some(block_expr) = block.expr() {
210 if let Some(tail_exprs) = get_tail_expr_from_block(&block_expr) {
211 nodes.extend(tail_exprs);
212 }
213 } else if let Some(last_expr) = block.syntax().last_child() {
214 nodes.push(NodeType::Node(last_expr));
215 } else {
216 nodes.push(NodeType::Node(block.syntax().clone()));
217 }
218 }
219 Some(nodes)
220 }
221 Expr::LoopExpr(loop_expr) => {
222 loop_expr.syntax().last_child().map(|lc| vec![NodeType::Node(lc)])
223 }
224 Expr::ForExpr(for_expr) => {
225 for_expr.syntax().last_child().map(|lc| vec![NodeType::Node(lc)])
226 }
227 Expr::WhileExpr(while_expr) => {
228 while_expr.syntax().last_child().map(|lc| vec![NodeType::Node(lc)])
229 }
230 Expr::BlockExpr(block_expr) => {
231 block_expr.expr().map(|lc| vec![NodeType::Node(lc.syntax().clone())])
232 }
233 Expr::MatchExpr(match_expr) => {
234 let arm_list = match_expr.match_arm_list()?;
235 let arms: Vec<NodeType> = arm_list
236 .arms()
237 .filter_map(|match_arm| match_arm.expr())
238 .map(|expr| match expr {
239 Expr::ReturnExpr(ret_expr) => NodeType::Node(ret_expr.syntax().clone()),
240 Expr::BreakExpr(break_expr) => NodeType::Node(break_expr.syntax().clone()),
241 _ => match expr.syntax().last_child() {
242 Some(last_expr) => NodeType::Node(last_expr),
243 None => NodeType::Node(expr.syntax().clone()),
244 },
245 })
246 .collect();
247
248 Some(arms)
249 }
250 Expr::BreakExpr(expr) => expr.expr().map(|e| vec![NodeType::Leaf(e)]),
251 Expr::ReturnExpr(ret_expr) => Some(vec![NodeType::Node(ret_expr.syntax().clone())]),
252
253 Expr::CallExpr(_)
254 | Expr::Literal(_)
255 | Expr::TupleExpr(_)
256 | Expr::ArrayExpr(_)
257 | Expr::ParenExpr(_)
258 | Expr::PathExpr(_)
259 | Expr::RecordExpr(_)
260 | Expr::IndexExpr(_)
261 | Expr::MethodCallExpr(_)
262 | Expr::AwaitExpr(_)
263 | Expr::CastExpr(_)
264 | Expr::RefExpr(_)
265 | Expr::PrefixExpr(_)
266 | Expr::RangeExpr(_)
267 | Expr::BinExpr(_)
268 | Expr::MacroCall(_)
269 | Expr::BoxExpr(_) => Some(vec![NodeType::Leaf(expr.clone())]),
270 _ => None,
271 }
272}
273
274#[cfg(test)]
275mod tests {
276 use crate::tests::{check_assist, check_assist_not_applicable};
277
278 use super::*;
279
280 #[test]
281 fn wrap_return_type_in_result_simple() {
282 check_assist(
283 wrap_return_type_in_result,
284 r#"
285fn foo() -> i3<|>2 {
286 let test = "test";
287 return 42i32;
288}
289"#,
290 r#"
291fn foo() -> Result<i32, ${0:_}> {
292 let test = "test";
293 return Ok(42i32);
294}
295"#,
296 );
297 }
298
299 #[test]
300 fn wrap_return_type_in_result_simple_closure() {
301 check_assist(
302 wrap_return_type_in_result,
303 r#"
304fn foo() {
305 || -> i32<|> {
306 let test = "test";
307 return 42i32;
308 };
309}
310"#,
311 r#"
312fn foo() {
313 || -> Result<i32, ${0:_}> {
314 let test = "test";
315 return Ok(42i32);
316 };
317}
318"#,
319 );
320 }
321
322 #[test]
323 fn wrap_return_type_in_result_simple_return_type_bad_cursor() {
324 check_assist_not_applicable(
325 wrap_return_type_in_result,
326 r#"
327fn foo() -> i32 {
328 let test = "test";<|>
329 return 42i32;
330}
331"#,
332 );
333 }
334
335 #[test]
336 fn wrap_return_type_in_result_simple_return_type_bad_cursor_closure() {
337 check_assist_not_applicable(
338 wrap_return_type_in_result,
339 r#"
340fn foo() {
341 || -> i32 {
342 let test = "test";<|>
343 return 42i32;
344 };
345}
346"#,
347 );
348 }
349
350 #[test]
351 fn wrap_return_type_in_result_closure_non_block() {
352 check_assist_not_applicable(wrap_return_type_in_result, r#"fn foo() { || -> i<|>32 3; }"#);
353 }
354
355 #[test]
356 fn wrap_return_type_in_result_simple_return_type_already_result_std() {
357 check_assist_not_applicable(
358 wrap_return_type_in_result,
359 r#"
360fn foo() -> std::result::Result<i32<|>, String> {
361 let test = "test";
362 return 42i32;
363}
364"#,
365 );
366 }
367
368 #[test]
369 fn wrap_return_type_in_result_simple_return_type_already_result() {
370 mark::check!(wrap_return_type_in_result_simple_return_type_already_result);
371 check_assist_not_applicable(
372 wrap_return_type_in_result,
373 r#"
374fn foo() -> Result<i32<|>, String> {
375 let test = "test";
376 return 42i32;
377}
378"#,
379 );
380 }
381
382 #[test]
383 fn wrap_return_type_in_result_simple_return_type_already_result_closure() {
384 check_assist_not_applicable(
385 wrap_return_type_in_result,
386 r#"
387fn foo() {
388 || -> Result<i32<|>, String> {
389 let test = "test";
390 return 42i32;
391 };
392}
393"#,
394 );
395 }
396
397 #[test]
398 fn wrap_return_type_in_result_simple_with_cursor() {
399 check_assist(
400 wrap_return_type_in_result,
401 r#"
402fn foo() -> <|>i32 {
403 let test = "test";
404 return 42i32;
405}
406"#,
407 r#"
408fn foo() -> Result<i32, ${0:_}> {
409 let test = "test";
410 return Ok(42i32);
411}
412"#,
413 );
414 }
415
416 #[test]
417 fn wrap_return_type_in_result_simple_with_tail() {
418 check_assist(
419 wrap_return_type_in_result,
420 r#"
421fn foo() -><|> i32 {
422 let test = "test";
423 42i32
424}
425"#,
426 r#"
427fn foo() -> Result<i32, ${0:_}> {
428 let test = "test";
429 Ok(42i32)
430}
431"#,
432 );
433 }
434
435 #[test]
436 fn wrap_return_type_in_result_simple_with_tail_closure() {
437 check_assist(
438 wrap_return_type_in_result,
439 r#"
440fn foo() {
441 || -><|> i32 {
442 let test = "test";
443 42i32
444 };
445}
446"#,
447 r#"
448fn foo() {
449 || -> Result<i32, ${0:_}> {
450 let test = "test";
451 Ok(42i32)
452 };
453}
454"#,
455 );
456 }
457
458 #[test]
459 fn wrap_return_type_in_result_simple_with_tail_only() {
460 check_assist(
461 wrap_return_type_in_result,
462 r#"fn foo() -> i32<|> { 42i32 }"#,
463 r#"fn foo() -> Result<i32, ${0:_}> { Ok(42i32) }"#,
464 );
465 }
466
467 #[test]
468 fn wrap_return_type_in_result_simple_with_tail_block_like() {
469 check_assist(
470 wrap_return_type_in_result,
471 r#"
472fn foo() -> i32<|> {
473 if true {
474 42i32
475 } else {
476 24i32
477 }
478}
479"#,
480 r#"
481fn foo() -> Result<i32, ${0:_}> {
482 if true {
483 Ok(42i32)
484 } else {
485 Ok(24i32)
486 }
487}
488"#,
489 );
490 }
491
492 #[test]
493 fn wrap_return_type_in_result_simple_without_block_closure() {
494 check_assist(
495 wrap_return_type_in_result,
496 r#"
497fn foo() {
498 || -> i32<|> {
499 if true {
500 42i32
501 } else {
502 24i32
503 }
504 };
505}
506"#,
507 r#"
508fn foo() {
509 || -> Result<i32, ${0:_}> {
510 if true {
511 Ok(42i32)
512 } else {
513 Ok(24i32)
514 }
515 };
516}
517"#,
518 );
519 }
520
521 #[test]
522 fn wrap_return_type_in_result_simple_with_nested_if() {
523 check_assist(
524 wrap_return_type_in_result,
525 r#"
526fn foo() -> i32<|> {
527 if true {
528 if false {
529 1
530 } else {
531 2
532 }
533 } else {
534 24i32
535 }
536}
537"#,
538 r#"
539fn foo() -> Result<i32, ${0:_}> {
540 if true {
541 if false {
542 Ok(1)
543 } else {
544 Ok(2)
545 }
546 } else {
547 Ok(24i32)
548 }
549}
550"#,
551 );
552 }
553
554 #[test]
555 fn wrap_return_type_in_result_simple_with_await() {
556 check_assist(
557 wrap_return_type_in_result,
558 r#"
559async fn foo() -> i<|>32 {
560 if true {
561 if false {
562 1.await
563 } else {
564 2.await
565 }
566 } else {
567 24i32.await
568 }
569}
570"#,
571 r#"
572async fn foo() -> Result<i32, ${0:_}> {
573 if true {
574 if false {
575 Ok(1.await)
576 } else {
577 Ok(2.await)
578 }
579 } else {
580 Ok(24i32.await)
581 }
582}
583"#,
584 );
585 }
586
587 #[test]
588 fn wrap_return_type_in_result_simple_with_array() {
589 check_assist(
590 wrap_return_type_in_result,
591 r#"fn foo() -> [i32;<|> 3] { [1, 2, 3] }"#,
592 r#"fn foo() -> Result<[i32; 3], ${0:_}> { Ok([1, 2, 3]) }"#,
593 );
594 }
595
596 #[test]
597 fn wrap_return_type_in_result_simple_with_cast() {
598 check_assist(
599 wrap_return_type_in_result,
600 r#"
601fn foo() -<|>> i32 {
602 if true {
603 if false {
604 1 as i32
605 } else {
606 2 as i32
607 }
608 } else {
609 24 as i32
610 }
611}
612"#,
613 r#"
614fn foo() -> Result<i32, ${0:_}> {
615 if true {
616 if false {
617 Ok(1 as i32)
618 } else {
619 Ok(2 as i32)
620 }
621 } else {
622 Ok(24 as i32)
623 }
624}
625"#,
626 );
627 }
628
629 #[test]
630 fn wrap_return_type_in_result_simple_with_tail_block_like_match() {
631 check_assist(
632 wrap_return_type_in_result,
633 r#"
634fn foo() -> i32<|> {
635 let my_var = 5;
636 match my_var {
637 5 => 42i32,
638 _ => 24i32,
639 }
640}
641"#,
642 r#"
643fn foo() -> Result<i32, ${0:_}> {
644 let my_var = 5;
645 match my_var {
646 5 => Ok(42i32),
647 _ => Ok(24i32),
648 }
649}
650"#,
651 );
652 }
653
654 #[test]
655 fn wrap_return_type_in_result_simple_with_loop_with_tail() {
656 check_assist(
657 wrap_return_type_in_result,
658 r#"
659fn foo() -> i32<|> {
660 let my_var = 5;
661 loop {
662 println!("test");
663 5
664 }
665 my_var
666}
667"#,
668 r#"
669fn foo() -> Result<i32, ${0:_}> {
670 let my_var = 5;
671 loop {
672 println!("test");
673 5
674 }
675 Ok(my_var)
676}
677"#,
678 );
679 }
680
681 #[test]
682 fn wrap_return_type_in_result_simple_with_loop_in_let_stmt() {
683 check_assist(
684 wrap_return_type_in_result,
685 r#"
686fn foo() -> i32<|> {
687 let my_var = let x = loop {
688 break 1;
689 };
690 my_var
691}
692"#,
693 r#"
694fn foo() -> Result<i32, ${0:_}> {
695 let my_var = let x = loop {
696 break 1;
697 };
698 Ok(my_var)
699}
700"#,
701 );
702 }
703
704 #[test]
705 fn wrap_return_type_in_result_simple_with_tail_block_like_match_return_expr() {
706 check_assist(
707 wrap_return_type_in_result,
708 r#"
709fn foo() -> i32<|> {
710 let my_var = 5;
711 let res = match my_var {
712 5 => 42i32,
713 _ => return 24i32,
714 };
715 res
716}
717"#,
718 r#"
719fn foo() -> Result<i32, ${0:_}> {
720 let my_var = 5;
721 let res = match my_var {
722 5 => 42i32,
723 _ => return Ok(24i32),
724 };
725 Ok(res)
726}
727"#,
728 );
729
730 check_assist(
731 wrap_return_type_in_result,
732 r#"
733fn foo() -> i32<|> {
734 let my_var = 5;
735 let res = if my_var == 5 {
736 42i32
737 } else {
738 return 24i32;
739 };
740 res
741}
742"#,
743 r#"
744fn foo() -> Result<i32, ${0:_}> {
745 let my_var = 5;
746 let res = if my_var == 5 {
747 42i32
748 } else {
749 return Ok(24i32);
750 };
751 Ok(res)
752}
753"#,
754 );
755 }
756
757 #[test]
758 fn wrap_return_type_in_result_simple_with_tail_block_like_match_deeper() {
759 check_assist(
760 wrap_return_type_in_result,
761 r#"
762fn foo() -> i32<|> {
763 let my_var = 5;
764 match my_var {
765 5 => {
766 if true {
767 42i32
768 } else {
769 25i32
770 }
771 },
772 _ => {
773 let test = "test";
774 if test == "test" {
775 return bar();
776 }
777 53i32
778 },
779 }
780}
781"#,
782 r#"
783fn foo() -> Result<i32, ${0:_}> {
784 let my_var = 5;
785 match my_var {
786 5 => {
787 if true {
788 Ok(42i32)
789 } else {
790 Ok(25i32)
791 }
792 },
793 _ => {
794 let test = "test";
795 if test == "test" {
796 return Ok(bar());
797 }
798 Ok(53i32)
799 },
800 }
801}
802"#,
803 );
804 }
805
806 #[test]
807 fn wrap_return_type_in_result_simple_with_tail_block_like_early_return() {
808 check_assist(
809 wrap_return_type_in_result,
810 r#"
811fn foo() -> i<|>32 {
812 let test = "test";
813 if test == "test" {
814 return 24i32;
815 }
816 53i32
817}
818"#,
819 r#"
820fn foo() -> Result<i32, ${0:_}> {
821 let test = "test";
822 if test == "test" {
823 return Ok(24i32);
824 }
825 Ok(53i32)
826}
827"#,
828 );
829 }
830
831 #[test]
832 fn wrap_return_type_in_result_simple_with_closure() {
833 check_assist(
834 wrap_return_type_in_result,
835 r#"
836fn foo(the_field: u32) -><|> u32 {
837 let true_closure = || { return true; };
838 if the_field < 5 {
839 let mut i = 0;
840 if true_closure() {
841 return 99;
842 } else {
843 return 0;
844 }
845 }
846 the_field
847}
848"#,
849 r#"
850fn foo(the_field: u32) -> Result<u32, ${0:_}> {
851 let true_closure = || { return true; };
852 if the_field < 5 {
853 let mut i = 0;
854 if true_closure() {
855 return Ok(99);
856 } else {
857 return Ok(0);
858 }
859 }
860 Ok(the_field)
861}
862"#,
863 );
864
865 check_assist(
866 wrap_return_type_in_result,
867 r#"
868 fn foo(the_field: u32) -> u32<|> {
869 let true_closure = || {
870 return true;
871 };
872 if the_field < 5 {
873 let mut i = 0;
874
875
876 if true_closure() {
877 return 99;
878 } else {
879 return 0;
880 }
881 }
882 let t = None;
883
884 t.unwrap_or_else(|| the_field)
885 }
886 "#,
887 r#"
888 fn foo(the_field: u32) -> Result<u32, ${0:_}> {
889 let true_closure = || {
890 return true;
891 };
892 if the_field < 5 {
893 let mut i = 0;
894
895
896 if true_closure() {
897 return Ok(99);
898 } else {
899 return Ok(0);
900 }
901 }
902 let t = None;
903
904 Ok(t.unwrap_or_else(|| the_field))
905 }
906 "#,
907 );
908 }
909
910 #[test]
911 fn wrap_return_type_in_result_simple_with_weird_forms() {
912 check_assist(
913 wrap_return_type_in_result,
914 r#"
915fn foo() -> i32<|> {
916 let test = "test";
917 if test == "test" {
918 return 24i32;
919 }
920 let mut i = 0;
921 loop {
922 if i == 1 {
923 break 55;
924 }
925 i += 1;
926 }
927}
928"#,
929 r#"
930fn foo() -> Result<i32, ${0:_}> {
931 let test = "test";
932 if test == "test" {
933 return Ok(24i32);
934 }
935 let mut i = 0;
936 loop {
937 if i == 1 {
938 break Ok(55);
939 }
940 i += 1;
941 }
942}
943"#,
944 );
945
946 check_assist(
947 wrap_return_type_in_result,
948 r#"
949fn foo() -> i32<|> {
950 let test = "test";
951 if test == "test" {
952 return 24i32;
953 }
954 let mut i = 0;
955 loop {
956 loop {
957 if i == 1 {
958 break 55;
959 }
960 i += 1;
961 }
962 }
963}
964"#,
965 r#"
966fn foo() -> Result<i32, ${0:_}> {
967 let test = "test";
968 if test == "test" {
969 return Ok(24i32);
970 }
971 let mut i = 0;
972 loop {
973 loop {
974 if i == 1 {
975 break Ok(55);
976 }
977 i += 1;
978 }
979 }
980}
981"#,
982 );
983
984 check_assist(
985 wrap_return_type_in_result,
986 r#"
987fn foo() -> i3<|>2 {
988 let test = "test";
989 let other = 5;
990 if test == "test" {
991 let res = match other {
992 5 => 43,
993 _ => return 56,
994 };
995 }
996 let mut i = 0;
997 loop {
998 loop {
999 if i == 1 {
1000 break 55;
1001 }
1002 i += 1;
1003 }
1004 }
1005}
1006"#,
1007 r#"
1008fn foo() -> Result<i32, ${0:_}> {
1009 let test = "test";
1010 let other = 5;
1011 if test == "test" {
1012 let res = match other {
1013 5 => 43,
1014 _ => return Ok(56),
1015 };
1016 }
1017 let mut i = 0;
1018 loop {
1019 loop {
1020 if i == 1 {
1021 break Ok(55);
1022 }
1023 i += 1;
1024 }
1025 }
1026}
1027"#,
1028 );
1029
1030 check_assist(
1031 wrap_return_type_in_result,
1032 r#"
1033fn foo(the_field: u32) -> u32<|> {
1034 if the_field < 5 {
1035 let mut i = 0;
1036 loop {
1037 if i > 5 {
1038 return 55u32;
1039 }
1040 i += 3;
1041 }
1042 match i {
1043 5 => return 99,
1044 _ => return 0,
1045 };
1046 }
1047 the_field
1048}
1049"#,
1050 r#"
1051fn foo(the_field: u32) -> Result<u32, ${0:_}> {
1052 if the_field < 5 {
1053 let mut i = 0;
1054 loop {
1055 if i > 5 {
1056 return Ok(55u32);
1057 }
1058 i += 3;
1059 }
1060 match i {
1061 5 => return Ok(99),
1062 _ => return Ok(0),
1063 };
1064 }
1065 Ok(the_field)
1066}
1067"#,
1068 );
1069
1070 check_assist(
1071 wrap_return_type_in_result,
1072 r#"
1073fn foo(the_field: u32) -> u3<|>2 {
1074 if the_field < 5 {
1075 let mut i = 0;
1076 match i {
1077 5 => return 99,
1078 _ => return 0,
1079 }
1080 }
1081 the_field
1082}
1083"#,
1084 r#"
1085fn foo(the_field: u32) -> Result<u32, ${0:_}> {
1086 if the_field < 5 {
1087 let mut i = 0;
1088 match i {
1089 5 => return Ok(99),
1090 _ => return Ok(0),
1091 }
1092 }
1093 Ok(the_field)
1094}
1095"#,
1096 );
1097
1098 check_assist(
1099 wrap_return_type_in_result,
1100 r#"
1101fn foo(the_field: u32) -> u32<|> {
1102 if the_field < 5 {
1103 let mut i = 0;
1104 if i == 5 {
1105 return 99
1106 } else {
1107 return 0
1108 }
1109 }
1110 the_field
1111}
1112"#,
1113 r#"
1114fn foo(the_field: u32) -> Result<u32, ${0:_}> {
1115 if the_field < 5 {
1116 let mut i = 0;
1117 if i == 5 {
1118 return Ok(99)
1119 } else {
1120 return Ok(0)
1121 }
1122 }
1123 Ok(the_field)
1124}
1125"#,
1126 );
1127
1128 check_assist(
1129 wrap_return_type_in_result,
1130 r#"
1131fn foo(the_field: u32) -> <|>u32 {
1132 if the_field < 5 {
1133 let mut i = 0;
1134 if i == 5 {
1135 return 99;
1136 } else {
1137 return 0;
1138 }
1139 }
1140 the_field
1141}
1142"#,
1143 r#"
1144fn foo(the_field: u32) -> Result<u32, ${0:_}> {
1145 if the_field < 5 {
1146 let mut i = 0;
1147 if i == 5 {
1148 return Ok(99);
1149 } else {
1150 return Ok(0);
1151 }
1152 }
1153 Ok(the_field)
1154}
1155"#,
1156 );
1157 }
1158}