aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_completion/src/completions/dot.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_completion/src/completions/dot.rs')
-rw-r--r--crates/ide_completion/src/completions/dot.rs84
1 files changed, 75 insertions, 9 deletions
diff --git a/crates/ide_completion/src/completions/dot.rs b/crates/ide_completion/src/completions/dot.rs
index fd9738743..302c9ccbd 100644
--- a/crates/ide_completion/src/completions/dot.rs
+++ b/crates/ide_completion/src/completions/dot.rs
@@ -1,6 +1,7 @@
1//! Completes references after dot (fields and method calls). 1//! Completes references after dot (fields and method calls).
2 2
3use hir::{HasVisibility, Type}; 3use either::Either;
4use hir::{HasVisibility, ScopeDef};
4use rustc_hash::FxHashSet; 5use rustc_hash::FxHashSet;
5 6
6use crate::{context::CompletionContext, Completions}; 7use crate::{context::CompletionContext, Completions};
@@ -9,7 +10,7 @@ use crate::{context::CompletionContext, Completions};
9pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { 10pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
10 let dot_receiver = match &ctx.dot_receiver { 11 let dot_receiver = match &ctx.dot_receiver {
11 Some(expr) => expr, 12 Some(expr) => expr,
12 _ => return, 13 _ => return complete_undotted_self(acc, ctx),
13 }; 14 };
14 15
15 let receiver_ty = match ctx.sema.type_of_expr(&dot_receiver) { 16 let receiver_ty = match ctx.sema.type_of_expr(&dot_receiver) {
@@ -20,12 +21,43 @@ pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
20 if ctx.is_call { 21 if ctx.is_call {
21 cov_mark::hit!(test_no_struct_field_completion_for_method_call); 22 cov_mark::hit!(test_no_struct_field_completion_for_method_call);
22 } else { 23 } else {
23 complete_fields(acc, ctx, &receiver_ty); 24 complete_fields(ctx, &receiver_ty, |field, ty| match field {
25 Either::Left(field) => acc.add_field(ctx, None, field, &ty),
26 Either::Right(tuple_idx) => acc.add_tuple_field(ctx, None, tuple_idx, &ty),
27 });
24 } 28 }
25 complete_methods(acc, ctx, &receiver_ty); 29 complete_methods(ctx, &receiver_ty, |func| acc.add_method(ctx, func, None, None));
30}
31
32fn complete_undotted_self(acc: &mut Completions, ctx: &CompletionContext) {
33 if !ctx.is_trivial_path || !ctx.config.enable_self_on_the_fly {
34 return;
35 }
36 ctx.scope.process_all_names(&mut |name, def| {
37 if let ScopeDef::Local(local) = &def {
38 if local.is_self(ctx.db) {
39 let ty = local.ty(ctx.db);
40 complete_fields(ctx, &ty, |field, ty| match field {
41 either::Either::Left(field) => {
42 acc.add_field(ctx, Some(name.clone()), field, &ty)
43 }
44 either::Either::Right(tuple_idx) => {
45 acc.add_tuple_field(ctx, Some(name.clone()), tuple_idx, &ty)
46 }
47 });
48 complete_methods(ctx, &ty, |func| {
49 acc.add_method(ctx, func, Some(name.clone()), None)
50 });
51 }
52 }
53 });
26} 54}
27 55
28fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) { 56fn complete_fields(
57 ctx: &CompletionContext,
58 receiver: &hir::Type,
59 mut f: impl FnMut(Either<hir::Field, usize>, hir::Type),
60) {
29 for receiver in receiver.autoderef(ctx.db) { 61 for receiver in receiver.autoderef(ctx.db) {
30 for (field, ty) in receiver.fields(ctx.db) { 62 for (field, ty) in receiver.fields(ctx.db) {
31 if ctx.scope.module().map_or(false, |m| !field.is_visible_from(ctx.db, m)) { 63 if ctx.scope.module().map_or(false, |m| !field.is_visible_from(ctx.db, m)) {
@@ -33,16 +65,20 @@ fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Ty
33 // field is editable, we should show the completion 65 // field is editable, we should show the completion
34 continue; 66 continue;
35 } 67 }
36 acc.add_field(ctx, field, &ty); 68 f(Either::Left(field), ty);
37 } 69 }
38 for (i, ty) in receiver.tuple_fields(ctx.db).into_iter().enumerate() { 70 for (i, ty) in receiver.tuple_fields(ctx.db).into_iter().enumerate() {
39 // FIXME: Handle visibility 71 // FIXME: Handle visibility
40 acc.add_tuple_field(ctx, i, &ty); 72 f(Either::Right(i), ty);
41 } 73 }
42 } 74 }
43} 75}
44 76
45fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) { 77fn complete_methods(
78 ctx: &CompletionContext,
79 receiver: &hir::Type,
80 mut f: impl FnMut(hir::Function),
81) {
46 if let Some(krate) = ctx.krate { 82 if let Some(krate) = ctx.krate {
47 let mut seen_methods = FxHashSet::default(); 83 let mut seen_methods = FxHashSet::default();
48 let traits_in_scope = ctx.scope.traits_in_scope(); 84 let traits_in_scope = ctx.scope.traits_in_scope();
@@ -51,7 +87,7 @@ fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &T
51 && ctx.scope.module().map_or(true, |m| func.is_visible_from(ctx.db, m)) 87 && ctx.scope.module().map_or(true, |m| func.is_visible_from(ctx.db, m))
52 && seen_methods.insert(func.name(ctx.db)) 88 && seen_methods.insert(func.name(ctx.db))
53 { 89 {
54 acc.add_method(ctx, func, None); 90 f(func);
55 } 91 }
56 None::<()> 92 None::<()>
57 }); 93 });
@@ -484,4 +520,34 @@ impl S {
484 "#]], 520 "#]],
485 ); 521 );
486 } 522 }
523
524 #[test]
525 fn completes_bare_fields_and_methods_in_methods() {
526 check(
527 r#"
528struct Foo { field: i32 }
529
530impl Foo { fn foo(&self) { $0 } }"#,
531 expect![[r#"
532 lc self &Foo
533 sp Self
534 st Foo
535 fd self.field i32
536 me self.foo() fn(&self)
537 "#]],
538 );
539 check(
540 r#"
541struct Foo(i32);
542
543impl Foo { fn foo(&mut self) { $0 } }"#,
544 expect![[r#"
545 lc self &mut Foo
546 sp Self
547 st Foo
548 fd self.0 i32
549 me self.foo() fn(&mut self)
550 "#]],
551 );
552 }
487} 553}