diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index b43d61d0e8..c2b68a8530 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -50,7 +50,6 @@ use hir_def::{ per_ns::PerNs, resolver::{HasResolver, Resolver}, src::HasSource as _, - type_ref::TraitRef, AdtId, AssocContainerId, AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId, DefWithBodyId, EnumId, FunctionId, GenericDefId, HasModule, ImplId, LifetimeParamId, LocalEnumVariantId, LocalFieldId, Lookup, ModuleId, StaticId, StructId, TraitId, TypeAliasId, @@ -1797,9 +1796,11 @@ impl Impl { } // FIXME: the return type is wrong. This should be a hir version of - // `TraitRef` (ie, resolved `TypeRef`). - pub fn trait_(self, db: &dyn HirDatabase) -> Option { - db.impl_data(self.id).target_trait.as_deref().cloned() + // `TraitRef` (to account for parameters and qualifiers) + pub fn trait_(self, db: &dyn HirDatabase) -> Option { + let trait_ref = db.impl_trait(self.id)?.skip_binders().clone(); + let id = hir_ty::from_chalk_trait_id(trait_ref.trait_id); + Some(Trait { id }) } pub fn self_ty(self, db: &dyn HirDatabase) -> Type { diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index b0bfd646e0..2d36c34e99 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs @@ -1,10 +1,10 @@ use std::convert::TryInto; use either::Either; -use hir::{InFile, Semantics}; +use hir::{AsAssocItem, InFile, ModuleDef, Semantics}; use ide_db::{ base_db::{AnchoredPath, FileId, FileLoader}, - defs::{NameClass, NameRefClass}, + defs::{Definition, NameClass, NameRefClass}, RootDatabase, }; use syntax::{ @@ -57,7 +57,8 @@ pub(crate) fn goto_definition( }, ast::Name(name) => { let def = NameClass::classify(&sema, &name)?.referenced_or_defined(sema.db); - def.try_to_nav(sema.db) + try_find_trait_item_definition(&sema.db, &def) + .or_else(|| def.try_to_nav(sema.db)) }, ast::Lifetime(lt) => if let Some(name_class) = NameClass::classify_lifetime(&sema, <) { let def = name_class.referenced_or_defined(sema.db); @@ -99,6 +100,34 @@ fn try_lookup_include_path( }) } +/// finds the trait definition of an impl'd item +/// e.g. +/// ```rust +/// trait A { fn a(); } +/// struct S; +/// impl A for S { fn a(); } // <-- on this function, will get the location of a() in the trait +/// ``` +fn try_find_trait_item_definition(db: &RootDatabase, def: &Definition) -> Option { + let name = def.name(db)?; + let assoc = match def { + Definition::ModuleDef(ModuleDef::Function(f)) => f.as_assoc_item(db), + Definition::ModuleDef(ModuleDef::Const(c)) => c.as_assoc_item(db), + Definition::ModuleDef(ModuleDef::TypeAlias(ty)) => ty.as_assoc_item(db), + _ => None, + }?; + + 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()) +} + fn pick_best(tokens: TokenAtOffset) -> Option { return tokens.max_by_key(priority); fn priority(n: &SyntaxToken) -> usize { @@ -1259,6 +1288,60 @@ fn main() { //- /foo.txt // empty //^ file +"#, + ); + } + + #[test] + fn goto_def_of_trait_impl_fn() { + check( + r#" +trait Twait { + fn a(); + // ^ +} + +struct Stwuct; + +impl Twait for Stwuct { + fn a$0(); +} +"#, + ); + } + + #[test] + fn goto_def_of_trait_impl_const() { + check( + r#" +trait Twait { + const NOMS: bool; + // ^^^^ +} + +struct Stwuct; + +impl Twait for Stwuct { + const NOMS$0: bool = true; +} +"#, + ); + } + + #[test] + fn goto_def_of_trait_impl_type_alias() { + check( + r#" +trait Twait { + type IsBad; + // ^^^^^ +} + +struct Stwuct; + +impl Twait for Stwuct { + type IsBad$0 = !; +} "#, ); }