diff options
Diffstat (limited to 'crates/ide/src')
-rw-r--r-- | crates/ide/src/goto_definition.rs | 141 |
1 files changed, 135 insertions, 6 deletions
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index a04333e63..2d36c34e9 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs | |||
@@ -1,10 +1,15 @@ | |||
1 | use std::convert::TryInto; | ||
2 | |||
1 | use either::Either; | 3 | use either::Either; |
2 | use hir::{InFile, Semantics}; | 4 | use hir::{AsAssocItem, InFile, ModuleDef, Semantics}; |
3 | use ide_db::{ | 5 | use ide_db::{ |
4 | defs::{NameClass, NameRefClass}, | 6 | base_db::{AnchoredPath, FileId, FileLoader}, |
7 | defs::{Definition, NameClass, NameRefClass}, | ||
5 | RootDatabase, | 8 | RootDatabase, |
6 | }; | 9 | }; |
7 | use syntax::{ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TokenAtOffset, T}; | 10 | use syntax::{ |
11 | ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TextRange, TokenAtOffset, T, | ||
12 | }; | ||
8 | 13 | ||
9 | use crate::{ | 14 | use crate::{ |
10 | display::TryToNav, | 15 | display::TryToNav, |
@@ -32,7 +37,7 @@ pub(crate) fn goto_definition( | |||
32 | let original_token = pick_best(file.token_at_offset(position.offset))?; | 37 | let original_token = pick_best(file.token_at_offset(position.offset))?; |
33 | let token = sema.descend_into_macros(original_token.clone()); | 38 | let token = sema.descend_into_macros(original_token.clone()); |
34 | let parent = token.parent()?; | 39 | let parent = token.parent()?; |
35 | if let Some(_) = ast::Comment::cast(token) { | 40 | if let Some(_) = ast::Comment::cast(token.clone()) { |
36 | let (attributes, def) = doc_attributes(&sema, &parent)?; | 41 | let (attributes, def) = doc_attributes(&sema, &parent)?; |
37 | 42 | ||
38 | let (docs, doc_mapping) = attributes.docs_with_rangemap(db)?; | 43 | let (docs, doc_mapping) = attributes.docs_with_rangemap(db)?; |
@@ -45,7 +50,6 @@ pub(crate) fn goto_definition( | |||
45 | let nav = resolve_doc_path_for_def(db, def, &link, ns)?.try_to_nav(db)?; | 50 | let nav = resolve_doc_path_for_def(db, def, &link, ns)?.try_to_nav(db)?; |
46 | return Some(RangeInfo::new(original_token.text_range(), vec![nav])); | 51 | return Some(RangeInfo::new(original_token.text_range(), vec![nav])); |
47 | } | 52 | } |
48 | |||
49 | let nav = match_ast! { | 53 | let nav = match_ast! { |
50 | match parent { | 54 | match parent { |
51 | ast::NameRef(name_ref) => { | 55 | ast::NameRef(name_ref) => { |
@@ -53,7 +57,8 @@ pub(crate) fn goto_definition( | |||
53 | }, | 57 | }, |
54 | ast::Name(name) => { | 58 | ast::Name(name) => { |
55 | let def = NameClass::classify(&sema, &name)?.referenced_or_defined(sema.db); | 59 | let def = NameClass::classify(&sema, &name)?.referenced_or_defined(sema.db); |
56 | def.try_to_nav(sema.db) | 60 | try_find_trait_item_definition(&sema.db, &def) |
61 | .or_else(|| def.try_to_nav(sema.db)) | ||
57 | }, | 62 | }, |
58 | 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, <) { |
59 | let def = name_class.referenced_or_defined(sema.db); | 64 | let def = name_class.referenced_or_defined(sema.db); |
@@ -61,6 +66,7 @@ pub(crate) fn goto_definition( | |||
61 | } else { | 66 | } else { |
62 | reference_definition(&sema, Either::Left(<)) | 67 | reference_definition(&sema, Either::Left(<)) |
63 | }, | 68 | }, |
69 | ast::TokenTree(tt) => try_lookup_include_path(sema.db, tt, token, position.file_id), | ||
64 | _ => return None, | 70 | _ => return None, |
65 | } | 71 | } |
66 | }; | 72 | }; |
@@ -68,6 +74,60 @@ pub(crate) fn goto_definition( | |||
68 | Some(RangeInfo::new(original_token.text_range(), nav.into_iter().collect())) | 74 | Some(RangeInfo::new(original_token.text_range(), nav.into_iter().collect())) |
69 | } | 75 | } |
70 | 76 | ||
77 | fn try_lookup_include_path( | ||
78 | db: &RootDatabase, | ||
79 | tt: ast::TokenTree, | ||
80 | token: SyntaxToken, | ||
81 | file_id: FileId, | ||
82 | ) -> Option<NavigationTarget> { | ||
83 | let path = ast::String::cast(token)?.value()?.into_owned(); | ||
84 | let macro_call = tt.syntax().parent().and_then(ast::MacroCall::cast)?; | ||
85 | let name = macro_call.path()?.segment()?.name_ref()?; | ||
86 | if !matches!(&*name.text(), "include" | "include_str" | "include_bytes") { | ||
87 | return None; | ||
88 | } | ||
89 | let file_id = db.resolve_path(AnchoredPath { anchor: file_id, path: &path })?; | ||
90 | let size = db.file_text(file_id).len().try_into().ok()?; | ||
91 | Some(NavigationTarget { | ||
92 | file_id, | ||
93 | full_range: TextRange::new(0.into(), size), | ||
94 | name: path.into(), | ||
95 | focus_range: None, | ||
96 | kind: None, | ||
97 | container_name: None, | ||
98 | description: None, | ||
99 | docs: None, | ||
100 | }) | ||
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 | |||
71 | fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { | 131 | fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { |
72 | return tokens.max_by_key(priority); | 132 | return tokens.max_by_key(priority); |
73 | fn priority(n: &SyntaxToken) -> usize { | 133 | fn priority(n: &SyntaxToken) -> usize { |
@@ -1216,4 +1276,73 @@ fn f(e: Enum) { | |||
1216 | "#, | 1276 | "#, |
1217 | ); | 1277 | ); |
1218 | } | 1278 | } |
1279 | |||
1280 | #[test] | ||
1281 | fn goto_include() { | ||
1282 | check( | ||
1283 | r#" | ||
1284 | //- /main.rs | ||
1285 | fn main() { | ||
1286 | let str = include_str!("foo.txt$0"); | ||
1287 | } | ||
1288 | //- /foo.txt | ||
1289 | // empty | ||
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 | } | ||
1345 | "#, | ||
1346 | ); | ||
1347 | } | ||
1219 | } | 1348 | } |