Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--crates/hir-ty/src/method_resolution.rs229
-rw-r--r--crates/hir/src/semantics.rs7
-rw-r--r--crates/hir/src/source_analyzer.rs61
-rw-r--r--crates/ide-db/src/defs.rs45
-rw-r--r--crates/ide/src/goto_definition.rs139
5 files changed, 365 insertions, 116 deletions
diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs
index d5285c1710..afac92b266 100644
--- a/crates/hir-ty/src/method_resolution.rs
+++ b/crates/hir-ty/src/method_resolution.rs
@@ -8,8 +8,9 @@ use arrayvec::ArrayVec;
use base_db::{CrateId, Edition};
use chalk_ir::{cast::Cast, Mutability, UniverseIndex};
use hir_def::{
- item_scope::ItemScope, nameres::DefMap, AssocItemId, BlockId, ConstId, FunctionId,
- GenericDefId, HasModule, ImplId, ItemContainerId, Lookup, ModuleDefId, ModuleId, TraitId,
+ data::ImplData, item_scope::ItemScope, nameres::DefMap, AssocItemId, BlockId, ConstId,
+ FunctionId, GenericDefId, HasModule, ImplId, ItemContainerId, Lookup, ModuleDefId, ModuleId,
+ TraitId,
};
use hir_expand::name::Name;
use rustc_hash::{FxHashMap, FxHashSet};
@@ -247,7 +248,7 @@ impl TraitImpls {
self.map
.get(&trait_)
.into_iter()
- .flat_map(move |map| map.get(&None).into_iter().chain(map.get(&Some(self_ty))))
+ .flat_map(move |map| map.get(&Some(self_ty)).into_iter().chain(map.get(&None)))
.flat_map(|v| v.iter().copied())
}
@@ -575,6 +576,32 @@ pub(crate) fn iterate_method_candidates<T>(
slot
}
+pub fn lookup_trait_m_for_self_ty(
+ self_ty: &Ty,
+ db: &dyn HirDatabase,
+ env: Arc<TraitEnvironment>,
+ implied_trait: TraitId,
+ name: &Name,
+) -> Option<FunctionId> {
+ let self_ty_tp = TyFingerprint::for_trait_impl(self_ty)?;
+ let trait_impls = TraitImpls::trait_impls_in_deps_query(db, env.krate);
+ let impls = trait_impls.for_trait_and_self_ty(implied_trait, self_ty_tp);
+ let mut table = InferenceTable::new(db, env.clone());
+ if let Some(data) = Valid::valid_impl(impls, &mut table, &self_ty) {
+ for &impl_item in data.items.iter() {
+ if Valid::is_valid_item(&mut table, Some(name), None, impl_item, self_ty, None) {
+ match impl_item {
+ AssocItemId::FunctionId(f) => {
+ return Some(f);
+ }
+ _ => (),
+ }
+ }
+ }
+ }
+ None
+}
+
pub fn iterate_path_candidates(
ty: &Canonical<Ty>,
db: &dyn HirDatabase,
@@ -850,7 +877,7 @@ fn iterate_trait_method_candidates(
for &(_, item) in data.items.iter() {
// Don't pass a `visible_from_module` down to `is_valid_candidate`,
// since only inherent methods should be included into visibility checking.
- if !is_valid_candidate(table, name, receiver_ty, item, self_ty, None) {
+ if !Valid::is_valid_item(table, name, receiver_ty, item, self_ty, None) {
continue;
}
if !known_implemented {
@@ -932,8 +959,14 @@ fn iterate_inherent_methods(
let impls_for_self_ty = impls.for_self_ty(self_ty);
for &impl_def in impls_for_self_ty {
for &item in &db.impl_data(impl_def).items {
- if !is_valid_candidate(table, name, receiver_ty, item, self_ty, visible_from_module)
- {
+ if !Valid::is_valid_item(
+ table,
+ name,
+ receiver_ty,
+ item,
+ self_ty,
+ visible_from_module,
+ ) {
continue;
}
callback(receiver_adjustments.clone().unwrap_or_default(), item)?;
@@ -961,97 +994,125 @@ pub fn resolve_indexing_op(
}
None
}
+struct Valid;
+impl Valid {
+ fn valid_impl(
+ impls: impl Iterator<Item = ImplId>,
+ table: &mut InferenceTable,
+ self_ty: &Ty,
+ ) -> Option<Arc<ImplData>> {
+ let db = table.db;
+ for impl_ in impls {
+ let impl_data = db.impl_data(impl_);
+ let substs =
+ TyBuilder::subst_for_def(db, impl_).fill_with_inference_vars(table).build();
+ let impl_ty =
+ substs.apply(db.impl_self_ty(impl_).into_value_and_skipped_binders().0, Interner);
+
+ if !table.unify(self_ty, &impl_ty) {
+ continue;
+ }
-fn is_valid_candidate(
- table: &mut InferenceTable,
- name: Option<&Name>,
- receiver_ty: Option<&Ty>,
- item: AssocItemId,
- self_ty: &Ty,
- visible_from_module: Option<ModuleId>,
-) -> bool {
- let db = table.db;
- match item {
- AssocItemId::FunctionId(m) => {
- let data = db.function_data(m);
- if let Some(name) = name {
- if &data.name != name {
- return false;
- }
+ let wh_goals = crate::chalk_db::convert_where_clauses(db, impl_.into(), &substs)
+ .into_iter()
+ .map(|b| b.into_well_formed_goal(Interner).cast(Interner));
+
+ let goal = crate::Goal::all(Interner, wh_goals);
+
+ if table.try_obligation(goal).is_some() {
+ return Some(impl_data);
}
- table.run_in_snapshot(|table| {
- let subst = TyBuilder::subst_for_def(db, m).fill_with_inference_vars(table).build();
- let expected_self_ty = match m.lookup(db.upcast()).container {
- ItemContainerId::TraitId(_) => {
- subst.at(Interner, 0).assert_ty_ref(Interner).clone()
- }
- ItemContainerId::ImplId(impl_id) => {
- subst.apply(db.impl_self_ty(impl_id).skip_binders().clone(), Interner)
- }
- // We should only get called for associated items (impl/trait)
- ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => {
- unreachable!()
- }
- };
- if !table.unify(&expected_self_ty, &self_ty) {
+ }
+ None
+ }
+
+ fn is_valid_item(
+ table: &mut InferenceTable,
+ name: Option<&Name>,
+ receiver_ty: Option<&Ty>,
+ item: AssocItemId,
+ self_ty: &Ty,
+ visible_from_module: Option<ModuleId>,
+ ) -> bool {
+ macro_rules! assert {
+ ($cond:expr) => {
+ if !$cond {
return false;
}
- if let Some(receiver_ty) = receiver_ty {
- if !data.has_self_param() {
- return false;
- }
+ };
+ }
- let sig = db.callable_item_signature(m.into());
- let expected_receiver =
- sig.map(|s| s.params()[0].clone()).substitute(Interner, &subst);
- let receiver_matches = table.unify(&receiver_ty, &expected_receiver);
+ let db = table.db;
+ match item {
+ AssocItemId::FunctionId(m) => {
+ let data = db.function_data(m);
+
+ assert!(name.map_or(true, |n| n == &data.name));
+ assert!(visible_from_module.map_or(true, |from_module| {
+ let v = db.function_visibility(m).is_visible_from(db.upcast(), from_module);
+ if !v {
+ cov_mark::hit!(autoderef_candidate_not_visible);
+ }
+ v
+ }));
- if !receiver_matches {
- return false;
+ table.run_in_snapshot(|table| {
+ let subst =
+ TyBuilder::subst_for_def(db, m).fill_with_inference_vars(table).build();
+ let expect_self_ty = match m.lookup(db.upcast()).container {
+ ItemContainerId::TraitId(_) => {
+ subst.at(Interner, 0).assert_ty_ref(Interner).clone()
+ }
+ ItemContainerId::ImplId(impl_id) => {
+ subst.apply(db.impl_self_ty(impl_id).skip_binders().clone(), Interner)
+ }
+ // We should only get called for associated items (impl/trait)
+ ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => {
+ unreachable!()
+ }
+ };
+ assert!(table.unify(&expect_self_ty, self_ty));
+ if let Some(receiver_ty) = receiver_ty {
+ assert!(data.has_self_param());
+
+ let sig = db.callable_item_signature(m.into());
+ let expected_receiver =
+ sig.map(|s| s.params()[0].clone()).substitute(Interner, &subst);
+
+ assert!(table.unify(&receiver_ty, &expected_receiver));
}
- }
- if let Some(from_module) = visible_from_module {
- if !db.function_visibility(m).is_visible_from(db.upcast(), from_module) {
- cov_mark::hit!(autoderef_candidate_not_visible);
+ true
+ })
+ }
+ AssocItemId::ConstId(c) => {
+ let data = db.const_data(c);
+ assert!(receiver_ty.is_none());
+
+ assert!(name.map_or(true, |n| data.name.as_ref() == Some(n)));
+ assert!(visible_from_module.map_or(true, |from_module| {
+ let v = db.const_visibility(c).is_visible_from(db.upcast(), from_module);
+ if !v {
+ cov_mark::hit!(const_candidate_not_visible);
+ }
+ v
+ }));
+ if let ItemContainerId::ImplId(impl_id) = c.lookup(db.upcast()).container {
+ let self_ty_matches = table.run_in_snapshot(|table| {
+ let subst =
+ TyBuilder::subst_for_def(db, c).fill_with_inference_vars(table).build();
+ let expected_self_ty =
+ subst.apply(db.impl_self_ty(impl_id).skip_binders().clone(), Interner);
+ table.unify(&expected_self_ty, &self_ty)
+ });
+ if !self_ty_matches {
+ cov_mark::hit!(const_candidate_self_type_mismatch);
return false;
}
}
-
true
- })
- }
- AssocItemId::ConstId(c) => {
- let data = db.const_data(c);
- if receiver_ty.is_some() {
- return false;
- }
- if let Some(name) = name {
- if data.name.as_ref() != Some(name) {
- return false;
- }
- }
- if let Some(from_module) = visible_from_module {
- if !db.const_visibility(c).is_visible_from(db.upcast(), from_module) {
- cov_mark::hit!(const_candidate_not_visible);
- return false;
- }
- }
- if let ItemContainerId::ImplId(impl_id) = c.lookup(db.upcast()).container {
- let self_ty_matches = table.run_in_snapshot(|table| {
- let subst =
- TyBuilder::subst_for_def(db, c).fill_with_inference_vars(table).build();
- let expected_self_ty =
- subst.apply(db.impl_self_ty(impl_id).skip_binders().clone(), Interner);
- table.unify(&expected_self_ty, &self_ty)
- });
- if !self_ty_matches {
- cov_mark::hit!(const_candidate_self_type_mismatch);
- return false;
- }
}
- true
+ _ => false,
}
- _ => false,
}
}
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index aa10b0f878..2574adb35a 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -348,6 +348,9 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
self.imp.resolve_method_call(call).map(Function::from)
}
+ pub fn resolve_impl_method(&self, call: &ast::Expr) -> Option<Function> {
+ self.imp.resolve_impl_method(call).map(Function::from)
+ }
pub fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option<Callable> {
self.imp.resolve_method_call_as_callable(call)
}
@@ -978,6 +981,10 @@ impl<'db> SemanticsImpl<'db> {
self.analyze(call.syntax())?.resolve_method_call(self.db, call).map(|(id, _)| id)
}
+ fn resolve_impl_method(&self, call: &ast::Expr) -> Option<FunctionId> {
+ self.analyze(call.syntax())?.resolve_impl_method(self.db, call)
+ }
+
fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option<Callable> {
let source_analyzer = self.analyze(call.syntax())?;
let (func, subst) = source_analyzer.resolve_method_call(self.db, call)?;
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index d624d37577..4d8d6a1460 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -21,7 +21,8 @@ use hir_def::{
path::{ModPath, Path, PathKind},
resolver::{resolver_for_scope, Resolver, TypeNs, ValueNs},
type_ref::Mutability,
- AsMacroCall, DefWithBodyId, FieldId, FunctionId, LocalFieldId, Lookup, ModuleDefId, VariantId,
+ AsMacroCall, DefWithBodyId, FieldId, FunctionId, ItemContainerId, LocalFieldId, Lookup,
+ ModuleDefId, VariantId,
};
use hir_expand::{
builtin_fn_macro::BuiltinFnLikeExpander, hygiene::Hygiene, name::AsName, HirFileId, InFile,
@@ -31,8 +32,8 @@ use hir_ty::{
record_literal_missing_fields, record_pattern_missing_fields, unsafe_expressions,
UnsafeExpr,
},
- Adjust, Adjustment, AutoBorrow, InferenceResult, Interner, Substitution, TyExt,
- TyLoweringContext,
+ method_resolution, Adjust, Adjustment, AutoBorrow, InferenceResult, Interner, Substitution,
+ TyExt, TyKind, TyLoweringContext,
};
use smallvec::SmallVec;
use syntax::{
@@ -247,6 +248,60 @@ impl SourceAnalyzer {
self.infer.as_ref()?.method_resolution(expr_id)
}
+ pub(crate) fn resolve_impl_method(
+ &self,
+ db: &dyn HirDatabase,
+ call: &ast::Expr,
+ ) -> Option<FunctionId> {
+ let infered = self.infer.as_ref()?;
+ let expr_id = self.expr_id(db, call)?;
+
+ let mut fun_info = None;
+ match call {
+ &ast::Expr::MethodCallExpr(..) => {
+ let (func, subs) = infered.method_resolution(expr_id)?;
+ if subs.is_empty(Interner) {
+ return None;
+ }
+ fun_info.replace((func, subs.at(Interner, 0).ty(Interner)?.clone()));
+ }
+ &ast::Expr::PathExpr(..) => {
+ let func_ty = infered.type_of_expr.get(expr_id)?;
+ if let TyKind::FnDef(fn_def, subs) = func_ty.kind(Interner) {
+ if subs.is_empty(Interner) {
+ return None;
+ }
+ if let hir_ty::CallableDefId::FunctionId(f_id) =
+ db.lookup_intern_callable_def(fn_def.clone().into())
+ {
+ fun_info.replace((f_id, subs.at(Interner, 0).ty(Interner)?.clone()));
+ }
+ }
+ }
+ _ => (),
+ };
+ let (func, self_ty) = fun_info?;
+ let implied_trait = match func.lookup(db.upcast()).container {
+ ItemContainerId::TraitId(trait_id) => trait_id,
+ _ => return None,
+ };
+
+ let krate = self.resolver.krate();
+ let trait_env = self.resolver.body_owner()?.as_generic_def_id().map_or_else(
+ || Arc::new(hir_ty::TraitEnvironment::empty(krate)),
+ |d| db.trait_environment(d),
+ );
+
+ let fun_data = db.function_data(func);
+ method_resolution::lookup_trait_m_for_self_ty(
+ &self_ty,
+ db,
+ trait_env,
+ implied_trait,
+ &fun_data.name,
+ )
+ }
+
pub(crate) fn resolve_field(
&self,
db: &dyn HirDatabase,
diff --git a/crates/ide-db/src/defs.rs b/crates/ide-db/src/defs.rs
index 540cc70dd5..e5390eeb32 100644
--- a/crates/ide-db/src/defs.rs
+++ b/crates/ide-db/src/defs.rs
@@ -162,6 +162,22 @@ impl IdentClass {
.or_else(|| NameClass::classify_lifetime(sema, lifetime).map(IdentClass::NameClass))
}
+ pub fn classify_token_to_impl(
+ sema: &Semantics<RootDatabase>,
+ token: &SyntaxToken,
+ ) -> Option<Definition> {
+ let p = token.parent()?;
+ match_ast! {
+ match p {
+ ast::NameRef(name_ref) => match NameRefClass::classify_to_impl(sema, name_ref)? {
+ NameRefClass::Definition(d) => Some(d),
+ _ => None,
+ },
+ _ => None,
+ }
+ }
+ }
+
pub fn definitions(self) -> ArrayVec<Definition, 2> {
let mut res = ArrayVec::new();
match self {
@@ -417,6 +433,35 @@ impl NameRefClass {
}
}
+ fn classify_to_impl(
+ sema: &Semantics<RootDatabase>,
+ name_ref: ast::NameRef,
+ ) -> Option<NameRefClass> {
+ let parent = name_ref.syntax().parent()?;
+ match_ast! {
+ match parent {
+ ast::MethodCallExpr(method_call) => {
+ sema.resolve_impl_method(&ast::Expr::MethodCallExpr(method_call))
+ .map(Definition::Function)
+ .map(NameRefClass::Definition)
+ },
+ ast::PathSegment(ps) => {
+ ps.syntax().parent().and_then(ast::Path::cast)
+ .map(|p|
+ p.syntax()
+ .parent()
+ .and_then(ast::PathExpr::cast)
+ .map(|pe|
+ sema.resolve_impl_method(&ast::Expr::PathExpr(pe))
+ .map(Definition::Function)
+ .map(NameRefClass::Definition)
+ ).flatten()
+ ).flatten()
+ },
+ _=> None
+ }
+ }
+ }
pub fn classify_lifetime(
sema: &Semantics<RootDatabase>,
lifetime: &ast::Lifetime,
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
index df73879aed..ea7fd3e072 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -1,7 +1,7 @@
use std::convert::TryInto;
use crate::{doc_links::token_as_doc_comment, FilePosition, NavigationTarget, RangeInfo, TryToNav};
-use hir::{AsAssocItem, Semantics};
+use hir::{AsAssocItem, AssocItem, Semantics};
use ide_db::{
base_db::{AnchoredPath, FileId, FileLoader},
defs::{Definition, IdentClass},
@@ -65,7 +65,7 @@ pub(crate) fn goto_definition(
.definitions()
.into_iter()
.flat_map(|def| {
- try_find_trait_item_definition(sema.db, &def)
+ try_filter_trait_item_definition(sema, &def, &token)
.unwrap_or_else(|| def_to_nav(sema.db, def))
})
.collect(),
@@ -104,32 +104,38 @@ fn try_lookup_include_path(
docs: None,
})
}
-
-/// finds the trait definition of an impl'd item
+/// finds the trait definition of an impl'd item, except function
/// e.g.
/// ```rust
-/// trait A { fn a(); }
+/// trait A { type a; }
/// struct S;
-/// impl A for S { fn a(); } // <-- on this function, will get the location of a() in the trait
+/// impl A for S { type a = i32; } // <-- on this associate type, will get the location of a in the trait
/// ```
-fn try_find_trait_item_definition(
- db: &RootDatabase,
+fn try_filter_trait_item_definition(
+ sema: &Semantics<RootDatabase>,
def: &Definition,
+ token: &SyntaxToken,
) -> Option<Vec<NavigationTarget>> {
- let name = def.name(db)?;
+ let db = sema.db;
let assoc = def.as_assoc_item(db)?;
-
- let imp = match assoc.container(db) {
- hir::AssocItemContainer::Impl(imp) => imp,
- _ => return None,
- };
-
- let trait_ = imp.trait_(db)?;
- trait_
- .items(db)
- .iter()
- .find_map(|itm| (itm.name(db)? == name).then(|| itm.try_to_nav(db)).flatten())
- .map(|it| vec![it])
+ match assoc {
+ AssocItem::Function(..) => {
+ IdentClass::classify_token_to_impl(sema, &token).map(|def| def_to_nav(db, def))
+ }
+ AssocItem::Const(..) | AssocItem::TypeAlias(..) => {
+ let imp = match assoc.container(db) {
+ hir::AssocItemContainer::Impl(imp) => imp,
+ _ => return None,
+ };
+ let trait_ = imp.trait_(db)?;
+ let name = def.name(db)?;
+ trait_
+ .items(db)
+ .iter()
+ .find_map(|itm| (itm.name(db)? == name).then(|| itm.try_to_nav(db)).flatten())
+ .map(|it| vec![it])
+ }
+ }
}
fn def_to_nav(db: &RootDatabase, def: Definition) -> Vec<NavigationTarget> {
@@ -1331,23 +1337,98 @@ fn main() {
"#,
);
}
-
- #[test]
- fn goto_def_of_trait_impl_fn() {
- check(
- r#"
+ #[cfg(test)]
+ mod goto_impl_of_trait_fn {
+ use super::check;
+ #[test]
+ fn cursor_on_impl() {
+ check(
+ r#"
trait Twait {
fn a();
- // ^
}
struct Stwuct;
impl Twait for Stwuct {
fn a$0();
+ //^
}
-"#,
- );
+ "#,
+ );
+ }
+ #[test]
+ fn method_call() {
+ check(
+ r#"
+trait Twait {
+ fn a(&self);
+}
+
+struct Stwuct;
+
+impl Twait for Stwuct {
+ fn a(&self){};
+ //^
+}
+fn f() {
+ let s = Stwuct;
+ s.a$0();
+}
+ "#,
+ );
+ }
+ #[test]
+ fn path_call() {
+ check(
+ r#"
+trait Twait {
+ fn a(&self);
+}
+
+struct Stwuct;
+
+impl Twait for Stwuct {
+ fn a(&self){};
+ //^
+}
+fn f() {
+ let s = Stwuct;
+ Stwuct::a$0(&s);
+}
+ "#,
+ );
+ }
+ #[test]
+ fn where_clause_can_work() {
+ check(
+ r#"
+trait G {
+ fn g(&self);
+}
+trait Bound{}
+trait EA{}
+struct Gen<T>(T);
+impl <T:EA> G for Gen<T> {
+ fn g(&self) {
+ }
+}
+impl <T> G for Gen<T>
+where T : Bound
+{
+ fn g(&self){
+ //^
+ }
+}
+struct A;
+impl Bound for A{}
+fn f() {
+ let gen = Gen::<A>(A);
+ gen.g$0();
+}
+ "#,
+ );
+ }
}
#[test]