Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide-completion/src/completions/dot.rs')
| -rw-r--r-- | crates/ide-completion/src/completions/dot.rs | 125 |
1 files changed, 85 insertions, 40 deletions
diff --git a/crates/ide-completion/src/completions/dot.rs b/crates/ide-completion/src/completions/dot.rs index 5340d65a14..511b593857 100644 --- a/crates/ide-completion/src/completions/dot.rs +++ b/crates/ide-completion/src/completions/dot.rs @@ -2,7 +2,7 @@ use std::ops::ControlFlow; -use hir::{Complete, HasContainer, ItemContainer, MethodCandidateCallback, Name}; +use hir::{Complete, Function, HasContainer, ItemContainer, MethodCandidateCallback}; use ide_db::FxHashSet; use syntax::SmolStr; @@ -25,9 +25,7 @@ pub(crate) fn complete_dot( _ => return, }; - let is_field_access = matches!(dot_access.kind, DotAccessKind::Field { .. }); - let is_method_access_with_parens = - matches!(dot_access.kind, DotAccessKind::Method { has_parens: true }); + let has_parens = matches!(dot_access.kind, DotAccessKind::Method); let traits_in_scope = ctx.traits_in_scope(); // Suggest .await syntax for types that implement Future trait @@ -48,7 +46,7 @@ pub(crate) fn complete_dot( DotAccessKind::Field { receiver_is_ambiguous_float_literal: _ } => { DotAccessKind::Field { receiver_is_ambiguous_float_literal: false } } - it @ DotAccessKind::Method { .. } => *it, + it @ DotAccessKind::Method => *it, }; let dot_access = DotAccess { receiver: dot_access.receiver.clone(), @@ -67,8 +65,7 @@ pub(crate) fn complete_dot( acc.add_field(ctx, &dot_access, Some(await_str.clone()), field, &ty) }, |acc, field, ty| acc.add_tuple_field(ctx, Some(await_str.clone()), field, &ty), - is_field_access, - is_method_access_with_parens, + has_parens, ); complete_methods(ctx, &future_output, &traits_in_scope, |func| { acc.add_method(ctx, &dot_access, func, Some(await_str.clone()), None) @@ -82,8 +79,7 @@ pub(crate) fn complete_dot( receiver_ty, |acc, field, ty| acc.add_field(ctx, dot_access, None, field, &ty), |acc, field, ty| acc.add_tuple_field(ctx, None, field, &ty), - is_field_access, - is_method_access_with_parens, + has_parens, ); complete_methods(ctx, receiver_ty, &traits_in_scope, |func| { acc.add_method(ctx, dot_access, func, None, None) @@ -112,7 +108,7 @@ pub(crate) fn complete_dot( DotAccessKind::Field { receiver_is_ambiguous_float_literal: _ } => { DotAccessKind::Field { receiver_is_ambiguous_float_literal: false } } - it @ DotAccessKind::Method { .. } => *it, + it @ DotAccessKind::Method => *it, }; let dot_access = DotAccess { receiver: dot_access.receiver.clone(), @@ -173,7 +169,6 @@ pub(crate) fn complete_undotted_self( ) }, |acc, field, ty| acc.add_tuple_field(ctx, Some(SmolStr::new_static("self")), field, &ty), - true, false, ); complete_methods(ctx, &ty, &ctx.traits_in_scope(), |func| { @@ -182,7 +177,7 @@ pub(crate) fn complete_undotted_self( &DotAccess { receiver: None, receiver_ty: None, - kind: DotAccessKind::Method { has_parens: false }, + kind: DotAccessKind::Field { receiver_is_ambiguous_float_literal: false }, ctx: DotAccessExprCtx { in_block_expr: expr_ctx.in_block_expr, in_breakable: expr_ctx.in_breakable, @@ -201,15 +196,13 @@ fn complete_fields( receiver: &hir::Type<'_>, mut named_field: impl FnMut(&mut Completions, hir::Field, hir::Type<'_>), mut tuple_index: impl FnMut(&mut Completions, usize, hir::Type<'_>), - is_field_access: bool, - is_method_access_with_parens: bool, + has_parens: bool, ) { let mut seen_names = FxHashSet::default(); for receiver in receiver.autoderef(ctx.db) { for (field, ty) in receiver.fields(ctx.db) { if seen_names.insert(field.name(ctx.db)) - && (is_field_access - || (is_method_access_with_parens && (ty.is_fn() || ty.is_closure()))) + && (!has_parens || ty.is_fn() || ty.is_closure()) { named_field(acc, field, ty); } @@ -218,8 +211,7 @@ fn complete_fields( // Tuples are always the last type in a deref chain, so just check if the name is // already seen without inserting into the hashset. if !seen_names.contains(&hir::Name::new_tuple_field(i)) - && (is_field_access - || (is_method_access_with_parens && (ty.is_fn() || ty.is_closure()))) + && (!has_parens || ty.is_fn() || ty.is_closure()) { // Tuple fields are always public (tuple struct fields are handled above). tuple_index(acc, i, ty); @@ -237,7 +229,10 @@ fn complete_methods( struct Callback<'a, F> { ctx: &'a CompletionContext<'a>, f: F, - seen_methods: FxHashSet<Name>, + // We deliberately deduplicate by function ID and not name, because while inherent methods cannot be + // duplicated, trait methods can. And it is still useful to show all of them (even when there + // is also an inherent method, especially considering that it may be private, and filtered later). + seen_methods: FxHashSet<Function>, } impl<F> MethodCandidateCallback for Callback<'_, F> @@ -247,9 +242,7 @@ fn complete_methods( // We don't want to exclude inherent trait methods - that is, methods of traits available from // `where` clauses or `dyn Trait`. fn on_inherent_method(&mut self, func: hir::Function) -> ControlFlow<()> { - if func.self_param(self.ctx.db).is_some() - && self.seen_methods.insert(func.name(self.ctx.db)) - { + if func.self_param(self.ctx.db).is_some() && self.seen_methods.insert(func) { (self.f)(func); } ControlFlow::Continue(()) @@ -258,17 +251,14 @@ fn complete_methods( fn on_trait_method(&mut self, func: hir::Function) -> ControlFlow<()> { // This needs to come before the `seen_methods` test, so that if we see the same method twice, // once as inherent and once not, we will include it. - if let ItemContainer::Trait(trait_) = func.container(self.ctx.db) { - if self.ctx.exclude_traits.contains(&trait_) - || trait_.complete(self.ctx.db) == Complete::IgnoreMethods - { - return ControlFlow::Continue(()); - } + if let ItemContainer::Trait(trait_) = func.container(self.ctx.db) + && (self.ctx.exclude_traits.contains(&trait_) + || trait_.complete(self.ctx.db) == Complete::IgnoreMethods) + { + return ControlFlow::Continue(()); } - if func.self_param(self.ctx.db).is_some() - && self.seen_methods.insert(func.name(self.ctx.db)) - { + if func.self_param(self.ctx.db).is_some() && self.seen_methods.insert(func) { (self.f)(func); } @@ -796,8 +786,7 @@ struct T(S); impl T { fn foo(&self) { - // FIXME: This doesn't work without the trailing `a` as `0.` is a float - self.0.a$0 + self.0.$0 } } "#, @@ -1367,32 +1356,86 @@ fn foo() { r#" struct Foo { baz: fn() } impl Foo { - fn bar<T>(self, t: T): T { t } + fn bar<T>(self, t: T) -> T { t } } fn baz() { let foo = Foo{ baz: || {} }; - foo.ba$0::<>; + foo.ba$0; } "#, expect![[r#" - me bar(…) fn(self, T) + fd baz fn() + me bar(…) fn(self, T) -> T "#]], ); + + check_edit( + "baz", + r#" +struct Foo { baz: fn() } +impl Foo { + fn bar<T>(self, t: T) -> T { t } +} + +fn baz() { + let foo = Foo{ baz: || {} }; + foo.ba$0; +} +"#, + r#" +struct Foo { baz: fn() } +impl Foo { + fn bar<T>(self, t: T) -> T { t } +} + +fn baz() { + let foo = Foo{ baz: || {} }; + (foo.baz)(); +} +"#, + ); + + check_edit( + "bar", + r#" +struct Foo { baz: fn() } +impl Foo { + fn bar<T>(self, t: T) -> T { t } +} + +fn baz() { + let foo = Foo{ baz: || {} }; + foo.ba$0; +} +"#, + r#" +struct Foo { baz: fn() } +impl Foo { + fn bar<T>(self, t: T) -> T { t } +} + +fn baz() { + let foo = Foo{ baz: || {} }; + foo.bar(${1:t})$0; +} +"#, + ); } #[test] fn skip_iter() { check_no_kw( r#" - //- minicore: iterator + //- minicore: iterator, clone, builtin_impls fn foo() { [].$0 } "#, expect![[r#" - me clone() (as Clone) fn(&self) -> Self - me into_iter() (as IntoIterator) fn(self) -> <Self as IntoIterator>::IntoIter + me clone() (as Clone) fn(&self) -> Self + me fmt(…) (use core::fmt::Debug) fn(&self, &mut Formatter<'_>) -> Result<(), Error> + me into_iter() (as IntoIterator) fn(self) -> <Self as IntoIterator>::IntoIter "#]], ); check_no_kw( @@ -1502,7 +1545,9 @@ fn main() { bar.$0 } "#, - expect![[r#""#]], + expect![[r#" + me foo() fn(self: Bar) + "#]], ); } |