diff options
Diffstat (limited to 'crates/ra_assists/src/handlers')
6 files changed, 261 insertions, 36 deletions
diff --git a/crates/ra_assists/src/handlers/add_explicit_type.rs b/crates/ra_assists/src/handlers/add_explicit_type.rs index d86d804b2..6c56d93d8 100644 --- a/crates/ra_assists/src/handlers/add_explicit_type.rs +++ b/crates/ra_assists/src/handlers/add_explicit_type.rs | |||
@@ -52,21 +52,22 @@ pub(crate) fn add_explicit_type(ctx: AssistCtx) -> Option<Assist> { | |||
52 | } | 52 | } |
53 | // Infer type | 53 | // Infer type |
54 | let ty = ctx.sema.type_of_expr(&expr)?; | 54 | let ty = ctx.sema.type_of_expr(&expr)?; |
55 | // Assist not applicable if the type is unknown | 55 | |
56 | if ty.contains_unknown() { | 56 | if ty.contains_unknown() || ty.is_closure() { |
57 | return None; | 57 | return None; |
58 | } | 58 | } |
59 | 59 | ||
60 | let db = ctx.db; | 60 | let db = ctx.db; |
61 | let new_type_string = ty.display_truncated(db, None).to_string(); | ||
61 | ctx.add_assist( | 62 | ctx.add_assist( |
62 | AssistId("add_explicit_type"), | 63 | AssistId("add_explicit_type"), |
63 | format!("Insert explicit type '{}'", ty.display(db)), | 64 | format!("Insert explicit type '{}'", new_type_string), |
64 | |edit| { | 65 | |edit| { |
65 | edit.target(pat_range); | 66 | edit.target(pat_range); |
66 | if let Some(ascribed_ty) = ascribed_ty { | 67 | if let Some(ascribed_ty) = ascribed_ty { |
67 | edit.replace(ascribed_ty.syntax().text_range(), format!("{}", ty.display(db))); | 68 | edit.replace(ascribed_ty.syntax().text_range(), new_type_string); |
68 | } else { | 69 | } else { |
69 | edit.insert(name_range.end(), format!(": {}", ty.display(db))); | 70 | edit.insert(name_range.end(), format!(": {}", new_type_string)); |
70 | } | 71 | } |
71 | }, | 72 | }, |
72 | ) | 73 | ) |
@@ -174,4 +175,41 @@ mod tests { | |||
174 | "fn f() <|>{let a = match 1 {2 => 3, 3 => 5};}", | 175 | "fn f() <|>{let a = match 1 {2 => 3, 3 => 5};}", |
175 | ) | 176 | ) |
176 | } | 177 | } |
178 | |||
179 | #[test] | ||
180 | fn closure_parameters_are_not_added() { | ||
181 | check_assist_not_applicable( | ||
182 | add_explicit_type, | ||
183 | r#" | ||
184 | fn main() { | ||
185 | let multiply_by_two<|> = |i| i * 3; | ||
186 | let six = multiply_by_two(2); | ||
187 | }"#, | ||
188 | ) | ||
189 | } | ||
190 | |||
191 | #[test] | ||
192 | fn default_generics_should_not_be_added() { | ||
193 | check_assist( | ||
194 | add_explicit_type, | ||
195 | r#" | ||
196 | struct Test<K, T = u8> { | ||
197 | k: K, | ||
198 | t: T, | ||
199 | } | ||
200 | |||
201 | fn main() { | ||
202 | let test<|> = Test { t: 23, k: 33 }; | ||
203 | }"#, | ||
204 | r#" | ||
205 | struct Test<K, T = u8> { | ||
206 | k: K, | ||
207 | t: T, | ||
208 | } | ||
209 | |||
210 | fn main() { | ||
211 | let test<|>: Test<i32> = Test { t: 23, k: 33 }; | ||
212 | }"#, | ||
213 | ); | ||
214 | } | ||
177 | } | 215 | } |
diff --git a/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs b/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs index 864373aa5..0621487e8 100644 --- a/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs +++ b/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs | |||
@@ -98,7 +98,7 @@ fn already_has_from_impl( | |||
98 | }; | 98 | }; |
99 | let var_ty = hir_enum_var.fields(sema.db)[0].signature_ty(sema.db); | 99 | let var_ty = hir_enum_var.fields(sema.db)[0].signature_ty(sema.db); |
100 | 100 | ||
101 | e_ty.impls_trait(sema.db, from_trait, &[var_ty.clone()]) | 101 | e_ty.impls_trait(sema.db, from_trait, &[var_ty]) |
102 | } | 102 | } |
103 | 103 | ||
104 | #[cfg(test)] | 104 | #[cfg(test)] |
diff --git a/crates/ra_assists/src/handlers/add_function.rs b/crates/ra_assists/src/handlers/add_function.rs index ad4ab66ed..f185cffdb 100644 --- a/crates/ra_assists/src/handlers/add_function.rs +++ b/crates/ra_assists/src/handlers/add_function.rs | |||
@@ -3,8 +3,8 @@ use ra_syntax::{ | |||
3 | SyntaxKind, SyntaxNode, TextUnit, | 3 | SyntaxKind, SyntaxNode, TextUnit, |
4 | }; | 4 | }; |
5 | 5 | ||
6 | use crate::{Assist, AssistCtx, AssistId}; | 6 | use crate::{Assist, AssistCtx, AssistFile, AssistId}; |
7 | use ast::{edit::IndentLevel, ArgListOwner, CallExpr, Expr}; | 7 | use ast::{edit::IndentLevel, ArgListOwner, ModuleItemOwner}; |
8 | use hir::HirDisplay; | 8 | use hir::HirDisplay; |
9 | use rustc_hash::{FxHashMap, FxHashSet}; | 9 | use rustc_hash::{FxHashMap, FxHashSet}; |
10 | 10 | ||
@@ -16,7 +16,7 @@ use rustc_hash::{FxHashMap, FxHashSet}; | |||
16 | // struct Baz; | 16 | // struct Baz; |
17 | // fn baz() -> Baz { Baz } | 17 | // fn baz() -> Baz { Baz } |
18 | // fn foo() { | 18 | // fn foo() { |
19 | // bar<|>("", baz()); | 19 | // bar<|>("", baz()); |
20 | // } | 20 | // } |
21 | // | 21 | // |
22 | // ``` | 22 | // ``` |
@@ -25,7 +25,7 @@ use rustc_hash::{FxHashMap, FxHashSet}; | |||
25 | // struct Baz; | 25 | // struct Baz; |
26 | // fn baz() -> Baz { Baz } | 26 | // fn baz() -> Baz { Baz } |
27 | // fn foo() { | 27 | // fn foo() { |
28 | // bar("", baz()); | 28 | // bar("", baz()); |
29 | // } | 29 | // } |
30 | // | 30 | // |
31 | // fn bar(arg: &str, baz: Baz) { | 31 | // fn bar(arg: &str, baz: Baz) { |
@@ -38,21 +38,30 @@ pub(crate) fn add_function(ctx: AssistCtx) -> Option<Assist> { | |||
38 | let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?; | 38 | let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?; |
39 | let path = path_expr.path()?; | 39 | let path = path_expr.path()?; |
40 | 40 | ||
41 | if path.qualifier().is_some() { | ||
42 | return None; | ||
43 | } | ||
44 | |||
45 | if ctx.sema.resolve_path(&path).is_some() { | 41 | if ctx.sema.resolve_path(&path).is_some() { |
46 | // The function call already resolves, no need to add a function | 42 | // The function call already resolves, no need to add a function |
47 | return None; | 43 | return None; |
48 | } | 44 | } |
49 | 45 | ||
50 | let function_builder = FunctionBuilder::from_call(&ctx, &call)?; | 46 | let target_module = if let Some(qualifier) = path.qualifier() { |
47 | if let Some(hir::PathResolution::Def(hir::ModuleDef::Module(module))) = | ||
48 | ctx.sema.resolve_path(&qualifier) | ||
49 | { | ||
50 | Some(module.definition_source(ctx.sema.db)) | ||
51 | } else { | ||
52 | return None; | ||
53 | } | ||
54 | } else { | ||
55 | None | ||
56 | }; | ||
57 | |||
58 | let function_builder = FunctionBuilder::from_call(&ctx, &call, &path, target_module)?; | ||
51 | 59 | ||
52 | ctx.add_assist(AssistId("add_function"), "Add function", |edit| { | 60 | ctx.add_assist(AssistId("add_function"), "Add function", |edit| { |
53 | edit.target(call.syntax().text_range()); | 61 | edit.target(call.syntax().text_range()); |
54 | 62 | ||
55 | if let Some(function_template) = function_builder.render() { | 63 | if let Some(function_template) = function_builder.render() { |
64 | edit.set_file(function_template.file); | ||
56 | edit.set_cursor(function_template.cursor_offset); | 65 | edit.set_cursor(function_template.cursor_offset); |
57 | edit.insert(function_template.insert_offset, function_template.fn_def.to_string()); | 66 | edit.insert(function_template.insert_offset, function_template.fn_def.to_string()); |
58 | } | 67 | } |
@@ -63,29 +72,67 @@ struct FunctionTemplate { | |||
63 | insert_offset: TextUnit, | 72 | insert_offset: TextUnit, |
64 | cursor_offset: TextUnit, | 73 | cursor_offset: TextUnit, |
65 | fn_def: ast::SourceFile, | 74 | fn_def: ast::SourceFile, |
75 | file: AssistFile, | ||
66 | } | 76 | } |
67 | 77 | ||
68 | struct FunctionBuilder { | 78 | struct FunctionBuilder { |
69 | append_fn_at: SyntaxNode, | 79 | target: GeneratedFunctionTarget, |
70 | fn_name: ast::Name, | 80 | fn_name: ast::Name, |
71 | type_params: Option<ast::TypeParamList>, | 81 | type_params: Option<ast::TypeParamList>, |
72 | params: ast::ParamList, | 82 | params: ast::ParamList, |
83 | file: AssistFile, | ||
84 | needs_pub: bool, | ||
73 | } | 85 | } |
74 | 86 | ||
75 | impl FunctionBuilder { | 87 | impl FunctionBuilder { |
76 | fn from_call(ctx: &AssistCtx, call: &ast::CallExpr) -> Option<Self> { | 88 | /// Prepares a generated function that matches `call` in `generate_in` |
77 | let append_fn_at = next_space_for_fn(&call)?; | 89 | /// (or as close to `call` as possible, if `generate_in` is `None`) |
78 | let fn_name = fn_name(&call)?; | 90 | fn from_call( |
91 | ctx: &AssistCtx, | ||
92 | call: &ast::CallExpr, | ||
93 | path: &ast::Path, | ||
94 | target_module: Option<hir::InFile<hir::ModuleSource>>, | ||
95 | ) -> Option<Self> { | ||
96 | let needs_pub = target_module.is_some(); | ||
97 | let mut file = AssistFile::default(); | ||
98 | let target = if let Some(target_module) = target_module { | ||
99 | let (in_file, target) = next_space_for_fn_in_module(ctx.sema.db, target_module)?; | ||
100 | file = in_file; | ||
101 | target | ||
102 | } else { | ||
103 | next_space_for_fn_after_call_site(&call)? | ||
104 | }; | ||
105 | let fn_name = fn_name(&path)?; | ||
79 | let (type_params, params) = fn_args(ctx, &call)?; | 106 | let (type_params, params) = fn_args(ctx, &call)?; |
80 | Some(Self { append_fn_at, fn_name, type_params, params }) | 107 | Some(Self { target, fn_name, type_params, params, file, needs_pub }) |
81 | } | 108 | } |
109 | |||
82 | fn render(self) -> Option<FunctionTemplate> { | 110 | fn render(self) -> Option<FunctionTemplate> { |
83 | let placeholder_expr = ast::make::expr_todo(); | 111 | let placeholder_expr = ast::make::expr_todo(); |
84 | let fn_body = ast::make::block_expr(vec![], Some(placeholder_expr)); | 112 | let fn_body = ast::make::block_expr(vec![], Some(placeholder_expr)); |
85 | let fn_def = ast::make::fn_def(self.fn_name, self.type_params, self.params, fn_body); | 113 | let mut fn_def = ast::make::fn_def(self.fn_name, self.type_params, self.params, fn_body); |
86 | let fn_def = ast::make::add_newlines(2, fn_def); | 114 | if self.needs_pub { |
87 | let fn_def = IndentLevel::from_node(&self.append_fn_at).increase_indent(fn_def); | 115 | fn_def = ast::make::add_pub_crate_modifier(fn_def); |
88 | let insert_offset = self.append_fn_at.text_range().end(); | 116 | } |
117 | |||
118 | let (fn_def, insert_offset) = match self.target { | ||
119 | GeneratedFunctionTarget::BehindItem(it) => { | ||
120 | let with_leading_blank_line = ast::make::add_leading_newlines(2, fn_def); | ||
121 | let indented = IndentLevel::from_node(&it).increase_indent(with_leading_blank_line); | ||
122 | (indented, it.text_range().end()) | ||
123 | } | ||
124 | GeneratedFunctionTarget::InEmptyItemList(it) => { | ||
125 | let indent_once = IndentLevel(1); | ||
126 | let indent = IndentLevel::from_node(it.syntax()); | ||
127 | |||
128 | let fn_def = ast::make::add_leading_newlines(1, fn_def); | ||
129 | let fn_def = indent_once.increase_indent(fn_def); | ||
130 | let fn_def = ast::make::add_trailing_newlines(1, fn_def); | ||
131 | let fn_def = indent.increase_indent(fn_def); | ||
132 | (fn_def, it.syntax().text_range().start() + TextUnit::from_usize(1)) | ||
133 | } | ||
134 | }; | ||
135 | |||
89 | let cursor_offset_from_fn_start = fn_def | 136 | let cursor_offset_from_fn_start = fn_def |
90 | .syntax() | 137 | .syntax() |
91 | .descendants() | 138 | .descendants() |
@@ -94,19 +141,24 @@ impl FunctionBuilder { | |||
94 | .text_range() | 141 | .text_range() |
95 | .start(); | 142 | .start(); |
96 | let cursor_offset = insert_offset + cursor_offset_from_fn_start; | 143 | let cursor_offset = insert_offset + cursor_offset_from_fn_start; |
97 | Some(FunctionTemplate { insert_offset, cursor_offset, fn_def }) | 144 | Some(FunctionTemplate { insert_offset, cursor_offset, fn_def, file: self.file }) |
98 | } | 145 | } |
99 | } | 146 | } |
100 | 147 | ||
101 | fn fn_name(call: &CallExpr) -> Option<ast::Name> { | 148 | enum GeneratedFunctionTarget { |
102 | let name = call.expr()?.syntax().to_string(); | 149 | BehindItem(SyntaxNode), |
150 | InEmptyItemList(ast::ItemList), | ||
151 | } | ||
152 | |||
153 | fn fn_name(call: &ast::Path) -> Option<ast::Name> { | ||
154 | let name = call.segment()?.syntax().to_string(); | ||
103 | Some(ast::make::name(&name)) | 155 | Some(ast::make::name(&name)) |
104 | } | 156 | } |
105 | 157 | ||
106 | /// Computes the type variables and arguments required for the generated function | 158 | /// Computes the type variables and arguments required for the generated function |
107 | fn fn_args( | 159 | fn fn_args( |
108 | ctx: &AssistCtx, | 160 | ctx: &AssistCtx, |
109 | call: &CallExpr, | 161 | call: &ast::CallExpr, |
110 | ) -> Option<(Option<ast::TypeParamList>, ast::ParamList)> { | 162 | ) -> Option<(Option<ast::TypeParamList>, ast::ParamList)> { |
111 | let mut arg_names = Vec::new(); | 163 | let mut arg_names = Vec::new(); |
112 | let mut arg_types = Vec::new(); | 164 | let mut arg_types = Vec::new(); |
@@ -158,9 +210,9 @@ fn deduplicate_arg_names(arg_names: &mut Vec<String>) { | |||
158 | } | 210 | } |
159 | } | 211 | } |
160 | 212 | ||
161 | fn fn_arg_name(fn_arg: &Expr) -> Option<String> { | 213 | fn fn_arg_name(fn_arg: &ast::Expr) -> Option<String> { |
162 | match fn_arg { | 214 | match fn_arg { |
163 | Expr::CastExpr(cast_expr) => fn_arg_name(&cast_expr.expr()?), | 215 | ast::Expr::CastExpr(cast_expr) => fn_arg_name(&cast_expr.expr()?), |
164 | _ => Some( | 216 | _ => Some( |
165 | fn_arg | 217 | fn_arg |
166 | .syntax() | 218 | .syntax() |
@@ -172,7 +224,7 @@ fn fn_arg_name(fn_arg: &Expr) -> Option<String> { | |||
172 | } | 224 | } |
173 | } | 225 | } |
174 | 226 | ||
175 | fn fn_arg_type(ctx: &AssistCtx, fn_arg: &Expr) -> Option<String> { | 227 | fn fn_arg_type(ctx: &AssistCtx, fn_arg: &ast::Expr) -> Option<String> { |
176 | let ty = ctx.sema.type_of_expr(fn_arg)?; | 228 | let ty = ctx.sema.type_of_expr(fn_arg)?; |
177 | if ty.is_unknown() { | 229 | if ty.is_unknown() { |
178 | return None; | 230 | return None; |
@@ -184,7 +236,7 @@ fn fn_arg_type(ctx: &AssistCtx, fn_arg: &Expr) -> Option<String> { | |||
184 | /// directly after the current block | 236 | /// directly after the current block |
185 | /// We want to write the generated function directly after | 237 | /// We want to write the generated function directly after |
186 | /// fns, impls or macro calls, but inside mods | 238 | /// fns, impls or macro calls, but inside mods |
187 | fn next_space_for_fn(expr: &CallExpr) -> Option<SyntaxNode> { | 239 | fn next_space_for_fn_after_call_site(expr: &ast::CallExpr) -> Option<GeneratedFunctionTarget> { |
188 | let mut ancestors = expr.syntax().ancestors().peekable(); | 240 | let mut ancestors = expr.syntax().ancestors().peekable(); |
189 | let mut last_ancestor: Option<SyntaxNode> = None; | 241 | let mut last_ancestor: Option<SyntaxNode> = None; |
190 | while let Some(next_ancestor) = ancestors.next() { | 242 | while let Some(next_ancestor) = ancestors.next() { |
@@ -201,7 +253,32 @@ fn next_space_for_fn(expr: &CallExpr) -> Option<SyntaxNode> { | |||
201 | } | 253 | } |
202 | last_ancestor = Some(next_ancestor); | 254 | last_ancestor = Some(next_ancestor); |
203 | } | 255 | } |
204 | last_ancestor | 256 | last_ancestor.map(GeneratedFunctionTarget::BehindItem) |
257 | } | ||
258 | |||
259 | fn next_space_for_fn_in_module( | ||
260 | db: &dyn hir::db::AstDatabase, | ||
261 | module: hir::InFile<hir::ModuleSource>, | ||
262 | ) -> Option<(AssistFile, GeneratedFunctionTarget)> { | ||
263 | let file = module.file_id.original_file(db); | ||
264 | let assist_file = AssistFile::TargetFile(file); | ||
265 | let assist_item = match module.value { | ||
266 | hir::ModuleSource::SourceFile(it) => { | ||
267 | if let Some(last_item) = it.items().last() { | ||
268 | GeneratedFunctionTarget::BehindItem(last_item.syntax().clone()) | ||
269 | } else { | ||
270 | GeneratedFunctionTarget::BehindItem(it.syntax().clone()) | ||
271 | } | ||
272 | } | ||
273 | hir::ModuleSource::Module(it) => { | ||
274 | if let Some(last_item) = it.item_list().and_then(|it| it.items().last()) { | ||
275 | GeneratedFunctionTarget::BehindItem(last_item.syntax().clone()) | ||
276 | } else { | ||
277 | GeneratedFunctionTarget::InEmptyItemList(it.item_list()?) | ||
278 | } | ||
279 | } | ||
280 | }; | ||
281 | Some((assist_file, assist_item)) | ||
205 | } | 282 | } |
206 | 283 | ||
207 | #[cfg(test)] | 284 | #[cfg(test)] |
@@ -714,6 +791,111 @@ fn bar(baz_1: Baz, baz_2: Baz, arg_1: &str, arg_2: &str) { | |||
714 | } | 791 | } |
715 | 792 | ||
716 | #[test] | 793 | #[test] |
794 | fn add_function_in_module() { | ||
795 | check_assist( | ||
796 | add_function, | ||
797 | r" | ||
798 | mod bar {} | ||
799 | |||
800 | fn foo() { | ||
801 | bar::my_fn<|>() | ||
802 | } | ||
803 | ", | ||
804 | r" | ||
805 | mod bar { | ||
806 | pub(crate) fn my_fn() { | ||
807 | <|>todo!() | ||
808 | } | ||
809 | } | ||
810 | |||
811 | fn foo() { | ||
812 | bar::my_fn() | ||
813 | } | ||
814 | ", | ||
815 | ) | ||
816 | } | ||
817 | |||
818 | #[test] | ||
819 | fn add_function_in_module_containing_other_items() { | ||
820 | check_assist( | ||
821 | add_function, | ||
822 | r" | ||
823 | mod bar { | ||
824 | fn something_else() {} | ||
825 | } | ||
826 | |||
827 | fn foo() { | ||
828 | bar::my_fn<|>() | ||
829 | } | ||
830 | ", | ||
831 | r" | ||
832 | mod bar { | ||
833 | fn something_else() {} | ||
834 | |||
835 | pub(crate) fn my_fn() { | ||
836 | <|>todo!() | ||
837 | } | ||
838 | } | ||
839 | |||
840 | fn foo() { | ||
841 | bar::my_fn() | ||
842 | } | ||
843 | ", | ||
844 | ) | ||
845 | } | ||
846 | |||
847 | #[test] | ||
848 | fn add_function_in_nested_module() { | ||
849 | check_assist( | ||
850 | add_function, | ||
851 | r" | ||
852 | mod bar { | ||
853 | mod baz {} | ||
854 | } | ||
855 | |||
856 | fn foo() { | ||
857 | bar::baz::my_fn<|>() | ||
858 | } | ||
859 | ", | ||
860 | r" | ||
861 | mod bar { | ||
862 | mod baz { | ||
863 | pub(crate) fn my_fn() { | ||
864 | <|>todo!() | ||
865 | } | ||
866 | } | ||
867 | } | ||
868 | |||
869 | fn foo() { | ||
870 | bar::baz::my_fn() | ||
871 | } | ||
872 | ", | ||
873 | ) | ||
874 | } | ||
875 | |||
876 | #[test] | ||
877 | fn add_function_in_another_file() { | ||
878 | check_assist( | ||
879 | add_function, | ||
880 | r" | ||
881 | //- /main.rs | ||
882 | mod foo; | ||
883 | |||
884 | fn main() { | ||
885 | foo::bar<|>() | ||
886 | } | ||
887 | //- /foo.rs | ||
888 | ", | ||
889 | r" | ||
890 | |||
891 | |||
892 | pub(crate) fn bar() { | ||
893 | <|>todo!() | ||
894 | }", | ||
895 | ) | ||
896 | } | ||
897 | |||
898 | #[test] | ||
717 | fn add_function_not_applicable_if_function_already_exists() { | 899 | fn add_function_not_applicable_if_function_already_exists() { |
718 | check_assist_not_applicable( | 900 | check_assist_not_applicable( |
719 | add_function, | 901 | add_function, |
diff --git a/crates/ra_assists/src/handlers/introduce_variable.rs b/crates/ra_assists/src/handlers/introduce_variable.rs index 8d0f7e922..8c09e6bcd 100644 --- a/crates/ra_assists/src/handlers/introduce_variable.rs +++ b/crates/ra_assists/src/handlers/introduce_variable.rs | |||
@@ -124,7 +124,7 @@ fn anchor_stmt(expr: ast::Expr) -> Option<(SyntaxNode, bool)> { | |||
124 | } | 124 | } |
125 | } | 125 | } |
126 | 126 | ||
127 | if ast::Stmt::cast(node.clone().into()).is_some() { | 127 | if ast::Stmt::cast(node.clone()).is_some() { |
128 | return Some((node, false)); | 128 | return Some((node, false)); |
129 | } | 129 | } |
130 | 130 | ||
diff --git a/crates/ra_assists/src/handlers/merge_imports.rs b/crates/ra_assists/src/handlers/merge_imports.rs index ef0ce0586..4be1238f1 100644 --- a/crates/ra_assists/src/handlers/merge_imports.rs +++ b/crates/ra_assists/src/handlers/merge_imports.rs | |||
@@ -30,7 +30,7 @@ pub(crate) fn merge_imports(ctx: AssistCtx) -> Option<Assist> { | |||
30 | .filter_map(|dir| neighbor(&use_item, dir)) | 30 | .filter_map(|dir| neighbor(&use_item, dir)) |
31 | .filter_map(|it| Some((it.clone(), it.use_tree()?))) | 31 | .filter_map(|it| Some((it.clone(), it.use_tree()?))) |
32 | .find_map(|(use_item, use_tree)| { | 32 | .find_map(|(use_item, use_tree)| { |
33 | Some((try_merge_trees(&tree, &use_tree)?, use_item.clone())) | 33 | Some((try_merge_trees(&tree, &use_tree)?, use_item)) |
34 | })?; | 34 | })?; |
35 | 35 | ||
36 | rewriter.replace_ast(&tree, &merged); | 36 | rewriter.replace_ast(&tree, &merged); |
diff --git a/crates/ra_assists/src/handlers/split_import.rs b/crates/ra_assists/src/handlers/split_import.rs index d9244f22d..f25826796 100644 --- a/crates/ra_assists/src/handlers/split_import.rs +++ b/crates/ra_assists/src/handlers/split_import.rs | |||
@@ -37,7 +37,7 @@ pub(crate) fn split_import(ctx: AssistCtx) -> Option<Assist> { | |||
37 | 37 | ||
38 | #[cfg(test)] | 38 | #[cfg(test)] |
39 | mod tests { | 39 | mod tests { |
40 | use crate::helpers::{check_assist, check_assist_target}; | 40 | use crate::helpers::{check_assist, check_assist_not_applicable, check_assist_target}; |
41 | 41 | ||
42 | use super::*; | 42 | use super::*; |
43 | 43 | ||
@@ -63,4 +63,9 @@ mod tests { | |||
63 | fn split_import_target() { | 63 | fn split_import_target() { |
64 | check_assist_target(split_import, "use crate::<|>db::{RootDatabase, FileSymbol}", "::"); | 64 | check_assist_target(split_import, "use crate::<|>db::{RootDatabase, FileSymbol}", "::"); |
65 | } | 65 | } |
66 | |||
67 | #[test] | ||
68 | fn issue4044() { | ||
69 | check_assist_not_applicable(split_import, "use crate::<|>:::self;") | ||
70 | } | ||
66 | } | 71 | } |