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.rs | 79 |
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()); |