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 | 123 |
1 files changed, 86 insertions, 37 deletions
diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs index d5285c1710..d2081d22d7 100644 --- a/crates/hir-ty/src/method_resolution.rs +++ b/crates/hir-ty/src/method_resolution.rs @@ -8,8 +8,9 @@ use arrayvec::ArrayVec; use base_db::{CrateId, Edition}; use chalk_ir::{cast::Cast, Mutability, UniverseIndex}; use hir_def::{ - item_scope::ItemScope, nameres::DefMap, AssocItemId, BlockId, ConstId, FunctionId, - GenericDefId, HasModule, ImplId, ItemContainerId, Lookup, ModuleDefId, ModuleId, TraitId, + data::ImplData, item_scope::ItemScope, nameres::DefMap, AssocItemId, BlockId, ConstId, + FunctionId, GenericDefId, HasModule, ImplId, ItemContainerId, Lookup, ModuleDefId, ModuleId, + TraitId, }; use hir_expand::name::Name; use rustc_hash::{FxHashMap, FxHashSet}; @@ -247,7 +248,7 @@ impl TraitImpls { self.map .get(&trait_) .into_iter() - .flat_map(move |map| map.get(&None).into_iter().chain(map.get(&Some(self_ty)))) + .flat_map(move |map| map.get(&Some(self_ty)).into_iter().chain(map.get(&None))) .flat_map(|v| v.iter().copied()) } @@ -575,6 +576,59 @@ pub(crate) fn iterate_method_candidates<T>( slot } +pub fn lookup_impl_method( + self_ty: &Ty, + db: &dyn HirDatabase, + env: Arc<TraitEnvironment>, + trait_: TraitId, + name: &Name, +) -> Option<FunctionId> { + let self_ty_fp = TyFingerprint::for_trait_impl(self_ty)?; + let trait_impls = TraitImpls::trait_impls_in_deps_query(db, env.krate); + let impls = trait_impls.for_trait_and_self_ty(trait_, self_ty_fp); + let mut table = InferenceTable::new(db, env.clone()); + find_matching_impl(impls, &mut table, &self_ty).and_then(|data| { + data.items.iter().find_map(|it| match it { + AssocItemId::FunctionId(f) => (db.function_data(*f).name == *name).then(|| *f), + _ => None, + }) + }) +} + +fn find_matching_impl( + mut impls: impl Iterator<Item = ImplId>, + table: &mut InferenceTable, + self_ty: &Ty, +) -> Option<Arc<ImplData>> { + let db = table.db; + loop { + let impl_ = impls.next()?; + let r = table.run_in_snapshot(|table| { + let impl_data = db.impl_data(impl_); + let substs = + TyBuilder::subst_for_def(db, impl_).fill_with_inference_vars(table).build(); + let impl_ty = db.impl_self_ty(impl_).substitute(Interner, &substs); + + table + .unify(self_ty, &impl_ty) + .then(|| { + let wh_goals = + crate::chalk_db::convert_where_clauses(db, impl_.into(), &substs) + .into_iter() + .map(|b| b.cast(Interner)); + + let goal = crate::Goal::all(Interner, wh_goals); + + table.try_obligation(goal).map(|_| impl_data) + }) + .flatten() + }); + if r.is_some() { + break r; + } + } +} + pub fn iterate_path_candidates( ty: &Canonical<Ty>, db: &dyn HirDatabase, @@ -970,18 +1024,31 @@ fn is_valid_candidate( self_ty: &Ty, visible_from_module: Option<ModuleId>, ) -> bool { + macro_rules! check_that { + ($cond:expr) => { + if !$cond { + return false; + } + }; + } + let db = table.db; match item { AssocItemId::FunctionId(m) => { let data = db.function_data(m); - if let Some(name) = name { - if &data.name != name { - return false; + + check_that!(name.map_or(true, |n| n == &data.name)); + check_that!(visible_from_module.map_or(true, |from_module| { + let v = db.function_visibility(m).is_visible_from(db.upcast(), from_module); + if !v { + cov_mark::hit!(autoderef_candidate_not_visible); } - } + v + })); + table.run_in_snapshot(|table| { let subst = TyBuilder::subst_for_def(db, m).fill_with_inference_vars(table).build(); - let expected_self_ty = match m.lookup(db.upcast()).container { + let expect_self_ty = match m.lookup(db.upcast()).container { ItemContainerId::TraitId(_) => { subst.at(Interner, 0).assert_ty_ref(Interner).clone() } @@ -993,49 +1060,31 @@ fn is_valid_candidate( unreachable!() } }; - if !table.unify(&expected_self_ty, &self_ty) { - return false; - } + check_that!(table.unify(&expect_self_ty, self_ty)); if let Some(receiver_ty) = receiver_ty { - if !data.has_self_param() { - return false; - } + check_that!(data.has_self_param()); let sig = db.callable_item_signature(m.into()); let expected_receiver = sig.map(|s| s.params()[0].clone()).substitute(Interner, &subst); - let receiver_matches = table.unify(&receiver_ty, &expected_receiver); - if !receiver_matches { - return false; - } + check_that!(table.unify(&receiver_ty, &expected_receiver)); } - if let Some(from_module) = visible_from_module { - if !db.function_visibility(m).is_visible_from(db.upcast(), from_module) { - cov_mark::hit!(autoderef_candidate_not_visible); - return false; - } - } - true }) } AssocItemId::ConstId(c) => { let data = db.const_data(c); - if receiver_ty.is_some() { - return false; - } - if let Some(name) = name { - if data.name.as_ref() != Some(name) { - return false; - } - } - if let Some(from_module) = visible_from_module { - if !db.const_visibility(c).is_visible_from(db.upcast(), from_module) { + check_that!(receiver_ty.is_none()); + + check_that!(name.map_or(true, |n| data.name.as_ref() == Some(n))); + check_that!(visible_from_module.map_or(true, |from_module| { + let v = db.const_visibility(c).is_visible_from(db.upcast(), from_module); + if !v { cov_mark::hit!(const_candidate_not_visible); - return false; } - } + v + })); if let ItemContainerId::ImplId(impl_id) = c.lookup(db.upcast()).container { let self_ty_matches = table.run_in_snapshot(|table| { let subst = |