aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ide_completion/src/completions.rs45
-rw-r--r--crates/ide_completion/src/completions/dot.rs103
-rw-r--r--crates/ide_completion/src/completions/unqualified_path.rs46
3 files changed, 101 insertions, 93 deletions
diff --git a/crates/ide_completion/src/completions.rs b/crates/ide_completion/src/completions.rs
index dd92bc510..ffdcdc930 100644
--- a/crates/ide_completion/src/completions.rs
+++ b/crates/ide_completion/src/completions.rs
@@ -18,10 +18,8 @@ pub(crate) mod unqualified_path;
18 18
19use std::iter; 19use std::iter;
20 20
21use either::Either; 21use hir::known;
22use hir::{known, HasVisibility};
23use ide_db::SymbolKind; 22use ide_db::SymbolKind;
24use rustc_hash::FxHashSet;
25 23
26use crate::{ 24use crate::{
27 item::{Builder, CompletionKind}, 25 item::{Builder, CompletionKind},
@@ -254,44 +252,3 @@ fn complete_enum_variants(
254 } 252 }
255 } 253 }
256} 254}
257
258fn complete_fields(
259 ctx: &CompletionContext,
260 receiver: &hir::Type,
261 mut f: impl FnMut(Either<hir::Field, usize>, hir::Type),
262) {
263 for receiver in receiver.autoderef(ctx.db) {
264 for (field, ty) in receiver.fields(ctx.db) {
265 if ctx.scope.module().map_or(false, |m| !field.is_visible_from(ctx.db, m)) {
266 // Skip private field. FIXME: If the definition location of the
267 // field is editable, we should show the completion
268 continue;
269 }
270 f(Either::Left(field), ty);
271 }
272 for (i, ty) in receiver.tuple_fields(ctx.db).into_iter().enumerate() {
273 // FIXME: Handle visibility
274 f(Either::Right(i), ty);
275 }
276 }
277}
278
279fn complete_methods(
280 ctx: &CompletionContext,
281 receiver: &hir::Type,
282 mut f: impl FnMut(hir::Function),
283) {
284 if let Some(krate) = ctx.krate {
285 let mut seen_methods = FxHashSet::default();
286 let traits_in_scope = ctx.scope.traits_in_scope();
287 receiver.iterate_method_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, func| {
288 if func.self_param(ctx.db).is_some()
289 && ctx.scope.module().map_or(true, |m| func.is_visible_from(ctx.db, m))
290 && seen_methods.insert(func.name(ctx.db))
291 {
292 f(func);
293 }
294 None::<()>
295 });
296 }
297}
diff --git a/crates/ide_completion/src/completions/dot.rs b/crates/ide_completion/src/completions/dot.rs
index 93f7bd6d4..886251639 100644
--- a/crates/ide_completion/src/completions/dot.rs
+++ b/crates/ide_completion/src/completions/dot.rs
@@ -1,6 +1,8 @@
1//! Completes references after dot (fields and method calls). 1//! Completes references after dot (fields and method calls).
2 2
3use either::Either; 3use either::Either;
4use hir::{HasVisibility, ScopeDef};
5use rustc_hash::FxHashSet;
4 6
5use crate::{context::CompletionContext, Completions}; 7use crate::{context::CompletionContext, Completions};
6 8
@@ -8,7 +10,7 @@ use crate::{context::CompletionContext, Completions};
8pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { 10pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
9 let dot_receiver = match &ctx.dot_receiver { 11 let dot_receiver = match &ctx.dot_receiver {
10 Some(expr) => expr, 12 Some(expr) => expr,
11 _ => return, 13 _ => return complete_undotted_self(acc, ctx),
12 }; 14 };
13 15
14 let receiver_ty = match ctx.sema.type_of_expr(&dot_receiver) { 16 let receiver_ty = match ctx.sema.type_of_expr(&dot_receiver) {
@@ -19,12 +21,77 @@ pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
19 if ctx.is_call { 21 if ctx.is_call {
20 cov_mark::hit!(test_no_struct_field_completion_for_method_call); 22 cov_mark::hit!(test_no_struct_field_completion_for_method_call);
21 } else { 23 } else {
22 super::complete_fields(ctx, &receiver_ty, |field, ty| match field { 24 complete_fields(ctx, &receiver_ty, |field, ty| match field {
23 Either::Left(field) => acc.add_field(ctx, None, field, &ty), 25 Either::Left(field) => acc.add_field(ctx, None, field, &ty),
24 Either::Right(tuple_idx) => acc.add_tuple_field(ctx, None, tuple_idx, &ty), 26 Either::Right(tuple_idx) => acc.add_tuple_field(ctx, None, tuple_idx, &ty),
25 }); 27 });
26 } 28 }
27 super::complete_methods(ctx, &receiver_ty, |func| acc.add_method(ctx, func, None, None)); 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 {
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 });
54}
55
56fn complete_fields(
57 ctx: &CompletionContext,
58 receiver: &hir::Type,
59 mut f: impl FnMut(Either<hir::Field, usize>, hir::Type),
60) {
61 for receiver in receiver.autoderef(ctx.db) {
62 for (field, ty) in receiver.fields(ctx.db) {
63 if ctx.scope.module().map_or(false, |m| !field.is_visible_from(ctx.db, m)) {
64 // Skip private field. FIXME: If the definition location of the
65 // field is editable, we should show the completion
66 continue;
67 }
68 f(Either::Left(field), ty);
69 }
70 for (i, ty) in receiver.tuple_fields(ctx.db).into_iter().enumerate() {
71 // FIXME: Handle visibility
72 f(Either::Right(i), ty);
73 }
74 }
75}
76
77fn complete_methods(
78 ctx: &CompletionContext,
79 receiver: &hir::Type,
80 mut f: impl FnMut(hir::Function),
81) {
82 if let Some(krate) = ctx.krate {
83 let mut seen_methods = FxHashSet::default();
84 let traits_in_scope = ctx.scope.traits_in_scope();
85 receiver.iterate_method_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, func| {
86 if func.self_param(ctx.db).is_some()
87 && ctx.scope.module().map_or(true, |m| func.is_visible_from(ctx.db, m))
88 && seen_methods.insert(func.name(ctx.db))
89 {
90 f(func);
91 }
92 None::<()>
93 });
94 }
28} 95}
29 96
30#[cfg(test)] 97#[cfg(test)]
@@ -453,4 +520,34 @@ impl S {
453 "#]], 520 "#]],
454 ); 521 );
455 } 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 }
456} 553}
diff --git a/crates/ide_completion/src/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs
index 83cb67101..20188a7dd 100644
--- a/crates/ide_completion/src/completions/unqualified_path.rs
+++ b/crates/ide_completion/src/completions/unqualified_path.rs
@@ -47,22 +47,6 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
47 cov_mark::hit!(skip_lifetime_completion); 47 cov_mark::hit!(skip_lifetime_completion);
48 return; 48 return;
49 } 49 }
50 if let ScopeDef::Local(local) = &res {
51 if local.is_self(ctx.db) {
52 let ty = local.ty(ctx.db);
53 super::complete_fields(ctx, &ty, |field, ty| match field {
54 either::Either::Left(field) => {
55 acc.add_field(ctx, Some(name.clone()), field, &ty)
56 }
57 either::Either::Right(tuple_idx) => {
58 acc.add_tuple_field(ctx, Some(name.clone()), tuple_idx, &ty)
59 }
60 });
61 super::complete_methods(ctx, &ty, |func| {
62 acc.add_method(ctx, func, Some(name.clone()), None)
63 });
64 }
65 }
66 acc.add_resolution(ctx, name, &res); 50 acc.add_resolution(ctx, name, &res);
67 }); 51 });
68} 52}
@@ -394,36 +378,6 @@ fn foo() {
394 } 378 }
395 379
396 #[test] 380 #[test]
397 fn completes_qualified_fields_and_methods_in_methods() {
398 check(
399 r#"
400struct Foo { field: i32 }
401
402impl Foo { fn foo(&self) { $0 } }"#,
403 expect![[r#"
404 fd self.field i32
405 me self.foo() fn(&self)
406 lc self &Foo
407 sp Self
408 st Foo
409 "#]],
410 );
411 check(
412 r#"
413struct Foo(i32);
414
415impl Foo { fn foo(&mut self) { $0 } }"#,
416 expect![[r#"
417 fd self.0 i32
418 me self.foo() fn(&mut self)
419 lc self &mut Foo
420 sp Self
421 st Foo
422 "#]],
423 );
424 }
425
426 #[test]
427 fn completes_prelude() { 381 fn completes_prelude() {
428 check( 382 check(
429 r#" 383 r#"