diff options
-rw-r--r-- | crates/ra_hir/src/nameres.rs | 5 | ||||
-rw-r--r-- | crates/ra_hir/src/nameres/collector.rs | 84 | ||||
-rw-r--r-- | crates/ra_hir/src/nameres/raw.rs | 12 | ||||
-rw-r--r-- | crates/ra_hir/src/nameres/tests/macros.rs | 91 |
4 files changed, 158 insertions, 34 deletions
diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs index 7de422128..e6bf0e90c 100644 --- a/crates/ra_hir/src/nameres.rs +++ b/crates/ra_hir/src/nameres.rs | |||
@@ -139,6 +139,7 @@ pub(crate) struct ModuleData { | |||
139 | pub struct ModuleScope { | 139 | pub struct ModuleScope { |
140 | items: FxHashMap<Name, Resolution>, | 140 | items: FxHashMap<Name, Resolution>, |
141 | macros: FxHashMap<Name, MacroDef>, | 141 | macros: FxHashMap<Name, MacroDef>, |
142 | textual_macros: FxHashMap<Name, MacroDef>, | ||
142 | } | 143 | } |
143 | 144 | ||
144 | static BUILTIN_SCOPE: Lazy<FxHashMap<Name, Resolution>> = Lazy::new(|| { | 145 | static BUILTIN_SCOPE: Lazy<FxHashMap<Name, Resolution>> = Lazy::new(|| { |
@@ -164,6 +165,7 @@ impl ModuleScope { | |||
164 | _ => None, | 165 | _ => None, |
165 | }) | 166 | }) |
166 | } | 167 | } |
168 | /// It resolves in module scope. Textual scoped macros are ignored here. | ||
167 | fn get_item_or_macro(&self, name: &Name) -> Option<ItemOrMacro> { | 169 | fn get_item_or_macro(&self, name: &Name) -> Option<ItemOrMacro> { |
168 | match (self.get(name), self.macros.get(name)) { | 170 | match (self.get(name), self.macros.get(name)) { |
169 | (Some(item), _) if !item.def.is_none() => Some(Either::A(item.def)), | 171 | (Some(item), _) if !item.def.is_none() => Some(Either::A(item.def)), |
@@ -171,6 +173,9 @@ impl ModuleScope { | |||
171 | _ => None, | 173 | _ => None, |
172 | } | 174 | } |
173 | } | 175 | } |
176 | fn get_textual_macro(&self, name: &Name) -> Option<MacroDef> { | ||
177 | self.textual_macros.get(name).copied() | ||
178 | } | ||
174 | } | 179 | } |
175 | 180 | ||
176 | type ItemOrMacro = Either<PerNs<ModuleDef>, MacroDef>; | 181 | type ItemOrMacro = Either<PerNs<ModuleDef>, MacroDef>; |
diff --git a/crates/ra_hir/src/nameres/collector.rs b/crates/ra_hir/src/nameres/collector.rs index 1c0d4369e..f897547e3 100644 --- a/crates/ra_hir/src/nameres/collector.rs +++ b/crates/ra_hir/src/nameres/collector.rs | |||
@@ -40,7 +40,6 @@ pub(super) fn collect_defs(db: &impl DefDatabase, mut def_map: CrateDefMap) -> C | |||
40 | glob_imports: FxHashMap::default(), | 40 | glob_imports: FxHashMap::default(), |
41 | unresolved_imports: Vec::new(), | 41 | unresolved_imports: Vec::new(), |
42 | unexpanded_macros: Vec::new(), | 42 | unexpanded_macros: Vec::new(), |
43 | global_macro_scope: FxHashMap::default(), | ||
44 | macro_stack_monitor: MacroStackMonitor::default(), | 43 | macro_stack_monitor: MacroStackMonitor::default(), |
45 | }; | 44 | }; |
46 | collector.collect(); | 45 | collector.collect(); |
@@ -82,7 +81,6 @@ struct DefCollector<DB> { | |||
82 | glob_imports: FxHashMap<CrateModuleId, Vec<(CrateModuleId, raw::ImportId)>>, | 81 | glob_imports: FxHashMap<CrateModuleId, Vec<(CrateModuleId, raw::ImportId)>>, |
83 | unresolved_imports: Vec<(CrateModuleId, raw::ImportId, raw::ImportData)>, | 82 | unresolved_imports: Vec<(CrateModuleId, raw::ImportId, raw::ImportData)>, |
84 | unexpanded_macros: Vec<(CrateModuleId, AstId<ast::MacroCall>, Path)>, | 83 | unexpanded_macros: Vec<(CrateModuleId, AstId<ast::MacroCall>, Path)>, |
85 | global_macro_scope: FxHashMap<Name, MacroDefId>, | ||
86 | 84 | ||
87 | /// Some macro use `$tt:tt which mean we have to handle the macro perfectly | 85 | /// Some macro use `$tt:tt which mean we have to handle the macro perfectly |
88 | /// To prevent stack overflow, we add a deep counter here for prevent that. | 86 | /// To prevent stack overflow, we add a deep counter here for prevent that. |
@@ -136,20 +134,6 @@ where | |||
136 | macro_id: MacroDefId, | 134 | macro_id: MacroDefId, |
137 | export: bool, | 135 | export: bool, |
138 | ) { | 136 | ) { |
139 | // macro-by-example in Rust have completely weird name resolution logic, | ||
140 | // unlike anything else in the language. We'd don't fully implement yet, | ||
141 | // just give a somewhat precise approximation. | ||
142 | // | ||
143 | // Specifically, we store a set of visible macros in each module, just | ||
144 | // like how we do with usual items. This is wrong, however, because | ||
145 | // macros can be shadowed and their scopes are mostly unrelated to | ||
146 | // modules. To paper over the second problem, we also maintain | ||
147 | // `global_macro_scope` which works when we construct `CrateDefMap`, but | ||
148 | // is completely ignored in expressions. | ||
149 | // | ||
150 | // What we should do is that, in CrateDefMap, we should maintain a | ||
151 | // separate tower of macro scopes, with ids. Then, for each item in the | ||
152 | // module, we need to store it's macro scope. | ||
153 | let def = Either::B(MacroDef { id: macro_id }); | 137 | let def = Either::B(MacroDef { id: macro_id }); |
154 | 138 | ||
155 | // In Rust, `#[macro_export]` macros are unconditionally visible at the | 139 | // In Rust, `#[macro_export]` macros are unconditionally visible at the |
@@ -162,13 +146,35 @@ where | |||
162 | self.def_map.exported_macros.insert(name.clone(), macro_id); | 146 | self.def_map.exported_macros.insert(name.clone(), macro_id); |
163 | } | 147 | } |
164 | self.update(module_id, None, &[(name.clone(), def)]); | 148 | self.update(module_id, None, &[(name.clone(), def)]); |
165 | self.global_macro_scope.insert(name, macro_id); | 149 | self.define_textual_macro(module_id, name.clone(), macro_id); |
150 | } | ||
151 | |||
152 | /// Define a macro in current textual scope. | ||
153 | /// | ||
154 | /// We use a map `textual_macros` to store all textual macros visable per module. | ||
155 | /// It will clone all macros from parent textual scope, whose definition is prior to | ||
156 | /// the definition of current module. | ||
157 | /// And also, `macro_use` on a module will import all textual macros visable inside to | ||
158 | /// current textual scope, with possible shadowing. | ||
159 | /// | ||
160 | /// In a single module, the order of definition/usage of textual scoped macros matters. | ||
161 | /// But we ignore it here to make it easy to implement. | ||
162 | fn define_textual_macro(&mut self, module_id: CrateModuleId, name: Name, macro_id: MacroDefId) { | ||
163 | // Always shadowing | ||
164 | self.def_map.modules[module_id] | ||
165 | .scope | ||
166 | .textual_macros | ||
167 | .insert(name, MacroDef { id: macro_id }); | ||
166 | } | 168 | } |
167 | 169 | ||
168 | /// Import macros from `#[macro_use] extern crate`. | 170 | /// Import macros from `#[macro_use] extern crate`. |
169 | /// | 171 | /// |
170 | /// They are non-scoped, and will only be inserted into mutable `global_macro_scope`. | 172 | /// They are non-scoped, and will only be inserted into mutable `global_macro_scope`. |
171 | fn import_macros_from_extern_crate(&mut self, import: &raw::ImportData) { | 173 | fn import_macros_from_extern_crate( |
174 | &mut self, | ||
175 | current_module_id: CrateModuleId, | ||
176 | import: &raw::ImportData, | ||
177 | ) { | ||
172 | log::debug!( | 178 | log::debug!( |
173 | "importing macros from extern crate: {:?} ({:?})", | 179 | "importing macros from extern crate: {:?} ({:?})", |
174 | import, | 180 | import, |
@@ -184,14 +190,14 @@ where | |||
184 | 190 | ||
185 | if let Some(ModuleDef::Module(m)) = res.take_types() { | 191 | if let Some(ModuleDef::Module(m)) = res.take_types() { |
186 | tested_by!(macro_rules_from_other_crates_are_visible_with_macro_use); | 192 | tested_by!(macro_rules_from_other_crates_are_visible_with_macro_use); |
187 | self.import_all_macros_exported(m); | 193 | self.import_all_macros_exported(current_module_id, m); |
188 | } | 194 | } |
189 | } | 195 | } |
190 | 196 | ||
191 | fn import_all_macros_exported(&mut self, module: Module) { | 197 | fn import_all_macros_exported(&mut self, current_module_id: CrateModuleId, module: Module) { |
192 | let item_map = self.db.crate_def_map(module.krate); | 198 | let item_map = self.db.crate_def_map(module.krate); |
193 | for (name, ¯o_id) in &item_map.exported_macros { | 199 | for (name, ¯o_id) in &item_map.exported_macros { |
194 | self.global_macro_scope.insert(name.clone(), macro_id); | 200 | self.define_textual_macro(current_module_id, name.clone(), macro_id); |
195 | } | 201 | } |
196 | } | 202 | } |
197 | 203 | ||
@@ -528,7 +534,7 @@ where | |||
528 | if let Some(prelude_module) = self.def_collector.def_map.prelude { | 534 | if let Some(prelude_module) = self.def_collector.def_map.prelude { |
529 | if prelude_module.krate != self.def_collector.def_map.krate { | 535 | if prelude_module.krate != self.def_collector.def_map.krate { |
530 | tested_by!(prelude_is_macro_use); | 536 | tested_by!(prelude_is_macro_use); |
531 | self.def_collector.import_all_macros_exported(prelude_module); | 537 | self.def_collector.import_all_macros_exported(self.module_id, prelude_module); |
532 | } | 538 | } |
533 | } | 539 | } |
534 | 540 | ||
@@ -539,7 +545,7 @@ where | |||
539 | if let raw::RawItem::Import(import_id) = *item { | 545 | if let raw::RawItem::Import(import_id) = *item { |
540 | let import = self.raw_items[import_id].clone(); | 546 | let import = self.raw_items[import_id].clone(); |
541 | if import.is_extern_crate && import.is_macro_use { | 547 | if import.is_extern_crate && import.is_macro_use { |
542 | self.def_collector.import_macros_from_extern_crate(&import); | 548 | self.def_collector.import_macros_from_extern_crate(self.module_id, &import); |
543 | } | 549 | } |
544 | } | 550 | } |
545 | } | 551 | } |
@@ -561,10 +567,11 @@ where | |||
561 | fn collect_module(&mut self, module: &raw::ModuleData) { | 567 | fn collect_module(&mut self, module: &raw::ModuleData) { |
562 | match module { | 568 | match module { |
563 | // inline module, just recurse | 569 | // inline module, just recurse |
564 | raw::ModuleData::Definition { name, items, ast_id, attr_path } => { | 570 | raw::ModuleData::Definition { name, items, ast_id, attr_path, is_macro_use } => { |
565 | let module_id = | 571 | let module_id = |
566 | self.push_child_module(name.clone(), ast_id.with_file_id(self.file_id), None); | 572 | self.push_child_module(name.clone(), ast_id.with_file_id(self.file_id), None); |
567 | let parent_module = ParentModule { name, attr_path: attr_path.as_ref() }; | 573 | let parent_module = ParentModule { name, attr_path: attr_path.as_ref() }; |
574 | |||
568 | ModCollector { | 575 | ModCollector { |
569 | def_collector: &mut *self.def_collector, | 576 | def_collector: &mut *self.def_collector, |
570 | module_id, | 577 | module_id, |
@@ -573,9 +580,12 @@ where | |||
573 | parent_module: Some(parent_module), | 580 | parent_module: Some(parent_module), |
574 | } | 581 | } |
575 | .collect(&*items); | 582 | .collect(&*items); |
583 | if *is_macro_use { | ||
584 | self.import_all_textual_macros(module_id); | ||
585 | } | ||
576 | } | 586 | } |
577 | // out of line module, resolve, parse and recurse | 587 | // out of line module, resolve, parse and recurse |
578 | raw::ModuleData::Declaration { name, ast_id, attr_path } => { | 588 | raw::ModuleData::Declaration { name, ast_id, attr_path, is_macro_use } => { |
579 | let ast_id = ast_id.with_file_id(self.file_id); | 589 | let ast_id = ast_id.with_file_id(self.file_id); |
580 | let is_root = self.def_collector.def_map.modules[self.module_id].parent.is_none(); | 590 | let is_root = self.def_collector.def_map.modules[self.module_id].parent.is_none(); |
581 | match resolve_submodule( | 591 | match resolve_submodule( |
@@ -596,7 +606,10 @@ where | |||
596 | raw_items: &raw_items, | 606 | raw_items: &raw_items, |
597 | parent_module: None, | 607 | parent_module: None, |
598 | } | 608 | } |
599 | .collect(raw_items.items()) | 609 | .collect(raw_items.items()); |
610 | if *is_macro_use { | ||
611 | self.import_all_textual_macros(module_id); | ||
612 | } | ||
600 | } | 613 | } |
601 | Err(candidate) => self.def_collector.def_map.diagnostics.push( | 614 | Err(candidate) => self.def_collector.def_map.diagnostics.push( |
602 | DefDiagnostic::UnresolvedModule { | 615 | DefDiagnostic::UnresolvedModule { |
@@ -621,6 +634,7 @@ where | |||
621 | modules[res].parent = Some(self.module_id); | 634 | modules[res].parent = Some(self.module_id); |
622 | modules[res].declaration = Some(declaration); | 635 | modules[res].declaration = Some(declaration); |
623 | modules[res].definition = definition; | 636 | modules[res].definition = definition; |
637 | modules[res].scope.textual_macros = modules[self.module_id].scope.textual_macros.clone(); | ||
624 | modules[self.module_id].children.insert(name.clone(), res); | 638 | modules[self.module_id].children.insert(name.clone(), res); |
625 | let resolution = Resolution { | 639 | let resolution = Resolution { |
626 | def: PerNs::types( | 640 | def: PerNs::types( |
@@ -674,12 +688,12 @@ where | |||
674 | 688 | ||
675 | let ast_id = mac.ast_id.with_file_id(self.file_id); | 689 | let ast_id = mac.ast_id.with_file_id(self.file_id); |
676 | 690 | ||
677 | // Case 2: try to expand macro_rules from this crate, triggering | 691 | // Case 2: try to resolve in textual scope and expand macro_rules, triggering |
678 | // recursive item collection. | 692 | // recursive item collection. |
679 | if let Some(macro_id) = | 693 | if let Some(macro_def) = mac.path.as_ident().and_then(|name| { |
680 | mac.path.as_ident().and_then(|name| self.def_collector.global_macro_scope.get(&name)) | 694 | self.def_collector.def_map[self.module_id].scope.get_textual_macro(&name) |
681 | { | 695 | }) { |
682 | let def = *macro_id; | 696 | let def = macro_def.id; |
683 | let macro_call_id = MacroCallLoc { def, ast_id }.id(self.def_collector.db); | 697 | let macro_call_id = MacroCallLoc { def, ast_id }.id(self.def_collector.db); |
684 | 698 | ||
685 | self.def_collector.collect_macro_expansion(self.module_id, macro_call_id, def); | 699 | self.def_collector.collect_macro_expansion(self.module_id, macro_call_id, def); |
@@ -689,6 +703,13 @@ where | |||
689 | // Case 3: path to a macro from another crate, expand during name resolution | 703 | // Case 3: path to a macro from another crate, expand during name resolution |
690 | self.def_collector.unexpanded_macros.push((self.module_id, ast_id, mac.path.clone())) | 704 | self.def_collector.unexpanded_macros.push((self.module_id, ast_id, mac.path.clone())) |
691 | } | 705 | } |
706 | |||
707 | fn import_all_textual_macros(&mut self, module_id: CrateModuleId) { | ||
708 | let macros = self.def_collector.def_map[module_id].scope.textual_macros.clone(); | ||
709 | for (name, macro_) in macros { | ||
710 | self.def_collector.define_textual_macro(self.module_id, name.clone(), macro_.id); | ||
711 | } | ||
712 | } | ||
692 | } | 713 | } |
693 | 714 | ||
694 | fn is_macro_rules(path: &Path) -> bool { | 715 | fn is_macro_rules(path: &Path) -> bool { |
@@ -715,7 +736,6 @@ mod tests { | |||
715 | glob_imports: FxHashMap::default(), | 736 | glob_imports: FxHashMap::default(), |
716 | unresolved_imports: Vec::new(), | 737 | unresolved_imports: Vec::new(), |
717 | unexpanded_macros: Vec::new(), | 738 | unexpanded_macros: Vec::new(), |
718 | global_macro_scope: FxHashMap::default(), | ||
719 | macro_stack_monitor: monitor, | 739 | macro_stack_monitor: monitor, |
720 | }; | 740 | }; |
721 | collector.collect(); | 741 | collector.collect(); |
diff --git a/crates/ra_hir/src/nameres/raw.rs b/crates/ra_hir/src/nameres/raw.rs index 7963736e0..c646d3d00 100644 --- a/crates/ra_hir/src/nameres/raw.rs +++ b/crates/ra_hir/src/nameres/raw.rs | |||
@@ -134,12 +134,14 @@ pub(super) enum ModuleData { | |||
134 | name: Name, | 134 | name: Name, |
135 | ast_id: FileAstId<ast::Module>, | 135 | ast_id: FileAstId<ast::Module>, |
136 | attr_path: Option<SmolStr>, | 136 | attr_path: Option<SmolStr>, |
137 | is_macro_use: bool, | ||
137 | }, | 138 | }, |
138 | Definition { | 139 | Definition { |
139 | name: Name, | 140 | name: Name, |
140 | ast_id: FileAstId<ast::Module>, | 141 | ast_id: FileAstId<ast::Module>, |
141 | items: Vec<RawItem>, | 142 | items: Vec<RawItem>, |
142 | attr_path: Option<SmolStr>, | 143 | attr_path: Option<SmolStr>, |
144 | is_macro_use: bool, | ||
143 | }, | 145 | }, |
144 | } | 146 | } |
145 | 147 | ||
@@ -267,10 +269,15 @@ impl RawItemsCollector { | |||
267 | }; | 269 | }; |
268 | 270 | ||
269 | let ast_id = self.source_ast_id_map.ast_id(&module); | 271 | let ast_id = self.source_ast_id_map.ast_id(&module); |
272 | let is_macro_use = module.has_atom_attr("macro_use"); | ||
270 | if module.has_semi() { | 273 | if module.has_semi() { |
271 | let attr_path = extract_mod_path_attribute(&module); | 274 | let attr_path = extract_mod_path_attribute(&module); |
272 | let item = | 275 | let item = self.raw_items.modules.alloc(ModuleData::Declaration { |
273 | self.raw_items.modules.alloc(ModuleData::Declaration { name, ast_id, attr_path }); | 276 | name, |
277 | ast_id, | ||
278 | attr_path, | ||
279 | is_macro_use, | ||
280 | }); | ||
274 | self.push_item(current_module, RawItem::Module(item)); | 281 | self.push_item(current_module, RawItem::Module(item)); |
275 | return; | 282 | return; |
276 | } | 283 | } |
@@ -282,6 +289,7 @@ impl RawItemsCollector { | |||
282 | ast_id, | 289 | ast_id, |
283 | items: Vec::new(), | 290 | items: Vec::new(), |
284 | attr_path, | 291 | attr_path, |
292 | is_macro_use, | ||
285 | }); | 293 | }); |
286 | self.process_module(Some(item), item_list); | 294 | self.process_module(Some(item), item_list); |
287 | self.push_item(current_module, RawItem::Module(item)); | 295 | self.push_item(current_module, RawItem::Module(item)); |
diff --git a/crates/ra_hir/src/nameres/tests/macros.rs b/crates/ra_hir/src/nameres/tests/macros.rs index c62152d26..8f0db95f2 100644 --- a/crates/ra_hir/src/nameres/tests/macros.rs +++ b/crates/ra_hir/src/nameres/tests/macros.rs | |||
@@ -277,3 +277,94 @@ fn prelude_cycle() { | |||
277 | ⋮crate::foo | 277 | ⋮crate::foo |
278 | "###); | 278 | "###); |
279 | } | 279 | } |
280 | |||
281 | #[test] | ||
282 | fn plain_macros_are_textual_scoped_between_modules() { | ||
283 | let map = def_map( | ||
284 | r#" | ||
285 | //- /main.rs | ||
286 | mod m1; | ||
287 | bar!(NotFoundNotMacroUse); | ||
288 | |||
289 | mod m2 { | ||
290 | foo!(NotFoundBeforeInside2); | ||
291 | } | ||
292 | |||
293 | macro_rules! foo { | ||
294 | ($x:ident) => { struct $x; } | ||
295 | } | ||
296 | foo!(Ok); | ||
297 | |||
298 | mod m3; | ||
299 | foo!(OkShadowStop); | ||
300 | bar!(NotFoundMacroUseStop); | ||
301 | |||
302 | #[macro_use] | ||
303 | mod m5 { | ||
304 | #[macro_use] | ||
305 | mod m6 { | ||
306 | macro_rules! foo { | ||
307 | ($x:ident) => { fn $x() {} } | ||
308 | } | ||
309 | } | ||
310 | } | ||
311 | foo!(ok_double_macro_use_shadow); | ||
312 | |||
313 | //- /m1.rs | ||
314 | foo!(NotFoundBeforeInside1); | ||
315 | macro_rules! bar { | ||
316 | ($x:ident) => { struct $x; } | ||
317 | } | ||
318 | |||
319 | //- /m3/mod.rs | ||
320 | foo!(OkAfterInside); | ||
321 | macro_rules! foo { | ||
322 | ($x:ident) => { fn $x() {} } | ||
323 | } | ||
324 | foo!(ok_shadow); | ||
325 | |||
326 | #[macro_use] | ||
327 | mod m4; | ||
328 | bar!(OkMacroUse); | ||
329 | |||
330 | //- /m3/m4.rs | ||
331 | foo!(ok_shadow_deep); | ||
332 | macro_rules! bar { | ||
333 | ($x:ident) => { struct $x; } | ||
334 | } | ||
335 | "#, | ||
336 | ); | ||
337 | assert_snapshot!(map, @r###" | ||
338 | ⋮crate | ||
339 | ⋮Ok: t v | ||
340 | ⋮OkShadowStop: t v | ||
341 | ⋮foo: m | ||
342 | ⋮m1: t | ||
343 | ⋮m2: t | ||
344 | ⋮m3: t | ||
345 | ⋮m5: t | ||
346 | ⋮ok_double_macro_use_shadow: v | ||
347 | ⋮ | ||
348 | ⋮crate::m1 | ||
349 | ⋮bar: m | ||
350 | ⋮ | ||
351 | ⋮crate::m5 | ||
352 | ⋮m6: t | ||
353 | ⋮ | ||
354 | ⋮crate::m5::m6 | ||
355 | ⋮foo: m | ||
356 | ⋮ | ||
357 | ⋮crate::m2 | ||
358 | ⋮ | ||
359 | ⋮crate::m3 | ||
360 | ⋮OkAfterInside: t v | ||
361 | ⋮OkMacroUse: t v | ||
362 | ⋮foo: m | ||
363 | ⋮m4: t | ||
364 | ⋮ok_shadow: v | ||
365 | ⋮ | ||
366 | ⋮crate::m3::m4 | ||
367 | ⋮bar: m | ||
368 | ⋮ok_shadow_deep: v | ||
369 | "###); | ||
370 | } | ||