diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2019-10-05 15:25:59 +0100 |
---|---|---|
committer | GitHub <[email protected]> | 2019-10-05 15:25:59 +0100 |
commit | ae6305b90c80eb919cfde985cba66975b6222ed2 (patch) | |
tree | de601daf3714c4bda937e7cad05d048b69d16e71 /crates/ra_hir/src/nameres | |
parent | dbf869b4d2dac09df17609edf6e67c1611b71dc5 (diff) | |
parent | c6303d9fee98232ac83a77f943c39d65c9c6b6db (diff) |
Merge #1928
1928: Support `#[cfg(..)]` r=matklad a=oxalica
This PR implement `#[cfg(..)]` conditional compilation. It read default cfg options from `rustc --print cfg` with also hard-coded `test` and `debug_assertion` enabled.
Front-end settings are **not** included in this PR.
There is also a known issue that inner control attributes are totally ignored. I think it is **not** a part of `cfg` and create a separated issue for it. #1949
Fixes #1920
Related: #1073
Co-authored-by: uHOOCCOOHu <[email protected]>
Co-authored-by: oxalica <[email protected]>
Diffstat (limited to 'crates/ra_hir/src/nameres')
-rw-r--r-- | crates/ra_hir/src/nameres/collector.rs | 53 | ||||
-rw-r--r-- | crates/ra_hir/src/nameres/raw.rs | 54 | ||||
-rw-r--r-- | crates/ra_hir/src/nameres/tests.rs | 70 |
3 files changed, 150 insertions, 27 deletions
diff --git a/crates/ra_hir/src/nameres/collector.rs b/crates/ra_hir/src/nameres/collector.rs index a568fdabd..cef2dc9d2 100644 --- a/crates/ra_hir/src/nameres/collector.rs +++ b/crates/ra_hir/src/nameres/collector.rs | |||
@@ -1,5 +1,6 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use ra_cfg::CfgOptions; | ||
3 | use ra_db::FileId; | 4 | use ra_db::FileId; |
4 | use ra_syntax::{ast, SmolStr}; | 5 | use ra_syntax::{ast, SmolStr}; |
5 | use rustc_hash::FxHashMap; | 6 | use rustc_hash::FxHashMap; |
@@ -35,6 +36,9 @@ pub(super) fn collect_defs(db: &impl DefDatabase, mut def_map: CrateDefMap) -> C | |||
35 | } | 36 | } |
36 | } | 37 | } |
37 | 38 | ||
39 | let crate_graph = db.crate_graph(); | ||
40 | let cfg_options = crate_graph.cfg_options(def_map.krate().crate_id()); | ||
41 | |||
38 | let mut collector = DefCollector { | 42 | let mut collector = DefCollector { |
39 | db, | 43 | db, |
40 | def_map, | 44 | def_map, |
@@ -42,6 +46,7 @@ pub(super) fn collect_defs(db: &impl DefDatabase, mut def_map: CrateDefMap) -> C | |||
42 | unresolved_imports: Vec::new(), | 46 | unresolved_imports: Vec::new(), |
43 | unexpanded_macros: Vec::new(), | 47 | unexpanded_macros: Vec::new(), |
44 | macro_stack_monitor: MacroStackMonitor::default(), | 48 | macro_stack_monitor: MacroStackMonitor::default(), |
49 | cfg_options, | ||
45 | }; | 50 | }; |
46 | collector.collect(); | 51 | collector.collect(); |
47 | collector.finish() | 52 | collector.finish() |
@@ -76,8 +81,8 @@ impl MacroStackMonitor { | |||
76 | } | 81 | } |
77 | 82 | ||
78 | /// Walks the tree of module recursively | 83 | /// Walks the tree of module recursively |
79 | struct DefCollector<DB> { | 84 | struct DefCollector<'a, DB> { |
80 | db: DB, | 85 | db: &'a DB, |
81 | def_map: CrateDefMap, | 86 | def_map: CrateDefMap, |
82 | glob_imports: FxHashMap<CrateModuleId, Vec<(CrateModuleId, raw::ImportId)>>, | 87 | glob_imports: FxHashMap<CrateModuleId, Vec<(CrateModuleId, raw::ImportId)>>, |
83 | unresolved_imports: Vec<(CrateModuleId, raw::ImportId, raw::ImportData)>, | 88 | unresolved_imports: Vec<(CrateModuleId, raw::ImportId, raw::ImportData)>, |
@@ -86,9 +91,11 @@ struct DefCollector<DB> { | |||
86 | /// Some macro use `$tt:tt which mean we have to handle the macro perfectly | 91 | /// Some macro use `$tt:tt which mean we have to handle the macro perfectly |
87 | /// To prevent stack overflow, we add a deep counter here for prevent that. | 92 | /// To prevent stack overflow, we add a deep counter here for prevent that. |
88 | macro_stack_monitor: MacroStackMonitor, | 93 | macro_stack_monitor: MacroStackMonitor, |
94 | |||
95 | cfg_options: &'a CfgOptions, | ||
89 | } | 96 | } |
90 | 97 | ||
91 | impl<'a, DB> DefCollector<&'a DB> | 98 | impl<DB> DefCollector<'_, DB> |
92 | where | 99 | where |
93 | DB: DefDatabase, | 100 | DB: DefDatabase, |
94 | { | 101 | { |
@@ -506,7 +513,7 @@ struct ModCollector<'a, D> { | |||
506 | parent_module: Option<ParentModule<'a>>, | 513 | parent_module: Option<ParentModule<'a>>, |
507 | } | 514 | } |
508 | 515 | ||
509 | impl<DB> ModCollector<'_, &'_ mut DefCollector<&'_ DB>> | 516 | impl<DB> ModCollector<'_, &'_ mut DefCollector<'_, DB>> |
510 | where | 517 | where |
511 | DB: DefDatabase, | 518 | DB: DefDatabase, |
512 | { | 519 | { |
@@ -523,24 +530,27 @@ where | |||
523 | // `#[macro_use] extern crate` is hoisted to imports macros before collecting | 530 | // `#[macro_use] extern crate` is hoisted to imports macros before collecting |
524 | // any other items. | 531 | // any other items. |
525 | for item in items { | 532 | for item in items { |
526 | if let raw::RawItem::Import(import_id) = *item { | 533 | if self.is_cfg_enabled(&item.attrs) { |
527 | let import = self.raw_items[import_id].clone(); | 534 | if let raw::RawItemKind::Import(import_id) = item.kind { |
528 | if import.is_extern_crate && import.is_macro_use { | 535 | let import = self.raw_items[import_id].clone(); |
529 | self.def_collector.import_macros_from_extern_crate(self.module_id, &import); | 536 | if import.is_extern_crate && import.is_macro_use { |
537 | self.def_collector.import_macros_from_extern_crate(self.module_id, &import); | ||
538 | } | ||
530 | } | 539 | } |
531 | } | 540 | } |
532 | } | 541 | } |
533 | 542 | ||
534 | for item in items { | 543 | for item in items { |
535 | match *item { | 544 | if self.is_cfg_enabled(&item.attrs) { |
536 | raw::RawItem::Module(m) => self.collect_module(&self.raw_items[m]), | 545 | match item.kind { |
537 | raw::RawItem::Import(import_id) => self.def_collector.unresolved_imports.push(( | 546 | raw::RawItemKind::Module(m) => self.collect_module(&self.raw_items[m]), |
538 | self.module_id, | 547 | raw::RawItemKind::Import(import_id) => self |
539 | import_id, | 548 | .def_collector |
540 | self.raw_items[import_id].clone(), | 549 | .unresolved_imports |
541 | )), | 550 | .push((self.module_id, import_id, self.raw_items[import_id].clone())), |
542 | raw::RawItem::Def(def) => self.define_def(&self.raw_items[def]), | 551 | raw::RawItemKind::Def(def) => self.define_def(&self.raw_items[def]), |
543 | raw::RawItem::Macro(mac) => self.collect_macro(&self.raw_items[mac]), | 552 | raw::RawItemKind::Macro(mac) => self.collect_macro(&self.raw_items[mac]), |
553 | } | ||
544 | } | 554 | } |
545 | } | 555 | } |
546 | } | 556 | } |
@@ -703,6 +713,14 @@ where | |||
703 | self.def_collector.define_legacy_macro(self.module_id, name.clone(), macro_); | 713 | self.def_collector.define_legacy_macro(self.module_id, name.clone(), macro_); |
704 | } | 714 | } |
705 | } | 715 | } |
716 | |||
717 | fn is_cfg_enabled(&self, attrs: &raw::Attrs) -> bool { | ||
718 | attrs.as_ref().map_or(true, |attrs| { | ||
719 | attrs | ||
720 | .iter() | ||
721 | .all(|attr| attr.is_cfg_enabled(&self.def_collector.cfg_options) != Some(false)) | ||
722 | }) | ||
723 | } | ||
706 | } | 724 | } |
707 | 725 | ||
708 | fn is_macro_rules(path: &Path) -> bool { | 726 | fn is_macro_rules(path: &Path) -> bool { |
@@ -730,6 +748,7 @@ mod tests { | |||
730 | unresolved_imports: Vec::new(), | 748 | unresolved_imports: Vec::new(), |
731 | unexpanded_macros: Vec::new(), | 749 | unexpanded_macros: Vec::new(), |
732 | macro_stack_monitor: monitor, | 750 | macro_stack_monitor: monitor, |
751 | cfg_options: &CfgOptions::default(), | ||
733 | }; | 752 | }; |
734 | collector.collect(); | 753 | collector.collect(); |
735 | collector.finish() | 754 | collector.finish() |
diff --git a/crates/ra_hir/src/nameres/raw.rs b/crates/ra_hir/src/nameres/raw.rs index 606bd1a95..623b343c4 100644 --- a/crates/ra_hir/src/nameres/raw.rs +++ b/crates/ra_hir/src/nameres/raw.rs | |||
@@ -10,6 +10,7 @@ use ra_syntax::{ | |||
10 | use test_utils::tested_by; | 10 | use test_utils::tested_by; |
11 | 11 | ||
12 | use crate::{ | 12 | use crate::{ |
13 | attr::Attr, | ||
13 | db::{AstDatabase, DefDatabase}, | 14 | db::{AstDatabase, DefDatabase}, |
14 | AsName, AstIdMap, Either, FileAstId, HirFileId, ModuleSource, Name, Path, Source, | 15 | AsName, AstIdMap, Either, FileAstId, HirFileId, ModuleSource, Name, Path, Source, |
15 | }; | 16 | }; |
@@ -119,8 +120,17 @@ impl Index<Macro> for RawItems { | |||
119 | } | 120 | } |
120 | } | 121 | } |
121 | 122 | ||
123 | // Avoid heap allocation on items without attributes. | ||
124 | pub(super) type Attrs = Option<Arc<[Attr]>>; | ||
125 | |||
126 | #[derive(Debug, PartialEq, Eq, Clone)] | ||
127 | pub(super) struct RawItem { | ||
128 | pub(super) attrs: Attrs, | ||
129 | pub(super) kind: RawItemKind, | ||
130 | } | ||
131 | |||
122 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | 132 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] |
123 | pub(super) enum RawItem { | 133 | pub(super) enum RawItemKind { |
124 | Module(Module), | 134 | Module(Module), |
125 | Import(ImportId), | 135 | Import(ImportId), |
126 | Def(Def), | 136 | Def(Def), |
@@ -215,6 +225,7 @@ impl<DB: AstDatabase> RawItemsCollector<&DB> { | |||
215 | } | 225 | } |
216 | 226 | ||
217 | fn add_item(&mut self, current_module: Option<Module>, item: ast::ModuleItem) { | 227 | fn add_item(&mut self, current_module: Option<Module>, item: ast::ModuleItem) { |
228 | let attrs = self.parse_attrs(&item); | ||
218 | let (kind, name) = match item { | 229 | let (kind, name) = match item { |
219 | ast::ModuleItem::Module(module) => { | 230 | ast::ModuleItem::Module(module) => { |
220 | self.add_module(current_module, module); | 231 | self.add_module(current_module, module); |
@@ -263,7 +274,7 @@ impl<DB: AstDatabase> RawItemsCollector<&DB> { | |||
263 | if let Some(name) = name { | 274 | if let Some(name) = name { |
264 | let name = name.as_name(); | 275 | let name = name.as_name(); |
265 | let def = self.raw_items.defs.alloc(DefData { name, kind }); | 276 | let def = self.raw_items.defs.alloc(DefData { name, kind }); |
266 | self.push_item(current_module, RawItem::Def(def)) | 277 | self.push_item(current_module, attrs, RawItemKind::Def(def)); |
267 | } | 278 | } |
268 | } | 279 | } |
269 | 280 | ||
@@ -272,8 +283,10 @@ impl<DB: AstDatabase> RawItemsCollector<&DB> { | |||
272 | Some(it) => it.as_name(), | 283 | Some(it) => it.as_name(), |
273 | None => return, | 284 | None => return, |
274 | }; | 285 | }; |
286 | let attrs = self.parse_attrs(&module); | ||
275 | 287 | ||
276 | let ast_id = self.source_ast_id_map.ast_id(&module); | 288 | let ast_id = self.source_ast_id_map.ast_id(&module); |
289 | // FIXME: cfg_attr | ||
277 | let is_macro_use = module.has_atom_attr("macro_use"); | 290 | let is_macro_use = module.has_atom_attr("macro_use"); |
278 | if module.has_semi() { | 291 | if module.has_semi() { |
279 | let attr_path = extract_mod_path_attribute(&module); | 292 | let attr_path = extract_mod_path_attribute(&module); |
@@ -283,7 +296,7 @@ impl<DB: AstDatabase> RawItemsCollector<&DB> { | |||
283 | attr_path, | 296 | attr_path, |
284 | is_macro_use, | 297 | is_macro_use, |
285 | }); | 298 | }); |
286 | self.push_item(current_module, RawItem::Module(item)); | 299 | self.push_item(current_module, attrs, RawItemKind::Module(item)); |
287 | return; | 300 | return; |
288 | } | 301 | } |
289 | 302 | ||
@@ -297,14 +310,16 @@ impl<DB: AstDatabase> RawItemsCollector<&DB> { | |||
297 | is_macro_use, | 310 | is_macro_use, |
298 | }); | 311 | }); |
299 | self.process_module(Some(item), item_list); | 312 | self.process_module(Some(item), item_list); |
300 | self.push_item(current_module, RawItem::Module(item)); | 313 | self.push_item(current_module, attrs, RawItemKind::Module(item)); |
301 | return; | 314 | return; |
302 | } | 315 | } |
303 | tested_by!(name_res_works_for_broken_modules); | 316 | tested_by!(name_res_works_for_broken_modules); |
304 | } | 317 | } |
305 | 318 | ||
306 | fn add_use_item(&mut self, current_module: Option<Module>, use_item: ast::UseItem) { | 319 | fn add_use_item(&mut self, current_module: Option<Module>, use_item: ast::UseItem) { |
320 | // FIXME: cfg_attr | ||
307 | let is_prelude = use_item.has_atom_attr("prelude_import"); | 321 | let is_prelude = use_item.has_atom_attr("prelude_import"); |
322 | let attrs = self.parse_attrs(&use_item); | ||
308 | 323 | ||
309 | Path::expand_use_item( | 324 | Path::expand_use_item( |
310 | Source { ast: use_item, file_id: self.file_id }, | 325 | Source { ast: use_item, file_id: self.file_id }, |
@@ -318,7 +333,12 @@ impl<DB: AstDatabase> RawItemsCollector<&DB> { | |||
318 | is_extern_crate: false, | 333 | is_extern_crate: false, |
319 | is_macro_use: false, | 334 | is_macro_use: false, |
320 | }; | 335 | }; |
321 | self.push_import(current_module, import_data, Either::A(AstPtr::new(use_tree))); | 336 | self.push_import( |
337 | current_module, | ||
338 | attrs.clone(), | ||
339 | import_data, | ||
340 | Either::A(AstPtr::new(use_tree)), | ||
341 | ); | ||
322 | }, | 342 | }, |
323 | ) | 343 | ) |
324 | } | 344 | } |
@@ -331,6 +351,8 @@ impl<DB: AstDatabase> RawItemsCollector<&DB> { | |||
331 | if let Some(name_ref) = extern_crate.name_ref() { | 351 | if let Some(name_ref) = extern_crate.name_ref() { |
332 | let path = Path::from_name_ref(&name_ref); | 352 | let path = Path::from_name_ref(&name_ref); |
333 | let alias = extern_crate.alias().and_then(|a| a.name()).map(|it| it.as_name()); | 353 | let alias = extern_crate.alias().and_then(|a| a.name()).map(|it| it.as_name()); |
354 | let attrs = self.parse_attrs(&extern_crate); | ||
355 | // FIXME: cfg_attr | ||
334 | let is_macro_use = extern_crate.has_atom_attr("macro_use"); | 356 | let is_macro_use = extern_crate.has_atom_attr("macro_use"); |
335 | let import_data = ImportData { | 357 | let import_data = ImportData { |
336 | path, | 358 | path, |
@@ -340,11 +362,17 @@ impl<DB: AstDatabase> RawItemsCollector<&DB> { | |||
340 | is_extern_crate: true, | 362 | is_extern_crate: true, |
341 | is_macro_use, | 363 | is_macro_use, |
342 | }; | 364 | }; |
343 | self.push_import(current_module, import_data, Either::B(AstPtr::new(&extern_crate))); | 365 | self.push_import( |
366 | current_module, | ||
367 | attrs, | ||
368 | import_data, | ||
369 | Either::B(AstPtr::new(&extern_crate)), | ||
370 | ); | ||
344 | } | 371 | } |
345 | } | 372 | } |
346 | 373 | ||
347 | fn add_macro(&mut self, current_module: Option<Module>, m: ast::MacroCall) { | 374 | fn add_macro(&mut self, current_module: Option<Module>, m: ast::MacroCall) { |
375 | let attrs = self.parse_attrs(&m); | ||
348 | let path = match m | 376 | let path = match m |
349 | .path() | 377 | .path() |
350 | .and_then(|path| Path::from_src(Source { ast: path, file_id: self.file_id }, self.db)) | 378 | .and_then(|path| Path::from_src(Source { ast: path, file_id: self.file_id }, self.db)) |
@@ -355,24 +383,26 @@ impl<DB: AstDatabase> RawItemsCollector<&DB> { | |||
355 | 383 | ||
356 | let name = m.name().map(|it| it.as_name()); | 384 | let name = m.name().map(|it| it.as_name()); |
357 | let ast_id = self.source_ast_id_map.ast_id(&m); | 385 | let ast_id = self.source_ast_id_map.ast_id(&m); |
386 | // FIXME: cfg_attr | ||
358 | let export = m.attrs().filter_map(|x| x.simple_name()).any(|name| name == "macro_export"); | 387 | let export = m.attrs().filter_map(|x| x.simple_name()).any(|name| name == "macro_export"); |
359 | 388 | ||
360 | let m = self.raw_items.macros.alloc(MacroData { ast_id, path, name, export }); | 389 | let m = self.raw_items.macros.alloc(MacroData { ast_id, path, name, export }); |
361 | self.push_item(current_module, RawItem::Macro(m)); | 390 | self.push_item(current_module, attrs, RawItemKind::Macro(m)); |
362 | } | 391 | } |
363 | 392 | ||
364 | fn push_import( | 393 | fn push_import( |
365 | &mut self, | 394 | &mut self, |
366 | current_module: Option<Module>, | 395 | current_module: Option<Module>, |
396 | attrs: Attrs, | ||
367 | data: ImportData, | 397 | data: ImportData, |
368 | source: ImportSourcePtr, | 398 | source: ImportSourcePtr, |
369 | ) { | 399 | ) { |
370 | let import = self.raw_items.imports.alloc(data); | 400 | let import = self.raw_items.imports.alloc(data); |
371 | self.source_map.insert(import, source); | 401 | self.source_map.insert(import, source); |
372 | self.push_item(current_module, RawItem::Import(import)) | 402 | self.push_item(current_module, attrs, RawItemKind::Import(import)) |
373 | } | 403 | } |
374 | 404 | ||
375 | fn push_item(&mut self, current_module: Option<Module>, item: RawItem) { | 405 | fn push_item(&mut self, current_module: Option<Module>, attrs: Attrs, kind: RawItemKind) { |
376 | match current_module { | 406 | match current_module { |
377 | Some(module) => match &mut self.raw_items.modules[module] { | 407 | Some(module) => match &mut self.raw_items.modules[module] { |
378 | ModuleData::Definition { items, .. } => items, | 408 | ModuleData::Definition { items, .. } => items, |
@@ -380,7 +410,11 @@ impl<DB: AstDatabase> RawItemsCollector<&DB> { | |||
380 | }, | 410 | }, |
381 | None => &mut self.raw_items.items, | 411 | None => &mut self.raw_items.items, |
382 | } | 412 | } |
383 | .push(item) | 413 | .push(RawItem { attrs, kind }) |
414 | } | ||
415 | |||
416 | fn parse_attrs(&self, item: &impl ast::AttrsOwner) -> Attrs { | ||
417 | Attr::from_attrs_owner(self.file_id, item, self.db) | ||
384 | } | 418 | } |
385 | } | 419 | } |
386 | 420 | ||
diff --git a/crates/ra_hir/src/nameres/tests.rs b/crates/ra_hir/src/nameres/tests.rs index bc4b47b70..34dd79574 100644 --- a/crates/ra_hir/src/nameres/tests.rs +++ b/crates/ra_hir/src/nameres/tests.rs | |||
@@ -7,6 +7,7 @@ mod mod_resolution; | |||
7 | use std::sync::Arc; | 7 | use std::sync::Arc; |
8 | 8 | ||
9 | use insta::assert_snapshot; | 9 | use insta::assert_snapshot; |
10 | use ra_cfg::CfgOptions; | ||
10 | use ra_db::SourceDatabase; | 11 | use ra_db::SourceDatabase; |
11 | use test_utils::covers; | 12 | use test_utils::covers; |
12 | 13 | ||
@@ -507,3 +508,72 @@ fn values_dont_shadow_extern_crates() { | |||
507 | ⋮foo: v | 508 | ⋮foo: v |
508 | "###); | 509 | "###); |
509 | } | 510 | } |
511 | |||
512 | #[test] | ||
513 | fn cfg_not_test() { | ||
514 | let map = def_map_with_crate_graph( | ||
515 | r#" | ||
516 | //- /main.rs | ||
517 | use {Foo, Bar, Baz}; | ||
518 | //- /lib.rs | ||
519 | #[prelude_import] | ||
520 | pub use self::prelude::*; | ||
521 | mod prelude { | ||
522 | #[cfg(test)] | ||
523 | pub struct Foo; | ||
524 | #[cfg(not(test))] | ||
525 | pub struct Bar; | ||
526 | #[cfg(all(not(any()), feature = "foo", feature = "bar", opt = "42"))] | ||
527 | pub struct Baz; | ||
528 | } | ||
529 | "#, | ||
530 | crate_graph! { | ||
531 | "main": ("/main.rs", ["std"]), | ||
532 | "std": ("/lib.rs", []), | ||
533 | }, | ||
534 | ); | ||
535 | |||
536 | assert_snapshot!(map, @r###" | ||
537 | ⋮crate | ||
538 | ⋮Bar: t v | ||
539 | ⋮Baz: _ | ||
540 | ⋮Foo: _ | ||
541 | "###); | ||
542 | } | ||
543 | |||
544 | #[test] | ||
545 | fn cfg_test() { | ||
546 | let map = def_map_with_crate_graph( | ||
547 | r#" | ||
548 | //- /main.rs | ||
549 | use {Foo, Bar, Baz}; | ||
550 | //- /lib.rs | ||
551 | #[prelude_import] | ||
552 | pub use self::prelude::*; | ||
553 | mod prelude { | ||
554 | #[cfg(test)] | ||
555 | pub struct Foo; | ||
556 | #[cfg(not(test))] | ||
557 | pub struct Bar; | ||
558 | #[cfg(all(not(any()), feature = "foo", feature = "bar", opt = "42"))] | ||
559 | pub struct Baz; | ||
560 | } | ||
561 | "#, | ||
562 | crate_graph! { | ||
563 | "main": ("/main.rs", ["std"]), | ||
564 | "std": ("/lib.rs", [], CfgOptions::default() | ||
565 | .atom("test".into()) | ||
566 | .key_value("feature".into(), "foo".into()) | ||
567 | .key_value("feature".into(), "bar".into()) | ||
568 | .key_value("opt".into(), "42".into()) | ||
569 | ), | ||
570 | }, | ||
571 | ); | ||
572 | |||
573 | assert_snapshot!(map, @r###" | ||
574 | ⋮crate | ||
575 | ⋮Bar: _ | ||
576 | ⋮Baz: t v | ||
577 | ⋮Foo: t v | ||
578 | "###); | ||
579 | } | ||