aboutsummaryrefslogtreecommitdiff
path: root/crates/completion/src/render.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/completion/src/render.rs')
-rw-r--r--crates/completion/src/render.rs879
1 files changed, 0 insertions, 879 deletions
diff --git a/crates/completion/src/render.rs b/crates/completion/src/render.rs
deleted file mode 100644
index 820dd01d1..000000000
--- a/crates/completion/src/render.rs
+++ /dev/null
@@ -1,879 +0,0 @@
1//! `render` module provides utilities for rendering completion suggestions
2//! into code pieces that will be presented to user.
3
4pub(crate) mod macro_;
5pub(crate) mod function;
6pub(crate) mod enum_variant;
7pub(crate) mod const_;
8pub(crate) mod pattern;
9pub(crate) mod type_alias;
10
11mod builder_ext;
12
13use hir::{Documentation, HasAttrs, HirDisplay, Mutability, ScopeDef, Type};
14use ide_db::{helpers::SnippetCap, RootDatabase};
15use syntax::TextRange;
16use test_utils::mark;
17
18use crate::{
19 item::ImportEdit, CompletionContext, CompletionItem, CompletionItemKind, CompletionKind,
20 CompletionScore,
21};
22
23use crate::render::{enum_variant::render_variant, function::render_fn, macro_::render_macro};
24
25pub(crate) fn render_field<'a>(
26 ctx: RenderContext<'a>,
27 field: hir::Field,
28 ty: &Type,
29) -> CompletionItem {
30 Render::new(ctx).add_field(field, ty)
31}
32
33pub(crate) fn render_tuple_field<'a>(
34 ctx: RenderContext<'a>,
35 field: usize,
36 ty: &Type,
37) -> CompletionItem {
38 Render::new(ctx).add_tuple_field(field, ty)
39}
40
41pub(crate) fn render_resolution<'a>(
42 ctx: RenderContext<'a>,
43 local_name: String,
44 resolution: &ScopeDef,
45) -> Option<CompletionItem> {
46 Render::new(ctx).render_resolution(local_name, None, resolution)
47}
48
49pub(crate) fn render_resolution_with_import<'a>(
50 ctx: RenderContext<'a>,
51 import_edit: ImportEdit,
52 resolution: &ScopeDef,
53) -> Option<CompletionItem> {
54 Render::new(ctx)
55 .render_resolution(
56 import_edit.import_path.segments.last()?.to_string(),
57 Some(import_edit),
58 resolution,
59 )
60 .map(|mut item| {
61 item.completion_kind = CompletionKind::Magic;
62 item
63 })
64}
65
66/// Interface for data and methods required for items rendering.
67#[derive(Debug)]
68pub(crate) struct RenderContext<'a> {
69 completion: &'a CompletionContext<'a>,
70}
71
72impl<'a> RenderContext<'a> {
73 pub(crate) fn new(completion: &'a CompletionContext<'a>) -> RenderContext<'a> {
74 RenderContext { completion }
75 }
76
77 fn snippet_cap(&self) -> Option<SnippetCap> {
78 self.completion.config.snippet_cap.clone()
79 }
80
81 fn db(&self) -> &'a RootDatabase {
82 &self.completion.db
83 }
84
85 fn source_range(&self) -> TextRange {
86 self.completion.source_range()
87 }
88
89 fn is_deprecated(&self, node: impl HasAttrs) -> bool {
90 node.attrs(self.db()).by_key("deprecated").exists()
91 }
92
93 fn docs(&self, node: impl HasAttrs) -> Option<Documentation> {
94 node.docs(self.db())
95 }
96
97 fn active_name_and_type(&self) -> Option<(String, Type)> {
98 if let Some(record_field) = &self.completion.record_field_syntax {
99 mark::hit!(record_field_type_match);
100 let (struct_field, _local) = self.completion.sema.resolve_record_field(record_field)?;
101 Some((struct_field.name(self.db()).to_string(), struct_field.signature_ty(self.db())))
102 } else if let Some(active_parameter) = &self.completion.active_parameter {
103 mark::hit!(active_param_type_match);
104 Some((active_parameter.name.clone(), active_parameter.ty.clone()))
105 } else {
106 None
107 }
108 }
109}
110
111/// Generic renderer for completion items.
112#[derive(Debug)]
113struct Render<'a> {
114 ctx: RenderContext<'a>,
115}
116
117impl<'a> Render<'a> {
118 fn new(ctx: RenderContext<'a>) -> Render<'a> {
119 Render { ctx }
120 }
121
122 fn add_field(&mut self, field: hir::Field, ty: &Type) -> CompletionItem {
123 let is_deprecated = self.ctx.is_deprecated(field);
124 let name = field.name(self.ctx.db());
125 let mut item = CompletionItem::new(
126 CompletionKind::Reference,
127 self.ctx.source_range(),
128 name.to_string(),
129 )
130 .kind(CompletionItemKind::Field)
131 .detail(ty.display(self.ctx.db()).to_string())
132 .set_documentation(field.docs(self.ctx.db()))
133 .set_deprecated(is_deprecated);
134
135 if let Some(score) = compute_score(&self.ctx, &ty, &name.to_string()) {
136 item = item.set_score(score);
137 }
138
139 item.build()
140 }
141
142 fn add_tuple_field(&mut self, field: usize, ty: &Type) -> CompletionItem {
143 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), field.to_string())
144 .kind(CompletionItemKind::Field)
145 .detail(ty.display(self.ctx.db()).to_string())
146 .build()
147 }
148
149 fn render_resolution(
150 self,
151 local_name: String,
152 import_to_add: Option<ImportEdit>,
153 resolution: &ScopeDef,
154 ) -> Option<CompletionItem> {
155 let _p = profile::span("render_resolution");
156 use hir::ModuleDef::*;
157
158 let completion_kind = match resolution {
159 ScopeDef::ModuleDef(BuiltinType(..)) => CompletionKind::BuiltinType,
160 _ => CompletionKind::Reference,
161 };
162
163 let kind = match resolution {
164 ScopeDef::ModuleDef(Function(func)) => {
165 return render_fn(self.ctx, import_to_add, Some(local_name), *func);
166 }
167 ScopeDef::ModuleDef(Variant(_))
168 if self.ctx.completion.is_pat_binding_or_const
169 | self.ctx.completion.is_irrefutable_pat_binding =>
170 {
171 CompletionItemKind::EnumVariant
172 }
173 ScopeDef::ModuleDef(Variant(var)) => {
174 let item = render_variant(self.ctx, import_to_add, Some(local_name), *var, None);
175 return Some(item);
176 }
177 ScopeDef::MacroDef(mac) => {
178 let item = render_macro(self.ctx, import_to_add, local_name, *mac);
179 return item;
180 }
181
182 ScopeDef::ModuleDef(Module(..)) => CompletionItemKind::Module,
183 ScopeDef::ModuleDef(Adt(hir::Adt::Struct(_))) => CompletionItemKind::Struct,
184 // FIXME: add CompletionItemKind::Union
185 ScopeDef::ModuleDef(Adt(hir::Adt::Union(_))) => CompletionItemKind::Struct,
186 ScopeDef::ModuleDef(Adt(hir::Adt::Enum(_))) => CompletionItemKind::Enum,
187 ScopeDef::ModuleDef(Const(..)) => CompletionItemKind::Const,
188 ScopeDef::ModuleDef(Static(..)) => CompletionItemKind::Static,
189 ScopeDef::ModuleDef(Trait(..)) => CompletionItemKind::Trait,
190 ScopeDef::ModuleDef(TypeAlias(..)) => CompletionItemKind::TypeAlias,
191 ScopeDef::ModuleDef(BuiltinType(..)) => CompletionItemKind::BuiltinType,
192 ScopeDef::GenericParam(..) => CompletionItemKind::TypeParam,
193 ScopeDef::Local(..) => CompletionItemKind::Binding,
194 // (does this need its own kind?)
195 ScopeDef::AdtSelfType(..) | ScopeDef::ImplSelfType(..) => CompletionItemKind::TypeParam,
196 ScopeDef::Unknown => {
197 let item = CompletionItem::new(
198 CompletionKind::Reference,
199 self.ctx.source_range(),
200 local_name,
201 )
202 .kind(CompletionItemKind::UnresolvedReference)
203 .add_import(import_to_add)
204 .build();
205 return Some(item);
206 }
207 };
208
209 let docs = self.docs(resolution);
210
211 let mut item =
212 CompletionItem::new(completion_kind, self.ctx.source_range(), local_name.clone());
213 if let ScopeDef::Local(local) = resolution {
214 let ty = local.ty(self.ctx.db());
215 if !ty.is_unknown() {
216 item = item.detail(ty.display(self.ctx.db()).to_string());
217 }
218 };
219
220 let mut ref_match = None;
221 if let ScopeDef::Local(local) = resolution {
222 if let Some((active_name, active_type)) = self.ctx.active_name_and_type() {
223 let ty = local.ty(self.ctx.db());
224 if let Some(score) =
225 compute_score_from_active(&active_type, &active_name, &ty, &local_name)
226 {
227 item = item.set_score(score);
228 }
229 ref_match = refed_type_matches(&active_type, &active_name, &ty, &local_name);
230 }
231 }
232
233 // Add `<>` for generic types
234 if self.ctx.completion.is_path_type
235 && !self.ctx.completion.has_type_args
236 && self.ctx.completion.config.add_call_parenthesis
237 {
238 if let Some(cap) = self.ctx.snippet_cap() {
239 let has_non_default_type_params = match resolution {
240 ScopeDef::ModuleDef(Adt(it)) => it.has_non_default_type_params(self.ctx.db()),
241 ScopeDef::ModuleDef(TypeAlias(it)) => {
242 it.has_non_default_type_params(self.ctx.db())
243 }
244 _ => false,
245 };
246 if has_non_default_type_params {
247 mark::hit!(inserts_angle_brackets_for_generics);
248 item = item
249 .lookup_by(local_name.clone())
250 .label(format!("{}<…>", local_name))
251 .insert_snippet(cap, format!("{}<$0>", local_name));
252 }
253 }
254 }
255
256 let item = item
257 .kind(kind)
258 .add_import(import_to_add)
259 .set_documentation(docs)
260 .set_ref_match(ref_match)
261 .build();
262 Some(item)
263 }
264
265 fn docs(&self, resolution: &ScopeDef) -> Option<Documentation> {
266 use hir::ModuleDef::*;
267 match resolution {
268 ScopeDef::ModuleDef(Module(it)) => it.docs(self.ctx.db()),
269 ScopeDef::ModuleDef(Adt(it)) => it.docs(self.ctx.db()),
270 ScopeDef::ModuleDef(Variant(it)) => it.docs(self.ctx.db()),
271 ScopeDef::ModuleDef(Const(it)) => it.docs(self.ctx.db()),
272 ScopeDef::ModuleDef(Static(it)) => it.docs(self.ctx.db()),
273 ScopeDef::ModuleDef(Trait(it)) => it.docs(self.ctx.db()),
274 ScopeDef::ModuleDef(TypeAlias(it)) => it.docs(self.ctx.db()),
275 _ => None,
276 }
277 }
278}
279
280fn compute_score_from_active(
281 active_type: &Type,
282 active_name: &str,
283 ty: &Type,
284 name: &str,
285) -> Option<CompletionScore> {
286 // Compute score
287 // For the same type
288 if active_type != ty {
289 return None;
290 }
291
292 let mut res = CompletionScore::TypeMatch;
293
294 // If same type + same name then go top position
295 if active_name == name {
296 res = CompletionScore::TypeAndNameMatch
297 }
298
299 Some(res)
300}
301fn refed_type_matches(
302 active_type: &Type,
303 active_name: &str,
304 ty: &Type,
305 name: &str,
306) -> Option<(Mutability, CompletionScore)> {
307 let derefed_active = active_type.remove_ref()?;
308 let score = compute_score_from_active(&derefed_active, &active_name, &ty, &name)?;
309 Some((
310 if active_type.is_mutable_reference() { Mutability::Mut } else { Mutability::Shared },
311 score,
312 ))
313}
314
315fn compute_score(ctx: &RenderContext, ty: &Type, name: &str) -> Option<CompletionScore> {
316 let (active_name, active_type) = ctx.active_name_and_type()?;
317 compute_score_from_active(&active_type, &active_name, ty, name)
318}
319
320#[cfg(test)]
321mod tests {
322 use std::cmp::Reverse;
323
324 use expect_test::{expect, Expect};
325 use test_utils::mark;
326
327 use crate::{
328 test_utils::{check_edit, do_completion, get_all_items, TEST_CONFIG},
329 CompletionKind, CompletionScore,
330 };
331
332 fn check(ra_fixture: &str, expect: Expect) {
333 let actual = do_completion(ra_fixture, CompletionKind::Reference);
334 expect.assert_debug_eq(&actual);
335 }
336
337 fn check_scores(ra_fixture: &str, expect: Expect) {
338 fn display_score(score: Option<CompletionScore>) -> &'static str {
339 match score {
340 Some(CompletionScore::TypeMatch) => "[type]",
341 Some(CompletionScore::TypeAndNameMatch) => "[type+name]",
342 None => "[]".into(),
343 }
344 }
345
346 let mut completions = get_all_items(TEST_CONFIG, ra_fixture);
347 completions.sort_by_key(|it| (Reverse(it.score()), it.label().to_string()));
348 let actual = completions
349 .into_iter()
350 .filter(|it| it.completion_kind == CompletionKind::Reference)
351 .map(|it| {
352 let tag = it.kind().unwrap().tag();
353 let score = display_score(it.score());
354 format!("{} {} {}\n", tag, it.label(), score)
355 })
356 .collect::<String>();
357 expect.assert_eq(&actual);
358 }
359
360 #[test]
361 fn enum_detail_includes_record_fields() {
362 check(
363 r#"
364enum Foo { Foo { x: i32, y: i32 } }
365
366fn main() { Foo::Fo$0 }
367"#,
368 expect![[r#"
369 [
370 CompletionItem {
371 label: "Foo",
372 source_range: 54..56,
373 delete: 54..56,
374 insert: "Foo",
375 kind: EnumVariant,
376 detail: "{ x: i32, y: i32 }",
377 },
378 ]
379 "#]],
380 );
381 }
382
383 #[test]
384 fn enum_detail_doesnt_include_tuple_fields() {
385 check(
386 r#"
387enum Foo { Foo (i32, i32) }
388
389fn main() { Foo::Fo$0 }
390"#,
391 expect![[r#"
392 [
393 CompletionItem {
394 label: "Foo(…)",
395 source_range: 46..48,
396 delete: 46..48,
397 insert: "Foo($0)",
398 kind: EnumVariant,
399 lookup: "Foo",
400 detail: "(i32, i32)",
401 trigger_call_info: true,
402 },
403 ]
404 "#]],
405 );
406 }
407
408 #[test]
409 fn enum_detail_just_parentheses_for_unit() {
410 check(
411 r#"
412enum Foo { Foo }
413
414fn main() { Foo::Fo$0 }
415"#,
416 expect![[r#"
417 [
418 CompletionItem {
419 label: "Foo",
420 source_range: 35..37,
421 delete: 35..37,
422 insert: "Foo",
423 kind: EnumVariant,
424 detail: "()",
425 },
426 ]
427 "#]],
428 );
429 }
430
431 #[test]
432 fn lookup_enums_by_two_qualifiers() {
433 check(
434 r#"
435mod m {
436 pub enum Spam { Foo, Bar(i32) }
437}
438fn main() { let _: m::Spam = S$0 }
439"#,
440 expect![[r#"
441 [
442 CompletionItem {
443 label: "Spam::Bar(…)",
444 source_range: 75..76,
445 delete: 75..76,
446 insert: "Spam::Bar($0)",
447 kind: EnumVariant,
448 lookup: "Spam::Bar",
449 detail: "(i32)",
450 trigger_call_info: true,
451 },
452 CompletionItem {
453 label: "m",
454 source_range: 75..76,
455 delete: 75..76,
456 insert: "m",
457 kind: Module,
458 },
459 CompletionItem {
460 label: "m::Spam::Foo",
461 source_range: 75..76,
462 delete: 75..76,
463 insert: "m::Spam::Foo",
464 kind: EnumVariant,
465 lookup: "Spam::Foo",
466 detail: "()",
467 },
468 CompletionItem {
469 label: "main()",
470 source_range: 75..76,
471 delete: 75..76,
472 insert: "main()$0",
473 kind: Function,
474 lookup: "main",
475 detail: "fn main()",
476 },
477 ]
478 "#]],
479 )
480 }
481
482 #[test]
483 fn sets_deprecated_flag_in_items() {
484 check(
485 r#"
486#[deprecated]
487fn something_deprecated() {}
488#[deprecated(since = "1.0.0")]
489fn something_else_deprecated() {}
490
491fn main() { som$0 }
492"#,
493 expect![[r#"
494 [
495 CompletionItem {
496 label: "main()",
497 source_range: 121..124,
498 delete: 121..124,
499 insert: "main()$0",
500 kind: Function,
501 lookup: "main",
502 detail: "fn main()",
503 },
504 CompletionItem {
505 label: "something_deprecated()",
506 source_range: 121..124,
507 delete: 121..124,
508 insert: "something_deprecated()$0",
509 kind: Function,
510 lookup: "something_deprecated",
511 detail: "fn something_deprecated()",
512 deprecated: true,
513 },
514 CompletionItem {
515 label: "something_else_deprecated()",
516 source_range: 121..124,
517 delete: 121..124,
518 insert: "something_else_deprecated()$0",
519 kind: Function,
520 lookup: "something_else_deprecated",
521 detail: "fn something_else_deprecated()",
522 deprecated: true,
523 },
524 ]
525 "#]],
526 );
527
528 check(
529 r#"
530struct A { #[deprecated] the_field: u32 }
531fn foo() { A { the$0 } }
532"#,
533 expect![[r#"
534 [
535 CompletionItem {
536 label: "the_field",
537 source_range: 57..60,
538 delete: 57..60,
539 insert: "the_field",
540 kind: Field,
541 detail: "u32",
542 deprecated: true,
543 },
544 ]
545 "#]],
546 );
547 }
548
549 #[test]
550 fn renders_docs() {
551 check(
552 r#"
553struct S {
554 /// Field docs
555 foo:
556}
557impl S {
558 /// Method docs
559 fn bar(self) { self.$0 }
560}"#,
561 expect![[r#"
562 [
563 CompletionItem {
564 label: "bar()",
565 source_range: 94..94,
566 delete: 94..94,
567 insert: "bar()$0",
568 kind: Method,
569 lookup: "bar",
570 detail: "fn bar(self)",
571 documentation: Documentation(
572 "Method docs",
573 ),
574 },
575 CompletionItem {
576 label: "foo",
577 source_range: 94..94,
578 delete: 94..94,
579 insert: "foo",
580 kind: Field,
581 detail: "{unknown}",
582 documentation: Documentation(
583 "Field docs",
584 ),
585 },
586 ]
587 "#]],
588 );
589
590 check(
591 r#"
592use self::my$0;
593
594/// mod docs
595mod my { }
596
597/// enum docs
598enum E {
599 /// variant docs
600 V
601}
602use self::E::*;
603"#,
604 expect![[r#"
605 [
606 CompletionItem {
607 label: "E",
608 source_range: 10..12,
609 delete: 10..12,
610 insert: "E",
611 kind: Enum,
612 documentation: Documentation(
613 "enum docs",
614 ),
615 },
616 CompletionItem {
617 label: "V",
618 source_range: 10..12,
619 delete: 10..12,
620 insert: "V",
621 kind: EnumVariant,
622 detail: "()",
623 documentation: Documentation(
624 "variant docs",
625 ),
626 },
627 CompletionItem {
628 label: "my",
629 source_range: 10..12,
630 delete: 10..12,
631 insert: "my",
632 kind: Module,
633 documentation: Documentation(
634 "mod docs",
635 ),
636 },
637 ]
638 "#]],
639 )
640 }
641
642 #[test]
643 fn dont_render_attrs() {
644 check(
645 r#"
646struct S;
647impl S {
648 #[inline]
649 fn the_method(&self) { }
650}
651fn foo(s: S) { s.$0 }
652"#,
653 expect![[r#"
654 [
655 CompletionItem {
656 label: "the_method()",
657 source_range: 81..81,
658 delete: 81..81,
659 insert: "the_method()$0",
660 kind: Method,
661 lookup: "the_method",
662 detail: "fn the_method(&self)",
663 },
664 ]
665 "#]],
666 )
667 }
668
669 #[test]
670 fn no_call_parens_if_fn_ptr_needed() {
671 mark::check!(no_call_parens_if_fn_ptr_needed);
672 check_edit(
673 "foo",
674 r#"
675fn foo(foo: u8, bar: u8) {}
676struct ManualVtable { f: fn(u8, u8) }
677
678fn main() -> ManualVtable {
679 ManualVtable { f: f$0 }
680}
681"#,
682 r#"
683fn foo(foo: u8, bar: u8) {}
684struct ManualVtable { f: fn(u8, u8) }
685
686fn main() -> ManualVtable {
687 ManualVtable { f: foo }
688}
689"#,
690 );
691 }
692
693 #[test]
694 fn no_parens_in_use_item() {
695 mark::check!(no_parens_in_use_item);
696 check_edit(
697 "foo",
698 r#"
699mod m { pub fn foo() {} }
700use crate::m::f$0;
701"#,
702 r#"
703mod m { pub fn foo() {} }
704use crate::m::foo;
705"#,
706 );
707 }
708
709 #[test]
710 fn no_parens_in_call() {
711 check_edit(
712 "foo",
713 r#"
714fn foo(x: i32) {}
715fn main() { f$0(); }
716"#,
717 r#"
718fn foo(x: i32) {}
719fn main() { foo(); }
720"#,
721 );
722 check_edit(
723 "foo",
724 r#"
725struct Foo;
726impl Foo { fn foo(&self){} }
727fn f(foo: &Foo) { foo.f$0(); }
728"#,
729 r#"
730struct Foo;
731impl Foo { fn foo(&self){} }
732fn f(foo: &Foo) { foo.foo(); }
733"#,
734 );
735 }
736
737 #[test]
738 fn inserts_angle_brackets_for_generics() {
739 mark::check!(inserts_angle_brackets_for_generics);
740 check_edit(
741 "Vec",
742 r#"
743struct Vec<T> {}
744fn foo(xs: Ve$0)
745"#,
746 r#"
747struct Vec<T> {}
748fn foo(xs: Vec<$0>)
749"#,
750 );
751 check_edit(
752 "Vec",
753 r#"
754type Vec<T> = (T,);
755fn foo(xs: Ve$0)
756"#,
757 r#"
758type Vec<T> = (T,);
759fn foo(xs: Vec<$0>)
760"#,
761 );
762 check_edit(
763 "Vec",
764 r#"
765struct Vec<T = i128> {}
766fn foo(xs: Ve$0)
767"#,
768 r#"
769struct Vec<T = i128> {}
770fn foo(xs: Vec)
771"#,
772 );
773 check_edit(
774 "Vec",
775 r#"
776struct Vec<T> {}
777fn foo(xs: Ve$0<i128>)
778"#,
779 r#"
780struct Vec<T> {}
781fn foo(xs: Vec<i128>)
782"#,
783 );
784 }
785
786 #[test]
787 fn active_param_score() {
788 mark::check!(active_param_type_match);
789 check_scores(
790 r#"
791struct S { foo: i64, bar: u32, baz: u32 }
792fn test(bar: u32) { }
793fn foo(s: S) { test(s.$0) }
794"#,
795 expect![[r#"
796 fd bar [type+name]
797 fd baz [type]
798 fd foo []
799 "#]],
800 );
801 }
802
803 #[test]
804 fn record_field_scores() {
805 mark::check!(record_field_type_match);
806 check_scores(
807 r#"
808struct A { foo: i64, bar: u32, baz: u32 }
809struct B { x: (), y: f32, bar: u32 }
810fn foo(a: A) { B { bar: a.$0 }; }
811"#,
812 expect![[r#"
813 fd bar [type+name]
814 fd baz [type]
815 fd foo []
816 "#]],
817 )
818 }
819
820 #[test]
821 fn record_field_and_call_scores() {
822 check_scores(
823 r#"
824struct A { foo: i64, bar: u32, baz: u32 }
825struct B { x: (), y: f32, bar: u32 }
826fn f(foo: i64) { }
827fn foo(a: A) { B { bar: f(a.$0) }; }
828"#,
829 expect![[r#"
830 fd foo [type+name]
831 fd bar []
832 fd baz []
833 "#]],
834 );
835 check_scores(
836 r#"
837struct A { foo: i64, bar: u32, baz: u32 }
838struct B { x: (), y: f32, bar: u32 }
839fn f(foo: i64) { }
840fn foo(a: A) { f(B { bar: a.$0 }); }
841"#,
842 expect![[r#"
843 fd bar [type+name]
844 fd baz [type]
845 fd foo []
846 "#]],
847 );
848 }
849
850 #[test]
851 fn prioritize_exact_ref_match() {
852 check_scores(
853 r#"
854struct WorldSnapshot { _f: () };
855fn go(world: &WorldSnapshot) { go(w$0) }
856"#,
857 expect![[r#"
858 bn world [type+name]
859 st WorldSnapshot []
860 fn go(…) []
861 "#]],
862 );
863 }
864
865 #[test]
866 fn too_many_arguments() {
867 check_scores(
868 r#"
869struct Foo;
870fn f(foo: &Foo) { f(foo, w$0) }
871"#,
872 expect![[r#"
873 st Foo []
874 fn f(…) []
875 bn foo []
876 "#]],
877 );
878 }
879}