diff options
Diffstat (limited to 'crates/ide_assists')
24 files changed, 905 insertions, 442 deletions
diff --git a/crates/ide_assists/src/assist_context.rs b/crates/ide_assists/src/assist_context.rs index 682f0ff5e..20754a02a 100644 --- a/crates/ide_assists/src/assist_context.rs +++ b/crates/ide_assists/src/assist_context.rs | |||
@@ -238,8 +238,8 @@ impl AssistBuilder { | |||
238 | } | 238 | } |
239 | } | 239 | } |
240 | 240 | ||
241 | pub(crate) fn make_ast_mut<N: AstNode>(&mut self, node: N) -> N { | 241 | pub(crate) fn make_mut<N: AstNode>(&mut self, node: N) -> N { |
242 | N::cast(self.make_mut(node.syntax().clone())).unwrap() | 242 | self.mutated_tree.get_or_insert_with(|| TreeMutator::new(node.syntax())).make_mut(&node) |
243 | } | 243 | } |
244 | /// Returns a copy of the `node`, suitable for mutation. | 244 | /// Returns a copy of the `node`, suitable for mutation. |
245 | /// | 245 | /// |
@@ -251,7 +251,7 @@ impl AssistBuilder { | |||
251 | /// The typical pattern for an assist is to find specific nodes in the read | 251 | /// The typical pattern for an assist is to find specific nodes in the read |
252 | /// phase, and then get their mutable couterparts using `make_mut` in the | 252 | /// phase, and then get their mutable couterparts using `make_mut` in the |
253 | /// mutable state. | 253 | /// mutable state. |
254 | pub(crate) fn make_mut(&mut self, node: SyntaxNode) -> SyntaxNode { | 254 | pub(crate) fn make_syntax_mut(&mut self, node: SyntaxNode) -> SyntaxNode { |
255 | self.mutated_tree.get_or_insert_with(|| TreeMutator::new(&node)).make_syntax_mut(&node) | 255 | self.mutated_tree.get_or_insert_with(|| TreeMutator::new(&node)).make_syntax_mut(&node) |
256 | } | 256 | } |
257 | 257 | ||
diff --git a/crates/ide_assists/src/handlers/add_explicit_type.rs b/crates/ide_assists/src/handlers/add_explicit_type.rs index 62db31952..36589203d 100644 --- a/crates/ide_assists/src/handlers/add_explicit_type.rs +++ b/crates/ide_assists/src/handlers/add_explicit_type.rs | |||
@@ -198,6 +198,34 @@ fn main() { | |||
198 | ) | 198 | ) |
199 | } | 199 | } |
200 | 200 | ||
201 | /// https://github.com/rust-analyzer/rust-analyzer/issues/2922 | ||
202 | #[test] | ||
203 | fn regression_issue_2922() { | ||
204 | check_assist( | ||
205 | add_explicit_type, | ||
206 | r#" | ||
207 | fn main() { | ||
208 | let $0v = [0.0; 2]; | ||
209 | } | ||
210 | "#, | ||
211 | r#" | ||
212 | fn main() { | ||
213 | let v: [f64; 2] = [0.0; 2]; | ||
214 | } | ||
215 | "#, | ||
216 | ); | ||
217 | // note: this may break later if we add more consteval. it just needs to be something that our | ||
218 | // consteval engine doesn't understand | ||
219 | check_assist_not_applicable( | ||
220 | add_explicit_type, | ||
221 | r#" | ||
222 | fn main() { | ||
223 | let $0l = [0.0; 2+2]; | ||
224 | } | ||
225 | "#, | ||
226 | ); | ||
227 | } | ||
228 | |||
201 | #[test] | 229 | #[test] |
202 | fn default_generics_should_not_be_added() { | 230 | fn default_generics_should_not_be_added() { |
203 | check_assist( | 231 | check_assist( |
diff --git a/crates/ide_assists/src/handlers/add_missing_impl_members.rs b/crates/ide_assists/src/handlers/add_missing_impl_members.rs index 0148635f9..8225ae22c 100644 --- a/crates/ide_assists/src/handlers/add_missing_impl_members.rs +++ b/crates/ide_assists/src/handlers/add_missing_impl_members.rs | |||
@@ -64,7 +64,6 @@ pub(crate) fn add_missing_impl_members(acc: &mut Assists, ctx: &AssistContext) - | |||
64 | // impl Trait for () { | 64 | // impl Trait for () { |
65 | // type X = (); | 65 | // type X = (); |
66 | // fn foo(&self) {}$0 | 66 | // fn foo(&self) {}$0 |
67 | // | ||
68 | // } | 67 | // } |
69 | // ``` | 68 | // ``` |
70 | // -> | 69 | // -> |
@@ -195,6 +194,7 @@ impl Foo for S { | |||
195 | fn baz(&self) { | 194 | fn baz(&self) { |
196 | todo!() | 195 | todo!() |
197 | } | 196 | } |
197 | |||
198 | }"#, | 198 | }"#, |
199 | ); | 199 | ); |
200 | } | 200 | } |
@@ -231,6 +231,7 @@ impl Foo for S { | |||
231 | fn foo(&self) { | 231 | fn foo(&self) { |
232 | ${0:todo!()} | 232 | ${0:todo!()} |
233 | } | 233 | } |
234 | |||
234 | }"#, | 235 | }"#, |
235 | ); | 236 | ); |
236 | } | 237 | } |
diff --git a/crates/ide_assists/src/handlers/auto_import.rs b/crates/ide_assists/src/handlers/auto_import.rs index a454a2af3..dda5a6631 100644 --- a/crates/ide_assists/src/handlers/auto_import.rs +++ b/crates/ide_assists/src/handlers/auto_import.rs | |||
@@ -102,8 +102,8 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
102 | range, | 102 | range, |
103 | |builder| { | 103 | |builder| { |
104 | let scope = match scope.clone() { | 104 | let scope = match scope.clone() { |
105 | ImportScope::File(it) => ImportScope::File(builder.make_ast_mut(it)), | 105 | ImportScope::File(it) => ImportScope::File(builder.make_mut(it)), |
106 | ImportScope::Module(it) => ImportScope::Module(builder.make_ast_mut(it)), | 106 | ImportScope::Module(it) => ImportScope::Module(builder.make_mut(it)), |
107 | }; | 107 | }; |
108 | insert_use(&scope, mod_path_to_ast(&import.import_path), ctx.config.insert_use); | 108 | insert_use(&scope, mod_path_to_ast(&import.import_path), ctx.config.insert_use); |
109 | }, | 109 | }, |
@@ -969,4 +969,30 @@ mod bar { | |||
969 | "#, | 969 | "#, |
970 | ); | 970 | ); |
971 | } | 971 | } |
972 | |||
973 | #[test] | ||
974 | fn uses_abs_path_with_extern_crate_clash() { | ||
975 | check_assist( | ||
976 | auto_import, | ||
977 | r#" | ||
978 | //- /main.rs crate:main deps:foo | ||
979 | mod foo {} | ||
980 | |||
981 | const _: () = { | ||
982 | Foo$0 | ||
983 | }; | ||
984 | //- /foo.rs crate:foo | ||
985 | pub struct Foo | ||
986 | "#, | ||
987 | r#" | ||
988 | use ::foo::Foo; | ||
989 | |||
990 | mod foo {} | ||
991 | |||
992 | const _: () = { | ||
993 | Foo | ||
994 | }; | ||
995 | "#, | ||
996 | ); | ||
997 | } | ||
972 | } | 998 | } |
diff --git a/crates/ide_assists/src/handlers/expand_glob_import.rs b/crates/ide_assists/src/handlers/expand_glob_import.rs index da8d245e5..79cb08d69 100644 --- a/crates/ide_assists/src/handlers/expand_glob_import.rs +++ b/crates/ide_assists/src/handlers/expand_glob_import.rs | |||
@@ -61,7 +61,7 @@ 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 use_tree = builder.make_ast_mut(use_tree); | 64 | let use_tree = builder.make_mut(use_tree); |
65 | 65 | ||
66 | let names_to_import = find_names_to_import(ctx, refs_in_target, imported_defs); | 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| { | 67 | let expanded = make::use_tree_list(names_to_import.iter().map(|n| { |
diff --git a/crates/ide_assists/src/handlers/extract_function.rs b/crates/ide_assists/src/handlers/extract_function.rs index 6311afc1f..a2dba915c 100644 --- a/crates/ide_assists/src/handlers/extract_function.rs +++ b/crates/ide_assists/src/handlers/extract_function.rs | |||
@@ -642,6 +642,10 @@ fn vars_used_in_body(ctx: &AssistContext, body: &FunctionBody) -> Vec<Local> { | |||
642 | .collect() | 642 | .collect() |
643 | } | 643 | } |
644 | 644 | ||
645 | fn body_contains_await(body: &FunctionBody) -> bool { | ||
646 | body.descendants().any(|d| matches!(d.kind(), SyntaxKind::AWAIT_EXPR)) | ||
647 | } | ||
648 | |||
645 | /// find `self` param, that was not defined inside `body` | 649 | /// find `self` param, that was not defined inside `body` |
646 | /// | 650 | /// |
647 | /// It should skip `self` params from impls inside `body` | 651 | /// It should skip `self` params from impls inside `body` |
@@ -1123,9 +1127,10 @@ fn format_function( | |||
1123 | let params = make_param_list(ctx, module, fun); | 1127 | let params = make_param_list(ctx, module, fun); |
1124 | let ret_ty = make_ret_ty(ctx, module, fun); | 1128 | let ret_ty = make_ret_ty(ctx, module, fun); |
1125 | let body = make_body(ctx, old_indent, new_indent, fun); | 1129 | let body = make_body(ctx, old_indent, new_indent, fun); |
1130 | let async_kw = if body_contains_await(&fun.body) { "async " } else { "" }; | ||
1126 | match ctx.config.snippet_cap { | 1131 | match ctx.config.snippet_cap { |
1127 | Some(_) => format_to!(fn_def, "\n\n{}fn $0{}{}", new_indent, fun.name, params), | 1132 | Some(_) => format_to!(fn_def, "\n\n{}{}fn $0{}{}", new_indent, async_kw, fun.name, params), |
1128 | None => format_to!(fn_def, "\n\n{}fn {}{}", new_indent, fun.name, params), | 1133 | None => format_to!(fn_def, "\n\n{}{}fn {}{}", new_indent, async_kw, fun.name, params), |
1129 | } | 1134 | } |
1130 | if let Some(ret_ty) = ret_ty { | 1135 | if let Some(ret_ty) = ret_ty { |
1131 | format_to!(fn_def, " {}", ret_ty); | 1136 | format_to!(fn_def, " {}", ret_ty); |
@@ -3565,4 +3570,60 @@ fn $0fun_name(n: i32) -> i32 { | |||
3565 | }", | 3570 | }", |
3566 | ); | 3571 | ); |
3567 | } | 3572 | } |
3573 | |||
3574 | #[test] | ||
3575 | fn extract_with_await() { | ||
3576 | check_assist( | ||
3577 | extract_function, | ||
3578 | r#"fn main() { | ||
3579 | $0some_function().await;$0 | ||
3580 | } | ||
3581 | |||
3582 | async fn some_function() { | ||
3583 | |||
3584 | } | ||
3585 | "#, | ||
3586 | r#" | ||
3587 | fn main() { | ||
3588 | fun_name(); | ||
3589 | } | ||
3590 | |||
3591 | async fn $0fun_name() { | ||
3592 | some_function().await; | ||
3593 | } | ||
3594 | |||
3595 | async fn some_function() { | ||
3596 | |||
3597 | } | ||
3598 | "#, | ||
3599 | ); | ||
3600 | } | ||
3601 | |||
3602 | #[test] | ||
3603 | fn extract_with_await_in_args() { | ||
3604 | check_assist( | ||
3605 | extract_function, | ||
3606 | r#"fn main() { | ||
3607 | $0function_call("a", some_function().await);$0 | ||
3608 | } | ||
3609 | |||
3610 | async fn some_function() { | ||
3611 | |||
3612 | } | ||
3613 | "#, | ||
3614 | r#" | ||
3615 | fn main() { | ||
3616 | fun_name(); | ||
3617 | } | ||
3618 | |||
3619 | async fn $0fun_name() { | ||
3620 | function_call("a", some_function().await); | ||
3621 | } | ||
3622 | |||
3623 | async fn some_function() { | ||
3624 | |||
3625 | } | ||
3626 | "#, | ||
3627 | ); | ||
3628 | } | ||
3568 | } | 3629 | } |
diff --git a/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs index 8e2178391..007aba23d 100644 --- a/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs | |||
@@ -70,7 +70,7 @@ pub(crate) fn extract_struct_from_enum_variant( | |||
70 | continue; | 70 | continue; |
71 | } | 71 | } |
72 | builder.edit_file(file_id); | 72 | builder.edit_file(file_id); |
73 | let source_file = builder.make_ast_mut(ctx.sema.parse(file_id)); | 73 | let source_file = builder.make_mut(ctx.sema.parse(file_id)); |
74 | let processed = process_references( | 74 | let processed = process_references( |
75 | ctx, | 75 | ctx, |
76 | &mut visited_modules_set, | 76 | &mut visited_modules_set, |
@@ -84,8 +84,8 @@ pub(crate) fn extract_struct_from_enum_variant( | |||
84 | }); | 84 | }); |
85 | } | 85 | } |
86 | builder.edit_file(ctx.frange.file_id); | 86 | builder.edit_file(ctx.frange.file_id); |
87 | let source_file = builder.make_ast_mut(ctx.sema.parse(ctx.frange.file_id)); | 87 | let source_file = builder.make_mut(ctx.sema.parse(ctx.frange.file_id)); |
88 | let variant = builder.make_ast_mut(variant.clone()); | 88 | let variant = builder.make_mut(variant.clone()); |
89 | if let Some(references) = def_file_references { | 89 | if let Some(references) = def_file_references { |
90 | let processed = process_references( | 90 | let processed = process_references( |
91 | ctx, | 91 | ctx, |
diff --git a/crates/ide_assists/src/handlers/fill_match_arms.rs b/crates/ide_assists/src/handlers/fill_match_arms.rs index be927cc1c..58b001050 100644 --- a/crates/ide_assists/src/handlers/fill_match_arms.rs +++ b/crates/ide_assists/src/handlers/fill_match_arms.rs | |||
@@ -71,6 +71,7 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option< | |||
71 | .filter_map(|variant| build_pat(ctx.db(), module, variant)) | 71 | .filter_map(|variant| build_pat(ctx.db(), module, variant)) |
72 | .filter(|variant_pat| is_variant_missing(&top_lvl_pats, variant_pat)) | 72 | .filter(|variant_pat| is_variant_missing(&top_lvl_pats, variant_pat)) |
73 | .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block())) | 73 | .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block())) |
74 | .map(|it| it.clone_for_update()) | ||
74 | .collect::<Vec<_>>(); | 75 | .collect::<Vec<_>>(); |
75 | if Some(enum_def) | 76 | if Some(enum_def) |
76 | == FamousDefs(&ctx.sema, Some(module.krate())) | 77 | == FamousDefs(&ctx.sema, Some(module.krate())) |
@@ -99,6 +100,7 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option< | |||
99 | }) | 100 | }) |
100 | .filter(|variant_pat| is_variant_missing(&top_lvl_pats, variant_pat)) | 101 | .filter(|variant_pat| is_variant_missing(&top_lvl_pats, variant_pat)) |
101 | .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block())) | 102 | .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block())) |
103 | .map(|it| it.clone_for_update()) | ||
102 | .collect() | 104 | .collect() |
103 | } else { | 105 | } else { |
104 | return None; | 106 | return None; |
@@ -114,10 +116,20 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option< | |||
114 | "Fill match arms", | 116 | "Fill match arms", |
115 | target, | 117 | target, |
116 | |builder| { | 118 | |builder| { |
117 | let new_arm_list = match_arm_list.remove_placeholder(); | 119 | let new_match_arm_list = match_arm_list.clone_for_update(); |
118 | let n_old_arms = new_arm_list.arms().count(); | 120 | |
119 | let new_arm_list = new_arm_list.append_arms(missing_arms); | 121 | let catch_all_arm = new_match_arm_list |
120 | let first_new_arm = new_arm_list.arms().nth(n_old_arms); | 122 | .arms() |
123 | .find(|arm| matches!(arm.pat(), Some(ast::Pat::WildcardPat(_)))); | ||
124 | if let Some(arm) = catch_all_arm { | ||
125 | arm.remove() | ||
126 | } | ||
127 | let mut first_new_arm = None; | ||
128 | for arm in missing_arms { | ||
129 | first_new_arm.get_or_insert_with(|| arm.clone()); | ||
130 | new_match_arm_list.add_arm(arm); | ||
131 | } | ||
132 | |||
121 | let old_range = ctx.sema.original_range(match_arm_list.syntax()).range; | 133 | let old_range = ctx.sema.original_range(match_arm_list.syntax()).range; |
122 | match (first_new_arm, ctx.config.snippet_cap) { | 134 | match (first_new_arm, ctx.config.snippet_cap) { |
123 | (Some(first_new_arm), Some(cap)) => { | 135 | (Some(first_new_arm), Some(cap)) => { |
@@ -131,10 +143,10 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option< | |||
131 | } | 143 | } |
132 | None => Cursor::Before(first_new_arm.syntax()), | 144 | None => Cursor::Before(first_new_arm.syntax()), |
133 | }; | 145 | }; |
134 | let snippet = render_snippet(cap, new_arm_list.syntax(), cursor); | 146 | let snippet = render_snippet(cap, new_match_arm_list.syntax(), cursor); |
135 | builder.replace_snippet(cap, old_range, snippet); | 147 | builder.replace_snippet(cap, old_range, snippet); |
136 | } | 148 | } |
137 | _ => builder.replace(old_range, new_arm_list.to_string()), | 149 | _ => builder.replace(old_range, new_match_arm_list.to_string()), |
138 | } | 150 | } |
139 | }, | 151 | }, |
140 | ) | 152 | ) |
@@ -263,18 +275,18 @@ mod tests { | |||
263 | check_assist_not_applicable( | 275 | check_assist_not_applicable( |
264 | fill_match_arms, | 276 | fill_match_arms, |
265 | r#" | 277 | r#" |
266 | enum A { | 278 | enum A { |
267 | As, | 279 | As, |
268 | Bs{x:i32, y:Option<i32>}, | 280 | Bs{x:i32, y:Option<i32>}, |
269 | Cs(i32, Option<i32>), | 281 | Cs(i32, Option<i32>), |
270 | } | 282 | } |
271 | fn main() { | 283 | fn main() { |
272 | match A::As$0 { | 284 | match A::As$0 { |
273 | A::As, | 285 | A::As, |
274 | A::Bs{x,y:Some(_)} => {} | 286 | A::Bs{x,y:Some(_)} => {} |
275 | A::Cs(_, Some(_)) => {} | 287 | A::Cs(_, Some(_)) => {} |
276 | } | 288 | } |
277 | } | 289 | } |
278 | "#, | 290 | "#, |
279 | ); | 291 | ); |
280 | } | 292 | } |
@@ -284,13 +296,13 @@ mod tests { | |||
284 | check_assist_not_applicable( | 296 | check_assist_not_applicable( |
285 | fill_match_arms, | 297 | fill_match_arms, |
286 | r#" | 298 | r#" |
287 | fn foo(a: bool) { | 299 | fn foo(a: bool) { |
288 | match a$0 { | 300 | match a$0 { |
289 | true => {} | 301 | true => {} |
290 | false => {} | 302 | false => {} |
291 | } | 303 | } |
292 | } | 304 | } |
293 | "#, | 305 | "#, |
294 | ) | 306 | ) |
295 | } | 307 | } |
296 | 308 | ||
@@ -301,11 +313,11 @@ mod tests { | |||
301 | check_assist_not_applicable( | 313 | check_assist_not_applicable( |
302 | fill_match_arms, | 314 | fill_match_arms, |
303 | r#" | 315 | r#" |
304 | fn main() { | 316 | fn main() { |
305 | match (0, false)$0 { | 317 | match (0, false)$0 { |
306 | } | 318 | } |
307 | } | 319 | } |
308 | "#, | 320 | "#, |
309 | ); | 321 | ); |
310 | } | 322 | } |
311 | 323 | ||
@@ -314,19 +326,19 @@ mod tests { | |||
314 | check_assist( | 326 | check_assist( |
315 | fill_match_arms, | 327 | fill_match_arms, |
316 | r#" | 328 | r#" |
317 | fn foo(a: bool) { | 329 | fn foo(a: bool) { |
318 | match a$0 { | 330 | match a$0 { |
319 | } | 331 | } |
320 | } | 332 | } |
321 | "#, | 333 | "#, |
322 | r#" | 334 | r#" |
323 | fn foo(a: bool) { | 335 | fn foo(a: bool) { |
324 | match a { | 336 | match a { |
325 | $0true => {} | 337 | $0true => {} |
326 | false => {} | 338 | false => {} |
327 | } | 339 | } |
328 | } | 340 | } |
329 | "#, | 341 | "#, |
330 | ) | 342 | ) |
331 | } | 343 | } |
332 | 344 | ||
@@ -335,20 +347,20 @@ mod tests { | |||
335 | check_assist( | 347 | check_assist( |
336 | fill_match_arms, | 348 | fill_match_arms, |
337 | r#" | 349 | r#" |
338 | fn foo(a: bool) { | 350 | fn foo(a: bool) { |
339 | match a$0 { | 351 | match a$0 { |
340 | true => {} | 352 | true => {} |
341 | } | 353 | } |
342 | } | 354 | } |
343 | "#, | 355 | "#, |
344 | r#" | 356 | r#" |
345 | fn foo(a: bool) { | 357 | fn foo(a: bool) { |
346 | match a { | 358 | match a { |
347 | true => {} | 359 | true => {} |
348 | $0false => {} | 360 | $0false => {} |
349 | } | 361 | } |
350 | } | 362 | } |
351 | "#, | 363 | "#, |
352 | ) | 364 | ) |
353 | } | 365 | } |
354 | 366 | ||
@@ -357,15 +369,15 @@ mod tests { | |||
357 | check_assist_not_applicable( | 369 | check_assist_not_applicable( |
358 | fill_match_arms, | 370 | fill_match_arms, |
359 | r#" | 371 | r#" |
360 | fn foo(a: bool) { | 372 | fn foo(a: bool) { |
361 | match (a, a)$0 { | 373 | match (a, a)$0 { |
362 | (true, true) => {} | 374 | (true, true) => {} |
363 | (true, false) => {} | 375 | (true, false) => {} |
364 | (false, true) => {} | 376 | (false, true) => {} |
365 | (false, false) => {} | 377 | (false, false) => {} |
366 | } | 378 | } |
367 | } | 379 | } |
368 | "#, | 380 | "#, |
369 | ) | 381 | ) |
370 | } | 382 | } |
371 | 383 | ||
@@ -374,21 +386,21 @@ mod tests { | |||
374 | check_assist( | 386 | check_assist( |
375 | fill_match_arms, | 387 | fill_match_arms, |
376 | r#" | 388 | r#" |
377 | fn foo(a: bool) { | 389 | fn foo(a: bool) { |
378 | match (a, a)$0 { | 390 | match (a, a)$0 { |
379 | } | 391 | } |
380 | } | 392 | } |
381 | "#, | 393 | "#, |
382 | r#" | 394 | r#" |
383 | fn foo(a: bool) { | 395 | fn foo(a: bool) { |
384 | match (a, a) { | 396 | match (a, a) { |
385 | $0(true, true) => {} | 397 | $0(true, true) => {} |
386 | (true, false) => {} | 398 | (true, false) => {} |
387 | (false, true) => {} | 399 | (false, true) => {} |
388 | (false, false) => {} | 400 | (false, false) => {} |
389 | } | 401 | } |
390 | } | 402 | } |
391 | "#, | 403 | "#, |
392 | ) | 404 | ) |
393 | } | 405 | } |
394 | 406 | ||
@@ -397,22 +409,22 @@ mod tests { | |||
397 | check_assist( | 409 | check_assist( |
398 | fill_match_arms, | 410 | fill_match_arms, |
399 | r#" | 411 | r#" |
400 | fn foo(a: bool) { | 412 | fn foo(a: bool) { |
401 | match (a, a)$0 { | 413 | match (a, a)$0 { |
402 | (false, true) => {} | 414 | (false, true) => {} |
403 | } | 415 | } |
404 | } | 416 | } |
405 | "#, | 417 | "#, |
406 | r#" | 418 | r#" |
407 | fn foo(a: bool) { | 419 | fn foo(a: bool) { |
408 | match (a, a) { | 420 | match (a, a) { |
409 | (false, true) => {} | 421 | (false, true) => {} |
410 | $0(true, true) => {} | 422 | $0(true, true) => {} |
411 | (true, false) => {} | 423 | (true, false) => {} |
412 | (false, false) => {} | 424 | (false, false) => {} |
413 | } | 425 | } |
414 | } | 426 | } |
415 | "#, | 427 | "#, |
416 | ) | 428 | ) |
417 | } | 429 | } |
418 | 430 | ||
@@ -421,32 +433,32 @@ mod tests { | |||
421 | check_assist( | 433 | check_assist( |
422 | fill_match_arms, | 434 | fill_match_arms, |
423 | r#" | 435 | r#" |
424 | enum A { | 436 | enum A { |
425 | As, | 437 | As, |
426 | Bs { x: i32, y: Option<i32> }, | 438 | Bs { x: i32, y: Option<i32> }, |
427 | Cs(i32, Option<i32>), | 439 | Cs(i32, Option<i32>), |
428 | } | 440 | } |
429 | fn main() { | 441 | fn main() { |
430 | match A::As$0 { | 442 | match A::As$0 { |
431 | A::Bs { x, y: Some(_) } => {} | 443 | A::Bs { x, y: Some(_) } => {} |
432 | A::Cs(_, Some(_)) => {} | 444 | A::Cs(_, Some(_)) => {} |
433 | } | 445 | } |
434 | } | 446 | } |
435 | "#, | 447 | "#, |
436 | r#" | 448 | r#" |
437 | enum A { | 449 | enum A { |
438 | As, | 450 | As, |
439 | Bs { x: i32, y: Option<i32> }, | 451 | Bs { x: i32, y: Option<i32> }, |
440 | Cs(i32, Option<i32>), | 452 | Cs(i32, Option<i32>), |
441 | } | 453 | } |
442 | fn main() { | 454 | fn main() { |
443 | match A::As { | 455 | match A::As { |
444 | A::Bs { x, y: Some(_) } => {} | 456 | A::Bs { x, y: Some(_) } => {} |
445 | A::Cs(_, Some(_)) => {} | 457 | A::Cs(_, Some(_)) => {} |
446 | $0A::As => {} | 458 | $0A::As => {} |
447 | } | 459 | } |
448 | } | 460 | } |
449 | "#, | 461 | "#, |
450 | ); | 462 | ); |
451 | } | 463 | } |
452 | 464 | ||
@@ -593,30 +605,30 @@ fn main() { | |||
593 | check_assist( | 605 | check_assist( |
594 | fill_match_arms, | 606 | fill_match_arms, |
595 | r#" | 607 | r#" |
596 | enum A { One, Two } | 608 | enum A { One, Two } |
597 | enum B { One, Two } | 609 | enum B { One, Two } |
598 | 610 | ||
599 | fn main() { | 611 | fn main() { |
600 | let a = A::One; | 612 | let a = A::One; |
601 | let b = B::One; | 613 | let b = B::One; |
602 | match (a$0, b) {} | 614 | match (a$0, b) {} |
603 | } | 615 | } |
604 | "#, | 616 | "#, |
605 | r#" | 617 | r#" |
606 | enum A { One, Two } | 618 | enum A { One, Two } |
607 | enum B { One, Two } | 619 | enum B { One, Two } |
608 | 620 | ||
609 | fn main() { | 621 | fn main() { |
610 | let a = A::One; | 622 | let a = A::One; |
611 | let b = B::One; | 623 | let b = B::One; |
612 | match (a, b) { | 624 | match (a, b) { |
613 | $0(A::One, B::One) => {} | 625 | $0(A::One, B::One) => {} |
614 | (A::One, B::Two) => {} | 626 | (A::One, B::Two) => {} |
615 | (A::Two, B::One) => {} | 627 | (A::Two, B::One) => {} |
616 | (A::Two, B::Two) => {} | 628 | (A::Two, B::Two) => {} |
617 | } | 629 | } |
618 | } | 630 | } |
619 | "#, | 631 | "#, |
620 | ); | 632 | ); |
621 | } | 633 | } |
622 | 634 | ||
@@ -625,30 +637,30 @@ fn main() { | |||
625 | check_assist( | 637 | check_assist( |
626 | fill_match_arms, | 638 | fill_match_arms, |
627 | r#" | 639 | r#" |
628 | enum A { One, Two } | 640 | enum A { One, Two } |
629 | enum B { One, Two } | 641 | enum B { One, Two } |
630 | 642 | ||
631 | fn main() { | 643 | fn main() { |
632 | let a = A::One; | 644 | let a = A::One; |
633 | let b = B::One; | 645 | let b = B::One; |
634 | match (&a$0, &b) {} | 646 | match (&a$0, &b) {} |
635 | } | 647 | } |
636 | "#, | 648 | "#, |
637 | r#" | 649 | r#" |
638 | enum A { One, Two } | 650 | enum A { One, Two } |
639 | enum B { One, Two } | 651 | enum B { One, Two } |
640 | 652 | ||
641 | fn main() { | 653 | fn main() { |
642 | let a = A::One; | 654 | let a = A::One; |
643 | let b = B::One; | 655 | let b = B::One; |
644 | match (&a, &b) { | 656 | match (&a, &b) { |
645 | $0(A::One, B::One) => {} | 657 | $0(A::One, B::One) => {} |
646 | (A::One, B::Two) => {} | 658 | (A::One, B::Two) => {} |
647 | (A::Two, B::One) => {} | 659 | (A::Two, B::One) => {} |
648 | (A::Two, B::Two) => {} | 660 | (A::Two, B::Two) => {} |
649 | } | 661 | } |
650 | } | 662 | } |
651 | "#, | 663 | "#, |
652 | ); | 664 | ); |
653 | } | 665 | } |
654 | 666 | ||
@@ -737,20 +749,20 @@ fn main() { | |||
737 | check_assist_not_applicable( | 749 | check_assist_not_applicable( |
738 | fill_match_arms, | 750 | fill_match_arms, |
739 | r#" | 751 | r#" |
740 | enum A { One, Two } | 752 | enum A { One, Two } |
741 | enum B { One, Two } | 753 | enum B { One, Two } |
742 | 754 | ||
743 | fn main() { | 755 | fn main() { |
744 | let a = A::One; | 756 | let a = A::One; |
745 | let b = B::One; | 757 | let b = B::One; |
746 | match (a$0, b) { | 758 | match (a$0, b) { |
747 | (A::Two, B::One) => {} | 759 | (A::Two, B::One) => {} |
748 | (A::One, B::One) => {} | 760 | (A::One, B::One) => {} |
749 | (A::One, B::Two) => {} | 761 | (A::One, B::Two) => {} |
750 | (A::Two, B::Two) => {} | 762 | (A::Two, B::Two) => {} |
751 | } | 763 | } |
752 | } | 764 | } |
753 | "#, | 765 | "#, |
754 | ); | 766 | ); |
755 | } | 767 | } |
756 | 768 | ||
@@ -759,25 +771,25 @@ fn main() { | |||
759 | check_assist( | 771 | check_assist( |
760 | fill_match_arms, | 772 | fill_match_arms, |
761 | r#" | 773 | r#" |
762 | enum A { One, Two } | 774 | enum A { One, Two } |
763 | 775 | ||
764 | fn main() { | 776 | fn main() { |
765 | let a = A::One; | 777 | let a = A::One; |
766 | match (a$0, ) { | 778 | match (a$0, ) { |
767 | } | 779 | } |
768 | } | 780 | } |
769 | "#, | 781 | "#, |
770 | r#" | 782 | r#" |
771 | enum A { One, Two } | 783 | enum A { One, Two } |
772 | 784 | ||
773 | fn main() { | 785 | fn main() { |
774 | let a = A::One; | 786 | let a = A::One; |
775 | match (a, ) { | 787 | match (a, ) { |
776 | $0(A::One,) => {} | 788 | $0(A::One,) => {} |
777 | (A::Two,) => {} | 789 | (A::Two,) => {} |
778 | } | 790 | } |
779 | } | 791 | } |
780 | "#, | 792 | "#, |
781 | ); | 793 | ); |
782 | } | 794 | } |
783 | 795 | ||
@@ -786,47 +798,47 @@ fn main() { | |||
786 | check_assist( | 798 | check_assist( |
787 | fill_match_arms, | 799 | fill_match_arms, |
788 | r#" | 800 | r#" |
789 | enum A { As } | 801 | enum A { As } |
790 | 802 | ||
791 | fn foo(a: &A) { | 803 | fn foo(a: &A) { |
792 | match a$0 { | 804 | match a$0 { |
793 | } | 805 | } |
794 | } | 806 | } |
795 | "#, | 807 | "#, |
796 | r#" | 808 | r#" |
797 | enum A { As } | 809 | enum A { As } |
798 | 810 | ||
799 | fn foo(a: &A) { | 811 | fn foo(a: &A) { |
800 | match a { | 812 | match a { |
801 | $0A::As => {} | 813 | $0A::As => {} |
802 | } | 814 | } |
803 | } | 815 | } |
804 | "#, | 816 | "#, |
805 | ); | 817 | ); |
806 | 818 | ||
807 | check_assist( | 819 | check_assist( |
808 | fill_match_arms, | 820 | fill_match_arms, |
809 | r#" | 821 | r#" |
810 | enum A { | 822 | enum A { |
811 | Es { x: usize, y: usize } | 823 | Es { x: usize, y: usize } |
812 | } | 824 | } |
813 | 825 | ||
814 | fn foo(a: &mut A) { | 826 | fn foo(a: &mut A) { |
815 | match a$0 { | 827 | match a$0 { |
816 | } | 828 | } |
817 | } | 829 | } |
818 | "#, | 830 | "#, |
819 | r#" | 831 | r#" |
820 | enum A { | 832 | enum A { |
821 | Es { x: usize, y: usize } | 833 | Es { x: usize, y: usize } |
822 | } | 834 | } |
823 | 835 | ||
824 | fn foo(a: &mut A) { | 836 | fn foo(a: &mut A) { |
825 | match a { | 837 | match a { |
826 | $0A::Es { x, y } => {} | 838 | $0A::Es { x, y } => {} |
827 | } | 839 | } |
828 | } | 840 | } |
829 | "#, | 841 | "#, |
830 | ); | 842 | ); |
831 | } | 843 | } |
832 | 844 | ||
@@ -835,12 +847,12 @@ fn main() { | |||
835 | check_assist_target( | 847 | check_assist_target( |
836 | fill_match_arms, | 848 | fill_match_arms, |
837 | r#" | 849 | r#" |
838 | enum E { X, Y } | 850 | enum E { X, Y } |
839 | 851 | ||
840 | fn main() { | 852 | fn main() { |
841 | match E::X$0 {} | 853 | match E::X$0 {} |
842 | } | 854 | } |
843 | "#, | 855 | "#, |
844 | "match E::X {}", | 856 | "match E::X {}", |
845 | ); | 857 | ); |
846 | } | 858 | } |
@@ -850,24 +862,24 @@ fn main() { | |||
850 | check_assist( | 862 | check_assist( |
851 | fill_match_arms, | 863 | fill_match_arms, |
852 | r#" | 864 | r#" |
853 | enum E { X, Y } | 865 | enum E { X, Y } |
854 | 866 | ||
855 | fn main() { | 867 | fn main() { |
856 | match E::X { | 868 | match E::X { |
857 | $0_ => {} | 869 | $0_ => {} |
858 | } | 870 | } |
859 | } | 871 | } |
860 | "#, | 872 | "#, |
861 | r#" | 873 | r#" |
862 | enum E { X, Y } | 874 | enum E { X, Y } |
863 | 875 | ||
864 | fn main() { | 876 | fn main() { |
865 | match E::X { | 877 | match E::X { |
866 | $0E::X => {} | 878 | $0E::X => {} |
867 | E::Y => {} | 879 | E::Y => {} |
868 | } | 880 | } |
869 | } | 881 | } |
870 | "#, | 882 | "#, |
871 | ); | 883 | ); |
872 | } | 884 | } |
873 | 885 | ||
@@ -876,26 +888,26 @@ fn main() { | |||
876 | check_assist( | 888 | check_assist( |
877 | fill_match_arms, | 889 | fill_match_arms, |
878 | r#" | 890 | r#" |
879 | mod foo { pub enum E { X, Y } } | 891 | mod foo { pub enum E { X, Y } } |
880 | use foo::E::X; | 892 | use foo::E::X; |
881 | 893 | ||
882 | fn main() { | 894 | fn main() { |
883 | match X { | 895 | match X { |
884 | $0 | 896 | $0 |
885 | } | 897 | } |
886 | } | 898 | } |
887 | "#, | 899 | "#, |
888 | r#" | 900 | r#" |
889 | mod foo { pub enum E { X, Y } } | 901 | mod foo { pub enum E { X, Y } } |
890 | use foo::E::X; | 902 | use foo::E::X; |
891 | 903 | ||
892 | fn main() { | 904 | fn main() { |
893 | match X { | 905 | match X { |
894 | $0X => {} | 906 | $0X => {} |
895 | foo::E::Y => {} | 907 | foo::E::Y => {} |
896 | } | 908 | } |
897 | } | 909 | } |
898 | "#, | 910 | "#, |
899 | ); | 911 | ); |
900 | } | 912 | } |
901 | 913 | ||
@@ -904,26 +916,26 @@ fn main() { | |||
904 | check_assist( | 916 | check_assist( |
905 | fill_match_arms, | 917 | fill_match_arms, |
906 | r#" | 918 | r#" |
907 | enum A { One, Two } | 919 | enum A { One, Two } |
908 | fn foo(a: A) { | 920 | fn foo(a: A) { |
909 | match a { | 921 | match a { |
910 | // foo bar baz$0 | 922 | // foo bar baz$0 |
911 | A::One => {} | 923 | A::One => {} |
912 | // This is where the rest should be | 924 | // This is where the rest should be |
913 | } | 925 | } |
914 | } | 926 | } |
915 | "#, | 927 | "#, |
916 | r#" | 928 | r#" |
917 | enum A { One, Two } | 929 | enum A { One, Two } |
918 | fn foo(a: A) { | 930 | fn foo(a: A) { |
919 | match a { | 931 | match a { |
920 | // foo bar baz | 932 | // foo bar baz |
921 | A::One => {} | 933 | A::One => {} |
922 | // This is where the rest should be | 934 | $0A::Two => {} |
923 | $0A::Two => {} | 935 | // This is where the rest should be |
924 | } | 936 | } |
925 | } | 937 | } |
926 | "#, | 938 | "#, |
927 | ); | 939 | ); |
928 | } | 940 | } |
929 | 941 | ||
@@ -932,23 +944,23 @@ fn main() { | |||
932 | check_assist( | 944 | check_assist( |
933 | fill_match_arms, | 945 | fill_match_arms, |
934 | r#" | 946 | r#" |
935 | enum A { One, Two } | 947 | enum A { One, Two } |
936 | fn foo(a: A) { | 948 | fn foo(a: A) { |
937 | match a { | 949 | match a { |
938 | // foo bar baz$0 | 950 | // foo bar baz$0 |
939 | } | 951 | } |
940 | } | 952 | } |
941 | "#, | 953 | "#, |
942 | r#" | 954 | r#" |
943 | enum A { One, Two } | 955 | enum A { One, Two } |
944 | fn foo(a: A) { | 956 | fn foo(a: A) { |
945 | match a { | 957 | match a { |
946 | // foo bar baz | 958 | $0A::One => {} |
947 | $0A::One => {} | 959 | A::Two => {} |
948 | A::Two => {} | 960 | // foo bar baz |
949 | } | 961 | } |
950 | } | 962 | } |
951 | "#, | 963 | "#, |
952 | ); | 964 | ); |
953 | } | 965 | } |
954 | 966 | ||
@@ -957,22 +969,22 @@ fn main() { | |||
957 | check_assist( | 969 | check_assist( |
958 | fill_match_arms, | 970 | fill_match_arms, |
959 | r#" | 971 | r#" |
960 | enum A { One, Two, } | 972 | enum A { One, Two, } |
961 | fn foo(a: A) { | 973 | fn foo(a: A) { |
962 | match a$0 { | 974 | match a$0 { |
963 | _ => (), | 975 | _ => (), |
964 | } | 976 | } |
965 | } | 977 | } |
966 | "#, | 978 | "#, |
967 | r#" | 979 | r#" |
968 | enum A { One, Two, } | 980 | enum A { One, Two, } |
969 | fn foo(a: A) { | 981 | fn foo(a: A) { |
970 | match a { | 982 | match a { |
971 | $0A::One => {} | 983 | $0A::One => {} |
972 | A::Two => {} | 984 | A::Two => {} |
973 | } | 985 | } |
974 | } | 986 | } |
975 | "#, | 987 | "#, |
976 | ); | 988 | ); |
977 | } | 989 | } |
978 | 990 | ||
@@ -1016,7 +1028,8 @@ enum Test { | |||
1016 | fn foo(t: Test) { | 1028 | fn foo(t: Test) { |
1017 | m!(match t$0 {}); | 1029 | m!(match t$0 {}); |
1018 | }"#, | 1030 | }"#, |
1019 | r#"macro_rules! m { ($expr:expr) => {$expr}} | 1031 | r#" |
1032 | macro_rules! m { ($expr:expr) => {$expr}} | ||
1020 | enum Test { | 1033 | enum Test { |
1021 | A, | 1034 | A, |
1022 | B, | 1035 | B, |
diff --git a/crates/ide_assists/src/handlers/generate_default_from_new.rs b/crates/ide_assists/src/handlers/generate_default_from_new.rs index dc14552d6..bad826366 100644 --- a/crates/ide_assists/src/handlers/generate_default_from_new.rs +++ b/crates/ide_assists/src/handlers/generate_default_from_new.rs | |||
@@ -3,8 +3,10 @@ use crate::{ | |||
3 | AssistId, | 3 | AssistId, |
4 | }; | 4 | }; |
5 | use ide_db::helpers::FamousDefs; | 5 | use ide_db::helpers::FamousDefs; |
6 | use itertools::Itertools; | ||
7 | use stdx::format_to; | ||
6 | use syntax::{ | 8 | use syntax::{ |
7 | ast::{self, Impl, NameOwner}, | 9 | ast::{self, GenericParamsOwner, Impl, NameOwner, TypeBoundsOwner}, |
8 | AstNode, | 10 | AstNode, |
9 | }; | 11 | }; |
10 | 12 | ||
@@ -65,23 +67,56 @@ pub(crate) fn generate_default_from_new(acc: &mut Assists, ctx: &AssistContext) | |||
65 | "Generate a Default impl from a new fn", | 67 | "Generate a Default impl from a new fn", |
66 | insert_location, | 68 | insert_location, |
67 | move |builder| { | 69 | move |builder| { |
68 | let code = default_fn_node_for_new(impl_); | 70 | let default_code = " fn default() -> Self { |
71 | Self::new() | ||
72 | }"; | ||
73 | let code = generate_trait_impl_text_from_impl(&impl_, "Default", default_code); | ||
69 | builder.insert(insert_location.end(), code); | 74 | builder.insert(insert_location.end(), code); |
70 | }, | 75 | }, |
71 | ) | 76 | ) |
72 | } | 77 | } |
73 | 78 | ||
74 | fn default_fn_node_for_new(impl_: Impl) -> String { | 79 | fn generate_trait_impl_text_from_impl(impl_: &ast::Impl, trait_text: &str, code: &str) -> String { |
75 | format!( | 80 | let generic_params = impl_.generic_param_list(); |
76 | " | 81 | let mut buf = String::with_capacity(code.len()); |
82 | buf.push_str("\n\n"); | ||
83 | buf.push_str("impl"); | ||
84 | |||
85 | if let Some(generic_params) = &generic_params { | ||
86 | let lifetimes = generic_params.lifetime_params().map(|lt| format!("{}", lt.syntax())); | ||
87 | let type_params = generic_params.type_params().map(|type_param| { | ||
88 | let mut buf = String::new(); | ||
89 | if let Some(it) = type_param.name() { | ||
90 | format_to!(buf, "{}", it.syntax()); | ||
91 | } | ||
92 | if let Some(it) = type_param.colon_token() { | ||
93 | format_to!(buf, "{} ", it); | ||
94 | } | ||
95 | if let Some(it) = type_param.type_bound_list() { | ||
96 | format_to!(buf, "{}", it.syntax()); | ||
97 | } | ||
98 | buf | ||
99 | }); | ||
100 | let const_params = generic_params.const_params().map(|t| t.syntax().to_string()); | ||
101 | let generics = lifetimes.chain(type_params).chain(const_params).format(", "); | ||
102 | format_to!(buf, "<{}>", generics); | ||
103 | } | ||
104 | |||
105 | buf.push(' '); | ||
106 | buf.push_str(trait_text); | ||
107 | buf.push_str(" for "); | ||
108 | buf.push_str(&impl_.self_ty().unwrap().syntax().text().to_string()); | ||
109 | |||
110 | match impl_.where_clause() { | ||
111 | Some(where_clause) => { | ||
112 | format_to!(buf, "\n{}\n{{\n{}\n}}", where_clause, code); | ||
113 | } | ||
114 | None => { | ||
115 | format_to!(buf, " {{\n{}\n}}", code); | ||
116 | } | ||
117 | } | ||
77 | 118 | ||
78 | impl Default for {} {{ | 119 | buf |
79 | fn default() -> Self {{ | ||
80 | Self::new() | ||
81 | }} | ||
82 | }}", | ||
83 | impl_.self_ty().unwrap().syntax().text() | ||
84 | ) | ||
85 | } | 120 | } |
86 | 121 | ||
87 | fn is_default_implemented(ctx: &AssistContext, impl_: &Impl) -> bool { | 122 | fn is_default_implemented(ctx: &AssistContext, impl_: &Impl) -> bool { |
@@ -176,6 +211,234 @@ impl Default for Test { | |||
176 | } | 211 | } |
177 | 212 | ||
178 | #[test] | 213 | #[test] |
214 | fn new_function_with_generic() { | ||
215 | check_pass( | ||
216 | r#" | ||
217 | pub struct Foo<T> { | ||
218 | _bar: *mut T, | ||
219 | } | ||
220 | |||
221 | impl<T> Foo<T> { | ||
222 | pub fn ne$0w() -> Self { | ||
223 | unimplemented!() | ||
224 | } | ||
225 | } | ||
226 | "#, | ||
227 | r#" | ||
228 | pub struct Foo<T> { | ||
229 | _bar: *mut T, | ||
230 | } | ||
231 | |||
232 | impl<T> Foo<T> { | ||
233 | pub fn new() -> Self { | ||
234 | unimplemented!() | ||
235 | } | ||
236 | } | ||
237 | |||
238 | impl<T> Default for Foo<T> { | ||
239 | fn default() -> Self { | ||
240 | Self::new() | ||
241 | } | ||
242 | } | ||
243 | "#, | ||
244 | ); | ||
245 | } | ||
246 | |||
247 | #[test] | ||
248 | fn new_function_with_generics() { | ||
249 | check_pass( | ||
250 | r#" | ||
251 | pub struct Foo<T, B> { | ||
252 | _tars: *mut T, | ||
253 | _bar: *mut B, | ||
254 | } | ||
255 | |||
256 | impl<T, B> Foo<T, B> { | ||
257 | pub fn ne$0w() -> Self { | ||
258 | unimplemented!() | ||
259 | } | ||
260 | } | ||
261 | "#, | ||
262 | r#" | ||
263 | pub struct Foo<T, B> { | ||
264 | _tars: *mut T, | ||
265 | _bar: *mut B, | ||
266 | } | ||
267 | |||
268 | impl<T, B> Foo<T, B> { | ||
269 | pub fn new() -> Self { | ||
270 | unimplemented!() | ||
271 | } | ||
272 | } | ||
273 | |||
274 | impl<T, B> Default for Foo<T, B> { | ||
275 | fn default() -> Self { | ||
276 | Self::new() | ||
277 | } | ||
278 | } | ||
279 | "#, | ||
280 | ); | ||
281 | } | ||
282 | |||
283 | #[test] | ||
284 | fn new_function_with_generic_and_bound() { | ||
285 | check_pass( | ||
286 | r#" | ||
287 | pub struct Foo<T> { | ||
288 | t: T, | ||
289 | } | ||
290 | |||
291 | impl<T: From<i32>> Foo<T> { | ||
292 | pub fn ne$0w() -> Self { | ||
293 | Foo { t: 0.into() } | ||
294 | } | ||
295 | } | ||
296 | "#, | ||
297 | r#" | ||
298 | pub struct Foo<T> { | ||
299 | t: T, | ||
300 | } | ||
301 | |||
302 | impl<T: From<i32>> Foo<T> { | ||
303 | pub fn new() -> Self { | ||
304 | Foo { t: 0.into() } | ||
305 | } | ||
306 | } | ||
307 | |||
308 | impl<T: From<i32>> Default for Foo<T> { | ||
309 | fn default() -> Self { | ||
310 | Self::new() | ||
311 | } | ||
312 | } | ||
313 | "#, | ||
314 | ); | ||
315 | } | ||
316 | |||
317 | #[test] | ||
318 | fn new_function_with_generics_and_bounds() { | ||
319 | check_pass( | ||
320 | r#" | ||
321 | pub struct Foo<T, B> { | ||
322 | _tars: T, | ||
323 | _bar: B, | ||
324 | } | ||
325 | |||
326 | impl<T: From<i32>, B: From<i64>> Foo<T, B> { | ||
327 | pub fn ne$0w() -> Self { | ||
328 | unimplemented!() | ||
329 | } | ||
330 | } | ||
331 | "#, | ||
332 | r#" | ||
333 | pub struct Foo<T, B> { | ||
334 | _tars: T, | ||
335 | _bar: B, | ||
336 | } | ||
337 | |||
338 | impl<T: From<i32>, B: From<i64>> Foo<T, B> { | ||
339 | pub fn new() -> Self { | ||
340 | unimplemented!() | ||
341 | } | ||
342 | } | ||
343 | |||
344 | impl<T: From<i32>, B: From<i64>> Default for Foo<T, B> { | ||
345 | fn default() -> Self { | ||
346 | Self::new() | ||
347 | } | ||
348 | } | ||
349 | "#, | ||
350 | ); | ||
351 | } | ||
352 | |||
353 | #[test] | ||
354 | fn new_function_with_generic_and_where() { | ||
355 | check_pass( | ||
356 | r#" | ||
357 | pub struct Foo<T> { | ||
358 | t: T, | ||
359 | } | ||
360 | |||
361 | impl<T: From<i32>> Foo<T> | ||
362 | where | ||
363 | Option<T>: Debug | ||
364 | { | ||
365 | pub fn ne$0w() -> Self { | ||
366 | Foo { t: 0.into() } | ||
367 | } | ||
368 | } | ||
369 | "#, | ||
370 | r#" | ||
371 | pub struct Foo<T> { | ||
372 | t: T, | ||
373 | } | ||
374 | |||
375 | impl<T: From<i32>> Foo<T> | ||
376 | where | ||
377 | Option<T>: Debug | ||
378 | { | ||
379 | pub fn new() -> Self { | ||
380 | Foo { t: 0.into() } | ||
381 | } | ||
382 | } | ||
383 | |||
384 | impl<T: From<i32>> Default for Foo<T> | ||
385 | where | ||
386 | Option<T>: Debug | ||
387 | { | ||
388 | fn default() -> Self { | ||
389 | Self::new() | ||
390 | } | ||
391 | } | ||
392 | "#, | ||
393 | ); | ||
394 | } | ||
395 | |||
396 | #[test] | ||
397 | fn new_function_with_generics_and_wheres() { | ||
398 | check_pass( | ||
399 | r#" | ||
400 | pub struct Foo<T, B> { | ||
401 | _tars: T, | ||
402 | _bar: B, | ||
403 | } | ||
404 | |||
405 | impl<T: From<i32>, B: From<i64>> Foo<T, B> | ||
406 | where | ||
407 | Option<T>: Debug, Option<B>: Debug, | ||
408 | { | ||
409 | pub fn ne$0w() -> Self { | ||
410 | unimplemented!() | ||
411 | } | ||
412 | } | ||
413 | "#, | ||
414 | r#" | ||
415 | pub struct Foo<T, B> { | ||
416 | _tars: T, | ||
417 | _bar: B, | ||
418 | } | ||
419 | |||
420 | impl<T: From<i32>, B: From<i64>> Foo<T, B> | ||
421 | where | ||
422 | Option<T>: Debug, Option<B>: Debug, | ||
423 | { | ||
424 | pub fn new() -> Self { | ||
425 | unimplemented!() | ||
426 | } | ||
427 | } | ||
428 | |||
429 | impl<T: From<i32>, B: From<i64>> Default for Foo<T, B> | ||
430 | where | ||
431 | Option<T>: Debug, Option<B>: Debug, | ||
432 | { | ||
433 | fn default() -> Self { | ||
434 | Self::new() | ||
435 | } | ||
436 | } | ||
437 | "#, | ||
438 | ); | ||
439 | } | ||
440 | |||
441 | #[test] | ||
179 | fn new_function_with_parameters() { | 442 | fn new_function_with_parameters() { |
180 | cov_mark::check!(new_function_with_parameters); | 443 | cov_mark::check!(new_function_with_parameters); |
181 | check_not_applicable( | 444 | check_not_applicable( |
diff --git a/crates/ide_assists/src/handlers/generate_new.rs b/crates/ide_assists/src/handlers/generate_new.rs index 8ce5930b7..959a1f86c 100644 --- a/crates/ide_assists/src/handlers/generate_new.rs +++ b/crates/ide_assists/src/handlers/generate_new.rs | |||
@@ -1,4 +1,3 @@ | |||
1 | use ast::Adt; | ||
2 | use itertools::Itertools; | 1 | use itertools::Itertools; |
3 | use stdx::format_to; | 2 | use stdx::format_to; |
4 | use syntax::ast::{self, AstNode, NameOwner, StructKind, VisibilityOwner}; | 3 | use syntax::ast::{self, AstNode, NameOwner, StructKind, VisibilityOwner}; |
@@ -37,7 +36,7 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
37 | }; | 36 | }; |
38 | 37 | ||
39 | // Return early if we've found an existing new fn | 38 | // Return early if we've found an existing new fn |
40 | let impl_def = find_struct_impl(&ctx, &Adt::Struct(strukt.clone()), "new")?; | 39 | let impl_def = find_struct_impl(&ctx, &ast::Adt::Struct(strukt.clone()), "new")?; |
41 | 40 | ||
42 | let target = strukt.syntax().text_range(); | 41 | let target = strukt.syntax().text_range(); |
43 | acc.add(AssistId("generate_new", AssistKind::Generate), "Generate `new`", target, |builder| { | 42 | acc.add(AssistId("generate_new", AssistKind::Generate), "Generate `new`", target, |builder| { |
@@ -60,7 +59,7 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
60 | let start_offset = impl_def | 59 | let start_offset = impl_def |
61 | .and_then(|impl_def| find_impl_block_start(impl_def, &mut buf)) | 60 | .and_then(|impl_def| find_impl_block_start(impl_def, &mut buf)) |
62 | .unwrap_or_else(|| { | 61 | .unwrap_or_else(|| { |
63 | buf = generate_impl_text(&Adt::Struct(strukt.clone()), &buf); | 62 | buf = generate_impl_text(&ast::Adt::Struct(strukt.clone()), &buf); |
64 | strukt.syntax().text_range().end() | 63 | strukt.syntax().text_range().end() |
65 | }); | 64 | }); |
66 | 65 | ||
@@ -81,101 +80,132 @@ mod tests { | |||
81 | use super::*; | 80 | use super::*; |
82 | 81 | ||
83 | #[test] | 82 | #[test] |
84 | #[rustfmt::skip] | ||
85 | fn test_generate_new() { | 83 | fn test_generate_new() { |
86 | // Check output of generation | ||
87 | check_assist( | 84 | check_assist( |
88 | generate_new, | 85 | generate_new, |
89 | "struct Foo {$0}", | 86 | r#" |
90 | "struct Foo {} | 87 | struct Foo {$0} |
88 | "#, | ||
89 | r#" | ||
90 | struct Foo {} | ||
91 | 91 | ||
92 | impl Foo { | 92 | impl Foo { |
93 | fn $0new() -> Self { Self { } } | 93 | fn $0new() -> Self { Self { } } |
94 | }", | 94 | } |
95 | "#, | ||
95 | ); | 96 | ); |
96 | check_assist( | 97 | check_assist( |
97 | generate_new, | 98 | generate_new, |
98 | "struct Foo<T: Clone> {$0}", | 99 | r#" |
99 | "struct Foo<T: Clone> {} | 100 | struct Foo<T: Clone> {$0} |
101 | "#, | ||
102 | r#" | ||
103 | struct Foo<T: Clone> {} | ||
100 | 104 | ||
101 | impl<T: Clone> Foo<T> { | 105 | impl<T: Clone> Foo<T> { |
102 | fn $0new() -> Self { Self { } } | 106 | fn $0new() -> Self { Self { } } |
103 | }", | 107 | } |
108 | "#, | ||
104 | ); | 109 | ); |
105 | check_assist( | 110 | check_assist( |
106 | generate_new, | 111 | generate_new, |
107 | "struct Foo<'a, T: Foo<'a>> {$0}", | 112 | r#" |
108 | "struct Foo<'a, T: Foo<'a>> {} | 113 | struct Foo<'a, T: Foo<'a>> {$0} |
114 | "#, | ||
115 | r#" | ||
116 | struct Foo<'a, T: Foo<'a>> {} | ||
109 | 117 | ||
110 | impl<'a, T: Foo<'a>> Foo<'a, T> { | 118 | impl<'a, T: Foo<'a>> Foo<'a, T> { |
111 | fn $0new() -> Self { Self { } } | 119 | fn $0new() -> Self { Self { } } |
112 | }", | 120 | } |
121 | "#, | ||
113 | ); | 122 | ); |
114 | check_assist( | 123 | check_assist( |
115 | generate_new, | 124 | generate_new, |
116 | "struct Foo { baz: String $0}", | 125 | r#" |
117 | "struct Foo { baz: String } | 126 | struct Foo { baz: String $0} |
127 | "#, | ||
128 | r#" | ||
129 | struct Foo { baz: String } | ||
118 | 130 | ||
119 | impl Foo { | 131 | impl Foo { |
120 | fn $0new(baz: String) -> Self { Self { baz } } | 132 | fn $0new(baz: String) -> Self { Self { baz } } |
121 | }", | 133 | } |
134 | "#, | ||
122 | ); | 135 | ); |
123 | check_assist( | 136 | check_assist( |
124 | generate_new, | 137 | generate_new, |
125 | "struct Foo { baz: String, qux: Vec<i32> $0}", | 138 | r#" |
126 | "struct Foo { baz: String, qux: Vec<i32> } | 139 | struct Foo { baz: String, qux: Vec<i32> $0} |
140 | "#, | ||
141 | r#" | ||
142 | struct Foo { baz: String, qux: Vec<i32> } | ||
127 | 143 | ||
128 | impl Foo { | 144 | impl Foo { |
129 | fn $0new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } } | 145 | fn $0new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } } |
130 | }", | 146 | } |
147 | "#, | ||
131 | ); | 148 | ); |
149 | } | ||
132 | 150 | ||
133 | // Check that visibility modifiers don't get brought in for fields | 151 | #[test] |
152 | fn check_that_visibility_modifiers_dont_get_brought_in() { | ||
134 | check_assist( | 153 | check_assist( |
135 | generate_new, | 154 | generate_new, |
136 | "struct Foo { pub baz: String, pub qux: Vec<i32> $0}", | 155 | r#" |
137 | "struct Foo { pub baz: String, pub qux: Vec<i32> } | 156 | struct Foo { pub baz: String, pub qux: Vec<i32> $0} |
157 | "#, | ||
158 | r#" | ||
159 | struct Foo { pub baz: String, pub qux: Vec<i32> } | ||
138 | 160 | ||
139 | impl Foo { | 161 | impl Foo { |
140 | fn $0new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } } | 162 | fn $0new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } } |
141 | }", | 163 | } |
164 | "#, | ||
142 | ); | 165 | ); |
166 | } | ||
143 | 167 | ||
144 | // Check that it reuses existing impls | 168 | #[test] |
169 | fn check_it_reuses_existing_impls() { | ||
145 | check_assist( | 170 | check_assist( |
146 | generate_new, | 171 | generate_new, |
147 | "struct Foo {$0} | 172 | r#" |
173 | struct Foo {$0} | ||
148 | 174 | ||
149 | impl Foo {} | 175 | impl Foo {} |
150 | ", | 176 | "#, |
151 | "struct Foo {} | 177 | r#" |
178 | struct Foo {} | ||
152 | 179 | ||
153 | impl Foo { | 180 | impl Foo { |
154 | fn $0new() -> Self { Self { } } | 181 | fn $0new() -> Self { Self { } } |
155 | } | 182 | } |
156 | ", | 183 | "#, |
157 | ); | 184 | ); |
158 | check_assist( | 185 | check_assist( |
159 | generate_new, | 186 | generate_new, |
160 | "struct Foo {$0} | 187 | r#" |
188 | struct Foo {$0} | ||
161 | 189 | ||
162 | impl Foo { | 190 | impl Foo { |
163 | fn qux(&self) {} | 191 | fn qux(&self) {} |
164 | } | 192 | } |
165 | ", | 193 | "#, |
166 | "struct Foo {} | 194 | r#" |
195 | struct Foo {} | ||
167 | 196 | ||
168 | impl Foo { | 197 | impl Foo { |
169 | fn $0new() -> Self { Self { } } | 198 | fn $0new() -> Self { Self { } } |
170 | 199 | ||
171 | fn qux(&self) {} | 200 | fn qux(&self) {} |
172 | } | 201 | } |
173 | ", | 202 | "#, |
174 | ); | 203 | ); |
175 | 204 | ||
176 | check_assist( | 205 | check_assist( |
177 | generate_new, | 206 | generate_new, |
178 | "struct Foo {$0} | 207 | r#" |
208 | struct Foo {$0} | ||
179 | 209 | ||
180 | impl Foo { | 210 | impl Foo { |
181 | fn qux(&self) {} | 211 | fn qux(&self) {} |
@@ -183,8 +213,9 @@ impl Foo { | |||
183 | 5 | 213 | 5 |
184 | } | 214 | } |
185 | } | 215 | } |
186 | ", | 216 | "#, |
187 | "struct Foo {} | 217 | r#" |
218 | struct Foo {} | ||
188 | 219 | ||
189 | impl Foo { | 220 | impl Foo { |
190 | fn $0new() -> Self { Self { } } | 221 | fn $0new() -> Self { Self { } } |
@@ -194,27 +225,37 @@ impl Foo { | |||
194 | 5 | 225 | 5 |
195 | } | 226 | } |
196 | } | 227 | } |
197 | ", | 228 | "#, |
198 | ); | 229 | ); |
230 | } | ||
199 | 231 | ||
200 | // Check visibility of new fn based on struct | 232 | #[test] |
233 | fn check_visibility_of_new_fn_based_on_struct() { | ||
201 | check_assist( | 234 | check_assist( |
202 | generate_new, | 235 | generate_new, |
203 | "pub struct Foo {$0}", | 236 | r#" |
204 | "pub struct Foo {} | 237 | pub struct Foo {$0} |
238 | "#, | ||
239 | r#" | ||
240 | pub struct Foo {} | ||
205 | 241 | ||
206 | impl Foo { | 242 | impl Foo { |
207 | pub fn $0new() -> Self { Self { } } | 243 | pub fn $0new() -> Self { Self { } } |
208 | }", | 244 | } |
245 | "#, | ||
209 | ); | 246 | ); |
210 | check_assist( | 247 | check_assist( |
211 | generate_new, | 248 | generate_new, |
212 | "pub(crate) struct Foo {$0}", | 249 | r#" |
213 | "pub(crate) struct Foo {} | 250 | pub(crate) struct Foo {$0} |
251 | "#, | ||
252 | r#" | ||
253 | pub(crate) struct Foo {} | ||
214 | 254 | ||
215 | impl Foo { | 255 | impl Foo { |
216 | pub(crate) fn $0new() -> Self { Self { } } | 256 | pub(crate) fn $0new() -> Self { Self { } } |
217 | }", | 257 | } |
258 | "#, | ||
218 | ); | 259 | ); |
219 | } | 260 | } |
220 | 261 | ||
@@ -222,26 +263,28 @@ impl Foo { | |||
222 | fn generate_new_not_applicable_if_fn_exists() { | 263 | fn generate_new_not_applicable_if_fn_exists() { |
223 | check_assist_not_applicable( | 264 | check_assist_not_applicable( |
224 | generate_new, | 265 | generate_new, |
225 | " | 266 | r#" |
226 | struct Foo {$0} | 267 | struct Foo {$0} |
227 | 268 | ||
228 | impl Foo { | 269 | impl Foo { |
229 | fn new() -> Self { | 270 | fn new() -> Self { |
230 | Self | 271 | Self |
231 | } | 272 | } |
232 | }", | 273 | } |
274 | "#, | ||
233 | ); | 275 | ); |
234 | 276 | ||
235 | check_assist_not_applicable( | 277 | check_assist_not_applicable( |
236 | generate_new, | 278 | generate_new, |
237 | " | 279 | r#" |
238 | struct Foo {$0} | 280 | struct Foo {$0} |
239 | 281 | ||
240 | impl Foo { | 282 | impl Foo { |
241 | fn New() -> Self { | 283 | fn New() -> Self { |
242 | Self | 284 | Self |
243 | } | 285 | } |
244 | }", | 286 | } |
287 | "#, | ||
245 | ); | 288 | ); |
246 | } | 289 | } |
247 | 290 | ||
@@ -249,12 +292,12 @@ impl Foo { | |||
249 | fn generate_new_target() { | 292 | fn generate_new_target() { |
250 | check_assist_target( | 293 | check_assist_target( |
251 | generate_new, | 294 | generate_new, |
252 | " | 295 | r#" |
253 | struct SomeThingIrrelevant; | 296 | struct SomeThingIrrelevant; |
254 | /// Has a lifetime parameter | 297 | /// Has a lifetime parameter |
255 | struct Foo<'a, T: Foo<'a>> {$0} | 298 | struct Foo<'a, T: Foo<'a>> {$0} |
256 | struct EvenMoreIrrelevant; | 299 | struct EvenMoreIrrelevant; |
257 | ", | 300 | "#, |
258 | "/// Has a lifetime parameter | 301 | "/// Has a lifetime parameter |
259 | struct Foo<'a, T: Foo<'a>> {}", | 302 | struct Foo<'a, T: Foo<'a>> {}", |
260 | ); | 303 | ); |
@@ -264,7 +307,7 @@ struct Foo<'a, T: Foo<'a>> {}", | |||
264 | fn test_unrelated_new() { | 307 | fn test_unrelated_new() { |
265 | check_assist( | 308 | check_assist( |
266 | generate_new, | 309 | generate_new, |
267 | r##" | 310 | r#" |
268 | pub struct AstId<N: AstNode> { | 311 | pub struct AstId<N: AstNode> { |
269 | file_id: HirFileId, | 312 | file_id: HirFileId, |
270 | file_ast_id: FileAstId<N>, | 313 | file_ast_id: FileAstId<N>, |
@@ -285,8 +328,9 @@ impl<T> Source<T> { | |||
285 | pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> { | 328 | pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> { |
286 | Source { file_id: self.file_id, ast: f(self.ast) } | 329 | Source { file_id: self.file_id, ast: f(self.ast) } |
287 | } | 330 | } |
288 | }"##, | 331 | } |
289 | r##" | 332 | "#, |
333 | r#" | ||
290 | pub struct AstId<N: AstNode> { | 334 | pub struct AstId<N: AstNode> { |
291 | file_id: HirFileId, | 335 | file_id: HirFileId, |
292 | file_ast_id: FileAstId<N>, | 336 | file_ast_id: FileAstId<N>, |
@@ -309,7 +353,8 @@ impl<T> Source<T> { | |||
309 | pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> { | 353 | pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> { |
310 | Source { file_id: self.file_id, ast: f(self.ast) } | 354 | Source { file_id: self.file_id, ast: f(self.ast) } |
311 | } | 355 | } |
312 | }"##, | 356 | } |
357 | "#, | ||
313 | ); | 358 | ); |
314 | } | 359 | } |
315 | } | 360 | } |
diff --git a/crates/ide_assists/src/handlers/introduce_named_lifetime.rs b/crates/ide_assists/src/handlers/introduce_named_lifetime.rs index 68bc15120..16f8f9d70 100644 --- a/crates/ide_assists/src/handlers/introduce_named_lifetime.rs +++ b/crates/ide_assists/src/handlers/introduce_named_lifetime.rs | |||
@@ -84,8 +84,8 @@ fn generate_fn_def_assist( | |||
84 | } | 84 | } |
85 | }; | 85 | }; |
86 | acc.add(AssistId(ASSIST_NAME, AssistKind::Refactor), ASSIST_LABEL, lifetime_loc, |builder| { | 86 | acc.add(AssistId(ASSIST_NAME, AssistKind::Refactor), ASSIST_LABEL, lifetime_loc, |builder| { |
87 | let fn_def = builder.make_ast_mut(fn_def); | 87 | let fn_def = builder.make_mut(fn_def); |
88 | let lifetime = builder.make_ast_mut(lifetime); | 88 | let lifetime = builder.make_mut(lifetime); |
89 | let loc_needing_lifetime = | 89 | let loc_needing_lifetime = |
90 | loc_needing_lifetime.and_then(|it| it.make_mut(builder).to_position()); | 90 | loc_needing_lifetime.and_then(|it| it.make_mut(builder).to_position()); |
91 | 91 | ||
@@ -107,8 +107,8 @@ fn generate_impl_def_assist( | |||
107 | ) -> Option<()> { | 107 | ) -> Option<()> { |
108 | let new_lifetime_param = generate_unique_lifetime_param_name(impl_def.generic_param_list())?; | 108 | let new_lifetime_param = generate_unique_lifetime_param_name(impl_def.generic_param_list())?; |
109 | acc.add(AssistId(ASSIST_NAME, AssistKind::Refactor), ASSIST_LABEL, lifetime_loc, |builder| { | 109 | acc.add(AssistId(ASSIST_NAME, AssistKind::Refactor), ASSIST_LABEL, lifetime_loc, |builder| { |
110 | let impl_def = builder.make_ast_mut(impl_def); | 110 | let impl_def = builder.make_mut(impl_def); |
111 | let lifetime = builder.make_ast_mut(lifetime); | 111 | let lifetime = builder.make_mut(lifetime); |
112 | 112 | ||
113 | impl_def.get_or_create_generic_param_list().add_generic_param( | 113 | impl_def.get_or_create_generic_param_list().add_generic_param( |
114 | make::lifetime_param(new_lifetime_param.clone()).clone_for_update().into(), | 114 | make::lifetime_param(new_lifetime_param.clone()).clone_for_update().into(), |
@@ -141,8 +141,8 @@ enum NeedsLifetime { | |||
141 | impl NeedsLifetime { | 141 | impl NeedsLifetime { |
142 | fn make_mut(self, builder: &mut AssistBuilder) -> Self { | 142 | fn make_mut(self, builder: &mut AssistBuilder) -> Self { |
143 | match self { | 143 | match self { |
144 | Self::SelfParam(it) => Self::SelfParam(builder.make_ast_mut(it)), | 144 | Self::SelfParam(it) => Self::SelfParam(builder.make_mut(it)), |
145 | Self::RefType(it) => Self::RefType(builder.make_ast_mut(it)), | 145 | Self::RefType(it) => Self::RefType(builder.make_mut(it)), |
146 | } | 146 | } |
147 | } | 147 | } |
148 | 148 | ||
diff --git a/crates/ide_assists/src/handlers/merge_imports.rs b/crates/ide_assists/src/handlers/merge_imports.rs index 3cd090737..31854840c 100644 --- a/crates/ide_assists/src/handlers/merge_imports.rs +++ b/crates/ide_assists/src/handlers/merge_imports.rs | |||
@@ -47,16 +47,16 @@ pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext) -> Option<() | |||
47 | target, | 47 | target, |
48 | |builder| { | 48 | |builder| { |
49 | if let Some((to_replace, replacement, to_remove)) = imports { | 49 | if let Some((to_replace, replacement, to_remove)) = imports { |
50 | let to_replace = builder.make_ast_mut(to_replace); | 50 | let to_replace = builder.make_mut(to_replace); |
51 | let to_remove = builder.make_ast_mut(to_remove); | 51 | let to_remove = builder.make_mut(to_remove); |
52 | 52 | ||
53 | ted::replace(to_replace.syntax(), replacement.syntax()); | 53 | ted::replace(to_replace.syntax(), replacement.syntax()); |
54 | to_remove.remove(); | 54 | to_remove.remove(); |
55 | } | 55 | } |
56 | 56 | ||
57 | if let Some((to_replace, replacement, to_remove)) = uses { | 57 | if let Some((to_replace, replacement, to_remove)) = uses { |
58 | let to_replace = builder.make_ast_mut(to_replace); | 58 | let to_replace = builder.make_mut(to_replace); |
59 | let to_remove = builder.make_ast_mut(to_remove); | 59 | let to_remove = builder.make_mut(to_remove); |
60 | 60 | ||
61 | ted::replace(to_replace.syntax(), replacement.syntax()); | 61 | ted::replace(to_replace.syntax(), replacement.syntax()); |
62 | to_remove.remove() | 62 | to_remove.remove() |
diff --git a/crates/ide_assists/src/handlers/move_bounds.rs b/crates/ide_assists/src/handlers/move_bounds.rs index fa3f76609..d89d11bdf 100644 --- a/crates/ide_assists/src/handlers/move_bounds.rs +++ b/crates/ide_assists/src/handlers/move_bounds.rs | |||
@@ -36,8 +36,8 @@ pub(crate) fn move_bounds_to_where_clause(acc: &mut Assists, ctx: &AssistContext | |||
36 | "Move to where clause", | 36 | "Move to where clause", |
37 | target, | 37 | target, |
38 | |edit| { | 38 | |edit| { |
39 | let type_param_list = edit.make_ast_mut(type_param_list); | 39 | let type_param_list = edit.make_mut(type_param_list); |
40 | let parent = edit.make_mut(parent); | 40 | let parent = edit.make_syntax_mut(parent); |
41 | 41 | ||
42 | let where_clause: ast::WhereClause = match_ast! { | 42 | let where_clause: ast::WhereClause = match_ast! { |
43 | match parent { | 43 | match parent { |
diff --git a/crates/ide_assists/src/handlers/move_module_to_file.rs b/crates/ide_assists/src/handlers/move_module_to_file.rs index 6e685b4b2..93f702c55 100644 --- a/crates/ide_assists/src/handlers/move_module_to_file.rs +++ b/crates/ide_assists/src/handlers/move_module_to_file.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | use ast::{edit::IndentLevel, VisibilityOwner}; | 1 | use ast::edit::IndentLevel; |
2 | use ide_db::base_db::AnchoredPathBuf; | 2 | use ide_db::base_db::AnchoredPathBuf; |
3 | use stdx::format_to; | 3 | use stdx::format_to; |
4 | use syntax::{ | 4 | use syntax::{ |
@@ -60,12 +60,18 @@ pub(crate) fn move_module_to_file(acc: &mut Assists, ctx: &AssistContext) -> Opt | |||
60 | }; | 60 | }; |
61 | 61 | ||
62 | let mut buf = String::new(); | 62 | let mut buf = String::new(); |
63 | if let Some(v) = module_ast.visibility() { | ||
64 | format_to!(buf, "{} ", v); | ||
65 | } | ||
66 | format_to!(buf, "mod {};", module_name); | 63 | format_to!(buf, "mod {};", module_name); |
67 | 64 | ||
68 | builder.replace(module_ast.syntax().text_range(), buf); | 65 | let replacement_start = if let Some(mod_token) = module_ast.mod_token() { |
66 | mod_token.text_range().start() | ||
67 | } else { | ||
68 | module_ast.syntax().text_range().start() | ||
69 | }; | ||
70 | |||
71 | builder.replace( | ||
72 | TextRange::new(replacement_start, module_ast.syntax().text_range().end()), | ||
73 | buf, | ||
74 | ); | ||
69 | 75 | ||
70 | let dst = AnchoredPathBuf { anchor: ctx.frange.file_id, path }; | 76 | let dst = AnchoredPathBuf { anchor: ctx.frange.file_id, path }; |
71 | builder.create_file(dst, contents); | 77 | builder.create_file(dst, contents); |
@@ -184,4 +190,26 @@ pub(crate) mod tests; | |||
184 | cov_mark::check!(available_before_curly); | 190 | cov_mark::check!(available_before_curly); |
185 | check_assist_not_applicable(move_module_to_file, r#"mod m { $0 }"#); | 191 | check_assist_not_applicable(move_module_to_file, r#"mod m { $0 }"#); |
186 | } | 192 | } |
193 | |||
194 | #[test] | ||
195 | fn keep_outer_comments_and_attributes() { | ||
196 | check_assist( | ||
197 | move_module_to_file, | ||
198 | r#" | ||
199 | /// doc comment | ||
200 | #[attribute] | ||
201 | mod $0tests { | ||
202 | #[test] fn t() {} | ||
203 | } | ||
204 | "#, | ||
205 | r#" | ||
206 | //- /main.rs | ||
207 | /// doc comment | ||
208 | #[attribute] | ||
209 | mod tests; | ||
210 | //- /tests.rs | ||
211 | #[test] fn t() {} | ||
212 | "#, | ||
213 | ); | ||
214 | } | ||
187 | } | 215 | } |
diff --git a/crates/ide_assists/src/handlers/pull_assignment_up.rs b/crates/ide_assists/src/handlers/pull_assignment_up.rs index 3128faa68..f07b8a6c0 100644 --- a/crates/ide_assists/src/handlers/pull_assignment_up.rs +++ b/crates/ide_assists/src/handlers/pull_assignment_up.rs | |||
@@ -74,10 +74,10 @@ pub(crate) fn pull_assignment_up(acc: &mut Assists, ctx: &AssistContext) -> Opti | |||
74 | let assignments: Vec<_> = collector | 74 | let assignments: Vec<_> = collector |
75 | .assignments | 75 | .assignments |
76 | .into_iter() | 76 | .into_iter() |
77 | .map(|(stmt, rhs)| (edit.make_ast_mut(stmt), rhs.clone_for_update())) | 77 | .map(|(stmt, rhs)| (edit.make_mut(stmt), rhs.clone_for_update())) |
78 | .collect(); | 78 | .collect(); |
79 | 79 | ||
80 | let tgt = edit.make_ast_mut(tgt); | 80 | let tgt = edit.make_mut(tgt); |
81 | 81 | ||
82 | for (stmt, rhs) in assignments { | 82 | for (stmt, rhs) in assignments { |
83 | let mut stmt = stmt.syntax().clone(); | 83 | let mut stmt = stmt.syntax().clone(); |
diff --git a/crates/ide_assists/src/handlers/raw_string.rs b/crates/ide_assists/src/handlers/raw_string.rs index d0f1613f3..d98a55ae4 100644 --- a/crates/ide_assists/src/handlers/raw_string.rs +++ b/crates/ide_assists/src/handlers/raw_string.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | use std::borrow::Cow; | 1 | use std::borrow::Cow; |
2 | 2 | ||
3 | use syntax::{ast, AstToken, TextRange, TextSize}; | 3 | use syntax::{ast, ast::IsString, AstToken, TextRange, TextSize}; |
4 | 4 | ||
5 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | 5 | use crate::{AssistContext, AssistId, AssistKind, Assists}; |
6 | 6 | ||
diff --git a/crates/ide_assists/src/handlers/reorder_fields.rs b/crates/ide_assists/src/handlers/reorder_fields.rs index e90bbdbcf..933acead1 100644 --- a/crates/ide_assists/src/handlers/reorder_fields.rs +++ b/crates/ide_assists/src/handlers/reorder_fields.rs | |||
@@ -70,10 +70,10 @@ pub(crate) fn reorder_fields(acc: &mut Assists, ctx: &AssistContext) -> Option<( | |||
70 | target, | 70 | target, |
71 | |builder| match fields { | 71 | |builder| match fields { |
72 | Either::Left((sorted, field_list)) => { | 72 | Either::Left((sorted, field_list)) => { |
73 | replace(builder.make_ast_mut(field_list).fields(), sorted) | 73 | replace(builder.make_mut(field_list).fields(), sorted) |
74 | } | 74 | } |
75 | Either::Right((sorted, field_list)) => { | 75 | Either::Right((sorted, field_list)) => { |
76 | replace(builder.make_ast_mut(field_list).fields(), sorted) | 76 | replace(builder.make_mut(field_list).fields(), sorted) |
77 | } | 77 | } |
78 | }, | 78 | }, |
79 | ) | 79 | ) |
diff --git a/crates/ide_assists/src/handlers/reorder_impl.rs b/crates/ide_assists/src/handlers/reorder_impl.rs index 54a9a468e..5a6a9f158 100644 --- a/crates/ide_assists/src/handlers/reorder_impl.rs +++ b/crates/ide_assists/src/handlers/reorder_impl.rs | |||
@@ -79,8 +79,7 @@ pub(crate) fn reorder_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
79 | "Sort methods", | 79 | "Sort methods", |
80 | target, | 80 | target, |
81 | |builder| { | 81 | |builder| { |
82 | let methods = | 82 | let methods = methods.into_iter().map(|fn_| builder.make_mut(fn_)).collect::<Vec<_>>(); |
83 | methods.into_iter().map(|fn_| builder.make_ast_mut(fn_)).collect::<Vec<_>>(); | ||
84 | methods | 83 | methods |
85 | .into_iter() | 84 | .into_iter() |
86 | .zip(sorted) | 85 | .zip(sorted) |
diff --git a/crates/ide_assists/src/handlers/replace_impl_trait_with_generic.rs b/crates/ide_assists/src/handlers/replace_impl_trait_with_generic.rs index 15420aedf..540a905cc 100644 --- a/crates/ide_assists/src/handlers/replace_impl_trait_with_generic.rs +++ b/crates/ide_assists/src/handlers/replace_impl_trait_with_generic.rs | |||
@@ -32,8 +32,8 @@ pub(crate) fn replace_impl_trait_with_generic( | |||
32 | "Replace impl trait with generic", | 32 | "Replace impl trait with generic", |
33 | target, | 33 | target, |
34 | |edit| { | 34 | |edit| { |
35 | let impl_trait_type = edit.make_ast_mut(impl_trait_type); | 35 | let impl_trait_type = edit.make_mut(impl_trait_type); |
36 | let fn_ = edit.make_ast_mut(fn_); | 36 | let fn_ = edit.make_mut(fn_); |
37 | 37 | ||
38 | let type_param_name = suggest_name::for_generic_parameter(&impl_trait_type); | 38 | let type_param_name = suggest_name::for_generic_parameter(&impl_trait_type); |
39 | 39 | ||
diff --git a/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs b/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs index 99ba79860..39f5eb4ff 100644 --- a/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs +++ b/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs | |||
@@ -40,7 +40,7 @@ pub(crate) fn replace_qualified_name_with_use( | |||
40 | |builder| { | 40 | |builder| { |
41 | // Now that we've brought the name into scope, re-qualify all paths that could be | 41 | // Now that we've brought the name into scope, re-qualify all paths that could be |
42 | // affected (that is, all paths inside the node we added the `use` to). | 42 | // affected (that is, all paths inside the node we added the `use` to). |
43 | let syntax = builder.make_mut(syntax.clone()); | 43 | let syntax = builder.make_syntax_mut(syntax.clone()); |
44 | if let Some(ref import_scope) = ImportScope::from(syntax.clone()) { | 44 | if let Some(ref import_scope) = ImportScope::from(syntax.clone()) { |
45 | shorten_paths(&syntax, &path.clone_for_update()); | 45 | shorten_paths(&syntax, &path.clone_for_update()); |
46 | insert_use(import_scope, path, ctx.config.insert_use); | 46 | insert_use(import_scope, path, ctx.config.insert_use); |
diff --git a/crates/ide_assists/src/handlers/replace_string_with_char.rs b/crates/ide_assists/src/handlers/replace_string_with_char.rs index 634b9c0b7..0800d291e 100644 --- a/crates/ide_assists/src/handlers/replace_string_with_char.rs +++ b/crates/ide_assists/src/handlers/replace_string_with_char.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | use syntax::{ast, AstToken, SyntaxKind::STRING}; | 1 | use syntax::{ast, ast::IsString, AstToken, SyntaxKind::STRING}; |
2 | 2 | ||
3 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | 3 | use crate::{AssistContext, AssistId, AssistKind, Assists}; |
4 | 4 | ||
diff --git a/crates/ide_assists/src/handlers/replace_unwrap_with_match.rs b/crates/ide_assists/src/handlers/replace_unwrap_with_match.rs index 0fec961b4..a3bfa221c 100644 --- a/crates/ide_assists/src/handlers/replace_unwrap_with_match.rs +++ b/crates/ide_assists/src/handlers/replace_unwrap_with_match.rs | |||
@@ -17,7 +17,7 @@ use ide_db::ty_filter::TryEnum; | |||
17 | 17 | ||
18 | // Assist: replace_unwrap_with_match | 18 | // Assist: replace_unwrap_with_match |
19 | // | 19 | // |
20 | // Replaces `unwrap` a `match` expression. Works for Result and Option. | 20 | // Replaces `unwrap` with a `match` expression. Works for Result and Option. |
21 | // | 21 | // |
22 | // ``` | 22 | // ``` |
23 | // enum Result<T, E> { Ok(T), Err(E) } | 23 | // enum Result<T, E> { Ok(T), Err(E) } |
diff --git a/crates/ide_assists/src/tests/generated.rs b/crates/ide_assists/src/tests/generated.rs index 49c1a9776..4406406a2 100644 --- a/crates/ide_assists/src/tests/generated.rs +++ b/crates/ide_assists/src/tests/generated.rs | |||
@@ -50,7 +50,6 @@ trait Trait { | |||
50 | impl Trait for () { | 50 | impl Trait for () { |
51 | type X = (); | 51 | type X = (); |
52 | fn foo(&self) {}$0 | 52 | fn foo(&self) {}$0 |
53 | |||
54 | } | 53 | } |
55 | "#####, | 54 | "#####, |
56 | r#####" | 55 | r#####" |
diff --git a/crates/ide_assists/src/utils.rs b/crates/ide_assists/src/utils.rs index 0dcf20c61..fc7caee04 100644 --- a/crates/ide_assists/src/utils.rs +++ b/crates/ide_assists/src/utils.rs | |||
@@ -17,7 +17,7 @@ use syntax::{ | |||
17 | ast::AttrsOwner, | 17 | ast::AttrsOwner, |
18 | ast::NameOwner, | 18 | ast::NameOwner, |
19 | ast::{self, edit, make, ArgListOwner, GenericParamsOwner}, | 19 | ast::{self, edit, make, ArgListOwner, GenericParamsOwner}, |
20 | AstNode, Direction, SmolStr, | 20 | ted, AstNode, Direction, SmolStr, |
21 | SyntaxKind::*, | 21 | SyntaxKind::*, |
22 | SyntaxNode, TextSize, T, | 22 | SyntaxNode, TextSize, T, |
23 | }; | 23 | }; |
@@ -128,43 +128,43 @@ pub fn add_trait_assoc_items_to_impl( | |||
128 | sema: &hir::Semantics<ide_db::RootDatabase>, | 128 | sema: &hir::Semantics<ide_db::RootDatabase>, |
129 | items: Vec<ast::AssocItem>, | 129 | items: Vec<ast::AssocItem>, |
130 | trait_: hir::Trait, | 130 | trait_: hir::Trait, |
131 | impl_def: ast::Impl, | 131 | impl_: ast::Impl, |
132 | target_scope: hir::SemanticsScope, | 132 | target_scope: hir::SemanticsScope, |
133 | ) -> (ast::Impl, ast::AssocItem) { | 133 | ) -> (ast::Impl, ast::AssocItem) { |
134 | let impl_item_list = impl_def.assoc_item_list().unwrap_or_else(make::assoc_item_list); | ||
135 | |||
136 | let n_existing_items = impl_item_list.assoc_items().count(); | ||
137 | let source_scope = sema.scope_for_def(trait_); | 134 | let source_scope = sema.scope_for_def(trait_); |
138 | let ast_transform = QualifyPaths::new(&target_scope, &source_scope) | 135 | let ast_transform = QualifyPaths::new(&target_scope, &source_scope) |
139 | .or(SubstituteTypeParams::for_trait_impl(&source_scope, trait_, impl_def.clone())); | 136 | .or(SubstituteTypeParams::for_trait_impl(&source_scope, trait_, impl_.clone())); |
140 | 137 | ||
141 | let items = items | 138 | let items = items |
142 | .into_iter() | 139 | .into_iter() |
143 | .map(|it| it.clone_for_update()) | 140 | .map(|it| it.clone_for_update()) |
144 | .inspect(|it| ast_transform::apply(&*ast_transform, it)) | 141 | .inspect(|it| ast_transform::apply(&*ast_transform, it)) |
145 | .map(|it| match it { | 142 | .map(|it| edit::remove_attrs_and_docs(&it).clone_subtree().clone_for_update()); |
146 | ast::AssocItem::Fn(def) => ast::AssocItem::Fn(add_body(def)), | 143 | |
147 | ast::AssocItem::TypeAlias(def) => ast::AssocItem::TypeAlias(def.remove_bounds()), | 144 | let res = impl_.clone_for_update(); |
148 | _ => it, | 145 | |
149 | }) | 146 | let assoc_item_list = res.get_or_create_assoc_item_list(); |
150 | .map(|it| edit::remove_attrs_and_docs(&it)); | 147 | let mut first_item = None; |
151 | 148 | for item in items { | |
152 | let new_impl_item_list = impl_item_list.append_items(items); | 149 | first_item.get_or_insert_with(|| item.clone()); |
153 | let new_impl_def = impl_def.with_assoc_item_list(new_impl_item_list); | 150 | match &item { |
154 | let first_new_item = | 151 | ast::AssocItem::Fn(fn_) if fn_.body().is_none() => { |
155 | new_impl_def.assoc_item_list().unwrap().assoc_items().nth(n_existing_items).unwrap(); | ||
156 | return (new_impl_def, first_new_item); | ||
157 | |||
158 | fn add_body(fn_def: ast::Fn) -> ast::Fn { | ||
159 | match fn_def.body() { | ||
160 | Some(_) => fn_def, | ||
161 | None => { | ||
162 | let body = make::block_expr(None, Some(make::ext::expr_todo())) | 152 | let body = make::block_expr(None, Some(make::ext::expr_todo())) |
163 | .indent(edit::IndentLevel(1)); | 153 | .indent(edit::IndentLevel(1)); |
164 | fn_def.with_body(body) | 154 | ted::replace(fn_.get_or_create_body().syntax(), body.clone_for_update().syntax()) |
165 | } | 155 | } |
156 | ast::AssocItem::TypeAlias(type_alias) => { | ||
157 | if let Some(type_bound_list) = type_alias.type_bound_list() { | ||
158 | type_bound_list.remove() | ||
159 | } | ||
160 | } | ||
161 | _ => {} | ||
166 | } | 162 | } |
163 | |||
164 | assoc_item_list.add_item(item) | ||
167 | } | 165 | } |
166 | |||
167 | (res, first_item.unwrap()) | ||
168 | } | 168 | } |
169 | 169 | ||
170 | #[derive(Clone, Copy, Debug)] | 170 | #[derive(Clone, Copy, Debug)] |