From ca49fbe0a1f6acc1352f6628c36bb7dfe3a950e5 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 28 May 2021 14:02:53 +0200 Subject: Complete `self.` prefixed fields and methods inside methods --- crates/ide_completion/src/completions/dot.rs | 41 +++--------------- crates/ide_completion/src/completions/record.rs | 2 +- .../src/completions/unqualified_path.rs | 48 ++++++++++++++++++++++ 3 files changed, 54 insertions(+), 37 deletions(-) (limited to 'crates/ide_completion/src/completions') diff --git a/crates/ide_completion/src/completions/dot.rs b/crates/ide_completion/src/completions/dot.rs index fd9738743..93f7bd6d4 100644 --- a/crates/ide_completion/src/completions/dot.rs +++ b/crates/ide_completion/src/completions/dot.rs @@ -1,7 +1,6 @@ //! Completes references after dot (fields and method calls). -use hir::{HasVisibility, Type}; -use rustc_hash::FxHashSet; +use either::Either; use crate::{context::CompletionContext, Completions}; @@ -20,42 +19,12 @@ pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { if ctx.is_call { cov_mark::hit!(test_no_struct_field_completion_for_method_call); } else { - complete_fields(acc, ctx, &receiver_ty); - } - complete_methods(acc, ctx, &receiver_ty); -} - -fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) { - for receiver in receiver.autoderef(ctx.db) { - for (field, ty) in receiver.fields(ctx.db) { - if ctx.scope.module().map_or(false, |m| !field.is_visible_from(ctx.db, m)) { - // Skip private field. FIXME: If the definition location of the - // field is editable, we should show the completion - continue; - } - acc.add_field(ctx, field, &ty); - } - for (i, ty) in receiver.tuple_fields(ctx.db).into_iter().enumerate() { - // FIXME: Handle visibility - acc.add_tuple_field(ctx, i, &ty); - } - } -} - -fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) { - if let Some(krate) = ctx.krate { - let mut seen_methods = FxHashSet::default(); - let traits_in_scope = ctx.scope.traits_in_scope(); - receiver.iterate_method_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, func| { - if func.self_param(ctx.db).is_some() - && ctx.scope.module().map_or(true, |m| func.is_visible_from(ctx.db, m)) - && seen_methods.insert(func.name(ctx.db)) - { - acc.add_method(ctx, func, None); - } - None::<()> + super::complete_fields(ctx, &receiver_ty, |field, ty| match field { + Either::Left(field) => acc.add_field(ctx, None, field, &ty), + Either::Right(tuple_idx) => acc.add_tuple_field(ctx, None, tuple_idx, &ty), }); } + super::complete_methods(ctx, &receiver_ty, |func| acc.add_method(ctx, func, None, None)); } #[cfg(test)] diff --git a/crates/ide_completion/src/completions/record.rs b/crates/ide_completion/src/completions/record.rs index 227c08d01..0ac47cdbe 100644 --- a/crates/ide_completion/src/completions/record.rs +++ b/crates/ide_completion/src/completions/record.rs @@ -39,7 +39,7 @@ pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> }; for (field, ty) in missing_fields { - acc.add_field(ctx, field, &ty); + acc.add_field(ctx, None, field, &ty); } Some(()) diff --git a/crates/ide_completion/src/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs index 9db8516d0..573a39996 100644 --- a/crates/ide_completion/src/completions/unqualified_path.rs +++ b/crates/ide_completion/src/completions/unqualified_path.rs @@ -11,6 +11,7 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC if ctx.is_path_disallowed() || ctx.expects_item() { return; } + if ctx.expects_assoc_item() { ctx.scope.process_all_names(&mut |name, def| { if let ScopeDef::MacroDef(macro_def) = def { @@ -32,6 +33,7 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC }); return; } + if let Some(hir::Adt::Enum(e)) = ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt()) { @@ -45,6 +47,22 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC cov_mark::hit!(skip_lifetime_completion); return; } + if let ScopeDef::Local(local) = &res { + if local.is_self(ctx.db) { + let ty = local.ty(ctx.db); + super::complete_fields(ctx, &ty, |field, ty| match field { + either::Either::Left(field) => { + acc.add_field(ctx, Some(name.to_string()), field, &ty) + } + either::Either::Right(tuple_idx) => { + acc.add_tuple_field(ctx, Some(name.to_string()), tuple_idx, &ty) + } + }); + super::complete_methods(ctx, &ty, |func| { + acc.add_method(ctx, func, Some(name.to_string()), None) + }); + } + } acc.add_resolution(ctx, name, &res); }); } @@ -375,6 +393,36 @@ fn foo() { ); } + #[test] + fn completes_qualified_fields_and_methods_in_methods() { + check( + r#" +struct Foo { field: i32 } + +impl Foo { fn foo(&self) { $0 } }"#, + expect![[r#" + fd self.field i32 + me self.foo() fn(&self) + lc self &Foo + sp Self + st Foo + "#]], + ); + check( + r#" +struct Foo(i32); + +impl Foo { fn foo(&mut self) { $0 } }"#, + expect![[r#" + fd self.0 i32 + me self.foo() fn(&mut self) + lc self &mut Foo + sp Self + st Foo + "#]], + ); + } + #[test] fn completes_prelude() { check( -- cgit v1.2.3