aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_completion/src/completions/trait_impl.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_completion/src/completions/trait_impl.rs')
-rw-r--r--crates/ide_completion/src/completions/trait_impl.rs736
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
34use hir::{self, HasAttrs, HasSource};
35use ide_db::{traits::get_missing_assoc_items, SymbolKind};
36use syntax::{
37 ast::{self, edit, Impl},
38 display::function_declaration,
39 AstNode, SyntaxKind, SyntaxNode, TextRange, T,
40};
41use text_edit::TextEdit;
42
43use crate::{
44 CompletionContext,
45 CompletionItem,
46 CompletionItemKind,
47 CompletionKind,
48 Completions,
49 // display::function_declaration,
50};
51
52#[derive(Debug, PartialEq, Eq)]
53enum ImplCompletionKind {
54 All,
55 Fn,
56 TypeAlias,
57 Const,
58}
59
60pub(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
83fn 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
134fn 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
176fn 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
196fn 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
221fn 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)]
245mod 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#"
262trait Test {
263 type TestType;
264 const TEST_CONST: u16;
265 fn test();
266}
267struct T;
268
269impl Test for T {
270 t$0
271}
272"#,
273 expect![["
274ta type TestType = \n\
275ct const TEST_CONST: u16 = \n\
276fn fn test()
277"]],
278 );
279 }
280
281 #[test]
282 fn no_completion_inside_fn() {
283 check(
284 r"
285trait Test { fn test(); fn test2(); }
286struct T;
287
288impl Test for T {
289 fn test() {
290 t$0
291 }
292}
293",
294 expect![[""]],
295 );
296
297 check(
298 r"
299trait Test { fn test(); fn test2(); }
300struct T;
301
302impl Test for T {
303 fn test() {
304 fn t$0
305 }
306}
307",
308 expect![[""]],
309 );
310
311 check(
312 r"
313trait Test { fn test(); fn test2(); }
314struct T;
315
316impl 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"
328trait Test { fn test(); fn test2(); }
329struct T;
330
331impl Test for T {
332 fn test() {
333 foo.$0
334 }
335}
336",
337 expect![[""]],
338 );
339
340 check(
341 r"
342trait Test { fn test(_: i32); fn test2(); }
343struct T;
344
345impl Test for T {
346 fn test(t$0)
347}
348",
349 expect![[""]],
350 );
351
352 check(
353 r"
354trait Test { fn test(_: fn()); fn test2(); }
355struct T;
356
357impl 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"
369trait Test { const TEST: fn(); const TEST2: u32; type Test; fn test(); }
370struct T;
371
372impl Test for T {
373 const TEST: fn $0
374}
375",
376 expect![[""]],
377 );
378
379 check(
380 r"
381trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
382struct T;
383
384impl Test for T {
385 const TEST: T$0
386}
387",
388 expect![[""]],
389 );
390
391 check(
392 r"
393trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
394struct T;
395
396impl Test for T {
397 const TEST: u32 = f$0
398}
399",
400 expect![[""]],
401 );
402
403 check(
404 r"
405trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
406struct T;
407
408impl Test for T {
409 const TEST: u32 = {
410 t$0
411 };
412}
413",
414 expect![[""]],
415 );
416
417 check(
418 r"
419trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
420struct T;
421
422impl Test for T {
423 const TEST: u32 = {
424 fn $0
425 };
426}
427",
428 expect![[""]],
429 );
430
431 check(
432 r"
433trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
434struct T;
435
436impl 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"
450trait Test { type Test; type Test2; fn test(); }
451struct T;
452
453impl Test for T {
454 type Test = T$0;
455}
456",
457 expect![[""]],
458 );
459
460 check(
461 r"
462trait Test { type Test; type Test2; fn test(); }
463struct T;
464
465impl 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#"
478trait Test {
479 fn test();
480}
481struct T;
482
483impl Test for T {
484 t$0
485}
486"#,
487 r#"
488trait Test {
489 fn test();
490}
491struct T;
492
493impl 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#"
507trait Test {
508 fn test();
509}
510struct T;
511
512impl Test for T {
513 fn t$0
514}
515"#,
516 r#"
517trait Test {
518 fn test();
519}
520struct T;
521
522impl 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#"
535trait Test {
536 fn foo();
537 fn foo_bar();
538}
539struct T;
540
541impl 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#"
557trait Test {
558 fn foo<T>();
559}
560struct T;
561
562impl Test for T {
563 fn f$0
564}
565"#,
566 r#"
567trait Test {
568 fn foo<T>();
569}
570struct T;
571
572impl Test for T {
573 fn foo<T>() {
574 $0
575}
576}
577"#,
578 );
579 check_edit(
580 "foo",
581 r#"
582trait Test {
583 fn foo<T>() where T: Into<String>;
584}
585struct T;
586
587impl Test for T {
588 fn f$0
589}
590"#,
591 r#"
592trait Test {
593 fn foo<T>() where T: Into<String>;
594}
595struct T;
596
597impl Test for T {
598 fn foo<T>()
599where T: Into<String> {
600 $0
601}
602}
603"#,
604 );
605 }
606
607 #[test]
608 fn associated_type() {
609 check_edit(
610 "SomeType",
611 r#"
612trait Test {
613 type SomeType;
614}
615
616impl Test for () {
617 type S$0
618}
619"#,
620 "
621trait Test {
622 type SomeType;
623}
624
625impl 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#"
637trait Test {
638 const SOME_CONST: u16;
639}
640
641impl Test for () {
642 const S$0
643}
644"#,
645 "
646trait Test {
647 const SOME_CONST: u16;
648}
649
650impl Test for () {
651 const SOME_CONST: u16 = \n\
652}
653",
654 );
655
656 check_edit(
657 "SOME_CONST",
658 r#"
659trait Test {
660 const SOME_CONST: u16 = 92;
661}
662
663impl Test for () {
664 const S$0
665}
666"#,
667 "
668trait Test {
669 const SOME_CONST: u16 = 92;
670}
671
672impl 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#"
686trait Test {{
687 type Foo;
688 const CONST: u16;
689 fn bar();
690}}
691struct T;
692
693impl Test for T {{
694 {}
695 {}
696}}
697"#,
698 hint, next_sibling
699 ),
700 &format!(
701 r#"
702trait Test {{
703 type Foo;
704 const CONST: u16;
705 fn bar();
706}}
707struct T;
708
709impl 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}