diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2021-02-17 14:54:55 +0000 |
---|---|---|
committer | GitHub <[email protected]> | 2021-02-17 14:54:55 +0000 |
commit | 20a911f3cc2beb0409ab71cc1560648374745f7f (patch) | |
tree | 5386aab9c452981be09bc3e4362643a34e6e3617 /crates/ide_completion/src/completions/trait_impl.rs | |
parent | 6334ce866ab095215381c4b72692b20a84d26e96 (diff) | |
parent | 3db64a400c78bbd2708e67ddc07df1001fff3f29 (diff) |
Merge #7707
7707: rename completion -> ide_completion r=matklad a=matklad
bors r+
🤖
Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'crates/ide_completion/src/completions/trait_impl.rs')
-rw-r--r-- | crates/ide_completion/src/completions/trait_impl.rs | 736 |
1 files changed, 736 insertions, 0 deletions
diff --git a/crates/ide_completion/src/completions/trait_impl.rs b/crates/ide_completion/src/completions/trait_impl.rs new file mode 100644 index 000000000..b999540b8 --- /dev/null +++ b/crates/ide_completion/src/completions/trait_impl.rs | |||
@@ -0,0 +1,736 @@ | |||
1 | //! Completion for associated items in a trait implementation. | ||
2 | //! | ||
3 | //! This module adds the completion items related to implementing associated | ||
4 | //! items within a `impl Trait for Struct` block. The current context node | ||
5 | //! must be within either a `FN`, `TYPE_ALIAS`, or `CONST` node | ||
6 | //! and an direct child of an `IMPL`. | ||
7 | //! | ||
8 | //! # Examples | ||
9 | //! | ||
10 | //! Considering the following trait `impl`: | ||
11 | //! | ||
12 | //! ```ignore | ||
13 | //! trait SomeTrait { | ||
14 | //! fn foo(); | ||
15 | //! } | ||
16 | //! | ||
17 | //! impl SomeTrait for () { | ||
18 | //! fn f$0 | ||
19 | //! } | ||
20 | //! ``` | ||
21 | //! | ||
22 | //! may result in the completion of the following method: | ||
23 | //! | ||
24 | //! ```ignore | ||
25 | //! # trait SomeTrait { | ||
26 | //! # fn foo(); | ||
27 | //! # } | ||
28 | //! | ||
29 | //! impl SomeTrait for () { | ||
30 | //! fn foo() {}$0 | ||
31 | //! } | ||
32 | //! ``` | ||
33 | |||
34 | use hir::{self, HasAttrs, HasSource}; | ||
35 | use ide_db::{traits::get_missing_assoc_items, SymbolKind}; | ||
36 | use syntax::{ | ||
37 | ast::{self, edit, Impl}, | ||
38 | display::function_declaration, | ||
39 | AstNode, SyntaxKind, SyntaxNode, TextRange, T, | ||
40 | }; | ||
41 | use text_edit::TextEdit; | ||
42 | |||
43 | use crate::{ | ||
44 | CompletionContext, | ||
45 | CompletionItem, | ||
46 | CompletionItemKind, | ||
47 | CompletionKind, | ||
48 | Completions, | ||
49 | // display::function_declaration, | ||
50 | }; | ||
51 | |||
52 | #[derive(Debug, PartialEq, Eq)] | ||
53 | enum ImplCompletionKind { | ||
54 | All, | ||
55 | Fn, | ||
56 | TypeAlias, | ||
57 | Const, | ||
58 | } | ||
59 | |||
60 | pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) { | ||
61 | if let Some((kind, trigger, impl_def)) = completion_match(ctx) { | ||
62 | get_missing_assoc_items(&ctx.sema, &impl_def).into_iter().for_each(|item| match item { | ||
63 | hir::AssocItem::Function(fn_item) | ||
64 | if kind == ImplCompletionKind::All || kind == ImplCompletionKind::Fn => | ||
65 | { | ||
66 | add_function_impl(&trigger, acc, ctx, fn_item) | ||
67 | } | ||
68 | hir::AssocItem::TypeAlias(type_item) | ||
69 | if kind == ImplCompletionKind::All || kind == ImplCompletionKind::TypeAlias => | ||
70 | { | ||
71 | add_type_alias_impl(&trigger, acc, ctx, type_item) | ||
72 | } | ||
73 | hir::AssocItem::Const(const_item) | ||
74 | if kind == ImplCompletionKind::All || kind == ImplCompletionKind::Const => | ||
75 | { | ||
76 | add_const_impl(&trigger, acc, ctx, const_item) | ||
77 | } | ||
78 | _ => {} | ||
79 | }); | ||
80 | } | ||
81 | } | ||
82 | |||
83 | fn completion_match(ctx: &CompletionContext) -> Option<(ImplCompletionKind, SyntaxNode, Impl)> { | ||
84 | let mut token = ctx.token.clone(); | ||
85 | // For keywork without name like `impl .. { fn $0 }`, the current position is inside | ||
86 | // the whitespace token, which is outside `FN` syntax node. | ||
87 | // We need to follow the previous token in this case. | ||
88 | if token.kind() == SyntaxKind::WHITESPACE { | ||
89 | token = token.prev_token()?; | ||
90 | } | ||
91 | |||
92 | let impl_item_offset = match token.kind() { | ||
93 | // `impl .. { const $0 }` | ||
94 | // ERROR 0 | ||
95 | // CONST_KW <- * | ||
96 | T![const] => 0, | ||
97 | // `impl .. { fn/type $0 }` | ||
98 | // FN/TYPE_ALIAS 0 | ||
99 | // FN_KW <- * | ||
100 | T![fn] | T![type] => 0, | ||
101 | // `impl .. { fn/type/const foo$0 }` | ||
102 | // FN/TYPE_ALIAS/CONST 1 | ||
103 | // NAME 0 | ||
104 | // IDENT <- * | ||
105 | SyntaxKind::IDENT if token.parent().kind() == SyntaxKind::NAME => 1, | ||
106 | // `impl .. { foo$0 }` | ||
107 | // MACRO_CALL 3 | ||
108 | // PATH 2 | ||
109 | // PATH_SEGMENT 1 | ||
110 | // NAME_REF 0 | ||
111 | // IDENT <- * | ||
112 | SyntaxKind::IDENT if token.parent().kind() == SyntaxKind::NAME_REF => 3, | ||
113 | _ => return None, | ||
114 | }; | ||
115 | |||
116 | let impl_item = token.ancestors().nth(impl_item_offset)?; | ||
117 | // Must directly belong to an impl block. | ||
118 | // IMPL | ||
119 | // ASSOC_ITEM_LIST | ||
120 | // <item> | ||
121 | let impl_def = ast::Impl::cast(impl_item.parent()?.parent()?)?; | ||
122 | let kind = match impl_item.kind() { | ||
123 | // `impl ... { const $0 fn/type/const }` | ||
124 | _ if token.kind() == T![const] => ImplCompletionKind::Const, | ||
125 | SyntaxKind::CONST | SyntaxKind::ERROR => ImplCompletionKind::Const, | ||
126 | SyntaxKind::TYPE_ALIAS => ImplCompletionKind::TypeAlias, | ||
127 | SyntaxKind::FN => ImplCompletionKind::Fn, | ||
128 | SyntaxKind::MACRO_CALL => ImplCompletionKind::All, | ||
129 | _ => return None, | ||
130 | }; | ||
131 | Some((kind, impl_item, impl_def)) | ||
132 | } | ||
133 | |||
134 | fn add_function_impl( | ||
135 | fn_def_node: &SyntaxNode, | ||
136 | acc: &mut Completions, | ||
137 | ctx: &CompletionContext, | ||
138 | func: hir::Function, | ||
139 | ) { | ||
140 | let fn_name = func.name(ctx.db).to_string(); | ||
141 | |||
142 | let label = if func.assoc_fn_params(ctx.db).is_empty() { | ||
143 | format!("fn {}()", fn_name) | ||
144 | } else { | ||
145 | format!("fn {}(..)", fn_name) | ||
146 | }; | ||
147 | |||
148 | let builder = CompletionItem::new(CompletionKind::Magic, ctx.source_range(), label) | ||
149 | .lookup_by(fn_name) | ||
150 | .set_documentation(func.docs(ctx.db)); | ||
151 | |||
152 | let completion_kind = if func.self_param(ctx.db).is_some() { | ||
153 | CompletionItemKind::Method | ||
154 | } else { | ||
155 | CompletionItemKind::SymbolKind(SymbolKind::Function) | ||
156 | }; | ||
157 | let range = TextRange::new(fn_def_node.text_range().start(), ctx.source_range().end()); | ||
158 | |||
159 | if let Some(src) = func.source(ctx.db) { | ||
160 | let function_decl = function_declaration(&src.value); | ||
161 | match ctx.config.snippet_cap { | ||
162 | Some(cap) => { | ||
163 | let snippet = format!("{} {{\n $0\n}}", function_decl); | ||
164 | builder.snippet_edit(cap, TextEdit::replace(range, snippet)) | ||
165 | } | ||
166 | None => { | ||
167 | let header = format!("{} {{", function_decl); | ||
168 | builder.text_edit(TextEdit::replace(range, header)) | ||
169 | } | ||
170 | } | ||
171 | .kind(completion_kind) | ||
172 | .add_to(acc); | ||
173 | } | ||
174 | } | ||
175 | |||
176 | fn add_type_alias_impl( | ||
177 | type_def_node: &SyntaxNode, | ||
178 | acc: &mut Completions, | ||
179 | ctx: &CompletionContext, | ||
180 | type_alias: hir::TypeAlias, | ||
181 | ) { | ||
182 | let alias_name = type_alias.name(ctx.db).to_string(); | ||
183 | |||
184 | let snippet = format!("type {} = ", alias_name); | ||
185 | |||
186 | let range = TextRange::new(type_def_node.text_range().start(), ctx.source_range().end()); | ||
187 | |||
188 | CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone()) | ||
189 | .text_edit(TextEdit::replace(range, snippet)) | ||
190 | .lookup_by(alias_name) | ||
191 | .kind(SymbolKind::TypeAlias) | ||
192 | .set_documentation(type_alias.docs(ctx.db)) | ||
193 | .add_to(acc); | ||
194 | } | ||
195 | |||
196 | fn add_const_impl( | ||
197 | const_def_node: &SyntaxNode, | ||
198 | acc: &mut Completions, | ||
199 | ctx: &CompletionContext, | ||
200 | const_: hir::Const, | ||
201 | ) { | ||
202 | let const_name = const_.name(ctx.db).map(|n| n.to_string()); | ||
203 | |||
204 | if let Some(const_name) = const_name { | ||
205 | if let Some(source) = const_.source(ctx.db) { | ||
206 | let snippet = make_const_compl_syntax(&source.value); | ||
207 | |||
208 | let range = | ||
209 | TextRange::new(const_def_node.text_range().start(), ctx.source_range().end()); | ||
210 | |||
211 | CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone()) | ||
212 | .text_edit(TextEdit::replace(range, snippet)) | ||
213 | .lookup_by(const_name) | ||
214 | .kind(SymbolKind::Const) | ||
215 | .set_documentation(const_.docs(ctx.db)) | ||
216 | .add_to(acc); | ||
217 | } | ||
218 | } | ||
219 | } | ||
220 | |||
221 | fn make_const_compl_syntax(const_: &ast::Const) -> String { | ||
222 | let const_ = edit::remove_attrs_and_docs(const_); | ||
223 | |||
224 | let const_start = const_.syntax().text_range().start(); | ||
225 | let const_end = const_.syntax().text_range().end(); | ||
226 | |||
227 | let start = | ||
228 | const_.syntax().first_child_or_token().map_or(const_start, |f| f.text_range().start()); | ||
229 | |||
230 | let end = const_ | ||
231 | .syntax() | ||
232 | .children_with_tokens() | ||
233 | .find(|s| s.kind() == T![;] || s.kind() == T![=]) | ||
234 | .map_or(const_end, |f| f.text_range().start()); | ||
235 | |||
236 | let len = end - start; | ||
237 | let range = TextRange::new(0.into(), len); | ||
238 | |||
239 | let syntax = const_.syntax().text().slice(range).to_string(); | ||
240 | |||
241 | format!("{} = ", syntax.trim_end()) | ||
242 | } | ||
243 | |||
244 | #[cfg(test)] | ||
245 | mod tests { | ||
246 | use expect_test::{expect, Expect}; | ||
247 | |||
248 | use crate::{ | ||
249 | test_utils::{check_edit, completion_list}, | ||
250 | CompletionKind, | ||
251 | }; | ||
252 | |||
253 | fn check(ra_fixture: &str, expect: Expect) { | ||
254 | let actual = completion_list(ra_fixture, CompletionKind::Magic); | ||
255 | expect.assert_eq(&actual) | ||
256 | } | ||
257 | |||
258 | #[test] | ||
259 | fn name_ref_function_type_const() { | ||
260 | check( | ||
261 | r#" | ||
262 | trait Test { | ||
263 | type TestType; | ||
264 | const TEST_CONST: u16; | ||
265 | fn test(); | ||
266 | } | ||
267 | struct T; | ||
268 | |||
269 | impl Test for T { | ||
270 | t$0 | ||
271 | } | ||
272 | "#, | ||
273 | expect![[" | ||
274 | ta type TestType = \n\ | ||
275 | ct const TEST_CONST: u16 = \n\ | ||
276 | fn fn test() | ||
277 | "]], | ||
278 | ); | ||
279 | } | ||
280 | |||
281 | #[test] | ||
282 | fn no_completion_inside_fn() { | ||
283 | check( | ||
284 | r" | ||
285 | trait Test { fn test(); fn test2(); } | ||
286 | struct T; | ||
287 | |||
288 | impl Test for T { | ||
289 | fn test() { | ||
290 | t$0 | ||
291 | } | ||
292 | } | ||
293 | ", | ||
294 | expect![[""]], | ||
295 | ); | ||
296 | |||
297 | check( | ||
298 | r" | ||
299 | trait Test { fn test(); fn test2(); } | ||
300 | struct T; | ||
301 | |||
302 | impl Test for T { | ||
303 | fn test() { | ||
304 | fn t$0 | ||
305 | } | ||
306 | } | ||
307 | ", | ||
308 | expect![[""]], | ||
309 | ); | ||
310 | |||
311 | check( | ||
312 | r" | ||
313 | trait Test { fn test(); fn test2(); } | ||
314 | struct T; | ||
315 | |||
316 | impl Test for T { | ||
317 | fn test() { | ||
318 | fn $0 | ||
319 | } | ||
320 | } | ||
321 | ", | ||
322 | expect![[""]], | ||
323 | ); | ||
324 | |||
325 | // https://github.com/rust-analyzer/rust-analyzer/pull/5976#issuecomment-692332191 | ||
326 | check( | ||
327 | r" | ||
328 | trait Test { fn test(); fn test2(); } | ||
329 | struct T; | ||
330 | |||
331 | impl Test for T { | ||
332 | fn test() { | ||
333 | foo.$0 | ||
334 | } | ||
335 | } | ||
336 | ", | ||
337 | expect![[""]], | ||
338 | ); | ||
339 | |||
340 | check( | ||
341 | r" | ||
342 | trait Test { fn test(_: i32); fn test2(); } | ||
343 | struct T; | ||
344 | |||
345 | impl Test for T { | ||
346 | fn test(t$0) | ||
347 | } | ||
348 | ", | ||
349 | expect![[""]], | ||
350 | ); | ||
351 | |||
352 | check( | ||
353 | r" | ||
354 | trait Test { fn test(_: fn()); fn test2(); } | ||
355 | struct T; | ||
356 | |||
357 | impl Test for T { | ||
358 | fn test(f: fn $0) | ||
359 | } | ||
360 | ", | ||
361 | expect![[""]], | ||
362 | ); | ||
363 | } | ||
364 | |||
365 | #[test] | ||
366 | fn no_completion_inside_const() { | ||
367 | check( | ||
368 | r" | ||
369 | trait Test { const TEST: fn(); const TEST2: u32; type Test; fn test(); } | ||
370 | struct T; | ||
371 | |||
372 | impl Test for T { | ||
373 | const TEST: fn $0 | ||
374 | } | ||
375 | ", | ||
376 | expect![[""]], | ||
377 | ); | ||
378 | |||
379 | check( | ||
380 | r" | ||
381 | trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); } | ||
382 | struct T; | ||
383 | |||
384 | impl Test for T { | ||
385 | const TEST: T$0 | ||
386 | } | ||
387 | ", | ||
388 | expect![[""]], | ||
389 | ); | ||
390 | |||
391 | check( | ||
392 | r" | ||
393 | trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); } | ||
394 | struct T; | ||
395 | |||
396 | impl Test for T { | ||
397 | const TEST: u32 = f$0 | ||
398 | } | ||
399 | ", | ||
400 | expect![[""]], | ||
401 | ); | ||
402 | |||
403 | check( | ||
404 | r" | ||
405 | trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); } | ||
406 | struct T; | ||
407 | |||
408 | impl Test for T { | ||
409 | const TEST: u32 = { | ||
410 | t$0 | ||
411 | }; | ||
412 | } | ||
413 | ", | ||
414 | expect![[""]], | ||
415 | ); | ||
416 | |||
417 | check( | ||
418 | r" | ||
419 | trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); } | ||
420 | struct T; | ||
421 | |||
422 | impl Test for T { | ||
423 | const TEST: u32 = { | ||
424 | fn $0 | ||
425 | }; | ||
426 | } | ||
427 | ", | ||
428 | expect![[""]], | ||
429 | ); | ||
430 | |||
431 | check( | ||
432 | r" | ||
433 | trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); } | ||
434 | struct T; | ||
435 | |||
436 | impl Test for T { | ||
437 | const TEST: u32 = { | ||
438 | fn t$0 | ||
439 | }; | ||
440 | } | ||
441 | ", | ||
442 | expect![[""]], | ||
443 | ); | ||
444 | } | ||
445 | |||
446 | #[test] | ||
447 | fn no_completion_inside_type() { | ||
448 | check( | ||
449 | r" | ||
450 | trait Test { type Test; type Test2; fn test(); } | ||
451 | struct T; | ||
452 | |||
453 | impl Test for T { | ||
454 | type Test = T$0; | ||
455 | } | ||
456 | ", | ||
457 | expect![[""]], | ||
458 | ); | ||
459 | |||
460 | check( | ||
461 | r" | ||
462 | trait Test { type Test; type Test2; fn test(); } | ||
463 | struct T; | ||
464 | |||
465 | impl Test for T { | ||
466 | type Test = fn $0; | ||
467 | } | ||
468 | ", | ||
469 | expect![[""]], | ||
470 | ); | ||
471 | } | ||
472 | |||
473 | #[test] | ||
474 | fn name_ref_single_function() { | ||
475 | check_edit( | ||
476 | "test", | ||
477 | r#" | ||
478 | trait Test { | ||
479 | fn test(); | ||
480 | } | ||
481 | struct T; | ||
482 | |||
483 | impl Test for T { | ||
484 | t$0 | ||
485 | } | ||
486 | "#, | ||
487 | r#" | ||
488 | trait Test { | ||
489 | fn test(); | ||
490 | } | ||
491 | struct T; | ||
492 | |||
493 | impl Test for T { | ||
494 | fn test() { | ||
495 | $0 | ||
496 | } | ||
497 | } | ||
498 | "#, | ||
499 | ); | ||
500 | } | ||
501 | |||
502 | #[test] | ||
503 | fn single_function() { | ||
504 | check_edit( | ||
505 | "test", | ||
506 | r#" | ||
507 | trait Test { | ||
508 | fn test(); | ||
509 | } | ||
510 | struct T; | ||
511 | |||
512 | impl Test for T { | ||
513 | fn t$0 | ||
514 | } | ||
515 | "#, | ||
516 | r#" | ||
517 | trait Test { | ||
518 | fn test(); | ||
519 | } | ||
520 | struct T; | ||
521 | |||
522 | impl Test for T { | ||
523 | fn test() { | ||
524 | $0 | ||
525 | } | ||
526 | } | ||
527 | "#, | ||
528 | ); | ||
529 | } | ||
530 | |||
531 | #[test] | ||
532 | fn hide_implemented_fn() { | ||
533 | check( | ||
534 | r#" | ||
535 | trait Test { | ||
536 | fn foo(); | ||
537 | fn foo_bar(); | ||
538 | } | ||
539 | struct T; | ||
540 | |||
541 | impl Test for T { | ||
542 | fn foo() {} | ||
543 | fn f$0 | ||
544 | } | ||
545 | "#, | ||
546 | expect![[r#" | ||
547 | fn fn foo_bar() | ||
548 | "#]], | ||
549 | ); | ||
550 | } | ||
551 | |||
552 | #[test] | ||
553 | fn generic_fn() { | ||
554 | check_edit( | ||
555 | "foo", | ||
556 | r#" | ||
557 | trait Test { | ||
558 | fn foo<T>(); | ||
559 | } | ||
560 | struct T; | ||
561 | |||
562 | impl Test for T { | ||
563 | fn f$0 | ||
564 | } | ||
565 | "#, | ||
566 | r#" | ||
567 | trait Test { | ||
568 | fn foo<T>(); | ||
569 | } | ||
570 | struct T; | ||
571 | |||
572 | impl Test for T { | ||
573 | fn foo<T>() { | ||
574 | $0 | ||
575 | } | ||
576 | } | ||
577 | "#, | ||
578 | ); | ||
579 | check_edit( | ||
580 | "foo", | ||
581 | r#" | ||
582 | trait Test { | ||
583 | fn foo<T>() where T: Into<String>; | ||
584 | } | ||
585 | struct T; | ||
586 | |||
587 | impl Test for T { | ||
588 | fn f$0 | ||
589 | } | ||
590 | "#, | ||
591 | r#" | ||
592 | trait Test { | ||
593 | fn foo<T>() where T: Into<String>; | ||
594 | } | ||
595 | struct T; | ||
596 | |||
597 | impl Test for T { | ||
598 | fn foo<T>() | ||
599 | where T: Into<String> { | ||
600 | $0 | ||
601 | } | ||
602 | } | ||
603 | "#, | ||
604 | ); | ||
605 | } | ||
606 | |||
607 | #[test] | ||
608 | fn associated_type() { | ||
609 | check_edit( | ||
610 | "SomeType", | ||
611 | r#" | ||
612 | trait Test { | ||
613 | type SomeType; | ||
614 | } | ||
615 | |||
616 | impl Test for () { | ||
617 | type S$0 | ||
618 | } | ||
619 | "#, | ||
620 | " | ||
621 | trait Test { | ||
622 | type SomeType; | ||
623 | } | ||
624 | |||
625 | impl Test for () { | ||
626 | type SomeType = \n\ | ||
627 | } | ||
628 | ", | ||
629 | ); | ||
630 | } | ||
631 | |||
632 | #[test] | ||
633 | fn associated_const() { | ||
634 | check_edit( | ||
635 | "SOME_CONST", | ||
636 | r#" | ||
637 | trait Test { | ||
638 | const SOME_CONST: u16; | ||
639 | } | ||
640 | |||
641 | impl Test for () { | ||
642 | const S$0 | ||
643 | } | ||
644 | "#, | ||
645 | " | ||
646 | trait Test { | ||
647 | const SOME_CONST: u16; | ||
648 | } | ||
649 | |||
650 | impl Test for () { | ||
651 | const SOME_CONST: u16 = \n\ | ||
652 | } | ||
653 | ", | ||
654 | ); | ||
655 | |||
656 | check_edit( | ||
657 | "SOME_CONST", | ||
658 | r#" | ||
659 | trait Test { | ||
660 | const SOME_CONST: u16 = 92; | ||
661 | } | ||
662 | |||
663 | impl Test for () { | ||
664 | const S$0 | ||
665 | } | ||
666 | "#, | ||
667 | " | ||
668 | trait Test { | ||
669 | const SOME_CONST: u16 = 92; | ||
670 | } | ||
671 | |||
672 | impl Test for () { | ||
673 | const SOME_CONST: u16 = \n\ | ||
674 | } | ||
675 | ", | ||
676 | ); | ||
677 | } | ||
678 | |||
679 | #[test] | ||
680 | fn complete_without_name() { | ||
681 | let test = |completion: &str, hint: &str, completed: &str, next_sibling: &str| { | ||
682 | check_edit( | ||
683 | completion, | ||
684 | &format!( | ||
685 | r#" | ||
686 | trait Test {{ | ||
687 | type Foo; | ||
688 | const CONST: u16; | ||
689 | fn bar(); | ||
690 | }} | ||
691 | struct T; | ||
692 | |||
693 | impl Test for T {{ | ||
694 | {} | ||
695 | {} | ||
696 | }} | ||
697 | "#, | ||
698 | hint, next_sibling | ||
699 | ), | ||
700 | &format!( | ||
701 | r#" | ||
702 | trait Test {{ | ||
703 | type Foo; | ||
704 | const CONST: u16; | ||
705 | fn bar(); | ||
706 | }} | ||
707 | struct T; | ||
708 | |||
709 | impl Test for T {{ | ||
710 | {} | ||
711 | {} | ||
712 | }} | ||
713 | "#, | ||
714 | completed, next_sibling | ||
715 | ), | ||
716 | ) | ||
717 | }; | ||
718 | |||
719 | // Enumerate some possible next siblings. | ||
720 | for next_sibling in &[ | ||
721 | "", | ||
722 | "fn other_fn() {}", // `const $0 fn` -> `const fn` | ||
723 | "type OtherType = i32;", | ||
724 | "const OTHER_CONST: i32 = 0;", | ||
725 | "async fn other_fn() {}", | ||
726 | "unsafe fn other_fn() {}", | ||
727 | "default fn other_fn() {}", | ||
728 | "default type OtherType = i32;", | ||
729 | "default const OTHER_CONST: i32 = 0;", | ||
730 | ] { | ||
731 | test("bar", "fn $0", "fn bar() {\n $0\n}", next_sibling); | ||
732 | test("Foo", "type $0", "type Foo = ", next_sibling); | ||
733 | test("CONST", "const $0", "const CONST: u16 = ", next_sibling); | ||
734 | } | ||
735 | } | ||
736 | } | ||