aboutsummaryrefslogtreecommitdiff
path: root/crates/completion/src/completions/trait_impl.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/completion/src/completions/trait_impl.rs')
-rw-r--r--crates/completion/src/completions/trait_impl.rs736
1 files changed, 736 insertions, 0 deletions
diff --git a/crates/completion/src/completions/trait_impl.rs b/crates/completion/src/completions/trait_impl.rs
new file mode 100644
index 000000000..a14be9c73
--- /dev/null
+++ b/crates/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<|>
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() {}<|>
31//! }
32//! ```
33
34use hir::{self, HasAttrs, HasSource};
35use ide_db::traits::get_missing_assoc_items;
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 <|> }`, 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 <|> }`
94 // ERROR 0
95 // CONST_KW <- *
96 SyntaxKind::CONST_KW => 0,
97 // `impl .. { fn/type <|> }`
98 // FN/TYPE_ALIAS 0
99 // FN_KW <- *
100 SyntaxKind::FN_KW | SyntaxKind::TYPE_KW => 0,
101 // `impl .. { fn/type/const foo<|> }`
102 // FN/TYPE_ALIAS/CONST 1
103 // NAME 0
104 // IDENT <- *
105 SyntaxKind::IDENT if token.parent().kind() == SyntaxKind::NAME => 1,
106 // `impl .. { foo<|> }`
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 <|> fn/type/const }`
124 _ if token.kind() == SyntaxKind::CONST_KW => 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.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::Function
156 };
157 let range = TextRange::new(fn_def_node.text_range().start(), ctx.source_range().end());
158
159 let function_decl = function_declaration(&func.source(ctx.db).value);
160 match ctx.config.snippet_cap {
161 Some(cap) => {
162 let snippet = format!("{} {{\n $0\n}}", function_decl);
163 builder.snippet_edit(cap, TextEdit::replace(range, snippet))
164 }
165 None => {
166 let header = format!("{} {{", function_decl);
167 builder.text_edit(TextEdit::replace(range, header))
168 }
169 }
170 .kind(completion_kind)
171 .add_to(acc);
172}
173
174fn add_type_alias_impl(
175 type_def_node: &SyntaxNode,
176 acc: &mut Completions,
177 ctx: &CompletionContext,
178 type_alias: hir::TypeAlias,
179) {
180 let alias_name = type_alias.name(ctx.db).to_string();
181
182 let snippet = format!("type {} = ", alias_name);
183
184 let range = TextRange::new(type_def_node.text_range().start(), ctx.source_range().end());
185
186 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone())
187 .text_edit(TextEdit::replace(range, snippet))
188 .lookup_by(alias_name)
189 .kind(CompletionItemKind::TypeAlias)
190 .set_documentation(type_alias.docs(ctx.db))
191 .add_to(acc);
192}
193
194fn 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 let snippet = make_const_compl_syntax(&const_.source(ctx.db).value);
204
205 let range = TextRange::new(const_def_node.text_range().start(), ctx.source_range().end());
206
207 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone())
208 .text_edit(TextEdit::replace(range, snippet))
209 .lookup_by(const_name)
210 .kind(CompletionItemKind::Const)
211 .set_documentation(const_.docs(ctx.db))
212 .add_to(acc);
213 }
214}
215
216fn make_const_compl_syntax(const_: &ast::Const) -> String {
217 let const_ = edit::remove_attrs_and_docs(const_);
218
219 let const_start = const_.syntax().text_range().start();
220 let const_end = const_.syntax().text_range().end();
221
222 let start =
223 const_.syntax().first_child_or_token().map_or(const_start, |f| f.text_range().start());
224
225 let end = const_
226 .syntax()
227 .children_with_tokens()
228 .find(|s| s.kind() == T![;] || s.kind() == T![=])
229 .map_or(const_end, |f| f.text_range().start());
230
231 let len = end - start;
232 let range = TextRange::new(0.into(), len);
233
234 let syntax = const_.syntax().text().slice(range).to_string();
235
236 format!("{} = ", syntax.trim_end())
237}
238
239#[cfg(test)]
240mod tests {
241 use expect_test::{expect, Expect};
242
243 use crate::{
244 test_utils::{check_edit, completion_list},
245 CompletionKind,
246 };
247
248 fn check(ra_fixture: &str, expect: Expect) {
249 let actual = completion_list(ra_fixture, CompletionKind::Magic);
250 expect.assert_eq(&actual)
251 }
252
253 #[test]
254 fn name_ref_function_type_const() {
255 check(
256 r#"
257trait Test {
258 type TestType;
259 const TEST_CONST: u16;
260 fn test();
261}
262struct T;
263
264impl Test for T {
265 t<|>
266}
267"#,
268 expect![["
269ct const TEST_CONST: u16 = \n\
270fn fn test()
271ta type TestType = \n\
272 "]],
273 );
274 }
275
276 #[test]
277 fn no_completion_inside_fn() {
278 check(
279 r"
280trait Test { fn test(); fn test2(); }
281struct T;
282
283impl Test for T {
284 fn test() {
285 t<|>
286 }
287}
288",
289 expect![[""]],
290 );
291
292 check(
293 r"
294trait Test { fn test(); fn test2(); }
295struct T;
296
297impl Test for T {
298 fn test() {
299 fn t<|>
300 }
301}
302",
303 expect![[""]],
304 );
305
306 check(
307 r"
308trait Test { fn test(); fn test2(); }
309struct T;
310
311impl Test for T {
312 fn test() {
313 fn <|>
314 }
315}
316",
317 expect![[""]],
318 );
319
320 // https://github.com/rust-analyzer/rust-analyzer/pull/5976#issuecomment-692332191
321 check(
322 r"
323trait Test { fn test(); fn test2(); }
324struct T;
325
326impl Test for T {
327 fn test() {
328 foo.<|>
329 }
330}
331",
332 expect![[""]],
333 );
334
335 check(
336 r"
337trait Test { fn test(_: i32); fn test2(); }
338struct T;
339
340impl Test for T {
341 fn test(t<|>)
342}
343",
344 expect![[""]],
345 );
346
347 check(
348 r"
349trait Test { fn test(_: fn()); fn test2(); }
350struct T;
351
352impl Test for T {
353 fn test(f: fn <|>)
354}
355",
356 expect![[""]],
357 );
358 }
359
360 #[test]
361 fn no_completion_inside_const() {
362 check(
363 r"
364trait Test { const TEST: fn(); const TEST2: u32; type Test; fn test(); }
365struct T;
366
367impl Test for T {
368 const TEST: fn <|>
369}
370",
371 expect![[""]],
372 );
373
374 check(
375 r"
376trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
377struct T;
378
379impl Test for T {
380 const TEST: T<|>
381}
382",
383 expect![[""]],
384 );
385
386 check(
387 r"
388trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
389struct T;
390
391impl Test for T {
392 const TEST: u32 = f<|>
393}
394",
395 expect![[""]],
396 );
397
398 check(
399 r"
400trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
401struct T;
402
403impl Test for T {
404 const TEST: u32 = {
405 t<|>
406 };
407}
408",
409 expect![[""]],
410 );
411
412 check(
413 r"
414trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
415struct T;
416
417impl Test for T {
418 const TEST: u32 = {
419 fn <|>
420 };
421}
422",
423 expect![[""]],
424 );
425
426 check(
427 r"
428trait Test { const TEST: u32; const TEST2: u32; type Test; fn test(); }
429struct T;
430
431impl Test for T {
432 const TEST: u32 = {
433 fn t<|>
434 };
435}
436",
437 expect![[""]],
438 );
439 }
440
441 #[test]
442 fn no_completion_inside_type() {
443 check(
444 r"
445trait Test { type Test; type Test2; fn test(); }
446struct T;
447
448impl Test for T {
449 type Test = T<|>;
450}
451",
452 expect![[""]],
453 );
454
455 check(
456 r"
457trait Test { type Test; type Test2; fn test(); }
458struct T;
459
460impl Test for T {
461 type Test = fn <|>;
462}
463",
464 expect![[""]],
465 );
466 }
467
468 #[test]
469 fn name_ref_single_function() {
470 check_edit(
471 "test",
472 r#"
473trait Test {
474 fn test();
475}
476struct T;
477
478impl Test for T {
479 t<|>
480}
481"#,
482 r#"
483trait Test {
484 fn test();
485}
486struct T;
487
488impl Test for T {
489 fn test() {
490 $0
491}
492}
493"#,
494 );
495 }
496
497 #[test]
498 fn single_function() {
499 check_edit(
500 "test",
501 r#"
502trait Test {
503 fn test();
504}
505struct T;
506
507impl Test for T {
508 fn t<|>
509}
510"#,
511 r#"
512trait Test {
513 fn test();
514}
515struct T;
516
517impl Test for T {
518 fn test() {
519 $0
520}
521}
522"#,
523 );
524 }
525
526 #[test]
527 fn hide_implemented_fn() {
528 check(
529 r#"
530trait Test {
531 fn foo();
532 fn foo_bar();
533}
534struct T;
535
536impl Test for T {
537 fn foo() {}
538 fn f<|>
539}
540"#,
541 expect![[r#"
542 fn fn foo_bar()
543 "#]],
544 );
545 }
546
547 #[test]
548 fn generic_fn() {
549 check_edit(
550 "foo",
551 r#"
552trait Test {
553 fn foo<T>();
554}
555struct T;
556
557impl Test for T {
558 fn f<|>
559}
560"#,
561 r#"
562trait Test {
563 fn foo<T>();
564}
565struct T;
566
567impl Test for T {
568 fn foo<T>() {
569 $0
570}
571}
572"#,
573 );
574 check_edit(
575 "foo",
576 r#"
577trait Test {
578 fn foo<T>() where T: Into<String>;
579}
580struct T;
581
582impl Test for T {
583 fn f<|>
584}
585"#,
586 r#"
587trait Test {
588 fn foo<T>() where T: Into<String>;
589}
590struct T;
591
592impl Test for T {
593 fn foo<T>()
594where T: Into<String> {
595 $0
596}
597}
598"#,
599 );
600 }
601
602 #[test]
603 fn associated_type() {
604 check_edit(
605 "SomeType",
606 r#"
607trait Test {
608 type SomeType;
609}
610
611impl Test for () {
612 type S<|>
613}
614"#,
615 "
616trait Test {
617 type SomeType;
618}
619
620impl Test for () {
621 type SomeType = \n\
622}
623",
624 );
625 }
626
627 #[test]
628 fn associated_const() {
629 check_edit(
630 "SOME_CONST",
631 r#"
632trait Test {
633 const SOME_CONST: u16;
634}
635
636impl Test for () {
637 const S<|>
638}
639"#,
640 "
641trait Test {
642 const SOME_CONST: u16;
643}
644
645impl Test for () {
646 const SOME_CONST: u16 = \n\
647}
648",
649 );
650
651 check_edit(
652 "SOME_CONST",
653 r#"
654trait Test {
655 const SOME_CONST: u16 = 92;
656}
657
658impl Test for () {
659 const S<|>
660}
661"#,
662 "
663trait Test {
664 const SOME_CONST: u16 = 92;
665}
666
667impl Test for () {
668 const SOME_CONST: u16 = \n\
669}
670",
671 );
672 }
673
674 #[test]
675 fn complete_without_name() {
676 let test = |completion: &str, hint: &str, completed: &str, next_sibling: &str| {
677 println!(
678 "completion='{}', hint='{}', next_sibling='{}'",
679 completion, hint, next_sibling
680 );
681
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 <|> 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 <|>", "fn bar() {\n $0\n}", next_sibling);
732 test("Foo", "type <|>", "type Foo = ", next_sibling);
733 test("CONST", "const <|>", "const CONST: u16 = ", next_sibling);
734 }
735 }
736}