Skip to content

Commit 22e53f1

Browse files
committed
Auto merge of #12549 - bitgaoshu:goto_where_trait_m_impl, r=Veykril
feat: Go to implementation of trait methods try goto where the trait method implies, #4558
2 parents ea41617 + 38c11be commit 22e53f1

File tree

11 files changed

+419
-130
lines changed

11 files changed

+419
-130
lines changed

crates/hir-ty/src/method_resolution.rs

+86-37
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@ use arrayvec::ArrayVec;
88
use base_db::{CrateId, Edition};
99
use chalk_ir::{cast::Cast, Mutability, UniverseIndex};
1010
use hir_def::{
11-
item_scope::ItemScope, nameres::DefMap, AssocItemId, BlockId, ConstId, FunctionId,
12-
GenericDefId, HasModule, ImplId, ItemContainerId, Lookup, ModuleDefId, ModuleId, TraitId,
11+
data::ImplData, item_scope::ItemScope, nameres::DefMap, AssocItemId, BlockId, ConstId,
12+
FunctionId, GenericDefId, HasModule, ImplId, ItemContainerId, Lookup, ModuleDefId, ModuleId,
13+
TraitId,
1314
};
1415
use hir_expand::name::Name;
1516
use rustc_hash::{FxHashMap, FxHashSet};
@@ -247,7 +248,7 @@ impl TraitImpls {
247248
self.map
248249
.get(&trait_)
249250
.into_iter()
250-
.flat_map(move |map| map.get(&None).into_iter().chain(map.get(&Some(self_ty))))
251+
.flat_map(move |map| map.get(&Some(self_ty)).into_iter().chain(map.get(&None)))
251252
.flat_map(|v| v.iter().copied())
252253
}
253254

@@ -575,6 +576,59 @@ pub(crate) fn iterate_method_candidates<T>(
575576
slot
576577
}
577578

