Skip to content

Commit

Permalink
Merge pull request #18179 from ChayimFriedman2/omit-trait-completion
Browse files Browse the repository at this point in the history
feat: Allow excluding specific traits from completion
  • Loading branch information
Veykril authored Jan 1, 2025
2 parents e5950cd + 1adc805 commit 7e639ee
Show file tree
Hide file tree
Showing 20 changed files with 1,028 additions and 100 deletions.
69 changes: 58 additions & 11 deletions crates/hir-ty/src/method_resolution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -913,7 +913,7 @@ pub fn iterate_path_candidates(
traits_in_scope: &FxHashSet<TraitId>,
visible_from_module: VisibleFromModule,
name: Option<&Name>,
callback: &mut dyn FnMut(AssocItemId) -> ControlFlow<()>,
callback: &mut dyn MethodCandidateCallback,
) -> ControlFlow<()> {
iterate_method_candidates_dyn(
ty,
Expand All @@ -924,7 +924,7 @@ pub fn iterate_path_candidates(
name,
LookupMode::Path,
// the adjustments are not relevant for path lookup
&mut |_, id, _| callback(id),
callback,
)
}

Expand All @@ -936,7 +936,7 @@ pub fn iterate_method_candidates_dyn(
visible_from_module: VisibleFromModule,
name: Option<&Name>,
mode: LookupMode,
callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>,
callback: &mut dyn MethodCandidateCallback,
) -> ControlFlow<()> {
let _p = tracing::info_span!(
"iterate_method_candidates_dyn",
Expand Down Expand Up @@ -1006,7 +1006,7 @@ fn iterate_method_candidates_with_autoref(
traits_in_scope: &FxHashSet<TraitId>,
visible_from_module: VisibleFromModule,
name: Option<&Name>,
mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>,
callback: &mut dyn MethodCandidateCallback,
) -> ControlFlow<()> {
if receiver_ty.value.is_general_var(Interner, &receiver_ty.binders) {
// don't try to resolve methods on unknown types
Expand All @@ -1021,7 +1021,7 @@ fn iterate_method_candidates_with_autoref(
traits_in_scope,
visible_from_module,
name,
&mut callback,
callback,
)
};

Expand Down Expand Up @@ -1051,6 +1051,45 @@ fn iterate_method_candidates_with_autoref(
iterate_method_candidates_by_receiver(ref_muted, first_adjustment.with_autoref(Mutability::Mut))
}

pub trait MethodCandidateCallback {
fn on_inherent_method(
&mut self,
adjustments: ReceiverAdjustments,
item: AssocItemId,
is_visible: bool,
) -> ControlFlow<()>;

fn on_trait_method(
&mut self,
adjustments: ReceiverAdjustments,
item: AssocItemId,
is_visible: bool,
) -> ControlFlow<()>;
}

impl<F> MethodCandidateCallback for F
where
F: FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>,
{
fn on_inherent_method(
&mut self,
adjustments: ReceiverAdjustments,
item: AssocItemId,
is_visible: bool,
) -> ControlFlow<()> {
self(adjustments, item, is_visible)
}

fn on_trait_method(
&mut self,
adjustments: ReceiverAdjustments,
item: AssocItemId,
is_visible: bool,
) -> ControlFlow<()> {
self(adjustments, item, is_visible)
}
}

#[tracing::instrument(skip_all, fields(name = ?name))]
fn iterate_method_candidates_by_receiver(
table: &mut InferenceTable<'_>,
Expand All @@ -1059,7 +1098,7 @@ fn iterate_method_candidates_by_receiver(
traits_in_scope: &FxHashSet<TraitId>,
visible_from_module: VisibleFromModule,
name: Option<&Name>,
mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>,
callback: &mut dyn MethodCandidateCallback,
) -> ControlFlow<()> {
let receiver_ty = table.instantiate_canonical(receiver_ty);
// We're looking for methods with *receiver* type receiver_ty. These could
Expand All @@ -1075,7 +1114,9 @@ fn iterate_method_candidates_by_receiver(
Some(&receiver_ty),
Some(receiver_adjustments.clone()),
visible_from_module,
&mut callback,
&mut |adjustments, item, is_visible| {
callback.on_inherent_method(adjustments, item, is_visible)
},
)?
}
ControlFlow::Continue(())
Expand All @@ -1095,7 +1136,9 @@ fn iterate_method_candidates_by_receiver(
name,
Some(&receiver_ty),
Some(receiver_adjustments.clone()),
&mut callback,
&mut |adjustments, item, is_visible| {
callback.on_trait_method(adjustments, item, is_visible)
},
)?
}
ControlFlow::Continue(())
Expand All @@ -1110,7 +1153,7 @@ fn iterate_method_candidates_for_self_ty(
traits_in_scope: &FxHashSet<TraitId>,
visible_from_module: VisibleFromModule,
name: Option<&Name>,
mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>,
callback: &mut dyn MethodCandidateCallback,
) -> ControlFlow<()> {
let mut table = InferenceTable::new(db, env);
let self_ty = table.instantiate_canonical(self_ty.clone());
Expand All @@ -1121,7 +1164,9 @@ fn iterate_method_candidates_for_self_ty(
None,
None,
visible_from_module,
&mut callback,
&mut |adjustments, item, is_visible| {
callback.on_inherent_method(adjustments, item, is_visible)
},
)?;
iterate_trait_method_candidates(
&self_ty,
Expand All @@ -1130,7 +1175,9 @@ fn iterate_method_candidates_for_self_ty(
name,
None,
None,
callback,
&mut |adjustments, item, is_visible| {
callback.on_trait_method(adjustments, item, is_visible)
},
)
}

Expand Down
2 changes: 1 addition & 1 deletion crates/hir/src/attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ fn resolve_impl_trait_item(
&traits_in_scope,
method_resolution::VisibleFromModule::None,
Some(name),
&mut |assoc_item_id| {
&mut |_, assoc_item_id: AssocItemId, _| {
// If two traits in scope define the same item, Rustdoc links to no specific trait (for
// instance, given two methods `a`, Rustdoc simply links to `method.a` with no
// disambiguation) so we just pick the first one we find as well.
Expand Down
139 changes: 118 additions & 21 deletions crates/hir/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5223,21 +5223,18 @@ impl Type {
) -> Option<T> {
let _p = tracing::info_span!("iterate_method_candidates_with_traits").entered();
let mut slot = None;

self.iterate_method_candidates_dyn(
self.iterate_method_candidates_split_inherent(
db,
scope,
traits_in_scope,
with_local_impls,
name,
&mut |assoc_item_id| {
if let AssocItemId::FunctionId(func) = assoc_item_id {
if let Some(res) = callback(func.into()) {
slot = Some(res);
return ControlFlow::Break(());
}
|f| match callback(f) {
it @ Some(_) => {
slot = it;
ControlFlow::Break(())
}
ControlFlow::Continue(())
None => ControlFlow::Continue(()),
},
);
slot
Expand All @@ -5261,15 +5258,49 @@ impl Type {
)
}

fn iterate_method_candidates_dyn(
/// Allows you to treat inherent and non-inherent methods differently.
///
/// Note that inherent methods may actually be trait methods! For example, in `dyn Trait`, the trait's methods
/// are considered inherent methods.
pub fn iterate_method_candidates_split_inherent(
&self,
db: &dyn HirDatabase,
scope: &SemanticsScope<'_>,
traits_in_scope: &FxHashSet<TraitId>,
with_local_impls: Option<Module>,
name: Option<&Name>,
callback: &mut dyn FnMut(AssocItemId) -> ControlFlow<()>,
callback: impl MethodCandidateCallback,
) {
struct Callback<T>(T);

impl<T: MethodCandidateCallback> method_resolution::MethodCandidateCallback for Callback<T> {
fn on_inherent_method(
&mut self,
_adjustments: method_resolution::ReceiverAdjustments,
item: AssocItemId,
_is_visible: bool,
) -> ControlFlow<()> {
if let AssocItemId::FunctionId(func) = item {
self.0.on_inherent_method(func.into())
} else {
ControlFlow::Continue(())
}
}

fn on_trait_method(
&mut self,
_adjustments: method_resolution::ReceiverAdjustments,
item: AssocItemId,
_is_visible: bool,
) -> ControlFlow<()> {
if let AssocItemId::FunctionId(func) = item {
self.0.on_trait_method(func.into())
} else {
ControlFlow::Continue(())
}
}
}

let _p = tracing::info_span!(
"iterate_method_candidates_dyn",
with_local_impls = traits_in_scope.len(),
Expand All @@ -5294,7 +5325,7 @@ impl Type {
with_local_impls.and_then(|b| b.id.containing_block()).into(),
name,
method_resolution::LookupMode::MethodCall,
&mut |_adj, id, _| callback(id),
&mut Callback(callback),
);
}

Expand All @@ -5310,33 +5341,61 @@ impl Type {
) -> Option<T> {
let _p = tracing::info_span!("iterate_path_candidates").entered();
let mut slot = None;
self.iterate_path_candidates_dyn(

self.iterate_path_candidates_split_inherent(
db,
scope,
traits_in_scope,
with_local_impls,
name,
&mut |assoc_item_id| {
if let Some(res) = callback(assoc_item_id.into()) {
slot = Some(res);
return ControlFlow::Break(());
|item| match callback(item) {
it @ Some(_) => {
slot = it;
ControlFlow::Break(())
}
ControlFlow::Continue(())
None => ControlFlow::Continue(()),
},
);
slot
}

/// Iterates over inherent methods.
///
/// In some circumstances, inherent methods methods may actually be trait methods!
/// For example, when `dyn Trait` is a receiver, _trait_'s methods would be considered
/// to be inherent methods.
#[tracing::instrument(skip_all, fields(name = ?name))]
fn iterate_path_candidates_dyn(
pub fn iterate_path_candidates_split_inherent(
&self,
db: &dyn HirDatabase,
scope: &SemanticsScope<'_>,
traits_in_scope: &FxHashSet<TraitId>,
with_local_impls: Option<Module>,
name: Option<&Name>,
callback: &mut dyn FnMut(AssocItemId) -> ControlFlow<()>,
callback: impl PathCandidateCallback,
) {
struct Callback<T>(T);

impl<T: PathCandidateCallback> method_resolution::MethodCandidateCallback for Callback<T> {
fn on_inherent_method(
&mut self,
_adjustments: method_resolution::ReceiverAdjustments,
item: AssocItemId,
_is_visible: bool,
) -> ControlFlow<()> {
self.0.on_inherent_item(item.into())
}

fn on_trait_method(
&mut self,
_adjustments: method_resolution::ReceiverAdjustments,
item: AssocItemId,
_is_visible: bool,
) -> ControlFlow<()> {
self.0.on_trait_item(item.into())
}
}

let canonical = hir_ty::replace_errors_with_variables(&self.ty);

let krate = scope.krate();
Expand All @@ -5352,7 +5411,7 @@ impl Type {
traits_in_scope,
with_local_impls.and_then(|b| b.id.containing_block()).into(),
name,
callback,
&mut Callback(callback),
);
}

Expand Down Expand Up @@ -6060,3 +6119,41 @@ fn push_ty_diagnostics(
);
}
}

pub trait MethodCandidateCallback {
fn on_inherent_method(&mut self, f: Function) -> ControlFlow<()>;

fn on_trait_method(&mut self, f: Function) -> ControlFlow<()>;
}

impl<F> MethodCandidateCallback for F
where
F: FnMut(Function) -> ControlFlow<()>,
{
fn on_inherent_method(&mut self, f: Function) -> ControlFlow<()> {
self(f)
}

fn on_trait_method(&mut self, f: Function) -> ControlFlow<()> {
self(f)
}
}

pub trait PathCandidateCallback {
fn on_inherent_item(&mut self, item: AssocItem) -> ControlFlow<()>;

fn on_trait_item(&mut self, item: AssocItem) -> ControlFlow<()>;
}

impl<F> PathCandidateCallback for F
where
F: FnMut(AssocItem) -> ControlFlow<()>,
{
fn on_inherent_item(&mut self, item: AssocItem) -> ControlFlow<()> {
self(item)
}

fn on_trait_item(&mut self, item: AssocItem) -> ControlFlow<()> {
self(item)
}
}
5 changes: 5 additions & 0 deletions crates/hir/src/semantics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2060,6 +2060,11 @@ impl SemanticsScope<'_> {
)
}

pub fn resolve_mod_path(&self, path: &ModPath) -> impl Iterator<Item = ItemInNs> {
let items = self.resolver.resolve_module_path_in_items(self.db.upcast(), path);
items.iter_items().map(|(item, _)| item.into())
}

/// Iterates over associated types that may be specified after the given path (using
/// `Ty::Assoc` syntax).
pub fn assoc_type_shorthand_candidates<R>(
Expand Down
Loading

0 comments on commit 7e639ee

Please sign in to comment.