aboutsummaryrefslogtreecommitdiff
path: root/crates/ide/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide/src')
-rw-r--r--crates/ide/src/goto_definition.rs141
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 @@
1use std::convert::TryInto;
2
1use either::Either; 3use either::Either;
2use hir::{InFile, Semantics}; 4use hir::{AsAssocItem, InFile, ModuleDef, Semantics};
3use ide_db::{ 5use ide_db::{
4 defs::{NameClass, NameRefClass}, 6 base_db::{AnchoredPath, FileId, FileLoader},
7 defs::{Definition, NameClass, NameRefClass},
5 RootDatabase, 8 RootDatabase,
6}; 9};
7use syntax::{ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TokenAtOffset, T}; 10use syntax::{
11 ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TextRange, TokenAtOffset, T,
12};
8 13
9use crate::{ 14use 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, &lt) { 63 ast::Lifetime(lt) => if let Some(name_class) = NameClass::classify_lifetime(&sema, &lt) {
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(&lt)) 67 reference_definition(&sema, Either::Left(&lt))
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
77fn 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/// ```
110fn 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
71fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { 131fn 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
1285fn 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#"
1299trait Twait {
1300 fn a();
1301 // ^
1302}
1303
1304struct Stwuct;
1305
1306impl 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#"
1317trait Twait {
1318 const NOMS: bool;
1319 // ^^^^
1320}
1321
1322struct Stwuct;
1323
1324impl 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#"
1335trait Twait {
1336 type IsBad;
1337 // ^^^^^
1338}
1339
1340struct Stwuct;
1341
1342impl Twait for Stwuct {
1343 type IsBad$0 = !;
1344}
1345"#,
1346 );
1347 }
1219} 1348}