Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/method_resolution.rs')
-rw-r--r--crates/hir-ty/src/method_resolution.rs79
1 files changed, 78 insertions, 1 deletions
diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs
index a4baf572d9..4bb412d01c 100644
--- a/crates/hir-ty/src/method_resolution.rs
+++ b/crates/hir-ty/src/method_resolution.rs
@@ -254,6 +254,11 @@ impl TraitImpls {
.flat_map(|v| v.iter().copied())
}
+ /// Queries whether `self_ty` has potentially applicable implementations of `trait_`.
+ pub fn has_impls_for_trait_and_self_ty(&self, trait_: TraitId, self_ty: TyFingerprint) -> bool {
+ self.for_trait_and_self_ty(trait_, self_ty).next().is_some()
+ }
+
pub fn all_impls(&self) -> impl Iterator<Item = ImplId> + '_ {
self.map.values().flat_map(|map| map.values().flat_map(|v| v.iter().copied()))
}
@@ -1170,7 +1175,7 @@ fn iterate_trait_method_candidates(
for &(_, item) in data.items.iter() {
// Don't pass a `visible_from_module` down to `is_valid_candidate`,
// since only inherent methods should be included into visibility checking.
- let visible = match is_valid_candidate(table, name, receiver_ty, item, self_ty, None) {
+ let visible = match is_valid_method_candidate(table, name, receiver_ty, item, self_ty) {
IsValidCandidate::Yes => true,
IsValidCandidate::NotVisible => false,
IsValidCandidate::No => continue,
@@ -1414,6 +1419,74 @@ fn is_valid_candidate(
}
}
+/// Checks whether a given `AssocItemId` is applicable for `receiver_ty`.
+///
+/// This method should *only* be called by [`iterate_trait_method_candidates`],
+/// as it is responsible for determining applicability in completions.
+#[tracing::instrument(skip_all, fields(name))]
+fn is_valid_method_candidate(
+ table: &mut InferenceTable<'_>,
+ name: Option<&Name>,
+ receiver_ty: Option<&Ty>,
+ item: AssocItemId,
+ self_ty: &Ty,
+) -> IsValidCandidate {
+ let db = table.db;
+ match item {
+ AssocItemId::FunctionId(fn_id) => {
+ let data = db.function_data(fn_id);
+
+ check_that!(name.map_or(true, |n| n == &data.name));
+
+ table.run_in_snapshot(|table| {
+ let container = fn_id.lookup(db.upcast()).container;
+ let (impl_subst, expect_self_ty) = match container {
+ ItemContainerId::ImplId(it) => {
+ let subst = TyBuilder::subst_for_def(db, it, None)
+ .fill_with_inference_vars(table)
+ .build();
+ let self_ty = db.impl_self_ty(it).substitute(Interner, &subst);
+ (subst, self_ty)
+ }
+ ItemContainerId::TraitId(it) => {
+ let subst = TyBuilder::subst_for_def(db, it, None)
+ .fill_with_inference_vars(table)
+ .build();
+ let self_ty = subst.at(Interner, 0).assert_ty_ref(Interner).clone();
+ (subst, self_ty)
+ }
+ _ => unreachable!(),
+ };
+
+ check_that!(table.unify(&expect_self_ty, self_ty));
+
+ if let Some(receiver_ty) = receiver_ty {
+ check_that!(data.has_self_param());
+
+ let fn_subst = TyBuilder::subst_for_def(db, fn_id, Some(impl_subst.clone()))
+ .fill_with_inference_vars(table)
+ .build();
+
+ let sig = db.callable_item_signature(fn_id.into());
+ let expected_receiver =
+ sig.map(|s| s.params()[0].clone()).substitute(Interner, &fn_subst);
+
+ check_that!(table.unify(receiver_ty, &expected_receiver));
+ }
+
+ IsValidCandidate::Yes
+ })
+ }
+ AssocItemId::ConstId(c) => {
+ check_that!(receiver_ty.is_none());
+ check_that!(name.map_or(true, |n| db.const_data(c).name.as_ref() == Some(n)));
+
+ IsValidCandidate::Yes
+ }
+ _ => IsValidCandidate::No,
+ }
+}
+
enum IsValidCandidate {
Yes,
No,
@@ -1441,6 +1514,8 @@ fn is_valid_fn_candidate(
}
table.run_in_snapshot(|table| {
let container = fn_id.lookup(db.upcast()).container;
+
+ let _p = tracing::span!(tracing::Level::INFO, "subst_for_def").entered();
let (impl_subst, expect_self_ty) = match container {
ItemContainerId::ImplId(it) => {
let subst =
@@ -1460,6 +1535,7 @@ fn is_valid_fn_candidate(
check_that!(table.unify(&expect_self_ty, self_ty));
if let Some(receiver_ty) = receiver_ty {
+ let _p = tracing::span!(tracing::Level::INFO, "check_receiver_ty").entered();
check_that!(data.has_self_param());
let fn_subst = TyBuilder::subst_for_def(db, fn_id, Some(impl_subst.clone()))
@@ -1474,6 +1550,7 @@ fn is_valid_fn_candidate(
}
if let ItemContainerId::ImplId(impl_id) = container {
+ let _p = tracing::span!(tracing::Level::INFO, "check_item_container").entered();
// We need to consider the bounds on the impl to distinguish functions of the same name
// for a type.
let predicates = db.generic_predicates(impl_id.into());