Skip to content

Commit 96e9652

Browse files
committed
goto where trait method impl
1 parent f58c26e commit 96e9652

File tree

5 files changed

+365
-116
lines changed

5 files changed

+365
-116
lines changed

crates/hir-ty/src/method_resolution.rs

Lines changed: 145 additions & 84 deletions
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,32 @@ pub(crate) fn iterate_method_candidates<T>(
575576
slot
576577
}
577578

579+
pub fn lookup_trait_m_for_self_ty(
580+
self_ty: &Ty,
581+
db: &dyn HirDatabase,
582+
env: Arc<TraitEnvironment>,
583+
implied_trait: TraitId,
584+
name: &Name,
585+
) -> Option<FunctionId> {
586+
let self_ty_tp = 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(implied_trait, self_ty_tp);
589+
let mut table = InferenceTable::new(db, env.clone());
590+
if let Some(data) = Valid::valid_impl(impls, &mut table, &self_ty) {
591+
for &impl_item in data.items.iter() {
592+
if Valid::is_valid_item(&mut table, Some(name), None, impl_item, self_ty, None) {
593+
match impl_item {
594+
AssocItemId::FunctionId(f) => {
595+
return Some(f);
596+
}
597+
_ => (),
598+
}
599+
}
600+
}
601+
}
602+
None
603+
}
604+
578605
pub fn iterate_path_candidates(
579606
ty: &Canonical<Ty>,
580607
db: &dyn HirDatabase,
@@ -850,7 +877,7 @@ fn iterate_trait_method_candidates(
850877
for &(_, item) in data.items.iter() {
851878
// Don't pass a `visible_from_module` down to `is_valid_candidate`,
852879
// since only inherent methods should be included into visibility checking.
853-
if !is_valid_candidate(table, name, receiver_ty, item, self_ty, None) {
880+
if !Valid::is_valid_item(table, name, receiver_ty, item, self_ty, None) {
854881
continue;
855882
}
856883
if !known_implemented {
@@ -932,8 +959,14 @@ fn iterate_inherent_methods(
932959
let impls_for_self_ty = impls.for_self_ty(self_ty);
933960
for &impl_def in impls_for_self_ty {
934961
for &item in &db.impl_data(impl_def).items {
935-
if !is_valid_candidate(table, name, receiver_ty, item, self_ty, visible_from_module)
936-
{
962+
if !Valid::is_valid_item(
963+
table,
964+
name,
965+
receiver_ty,
966+
item,
967+
self_ty,
968+
visible_from_module,
969+
) {
937970
continue;
938971
}
939972
callback(receiver_adjustments.clone().unwrap_or_default(), item)?;
@@ -961,97 +994,125 @@ pub fn resolve_indexing_op(
961994
}
962995
None
963996
}
997+
struct Valid;
998+
impl Valid {
999+
fn valid_impl(
1000+
impls: impl Iterator<Item = ImplId>,
1001+
table: &mut InferenceTable,
1002+
self_ty: &Ty,
1003+
) -> Option<Arc<ImplData>> {
1004+
let db = table.db;
1005+
for impl_ in impls {
1006+
let impl_data = db.impl_data(impl_);
1007+
let substs =
1008+
TyBuilder::subst_for_def(db, impl_).fill_with_inference_vars(table).build();
1009+
let impl_ty =
1010+
substs.apply(db.impl_self_ty(impl_).into_value_and_skipped_binders().0, Interner);
1011+
1012+
if !table.unify(self_ty, &impl_ty) {
1013+
continue;
1014+
}
9641015

965-
fn is_valid_candidate(
966-
table: &mut InferenceTable,
967-
name: Option<&Name>,
968-
receiver_ty: Option<&Ty>,
969-
item: AssocItemId,
970-
self_ty: &Ty,
971-
visible_from_module: Option<ModuleId>,
972-
) -> bool {
973-
let db = table.db;
974-
match item {
975-
AssocItemId::FunctionId(m) => {
976-
let data = db.function_data(m);
977-
if let Some(name) = name {
978-
if &data.name != name {
979-
return false;
980-
}
1016+
let wh_goals = crate::chalk_db::convert_where_clauses(db, impl_.into(), &substs)
1017+
.into_iter()
1018+
.map(|b| b.into_well_formed_goal(Interner).cast(Interner));
1019+
1020+
let goal = crate::Goal::all(Interner, wh_goals);
1021+
1022+
if table.try_obligation(goal).is_some() {
1023+
return Some(impl_data);
9811024
}
982-
table.run_in_snapshot(|table| {
983-
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 {
985-
ItemContainerId::TraitId(_) => {
986-
subst.at(Interner, 0).assert_ty_ref(Interner).clone()
987-
}
988-
ItemContainerId::ImplId(impl_id) => {
989-
subst.apply(db.impl_self_ty(impl_id).skip_binders().clone(), Interner)
990-
}
991-
// We should only get called for associated items (impl/trait)
992-
ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => {
993-
unreachable!()
994-
}
995-
};
996-
if !table.unify(&expected_self_ty, &self_ty) {
1025+
}
1026+
None
1027+
}
1028+
1029+
fn is_valid_item(
1030+
table: &mut InferenceTable,
1031+
name: Option<&Name>,
1032+
receiver_ty: Option<&Ty>,
1033+
item: AssocItemId,
1034+
self_ty: &Ty,
1035+
visible_from_module: Option<ModuleId>,
1036+
) -> bool {
1037+
macro_rules! assert {
1038+
($cond:expr) => {
1039+
if !$cond {
9971040
return false;
9981041
}
999-
if let Some(receiver_ty) = receiver_ty {
1000-
if !data.has_self_param() {
1001-
return false;
1002-
}
1042+
};
1043+
}
10031044

1004-
let sig = db.callable_item_signature(m.into());
1005-
let expected_receiver =
1006-
sig.map(|s| s.params()[0].clone()).substitute(Interner, &subst);
1007-
let receiver_matches = table.unify(&receiver_ty, &expected_receiver);
1045+
let db = table.db;
1046+
match item {
1047+
AssocItemId::FunctionId(m) => {
1048+
let data = db.function_data(m);
1049+
1050+
assert!(name.map_or(true, |n| n == &data.name));
1051+
assert!(visible_from_module.map_or(true, |from_module| {
1052+
let v = db.function_visibility(m).is_visible_from(db.upcast(), from_module);
1053+
if !v {
1054+
cov_mark::hit!(autoderef_candidate_not_visible);
1055+
}
1056+
v
1057+
}));
10081058

1009-
if !receiver_matches {
1010-
return false;
1059+
table.run_in_snapshot(|table| {
1060+
let subst =
1061+
TyBuilder::subst_for_def(db, m).fill_with_inference_vars(table).build();
1062+
let expect_self_ty = match m.lookup(db.upcast()).container {
1063+
ItemContainerId::TraitId(_) => {
1064+
subst.at(Interner, 0).assert_ty_ref(Interner).clone()
1065+
}
1066+
ItemContainerId::ImplId(impl_id) => {
1067+
subst.apply(db.impl_self_ty(impl_id).skip_binders().clone(), Interner)
1068+
}
1069+
// We should only get called for associated items (impl/trait)
1070+
ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => {
1071+
unreachable!()
1072+
}
1073+
};
1074+
assert!(table.unify(&expect_self_ty, self_ty));
1075+
if let Some(receiver_ty) = receiver_ty {
1076+
assert!(data.has_self_param());
1077+
1078+
let sig = db.callable_item_signature(m.into());
1079+
let expected_receiver =
1080+
sig.map(|s| s.params()[0].clone()).substitute(Interner, &subst);
1081+
1082+
assert!(table.unify(&receiver_ty, &expected_receiver));
10111083
}
1012-
}
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);
1084+
true
1085+
})
1086+
}
1087+
AssocItemId::ConstId(c) => {
1088+
let data = db.const_data(c);
1089+
assert!(receiver_ty.is_none());
1090+
1091+
assert!(name.map_or(true, |n| data.name.as_ref() == Some(n)));
1092+
assert!(visible_from_module.map_or(true, |from_module| {
1093+
let v = db.const_visibility(c).is_visible_from(db.upcast(), from_module);
1094+
if !v {
1095+
cov_mark::hit!(const_candidate_not_visible);
1096+
}
1097+
v
1098+
}));
1099+
if let ItemContainerId::ImplId(impl_id) = c.lookup(db.upcast()).container {
1100+
let self_ty_matches = table.run_in_snapshot(|table| {
1101+
let subst =
1102+
TyBuilder::subst_for_def(db, c).fill_with_inference_vars(table).build();
1103+
let expected_self_ty =
1104+
subst.apply(db.impl_self_ty(impl_id).skip_binders().clone(), Interner);
1105+
table.unify(&expected_self_ty, &self_ty)
1106+
});
1107+
if !self_ty_matches {
1108+
cov_mark::hit!(const_candidate_self_type_mismatch);
10161109
return false;
10171110
}
10181111
}
1019-
10201112
true
1021-
})
1022-
}
1023-
AssocItemId::ConstId(c) => {
1024-
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) {
1035-
cov_mark::hit!(const_candidate_not_visible);
1036-
return false;
1037-
}
1038-
}
1039-
if let ItemContainerId::ImplId(impl_id) = c.lookup(db.upcast()).container {
1040-
let self_ty_matches = table.run_in_snapshot(|table| {
1041-
let subst =
1042-
TyBuilder::subst_for_def(db, c).fill_with_inference_vars(table).build();
1043-
let expected_self_ty =
1044-
subst.apply(db.impl_self_ty(impl_id).skip_binders().clone(), Interner);
1045-
table.unify(&expected_self_ty, &self_ty)
1046-
});
1047-
if !self_ty_matches {
1048-
cov_mark::hit!(const_candidate_self_type_mismatch);
1049-
return false;
1050-
}
10511113
}
1052-
true
1114+
_ => false,
10531115
}
1054-
_ => false,
10551116
}
10561117
}
10571118

crates/hir/src/semantics.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,9 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
348348
self.imp.resolve_method_call(call).map(Function::from)
349349
}
350350

351+
pub fn resolve_impl_method(&self, call: &ast::Expr) -> Option<Function> {
352+
self.imp.resolve_impl_method(call).map(Function::from)
353+
}
351354
pub fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option<Callable> {
352355
self.imp.resolve_method_call_as_callable(call)
353356
}
@@ -973,6 +976,10 @@ impl<'db> SemanticsImpl<'db> {
973976
self.analyze(call.syntax())?.resolve_method_call(self.db, call).map(|(id, _)| id)
974977
}
975978

979+
fn resolve_impl_method(&self, call: &ast::Expr) -> Option<FunctionId> {
980+
self.analyze(call.syntax())?.resolve_impl_method(self.db, call)
981+
}
982+
976983
fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option<Callable> {
977984
let source_analyzer = self.analyze(call.syntax())?;
978985
let (func, subst) = source_analyzer.resolve_method_call(self.db, call)?;

crates/hir/src/source_analyzer.rs

Lines changed: 58 additions & 3 deletions
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, DefWithBodyId, FieldId, FunctionId, ItemContainerId, LocalFieldId, Lookup,
25+
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::{
@@ -241,6 +242,60 @@ impl SourceAnalyzer {
241242
self.infer.as_ref()?.method_resolution(expr_id)
242243
}
243244

245+
pub(crate) fn resolve_impl_method(
246+
&self,
247+
db: &dyn HirDatabase,
248+
call: &ast::Expr,
249+
) -> Option<FunctionId> {
250+
let infered = self.infer.as_ref()?;
251+
let expr_id = self.expr_id(db, call)?;
252+
253+
let mut fun_info = None;
254+
match call {
255+
&ast::Expr::MethodCallExpr(..) => {
256+
let (func, subs) = infered.method_resolution(expr_id)?;
257+
if subs.is_empty(Interner) {
258+
return None;
259+
}
260+
fun_info.replace((func, subs.at(Interner, 0).ty(Interner)?.clone()));
261+
}
262+
&ast::Expr::PathExpr(..) => {
263+
let func_ty = infered.type_of_expr.get(expr_id)?;
264+
if let TyKind::FnDef(fn_def, subs) = func_ty.kind(Interner) {
265+
if subs.is_empty(Interner) {
266+
return None;
267+
}
268+
if let hir_ty::CallableDefId::FunctionId(f_id) =
269+
db.lookup_intern_callable_def(fn_def.clone().into())
270+
{
271+
fun_info.replace((f_id, subs.at(Interner, 0).ty(Interner)?.clone()));
272+
}
273+
}
274+
}
275+
_ => (),
276+
};
277+
let (func, self_ty) = fun_info?;
278+
let implied_trait = match func.lookup(db.upcast()).container {
279+
ItemContainerId::TraitId(trait_id) => trait_id,
280+
_ => return None,
281+
};
282+
283+
let krate = self.resolver.krate();
284+
let trait_env = self.resolver.body_owner()?.as_generic_def_id().map_or_else(
285+
|| Arc::new(hir_ty::TraitEnvironment::empty(krate)),
286+
|d| db.trait_environment(d),
287+
);
288+
289+
let fun_data = db.function_data(func);
290+
method_resolution::lookup_trait_m_for_self_ty(
291+
&self_ty,
292+
db,
293+
trait_env,
294+
implied_trait,
295+
&fun_data.name,
296+
)
297+
}
298+
244299
pub(crate) fn resolve_field(
245300
&self,
246301
db: &dyn HirDatabase,

0 commit comments

Comments
 (0)