feat: goto definition on an impl fn goes to that fn in the trait

e.g. if you have a trait T and `impl T for S` for some struct, if you
goto definition on some function name inside the impl, it will go to the
definition of that function inside the `trait T` block, rather than the
current behaviour of not going anywhere at all.
This commit is contained in:
Jade 2021-06-01 16:16:59 -07:00
parent 13da28cc2b
commit 8a57c73640
2 changed files with 53 additions and 7 deletions

View File

@ -50,7 +50,6 @@
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 @@ pub fn all_for_trait(db: &dyn HirDatabase, trait_: Trait) -> Vec<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<TraitRef> {
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<Trait> {
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 {

View File

@ -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_fn_definition(&sema.db, &def)
.or_else(|| def.try_to_nav(sema.db))
},
ast::Lifetime(lt) => if let Some(name_class) = NameClass::classify_lifetime(&sema, &lt) {
let def = name_class.referenced_or_defined(sema.db);
@ -99,6 +100,32 @@ fn try_lookup_include_path(
})
}
/// finds the trait definition of an impl'd function
/// 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_fn_definition(db: &RootDatabase, def: &Definition) -> Option<NavigationTarget> {
match def {
Definition::ModuleDef(ModuleDef::Function(f)) => {
let name = def.name(db)?;
let assoc = f.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())
}
_ => None,
}
}
fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
return tokens.max_by_key(priority);
fn priority(n: &SyntaxToken) -> usize {
@ -1259,6 +1286,24 @@ 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();
}
"#,
);
}