]> git.lizzy.rs Git - rust.git/blob - crates/ide_completion/src/completions/trait_impl.rs
Merge #8398
[rust.git] / crates / ide_completion / src / completions / trait_impl.rs
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, SyntaxElement, 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 keyword 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 parent_kind = token.parent().map_or(SyntaxKind::EOF, |it| it.kind());
93     let impl_item_offset = match token.kind() {
94         // `impl .. { const $0 }`
95         // ERROR      0
96         //   CONST_KW <- *
97         T![const] => 0,
98         // `impl .. { fn/type $0 }`
99         // FN/TYPE_ALIAS  0
100         //   FN_KW        <- *
101         T![fn] | T![type] => 0,
102         // `impl .. { fn/type/const foo$0 }`
103         // FN/TYPE_ALIAS/CONST  1
104         //  NAME                0
105         //    IDENT             <- *
106         SyntaxKind::IDENT if parent_kind == SyntaxKind::NAME => 1,
107         // `impl .. { foo$0 }`
108         // MACRO_CALL       3
109         //  PATH            2
110         //    PATH_SEGMENT  1
111         //      NAME_REF    0
112         //        IDENT     <- *
113         SyntaxKind::IDENT if parent_kind == SyntaxKind::NAME_REF => 3,
114         _ => return None,
115     };
116
117     let impl_item = token.ancestors().nth(impl_item_offset)?;
118     // Must directly belong to an impl block.
119     // IMPL
120     //   ASSOC_ITEM_LIST
121     //     <item>
122     let impl_def = ast::Impl::cast(impl_item.parent()?.parent()?)?;
123     let kind = match impl_item.kind() {
124         // `impl ... { const $0 fn/type/const }`
125         _ if token.kind() == T![const] => ImplCompletionKind::Const,
126         SyntaxKind::CONST | SyntaxKind::ERROR => ImplCompletionKind::Const,
127         SyntaxKind::TYPE_ALIAS => ImplCompletionKind::TypeAlias,
128         SyntaxKind::FN => ImplCompletionKind::Fn,
129         SyntaxKind::MACRO_CALL => ImplCompletionKind::All,
130         _ => return None,
131     };
132     Some((kind, impl_item, impl_def))
133 }
134
135 fn add_function_impl(
136     fn_def_node: &SyntaxNode,
137     acc: &mut Completions,
138     ctx: &CompletionContext,
139     func: hir::Function,
140 ) {
141     let fn_name = func.name(ctx.db).to_string();
142
143     let label = if func.assoc_fn_params(ctx.db).is_empty() {
144         format!("fn {}()", fn_name)
145     } else {
146         format!("fn {}(..)", fn_name)
147     };
148
149     let mut item = CompletionItem::new(CompletionKind::Magic, ctx.source_range(), label);
150     item.lookup_by(fn_name).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 = replacement_range(ctx, fn_def_node);
158     if let Some(src) = func.source(ctx.db) {
159         let function_decl = function_declaration(&src.value);
160         match ctx.config.snippet_cap {
161             Some(cap) => {
162                 let snippet = format!("{} {{\n    $0\n}}", function_decl);
163                 item.snippet_edit(cap, TextEdit::replace(range, snippet));
164             }
165             None => {
166                 let header = format!("{} {{", function_decl);
167                 item.text_edit(TextEdit::replace(range, header));
168             }
169         };
170         item.kind(completion_kind);
171         item.add_to(acc);
172     }
173 }
174
175 fn add_type_alias_impl(
176     type_def_node: &SyntaxNode,
177     acc: &mut Completions,
178     ctx: &CompletionContext,
179     type_alias: hir::TypeAlias,
180 ) {
181     let alias_name = type_alias.name(ctx.db).to_string();
182
183     let snippet = format!("type {} = ", alias_name);
184
185     let range = replacement_range(ctx, type_def_node);
186     let mut item = CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone());
187     item.text_edit(TextEdit::replace(range, snippet))
188         .lookup_by(alias_name)
189         .kind(SymbolKind::TypeAlias)
190         .set_documentation(type_alias.docs(ctx.db));
191     item.add_to(acc);
192 }
193
194 fn add_const_impl(
195     const_def_node: &SyntaxNode,
196     acc: &mut Completions,
197     ctx: &CompletionContext,
198     const_: hir::Const,
199 ) {
200     let const_name = const_.name(ctx.db).map(|n| n.to_string());
201
202     if let Some(const_name) = const_name {
203         if let Some(source) = const_.source(ctx.db) {
204             let snippet = make_const_compl_syntax(&source.value);
205
206             let range = replacement_range(ctx, const_def_node);
207             let mut item =
208                 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone());
209             item.text_edit(TextEdit::replace(range, snippet))
210                 .lookup_by(const_name)
211                 .kind(SymbolKind::Const)
212                 .set_documentation(const_.docs(ctx.db));
213             item.add_to(acc);
214         }
215     }
216 }
217
218 fn make_const_compl_syntax(const_: &ast::Const) -> String {
219     let const_ = edit::remove_attrs_and_docs(const_);
220
221     let const_start = const_.syntax().text_range().start();
222     let const_end = const_.syntax().text_range().end();
223
224     let start =
225         const_.syntax().first_child_or_token().map_or(const_start, |f| f.text_range().start());
226
227     let end = const_
228         .syntax()
229         .children_with_tokens()
230         .find(|s| s.kind() == T![;] || s.kind() == T![=])
231         .map_or(const_end, |f| f.text_range().start());
232
233     let len = end - start;
234     let range = TextRange::new(0.into(), len);
235
236     let syntax = const_.syntax().text().slice(range).to_string();
237
238     format!("{} = ", syntax.trim_end())
239 }
240
241 fn replacement_range(ctx: &CompletionContext, item: &SyntaxNode) -> TextRange {
242     let first_child = item
243         .children_with_tokens()
244         .find(|child| {
245             !matches!(child.kind(), SyntaxKind::COMMENT | SyntaxKind::WHITESPACE | SyntaxKind::ATTR)
246         })
247         .unwrap_or_else(|| SyntaxElement::Node(item.clone()));
248
249     TextRange::new(first_child.text_range().start(), ctx.source_range().end())
250 }
251
252 #[cfg(test)]
253 mod tests {
254     use expect_test::{expect, Expect};
255
256     use crate::{
257         test_utils::{check_edit, completion_list},
258         CompletionKind,
259     };
260
261     fn check(ra_fixture: &str, expect: Expect) {
262         let actual = completion_list(ra_fixture, CompletionKind::Magic);
263         expect.assert_eq(&actual)
264     }
265
266     #[test]
267     fn name_ref_function_type_const() {
268         check(
269             r#"
270 trait Test {
271     type TestType;
272     const TEST_CONST: u16;
273     fn test();
274 }
275 struct T;
276
277 impl Test for T {
278     t$0
279 }
280 "#,
281             expect![["
282 ta type TestType = \n\
283 ct const TEST_CONST: u16 = \n\
284 fn fn test()
285 "]],
286         );
287     }
288
289     #[test]
290     fn no_completion_inside_fn() {
291         check(
292             r"
293 trait Test { fn test(); fn test2(); }
294 struct T;
295
296 impl Test for T {
297     fn test() {
298         t$0
299     }
300 }
301 ",
302             expect![[""]],
303         );
304
305         check(
306             r"
307 trait Test { fn test(); fn test2(); }
308 struct T;
309
310 impl Test for T {
311     fn test() {
312         fn t$0
313     }
314 }
315 ",
316             expect![[""]],
317         );
318
319         check(
320             r"
321 trait Test { fn test(); fn test2(); }
322 struct T;
323
324 impl Test for T {
325     fn test() {
326         fn $0
327     }
328 }
329 ",
330             expect![[""]],
331         );
332
333         // https://github.com/rust-analyzer/rust-analyzer/pull/5976#issuecomment-692332191
334         check(
335             r"
336 trait Test { fn test(); fn test2(); }
337 struct T;
338
339 impl Test for T {
340     fn test() {
341         foo.$0
342     }
343 }
344 ",
345             expect![[""]],
346         );
347
348         check(
349             r"
350 trait Test { fn test(_: i32); fn test2(); }
351 struct T;
352
353 impl Test for T {
354     fn test(t$0)
355 }
356 ",
357             expect![[""]],
358         );
359
360         check(
361             r"
362 trait Test { fn test(_: fn()); fn test2(); }
363 struct T;
364
365 impl Test for T {
366     fn test(f: fn $0)
367 }
368 ",
369             expect![[""]],
370         );
371     }
372
373     #[test]
374     fn no_completion_inside_const() {
375         check(
376             r"
377 trait Test { const TEST: fn(); const TEST2: u32; type Test; fn test(); }
378 struct T;
379
380 impl Test for T {
381     const TEST: fn $0
382 }
383 ",
384             expect![[""]],
385         );
386
387         check(
388             r"
389 trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
390 struct T;
391
392 impl Test for T {
393     const TEST: T$0
394 }
395 ",
396             expect![[""]],
397         );
398
399         check(
400             r"
401 trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
402 struct T;
403
404 impl Test for T {
405     const TEST: u32 = f$0
406 }
407 ",
408             expect![[""]],
409         );
410
411         check(
412             r"
413 trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
414 struct T;
415
416 impl Test for T {
417     const TEST: u32 = {
418         t$0
419     };
420 }
421 ",
422             expect![[""]],
423         );
424
425         check(
426             r"
427 trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
428 struct T;
429
430 impl Test for T {
431     const TEST: u32 = {
432         fn $0
433     };
434 }
435 ",
436             expect![[""]],
437         );
438
439         check(
440             r"
441 trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
442 struct T;
443
444 impl Test for T {
445     const TEST: u32 = {
446         fn t$0
447     };
448 }
449 ",
450             expect![[""]],
451         );
452     }
453
454     #[test]
455     fn no_completion_inside_type() {
456         check(
457             r"
458 trait Test { type Test; type Test2; fn test(); }
459 struct T;
460
461 impl Test for T {
462     type Test = T$0;
463 }
464 ",
465             expect![[""]],
466         );
467
468         check(
469             r"
470 trait Test { type Test; type Test2; fn test(); }
471 struct T;
472
473 impl Test for T {
474     type Test = fn $0;
475 }
476 ",
477             expect![[""]],
478         );
479     }
480
481     #[test]
482     fn name_ref_single_function() {
483         check_edit(
484             "test",
485             r#"
486 trait Test {
487     fn test();
488 }
489 struct T;
490
491 impl Test for T {
492     t$0
493 }
494 "#,
495             r#"
496 trait Test {
497     fn test();
498 }
499 struct T;
500
501 impl Test for T {
502     fn test() {
503     $0
504 }
505 }
506 "#,
507         );
508     }
509
510     #[test]
511     fn single_function() {
512         check_edit(
513             "test",
514             r#"
515 trait Test {
516     fn test();
517 }
518 struct T;
519
520 impl Test for T {
521     fn t$0
522 }
523 "#,
524             r#"
525 trait Test {
526     fn test();
527 }
528 struct T;
529
530 impl Test for T {
531     fn test() {
532     $0
533 }
534 }
535 "#,
536         );
537     }
538
539     #[test]
540     fn hide_implemented_fn() {
541         check(
542             r#"
543 trait Test {
544     fn foo();
545     fn foo_bar();
546 }
547 struct T;
548
549 impl Test for T {
550     fn foo() {}
551     fn f$0
552 }
553 "#,
554             expect![[r#"
555                 fn fn foo_bar()
556             "#]],
557         );
558     }
559
560     #[test]
561     fn generic_fn() {
562         check_edit(
563             "foo",
564             r#"
565 trait Test {
566     fn foo<T>();
567 }
568 struct T;
569
570 impl Test for T {
571     fn f$0
572 }
573 "#,
574             r#"
575 trait Test {
576     fn foo<T>();
577 }
578 struct T;
579
580 impl Test for T {
581     fn foo<T>() {
582     $0
583 }
584 }
585 "#,
586         );
587         check_edit(
588             "foo",
589             r#"
590 trait Test {
591     fn foo<T>() where T: Into<String>;
592 }
593 struct T;
594
595 impl Test for T {
596     fn f$0
597 }
598 "#,
599             r#"
600 trait Test {
601     fn foo<T>() where T: Into<String>;
602 }
603 struct T;
604
605 impl Test for T {
606     fn foo<T>()
607 where T: Into<String> {
608     $0
609 }
610 }
611 "#,
612         );
613     }
614
615     #[test]
616     fn associated_type() {
617         check_edit(
618             "SomeType",
619             r#"
620 trait Test {
621     type SomeType;
622 }
623
624 impl Test for () {
625     type S$0
626 }
627 "#,
628             "
629 trait Test {
630     type SomeType;
631 }
632
633 impl Test for () {
634     type SomeType = \n\
635 }
636 ",
637         );
638     }
639
640     #[test]
641     fn associated_const() {
642         check_edit(
643             "SOME_CONST",
644             r#"
645 trait Test {
646     const SOME_CONST: u16;
647 }
648
649 impl Test for () {
650     const S$0
651 }
652 "#,
653             "
654 trait Test {
655     const SOME_CONST: u16;
656 }
657
658 impl Test for () {
659     const SOME_CONST: u16 = \n\
660 }
661 ",
662         );
663
664         check_edit(
665             "SOME_CONST",
666             r#"
667 trait Test {
668     const SOME_CONST: u16 = 92;
669 }
670
671 impl Test for () {
672     const S$0
673 }
674 "#,
675             "
676 trait Test {
677     const SOME_CONST: u16 = 92;
678 }
679
680 impl Test for () {
681     const SOME_CONST: u16 = \n\
682 }
683 ",
684         );
685     }
686
687     #[test]
688     fn complete_without_name() {
689         let test = |completion: &str, hint: &str, completed: &str, next_sibling: &str| {
690             check_edit(
691                 completion,
692                 &format!(
693                     r#"
694 trait Test {{
695     type Foo;
696     const CONST: u16;
697     fn bar();
698 }}
699 struct T;
700
701 impl Test for T {{
702     {}
703     {}
704 }}
705 "#,
706                     hint, next_sibling
707                 ),
708                 &format!(
709                     r#"
710 trait Test {{
711     type Foo;
712     const CONST: u16;
713     fn bar();
714 }}
715 struct T;
716
717 impl Test for T {{
718     {}
719     {}
720 }}
721 "#,
722                     completed, next_sibling
723                 ),
724             )
725         };
726
727         // Enumerate some possible next siblings.
728         for next_sibling in &[
729             "",
730             "fn other_fn() {}", // `const $0 fn` -> `const fn`
731             "type OtherType = i32;",
732             "const OTHER_CONST: i32 = 0;",
733             "async fn other_fn() {}",
734             "unsafe fn other_fn() {}",
735             "default fn other_fn() {}",
736             "default type OtherType = i32;",
737             "default const OTHER_CONST: i32 = 0;",
738         ] {
739             test("bar", "fn $0", "fn bar() {\n    $0\n}", next_sibling);
740             test("Foo", "type $0", "type Foo = ", next_sibling);
741             test("CONST", "const $0", "const CONST: u16 = ", next_sibling);
742         }
743     }
744
745     #[test]
746     fn snippet_does_not_overwrite_comment_or_attr() {
747         let test = |completion: &str, hint: &str, completed: &str| {
748             check_edit(
749                 completion,
750                 &format!(
751                     r#"
752 trait Foo {{
753     type Type;
754     fn function();
755     const CONST: i32 = 0;
756 }}
757 struct T;
758
759 impl Foo for T {{
760     // Comment
761     #[bar]
762     {}
763 }}
764 "#,
765                     hint
766                 ),
767                 &format!(
768                     r#"
769 trait Foo {{
770     type Type;
771     fn function();
772     const CONST: i32 = 0;
773 }}
774 struct T;
775
776 impl Foo for T {{
777     // Comment
778     #[bar]
779     {}
780 }}
781 "#,
782                     completed
783                 ),
784             )
785         };
786         test("function", "fn f$0", "fn function() {\n    $0\n}");
787         test("Type", "type T$0", "type Type = ");
788         test("CONST", "const C$0", "const CONST: i32 = ");
789     }
790 }