diff options
Diffstat (limited to 'crates/ra_ide_api/src/goto_definition.rs')
-rw-r--r-- | crates/ra_ide_api/src/goto_definition.rs | 115 |
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 | ||
3 | use ra_db::{FileId, SourceDatabase}; | 3 | use std::iter::successors; |
4 | |||
5 | use hir::{db::AstDatabase, Source}; | ||
4 | use ra_syntax::{ | 6 | use 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 | ||
10 | use crate::{ | 11 | use 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 | |||
42 | fn 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, ¯o_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 | ||
50 | pub(crate) fn reference_definition( | 76 | pub(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 | ||
86 | pub(crate) fn name_definition( | 111 | pub(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 | ||
110 | fn named_target(db: &RootDatabase, file_id: FileId, node: &SyntaxNode) -> Option<NavigationTarget> { | 134 | fn 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 | } |