Unnamed repository; edit this file 'description' to name the repository.
Deduplicate methods in completion by function ID and not by name
Because duplicates can be found with traits. Worse, the inherent methods could be private, and we'll discover that only later. But even if they're not they're different methods, and its seems worthy to present them all to the user.
Chayim Refael Friedman 8 months ago
parent 21614ed · commit 8a2e845
-rw-r--r--crates/ide-completion/src/completions/dot.rs15
-rw-r--r--crates/ide-completion/src/tests/expression.rs40
2 files changed, 47 insertions, 8 deletions
diff --git a/crates/ide-completion/src/completions/dot.rs b/crates/ide-completion/src/completions/dot.rs
index 129964f838..b9ef68cc2d 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;
@@ -237,7 +237,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 +250,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(())
@@ -265,9 +266,7 @@ fn complete_methods(
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);
}
diff --git a/crates/ide-completion/src/tests/expression.rs b/crates/ide-completion/src/tests/expression.rs
index 7a0d004441..af6ef34761 100644
--- a/crates/ide-completion/src/tests/expression.rs
+++ b/crates/ide-completion/src/tests/expression.rs
@@ -2632,3 +2632,43 @@ fn let_in_condition() {
fn let_in_let_chain() {
check_edit("let", r#"fn f() { if true && $0 {} }"#, r#"fn f() { if true && let $1 = $0 {} }"#);
}
+
+#[test]
+fn private_inherent_and_public_trait() {
+ check(
+ r#"
+struct Foo;
+
+mod private {
+ impl super::Foo {
+ fn method(&self) {}
+ }
+}
+
+trait Trait {
+ fn method(&self) {}
+}
+impl Trait for Foo {}
+
+fn main() {
+ Foo.$0
+}
+ "#,
+ expect![[r#"
+ me method() (as Trait) fn(&self)
+ sn box Box::new(expr)
+ sn call function(expr)
+ sn const const {}
+ sn dbg dbg!(expr)
+ sn dbgr dbg!(&expr)
+ sn deref *expr
+ sn let let
+ sn letm let mut
+ sn match match expr {}
+ sn ref &expr
+ sn refm &mut expr
+ sn return return expr
+ sn unsafe unsafe {}
+ "#]],
+ );
+}