aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_assists/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_assists/src')
-rw-r--r--crates/ide_assists/src/assist_context.rs14
-rw-r--r--crates/ide_assists/src/handlers/add_explicit_type.rs28
-rw-r--r--crates/ide_assists/src/handlers/add_missing_impl_members.rs3
-rw-r--r--crates/ide_assists/src/handlers/auto_import.rs30
-rw-r--r--crates/ide_assists/src/handlers/convert_tuple_struct_to_named_struct.rs2
-rw-r--r--crates/ide_assists/src/handlers/early_return.rs4
-rw-r--r--crates/ide_assists/src/handlers/expand_glob_import.rs89
-rw-r--r--crates/ide_assists/src/handlers/extract_function.rs164
-rw-r--r--crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs141
-rw-r--r--crates/ide_assists/src/handlers/extract_variable.rs2
-rw-r--r--crates/ide_assists/src/handlers/fill_match_arms.rs633
-rw-r--r--crates/ide_assists/src/handlers/generate_default_from_new.rs287
-rw-r--r--crates/ide_assists/src/handlers/generate_function.rs2
-rw-r--r--crates/ide_assists/src/handlers/generate_new.rs153
-rw-r--r--crates/ide_assists/src/handlers/introduce_named_lifetime.rs58
-rw-r--r--crates/ide_assists/src/handlers/merge_imports.rs12
-rw-r--r--crates/ide_assists/src/handlers/move_bounds.rs10
-rw-r--r--crates/ide_assists/src/handlers/move_module_to_file.rs38
-rw-r--r--crates/ide_assists/src/handlers/pull_assignment_up.rs212
-rw-r--r--crates/ide_assists/src/handlers/raw_string.rs2
-rw-r--r--crates/ide_assists/src/handlers/reorder_fields.rs4
-rw-r--r--crates/ide_assists/src/handlers/reorder_impl.rs3
-rw-r--r--crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs8
-rw-r--r--crates/ide_assists/src/handlers/replace_impl_trait_with_generic.rs120
-rw-r--r--crates/ide_assists/src/handlers/replace_let_with_if_let.rs21
-rw-r--r--crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs2
-rw-r--r--crates/ide_assists/src/handlers/replace_string_with_char.rs2
-rw-r--r--crates/ide_assists/src/handlers/replace_unwrap_with_match.rs23
-rw-r--r--crates/ide_assists/src/handlers/wrap_return_type_in_result.rs4
-rw-r--r--crates/ide_assists/src/tests.rs2
-rw-r--r--crates/ide_assists/src/tests/generated.rs3
-rw-r--r--crates/ide_assists/src/utils.rs52
-rw-r--r--crates/ide_assists/src/utils/suggest_name.rs15
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};
15use syntax::{ 15use 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#"
207fn main() {
208 let $0v = [0.0; 2];
209}
210"#,
211 r#"
212fn 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#"
222fn 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
979mod foo {}
980
981const _: () = {
982 Foo$0
983};
984//- /foo.rs crate:foo
985pub struct Foo
986"#,
987 r#"
988use ::foo::Foo;
989
990mod foo {}
991
992const _: () = {
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};
7use stdx::never;
7use syntax::{ 8use 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
13use crate::{ 13use crate::{
@@ -42,6 +42,7 @@ use crate::{
42// ``` 42// ```
43pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 43pub(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
235fn 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)]
283mod tests { 258mod 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
353use foo::{f, Baz, Bar}; 328use foo::{Baz, Bar, f};
354 329
355fn qux(bar: Bar, baz: Baz) { 330fn qux(bar: Bar, baz: Baz) {
356 f(); 331 f();
@@ -389,7 +364,7 @@ mod foo {
389} 364}
390 365
391use foo::Bar; 366use foo::Bar;
392use foo::{f, Baz}; 367use foo::{Baz, f};
393 368
394fn qux(bar: Bar, baz: Baz) { 369fn qux(bar: Bar, baz: Baz) {
395 f(); 370 f();
@@ -439,7 +414,7 @@ mod foo {
439 } 414 }
440} 415}
441 416
442use foo::{bar::{f, Baz, Bar}, baz::*}; 417use foo::{bar::{Baz, Bar, f}, baz::*};
443 418
444fn qux(bar: Bar, baz: Baz) { 419fn 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
894use foo::Bar; 869use foo::{Bar};
895 870
896struct Baz { 871struct 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::{
10use itertools::Itertools; 10use itertools::Itertools;
11use stdx::format_to; 11use stdx::format_to;
12use syntax::{ 12use 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
1111fn make_path_from_text(text: &str) -> ast::Path {
1112 make::path_unqualified(make::path_segment(make::name_ref(text)))
1113}
1114
1115fn path_expr_from_local(ctx: &AssistContext, var: Local) -> ast::Expr { 1110fn 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
1120fn format_function( 1115fn 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
1418fn update_external_control_flow(handler: &FlowHandler, syntax: &SyntaxNode) -> SyntaxNode { 1406fn 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
1489fn make_rewritten_flow(handler: &FlowHandler, arg_expr: Option<ast::Expr>) -> Option<ast::Expr> { 1467fn 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#"
319enum A {
320 $0One {
321 // leading comment
322 /// doc comment
323 #[an_attr]
324 foo: u32
325 // trailing comment
326 }
327}"#,
328 r#"
329struct One{
330 // leading comment
331 /// doc comment
332 #[an_attr]
333 pub foo: u32
334 // trailing comment
335 }
336
337enum 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#"
348enum 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#"
361struct One{
362 // comment
363 /// doc
364 #[attr]
365 pub foo: u32,
366 // comment
367 #[attr]
368 /// doc
369 pub bar: u32
370 }
371
372enum 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#"
384struct One(/* comment */ #[attr] pub u32, /* another */ pub u32 /* tail */);
385
386enum 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#"
396struct One{ pub a: u32, pub(crate) b: u32, pub(super) c: u32, pub d: u32 }
397
398enum 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#"
408struct One(pub u32, pub(crate) u32, pub(super) u32, pub u32);
409
410enum 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 { 278enum 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() { 283fn 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) { 299fn 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() { 316fn 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) { 329fn 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) { 335fn 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) { 350fn 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) { 357fn 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) { 372fn 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) { 389fn 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) { 395fn 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) { 412fn 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) { 419fn 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 { 436enum 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() { 441fn 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 { 449enum 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() { 454fn 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 } 608enum A { One, Two }
597 enum B { One, Two } 609enum B { One, Two }
598 610
599 fn main() { 611fn 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 } 618enum A { One, Two }
607 enum B { One, Two } 619enum B { One, Two }
608 620
609 fn main() { 621fn 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 } 640enum A { One, Two }
629 enum B { One, Two } 641enum B { One, Two }
630 642
631 fn main() { 643fn 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 } 650enum A { One, Two }
639 enum B { One, Two } 651enum B { One, Two }
640 652
641 fn main() { 653fn 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 } 752enum A { One, Two }
741 enum B { One, Two } 753enum B { One, Two }
742 754
743 fn main() { 755fn 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 } 774enum A { One, Two }
763 775
764 fn main() { 776fn 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 } 783enum A { One, Two }
772 784
773 fn main() { 785fn 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 } 801enum A { As }
790 802
791 fn foo(a: &A) { 803fn 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 } 809enum A { As }
798 810
799 fn foo(a: &A) { 811fn 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 { 822enum A {
811 Es { x: usize, y: usize } 823 Es { x: usize, y: usize }
812 } 824}
813 825
814 fn foo(a: &mut A) { 826fn 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 { 832enum A {
821 Es { x: usize, y: usize } 833 Es { x: usize, y: usize }
822 } 834}
823 835
824 fn foo(a: &mut A) { 836fn 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 } 850enum E { X, Y }
839 851
840 fn main() { 852fn 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 } 865enum E { X, Y }
854 866
855 fn main() { 867fn 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 } 874enum E { X, Y }
863 875
864 fn main() { 876fn 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 } } 891mod foo { pub enum E { X, Y } }
880 use foo::E::X; 892use foo::E::X;
881 893
882 fn main() { 894fn 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 } } 901mod foo { pub enum E { X, Y } }
890 use foo::E::X; 902use foo::E::X;
891 903
892 fn main() { 904fn 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 } 919enum A { One, Two }
908 fn foo(a: A) { 920fn 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 } 929enum A { One, Two }
918 fn foo(a: A) { 930fn 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 } 947enum A { One, Two }
936 fn foo(a: A) { 948fn 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 } 955enum A { One, Two }
944 fn foo(a: A) { 956fn 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, } 972enum A { One, Two, }
961 fn foo(a: A) { 973fn 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, } 980enum A { One, Two, }
969 fn foo(a: A) { 981fn 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 {
1016fn foo(t: Test) { 1028fn 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#"
1032macro_rules! m { ($expr:expr) => {$expr}}
1020enum Test { 1033enum 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};
5use ide_db::helpers::FamousDefs; 5use ide_db::helpers::FamousDefs;
6use itertools::Itertools;
7use stdx::format_to;
6use syntax::{ 8use 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
74fn default_fn_node_for_new(impl_: Impl) -> String { 79fn 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
78impl 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
87fn is_default_implemented(ctx: &AssistContext, impl_: &Impl) -> bool { 122fn 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#"
217pub struct Foo<T> {
218 _bar: *mut T,
219}
220
221impl<T> Foo<T> {
222 pub fn ne$0w() -> Self {
223 unimplemented!()
224 }
225}
226"#,
227 r#"
228pub struct Foo<T> {
229 _bar: *mut T,
230}
231
232impl<T> Foo<T> {
233 pub fn new() -> Self {
234 unimplemented!()
235 }
236}
237
238impl<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#"
251pub struct Foo<T, B> {
252 _tars: *mut T,
253 _bar: *mut B,
254}
255
256impl<T, B> Foo<T, B> {
257 pub fn ne$0w() -> Self {
258 unimplemented!()
259 }
260}
261"#,
262 r#"
263pub struct Foo<T, B> {
264 _tars: *mut T,
265 _bar: *mut B,
266}
267
268impl<T, B> Foo<T, B> {
269 pub fn new() -> Self {
270 unimplemented!()
271 }
272}
273
274impl<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#"
287pub struct Foo<T> {
288 t: T,
289}
290
291impl<T: From<i32>> Foo<T> {
292 pub fn ne$0w() -> Self {
293 Foo { t: 0.into() }
294 }
295}
296"#,
297 r#"
298pub struct Foo<T> {
299 t: T,
300}
301
302impl<T: From<i32>> Foo<T> {
303 pub fn new() -> Self {
304 Foo { t: 0.into() }
305 }
306}
307
308impl<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#"
321pub struct Foo<T, B> {
322 _tars: T,
323 _bar: B,
324}
325
326impl<T: From<i32>, B: From<i64>> Foo<T, B> {
327 pub fn ne$0w() -> Self {
328 unimplemented!()
329 }
330}
331"#,
332 r#"
333pub struct Foo<T, B> {
334 _tars: T,
335 _bar: B,
336}
337
338impl<T: From<i32>, B: From<i64>> Foo<T, B> {
339 pub fn new() -> Self {
340 unimplemented!()
341 }
342}
343
344impl<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#"
357pub struct Foo<T> {
358 t: T,
359}
360
361impl<T: From<i32>> Foo<T>
362where
363 Option<T>: Debug
364{
365 pub fn ne$0w() -> Self {
366 Foo { t: 0.into() }
367 }
368}
369"#,
370 r#"
371pub struct Foo<T> {
372 t: T,
373}
374
375impl<T: From<i32>> Foo<T>
376where
377 Option<T>: Debug
378{
379 pub fn new() -> Self {
380 Foo { t: 0.into() }
381 }
382}
383
384impl<T: From<i32>> Default for Foo<T>
385where
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#"
400pub struct Foo<T, B> {
401 _tars: T,
402 _bar: B,
403}
404
405impl<T: From<i32>, B: From<i64>> Foo<T, B>
406where
407 Option<T>: Debug, Option<B>: Debug,
408{
409 pub fn ne$0w() -> Self {
410 unimplemented!()
411 }
412}
413"#,
414 r#"
415pub struct Foo<T, B> {
416 _tars: T,
417 _bar: B,
418}
419
420impl<T: From<i32>, B: From<i64>> Foo<T, B>
421where
422 Option<T>: Debug, Option<B>: Debug,
423{
424 pub fn new() -> Self {
425 unimplemented!()
426 }
427}
428
429impl<T: From<i32>, B: From<i64>> Default for Foo<T, B>
430where
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 @@
1use ast::Adt;
2use itertools::Itertools; 1use itertools::Itertools;
3use stdx::format_to; 2use stdx::format_to;
4use syntax::ast::{self, AstNode, NameOwner, StructKind, VisibilityOwner}; 3use 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 {} 87struct Foo {$0}
88"#,
89 r#"
90struct Foo {}
91 91
92impl Foo { 92impl 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> {} 100struct Foo<T: Clone> {$0}
101"#,
102 r#"
103struct Foo<T: Clone> {}
100 104
101impl<T: Clone> Foo<T> { 105impl<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>> {} 113struct Foo<'a, T: Foo<'a>> {$0}
114"#,
115 r#"
116struct Foo<'a, T: Foo<'a>> {}
109 117
110impl<'a, T: Foo<'a>> Foo<'a, T> { 118impl<'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 } 126struct Foo { baz: String $0}
127"#,
128 r#"
129struct Foo { baz: String }
118 130
119impl Foo { 131impl 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> } 139struct Foo { baz: String, qux: Vec<i32> $0}
140"#,
141 r#"
142struct Foo { baz: String, qux: Vec<i32> }
127 143
128impl Foo { 144impl 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> } 156struct Foo { pub baz: String, pub qux: Vec<i32> $0}
157"#,
158 r#"
159struct Foo { pub baz: String, pub qux: Vec<i32> }
138 160
139impl Foo { 161impl 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#"
173struct Foo {$0}
148 174
149impl Foo {} 175impl Foo {}
150", 176"#,
151"struct Foo {} 177 r#"
178struct Foo {}
152 179
153impl Foo { 180impl 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#"
188struct Foo {$0}
161 189
162impl Foo { 190impl Foo {
163 fn qux(&self) {} 191 fn qux(&self) {}
164} 192}
165", 193"#,
166"struct Foo {} 194 r#"
195struct Foo {}
167 196
168impl Foo { 197impl 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#"
208struct Foo {$0}
179 209
180impl Foo { 210impl 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#"
218struct Foo {}
188 219
189impl Foo { 220impl 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 {} 237pub struct Foo {$0}
238"#,
239 r#"
240pub struct Foo {}
205 241
206impl Foo { 242impl 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 {} 250pub(crate) struct Foo {$0}
251"#,
252 r#"
253pub(crate) struct Foo {}
214 254
215impl Foo { 255impl 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#"
226struct Foo {$0} 267struct Foo {$0}
227 268
228impl Foo { 269impl 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#"
238struct Foo {$0} 280struct Foo {$0}
239 281
240impl Foo { 282impl 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#"
253struct SomeThingIrrelevant; 296struct SomeThingIrrelevant;
254/// Has a lifetime parameter 297/// Has a lifetime parameter
255struct Foo<'a, T: Foo<'a>> {$0} 298struct Foo<'a, T: Foo<'a>> {$0}
256struct EvenMoreIrrelevant; 299struct EvenMoreIrrelevant;
257", 300"#,
258 "/// Has a lifetime parameter 301 "/// Has a lifetime parameter
259struct Foo<'a, T: Foo<'a>> {}", 302struct 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#"
268pub struct AstId<N: AstNode> { 311pub 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#"
290pub struct AstId<N: AstNode> { 334pub 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
125fn generate_unique_lifetime_param_name( 122fn 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
140fn 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
146fn 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
154enum NeedsLifetime { 136enum NeedsLifetime {
@@ -159,8 +141,8 @@ enum NeedsLifetime {
159impl NeedsLifetime { 141impl 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
65fn build_predicate(param: ast::TypeParam) -> Option<ast::WherePred> { 65fn build_predicate(param: ast::TypeParam) -> Option<ast::WherePred> {
66 let path = { 66 let path = make::ext::ident_path(&param.name()?.syntax().to_string());
67 let name_ref = make::name_ref(&param.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 @@
1use ast::{edit::IndentLevel, VisibilityOwner}; 1use ast::edit::IndentLevel;
2use ide_db::base_db::AnchoredPathBuf; 2use ide_db::base_db::AnchoredPathBuf;
3use stdx::format_to; 3use stdx::format_to;
4use syntax::{ 4use 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]
201mod $0tests {
202 #[test] fn t() {}
203}
204"#,
205 r#"
206//- /main.rs
207/// doc comment
208#[attribute]
209mod 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 @@
1use syntax::{ 1use syntax::{
2 ast::{self, edit::AstNodeEdit, make}, 2 ast::{self, make},
3 AstNode, 3 ted, AstNode,
4}; 4};
5 5
6use crate::{ 6use crate::{
@@ -37,103 +37,120 @@ use crate::{
37// ``` 37// ```
38pub(crate) fn pull_assignment_up(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 38pub(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
72fn exprify_match( 99struct 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()? 105impl<'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
95fn 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
115fn 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
139fn is_equivalent( 156fn 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#"
267fn 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#"
279fn 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#"
497fn 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 @@
1use std::borrow::Cow; 1use std::borrow::Cow;
2 2
3use syntax::{ast, AstToken, TextRange, TextSize}; 3use syntax::{ast, ast::IsString, AstToken, TextRange, TextSize};
4 4
5use crate::{AssistContext, AssistId, AssistKind, Assists}; 5use 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 @@
1use syntax::ast::{self, edit::AstNodeEdit, make, AstNode, GenericParamsOwner}; 1use syntax::{
2 ast::{self, edit_in_place::GenericParamsOwnerEdit, make, AstNode},
3 ted,
4};
2 5
3use crate::{AssistContext, AssistId, AssistKind, Assists}; 6use 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< 97fn foo<
112 >(bar: $0impl Bar) {} 98>(bar: $0impl Bar) {}
113 "#, 99"#,
114 r#" 100 r#"
115 fn foo<B: Bar 101fn 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< 122fn 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< 129fn 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 @@
1use syntax::{ast, AstToken, SyntaxKind::STRING}; 1use syntax::{ast, ast::IsString, AstToken, SyntaxKind::STRING};
2 2
3use crate::{AssistContext, AssistId, AssistKind, Assists}; 3use 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 }
110fn main() { 111fn 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 }
136fn main() { 137fn 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 }
162fn main() { 163fn 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 {
50impl Trait for () { 50impl 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) }
1531fn main() { 1530fn 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;
6use stdx::to_lower_snake_case; 6use stdx::to_lower_snake_case;
7use syntax::{ 7use 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
60pub(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.
78pub(crate) fn variable(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> String { 86// FIXME: Microoptimize and return a `SmolStr` here.
87pub(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