579+
pub fn lookup_impl_method(
580+
self_ty: &Ty,
581+
db: &dyn HirDatabase,
582+
env: Arc<TraitEnvironment>,
583+
trait_: TraitId,
584+
name: &Name,
585+
) -> Option<FunctionId> {
586+
let self_ty_fp = TyFingerprint::for_trait_impl(self_ty)?;
587+
let trait_impls = TraitImpls::trait_impls_in_deps_query(db, env.krate);
588+
let impls = trait_impls.for_trait_and_self_ty(trait_, self_ty_fp);
589+
let mut table = InferenceTable::new(db, env.clone());
590+
find_matching_impl(impls, &mut table, &self_ty).and_then(|data| {
591+
data.items.iter().find_map(|it| match it {
592+
AssocItemId::FunctionId(f) => (db.function_data(*f).name == *name).then(|| *f),
593+
_ => None,
594+
})
595+
})
596+
}
597+
598+
fn find_matching_impl(
599+
mut impls: impl Iterator<Item = ImplId>,
600+
table: &mut InferenceTable,
601+
self_ty: &Ty,
602+
) -> Option<Arc<ImplData>> {
603+
let db = table.db;
604+
loop {
605+
let impl_ = impls.next()?;
606+
let r = table.run_in_snapshot(|table| {
607+
let impl_data = db.impl_data(impl_);
608+
let substs =
609+
TyBuilder::subst_for_def(db, impl_).fill_with_inference_vars(table).build();
610+
let impl_ty = db.impl_self_ty(impl_).substitute(Interner, &substs);
611+
612+
table
613+
.unify(self_ty, &impl_ty)
614+
.then(|| {
615+
let wh_goals =
616+
crate::chalk_db::convert_where_clauses(db, impl_.into(), &substs)
617+
.into_iter()
618+
.map(|b| b.cast(Interner));
619+
620+
let goal = crate::Goal::all(Interner, wh_goals);
621+
622+
table.try_obligation(goal).map(|_| impl_data)
623+
})
624+
.flatten()
625+
});
626+
if r.is_some() {
627+
break r;
628+
}
629+
}
630+
}
631+
578632
pub fn iterate_path_candidates(
579633
ty: &Canonical<Ty>,
580634
db: &dyn HirDatabase,
@@ -970,18 +1024,31 @@ fn is_valid_candidate(
9701024
self_ty: &Ty,
9711025
visible_from_module: Option<ModuleId>,
9721026
) -> bool {
1027+
macro_rules! check_that {
1028+
($cond:expr) => {
1029+
if !$cond {
1030+
return false;
1031+
}
1032+
};
1033+
}
1034+
9731035
let db = table.db;
9741036
match item {
9751037
AssocItemId::FunctionId(m) => {
9761038
let data = db.function_data(m);
977-
if let Some(name) = name {
978-
if &data.name != name {
979-
return false;
1039+
1040+
check_that!(name.map_or(true, |n| n == &data.name));
1041+
check_that!(visible_from_module.map_or(true, |from_module| {
1042+
let v = db.function_visibility(m).is_visible_from(db.upcast(), from_module);
1043+
if !v {
1044+
cov_mark::hit!(autoderef_candidate_not_visible);
9801045
}
981-
}
1046+
v
1047+
}));
1048+
9821049
table.run_in_snapshot(|table| {
9831050
let subst = TyBuilder::subst_for_def(db, m).fill_with_inference_vars(table).build();
984-
let expected_self_ty = match m.lookup(db.upcast()).container {
1051+
let expect_self_ty = match m.lookup(db.upcast()).container {
9851052
ItemContainerId::TraitId(_) => {
9861053
subst.at(Interner, 0).assert_ty_ref(Interner).clone()
9871054
}
@@ -993,49 +1060,31 @@ fn is_valid_candidate(
9931060
unreachable!()
9941061
}
9951062
};
996-
if !table.unify(&expected_self_ty, &self_ty) {
997-
return false;
998-
}
1063+
check_that!(table.unify(&expect_self_ty, self_ty));
9991064
if let Some(receiver_ty) = receiver_ty {
1000-
if !data.has_self_param() {
1001-
return false;
1002-
}
1065+
check_that!(data.has_self_param());
10031066

10041067
let sig = db.callable_item_signature(m.into());
10051068
let expected_receiver =
10061069
sig.map(|s| s.params()[0].clone()).substitute(Interner, &subst);
1007-
let receiver_matches = table.unify(&receiver_ty, &expected_receiver);
10081070

1009-
if !receiver_matches {
1010-
return false;
1011-
}
1071+
check_that!(table.unify(&receiver_ty, &expected_receiver));
10121072
}
1013-
if let Some(from_module) = visible_from_module {
1014-
if !db.function_visibility(m).is_visible_from(db.upcast(), from_module) {
1015-
cov_mark::hit!(autoderef_candidate_not_visible);
1016-
return false;
1017-
}
1018-
}
1019-
10201073
true
10211074
})
10221075
}
10231076
AssocItemId::ConstId(c) => {
10241077
let data = db.const_data(c);
1025-
if receiver_ty.is_some() {
1026-
return false;
1027-
}
1028-
if let Some(name) = name {
1029-
if data.name.as_ref() != Some(name) {
1030-
return false;
1031-
}
1032-
}
1033-
if let Some(from_module) = visible_from_module {
1034-
if !db.const_visibility(c).is_visible_from(db.upcast(), from_module) {
1078+
check_that!(receiver_ty.is_none());
1079+
1080+
check_that!(name.map_or(true, |n| data.name.as_ref() == Some(n)));
1081+
check_that!(visible_from_module.map_or(true, |from_module| {
1082+
let v = db.const_visibility(c).is_visible_from(db.upcast(), from_module);
1083+
if !v {
10351084
cov_mark::hit!(const_candidate_not_visible);
1036-
return false;
10371085
}
1038-
}
1086+
v
1087+
}));
10391088
if let ItemContainerId::ImplId(impl_id) = c.lookup(db.upcast()).container {
10401089
let self_ty_matches = table.run_in_snapshot(|table| {
10411090
let subst =

crates/hir/src/semantics.rs

+2-10
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ use hir_expand::{
1616
name::{known, AsName},
1717
ExpansionInfo, MacroCallId,
1818
};
19-
use hir_ty::Interner;
2019
use itertools::Itertools;
2120
use rustc_hash::{FxHashMap, FxHashSet};
2221
use smallvec::{smallvec, SmallVec};
@@ -975,18 +974,11 @@ impl<'db> SemanticsImpl<'db> {
975974
}
976975

977976
fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<FunctionId> {
978-
self.analyze(call.syntax())?.resolve_method_call(self.db, call).map(|(id, _)| id)
977+
self.analyze(call.syntax())?.resolve_method_call(self.db, call)
979978
}
980979

981980
fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option<Callable> {
982-
let source_analyzer = self.analyze(call.syntax())?;
983-
let (func, subst) = source_analyzer.resolve_method_call(self.db, call)?;
984-
let ty = self.db.value_ty(func.into()).substitute(Interner, &subst);
985-
let resolver = source_analyzer.resolver;
986-
let ty = Type::new_with_resolver(self.db, &resolver, ty);
987-
let mut res = ty.as_callable(self.db)?;
988-
res.is_bound_method = true;
989-
Some(res)
981+
self.analyze(call.syntax())?.resolve_method_call_as_callable(self.db, call)
990982
}
991983

992984
fn resolve_field(&self, field: &ast::FieldExpr) -> Option<Field> {

crates/hir/src/source_analyzer.rs

+67-7
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ use hir_def::{
2121
path::{ModPath, Path, PathKind},
2222
resolver::{resolver_for_scope, Resolver, TypeNs, ValueNs},
2323
type_ref::Mutability,
24-
AsMacroCall, DefWithBodyId, FieldId, FunctionId, LocalFieldId, Lookup, ModuleDefId, VariantId,
24+
AsMacroCall, AssocItemId, DefWithBodyId, FieldId, FunctionId, ItemContainerId, LocalFieldId,
25+
Lookup, ModuleDefId, VariantId,
2526
};
2627
use hir_expand::{
2728
builtin_fn_macro::BuiltinFnLikeExpander, hygiene::Hygiene, name::AsName, HirFileId, InFile,
@@ -31,8 +32,8 @@ use hir_ty::{
3132
record_literal_missing_fields, record_pattern_missing_fields, unsafe_expressions,
3233
UnsafeExpr,
3334
},
34-
Adjust, Adjustment, AutoBorrow, InferenceResult, Interner, Substitution, TyExt,
35-
TyLoweringContext,
35+
method_resolution, Adjust, Adjustment, AutoBorrow, InferenceResult, Interner, Substitution,
36+
TyExt, TyKind, TyLoweringContext,
3637
};
3738
use smallvec::SmallVec;
3839
use syntax::{
@@ -42,8 +43,8 @@ use syntax::{
4243

4344
use crate::{
4445
db::HirDatabase, semantics::PathResolution, Adt, AssocItem, BindingMode, BuiltinAttr,
45-
BuiltinType, Const, Field, Function, Local, Macro, ModuleDef, Static, Struct, ToolModule,
46-
Trait, Type, TypeAlias, Variant,
46+
BuiltinType, Callable, Const, Field, Function, Local, Macro, ModuleDef, Static, Struct,
47+
ToolModule, Trait, Type, TypeAlias, Variant,
4748
};
4849

4950
/// `SourceAnalyzer` is a convenience wrapper which exposes HIR API in terms of
@@ -232,13 +233,29 @@ impl SourceAnalyzer {
232233
)
233234
}
234235

236+
pub(crate) fn resolve_method_call_as_callable(
237+
&self,
238+
db: &dyn HirDatabase,
239+
call: &ast::MethodCallExpr,
240+
) -> Option<Callable> {
241+
let expr_id = self.expr_id(db, &call.clone().into())?;
242+
let (func, substs) = self.infer.as_ref()?.method_resolution(expr_id)?;
243+
let ty = db.value_ty(func.into()).substitute(Interner, &substs);
244+
let ty = Type::new_with_resolver(db, &self.resolver, ty);
245+
let mut res = ty.as_callable(db)?;
246+
res.is_bound_method = true;
247+
Some(res)
248+
}
249+
235250
pub(crate) fn resolve_method_call(
236251
&self,
237252
db: &dyn HirDatabase,
238253
call: &ast::MethodCallExpr,
239-
) -> Option<(FunctionId, Substitution)> {
254+
) -> Option<FunctionId> {
240255
let expr_id = self.expr_id(db, &call.clone().into())?;
241-
self.infer.as_ref()?.method_resolution(expr_id)
256+
let (f_in_trait, substs) = self.infer.as_ref()?.method_resolution(expr_id)?;
257+
let f_in_impl = self.resolve_impl_method(db, f_in_trait, &substs);
258+
f_in_impl.or(Some(f_in_trait))
242259
}
243260

244261
pub(crate) fn resolve_field(
@@ -336,6 +353,25 @@ impl SourceAnalyzer {
336353
let expr_id = self.expr_id(db, &path_expr.into())?;
337354
let infer = self.infer.as_ref()?;
338355
if let Some(assoc) = infer.assoc_resolutions_for_expr(expr_id) {
356+
let assoc = match assoc {
357+
AssocItemId::FunctionId(f_in_trait) => {
358+
match infer.type_of_expr.get(expr_id) {
359+
None => assoc,
360+
Some(func_ty) => {
361+
if let TyKind::FnDef(_fn_def, subs) = func_ty.kind(Interner) {
362+
self.resolve_impl_method(db, f_in_trait, subs)
363+
.map(AssocItemId::FunctionId)
364+
.unwrap_or(assoc)
365+
} else {
366+
assoc
367+
}
368+
}
369+
}
370+
}
371+
372+
_ => assoc,
373+
};
374+
339375
return Some(PathResolution::Def(AssocItem::from(assoc).into()));
340376
}
341377
if let Some(VariantId::EnumVariantId(variant)) =
@@ -563,6 +599,30 @@ impl SourceAnalyzer {
563599
}
564600
false
565601
}
602+
603+
fn resolve_impl_method(
604+
&self,
605+
db: &dyn HirDatabase,
606+
func: FunctionId,
607+
substs: &Substitution,
608+
) -> Option<FunctionId> {
609+
let impled_trait = match func.lookup(db.upcast()).container {
610+
ItemContainerId::TraitId(trait_id) => trait_id,
611+
_ => return None,
612+
};
613+
if substs.is_empty(Interner) {
614+
return None;
615+
}
616+
let self_ty = substs.at(Interner, 0).ty(Interner)?;
617+
let krate = self.resolver.krate();
618+
let trait_env = self.resolver.body_owner()?.as_generic_def_id().map_or_else(
619+
|| Arc::new(hir_ty::TraitEnvironment::empty(krate)),
620+
|d| db.trait_environment(d),
621+
);
622+
623+
let fun_data = db.function_data(func);
624+
method_resolution::lookup_impl_method(self_ty, db, trait_env, impled_trait, &fun_data.name)
625+
}
566626
}
567627

568628
fn scope_for(

crates/ide-assists/src/handlers/qualify_method_call.rs

+22-5
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
1-
use hir::{ItemInNs, ModuleDef};
2-
use ide_db::{
3-
assists::{AssistId, AssistKind},
4-
imports::import_assets::item_for_path_search,
5-
};
1+
use hir::{db::HirDatabase, AsAssocItem, AssocItem, AssocItemContainer, ItemInNs, ModuleDef};
2+
use ide_db::assists::{AssistId, AssistKind};
63
use syntax::{ast, AstNode};
74

85
use crate::{
@@ -67,6 +64,26 @@ pub(crate) fn qualify_method_call(acc: &mut Assists, ctx: &AssistContext) -> Opt
6764
Some(())
6865
}
6966

67+
fn item_for_path_search(db: &dyn HirDatabase, item: ItemInNs) -> Option<ItemInNs> {
68+
Some(match item {
69+
ItemInNs::Types(_) | ItemInNs::Values(_) => match item_as_assoc(db, item) {
70+
Some(assoc_item) => match assoc_item.container(db) {
71+
AssocItemContainer::Trait(trait_) => ItemInNs::from(ModuleDef::from(trait_)),
72+
AssocItemContainer::Impl(impl_) => match impl_.trait_(db) {
73+
None => ItemInNs::from(ModuleDef::from(impl_.self_ty(db).as_adt()?)),
74+
Some(trait_) => ItemInNs::from(ModuleDef::from(trait_)),
75+
},
76+
},
77+
None => item,
78+
},
79+
ItemInNs::Macros(_) => item,
80+
})
81+
}
82+
83+
fn item_as_assoc(db: &dyn HirDatabase, item: ItemInNs) -> Option<AssocItem> {
84+
item.as_module_def().and_then(|module_def| module_def.as_assoc_item(db))
85+
}
86+
7087
#[cfg(test)]
7188
mod tests {
7289
use super::*;

crates/ide-assists/src/utils/suggest_name.rs

+15
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,21 @@ fn foo() { S.bar($01$0, 2) }
460460
);
461461
}
462462

463+
#[test]
464+
fn method_on_impl_trait() {
465+
check(
466+
r#"
467+
struct S;
468+
trait T {
469+
fn bar(&self, n: i32, m: u32);
470+
}
471+
impl T for S { fn bar(&self, n: i32, m: u32); }
472+
fn foo() { S.bar($01$0, 2) }
473+
"#,
474+
"n",
475+
);
476+
}
477+
463478
#[test]
464479
fn method_ufcs() {
465480
check(

0 commit comments

Comments
 (0)