aboutsummaryrefslogtreecommitdiff
path: root/crates/ide/src/goto_definition.rs
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 /crates/ide/src/goto_definition.rs
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.
Diffstat (limited to 'crates/ide/src/goto_definition.rs')
-rw-r--r--crates/ide/src/goto_definition.rs51
1 files changed, 48 insertions, 3 deletions
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}