Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--crates/hir-ty/src/method_resolution.rs123
-rw-r--r--crates/hir/src/semantics.rs12
-rw-r--r--crates/hir/src/source_analyzer.rs74
-rw-r--r--crates/ide-assists/src/handlers/qualify_method_call.rs27
-rw-r--r--crates/ide-assists/src/utils/suggest_name.rs15
-rw-r--r--crates/ide-db/src/rename.rs41
-rw-r--r--crates/ide-db/src/search.rs5
-rw-r--r--crates/ide-db/src/traits.rs26
-rw-r--r--crates/ide/src/goto_definition.rs218
-rw-r--r--crates/ide/src/syntax_highlighting/highlight.rs6
-rw-r--r--crates/rust-analyzer/tests/slow-tests/main.rs2
11 files changed, 419 insertions, 130 deletions
diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs
index d5285c1710..d2081d22d7 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,59 @@ pub(crate) fn iterate_method_candidates<T>(
slot
}
+pub fn lookup_impl_method(
+ self_ty: &Ty,
+ db: &dyn HirDatabase,
+ env: Arc<TraitEnvironment>,
+ trait_: TraitId,
+ name: &Name,
+) -> Option<FunctionId> {
+ let self_ty_fp = 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(trait_, self_ty_fp);
+ let mut table = InferenceTable::new(db, env.clone());
+ find_matching_impl(impls, &mut table, &self_ty).and_then(|data| {
+ data.items.iter().find_map(|it| match it {
+ AssocItemId::FunctionId(f) => (db.function_data(*f).name == *name).then(|| *f),
+ _ => None,
+ })
+ })
+}
+
+fn find_matching_impl(
+ mut impls: impl Iterator<Item = ImplId>,
+ table: &mut InferenceTable,
+ self_ty: &Ty,
+) -> Option<Arc<ImplData>> {
+ let db = table.db;
+ loop {
+ let impl_ = impls.next()?;
+ let r = table.run_in_snapshot(|table| {
+ let impl_data = db.impl_data(impl_);
+ let substs =
+ TyBuilder::subst_for_def(db, impl_).fill_with_inference_vars(table).build();
+ let impl_ty = db.impl_self_ty(impl_).substitute(Interner, &substs);
+
+ table
+ .unify(self_ty, &impl_ty)
+ .then(|| {
+ let wh_goals =
+ crate::chalk_db::convert_where_clauses(db, impl_.into(), &substs)
+ .into_iter()
+ .map(|b| b.cast(Interner));
+
+ let goal = crate::Goal::all(Interner, wh_goals);
+
+ table.try_obligation(goal).map(|_| impl_data)
+ })
+ .flatten()
+ });
+ if r.is_some() {
+ break r;
+ }
+ }
+}
+
pub fn iterate_path_candidates(
ty: &Canonical<Ty>,
db: &dyn HirDatabase,
@@ -970,18 +1024,31 @@ fn is_valid_candidate(
self_ty: &Ty,
visible_from_module: Option<ModuleId>,
) -> bool {
+ macro_rules! check_that {
+ ($cond:expr) => {
+ if !$cond {
+ return false;
+ }
+ };
+ }
+
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;
+
+ check_that!(name.map_or(true, |n| n == &data.name));
+ check_that!(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
+ }));
+
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 {
+ let expect_self_ty = match m.lookup(db.upcast()).container {
ItemContainerId::TraitId(_) => {
subst.at(Interner, 0).assert_ty_ref(Interner).clone()
}
@@ -993,49 +1060,31 @@ fn is_valid_candidate(
unreachable!()
}
};
- if !table.unify(&expected_self_ty, &self_ty) {
- return false;
- }
+ check_that!(table.unify(&expect_self_ty, self_ty));
if let Some(receiver_ty) = receiver_ty {
- if !data.has_self_param() {
- return false;
- }
+ check_that!(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);
- let receiver_matches = table.unify(&receiver_ty, &expected_receiver);
- if !receiver_matches {
- return false;
- }
+ check_that!(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);
- 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) {
+ check_that!(receiver_ty.is_none());
+
+ check_that!(name.map_or(true, |n| data.name.as_ref() == Some(n)));
+ check_that!(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);
- return false;
}
- }
+ v
+ }));
if let ItemContainerId::ImplId(impl_id) = c.lookup(db.upcast()).container {
let self_ty_matches = table.run_in_snapshot(|table| {
let subst =
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index aa10b0f878..744f3865aa 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -16,7 +16,6 @@ use hir_expand::{
name::{known, AsName},
ExpansionInfo, MacroCallId,
};
-use hir_ty::Interner;
use itertools::Itertools;
use rustc_hash::{FxHashMap, FxHashSet};
use smallvec::{smallvec, SmallVec};
@@ -975,18 +974,11 @@ impl<'db> SemanticsImpl<'db> {
}
fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<FunctionId> {
- self.analyze(call.syntax())?.resolve_method_call(self.db, call).map(|(id, _)| id)
+ self.analyze(call.syntax())?.resolve_method_call(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)?;
- let ty = self.db.value_ty(func.into()).substitute(Interner, &subst);
- let resolver = source_analyzer.resolver;
- let ty = Type::new_with_resolver(self.db, &resolver, ty);
- let mut res = ty.as_callable(self.db)?;
- res.is_bound_method = true;
- Some(res)
+ self.analyze(call.syntax())?.resolve_method_call_as_callable(self.db, call)
}
fn resolve_field(&self, field: &ast::FieldExpr) -> Option<Field> {
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index 2298a75d57..984a464ed1 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, AssocItemId, 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::{
@@ -42,8 +43,8 @@ use syntax::{
use crate::{
db::HirDatabase, semantics::PathResolution, Adt, AssocItem, BindingMode, BuiltinAttr,
- BuiltinType, Const, Field, Function, Local, Macro, ModuleDef, Static, Struct, ToolModule,
- Trait, Type, TypeAlias, Variant,
+ BuiltinType, Callable, Const, Field, Function, Local, Macro, ModuleDef, Static, Struct,
+ ToolModule, Trait, Type, TypeAlias, Variant,
};
/// `SourceAnalyzer` is a convenience wrapper which exposes HIR API in terms of
@@ -232,13 +233,29 @@ impl SourceAnalyzer {
)
}
+ pub(crate) fn resolve_method_call_as_callable(
+ &self,
+ db: &dyn HirDatabase,
+ call: &ast::MethodCallExpr,
+ ) -> Option<Callable> {
+ let expr_id = self.expr_id(db, &call.clone().into())?;
+ let (func, substs) = self.infer.as_ref()?.method_resolution(expr_id)?;
+ let ty = db.value_ty(func.into()).substitute(Interner, &substs);
+ let ty = Type::new_with_resolver(db, &self.resolver, ty);
+ let mut res = ty.as_callable(db)?;
+ res.is_bound_method = true;
+ Some(res)
+ }
+
pub(crate) fn resolve_method_call(
&self,
db: &dyn HirDatabase,
call: &ast::MethodCallExpr,
- ) -> Option<(FunctionId, Substitution)> {
+ ) -> Option<FunctionId> {
let expr_id = self.expr_id(db, &call.clone().into())?;
- self.infer.as_ref()?.method_resolution(expr_id)
+ let (f_in_trait, substs) = self.infer.as_ref()?.method_resolution(expr_id)?;
+ let f_in_impl = self.resolve_impl_method(db, f_in_trait, &substs);
+ f_in_impl.or(Some(f_in_trait))
}
pub(crate) fn resolve_field(
@@ -336,6 +353,25 @@ impl SourceAnalyzer {
let expr_id = self.expr_id(db, &path_expr.into())?;
let infer = self.infer.as_ref()?;
if let Some(assoc) = infer.assoc_resolutions_for_expr(expr_id) {
+ let assoc = match assoc {
+ AssocItemId::FunctionId(f_in_trait) => {
+ match infer.type_of_expr.get(expr_id) {
+ None => assoc,
+ Some(func_ty) => {
+ if let TyKind::FnDef(_fn_def, subs) = func_ty.kind(Interner) {
+ self.resolve_impl_method(db, f_in_trait, subs)
+ .map(AssocItemId::FunctionId)
+ .unwrap_or(assoc)
+ } else {
+ assoc
+ }
+ }
+ }
+ }
+
+ _ => assoc,
+ };
+
return Some(PathResolution::Def(AssocItem::from(assoc).into()));
}
if let Some(VariantId::EnumVariantId(variant)) =
@@ -563,6 +599,30 @@ impl SourceAnalyzer {
}
false
}
+
+ fn resolve_impl_method(
+ &self,
+ db: &dyn HirDatabase,
+ func: FunctionId,
+ substs: &Substitution,
+ ) -> Option<FunctionId> {
+ let impled_trait = match func.lookup(db.upcast()).container {
+ ItemContainerId::TraitId(trait_id) => trait_id,
+ _ => return None,
+ };
+ if substs.is_empty(Interner) {
+ return None;
+ }
+ let self_ty = substs.at(Interner, 0).ty(Interner)?;
+ 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_impl_method(self_ty, db, trait_env, impled_trait, &fun_data.name)
+ }
}
fn scope_for(
diff --git a/crates/ide-assists/src/handlers/qualify_method_call.rs b/crates/ide-assists/src/handlers/qualify_method_call.rs
index ba0a142718..61cb2f0479 100644
--- a/crates/ide-assists/src/handlers/qualify_method_call.rs
+++ b/crates/ide-assists/src/handlers/qualify_method_call.rs
@@ -1,8 +1,5 @@
-use hir::{ItemInNs, ModuleDef};
-use ide_db::{
- assists::{AssistId, AssistKind},
- imports::import_assets::item_for_path_search,
-};
+use hir::{db::HirDatabase, AsAssocItem, AssocItem, AssocItemContainer, ItemInNs, ModuleDef};
+use ide_db::assists::{AssistId, AssistKind};
use syntax::{ast, AstNode};
use crate::{
@@ -67,6 +64,26 @@ pub(crate) fn qualify_method_call(acc: &mut Assists, ctx: &AssistContext) -> Opt
Some(())
}
+fn item_for_path_search(db: &dyn HirDatabase, item: ItemInNs) -> Option<ItemInNs> {
+ Some(match item {
+ ItemInNs::Types(_) | ItemInNs::Values(_) => match item_as_assoc(db, item) {
+ Some(assoc_item) => match assoc_item.container(db) {
+ AssocItemContainer::Trait(trait_) => ItemInNs::from(ModuleDef::from(trait_)),
+ AssocItemContainer::Impl(impl_) => match impl_.trait_(db) {
+ None => ItemInNs::from(ModuleDef::from(impl_.self_ty(db).as_adt()?)),
+ Some(trait_) => ItemInNs::from(ModuleDef::from(trait_)),
+ },
+ },
+ None => item,
+ },
+ ItemInNs::Macros(_) => item,
+ })
+}
+
+fn item_as_assoc(db: &dyn HirDatabase, item: ItemInNs) -> Option<AssocItem> {
+ item.as_module_def().and_then(|module_def| module_def.as_assoc_item(db))
+}
+
#[cfg(test)]
mod tests {
use super::*;
diff --git a/crates/ide-assists/src/utils/suggest_name.rs b/crates/ide-assists/src/utils/suggest_name.rs
index 411e5b25b7..779cdbc93c 100644
--- a/crates/ide-assists/src/utils/suggest_name.rs
+++ b/crates/ide-assists/src/utils/suggest_name.rs
@@ -461,6 +461,21 @@ fn foo() { S.bar($01$0, 2) }
}
#[test]
+ fn method_on_impl_trait() {
+ check(
+ r#"
+struct S;
+trait T {
+ fn bar(&self, n: i32, m: u32);
+}
+impl T for S { fn bar(&self, n: i32, m: u32); }
+fn foo() { S.bar($01$0, 2) }
+"#,
+ "n",
+ );
+ }
+
+ #[test]
fn method_ufcs() {
check(
r#"
diff --git a/crates/ide-db/src/rename.rs b/crates/ide-db/src/rename.rs
index bff8265e01..c2e7142f52 100644
--- a/crates/ide-db/src/rename.rs
+++ b/crates/ide-db/src/rename.rs
@@ -24,7 +24,7 @@ use std::fmt;
use base_db::{AnchoredPathBuf, FileId, FileRange};
use either::Either;
-use hir::{AsAssocItem, FieldSource, HasSource, InFile, ModuleSource, Semantics};
+use hir::{FieldSource, HasSource, InFile, ModuleSource, Semantics};
use stdx::never;
use syntax::{
ast::{self, HasName},
@@ -37,6 +37,7 @@ use crate::{
search::FileReference,
source_change::{FileSystemEdit, SourceChange},
syntax_helpers::node_ext::expr_as_name_ref,
+ traits::convert_to_def_in_trait,
RootDatabase,
};
@@ -248,7 +249,7 @@ fn rename_mod(
fn rename_reference(
sema: &Semantics<RootDatabase>,
- mut def: Definition,
+ def: Definition,
new_name: &str,
) -> Result<SourceChange> {
let ident_kind = IdentifierKind::classify(new_name)?;
@@ -275,41 +276,7 @@ fn rename_reference(
}
}
- let assoc_item = match def {
- // HACK: resolve trait impl items to the item def of the trait definition
- // so that we properly resolve all trait item references
- Definition::Function(it) => it.as_assoc_item(sema.db),
- Definition::TypeAlias(it) => it.as_assoc_item(sema.db),
- Definition::Const(it) => it.as_assoc_item(sema.db),
- _ => None,
- };
- def = match assoc_item {
- Some(assoc) => assoc
- .containing_trait_impl(sema.db)
- .and_then(|trait_| {
- trait_.items(sema.db).into_iter().find_map(|it| match (it, assoc) {
- (hir::AssocItem::Function(trait_func), hir::AssocItem::Function(func))
- if trait_func.name(sema.db) == func.name(sema.db) =>
- {
- Some(Definition::Function(trait_func))
- }
- (hir::AssocItem::Const(trait_konst), hir::AssocItem::Const(konst))
- if trait_konst.name(sema.db) == konst.name(sema.db) =>
- {
- Some(Definition::Const(trait_konst))
- }
- (
- hir::AssocItem::TypeAlias(trait_type_alias),
- hir::AssocItem::TypeAlias(type_alias),
- ) if trait_type_alias.name(sema.db) == type_alias.name(sema.db) => {
- Some(Definition::TypeAlias(trait_type_alias))
- }
- _ => None,
- })
- })
- .unwrap_or(def),
- None => def,
- };
+ let def = convert_to_def_in_trait(sema.db, def);
let usages = def.usages(sema).all();
if !usages.is_empty() && ident_kind == IdentifierKind::Underscore {
diff --git a/crates/ide-db/src/search.rs b/crates/ide-db/src/search.rs
index 4a11fb73cd..692fce06b0 100644
--- a/crates/ide-db/src/search.rs
+++ b/crates/ide-db/src/search.rs
@@ -16,6 +16,7 @@ use syntax::{ast, match_ast, AstNode, TextRange, TextSize};
use crate::{
defs::{Definition, NameClass, NameRefClass},
+ traits::convert_to_def_in_trait,
RootDatabase,
};
@@ -619,7 +620,9 @@ impl<'a> FindUsages<'a> {
};
sink(file_id, reference)
}
- Some(NameRefClass::Definition(def)) if def == self.def => {
+ Some(NameRefClass::Definition(def))
+ if convert_to_def_in_trait(self.sema.db, def) == self.def =>
+ {
let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
let reference = FileReference {
range,
diff --git a/crates/ide-db/src/traits.rs b/crates/ide-db/src/traits.rs
index 0fbfd86992..666499ed7a 100644
--- a/crates/ide-db/src/traits.rs
+++ b/crates/ide-db/src/traits.rs
@@ -1,7 +1,7 @@
//! Functionality for obtaining data related to traits from the DB.
-use crate::RootDatabase;
-use hir::Semantics;
+use crate::{defs::Definition, RootDatabase};
+use hir::{db::HirDatabase, AsAssocItem, Semantics};
use rustc_hash::FxHashSet;
use syntax::{ast, AstNode};
@@ -69,6 +69,28 @@ pub fn get_missing_assoc_items(
})
}
+/// Converts associated trait impl items to their trait definition counterpart
+pub(crate) fn convert_to_def_in_trait(db: &dyn HirDatabase, def: Definition) -> Definition {
+ use hir::AssocItem::*;
+ (|| {
+ let assoc = def.as_assoc_item(db)?;
+ let trait_ = assoc.containing_trait_impl(db)?;
+ let name = match assoc {
+ Function(it) => it.name(db),
+ Const(it) => it.name(db)?,
+ TypeAlias(it) => it.name(db),
+ };
+ let item = trait_.items(db).into_iter().find(|it| match (it, assoc) {
+ (Function(trait_func), Function(_)) => trait_func.name(db) == name,
+ (Const(trait_konst), Const(_)) => trait_konst.name(db).map_or(false, |it| it == name),
+ (TypeAlias(trait_type_alias), TypeAlias(_)) => trait_type_alias.name(db) == name,
+ _ => false,
+ })?;
+ Some(Definition::from(item))
+ })()
+ .unwrap_or(def)
+}
+
#[cfg(test)]
mod tests {
use base_db::{fixture::ChangeFixture, FilePosition};
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
index df73879aed..e10789fd47 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -1,7 +1,7 @@
-use std::convert::TryInto;
+use std::{convert::TryInto, mem::discriminant};
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)
.unwrap_or_else(|| def_to_nav(sema.db, def))
})
.collect(),
@@ -104,32 +104,37 @@ 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,
) -> 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(..) => None,
+ 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)?;
+ let discri_value = discriminant(&assoc);
+ trait_
+ .items(db)
+ .iter()
+ .filter(|itm| discriminant(*itm) == discri_value)
+ .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> {
@@ -173,6 +178,23 @@ mod tests {
}
#[test]
+ fn goto_def_if_items_same_name() {
+ check(
+ r#"
+trait Trait {
+ type A;
+ const A: i32;
+ //^
+}
+
+struct T;
+impl Trait for T {
+ type A = i32;
+ const A$0: i32 = -9;
+}"#,
+ );
+ }
+ #[test]
fn goto_def_in_mac_call_in_attr_invoc() {
check(
r#"
@@ -1331,23 +1353,161 @@ 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]
+ fn wc_case_is_ok() {
+ check(
+ r#"
+trait G {
+ fn g(&self);
+}
+trait BParent{}
+trait Bound: BParent{}
+struct Gen<T>(T);
+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]
+ fn method_call_defaulted() {
+ check(
+ r#"
+trait Twait {
+ fn a(&self) {}
+ //^
+}
+
+struct Stwuct;
+
+impl Twait for Stwuct {
+}
+fn f() {
+ let s = Stwuct;
+ s.a$0();
+}
+ "#,
+ );
+ }
+
+ #[test]
+ fn method_call_on_generic() {
+ check(
+ r#"
+trait Twait {
+ fn a(&self) {}
+ //^
+}
+
+fn f<T: Twait>(s: T) {
+ s.a$0();
+}
+ "#,
+ );
+ }
}
#[test]
diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs
index b91d54dd92..788cbe9272 100644
--- a/crates/ide/src/syntax_highlighting/highlight.rs
+++ b/crates/ide/src/syntax_highlighting/highlight.rs
@@ -510,7 +510,11 @@ fn highlight_method_call(
if func.is_async(sema.db) {
h |= HlMod::Async;
}
- if func.as_assoc_item(sema.db).and_then(|it| it.containing_trait(sema.db)).is_some() {
+ if func
+ .as_assoc_item(sema.db)
+ .and_then(|it| it.containing_trait_or_trait_impl(sema.db))
+ .is_some()
+ {
h |= HlMod::Trait;
}
diff --git a/crates/rust-analyzer/tests/slow-tests/main.rs b/crates/rust-analyzer/tests/slow-tests/main.rs
index 6d2788d337..884224960f 100644
--- a/crates/rust-analyzer/tests/slow-tests/main.rs
+++ b/crates/rust-analyzer/tests/slow-tests/main.rs
@@ -919,7 +919,7 @@ pub fn foo(_input: TokenStream) -> TokenStream {
expect![[r#"
```rust
- foo::Bar
+ foo::Foo
```
```rust