aboutsummaryrefslogtreecommitdiff
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
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]>
-rw-r--r--crates/ra_hir_expand/src/db.rs43
-rw-r--r--crates/ra_hir_expand/src/lib.rs57
-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
-rw-r--r--crates/ra_mbe/src/lib.rs6
-rw-r--r--crates/ra_mbe/src/syntax_bridge.rs83
-rw-r--r--crates/ra_mbe/src/tests.rs16
8 files changed, 305 insertions, 101 deletions
diff --git a/crates/ra_hir_expand/src/db.rs b/crates/ra_hir_expand/src/db.rs
index a4ee9a529..b789c6e7b 100644
--- a/crates/ra_hir_expand/src/db.rs
+++ b/crates/ra_hir_expand/src/db.rs
@@ -22,9 +22,12 @@ pub trait AstDatabase: SourceDatabase {
22 22
23 #[salsa::interned] 23 #[salsa::interned]
24 fn intern_macro(&self, macro_call: MacroCallLoc) -> MacroCallId; 24 fn intern_macro(&self, macro_call: MacroCallLoc) -> MacroCallId;
25 fn macro_arg(&self, id: MacroCallId) -> Option<Arc<tt::Subtree>>; 25 fn macro_arg(&self, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>>;
26 fn macro_def(&self, id: MacroDefId) -> Option<Arc<mbe::MacroRules>>; 26 fn macro_def(&self, id: MacroDefId) -> Option<Arc<(mbe::MacroRules, mbe::TokenMap)>>;
27 fn parse_macro(&self, macro_file: MacroFile) -> Option<Parse<SyntaxNode>>; 27 fn parse_macro(
28 &self,
29 macro_file: MacroFile,
30 ) -> Option<(Parse<SyntaxNode>, Arc<mbe::RevTokenMap>)>;
28 fn macro_expand(&self, macro_call: MacroCallId) -> Result<Arc<tt::Subtree>, String>; 31 fn macro_expand(&self, macro_call: MacroCallId) -> Result<Arc<tt::Subtree>, String>;
29} 32}
30 33
@@ -34,10 +37,13 @@ pub(crate) fn ast_id_map(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<AstIdM
34 Arc::new(map) 37 Arc::new(map)
35} 38}
36 39
37pub(crate) fn macro_def(db: &dyn AstDatabase, id: MacroDefId) -> Option<Arc<MacroRules>> { 40pub(crate) fn macro_def(
41 db: &dyn AstDatabase,
42 id: MacroDefId,
43) -> Option<Arc<(mbe::MacroRules, mbe::TokenMap)>> {
38 let macro_call = id.ast_id.to_node(db); 44 let macro_call = id.ast_id.to_node(db);
39 let arg = macro_call.token_tree()?; 45 let arg = macro_call.token_tree()?;
40 let (tt, _) = mbe::ast_to_token_tree(&arg).or_else(|| { 46 let (tt, tmap) = mbe::ast_to_token_tree(&arg).or_else(|| {
41 log::warn!("fail on macro_def to token tree: {:#?}", arg); 47 log::warn!("fail on macro_def to token tree: {:#?}", arg);
42 None 48 None
43 })?; 49 })?;
@@ -45,15 +51,18 @@ pub(crate) fn macro_def(db: &dyn AstDatabase, id: MacroDefId) -> Option<Arc<Macr
45 log::warn!("fail on macro_def parse: {:#?}", tt); 51 log::warn!("fail on macro_def parse: {:#?}", tt);
46 None 52 None
47 })?; 53 })?;
48 Some(Arc::new(rules)) 54 Some(Arc::new((rules, tmap)))
49} 55}
50 56
51pub(crate) fn macro_arg(db: &dyn AstDatabase, id: MacroCallId) -> Option<Arc<tt::Subtree>> { 57pub(crate) fn macro_arg(
58 db: &dyn AstDatabase,
59 id: MacroCallId,
60) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>> {
52 let loc = db.lookup_intern_macro(id); 61 let loc = db.lookup_intern_macro(id);
53 let macro_call = loc.ast_id.to_node(db); 62 let macro_call = loc.ast_id.to_node(db);
54 let arg = macro_call.token_tree()?; 63 let arg = macro_call.token_tree()?;
55 let (tt, _) = mbe::ast_to_token_tree(&arg)?; 64 let (tt, tmap) = mbe::ast_to_token_tree(&arg)?;
56 Some(Arc::new(tt)) 65 Some(Arc::new((tt, tmap)))
57} 66}
58 67
59pub(crate) fn macro_expand( 68pub(crate) fn macro_expand(
@@ -64,7 +73,7 @@ pub(crate) fn macro_expand(
64 let macro_arg = db.macro_arg(id).ok_or("Fail to args in to tt::TokenTree")?; 73 let macro_arg = db.macro_arg(id).ok_or("Fail to args in to tt::TokenTree")?;
65 74
66 let macro_rules = db.macro_def(loc.def).ok_or("Fail to find macro definition")?; 75 let macro_rules = db.macro_def(loc.def).ok_or("Fail to find macro definition")?;
67 let tt = macro_rules.expand(&macro_arg).map_err(|err| format!("{:?}", err))?; 76 let tt = macro_rules.0.expand(&macro_arg.0).map_err(|err| format!("{:?}", err))?;
68 // Set a hard limit for the expanded tt 77 // Set a hard limit for the expanded tt
69 let count = tt.count(); 78 let count = tt.count();
70 if count > 65536 { 79 if count > 65536 {
@@ -77,7 +86,7 @@ pub(crate) fn parse_or_expand(db: &dyn AstDatabase, file_id: HirFileId) -> Optio
77 match file_id.0 { 86 match file_id.0 {
78 HirFileIdRepr::FileId(file_id) => Some(db.parse(file_id).tree().syntax().clone()), 87 HirFileIdRepr::FileId(file_id) => Some(db.parse(file_id).tree().syntax().clone()),
79 HirFileIdRepr::MacroFile(macro_file) => { 88 HirFileIdRepr::MacroFile(macro_file) => {
80 db.parse_macro(macro_file).map(|it| it.syntax_node()) 89 db.parse_macro(macro_file).map(|(it, _)| it.syntax_node())
81 } 90 }
82 } 91 }
83} 92}
@@ -85,8 +94,9 @@ pub(crate) fn parse_or_expand(db: &dyn AstDatabase, file_id: HirFileId) -> Optio
85pub(crate) fn parse_macro( 94pub(crate) fn parse_macro(
86 db: &dyn AstDatabase, 95 db: &dyn AstDatabase,
87 macro_file: MacroFile, 96 macro_file: MacroFile,
88) -> Option<Parse<SyntaxNode>> { 97) -> Option<(Parse<SyntaxNode>, Arc<mbe::RevTokenMap>)> {
89 let _p = profile("parse_macro_query"); 98 let _p = profile("parse_macro_query");
99
90 let macro_call_id = macro_file.macro_call_id; 100 let macro_call_id = macro_file.macro_call_id;
91 let tt = db 101 let tt = db
92 .macro_expand(macro_call_id) 102 .macro_expand(macro_call_id)
@@ -97,8 +107,13 @@ pub(crate) fn parse_macro(
97 log::warn!("fail on macro_parse: (reason: {})", err,); 107 log::warn!("fail on macro_parse: (reason: {})", err,);
98 }) 108 })
99 .ok()?; 109 .ok()?;
110
100 match macro_file.macro_file_kind { 111 match macro_file.macro_file_kind {
101 MacroFileKind::Items => mbe::token_tree_to_items(&tt).ok().map(Parse::to_syntax), 112 MacroFileKind::Items => {
102 MacroFileKind::Expr => mbe::token_tree_to_expr(&tt).ok().map(Parse::to_syntax), 113 mbe::token_tree_to_items(&tt).ok().map(|(p, map)| (p.to_syntax(), Arc::new(map)))
114 }
115 MacroFileKind::Expr => {
116 mbe::token_tree_to_expr(&tt).ok().map(|(p, map)| (p.to_syntax(), Arc::new(map)))
117 }
103 } 118 }
104} 119}
diff --git a/crates/ra_hir_expand/src/lib.rs b/crates/ra_hir_expand/src/lib.rs
index dd07a16b4..b219b8fbf 100644
--- a/crates/ra_hir_expand/src/lib.rs
+++ b/crates/ra_hir_expand/src/lib.rs
@@ -12,11 +12,12 @@ pub mod hygiene;
12pub mod diagnostics; 12pub mod diagnostics;
13 13
14use std::hash::{Hash, Hasher}; 14use std::hash::{Hash, Hasher};
15use std::sync::Arc;
15 16
16use ra_db::{salsa, CrateId, FileId}; 17use ra_db::{salsa, CrateId, FileId};
17use ra_syntax::{ 18use ra_syntax::{
18 ast::{self, AstNode}, 19 ast::{self, AstNode},
19 SyntaxNode, 20 SyntaxNode, TextRange, TextUnit,
20}; 21};
21 22
22use crate::ast_id_map::FileAstId; 23use crate::ast_id_map::FileAstId;
@@ -66,6 +67,30 @@ impl HirFileId {
66 } 67 }
67 } 68 }
68 } 69 }
70
71 /// Return expansion information if it is a macro-expansion file
72 pub fn expansion_info(self, db: &dyn db::AstDatabase) -> Option<ExpansionInfo> {
73 match self.0 {
74 HirFileIdRepr::FileId(_) => None,
75 HirFileIdRepr::MacroFile(macro_file) => {
76 let loc: MacroCallLoc = db.lookup_intern_macro(macro_file.macro_call_id);
77
78 let arg_start = loc.ast_id.to_node(db).token_tree()?.syntax().text_range().start();
79 let def_start =
80 loc.def.ast_id.to_node(db).token_tree()?.syntax().text_range().start();
81
82 let macro_def = db.macro_def(loc.def)?;
83 let shift = macro_def.0.shift();
84 let exp_map = db.parse_macro(macro_file)?.1;
85 let macro_arg = db.macro_arg(macro_file.macro_call_id)?;
86
87 let arg_start = (loc.ast_id.file_id, arg_start);
88 let def_start = (loc.def.ast_id.file_id, def_start);
89
90 Some(ExpansionInfo { arg_start, def_start, macro_arg, macro_def, exp_map, shift })
91 }
92 }
93 }
69} 94}
70 95
71#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 96#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -112,6 +137,36 @@ impl MacroCallId {
112 } 137 }
113} 138}
114 139
140#[derive(Debug, Clone, PartialEq, Eq)]
141/// ExpansionInfo mainly describes how to map text range between src and expanded macro
142pub struct ExpansionInfo {
143 pub(crate) arg_start: (HirFileId, TextUnit),
144 pub(crate) def_start: (HirFileId, TextUnit),
145 pub(crate) shift: u32,
146
147 pub(crate) macro_def: Arc<(mbe::MacroRules, mbe::TokenMap)>,
148 pub(crate) macro_arg: Arc<(tt::Subtree, mbe::TokenMap)>,
149 pub(crate) exp_map: Arc<mbe::RevTokenMap>,
150}
151
152impl ExpansionInfo {
153 pub fn find_range(&self, from: TextRange) -> Option<(HirFileId, TextRange)> {
154 fn look_in_rev_map(exp_map: &mbe::RevTokenMap, from: TextRange) -> Option<tt::TokenId> {
155 exp_map.ranges.iter().find(|&it| it.0.is_subrange(&from)).map(|it| it.1)
156 }
157
158 let token_id = look_in_rev_map(&self.exp_map, from)?;
159 let (token_map, file_offset, token_id) = if token_id.0 >= self.shift {
160 (&self.macro_arg.1, self.arg_start, tt::TokenId(token_id.0 - self.shift).into())
161 } else {
162 (&self.macro_def.1, self.def_start, token_id)
163 };
164
165 let range = token_map.relative_range_of(token_id)?;
166 Some((file_offset.0, TextRange::offset_len(range.start() + file_offset.1, range.len())))
167 }
168}
169
115/// `AstId` points to an AST node in any file. 170/// `AstId` points to an AST node in any file.
116/// 171///
117/// It is stable across reparses, and can be used as salsa key/value. 172/// It is stable across reparses, and can be used as salsa key/value.
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 {
diff --git a/crates/ra_mbe/src/lib.rs b/crates/ra_mbe/src/lib.rs
index 15f000175..70a289f09 100644
--- a/crates/ra_mbe/src/lib.rs
+++ b/crates/ra_mbe/src/lib.rs
@@ -32,7 +32,7 @@ pub enum ExpandError {
32 32
33pub use crate::syntax_bridge::{ 33pub use crate::syntax_bridge::{
34 ast_to_token_tree, syntax_node_to_token_tree, token_tree_to_expr, token_tree_to_items, 34 ast_to_token_tree, syntax_node_to_token_tree, token_tree_to_expr, token_tree_to_items,
35 token_tree_to_macro_stmts, token_tree_to_pat, token_tree_to_ty, 35 token_tree_to_macro_stmts, token_tree_to_pat, token_tree_to_ty, RevTokenMap, TokenMap,
36}; 36};
37 37
38/// This struct contains AST for a single `macro_rules` definition. What might 38/// This struct contains AST for a single `macro_rules` definition. What might
@@ -118,6 +118,10 @@ impl MacroRules {
118 shift_subtree(&mut tt, self.shift); 118 shift_subtree(&mut tt, self.shift);
119 mbe_expander::expand(self, &tt) 119 mbe_expander::expand(self, &tt)
120 } 120 }
121
122 pub fn shift(&self) -> u32 {
123 self.shift
124 }
121} 125}
122 126
123impl Rule { 127impl Rule {
diff --git a/crates/ra_mbe/src/syntax_bridge.rs b/crates/ra_mbe/src/syntax_bridge.rs
index 592fcf527..9653f7fef 100644
--- a/crates/ra_mbe/src/syntax_bridge.rs
+++ b/crates/ra_mbe/src/syntax_bridge.rs
@@ -14,12 +14,18 @@ use crate::subtree_source::SubtreeTokenSource;
14use crate::ExpandError; 14use crate::ExpandError;
15 15
16/// Maps `tt::TokenId` to the relative range of the original token. 16/// Maps `tt::TokenId` to the relative range of the original token.
17#[derive(Default)] 17#[derive(Debug, PartialEq, Eq, Default)]
18pub struct TokenMap { 18pub struct TokenMap {
19 /// Maps `tt::TokenId` to the *relative* source range. 19 /// Maps `tt::TokenId` to the *relative* source range.
20 tokens: Vec<TextRange>, 20 tokens: Vec<TextRange>,
21} 21}
22 22
23/// Maps relative range of the expanded syntax node to `tt::TokenId`
24#[derive(Debug, PartialEq, Eq, Default)]
25pub struct RevTokenMap {
26 pub ranges: Vec<(TextRange, tt::TokenId)>,
27}
28
23/// Convert the syntax tree (what user has written) to a `TokenTree` (what macro 29/// Convert the syntax tree (what user has written) to a `TokenTree` (what macro
24/// will consume). 30/// will consume).
25pub fn ast_to_token_tree(ast: &ast::TokenTree) -> Option<(tt::Subtree, TokenMap)> { 31pub fn ast_to_token_tree(ast: &ast::TokenTree) -> Option<(tt::Subtree, TokenMap)> {
@@ -52,7 +58,7 @@ pub fn syntax_node_to_token_tree(node: &SyntaxNode) -> Option<(tt::Subtree, Toke
52fn fragment_to_syntax_node( 58fn fragment_to_syntax_node(
53 tt: &tt::Subtree, 59 tt: &tt::Subtree,
54 fragment_kind: FragmentKind, 60 fragment_kind: FragmentKind,
55) -> Result<Parse<SyntaxNode>, ExpandError> { 61) -> Result<(Parse<SyntaxNode>, RevTokenMap), ExpandError> {
56 let tmp; 62 let tmp;
57 let tokens = match tt { 63 let tokens = match tt {
58 tt::Subtree { delimiter: tt::Delimiter::None, token_trees } => token_trees.as_slice(), 64 tt::Subtree { delimiter: tt::Delimiter::None, token_trees } => token_trees.as_slice(),
@@ -69,38 +75,33 @@ fn fragment_to_syntax_node(
69 return Err(ExpandError::ConversionError); 75 return Err(ExpandError::ConversionError);
70 } 76 }
71 //FIXME: would be cool to report errors 77 //FIXME: would be cool to report errors
72 let parse = tree_sink.inner.finish(); 78 let (parse, range_map) = tree_sink.finish();
73 Ok(parse) 79 Ok((parse, range_map))
74} 80}
75 81
76/// Parses the token tree (result of macro expansion) to an expression 82macro_rules! impl_token_tree_conversions {
77pub fn token_tree_to_expr(tt: &tt::Subtree) -> Result<Parse<ast::Expr>, ExpandError> { 83 ($($(#[$attr:meta])* $name:ident => ($kind:ident, $t:ty) ),*) => {
78 let parse = fragment_to_syntax_node(tt, Expr)?; 84 $(
79 parse.cast().ok_or_else(|| crate::ExpandError::ConversionError) 85 $(#[$attr])*
80} 86 pub fn $name(tt: &tt::Subtree) -> Result<(Parse<$t>, RevTokenMap), ExpandError> {
81 87 let (parse, map) = fragment_to_syntax_node(tt, $kind)?;
82/// Parses the token tree (result of macro expansion) to a Pattern 88 parse.cast().ok_or_else(|| crate::ExpandError::ConversionError).map(|p| (p, map))
83pub fn token_tree_to_pat(tt: &tt::Subtree) -> Result<Parse<ast::Pat>, ExpandError> { 89 }
84 let parse = fragment_to_syntax_node(tt, Pattern)?; 90 )*
85 parse.cast().ok_or_else(|| crate::ExpandError::ConversionError) 91 }
86}
87
88/// Parses the token tree (result of macro expansion) to a Type
89pub fn token_tree_to_ty(tt: &tt::Subtree) -> Result<Parse<ast::TypeRef>, ExpandError> {
90 let parse = fragment_to_syntax_node(tt, Type)?;
91 parse.cast().ok_or_else(|| crate::ExpandError::ConversionError)
92}
93
94/// Parses the token tree (result of macro expansion) as a sequence of stmts
95pub fn token_tree_to_macro_stmts(tt: &tt::Subtree) -> Result<Parse<ast::MacroStmts>, ExpandError> {
96 let parse = fragment_to_syntax_node(tt, Statements)?;
97 parse.cast().ok_or_else(|| crate::ExpandError::ConversionError)
98} 92}
99 93
100/// Parses the token tree (result of macro expansion) as a sequence of items 94impl_token_tree_conversions! {
101pub fn token_tree_to_items(tt: &tt::Subtree) -> Result<Parse<ast::MacroItems>, ExpandError> { 95 /// Parses the token tree (result of macro expansion) to an expression
102 let parse = fragment_to_syntax_node(tt, Items)?; 96 token_tree_to_expr => (Expr, ast::Expr),
103 parse.cast().ok_or_else(|| crate::ExpandError::ConversionError) 97 /// Parses the token tree (result of macro expansion) to a Pattern
98 token_tree_to_pat => (Pattern, ast::Pat),
99 /// Parses the token tree (result of macro expansion) to a Type
100 token_tree_to_ty => (Type, ast::TypeRef),
101 /// Parses the token tree (result of macro expansion) as a sequence of stmts
102 token_tree_to_macro_stmts => (Statements, ast::MacroStmts),
103 /// Parses the token tree (result of macro expansion) as a sequence of items
104 token_tree_to_items => (Items, ast::MacroItems)
104} 105}
105 106
106impl TokenMap { 107impl TokenMap {
@@ -116,6 +117,12 @@ impl TokenMap {
116 } 117 }
117} 118}
118 119
120impl RevTokenMap {
121 fn add(&mut self, relative_range: TextRange, token_id: tt::TokenId) {
122 self.ranges.push((relative_range, token_id.clone()))
123 }
124}
125
119/// Returns the textual content of a doc comment block as a quoted string 126/// Returns the textual content of a doc comment block as a quoted string
120/// That is, strips leading `///` (or `/**`, etc) 127/// That is, strips leading `///` (or `/**`, etc)
121/// and strips the ending `*/` 128/// and strips the ending `*/`
@@ -262,6 +269,7 @@ struct TtTreeSink<'a> {
262 cursor: Cursor<'a>, 269 cursor: Cursor<'a>,
263 text_pos: TextUnit, 270 text_pos: TextUnit,
264 inner: SyntaxTreeBuilder, 271 inner: SyntaxTreeBuilder,
272 range_map: RevTokenMap,
265 273
266 // Number of roots 274 // Number of roots
267 // Use for detect ill-form tree which is not single root 275 // Use for detect ill-form tree which is not single root
@@ -276,8 +284,13 @@ impl<'a> TtTreeSink<'a> {
276 text_pos: 0.into(), 284 text_pos: 0.into(),
277 inner: SyntaxTreeBuilder::default(), 285 inner: SyntaxTreeBuilder::default(),
278 roots: smallvec::SmallVec::new(), 286 roots: smallvec::SmallVec::new(),
287 range_map: RevTokenMap::default(),
279 } 288 }
280 } 289 }
290
291 fn finish(self) -> (Parse<SyntaxNode>, RevTokenMap) {
292 (self.inner.finish(), self.range_map)
293 }
281} 294}
282 295
283fn delim_to_str(d: tt::Delimiter, closing: bool) -> SmolStr { 296fn delim_to_str(d: tt::Delimiter, closing: bool) -> SmolStr {
@@ -307,6 +320,15 @@ impl<'a> TreeSink for TtTreeSink<'a> {
307 320
308 match self.cursor.token_tree() { 321 match self.cursor.token_tree() {
309 Some(tt::TokenTree::Leaf(leaf)) => { 322 Some(tt::TokenTree::Leaf(leaf)) => {
323 // Mark the range if needed
324 if let tt::Leaf::Ident(ident) = leaf {
325 if kind == IDENT {
326 let range =
327 TextRange::offset_len(self.text_pos, TextUnit::of_str(&ident.text));
328 self.range_map.add(range, ident.id);
329 }
330 }
331
310 self.cursor = self.cursor.bump(); 332 self.cursor = self.cursor.bump();
311 self.buf += &format!("{}", leaf); 333 self.buf += &format!("{}", leaf);
312 } 334 }
@@ -337,6 +359,7 @@ impl<'a> TreeSink for TtTreeSink<'a> {
337 { 359 {
338 if curr.spacing == tt::Spacing::Alone { 360 if curr.spacing == tt::Spacing::Alone {
339 self.inner.token(WHITESPACE, " ".into()); 361 self.inner.token(WHITESPACE, " ".into());
362 self.text_pos += TextUnit::of_char(' ');
340 } 363 }
341 } 364 }
342 } 365 }
diff --git a/crates/ra_mbe/src/tests.rs b/crates/ra_mbe/src/tests.rs
index a23e3afe3..a848ea334 100644
--- a/crates/ra_mbe/src/tests.rs
+++ b/crates/ra_mbe/src/tests.rs
@@ -126,7 +126,7 @@ fn test_expr_order() {
126"#, 126"#,
127 ); 127 );
128 let expanded = expand(&rules, "foo! { 1 + 1}"); 128 let expanded = expand(&rules, "foo! { 1 + 1}");
129 let tree = token_tree_to_items(&expanded).unwrap().tree(); 129 let tree = token_tree_to_items(&expanded).unwrap().0.tree();
130 130
131 let dump = format!("{:#?}", tree.syntax()); 131 let dump = format!("{:#?}", tree.syntax());
132 assert_eq_text!( 132 assert_eq_text!(
@@ -383,7 +383,7 @@ fn test_expand_to_item_list() {
383 ", 383 ",
384 ); 384 );
385 let expansion = expand(&rules, "structs!(Foo, Bar);"); 385 let expansion = expand(&rules, "structs!(Foo, Bar);");
386 let tree = token_tree_to_items(&expansion).unwrap().tree(); 386 let tree = token_tree_to_items(&expansion).unwrap().0.tree();
387 assert_eq!( 387 assert_eq!(
388 format!("{:#?}", tree.syntax()).trim(), 388 format!("{:#?}", tree.syntax()).trim(),
389 r#" 389 r#"
@@ -501,7 +501,7 @@ fn test_tt_to_stmts() {
501 ); 501 );
502 502
503 let expanded = expand(&rules, "foo!{}"); 503 let expanded = expand(&rules, "foo!{}");
504 let stmts = token_tree_to_macro_stmts(&expanded).unwrap().tree(); 504 let stmts = token_tree_to_macro_stmts(&expanded).unwrap().0.tree();
505 505
506 assert_eq!( 506 assert_eq!(
507 format!("{:#?}", stmts.syntax()).trim(), 507 format!("{:#?}", stmts.syntax()).trim(),
@@ -946,7 +946,7 @@ fn test_vec() {
946 ); 946 );
947 947
948 let expansion = expand(&rules, r#"vec![1u32,2];"#); 948 let expansion = expand(&rules, r#"vec![1u32,2];"#);
949 let tree = token_tree_to_expr(&expansion).unwrap().tree(); 949 let tree = token_tree_to_expr(&expansion).unwrap().0.tree();
950 950
951 assert_eq!( 951 assert_eq!(
952 format!("{:#?}", tree.syntax()).trim(), 952 format!("{:#?}", tree.syntax()).trim(),
@@ -1436,8 +1436,8 @@ pub(crate) fn assert_expansion(
1436 }; 1436 };
1437 let (expanded_tree, expected_tree) = match kind { 1437 let (expanded_tree, expected_tree) = match kind {
1438 MacroKind::Items => { 1438 MacroKind::Items => {
1439 let expanded_tree = token_tree_to_items(&expanded).unwrap().tree(); 1439 let expanded_tree = token_tree_to_items(&expanded).unwrap().0.tree();
1440 let expected_tree = token_tree_to_items(&expected).unwrap().tree(); 1440 let expected_tree = token_tree_to_items(&expected).unwrap().0.tree();
1441 1441
1442 ( 1442 (
1443 debug_dump_ignore_spaces(expanded_tree.syntax()).trim().to_string(), 1443 debug_dump_ignore_spaces(expanded_tree.syntax()).trim().to_string(),
@@ -1446,8 +1446,8 @@ pub(crate) fn assert_expansion(
1446 } 1446 }
1447 1447
1448 MacroKind::Stmts => { 1448 MacroKind::Stmts => {
1449 let expanded_tree = token_tree_to_macro_stmts(&expanded).unwrap().tree(); 1449 let expanded_tree = token_tree_to_macro_stmts(&expanded).unwrap().0.tree();
1450 let expected_tree = token_tree_to_macro_stmts(&expected).unwrap().tree(); 1450 let expected_tree = token_tree_to_macro_stmts(&expected).unwrap().0.tree();
1451 1451
1452 ( 1452 (
1453 debug_dump_ignore_spaces(expanded_tree.syntax()).trim().to_string(), 1453 debug_dump_ignore_spaces(expanded_tree.syntax()).trim().to_string(),