aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide_api/src/goto_definition.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide_api/src/goto_definition.rs')
-rw-r--r--crates/ra_ide_api/src/goto_definition.rs115
1 files changed, 79 insertions, 36 deletions
diff --git a/crates/ra_ide_api/src/goto_definition.rs b/crates/ra_ide_api/src/goto_definition.rs
index 6c8387f6c..b693a4c31 100644
--- a/crates/ra_ide_api/src/goto_definition.rs
+++ b/crates/ra_ide_api/src/goto_definition.rs
@@ -1,10 +1,11 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use ra_db::{FileId, SourceDatabase}; 3use std::iter::successors;
4
5use hir::{db::AstDatabase, Source};
4use ra_syntax::{ 6use ra_syntax::{
5 algo::find_node_at_offset,
6 ast::{self, DocCommentsOwner}, 7 ast::{self, DocCommentsOwner},
7 match_ast, AstNode, SyntaxNode, 8 match_ast, AstNode, SyntaxNode, SyntaxToken,
8}; 9};
9 10
10use crate::{ 11use crate::{
@@ -18,17 +19,42 @@ pub(crate) fn goto_definition(
18 db: &RootDatabase, 19 db: &RootDatabase,
19 position: FilePosition, 20 position: FilePosition,
20) -> Option<RangeInfo<Vec<NavigationTarget>>> { 21) -> Option<RangeInfo<Vec<NavigationTarget>>> {
21 let parse = db.parse(position.file_id); 22 let token = descend_into_macros(db, position)?;
22 let syntax = parse.tree().syntax().clone(); 23
23 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(&syntax, position.offset) { 24 let res = match_ast! {
24 let navs = reference_definition(db, position.file_id, &name_ref).to_vec(); 25 match (token.ast.parent()) {
25 return Some(RangeInfo::new(name_ref.syntax().text_range(), navs.to_vec())); 26 ast::NameRef(name_ref) => {
26 } 27 let navs = reference_definition(db, token.with_ast(&name_ref)).to_vec();
27 if let Some(name) = find_node_at_offset::<ast::Name>(&syntax, position.offset) { 28 RangeInfo::new(name_ref.syntax().text_range(), navs.to_vec())
28 let navs = name_definition(db, position.file_id, &name)?; 29 },
29 return Some(RangeInfo::new(name.syntax().text_range(), navs)); 30 ast::Name(name) => {
30 } 31 let navs = name_definition(db, token.with_ast(&name))?;
31 None 32 RangeInfo::new(name.syntax().text_range(), navs)
33
34 },
35 _ => return None,
36 }
37 };
38
39 Some(res)
40}
41
42fn descend_into_macros(db: &RootDatabase, position: FilePosition) -> Option<Source<SyntaxToken>> {
43 let file = db.parse_or_expand(position.file_id.into())?;
44 let token = file.token_at_offset(position.offset).filter(|it| !it.kind().is_trivia()).next()?;
45
46 successors(Some(Source::new(position.file_id.into(), token)), |token| {
47 let macro_call = token.ast.ancestors().find_map(ast::MacroCall::cast)?;
48 let tt = macro_call.token_tree()?;
49 if !token.ast.text_range().is_subrange(&tt.syntax().text_range()) {
50 return None;
51 }
52 let source_analyzer =
53 hir::SourceAnalyzer::new(db, token.with_ast(token.ast.parent()).as_ref(), None);
54 let exp = source_analyzer.expand(db, &macro_call)?;
55 exp.map_token_down(db, token.as_ref())
56 })
57 .last()
32} 58}
33 59
34#[derive(Debug)] 60#[derive(Debug)]
@@ -49,12 +75,11 @@ impl ReferenceResult {
49 75
50pub(crate) fn reference_definition( 76pub(crate) fn reference_definition(
51 db: &RootDatabase, 77 db: &RootDatabase,
52 file_id: FileId, 78 name_ref: Source<&ast::NameRef>,
53 name_ref: &ast::NameRef,
54) -> ReferenceResult { 79) -> ReferenceResult {
55 use self::ReferenceResult::*; 80 use self::ReferenceResult::*;
56 81
57 let name_kind = classify_name_ref(db, file_id, &name_ref).map(|d| d.kind); 82 let name_kind = classify_name_ref(db, name_ref).map(|d| d.kind);
58 match name_kind { 83 match name_kind {
59 Some(Macro(mac)) => return Exact(mac.to_nav(db)), 84 Some(Macro(mac)) => return Exact(mac.to_nav(db)),
60 Some(Field(field)) => return Exact(field.to_nav(db)), 85 Some(Field(field)) => return Exact(field.to_nav(db)),
@@ -76,7 +101,7 @@ pub(crate) fn reference_definition(
76 }; 101 };
77 102
78 // Fallback index based approach: 103 // Fallback index based approach:
79 let navs = crate::symbol_index::index_resolve(db, name_ref) 104 let navs = crate::symbol_index::index_resolve(db, name_ref.ast)
80 .into_iter() 105 .into_iter()
81 .map(|s| s.to_nav(db)) 106 .map(|s| s.to_nav(db))
82 .collect(); 107 .collect();
@@ -85,14 +110,13 @@ pub(crate) fn reference_definition(
85 110
86pub(crate) fn name_definition( 111pub(crate) fn name_definition(
87 db: &RootDatabase, 112 db: &RootDatabase,
88 file_id: FileId, 113 name: Source<&ast::Name>,
89 name: &ast::Name,
90) -> Option<Vec<NavigationTarget>> { 114) -> Option<Vec<NavigationTarget>> {
91 let parent = name.syntax().parent()?; 115 let parent = name.ast.syntax().parent()?;
92 116
93 if let Some(module) = ast::Module::cast(parent.clone()) { 117 if let Some(module) = ast::Module::cast(parent.clone()) {
94 if module.has_semi() { 118 if module.has_semi() {
95 let src = hir::Source { file_id: file_id.into(), ast: module }; 119 let src = name.with_ast(module);
96 if let Some(child_module) = hir::Module::from_declaration(db, src) { 120 if let Some(child_module) = hir::Module::from_declaration(db, src) {
97 let nav = child_module.to_nav(db); 121 let nav = child_module.to_nav(db);
98 return Some(vec![nav]); 122 return Some(vec![nav]);
@@ -100,20 +124,20 @@ pub(crate) fn name_definition(
100 } 124 }
101 } 125 }
102 126
103 if let Some(nav) = named_target(db, file_id, &parent) { 127 if let Some(nav) = named_target(db, name.with_ast(&parent)) {
104 return Some(vec![nav]); 128 return Some(vec![nav]);
105 } 129 }
106 130
107 None 131 None
108} 132}
109 133
110fn named_target(db: &RootDatabase, file_id: FileId, node: &SyntaxNode) -> Option<NavigationTarget> { 134fn named_target(db: &RootDatabase, node: Source<&SyntaxNode>) -> Option<NavigationTarget> {
111 match_ast! { 135 match_ast! {
112 match node { 136 match (node.ast) {
113 ast::StructDef(it) => { 137 ast::StructDef(it) => {
114 Some(NavigationTarget::from_named( 138 Some(NavigationTarget::from_named(
115 db, 139 db,
116 file_id.into(), 140 node.file_id,
117 &it, 141 &it,
118 it.doc_comment_text(), 142 it.doc_comment_text(),
119 it.short_label(), 143 it.short_label(),
@@ -122,7 +146,7 @@ fn named_target(db: &RootDatabase, file_id: FileId, node: &SyntaxNode) -> Option
122 ast::EnumDef(it) => { 146 ast::EnumDef(it) => {
123 Some(NavigationTarget::from_named( 147 Some(NavigationTarget::from_named(
124 db, 148 db,
125 file_id.into(), 149 node.file_id,
126 &it, 150 &it,
127 it.doc_comment_text(), 151 it.doc_comment_text(),
128 it.short_label(), 152 it.short_label(),
@@ -131,7 +155,7 @@ fn named_target(db: &RootDatabase, file_id: FileId, node: &SyntaxNode) -> Option
131 ast::EnumVariant(it) => { 155 ast::EnumVariant(it) => {
132 Some(NavigationTarget::from_named( 156 Some(NavigationTarget::from_named(
133 db, 157 db,
134 file_id.into(), 158 node.file_id,
135 &it, 159 &it,
136 it.doc_comment_text(), 160 it.doc_comment_text(),
137 it.short_label(), 161 it.short_label(),
@@ -140,7 +164,7 @@ fn named_target(db: &RootDatabase, file_id: FileId, node: &SyntaxNode) -> Option
140 ast::FnDef(it) => { 164 ast::FnDef(it) => {
141 Some(NavigationTarget::from_named( 165 Some(NavigationTarget::from_named(
142 db, 166 db,
143 file_id.into(), 167 node.file_id,
144 &it, 168 &it,
145 it.doc_comment_text(), 169 it.doc_comment_text(),
146 it.short_label(), 170 it.short_label(),
@@ -149,7 +173,7 @@ fn named_target(db: &RootDatabase, file_id: FileId, node: &SyntaxNode) -> Option
149 ast::TypeAliasDef(it) => { 173 ast::TypeAliasDef(it) => {
150 Some(NavigationTarget::from_named( 174 Some(NavigationTarget::from_named(
151 db, 175 db,
152 file_id.into(), 176 node.file_id,
153 &it, 177 &it,
154 it.doc_comment_text(), 178 it.doc_comment_text(),
155 it.short_label(), 179 it.short_label(),
@@ -158,7 +182,7 @@ fn named_target(db: &RootDatabase, file_id: FileId, node: &SyntaxNode) -> Option
158 ast::ConstDef(it) => { 182 ast::ConstDef(it) => {
159 Some(NavigationTarget::from_named( 183 Some(NavigationTarget::from_named(
160 db, 184 db,
161 file_id.into(), 185 node.file_id,
162 &it, 186 &it,
163 it.doc_comment_text(), 187 it.doc_comment_text(),
164 it.short_label(), 188 it.short_label(),
@@ -167,7 +191,7 @@ fn named_target(db: &RootDatabase, file_id: FileId, node: &SyntaxNode) -> Option
167 ast::StaticDef(it) => { 191 ast::StaticDef(it) => {
168 Some(NavigationTarget::from_named( 192 Some(NavigationTarget::from_named(
169 db, 193 db,
170 file_id.into(), 194 node.file_id,
171 &it, 195 &it,
172 it.doc_comment_text(), 196 it.doc_comment_text(),
173 it.short_label(), 197 it.short_label(),
@@ -176,7 +200,7 @@ fn named_target(db: &RootDatabase, file_id: FileId, node: &SyntaxNode) -> Option
176 ast::TraitDef(it) => { 200 ast::TraitDef(it) => {
177 Some(NavigationTarget::from_named( 201 Some(NavigationTarget::from_named(
178 db, 202 db,
179 file_id.into(), 203 node.file_id,
180 &it, 204 &it,
181 it.doc_comment_text(), 205 it.doc_comment_text(),
182 it.short_label(), 206 it.short_label(),
@@ -185,7 +209,7 @@ fn named_target(db: &RootDatabase, file_id: FileId, node: &SyntaxNode) -> Option
185 ast::RecordFieldDef(it) => { 209 ast::RecordFieldDef(it) => {
186 Some(NavigationTarget::from_named( 210 Some(NavigationTarget::from_named(
187 db, 211 db,
188 file_id.into(), 212 node.file_id,
189 &it, 213 &it,
190 it.doc_comment_text(), 214 it.doc_comment_text(),
191 it.short_label(), 215 it.short_label(),
@@ -194,7 +218,7 @@ fn named_target(db: &RootDatabase, file_id: FileId, node: &SyntaxNode) -> Option
194 ast::Module(it) => { 218 ast::Module(it) => {
195 Some(NavigationTarget::from_named( 219 Some(NavigationTarget::from_named(
196 db, 220 db,
197 file_id.into(), 221 node.file_id,
198 &it, 222 &it,
199 it.doc_comment_text(), 223 it.doc_comment_text(),
200 it.short_label(), 224 it.short_label(),
@@ -203,7 +227,7 @@ fn named_target(db: &RootDatabase, file_id: FileId, node: &SyntaxNode) -> Option
203 ast::MacroCall(it) => { 227 ast::MacroCall(it) => {
204 Some(NavigationTarget::from_named( 228 Some(NavigationTarget::from_named(
205 db, 229 db,
206 file_id.into(), 230 node.file_id,
207 &it, 231 &it,
208 it.doc_comment_text(), 232 it.doc_comment_text(),
209 None, 233 None,
@@ -677,4 +701,23 @@ mod tests {
677 "bar MODULE FileId(1) [0; 11) [4; 7)", 701 "bar MODULE FileId(1) [0; 11) [4; 7)",
678 ); 702 );
679 } 703 }
704
705 #[test]
706 fn goto_from_macro() {
707 check_goto(
708 "
709 //- /lib.rs
710 macro_rules! id {
711 ($($tt:tt)*) => { $($tt)* }
712 }
713 fn foo() {}
714 id! {
715 fn bar() {
716 fo<|>o();
717 }
718 }
719 ",
720 "foo FN_DEF FileId(1) [52; 63) [55; 58)",
721 );
722 }
680} 723}