aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide_api
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2019-11-09 09:13:14 +0000
committerGitHub <[email protected]>2019-11-09 09:13:14 +0000
commit561bb979cecd786f5d311ea7bddb1e15d77a3848 (patch)
treea6aaa81c3acbfc5e6add5ebbee9abbc0f8357014 /crates/ra_ide_api
parent23939cabcc10ecc045a97361df182b9b4db32953 (diff)
parent0a5ec69404a2556dd82e5bb00b295aebaa291f04 (diff)
Merge #2169
2169: MBE: Mapping spans for goto definition r=matklad a=edwin0cheng Currently, go to definition gives the wrong span in MBE. This PR implement a mapping mechanism to fix it and it could be used for future MBE hygiene implementation. The basic idea of the mapping is: 1. When expanding the macro, generated 2 `TokenMap` which maps the macro args and macro defs between tokens and input text-ranges. 2. Before converting generated `TokenTree` to `SyntaxNode`, generated a `ExpandedRangeMap` which is a mapping between token and output text-ranges. 3. Using these 3 mappings to construct an `ExpansionInfo` which can map between input text ranges and output text ranges. Co-authored-by: Edwin Cheng <[email protected]>
Diffstat (limited to 'crates/ra_ide_api')
-rw-r--r--crates/ra_ide_api/src/display/navigation_target.rs120
-rw-r--r--crates/ra_ide_api/src/goto_definition.rs77
-rw-r--r--crates/ra_ide_api/src/status.rs4
3 files changed, 154 insertions, 47 deletions
diff --git a/crates/ra_ide_api/src/display/navigation_target.rs b/crates/ra_ide_api/src/display/navigation_target.rs
index 5cb67fb95..1bf81e7d5 100644
--- a/crates/ra_ide_api/src/display/navigation_target.rs
+++ b/crates/ra_ide_api/src/display/navigation_target.rs
@@ -29,6 +29,21 @@ pub struct NavigationTarget {
29 docs: Option<String>, 29 docs: Option<String>,
30} 30}
31 31
32fn find_range_from_node(
33 db: &RootDatabase,
34 src: hir::HirFileId,
35 node: &SyntaxNode,
36) -> (FileId, TextRange) {
37 let text_range = node.text_range();
38 let (file_id, text_range) = src
39 .expansion_info(db)
40 .and_then(|expansion_info| expansion_info.find_range(text_range))
41 .unwrap_or((src, text_range));
42
43 // FIXME: handle recursive macro generated macro
44 (file_id.original_file(db), text_range)
45}
46
32impl NavigationTarget { 47impl NavigationTarget {
33 /// When `focus_range` is specified, returns it. otherwise 48 /// When `focus_range` is specified, returns it. otherwise
34 /// returns `full_range` 49 /// returns `full_range`
@@ -72,8 +87,12 @@ impl NavigationTarget {
72 self.focus_range 87 self.focus_range
73 } 88 }
74 89
75 pub(crate) fn from_bind_pat(file_id: FileId, pat: &ast::BindPat) -> NavigationTarget { 90 pub(crate) fn from_bind_pat(
76 NavigationTarget::from_named(file_id, pat, None, None) 91 db: &RootDatabase,
92 file_id: FileId,
93 pat: &ast::BindPat,
94 ) -> NavigationTarget {
95 NavigationTarget::from_named(db, file_id.into(), pat, None, None)
77 } 96 }
78 97
79 pub(crate) fn from_symbol(db: &RootDatabase, symbol: FileSymbol) -> NavigationTarget { 98 pub(crate) fn from_symbol(db: &RootDatabase, symbol: FileSymbol) -> NavigationTarget {
@@ -96,7 +115,7 @@ impl NavigationTarget {
96 ) -> NavigationTarget { 115 ) -> NavigationTarget {
97 let parse = db.parse(file_id); 116 let parse = db.parse(file_id);
98 let pat = pat.to_node(parse.tree().syntax()); 117 let pat = pat.to_node(parse.tree().syntax());
99 NavigationTarget::from_bind_pat(file_id, &pat) 118 NavigationTarget::from_bind_pat(db, file_id, &pat)
100 } 119 }
101 120
102 pub(crate) fn from_self_param( 121 pub(crate) fn from_self_param(
@@ -119,31 +138,46 @@ impl NavigationTarget {
119 138
120 pub(crate) fn from_module(db: &RootDatabase, module: hir::Module) -> NavigationTarget { 139 pub(crate) fn from_module(db: &RootDatabase, module: hir::Module) -> NavigationTarget {
121 let src = module.definition_source(db); 140 let src = module.definition_source(db);
122 let file_id = src.file_id.original_file(db);
123 let name = module.name(db).map(|it| it.to_string().into()).unwrap_or_default(); 141 let name = module.name(db).map(|it| it.to_string().into()).unwrap_or_default();
124 match src.ast { 142 match src.ast {
125 ModuleSource::SourceFile(node) => { 143 ModuleSource::SourceFile(node) => {
126 NavigationTarget::from_syntax(file_id, name, None, node.syntax(), None, None) 144 let (file_id, text_range) = find_range_from_node(db, src.file_id, node.syntax());
145
146 NavigationTarget::from_syntax(
147 file_id,
148 name,
149 None,
150 text_range,
151 node.syntax(),
152 None,
153 None,
154 )
155 }
156 ModuleSource::Module(node) => {
157 let (file_id, text_range) = find_range_from_node(db, src.file_id, node.syntax());
158
159 NavigationTarget::from_syntax(
160 file_id,
161 name,
162 None,
163 text_range,
164 node.syntax(),
165 node.doc_comment_text(),
166 node.short_label(),
167 )
127 } 168 }
128 ModuleSource::Module(node) => NavigationTarget::from_syntax(
129 file_id,
130 name,
131 None,
132 node.syntax(),
133 node.doc_comment_text(),
134 node.short_label(),
135 ),
136 } 169 }
137 } 170 }
138 171
139 pub(crate) fn from_module_to_decl(db: &RootDatabase, module: hir::Module) -> NavigationTarget { 172 pub(crate) fn from_module_to_decl(db: &RootDatabase, module: hir::Module) -> NavigationTarget {
140 let name = module.name(db).map(|it| it.to_string().into()).unwrap_or_default(); 173 let name = module.name(db).map(|it| it.to_string().into()).unwrap_or_default();
141 if let Some(src) = module.declaration_source(db) { 174 if let Some(src) = module.declaration_source(db) {
142 let file_id = src.file_id.original_file(db); 175 let (file_id, text_range) = find_range_from_node(db, src.file_id, src.ast.syntax());
143 return NavigationTarget::from_syntax( 176 return NavigationTarget::from_syntax(
144 file_id, 177 file_id,
145 name, 178 name,
146 None, 179 None,
180 text_range,
147 src.ast.syntax(), 181 src.ast.syntax(),
148 src.ast.doc_comment_text(), 182 src.ast.doc_comment_text(),
149 src.ast.short_label(), 183 src.ast.short_label(),
@@ -154,13 +188,25 @@ impl NavigationTarget {
154 188
155 pub(crate) fn from_field(db: &RootDatabase, field: hir::StructField) -> NavigationTarget { 189 pub(crate) fn from_field(db: &RootDatabase, field: hir::StructField) -> NavigationTarget {
156 let src = field.source(db); 190 let src = field.source(db);
157 let file_id = src.file_id.original_file(db);
158 match src.ast { 191 match src.ast {
159 FieldSource::Named(it) => { 192 FieldSource::Named(it) => NavigationTarget::from_named(
160 NavigationTarget::from_named(file_id, &it, it.doc_comment_text(), it.short_label()) 193 db,
161 } 194 src.file_id,
195 &it,
196 it.doc_comment_text(),
197 it.short_label(),
198 ),
162 FieldSource::Pos(it) => { 199 FieldSource::Pos(it) => {
163 NavigationTarget::from_syntax(file_id, "".into(), None, it.syntax(), None, None) 200 let (file_id, text_range) = find_range_from_node(db, src.file_id, it.syntax());
201 NavigationTarget::from_syntax(
202 file_id,
203 "".into(),
204 None,
205 text_range,
206 it.syntax(),
207 None,
208 None,
209 )
164 } 210 }
165 } 211 }
166 } 212 }
@@ -172,7 +218,8 @@ impl NavigationTarget {
172 { 218 {
173 let src = def.source(db); 219 let src = def.source(db);
174 NavigationTarget::from_named( 220 NavigationTarget::from_named(
175 src.file_id.original_file(db), 221 db,
222 src.file_id,
176 &src.ast, 223 &src.ast,
177 src.ast.doc_comment_text(), 224 src.ast.doc_comment_text(),
178 src.ast.short_label(), 225 src.ast.short_label(),
@@ -212,10 +259,13 @@ impl NavigationTarget {
212 impl_block: hir::ImplBlock, 259 impl_block: hir::ImplBlock,
213 ) -> NavigationTarget { 260 ) -> NavigationTarget {
214 let src = impl_block.source(db); 261 let src = impl_block.source(db);
262 let (file_id, text_range) = find_range_from_node(db, src.file_id, src.ast.syntax());
263
215 NavigationTarget::from_syntax( 264 NavigationTarget::from_syntax(
216 src.file_id.original_file(db), 265 file_id,
217 "impl".into(), 266 "impl".into(),
218 None, 267 None,
268 text_range,
219 src.ast.syntax(), 269 src.ast.syntax(),
220 None, 270 None,
221 None, 271 None,
@@ -236,12 +286,7 @@ impl NavigationTarget {
236 pub(crate) fn from_macro_def(db: &RootDatabase, macro_call: hir::MacroDef) -> NavigationTarget { 286 pub(crate) fn from_macro_def(db: &RootDatabase, macro_call: hir::MacroDef) -> NavigationTarget {
237 let src = macro_call.source(db); 287 let src = macro_call.source(db);
238 log::debug!("nav target {:#?}", src.ast.syntax()); 288 log::debug!("nav target {:#?}", src.ast.syntax());
239 NavigationTarget::from_named( 289 NavigationTarget::from_named(db, src.file_id, &src.ast, src.ast.doc_comment_text(), None)
240 src.file_id.original_file(db),
241 &src.ast,
242 src.ast.doc_comment_text(),
243 None,
244 )
245 } 290 }
246 291
247 #[cfg(test)] 292 #[cfg(test)]
@@ -270,21 +315,33 @@ impl NavigationTarget {
270 315
271 /// Allows `NavigationTarget` to be created from a `NameOwner` 316 /// Allows `NavigationTarget` to be created from a `NameOwner`
272 pub(crate) fn from_named( 317 pub(crate) fn from_named(
273 file_id: FileId, 318 db: &RootDatabase,
319 file_id: hir::HirFileId,
274 node: &impl ast::NameOwner, 320 node: &impl ast::NameOwner,
275 docs: Option<String>, 321 docs: Option<String>,
276 description: Option<String>, 322 description: Option<String>,
277 ) -> NavigationTarget { 323 ) -> NavigationTarget {
278 //FIXME: use `_` instead of empty string 324 //FIXME: use `_` instead of empty string
279 let name = node.name().map(|it| it.text().clone()).unwrap_or_default(); 325 let name = node.name().map(|it| it.text().clone()).unwrap_or_default();
280 let focus_range = node.name().map(|it| it.syntax().text_range()); 326 let focus_range = node.name().map(|it| find_range_from_node(db, file_id, it.syntax()).1);
281 NavigationTarget::from_syntax(file_id, name, focus_range, node.syntax(), docs, description) 327 let (file_id, full_range) = find_range_from_node(db, file_id, node.syntax());
328
329 NavigationTarget::from_syntax(
330 file_id,
331 name,
332 focus_range,
333 full_range,
334 node.syntax(),
335 docs,
336 description,
337 )
282 } 338 }
283 339
284 fn from_syntax( 340 fn from_syntax(
285 file_id: FileId, 341 file_id: FileId,
286 name: SmolStr, 342 name: SmolStr,
287 focus_range: Option<TextRange>, 343 focus_range: Option<TextRange>,
344 full_range: TextRange,
288 node: &SyntaxNode, 345 node: &SyntaxNode,
289 docs: Option<String>, 346 docs: Option<String>,
290 description: Option<String>, 347 description: Option<String>,
@@ -293,9 +350,8 @@ impl NavigationTarget {
293 file_id, 350 file_id,
294 name, 351 name,
295 kind: node.kind(), 352 kind: node.kind(),
296 full_range: node.text_range(), 353 full_range,
297 focus_range, 354 focus_range,
298 // ptr: Some(LocalSyntaxPtr::new(node)),
299 container_name: None, 355 container_name: None,
300 description, 356 description,
301 docs, 357 docs,
diff --git a/crates/ra_ide_api/src/goto_definition.rs b/crates/ra_ide_api/src/goto_definition.rs
index c1ce54bea..afa59cbe3 100644
--- a/crates/ra_ide_api/src/goto_definition.rs
+++ b/crates/ra_ide_api/src/goto_definition.rs
@@ -101,19 +101,20 @@ pub(crate) fn name_definition(
101 } 101 }
102 } 102 }
103 103
104 if let Some(nav) = named_target(file_id, &parent) { 104 if let Some(nav) = named_target(db, file_id, &parent) {
105 return Some(vec![nav]); 105 return Some(vec![nav]);
106 } 106 }
107 107
108 None 108 None
109} 109}
110 110
111fn named_target(file_id: FileId, node: &SyntaxNode) -> Option<NavigationTarget> { 111fn named_target(db: &RootDatabase, file_id: FileId, node: &SyntaxNode) -> Option<NavigationTarget> {
112 match_ast! { 112 match_ast! {
113 match node { 113 match node {
114 ast::StructDef(it) => { 114 ast::StructDef(it) => {
115 Some(NavigationTarget::from_named( 115 Some(NavigationTarget::from_named(
116 file_id, 116 db,
117 file_id.into(),
117 &it, 118 &it,
118 it.doc_comment_text(), 119 it.doc_comment_text(),
119 it.short_label(), 120 it.short_label(),
@@ -121,7 +122,8 @@ fn named_target(file_id: FileId, node: &SyntaxNode) -> Option<NavigationTarget>
121 }, 122 },
122 ast::EnumDef(it) => { 123 ast::EnumDef(it) => {
123 Some(NavigationTarget::from_named( 124 Some(NavigationTarget::from_named(
124 file_id, 125 db,
126 file_id.into(),
125 &it, 127 &it,
126 it.doc_comment_text(), 128 it.doc_comment_text(),
127 it.short_label(), 129 it.short_label(),
@@ -129,7 +131,8 @@ fn named_target(file_id: FileId, node: &SyntaxNode) -> Option<NavigationTarget>
129 }, 131 },
130 ast::EnumVariant(it) => { 132 ast::EnumVariant(it) => {
131 Some(NavigationTarget::from_named( 133 Some(NavigationTarget::from_named(
132 file_id, 134 db,
135 file_id.into(),
133 &it, 136 &it,
134 it.doc_comment_text(), 137 it.doc_comment_text(),
135 it.short_label(), 138 it.short_label(),
@@ -137,7 +140,8 @@ fn named_target(file_id: FileId, node: &SyntaxNode) -> Option<NavigationTarget>
137 }, 140 },
138 ast::FnDef(it) => { 141 ast::FnDef(it) => {
139 Some(NavigationTarget::from_named( 142 Some(NavigationTarget::from_named(
140 file_id, 143 db,
144 file_id.into(),
141 &it, 145 &it,
142 it.doc_comment_text(), 146 it.doc_comment_text(),
143 it.short_label(), 147 it.short_label(),
@@ -145,7 +149,8 @@ fn named_target(file_id: FileId, node: &SyntaxNode) -> Option<NavigationTarget>
145 }, 149 },
146 ast::TypeAliasDef(it) => { 150 ast::TypeAliasDef(it) => {
147 Some(NavigationTarget::from_named( 151 Some(NavigationTarget::from_named(
148 file_id, 152 db,
153 file_id.into(),
149 &it, 154 &it,
150 it.doc_comment_text(), 155 it.doc_comment_text(),
151 it.short_label(), 156 it.short_label(),
@@ -153,7 +158,8 @@ fn named_target(file_id: FileId, node: &SyntaxNode) -> Option<NavigationTarget>
153 }, 158 },
154 ast::ConstDef(it) => { 159 ast::ConstDef(it) => {
155 Some(NavigationTarget::from_named( 160 Some(NavigationTarget::from_named(
156 file_id, 161 db,
162 file_id.into(),
157 &it, 163 &it,
158 it.doc_comment_text(), 164 it.doc_comment_text(),
159 it.short_label(), 165 it.short_label(),
@@ -161,7 +167,8 @@ fn named_target(file_id: FileId, node: &SyntaxNode) -> Option<NavigationTarget>
161 }, 167 },
162 ast::StaticDef(it) => { 168 ast::StaticDef(it) => {
163 Some(NavigationTarget::from_named( 169 Some(NavigationTarget::from_named(
164 file_id, 170 db,
171 file_id.into(),
165 &it, 172 &it,
166 it.doc_comment_text(), 173 it.doc_comment_text(),
167 it.short_label(), 174 it.short_label(),
@@ -169,7 +176,8 @@ fn named_target(file_id: FileId, node: &SyntaxNode) -> Option<NavigationTarget>
169 }, 176 },
170 ast::TraitDef(it) => { 177 ast::TraitDef(it) => {
171 Some(NavigationTarget::from_named( 178 Some(NavigationTarget::from_named(
172 file_id, 179 db,
180 file_id.into(),
173 &it, 181 &it,
174 it.doc_comment_text(), 182 it.doc_comment_text(),
175 it.short_label(), 183 it.short_label(),
@@ -177,7 +185,8 @@ fn named_target(file_id: FileId, node: &SyntaxNode) -> Option<NavigationTarget>
177 }, 185 },
178 ast::RecordFieldDef(it) => { 186 ast::RecordFieldDef(it) => {
179 Some(NavigationTarget::from_named( 187 Some(NavigationTarget::from_named(
180 file_id, 188 db,
189 file_id.into(),
181 &it, 190 &it,
182 it.doc_comment_text(), 191 it.doc_comment_text(),
183 it.short_label(), 192 it.short_label(),
@@ -185,7 +194,8 @@ fn named_target(file_id: FileId, node: &SyntaxNode) -> Option<NavigationTarget>
185 }, 194 },
186 ast::Module(it) => { 195 ast::Module(it) => {
187 Some(NavigationTarget::from_named( 196 Some(NavigationTarget::from_named(
188 file_id, 197 db,
198 file_id.into(),
189 &it, 199 &it,
190 it.doc_comment_text(), 200 it.doc_comment_text(),
191 it.short_label(), 201 it.short_label(),
@@ -193,7 +203,8 @@ fn named_target(file_id: FileId, node: &SyntaxNode) -> Option<NavigationTarget>
193 }, 203 },
194 ast::MacroCall(it) => { 204 ast::MacroCall(it) => {
195 Some(NavigationTarget::from_named( 205 Some(NavigationTarget::from_named(
196 file_id, 206 db,
207 file_id.into(),
197 &it, 208 &it,
198 it.doc_comment_text(), 209 it.doc_comment_text(),
199 None, 210 None,
@@ -335,6 +346,46 @@ mod tests {
335 } 346 }
336 347
337 #[test] 348 #[test]
349 fn goto_definition_works_for_macro_defined_fn_with_arg() {
350 check_goto(
351 "
352 //- /lib.rs
353 macro_rules! define_fn {
354 ($name:ident) => (fn $name() {})
355 }
356
357 define_fn!(
358 foo
359 )
360
361 fn bar() {
362 <|>foo();
363 }
364 ",
365 "foo FN_DEF FileId(1) [80; 83) [80; 83)",
366 );
367 }
368
369 #[test]
370 fn goto_definition_works_for_macro_defined_fn_no_arg() {
371 check_goto(
372 "
373 //- /lib.rs
374 macro_rules! define_fn {
375 () => (fn foo() {})
376 }
377
378 define_fn!();
379
380 fn bar() {
381 <|>foo();
382 }
383 ",
384 "foo FN_DEF FileId(1) [39; 42) [39; 42)",
385 );
386 }
387
388 #[test]
338 fn goto_definition_works_for_methods() { 389 fn goto_definition_works_for_methods() {
339 covers!(goto_definition_works_for_methods); 390 covers!(goto_definition_works_for_methods);
340 check_goto( 391 check_goto(
diff --git a/crates/ra_ide_api/src/status.rs b/crates/ra_ide_api/src/status.rs
index f91f16c8e..1bb27eb85 100644
--- a/crates/ra_ide_api/src/status.rs
+++ b/crates/ra_ide_api/src/status.rs
@@ -94,10 +94,10 @@ impl FromIterator<TableEntry<FileId, Parse<ast::SourceFile>>> for SyntaxTreeStat
94 } 94 }
95} 95}
96 96
97impl FromIterator<TableEntry<MacroFile, Option<Parse<SyntaxNode>>>> for SyntaxTreeStats { 97impl<M> FromIterator<TableEntry<MacroFile, Option<(Parse<SyntaxNode>, M)>>> for SyntaxTreeStats {
98 fn from_iter<T>(iter: T) -> SyntaxTreeStats 98 fn from_iter<T>(iter: T) -> SyntaxTreeStats
99 where 99 where
100 T: IntoIterator<Item = TableEntry<MacroFile, Option<Parse<SyntaxNode>>>>, 100 T: IntoIterator<Item = TableEntry<MacroFile, Option<(Parse<SyntaxNode>, M)>>>,
101 { 101 {
102 let mut res = SyntaxTreeStats::default(); 102 let mut res = SyntaxTreeStats::default();
103 for entry in iter { 103 for entry in iter {