diff options
Diffstat (limited to 'crates/assists/src/handlers')
-rw-r--r-- | crates/assists/src/handlers/add_custom_impl.rs | 284 | ||||
-rw-r--r-- | crates/assists/src/handlers/add_missing_impl_members.rs | 92 | ||||
-rw-r--r-- | crates/assists/src/handlers/auto_import.rs | 3 | ||||
-rw-r--r-- | crates/assists/src/handlers/change_return_type_to_result.rs | 1091 | ||||
-rw-r--r-- | crates/assists/src/handlers/expand_glob_import.rs | 76 | ||||
-rw-r--r-- | crates/assists/src/handlers/extract_struct_from_enum_variant.rs | 180 | ||||
-rw-r--r-- | crates/assists/src/handlers/ignore_test.rs | 103 | ||||
-rw-r--r-- | crates/assists/src/handlers/remove_dbg.rs | 71 | ||||
-rw-r--r-- | crates/assists/src/handlers/remove_unused_param.rs | 83 | ||||
-rw-r--r-- | crates/assists/src/handlers/reorder_fields.rs | 4 | ||||
-rw-r--r-- | crates/assists/src/handlers/replace_derive_with_manual_impl.rs | 400 | ||||
-rw-r--r-- | crates/assists/src/handlers/replace_qualified_name_with_use.rs | 2 | ||||
-rw-r--r-- | crates/assists/src/handlers/unwrap_block.rs | 613 | ||||
-rw-r--r-- | crates/assists/src/handlers/wrap_return_type_in_result.rs | 1158 |
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 @@ | |||
1 | use ide_db::imports_locator; | ||
2 | use itertools::Itertools; | ||
3 | use syntax::{ | ||
4 | ast::{self, make, AstNode}, | ||
5 | Direction, SmolStr, | ||
6 | SyntaxKind::{IDENT, WHITESPACE}, | ||
7 | TextRange, TextSize, | ||
8 | }; | ||
9 | |||
10 | use 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 | // ``` | ||
34 | pub(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 | |||
81 | fn 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 | |||
114 | fn 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)] | ||
147 | mod 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 | " | ||
157 | mod fmt { | ||
158 | pub trait Debug {} | ||
159 | } | ||
160 | |||
161 | #[derive(Debu<|>g)] | ||
162 | struct Foo { | ||
163 | bar: String, | ||
164 | } | ||
165 | ", | ||
166 | " | ||
167 | mod fmt { | ||
168 | pub trait Debug {} | ||
169 | } | ||
170 | |||
171 | struct Foo { | ||
172 | bar: String, | ||
173 | } | ||
174 | |||
175 | impl 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)] | ||
187 | struct Foo { | ||
188 | bar: String, | ||
189 | } | ||
190 | ", | ||
191 | " | ||
192 | struct Foo { | ||
193 | bar: String, | ||
194 | } | ||
195 | |||
196 | impl 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<|>)] | ||
209 | pub struct Foo { | ||
210 | bar: String, | ||
211 | } | ||
212 | ", | ||
213 | " | ||
214 | pub struct Foo { | ||
215 | bar: String, | ||
216 | } | ||
217 | |||
218 | impl 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)] | ||
231 | struct Foo {} | ||
232 | ", | ||
233 | " | ||
234 | #[derive(Display, Serialize)] | ||
235 | struct Foo {} | ||
236 | |||
237 | impl 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(<|>)] | ||
250 | struct 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)] | ||
261 | struct Foo {} | ||
262 | ", | ||
263 | ); | ||
264 | |||
265 | check_assist_not_applicable( | ||
266 | add_custom_impl, | ||
267 | " | ||
268 | #[derive(Debug)<|>] | ||
269 | struct 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)] | ||
280 | struct 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 @@ | |||
1 | use hir::HasSource; | 1 | use ide_db::traits::resolve_target_trait; |
2 | use ide_db::traits::{get_missing_assoc_items, resolve_target_trait}; | 2 | use syntax::ast::{self, AstNode}; |
3 | use syntax::{ | ||
4 | ast::{ | ||
5 | self, | ||
6 | edit::{self, AstNodeEdit, IndentLevel}, | ||
7 | make, AstNode, NameOwner, | ||
8 | }, | ||
9 | SmolStr, | ||
10 | }; | ||
11 | 3 | ||
12 | use crate::{ | 4 | use 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)] | ||
20 | enum 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 | |||
106 | fn add_missing_impl_members_inner( | 93 | fn 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 | ||
196 | fn 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)] |
205 | mod tests { | 143 | mod 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 @@ | |||
1 | use std::iter; | ||
2 | |||
3 | use syntax::{ | ||
4 | ast::{self, make, BlockExpr, Expr, LoopBodyOwner}, | ||
5 | match_ast, AstNode, SyntaxNode, | ||
6 | }; | ||
7 | use test_utils::mark; | ||
8 | |||
9 | use 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 | // ``` | ||
22 | pub(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 | |||
78 | struct TailReturnCollector { | ||
79 | exprs_to_wrap: Vec<ast::Expr>, | ||
80 | } | ||
81 | |||
82 | impl 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)] | ||
198 | enum NodeType { | ||
199 | Leaf(ast::Expr), | ||
200 | Node(SyntaxNode), | ||
201 | } | ||
202 | |||
203 | /// Get a tail expression inside a block | ||
204 | fn 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)] | ||
275 | mod 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 | }; |
7 | use syntax::{ | 7 | use 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 | ||
13 | use crate::{ | 13 | use 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 | ||
238 | fn replace_ast( | 240 | fn 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" | ||
884 | mod foo { | ||
885 | pub struct Bar; | ||
886 | } | ||
887 | |||
888 | use foo::{*<|>}; | ||
889 | |||
890 | struct Baz { | ||
891 | bar: Bar | ||
892 | } | ||
893 | ", | ||
894 | r" | ||
895 | mod foo { | ||
896 | pub struct Bar; | ||
897 | } | ||
898 | |||
899 | use foo::Bar; | ||
900 | |||
901 | struct 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}; | |||
5 | use ide_db::{defs::Definition, search::Reference, RootDatabase}; | 5 | use ide_db::{defs::Definition, search::Reference, RootDatabase}; |
6 | use rustc_hash::{FxHashMap, FxHashSet}; | 6 | use rustc_hash::{FxHashMap, FxHashSet}; |
7 | use syntax::{ | 7 | use 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 | ||
14 | use crate::{ | 13 | use crate::{ |
@@ -130,18 +129,21 @@ fn existing_definition(db: &RootDatabase, variant_name: &ast::Name, variant: &En | |||
130 | fn insert_import( | 129 | fn 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 | ||
322 | mod my_mod { | 328 | mod 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#" | ||
359 | enum E { | ||
360 | <|>V { i: i32, j: i32 } | ||
361 | } | ||
362 | |||
363 | fn f() { | ||
364 | let e = E::V { i: 9, j: 2 }; | ||
365 | } | ||
366 | "#, | ||
367 | r#" | ||
368 | struct V{ pub i: i32, pub j: i32 } | ||
369 | |||
370 | enum E { | ||
371 | V(V) | ||
372 | } | ||
373 | |||
374 | fn 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 | ||
387 | enum E { | ||
388 | <|>V(i32, i32) | ||
389 | } | ||
390 | mod foo; | ||
391 | |||
392 | //- /foo.rs | ||
393 | use crate::E; | ||
394 | fn f() { | ||
395 | let e = E::V(9, 2); | ||
396 | } | ||
397 | "#, | ||
398 | r#" | ||
399 | //- /main.rs | ||
400 | struct V(pub i32, pub i32); | ||
401 | |||
402 | enum E { | ||
403 | V(V) | ||
404 | } | ||
405 | mod foo; | ||
406 | |||
407 | //- /foo.rs | ||
408 | use crate::{E, V}; | ||
409 | fn 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 | ||
422 | enum E { | ||
423 | <|>V { i: i32, j: i32 } | ||
424 | } | ||
425 | mod foo; | ||
426 | |||
427 | //- /foo.rs | ||
428 | use crate::E; | ||
429 | fn f() { | ||
430 | let e = E::V { i: 9, j: 2 }; | ||
431 | } | ||
432 | "#, | ||
433 | r#" | ||
434 | //- /main.rs | ||
435 | struct V{ pub i: i32, pub j: i32 } | ||
436 | |||
437 | enum E { | ||
438 | V(V) | ||
439 | } | ||
440 | mod foo; | ||
441 | |||
442 | //- /foo.rs | ||
443 | use crate::{E, V}; | ||
444 | fn 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#" | ||
456 | enum A { <|>One { a: u32, b: u32 } } | ||
457 | |||
458 | struct B(A); | ||
459 | |||
460 | fn foo() { | ||
461 | let _ = B(A::One { a: 1, b: 2 }); | ||
462 | } | ||
463 | "#, | ||
464 | r#" | ||
465 | struct One{ pub a: u32, pub b: u32 } | ||
466 | |||
467 | enum A { One(One) } | ||
468 | |||
469 | struct B(A); | ||
470 | |||
471 | fn 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 @@ | |||
1 | use syntax::{ | ||
2 | ast::{self, AttrsOwner}, | ||
3 | AstNode, AstToken, | ||
4 | }; | ||
5 | |||
6 | use 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 | // ``` | ||
26 | pub(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 | |||
57 | fn 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)] | ||
68 | mod 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 @@ | |||
1 | use syntax::{ | 1 | use 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 | ||
6 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | 6 | use 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 | |||
63 | fn 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#" | ||
126 | mod bar { pub fn foo(x: i32, <|>y: i32) { x; } } | ||
127 | fn b() { bar::foo(9, 2) } | ||
128 | "#, | ||
129 | r#" | ||
130 | mod bar { pub fn foo(x: i32) { x; } } | ||
131 | fn 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#" | ||
141 | pub fn foo<T>(x: T, <|>y: i32) { x; } | ||
142 | fn b() { foo::<i32>(9, 2) } | ||
143 | "#, | ||
144 | r#" | ||
145 | pub fn foo<T>(x: T) { x; } | ||
146 | fn 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#" | ||
156 | pub fn foo<T>(x: i32, <|>y: T) { x; } | ||
157 | fn b() { foo::<i32>(9, 2) } | ||
158 | fn b2() { foo(9, 2) } | ||
159 | "#, | ||
160 | r#" | ||
161 | pub fn foo<T>(x: i32) { x; } | ||
162 | fn b() { foo::<i32>(9) } | ||
163 | fn 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 | ||
186 | fn foo(x: i32, <|>y: i32) { x; } | ||
187 | |||
188 | mod foo; | ||
189 | |||
190 | //- /foo.rs | ||
191 | use super::foo; | ||
192 | |||
193 | fn bar() { | ||
194 | let _ = foo(1, 2); | ||
195 | } | ||
196 | "#, | ||
197 | r#" | ||
198 | //- /main.rs | ||
199 | fn foo(x: i32) { x; } | ||
200 | |||
201 | mod foo; | ||
202 | |||
203 | //- /foo.rs | ||
204 | use super::foo; | ||
205 | |||
206 | fn 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 @@ | |||
1 | use ide_db::imports_locator; | ||
2 | use itertools::Itertools; | ||
3 | use syntax::{ | ||
4 | ast::{self, make, AstNode}, | ||
5 | Direction, SmolStr, | ||
6 | SyntaxKind::{IDENT, WHITESPACE}, | ||
7 | TextSize, | ||
8 | }; | ||
9 | |||
10 | use 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 | // ``` | ||
40 | pub(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 | |||
91 | fn 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 | |||
147 | fn 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 | |||
168 | fn 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)] | ||
202 | mod 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 | " | ||
212 | mod 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)] | ||
222 | struct Foo { | ||
223 | bar: String, | ||
224 | } | ||
225 | ", | ||
226 | " | ||
227 | mod 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 | |||
236 | struct Foo { | ||
237 | bar: String, | ||
238 | } | ||
239 | |||
240 | impl 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 | " | ||
253 | mod 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)] | ||
264 | struct Foo { | ||
265 | bar: String, | ||
266 | } | ||
267 | ", | ||
268 | " | ||
269 | mod 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 | |||
279 | struct Foo { | ||
280 | bar: String, | ||
281 | } | ||
282 | |||
283 | impl 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)] | ||
303 | struct Foo { | ||
304 | bar: String, | ||
305 | } | ||
306 | ", | ||
307 | " | ||
308 | struct Foo { | ||
309 | bar: String, | ||
310 | } | ||
311 | |||
312 | impl 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<|>)] | ||
325 | pub struct Foo { | ||
326 | bar: String, | ||
327 | } | ||
328 | ", | ||
329 | " | ||
330 | pub struct Foo { | ||
331 | bar: String, | ||
332 | } | ||
333 | |||
334 | impl 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)] | ||
347 | struct Foo {} | ||
348 | ", | ||
349 | " | ||
350 | #[derive(Display, Serialize)] | ||
351 | struct Foo {} | ||
352 | |||
353 | impl 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(<|>)] | ||
366 | struct 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)] | ||
377 | struct Foo {} | ||
378 | ", | ||
379 | ); | ||
380 | |||
381 | check_assist_not_applicable( | ||
382 | replace_derive_with_manual_impl, | ||
383 | " | ||
384 | #[derive(Debug)<|>] | ||
385 | struct 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)] | ||
396 | struct 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 | ||
9 | use crate::{utils::unwrap_trivial_block, AssistContext, AssistId, AssistKind, Assists}; | 9 | use 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#" | ||
126 | fn main() { | ||
127 | <|>{ | ||
128 | 92 | ||
129 | } | ||
130 | } | ||
131 | "#, | ||
132 | r#" | ||
133 | fn main() { | ||
134 | 92 | ||
135 | } | ||
136 | "#, | ||
137 | ) | ||
138 | } | ||
139 | |||
140 | #[test] | ||
141 | fn unwrap_stmt_expr_block() { | ||
142 | check_assist( | ||
143 | unwrap_block, | ||
144 | r#" | ||
145 | fn main() { | ||
146 | <|>{ | ||
147 | 92; | ||
148 | } | ||
149 | () | ||
150 | } | ||
151 | "#, | ||
152 | r#" | ||
153 | fn main() { | ||
154 | 92; | ||
155 | () | ||
156 | } | ||
157 | "#, | ||
158 | ); | ||
159 | // Pedantically, we should add an `;` here... | ||
160 | check_assist( | ||
161 | unwrap_block, | ||
162 | r#" | ||
163 | fn main() { | ||
164 | <|>{ | ||
165 | 92 | ||
166 | } | ||
167 | () | ||
168 | } | ||
169 | "#, | ||
170 | r#" | ||
171 | fn 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() { | 184 | fn 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() { | 197 | fn 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() { | 213 | fn 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() { | 226 | fn 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() { | 245 | fn 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() { | 260 | fn 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() { | 279 | fn 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() { | 294 | fn 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() { | 315 | fn 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() { | 332 | fn 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() { | 355 | fn 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() { | 372 | fn 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() { | 393 | fn 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() { | 413 | fn 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() { | 427 | fn 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() { | 446 | fn 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() { | 460 | fn 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() { | 477 | fn 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() { | 491 | fn 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() { | 510 | fn 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() { | 524 | fn 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() { | 567 | fn 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 @@ | |||
1 | use std::iter; | ||
2 | |||
3 | use syntax::{ | ||
4 | ast::{self, make, BlockExpr, Expr, LoopBodyOwner}, | ||
5 | match_ast, AstNode, SyntaxNode, | ||
6 | }; | ||
7 | use test_utils::mark; | ||
8 | |||
9 | use 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 | // ``` | ||
22 | pub(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 | |||
78 | struct TailReturnCollector { | ||
79 | exprs_to_wrap: Vec<ast::Expr>, | ||
80 | } | ||
81 | |||
82 | impl 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)] | ||
198 | enum NodeType { | ||
199 | Leaf(ast::Expr), | ||
200 | Node(SyntaxNode), | ||
201 | } | ||
202 | |||
203 | /// Get a tail expression inside a block | ||
204 | fn 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)] | ||
275 | mod 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#" | ||
285 | fn foo() -> i3<|>2 { | ||
286 | let test = "test"; | ||
287 | return 42i32; | ||
288 | } | ||
289 | "#, | ||
290 | r#" | ||
291 | fn 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#" | ||
304 | fn foo() { | ||
305 | || -> i32<|> { | ||
306 | let test = "test"; | ||
307 | return 42i32; | ||
308 | }; | ||
309 | } | ||
310 | "#, | ||
311 | r#" | ||
312 | fn 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#" | ||
327 | fn 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#" | ||
340 | fn 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#" | ||
360 | fn 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#" | ||
374 | fn 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#" | ||
387 | fn 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#" | ||
402 | fn foo() -> <|>i32 { | ||
403 | let test = "test"; | ||
404 | return 42i32; | ||
405 | } | ||
406 | "#, | ||
407 | r#" | ||
408 | fn 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#" | ||
421 | fn foo() -><|> i32 { | ||
422 | let test = "test"; | ||
423 | 42i32 | ||
424 | } | ||
425 | "#, | ||
426 | r#" | ||
427 | fn 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#" | ||
440 | fn foo() { | ||
441 | || -><|> i32 { | ||
442 | let test = "test"; | ||
443 | 42i32 | ||
444 | }; | ||
445 | } | ||
446 | "#, | ||
447 | r#" | ||
448 | fn 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#" | ||
472 | fn foo() -> i32<|> { | ||
473 | if true { | ||
474 | 42i32 | ||
475 | } else { | ||
476 | 24i32 | ||
477 | } | ||
478 | } | ||
479 | "#, | ||
480 | r#" | ||
481 | fn 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#" | ||
497 | fn foo() { | ||
498 | || -> i32<|> { | ||
499 | if true { | ||
500 | 42i32 | ||
501 | } else { | ||
502 | 24i32 | ||
503 | } | ||
504 | }; | ||
505 | } | ||
506 | "#, | ||
507 | r#" | ||
508 | fn 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#" | ||
526 | fn foo() -> i32<|> { | ||
527 | if true { | ||
528 | if false { | ||
529 | 1 | ||
530 | } else { | ||
531 | 2 | ||
532 | } | ||
533 | } else { | ||
534 | 24i32 | ||
535 | } | ||
536 | } | ||
537 | "#, | ||
538 | r#" | ||
539 | fn 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#" | ||
559 | async 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#" | ||
572 | async 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#" | ||
601 | fn 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#" | ||
614 | fn 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#" | ||
634 | fn foo() -> i32<|> { | ||
635 | let my_var = 5; | ||
636 | match my_var { | ||
637 | 5 => 42i32, | ||
638 | _ => 24i32, | ||
639 | } | ||
640 | } | ||
641 | "#, | ||
642 | r#" | ||
643 | fn 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#" | ||
659 | fn foo() -> i32<|> { | ||
660 | let my_var = 5; | ||
661 | loop { | ||
662 | println!("test"); | ||
663 | 5 | ||
664 | } | ||
665 | my_var | ||
666 | } | ||
667 | "#, | ||
668 | r#" | ||
669 | fn 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#" | ||
686 | fn foo() -> i32<|> { | ||
687 | let my_var = let x = loop { | ||
688 | break 1; | ||
689 | }; | ||
690 | my_var | ||
691 | } | ||
692 | "#, | ||
693 | r#" | ||
694 | fn 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#" | ||
709 | fn 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#" | ||
719 | fn 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#" | ||
733 | fn 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#" | ||
744 | fn 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#" | ||
762 | fn 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#" | ||
783 | fn 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#" | ||
811 | fn foo() -> i<|>32 { | ||
812 | let test = "test"; | ||
813 | if test == "test" { | ||
814 | return 24i32; | ||
815 | } | ||
816 | 53i32 | ||
817 | } | ||
818 | "#, | ||
819 | r#" | ||
820 | fn 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#" | ||
836 | fn 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#" | ||
850 | fn 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#" | ||
915 | fn 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#" | ||
930 | fn 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#" | ||
949 | fn 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#" | ||
966 | fn 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#" | ||
987 | fn 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#" | ||
1008 | fn 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#" | ||
1033 | fn 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#" | ||
1051 | fn 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#" | ||
1073 | fn 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#" | ||
1085 | fn 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#" | ||
1101 | fn 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#" | ||
1114 | fn 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#" | ||
1131 | fn 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#" | ||
1144 | fn 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 | } | ||