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