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