diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2019-09-09 09:04:00 +0100 |
---|---|---|
committer | GitHub <[email protected]> | 2019-09-09 09:04:00 +0100 |
commit | 1db08a54c20c244718e2378272c72ae5c6651f06 (patch) | |
tree | 90fe99eb39d045d9bac34cc200a74d0410d51763 /crates/ra_hir/src/nameres | |
parent | 72259b67bf2393bec1faf6a9f95a575d6fe9cfea (diff) | |
parent | 9ed21d65fb6727f6de4961dfa440981864451af6 (diff) |
Merge #1784
1784: Support textual scoped macros r=matklad a=uHOOCCOOHu
Refactor the old simulation with `global_macro_scope`.
Now it is quite accurate to resolve textual scoped macros.
- Expand textual scoped macros in item and non-item place.
- Support `#[macro_use]` on `mod`.
- Textual scoped macros are collected into `nameres::ModuleScope`, so I think it makes #1727 easier to fix.
- It is implemented in a simple way to `clone()` current scoped macro ids into sub-modules. Though only indices are cloned, it will still increase some resolving time. Well, I've not bench-marked yet.
In my test with vscode extension, it can now successfully expand `dbg!` from `std` without `std::` prefix. "Goto definition" also works. Screenshot here:
<img width="281" alt="Screenshot_20190907_043442" src="https://user-images.githubusercontent.com/14816024/64458794-ddb47900-d128-11e9-95e3-1c8569978825.png">
Co-authored-by: uHOOCCOOHu <[email protected]>
Diffstat (limited to 'crates/ra_hir/src/nameres')
-rw-r--r-- | crates/ra_hir/src/nameres/collector.rs | 91 | ||||
-rw-r--r-- | crates/ra_hir/src/nameres/raw.rs | 12 | ||||
-rw-r--r-- | crates/ra_hir/src/nameres/tests/macros.rs | 108 |
3 files changed, 170 insertions, 41 deletions
diff --git a/crates/ra_hir/src/nameres/collector.rs b/crates/ra_hir/src/nameres/collector.rs index 1c0d4369e..09cda7656 100644 --- a/crates/ra_hir/src/nameres/collector.rs +++ b/crates/ra_hir/src/nameres/collector.rs | |||
@@ -14,8 +14,8 @@ use crate::{ | |||
14 | raw, CrateDefMap, CrateModuleId, ItemOrMacro, ModuleData, ModuleDef, PerNs, | 14 | raw, CrateDefMap, CrateModuleId, ItemOrMacro, ModuleData, ModuleDef, PerNs, |
15 | ReachedFixedPoint, Resolution, ResolveMode, | 15 | ReachedFixedPoint, Resolution, ResolveMode, |
16 | }, | 16 | }, |
17 | AstId, Const, Enum, Function, HirFileId, MacroDef, Module, Name, Path, Static, Struct, Trait, | 17 | AstId, Const, Enum, Function, HirFileId, MacroDef, Module, Name, Path, PathKind, Static, |
18 | TypeAlias, Union, | 18 | Struct, Trait, TypeAlias, Union, |
19 | }; | 19 | }; |
20 | 20 | ||
21 | pub(super) fn collect_defs(db: &impl DefDatabase, mut def_map: CrateDefMap) -> CrateDefMap { | 21 | pub(super) fn collect_defs(db: &impl DefDatabase, mut def_map: CrateDefMap) -> CrateDefMap { |
@@ -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,29 @@ 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_legacy_macro(module_id, name.clone(), macro_id); |
150 | } | ||
151 | |||
152 | /// Define a legacy textual scoped macro in module | ||
153 | /// | ||
154 | /// We use a map `legacy_macros` to store all legacy textual scoped macros visable per module. | ||
155 | /// It will clone all macros from parent legacy scope, whose definition is prior to | ||
156 | /// the definition of current module. | ||
157 | /// And also, `macro_use` on a module will import all legacy macros visable inside to | ||
158 | /// current legacy scope, with possible shadowing. | ||
159 | fn define_legacy_macro(&mut self, module_id: CrateModuleId, name: Name, macro_id: MacroDefId) { | ||
160 | // Always shadowing | ||
161 | self.def_map.modules[module_id].scope.legacy_macros.insert(name, MacroDef { id: macro_id }); | ||
166 | } | 162 | } |
167 | 163 | ||
168 | /// Import macros from `#[macro_use] extern crate`. | 164 | /// Import macros from `#[macro_use] extern crate`. |
169 | /// | 165 | /// |
170 | /// They are non-scoped, and will only be inserted into mutable `global_macro_scope`. | 166 | /// 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) { | 167 | fn import_macros_from_extern_crate( |
168 | &mut self, | ||
169 | current_module_id: CrateModuleId, | ||
170 | import: &raw::ImportData, | ||
171 | ) { | ||
172 | log::debug!( | 172 | log::debug!( |
173 | "importing macros from extern crate: {:?} ({:?})", | 173 | "importing macros from extern crate: {:?} ({:?})", |
174 | import, | 174 | import, |
@@ -184,14 +184,14 @@ where | |||
184 | 184 | ||
185 | if let Some(ModuleDef::Module(m)) = res.take_types() { | 185 | if let Some(ModuleDef::Module(m)) = res.take_types() { |
186 | tested_by!(macro_rules_from_other_crates_are_visible_with_macro_use); | 186 | tested_by!(macro_rules_from_other_crates_are_visible_with_macro_use); |
187 | self.import_all_macros_exported(m); | 187 | self.import_all_macros_exported(current_module_id, m); |
188 | } | 188 | } |
189 | } | 189 | } |
190 | 190 | ||
191 | fn import_all_macros_exported(&mut self, module: Module) { | 191 | fn import_all_macros_exported(&mut self, current_module_id: CrateModuleId, module: Module) { |
192 | let item_map = self.db.crate_def_map(module.krate); | 192 | let item_map = self.db.crate_def_map(module.krate); |
193 | for (name, ¯o_id) in &item_map.exported_macros { | 193 | for (name, ¯o_id) in &item_map.exported_macros { |
194 | self.global_macro_scope.insert(name.clone(), macro_id); | 194 | self.define_legacy_macro(current_module_id, name.clone(), macro_id); |
195 | } | 195 | } |
196 | } | 196 | } |
197 | 197 | ||
@@ -528,7 +528,7 @@ where | |||
528 | if let Some(prelude_module) = self.def_collector.def_map.prelude { | 528 | if let Some(prelude_module) = self.def_collector.def_map.prelude { |
529 | if prelude_module.krate != self.def_collector.def_map.krate { | 529 | if prelude_module.krate != self.def_collector.def_map.krate { |
530 | tested_by!(prelude_is_macro_use); | 530 | tested_by!(prelude_is_macro_use); |
531 | self.def_collector.import_all_macros_exported(prelude_module); | 531 | self.def_collector.import_all_macros_exported(self.module_id, prelude_module); |
532 | } | 532 | } |
533 | } | 533 | } |
534 | 534 | ||
@@ -539,7 +539,7 @@ where | |||
539 | if let raw::RawItem::Import(import_id) = *item { | 539 | if let raw::RawItem::Import(import_id) = *item { |
540 | let import = self.raw_items[import_id].clone(); | 540 | let import = self.raw_items[import_id].clone(); |
541 | if import.is_extern_crate && import.is_macro_use { | 541 | if import.is_extern_crate && import.is_macro_use { |
542 | self.def_collector.import_macros_from_extern_crate(&import); | 542 | self.def_collector.import_macros_from_extern_crate(self.module_id, &import); |
543 | } | 543 | } |
544 | } | 544 | } |
545 | } | 545 | } |
@@ -561,10 +561,11 @@ where | |||
561 | fn collect_module(&mut self, module: &raw::ModuleData) { | 561 | fn collect_module(&mut self, module: &raw::ModuleData) { |
562 | match module { | 562 | match module { |
563 | // inline module, just recurse | 563 | // inline module, just recurse |
564 | raw::ModuleData::Definition { name, items, ast_id, attr_path } => { | 564 | raw::ModuleData::Definition { name, items, ast_id, attr_path, is_macro_use } => { |
565 | let module_id = | 565 | let module_id = |
566 | self.push_child_module(name.clone(), ast_id.with_file_id(self.file_id), None); | 566 | 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() }; | 567 | let parent_module = ParentModule { name, attr_path: attr_path.as_ref() }; |
568 | |||
568 | ModCollector { | 569 | ModCollector { |
569 | def_collector: &mut *self.def_collector, | 570 | def_collector: &mut *self.def_collector, |
570 | module_id, | 571 | module_id, |
@@ -573,9 +574,12 @@ where | |||
573 | parent_module: Some(parent_module), | 574 | parent_module: Some(parent_module), |
574 | } | 575 | } |
575 | .collect(&*items); | 576 | .collect(&*items); |
577 | if *is_macro_use { | ||
578 | self.import_all_legacy_macros(module_id); | ||
579 | } | ||
576 | } | 580 | } |
577 | // out of line module, resolve, parse and recurse | 581 | // out of line module, resolve, parse and recurse |
578 | raw::ModuleData::Declaration { name, ast_id, attr_path } => { | 582 | raw::ModuleData::Declaration { name, ast_id, attr_path, is_macro_use } => { |
579 | let ast_id = ast_id.with_file_id(self.file_id); | 583 | 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(); | 584 | let is_root = self.def_collector.def_map.modules[self.module_id].parent.is_none(); |
581 | match resolve_submodule( | 585 | match resolve_submodule( |
@@ -596,7 +600,10 @@ where | |||
596 | raw_items: &raw_items, | 600 | raw_items: &raw_items, |
597 | parent_module: None, | 601 | parent_module: None, |
598 | } | 602 | } |
599 | .collect(raw_items.items()) | 603 | .collect(raw_items.items()); |
604 | if *is_macro_use { | ||
605 | self.import_all_legacy_macros(module_id); | ||
606 | } | ||
600 | } | 607 | } |
601 | Err(candidate) => self.def_collector.def_map.diagnostics.push( | 608 | Err(candidate) => self.def_collector.def_map.diagnostics.push( |
602 | DefDiagnostic::UnresolvedModule { | 609 | DefDiagnostic::UnresolvedModule { |
@@ -621,6 +628,7 @@ where | |||
621 | modules[res].parent = Some(self.module_id); | 628 | modules[res].parent = Some(self.module_id); |
622 | modules[res].declaration = Some(declaration); | 629 | modules[res].declaration = Some(declaration); |
623 | modules[res].definition = definition; | 630 | modules[res].definition = definition; |
631 | modules[res].scope.legacy_macros = modules[self.module_id].scope.legacy_macros.clone(); | ||
624 | modules[self.module_id].children.insert(name.clone(), res); | 632 | modules[self.module_id].children.insert(name.clone(), res); |
625 | let resolution = Resolution { | 633 | let resolution = Resolution { |
626 | def: PerNs::types( | 634 | def: PerNs::types( |
@@ -674,20 +682,32 @@ where | |||
674 | 682 | ||
675 | let ast_id = mac.ast_id.with_file_id(self.file_id); | 683 | let ast_id = mac.ast_id.with_file_id(self.file_id); |
676 | 684 | ||
677 | // Case 2: try to expand macro_rules from this crate, triggering | 685 | // Case 2: try to resolve in legacy scope and expand macro_rules, triggering |
678 | // recursive item collection. | 686 | // recursive item collection. |
679 | if let Some(macro_id) = | 687 | 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)) | 688 | self.def_collector.def_map[self.module_id].scope.get_legacy_macro(&name) |
681 | { | 689 | }) { |
682 | let def = *macro_id; | 690 | let def = macro_def.id; |
683 | let macro_call_id = MacroCallLoc { def, ast_id }.id(self.def_collector.db); | 691 | let macro_call_id = MacroCallLoc { def, ast_id }.id(self.def_collector.db); |
684 | 692 | ||
685 | self.def_collector.collect_macro_expansion(self.module_id, macro_call_id, def); | 693 | self.def_collector.collect_macro_expansion(self.module_id, macro_call_id, def); |
686 | return; | 694 | return; |
687 | } | 695 | } |
688 | 696 | ||
689 | // Case 3: path to a macro from another crate, expand during name resolution | 697 | // Case 3: resolve in module scope, expand during name resolution. |
690 | self.def_collector.unexpanded_macros.push((self.module_id, ast_id, mac.path.clone())) | 698 | // We rewrite simple path `macro_name` to `self::macro_name` to force resolve in module scope only. |
699 | let mut path = mac.path.clone(); | ||
700 | if path.is_ident() { | ||
701 | path.kind = PathKind::Self_; | ||
702 | } | ||
703 | self.def_collector.unexpanded_macros.push((self.module_id, ast_id, path)); | ||
704 | } | ||
705 | |||
706 | fn import_all_legacy_macros(&mut self, module_id: CrateModuleId) { | ||
707 | let macros = self.def_collector.def_map[module_id].scope.legacy_macros.clone(); | ||
708 | for (name, macro_) in macros { | ||
709 | self.def_collector.define_legacy_macro(self.module_id, name.clone(), macro_.id); | ||
710 | } | ||
691 | } | 711 | } |
692 | } | 712 | } |
693 | 713 | ||
@@ -715,7 +735,6 @@ mod tests { | |||
715 | glob_imports: FxHashMap::default(), | 735 | glob_imports: FxHashMap::default(), |
716 | unresolved_imports: Vec::new(), | 736 | unresolved_imports: Vec::new(), |
717 | unexpanded_macros: Vec::new(), | 737 | unexpanded_macros: Vec::new(), |
718 | global_macro_scope: FxHashMap::default(), | ||
719 | macro_stack_monitor: monitor, | 738 | macro_stack_monitor: monitor, |
720 | }; | 739 | }; |
721 | collector.collect(); | 740 | 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..ebc4d6890 100644 --- a/crates/ra_hir/src/nameres/tests/macros.rs +++ b/crates/ra_hir/src/nameres/tests/macros.rs | |||
@@ -268,12 +268,114 @@ fn prelude_cycle() { | |||
268 | ); | 268 | ); |
269 | assert_snapshot!(map, @r###" | 269 | assert_snapshot!(map, @r###" |
270 | ⋮crate | 270 | ⋮crate |
271 | ⋮foo: t | ||
272 | ⋮prelude: t | 271 | ⋮prelude: t |
273 | ⋮ | 272 | ⋮ |
274 | ⋮crate::prelude | 273 | ⋮crate::prelude |
275 | ⋮declare_mod: m | 274 | ⋮declare_mod: m |
276 | ⋮ | 275 | "###); |
277 | ⋮crate::foo | 276 | } |
277 | |||
278 | #[test] | ||
279 | fn plain_macros_are_legacy_textual_scoped() { | ||
280 | let map = def_map( | ||
281 | r#" | ||
282 | //- /main.rs | ||
283 | mod m1; | ||
284 | bar!(NotFoundNotMacroUse); | ||
285 | |||
286 | mod m2 { | ||
287 | foo!(NotFoundBeforeInside2); | ||
288 | } | ||
289 | |||
290 | macro_rules! foo { | ||
291 | ($x:ident) => { struct $x; } | ||
292 | } | ||
293 | foo!(Ok); | ||
294 | |||
295 | mod m3; | ||
296 | foo!(OkShadowStop); | ||
297 | bar!(NotFoundMacroUseStop); | ||
298 | |||
299 | #[macro_use] | ||
300 | mod m5 { | ||
301 | #[macro_use] | ||
302 | mod m6 { | ||
303 | macro_rules! foo { | ||
304 | ($x:ident) => { fn $x() {} } | ||
305 | } | ||
306 | } | ||
307 | } | ||
308 | foo!(ok_double_macro_use_shadow); | ||
309 | |||
310 | baz!(NotFoundBefore); | ||
311 | #[macro_use] | ||
312 | mod m7 { | ||
313 | macro_rules! baz { | ||
314 | ($x:ident) => { struct $x; } | ||
315 | } | ||
316 | } | ||
317 | baz!(OkAfter); | ||
318 | |||
319 | //- /m1.rs | ||
320 | foo!(NotFoundBeforeInside1); | ||
321 | macro_rules! bar { | ||
322 | ($x:ident) => { struct $x; } | ||
323 | } | ||
324 | |||
325 | //- /m3/mod.rs | ||
326 | foo!(OkAfterInside); | ||
327 | macro_rules! foo { | ||
328 | ($x:ident) => { fn $x() {} } | ||
329 | } | ||
330 | foo!(ok_shadow); | ||
331 | |||
332 | #[macro_use] | ||
333 | mod m4; | ||
334 | bar!(OkMacroUse); | ||
335 | |||
336 | //- /m3/m4.rs | ||
337 | foo!(ok_shadow_deep); | ||
338 | macro_rules! bar { | ||
339 | ($x:ident) => { struct $x; } | ||
340 | } | ||
341 | "#, | ||
342 | ); | ||
343 | assert_snapshot!(map, @r###" | ||
344 | ⋮crate | ||
345 | ⋮Ok: t v | ||
346 | ⋮OkAfter: t v | ||
347 | ⋮OkShadowStop: t v | ||
348 | ⋮foo: m | ||
349 | ⋮m1: t | ||
350 | ⋮m2: t | ||
351 | ⋮m3: t | ||
352 | ⋮m5: t | ||
353 | ⋮m7: t | ||
354 | ⋮ok_double_macro_use_shadow: v | ||
355 | ⋮ | ||
356 | ⋮crate::m7 | ||
357 | ⋮baz: m | ||
358 | ⋮ | ||
359 | ⋮crate::m1 | ||
360 | ⋮bar: m | ||
361 | ⋮ | ||
362 | ⋮crate::m5 | ||
363 | ⋮m6: t | ||
364 | ⋮ | ||
365 | ⋮crate::m5::m6 | ||
366 | ⋮foo: m | ||
367 | ⋮ | ||
368 | ⋮crate::m2 | ||
369 | ⋮ | ||
370 | ⋮crate::m3 | ||
371 | ⋮OkAfterInside: t v | ||
372 | ⋮OkMacroUse: t v | ||
373 | ⋮foo: m | ||
374 | ⋮m4: t | ||
375 | ⋮ok_shadow: v | ||
376 | ⋮ | ||
377 | ⋮crate::m3::m4 | ||
378 | ⋮bar: m | ||
379 | ⋮ok_shadow_deep: v | ||
278 | "###); | 380 | "###); |
279 | } | 381 | } |