From 4507382f2e66cd0e6498228bfdffb16769063b0f Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 28 May 2021 15:09:10 +0200 Subject: Move unprefixed field/method completion to `dot` --- crates/ide_completion/src/completions.rs | 45 +-------- crates/ide_completion/src/completions/dot.rs | 103 ++++++++++++++++++++- .../src/completions/unqualified_path.rs | 46 --------- 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; use std::iter; -use either::Either; -use hir::{known, HasVisibility}; +use hir::known; use ide_db::SymbolKind; -use rustc_hash::FxHashSet; use crate::{ item::{Builder, CompletionKind}, @@ -254,44 +252,3 @@ fn complete_enum_variants( } } } - -fn complete_fields( - ctx: &CompletionContext, - receiver: &hir::Type, - mut f: impl FnMut(Either, hir::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; - } - f(Either::Left(field), ty); - } - for (i, ty) in receiver.tuple_fields(ctx.db).into_iter().enumerate() { - // FIXME: Handle visibility - f(Either::Right(i), ty); - } - } -} - -fn complete_methods( - ctx: &CompletionContext, - receiver: &hir::Type, - mut f: impl FnMut(hir::Function), -) { - 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)) - { - f(func); - } - None::<()> - }); - } -} 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 @@ //! Completes references after dot (fields and method calls). use either::Either; +use hir::{HasVisibility, ScopeDef}; +use rustc_hash::FxHashSet; use crate::{context::CompletionContext, Completions}; @@ -8,7 +10,7 @@ use crate::{context::CompletionContext, Completions}; pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { let dot_receiver = match &ctx.dot_receiver { Some(expr) => expr, - _ => return, + _ => return complete_undotted_self(acc, ctx), }; 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) { if ctx.is_call { cov_mark::hit!(test_no_struct_field_completion_for_method_call); } else { - super::complete_fields(ctx, &receiver_ty, |field, ty| match field { + 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)); + complete_methods(ctx, &receiver_ty, |func| acc.add_method(ctx, func, None, None)); +} + +fn complete_undotted_self(acc: &mut Completions, ctx: &CompletionContext) { + if !ctx.is_trivial_path { + return; + } + ctx.scope.process_all_names(&mut |name, def| { + if let ScopeDef::Local(local) = &def { + if local.is_self(ctx.db) { + let ty = local.ty(ctx.db); + complete_fields(ctx, &ty, |field, ty| match field { + either::Either::Left(field) => { + acc.add_field(ctx, Some(name.clone()), field, &ty) + } + either::Either::Right(tuple_idx) => { + acc.add_tuple_field(ctx, Some(name.clone()), tuple_idx, &ty) + } + }); + complete_methods(ctx, &ty, |func| { + acc.add_method(ctx, func, Some(name.clone()), None) + }); + } + } + }); +} + +fn complete_fields( + ctx: &CompletionContext, + receiver: &hir::Type, + mut f: impl FnMut(Either, hir::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; + } + f(Either::Left(field), ty); + } + for (i, ty) in receiver.tuple_fields(ctx.db).into_iter().enumerate() { + // FIXME: Handle visibility + f(Either::Right(i), ty); + } + } +} + +fn complete_methods( + ctx: &CompletionContext, + receiver: &hir::Type, + mut f: impl FnMut(hir::Function), +) { + 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)) + { + f(func); + } + None::<()> + }); + } } #[cfg(test)] @@ -453,4 +520,34 @@ impl S { "#]], ); } + + #[test] + fn completes_bare_fields_and_methods_in_methods() { + check( + r#" +struct Foo { field: i32 } + +impl Foo { fn foo(&self) { $0 } }"#, + expect![[r#" + lc self &Foo + sp Self + st Foo + fd self.field i32 + me self.foo() fn(&self) + "#]], + ); + check( + r#" +struct Foo(i32); + +impl Foo { fn foo(&mut self) { $0 } }"#, + expect![[r#" + lc self &mut Foo + sp Self + st Foo + fd self.0 i32 + me self.foo() fn(&mut self) + "#]], + ); + } } 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 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.clone()), field, &ty) - } - either::Either::Right(tuple_idx) => { - acc.add_tuple_field(ctx, Some(name.clone()), tuple_idx, &ty) - } - }); - super::complete_methods(ctx, &ty, |func| { - acc.add_method(ctx, func, Some(name.clone()), None) - }); - } - } acc.add_resolution(ctx, name, &res); }); } @@ -393,36 +377,6 @@ 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