mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-26 13:03:31 +00:00
Merge #9106
9106: feat: goto definition on an impl fn goes to that fn in the trait r=lf- a=lf- 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. ![ra goto def trait the other way](https://user-images.githubusercontent.com/6652840/120403989-39aa3280-c2fa-11eb-9359-639346878acd.gif) Co-authored-by: Jade <software@lfcode.ca>
This commit is contained in:
commit
efa84cd08d
2 changed files with 91 additions and 7 deletions
|
@ -50,7 +50,6 @@ use hir_def::{
|
||||||
per_ns::PerNs,
|
per_ns::PerNs,
|
||||||
resolver::{HasResolver, Resolver},
|
resolver::{HasResolver, Resolver},
|
||||||
src::HasSource as _,
|
src::HasSource as _,
|
||||||
type_ref::TraitRef,
|
|
||||||
AdtId, AssocContainerId, AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId,
|
AdtId, AssocContainerId, AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId,
|
||||||
DefWithBodyId, EnumId, FunctionId, GenericDefId, HasModule, ImplId, LifetimeParamId,
|
DefWithBodyId, EnumId, FunctionId, GenericDefId, HasModule, ImplId, LifetimeParamId,
|
||||||
LocalEnumVariantId, LocalFieldId, Lookup, ModuleId, StaticId, StructId, TraitId, TypeAliasId,
|
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
|
// FIXME: the return type is wrong. This should be a hir version of
|
||||||
// `TraitRef` (ie, resolved `TypeRef`).
|
// `TraitRef` (to account for parameters and qualifiers)
|
||||||
pub fn trait_(self, db: &dyn HirDatabase) -> Option<TraitRef> {
|
pub fn trait_(self, db: &dyn HirDatabase) -> Option<Trait> {
|
||||||
db.impl_data(self.id).target_trait.as_deref().cloned()
|
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 {
|
pub fn self_ty(self, db: &dyn HirDatabase) -> Type {
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
|
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use hir::{InFile, Semantics};
|
use hir::{AsAssocItem, InFile, ModuleDef, Semantics};
|
||||||
use ide_db::{
|
use ide_db::{
|
||||||
base_db::{AnchoredPath, FileId, FileLoader},
|
base_db::{AnchoredPath, FileId, FileLoader},
|
||||||
defs::{NameClass, NameRefClass},
|
defs::{Definition, NameClass, NameRefClass},
|
||||||
RootDatabase,
|
RootDatabase,
|
||||||
};
|
};
|
||||||
use syntax::{
|
use syntax::{
|
||||||
|
@ -57,7 +57,8 @@ pub(crate) fn goto_definition(
|
||||||
},
|
},
|
||||||
ast::Name(name) => {
|
ast::Name(name) => {
|
||||||
let def = NameClass::classify(&sema, &name)?.referenced_or_defined(sema.db);
|
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, <) {
|
ast::Lifetime(lt) => if let Some(name_class) = NameClass::classify_lifetime(&sema, <) {
|
||||||
let def = name_class.referenced_or_defined(sema.db);
|
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<NavigationTarget> {
|
||||||
|
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<SyntaxToken>) -> Option<SyntaxToken> {
|
fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
|
||||||
return tokens.max_by_key(priority);
|
return tokens.max_by_key(priority);
|
||||||
fn priority(n: &SyntaxToken) -> usize {
|
fn priority(n: &SyntaxToken) -> usize {
|
||||||
|
@ -1259,6 +1288,60 @@ fn main() {
|
||||||
//- /foo.txt
|
//- /foo.txt
|
||||||
// empty
|
// empty
|
||||||
//^ file
|
//^ 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 = !;
|
||||||
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue