completions: speed up completions by filtering non-applicable traits
This commit is contained in:
parent
9f0495761a
commit
c246a93046
@ -457,6 +457,7 @@ pub(super) fn fallback_if_possible(&mut self) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Unify two relatable values (e.g. `Ty`) and register new trait goals that arise from that.
|
/// 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 {
|
pub(crate) fn unify<T: ?Sized + Zip<Interner>>(&mut self, ty1: &T, ty2: &T) -> bool {
|
||||||
let result = match self.try_unify(ty1, ty2) {
|
let result = match self.try_unify(ty1, ty2) {
|
||||||
Ok(r) => r,
|
Ok(r) => r,
|
||||||
|
@ -254,6 +254,11 @@ pub fn for_trait_and_self_ty(
|
|||||||
.flat_map(|v| v.iter().copied())
|
.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> + '_ {
|
pub fn all_impls(&self) -> impl Iterator<Item = ImplId> + '_ {
|
||||||
self.map.values().flat_map(|map| map.values().flat_map(|v| v.iter().copied()))
|
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() {
|
for &(_, item) in data.items.iter() {
|
||||||
// Don't pass a `visible_from_module` down to `is_valid_candidate`,
|
// Don't pass a `visible_from_module` down to `is_valid_candidate`,
|
||||||
// since only inherent methods should be included into visibility checking.
|
// 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::Yes => true,
|
||||||
IsValidCandidate::NotVisible => false,
|
IsValidCandidate::NotVisible => false,
|
||||||
IsValidCandidate::No => continue,
|
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 {
|
enum IsValidCandidate {
|
||||||
Yes,
|
Yes,
|
||||||
No,
|
No,
|
||||||
@ -1441,6 +1514,8 @@ fn is_valid_fn_candidate(
|
|||||||
}
|
}
|
||||||
table.run_in_snapshot(|table| {
|
table.run_in_snapshot(|table| {
|
||||||
let container = fn_id.lookup(db.upcast()).container;
|
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 {
|
let (impl_subst, expect_self_ty) = match container {
|
||||||
ItemContainerId::ImplId(it) => {
|
ItemContainerId::ImplId(it) => {
|
||||||
let subst =
|
let subst =
|
||||||
@ -1460,6 +1535,7 @@ fn is_valid_fn_candidate(
|
|||||||
check_that!(table.unify(&expect_self_ty, self_ty));
|
check_that!(table.unify(&expect_self_ty, self_ty));
|
||||||
|
|
||||||
if let Some(receiver_ty) = receiver_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());
|
check_that!(data.has_self_param());
|
||||||
|
|
||||||
let fn_subst = TyBuilder::subst_for_def(db, fn_id, Some(impl_subst.clone()))
|
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 {
|
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
|
// We need to consider the bounds on the impl to distinguish functions of the same name
|
||||||
// for a type.
|
// for a type.
|
||||||
let predicates = db.generic_predicates(impl_id.into());
|
let predicates = db.generic_predicates(impl_id.into());
|
||||||
|
@ -139,6 +139,7 @@ fn solve(
|
|||||||
block: Option<BlockId>,
|
block: Option<BlockId>,
|
||||||
goal: &chalk_ir::UCanonical<chalk_ir::InEnvironment<chalk_ir::Goal<Interner>>>,
|
goal: &chalk_ir::UCanonical<chalk_ir::InEnvironment<chalk_ir::Goal<Interner>>>,
|
||||||
) -> Option<chalk_solve::Solution<Interner>> {
|
) -> Option<chalk_solve::Solution<Interner>> {
|
||||||
|
let _p = tracing::span!(tracing::Level::INFO, "solve", ?krate, ?block).entered();
|
||||||
let context = ChalkContext { db, krate, block };
|
let context = ChalkContext { db, krate, block };
|
||||||
tracing::debug!("solve goal: {:?}", goal);
|
tracing::debug!("solve goal: {:?}", goal);
|
||||||
let mut solver = create_chalk_solver();
|
let mut solver = create_chalk_solver();
|
||||||
|
@ -4239,6 +4239,10 @@ pub fn as_array(&self, db: &dyn HirDatabase) -> Option<(Type, usize)> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn fingerprint_for_trait_impl(&self) -> Option<TyFingerprint> {
|
||||||
|
TyFingerprint::for_trait_impl(&self.ty)
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn canonical(&self) -> Canonical<Ty> {
|
pub(crate) fn canonical(&self) -> Canonical<Ty> {
|
||||||
hir_ty::replace_errors_with_variables(&self.ty)
|
hir_ty::replace_errors_with_variables(&self.ty)
|
||||||
}
|
}
|
||||||
|
@ -374,6 +374,135 @@ fn main() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn trait_method_fuzzy_completion_aware_of_fundamental_boxes() {
|
||||||
|
let fixture = r#"
|
||||||
|
//- /fundamental.rs crate:fundamental
|
||||||
|
#[lang = "owned_box"]
|
||||||
|
#[fundamental]
|
||||||
|
pub struct Box<T>(T);
|
||||||
|
//- /foo.rs crate:foo
|
||||||
|
pub trait TestTrait {
|
||||||
|
fn some_method(&self);
|
||||||
|
}
|
||||||
|
//- /main.rs crate:main deps:foo,fundamental
|
||||||
|
struct TestStruct;
|
||||||
|
|
||||||
|
impl foo::TestTrait for fundamental::Box<TestStruct> {
|
||||||
|
fn some_method(&self) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let t = fundamental::Box(TestStruct);
|
||||||
|
t.$0
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
check(
|
||||||
|
fixture,
|
||||||
|
expect![[r#"
|
||||||
|
me some_method() (use foo::TestTrait) fn(&self)
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
|
||||||
|
check_edit(
|
||||||
|
"some_method",
|
||||||
|
fixture,
|
||||||
|
r#"
|
||||||
|
use foo::TestTrait;
|
||||||
|
|
||||||
|
struct TestStruct;
|
||||||
|
|
||||||
|
impl foo::TestTrait for fundamental::Box<TestStruct> {
|
||||||
|
fn some_method(&self) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let t = fundamental::Box(TestStruct);
|
||||||
|
t.some_method()$0
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn trait_method_fuzzy_completion_aware_of_fundamental_references() {
|
||||||
|
let fixture = r#"
|
||||||
|
//- /foo.rs crate:foo
|
||||||
|
pub trait TestTrait {
|
||||||
|
fn some_method(&self);
|
||||||
|
}
|
||||||
|
//- /main.rs crate:main deps:foo
|
||||||
|
struct TestStruct;
|
||||||
|
|
||||||
|
impl foo::TestTrait for &TestStruct {
|
||||||
|
fn some_method(&self) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let t = &TestStruct;
|
||||||
|
t.$0
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
check(
|
||||||
|
fixture,
|
||||||
|
expect![[r#"
|
||||||
|
me some_method() (use foo::TestTrait) fn(&self)
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
|
||||||
|
check_edit(
|
||||||
|
"some_method",
|
||||||
|
fixture,
|
||||||
|
r#"
|
||||||
|
use foo::TestTrait;
|
||||||
|
|
||||||
|
struct TestStruct;
|
||||||
|
|
||||||
|
impl foo::TestTrait for &TestStruct {
|
||||||
|
fn some_method(&self) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let t = &TestStruct;
|
||||||
|
t.some_method()$0
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn trait_method_fuzzy_completion_aware_of_unit_type() {
|
||||||
|
let fixture = r#"
|
||||||
|
//- /test_trait.rs crate:test_trait
|
||||||
|
pub trait TestInto<T> {
|
||||||
|
fn into(self) -> T;
|
||||||
|
}
|
||||||
|
|
||||||
|
//- /main.rs crate:main deps:test_trait
|
||||||
|
struct A;
|
||||||
|
|
||||||
|
impl test_trait::TestInto<A> for () {
|
||||||
|
fn into(self) -> A {
|
||||||
|
A
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let a = ();
|
||||||
|
a.$0
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
check(
|
||||||
|
fixture,
|
||||||
|
expect![[r#"
|
||||||
|
me into() (use test_trait::TestInto) fn(self) -> T
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn trait_method_from_alias() {
|
fn trait_method_from_alias() {
|
||||||
let fixture = r#"
|
let fixture = r#"
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
//! Look up accessible paths for items.
|
//! Look up accessible paths for items.
|
||||||
|
|
||||||
use hir::{
|
use hir::{
|
||||||
AsAssocItem, AssocItem, AssocItemContainer, Crate, ItemInNs, ModPath, Module, ModuleDef, Name,
|
db::HirDatabase, AsAssocItem, AssocItem, AssocItemContainer, Crate, HasCrate, ItemInNs,
|
||||||
PathResolution, PrefixKind, ScopeDef, Semantics, SemanticsScope, Type,
|
ModPath, Module, ModuleDef, Name, PathResolution, PrefixKind, ScopeDef, Semantics,
|
||||||
|
SemanticsScope, Trait, Type,
|
||||||
};
|
};
|
||||||
use itertools::{EitherOrBoth, Itertools};
|
use itertools::{EitherOrBoth, Itertools};
|
||||||
use rustc_hash::{FxHashMap, FxHashSet};
|
use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
@ -517,7 +518,7 @@ fn trait_applicable_items(
|
|||||||
let related_traits = inherent_traits.chain(env_traits).collect::<FxHashSet<_>>();
|
let related_traits = inherent_traits.chain(env_traits).collect::<FxHashSet<_>>();
|
||||||
|
|
||||||
let mut required_assoc_items = FxHashSet::default();
|
let mut required_assoc_items = FxHashSet::default();
|
||||||
let trait_candidates: FxHashSet<_> = items_locator::items_with_name(
|
let mut trait_candidates: FxHashSet<_> = items_locator::items_with_name(
|
||||||
sema,
|
sema,
|
||||||
current_crate,
|
current_crate,
|
||||||
trait_candidate.assoc_item_name.clone(),
|
trait_candidate.assoc_item_name.clone(),
|
||||||
@ -538,6 +539,32 @@ fn trait_applicable_items(
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
trait_candidates.retain(|&candidate_trait_id| {
|
||||||
|
// we care about the following cases:
|
||||||
|
// 1. Trait's definition crate
|
||||||
|
// 2. Definition crates for all trait's generic arguments
|
||||||
|
// a. This is recursive for fundamental types: `Into<Box<A>> for ()`` is OK, but
|
||||||
|
// `Into<Vec<A>> for ()`` is *not*.
|
||||||
|
// 3. Receiver type definition crate
|
||||||
|
// a. This is recursive for fundamental types
|
||||||
|
let defining_crate_for_trait = Trait::from(candidate_trait_id).krate(db);
|
||||||
|
let Some(receiver) = trait_candidate.receiver_ty.fingerprint_for_trait_impl() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
let definitions_exist_in_trait_crate = db
|
||||||
|
.trait_impls_in_crate(defining_crate_for_trait.into())
|
||||||
|
.has_impls_for_trait_and_self_ty(candidate_trait_id, receiver);
|
||||||
|
|
||||||
|
// this is a closure for laziness: if `definitions_exist_in_trait_crate` is true,
|
||||||
|
// we can avoid a second db lookup.
|
||||||
|
let definitions_exist_in_receiver_crate = || {
|
||||||
|
db.trait_impls_in_crate(trait_candidate.receiver_ty.krate(db).into())
|
||||||
|
.has_impls_for_trait_and_self_ty(candidate_trait_id, receiver)
|
||||||
|
};
|
||||||
|
|
||||||
|
definitions_exist_in_trait_crate || definitions_exist_in_receiver_crate()
|
||||||
|
});
|
||||||
|
|
||||||
let mut located_imports = FxHashSet::default();
|
let mut located_imports = FxHashSet::default();
|
||||||
let mut trait_import_paths = FxHashMap::default();
|
let mut trait_import_paths = FxHashMap::default();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user