aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_completion/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_completion/src')
-rw-r--r--crates/ide_completion/src/completions/pattern.rs116
-rw-r--r--crates/ide_completion/src/completions/postfix/format_like.rs2
-rw-r--r--crates/ide_completion/src/completions/unqualified_path.rs88
-rw-r--r--crates/ide_completion/src/context.rs295
-rw-r--r--crates/ide_completion/src/item.rs37
-rw-r--r--crates/ide_completion/src/render.rs424
-rw-r--r--crates/ide_completion/src/render/enum_variant.rs13
-rw-r--r--crates/ide_completion/src/render/function.rs20
-rw-r--r--crates/ide_completion/src/render/pattern.rs36
9 files changed, 825 insertions, 206 deletions
diff --git a/crates/ide_completion/src/completions/pattern.rs b/crates/ide_completion/src/completions/pattern.rs
index 9282c3827..46cef58f0 100644
--- a/crates/ide_completion/src/completions/pattern.rs
+++ b/crates/ide_completion/src/completions/pattern.rs
@@ -11,10 +11,13 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
11 return; 11 return;
12 } 12 }
13 13
14 if let Some(ty) = &ctx.expected_type { 14 if !ctx.is_irrefutable_pat_binding {
15 super::complete_enum_variants(acc, ctx, ty, |acc, ctx, variant, path| { 15 if let Some(ty) = ctx.expected_type.as_ref() {
16 acc.add_qualified_variant_pat(ctx, variant, path) 16 super::complete_enum_variants(acc, ctx, ty, |acc, ctx, variant, path| {
17 }); 17 acc.add_qualified_variant_pat(ctx, variant, path.clone());
18 acc.add_qualified_enum_variant(ctx, variant, path);
19 });
20 }
18 } 21 }
19 22
20 // FIXME: ideally, we should look at the type we are matching against and 23 // FIXME: ideally, we should look at the type we are matching against and
@@ -85,7 +88,7 @@ static FOO: E = E::X;
85struct Bar { f: u32 } 88struct Bar { f: u32 }
86 89
87fn foo() { 90fn foo() {
88 match E::X { $0 } 91 match E::X { a$0 }
89} 92}
90"#, 93"#,
91 expect![[r#" 94 expect![[r#"
@@ -106,10 +109,11 @@ macro_rules! m { ($e:expr) => { $e } }
106enum E { X } 109enum E { X }
107 110
108fn foo() { 111fn foo() {
109 m!(match E::X { $0 }) 112 m!(match E::X { a$0 })
110} 113}
111"#, 114"#,
112 expect![[r#" 115 expect![[r#"
116 ev E::X ()
113 en E 117 en E
114 ma m!(…) macro_rules! m 118 ma m!(…) macro_rules! m
115 "#]], 119 "#]],
@@ -129,7 +133,7 @@ static FOO: E = E::X;
129struct Bar { f: u32 } 133struct Bar { f: u32 }
130 134
131fn foo() { 135fn foo() {
132 let $0 136 let a$0
133} 137}
134"#, 138"#,
135 expect![[r#" 139 expect![[r#"
@@ -147,7 +151,7 @@ enum E { X }
147static FOO: E = E::X; 151static FOO: E = E::X;
148struct Bar { f: u32 } 152struct Bar { f: u32 }
149 153
150fn foo($0) { 154fn foo(a$0) {
151} 155}
152"#, 156"#,
153 expect![[r#" 157 expect![[r#"
@@ -163,7 +167,7 @@ fn foo($0) {
163struct Bar { f: u32 } 167struct Bar { f: u32 }
164 168
165fn foo() { 169fn foo() {
166 let $0 170 let a$0
167} 171}
168"#, 172"#,
169 expect![[r#" 173 expect![[r#"
@@ -179,7 +183,7 @@ fn foo() {
179struct Foo { bar: String, baz: String } 183struct Foo { bar: String, baz: String }
180struct Bar(String, String); 184struct Bar(String, String);
181struct Baz; 185struct Baz;
182fn outer($0) {} 186fn outer(a$0) {}
183"#, 187"#,
184 expect![[r#" 188 expect![[r#"
185 bn Foo Foo { bar$1, baz$2 }: Foo$0 189 bn Foo Foo { bar$1, baz$2 }: Foo$0
@@ -196,7 +200,7 @@ struct Foo { bar: String, baz: String }
196struct Bar(String, String); 200struct Bar(String, String);
197struct Baz; 201struct Baz;
198fn outer() { 202fn outer() {
199 let $0 203 let a$0
200} 204}
201"#, 205"#,
202 expect![[r#" 206 expect![[r#"
@@ -215,7 +219,7 @@ struct Bar(String, String);
215struct Baz; 219struct Baz;
216fn outer() { 220fn outer() {
217 match () { 221 match () {
218 $0 222 a$0
219 } 223 }
220} 224}
221"#, 225"#,
@@ -239,7 +243,7 @@ use foo::*;
239 243
240fn outer() { 244fn outer() {
241 match () { 245 match () {
242 $0 246 a$0
243 } 247 }
244} 248}
245"#, 249"#,
@@ -258,7 +262,7 @@ fn outer() {
258struct Foo(i32); 262struct Foo(i32);
259fn main() { 263fn main() {
260 match Foo(92) { 264 match Foo(92) {
261 $0(92) => (), 265 a$0(92) => (),
262 } 266 }
263} 267}
264"#, 268"#,
@@ -281,7 +285,7 @@ struct Foo(i32);
281impl Foo { 285impl Foo {
282 fn foo() { 286 fn foo() {
283 match () { 287 match () {
284 $0 288 a$0
285 } 289 }
286 } 290 }
287} 291}
@@ -314,4 +318,86 @@ impl Foo {
314 "#]], 318 "#]],
315 ) 319 )
316 } 320 }
321
322 #[test]
323 fn completes_enum_variant_matcharm() {
324 check(
325 r#"
326enum Foo { Bar, Baz, Quux }
327
328fn main() {
329 let foo = Foo::Quux;
330 match foo { Qu$0 }
331}
332"#,
333 expect![[r#"
334 ev Foo::Bar ()
335 ev Foo::Baz ()
336 ev Foo::Quux ()
337 en Foo
338 "#]],
339 )
340 }
341
342 #[test]
343 fn completes_enum_variant_matcharm_ref() {
344 check(
345 r#"
346enum Foo { Bar, Baz, Quux }
347
348fn main() {
349 let foo = Foo::Quux;
350 match &foo { Qu$0 }
351}
352"#,
353 expect![[r#"
354 ev Foo::Bar ()
355 ev Foo::Baz ()
356 ev Foo::Quux ()
357 en Foo
358 "#]],
359 )
360 }
361
362 #[test]
363 fn completes_enum_variant_iflet() {
364 check(
365 r#"
366enum Foo { Bar, Baz, Quux }
367
368fn main() {
369 let foo = Foo::Quux;
370 if let Qu$0 = foo { }
371}
372"#,
373 expect![[r#"
374 ev Foo::Bar ()
375 ev Foo::Baz ()
376 ev Foo::Quux ()
377 en Foo
378 "#]],
379 )
380 }
381
382 #[test]
383 fn completes_enum_variant_impl() {
384 check(
385 r#"
386enum Foo { Bar, Baz, Quux }
387impl Foo {
388 fn foo() { match Foo::Bar { Q$0 } }
389}
390"#,
391 expect![[r#"
392 ev Self::Bar ()
393 ev Self::Baz ()
394 ev Self::Quux ()
395 ev Foo::Bar ()
396 ev Foo::Baz ()
397 ev Foo::Quux ()
398 sp Self
399 en Foo
400 "#]],
401 )
402 }
317} 403}
diff --git a/crates/ide_completion/src/completions/postfix/format_like.rs b/crates/ide_completion/src/completions/postfix/format_like.rs
index 3afc63021..cee4eec10 100644
--- a/crates/ide_completion/src/completions/postfix/format_like.rs
+++ b/crates/ide_completion/src/completions/postfix/format_like.rs
@@ -59,7 +59,7 @@ pub(crate) fn add_format_like_completions(
59/// Checks whether provided item is a string literal. 59/// Checks whether provided item is a string literal.
60fn string_literal_contents(item: &ast::String) -> Option<String> { 60fn string_literal_contents(item: &ast::String) -> Option<String> {
61 let item = item.text(); 61 let item = item.text();
62 if item.len() >= 2 && item.starts_with("\"") && item.ends_with("\"") { 62 if item.len() >= 2 && item.starts_with('\"') && item.ends_with('\"') {
63 return Some(item[1..item.len() - 1].to_owned()); 63 return Some(item[1..item.len() - 1].to_owned());
64 } 64 }
65 65
diff --git a/crates/ide_completion/src/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs
index e4bf4a043..1b8b063e7 100644
--- a/crates/ide_completion/src/completions/unqualified_path.rs
+++ b/crates/ide_completion/src/completions/unqualified_path.rs
@@ -6,7 +6,7 @@ use syntax::AstNode;
6use crate::{CompletionContext, Completions}; 6use crate::{CompletionContext, Completions};
7 7
8pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { 8pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) {
9 if !(ctx.is_trivial_path || ctx.is_pat_binding_or_const) { 9 if !ctx.is_trivial_path {
10 return; 10 return;
11 } 11 }
12 if ctx.record_lit_syntax.is_some() 12 if ctx.record_lit_syntax.is_some()
@@ -23,10 +23,6 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
23 }); 23 });
24 } 24 }
25 25
26 if ctx.is_pat_binding_or_const {
27 return;
28 }
29
30 ctx.scope.process_all_names(&mut |name, res| { 26 ctx.scope.process_all_names(&mut |name, res| {
31 if let ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_)) = res { 27 if let ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_)) = res {
32 cov_mark::hit!(skip_lifetime_completion); 28 cov_mark::hit!(skip_lifetime_completion);
@@ -609,66 +605,6 @@ fn main() { $0 }
609 } 605 }
610 606
611 #[test] 607 #[test]
612 fn completes_enum_variant_matcharm() {
613 check(
614 r#"
615enum Foo { Bar, Baz, Quux }
616
617fn main() {
618 let foo = Foo::Quux;
619 match foo { Qu$0 }
620}
621"#,
622 expect![[r#"
623 ev Foo::Bar ()
624 ev Foo::Baz ()
625 ev Foo::Quux ()
626 en Foo
627 "#]],
628 )
629 }
630
631 #[test]
632 fn completes_enum_variant_matcharm_ref() {
633 check(
634 r#"
635enum Foo { Bar, Baz, Quux }
636
637fn main() {
638 let foo = Foo::Quux;
639 match &foo { Qu$0 }
640}
641"#,
642 expect![[r#"
643 ev Foo::Bar ()
644 ev Foo::Baz ()
645 ev Foo::Quux ()
646 en Foo
647 "#]],
648 )
649 }
650
651 #[test]
652 fn completes_enum_variant_iflet() {
653 check(
654 r#"
655enum Foo { Bar, Baz, Quux }
656
657fn main() {
658 let foo = Foo::Quux;
659 if let Qu$0 = foo { }
660}
661"#,
662 expect![[r#"
663 ev Foo::Bar ()
664 ev Foo::Baz ()
665 ev Foo::Quux ()
666 en Foo
667 "#]],
668 )
669 }
670
671 #[test]
672 fn completes_enum_variant_basic_expr() { 608 fn completes_enum_variant_basic_expr() {
673 check( 609 check(
674 r#" 610 r#"
@@ -701,28 +637,6 @@ fn f() -> m::E { V$0 }
701 } 637 }
702 638
703 #[test] 639 #[test]
704 fn completes_enum_variant_impl() {
705 check(
706 r#"
707enum Foo { Bar, Baz, Quux }
708impl Foo {
709 fn foo() { match Foo::Bar { Q$0 } }
710}
711"#,
712 expect![[r#"
713 ev Self::Bar ()
714 ev Self::Baz ()
715 ev Self::Quux ()
716 ev Foo::Bar ()
717 ev Foo::Baz ()
718 ev Foo::Quux ()
719 sp Self
720 en Foo
721 "#]],
722 )
723 }
724
725 #[test]
726 fn dont_complete_attr() { 640 fn dont_complete_attr() {
727 check( 641 check(
728 r#" 642 r#"
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs
index 17d9a3adf..e6cc6329c 100644
--- a/crates/ide_completion/src/context.rs
+++ b/crates/ide_completion/src/context.rs
@@ -35,6 +35,7 @@ pub(crate) struct CompletionContext<'a> {
35 /// The token before the cursor, in the macro-expanded file. 35 /// The token before the cursor, in the macro-expanded file.
36 pub(super) token: SyntaxToken, 36 pub(super) token: SyntaxToken,
37 pub(super) krate: Option<hir::Crate>, 37 pub(super) krate: Option<hir::Crate>,
38 pub(super) expected_name: Option<String>,
38 pub(super) expected_type: Option<Type>, 39 pub(super) expected_type: Option<Type>,
39 pub(super) name_ref_syntax: Option<ast::NameRef>, 40 pub(super) name_ref_syntax: Option<ast::NameRef>,
40 pub(super) function_syntax: Option<ast::Fn>, 41 pub(super) function_syntax: Option<ast::Fn>,
@@ -135,6 +136,7 @@ impl<'a> CompletionContext<'a> {
135 original_token, 136 original_token,
136 token, 137 token,
137 krate, 138 krate,
139 expected_name: None,
138 expected_type: None, 140 expected_type: None,
139 name_ref_syntax: None, 141 name_ref_syntax: None,
140 function_syntax: None, 142 function_syntax: None,
@@ -290,23 +292,95 @@ impl<'a> CompletionContext<'a> {
290 file_with_fake_ident: SyntaxNode, 292 file_with_fake_ident: SyntaxNode,
291 offset: TextSize, 293 offset: TextSize,
292 ) { 294 ) {
293 // FIXME: this is wrong in at least two cases: 295 let expected = {
294 // * when there's no token `foo($0)` 296 let mut node = self.token.parent();
295 // * when there is a token, but it happens to have type of it's own 297 loop {
296 self.expected_type = self 298 let ret = match_ast! {
297 .token
298 .ancestors()
299 .find_map(|node| {
300 let ty = match_ast! {
301 match node { 299 match node {
302 ast::Pat(it) => self.sema.type_of_pat(&it), 300 ast::LetStmt(it) => {
303 ast::Expr(it) => self.sema.type_of_expr(&it), 301 cov_mark::hit!(expected_type_let_with_leading_char);
304 _ => return None, 302 cov_mark::hit!(expected_type_let_without_leading_char);
303 let ty = it.pat()
304 .and_then(|pat| self.sema.type_of_pat(&pat));
305 let name = if let Some(ast::Pat::IdentPat(ident)) = it.pat() {
306 Some(ident.syntax().text().to_string())
307 } else {
308 None
309 };
310
311 (ty, name)
312 },
313 ast::ArgList(it) => {
314 cov_mark::hit!(expected_type_fn_param_with_leading_char);
315 cov_mark::hit!(expected_type_fn_param_without_leading_char);
316 ActiveParameter::at_token(
317 &self.sema,
318 self.token.clone(),
319 ).map(|ap| (Some(ap.ty), Some(ap.name)))
320 .unwrap_or((None, None))
321 },
322 ast::RecordExprFieldList(it) => {
323 cov_mark::hit!(expected_type_struct_field_without_leading_char);
324 self.token.prev_sibling_or_token()
325 .and_then(|se| se.into_node())
326 .and_then(|node| ast::RecordExprField::cast(node))
327 .and_then(|rf| self.sema.resolve_record_field(&rf))
328 .map(|f|(
329 Some(f.0.signature_ty(self.db)),
330 Some(f.0.name(self.db).to_string()),
331 ))
332 .unwrap_or((None, None))
333 },
334 ast::RecordExprField(it) => {
335 cov_mark::hit!(expected_type_struct_field_with_leading_char);
336 self.sema
337 .resolve_record_field(&it)
338 .map(|f|(
339 Some(f.0.signature_ty(self.db)),
340 Some(f.0.name(self.db).to_string()),
341 ))
342 .unwrap_or((None, None))
343 },
344 ast::MatchExpr(it) => {
345 cov_mark::hit!(expected_type_match_arm_without_leading_char);
346 let ty = it.expr()
347 .and_then(|e| self.sema.type_of_expr(&e));
348
349 (ty, None)
350 },
351 ast::IdentPat(it) => {
352 cov_mark::hit!(expected_type_if_let_with_leading_char);
353 cov_mark::hit!(expected_type_match_arm_with_leading_char);
354 let ty = self.sema.type_of_pat(&ast::Pat::from(it));
355
356 (ty, None)
357 },
358 ast::Fn(it) => {
359 cov_mark::hit!(expected_type_fn_ret_with_leading_char);
360 cov_mark::hit!(expected_type_fn_ret_without_leading_char);
361 let ty = self.token.ancestors()
362 .find_map(|ancestor| ast::Expr::cast(ancestor))
363 .and_then(|expr| self.sema.type_of_expr(&expr));
364
365 (ty, None)
366 },
367 _ => {
368 match node.parent() {
369 Some(n) => {
370 node = n;
371 continue;
372 },
373 None => (None, None),
374 }
375 },
305 } 376 }
306 }; 377 };
307 Some(ty) 378
308 }) 379 break ret;
309 .flatten(); 380 }
381 };
382 self.expected_type = expected.0;
383 self.expected_name = expected.1;
310 self.attribute_under_caret = find_node_at_offset(&file_with_fake_ident, offset); 384 self.attribute_under_caret = find_node_at_offset(&file_with_fake_ident, offset);
311 385
312 // First, let's try to complete a reference to some declaration. 386 // First, let's try to complete a reference to some declaration.
@@ -535,3 +609,196 @@ fn path_or_use_tree_qualifier(path: &ast::Path) -> Option<ast::Path> {
535 let use_tree = use_tree_list.syntax().parent().and_then(ast::UseTree::cast)?; 609 let use_tree = use_tree_list.syntax().parent().and_then(ast::UseTree::cast)?;
536 use_tree.path() 610 use_tree.path()
537} 611}
612
613#[cfg(test)]
614mod tests {
615 use expect_test::{expect, Expect};
616 use hir::HirDisplay;
617
618 use crate::test_utils::{position, TEST_CONFIG};
619
620 use super::CompletionContext;
621
622 fn check_expected_type_and_name(ra_fixture: &str, expect: Expect) {
623 let (db, pos) = position(ra_fixture);
624 let completion_context = CompletionContext::new(&db, pos, &TEST_CONFIG).unwrap();
625
626 let ty = completion_context
627 .expected_type
628 .map(|t| t.display_test(&db).to_string())
629 .unwrap_or("?".to_owned());
630
631 let name = completion_context.expected_name.unwrap_or("?".to_owned());
632
633 expect.assert_eq(&format!("ty: {}, name: {}", ty, name));
634 }
635
636 #[test]
637 fn expected_type_let_without_leading_char() {
638 cov_mark::check!(expected_type_let_without_leading_char);
639 check_expected_type_and_name(
640 r#"
641fn foo() {
642 let x: u32 = $0;
643}
644"#,
645 expect![[r#"ty: u32, name: x"#]],
646 );
647 }
648
649 #[test]
650 fn expected_type_let_with_leading_char() {
651 cov_mark::check!(expected_type_let_with_leading_char);
652 check_expected_type_and_name(
653 r#"
654fn foo() {
655 let x: u32 = c$0;
656}
657"#,
658 expect![[r#"ty: u32, name: x"#]],
659 );
660 }
661
662 #[test]
663 fn expected_type_fn_param_without_leading_char() {
664 cov_mark::check!(expected_type_fn_param_without_leading_char);
665 check_expected_type_and_name(
666 r#"
667fn foo() {
668 bar($0);
669}
670
671fn bar(x: u32) {}
672"#,
673 expect![[r#"ty: u32, name: x"#]],
674 );
675 }
676
677 #[test]
678 fn expected_type_fn_param_with_leading_char() {
679 cov_mark::check!(expected_type_fn_param_with_leading_char);
680 check_expected_type_and_name(
681 r#"
682fn foo() {
683 bar(c$0);
684}
685
686fn bar(x: u32) {}
687"#,
688 expect![[r#"ty: u32, name: x"#]],
689 );
690 }
691
692 #[test]
693 fn expected_type_struct_field_without_leading_char() {
694 cov_mark::check!(expected_type_struct_field_without_leading_char);
695 check_expected_type_and_name(
696 r#"
697struct Foo { a: u32 }
698fn foo() {
699 Foo { a: $0 };
700}
701"#,
702 expect![[r#"ty: u32, name: a"#]],
703 )
704 }
705
706 #[test]
707 fn expected_type_struct_field_with_leading_char() {
708 cov_mark::check!(expected_type_struct_field_with_leading_char);
709 check_expected_type_and_name(
710 r#"
711struct Foo { a: u32 }
712fn foo() {
713 Foo { a: c$0 };
714}
715"#,
716 expect![[r#"ty: u32, name: a"#]],
717 );
718 }
719
720 #[test]
721 fn expected_type_match_arm_without_leading_char() {
722 cov_mark::check!(expected_type_match_arm_without_leading_char);
723 check_expected_type_and_name(
724 r#"
725enum E { X }
726fn foo() {
727 match E::X { $0 }
728}
729"#,
730 expect![[r#"ty: E, name: ?"#]],
731 );
732 }
733
734 #[test]
735 fn expected_type_match_arm_with_leading_char() {
736 cov_mark::check!(expected_type_match_arm_with_leading_char);
737 check_expected_type_and_name(
738 r#"
739enum E { X }
740fn foo() {
741 match E::X { c$0 }
742}
743"#,
744 expect![[r#"ty: E, name: ?"#]],
745 );
746 }
747
748 #[test]
749 fn expected_type_if_let_without_leading_char() {
750 check_expected_type_and_name(
751 r#"
752enum Foo { Bar, Baz, Quux }
753
754fn foo() {
755 let f = Foo::Quux;
756 if let $0 = f { }
757}
758"#,
759 expect![[r#"ty: (), name: ?"#]],
760 ) // FIXME should be `ty: u32, name: ?`
761 }
762
763 #[test]
764 fn expected_type_if_let_with_leading_char() {
765 cov_mark::check!(expected_type_if_let_with_leading_char);
766 check_expected_type_and_name(
767 r#"
768enum Foo { Bar, Baz, Quux }
769
770fn foo() {
771 let f = Foo::Quux;
772 if let c$0 = f { }
773}
774"#,
775 expect![[r#"ty: Foo, name: ?"#]],
776 )
777 }
778
779 #[test]
780 fn expected_type_fn_ret_without_leading_char() {
781 cov_mark::check!(expected_type_fn_ret_without_leading_char);
782 check_expected_type_and_name(
783 r#"
784fn foo() -> u32 {
785 $0
786}
787"#,
788 expect![[r#"ty: (), name: ?"#]],
789 ) // FIXME this should be `ty: u32, name: ?`
790 }
791
792 #[test]
793 fn expected_type_fn_ret_with_leading_char() {
794 cov_mark::check!(expected_type_fn_ret_with_leading_char);
795 check_expected_type_and_name(
796 r#"
797fn foo() -> u32 {
798 c$0
799}
800"#,
801 expect![[r#"ty: u32, name: ?"#]],
802 )
803 }
804}
diff --git a/crates/ide_completion/src/item.rs b/crates/ide_completion/src/item.rs
index 3febab32b..9a4b5217a 100644
--- a/crates/ide_completion/src/item.rs
+++ b/crates/ide_completion/src/item.rs
@@ -144,6 +144,21 @@ pub struct CompletionRelevance {
144 /// } 144 /// }
145 /// ``` 145 /// ```
146 pub exact_type_match: bool, 146 pub exact_type_match: bool,
147 /// This is set in cases like these:
148 ///
149 /// ```
150 /// fn foo(bar: u32) {
151 /// $0 // `bar` is local
152 /// }
153 /// ```
154 ///
155 /// ```
156 /// fn foo() {
157 /// let bar = 0;
158 /// $0 // `bar` is local
159 /// }
160 /// ```
161 pub is_local: bool,
147} 162}
148 163
149impl CompletionRelevance { 164impl CompletionRelevance {
@@ -163,6 +178,9 @@ impl CompletionRelevance {
163 score += 1; 178 score += 1;
164 } 179 }
165 if self.exact_type_match { 180 if self.exact_type_match {
181 score += 3;
182 }
183 if self.is_local {
166 score += 1; 184 score += 1;
167 } 185 }
168 186
@@ -551,9 +569,24 @@ mod tests {
551 vec![CompletionRelevance::default()], 569 vec![CompletionRelevance::default()],
552 vec![ 570 vec![
553 CompletionRelevance { exact_name_match: true, ..CompletionRelevance::default() }, 571 CompletionRelevance { exact_name_match: true, ..CompletionRelevance::default() },
554 CompletionRelevance { exact_type_match: true, ..CompletionRelevance::default() }, 572 CompletionRelevance { is_local: true, ..CompletionRelevance::default() },
555 ], 573 ],
556 vec![CompletionRelevance { exact_name_match: true, exact_type_match: true }], 574 vec![CompletionRelevance {
575 exact_name_match: true,
576 is_local: true,
577 ..CompletionRelevance::default()
578 }],
579 vec![CompletionRelevance { exact_type_match: true, ..CompletionRelevance::default() }],
580 vec![CompletionRelevance {
581 exact_name_match: true,
582 exact_type_match: true,
583 ..CompletionRelevance::default()
584 }],
585 vec![CompletionRelevance {
586 exact_name_match: true,
587 exact_type_match: true,
588 is_local: true,
589 }],
557 ]; 590 ];
558 591
559 check_relevance_score_ordered(expected_relevance_order); 592 check_relevance_score_ordered(expected_relevance_order);
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs
index d9bf52582..36655667c 100644
--- a/crates/ide_completion/src/render.rs
+++ b/crates/ide_completion/src/render.rs
@@ -20,8 +20,8 @@ use ide_db::{
20use syntax::TextRange; 20use syntax::TextRange;
21 21
22use crate::{ 22use crate::{
23 item::{CompletionRelevance, ImportEdit}, 23 item::ImportEdit, CompletionContext, CompletionItem, CompletionItemKind, CompletionKind,
24 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, 24 CompletionRelevance,
25}; 25};
26 26
27use crate::render::{enum_variant::render_variant, function::render_fn, macro_::render_macro}; 27use crate::render::{enum_variant::render_variant, function::render_fn, macro_::render_macro};
@@ -116,19 +116,6 @@ impl<'a> RenderContext<'a> {
116 fn docs(&self, node: impl HasAttrs) -> Option<Documentation> { 116 fn docs(&self, node: impl HasAttrs) -> Option<Documentation> {
117 node.docs(self.db()) 117 node.docs(self.db())
118 } 118 }
119
120 fn expected_name_and_type(&self) -> Option<(String, Type)> {
121 if let Some(record_field) = &self.completion.record_field_syntax {
122 cov_mark::hit!(record_field_type_match);
123 let (struct_field, _local) = self.completion.sema.resolve_record_field(record_field)?;
124 Some((struct_field.name(self.db()).to_string(), struct_field.signature_ty(self.db())))
125 } else if let Some(active_parameter) = &self.completion.active_parameter {
126 cov_mark::hit!(active_param_type_match);
127 Some((active_parameter.name.clone(), active_parameter.ty.clone()))
128 } else {
129 None
130 }
131 }
132} 119}
133 120
134/// Generic renderer for completion items. 121/// Generic renderer for completion items.
@@ -155,8 +142,14 @@ impl<'a> Render<'a> {
155 .set_documentation(field.docs(self.ctx.db())) 142 .set_documentation(field.docs(self.ctx.db()))
156 .set_deprecated(is_deprecated); 143 .set_deprecated(is_deprecated);
157 144
158 if let Some(relevance) = compute_relevance(&self.ctx, &ty, &name.to_string()) { 145 item.set_relevance(CompletionRelevance {
159 item.set_relevance(relevance); 146 exact_type_match: compute_exact_type_match(self.ctx.completion, ty),
147 exact_name_match: compute_exact_name_match(self.ctx.completion, name.to_string()),
148 ..CompletionRelevance::default()
149 });
150
151 if let Some(ref_match) = compute_ref_match(self.ctx.completion, ty) {
152 item.ref_match(ref_match);
160 } 153 }
161 154
162 item.build() 155 item.build()
@@ -247,27 +240,18 @@ impl<'a> Render<'a> {
247 if !ty.is_unknown() { 240 if !ty.is_unknown() {
248 item.detail(ty.display(self.ctx.db()).to_string()); 241 item.detail(ty.display(self.ctx.db()).to_string());
249 } 242 }
250 };
251 243
252 if let ScopeDef::Local(local) = resolution { 244 item.set_relevance(CompletionRelevance {
253 let ty = local.ty(self.ctx.db()); 245 exact_type_match: compute_exact_type_match(self.ctx.completion, &ty),
254 if let Some(relevance) = compute_relevance(&self.ctx, &ty, &local_name) { 246 exact_name_match: compute_exact_name_match(self.ctx.completion, local_name.clone()),
255 item.set_relevance(relevance); 247 is_local: true,
256 } 248 ..CompletionRelevance::default()
257 if let Some((_expected_name, expected_type)) = self.ctx.expected_name_and_type() { 249 });
258 if let Some(ty_without_ref) = expected_type.remove_ref() { 250
259 if ty_without_ref == ty { 251 if let Some(ref_match) = compute_ref_match(self.ctx.completion, &ty) {
260 cov_mark::hit!(suggest_ref); 252 item.ref_match(ref_match);
261 let mutability = if expected_type.is_mutable_reference() {
262 Mutability::Mut
263 } else {
264 Mutability::Shared
265 };
266 item.ref_match(mutability);
267 }
268 }
269 } 253 }
270 } 254 };
271 255
272 // Add `<>` for generic types 256 // Add `<>` for generic types
273 if self.ctx.completion.is_path_type 257 if self.ctx.completion.is_path_type
@@ -322,19 +306,50 @@ impl<'a> Render<'a> {
322 } 306 }
323} 307}
324 308
325fn compute_relevance(ctx: &RenderContext, ty: &Type, name: &str) -> Option<CompletionRelevance> { 309fn compute_exact_type_match(ctx: &CompletionContext, completion_ty: &hir::Type) -> bool {
326 let (expected_name, expected_type) = ctx.expected_name_and_type()?; 310 if let Some(expected_type) = ctx.expected_type.as_ref() {
327 let mut res = CompletionRelevance::default(); 311 // We don't ever consider unit type to be an exact type match, since
328 res.exact_type_match = ty == &expected_type; 312 // nearly always this is not meaningful to the user.
329 res.exact_name_match = name == &expected_name; 313 completion_ty == expected_type && !expected_type.is_unit()
330 Some(res) 314 } else {
315 false
316 }
317}
318
319fn compute_exact_name_match(ctx: &CompletionContext, completion_name: impl Into<String>) -> bool {
320 let completion_name = completion_name.into();
321
322 Some(&completion_name) == ctx.expected_name.as_ref()
323}
324
325fn compute_ref_match(ctx: &CompletionContext, completion_ty: &hir::Type) -> Option<Mutability> {
326 let mut ref_match = None;
327 if let Some(expected_type) = &ctx.expected_type {
328 if completion_ty != expected_type {
329 if let Some(expected_type_without_ref) = expected_type.remove_ref() {
330 if completion_ty == &expected_type_without_ref
331 || completion_ty
332 .autoderef(ctx.db)
333 .any(|deref_ty| deref_ty == expected_type_without_ref)
334 {
335 cov_mark::hit!(suggest_ref);
336 let mutability = if expected_type.is_mutable_reference() {
337 Mutability::Mut
338 } else {
339 Mutability::Shared
340 };
341 ref_match = Some(mutability);
342 }
343 }
344 }
345 };
346 ref_match
331} 347}
332 348
333#[cfg(test)] 349#[cfg(test)]
334mod tests { 350mod tests {
335 use std::cmp::Reverse;
336
337 use expect_test::{expect, Expect}; 351 use expect_test::{expect, Expect};
352 use itertools::Itertools;
338 353
339 use crate::{ 354 use crate::{
340 test_utils::{check_edit, do_completion, get_all_items, TEST_CONFIG}, 355 test_utils::{check_edit, do_completion, get_all_items, TEST_CONFIG},
@@ -347,28 +362,40 @@ mod tests {
347 } 362 }
348 363
349 fn check_relevance(ra_fixture: &str, expect: Expect) { 364 fn check_relevance(ra_fixture: &str, expect: Expect) {
350 fn display_relevance(relevance: CompletionRelevance) -> &'static str { 365 fn display_relevance(relevance: CompletionRelevance) -> String {
351 match relevance { 366 let relevance_factors = vec![
352 CompletionRelevance { exact_type_match: true, exact_name_match: true } => { 367 (relevance.exact_type_match, "type"),
353 "[type+name]" 368 (relevance.exact_name_match, "name"),
354 } 369 (relevance.is_local, "local"),
355 CompletionRelevance { exact_type_match: true, exact_name_match: false } => "[type]", 370 ]
356 CompletionRelevance { exact_type_match: false, exact_name_match: true } => "[name]", 371 .into_iter()
357 CompletionRelevance { exact_type_match: false, exact_name_match: false } => "[]", 372 .filter_map(|(cond, desc)| if cond { Some(desc) } else { None })
358 } 373 .join("+");
374
375 format!("[{}]", relevance_factors)
359 } 376 }
360 377
361 let mut completions = get_all_items(TEST_CONFIG, ra_fixture); 378 let actual = get_all_items(TEST_CONFIG, ra_fixture)
362 completions.sort_by_key(|it| (Reverse(it.relevance()), it.label().to_string()));
363 let actual = completions
364 .into_iter() 379 .into_iter()
365 .filter(|it| it.completion_kind == CompletionKind::Reference) 380 .filter(|it| it.completion_kind == CompletionKind::Reference)
366 .map(|it| { 381 .flat_map(|it| {
382 let mut items = vec![];
383
367 let tag = it.kind().unwrap().tag(); 384 let tag = it.kind().unwrap().tag();
368 let relevance = display_relevance(it.relevance()); 385 let relevance = display_relevance(it.relevance());
369 format!("{} {} {}\n", tag, it.label(), relevance) 386 items.push(format!("{} {} {}\n", tag, it.label(), relevance));
387
388 if let Some((mutability, relevance)) = it.ref_match() {
389 let label = format!("&{}{}", mutability.as_keyword_for_ref(), it.label());
390 let relevance = display_relevance(relevance);
391
392 items.push(format!("{} {} {}\n", tag, label, relevance));
393 }
394
395 items
370 }) 396 })
371 .collect::<String>(); 397 .collect::<String>();
398
372 expect.assert_eq(&actual); 399 expect.assert_eq(&actual);
373 } 400 }
374 401
@@ -508,6 +535,11 @@ fn main() { let _: m::Spam = S$0 }
508 ), 535 ),
509 lookup: "Spam::Bar", 536 lookup: "Spam::Bar",
510 detail: "(i32)", 537 detail: "(i32)",
538 relevance: CompletionRelevance {
539 exact_name_match: false,
540 exact_type_match: true,
541 is_local: false,
542 },
511 trigger_call_info: true, 543 trigger_call_info: true,
512 }, 544 },
513 CompletionItem { 545 CompletionItem {
@@ -529,6 +561,11 @@ fn main() { let _: m::Spam = S$0 }
529 ), 561 ),
530 lookup: "Spam::Foo", 562 lookup: "Spam::Foo",
531 detail: "()", 563 detail: "()",
564 relevance: CompletionRelevance {
565 exact_name_match: false,
566 exact_type_match: true,
567 is_local: false,
568 },
532 }, 569 },
533 CompletionItem { 570 CompletionItem {
534 label: "main()", 571 label: "main()",
@@ -868,7 +905,6 @@ fn foo(xs: Vec<i128>)
868 905
869 #[test] 906 #[test]
870 fn active_param_relevance() { 907 fn active_param_relevance() {
871 cov_mark::check!(active_param_type_match);
872 check_relevance( 908 check_relevance(
873 r#" 909 r#"
874struct S { foo: i64, bar: u32, baz: u32 } 910struct S { foo: i64, bar: u32, baz: u32 }
@@ -876,16 +912,15 @@ fn test(bar: u32) { }
876fn foo(s: S) { test(s.$0) } 912fn foo(s: S) { test(s.$0) }
877"#, 913"#,
878 expect![[r#" 914 expect![[r#"
915 fd foo []
879 fd bar [type+name] 916 fd bar [type+name]
880 fd baz [type] 917 fd baz [type]
881 fd foo []
882 "#]], 918 "#]],
883 ); 919 );
884 } 920 }
885 921
886 #[test] 922 #[test]
887 fn record_field_relevances() { 923 fn record_field_relevances() {
888 cov_mark::check!(record_field_type_match);
889 check_relevance( 924 check_relevance(
890 r#" 925 r#"
891struct A { foo: i64, bar: u32, baz: u32 } 926struct A { foo: i64, bar: u32, baz: u32 }
@@ -893,9 +928,9 @@ struct B { x: (), y: f32, bar: u32 }
893fn foo(a: A) { B { bar: a.$0 }; } 928fn foo(a: A) { B { bar: a.$0 }; }
894"#, 929"#,
895 expect![[r#" 930 expect![[r#"
931 fd foo []
896 fd bar [type+name] 932 fd bar [type+name]
897 fd baz [type] 933 fd baz [type]
898 fd foo []
899 "#]], 934 "#]],
900 ) 935 )
901 } 936 }
@@ -923,9 +958,9 @@ fn f(foo: i64) { }
923fn foo(a: A) { f(B { bar: a.$0 }); } 958fn foo(a: A) { f(B { bar: a.$0 }); }
924"#, 959"#,
925 expect![[r#" 960 expect![[r#"
961 fd foo []
926 fd bar [type+name] 962 fd bar [type+name]
927 fd baz [type] 963 fd baz [type]
928 fd foo []
929 "#]], 964 "#]],
930 ); 965 );
931 } 966 }
@@ -938,7 +973,7 @@ struct WorldSnapshot { _f: () };
938fn go(world: &WorldSnapshot) { go(w$0) } 973fn go(world: &WorldSnapshot) { go(w$0) }
939"#, 974"#,
940 expect![[r#" 975 expect![[r#"
941 lc world [type+name] 976 lc world [type+name+local]
942 st WorldSnapshot [] 977 st WorldSnapshot []
943 fn go(…) [] 978 fn go(…) []
944 "#]], 979 "#]],
@@ -953,9 +988,69 @@ struct Foo;
953fn f(foo: &Foo) { f(foo, w$0) } 988fn f(foo: &Foo) { f(foo, w$0) }
954"#, 989"#,
955 expect![[r#" 990 expect![[r#"
991 lc foo [local]
956 st Foo [] 992 st Foo []
957 fn f(…) [] 993 fn f(…) []
958 lc foo [] 994 "#]],
995 );
996 }
997
998 #[test]
999 fn score_fn_type_and_name_match() {
1000 check_relevance(
1001 r#"
1002struct A { bar: u8 }
1003fn baz() -> u8 { 0 }
1004fn bar() -> u8 { 0 }
1005fn f() { A { bar: b$0 }; }
1006"#,
1007 expect![[r#"
1008 fn baz() [type]
1009 st A []
1010 fn bar() [type+name]
1011 fn f() []
1012 "#]],
1013 );
1014 }
1015
1016 #[test]
1017 fn score_method_type_and_name_match() {
1018 check_relevance(
1019 r#"
1020fn baz(aaa: u32){}
1021struct Foo;
1022impl Foo {
1023fn aaa(&self) -> u32 { 0 }
1024fn bbb(&self) -> u32 { 0 }
1025fn ccc(&self) -> u64 { 0 }
1026}
1027fn f() {
1028 baz(Foo.$0
1029}
1030"#,
1031 expect![[r#"
1032 me aaa() [type+name]
1033 me bbb() [type]
1034 me ccc() []
1035 "#]],
1036 );
1037 }
1038
1039 #[test]
1040 fn score_method_name_match_only() {
1041 check_relevance(
1042 r#"
1043fn baz(aaa: u32){}
1044struct Foo;
1045impl Foo {
1046fn aaa(&self) -> u64 { 0 }
1047}
1048fn f() {
1049 baz(Foo.$0
1050}
1051"#,
1052 expect![[r#"
1053 me aaa() [name]
959 "#]], 1054 "#]],
960 ); 1055 );
961 } 1056 }
@@ -1018,6 +1113,7 @@ fn main() {
1018 relevance: CompletionRelevance { 1113 relevance: CompletionRelevance {
1019 exact_name_match: true, 1114 exact_name_match: true,
1020 exact_type_match: false, 1115 exact_type_match: false,
1116 is_local: true,
1021 }, 1117 },
1022 ref_match: "&mut ", 1118 ref_match: "&mut ",
1023 }, 1119 },
@@ -1025,4 +1121,202 @@ fn main() {
1025 "#]], 1121 "#]],
1026 ) 1122 )
1027 } 1123 }
1124
1125 #[test]
1126 fn suggest_deref() {
1127 check_relevance(
1128 r#"
1129#[lang = "deref"]
1130trait Deref {
1131 type Target;
1132 fn deref(&self) -> &Self::Target;
1133}
1134
1135struct S;
1136struct T(S);
1137
1138impl Deref for T {
1139 type Target = S;
1140
1141 fn deref(&self) -> &Self::Target {
1142 &self.0
1143 }
1144}
1145
1146fn foo(s: &S) {}
1147
1148fn main() {
1149 let t = T(S);
1150 let m = 123;
1151
1152 foo($0);
1153}
1154 "#,
1155 expect![[r#"
1156 lc m [local]
1157 lc t [local]
1158 lc &t [type+local]
1159 st T []
1160 st S []
1161 fn main() []
1162 tt Deref []
1163 fn foo(…) []
1164 "#]],
1165 )
1166 }
1167
1168 #[test]
1169 fn suggest_deref_mut() {
1170 check_relevance(
1171 r#"
1172#[lang = "deref"]
1173trait Deref {
1174 type Target;
1175 fn deref(&self) -> &Self::Target;
1176}
1177
1178#[lang = "deref_mut"]
1179pub trait DerefMut: Deref {
1180 fn deref_mut(&mut self) -> &mut Self::Target;
1181}
1182
1183struct S;
1184struct T(S);
1185
1186impl Deref for T {
1187 type Target = S;
1188
1189 fn deref(&self) -> &Self::Target {
1190 &self.0
1191 }
1192}
1193
1194impl DerefMut for T {
1195 fn deref_mut(&mut self) -> &mut Self::Target {
1196 &mut self.0
1197 }
1198}
1199
1200fn foo(s: &mut S) {}
1201
1202fn main() {
1203 let t = T(S);
1204 let m = 123;
1205
1206 foo($0);
1207}
1208 "#,
1209 expect![[r#"
1210 lc m [local]
1211 lc t [local]
1212 lc &mut t [type+local]
1213 tt DerefMut []
1214 tt Deref []
1215 fn foo(…) []
1216 st T []
1217 st S []
1218 fn main() []
1219 "#]],
1220 )
1221 }
1222
1223 #[test]
1224 fn locals() {
1225 check_relevance(
1226 r#"
1227fn foo(bar: u32) {
1228 let baz = 0;
1229
1230 f$0
1231}
1232"#,
1233 expect![[r#"
1234 lc baz [local]
1235 lc bar [local]
1236 fn foo(…) []
1237 "#]],
1238 );
1239 }
1240
1241 #[test]
1242 fn enum_owned() {
1243 check_relevance(
1244 r#"
1245enum Foo { A, B }
1246fn foo() {
1247 bar($0);
1248}
1249fn bar(t: Foo) {}
1250"#,
1251 expect![[r#"
1252 ev Foo::A [type]
1253 ev Foo::B [type]
1254 en Foo []
1255 fn bar(…) []
1256 fn foo() []
1257 "#]],
1258 );
1259 }
1260
1261 #[test]
1262 fn enum_ref() {
1263 check_relevance(
1264 r#"
1265enum Foo { A, B }
1266fn foo() {
1267 bar($0);
1268}
1269fn bar(t: &Foo) {}
1270"#,
1271 expect![[r#"
1272 ev Foo::A []
1273 ev &Foo::A [type]
1274 ev Foo::B []
1275 ev &Foo::B [type]
1276 en Foo []
1277 fn bar(…) []
1278 fn foo() []
1279 "#]],
1280 );
1281 }
1282
1283 #[test]
1284 fn suggest_deref_fn_ret() {
1285 check_relevance(
1286 r#"
1287#[lang = "deref"]
1288trait Deref {
1289 type Target;
1290 fn deref(&self) -> &Self::Target;
1291}
1292
1293struct S;
1294struct T(S);
1295
1296impl Deref for T {
1297 type Target = S;
1298
1299 fn deref(&self) -> &Self::Target {
1300 &self.0
1301 }
1302}
1303
1304fn foo(s: &S) {}
1305fn bar() -> T {}
1306
1307fn main() {
1308 foo($0);
1309}
1310 "#,
1311 expect![[r#"
1312 tt Deref []
1313 fn bar() []
1314 fn &bar() [type]
1315 fn foo(…) []
1316 st T []
1317 st S []
1318 fn main() []
1319 "#]],
1320 )
1321 }
1028} 1322}
diff --git a/crates/ide_completion/src/render/enum_variant.rs b/crates/ide_completion/src/render/enum_variant.rs
index e8cfcc0c7..374247b05 100644
--- a/crates/ide_completion/src/render/enum_variant.rs
+++ b/crates/ide_completion/src/render/enum_variant.rs
@@ -6,7 +6,8 @@ use itertools::Itertools;
6 6
7use crate::{ 7use crate::{
8 item::{CompletionItem, CompletionKind, ImportEdit}, 8 item::{CompletionItem, CompletionKind, ImportEdit},
9 render::{builder_ext::Params, RenderContext}, 9 render::{builder_ext::Params, compute_exact_type_match, compute_ref_match, RenderContext},
10 CompletionRelevance,
10}; 11};
11 12
12pub(crate) fn render_variant<'a>( 13pub(crate) fn render_variant<'a>(
@@ -74,6 +75,16 @@ impl<'a> EnumRender<'a> {
74 item.lookup_by(self.short_qualified_name); 75 item.lookup_by(self.short_qualified_name);
75 } 76 }
76 77
78 let ty = self.variant.parent_enum(self.ctx.completion.db).ty(self.ctx.completion.db);
79 item.set_relevance(CompletionRelevance {
80 exact_type_match: compute_exact_type_match(self.ctx.completion, &ty),
81 ..CompletionRelevance::default()
82 });
83
84 if let Some(ref_match) = compute_ref_match(self.ctx.completion, &ty) {
85 item.ref_match(ref_match);
86 }
87
77 item.build() 88 item.build()
78 } 89 }
79 90
diff --git a/crates/ide_completion/src/render/function.rs b/crates/ide_completion/src/render/function.rs
index b6b67e7a7..010303182 100644
--- a/crates/ide_completion/src/render/function.rs
+++ b/crates/ide_completion/src/render/function.rs
@@ -6,8 +6,11 @@ use itertools::Itertools;
6use syntax::ast::Fn; 6use syntax::ast::Fn;
7 7
8use crate::{ 8use crate::{
9 item::{CompletionItem, CompletionItemKind, CompletionKind, ImportEdit}, 9 item::{CompletionItem, CompletionItemKind, CompletionKind, CompletionRelevance, ImportEdit},
10 render::{builder_ext::Params, RenderContext}, 10 render::{
11 builder_ext::Params, compute_exact_name_match, compute_exact_type_match, compute_ref_match,
12 RenderContext,
13 },
11}; 14};
12 15
13pub(crate) fn render_fn<'a>( 16pub(crate) fn render_fn<'a>(
@@ -53,9 +56,20 @@ impl<'a> FunctionRender<'a> {
53 self.ctx.is_deprecated(self.func) || self.ctx.is_deprecated_assoc_item(self.func), 56 self.ctx.is_deprecated(self.func) || self.ctx.is_deprecated_assoc_item(self.func),
54 ) 57 )
55 .detail(self.detail()) 58 .detail(self.detail())
56 .add_call_parens(self.ctx.completion, self.name, params) 59 .add_call_parens(self.ctx.completion, self.name.clone(), params)
57 .add_import(import_to_add); 60 .add_import(import_to_add);
58 61
62 let ret_type = self.func.ret_type(self.ctx.db());
63 item.set_relevance(CompletionRelevance {
64 exact_type_match: compute_exact_type_match(self.ctx.completion, &ret_type),
65 exact_name_match: compute_exact_name_match(self.ctx.completion, self.name.clone()),
66 ..CompletionRelevance::default()
67 });
68
69 if let Some(ref_match) = compute_ref_match(self.ctx.completion, &ret_type) {
70 item.ref_match(ref_match);
71 }
72
59 item.build() 73 item.build()
60 } 74 }
61 75
diff --git a/crates/ide_completion/src/render/pattern.rs b/crates/ide_completion/src/render/pattern.rs
index ca2926125..b4e80f424 100644
--- a/crates/ide_completion/src/render/pattern.rs
+++ b/crates/ide_completion/src/render/pattern.rs
@@ -6,24 +6,6 @@ use itertools::Itertools;
6 6
7use crate::{item::CompletionKind, render::RenderContext, CompletionItem, CompletionItemKind}; 7use crate::{item::CompletionKind, render::RenderContext, CompletionItem, CompletionItemKind};
8 8
9fn visible_fields(
10 ctx: &RenderContext<'_>,
11 fields: &[hir::Field],
12 item: impl HasAttrs,
13) -> Option<(Vec<hir::Field>, bool)> {
14 let module = ctx.completion.scope.module()?;
15 let n_fields = fields.len();
16 let fields = fields
17 .into_iter()
18 .filter(|field| field.is_visible_from(ctx.db(), module))
19 .copied()
20 .collect::<Vec<_>>();
21
22 let fields_omitted =
23 n_fields - fields.len() > 0 || item.attrs(ctx.db()).by_key("non_exhaustive").exists();
24 Some((fields, fields_omitted))
25}
26
27pub(crate) fn render_struct_pat( 9pub(crate) fn render_struct_pat(
28 ctx: RenderContext<'_>, 10 ctx: RenderContext<'_>,
29 strukt: hir::Struct, 11 strukt: hir::Struct,
@@ -148,3 +130,21 @@ fn render_tuple_as_pat(fields: &[hir::Field], name: &str, fields_omitted: bool)
148 name = name 130 name = name
149 ) 131 )
150} 132}
133
134fn visible_fields(
135 ctx: &RenderContext<'_>,
136 fields: &[hir::Field],
137 item: impl HasAttrs,
138) -> Option<(Vec<hir::Field>, bool)> {
139 let module = ctx.completion.scope.module()?;
140 let n_fields = fields.len();
141 let fields = fields
142 .into_iter()
143 .filter(|field| field.is_visible_from(ctx.db(), module))
144 .copied()
145 .collect::<Vec<_>>();
146
147 let fields_omitted =
148 n_fields - fields.len() > 0 || item.attrs(ctx.db()).by_key("non_exhaustive").exists();
149 Some((fields, fields_omitted))
150}