diff options
Diffstat (limited to 'crates/ide_assists')
33 files changed, 1317 insertions, 826 deletions
diff --git a/crates/ide_assists/src/assist_context.rs b/crates/ide_assists/src/assist_context.rs index 112939948..20754a02a 100644 --- a/crates/ide_assists/src/assist_context.rs +++ b/crates/ide_assists/src/assist_context.rs | |||
@@ -13,7 +13,7 @@ use ide_db::{ | |||
13 | RootDatabase, | 13 | RootDatabase, |
14 | }; | 14 | }; |
15 | use syntax::{ | 15 | use syntax::{ |
16 | algo::{self, find_node_at_offset, find_node_at_range, SyntaxRewriter}, | 16 | algo::{self, find_node_at_offset, find_node_at_range}, |
17 | AstNode, AstToken, SourceFile, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxNodePtr, | 17 | AstNode, AstToken, SourceFile, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxNodePtr, |
18 | SyntaxToken, TextRange, TextSize, TokenAtOffset, | 18 | SyntaxToken, TextRange, TextSize, TokenAtOffset, |
19 | }; | 19 | }; |
@@ -238,8 +238,8 @@ impl AssistBuilder { | |||
238 | } | 238 | } |
239 | } | 239 | } |
240 | 240 | ||
241 | pub(crate) fn make_ast_mut<N: AstNode>(&mut self, node: N) -> N { | 241 | pub(crate) fn make_mut<N: AstNode>(&mut self, node: N) -> N { |
242 | N::cast(self.make_mut(node.syntax().clone())).unwrap() | 242 | self.mutated_tree.get_or_insert_with(|| TreeMutator::new(node.syntax())).make_mut(&node) |
243 | } | 243 | } |
244 | /// Returns a copy of the `node`, suitable for mutation. | 244 | /// Returns a copy of the `node`, suitable for mutation. |
245 | /// | 245 | /// |
@@ -251,7 +251,7 @@ impl AssistBuilder { | |||
251 | /// The typical pattern for an assist is to find specific nodes in the read | 251 | /// The typical pattern for an assist is to find specific nodes in the read |
252 | /// phase, and then get their mutable couterparts using `make_mut` in the | 252 | /// phase, and then get their mutable couterparts using `make_mut` in the |
253 | /// mutable state. | 253 | /// mutable state. |
254 | pub(crate) fn make_mut(&mut self, node: SyntaxNode) -> SyntaxNode { | 254 | pub(crate) fn make_syntax_mut(&mut self, node: SyntaxNode) -> SyntaxNode { |
255 | self.mutated_tree.get_or_insert_with(|| TreeMutator::new(&node)).make_syntax_mut(&node) | 255 | self.mutated_tree.get_or_insert_with(|| TreeMutator::new(&node)).make_syntax_mut(&node) |
256 | } | 256 | } |
257 | 257 | ||
@@ -290,12 +290,6 @@ impl AssistBuilder { | |||
290 | pub(crate) fn replace_ast<N: AstNode>(&mut self, old: N, new: N) { | 290 | pub(crate) fn replace_ast<N: AstNode>(&mut self, old: N, new: N) { |
291 | algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit) | 291 | algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit) |
292 | } | 292 | } |
293 | pub(crate) fn rewrite(&mut self, rewriter: SyntaxRewriter) { | ||
294 | if let Some(node) = rewriter.rewrite_root() { | ||
295 | let new = rewriter.rewrite(&node); | ||
296 | algo::diff(&node, &new).into_text_edit(&mut self.edit); | ||
297 | } | ||
298 | } | ||
299 | pub(crate) fn create_file(&mut self, dst: AnchoredPathBuf, content: impl Into<String>) { | 293 | pub(crate) fn create_file(&mut self, dst: AnchoredPathBuf, content: impl Into<String>) { |
300 | let file_system_edit = | 294 | let file_system_edit = |
301 | FileSystemEdit::CreateFile { dst: dst, initial_contents: content.into() }; | 295 | FileSystemEdit::CreateFile { dst: dst, initial_contents: content.into() }; |
diff --git a/crates/ide_assists/src/handlers/add_explicit_type.rs b/crates/ide_assists/src/handlers/add_explicit_type.rs index 62db31952..36589203d 100644 --- a/crates/ide_assists/src/handlers/add_explicit_type.rs +++ b/crates/ide_assists/src/handlers/add_explicit_type.rs | |||
@@ -198,6 +198,34 @@ fn main() { | |||
198 | ) | 198 | ) |
199 | } | 199 | } |
200 | 200 | ||
201 | /// https://github.com/rust-analyzer/rust-analyzer/issues/2922 | ||
202 | #[test] | ||
203 | fn regression_issue_2922() { | ||
204 | check_assist( | ||
205 | add_explicit_type, | ||
206 | r#" | ||
207 | fn main() { | ||
208 | let $0v = [0.0; 2]; | ||
209 | } | ||
210 | "#, | ||
211 | r#" | ||
212 | fn main() { | ||
213 | let v: [f64; 2] = [0.0; 2]; | ||
214 | } | ||
215 | "#, | ||
216 | ); | ||
217 | // note: this may break later if we add more consteval. it just needs to be something that our | ||
218 | // consteval engine doesn't understand | ||
219 | check_assist_not_applicable( | ||
220 | add_explicit_type, | ||
221 | r#" | ||
222 | fn main() { | ||
223 | let $0l = [0.0; 2+2]; | ||
224 | } | ||
225 | "#, | ||
226 | ); | ||
227 | } | ||
228 | |||
201 | #[test] | 229 | #[test] |
202 | fn default_generics_should_not_be_added() { | 230 | fn default_generics_should_not_be_added() { |
203 | check_assist( | 231 | check_assist( |
diff --git a/crates/ide_assists/src/handlers/add_missing_impl_members.rs b/crates/ide_assists/src/handlers/add_missing_impl_members.rs index 0148635f9..8225ae22c 100644 --- a/crates/ide_assists/src/handlers/add_missing_impl_members.rs +++ b/crates/ide_assists/src/handlers/add_missing_impl_members.rs | |||
@@ -64,7 +64,6 @@ pub(crate) fn add_missing_impl_members(acc: &mut Assists, ctx: &AssistContext) - | |||
64 | // impl Trait for () { | 64 | // impl Trait for () { |
65 | // type X = (); | 65 | // type X = (); |
66 | // fn foo(&self) {}$0 | 66 | // fn foo(&self) {}$0 |
67 | // | ||
68 | // } | 67 | // } |
69 | // ``` | 68 | // ``` |
70 | // -> | 69 | // -> |
@@ -195,6 +194,7 @@ impl Foo for S { | |||
195 | fn baz(&self) { | 194 | fn baz(&self) { |
196 | todo!() | 195 | todo!() |
197 | } | 196 | } |
197 | |||
198 | }"#, | 198 | }"#, |
199 | ); | 199 | ); |
200 | } | 200 | } |
@@ -231,6 +231,7 @@ impl Foo for S { | |||
231 | fn foo(&self) { | 231 | fn foo(&self) { |
232 | ${0:todo!()} | 232 | ${0:todo!()} |
233 | } | 233 | } |
234 | |||
234 | }"#, | 235 | }"#, |
235 | ); | 236 | ); |
236 | } | 237 | } |
diff --git a/crates/ide_assists/src/handlers/auto_import.rs b/crates/ide_assists/src/handlers/auto_import.rs index a454a2af3..dda5a6631 100644 --- a/crates/ide_assists/src/handlers/auto_import.rs +++ b/crates/ide_assists/src/handlers/auto_import.rs | |||
@@ -102,8 +102,8 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
102 | range, | 102 | range, |
103 | |builder| { | 103 | |builder| { |
104 | let scope = match scope.clone() { | 104 | let scope = match scope.clone() { |
105 | ImportScope::File(it) => ImportScope::File(builder.make_ast_mut(it)), | 105 | ImportScope::File(it) => ImportScope::File(builder.make_mut(it)), |
106 | ImportScope::Module(it) => ImportScope::Module(builder.make_ast_mut(it)), | 106 | ImportScope::Module(it) => ImportScope::Module(builder.make_mut(it)), |
107 | }; | 107 | }; |
108 | insert_use(&scope, mod_path_to_ast(&import.import_path), ctx.config.insert_use); | 108 | insert_use(&scope, mod_path_to_ast(&import.import_path), ctx.config.insert_use); |
109 | }, | 109 | }, |
@@ -969,4 +969,30 @@ mod bar { | |||
969 | "#, | 969 | "#, |
970 | ); | 970 | ); |
971 | } | 971 | } |
972 | |||
973 | #[test] | ||
974 | fn uses_abs_path_with_extern_crate_clash() { | ||
975 | check_assist( | ||
976 | auto_import, | ||
977 | r#" | ||
978 | //- /main.rs crate:main deps:foo | ||
979 | mod foo {} | ||
980 | |||
981 | const _: () = { | ||
982 | Foo$0 | ||
983 | }; | ||
984 | //- /foo.rs crate:foo | ||
985 | pub struct Foo | ||
986 | "#, | ||
987 | r#" | ||
988 | use ::foo::Foo; | ||
989 | |||
990 | mod foo {} | ||
991 | |||
992 | const _: () = { | ||
993 | Foo | ||
994 | }; | ||
995 | "#, | ||
996 | ); | ||
997 | } | ||
972 | } | 998 | } |
diff --git a/crates/ide_assists/src/handlers/convert_tuple_struct_to_named_struct.rs b/crates/ide_assists/src/handlers/convert_tuple_struct_to_named_struct.rs index b5b5ada5e..70949ca35 100644 --- a/crates/ide_assists/src/handlers/convert_tuple_struct_to_named_struct.rs +++ b/crates/ide_assists/src/handlers/convert_tuple_struct_to_named_struct.rs | |||
@@ -107,7 +107,7 @@ fn edit_struct_references( | |||
107 | names: &[ast::Name], | 107 | names: &[ast::Name], |
108 | ) { | 108 | ) { |
109 | let strukt_def = Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Struct(strukt))); | 109 | let strukt_def = Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Struct(strukt))); |
110 | let usages = strukt_def.usages(&ctx.sema).include_self_kw_refs(true).all(); | 110 | let usages = strukt_def.usages(&ctx.sema).include_self_refs().all(); |
111 | 111 | ||
112 | let edit_node = |edit: &mut AssistBuilder, node: SyntaxNode| -> Option<()> { | 112 | let edit_node = |edit: &mut AssistBuilder, node: SyntaxNode| -> Option<()> { |
113 | match_ast! { | 113 | match_ast! { |
diff --git a/crates/ide_assists/src/handlers/early_return.rs b/crates/ide_assists/src/handlers/early_return.rs index c66f8c05d..5eb6a57f0 100644 --- a/crates/ide_assists/src/handlers/early_return.rs +++ b/crates/ide_assists/src/handlers/early_return.rs | |||
@@ -130,9 +130,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext) | |||
130 | once(make::ident_pat(make::name("it")).into()), | 130 | once(make::ident_pat(make::name("it")).into()), |
131 | ); | 131 | ); |
132 | let expr = { | 132 | let expr = { |
133 | let name_ref = make::name_ref("it"); | 133 | let path = make::ext::ident_path("it"); |
134 | let segment = make::path_segment(name_ref); | ||
135 | let path = make::path_unqualified(segment); | ||
136 | make::expr_path(path) | 134 | make::expr_path(path) |
137 | }; | 135 | }; |
138 | make::match_arm(once(pat.into()), expr) | 136 | make::match_arm(once(pat.into()), expr) |
diff --git a/crates/ide_assists/src/handlers/expand_glob_import.rs b/crates/ide_assists/src/handlers/expand_glob_import.rs index 98389e4f7..79cb08d69 100644 --- a/crates/ide_assists/src/handlers/expand_glob_import.rs +++ b/crates/ide_assists/src/handlers/expand_glob_import.rs | |||
@@ -4,10 +4,10 @@ use ide_db::{ | |||
4 | defs::{Definition, NameRefClass}, | 4 | defs::{Definition, NameRefClass}, |
5 | search::SearchScope, | 5 | search::SearchScope, |
6 | }; | 6 | }; |
7 | use stdx::never; | ||
7 | use syntax::{ | 8 | use syntax::{ |
8 | algo::SyntaxRewriter, | ||
9 | ast::{self, make}, | 9 | ast::{self, make}, |
10 | AstNode, Direction, SyntaxNode, SyntaxToken, T, | 10 | ted, AstNode, Direction, SyntaxNode, SyntaxToken, T, |
11 | }; | 11 | }; |
12 | 12 | ||
13 | use crate::{ | 13 | use crate::{ |
@@ -42,6 +42,7 @@ use crate::{ | |||
42 | // ``` | 42 | // ``` |
43 | pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 43 | pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
44 | let star = ctx.find_token_syntax_at_offset(T![*])?; | 44 | let star = ctx.find_token_syntax_at_offset(T![*])?; |
45 | let use_tree = star.parent().and_then(ast::UseTree::cast)?; | ||
45 | let (parent, mod_path) = find_parent_and_path(&star)?; | 46 | let (parent, mod_path) = find_parent_and_path(&star)?; |
46 | let target_module = match ctx.sema.resolve_path(&mod_path)? { | 47 | let target_module = match ctx.sema.resolve_path(&mod_path)? { |
47 | PathResolution::Def(ModuleDef::Module(it)) => it, | 48 | PathResolution::Def(ModuleDef::Module(it)) => it, |
@@ -53,7 +54,6 @@ pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext) -> Opti | |||
53 | 54 | ||
54 | let refs_in_target = find_refs_in_mod(ctx, target_module, Some(current_module))?; | 55 | let refs_in_target = find_refs_in_mod(ctx, target_module, Some(current_module))?; |
55 | let imported_defs = find_imported_defs(ctx, star)?; | 56 | let imported_defs = find_imported_defs(ctx, star)?; |
56 | let names_to_import = find_names_to_import(ctx, refs_in_target, imported_defs); | ||
57 | 57 | ||
58 | let target = parent.clone().either(|n| n.syntax().clone(), |n| n.syntax().clone()); | 58 | let target = parent.clone().either(|n| n.syntax().clone(), |n| n.syntax().clone()); |
59 | acc.add( | 59 | acc.add( |
@@ -61,9 +61,31 @@ 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 | let mut rewriter = SyntaxRewriter::default(); | 64 | let use_tree = builder.make_mut(use_tree); |
65 | replace_ast(&mut rewriter, parent, mod_path, names_to_import); | 65 | |
66 | builder.rewrite(rewriter); | 66 | let names_to_import = find_names_to_import(ctx, refs_in_target, imported_defs); |
67 | let expanded = make::use_tree_list(names_to_import.iter().map(|n| { | ||
68 | let path = make::ext::ident_path(&n.to_string()); | ||
69 | make::use_tree(path, None, None, false) | ||
70 | })) | ||
71 | .clone_for_update(); | ||
72 | |||
73 | match use_tree.star_token() { | ||
74 | Some(star) => { | ||
75 | let needs_braces = use_tree.path().is_some() && names_to_import.len() > 1; | ||
76 | if needs_braces { | ||
77 | ted::replace(star, expanded.syntax()) | ||
78 | } else { | ||
79 | let without_braces = expanded | ||
80 | .syntax() | ||
81 | .children_with_tokens() | ||
82 | .filter(|child| !matches!(child.kind(), T!['{'] | T!['}'])) | ||
83 | .collect(); | ||
84 | ted::replace_with_many(star, without_braces) | ||
85 | } | ||
86 | } | ||
87 | None => never!(), | ||
88 | } | ||
67 | }, | 89 | }, |
68 | ) | 90 | ) |
69 | } | 91 | } |
@@ -232,53 +254,6 @@ fn find_names_to_import( | |||
232 | used_refs.0.iter().map(|r| r.visible_name.clone()).collect() | 254 | used_refs.0.iter().map(|r| r.visible_name.clone()).collect() |
233 | } | 255 | } |
234 | 256 | ||
235 | fn replace_ast( | ||
236 | rewriter: &mut SyntaxRewriter, | ||
237 | parent: Either<ast::UseTree, ast::UseTreeList>, | ||
238 | path: ast::Path, | ||
239 | names_to_import: Vec<Name>, | ||
240 | ) { | ||
241 | let existing_use_trees = match parent.clone() { | ||
242 | Either::Left(_) => vec![], | ||
243 | Either::Right(u) => u | ||
244 | .use_trees() | ||
245 | .filter(|n| | ||
246 | // filter out star | ||
247 | n.star_token().is_none()) | ||
248 | .collect(), | ||
249 | }; | ||
250 | |||
251 | let new_use_trees: Vec<ast::UseTree> = names_to_import | ||
252 | .iter() | ||
253 | .map(|n| { | ||
254 | let path = make::path_unqualified(make::path_segment(make::name_ref(&n.to_string()))); | ||
255 | make::use_tree(path, None, None, false) | ||
256 | }) | ||
257 | .collect(); | ||
258 | |||
259 | let use_trees = [&existing_use_trees[..], &new_use_trees[..]].concat(); | ||
260 | |||
261 | match use_trees.as_slice() { | ||
262 | [name] => { | ||
263 | if let Some(end_path) = name.path() { | ||
264 | rewriter.replace_ast( | ||
265 | &parent.left_or_else(|tl| tl.parent_use_tree()), | ||
266 | &make::use_tree(make::path_concat(path, end_path), None, None, false), | ||
267 | ); | ||
268 | } | ||
269 | } | ||
270 | names => match &parent { | ||
271 | Either::Left(parent) => rewriter.replace_ast( | ||
272 | parent, | ||
273 | &make::use_tree(path, Some(make::use_tree_list(names.to_owned())), None, false), | ||
274 | ), | ||
275 | Either::Right(parent) => { | ||
276 | rewriter.replace_ast(parent, &make::use_tree_list(names.to_owned())) | ||
277 | } | ||
278 | }, | ||
279 | }; | ||
280 | } | ||
281 | |||
282 | #[cfg(test)] | 257 | #[cfg(test)] |
283 | mod tests { | 258 | mod tests { |
284 | use crate::tests::{check_assist, check_assist_not_applicable}; | 259 | use crate::tests::{check_assist, check_assist_not_applicable}; |
@@ -350,7 +325,7 @@ mod foo { | |||
350 | pub fn f() {} | 325 | pub fn f() {} |
351 | } | 326 | } |
352 | 327 | ||
353 | use foo::{f, Baz, Bar}; | 328 | use foo::{Baz, Bar, f}; |
354 | 329 | ||
355 | fn qux(bar: Bar, baz: Baz) { | 330 | fn qux(bar: Bar, baz: Baz) { |
356 | f(); | 331 | f(); |
@@ -389,7 +364,7 @@ mod foo { | |||
389 | } | 364 | } |
390 | 365 | ||
391 | use foo::Bar; | 366 | use foo::Bar; |
392 | use foo::{f, Baz}; | 367 | use foo::{Baz, f}; |
393 | 368 | ||
394 | fn qux(bar: Bar, baz: Baz) { | 369 | fn qux(bar: Bar, baz: Baz) { |
395 | f(); | 370 | f(); |
@@ -439,7 +414,7 @@ mod foo { | |||
439 | } | 414 | } |
440 | } | 415 | } |
441 | 416 | ||
442 | use foo::{bar::{f, Baz, Bar}, baz::*}; | 417 | use foo::{bar::{Baz, Bar, f}, baz::*}; |
443 | 418 | ||
444 | fn qux(bar: Bar, baz: Baz) { | 419 | fn qux(bar: Bar, baz: Baz) { |
445 | f(); | 420 | f(); |
@@ -891,7 +866,7 @@ mod foo { | |||
891 | pub struct Bar; | 866 | pub struct Bar; |
892 | } | 867 | } |
893 | 868 | ||
894 | use foo::Bar; | 869 | use foo::{Bar}; |
895 | 870 | ||
896 | struct Baz { | 871 | struct Baz { |
897 | bar: Bar | 872 | bar: Bar |
diff --git a/crates/ide_assists/src/handlers/extract_function.rs b/crates/ide_assists/src/handlers/extract_function.rs index 93b28370c..6311afc1f 100644 --- a/crates/ide_assists/src/handlers/extract_function.rs +++ b/crates/ide_assists/src/handlers/extract_function.rs | |||
@@ -10,7 +10,6 @@ use ide_db::{ | |||
10 | use itertools::Itertools; | 10 | use itertools::Itertools; |
11 | use stdx::format_to; | 11 | use stdx::format_to; |
12 | use syntax::{ | 12 | use syntax::{ |
13 | algo::SyntaxRewriter, | ||
14 | ast::{ | 13 | ast::{ |
15 | self, | 14 | self, |
16 | edit::{AstNodeEdit, IndentLevel}, | 15 | edit::{AstNodeEdit, IndentLevel}, |
@@ -957,10 +956,10 @@ fn format_replacement(ctx: &AssistContext, fun: &Function, indent: IndentLevel) | |||
957 | let args = fun.params.iter().map(|param| param.to_arg(ctx)); | 956 | let args = fun.params.iter().map(|param| param.to_arg(ctx)); |
958 | let args = make::arg_list(args); | 957 | let args = make::arg_list(args); |
959 | let call_expr = if fun.self_param.is_some() { | 958 | let call_expr = if fun.self_param.is_some() { |
960 | let self_arg = make::expr_path(make_path_from_text("self")); | 959 | let self_arg = make::expr_path(make::ext::ident_path("self")); |
961 | make::expr_method_call(self_arg, &fun.name, args) | 960 | make::expr_method_call(self_arg, &fun.name, args) |
962 | } else { | 961 | } else { |
963 | let func = make::expr_path(make_path_from_text(&fun.name)); | 962 | let func = make::expr_path(make::ext::ident_path(&fun.name)); |
964 | make::expr_call(func, args) | 963 | make::expr_call(func, args) |
965 | }; | 964 | }; |
966 | 965 | ||
@@ -1055,11 +1054,11 @@ impl FlowHandler { | |||
1055 | make::expr_if(condition, block, None) | 1054 | make::expr_if(condition, block, None) |
1056 | } | 1055 | } |
1057 | FlowHandler::IfOption { action } => { | 1056 | FlowHandler::IfOption { action } => { |
1058 | let path = make_path_from_text("Some"); | 1057 | let path = make::ext::ident_path("Some"); |
1059 | let value_pat = make::ident_pat(make::name("value")); | 1058 | let value_pat = make::ident_pat(make::name("value")); |
1060 | let pattern = make::tuple_struct_pat(path, iter::once(value_pat.into())); | 1059 | let pattern = make::tuple_struct_pat(path, iter::once(value_pat.into())); |
1061 | let cond = make::condition(call_expr, Some(pattern.into())); | 1060 | let cond = make::condition(call_expr, Some(pattern.into())); |
1062 | let value = make::expr_path(make_path_from_text("value")); | 1061 | let value = make::expr_path(make::ext::ident_path("value")); |
1063 | let action_expr = action.make_result_handler(Some(value)); | 1062 | let action_expr = action.make_result_handler(Some(value)); |
1064 | let action_stmt = make::expr_stmt(action_expr); | 1063 | let action_stmt = make::expr_stmt(action_expr); |
1065 | let then = make::block_expr(iter::once(action_stmt.into()), None); | 1064 | let then = make::block_expr(iter::once(action_stmt.into()), None); |
@@ -1069,14 +1068,14 @@ impl FlowHandler { | |||
1069 | let some_name = "value"; | 1068 | let some_name = "value"; |
1070 | 1069 | ||
1071 | let some_arm = { | 1070 | let some_arm = { |
1072 | let path = make_path_from_text("Some"); | 1071 | let path = make::ext::ident_path("Some"); |
1073 | let value_pat = make::ident_pat(make::name(some_name)); | 1072 | let value_pat = make::ident_pat(make::name(some_name)); |
1074 | let pat = make::tuple_struct_pat(path, iter::once(value_pat.into())); | 1073 | let pat = make::tuple_struct_pat(path, iter::once(value_pat.into())); |
1075 | let value = make::expr_path(make_path_from_text(some_name)); | 1074 | let value = make::expr_path(make::ext::ident_path(some_name)); |
1076 | make::match_arm(iter::once(pat.into()), value) | 1075 | make::match_arm(iter::once(pat.into()), value) |
1077 | }; | 1076 | }; |
1078 | let none_arm = { | 1077 | let none_arm = { |
1079 | let path = make_path_from_text("None"); | 1078 | let path = make::ext::ident_path("None"); |
1080 | let pat = make::path_pat(path); | 1079 | let pat = make::path_pat(path); |
1081 | make::match_arm(iter::once(pat), none.make_result_handler(None)) | 1080 | make::match_arm(iter::once(pat), none.make_result_handler(None)) |
1082 | }; | 1081 | }; |
@@ -1088,17 +1087,17 @@ impl FlowHandler { | |||
1088 | let err_name = "value"; | 1087 | let err_name = "value"; |
1089 | 1088 | ||
1090 | let ok_arm = { | 1089 | let ok_arm = { |
1091 | let path = make_path_from_text("Ok"); | 1090 | let path = make::ext::ident_path("Ok"); |
1092 | let value_pat = make::ident_pat(make::name(ok_name)); | 1091 | let value_pat = make::ident_pat(make::name(ok_name)); |
1093 | let pat = make::tuple_struct_pat(path, iter::once(value_pat.into())); | 1092 | let pat = make::tuple_struct_pat(path, iter::once(value_pat.into())); |
1094 | let value = make::expr_path(make_path_from_text(ok_name)); | 1093 | let value = make::expr_path(make::ext::ident_path(ok_name)); |
1095 | make::match_arm(iter::once(pat.into()), value) | 1094 | make::match_arm(iter::once(pat.into()), value) |
1096 | }; | 1095 | }; |
1097 | let err_arm = { | 1096 | let err_arm = { |
1098 | let path = make_path_from_text("Err"); | 1097 | let path = make::ext::ident_path("Err"); |
1099 | let value_pat = make::ident_pat(make::name(err_name)); | 1098 | let value_pat = make::ident_pat(make::name(err_name)); |
1100 | let pat = make::tuple_struct_pat(path, iter::once(value_pat.into())); | 1099 | let pat = make::tuple_struct_pat(path, iter::once(value_pat.into())); |
1101 | let value = make::expr_path(make_path_from_text(err_name)); | 1100 | let value = make::expr_path(make::ext::ident_path(err_name)); |
1102 | make::match_arm(iter::once(pat.into()), err.make_result_handler(Some(value))) | 1101 | make::match_arm(iter::once(pat.into()), err.make_result_handler(Some(value))) |
1103 | }; | 1102 | }; |
1104 | let arms = make::match_arm_list(vec![ok_arm, err_arm]); | 1103 | let arms = make::match_arm_list(vec![ok_arm, err_arm]); |
@@ -1108,13 +1107,9 @@ impl FlowHandler { | |||
1108 | } | 1107 | } |
1109 | } | 1108 | } |
1110 | 1109 | ||
1111 | fn make_path_from_text(text: &str) -> ast::Path { | ||
1112 | make::path_unqualified(make::path_segment(make::name_ref(text))) | ||
1113 | } | ||
1114 | |||
1115 | fn path_expr_from_local(ctx: &AssistContext, var: Local) -> ast::Expr { | 1110 | fn path_expr_from_local(ctx: &AssistContext, var: Local) -> ast::Expr { |
1116 | let name = var.name(ctx.db()).unwrap().to_string(); | 1111 | let name = var.name(ctx.db()).unwrap().to_string(); |
1117 | make::expr_path(make_path_from_text(&name)) | 1112 | make::expr_path(make::ext::ident_path(&name)) |
1118 | } | 1113 | } |
1119 | 1114 | ||
1120 | fn format_function( | 1115 | fn format_function( |
@@ -1180,7 +1175,7 @@ fn make_ret_ty(ctx: &AssistContext, module: hir::Module, fun: &Function) -> Opti | |||
1180 | fun_ty.make_ty(ctx, module) | 1175 | fun_ty.make_ty(ctx, module) |
1181 | } | 1176 | } |
1182 | FlowHandler::Try { kind: TryKind::Option } => { | 1177 | FlowHandler::Try { kind: TryKind::Option } => { |
1183 | make::ty_generic(make::name_ref("Option"), iter::once(fun_ty.make_ty(ctx, module))) | 1178 | make::ext::ty_option(fun_ty.make_ty(ctx, module)) |
1184 | } | 1179 | } |
1185 | FlowHandler::Try { kind: TryKind::Result { ty: parent_ret_ty } } => { | 1180 | FlowHandler::Try { kind: TryKind::Result { ty: parent_ret_ty } } => { |
1186 | let handler_ty = parent_ret_ty | 1181 | let handler_ty = parent_ret_ty |
@@ -1188,29 +1183,21 @@ fn make_ret_ty(ctx: &AssistContext, module: hir::Module, fun: &Function) -> Opti | |||
1188 | .nth(1) | 1183 | .nth(1) |
1189 | .map(|ty| make_ty(&ty, ctx, module)) | 1184 | .map(|ty| make_ty(&ty, ctx, module)) |
1190 | .unwrap_or_else(make::ty_unit); | 1185 | .unwrap_or_else(make::ty_unit); |
1191 | make::ty_generic( | 1186 | make::ext::ty_result(fun_ty.make_ty(ctx, module), handler_ty) |
1192 | make::name_ref("Result"), | ||
1193 | vec![fun_ty.make_ty(ctx, module), handler_ty], | ||
1194 | ) | ||
1195 | } | 1187 | } |
1196 | FlowHandler::If { .. } => make::ty("bool"), | 1188 | FlowHandler::If { .. } => make::ext::ty_bool(), |
1197 | FlowHandler::IfOption { action } => { | 1189 | FlowHandler::IfOption { action } => { |
1198 | let handler_ty = action | 1190 | let handler_ty = action |
1199 | .expr_ty(ctx) | 1191 | .expr_ty(ctx) |
1200 | .map(|ty| make_ty(&ty, ctx, module)) | 1192 | .map(|ty| make_ty(&ty, ctx, module)) |
1201 | .unwrap_or_else(make::ty_unit); | 1193 | .unwrap_or_else(make::ty_unit); |
1202 | make::ty_generic(make::name_ref("Option"), iter::once(handler_ty)) | 1194 | make::ext::ty_option(handler_ty) |
1203 | } | ||
1204 | FlowHandler::MatchOption { .. } => { | ||
1205 | make::ty_generic(make::name_ref("Option"), iter::once(fun_ty.make_ty(ctx, module))) | ||
1206 | } | 1195 | } |
1196 | FlowHandler::MatchOption { .. } => make::ext::ty_option(fun_ty.make_ty(ctx, module)), | ||
1207 | FlowHandler::MatchResult { err } => { | 1197 | FlowHandler::MatchResult { err } => { |
1208 | let handler_ty = | 1198 | let handler_ty = |
1209 | err.expr_ty(ctx).map(|ty| make_ty(&ty, ctx, module)).unwrap_or_else(make::ty_unit); | 1199 | err.expr_ty(ctx).map(|ty| make_ty(&ty, ctx, module)).unwrap_or_else(make::ty_unit); |
1210 | make::ty_generic( | 1200 | make::ext::ty_result(fun_ty.make_ty(ctx, module), handler_ty) |
1211 | make::name_ref("Result"), | ||
1212 | vec![fun_ty.make_ty(ctx, module), handler_ty], | ||
1213 | ) | ||
1214 | } | 1201 | } |
1215 | }; | 1202 | }; |
1216 | Some(make::ret_type(ret_ty)) | 1203 | Some(make::ret_type(ret_ty)) |
@@ -1297,7 +1284,7 @@ fn make_body( | |||
1297 | TryKind::Option => "Some", | 1284 | TryKind::Option => "Some", |
1298 | TryKind::Result { .. } => "Ok", | 1285 | TryKind::Result { .. } => "Ok", |
1299 | }; | 1286 | }; |
1300 | let func = make::expr_path(make_path_from_text(constructor)); | 1287 | let func = make::expr_path(make::ext::ident_path(constructor)); |
1301 | let args = make::arg_list(iter::once(tail_expr)); | 1288 | let args = make::arg_list(iter::once(tail_expr)); |
1302 | make::expr_call(func, args) | 1289 | make::expr_call(func, args) |
1303 | }) | 1290 | }) |
@@ -1307,16 +1294,16 @@ fn make_body( | |||
1307 | with_tail_expr(block, lit_false.into()) | 1294 | with_tail_expr(block, lit_false.into()) |
1308 | } | 1295 | } |
1309 | FlowHandler::IfOption { .. } => { | 1296 | FlowHandler::IfOption { .. } => { |
1310 | let none = make::expr_path(make_path_from_text("None")); | 1297 | let none = make::expr_path(make::ext::ident_path("None")); |
1311 | with_tail_expr(block, none) | 1298 | with_tail_expr(block, none) |
1312 | } | 1299 | } |
1313 | FlowHandler::MatchOption { .. } => map_tail_expr(block, |tail_expr| { | 1300 | FlowHandler::MatchOption { .. } => map_tail_expr(block, |tail_expr| { |
1314 | let some = make::expr_path(make_path_from_text("Some")); | 1301 | let some = make::expr_path(make::ext::ident_path("Some")); |
1315 | let args = make::arg_list(iter::once(tail_expr)); | 1302 | let args = make::arg_list(iter::once(tail_expr)); |
1316 | make::expr_call(some, args) | 1303 | make::expr_call(some, args) |
1317 | }), | 1304 | }), |
1318 | FlowHandler::MatchResult { .. } => map_tail_expr(block, |tail_expr| { | 1305 | FlowHandler::MatchResult { .. } => map_tail_expr(block, |tail_expr| { |
1319 | let ok = make::expr_path(make_path_from_text("Ok")); | 1306 | let ok = make::expr_path(make::ext::ident_path("Ok")); |
1320 | let args = make::arg_list(iter::once(tail_expr)); | 1307 | let args = make::arg_list(iter::once(tail_expr)); |
1321 | make::expr_call(ok, args) | 1308 | make::expr_call(ok, args) |
1322 | }), | 1309 | }), |
@@ -1362,7 +1349,8 @@ fn rewrite_body_segment( | |||
1362 | syntax: &SyntaxNode, | 1349 | syntax: &SyntaxNode, |
1363 | ) -> SyntaxNode { | 1350 | ) -> SyntaxNode { |
1364 | let syntax = fix_param_usages(ctx, params, syntax); | 1351 | let syntax = fix_param_usages(ctx, params, syntax); |
1365 | update_external_control_flow(handler, &syntax) | 1352 | update_external_control_flow(handler, &syntax); |
1353 | syntax | ||
1366 | } | 1354 | } |
1367 | 1355 | ||
1368 | /// change all usages to account for added `&`/`&mut` for some params | 1356 | /// change all usages to account for added `&`/`&mut` for some params |
@@ -1415,75 +1403,65 @@ fn fix_param_usages(ctx: &AssistContext, params: &[Param], syntax: &SyntaxNode) | |||
1415 | res | 1403 | res |
1416 | } | 1404 | } |
1417 | 1405 | ||
1418 | fn update_external_control_flow(handler: &FlowHandler, syntax: &SyntaxNode) -> SyntaxNode { | 1406 | fn update_external_control_flow(handler: &FlowHandler, syntax: &SyntaxNode) { |
1419 | let mut rewriter = SyntaxRewriter::default(); | ||
1420 | |||
1421 | let mut nested_loop = None; | 1407 | let mut nested_loop = None; |
1422 | let mut nested_scope = None; | 1408 | let mut nested_scope = None; |
1423 | for event in syntax.preorder() { | 1409 | for event in syntax.preorder() { |
1424 | let node = match event { | 1410 | match event { |
1425 | WalkEvent::Enter(e) => { | 1411 | WalkEvent::Enter(e) => match e.kind() { |
1426 | match e.kind() { | 1412 | SyntaxKind::LOOP_EXPR | SyntaxKind::WHILE_EXPR | SyntaxKind::FOR_EXPR => { |
1427 | SyntaxKind::LOOP_EXPR | SyntaxKind::WHILE_EXPR | SyntaxKind::FOR_EXPR => { | 1413 | if nested_loop.is_none() { |
1428 | if nested_loop.is_none() { | 1414 | nested_loop = Some(e.clone()); |
1429 | nested_loop = Some(e.clone()); | ||
1430 | } | ||
1431 | } | 1415 | } |
1432 | SyntaxKind::FN | 1416 | } |
1433 | | SyntaxKind::CONST | 1417 | SyntaxKind::FN |
1434 | | SyntaxKind::STATIC | 1418 | | SyntaxKind::CONST |
1435 | | SyntaxKind::IMPL | 1419 | | SyntaxKind::STATIC |
1436 | | SyntaxKind::MODULE => { | 1420 | | SyntaxKind::IMPL |
1437 | if nested_scope.is_none() { | 1421 | | SyntaxKind::MODULE => { |
1438 | nested_scope = Some(e.clone()); | 1422 | if nested_scope.is_none() { |
1439 | } | 1423 | nested_scope = Some(e.clone()); |
1440 | } | 1424 | } |
1441 | _ => {} | ||
1442 | } | 1425 | } |
1443 | e | 1426 | _ => {} |
1444 | } | 1427 | }, |
1445 | WalkEvent::Leave(e) => { | 1428 | WalkEvent::Leave(e) => { |
1429 | if nested_scope.is_none() { | ||
1430 | if let Some(expr) = ast::Expr::cast(e.clone()) { | ||
1431 | match expr { | ||
1432 | ast::Expr::ReturnExpr(return_expr) if nested_scope.is_none() => { | ||
1433 | let expr = return_expr.expr(); | ||
1434 | if let Some(replacement) = make_rewritten_flow(handler, expr) { | ||
1435 | ted::replace(return_expr.syntax(), replacement.syntax()) | ||
1436 | } | ||
1437 | } | ||
1438 | ast::Expr::BreakExpr(break_expr) if nested_loop.is_none() => { | ||
1439 | let expr = break_expr.expr(); | ||
1440 | if let Some(replacement) = make_rewritten_flow(handler, expr) { | ||
1441 | ted::replace(break_expr.syntax(), replacement.syntax()) | ||
1442 | } | ||
1443 | } | ||
1444 | ast::Expr::ContinueExpr(continue_expr) if nested_loop.is_none() => { | ||
1445 | if let Some(replacement) = make_rewritten_flow(handler, None) { | ||
1446 | ted::replace(continue_expr.syntax(), replacement.syntax()) | ||
1447 | } | ||
1448 | } | ||
1449 | _ => { | ||
1450 | // do nothing | ||
1451 | } | ||
1452 | } | ||
1453 | } | ||
1454 | } | ||
1455 | |||
1446 | if nested_loop.as_ref() == Some(&e) { | 1456 | if nested_loop.as_ref() == Some(&e) { |
1447 | nested_loop = None; | 1457 | nested_loop = None; |
1448 | } | 1458 | } |
1449 | if nested_scope.as_ref() == Some(&e) { | 1459 | if nested_scope.as_ref() == Some(&e) { |
1450 | nested_scope = None; | 1460 | nested_scope = None; |
1451 | } | 1461 | } |
1452 | continue; | ||
1453 | } | 1462 | } |
1454 | }; | 1463 | }; |
1455 | if nested_scope.is_some() { | ||
1456 | continue; | ||
1457 | } | ||
1458 | let expr = match ast::Expr::cast(node) { | ||
1459 | Some(e) => e, | ||
1460 | None => continue, | ||
1461 | }; | ||
1462 | match expr { | ||
1463 | ast::Expr::ReturnExpr(return_expr) if nested_scope.is_none() => { | ||
1464 | let expr = return_expr.expr(); | ||
1465 | if let Some(replacement) = make_rewritten_flow(handler, expr) { | ||
1466 | rewriter.replace_ast(&return_expr.into(), &replacement); | ||
1467 | } | ||
1468 | } | ||
1469 | ast::Expr::BreakExpr(break_expr) if nested_loop.is_none() => { | ||
1470 | let expr = break_expr.expr(); | ||
1471 | if let Some(replacement) = make_rewritten_flow(handler, expr) { | ||
1472 | rewriter.replace_ast(&break_expr.into(), &replacement); | ||
1473 | } | ||
1474 | } | ||
1475 | ast::Expr::ContinueExpr(continue_expr) if nested_loop.is_none() => { | ||
1476 | if let Some(replacement) = make_rewritten_flow(handler, None) { | ||
1477 | rewriter.replace_ast(&continue_expr.into(), &replacement); | ||
1478 | } | ||
1479 | } | ||
1480 | _ => { | ||
1481 | // do nothing | ||
1482 | } | ||
1483 | } | ||
1484 | } | 1464 | } |
1485 | |||
1486 | rewriter.rewrite(syntax) | ||
1487 | } | 1465 | } |
1488 | 1466 | ||
1489 | fn make_rewritten_flow(handler: &FlowHandler, arg_expr: Option<ast::Expr>) -> Option<ast::Expr> { | 1467 | fn make_rewritten_flow(handler: &FlowHandler, arg_expr: Option<ast::Expr>) -> Option<ast::Expr> { |
@@ -1493,16 +1471,16 @@ fn make_rewritten_flow(handler: &FlowHandler, arg_expr: Option<ast::Expr>) -> Op | |||
1493 | FlowHandler::IfOption { .. } => { | 1471 | FlowHandler::IfOption { .. } => { |
1494 | let expr = arg_expr.unwrap_or_else(|| make::expr_tuple(Vec::new())); | 1472 | let expr = arg_expr.unwrap_or_else(|| make::expr_tuple(Vec::new())); |
1495 | let args = make::arg_list(iter::once(expr)); | 1473 | let args = make::arg_list(iter::once(expr)); |
1496 | make::expr_call(make::expr_path(make_path_from_text("Some")), args) | 1474 | make::expr_call(make::expr_path(make::ext::ident_path("Some")), args) |
1497 | } | 1475 | } |
1498 | FlowHandler::MatchOption { .. } => make::expr_path(make_path_from_text("None")), | 1476 | FlowHandler::MatchOption { .. } => make::expr_path(make::ext::ident_path("None")), |
1499 | FlowHandler::MatchResult { .. } => { | 1477 | FlowHandler::MatchResult { .. } => { |
1500 | let expr = arg_expr.unwrap_or_else(|| make::expr_tuple(Vec::new())); | 1478 | let expr = arg_expr.unwrap_or_else(|| make::expr_tuple(Vec::new())); |
1501 | let args = make::arg_list(iter::once(expr)); | 1479 | let args = make::arg_list(iter::once(expr)); |
1502 | make::expr_call(make::expr_path(make_path_from_text("Err")), args) | 1480 | make::expr_call(make::expr_path(make::ext::ident_path("Err")), args) |
1503 | } | 1481 | } |
1504 | }; | 1482 | }; |
1505 | Some(make::expr_return(Some(value))) | 1483 | Some(make::expr_return(Some(value)).clone_for_update()) |
1506 | } | 1484 | } |
1507 | 1485 | ||
1508 | #[cfg(test)] | 1486 | #[cfg(test)] |
diff --git a/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs index 66f274fa7..007aba23d 100644 --- a/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs | |||
@@ -70,7 +70,7 @@ pub(crate) fn extract_struct_from_enum_variant( | |||
70 | continue; | 70 | continue; |
71 | } | 71 | } |
72 | builder.edit_file(file_id); | 72 | builder.edit_file(file_id); |
73 | let source_file = builder.make_ast_mut(ctx.sema.parse(file_id)); | 73 | let source_file = builder.make_mut(ctx.sema.parse(file_id)); |
74 | let processed = process_references( | 74 | let processed = process_references( |
75 | ctx, | 75 | ctx, |
76 | &mut visited_modules_set, | 76 | &mut visited_modules_set, |
@@ -84,8 +84,8 @@ pub(crate) fn extract_struct_from_enum_variant( | |||
84 | }); | 84 | }); |
85 | } | 85 | } |
86 | builder.edit_file(ctx.frange.file_id); | 86 | builder.edit_file(ctx.frange.file_id); |
87 | let source_file = builder.make_ast_mut(ctx.sema.parse(ctx.frange.file_id)); | 87 | let source_file = builder.make_mut(ctx.sema.parse(ctx.frange.file_id)); |
88 | let variant = builder.make_ast_mut(variant.clone()); | 88 | let variant = builder.make_mut(variant.clone()); |
89 | if let Some(references) = def_file_references { | 89 | if let Some(references) = def_file_references { |
90 | let processed = process_references( | 90 | let processed = process_references( |
91 | ctx, | 91 | ctx, |
@@ -151,20 +151,37 @@ fn create_struct_def( | |||
151 | field_list: &Either<ast::RecordFieldList, ast::TupleFieldList>, | 151 | field_list: &Either<ast::RecordFieldList, ast::TupleFieldList>, |
152 | visibility: Option<ast::Visibility>, | 152 | visibility: Option<ast::Visibility>, |
153 | ) -> ast::Struct { | 153 | ) -> ast::Struct { |
154 | let pub_vis = Some(make::visibility_pub()); | 154 | let pub_vis = make::visibility_pub(); |
155 | |||
156 | let insert_pub = |node: &'_ SyntaxNode| { | ||
157 | let pub_vis = pub_vis.clone_for_update(); | ||
158 | ted::insert(ted::Position::before(node), pub_vis.syntax()); | ||
159 | }; | ||
160 | |||
161 | // for fields without any existing visibility, use pub visibility | ||
155 | let field_list = match field_list { | 162 | let field_list = match field_list { |
156 | Either::Left(field_list) => { | 163 | Either::Left(field_list) => { |
157 | make::record_field_list(field_list.fields().flat_map(|field| { | 164 | let field_list = field_list.clone_for_update(); |
158 | Some(make::record_field(pub_vis.clone(), field.name()?, field.ty()?)) | 165 | |
159 | })) | 166 | field_list |
160 | .into() | 167 | .fields() |
168 | .filter(|field| field.visibility().is_none()) | ||
169 | .filter_map(|field| field.name()) | ||
170 | .for_each(|it| insert_pub(it.syntax())); | ||
171 | |||
172 | field_list.into() | ||
161 | } | 173 | } |
162 | Either::Right(field_list) => make::tuple_field_list( | 174 | Either::Right(field_list) => { |
175 | let field_list = field_list.clone_for_update(); | ||
176 | |||
163 | field_list | 177 | field_list |
164 | .fields() | 178 | .fields() |
165 | .flat_map(|field| Some(make::tuple_field(pub_vis.clone(), field.ty()?))), | 179 | .filter(|field| field.visibility().is_none()) |
166 | ) | 180 | .filter_map(|field| field.ty()) |
167 | .into(), | 181 | .for_each(|it| insert_pub(it.syntax())); |
182 | |||
183 | field_list.into() | ||
184 | } | ||
168 | }; | 185 | }; |
169 | 186 | ||
170 | make::struct_(visibility, variant_name, None, field_list).clone_for_update() | 187 | make::struct_(visibility, variant_name, None, field_list).clone_for_update() |
@@ -295,6 +312,106 @@ enum A { One(One) }"#, | |||
295 | } | 312 | } |
296 | 313 | ||
297 | #[test] | 314 | #[test] |
315 | fn test_extract_struct_keep_comments_and_attrs_one_field_named() { | ||
316 | check_assist( | ||
317 | extract_struct_from_enum_variant, | ||
318 | r#" | ||
319 | enum A { | ||
320 | $0One { | ||
321 | // leading comment | ||
322 | /// doc comment | ||
323 | #[an_attr] | ||
324 | foo: u32 | ||
325 | // trailing comment | ||
326 | } | ||
327 | }"#, | ||
328 | r#" | ||
329 | struct One{ | ||
330 | // leading comment | ||
331 | /// doc comment | ||
332 | #[an_attr] | ||
333 | pub foo: u32 | ||
334 | // trailing comment | ||
335 | } | ||
336 | |||
337 | enum A { | ||
338 | One(One) | ||
339 | }"#, | ||
340 | ); | ||
341 | } | ||
342 | |||
343 | #[test] | ||
344 | fn test_extract_struct_keep_comments_and_attrs_several_fields_named() { | ||
345 | check_assist( | ||
346 | extract_struct_from_enum_variant, | ||
347 | r#" | ||
348 | enum A { | ||
349 | $0One { | ||
350 | // comment | ||
351 | /// doc | ||
352 | #[attr] | ||
353 | foo: u32, | ||
354 | // comment | ||
355 | #[attr] | ||
356 | /// doc | ||
357 | bar: u32 | ||
358 | } | ||
359 | }"#, | ||
360 | r#" | ||
361 | struct One{ | ||
362 | // comment | ||
363 | /// doc | ||
364 | #[attr] | ||
365 | pub foo: u32, | ||
366 | // comment | ||
367 | #[attr] | ||
368 | /// doc | ||
369 | pub bar: u32 | ||
370 | } | ||
371 | |||
372 | enum A { | ||
373 | One(One) | ||
374 | }"#, | ||
375 | ); | ||
376 | } | ||
377 | |||
378 | #[test] | ||
379 | fn test_extract_struct_keep_comments_and_attrs_several_fields_tuple() { | ||
380 | check_assist( | ||
381 | extract_struct_from_enum_variant, | ||
382 | "enum A { $0One(/* comment */ #[attr] u32, /* another */ u32 /* tail */) }", | ||
383 | r#" | ||
384 | struct One(/* comment */ #[attr] pub u32, /* another */ pub u32 /* tail */); | ||
385 | |||
386 | enum A { One(One) }"#, | ||
387 | ); | ||
388 | } | ||
389 | |||
390 | #[test] | ||
391 | fn test_extract_struct_keep_existing_visibility_named() { | ||
392 | check_assist( | ||
393 | extract_struct_from_enum_variant, | ||
394 | "enum A { $0One{ pub a: u32, pub(crate) b: u32, pub(super) c: u32, d: u32 } }", | ||
395 | r#" | ||
396 | struct One{ pub a: u32, pub(crate) b: u32, pub(super) c: u32, pub d: u32 } | ||
397 | |||
398 | enum A { One(One) }"#, | ||
399 | ); | ||
400 | } | ||
401 | |||
402 | #[test] | ||
403 | fn test_extract_struct_keep_existing_visibility_tuple() { | ||
404 | check_assist( | ||
405 | extract_struct_from_enum_variant, | ||
406 | "enum A { $0One(pub u32, pub(crate) u32, pub(super) u32, u32) }", | ||
407 | r#" | ||
408 | struct One(pub u32, pub(crate) u32, pub(super) u32, pub u32); | ||
409 | |||
410 | enum A { One(One) }"#, | ||
411 | ); | ||
412 | } | ||
413 | |||
414 | #[test] | ||
298 | fn test_extract_enum_variant_name_value_namespace() { | 415 | fn test_extract_enum_variant_name_value_namespace() { |
299 | check_assist( | 416 | check_assist( |
300 | extract_struct_from_enum_variant, | 417 | extract_struct_from_enum_variant, |
diff --git a/crates/ide_assists/src/handlers/extract_variable.rs b/crates/ide_assists/src/handlers/extract_variable.rs index 136b9a55b..ae084c86c 100644 --- a/crates/ide_assists/src/handlers/extract_variable.rs +++ b/crates/ide_assists/src/handlers/extract_variable.rs | |||
@@ -54,7 +54,7 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext) -> Option | |||
54 | 54 | ||
55 | let var_name = match &field_shorthand { | 55 | let var_name = match &field_shorthand { |
56 | Some(it) => it.to_string(), | 56 | Some(it) => it.to_string(), |
57 | None => suggest_name::variable(&to_extract, &ctx.sema), | 57 | None => suggest_name::for_variable(&to_extract, &ctx.sema), |
58 | }; | 58 | }; |
59 | let expr_range = match &field_shorthand { | 59 | let expr_range = match &field_shorthand { |
60 | Some(it) => it.syntax().text_range().cover(to_extract.syntax().text_range()), | 60 | Some(it) => it.syntax().text_range().cover(to_extract.syntax().text_range()), |
diff --git a/crates/ide_assists/src/handlers/fill_match_arms.rs b/crates/ide_assists/src/handlers/fill_match_arms.rs index be927cc1c..58b001050 100644 --- a/crates/ide_assists/src/handlers/fill_match_arms.rs +++ b/crates/ide_assists/src/handlers/fill_match_arms.rs | |||
@@ -71,6 +71,7 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option< | |||
71 | .filter_map(|variant| build_pat(ctx.db(), module, variant)) | 71 | .filter_map(|variant| build_pat(ctx.db(), module, variant)) |
72 | .filter(|variant_pat| is_variant_missing(&top_lvl_pats, variant_pat)) | 72 | .filter(|variant_pat| is_variant_missing(&top_lvl_pats, variant_pat)) |
73 | .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block())) | 73 | .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block())) |
74 | .map(|it| it.clone_for_update()) | ||
74 | .collect::<Vec<_>>(); | 75 | .collect::<Vec<_>>(); |
75 | if Some(enum_def) | 76 | if Some(enum_def) |
76 | == FamousDefs(&ctx.sema, Some(module.krate())) | 77 | == FamousDefs(&ctx.sema, Some(module.krate())) |
@@ -99,6 +100,7 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option< | |||
99 | }) | 100 | }) |
100 | .filter(|variant_pat| is_variant_missing(&top_lvl_pats, variant_pat)) | 101 | .filter(|variant_pat| is_variant_missing(&top_lvl_pats, variant_pat)) |
101 | .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block())) | 102 | .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block())) |
103 | .map(|it| it.clone_for_update()) | ||
102 | .collect() | 104 | .collect() |
103 | } else { | 105 | } else { |
104 | return None; | 106 | return None; |
@@ -114,10 +116,20 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option< | |||
114 | "Fill match arms", | 116 | "Fill match arms", |
115 | target, | 117 | target, |
116 | |builder| { | 118 | |builder| { |
117 | let new_arm_list = match_arm_list.remove_placeholder(); | 119 | let new_match_arm_list = match_arm_list.clone_for_update(); |
118 | let n_old_arms = new_arm_list.arms().count(); | 120 | |
119 | let new_arm_list = new_arm_list.append_arms(missing_arms); | 121 | let catch_all_arm = new_match_arm_list |
120 | let first_new_arm = new_arm_list.arms().nth(n_old_arms); | 122 | .arms() |
123 | .find(|arm| matches!(arm.pat(), Some(ast::Pat::WildcardPat(_)))); | ||
124 | if let Some(arm) = catch_all_arm { | ||
125 | arm.remove() | ||
126 | } | ||
127 | let mut first_new_arm = None; | ||
128 | for arm in missing_arms { | ||
129 | first_new_arm.get_or_insert_with(|| arm.clone()); | ||
130 | new_match_arm_list.add_arm(arm); | ||
131 | } | ||
132 | |||
121 | let old_range = ctx.sema.original_range(match_arm_list.syntax()).range; | 133 | let old_range = ctx.sema.original_range(match_arm_list.syntax()).range; |
122 | match (first_new_arm, ctx.config.snippet_cap) { | 134 | match (first_new_arm, ctx.config.snippet_cap) { |
123 | (Some(first_new_arm), Some(cap)) => { | 135 | (Some(first_new_arm), Some(cap)) => { |
@@ -131,10 +143,10 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option< | |||
131 | } | 143 | } |
132 | None => Cursor::Before(first_new_arm.syntax()), | 144 | None => Cursor::Before(first_new_arm.syntax()), |
133 | }; | 145 | }; |
134 | let snippet = render_snippet(cap, new_arm_list.syntax(), cursor); | 146 | let snippet = render_snippet(cap, new_match_arm_list.syntax(), cursor); |
135 | builder.replace_snippet(cap, old_range, snippet); | 147 | builder.replace_snippet(cap, old_range, snippet); |
136 | } | 148 | } |
137 | _ => builder.replace(old_range, new_arm_list.to_string()), | 149 | _ => builder.replace(old_range, new_match_arm_list.to_string()), |
138 | } | 150 | } |
139 | }, | 151 | }, |
140 | ) | 152 | ) |
@@ -263,18 +275,18 @@ mod tests { | |||
263 | check_assist_not_applicable( | 275 | check_assist_not_applicable( |
264 | fill_match_arms, | 276 | fill_match_arms, |
265 | r#" | 277 | r#" |
266 | enum A { | 278 | enum A { |
267 | As, | 279 | As, |
268 | Bs{x:i32, y:Option<i32>}, | 280 | Bs{x:i32, y:Option<i32>}, |
269 | Cs(i32, Option<i32>), | 281 | Cs(i32, Option<i32>), |
270 | } | 282 | } |
271 | fn main() { | 283 | fn main() { |
272 | match A::As$0 { | 284 | match A::As$0 { |
273 | A::As, | 285 | A::As, |
274 | A::Bs{x,y:Some(_)} => {} | 286 | A::Bs{x,y:Some(_)} => {} |
275 | A::Cs(_, Some(_)) => {} | 287 | A::Cs(_, Some(_)) => {} |
276 | } | 288 | } |
277 | } | 289 | } |
278 | "#, | 290 | "#, |
279 | ); | 291 | ); |
280 | } | 292 | } |
@@ -284,13 +296,13 @@ mod tests { | |||
284 | check_assist_not_applicable( | 296 | check_assist_not_applicable( |
285 | fill_match_arms, | 297 | fill_match_arms, |
286 | r#" | 298 | r#" |
287 | fn foo(a: bool) { | 299 | fn foo(a: bool) { |
288 | match a$0 { | 300 | match a$0 { |
289 | true => {} | 301 | true => {} |
290 | false => {} | 302 | false => {} |
291 | } | 303 | } |
292 | } | 304 | } |
293 | "#, | 305 | "#, |
294 | ) | 306 | ) |
295 | } | 307 | } |
296 | 308 | ||
@@ -301,11 +313,11 @@ mod tests { | |||
301 | check_assist_not_applicable( | 313 | check_assist_not_applicable( |
302 | fill_match_arms, | 314 | fill_match_arms, |
303 | r#" | 315 | r#" |
304 | fn main() { | 316 | fn main() { |
305 | match (0, false)$0 { | 317 | match (0, false)$0 { |
306 | } | 318 | } |
307 | } | 319 | } |
308 | "#, | 320 | "#, |
309 | ); | 321 | ); |
310 | } | 322 | } |
311 | 323 | ||
@@ -314,19 +326,19 @@ mod tests { | |||
314 | check_assist( | 326 | check_assist( |
315 | fill_match_arms, | 327 | fill_match_arms, |
316 | r#" | 328 | r#" |
317 | fn foo(a: bool) { | 329 | fn foo(a: bool) { |
318 | match a$0 { | 330 | match a$0 { |
319 | } | 331 | } |
320 | } | 332 | } |
321 | "#, | 333 | "#, |
322 | r#" | 334 | r#" |
323 | fn foo(a: bool) { | 335 | fn foo(a: bool) { |
324 | match a { | 336 | match a { |
325 | $0true => {} | 337 | $0true => {} |
326 | false => {} | 338 | false => {} |
327 | } | 339 | } |
328 | } | 340 | } |
329 | "#, | 341 | "#, |
330 | ) | 342 | ) |
331 | } | 343 | } |
332 | 344 | ||
@@ -335,20 +347,20 @@ mod tests { | |||
335 | check_assist( | 347 | check_assist( |
336 | fill_match_arms, | 348 | fill_match_arms, |
337 | r#" | 349 | r#" |
338 | fn foo(a: bool) { | 350 | fn foo(a: bool) { |
339 | match a$0 { | 351 | match a$0 { |
340 | true => {} | 352 | true => {} |
341 | } | 353 | } |
342 | } | 354 | } |
343 | "#, | 355 | "#, |
344 | r#" | 356 | r#" |
345 | fn foo(a: bool) { | 357 | fn foo(a: bool) { |
346 | match a { | 358 | match a { |
347 | true => {} | 359 | true => {} |
348 | $0false => {} | 360 | $0false => {} |
349 | } | 361 | } |
350 | } | 362 | } |
351 | "#, | 363 | "#, |
352 | ) | 364 | ) |
353 | } | 365 | } |
354 | 366 | ||
@@ -357,15 +369,15 @@ mod tests { | |||
357 | check_assist_not_applicable( | 369 | check_assist_not_applicable( |
358 | fill_match_arms, | 370 | fill_match_arms, |
359 | r#" | 371 | r#" |
360 | fn foo(a: bool) { | 372 | fn foo(a: bool) { |
361 | match (a, a)$0 { | 373 | match (a, a)$0 { |
362 | (true, true) => {} | 374 | (true, true) => {} |
363 | (true, false) => {} | 375 | (true, false) => {} |
364 | (false, true) => {} | 376 | (false, true) => {} |
365 | (false, false) => {} | 377 | (false, false) => {} |
366 | } | 378 | } |
367 | } | 379 | } |
368 | "#, | 380 | "#, |
369 | ) | 381 | ) |
370 | } | 382 | } |
371 | 383 | ||
@@ -374,21 +386,21 @@ mod tests { | |||
374 | check_assist( | 386 | check_assist( |
375 | fill_match_arms, | 387 | fill_match_arms, |
376 | r#" | 388 | r#" |
377 | fn foo(a: bool) { | 389 | fn foo(a: bool) { |
378 | match (a, a)$0 { | 390 | match (a, a)$0 { |
379 | } | 391 | } |
380 | } | 392 | } |
381 | "#, | 393 | "#, |
382 | r#" | 394 | r#" |
383 | fn foo(a: bool) { | 395 | fn foo(a: bool) { |
384 | match (a, a) { | 396 | match (a, a) { |
385 | $0(true, true) => {} | 397 | $0(true, true) => {} |
386 | (true, false) => {} | 398 | (true, false) => {} |
387 | (false, true) => {} | 399 | (false, true) => {} |
388 | (false, false) => {} | 400 | (false, false) => {} |
389 | } | 401 | } |
390 | } | 402 | } |
391 | "#, | 403 | "#, |
392 | ) | 404 | ) |
393 | } | 405 | } |
394 | 406 | ||
@@ -397,22 +409,22 @@ mod tests { | |||
397 | check_assist( | 409 | check_assist( |
398 | fill_match_arms, | 410 | fill_match_arms, |
399 | r#" | 411 | r#" |
400 | fn foo(a: bool) { | 412 | fn foo(a: bool) { |
401 | match (a, a)$0 { | 413 | match (a, a)$0 { |
402 | (false, true) => {} | 414 | (false, true) => {} |
403 | } | 415 | } |
404 | } | 416 | } |
405 | "#, | 417 | "#, |
406 | r#" | 418 | r#" |
407 | fn foo(a: bool) { | 419 | fn foo(a: bool) { |
408 | match (a, a) { | 420 | match (a, a) { |
409 | (false, true) => {} | 421 | (false, true) => {} |
410 | $0(true, true) => {} | 422 | $0(true, true) => {} |
411 | (true, false) => {} | 423 | (true, false) => {} |
412 | (false, false) => {} | 424 | (false, false) => {} |
413 | } | 425 | } |
414 | } | 426 | } |
415 | "#, | 427 | "#, |
416 | ) | 428 | ) |
417 | } | 429 | } |
418 | 430 | ||
@@ -421,32 +433,32 @@ mod tests { | |||
421 | check_assist( | 433 | check_assist( |
422 | fill_match_arms, | 434 | fill_match_arms, |
423 | r#" | 435 | r#" |
424 | enum A { | 436 | enum A { |
425 | As, | 437 | As, |
426 | Bs { x: i32, y: Option<i32> }, | 438 | Bs { x: i32, y: Option<i32> }, |
427 | Cs(i32, Option<i32>), | 439 | Cs(i32, Option<i32>), |
428 | } | 440 | } |
429 | fn main() { | 441 | fn main() { |
430 | match A::As$0 { | 442 | match A::As$0 { |
431 | A::Bs { x, y: Some(_) } => {} | 443 | A::Bs { x, y: Some(_) } => {} |
432 | A::Cs(_, Some(_)) => {} | 444 | A::Cs(_, Some(_)) => {} |
433 | } | 445 | } |
434 | } | 446 | } |
435 | "#, | 447 | "#, |
436 | r#" | 448 | r#" |
437 | enum A { | 449 | enum A { |
438 | As, | 450 | As, |
439 | Bs { x: i32, y: Option<i32> }, | 451 | Bs { x: i32, y: Option<i32> }, |
440 | Cs(i32, Option<i32>), | 452 | Cs(i32, Option<i32>), |
441 | } | 453 | } |
442 | fn main() { | 454 | fn main() { |
443 | match A::As { | 455 | match A::As { |
444 | A::Bs { x, y: Some(_) } => {} | 456 | A::Bs { x, y: Some(_) } => {} |
445 | A::Cs(_, Some(_)) => {} | 457 | A::Cs(_, Some(_)) => {} |
446 | $0A::As => {} | 458 | $0A::As => {} |
447 | } | 459 | } |
448 | } | 460 | } |
449 | "#, | 461 | "#, |
450 | ); | 462 | ); |
451 | } | 463 | } |
452 | 464 | ||
@@ -593,30 +605,30 @@ fn main() { | |||
593 | check_assist( | 605 | check_assist( |
594 | fill_match_arms, | 606 | fill_match_arms, |
595 | r#" | 607 | r#" |
596 | enum A { One, Two } | 608 | enum A { One, Two } |
597 | enum B { One, Two } | 609 | enum B { One, Two } |
598 | 610 | ||
599 | fn main() { | 611 | fn main() { |
600 | let a = A::One; | 612 | let a = A::One; |
601 | let b = B::One; | 613 | let b = B::One; |
602 | match (a$0, b) {} | 614 | match (a$0, b) {} |
603 | } | 615 | } |
604 | "#, | 616 | "#, |
605 | r#" | 617 | r#" |
606 | enum A { One, Two } | 618 | enum A { One, Two } |
607 | enum B { One, Two } | 619 | enum B { One, Two } |
608 | 620 | ||
609 | fn main() { | 621 | fn main() { |
610 | let a = A::One; | 622 | let a = A::One; |
611 | let b = B::One; | 623 | let b = B::One; |
612 | match (a, b) { | 624 | match (a, b) { |
613 | $0(A::One, B::One) => {} | 625 | $0(A::One, B::One) => {} |
614 | (A::One, B::Two) => {} | 626 | (A::One, B::Two) => {} |
615 | (A::Two, B::One) => {} | 627 | (A::Two, B::One) => {} |
616 | (A::Two, B::Two) => {} | 628 | (A::Two, B::Two) => {} |
617 | } | 629 | } |
618 | } | 630 | } |
619 | "#, | 631 | "#, |
620 | ); | 632 | ); |
621 | } | 633 | } |
622 | 634 | ||
@@ -625,30 +637,30 @@ fn main() { | |||
625 | check_assist( | 637 | check_assist( |
626 | fill_match_arms, | 638 | fill_match_arms, |
627 | r#" | 639 | r#" |
628 | enum A { One, Two } | 640 | enum A { One, Two } |
629 | enum B { One, Two } | 641 | enum B { One, Two } |
630 | 642 | ||
631 | fn main() { | 643 | fn main() { |
632 | let a = A::One; | 644 | let a = A::One; |
633 | let b = B::One; | 645 | let b = B::One; |
634 | match (&a$0, &b) {} | 646 | match (&a$0, &b) {} |
635 | } | 647 | } |
636 | "#, | 648 | "#, |
637 | r#" | 649 | r#" |
638 | enum A { One, Two } | 650 | enum A { One, Two } |
639 | enum B { One, Two } | 651 | enum B { One, Two } |
640 | 652 | ||
641 | fn main() { | 653 | fn main() { |
642 | let a = A::One; | 654 | let a = A::One; |
643 | let b = B::One; | 655 | let b = B::One; |
644 | match (&a, &b) { | 656 | match (&a, &b) { |
645 | $0(A::One, B::One) => {} | 657 | $0(A::One, B::One) => {} |
646 | (A::One, B::Two) => {} | 658 | (A::One, B::Two) => {} |
647 | (A::Two, B::One) => {} | 659 | (A::Two, B::One) => {} |
648 | (A::Two, B::Two) => {} | 660 | (A::Two, B::Two) => {} |
649 | } | 661 | } |
650 | } | 662 | } |
651 | "#, | 663 | "#, |
652 | ); | 664 | ); |
653 | } | 665 | } |
654 | 666 | ||
@@ -737,20 +749,20 @@ fn main() { | |||
737 | check_assist_not_applicable( | 749 | check_assist_not_applicable( |
738 | fill_match_arms, | 750 | fill_match_arms, |
739 | r#" | 751 | r#" |
740 | enum A { One, Two } | 752 | enum A { One, Two } |
741 | enum B { One, Two } | 753 | enum B { One, Two } |
742 | 754 | ||
743 | fn main() { | 755 | fn main() { |
744 | let a = A::One; | 756 | let a = A::One; |
745 | let b = B::One; | 757 | let b = B::One; |
746 | match (a$0, b) { | 758 | match (a$0, b) { |
747 | (A::Two, B::One) => {} | 759 | (A::Two, B::One) => {} |
748 | (A::One, B::One) => {} | 760 | (A::One, B::One) => {} |
749 | (A::One, B::Two) => {} | 761 | (A::One, B::Two) => {} |
750 | (A::Two, B::Two) => {} | 762 | (A::Two, B::Two) => {} |
751 | } | 763 | } |
752 | } | 764 | } |
753 | "#, | 765 | "#, |
754 | ); | 766 | ); |
755 | } | 767 | } |
756 | 768 | ||
@@ -759,25 +771,25 @@ fn main() { | |||
759 | check_assist( | 771 | check_assist( |
760 | fill_match_arms, | 772 | fill_match_arms, |
761 | r#" | 773 | r#" |
762 | enum A { One, Two } | 774 | enum A { One, Two } |
763 | 775 | ||
764 | fn main() { | 776 | fn main() { |
765 | let a = A::One; | 777 | let a = A::One; |
766 | match (a$0, ) { | 778 | match (a$0, ) { |
767 | } | 779 | } |
768 | } | 780 | } |
769 | "#, | 781 | "#, |
770 | r#" | 782 | r#" |
771 | enum A { One, Two } | 783 | enum A { One, Two } |
772 | 784 | ||
773 | fn main() { | 785 | fn main() { |
774 | let a = A::One; | 786 | let a = A::One; |
775 | match (a, ) { | 787 | match (a, ) { |
776 | $0(A::One,) => {} | 788 | $0(A::One,) => {} |
777 | (A::Two,) => {} | 789 | (A::Two,) => {} |
778 | } | 790 | } |
779 | } | 791 | } |
780 | "#, | 792 | "#, |
781 | ); | 793 | ); |
782 | } | 794 | } |
783 | 795 | ||
@@ -786,47 +798,47 @@ fn main() { | |||
786 | check_assist( | 798 | check_assist( |
787 | fill_match_arms, | 799 | fill_match_arms, |
788 | r#" | 800 | r#" |
789 | enum A { As } | 801 | enum A { As } |
790 | 802 | ||
791 | fn foo(a: &A) { | 803 | fn foo(a: &A) { |
792 | match a$0 { | 804 | match a$0 { |
793 | } | 805 | } |
794 | } | 806 | } |
795 | "#, | 807 | "#, |
796 | r#" | 808 | r#" |
797 | enum A { As } | 809 | enum A { As } |
798 | 810 | ||
799 | fn foo(a: &A) { | 811 | fn foo(a: &A) { |
800 | match a { | 812 | match a { |
801 | $0A::As => {} | 813 | $0A::As => {} |
802 | } | 814 | } |
803 | } | 815 | } |
804 | "#, | 816 | "#, |
805 | ); | 817 | ); |
806 | 818 | ||
807 | check_assist( | 819 | check_assist( |
808 | fill_match_arms, | 820 | fill_match_arms, |
809 | r#" | 821 | r#" |
810 | enum A { | 822 | enum A { |
811 | Es { x: usize, y: usize } | 823 | Es { x: usize, y: usize } |
812 | } | 824 | } |
813 | 825 | ||
814 | fn foo(a: &mut A) { | 826 | fn foo(a: &mut A) { |
815 | match a$0 { | 827 | match a$0 { |
816 | } | 828 | } |
817 | } | 829 | } |
818 | "#, | 830 | "#, |
819 | r#" | 831 | r#" |
820 | enum A { | 832 | enum A { |
821 | Es { x: usize, y: usize } | 833 | Es { x: usize, y: usize } |
822 | } | 834 | } |
823 | 835 | ||
824 | fn foo(a: &mut A) { | 836 | fn foo(a: &mut A) { |
825 | match a { | 837 | match a { |
826 | $0A::Es { x, y } => {} | 838 | $0A::Es { x, y } => {} |
827 | } | 839 | } |
828 | } | 840 | } |
829 | "#, | 841 | "#, |
830 | ); | 842 | ); |
831 | } | 843 | } |
832 | 844 | ||
@@ -835,12 +847,12 @@ fn main() { | |||
835 | check_assist_target( | 847 | check_assist_target( |
836 | fill_match_arms, | 848 | fill_match_arms, |
837 | r#" | 849 | r#" |
838 | enum E { X, Y } | 850 | enum E { X, Y } |
839 | 851 | ||
840 | fn main() { | 852 | fn main() { |
841 | match E::X$0 {} | 853 | match E::X$0 {} |
842 | } | 854 | } |
843 | "#, | 855 | "#, |
844 | "match E::X {}", | 856 | "match E::X {}", |
845 | ); | 857 | ); |
846 | } | 858 | } |
@@ -850,24 +862,24 @@ fn main() { | |||
850 | check_assist( | 862 | check_assist( |
851 | fill_match_arms, | 863 | fill_match_arms, |
852 | r#" | 864 | r#" |
853 | enum E { X, Y } | 865 | enum E { X, Y } |
854 | 866 | ||
855 | fn main() { | 867 | fn main() { |
856 | match E::X { | 868 | match E::X { |
857 | $0_ => {} | 869 | $0_ => {} |
858 | } | 870 | } |
859 | } | 871 | } |
860 | "#, | 872 | "#, |
861 | r#" | 873 | r#" |
862 | enum E { X, Y } | 874 | enum E { X, Y } |
863 | 875 | ||
864 | fn main() { | 876 | fn main() { |
865 | match E::X { | 877 | match E::X { |
866 | $0E::X => {} | 878 | $0E::X => {} |
867 | E::Y => {} | 879 | E::Y => {} |
868 | } | 880 | } |
869 | } | 881 | } |
870 | "#, | 882 | "#, |
871 | ); | 883 | ); |
872 | } | 884 | } |
873 | 885 | ||
@@ -876,26 +888,26 @@ fn main() { | |||
876 | check_assist( | 888 | check_assist( |
877 | fill_match_arms, | 889 | fill_match_arms, |
878 | r#" | 890 | r#" |
879 | mod foo { pub enum E { X, Y } } | 891 | mod foo { pub enum E { X, Y } } |
880 | use foo::E::X; | 892 | use foo::E::X; |
881 | 893 | ||
882 | fn main() { | 894 | fn main() { |
883 | match X { | 895 | match X { |
884 | $0 | 896 | $0 |
885 | } | 897 | } |
886 | } | 898 | } |
887 | "#, | 899 | "#, |
888 | r#" | 900 | r#" |
889 | mod foo { pub enum E { X, Y } } | 901 | mod foo { pub enum E { X, Y } } |
890 | use foo::E::X; | 902 | use foo::E::X; |
891 | 903 | ||
892 | fn main() { | 904 | fn main() { |
893 | match X { | 905 | match X { |
894 | $0X => {} | 906 | $0X => {} |
895 | foo::E::Y => {} | 907 | foo::E::Y => {} |
896 | } | 908 | } |
897 | } | 909 | } |
898 | "#, | 910 | "#, |
899 | ); | 911 | ); |
900 | } | 912 | } |
901 | 913 | ||
@@ -904,26 +916,26 @@ fn main() { | |||
904 | check_assist( | 916 | check_assist( |
905 | fill_match_arms, | 917 | fill_match_arms, |
906 | r#" | 918 | r#" |
907 | enum A { One, Two } | 919 | enum A { One, Two } |
908 | fn foo(a: A) { | 920 | fn foo(a: A) { |
909 | match a { | 921 | match a { |
910 | // foo bar baz$0 | 922 | // foo bar baz$0 |
911 | A::One => {} | 923 | A::One => {} |
912 | // This is where the rest should be | 924 | // This is where the rest should be |
913 | } | 925 | } |
914 | } | 926 | } |
915 | "#, | 927 | "#, |
916 | r#" | 928 | r#" |
917 | enum A { One, Two } | 929 | enum A { One, Two } |
918 | fn foo(a: A) { | 930 | fn foo(a: A) { |
919 | match a { | 931 | match a { |
920 | // foo bar baz | 932 | // foo bar baz |
921 | A::One => {} | 933 | A::One => {} |
922 | // This is where the rest should be | 934 | $0A::Two => {} |
923 | $0A::Two => {} | 935 | // This is where the rest should be |
924 | } | 936 | } |
925 | } | 937 | } |
926 | "#, | 938 | "#, |
927 | ); | 939 | ); |
928 | } | 940 | } |
929 | 941 | ||
@@ -932,23 +944,23 @@ fn main() { | |||
932 | check_assist( | 944 | check_assist( |
933 | fill_match_arms, | 945 | fill_match_arms, |
934 | r#" | 946 | r#" |
935 | enum A { One, Two } | 947 | enum A { One, Two } |
936 | fn foo(a: A) { | 948 | fn foo(a: A) { |
937 | match a { | 949 | match a { |
938 | // foo bar baz$0 | 950 | // foo bar baz$0 |
939 | } | 951 | } |
940 | } | 952 | } |
941 | "#, | 953 | "#, |
942 | r#" | 954 | r#" |
943 | enum A { One, Two } | 955 | enum A { One, Two } |
944 | fn foo(a: A) { | 956 | fn foo(a: A) { |
945 | match a { | 957 | match a { |
946 | // foo bar baz | 958 | $0A::One => {} |
947 | $0A::One => {} | 959 | A::Two => {} |
948 | A::Two => {} | 960 | // foo bar baz |
949 | } | 961 | } |
950 | } | 962 | } |
951 | "#, | 963 | "#, |
952 | ); | 964 | ); |
953 | } | 965 | } |
954 | 966 | ||
@@ -957,22 +969,22 @@ fn main() { | |||
957 | check_assist( | 969 | check_assist( |
958 | fill_match_arms, | 970 | fill_match_arms, |
959 | r#" | 971 | r#" |
960 | enum A { One, Two, } | 972 | enum A { One, Two, } |
961 | fn foo(a: A) { | 973 | fn foo(a: A) { |
962 | match a$0 { | 974 | match a$0 { |
963 | _ => (), | 975 | _ => (), |
964 | } | 976 | } |
965 | } | 977 | } |
966 | "#, | 978 | "#, |
967 | r#" | 979 | r#" |
968 | enum A { One, Two, } | 980 | enum A { One, Two, } |
969 | fn foo(a: A) { | 981 | fn foo(a: A) { |
970 | match a { | 982 | match a { |
971 | $0A::One => {} | 983 | $0A::One => {} |
972 | A::Two => {} | 984 | A::Two => {} |
973 | } | 985 | } |
974 | } | 986 | } |
975 | "#, | 987 | "#, |
976 | ); | 988 | ); |
977 | } | 989 | } |
978 | 990 | ||
@@ -1016,7 +1028,8 @@ enum Test { | |||
1016 | fn foo(t: Test) { | 1028 | fn foo(t: Test) { |
1017 | m!(match t$0 {}); | 1029 | m!(match t$0 {}); |
1018 | }"#, | 1030 | }"#, |
1019 | r#"macro_rules! m { ($expr:expr) => {$expr}} | 1031 | r#" |
1032 | macro_rules! m { ($expr:expr) => {$expr}} | ||
1020 | enum Test { | 1033 | enum Test { |
1021 | A, | 1034 | A, |
1022 | B, | 1035 | B, |
diff --git a/crates/ide_assists/src/handlers/generate_default_from_new.rs b/crates/ide_assists/src/handlers/generate_default_from_new.rs index dc14552d6..bad826366 100644 --- a/crates/ide_assists/src/handlers/generate_default_from_new.rs +++ b/crates/ide_assists/src/handlers/generate_default_from_new.rs | |||
@@ -3,8 +3,10 @@ use crate::{ | |||
3 | AssistId, | 3 | AssistId, |
4 | }; | 4 | }; |
5 | use ide_db::helpers::FamousDefs; | 5 | use ide_db::helpers::FamousDefs; |
6 | use itertools::Itertools; | ||
7 | use stdx::format_to; | ||
6 | use syntax::{ | 8 | use syntax::{ |
7 | ast::{self, Impl, NameOwner}, | 9 | ast::{self, GenericParamsOwner, Impl, NameOwner, TypeBoundsOwner}, |
8 | AstNode, | 10 | AstNode, |
9 | }; | 11 | }; |
10 | 12 | ||
@@ -65,23 +67,56 @@ pub(crate) fn generate_default_from_new(acc: &mut Assists, ctx: &AssistContext) | |||
65 | "Generate a Default impl from a new fn", | 67 | "Generate a Default impl from a new fn", |
66 | insert_location, | 68 | insert_location, |
67 | move |builder| { | 69 | move |builder| { |
68 | let code = default_fn_node_for_new(impl_); | 70 | let default_code = " fn default() -> Self { |
71 | Self::new() | ||
72 | }"; | ||
73 | let code = generate_trait_impl_text_from_impl(&impl_, "Default", default_code); | ||
69 | builder.insert(insert_location.end(), code); | 74 | builder.insert(insert_location.end(), code); |
70 | }, | 75 | }, |
71 | ) | 76 | ) |
72 | } | 77 | } |
73 | 78 | ||
74 | fn default_fn_node_for_new(impl_: Impl) -> String { | 79 | fn generate_trait_impl_text_from_impl(impl_: &ast::Impl, trait_text: &str, code: &str) -> String { |
75 | format!( | 80 | let generic_params = impl_.generic_param_list(); |
76 | " | 81 | let mut buf = String::with_capacity(code.len()); |
82 | buf.push_str("\n\n"); | ||
83 | buf.push_str("impl"); | ||
84 | |||
85 | if let Some(generic_params) = &generic_params { | ||
86 | let lifetimes = generic_params.lifetime_params().map(|lt| format!("{}", lt.syntax())); | ||
87 | let type_params = generic_params.type_params().map(|type_param| { | ||
88 | let mut buf = String::new(); | ||
89 | if let Some(it) = type_param.name() { | ||
90 | format_to!(buf, "{}", it.syntax()); | ||
91 | } | ||
92 | if let Some(it) = type_param.colon_token() { | ||
93 | format_to!(buf, "{} ", it); | ||
94 | } | ||
95 | if let Some(it) = type_param.type_bound_list() { | ||
96 | format_to!(buf, "{}", it.syntax()); | ||
97 | } | ||
98 | buf | ||
99 | }); | ||
100 | let const_params = generic_params.const_params().map(|t| t.syntax().to_string()); | ||
101 | let generics = lifetimes.chain(type_params).chain(const_params).format(", "); | ||
102 | format_to!(buf, "<{}>", generics); | ||
103 | } | ||
104 | |||
105 | buf.push(' '); | ||
106 | buf.push_str(trait_text); | ||
107 | buf.push_str(" for "); | ||
108 | buf.push_str(&impl_.self_ty().unwrap().syntax().text().to_string()); | ||
109 | |||
110 | match impl_.where_clause() { | ||
111 | Some(where_clause) => { | ||
112 | format_to!(buf, "\n{}\n{{\n{}\n}}", where_clause, code); | ||
113 | } | ||
114 | None => { | ||
115 | format_to!(buf, " {{\n{}\n}}", code); | ||
116 | } | ||
117 | } | ||
77 | 118 | ||
78 | impl Default for {} {{ | 119 | buf |
79 | fn default() -> Self {{ | ||
80 | Self::new() | ||
81 | }} | ||
82 | }}", | ||
83 | impl_.self_ty().unwrap().syntax().text() | ||
84 | ) | ||
85 | } | 120 | } |
86 | 121 | ||
87 | fn is_default_implemented(ctx: &AssistContext, impl_: &Impl) -> bool { | 122 | fn is_default_implemented(ctx: &AssistContext, impl_: &Impl) -> bool { |
@@ -176,6 +211,234 @@ impl Default for Test { | |||
176 | } | 211 | } |
177 | 212 | ||
178 | #[test] | 213 | #[test] |
214 | fn new_function_with_generic() { | ||
215 | check_pass( | ||
216 | r#" | ||
217 | pub struct Foo<T> { | ||
218 | _bar: *mut T, | ||
219 | } | ||
220 | |||
221 | impl<T> Foo<T> { | ||
222 | pub fn ne$0w() -> Self { | ||
223 | unimplemented!() | ||
224 | } | ||
225 | } | ||
226 | "#, | ||
227 | r#" | ||
228 | pub struct Foo<T> { | ||
229 | _bar: *mut T, | ||
230 | } | ||
231 | |||
232 | impl<T> Foo<T> { | ||
233 | pub fn new() -> Self { | ||
234 | unimplemented!() | ||
235 | } | ||
236 | } | ||
237 | |||
238 | impl<T> Default for Foo<T> { | ||
239 | fn default() -> Self { | ||
240 | Self::new() | ||
241 | } | ||
242 | } | ||
243 | "#, | ||
244 | ); | ||
245 | } | ||
246 | |||
247 | #[test] | ||
248 | fn new_function_with_generics() { | ||
249 | check_pass( | ||
250 | r#" | ||
251 | pub struct Foo<T, B> { | ||
252 | _tars: *mut T, | ||
253 | _bar: *mut B, | ||
254 | } | ||
255 | |||
256 | impl<T, B> Foo<T, B> { | ||
257 | pub fn ne$0w() -> Self { | ||
258 | unimplemented!() | ||
259 | } | ||
260 | } | ||
261 | "#, | ||
262 | r#" | ||
263 | pub struct Foo<T, B> { | ||
264 | _tars: *mut T, | ||
265 | _bar: *mut B, | ||
266 | } | ||
267 | |||
268 | impl<T, B> Foo<T, B> { | ||
269 | pub fn new() -> Self { | ||
270 | unimplemented!() | ||
271 | } | ||
272 | } | ||
273 | |||
274 | impl<T, B> Default for Foo<T, B> { | ||
275 | fn default() -> Self { | ||
276 | Self::new() | ||
277 | } | ||
278 | } | ||
279 | "#, | ||
280 | ); | ||
281 | } | ||
282 | |||
283 | #[test] | ||
284 | fn new_function_with_generic_and_bound() { | ||
285 | check_pass( | ||
286 | r#" | ||
287 | pub struct Foo<T> { | ||
288 | t: T, | ||
289 | } | ||
290 | |||
291 | impl<T: From<i32>> Foo<T> { | ||
292 | pub fn ne$0w() -> Self { | ||
293 | Foo { t: 0.into() } | ||
294 | } | ||
295 | } | ||
296 | "#, | ||
297 | r#" | ||
298 | pub struct Foo<T> { | ||
299 | t: T, | ||
300 | } | ||
301 | |||
302 | impl<T: From<i32>> Foo<T> { | ||
303 | pub fn new() -> Self { | ||
304 | Foo { t: 0.into() } | ||
305 | } | ||
306 | } | ||
307 | |||
308 | impl<T: From<i32>> Default for Foo<T> { | ||
309 | fn default() -> Self { | ||
310 | Self::new() | ||
311 | } | ||
312 | } | ||
313 | "#, | ||
314 | ); | ||
315 | } | ||
316 | |||
317 | #[test] | ||
318 | fn new_function_with_generics_and_bounds() { | ||
319 | check_pass( | ||
320 | r#" | ||
321 | pub struct Foo<T, B> { | ||
322 | _tars: T, | ||
323 | _bar: B, | ||
324 | } | ||
325 | |||
326 | impl<T: From<i32>, B: From<i64>> Foo<T, B> { | ||
327 | pub fn ne$0w() -> Self { | ||
328 | unimplemented!() | ||
329 | } | ||
330 | } | ||
331 | "#, | ||
332 | r#" | ||
333 | pub struct Foo<T, B> { | ||
334 | _tars: T, | ||
335 | _bar: B, | ||
336 | } | ||
337 | |||
338 | impl<T: From<i32>, B: From<i64>> Foo<T, B> { | ||
339 | pub fn new() -> Self { | ||
340 | unimplemented!() | ||
341 | } | ||
342 | } | ||
343 | |||
344 | impl<T: From<i32>, B: From<i64>> Default for Foo<T, B> { | ||
345 | fn default() -> Self { | ||
346 | Self::new() | ||
347 | } | ||
348 | } | ||
349 | "#, | ||
350 | ); | ||
351 | } | ||
352 | |||
353 | #[test] | ||
354 | fn new_function_with_generic_and_where() { | ||
355 | check_pass( | ||
356 | r#" | ||
357 | pub struct Foo<T> { | ||
358 | t: T, | ||
359 | } | ||
360 | |||
361 | impl<T: From<i32>> Foo<T> | ||
362 | where | ||
363 | Option<T>: Debug | ||
364 | { | ||
365 | pub fn ne$0w() -> Self { | ||
366 | Foo { t: 0.into() } | ||
367 | } | ||
368 | } | ||
369 | "#, | ||
370 | r#" | ||
371 | pub struct Foo<T> { | ||
372 | t: T, | ||
373 | } | ||
374 | |||
375 | impl<T: From<i32>> Foo<T> | ||
376 | where | ||
377 | Option<T>: Debug | ||
378 | { | ||
379 | pub fn new() -> Self { | ||
380 | Foo { t: 0.into() } | ||
381 | } | ||
382 | } | ||
383 | |||
384 | impl<T: From<i32>> Default for Foo<T> | ||
385 | where | ||
386 | Option<T>: Debug | ||
387 | { | ||
388 | fn default() -> Self { | ||
389 | Self::new() | ||
390 | } | ||
391 | } | ||
392 | "#, | ||
393 | ); | ||
394 | } | ||
395 | |||
396 | #[test] | ||
397 | fn new_function_with_generics_and_wheres() { | ||
398 | check_pass( | ||
399 | r#" | ||
400 | pub struct Foo<T, B> { | ||
401 | _tars: T, | ||
402 | _bar: B, | ||
403 | } | ||
404 | |||
405 | impl<T: From<i32>, B: From<i64>> Foo<T, B> | ||
406 | where | ||
407 | Option<T>: Debug, Option<B>: Debug, | ||
408 | { | ||
409 | pub fn ne$0w() -> Self { | ||
410 | unimplemented!() | ||
411 | } | ||
412 | } | ||
413 | "#, | ||
414 | r#" | ||
415 | pub struct Foo<T, B> { | ||
416 | _tars: T, | ||
417 | _bar: B, | ||
418 | } | ||
419 | |||
420 | impl<T: From<i32>, B: From<i64>> Foo<T, B> | ||
421 | where | ||
422 | Option<T>: Debug, Option<B>: Debug, | ||
423 | { | ||
424 | pub fn new() -> Self { | ||
425 | unimplemented!() | ||
426 | } | ||
427 | } | ||
428 | |||
429 | impl<T: From<i32>, B: From<i64>> Default for Foo<T, B> | ||
430 | where | ||
431 | Option<T>: Debug, Option<B>: Debug, | ||
432 | { | ||
433 | fn default() -> Self { | ||
434 | Self::new() | ||
435 | } | ||
436 | } | ||
437 | "#, | ||
438 | ); | ||
439 | } | ||
440 | |||
441 | #[test] | ||
179 | fn new_function_with_parameters() { | 442 | fn new_function_with_parameters() { |
180 | cov_mark::check!(new_function_with_parameters); | 443 | cov_mark::check!(new_function_with_parameters); |
181 | check_not_applicable( | 444 | check_not_applicable( |
diff --git a/crates/ide_assists/src/handlers/generate_function.rs b/crates/ide_assists/src/handlers/generate_function.rs index 6f95b1a07..bc9fc524b 100644 --- a/crates/ide_assists/src/handlers/generate_function.rs +++ b/crates/ide_assists/src/handlers/generate_function.rs | |||
@@ -175,7 +175,7 @@ impl FunctionBuilder { | |||
175 | } | 175 | } |
176 | 176 | ||
177 | fn render(self) -> FunctionTemplate { | 177 | fn render(self) -> FunctionTemplate { |
178 | let placeholder_expr = make::expr_todo(); | 178 | let placeholder_expr = make::ext::expr_todo(); |
179 | let fn_body = make::block_expr(vec![], Some(placeholder_expr)); | 179 | let fn_body = make::block_expr(vec![], Some(placeholder_expr)); |
180 | let visibility = if self.needs_pub { Some(make::visibility_pub_crate()) } else { None }; | 180 | let visibility = if self.needs_pub { Some(make::visibility_pub_crate()) } else { None }; |
181 | let mut fn_def = make::fn_( | 181 | let mut fn_def = make::fn_( |
diff --git a/crates/ide_assists/src/handlers/generate_new.rs b/crates/ide_assists/src/handlers/generate_new.rs index 8ce5930b7..959a1f86c 100644 --- a/crates/ide_assists/src/handlers/generate_new.rs +++ b/crates/ide_assists/src/handlers/generate_new.rs | |||
@@ -1,4 +1,3 @@ | |||
1 | use ast::Adt; | ||
2 | use itertools::Itertools; | 1 | use itertools::Itertools; |
3 | use stdx::format_to; | 2 | use stdx::format_to; |
4 | use syntax::ast::{self, AstNode, NameOwner, StructKind, VisibilityOwner}; | 3 | use syntax::ast::{self, AstNode, NameOwner, StructKind, VisibilityOwner}; |
@@ -37,7 +36,7 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
37 | }; | 36 | }; |
38 | 37 | ||
39 | // Return early if we've found an existing new fn | 38 | // Return early if we've found an existing new fn |
40 | let impl_def = find_struct_impl(&ctx, &Adt::Struct(strukt.clone()), "new")?; | 39 | let impl_def = find_struct_impl(&ctx, &ast::Adt::Struct(strukt.clone()), "new")?; |
41 | 40 | ||
42 | let target = strukt.syntax().text_range(); | 41 | let target = strukt.syntax().text_range(); |
43 | acc.add(AssistId("generate_new", AssistKind::Generate), "Generate `new`", target, |builder| { | 42 | acc.add(AssistId("generate_new", AssistKind::Generate), "Generate `new`", target, |builder| { |
@@ -60,7 +59,7 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
60 | let start_offset = impl_def | 59 | let start_offset = impl_def |
61 | .and_then(|impl_def| find_impl_block_start(impl_def, &mut buf)) | 60 | .and_then(|impl_def| find_impl_block_start(impl_def, &mut buf)) |
62 | .unwrap_or_else(|| { | 61 | .unwrap_or_else(|| { |
63 | buf = generate_impl_text(&Adt::Struct(strukt.clone()), &buf); | 62 | buf = generate_impl_text(&ast::Adt::Struct(strukt.clone()), &buf); |
64 | strukt.syntax().text_range().end() | 63 | strukt.syntax().text_range().end() |
65 | }); | 64 | }); |
66 | 65 | ||
@@ -81,101 +80,132 @@ mod tests { | |||
81 | use super::*; | 80 | use super::*; |
82 | 81 | ||
83 | #[test] | 82 | #[test] |
84 | #[rustfmt::skip] | ||
85 | fn test_generate_new() { | 83 | fn test_generate_new() { |
86 | // Check output of generation | ||
87 | check_assist( | 84 | check_assist( |
88 | generate_new, | 85 | generate_new, |
89 | "struct Foo {$0}", | 86 | r#" |
90 | "struct Foo {} | 87 | struct Foo {$0} |
88 | "#, | ||
89 | r#" | ||
90 | struct Foo {} | ||
91 | 91 | ||
92 | impl Foo { | 92 | impl Foo { |
93 | fn $0new() -> Self { Self { } } | 93 | fn $0new() -> Self { Self { } } |
94 | }", | 94 | } |
95 | "#, | ||
95 | ); | 96 | ); |
96 | check_assist( | 97 | check_assist( |
97 | generate_new, | 98 | generate_new, |
98 | "struct Foo<T: Clone> {$0}", | 99 | r#" |
99 | "struct Foo<T: Clone> {} | 100 | struct Foo<T: Clone> {$0} |
101 | "#, | ||
102 | r#" | ||
103 | struct Foo<T: Clone> {} | ||
100 | 104 | ||
101 | impl<T: Clone> Foo<T> { | 105 | impl<T: Clone> Foo<T> { |
102 | fn $0new() -> Self { Self { } } | 106 | fn $0new() -> Self { Self { } } |
103 | }", | 107 | } |
108 | "#, | ||
104 | ); | 109 | ); |
105 | check_assist( | 110 | check_assist( |
106 | generate_new, | 111 | generate_new, |
107 | "struct Foo<'a, T: Foo<'a>> {$0}", | 112 | r#" |
108 | "struct Foo<'a, T: Foo<'a>> {} | 113 | struct Foo<'a, T: Foo<'a>> {$0} |
114 | "#, | ||
115 | r#" | ||
116 | struct Foo<'a, T: Foo<'a>> {} | ||
109 | 117 | ||
110 | impl<'a, T: Foo<'a>> Foo<'a, T> { | 118 | impl<'a, T: Foo<'a>> Foo<'a, T> { |
111 | fn $0new() -> Self { Self { } } | 119 | fn $0new() -> Self { Self { } } |
112 | }", | 120 | } |
121 | "#, | ||
113 | ); | 122 | ); |
114 | check_assist( | 123 | check_assist( |
115 | generate_new, | 124 | generate_new, |
116 | "struct Foo { baz: String $0}", | 125 | r#" |
117 | "struct Foo { baz: String } | 126 | struct Foo { baz: String $0} |
127 | "#, | ||
128 | r#" | ||
129 | struct Foo { baz: String } | ||
118 | 130 | ||
119 | impl Foo { | 131 | impl Foo { |
120 | fn $0new(baz: String) -> Self { Self { baz } } | 132 | fn $0new(baz: String) -> Self { Self { baz } } |
121 | }", | 133 | } |
134 | "#, | ||
122 | ); | 135 | ); |
123 | check_assist( | 136 | check_assist( |
124 | generate_new, | 137 | generate_new, |
125 | "struct Foo { baz: String, qux: Vec<i32> $0}", | 138 | r#" |
126 | "struct Foo { baz: String, qux: Vec<i32> } | 139 | struct Foo { baz: String, qux: Vec<i32> $0} |
140 | "#, | ||
141 | r#" | ||
142 | struct Foo { baz: String, qux: Vec<i32> } | ||
127 | 143 | ||
128 | impl Foo { | 144 | impl Foo { |
129 | fn $0new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } } | 145 | fn $0new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } } |
130 | }", | 146 | } |
147 | "#, | ||
131 | ); | 148 | ); |
149 | } | ||
132 | 150 | ||
133 | // Check that visibility modifiers don't get brought in for fields | 151 | #[test] |
152 | fn check_that_visibility_modifiers_dont_get_brought_in() { | ||
134 | check_assist( | 153 | check_assist( |
135 | generate_new, | 154 | generate_new, |
136 | "struct Foo { pub baz: String, pub qux: Vec<i32> $0}", | 155 | r#" |
137 | "struct Foo { pub baz: String, pub qux: Vec<i32> } | 156 | struct Foo { pub baz: String, pub qux: Vec<i32> $0} |
157 | "#, | ||
158 | r#" | ||
159 | struct Foo { pub baz: String, pub qux: Vec<i32> } | ||
138 | 160 | ||
139 | impl Foo { | 161 | impl Foo { |
140 | fn $0new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } } | 162 | fn $0new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } } |
141 | }", | 163 | } |
164 | "#, | ||
142 | ); | 165 | ); |
166 | } | ||
143 | 167 | ||
144 | // Check that it reuses existing impls | 168 | #[test] |
169 | fn check_it_reuses_existing_impls() { | ||
145 | check_assist( | 170 | check_assist( |
146 | generate_new, | 171 | generate_new, |
147 | "struct Foo {$0} | 172 | r#" |
173 | struct Foo {$0} | ||
148 | 174 | ||
149 | impl Foo {} | 175 | impl Foo {} |
150 | ", | 176 | "#, |
151 | "struct Foo {} | 177 | r#" |
178 | struct Foo {} | ||
152 | 179 | ||
153 | impl Foo { | 180 | impl Foo { |
154 | fn $0new() -> Self { Self { } } | 181 | fn $0new() -> Self { Self { } } |
155 | } | 182 | } |
156 | ", | 183 | "#, |
157 | ); | 184 | ); |
158 | check_assist( | 185 | check_assist( |
159 | generate_new, | 186 | generate_new, |
160 | "struct Foo {$0} | 187 | r#" |
188 | struct Foo {$0} | ||
161 | 189 | ||
162 | impl Foo { | 190 | impl Foo { |
163 | fn qux(&self) {} | 191 | fn qux(&self) {} |
164 | } | 192 | } |
165 | ", | 193 | "#, |
166 | "struct Foo {} | 194 | r#" |
195 | struct Foo {} | ||
167 | 196 | ||
168 | impl Foo { | 197 | impl Foo { |
169 | fn $0new() -> Self { Self { } } | 198 | fn $0new() -> Self { Self { } } |
170 | 199 | ||
171 | fn qux(&self) {} | 200 | fn qux(&self) {} |
172 | } | 201 | } |
173 | ", | 202 | "#, |
174 | ); | 203 | ); |
175 | 204 | ||
176 | check_assist( | 205 | check_assist( |
177 | generate_new, | 206 | generate_new, |
178 | "struct Foo {$0} | 207 | r#" |
208 | struct Foo {$0} | ||
179 | 209 | ||
180 | impl Foo { | 210 | impl Foo { |
181 | fn qux(&self) {} | 211 | fn qux(&self) {} |
@@ -183,8 +213,9 @@ impl Foo { | |||
183 | 5 | 213 | 5 |
184 | } | 214 | } |
185 | } | 215 | } |
186 | ", | 216 | "#, |
187 | "struct Foo {} | 217 | r#" |
218 | struct Foo {} | ||
188 | 219 | ||
189 | impl Foo { | 220 | impl Foo { |
190 | fn $0new() -> Self { Self { } } | 221 | fn $0new() -> Self { Self { } } |
@@ -194,27 +225,37 @@ impl Foo { | |||
194 | 5 | 225 | 5 |
195 | } | 226 | } |
196 | } | 227 | } |
197 | ", | 228 | "#, |
198 | ); | 229 | ); |
230 | } | ||
199 | 231 | ||
200 | // Check visibility of new fn based on struct | 232 | #[test] |
233 | fn check_visibility_of_new_fn_based_on_struct() { | ||
201 | check_assist( | 234 | check_assist( |
202 | generate_new, | 235 | generate_new, |
203 | "pub struct Foo {$0}", | 236 | r#" |
204 | "pub struct Foo {} | 237 | pub struct Foo {$0} |
238 | "#, | ||
239 | r#" | ||
240 | pub struct Foo {} | ||
205 | 241 | ||
206 | impl Foo { | 242 | impl Foo { |
207 | pub fn $0new() -> Self { Self { } } | 243 | pub fn $0new() -> Self { Self { } } |
208 | }", | 244 | } |
245 | "#, | ||
209 | ); | 246 | ); |
210 | check_assist( | 247 | check_assist( |
211 | generate_new, | 248 | generate_new, |
212 | "pub(crate) struct Foo {$0}", | 249 | r#" |
213 | "pub(crate) struct Foo {} | 250 | pub(crate) struct Foo {$0} |
251 | "#, | ||
252 | r#" | ||
253 | pub(crate) struct Foo {} | ||
214 | 254 | ||
215 | impl Foo { | 255 | impl Foo { |
216 | pub(crate) fn $0new() -> Self { Self { } } | 256 | pub(crate) fn $0new() -> Self { Self { } } |
217 | }", | 257 | } |
258 | "#, | ||
218 | ); | 259 | ); |
219 | } | 260 | } |
220 | 261 | ||
@@ -222,26 +263,28 @@ impl Foo { | |||
222 | fn generate_new_not_applicable_if_fn_exists() { | 263 | fn generate_new_not_applicable_if_fn_exists() { |
223 | check_assist_not_applicable( | 264 | check_assist_not_applicable( |
224 | generate_new, | 265 | generate_new, |
225 | " | 266 | r#" |
226 | struct Foo {$0} | 267 | struct Foo {$0} |
227 | 268 | ||
228 | impl Foo { | 269 | impl Foo { |
229 | fn new() -> Self { | 270 | fn new() -> Self { |
230 | Self | 271 | Self |
231 | } | 272 | } |
232 | }", | 273 | } |
274 | "#, | ||
233 | ); | 275 | ); |
234 | 276 | ||
235 | check_assist_not_applicable( | 277 | check_assist_not_applicable( |
236 | generate_new, | 278 | generate_new, |
237 | " | 279 | r#" |
238 | struct Foo {$0} | 280 | struct Foo {$0} |
239 | 281 | ||
240 | impl Foo { | 282 | impl Foo { |
241 | fn New() -> Self { | 283 | fn New() -> Self { |
242 | Self | 284 | Self |
243 | } | 285 | } |
244 | }", | 286 | } |
287 | "#, | ||
245 | ); | 288 | ); |
246 | } | 289 | } |
247 | 290 | ||
@@ -249,12 +292,12 @@ impl Foo { | |||
249 | fn generate_new_target() { | 292 | fn generate_new_target() { |
250 | check_assist_target( | 293 | check_assist_target( |
251 | generate_new, | 294 | generate_new, |
252 | " | 295 | r#" |
253 | struct SomeThingIrrelevant; | 296 | struct SomeThingIrrelevant; |
254 | /// Has a lifetime parameter | 297 | /// Has a lifetime parameter |
255 | struct Foo<'a, T: Foo<'a>> {$0} | 298 | struct Foo<'a, T: Foo<'a>> {$0} |
256 | struct EvenMoreIrrelevant; | 299 | struct EvenMoreIrrelevant; |
257 | ", | 300 | "#, |
258 | "/// Has a lifetime parameter | 301 | "/// Has a lifetime parameter |
259 | struct Foo<'a, T: Foo<'a>> {}", | 302 | struct Foo<'a, T: Foo<'a>> {}", |
260 | ); | 303 | ); |
@@ -264,7 +307,7 @@ struct Foo<'a, T: Foo<'a>> {}", | |||
264 | fn test_unrelated_new() { | 307 | fn test_unrelated_new() { |
265 | check_assist( | 308 | check_assist( |
266 | generate_new, | 309 | generate_new, |
267 | r##" | 310 | r#" |
268 | pub struct AstId<N: AstNode> { | 311 | pub struct AstId<N: AstNode> { |
269 | file_id: HirFileId, | 312 | file_id: HirFileId, |
270 | file_ast_id: FileAstId<N>, | 313 | file_ast_id: FileAstId<N>, |
@@ -285,8 +328,9 @@ impl<T> Source<T> { | |||
285 | pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> { | 328 | pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> { |
286 | Source { file_id: self.file_id, ast: f(self.ast) } | 329 | Source { file_id: self.file_id, ast: f(self.ast) } |
287 | } | 330 | } |
288 | }"##, | 331 | } |
289 | r##" | 332 | "#, |
333 | r#" | ||
290 | pub struct AstId<N: AstNode> { | 334 | pub struct AstId<N: AstNode> { |
291 | file_id: HirFileId, | 335 | file_id: HirFileId, |
292 | file_ast_id: FileAstId<N>, | 336 | file_ast_id: FileAstId<N>, |
@@ -309,7 +353,8 @@ impl<T> Source<T> { | |||
309 | pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> { | 353 | pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> { |
310 | Source { file_id: self.file_id, ast: f(self.ast) } | 354 | Source { file_id: self.file_id, ast: f(self.ast) } |
311 | } | 355 | } |
312 | }"##, | 356 | } |
357 | "#, | ||
313 | ); | 358 | ); |
314 | } | 359 | } |
315 | } | 360 | } |
diff --git a/crates/ide_assists/src/handlers/introduce_named_lifetime.rs b/crates/ide_assists/src/handlers/introduce_named_lifetime.rs index 9f4f71d6c..16f8f9d70 100644 --- a/crates/ide_assists/src/handlers/introduce_named_lifetime.rs +++ b/crates/ide_assists/src/handlers/introduce_named_lifetime.rs | |||
@@ -84,19 +84,17 @@ fn generate_fn_def_assist( | |||
84 | } | 84 | } |
85 | }; | 85 | }; |
86 | acc.add(AssistId(ASSIST_NAME, AssistKind::Refactor), ASSIST_LABEL, lifetime_loc, |builder| { | 86 | acc.add(AssistId(ASSIST_NAME, AssistKind::Refactor), ASSIST_LABEL, lifetime_loc, |builder| { |
87 | let fn_def = builder.make_ast_mut(fn_def); | 87 | let fn_def = builder.make_mut(fn_def); |
88 | let lifetime = builder.make_ast_mut(lifetime); | 88 | let lifetime = builder.make_mut(lifetime); |
89 | let loc_needing_lifetime = | 89 | let loc_needing_lifetime = |
90 | loc_needing_lifetime.and_then(|it| it.make_mut(builder).to_position()); | 90 | loc_needing_lifetime.and_then(|it| it.make_mut(builder).to_position()); |
91 | 91 | ||
92 | add_lifetime_param(fn_def.get_or_create_generic_param_list(), new_lifetime_param); | 92 | fn_def.get_or_create_generic_param_list().add_generic_param( |
93 | ted::replace( | 93 | make::lifetime_param(new_lifetime_param.clone()).clone_for_update().into(), |
94 | lifetime.syntax(), | ||
95 | make_ast_lifetime(new_lifetime_param).clone_for_update().syntax(), | ||
96 | ); | 94 | ); |
97 | loc_needing_lifetime.map(|position| { | 95 | ted::replace(lifetime.syntax(), new_lifetime_param.clone_for_update().syntax()); |
98 | ted::insert(position, make_ast_lifetime(new_lifetime_param).clone_for_update().syntax()) | 96 | loc_needing_lifetime |
99 | }); | 97 | .map(|position| ted::insert(position, new_lifetime_param.clone_for_update().syntax())); |
100 | }) | 98 | }) |
101 | } | 99 | } |
102 | 100 | ||
@@ -109,14 +107,13 @@ fn generate_impl_def_assist( | |||
109 | ) -> Option<()> { | 107 | ) -> Option<()> { |
110 | let new_lifetime_param = generate_unique_lifetime_param_name(impl_def.generic_param_list())?; | 108 | let new_lifetime_param = generate_unique_lifetime_param_name(impl_def.generic_param_list())?; |
111 | acc.add(AssistId(ASSIST_NAME, AssistKind::Refactor), ASSIST_LABEL, lifetime_loc, |builder| { | 109 | acc.add(AssistId(ASSIST_NAME, AssistKind::Refactor), ASSIST_LABEL, lifetime_loc, |builder| { |
112 | let impl_def = builder.make_ast_mut(impl_def); | 110 | let impl_def = builder.make_mut(impl_def); |
113 | let lifetime = builder.make_ast_mut(lifetime); | 111 | let lifetime = builder.make_mut(lifetime); |
114 | 112 | ||
115 | add_lifetime_param(impl_def.get_or_create_generic_param_list(), new_lifetime_param); | 113 | impl_def.get_or_create_generic_param_list().add_generic_param( |
116 | ted::replace( | 114 | make::lifetime_param(new_lifetime_param.clone()).clone_for_update().into(), |
117 | lifetime.syntax(), | ||
118 | make_ast_lifetime(new_lifetime_param).clone_for_update().syntax(), | ||
119 | ); | 115 | ); |
116 | ted::replace(lifetime.syntax(), new_lifetime_param.clone_for_update().syntax()); | ||
120 | }) | 117 | }) |
121 | } | 118 | } |
122 | 119 | ||
@@ -124,31 +121,16 @@ fn generate_impl_def_assist( | |||
124 | /// which is not in the list | 121 | /// which is not in the list |
125 | fn generate_unique_lifetime_param_name( | 122 | fn generate_unique_lifetime_param_name( |
126 | existing_type_param_list: Option<ast::GenericParamList>, | 123 | existing_type_param_list: Option<ast::GenericParamList>, |
127 | ) -> Option<char> { | 124 | ) -> Option<ast::Lifetime> { |
128 | match existing_type_param_list { | 125 | match existing_type_param_list { |
129 | Some(type_params) => { | 126 | Some(type_params) => { |
130 | let used_lifetime_params: FxHashSet<_> = type_params | 127 | let used_lifetime_params: FxHashSet<_> = |
131 | .lifetime_params() | 128 | type_params.lifetime_params().map(|p| p.syntax().text().to_string()).collect(); |
132 | .map(|p| p.syntax().text().to_string()[1..].to_owned()) | 129 | ('a'..='z').map(|it| format!("'{}", it)).find(|it| !used_lifetime_params.contains(it)) |
133 | .collect(); | ||
134 | (b'a'..=b'z').map(char::from).find(|c| !used_lifetime_params.contains(&c.to_string())) | ||
135 | } | 130 | } |
136 | None => Some('a'), | 131 | None => Some("'a".to_string()), |
137 | } | 132 | } |
138 | } | 133 | .map(|it| make::lifetime(&it)) |
139 | |||
140 | fn add_lifetime_param(type_params: ast::GenericParamList, new_lifetime_param: char) { | ||
141 | let generic_param = | ||
142 | make::generic_param(format!("'{}", new_lifetime_param), None).clone_for_update(); | ||
143 | type_params.add_generic_param(generic_param); | ||
144 | } | ||
145 | |||
146 | fn make_ast_lifetime(new_lifetime_param: char) -> ast::Lifetime { | ||
147 | make::generic_param(format!("'{}", new_lifetime_param), None) | ||
148 | .syntax() | ||
149 | .descendants() | ||
150 | .find_map(ast::Lifetime::cast) | ||
151 | .unwrap() | ||
152 | } | 134 | } |
153 | 135 | ||
154 | enum NeedsLifetime { | 136 | enum NeedsLifetime { |
@@ -159,8 +141,8 @@ enum NeedsLifetime { | |||
159 | impl NeedsLifetime { | 141 | impl NeedsLifetime { |
160 | fn make_mut(self, builder: &mut AssistBuilder) -> Self { | 142 | fn make_mut(self, builder: &mut AssistBuilder) -> Self { |
161 | match self { | 143 | match self { |
162 | Self::SelfParam(it) => Self::SelfParam(builder.make_ast_mut(it)), | 144 | Self::SelfParam(it) => Self::SelfParam(builder.make_mut(it)), |
163 | Self::RefType(it) => Self::RefType(builder.make_ast_mut(it)), | 145 | Self::RefType(it) => Self::RefType(builder.make_mut(it)), |
164 | } | 146 | } |
165 | } | 147 | } |
166 | 148 | ||
diff --git a/crates/ide_assists/src/handlers/merge_imports.rs b/crates/ide_assists/src/handlers/merge_imports.rs index add7b8e37..31854840c 100644 --- a/crates/ide_assists/src/handlers/merge_imports.rs +++ b/crates/ide_assists/src/handlers/merge_imports.rs | |||
@@ -27,14 +27,14 @@ pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext) -> Option<() | |||
27 | if let Some(use_item) = tree.syntax().parent().and_then(ast::Use::cast) { | 27 | if let Some(use_item) = tree.syntax().parent().and_then(ast::Use::cast) { |
28 | let (merged, to_remove) = | 28 | let (merged, to_remove) = |
29 | next_prev().filter_map(|dir| neighbor(&use_item, dir)).find_map(|use_item2| { | 29 | next_prev().filter_map(|dir| neighbor(&use_item, dir)).find_map(|use_item2| { |
30 | try_merge_imports(&use_item, &use_item2, MergeBehavior::Full).zip(Some(use_item2)) | 30 | try_merge_imports(&use_item, &use_item2, MergeBehavior::Crate).zip(Some(use_item2)) |
31 | })?; | 31 | })?; |
32 | 32 | ||
33 | imports = Some((use_item, merged, to_remove)); | 33 | imports = Some((use_item, merged, to_remove)); |
34 | } else { | 34 | } else { |
35 | let (merged, to_remove) = | 35 | let (merged, to_remove) = |
36 | next_prev().filter_map(|dir| neighbor(&tree, dir)).find_map(|use_tree| { | 36 | next_prev().filter_map(|dir| neighbor(&tree, dir)).find_map(|use_tree| { |
37 | try_merge_trees(&tree, &use_tree, MergeBehavior::Full).zip(Some(use_tree)) | 37 | try_merge_trees(&tree, &use_tree, MergeBehavior::Crate).zip(Some(use_tree)) |
38 | })?; | 38 | })?; |
39 | 39 | ||
40 | uses = Some((tree.clone(), merged, to_remove)) | 40 | uses = Some((tree.clone(), merged, to_remove)) |
@@ -47,16 +47,16 @@ pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext) -> Option<() | |||
47 | target, | 47 | target, |
48 | |builder| { | 48 | |builder| { |
49 | if let Some((to_replace, replacement, to_remove)) = imports { | 49 | if let Some((to_replace, replacement, to_remove)) = imports { |
50 | let to_replace = builder.make_ast_mut(to_replace); | 50 | let to_replace = builder.make_mut(to_replace); |
51 | let to_remove = builder.make_ast_mut(to_remove); | 51 | let to_remove = builder.make_mut(to_remove); |
52 | 52 | ||
53 | ted::replace(to_replace.syntax(), replacement.syntax()); | 53 | ted::replace(to_replace.syntax(), replacement.syntax()); |
54 | to_remove.remove(); | 54 | to_remove.remove(); |
55 | } | 55 | } |
56 | 56 | ||
57 | if let Some((to_replace, replacement, to_remove)) = uses { | 57 | if let Some((to_replace, replacement, to_remove)) = uses { |
58 | let to_replace = builder.make_ast_mut(to_replace); | 58 | let to_replace = builder.make_mut(to_replace); |
59 | let to_remove = builder.make_ast_mut(to_remove); | 59 | let to_remove = builder.make_mut(to_remove); |
60 | 60 | ||
61 | ted::replace(to_replace.syntax(), replacement.syntax()); | 61 | ted::replace(to_replace.syntax(), replacement.syntax()); |
62 | to_remove.remove() | 62 | to_remove.remove() |
diff --git a/crates/ide_assists/src/handlers/move_bounds.rs b/crates/ide_assists/src/handlers/move_bounds.rs index 011a28d44..d89d11bdf 100644 --- a/crates/ide_assists/src/handlers/move_bounds.rs +++ b/crates/ide_assists/src/handlers/move_bounds.rs | |||
@@ -36,8 +36,8 @@ pub(crate) fn move_bounds_to_where_clause(acc: &mut Assists, ctx: &AssistContext | |||
36 | "Move to where clause", | 36 | "Move to where clause", |
37 | target, | 37 | target, |
38 | |edit| { | 38 | |edit| { |
39 | let type_param_list = edit.make_ast_mut(type_param_list); | 39 | let type_param_list = edit.make_mut(type_param_list); |
40 | let parent = edit.make_mut(parent); | 40 | let parent = edit.make_syntax_mut(parent); |
41 | 41 | ||
42 | let where_clause: ast::WhereClause = match_ast! { | 42 | let where_clause: ast::WhereClause = match_ast! { |
43 | match parent { | 43 | match parent { |
@@ -63,11 +63,7 @@ pub(crate) fn move_bounds_to_where_clause(acc: &mut Assists, ctx: &AssistContext | |||
63 | } | 63 | } |
64 | 64 | ||
65 | fn build_predicate(param: ast::TypeParam) -> Option<ast::WherePred> { | 65 | fn build_predicate(param: ast::TypeParam) -> Option<ast::WherePred> { |
66 | let path = { | 66 | let path = make::ext::ident_path(¶m.name()?.syntax().to_string()); |
67 | let name_ref = make::name_ref(¶m.name()?.syntax().to_string()); | ||
68 | let segment = make::path_segment(name_ref); | ||
69 | make::path_unqualified(segment) | ||
70 | }; | ||
71 | let predicate = make::where_pred(path, param.type_bound_list()?.bounds()); | 67 | let predicate = make::where_pred(path, param.type_bound_list()?.bounds()); |
72 | Some(predicate.clone_for_update()) | 68 | Some(predicate.clone_for_update()) |
73 | } | 69 | } |
diff --git a/crates/ide_assists/src/handlers/move_module_to_file.rs b/crates/ide_assists/src/handlers/move_module_to_file.rs index 6e685b4b2..93f702c55 100644 --- a/crates/ide_assists/src/handlers/move_module_to_file.rs +++ b/crates/ide_assists/src/handlers/move_module_to_file.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | use ast::{edit::IndentLevel, VisibilityOwner}; | 1 | use ast::edit::IndentLevel; |
2 | use ide_db::base_db::AnchoredPathBuf; | 2 | use ide_db::base_db::AnchoredPathBuf; |
3 | use stdx::format_to; | 3 | use stdx::format_to; |
4 | use syntax::{ | 4 | use syntax::{ |
@@ -60,12 +60,18 @@ pub(crate) fn move_module_to_file(acc: &mut Assists, ctx: &AssistContext) -> Opt | |||
60 | }; | 60 | }; |
61 | 61 | ||
62 | let mut buf = String::new(); | 62 | let mut buf = String::new(); |
63 | if let Some(v) = module_ast.visibility() { | ||
64 | format_to!(buf, "{} ", v); | ||
65 | } | ||
66 | format_to!(buf, "mod {};", module_name); | 63 | format_to!(buf, "mod {};", module_name); |
67 | 64 | ||
68 | builder.replace(module_ast.syntax().text_range(), buf); | 65 | let replacement_start = if let Some(mod_token) = module_ast.mod_token() { |
66 | mod_token.text_range().start() | ||
67 | } else { | ||
68 | module_ast.syntax().text_range().start() | ||
69 | }; | ||
70 | |||
71 | builder.replace( | ||
72 | TextRange::new(replacement_start, module_ast.syntax().text_range().end()), | ||
73 | buf, | ||
74 | ); | ||
69 | 75 | ||
70 | let dst = AnchoredPathBuf { anchor: ctx.frange.file_id, path }; | 76 | let dst = AnchoredPathBuf { anchor: ctx.frange.file_id, path }; |
71 | builder.create_file(dst, contents); | 77 | builder.create_file(dst, contents); |
@@ -184,4 +190,26 @@ pub(crate) mod tests; | |||
184 | cov_mark::check!(available_before_curly); | 190 | cov_mark::check!(available_before_curly); |
185 | check_assist_not_applicable(move_module_to_file, r#"mod m { $0 }"#); | 191 | check_assist_not_applicable(move_module_to_file, r#"mod m { $0 }"#); |
186 | } | 192 | } |
193 | |||
194 | #[test] | ||
195 | fn keep_outer_comments_and_attributes() { | ||
196 | check_assist( | ||
197 | move_module_to_file, | ||
198 | r#" | ||
199 | /// doc comment | ||
200 | #[attribute] | ||
201 | mod $0tests { | ||
202 | #[test] fn t() {} | ||
203 | } | ||
204 | "#, | ||
205 | r#" | ||
206 | //- /main.rs | ||
207 | /// doc comment | ||
208 | #[attribute] | ||
209 | mod tests; | ||
210 | //- /tests.rs | ||
211 | #[test] fn t() {} | ||
212 | "#, | ||
213 | ); | ||
214 | } | ||
187 | } | 215 | } |
diff --git a/crates/ide_assists/src/handlers/pull_assignment_up.rs b/crates/ide_assists/src/handlers/pull_assignment_up.rs index 04bae4e58..f07b8a6c0 100644 --- a/crates/ide_assists/src/handlers/pull_assignment_up.rs +++ b/crates/ide_assists/src/handlers/pull_assignment_up.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | use syntax::{ | 1 | use syntax::{ |
2 | ast::{self, edit::AstNodeEdit, make}, | 2 | ast::{self, make}, |
3 | AstNode, | 3 | ted, AstNode, |
4 | }; | 4 | }; |
5 | 5 | ||
6 | use crate::{ | 6 | use crate::{ |
@@ -37,103 +37,120 @@ use crate::{ | |||
37 | // ``` | 37 | // ``` |
38 | pub(crate) fn pull_assignment_up(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 38 | pub(crate) fn pull_assignment_up(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
39 | let assign_expr = ctx.find_node_at_offset::<ast::BinExpr>()?; | 39 | let assign_expr = ctx.find_node_at_offset::<ast::BinExpr>()?; |
40 | let name_expr = if assign_expr.op_kind()? == ast::BinOp::Assignment { | 40 | |
41 | assign_expr.lhs()? | 41 | let op_kind = assign_expr.op_kind()?; |
42 | } else { | 42 | if op_kind != ast::BinOp::Assignment { |
43 | cov_mark::hit!(test_cant_pull_non_assignments); | ||
43 | return None; | 44 | return None; |
45 | } | ||
46 | |||
47 | let mut collector = AssignmentsCollector { | ||
48 | sema: &ctx.sema, | ||
49 | common_lhs: assign_expr.lhs()?, | ||
50 | assignments: Vec::new(), | ||
44 | }; | 51 | }; |
45 | 52 | ||
46 | let (old_stmt, new_stmt) = if let Some(if_expr) = ctx.find_node_at_offset::<ast::IfExpr>() { | 53 | let tgt: ast::Expr = if let Some(if_expr) = ctx.find_node_at_offset::<ast::IfExpr>() { |
47 | ( | 54 | collector.collect_if(&if_expr)?; |
48 | ast::Expr::cast(if_expr.syntax().to_owned())?, | 55 | if_expr.into() |
49 | exprify_if(&if_expr, &ctx.sema, &name_expr)?.indent(if_expr.indent_level()), | ||
50 | ) | ||
51 | } else if let Some(match_expr) = ctx.find_node_at_offset::<ast::MatchExpr>() { | 56 | } else if let Some(match_expr) = ctx.find_node_at_offset::<ast::MatchExpr>() { |
52 | ( | 57 | collector.collect_match(&match_expr)?; |
53 | ast::Expr::cast(match_expr.syntax().to_owned())?, | 58 | match_expr.into() |
54 | exprify_match(&match_expr, &ctx.sema, &name_expr)?, | ||
55 | ) | ||
56 | } else { | 59 | } else { |
57 | return None; | 60 | return None; |
58 | }; | 61 | }; |
59 | 62 | ||
60 | let expr_stmt = make::expr_stmt(new_stmt); | 63 | if let Some(parent) = tgt.syntax().parent() { |
64 | if matches!(parent.kind(), syntax::SyntaxKind::BIN_EXPR | syntax::SyntaxKind::LET_STMT) { | ||
65 | return None; | ||
66 | } | ||
67 | } | ||
61 | 68 | ||
62 | acc.add( | 69 | acc.add( |
63 | AssistId("pull_assignment_up", AssistKind::RefactorExtract), | 70 | AssistId("pull_assignment_up", AssistKind::RefactorExtract), |
64 | "Pull assignment up", | 71 | "Pull assignment up", |
65 | old_stmt.syntax().text_range(), | 72 | tgt.syntax().text_range(), |
66 | move |edit| { | 73 | move |edit| { |
67 | edit.replace(old_stmt.syntax().text_range(), format!("{} = {};", name_expr, expr_stmt)); | 74 | let assignments: Vec<_> = collector |
75 | .assignments | ||
76 | .into_iter() | ||
77 | .map(|(stmt, rhs)| (edit.make_mut(stmt), rhs.clone_for_update())) | ||
78 | .collect(); | ||
79 | |||
80 | let tgt = edit.make_mut(tgt); | ||
81 | |||
82 | for (stmt, rhs) in assignments { | ||
83 | let mut stmt = stmt.syntax().clone(); | ||
84 | if let Some(parent) = stmt.parent() { | ||
85 | if ast::ExprStmt::cast(parent.clone()).is_some() { | ||
86 | stmt = parent.clone(); | ||
87 | } | ||
88 | } | ||
89 | ted::replace(stmt, rhs.syntax()); | ||
90 | } | ||
91 | let assign_expr = make::expr_assignment(collector.common_lhs, tgt.clone()); | ||
92 | let assign_stmt = make::expr_stmt(assign_expr); | ||
93 | |||
94 | ted::replace(tgt.syntax(), assign_stmt.syntax().clone_for_update()); | ||
68 | }, | 95 | }, |
69 | ) | 96 | ) |
70 | } | 97 | } |
71 | 98 | ||
72 | fn exprify_match( | 99 | struct AssignmentsCollector<'a> { |
73 | match_expr: &ast::MatchExpr, | 100 | sema: &'a hir::Semantics<'a, ide_db::RootDatabase>, |
74 | sema: &hir::Semantics<ide_db::RootDatabase>, | 101 | common_lhs: ast::Expr, |
75 | name: &ast::Expr, | 102 | assignments: Vec<(ast::BinExpr, ast::Expr)>, |
76 | ) -> Option<ast::Expr> { | 103 | } |
77 | let new_arm_list = match_expr | 104 | |
78 | .match_arm_list()? | 105 | impl<'a> AssignmentsCollector<'a> { |
79 | .arms() | 106 | fn collect_match(&mut self, match_expr: &ast::MatchExpr) -> Option<()> { |
80 | .map(|arm| { | 107 | for arm in match_expr.match_arm_list()?.arms() { |
81 | if let ast::Expr::BlockExpr(block) = arm.expr()? { | 108 | match arm.expr()? { |
82 | let new_block = exprify_block(&block, sema, name)?.indent(block.indent_level()); | 109 | ast::Expr::BlockExpr(block) => self.collect_block(&block)?, |
83 | Some(arm.replace_descendant(block, new_block)) | 110 | ast::Expr::BinExpr(expr) => self.collect_expr(&expr)?, |
111 | _ => return None, | ||
112 | } | ||
113 | } | ||
114 | |||
115 | Some(()) | ||
116 | } | ||
117 | fn collect_if(&mut self, if_expr: &ast::IfExpr) -> Option<()> { | ||
118 | let then_branch = if_expr.then_branch()?; | ||
119 | self.collect_block(&then_branch)?; | ||
120 | |||
121 | match if_expr.else_branch()? { | ||
122 | ast::ElseBranch::Block(block) => self.collect_block(&block), | ||
123 | ast::ElseBranch::IfExpr(expr) => { | ||
124 | cov_mark::hit!(test_pull_assignment_up_chained_if); | ||
125 | self.collect_if(&expr) | ||
126 | } | ||
127 | } | ||
128 | } | ||
129 | fn collect_block(&mut self, block: &ast::BlockExpr) -> Option<()> { | ||
130 | let last_expr = block.tail_expr().or_else(|| { | ||
131 | if let ast::Stmt::ExprStmt(stmt) = block.statements().last()? { | ||
132 | stmt.expr() | ||
84 | } else { | 133 | } else { |
85 | None | 134 | None |
86 | } | 135 | } |
87 | }) | 136 | })?; |
88 | .collect::<Option<Vec<_>>>()?; | ||
89 | let new_arm_list = match_expr | ||
90 | .match_arm_list()? | ||
91 | .replace_descendants(match_expr.match_arm_list()?.arms().zip(new_arm_list)); | ||
92 | Some(make::expr_match(match_expr.expr()?, new_arm_list)) | ||
93 | } | ||
94 | 137 | ||
95 | fn exprify_if( | 138 | if let ast::Expr::BinExpr(expr) = last_expr { |
96 | statement: &ast::IfExpr, | 139 | return self.collect_expr(&expr); |
97 | sema: &hir::Semantics<ide_db::RootDatabase>, | ||
98 | name: &ast::Expr, | ||
99 | ) -> Option<ast::Expr> { | ||
100 | let then_branch = exprify_block(&statement.then_branch()?, sema, name)?; | ||
101 | let else_branch = match statement.else_branch()? { | ||
102 | ast::ElseBranch::Block(ref block) => { | ||
103 | ast::ElseBranch::Block(exprify_block(block, sema, name)?) | ||
104 | } | ||
105 | ast::ElseBranch::IfExpr(expr) => { | ||
106 | cov_mark::hit!(test_pull_assignment_up_chained_if); | ||
107 | ast::ElseBranch::IfExpr(ast::IfExpr::cast( | ||
108 | exprify_if(&expr, sema, name)?.syntax().to_owned(), | ||
109 | )?) | ||
110 | } | 140 | } |
111 | }; | ||
112 | Some(make::expr_if(statement.condition()?, then_branch, Some(else_branch))) | ||
113 | } | ||
114 | 141 | ||
115 | fn exprify_block( | 142 | None |
116 | block: &ast::BlockExpr, | ||
117 | sema: &hir::Semantics<ide_db::RootDatabase>, | ||
118 | name: &ast::Expr, | ||
119 | ) -> Option<ast::BlockExpr> { | ||
120 | if block.tail_expr().is_some() { | ||
121 | return None; | ||
122 | } | 143 | } |
123 | 144 | ||
124 | let mut stmts: Vec<_> = block.statements().collect(); | 145 | fn collect_expr(&mut self, expr: &ast::BinExpr) -> Option<()> { |
125 | let stmt = stmts.pop()?; | 146 | if expr.op_kind()? == ast::BinOp::Assignment |
126 | 147 | && is_equivalent(self.sema, &expr.lhs()?, &self.common_lhs) | |
127 | if let ast::Stmt::ExprStmt(stmt) = stmt { | 148 | { |
128 | if let ast::Expr::BinExpr(expr) = stmt.expr()? { | 149 | self.assignments.push((expr.clone(), expr.rhs()?)); |
129 | if expr.op_kind()? == ast::BinOp::Assignment && is_equivalent(sema, &expr.lhs()?, name) | 150 | return Some(()); |
130 | { | ||
131 | // The last statement in the block is an assignment to the name we want | ||
132 | return Some(make::block_expr(stmts, Some(expr.rhs()?))); | ||
133 | } | ||
134 | } | 151 | } |
152 | None | ||
135 | } | 153 | } |
136 | None | ||
137 | } | 154 | } |
138 | 155 | ||
139 | fn is_equivalent( | 156 | fn is_equivalent( |
@@ -243,6 +260,37 @@ fn foo() { | |||
243 | } | 260 | } |
244 | 261 | ||
245 | #[test] | 262 | #[test] |
263 | fn test_pull_assignment_up_assignment_expressions() { | ||
264 | check_assist( | ||
265 | pull_assignment_up, | ||
266 | r#" | ||
267 | fn foo() { | ||
268 | let mut a = 1; | ||
269 | |||
270 | match 1 { | ||
271 | 1 => { $0a = 2; }, | ||
272 | 2 => a = 3, | ||
273 | 3 => { | ||
274 | a = 4 | ||
275 | } | ||
276 | } | ||
277 | }"#, | ||
278 | r#" | ||
279 | fn foo() { | ||
280 | let mut a = 1; | ||
281 | |||
282 | a = match 1 { | ||
283 | 1 => { 2 }, | ||
284 | 2 => 3, | ||
285 | 3 => { | ||
286 | 4 | ||
287 | } | ||
288 | }; | ||
289 | }"#, | ||
290 | ); | ||
291 | } | ||
292 | |||
293 | #[test] | ||
246 | fn test_pull_assignment_up_not_last_not_applicable() { | 294 | fn test_pull_assignment_up_not_last_not_applicable() { |
247 | check_assist_not_applicable( | 295 | check_assist_not_applicable( |
248 | pull_assignment_up, | 296 | pull_assignment_up, |
@@ -439,4 +487,24 @@ fn foo() { | |||
439 | "#, | 487 | "#, |
440 | ) | 488 | ) |
441 | } | 489 | } |
490 | |||
491 | #[test] | ||
492 | fn test_cant_pull_non_assignments() { | ||
493 | cov_mark::check!(test_cant_pull_non_assignments); | ||
494 | check_assist_not_applicable( | ||
495 | pull_assignment_up, | ||
496 | r#" | ||
497 | fn foo() { | ||
498 | let mut a = 1; | ||
499 | let b = &mut a; | ||
500 | |||
501 | if true { | ||
502 | $0*b + 2; | ||
503 | } else { | ||
504 | *b + 3; | ||
505 | } | ||
506 | } | ||
507 | "#, | ||
508 | ) | ||
509 | } | ||
442 | } | 510 | } |
diff --git a/crates/ide_assists/src/handlers/raw_string.rs b/crates/ide_assists/src/handlers/raw_string.rs index d0f1613f3..d98a55ae4 100644 --- a/crates/ide_assists/src/handlers/raw_string.rs +++ b/crates/ide_assists/src/handlers/raw_string.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | use std::borrow::Cow; | 1 | use std::borrow::Cow; |
2 | 2 | ||
3 | use syntax::{ast, AstToken, TextRange, TextSize}; | 3 | use syntax::{ast, ast::IsString, AstToken, TextRange, TextSize}; |
4 | 4 | ||
5 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | 5 | use crate::{AssistContext, AssistId, AssistKind, Assists}; |
6 | 6 | ||
diff --git a/crates/ide_assists/src/handlers/reorder_fields.rs b/crates/ide_assists/src/handlers/reorder_fields.rs index e90bbdbcf..933acead1 100644 --- a/crates/ide_assists/src/handlers/reorder_fields.rs +++ b/crates/ide_assists/src/handlers/reorder_fields.rs | |||
@@ -70,10 +70,10 @@ pub(crate) fn reorder_fields(acc: &mut Assists, ctx: &AssistContext) -> Option<( | |||
70 | target, | 70 | target, |
71 | |builder| match fields { | 71 | |builder| match fields { |
72 | Either::Left((sorted, field_list)) => { | 72 | Either::Left((sorted, field_list)) => { |
73 | replace(builder.make_ast_mut(field_list).fields(), sorted) | 73 | replace(builder.make_mut(field_list).fields(), sorted) |
74 | } | 74 | } |
75 | Either::Right((sorted, field_list)) => { | 75 | Either::Right((sorted, field_list)) => { |
76 | replace(builder.make_ast_mut(field_list).fields(), sorted) | 76 | replace(builder.make_mut(field_list).fields(), sorted) |
77 | } | 77 | } |
78 | }, | 78 | }, |
79 | ) | 79 | ) |
diff --git a/crates/ide_assists/src/handlers/reorder_impl.rs b/crates/ide_assists/src/handlers/reorder_impl.rs index 54a9a468e..5a6a9f158 100644 --- a/crates/ide_assists/src/handlers/reorder_impl.rs +++ b/crates/ide_assists/src/handlers/reorder_impl.rs | |||
@@ -79,8 +79,7 @@ pub(crate) fn reorder_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
79 | "Sort methods", | 79 | "Sort methods", |
80 | target, | 80 | target, |
81 | |builder| { | 81 | |builder| { |
82 | let methods = | 82 | let methods = methods.into_iter().map(|fn_| builder.make_mut(fn_)).collect::<Vec<_>>(); |
83 | methods.into_iter().map(|fn_| builder.make_ast_mut(fn_)).collect::<Vec<_>>(); | ||
84 | methods | 83 | methods |
85 | .into_iter() | 84 | .into_iter() |
86 | .zip(sorted) | 85 | .zip(sorted) |
diff --git a/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs index 694d897d1..10d9cec31 100644 --- a/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs +++ b/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs | |||
@@ -84,7 +84,7 @@ pub(crate) fn replace_derive_with_manual_impl( | |||
84 | add_assist(acc, ctx, &attr, &args, &trait_path, Some(trait_), &adt)?; | 84 | add_assist(acc, ctx, &attr, &args, &trait_path, Some(trait_), &adt)?; |
85 | } | 85 | } |
86 | if no_traits_found { | 86 | if no_traits_found { |
87 | let trait_path = make::path_unqualified(make::path_segment(make::name_ref(trait_name))); | 87 | let trait_path = make::ext::ident_path(trait_name); |
88 | add_assist(acc, ctx, &attr, &args, &trait_path, None, &adt)?; | 88 | add_assist(acc, ctx, &attr, &args, &trait_path, None, &adt)?; |
89 | } | 89 | } |
90 | Some(()) | 90 | Some(()) |
@@ -159,10 +159,8 @@ fn impl_def_from_trait( | |||
159 | if trait_items.is_empty() { | 159 | if trait_items.is_empty() { |
160 | return None; | 160 | return None; |
161 | } | 161 | } |
162 | let impl_def = make::impl_trait( | 162 | let impl_def = |
163 | trait_path.clone(), | 163 | make::impl_trait(trait_path.clone(), make::ext::ident_path(&annotated_name.text())); |
164 | make::path_unqualified(make::path_segment(make::name_ref(&annotated_name.text()))), | ||
165 | ); | ||
166 | let (impl_def, first_assoc_item) = | 164 | let (impl_def, first_assoc_item) = |
167 | add_trait_assoc_items_to_impl(sema, trait_items, trait_, impl_def, target_scope); | 165 | add_trait_assoc_items_to_impl(sema, trait_items, trait_, impl_def, target_scope); |
168 | Some((impl_def, first_assoc_item)) | 166 | Some((impl_def, first_assoc_item)) |
diff --git a/crates/ide_assists/src/handlers/replace_impl_trait_with_generic.rs b/crates/ide_assists/src/handlers/replace_impl_trait_with_generic.rs index ff25b61ea..540a905cc 100644 --- a/crates/ide_assists/src/handlers/replace_impl_trait_with_generic.rs +++ b/crates/ide_assists/src/handlers/replace_impl_trait_with_generic.rs | |||
@@ -1,6 +1,9 @@ | |||
1 | use syntax::ast::{self, edit::AstNodeEdit, make, AstNode, GenericParamsOwner}; | 1 | use syntax::{ |
2 | ast::{self, edit_in_place::GenericParamsOwnerEdit, make, AstNode}, | ||
3 | ted, | ||
4 | }; | ||
2 | 5 | ||
3 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | 6 | use crate::{utils::suggest_name, AssistContext, AssistId, AssistKind, Assists}; |
4 | 7 | ||
5 | // Assist: replace_impl_trait_with_generic | 8 | // Assist: replace_impl_trait_with_generic |
6 | // | 9 | // |
@@ -17,30 +20,29 @@ pub(crate) fn replace_impl_trait_with_generic( | |||
17 | acc: &mut Assists, | 20 | acc: &mut Assists, |
18 | ctx: &AssistContext, | 21 | ctx: &AssistContext, |
19 | ) -> Option<()> { | 22 | ) -> Option<()> { |
20 | let type_impl_trait = ctx.find_node_at_offset::<ast::ImplTraitType>()?; | 23 | let impl_trait_type = ctx.find_node_at_offset::<ast::ImplTraitType>()?; |
21 | let type_param = type_impl_trait.syntax().parent().and_then(ast::Param::cast)?; | 24 | let param = impl_trait_type.syntax().parent().and_then(ast::Param::cast)?; |
22 | let type_fn = type_param.syntax().ancestors().find_map(ast::Fn::cast)?; | 25 | let fn_ = param.syntax().ancestors().find_map(ast::Fn::cast)?; |
23 | 26 | ||
24 | let impl_trait_ty = type_impl_trait.type_bound_list()?; | 27 | let type_bound_list = impl_trait_type.type_bound_list()?; |
25 | 28 | ||
26 | let target = type_fn.syntax().text_range(); | 29 | let target = fn_.syntax().text_range(); |
27 | acc.add( | 30 | acc.add( |
28 | AssistId("replace_impl_trait_with_generic", AssistKind::RefactorRewrite), | 31 | AssistId("replace_impl_trait_with_generic", AssistKind::RefactorRewrite), |
29 | "Replace impl trait with generic", | 32 | "Replace impl trait with generic", |
30 | target, | 33 | target, |
31 | |edit| { | 34 | |edit| { |
32 | let generic_letter = impl_trait_ty.to_string().chars().next().unwrap().to_string(); | 35 | let impl_trait_type = edit.make_mut(impl_trait_type); |
36 | let fn_ = edit.make_mut(fn_); | ||
33 | 37 | ||
34 | let generic_param_list = type_fn | 38 | let type_param_name = suggest_name::for_generic_parameter(&impl_trait_type); |
35 | .generic_param_list() | ||
36 | .unwrap_or_else(|| make::generic_param_list(None)) | ||
37 | .append_param(make::generic_param(generic_letter.clone(), Some(impl_trait_ty))); | ||
38 | 39 | ||
39 | let new_type_fn = type_fn | 40 | let type_param = make::type_param(make::name(&type_param_name), Some(type_bound_list)) |
40 | .replace_descendant::<ast::Type>(type_impl_trait.into(), make::ty(&generic_letter)) | 41 | .clone_for_update(); |
41 | .with_generic_param_list(generic_param_list); | 42 | let new_ty = make::ty(&type_param_name).clone_for_update(); |
42 | 43 | ||
43 | edit.replace_ast(type_fn.clone(), new_type_fn); | 44 | ted::replace(impl_trait_type.syntax(), new_ty.syntax()); |
45 | fn_.get_or_create_generic_param_list().add_generic_param(type_param.into()) | ||
44 | }, | 46 | }, |
45 | ) | 47 | ) |
46 | } | 48 | } |
@@ -55,12 +57,8 @@ mod tests { | |||
55 | fn replace_impl_trait_with_generic_params() { | 57 | fn replace_impl_trait_with_generic_params() { |
56 | check_assist( | 58 | check_assist( |
57 | replace_impl_trait_with_generic, | 59 | replace_impl_trait_with_generic, |
58 | r#" | 60 | r#"fn foo<G>(bar: $0impl Bar) {}"#, |
59 | fn foo<G>(bar: $0impl Bar) {} | 61 | r#"fn foo<G, B: Bar>(bar: B) {}"#, |
60 | "#, | ||
61 | r#" | ||
62 | fn foo<G, B: Bar>(bar: B) {} | ||
63 | "#, | ||
64 | ); | 62 | ); |
65 | } | 63 | } |
66 | 64 | ||
@@ -68,12 +66,8 @@ mod tests { | |||
68 | fn replace_impl_trait_without_generic_params() { | 66 | fn replace_impl_trait_without_generic_params() { |
69 | check_assist( | 67 | check_assist( |
70 | replace_impl_trait_with_generic, | 68 | replace_impl_trait_with_generic, |
71 | r#" | 69 | r#"fn foo(bar: $0impl Bar) {}"#, |
72 | fn foo(bar: $0impl Bar) {} | 70 | r#"fn foo<B: Bar>(bar: B) {}"#, |
73 | "#, | ||
74 | r#" | ||
75 | fn foo<B: Bar>(bar: B) {} | ||
76 | "#, | ||
77 | ); | 71 | ); |
78 | } | 72 | } |
79 | 73 | ||
@@ -81,12 +75,8 @@ mod tests { | |||
81 | fn replace_two_impl_trait_with_generic_params() { | 75 | fn replace_two_impl_trait_with_generic_params() { |
82 | check_assist( | 76 | check_assist( |
83 | replace_impl_trait_with_generic, | 77 | replace_impl_trait_with_generic, |
84 | r#" | 78 | r#"fn foo<G>(foo: impl Foo, bar: $0impl Bar) {}"#, |
85 | fn foo<G>(foo: impl Foo, bar: $0impl Bar) {} | 79 | r#"fn foo<G, B: Bar>(foo: impl Foo, bar: B) {}"#, |
86 | "#, | ||
87 | r#" | ||
88 | fn foo<G, B: Bar>(foo: impl Foo, bar: B) {} | ||
89 | "#, | ||
90 | ); | 80 | ); |
91 | } | 81 | } |
92 | 82 | ||
@@ -94,12 +84,8 @@ mod tests { | |||
94 | fn replace_impl_trait_with_empty_generic_params() { | 84 | fn replace_impl_trait_with_empty_generic_params() { |
95 | check_assist( | 85 | check_assist( |
96 | replace_impl_trait_with_generic, | 86 | replace_impl_trait_with_generic, |
97 | r#" | 87 | r#"fn foo<>(bar: $0impl Bar) {}"#, |
98 | fn foo<>(bar: $0impl Bar) {} | 88 | r#"fn foo<B: Bar>(bar: B) {}"#, |
99 | "#, | ||
100 | r#" | ||
101 | fn foo<B: Bar>(bar: B) {} | ||
102 | "#, | ||
103 | ); | 89 | ); |
104 | } | 90 | } |
105 | 91 | ||
@@ -108,13 +94,13 @@ mod tests { | |||
108 | check_assist( | 94 | check_assist( |
109 | replace_impl_trait_with_generic, | 95 | replace_impl_trait_with_generic, |
110 | r#" | 96 | r#" |
111 | fn foo< | 97 | fn foo< |
112 | >(bar: $0impl Bar) {} | 98 | >(bar: $0impl Bar) {} |
113 | "#, | 99 | "#, |
114 | r#" | 100 | r#" |
115 | fn foo<B: Bar | 101 | fn foo<B: Bar |
116 | >(bar: B) {} | 102 | >(bar: B) {} |
117 | "#, | 103 | "#, |
118 | ); | 104 | ); |
119 | } | 105 | } |
120 | 106 | ||
@@ -123,12 +109,8 @@ mod tests { | |||
123 | fn replace_impl_trait_with_exist_generic_letter() { | 109 | fn replace_impl_trait_with_exist_generic_letter() { |
124 | check_assist( | 110 | check_assist( |
125 | replace_impl_trait_with_generic, | 111 | replace_impl_trait_with_generic, |
126 | r#" | 112 | r#"fn foo<B>(bar: $0impl Bar) {}"#, |
127 | fn foo<B>(bar: $0impl Bar) {} | 113 | r#"fn foo<B, C: Bar>(bar: C) {}"#, |
128 | "#, | ||
129 | r#" | ||
130 | fn foo<B, C: Bar>(bar: C) {} | ||
131 | "#, | ||
132 | ); | 114 | ); |
133 | } | 115 | } |
134 | 116 | ||
@@ -137,19 +119,19 @@ mod tests { | |||
137 | check_assist( | 119 | check_assist( |
138 | replace_impl_trait_with_generic, | 120 | replace_impl_trait_with_generic, |
139 | r#" | 121 | r#" |
140 | fn foo< | 122 | fn foo< |
141 | G: Foo, | 123 | G: Foo, |
142 | F, | 124 | F, |
143 | H, | 125 | H, |
144 | >(bar: $0impl Bar) {} | 126 | >(bar: $0impl Bar) {} |
145 | "#, | 127 | "#, |
146 | r#" | 128 | r#" |
147 | fn foo< | 129 | fn foo< |
148 | G: Foo, | 130 | G: Foo, |
149 | F, | 131 | F, |
150 | H, B: Bar | 132 | H, B: Bar, |
151 | >(bar: B) {} | 133 | >(bar: B) {} |
152 | "#, | 134 | "#, |
153 | ); | 135 | ); |
154 | } | 136 | } |
155 | 137 | ||
@@ -157,12 +139,8 @@ mod tests { | |||
157 | fn replace_impl_trait_multiple() { | 139 | fn replace_impl_trait_multiple() { |
158 | check_assist( | 140 | check_assist( |
159 | replace_impl_trait_with_generic, | 141 | replace_impl_trait_with_generic, |
160 | r#" | 142 | r#"fn foo(bar: $0impl Foo + Bar) {}"#, |
161 | fn foo(bar: $0impl Foo + Bar) {} | 143 | r#"fn foo<F: Foo + Bar>(bar: F) {}"#, |
162 | "#, | ||
163 | r#" | ||
164 | fn foo<F: Foo + Bar>(bar: F) {} | ||
165 | "#, | ||
166 | ); | 144 | ); |
167 | } | 145 | } |
168 | } | 146 | } |
diff --git a/crates/ide_assists/src/handlers/replace_let_with_if_let.rs b/crates/ide_assists/src/handlers/replace_let_with_if_let.rs index be7e724b5..1ad0fa816 100644 --- a/crates/ide_assists/src/handlers/replace_let_with_if_let.rs +++ b/crates/ide_assists/src/handlers/replace_let_with_if_let.rs | |||
@@ -50,22 +50,19 @@ pub(crate) fn replace_let_with_if_let(acc: &mut Assists, ctx: &AssistContext) -> | |||
50 | "Replace with if-let", | 50 | "Replace with if-let", |
51 | target, | 51 | target, |
52 | |edit| { | 52 | |edit| { |
53 | let with_placeholder: ast::Pat = match happy_variant { | 53 | let pat = match happy_variant { |
54 | None => make::wildcard_pat().into(), | 54 | None => original_pat, |
55 | Some(var_name) => make::tuple_struct_pat( | 55 | Some(var_name) => { |
56 | make::path_unqualified(make::path_segment(make::name_ref(var_name))), | 56 | make::tuple_struct_pat(make::ext::ident_path(var_name), once(original_pat)) |
57 | once(make::wildcard_pat().into()), | 57 | .into() |
58 | ) | 58 | } |
59 | .into(), | ||
60 | }; | 59 | }; |
60 | |||
61 | let block = | 61 | let block = |
62 | make::block_expr(None, None).indent(IndentLevel::from_node(let_stmt.syntax())); | 62 | make::ext::empty_block_expr().indent(IndentLevel::from_node(let_stmt.syntax())); |
63 | let if_ = make::expr_if(make::condition(init, Some(with_placeholder)), block, None); | 63 | let if_ = make::expr_if(make::condition(init, Some(pat)), block, None); |
64 | let stmt = make::expr_stmt(if_); | 64 | let stmt = make::expr_stmt(if_); |
65 | 65 | ||
66 | let placeholder = stmt.syntax().descendants().find_map(ast::WildcardPat::cast).unwrap(); | ||
67 | let stmt = stmt.replace_descendant(placeholder.into(), original_pat); | ||
68 | |||
69 | edit.replace_ast(ast::Stmt::from(let_stmt), ast::Stmt::from(stmt)); | 66 | edit.replace_ast(ast::Stmt::from(let_stmt), ast::Stmt::from(stmt)); |
70 | }, | 67 | }, |
71 | ) | 68 | ) |
diff --git a/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs b/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs index 99ba79860..39f5eb4ff 100644 --- a/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs +++ b/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs | |||
@@ -40,7 +40,7 @@ pub(crate) fn replace_qualified_name_with_use( | |||
40 | |builder| { | 40 | |builder| { |
41 | // Now that we've brought the name into scope, re-qualify all paths that could be | 41 | // Now that we've brought the name into scope, re-qualify all paths that could be |
42 | // affected (that is, all paths inside the node we added the `use` to). | 42 | // affected (that is, all paths inside the node we added the `use` to). |
43 | let syntax = builder.make_mut(syntax.clone()); | 43 | let syntax = builder.make_syntax_mut(syntax.clone()); |
44 | if let Some(ref import_scope) = ImportScope::from(syntax.clone()) { | 44 | if let Some(ref import_scope) = ImportScope::from(syntax.clone()) { |
45 | shorten_paths(&syntax, &path.clone_for_update()); | 45 | shorten_paths(&syntax, &path.clone_for_update()); |
46 | insert_use(import_scope, path, ctx.config.insert_use); | 46 | insert_use(import_scope, path, ctx.config.insert_use); |
diff --git a/crates/ide_assists/src/handlers/replace_string_with_char.rs b/crates/ide_assists/src/handlers/replace_string_with_char.rs index 634b9c0b7..0800d291e 100644 --- a/crates/ide_assists/src/handlers/replace_string_with_char.rs +++ b/crates/ide_assists/src/handlers/replace_string_with_char.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | use syntax::{ast, AstToken, SyntaxKind::STRING}; | 1 | use syntax::{ast, ast::IsString, AstToken, SyntaxKind::STRING}; |
2 | 2 | ||
3 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | 3 | use crate::{AssistContext, AssistId, AssistKind, Assists}; |
4 | 4 | ||
diff --git a/crates/ide_assists/src/handlers/replace_unwrap_with_match.rs b/crates/ide_assists/src/handlers/replace_unwrap_with_match.rs index a986a6ae8..a3bfa221c 100644 --- a/crates/ide_assists/src/handlers/replace_unwrap_with_match.rs +++ b/crates/ide_assists/src/handlers/replace_unwrap_with_match.rs | |||
@@ -17,7 +17,7 @@ use ide_db::ty_filter::TryEnum; | |||
17 | 17 | ||
18 | // Assist: replace_unwrap_with_match | 18 | // Assist: replace_unwrap_with_match |
19 | // | 19 | // |
20 | // Replaces `unwrap` a `match` expression. Works for Result and Option. | 20 | // Replaces `unwrap` with a `match` expression. Works for Result and Option. |
21 | // | 21 | // |
22 | // ``` | 22 | // ``` |
23 | // enum Result<T, E> { Ok(T), Err(E) } | 23 | // enum Result<T, E> { Ok(T), Err(E) } |
@@ -32,7 +32,7 @@ use ide_db::ty_filter::TryEnum; | |||
32 | // fn main() { | 32 | // fn main() { |
33 | // let x: Result<i32, i32> = Result::Ok(92); | 33 | // let x: Result<i32, i32> = Result::Ok(92); |
34 | // let y = match x { | 34 | // let y = match x { |
35 | // Ok(a) => a, | 35 | // Ok(it) => it, |
36 | // $0_ => unreachable!(), | 36 | // $0_ => unreachable!(), |
37 | // }; | 37 | // }; |
38 | // } | 38 | // } |
@@ -52,16 +52,17 @@ pub(crate) fn replace_unwrap_with_match(acc: &mut Assists, ctx: &AssistContext) | |||
52 | "Replace unwrap with match", | 52 | "Replace unwrap with match", |
53 | target, | 53 | target, |
54 | |builder| { | 54 | |builder| { |
55 | let ok_path = make::path_unqualified(make::path_segment(make::name_ref(happy_variant))); | 55 | let ok_path = make::ext::ident_path(happy_variant); |
56 | let it = make::ident_pat(make::name("a")).into(); | 56 | let it = make::ident_pat(make::name("it")).into(); |
57 | let ok_tuple = make::tuple_struct_pat(ok_path, iter::once(it)).into(); | 57 | let ok_tuple = make::tuple_struct_pat(ok_path, iter::once(it)).into(); |
58 | 58 | ||
59 | let bind_path = make::path_unqualified(make::path_segment(make::name_ref("a"))); | 59 | let bind_path = make::ext::ident_path("it"); |
60 | let ok_arm = make::match_arm(iter::once(ok_tuple), make::expr_path(bind_path)); | 60 | let ok_arm = make::match_arm(iter::once(ok_tuple), make::expr_path(bind_path)); |
61 | 61 | ||
62 | let unreachable_call = make::expr_unreachable(); | 62 | let err_arm = make::match_arm( |
63 | let err_arm = | 63 | iter::once(make::wildcard_pat().into()), |
64 | make::match_arm(iter::once(make::wildcard_pat().into()), unreachable_call); | 64 | make::ext::expr_unreachable(), |
65 | ); | ||
65 | 66 | ||
66 | let match_arm_list = make::match_arm_list(vec![ok_arm, err_arm]); | 67 | let match_arm_list = make::match_arm_list(vec![ok_arm, err_arm]); |
67 | let match_expr = make::expr_match(caller.clone(), match_arm_list) | 68 | let match_expr = make::expr_match(caller.clone(), match_arm_list) |
@@ -110,7 +111,7 @@ fn i<T>(a: T) -> T { a } | |||
110 | fn main() { | 111 | fn main() { |
111 | let x: Result<i32, i32> = Result::Ok(92); | 112 | let x: Result<i32, i32> = Result::Ok(92); |
112 | let y = match i(x) { | 113 | let y = match i(x) { |
113 | Ok(a) => a, | 114 | Ok(it) => it, |
114 | $0_ => unreachable!(), | 115 | $0_ => unreachable!(), |
115 | }; | 116 | }; |
116 | } | 117 | } |
@@ -136,7 +137,7 @@ fn i<T>(a: T) -> T { a } | |||
136 | fn main() { | 137 | fn main() { |
137 | let x = Option::Some(92); | 138 | let x = Option::Some(92); |
138 | let y = match i(x) { | 139 | let y = match i(x) { |
139 | Some(a) => a, | 140 | Some(it) => it, |
140 | $0_ => unreachable!(), | 141 | $0_ => unreachable!(), |
141 | }; | 142 | }; |
142 | } | 143 | } |
@@ -162,7 +163,7 @@ fn i<T>(a: T) -> T { a } | |||
162 | fn main() { | 163 | fn main() { |
163 | let x: Result<i32, i32> = Result::Ok(92); | 164 | let x: Result<i32, i32> = Result::Ok(92); |
164 | let y = match i(x) { | 165 | let y = match i(x) { |
165 | Ok(a) => a, | 166 | Ok(it) => it, |
166 | $0_ => unreachable!(), | 167 | $0_ => unreachable!(), |
167 | }.count_zeroes(); | 168 | }.count_zeroes(); |
168 | } | 169 | } |
diff --git a/crates/ide_assists/src/handlers/wrap_return_type_in_result.rs b/crates/ide_assists/src/handlers/wrap_return_type_in_result.rs index e838630ea..2f1da82c7 100644 --- a/crates/ide_assists/src/handlers/wrap_return_type_in_result.rs +++ b/crates/ide_assists/src/handlers/wrap_return_type_in_result.rs | |||
@@ -54,9 +54,7 @@ pub(crate) fn wrap_return_type_in_result(acc: &mut Assists, ctx: &AssistContext) | |||
54 | 54 | ||
55 | for ret_expr_arg in tail_return_expr_collector.exprs_to_wrap { | 55 | for ret_expr_arg in tail_return_expr_collector.exprs_to_wrap { |
56 | let ok_wrapped = make::expr_call( | 56 | let ok_wrapped = make::expr_call( |
57 | make::expr_path(make::path_unqualified(make::path_segment(make::name_ref( | 57 | make::expr_path(make::ext::ident_path("Ok")), |
58 | "Ok", | ||
59 | )))), | ||
60 | make::arg_list(iter::once(ret_expr_arg.clone())), | 58 | make::arg_list(iter::once(ret_expr_arg.clone())), |
61 | ); | 59 | ); |
62 | builder.replace_ast(ret_expr_arg, ok_wrapped); | 60 | builder.replace_ast(ret_expr_arg, ok_wrapped); |
diff --git a/crates/ide_assists/src/tests.rs b/crates/ide_assists/src/tests.rs index 9c2847998..0d3969c36 100644 --- a/crates/ide_assists/src/tests.rs +++ b/crates/ide_assists/src/tests.rs | |||
@@ -21,7 +21,7 @@ pub(crate) const TEST_CONFIG: AssistConfig = AssistConfig { | |||
21 | snippet_cap: SnippetCap::new(true), | 21 | snippet_cap: SnippetCap::new(true), |
22 | allowed: None, | 22 | allowed: None, |
23 | insert_use: InsertUseConfig { | 23 | insert_use: InsertUseConfig { |
24 | merge: Some(MergeBehavior::Full), | 24 | merge: Some(MergeBehavior::Crate), |
25 | prefix_kind: hir::PrefixKind::Plain, | 25 | prefix_kind: hir::PrefixKind::Plain, |
26 | group: true, | 26 | group: true, |
27 | }, | 27 | }, |
diff --git a/crates/ide_assists/src/tests/generated.rs b/crates/ide_assists/src/tests/generated.rs index 59bcef8fb..4406406a2 100644 --- a/crates/ide_assists/src/tests/generated.rs +++ b/crates/ide_assists/src/tests/generated.rs | |||
@@ -50,7 +50,6 @@ trait Trait { | |||
50 | impl Trait for () { | 50 | impl Trait for () { |
51 | type X = (); | 51 | type X = (); |
52 | fn foo(&self) {}$0 | 52 | fn foo(&self) {}$0 |
53 | |||
54 | } | 53 | } |
55 | "#####, | 54 | "#####, |
56 | r#####" | 55 | r#####" |
@@ -1531,7 +1530,7 @@ enum Result<T, E> { Ok(T), Err(E) } | |||
1531 | fn main() { | 1530 | fn main() { |
1532 | let x: Result<i32, i32> = Result::Ok(92); | 1531 | let x: Result<i32, i32> = Result::Ok(92); |
1533 | let y = match x { | 1532 | let y = match x { |
1534 | Ok(a) => a, | 1533 | Ok(it) => it, |
1535 | $0_ => unreachable!(), | 1534 | $0_ => unreachable!(), |
1536 | }; | 1535 | }; |
1537 | } | 1536 | } |
diff --git a/crates/ide_assists/src/utils.rs b/crates/ide_assists/src/utils.rs index 5a90ad715..fc7caee04 100644 --- a/crates/ide_assists/src/utils.rs +++ b/crates/ide_assists/src/utils.rs | |||
@@ -17,7 +17,7 @@ use syntax::{ | |||
17 | ast::AttrsOwner, | 17 | ast::AttrsOwner, |
18 | ast::NameOwner, | 18 | ast::NameOwner, |
19 | ast::{self, edit, make, ArgListOwner, GenericParamsOwner}, | 19 | ast::{self, edit, make, ArgListOwner, GenericParamsOwner}, |
20 | AstNode, Direction, SmolStr, | 20 | ted, AstNode, Direction, SmolStr, |
21 | SyntaxKind::*, | 21 | SyntaxKind::*, |
22 | SyntaxNode, TextSize, T, | 22 | SyntaxNode, TextSize, T, |
23 | }; | 23 | }; |
@@ -128,43 +128,43 @@ pub fn add_trait_assoc_items_to_impl( | |||
128 | sema: &hir::Semantics<ide_db::RootDatabase>, | 128 | sema: &hir::Semantics<ide_db::RootDatabase>, |
129 | items: Vec<ast::AssocItem>, | 129 | items: Vec<ast::AssocItem>, |
130 | trait_: hir::Trait, | 130 | trait_: hir::Trait, |
131 | impl_def: ast::Impl, | 131 | impl_: ast::Impl, |
132 | target_scope: hir::SemanticsScope, | 132 | target_scope: hir::SemanticsScope, |
133 | ) -> (ast::Impl, ast::AssocItem) { | 133 | ) -> (ast::Impl, ast::AssocItem) { |
134 | let impl_item_list = impl_def.assoc_item_list().unwrap_or_else(make::assoc_item_list); | ||
135 | |||
136 | let n_existing_items = impl_item_list.assoc_items().count(); | ||
137 | let source_scope = sema.scope_for_def(trait_); | 134 | let source_scope = sema.scope_for_def(trait_); |
138 | let ast_transform = QualifyPaths::new(&target_scope, &source_scope) | 135 | let ast_transform = QualifyPaths::new(&target_scope, &source_scope) |
139 | .or(SubstituteTypeParams::for_trait_impl(&source_scope, trait_, impl_def.clone())); | 136 | .or(SubstituteTypeParams::for_trait_impl(&source_scope, trait_, impl_.clone())); |
140 | 137 | ||
141 | let items = items | 138 | let items = items |
142 | .into_iter() | 139 | .into_iter() |
143 | .map(|it| it.clone_for_update()) | 140 | .map(|it| it.clone_for_update()) |
144 | .inspect(|it| ast_transform::apply(&*ast_transform, it)) | 141 | .inspect(|it| ast_transform::apply(&*ast_transform, it)) |
145 | .map(|it| match it { | 142 | .map(|it| edit::remove_attrs_and_docs(&it).clone_subtree().clone_for_update()); |
146 | ast::AssocItem::Fn(def) => ast::AssocItem::Fn(add_body(def)), | 143 | |
147 | ast::AssocItem::TypeAlias(def) => ast::AssocItem::TypeAlias(def.remove_bounds()), | 144 | let res = impl_.clone_for_update(); |
148 | _ => it, | 145 | |
149 | }) | 146 | let assoc_item_list = res.get_or_create_assoc_item_list(); |
150 | .map(|it| edit::remove_attrs_and_docs(&it)); | 147 | let mut first_item = None; |
151 | 148 | for item in items { | |
152 | let new_impl_item_list = impl_item_list.append_items(items); | 149 | first_item.get_or_insert_with(|| item.clone()); |
153 | let new_impl_def = impl_def.with_assoc_item_list(new_impl_item_list); | 150 | match &item { |
154 | let first_new_item = | 151 | ast::AssocItem::Fn(fn_) if fn_.body().is_none() => { |
155 | new_impl_def.assoc_item_list().unwrap().assoc_items().nth(n_existing_items).unwrap(); | 152 | let body = make::block_expr(None, Some(make::ext::expr_todo())) |
156 | return (new_impl_def, first_new_item); | 153 | .indent(edit::IndentLevel(1)); |
157 | 154 | ted::replace(fn_.get_or_create_body().syntax(), body.clone_for_update().syntax()) | |
158 | fn add_body(fn_def: ast::Fn) -> ast::Fn { | 155 | } |
159 | match fn_def.body() { | 156 | ast::AssocItem::TypeAlias(type_alias) => { |
160 | Some(_) => fn_def, | 157 | if let Some(type_bound_list) = type_alias.type_bound_list() { |
161 | None => { | 158 | type_bound_list.remove() |
162 | let body = | 159 | } |
163 | make::block_expr(None, Some(make::expr_todo())).indent(edit::IndentLevel(1)); | ||
164 | fn_def.with_body(body) | ||
165 | } | 160 | } |
161 | _ => {} | ||
166 | } | 162 | } |
163 | |||
164 | assoc_item_list.add_item(item) | ||
167 | } | 165 | } |
166 | |||
167 | (res, first_item.unwrap()) | ||
168 | } | 168 | } |
169 | 169 | ||
170 | #[derive(Clone, Copy, Debug)] | 170 | #[derive(Clone, Copy, Debug)] |
diff --git a/crates/ide_assists/src/utils/suggest_name.rs b/crates/ide_assists/src/utils/suggest_name.rs index deafcd630..b3aabeab3 100644 --- a/crates/ide_assists/src/utils/suggest_name.rs +++ b/crates/ide_assists/src/utils/suggest_name.rs | |||
@@ -6,7 +6,7 @@ use itertools::Itertools; | |||
6 | use stdx::to_lower_snake_case; | 6 | use stdx::to_lower_snake_case; |
7 | use syntax::{ | 7 | use syntax::{ |
8 | ast::{self, NameOwner}, | 8 | ast::{self, NameOwner}, |
9 | match_ast, AstNode, | 9 | match_ast, AstNode, SmolStr, |
10 | }; | 10 | }; |
11 | 11 | ||
12 | /// Trait names, that will be ignored when in `impl Trait` and `dyn Trait` | 12 | /// Trait names, that will be ignored when in `impl Trait` and `dyn Trait` |
@@ -57,6 +57,14 @@ const USELESS_METHODS: &[&str] = &[ | |||
57 | "iter_mut", | 57 | "iter_mut", |
58 | ]; | 58 | ]; |
59 | 59 | ||
60 | pub(crate) fn for_generic_parameter(ty: &ast::ImplTraitType) -> SmolStr { | ||
61 | let c = ty | ||
62 | .type_bound_list() | ||
63 | .and_then(|bounds| bounds.syntax().text().char_at(0.into())) | ||
64 | .unwrap_or('T'); | ||
65 | c.encode_utf8(&mut [0; 4]).into() | ||
66 | } | ||
67 | |||
60 | /// Suggest name of variable for given expression | 68 | /// Suggest name of variable for given expression |
61 | /// | 69 | /// |
62 | /// **NOTE**: it is caller's responsibility to guarantee uniqueness of the name. | 70 | /// **NOTE**: it is caller's responsibility to guarantee uniqueness of the name. |
@@ -75,7 +83,8 @@ const USELESS_METHODS: &[&str] = &[ | |||
75 | /// It also applies heuristics to filter out less informative names | 83 | /// It also applies heuristics to filter out less informative names |
76 | /// | 84 | /// |
77 | /// Currently it sticks to the first name found. | 85 | /// Currently it sticks to the first name found. |
78 | pub(crate) fn variable(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> String { | 86 | // FIXME: Microoptimize and return a `SmolStr` here. |
87 | pub(crate) fn for_variable(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> String { | ||
79 | // `from_param` does not benifit from stripping | 88 | // `from_param` does not benifit from stripping |
80 | // it need the largest context possible | 89 | // it need the largest context possible |
81 | // so we check firstmost | 90 | // so we check firstmost |
@@ -276,7 +285,7 @@ mod tests { | |||
276 | frange.range, | 285 | frange.range, |
277 | "selection is not an expression(yet contained in one)" | 286 | "selection is not an expression(yet contained in one)" |
278 | ); | 287 | ); |
279 | let name = variable(&expr, &sema); | 288 | let name = for_variable(&expr, &sema); |
280 | assert_eq!(&name, expected); | 289 | assert_eq!(&name, expected); |
281 | } | 290 | } |
282 | 291 | ||