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.rs57
-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.rs197
-rw-r--r--crates/ide_assists/src/handlers/extract_variable.rs2
-rw-r--r--crates/ide_assists/src/handlers/generate_function.rs2
-rw-r--r--crates/ide_assists/src/handlers/introduce_named_lifetime.rs46
-rw-r--r--crates/ide_assists/src/handlers/merge_imports.rs4
-rw-r--r--crates/ide_assists/src/handlers/move_bounds.rs6
-rw-r--r--crates/ide_assists/src/handlers/pull_assignment_up.rs212
-rw-r--r--crates/ide_assists/src/handlers/reorder_impl.rs40
-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_unwrap_with_match.rs21
-rw-r--r--crates/ide_assists/src/handlers/wrap_return_type_in_result.rs4
-rw-r--r--crates/ide_assists/src/lib.rs68
-rw-r--r--crates/ide_assists/src/tests.rs264
-rw-r--r--crates/ide_assists/src/tests/generated.rs2
-rw-r--r--crates/ide_assists/src/utils.rs4
-rw-r--r--crates/ide_assists/src/utils/suggest_name.rs15
22 files changed, 765 insertions, 423 deletions
diff --git a/crates/ide_assists/src/assist_context.rs b/crates/ide_assists/src/assist_context.rs
index 8714e4978..682f0ff5e 100644
--- a/crates/ide_assists/src/assist_context.rs
+++ b/crates/ide_assists/src/assist_context.rs
@@ -13,13 +13,15 @@ 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};
20use text_edit::{TextEdit, TextEditBuilder}; 20use text_edit::{TextEdit, TextEditBuilder};
21 21
22use crate::{assist_config::AssistConfig, Assist, AssistId, AssistKind, GroupLabel}; 22use crate::{
23 assist_config::AssistConfig, Assist, AssistId, AssistKind, AssistResolveStrategy, GroupLabel,
24};
23 25
24/// `AssistContext` allows to apply an assist or check if it could be applied. 26/// `AssistContext` allows to apply an assist or check if it could be applied.
25/// 27///
@@ -105,14 +107,14 @@ impl<'a> AssistContext<'a> {
105} 107}
106 108
107pub(crate) struct Assists { 109pub(crate) struct Assists {
108 resolve: bool,
109 file: FileId, 110 file: FileId,
111 resolve: AssistResolveStrategy,
110 buf: Vec<Assist>, 112 buf: Vec<Assist>,
111 allowed: Option<Vec<AssistKind>>, 113 allowed: Option<Vec<AssistKind>>,
112} 114}
113 115
114impl Assists { 116impl Assists {
115 pub(crate) fn new(ctx: &AssistContext, resolve: bool) -> Assists { 117 pub(crate) fn new(ctx: &AssistContext, resolve: AssistResolveStrategy) -> Assists {
116 Assists { 118 Assists {
117 resolve, 119 resolve,
118 file: ctx.frange.file_id, 120 file: ctx.frange.file_id,
@@ -158,7 +160,7 @@ impl Assists {
158 } 160 }
159 161
160 fn add_impl(&mut self, mut assist: Assist, f: impl FnOnce(&mut AssistBuilder)) -> Option<()> { 162 fn add_impl(&mut self, mut assist: Assist, f: impl FnOnce(&mut AssistBuilder)) -> Option<()> {
161 let source_change = if self.resolve { 163 let source_change = if self.resolve.should_resolve(&assist.id) {
162 let mut builder = AssistBuilder::new(self.file); 164 let mut builder = AssistBuilder::new(self.file);
163 f(&mut builder); 165 f(&mut builder);
164 Some(builder.finish()) 166 Some(builder.finish())
@@ -185,7 +187,29 @@ pub(crate) struct AssistBuilder {
185 source_change: SourceChange, 187 source_change: SourceChange,
186 188
187 /// Maps the original, immutable `SyntaxNode` to a `clone_for_update` twin. 189 /// Maps the original, immutable `SyntaxNode` to a `clone_for_update` twin.
188 mutated_tree: Option<(SyntaxNode, SyntaxNode)>, 190 mutated_tree: Option<TreeMutator>,
191}
192
193pub(crate) struct TreeMutator {
194 immutable: SyntaxNode,
195 mutable_clone: SyntaxNode,
196}
197
198impl TreeMutator {
199 pub(crate) fn new(immutable: &SyntaxNode) -> TreeMutator {
200 let immutable = immutable.ancestors().last().unwrap();
201 let mutable_clone = immutable.clone_for_update();
202 TreeMutator { immutable, mutable_clone }
203 }
204
205 pub(crate) fn make_mut<N: AstNode>(&self, node: &N) -> N {
206 N::cast(self.make_syntax_mut(node.syntax())).unwrap()
207 }
208
209 pub(crate) fn make_syntax_mut(&self, node: &SyntaxNode) -> SyntaxNode {
210 let ptr = SyntaxNodePtr::new(node);
211 ptr.to_node(&self.mutable_clone)
212 }
189} 213}
190 214
191impl AssistBuilder { 215impl AssistBuilder {
@@ -204,8 +228,8 @@ impl AssistBuilder {
204 } 228 }
205 229
206 fn commit(&mut self) { 230 fn commit(&mut self) {
207 if let Some((old, new)) = self.mutated_tree.take() { 231 if let Some(tm) = self.mutated_tree.take() {
208 algo::diff(&old, &new).into_text_edit(&mut self.edit) 232 algo::diff(&tm.immutable, &tm.mutable_clone).into_text_edit(&mut self.edit)
209 } 233 }
210 234
211 let edit = mem::take(&mut self.edit).finish(); 235 let edit = mem::take(&mut self.edit).finish();
@@ -228,16 +252,7 @@ impl AssistBuilder {
228 /// 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
229 /// mutable state. 253 /// mutable state.
230 pub(crate) fn make_mut(&mut self, node: SyntaxNode) -> SyntaxNode { 254 pub(crate) fn make_mut(&mut self, node: SyntaxNode) -> SyntaxNode {
231 let root = &self 255 self.mutated_tree.get_or_insert_with(|| TreeMutator::new(&node)).make_syntax_mut(&node)
232 .mutated_tree
233 .get_or_insert_with(|| {
234 let immutable = node.ancestors().last().unwrap();
235 let mutable = immutable.clone_for_update();
236 (immutable, mutable)
237 })
238 .1;
239 let ptr = SyntaxNodePtr::new(&&node);
240 ptr.to_node(root)
241 } 256 }
242 257
243 /// Remove specified `range` of text. 258 /// Remove specified `range` of text.
@@ -275,12 +290,6 @@ impl AssistBuilder {
275 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) {
276 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)
277 } 292 }
278 pub(crate) fn rewrite(&mut self, rewriter: SyntaxRewriter) {
279 if let Some(node) = rewriter.rewrite_root() {
280 let new = rewriter.rewrite(&node);
281 algo::diff(&node, &new).into_text_edit(&mut self.edit);
282 }
283 }
284 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>) {
285 let file_system_edit = 294 let file_system_edit =
286 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/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..da8d245e5 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_ast_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 b30652a9d..6311afc1f 100644
--- a/crates/ide_assists/src/handlers/extract_function.rs
+++ b/crates/ide_assists/src/handlers/extract_function.rs
@@ -10,18 +10,18 @@ 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},
17 AstNode, 16 AstNode,
18 }, 17 },
18 ted,
19 SyntaxKind::{self, BLOCK_EXPR, BREAK_EXPR, COMMENT, PATH_EXPR, RETURN_EXPR}, 19 SyntaxKind::{self, BLOCK_EXPR, BREAK_EXPR, COMMENT, PATH_EXPR, RETURN_EXPR},
20 SyntaxNode, SyntaxToken, TextRange, TextSize, TokenAtOffset, WalkEvent, T, 20 SyntaxNode, SyntaxToken, TextRange, TextSize, TokenAtOffset, WalkEvent, T,
21}; 21};
22 22
23use crate::{ 23use crate::{
24 assist_context::{AssistContext, Assists}, 24 assist_context::{AssistContext, Assists, TreeMutator},
25 AssistId, 25 AssistId,
26}; 26};
27 27
@@ -956,10 +956,10 @@ fn format_replacement(ctx: &AssistContext, fun: &Function, indent: IndentLevel)
956 let args = fun.params.iter().map(|param| param.to_arg(ctx)); 956 let args = fun.params.iter().map(|param| param.to_arg(ctx));
957 let args = make::arg_list(args); 957 let args = make::arg_list(args);
958 let call_expr = if fun.self_param.is_some() { 958 let call_expr = if fun.self_param.is_some() {
959 let self_arg = make::expr_path(make_path_from_text("self")); 959 let self_arg = make::expr_path(make::ext::ident_path("self"));
960 make::expr_method_call(self_arg, &fun.name, args) 960 make::expr_method_call(self_arg, &fun.name, args)
961 } else { 961 } else {
962 let func = make::expr_path(make_path_from_text(&fun.name)); 962 let func = make::expr_path(make::ext::ident_path(&fun.name));
963 make::expr_call(func, args) 963 make::expr_call(func, args)
964 }; 964 };
965 965
@@ -1054,11 +1054,11 @@ impl FlowHandler {
1054 make::expr_if(condition, block, None) 1054 make::expr_if(condition, block, None)
1055 } 1055 }
1056 FlowHandler::IfOption { action } => { 1056 FlowHandler::IfOption { action } => {
1057 let path = make_path_from_text("Some"); 1057 let path = make::ext::ident_path("Some");
1058 let value_pat = make::ident_pat(make::name("value")); 1058 let value_pat = make::ident_pat(make::name("value"));
1059 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()));
1060 let cond = make::condition(call_expr, Some(pattern.into())); 1060 let cond = make::condition(call_expr, Some(pattern.into()));
1061 let value = make::expr_path(make_path_from_text("value")); 1061 let value = make::expr_path(make::ext::ident_path("value"));
1062 let action_expr = action.make_result_handler(Some(value)); 1062 let action_expr = action.make_result_handler(Some(value));
1063 let action_stmt = make::expr_stmt(action_expr); 1063 let action_stmt = make::expr_stmt(action_expr);
1064 let then = make::block_expr(iter::once(action_stmt.into()), None); 1064 let then = make::block_expr(iter::once(action_stmt.into()), None);
@@ -1068,14 +1068,14 @@ impl FlowHandler {
1068 let some_name = "value"; 1068 let some_name = "value";
1069 1069
1070 let some_arm = { 1070 let some_arm = {
1071 let path = make_path_from_text("Some"); 1071 let path = make::ext::ident_path("Some");
1072 let value_pat = make::ident_pat(make::name(some_name)); 1072 let value_pat = make::ident_pat(make::name(some_name));
1073 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()));
1074 let value = make::expr_path(make_path_from_text(some_name)); 1074 let value = make::expr_path(make::ext::ident_path(some_name));
1075 make::match_arm(iter::once(pat.into()), value) 1075 make::match_arm(iter::once(pat.into()), value)
1076 }; 1076 };
1077 let none_arm = { 1077 let none_arm = {
1078 let path = make_path_from_text("None"); 1078 let path = make::ext::ident_path("None");
1079 let pat = make::path_pat(path); 1079 let pat = make::path_pat(path);
1080 make::match_arm(iter::once(pat), none.make_result_handler(None)) 1080 make::match_arm(iter::once(pat), none.make_result_handler(None))
1081 }; 1081 };
@@ -1087,17 +1087,17 @@ impl FlowHandler {
1087 let err_name = "value"; 1087 let err_name = "value";
1088 1088
1089 let ok_arm = { 1089 let ok_arm = {
1090 let path = make_path_from_text("Ok"); 1090 let path = make::ext::ident_path("Ok");
1091 let value_pat = make::ident_pat(make::name(ok_name)); 1091 let value_pat = make::ident_pat(make::name(ok_name));
1092 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()));
1093 let value = make::expr_path(make_path_from_text(ok_name)); 1093 let value = make::expr_path(make::ext::ident_path(ok_name));
1094 make::match_arm(iter::once(pat.into()), value) 1094 make::match_arm(iter::once(pat.into()), value)
1095 }; 1095 };
1096 let err_arm = { 1096 let err_arm = {
1097 let path = make_path_from_text("Err"); 1097 let path = make::ext::ident_path("Err");
1098 let value_pat = make::ident_pat(make::name(err_name)); 1098 let value_pat = make::ident_pat(make::name(err_name));
1099 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()));
1100 let value = make::expr_path(make_path_from_text(err_name)); 1100 let value = make::expr_path(make::ext::ident_path(err_name));
1101 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)))
1102 }; 1102 };
1103 let arms = make::match_arm_list(vec![ok_arm, err_arm]); 1103 let arms = make::match_arm_list(vec![ok_arm, err_arm]);
@@ -1107,13 +1107,9 @@ impl FlowHandler {
1107 } 1107 }
1108} 1108}
1109 1109
1110fn make_path_from_text(text: &str) -> ast::Path {
1111 make::path_unqualified(make::path_segment(make::name_ref(text)))
1112}
1113
1114fn path_expr_from_local(ctx: &AssistContext, var: Local) -> ast::Expr { 1110fn path_expr_from_local(ctx: &AssistContext, var: Local) -> ast::Expr {
1115 let name = var.name(ctx.db()).unwrap().to_string(); 1111 let name = var.name(ctx.db()).unwrap().to_string();
1116 make::expr_path(make_path_from_text(&name)) 1112 make::expr_path(make::ext::ident_path(&name))
1117} 1113}
1118 1114
1119fn format_function( 1115fn format_function(
@@ -1179,7 +1175,7 @@ fn make_ret_ty(ctx: &AssistContext, module: hir::Module, fun: &Function) -> Opti
1179 fun_ty.make_ty(ctx, module) 1175 fun_ty.make_ty(ctx, module)
1180 } 1176 }
1181 FlowHandler::Try { kind: TryKind::Option } => { 1177 FlowHandler::Try { kind: TryKind::Option } => {
1182 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))
1183 } 1179 }
1184 FlowHandler::Try { kind: TryKind::Result { ty: parent_ret_ty } } => { 1180 FlowHandler::Try { kind: TryKind::Result { ty: parent_ret_ty } } => {
1185 let handler_ty = parent_ret_ty 1181 let handler_ty = parent_ret_ty
@@ -1187,29 +1183,21 @@ fn make_ret_ty(ctx: &AssistContext, module: hir::Module, fun: &Function) -> Opti
1187 .nth(1) 1183 .nth(1)
1188 .map(|ty| make_ty(&ty, ctx, module)) 1184 .map(|ty| make_ty(&ty, ctx, module))
1189 .unwrap_or_else(make::ty_unit); 1185 .unwrap_or_else(make::ty_unit);
1190 make::ty_generic( 1186 make::ext::ty_result(fun_ty.make_ty(ctx, module), handler_ty)
1191 make::name_ref("Result"),
1192 vec![fun_ty.make_ty(ctx, module), handler_ty],
1193 )
1194 } 1187 }
1195 FlowHandler::If { .. } => make::ty("bool"), 1188 FlowHandler::If { .. } => make::ext::ty_bool(),
1196 FlowHandler::IfOption { action } => { 1189 FlowHandler::IfOption { action } => {
1197 let handler_ty = action 1190 let handler_ty = action
1198 .expr_ty(ctx) 1191 .expr_ty(ctx)
1199 .map(|ty| make_ty(&ty, ctx, module)) 1192 .map(|ty| make_ty(&ty, ctx, module))
1200 .unwrap_or_else(make::ty_unit); 1193 .unwrap_or_else(make::ty_unit);
1201 make::ty_generic(make::name_ref("Option"), iter::once(handler_ty)) 1194 make::ext::ty_option(handler_ty)
1202 }
1203 FlowHandler::MatchOption { .. } => {
1204 make::ty_generic(make::name_ref("Option"), iter::once(fun_ty.make_ty(ctx, module)))
1205 } 1195 }
1196 FlowHandler::MatchOption { .. } => make::ext::ty_option(fun_ty.make_ty(ctx, module)),
1206 FlowHandler::MatchResult { err } => { 1197 FlowHandler::MatchResult { err } => {
1207 let handler_ty = 1198 let handler_ty =
1208 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);
1209 make::ty_generic( 1200 make::ext::ty_result(fun_ty.make_ty(ctx, module), handler_ty)
1210 make::name_ref("Result"),
1211 vec![fun_ty.make_ty(ctx, module), handler_ty],
1212 )
1213 } 1201 }
1214 }; 1202 };
1215 Some(make::ret_type(ret_ty)) 1203 Some(make::ret_type(ret_ty))
@@ -1296,7 +1284,7 @@ fn make_body(
1296 TryKind::Option => "Some", 1284 TryKind::Option => "Some",
1297 TryKind::Result { .. } => "Ok", 1285 TryKind::Result { .. } => "Ok",
1298 }; 1286 };
1299 let func = make::expr_path(make_path_from_text(constructor)); 1287 let func = make::expr_path(make::ext::ident_path(constructor));
1300 let args = make::arg_list(iter::once(tail_expr)); 1288 let args = make::arg_list(iter::once(tail_expr));
1301 make::expr_call(func, args) 1289 make::expr_call(func, args)
1302 }) 1290 })
@@ -1306,16 +1294,16 @@ fn make_body(
1306 with_tail_expr(block, lit_false.into()) 1294 with_tail_expr(block, lit_false.into())
1307 } 1295 }
1308 FlowHandler::IfOption { .. } => { 1296 FlowHandler::IfOption { .. } => {
1309 let none = make::expr_path(make_path_from_text("None")); 1297 let none = make::expr_path(make::ext::ident_path("None"));
1310 with_tail_expr(block, none) 1298 with_tail_expr(block, none)
1311 } 1299 }
1312 FlowHandler::MatchOption { .. } => map_tail_expr(block, |tail_expr| { 1300 FlowHandler::MatchOption { .. } => map_tail_expr(block, |tail_expr| {
1313 let some = make::expr_path(make_path_from_text("Some")); 1301 let some = make::expr_path(make::ext::ident_path("Some"));
1314 let args = make::arg_list(iter::once(tail_expr)); 1302 let args = make::arg_list(iter::once(tail_expr));
1315 make::expr_call(some, args) 1303 make::expr_call(some, args)
1316 }), 1304 }),
1317 FlowHandler::MatchResult { .. } => map_tail_expr(block, |tail_expr| { 1305 FlowHandler::MatchResult { .. } => map_tail_expr(block, |tail_expr| {
1318 let ok = make::expr_path(make_path_from_text("Ok")); 1306 let ok = make::expr_path(make::ext::ident_path("Ok"));
1319 let args = make::arg_list(iter::once(tail_expr)); 1307 let args = make::arg_list(iter::once(tail_expr));
1320 make::expr_call(ok, args) 1308 make::expr_call(ok, args)
1321 }), 1309 }),
@@ -1361,12 +1349,16 @@ fn rewrite_body_segment(
1361 syntax: &SyntaxNode, 1349 syntax: &SyntaxNode,
1362) -> SyntaxNode { 1350) -> SyntaxNode {
1363 let syntax = fix_param_usages(ctx, params, syntax); 1351 let syntax = fix_param_usages(ctx, params, syntax);
1364 update_external_control_flow(handler, &syntax) 1352 update_external_control_flow(handler, &syntax);
1353 syntax
1365} 1354}
1366 1355
1367/// change all usages to account for added `&`/`&mut` for some params 1356/// change all usages to account for added `&`/`&mut` for some params
1368fn fix_param_usages(ctx: &AssistContext, params: &[Param], syntax: &SyntaxNode) -> SyntaxNode { 1357fn fix_param_usages(ctx: &AssistContext, params: &[Param], syntax: &SyntaxNode) -> SyntaxNode {
1369 let mut rewriter = SyntaxRewriter::default(); 1358 let mut usages_for_param: Vec<(&Param, Vec<ast::Expr>)> = Vec::new();
1359
1360 let tm = TreeMutator::new(syntax);
1361
1370 for param in params { 1362 for param in params {
1371 if !param.kind().is_ref() { 1363 if !param.kind().is_ref() {
1372 continue; 1364 continue;
@@ -1376,101 +1368,100 @@ fn fix_param_usages(ctx: &AssistContext, params: &[Param], syntax: &SyntaxNode)
1376 let usages = usages 1368 let usages = usages
1377 .iter() 1369 .iter()
1378 .filter(|reference| syntax.text_range().contains_range(reference.range)) 1370 .filter(|reference| syntax.text_range().contains_range(reference.range))
1379 .filter_map(|reference| path_element_of_reference(syntax, reference)); 1371 .filter_map(|reference| path_element_of_reference(syntax, reference))
1380 for path in usages { 1372 .map(|expr| tm.make_mut(&expr));
1381 match path.syntax().ancestors().skip(1).find_map(ast::Expr::cast) { 1373
1374 usages_for_param.push((param, usages.collect()));
1375 }
1376
1377 let res = tm.make_syntax_mut(syntax);
1378
1379 for (param, usages) in usages_for_param {
1380 for usage in usages {
1381 match usage.syntax().ancestors().skip(1).find_map(ast::Expr::cast) {
1382 Some(ast::Expr::MethodCallExpr(_)) | Some(ast::Expr::FieldExpr(_)) => { 1382 Some(ast::Expr::MethodCallExpr(_)) | Some(ast::Expr::FieldExpr(_)) => {
1383 // do nothing 1383 // do nothing
1384 } 1384 }
1385 Some(ast::Expr::RefExpr(node)) 1385 Some(ast::Expr::RefExpr(node))
1386 if param.kind() == ParamKind::MutRef && node.mut_token().is_some() => 1386 if param.kind() == ParamKind::MutRef && node.mut_token().is_some() =>
1387 { 1387 {
1388 rewriter.replace_ast(&node.clone().into(), &node.expr().unwrap()); 1388 ted::replace(node.syntax(), node.expr().unwrap().syntax());
1389 } 1389 }
1390 Some(ast::Expr::RefExpr(node)) 1390 Some(ast::Expr::RefExpr(node))
1391 if param.kind() == ParamKind::SharedRef && node.mut_token().is_none() => 1391 if param.kind() == ParamKind::SharedRef && node.mut_token().is_none() =>
1392 { 1392 {
1393 rewriter.replace_ast(&node.clone().into(), &node.expr().unwrap()); 1393 ted::replace(node.syntax(), node.expr().unwrap().syntax());
1394 } 1394 }
1395 Some(_) | None => { 1395 Some(_) | None => {
1396 rewriter.replace_ast(&path, &make::expr_prefix(T![*], path.clone())); 1396 let p = &make::expr_prefix(T![*], usage.clone()).clone_for_update();
1397 ted::replace(usage.syntax(), p.syntax())
1397 } 1398 }
1398 }; 1399 }
1399 } 1400 }
1400 } 1401 }
1401 1402
1402 rewriter.rewrite(syntax) 1403 res
1403} 1404}
1404 1405
1405fn update_external_control_flow(handler: &FlowHandler, syntax: &SyntaxNode) -> SyntaxNode { 1406fn update_external_control_flow(handler: &FlowHandler, syntax: &SyntaxNode) {
1406 let mut rewriter = SyntaxRewriter::default();
1407
1408 let mut nested_loop = None; 1407 let mut nested_loop = None;
1409 let mut nested_scope = None; 1408 let mut nested_scope = None;
1410 for event in syntax.preorder() { 1409 for event in syntax.preorder() {
1411 let node = match event { 1410 match event {
1412 WalkEvent::Enter(e) => { 1411 WalkEvent::Enter(e) => match e.kind() {
1413 match e.kind() { 1412 SyntaxKind::LOOP_EXPR | SyntaxKind::WHILE_EXPR | SyntaxKind::FOR_EXPR => {
1414 SyntaxKind::LOOP_EXPR | SyntaxKind::WHILE_EXPR | SyntaxKind::FOR_EXPR => { 1413 if nested_loop.is_none() {
1415 if nested_loop.is_none() { 1414 nested_loop = Some(e.clone());
1416 nested_loop = Some(e.clone());
1417 }
1418 } 1415 }
1419 SyntaxKind::FN 1416 }
1420 | SyntaxKind::CONST 1417 SyntaxKind::FN
1421 | SyntaxKind::STATIC 1418 | SyntaxKind::CONST
1422 | SyntaxKind::IMPL 1419 | SyntaxKind::STATIC
1423 | SyntaxKind::MODULE => { 1420 | SyntaxKind::IMPL
1424 if nested_scope.is_none() { 1421 | SyntaxKind::MODULE => {
1425 nested_scope = Some(e.clone()); 1422 if nested_scope.is_none() {
1426 } 1423 nested_scope = Some(e.clone());
1427 } 1424 }
1428 _ => {}
1429 } 1425 }
1430 e 1426 _ => {}
1431 } 1427 },
1432 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
1433 if nested_loop.as_ref() == Some(&e) { 1456 if nested_loop.as_ref() == Some(&e) {
1434 nested_loop = None; 1457 nested_loop = None;
1435 } 1458 }
1436 if nested_scope.as_ref() == Some(&e) { 1459 if nested_scope.as_ref() == Some(&e) {
1437 nested_scope = None; 1460 nested_scope = None;
1438 } 1461 }
1439 continue;
1440 } 1462 }
1441 }; 1463 };
1442 if nested_scope.is_some() {
1443 continue;
1444 }
1445 let expr = match ast::Expr::cast(node) {
1446 Some(e) => e,
1447 None => continue,
1448 };
1449 match expr {
1450 ast::Expr::ReturnExpr(return_expr) if nested_scope.is_none() => {
1451 let expr = return_expr.expr();
1452 if let Some(replacement) = make_rewritten_flow(handler, expr) {
1453 rewriter.replace_ast(&return_expr.into(), &replacement);
1454 }
1455 }
1456 ast::Expr::BreakExpr(break_expr) if nested_loop.is_none() => {
1457 let expr = break_expr.expr();
1458 if let Some(replacement) = make_rewritten_flow(handler, expr) {
1459 rewriter.replace_ast(&break_expr.into(), &replacement);
1460 }
1461 }
1462 ast::Expr::ContinueExpr(continue_expr) if nested_loop.is_none() => {
1463 if let Some(replacement) = make_rewritten_flow(handler, None) {
1464 rewriter.replace_ast(&continue_expr.into(), &replacement);
1465 }
1466 }
1467 _ => {
1468 // do nothing
1469 }
1470 }
1471 } 1464 }
1472
1473 rewriter.rewrite(syntax)
1474} 1465}
1475 1466
1476fn 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> {
@@ -1480,16 +1471,16 @@ fn make_rewritten_flow(handler: &FlowHandler, arg_expr: Option<ast::Expr>) -> Op
1480 FlowHandler::IfOption { .. } => { 1471 FlowHandler::IfOption { .. } => {
1481 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()));
1482 let args = make::arg_list(iter::once(expr)); 1473 let args = make::arg_list(iter::once(expr));
1483 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)
1484 } 1475 }
1485 FlowHandler::MatchOption { .. } => make::expr_path(make_path_from_text("None")), 1476 FlowHandler::MatchOption { .. } => make::expr_path(make::ext::ident_path("None")),
1486 FlowHandler::MatchResult { .. } => { 1477 FlowHandler::MatchResult { .. } => {
1487 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()));
1488 let args = make::arg_list(iter::once(expr)); 1479 let args = make::arg_list(iter::once(expr));
1489 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)
1490 } 1481 }
1491 }; 1482 };
1492 Some(make::expr_return(Some(value))) 1483 Some(make::expr_return(Some(value)).clone_for_update())
1493} 1484}
1494 1485
1495#[cfg(test)] 1486#[cfg(test)]
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/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/introduce_named_lifetime.rs b/crates/ide_assists/src/handlers/introduce_named_lifetime.rs
index 9f4f71d6c..68bc15120 100644
--- a/crates/ide_assists/src/handlers/introduce_named_lifetime.rs
+++ b/crates/ide_assists/src/handlers/introduce_named_lifetime.rs
@@ -89,14 +89,12 @@ fn generate_fn_def_assist(
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
@@ -112,11 +110,10 @@ fn generate_impl_def_assist(
112 let impl_def = builder.make_ast_mut(impl_def); 110 let impl_def = builder.make_ast_mut(impl_def);
113 let lifetime = builder.make_ast_mut(lifetime); 111 let lifetime = builder.make_ast_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 {
diff --git a/crates/ide_assists/src/handlers/merge_imports.rs b/crates/ide_assists/src/handlers/merge_imports.rs
index add7b8e37..3cd090737 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))
diff --git a/crates/ide_assists/src/handlers/move_bounds.rs b/crates/ide_assists/src/handlers/move_bounds.rs
index 011a28d44..fa3f76609 100644
--- a/crates/ide_assists/src/handlers/move_bounds.rs
+++ b/crates/ide_assists/src/handlers/move_bounds.rs
@@ -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/pull_assignment_up.rs b/crates/ide_assists/src/handlers/pull_assignment_up.rs
index 04bae4e58..3128faa68 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_ast_mut(stmt), rhs.clone_for_update()))
78 .collect();
79
80 let tgt = edit.make_ast_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/reorder_impl.rs b/crates/ide_assists/src/handlers/reorder_impl.rs
index 72d889248..54a9a468e 100644
--- a/crates/ide_assists/src/handlers/reorder_impl.rs
+++ b/crates/ide_assists/src/handlers/reorder_impl.rs
@@ -79,9 +79,12 @@ 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 methods.into_iter().zip(sorted).for_each(|(old, new)| { 82 let methods =
83 ted::replace(builder.make_ast_mut(old).syntax(), new.clone_for_update().syntax()) 83 methods.into_iter().map(|fn_| builder.make_ast_mut(fn_)).collect::<Vec<_>>();
84 }); 84 methods
85 .into_iter()
86 .zip(sorted)
87 .for_each(|(old, new)| ted::replace(old.syntax(), new.clone_for_update().syntax()));
85 }, 88 },
86 ) 89 )
87} 90}
@@ -160,7 +163,7 @@ $0impl Bar for Foo {}
160 } 163 }
161 164
162 #[test] 165 #[test]
163 fn reorder_impl_trait_methods() { 166 fn reorder_impl_trait_functions() {
164 check_assist( 167 check_assist(
165 reorder_impl, 168 reorder_impl,
166 r#" 169 r#"
@@ -197,4 +200,33 @@ impl Bar for Foo {
197 "#, 200 "#,
198 ) 201 )
199 } 202 }
203
204 #[test]
205 fn reorder_impl_trait_methods_uneven_ident_lengths() {
206 check_assist(
207 reorder_impl,
208 r#"
209trait Bar {
210 fn foo(&mut self) {}
211 fn fooo(&mut self) {}
212}
213
214struct Foo;
215impl Bar for Foo {
216 fn fooo(&mut self) {}
217 fn foo(&mut self) {$0}
218}"#,
219 r#"
220trait Bar {
221 fn foo(&mut self) {}
222 fn fooo(&mut self) {}
223}
224
225struct Foo;
226impl Bar for Foo {
227 fn foo(&mut self) {}
228 fn fooo(&mut self) {}
229}"#,
230 )
231 }
200} 232}
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..15420aedf 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_ast_mut(impl_trait_type);
36 let fn_ = edit.make_ast_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_unwrap_with_match.rs b/crates/ide_assists/src/handlers/replace_unwrap_with_match.rs
index a986a6ae8..0fec961b4 100644
--- a/crates/ide_assists/src/handlers/replace_unwrap_with_match.rs
+++ b/crates/ide_assists/src/handlers/replace_unwrap_with_match.rs
@@ -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/lib.rs b/crates/ide_assists/src/lib.rs
index 88ae5c9a9..2e0c58504 100644
--- a/crates/ide_assists/src/lib.rs
+++ b/crates/ide_assists/src/lib.rs
@@ -17,6 +17,8 @@ mod tests;
17pub mod utils; 17pub mod utils;
18pub mod ast_transform; 18pub mod ast_transform;
19 19
20use std::str::FromStr;
21
20use hir::Semantics; 22use hir::Semantics;
21use ide_db::base_db::FileRange; 23use ide_db::base_db::FileRange;
22use ide_db::{label::Label, source_change::SourceChange, RootDatabase}; 24use ide_db::{label::Label, source_change::SourceChange, RootDatabase};
@@ -56,6 +58,35 @@ impl AssistKind {
56 _ => return false, 58 _ => return false,
57 } 59 }
58 } 60 }
61
62 pub fn name(&self) -> &str {
63 match self {
64 AssistKind::None => "None",
65 AssistKind::QuickFix => "QuickFix",
66 AssistKind::Generate => "Generate",
67 AssistKind::Refactor => "Refactor",
68 AssistKind::RefactorExtract => "RefactorExtract",
69 AssistKind::RefactorInline => "RefactorInline",
70 AssistKind::RefactorRewrite => "RefactorRewrite",
71 }
72 }
73}
74
75impl FromStr for AssistKind {
76 type Err = String;
77
78 fn from_str(s: &str) -> Result<Self, Self::Err> {
79 match s {
80 "None" => Ok(AssistKind::None),
81 "QuickFix" => Ok(AssistKind::QuickFix),
82 "Generate" => Ok(AssistKind::Generate),
83 "Refactor" => Ok(AssistKind::Refactor),
84 "RefactorExtract" => Ok(AssistKind::RefactorExtract),
85 "RefactorInline" => Ok(AssistKind::RefactorInline),
86 "RefactorRewrite" => Ok(AssistKind::RefactorRewrite),
87 unknown => Err(format!("Unknown AssistKind: '{}'", unknown)),
88 }
89 }
59} 90}
60 91
61/// Unique identifier of the assist, should not be shown to the user 92/// Unique identifier of the assist, should not be shown to the user
@@ -63,6 +94,41 @@ impl AssistKind {
63#[derive(Debug, Clone, Copy, PartialEq, Eq)] 94#[derive(Debug, Clone, Copy, PartialEq, Eq)]
64pub struct AssistId(pub &'static str, pub AssistKind); 95pub struct AssistId(pub &'static str, pub AssistKind);
65 96
97/// A way to control how many asssist to resolve during the assist resolution.
98/// When an assist is resolved, its edits are calculated that might be costly to always do by default.
99#[derive(Debug)]
100pub enum AssistResolveStrategy {
101 /// No assists should be resolved.
102 None,
103 /// All assists should be resolved.
104 All,
105 /// Only a certain assist should be resolved.
106 Single(SingleResolve),
107}
108
109/// Hold the [`AssistId`] data of a certain assist to resolve.
110/// The original id object cannot be used due to a `'static` lifetime
111/// and the requirement to construct this struct dynamically during the resolve handling.
112#[derive(Debug)]
113pub struct SingleResolve {
114 /// The id of the assist.
115 pub assist_id: String,
116 // The kind of the assist.
117 pub assist_kind: AssistKind,
118}
119
120impl AssistResolveStrategy {
121 pub fn should_resolve(&self, id: &AssistId) -> bool {
122 match self {
123 AssistResolveStrategy::None => false,
124 AssistResolveStrategy::All => true,
125 AssistResolveStrategy::Single(single_resolve) => {
126 single_resolve.assist_id == id.0 && single_resolve.assist_kind == id.1
127 }
128 }
129 }
130}
131
66#[derive(Clone, Debug)] 132#[derive(Clone, Debug)]
67pub struct GroupLabel(pub String); 133pub struct GroupLabel(pub String);
68 134
@@ -91,7 +157,7 @@ impl Assist {
91 pub fn get( 157 pub fn get(
92 db: &RootDatabase, 158 db: &RootDatabase,
93 config: &AssistConfig, 159 config: &AssistConfig,
94 resolve: bool, 160 resolve: AssistResolveStrategy,
95 range: FileRange, 161 range: FileRange,
96 ) -> Vec<Assist> { 162 ) -> Vec<Assist> {
97 let sema = Semantics::new(db); 163 let sema = Semantics::new(db);
diff --git a/crates/ide_assists/src/tests.rs b/crates/ide_assists/src/tests.rs
index 6f4f97361..0d3969c36 100644
--- a/crates/ide_assists/src/tests.rs
+++ b/crates/ide_assists/src/tests.rs
@@ -12,13 +12,16 @@ use stdx::{format_to, trim_indent};
12use syntax::TextRange; 12use syntax::TextRange;
13use test_utils::{assert_eq_text, extract_offset}; 13use test_utils::{assert_eq_text, extract_offset};
14 14
15use crate::{handlers::Handler, Assist, AssistConfig, AssistContext, AssistKind, Assists}; 15use crate::{
16 handlers::Handler, Assist, AssistConfig, AssistContext, AssistKind, AssistResolveStrategy,
17 Assists, SingleResolve,
18};
16 19
17pub(crate) const TEST_CONFIG: AssistConfig = AssistConfig { 20pub(crate) const TEST_CONFIG: AssistConfig = AssistConfig {
18 snippet_cap: SnippetCap::new(true), 21 snippet_cap: SnippetCap::new(true),
19 allowed: None, 22 allowed: None,
20 insert_use: InsertUseConfig { 23 insert_use: InsertUseConfig {
21 merge: Some(MergeBehavior::Full), 24 merge: Some(MergeBehavior::Crate),
22 prefix_kind: hir::PrefixKind::Plain, 25 prefix_kind: hir::PrefixKind::Plain,
23 group: true, 26 group: true,
24 }, 27 },
@@ -65,14 +68,14 @@ fn check_doc_test(assist_id: &str, before: &str, after: &str) {
65 let before = db.file_text(file_id).to_string(); 68 let before = db.file_text(file_id).to_string();
66 let frange = FileRange { file_id, range: selection.into() }; 69 let frange = FileRange { file_id, range: selection.into() };
67 70
68 let assist = Assist::get(&db, &TEST_CONFIG, true, frange) 71 let assist = Assist::get(&db, &TEST_CONFIG, AssistResolveStrategy::All, frange)
69 .into_iter() 72 .into_iter()
70 .find(|assist| assist.id.0 == assist_id) 73 .find(|assist| assist.id.0 == assist_id)
71 .unwrap_or_else(|| { 74 .unwrap_or_else(|| {
72 panic!( 75 panic!(
73 "\n\nAssist is not applicable: {}\nAvailable assists: {}", 76 "\n\nAssist is not applicable: {}\nAvailable assists: {}",
74 assist_id, 77 assist_id,
75 Assist::get(&db, &TEST_CONFIG, false, frange) 78 Assist::get(&db, &TEST_CONFIG, AssistResolveStrategy::None, frange)
76 .into_iter() 79 .into_iter()
77 .map(|assist| assist.id.0) 80 .map(|assist| assist.id.0)
78 .collect::<Vec<_>>() 81 .collect::<Vec<_>>()
@@ -108,7 +111,7 @@ fn check(handler: Handler, before: &str, expected: ExpectedResult, assist_label:
108 let sema = Semantics::new(&db); 111 let sema = Semantics::new(&db);
109 let config = TEST_CONFIG; 112 let config = TEST_CONFIG;
110 let ctx = AssistContext::new(sema, &config, frange); 113 let ctx = AssistContext::new(sema, &config, frange);
111 let mut acc = Assists::new(&ctx, true); 114 let mut acc = Assists::new(&ctx, AssistResolveStrategy::All);
112 handler(&mut acc, &ctx); 115 handler(&mut acc, &ctx);
113 let mut res = acc.finish(); 116 let mut res = acc.finish();
114 117
@@ -186,7 +189,7 @@ fn assist_order_field_struct() {
186 let (before_cursor_pos, before) = extract_offset(before); 189 let (before_cursor_pos, before) = extract_offset(before);
187 let (db, file_id) = with_single_file(&before); 190 let (db, file_id) = with_single_file(&before);
188 let frange = FileRange { file_id, range: TextRange::empty(before_cursor_pos) }; 191 let frange = FileRange { file_id, range: TextRange::empty(before_cursor_pos) };
189 let assists = Assist::get(&db, &TEST_CONFIG, false, frange); 192 let assists = Assist::get(&db, &TEST_CONFIG, AssistResolveStrategy::None, frange);
190 let mut assists = assists.iter(); 193 let mut assists = assists.iter();
191 194
192 assert_eq!(assists.next().expect("expected assist").label, "Change visibility to pub(crate)"); 195 assert_eq!(assists.next().expect("expected assist").label, "Change visibility to pub(crate)");
@@ -211,7 +214,7 @@ pub fn test_some_range(a: int) -> bool {
211"#, 214"#,
212 ); 215 );
213 216
214 let assists = Assist::get(&db, &TEST_CONFIG, false, frange); 217 let assists = Assist::get(&db, &TEST_CONFIG, AssistResolveStrategy::None, frange);
215 let expected = labels(&assists); 218 let expected = labels(&assists);
216 219
217 expect![[r#" 220 expect![[r#"
@@ -240,7 +243,7 @@ pub fn test_some_range(a: int) -> bool {
240 let mut cfg = TEST_CONFIG; 243 let mut cfg = TEST_CONFIG;
241 cfg.allowed = Some(vec![AssistKind::Refactor]); 244 cfg.allowed = Some(vec![AssistKind::Refactor]);
242 245
243 let assists = Assist::get(&db, &cfg, false, frange); 246 let assists = Assist::get(&db, &cfg, AssistResolveStrategy::None, frange);
244 let expected = labels(&assists); 247 let expected = labels(&assists);
245 248
246 expect![[r#" 249 expect![[r#"
@@ -255,7 +258,7 @@ pub fn test_some_range(a: int) -> bool {
255 { 258 {
256 let mut cfg = TEST_CONFIG; 259 let mut cfg = TEST_CONFIG;
257 cfg.allowed = Some(vec![AssistKind::RefactorExtract]); 260 cfg.allowed = Some(vec![AssistKind::RefactorExtract]);
258 let assists = Assist::get(&db, &cfg, false, frange); 261 let assists = Assist::get(&db, &cfg, AssistResolveStrategy::None, frange);
259 let expected = labels(&assists); 262 let expected = labels(&assists);
260 263
261 expect![[r#" 264 expect![[r#"
@@ -268,9 +271,250 @@ pub fn test_some_range(a: int) -> bool {
268 { 271 {
269 let mut cfg = TEST_CONFIG; 272 let mut cfg = TEST_CONFIG;
270 cfg.allowed = Some(vec![AssistKind::QuickFix]); 273 cfg.allowed = Some(vec![AssistKind::QuickFix]);
271 let assists = Assist::get(&db, &cfg, false, frange); 274 let assists = Assist::get(&db, &cfg, AssistResolveStrategy::None, frange);
272 let expected = labels(&assists); 275 let expected = labels(&assists);
273 276
274 expect![[r#""#]].assert_eq(&expected); 277 expect![[r#""#]].assert_eq(&expected);
275 } 278 }
276} 279}
280
281#[test]
282fn various_resolve_strategies() {
283 let (db, frange) = RootDatabase::with_range(
284 r#"
285pub fn test_some_range(a: int) -> bool {
286 if let 2..6 = $05$0 {
287 true
288 } else {
289 false
290 }
291}
292"#,
293 );
294
295 let mut cfg = TEST_CONFIG;
296 cfg.allowed = Some(vec![AssistKind::RefactorExtract]);
297
298 {
299 let assists = Assist::get(&db, &cfg, AssistResolveStrategy::None, frange);
300 assert_eq!(2, assists.len());
301 let mut assists = assists.into_iter();
302
303 let extract_into_variable_assist = assists.next().unwrap();
304 expect![[r#"
305 Assist {
306 id: AssistId(
307 "extract_variable",
308 RefactorExtract,
309 ),
310 label: "Extract into variable",
311 group: None,
312 target: 59..60,
313 source_change: None,
314 }
315 "#]]
316 .assert_debug_eq(&extract_into_variable_assist);
317
318 let extract_into_function_assist = assists.next().unwrap();
319 expect![[r#"
320 Assist {
321 id: AssistId(
322 "extract_function",
323 RefactorExtract,
324 ),
325 label: "Extract into function",
326 group: None,
327 target: 59..60,
328 source_change: None,
329 }
330 "#]]
331 .assert_debug_eq(&extract_into_function_assist);
332 }
333
334 {
335 let assists = Assist::get(
336 &db,
337 &cfg,
338 AssistResolveStrategy::Single(SingleResolve {
339 assist_id: "SOMETHING_MISMATCHING".to_string(),
340 assist_kind: AssistKind::RefactorExtract,
341 }),
342 frange,
343 );
344 assert_eq!(2, assists.len());
345 let mut assists = assists.into_iter();
346
347 let extract_into_variable_assist = assists.next().unwrap();
348 expect![[r#"
349 Assist {
350 id: AssistId(
351 "extract_variable",
352 RefactorExtract,
353 ),
354 label: "Extract into variable",
355 group: None,
356 target: 59..60,
357 source_change: None,
358 }
359 "#]]
360 .assert_debug_eq(&extract_into_variable_assist);
361
362 let extract_into_function_assist = assists.next().unwrap();
363 expect![[r#"
364 Assist {
365 id: AssistId(
366 "extract_function",
367 RefactorExtract,
368 ),
369 label: "Extract into function",
370 group: None,
371 target: 59..60,
372 source_change: None,
373 }
374 "#]]
375 .assert_debug_eq(&extract_into_function_assist);
376 }
377
378 {
379 let assists = Assist::get(
380 &db,
381 &cfg,
382 AssistResolveStrategy::Single(SingleResolve {
383 assist_id: "extract_variable".to_string(),
384 assist_kind: AssistKind::RefactorExtract,
385 }),
386 frange,
387 );
388 assert_eq!(2, assists.len());
389 let mut assists = assists.into_iter();
390
391 let extract_into_variable_assist = assists.next().unwrap();
392 expect![[r#"
393 Assist {
394 id: AssistId(
395 "extract_variable",
396 RefactorExtract,
397 ),
398 label: "Extract into variable",
399 group: None,
400 target: 59..60,
401 source_change: Some(
402 SourceChange {
403 source_file_edits: {
404 FileId(
405 0,
406 ): TextEdit {
407 indels: [
408 Indel {
409 insert: "let $0var_name = 5;\n ",
410 delete: 45..45,
411 },
412 Indel {
413 insert: "var_name",
414 delete: 59..60,
415 },
416 ],
417 },
418 },
419 file_system_edits: [],
420 is_snippet: true,
421 },
422 ),
423 }
424 "#]]
425 .assert_debug_eq(&extract_into_variable_assist);
426
427 let extract_into_function_assist = assists.next().unwrap();
428 expect![[r#"
429 Assist {
430 id: AssistId(
431 "extract_function",
432 RefactorExtract,
433 ),
434 label: "Extract into function",
435 group: None,
436 target: 59..60,
437 source_change: None,
438 }
439 "#]]
440 .assert_debug_eq(&extract_into_function_assist);
441 }
442
443 {
444 let assists = Assist::get(&db, &cfg, AssistResolveStrategy::All, frange);
445 assert_eq!(2, assists.len());
446 let mut assists = assists.into_iter();
447
448 let extract_into_variable_assist = assists.next().unwrap();
449 expect![[r#"
450 Assist {
451 id: AssistId(
452 "extract_variable",
453 RefactorExtract,
454 ),
455 label: "Extract into variable",
456 group: None,
457 target: 59..60,
458 source_change: Some(
459 SourceChange {
460 source_file_edits: {
461 FileId(
462 0,
463 ): TextEdit {
464 indels: [
465 Indel {
466 insert: "let $0var_name = 5;\n ",
467 delete: 45..45,
468 },
469 Indel {
470 insert: "var_name",
471 delete: 59..60,
472 },
473 ],
474 },
475 },
476 file_system_edits: [],
477 is_snippet: true,
478 },
479 ),
480 }
481 "#]]
482 .assert_debug_eq(&extract_into_variable_assist);
483
484 let extract_into_function_assist = assists.next().unwrap();
485 expect![[r#"
486 Assist {
487 id: AssistId(
488 "extract_function",
489 RefactorExtract,
490 ),
491 label: "Extract into function",
492 group: None,
493 target: 59..60,
494 source_change: Some(
495 SourceChange {
496 source_file_edits: {
497 FileId(
498 0,
499 ): TextEdit {
500 indels: [
501 Indel {
502 insert: "fun_name()",
503 delete: 59..60,
504 },
505 Indel {
506 insert: "\n\nfn $0fun_name() -> i32 {\n 5\n}",
507 delete: 110..110,
508 },
509 ],
510 },
511 },
512 file_system_edits: [],
513 is_snippet: true,
514 },
515 ),
516 }
517 "#]]
518 .assert_debug_eq(&extract_into_function_assist);
519 }
520}
diff --git a/crates/ide_assists/src/tests/generated.rs b/crates/ide_assists/src/tests/generated.rs
index 59bcef8fb..49c1a9776 100644
--- a/crates/ide_assists/src/tests/generated.rs
+++ b/crates/ide_assists/src/tests/generated.rs
@@ -1531,7 +1531,7 @@ enum Result<T, E> { Ok(T), Err(E) }
1531fn main() { 1531fn main() {
1532 let x: Result<i32, i32> = Result::Ok(92); 1532 let x: Result<i32, i32> = Result::Ok(92);
1533 let y = match x { 1533 let y = match x {
1534 Ok(a) => a, 1534 Ok(it) => it,
1535 $0_ => unreachable!(), 1535 $0_ => unreachable!(),
1536 }; 1536 };
1537} 1537}
diff --git a/crates/ide_assists/src/utils.rs b/crates/ide_assists/src/utils.rs
index 5a90ad715..0dcf20c61 100644
--- a/crates/ide_assists/src/utils.rs
+++ b/crates/ide_assists/src/utils.rs
@@ -159,8 +159,8 @@ pub fn add_trait_assoc_items_to_impl(
159 match fn_def.body() { 159 match fn_def.body() {
160 Some(_) => fn_def, 160 Some(_) => fn_def,
161 None => { 161 None => {
162 let body = 162 let body = make::block_expr(None, Some(make::ext::expr_todo()))
163 make::block_expr(None, Some(make::expr_todo())).indent(edit::IndentLevel(1)); 163 .indent(edit::IndentLevel(1));
164 fn_def.with_body(body) 164 fn_def.with_body(body)
165 } 165 }
166 } 166 }
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