aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJade <[email protected]>2021-06-02 00:16:59 +0100
committerJade <[email protected]>2021-06-07 05:51:17 +0100
commit8a57c736404abac81a6de20b0e90c19021e040b9 (patch)
tree5ece2fcdc66cf91dd269ff0c64c2dc91ff216565
parent13da28cc2bc1b59f7af817eca36927a71edb023c (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.rs9
-rw-r--r--crates/ide/src/goto_definition.rs51
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 @@
1use std::convert::TryInto; 1use std::convert::TryInto;
2 2
3use either::Either; 3use either::Either;
4use hir::{InFile, Semantics}; 4use hir::{AsAssocItem, InFile, ModuleDef, Semantics};
5use ide_db::{ 5use 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};
10use syntax::{ 10use 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, &lt) { 63 ast::Lifetime(lt) => if let Some(name_class) = NameClass::classify_lifetime(&sema, &lt) {
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/// ```
110fn 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
102fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { 129fn 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#"
1297trait Twait {
1298 fn a();
1299 // ^
1300}
1301
1302struct Stwuct;
1303
1304impl Twait for Stwuct {
1305 fn a$0();
1306}
1307"#,
1308 );
1309 }
1265} 1310}