|
1 | 1 | use std::convert::TryInto;
|
2 | 2 |
|
3 | 3 | use either::Either;
|
4 |
| -use hir::{InFile, Semantics}; |
| 4 | +use hir::{AsAssocItem, InFile, ModuleDef, Semantics}; |
5 | 5 | use ide_db::{
|
6 | 6 | base_db::{AnchoredPath, FileId, FileLoader},
|
7 |
| - defs::{NameClass, NameRefClass}, |
| 7 | + defs::{Definition, NameClass, NameRefClass}, |
8 | 8 | RootDatabase,
|
9 | 9 | };
|
10 | 10 | use syntax::{
|
@@ -57,7 +57,8 @@ pub(crate) fn goto_definition(
|
57 | 57 | },
|
58 | 58 | ast::Name(name) => {
|
59 | 59 | let def = NameClass::classify(&sema, &name)?.referenced_or_defined(sema.db);
|
60 |
| - def.try_to_nav(sema.db) |
| 60 | + try_find_trait_item_definition(&sema.db, &def) |
| 61 | + .or_else(|| def.try_to_nav(sema.db)) |
61 | 62 | },
|
62 | 63 | ast::Lifetime(lt) => if let Some(name_class) = NameClass::classify_lifetime(&sema, <) {
|
63 | 64 | let def = name_class.referenced_or_defined(sema.db);
|
@@ -99,6 +100,34 @@ fn try_lookup_include_path(
|
99 | 100 | })
|
100 | 101 | }
|
101 | 102 |
|
| 103 | +/// finds the trait definition of an impl'd item |
| 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_item_definition(db: &RootDatabase, def: &Definition) -> Option<NavigationTarget> { |
| 111 | + let name = def.name(db)?; |
| 112 | + let assoc = match def { |
| 113 | + Definition::ModuleDef(ModuleDef::Function(f)) => f.as_assoc_item(db), |
| 114 | + Definition::ModuleDef(ModuleDef::Const(c)) => c.as_assoc_item(db), |
| 115 | + Definition::ModuleDef(ModuleDef::TypeAlias(ty)) => ty.as_assoc_item(db), |
| 116 | + _ => None, |
| 117 | + }?; |
| 118 | + |
| 119 | + let imp = match assoc.container(db) { |
| 120 | + hir::AssocItemContainer::Impl(imp) => imp, |
| 121 | + _ => return None, |
| 122 | + }; |
| 123 | + |
| 124 | + let trait_ = imp.trait_(db)?; |
| 125 | + trait_ |
| 126 | + .items(db) |
| 127 | + .iter() |
| 128 | + .find_map(|itm| (itm.name(db)? == name).then(|| itm.try_to_nav(db)).flatten()) |
| 129 | +} |
| 130 | + |
102 | 131 | fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
|
103 | 132 | return tokens.max_by_key(priority);
|
104 | 133 | fn priority(n: &SyntaxToken) -> usize {
|
@@ -1259,6 +1288,60 @@ fn main() {
|
1259 | 1288 | //- /foo.txt
|
1260 | 1289 | // empty
|
1261 | 1290 | //^ file
|
| 1291 | +"#, |
| 1292 | + ); |
| 1293 | + } |
| 1294 | + |
| 1295 | + #[test] |
| 1296 | + fn goto_def_of_trait_impl_fn() { |
| 1297 | + check( |
| 1298 | + r#" |
| 1299 | +trait Twait { |
| 1300 | + fn a(); |
| 1301 | + // ^ |
| 1302 | +} |
| 1303 | +
|
| 1304 | +struct Stwuct; |
| 1305 | +
|
| 1306 | +impl Twait for Stwuct { |
| 1307 | + fn a$0(); |
| 1308 | +} |
| 1309 | +"#, |
| 1310 | + ); |
| 1311 | + } |
| 1312 | + |
| 1313 | + #[test] |
| 1314 | + fn goto_def_of_trait_impl_const() { |
| 1315 | + check( |
| 1316 | + r#" |
| 1317 | +trait Twait { |
| 1318 | + const NOMS: bool; |
| 1319 | + // ^^^^ |
| 1320 | +} |
| 1321 | +
|
| 1322 | +struct Stwuct; |
| 1323 | +
|
| 1324 | +impl Twait for Stwuct { |
| 1325 | + const NOMS$0: bool = true; |
| 1326 | +} |
| 1327 | +"#, |
| 1328 | + ); |
| 1329 | + } |
| 1330 | + |
| 1331 | + #[test] |
| 1332 | + fn goto_def_of_trait_impl_type_alias() { |
| 1333 | + check( |
| 1334 | + r#" |
| 1335 | +trait Twait { |
| 1336 | + type IsBad; |
| 1337 | + // ^^^^^ |
| 1338 | +} |
| 1339 | +
|
| 1340 | +struct Stwuct; |
| 1341 | +
|
| 1342 | +impl Twait for Stwuct { |
| 1343 | + type IsBad$0 = !; |
| 1344 | +} |
1262 | 1345 | "#,
|
1263 | 1346 | );
|
1264 | 1347 | }
|
|
0 commit comments