Skip to content

Commit f3d507f

Browse files
committed
completions: speed up completions by filtering non-applicable traits
1 parent 2c05da1 commit f3d507f

File tree

5 files changed

+113
-7
lines changed

5 files changed

+113
-7
lines changed

crates/hir-ty/src/infer/unify.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,7 @@ impl<'a> InferenceTable<'a> {
457457
}
458458

459459
/// Unify two relatable values (e.g. `Ty`) and register new trait goals that arise from that.
460+
#[tracing::instrument(skip_all)]
460461
pub(crate) fn unify<T: ?Sized + Zip<Interner>>(&mut self, ty1: &T, ty2: &T) -> bool {
461462
let result = match self.try_unify(ty1, ty2) {
462463
Ok(r) => r,

crates/hir-ty/src/method_resolution.rs

Lines changed: 78 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1167,10 +1167,13 @@ fn iterate_trait_method_candidates(
11671167
// trait, but if we find out it doesn't, we'll skip the rest of the
11681168
// iteration
11691169
let mut known_implemented = false;
1170-
for &(_, item) in data.items.iter() {
1170+
'items: for &(_, item) in data.items.iter() {
1171+
if let AssocItemId::TypeAliasId(_) = item {
1172+
continue 'items;
1173+
}
11711174
// Don't pass a `visible_from_module` down to `is_valid_candidate`,
11721175
// since only inherent methods should be included into visibility checking.
1173-
let visible = match is_valid_candidate(table, name, receiver_ty, item, self_ty, None) {
1176+
let visible = match is_valid_method_candidate(table, name, receiver_ty, item, self_ty) {
11741177
IsValidCandidate::Yes => true,
11751178
IsValidCandidate::NotVisible => false,
11761179
IsValidCandidate::No => continue,
@@ -1414,6 +1417,75 @@ fn is_valid_candidate(
14141417
}
14151418
}
14161419

1420+
/// Checks whether a given `AssocItemId` is applicable for `receiver_ty`.
1421+
///
1422+
/// This method should *only* be called by [`iterate_trait_method_candidates`],
1423+
/// as it is responsible for determining applicability in completions.
1424+
#[tracing::instrument(skip_all, fields(name))]
1425+
fn is_valid_method_candidate(
1426+
table: &mut InferenceTable<'_>,
1427+
name: Option<&Name>,
1428+
receiver_ty: Option<&Ty>,
1429+
item: AssocItemId,
1430+
self_ty: &Ty,
1431+
) -> IsValidCandidate {
1432+
let db = table.db;
1433+
match item {
1434+
AssocItemId::FunctionId(fn_id) => {
1435+
let db = table.db;
1436+
let data = db.function_data(fn_id);
1437+
1438+
check_that!(name.map_or(true, |n| n == &data.name));
1439+
1440+
table.run_in_snapshot(|table| {
1441+
let container = fn_id.lookup(db.upcast()).container;
1442+
let (impl_subst, expect_self_ty) = match container {
1443+
ItemContainerId::ImplId(it) => {
1444+
let subst = TyBuilder::subst_for_def(db, it, None)
1445+
.fill_with_inference_vars(table)
1446+
.build();
1447+
let self_ty = db.impl_self_ty(it).substitute(Interner, &subst);
1448+
(subst, self_ty)
1449+
}
1450+
ItemContainerId::TraitId(it) => {
1451+
let subst = TyBuilder::subst_for_def(db, it, None)
1452+
.fill_with_inference_vars(table)
1453+
.build();
1454+
let self_ty = subst.at(Interner, 0).assert_ty_ref(Interner).clone();
1455+
(subst, self_ty)
1456+
}
1457+
_ => unreachable!(),
1458+
};
1459+
1460+
check_that!(table.unify(&expect_self_ty, self_ty));
1461+
1462+
if let Some(receiver_ty) = receiver_ty {
1463+
check_that!(data.has_self_param());
1464+
1465+
let fn_subst = TyBuilder::subst_for_def(db, fn_id, Some(impl_subst.clone()))
1466+
.fill_with_inference_vars(table)
1467+
.build();
1468+
1469+
let sig = db.callable_item_signature(fn_id.into());
1470+
let expected_receiver =
1471+
sig.map(|s| s.params()[0].clone()).substitute(Interner, &fn_subst);
1472+
1473+
check_that!(table.unify(receiver_ty, &expected_receiver));
1474+
}
1475+
1476+
IsValidCandidate::Yes
1477+
})
1478+
}
1479+
AssocItemId::ConstId(c) => {
1480+
check_that!(receiver_ty.is_none());
1481+
check_that!(name.map_or(true, |n| db.const_data(c).name.as_ref() == Some(n)));
1482+
1483+
IsValidCandidate::Yes
1484+
}
1485+
_ => IsValidCandidate::No,
1486+
}
1487+
}
1488+
14171489
enum IsValidCandidate {
14181490
Yes,
14191491
No,
@@ -1441,6 +1513,8 @@ fn is_valid_fn_candidate(
14411513
}
14421514
table.run_in_snapshot(|table| {
14431515
let container = fn_id.lookup(db.upcast()).container;
1516+
1517+
let _p = tracing::span!(tracing::Level::INFO, "subst_for_def").entered();
14441518
let (impl_subst, expect_self_ty) = match container {
14451519
ItemContainerId::ImplId(it) => {
14461520
let subst =
@@ -1459,6 +1533,7 @@ fn is_valid_fn_candidate(
14591533

14601534
check_that!(table.unify(&expect_self_ty, self_ty));
14611535

1536+
let _p = tracing::span!(tracing::Level::INFO, "check_receiver_ty").entered();
14621537
if let Some(receiver_ty) = receiver_ty {
14631538
check_that!(data.has_self_param());
14641539

@@ -1473,6 +1548,7 @@ fn is_valid_fn_candidate(
14731548
check_that!(table.unify(receiver_ty, &expected_receiver));
14741549
}
14751550

1551+
let _p = tracing::span!(tracing::Level::INFO, "check_item_container").entered();
14761552
if let ItemContainerId::ImplId(impl_id) = container {
14771553
// We need to consider the bounds on the impl to distinguish functions of the same name
14781554
// for a type.

crates/hir-ty/src/traits.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ fn solve(
139139
block: Option<BlockId>,
140140
goal: &chalk_ir::UCanonical<chalk_ir::InEnvironment<chalk_ir::Goal<Interner>>>,
141141
) -> Option<chalk_solve::Solution<Interner>> {
142+
let _p = tracing::span!(tracing::Level::INFO, "solve", ?krate, ?block).entered();
142143
let context = ChalkContext { db, krate, block };
143144
tracing::debug!("solve goal: {:?}", goal);
144145
let mut solver = create_chalk_solver();

crates/hir/src/lib.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ use std::{iter, mem::discriminant, ops::ControlFlow};
4040
use arrayvec::ArrayVec;
4141
use base_db::{CrateDisplayName, CrateId, CrateOrigin, Edition, FileId};
4242
use either::Either;
43-
use hir_def::{
43+
pub use hir_def::{
4444
body::{BodyDiagnostic, SyntheticSyntax},
4545
data::adt::VariantData,
4646
generics::{LifetimeParamData, TypeOrConstParamData, TypeParamProvenance},
@@ -60,7 +60,7 @@ use hir_def::{
6060
TypeOrConstParamId, TypeParamId, UnionId,
6161
};
6262
use hir_expand::{attrs::collect_attrs, name::name, proc_macro::ProcMacroKind, MacroCallKind};
63-
use hir_ty::{
63+
pub use hir_ty::{
6464
all_super_traits, autoderef, check_orphan_rules,
6565
consteval::{try_const_usize, unknown_const_as_generic, ConstExt},
6666
db::InternedClosure,
@@ -266,6 +266,10 @@ impl Crate {
266266
let data = &db.crate_graph()[self.id];
267267
data.potential_cfg_options.clone().unwrap_or_else(|| data.cfg_options.clone())
268268
}
269+
270+
pub fn id(&self) -> CrateId {
271+
self.id
272+
}
269273
}
270274

271275
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -3691,7 +3695,7 @@ pub enum CaptureKind {
36913695
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
36923696
pub struct Type {
36933697
env: Arc<TraitEnvironment>,
3694-
ty: Ty,
3698+
pub ty: Ty,
36953699
}
36963700

36973701
impl Type {

crates/ide-db/src/imports/import_assets.rs

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
//! Look up accessible paths for items.
22
33
use hir::{
4-
AsAssocItem, AssocItem, AssocItemContainer, Crate, ItemInNs, ModPath, Module, ModuleDef, Name,
5-
PathResolution, PrefixKind, ScopeDef, Semantics, SemanticsScope, Type,
4+
db::HirDatabase, AsAssocItem, AssocItem, AssocItemContainer, Crate, HasCrate, ItemInNs,
5+
ModPath, Module, ModuleDef, Name, PathResolution, PrefixKind, ScopeDef, Semantics,
6+
SemanticsScope, TraitId, TyFingerprint, Type,
67
};
78
use itertools::{EitherOrBoth, Itertools};
89
use rustc_hash::{FxHashMap, FxHashSet};
@@ -536,6 +537,29 @@ fn trait_applicable_items(
536537
required_assoc_items.insert(assoc);
537538
Some(assoc_item_trait.into())
538539
})
540+
.filter(|candidate_trait_id| {
541+
// blanket `Trait` implementations for type `A` can only exist in the crate defining
542+
// the trait. However, an implementation for `A`` can only exist in `A`'s or `Trait`'s crate,
543+
// which allows us to reduce the search space substantially.
544+
let candidate_trait_id: TraitId = *candidate_trait_id;
545+
let definining_crate_for_trait = db.trait_environment(candidate_trait_id.into()).krate;
546+
547+
let receiver_fingerprint =
548+
TyFingerprint::for_trait_impl(&trait_candidate.receiver_ty.ty).unwrap();
549+
let definitions_exist_in_trait_crate = db
550+
.trait_impls_in_crate(definining_crate_for_trait)
551+
.for_trait_and_self_ty(candidate_trait_id, receiver_fingerprint)
552+
.next()
553+
.is_some();
554+
555+
let definitions_exist_in_receiver_crate = db
556+
.trait_impls_in_crate(trait_candidate.receiver_ty.krate(db).id())
557+
.for_trait_and_self_ty(candidate_trait_id, receiver_fingerprint)
558+
.next()
559+
.is_some();
560+
561+
definitions_exist_in_trait_crate || definitions_exist_in_receiver_crate
562+
})
539563
.collect();
540564

541565
let mut located_imports = FxHashSet::default();

0 commit comments

Comments
 (0)