mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-30 22:01:37 +00:00
completions: speed up completions by filtering non-applicable traits
This commit is contained in:
parent
9f0495761a
commit
c246a93046
6 changed files with 243 additions and 4 deletions
|
@ -457,6 +457,7 @@ impl<'a> InferenceTable<'a> {
|
|||
}
|
||||
|
||||
/// Unify two relatable values (e.g. `Ty`) and register new trait goals that arise from that.
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub(crate) fn unify<T: ?Sized + Zip<Interner>>(&mut self, ty1: &T, ty2: &T) -> bool {
|
||||
let result = match self.try_unify(ty1, ty2) {
|
||||
Ok(r) => r,
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -139,6 +139,7 @@ fn solve(
|
|||
block: Option<BlockId>,
|
||||
goal: &chalk_ir::UCanonical<chalk_ir::InEnvironment<chalk_ir::Goal<Interner>>>,
|
||||
) -> Option<chalk_solve::Solution<Interner>> {
|
||||
let _p = tracing::span!(tracing::Level::INFO, "solve", ?krate, ?block).entered();
|
||||
let context = ChalkContext { db, krate, block };
|
||||
tracing::debug!("solve goal: {:?}", goal);
|
||||
let mut solver = create_chalk_solver();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue