diff options
author | Jade <[email protected]> | 2021-06-02 00:16:59 +0100 |
---|---|---|
committer | Jade <[email protected]> | 2021-06-07 05:51:17 +0100 |
commit | 8a57c736404abac81a6de20b0e90c19021e040b9 (patch) | |
tree | 5ece2fcdc66cf91dd269ff0c64c2dc91ff216565 | |
parent | 13da28cc2bc1b59f7af817eca36927a71edb023c (diff) |
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.
-rw-r--r-- | crates/hir/src/lib.rs | 9 | ||||
-rw-r--r-- | crates/ide/src/goto_definition.rs | 51 |
2 files changed, 53 insertions, 7 deletions
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index b43d61d0e..c2b68a853 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs | |||
@@ -50,7 +50,6 @@ use hir_def::{ | |||
50 | per_ns::PerNs, | 50 | per_ns::PerNs, |
51 | resolver::{HasResolver, Resolver}, | 51 | resolver::{HasResolver, Resolver}, |
52 | src::HasSource as _, | 52 | src::HasSource as _, |
53 | type_ref::TraitRef, | ||
54 | AdtId, AssocContainerId, AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId, | 53 | AdtId, AssocContainerId, AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId, |
55 | DefWithBodyId, EnumId, FunctionId, GenericDefId, HasModule, ImplId, LifetimeParamId, | 54 | DefWithBodyId, EnumId, FunctionId, GenericDefId, HasModule, ImplId, LifetimeParamId, |
56 | LocalEnumVariantId, LocalFieldId, Lookup, ModuleId, StaticId, StructId, TraitId, TypeAliasId, | 55 | LocalEnumVariantId, LocalFieldId, Lookup, ModuleId, StaticId, StructId, TraitId, TypeAliasId, |
@@ -1797,9 +1796,11 @@ impl Impl { | |||
1797 | } | 1796 | } |
1798 | 1797 | ||
1799 | // FIXME: the return type is wrong. This should be a hir version of | 1798 | // FIXME: the return type is wrong. This should be a hir version of |
1800 | // `TraitRef` (ie, resolved `TypeRef`). | 1799 | // `TraitRef` (to account for parameters and qualifiers) |
1801 | pub fn trait_(self, db: &dyn HirDatabase) -> Option<TraitRef> { | 1800 | pub fn trait_(self, db: &dyn HirDatabase) -> Option<Trait> { |
1802 | db.impl_data(self.id).target_trait.as_deref().cloned() | 1801 | let trait_ref = db.impl_trait(self.id)?.skip_binders().clone(); |
1802 | let id = hir_ty::from_chalk_trait_id(trait_ref.trait_id); | ||
1803 | Some(Trait { id }) | ||
1803 | } | 1804 | } |
1804 | 1805 | ||
1805 | pub fn self_ty(self, db: &dyn HirDatabase) -> Type { | 1806 | 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 b0bfd646e..c236039f4 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs | |||
@@ -1,10 +1,10 @@ | |||
1 | use std::convert::TryInto; | 1 | use std::convert::TryInto; |
2 | 2 | ||
3 | use either::Either; | 3 | use either::Either; |
4 | use hir::{InFile, Semantics}; | 4 | use hir::{AsAssocItem, InFile, ModuleDef, Semantics}; |
5 | use ide_db::{ | 5 | use ide_db::{ |
6 | base_db::{AnchoredPath, FileId, FileLoader}, | 6 | base_db::{AnchoredPath, FileId, FileLoader}, |
7 | defs::{NameClass, NameRefClass}, | 7 | defs::{Definition, NameClass, NameRefClass}, |
8 | RootDatabase, | 8 | RootDatabase, |
9 | }; | 9 | }; |
10 | use syntax::{ | 10 | use syntax::{ |
@@ -57,7 +57,8 @@ pub(crate) fn goto_definition( | |||
57 | }, | 57 | }, |
58 | ast::Name(name) => { | 58 | ast::Name(name) => { |
59 | let def = NameClass::classify(&sema, &name)?.referenced_or_defined(sema.db); | 59 | let def = NameClass::classify(&sema, &name)?.referenced_or_defined(sema.db); |
60 | def.try_to_nav(sema.db) | 60 | try_find_trait_fn_definition(&sema.db, &def) |
61 | .or_else(|| def.try_to_nav(sema.db)) | ||
61 | }, | 62 | }, |
62 | ast::Lifetime(lt) => if let Some(name_class) = NameClass::classify_lifetime(&sema, <) { | 63 | ast::Lifetime(lt) => if let Some(name_class) = NameClass::classify_lifetime(&sema, <) { |
63 | let def = name_class.referenced_or_defined(sema.db); | 64 | let def = name_class.referenced_or_defined(sema.db); |
@@ -99,6 +100,32 @@ fn try_lookup_include_path( | |||
99 | }) | 100 | }) |
100 | } | 101 | } |
101 | 102 | ||
103 | /// finds the trait definition of an impl'd function | ||
104 | /// e.g. | ||
105 | /// ```rust | ||
106 | /// trait A { fn a(); } | ||
107 | /// struct S; | ||
108 | /// impl A for S { fn a(); } // <-- on this function, will get the location of a() in the trait | ||
109 | /// ``` | ||
110 | fn try_find_trait_fn_definition(db: &RootDatabase, def: &Definition) -> Option<NavigationTarget> { | ||
111 | match def { | ||
112 | Definition::ModuleDef(ModuleDef::Function(f)) => { | ||
113 | let name = def.name(db)?; | ||
114 | let assoc = f.as_assoc_item(db)?; | ||
115 | let imp = match assoc.container(db) { | ||
116 | hir::AssocItemContainer::Impl(imp) => imp, | ||
117 | _ => return None, | ||
118 | }; | ||
119 | let trait_ = imp.trait_(db)?; | ||
120 | trait_ | ||
121 | .items(db) | ||
122 | .iter() | ||
123 | .find_map(|itm| (itm.name(db)? == name).then(|| itm.try_to_nav(db)).flatten()) | ||
124 | } | ||
125 | _ => None, | ||
126 | } | ||
127 | } | ||
128 | |||
102 | fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { | 129 | fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { |
103 | return tokens.max_by_key(priority); | 130 | return tokens.max_by_key(priority); |
104 | fn priority(n: &SyntaxToken) -> usize { | 131 | fn priority(n: &SyntaxToken) -> usize { |
@@ -1262,4 +1289,22 @@ fn main() { | |||
1262 | "#, | 1289 | "#, |
1263 | ); | 1290 | ); |
1264 | } | 1291 | } |
1292 | |||
1293 | #[test] | ||
1294 | fn goto_def_of_trait_impl_fn() { | ||
1295 | check( | ||
1296 | r#" | ||
1297 | trait Twait { | ||
1298 | fn a(); | ||
1299 | // ^ | ||
1300 | } | ||
1301 | |||
1302 | struct Stwuct; | ||
1303 | |||
1304 | impl Twait for Stwuct { | ||
1305 | fn a$0(); | ||
1306 | } | ||
1307 | "#, | ||
1308 | ); | ||
1309 | } | ||
1265 | } | 1310 | } |