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.rs125
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)
+ "#]],
);
}