aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_def/src/nameres
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2020-06-24 16:07:37 +0100
committerGitHub <[email protected]>2020-06-24 16:07:37 +0100
commite9bdb05e9676e85bdd8fa5008e3ada3812b36fd9 (patch)
treea21d348fbfa2d06f1fba77622c5417383938e6fe /crates/ra_hir_def/src/nameres
parent1a3b507a007d0373a83bde203d780b860ea55ce1 (diff)
parent2928600374a8356c2c2bffee080c47cb0f463fb9 (diff)
Merge #4990
4990: Introduce an ItemTree layer to avoid reparsing files r=matklad a=jonas-schievink This reduces the latency of "go to definition" in a simple benchmark on rust-analyzer by around 30%. cc https://github.com/rust-analyzer/rust-analyzer/issues/1650 Closes https://github.com/rust-analyzer/rust-analyzer/issues/3485 Co-authored-by: Aleksey Kladov <[email protected]> Co-authored-by: Jonas Schievink <[email protected]> Co-authored-by: Jonas Schievink <[email protected]>
Diffstat (limited to 'crates/ra_hir_def/src/nameres')
-rw-r--r--crates/ra_hir_def/src/nameres/collector.rs386
-rw-r--r--crates/ra_hir_def/src/nameres/raw.rs482
-rw-r--r--crates/ra_hir_def/src/nameres/tests/incremental.rs38
-rw-r--r--crates/ra_hir_def/src/nameres/tests/mod_resolution.rs12
4 files changed, 260 insertions, 658 deletions
diff --git a/crates/ra_hir_def/src/nameres/collector.rs b/crates/ra_hir_def/src/nameres/collector.rs
index cbce04315..94da700ad 100644
--- a/crates/ra_hir_def/src/nameres/collector.rs
+++ b/crates/ra_hir_def/src/nameres/collector.rs
@@ -4,6 +4,7 @@
4//! resolves imports and expands macros. 4//! resolves imports and expands macros.
5 5
6use hir_expand::{ 6use hir_expand::{
7 ast_id_map::FileAstId,
7 builtin_derive::find_builtin_derive, 8 builtin_derive::find_builtin_derive,
8 builtin_macro::find_builtin_macro, 9 builtin_macro::find_builtin_macro,
9 name::{name, AsName, Name}, 10 name::{name, AsName, Name},
@@ -19,13 +20,16 @@ use test_utils::mark;
19use crate::{ 20use crate::{
20 attr::Attrs, 21 attr::Attrs,
21 db::DefDatabase, 22 db::DefDatabase,
23 item_tree::{
24 self, FileItemTreeId, ItemTree, ItemTreeId, MacroCall, Mod, ModItem, ModKind, StructDefKind,
25 },
22 nameres::{ 26 nameres::{
23 diagnostics::DefDiagnostic, mod_resolution::ModDir, path_resolution::ReachedFixedPoint, 27 diagnostics::DefDiagnostic, mod_resolution::ModDir, path_resolution::ReachedFixedPoint,
24 raw, BuiltinShadowMode, CrateDefMap, ModuleData, ModuleOrigin, ResolveMode, 28 BuiltinShadowMode, CrateDefMap, ModuleData, ModuleOrigin, ResolveMode,
25 }, 29 },
26 path::{ImportAlias, ModPath, PathKind}, 30 path::{ImportAlias, ModPath, PathKind},
27 per_ns::PerNs, 31 per_ns::PerNs,
28 visibility::Visibility, 32 visibility::{RawVisibility, Visibility},
29 AdtId, AsMacroCall, AstId, AstIdWithPath, ConstLoc, ContainerId, EnumLoc, EnumVariantId, 33 AdtId, AsMacroCall, AstId, AstIdWithPath, ConstLoc, ContainerId, EnumLoc, EnumVariantId,
30 FunctionLoc, ImplLoc, Intern, LocalModuleId, ModuleDefId, ModuleId, StaticLoc, StructLoc, 34 FunctionLoc, ImplLoc, Intern, LocalModuleId, ModuleDefId, ModuleId, StaticLoc, StructLoc,
31 TraitLoc, TypeAliasLoc, UnionLoc, 35 TraitLoc, TypeAliasLoc, UnionLoc,
@@ -102,10 +106,50 @@ impl PartialResolvedImport {
102} 106}
103 107
104#[derive(Clone, Debug, Eq, PartialEq)] 108#[derive(Clone, Debug, Eq, PartialEq)]
109struct Import {
110 pub path: ModPath,
111 pub alias: Option<ImportAlias>,
112 pub visibility: RawVisibility,
113 pub is_glob: bool,
114 pub is_prelude: bool,
115 pub is_extern_crate: bool,
116 pub is_macro_use: bool,
117}
118
119impl Import {
120 fn from_use(tree: &ItemTree, id: FileItemTreeId<item_tree::Import>) -> Self {
121 let it = &tree[id];
122 let visibility = &tree[it.visibility];
123 Self {
124 path: it.path.clone(),
125 alias: it.alias.clone(),
126 visibility: visibility.clone(),
127 is_glob: it.is_glob,
128 is_prelude: it.is_prelude,
129 is_extern_crate: false,
130 is_macro_use: false,
131 }
132 }
133
134 fn from_extern_crate(tree: &ItemTree, id: FileItemTreeId<item_tree::ExternCrate>) -> Self {
135 let it = &tree[id];
136 let visibility = &tree[it.visibility];
137 Self {
138 path: it.path.clone(),
139 alias: it.alias.clone(),
140 visibility: visibility.clone(),
141 is_glob: false,
142 is_prelude: false,
143 is_extern_crate: true,
144 is_macro_use: it.is_macro_use,
145 }
146 }
147}
148
149#[derive(Clone, Debug, Eq, PartialEq)]
105struct ImportDirective { 150struct ImportDirective {
106 module_id: LocalModuleId, 151 module_id: LocalModuleId,
107 import_id: raw::Import, 152 import: Import,
108 import: raw::ImportData,
109 status: PartialResolvedImport, 153 status: PartialResolvedImport,
110} 154}
111 155
@@ -123,6 +167,13 @@ struct DeriveDirective {
123 ast_id: AstIdWithPath<ast::ModuleItem>, 167 ast_id: AstIdWithPath<ast::ModuleItem>,
124} 168}
125 169
170struct DefData<'a> {
171 id: ModuleDefId,
172 name: &'a Name,
173 visibility: &'a RawVisibility,
174 has_constructor: bool,
175}
176
126/// Walks the tree of module recursively 177/// Walks the tree of module recursively
127struct DefCollector<'a> { 178struct DefCollector<'a> {
128 db: &'a dyn DefDatabase, 179 db: &'a dyn DefDatabase,
@@ -140,7 +191,7 @@ struct DefCollector<'a> {
140impl DefCollector<'_> { 191impl DefCollector<'_> {
141 fn collect(&mut self) { 192 fn collect(&mut self) {
142 let file_id = self.db.crate_graph()[self.def_map.krate].root_file_id; 193 let file_id = self.db.crate_graph()[self.def_map.krate].root_file_id;
143 let raw_items = self.db.raw_items(file_id.into()); 194 let item_tree = self.db.item_tree(file_id.into());
144 let module_id = self.def_map.root; 195 let module_id = self.def_map.root;
145 self.def_map.modules[module_id].origin = ModuleOrigin::CrateRoot { definition: file_id }; 196 self.def_map.modules[module_id].origin = ModuleOrigin::CrateRoot { definition: file_id };
146 ModCollector { 197 ModCollector {
@@ -148,10 +199,10 @@ impl DefCollector<'_> {
148 macro_depth: 0, 199 macro_depth: 0,
149 module_id, 200 module_id,
150 file_id: file_id.into(), 201 file_id: file_id.into(),
151 raw_items: &raw_items, 202 item_tree: &item_tree,
152 mod_dir: ModDir::root(), 203 mod_dir: ModDir::root(),
153 } 204 }
154 .collect(raw_items.items()); 205 .collect(item_tree.top_level_items());
155 206
156 // main name resolution fixed-point loop. 207 // main name resolution fixed-point loop.
157 let mut i = 0; 208 let mut i = 0;
@@ -286,7 +337,7 @@ impl DefCollector<'_> {
286 fn import_macros_from_extern_crate( 337 fn import_macros_from_extern_crate(
287 &mut self, 338 &mut self,
288 current_module_id: LocalModuleId, 339 current_module_id: LocalModuleId,
289 import: &raw::ImportData, 340 import: &item_tree::ExternCrate,
290 ) { 341 ) {
291 log::debug!( 342 log::debug!(
292 "importing macros from extern crate: {:?} ({:?})", 343 "importing macros from extern crate: {:?} ({:?})",
@@ -352,11 +403,7 @@ impl DefCollector<'_> {
352 } 403 }
353 } 404 }
354 405
355 fn resolve_import( 406 fn resolve_import(&self, module_id: LocalModuleId, import: &Import) -> PartialResolvedImport {
356 &self,
357 module_id: LocalModuleId,
358 import: &raw::ImportData,
359 ) -> PartialResolvedImport {
360 log::debug!("resolving import: {:?} ({:?})", import, self.def_map.edition); 407 log::debug!("resolving import: {:?} ({:?})", import, self.def_map.edition);
361 if import.is_extern_crate { 408 if import.is_extern_crate {
362 let res = self.def_map.resolve_name_in_extern_prelude( 409 let res = self.def_map.resolve_name_in_extern_prelude(
@@ -649,17 +696,17 @@ impl DefCollector<'_> {
649 depth: usize, 696 depth: usize,
650 ) { 697 ) {
651 let file_id: HirFileId = macro_call_id.as_file(); 698 let file_id: HirFileId = macro_call_id.as_file();
652 let raw_items = self.db.raw_items(file_id); 699 let item_tree = self.db.item_tree(file_id);
653 let mod_dir = self.mod_dirs[&module_id].clone(); 700 let mod_dir = self.mod_dirs[&module_id].clone();
654 ModCollector { 701 ModCollector {
655 def_collector: &mut *self, 702 def_collector: &mut *self,
656 macro_depth: depth, 703 macro_depth: depth,
657 file_id, 704 file_id,
658 module_id, 705 module_id,
659 raw_items: &raw_items, 706 item_tree: &item_tree,
660 mod_dir, 707 mod_dir,
661 } 708 }
662 .collect(raw_items.items()); 709 .collect(item_tree.top_level_items());
663 } 710 }
664 711
665 fn finish(self) -> CrateDefMap { 712 fn finish(self) -> CrateDefMap {
@@ -673,12 +720,12 @@ struct ModCollector<'a, 'b> {
673 macro_depth: usize, 720 macro_depth: usize,
674 module_id: LocalModuleId, 721 module_id: LocalModuleId,
675 file_id: HirFileId, 722 file_id: HirFileId,
676 raw_items: &'a raw::RawItems, 723 item_tree: &'a ItemTree,
677 mod_dir: ModDir, 724 mod_dir: ModDir,
678} 725}
679 726
680impl ModCollector<'_, '_> { 727impl ModCollector<'_, '_> {
681 fn collect(&mut self, items: &[raw::RawItem]) { 728 fn collect(&mut self, items: &[ModItem]) {
682 // Note: don't assert that inserted value is fresh: it's simply not true 729 // Note: don't assert that inserted value is fresh: it's simply not true
683 // for macros. 730 // for macros.
684 self.def_collector.mod_dirs.insert(self.module_id, self.mod_dir.clone()); 731 self.def_collector.mod_dirs.insert(self.module_id, self.mod_dir.clone());
@@ -695,64 +742,204 @@ impl ModCollector<'_, '_> {
695 // `#[macro_use] extern crate` is hoisted to imports macros before collecting 742 // `#[macro_use] extern crate` is hoisted to imports macros before collecting
696 // any other items. 743 // any other items.
697 for item in items { 744 for item in items {
698 if self.is_cfg_enabled(&item.attrs) { 745 if self.is_cfg_enabled(self.item_tree.attrs(*item)) {
699 if let raw::RawItemKind::Import(import_id) = item.kind { 746 if let ModItem::ExternCrate(id) = item {
700 let import = self.raw_items[import_id].clone(); 747 let import = self.item_tree[*id].clone();
701 if import.is_extern_crate && import.is_macro_use { 748 if import.is_macro_use {
702 self.def_collector.import_macros_from_extern_crate(self.module_id, &import); 749 self.def_collector.import_macros_from_extern_crate(self.module_id, &import);
703 } 750 }
704 } 751 }
705 } 752 }
706 } 753 }
707 754
708 for item in items { 755 for &item in items {
709 if self.is_cfg_enabled(&item.attrs) { 756 let attrs = self.item_tree.attrs(item);
710 match item.kind { 757 if self.is_cfg_enabled(attrs) {
711 raw::RawItemKind::Module(m) => { 758 let module =
712 self.collect_module(&self.raw_items[m], &item.attrs) 759 ModuleId { krate: self.def_collector.def_map.krate, local_id: self.module_id };
713 } 760 let container = ContainerId::ModuleId(module);
714 raw::RawItemKind::Import(import_id) => { 761
762 let mut def = None;
763 match item {
764 ModItem::Mod(m) => self.collect_module(&self.item_tree[m], attrs),
765 ModItem::Import(import_id) => {
715 self.def_collector.unresolved_imports.push(ImportDirective { 766 self.def_collector.unresolved_imports.push(ImportDirective {
716 module_id: self.module_id, 767 module_id: self.module_id,
717 import_id, 768 import: Import::from_use(&self.item_tree, import_id),
718 import: self.raw_items[import_id].clone(),
719 status: PartialResolvedImport::Unresolved, 769 status: PartialResolvedImport::Unresolved,
720 }) 770 })
721 } 771 }
722 raw::RawItemKind::Def(def) => { 772 ModItem::ExternCrate(import_id) => {
723 self.define_def(&self.raw_items[def], &item.attrs) 773 self.def_collector.unresolved_imports.push(ImportDirective {
774 module_id: self.module_id,
775 import: Import::from_extern_crate(&self.item_tree, import_id),
776 status: PartialResolvedImport::Unresolved,
777 })
724 } 778 }
725 raw::RawItemKind::Macro(mac) => self.collect_macro(&self.raw_items[mac]), 779 ModItem::MacroCall(mac) => self.collect_macro(&self.item_tree[mac]),
726 raw::RawItemKind::Impl(imp) => { 780 ModItem::Impl(imp) => {
727 let module = ModuleId { 781 let module = ModuleId {
728 krate: self.def_collector.def_map.krate, 782 krate: self.def_collector.def_map.krate,
729 local_id: self.module_id, 783 local_id: self.module_id,
730 }; 784 };
731 let container = ContainerId::ModuleId(module); 785 let container = ContainerId::ModuleId(module);
732 let ast_id = self.raw_items[imp].ast_id; 786 let impl_id = ImplLoc { container, id: ItemTreeId::new(self.file_id, imp) }
733 let impl_id = 787 .intern(self.def_collector.db);
734 ImplLoc { container, ast_id: AstId::new(self.file_id, ast_id) }
735 .intern(self.def_collector.db);
736 self.def_collector.def_map.modules[self.module_id] 788 self.def_collector.def_map.modules[self.module_id]
737 .scope 789 .scope
738 .define_impl(impl_id) 790 .define_impl(impl_id)
739 } 791 }
792 ModItem::Function(id) => {
793 let func = &self.item_tree[id];
794 def = Some(DefData {
795 id: FunctionLoc {
796 container: container.into(),
797 id: ItemTreeId::new(self.file_id, id),
798 }
799 .intern(self.def_collector.db)
800 .into(),
801 name: &func.name,
802 visibility: &self.item_tree[func.visibility],
803 has_constructor: false,
804 });
805 }
806 ModItem::Struct(id) => {
807 let it = &self.item_tree[id];
808
809 // FIXME: check attrs to see if this is an attribute macro invocation;
810 // in which case we don't add the invocation, just a single attribute
811 // macro invocation
812 self.collect_derives(attrs, it.ast_id.upcast());
813
814 def = Some(DefData {
815 id: StructLoc { container, id: ItemTreeId::new(self.file_id, id) }
816 .intern(self.def_collector.db)
817 .into(),
818 name: &it.name,
819 visibility: &self.item_tree[it.visibility],
820 has_constructor: it.kind != StructDefKind::Record,
821 });
822 }
823 ModItem::Union(id) => {
824 let it = &self.item_tree[id];
825
826 // FIXME: check attrs to see if this is an attribute macro invocation;
827 // in which case we don't add the invocation, just a single attribute
828 // macro invocation
829 self.collect_derives(attrs, it.ast_id.upcast());
830
831 def = Some(DefData {
832 id: UnionLoc { container, id: ItemTreeId::new(self.file_id, id) }
833 .intern(self.def_collector.db)
834 .into(),
835 name: &it.name,
836 visibility: &self.item_tree[it.visibility],
837 has_constructor: false,
838 });
839 }
840 ModItem::Enum(id) => {
841 let it = &self.item_tree[id];
842
843 // FIXME: check attrs to see if this is an attribute macro invocation;
844 // in which case we don't add the invocation, just a single attribute
845 // macro invocation
846 self.collect_derives(attrs, it.ast_id.upcast());
847
848 def = Some(DefData {
849 id: EnumLoc { container, id: ItemTreeId::new(self.file_id, id) }
850 .intern(self.def_collector.db)
851 .into(),
852 name: &it.name,
853 visibility: &self.item_tree[it.visibility],
854 has_constructor: false,
855 });
856 }
857 ModItem::Const(id) => {
858 let it = &self.item_tree[id];
859
860 if let Some(name) = &it.name {
861 def = Some(DefData {
862 id: ConstLoc {
863 container: container.into(),
864 id: ItemTreeId::new(self.file_id, id),
865 }
866 .intern(self.def_collector.db)
867 .into(),
868 name,
869 visibility: &self.item_tree[it.visibility],
870 has_constructor: false,
871 });
872 }
873 }
874 ModItem::Static(id) => {
875 let it = &self.item_tree[id];
876
877 def = Some(DefData {
878 id: StaticLoc { container, id: ItemTreeId::new(self.file_id, id) }
879 .intern(self.def_collector.db)
880 .into(),
881 name: &it.name,
882 visibility: &self.item_tree[it.visibility],
883 has_constructor: false,
884 });
885 }
886 ModItem::Trait(id) => {
887 let it = &self.item_tree[id];
888
889 def = Some(DefData {
890 id: TraitLoc { container, id: ItemTreeId::new(self.file_id, id) }
891 .intern(self.def_collector.db)
892 .into(),
893 name: &it.name,
894 visibility: &self.item_tree[it.visibility],
895 has_constructor: false,
896 });
897 }
898 ModItem::TypeAlias(id) => {
899 let it = &self.item_tree[id];
900
901 def = Some(DefData {
902 id: TypeAliasLoc {
903 container: container.into(),
904 id: ItemTreeId::new(self.file_id, id),
905 }
906 .intern(self.def_collector.db)
907 .into(),
908 name: &it.name,
909 visibility: &self.item_tree[it.visibility],
910 has_constructor: false,
911 });
912 }
913 }
914
915 if let Some(DefData { id, name, visibility, has_constructor }) = def {
916 self.def_collector.def_map.modules[self.module_id].scope.define_def(id);
917 let vis = self
918 .def_collector
919 .def_map
920 .resolve_visibility(self.def_collector.db, self.module_id, visibility)
921 .unwrap_or(Visibility::Public);
922 self.def_collector.update(
923 self.module_id,
924 &[(name.clone(), PerNs::from_def(id, vis, has_constructor))],
925 vis,
926 )
740 } 927 }
741 } 928 }
742 } 929 }
743 } 930 }
744 931
745 fn collect_module(&mut self, module: &raw::ModuleData, attrs: &Attrs) { 932 fn collect_module(&mut self, module: &Mod, attrs: &Attrs) {
746 let path_attr = attrs.by_key("path").string_value(); 933 let path_attr = attrs.by_key("path").string_value();
747 let is_macro_use = attrs.by_key("macro_use").exists(); 934 let is_macro_use = attrs.by_key("macro_use").exists();
748 match module { 935 match &module.kind {
749 // inline module, just recurse 936 // inline module, just recurse
750 raw::ModuleData::Definition { name, visibility, items, ast_id } => { 937 ModKind::Inline { items } => {
751 let module_id = self.push_child_module( 938 let module_id = self.push_child_module(
752 name.clone(), 939 module.name.clone(),
753 AstId::new(self.file_id, *ast_id), 940 AstId::new(self.file_id, module.ast_id),
754 None, 941 None,
755 &visibility, 942 &self.item_tree[module.visibility],
756 ); 943 );
757 944
758 ModCollector { 945 ModCollector {
@@ -760,8 +947,8 @@ impl ModCollector<'_, '_> {
760 macro_depth: self.macro_depth, 947 macro_depth: self.macro_depth,
761 module_id, 948 module_id,
762 file_id: self.file_id, 949 file_id: self.file_id,
763 raw_items: self.raw_items, 950 item_tree: self.item_tree,
764 mod_dir: self.mod_dir.descend_into_definition(name, path_attr), 951 mod_dir: self.mod_dir.descend_into_definition(&module.name, path_attr),
765 } 952 }
766 .collect(&*items); 953 .collect(&*items);
767 if is_macro_use { 954 if is_macro_use {
@@ -769,31 +956,31 @@ impl ModCollector<'_, '_> {
769 } 956 }
770 } 957 }
771 // out of line module, resolve, parse and recurse 958 // out of line module, resolve, parse and recurse
772 raw::ModuleData::Declaration { name, visibility, ast_id } => { 959 ModKind::Outline {} => {
773 let ast_id = AstId::new(self.file_id, *ast_id); 960 let ast_id = AstId::new(self.file_id, module.ast_id);
774 match self.mod_dir.resolve_declaration( 961 match self.mod_dir.resolve_declaration(
775 self.def_collector.db, 962 self.def_collector.db,
776 self.file_id, 963 self.file_id,
777 name, 964 &module.name,
778 path_attr, 965 path_attr,
779 ) { 966 ) {
780 Ok((file_id, is_mod_rs, mod_dir)) => { 967 Ok((file_id, is_mod_rs, mod_dir)) => {
781 let module_id = self.push_child_module( 968 let module_id = self.push_child_module(
782 name.clone(), 969 module.name.clone(),
783 ast_id, 970 ast_id,
784 Some((file_id, is_mod_rs)), 971 Some((file_id, is_mod_rs)),
785 &visibility, 972 &self.item_tree[module.visibility],
786 ); 973 );
787 let raw_items = self.def_collector.db.raw_items(file_id.into()); 974 let item_tree = self.def_collector.db.item_tree(file_id.into());
788 ModCollector { 975 ModCollector {
789 def_collector: &mut *self.def_collector, 976 def_collector: &mut *self.def_collector,
790 macro_depth: self.macro_depth, 977 macro_depth: self.macro_depth,
791 module_id, 978 module_id,
792 file_id: file_id.into(), 979 file_id: file_id.into(),
793 raw_items: &raw_items, 980 item_tree: &item_tree,
794 mod_dir, 981 mod_dir,
795 } 982 }
796 .collect(raw_items.items()); 983 .collect(item_tree.top_level_items());
797 if is_macro_use { 984 if is_macro_use {
798 self.import_all_legacy_macros(module_id); 985 self.import_all_legacy_macros(module_id);
799 } 986 }
@@ -842,77 +1029,7 @@ impl ModCollector<'_, '_> {
842 res 1029 res
843 } 1030 }
844 1031
845 fn define_def(&mut self, def: &raw::DefData, attrs: &Attrs) { 1032 fn collect_derives(&mut self, attrs: &Attrs, ast_id: FileAstId<ast::ModuleItem>) {
846 let module = ModuleId { krate: self.def_collector.def_map.krate, local_id: self.module_id };
847 // FIXME: check attrs to see if this is an attribute macro invocation;
848 // in which case we don't add the invocation, just a single attribute
849 // macro invocation
850 self.collect_derives(attrs, def);
851
852 let name = def.name.clone();
853 let container = ContainerId::ModuleId(module);
854 let vis = &def.visibility;
855 let mut has_constructor = false;
856
857 let def: ModuleDefId = match def.kind {
858 raw::DefKind::Function(ast_id) => FunctionLoc {
859 container: container.into(),
860 ast_id: AstId::new(self.file_id, ast_id),
861 }
862 .intern(self.def_collector.db)
863 .into(),
864 raw::DefKind::Struct(ast_id, mode) => {
865 has_constructor = mode != raw::StructDefKind::Record;
866 StructLoc { container, ast_id: AstId::new(self.file_id, ast_id) }
867 .intern(self.def_collector.db)
868 .into()
869 }
870 raw::DefKind::Union(ast_id) => {
871 UnionLoc { container, ast_id: AstId::new(self.file_id, ast_id) }
872 .intern(self.def_collector.db)
873 .into()
874 }
875 raw::DefKind::Enum(ast_id) => {
876 EnumLoc { container, ast_id: AstId::new(self.file_id, ast_id) }
877 .intern(self.def_collector.db)
878 .into()
879 }
880 raw::DefKind::Const(ast_id) => {
881 ConstLoc { container: container.into(), ast_id: AstId::new(self.file_id, ast_id) }
882 .intern(self.def_collector.db)
883 .into()
884 }
885 raw::DefKind::Static(ast_id) => {
886 StaticLoc { container, ast_id: AstId::new(self.file_id, ast_id) }
887 .intern(self.def_collector.db)
888 .into()
889 }
890 raw::DefKind::Trait(ast_id) => {
891 TraitLoc { container, ast_id: AstId::new(self.file_id, ast_id) }
892 .intern(self.def_collector.db)
893 .into()
894 }
895 raw::DefKind::TypeAlias(ast_id) => TypeAliasLoc {
896 container: container.into(),
897 ast_id: AstId::new(self.file_id, ast_id),
898 }
899 .intern(self.def_collector.db)
900 .into(),
901 };
902 self.def_collector.def_map.modules[self.module_id].scope.define_def(def);
903 let vis = self
904 .def_collector
905 .def_map
906 .resolve_visibility(self.def_collector.db, self.module_id, vis)
907 .unwrap_or(Visibility::Public);
908 self.def_collector.update(
909 self.module_id,
910 &[(name, PerNs::from_def(def, vis, has_constructor))],
911 vis,
912 )
913 }
914
915 fn collect_derives(&mut self, attrs: &Attrs, def: &raw::DefData) {
916 for derive_subtree in attrs.by_key("derive").tt_values() { 1033 for derive_subtree in attrs.by_key("derive").tt_values() {
917 // for #[derive(Copy, Clone)], `derive_subtree` is the `(Copy, Clone)` subtree 1034 // for #[derive(Copy, Clone)], `derive_subtree` is the `(Copy, Clone)` subtree
918 for tt in &derive_subtree.token_trees { 1035 for tt in &derive_subtree.token_trees {
@@ -923,7 +1040,7 @@ impl ModCollector<'_, '_> {
923 }; 1040 };
924 let path = ModPath::from_tt_ident(ident); 1041 let path = ModPath::from_tt_ident(ident);
925 1042
926 let ast_id = AstIdWithPath::new(self.file_id, def.kind.ast_id(), path); 1043 let ast_id = AstIdWithPath::new(self.file_id, ast_id, path);
927 self.def_collector 1044 self.def_collector
928 .unexpanded_attribute_macros 1045 .unexpanded_attribute_macros
929 .push(DeriveDirective { module_id: self.module_id, ast_id }); 1046 .push(DeriveDirective { module_id: self.module_id, ast_id });
@@ -931,11 +1048,11 @@ impl ModCollector<'_, '_> {
931 } 1048 }
932 } 1049 }
933 1050
934 fn collect_macro(&mut self, mac: &raw::MacroData) { 1051 fn collect_macro(&mut self, mac: &MacroCall) {
935 let mut ast_id = AstIdWithPath::new(self.file_id, mac.ast_id, mac.path.clone()); 1052 let mut ast_id = AstIdWithPath::new(self.file_id, mac.ast_id, mac.path.clone());
936 1053
937 // Case 0: builtin macros 1054 // Case 0: builtin macros
938 if mac.builtin { 1055 if mac.is_builtin {
939 if let Some(name) = &mac.name { 1056 if let Some(name) = &mac.name {
940 let krate = self.def_collector.def_map.krate; 1057 let krate = self.def_collector.def_map.krate;
941 if let Some(macro_id) = find_builtin_macro(name, krate, ast_id.ast_id) { 1058 if let Some(macro_id) = find_builtin_macro(name, krate, ast_id.ast_id) {
@@ -943,7 +1060,7 @@ impl ModCollector<'_, '_> {
943 self.module_id, 1060 self.module_id,
944 name.clone(), 1061 name.clone(),
945 macro_id, 1062 macro_id,
946 mac.export, 1063 mac.is_export,
947 ); 1064 );
948 return; 1065 return;
949 } 1066 }
@@ -957,9 +1074,14 @@ impl ModCollector<'_, '_> {
957 ast_id: Some(ast_id.ast_id), 1074 ast_id: Some(ast_id.ast_id),
958 krate: Some(self.def_collector.def_map.krate), 1075 krate: Some(self.def_collector.def_map.krate),
959 kind: MacroDefKind::Declarative, 1076 kind: MacroDefKind::Declarative,
960 local_inner: mac.local_inner, 1077 local_inner: mac.is_local_inner,
961 }; 1078 };
962 self.def_collector.define_macro(self.module_id, name.clone(), macro_id, mac.export); 1079 self.def_collector.define_macro(
1080 self.module_id,
1081 name.clone(),
1082 macro_id,
1083 mac.is_export,
1084 );
963 } 1085 }
964 return; 1086 return;
965 } 1087 }
diff --git a/crates/ra_hir_def/src/nameres/raw.rs b/crates/ra_hir_def/src/nameres/raw.rs
deleted file mode 100644
index f44baa579..000000000
--- a/crates/ra_hir_def/src/nameres/raw.rs
+++ /dev/null
@@ -1,482 +0,0 @@
1//! Lowers syntax tree of a rust file into a raw representation of containing
2//! items, *without* attaching them to a module structure.
3//!
4//! That is, raw items don't have semantics, just as syntax, but, unlike syntax,
5//! they don't change with trivial source code edits, making them a great tool
6//! for building salsa recomputation firewalls.
7
8use std::{ops::Index, sync::Arc};
9
10use hir_expand::{
11 ast_id_map::AstIdMap,
12 hygiene::Hygiene,
13 name::{AsName, Name},
14};
15use ra_arena::{Arena, Idx};
16use ra_prof::profile;
17use ra_syntax::{
18 ast::{self, AttrsOwner, NameOwner, VisibilityOwner},
19 AstNode,
20};
21use test_utils::mark;
22
23use crate::{
24 attr::Attrs,
25 db::DefDatabase,
26 path::{ImportAlias, ModPath},
27 visibility::RawVisibility,
28 FileAstId, HirFileId, InFile,
29};
30
31/// `RawItems` is a set of top-level items in a file (except for impls).
32///
33/// It is the input to name resolution algorithm. `RawItems` are not invalidated
34/// on most edits.
35#[derive(Debug, Default, PartialEq, Eq)]
36pub struct RawItems {
37 modules: Arena<ModuleData>,
38 imports: Arena<ImportData>,
39 defs: Arena<DefData>,
40 macros: Arena<MacroData>,
41 impls: Arena<ImplData>,
42 /// items for top-level module
43 items: Vec<RawItem>,
44}
45
46impl RawItems {
47 pub(crate) fn raw_items_query(db: &dyn DefDatabase, file_id: HirFileId) -> Arc<RawItems> {
48 let _p = profile("raw_items_query");
49 let mut collector = RawItemsCollector {
50 raw_items: RawItems::default(),
51 source_ast_id_map: db.ast_id_map(file_id),
52 file_id,
53 hygiene: Hygiene::new(db.upcast(), file_id),
54 };
55 if let Some(node) = db.parse_or_expand(file_id) {
56 if let Some(source_file) = ast::SourceFile::cast(node.clone()) {
57 collector.process_module(None, source_file);
58 } else if let Some(item_list) = ast::MacroItems::cast(node) {
59 collector.process_module(None, item_list);
60 }
61 }
62 let raw_items = collector.raw_items;
63 Arc::new(raw_items)
64 }
65
66 pub(super) fn items(&self) -> &[RawItem] {
67 &self.items
68 }
69}
70
71impl Index<Idx<ModuleData>> for RawItems {
72 type Output = ModuleData;
73 fn index(&self, idx: Idx<ModuleData>) -> &ModuleData {
74 &self.modules[idx]
75 }
76}
77
78impl Index<Import> for RawItems {
79 type Output = ImportData;
80 fn index(&self, idx: Import) -> &ImportData {
81 &self.imports[idx]
82 }
83}
84
85impl Index<Idx<DefData>> for RawItems {
86 type Output = DefData;
87 fn index(&self, idx: Idx<DefData>) -> &DefData {
88 &self.defs[idx]
89 }
90}
91
92impl Index<Idx<MacroData>> for RawItems {
93 type Output = MacroData;
94 fn index(&self, idx: Idx<MacroData>) -> &MacroData {
95 &self.macros[idx]
96 }
97}
98
99impl Index<Idx<ImplData>> for RawItems {
100 type Output = ImplData;
101 fn index(&self, idx: Idx<ImplData>) -> &ImplData {
102 &self.impls[idx]
103 }
104}
105
106#[derive(Debug, PartialEq, Eq, Clone)]
107pub(super) struct RawItem {
108 pub(super) attrs: Attrs,
109 pub(super) kind: RawItemKind,
110}
111
112#[derive(Debug, PartialEq, Eq, Clone, Copy)]
113pub(super) enum RawItemKind {
114 Module(Idx<ModuleData>),
115 Import(Import),
116 Def(Idx<DefData>),
117 Macro(Idx<MacroData>),
118 Impl(Idx<ImplData>),
119}
120
121#[derive(Debug, PartialEq, Eq)]
122pub(super) enum ModuleData {
123 Declaration {
124 name: Name,
125 visibility: RawVisibility,
126 ast_id: FileAstId<ast::Module>,
127 },
128 Definition {
129 name: Name,
130 visibility: RawVisibility,
131 ast_id: FileAstId<ast::Module>,
132 items: Vec<RawItem>,
133 },
134}
135
136pub(crate) type Import = Idx<ImportData>;
137
138#[derive(Debug, Clone, PartialEq, Eq)]
139pub struct ImportData {
140 pub(super) path: ModPath,
141 pub(super) alias: Option<ImportAlias>,
142 pub(super) is_glob: bool,
143 pub(super) is_prelude: bool,
144 pub(super) is_extern_crate: bool,
145 pub(super) is_macro_use: bool,
146 pub(super) visibility: RawVisibility,
147}
148
149// type Def = Idx<DefData>;
150
151#[derive(Debug, PartialEq, Eq)]
152pub(super) struct DefData {
153 pub(super) name: Name,
154 pub(super) kind: DefKind,
155 pub(super) visibility: RawVisibility,
156}
157
158#[derive(Debug, PartialEq, Eq, Clone, Copy)]
159pub(super) enum StructDefKind {
160 Record,
161 Tuple,
162 Unit,
163}
164
165#[derive(Debug, PartialEq, Eq, Clone, Copy)]
166pub(super) enum DefKind {
167 Function(FileAstId<ast::FnDef>),
168 Struct(FileAstId<ast::StructDef>, StructDefKind),
169 Union(FileAstId<ast::UnionDef>),
170 Enum(FileAstId<ast::EnumDef>),
171 Const(FileAstId<ast::ConstDef>),
172 Static(FileAstId<ast::StaticDef>),
173 Trait(FileAstId<ast::TraitDef>),
174 TypeAlias(FileAstId<ast::TypeAliasDef>),
175}
176
177impl DefKind {
178 pub fn ast_id(self) -> FileAstId<ast::ModuleItem> {
179 match self {
180 DefKind::Function(it) => it.upcast(),
181 DefKind::Struct(it, _) => it.upcast(),
182 DefKind::Union(it) => it.upcast(),
183 DefKind::Enum(it) => it.upcast(),
184 DefKind::Const(it) => it.upcast(),
185 DefKind::Static(it) => it.upcast(),
186 DefKind::Trait(it) => it.upcast(),
187 DefKind::TypeAlias(it) => it.upcast(),
188 }
189 }
190}
191
192#[derive(Debug, PartialEq, Eq)]
193pub(super) struct MacroData {
194 pub(super) ast_id: FileAstId<ast::MacroCall>,
195 pub(super) path: ModPath,
196 pub(super) name: Option<Name>,
197 pub(super) export: bool,
198 pub(super) local_inner: bool,
199 pub(super) builtin: bool,
200}
201
202#[derive(Debug, PartialEq, Eq)]
203pub(super) struct ImplData {
204 pub(super) ast_id: FileAstId<ast::ImplDef>,
205}
206
207struct RawItemsCollector {
208 raw_items: RawItems,
209 source_ast_id_map: Arc<AstIdMap>,
210 file_id: HirFileId,
211 hygiene: Hygiene,
212}
213
214impl RawItemsCollector {
215 fn process_module(
216 &mut self,
217 current_module: Option<Idx<ModuleData>>,
218 body: impl ast::ModuleItemOwner,
219 ) {
220 for item in body.items() {
221 self.add_item(current_module, item)
222 }
223 }
224
225 fn add_item(&mut self, current_module: Option<Idx<ModuleData>>, item: ast::ModuleItem) {
226 let attrs = self.parse_attrs(&item);
227 let visibility = RawVisibility::from_ast_with_hygiene(item.visibility(), &self.hygiene);
228 let (kind, name) = match item {
229 ast::ModuleItem::Module(module) => {
230 self.add_module(current_module, module);
231 return;
232 }
233 ast::ModuleItem::UseItem(use_item) => {
234 self.add_use_item(current_module, use_item);
235 return;
236 }
237 ast::ModuleItem::ExternCrateItem(extern_crate) => {
238 self.add_extern_crate_item(current_module, extern_crate);
239 return;
240 }
241 ast::ModuleItem::ImplDef(it) => {
242 self.add_impl(current_module, it);
243 return;
244 }
245 ast::ModuleItem::StructDef(it) => {
246 let kind = match it.kind() {
247 ast::StructKind::Record(_) => StructDefKind::Record,
248 ast::StructKind::Tuple(_) => StructDefKind::Tuple,
249 ast::StructKind::Unit => StructDefKind::Unit,
250 };
251 let id = self.source_ast_id_map.ast_id(&it);
252 let name = it.name();
253 (DefKind::Struct(id, kind), name)
254 }
255 ast::ModuleItem::UnionDef(it) => {
256 let id = self.source_ast_id_map.ast_id(&it);
257 let name = it.name();
258 (DefKind::Union(id), name)
259 }
260 ast::ModuleItem::EnumDef(it) => {
261 (DefKind::Enum(self.source_ast_id_map.ast_id(&it)), it.name())
262 }
263 ast::ModuleItem::FnDef(it) => {
264 (DefKind::Function(self.source_ast_id_map.ast_id(&it)), it.name())
265 }
266 ast::ModuleItem::TraitDef(it) => {
267 (DefKind::Trait(self.source_ast_id_map.ast_id(&it)), it.name())
268 }
269 ast::ModuleItem::TypeAliasDef(it) => {
270 (DefKind::TypeAlias(self.source_ast_id_map.ast_id(&it)), it.name())
271 }
272 ast::ModuleItem::ConstDef(it) => {
273 (DefKind::Const(self.source_ast_id_map.ast_id(&it)), it.name())
274 }
275 ast::ModuleItem::StaticDef(it) => {
276 (DefKind::Static(self.source_ast_id_map.ast_id(&it)), it.name())
277 }
278 ast::ModuleItem::MacroCall(it) => {
279 self.add_macro(current_module, it);
280 return;
281 }
282 ast::ModuleItem::ExternBlock(it) => {
283 self.add_extern_block(current_module, it);
284 return;
285 }
286 };
287 if let Some(name) = name {
288 let name = name.as_name();
289 let def = self.raw_items.defs.alloc(DefData { name, kind, visibility });
290 self.push_item(current_module, attrs, RawItemKind::Def(def));
291 }
292 }
293
294 fn add_extern_block(
295 &mut self,
296 current_module: Option<Idx<ModuleData>>,
297 block: ast::ExternBlock,
298 ) {
299 if let Some(items) = block.extern_item_list() {
300 for item in items.extern_items() {
301 let attrs = self.parse_attrs(&item);
302 let visibility =
303 RawVisibility::from_ast_with_hygiene(item.visibility(), &self.hygiene);
304 let (kind, name) = match item {
305 ast::ExternItem::FnDef(it) => {
306 (DefKind::Function(self.source_ast_id_map.ast_id(&it)), it.name())
307 }
308 ast::ExternItem::StaticDef(it) => {
309 (DefKind::Static(self.source_ast_id_map.ast_id(&it)), it.name())
310 }
311 };
312
313 if let Some(name) = name {
314 let name = name.as_name();
315 let def = self.raw_items.defs.alloc(DefData { name, kind, visibility });
316 self.push_item(current_module, attrs, RawItemKind::Def(def));
317 }
318 }
319 }
320 }
321
322 fn add_module(&mut self, current_module: Option<Idx<ModuleData>>, module: ast::Module) {
323 let name = match module.name() {
324 Some(it) => it.as_name(),
325 None => return,
326 };
327 let attrs = self.parse_attrs(&module);
328 let visibility = RawVisibility::from_ast_with_hygiene(module.visibility(), &self.hygiene);
329
330 let ast_id = self.source_ast_id_map.ast_id(&module);
331 if module.semicolon_token().is_some() {
332 let item =
333 self.raw_items.modules.alloc(ModuleData::Declaration { name, visibility, ast_id });
334 self.push_item(current_module, attrs, RawItemKind::Module(item));
335 return;
336 }
337
338 if let Some(item_list) = module.item_list() {
339 let item = self.raw_items.modules.alloc(ModuleData::Definition {
340 name,
341 visibility,
342 ast_id,
343 items: Vec::new(),
344 });
345 self.process_module(Some(item), item_list);
346 self.push_item(current_module, attrs, RawItemKind::Module(item));
347 return;
348 }
349 mark::hit!(name_res_works_for_broken_modules);
350 }
351
352 fn add_use_item(&mut self, current_module: Option<Idx<ModuleData>>, use_item: ast::UseItem) {
353 // FIXME: cfg_attr
354 let is_prelude = use_item.has_atom_attr("prelude_import");
355 let attrs = self.parse_attrs(&use_item);
356 let visibility = RawVisibility::from_ast_with_hygiene(use_item.visibility(), &self.hygiene);
357
358 let mut buf = Vec::new();
359 ModPath::expand_use_item(
360 InFile { value: use_item, file_id: self.file_id },
361 &self.hygiene,
362 |path, _use_tree, is_glob, alias| {
363 let import_data = ImportData {
364 path,
365 alias,
366 is_glob,
367 is_prelude,
368 is_extern_crate: false,
369 is_macro_use: false,
370 visibility: visibility.clone(),
371 };
372 buf.push(import_data);
373 },
374 );
375 for import_data in buf {
376 self.push_import(current_module, attrs.clone(), import_data);
377 }
378 }
379
380 fn add_extern_crate_item(
381 &mut self,
382 current_module: Option<Idx<ModuleData>>,
383 extern_crate: ast::ExternCrateItem,
384 ) {
385 if let Some(name_ref) = extern_crate.name_ref() {
386 let path = ModPath::from_name_ref(&name_ref);
387 let visibility =
388 RawVisibility::from_ast_with_hygiene(extern_crate.visibility(), &self.hygiene);
389 let alias = extern_crate.alias().map(|a| {
390 a.name().map(|it| it.as_name()).map_or(ImportAlias::Underscore, ImportAlias::Alias)
391 });
392 let attrs = self.parse_attrs(&extern_crate);
393 // FIXME: cfg_attr
394 let is_macro_use = extern_crate.has_atom_attr("macro_use");
395 let import_data = ImportData {
396 path,
397 alias,
398 is_glob: false,
399 is_prelude: false,
400 is_extern_crate: true,
401 is_macro_use,
402 visibility,
403 };
404 self.push_import(current_module, attrs, import_data);
405 }
406 }
407
408 fn add_macro(&mut self, current_module: Option<Idx<ModuleData>>, m: ast::MacroCall) {
409 let attrs = self.parse_attrs(&m);
410 let path = match m.path().and_then(|path| ModPath::from_src(path, &self.hygiene)) {
411 Some(it) => it,
412 _ => return,
413 };
414
415 let name = m.name().map(|it| it.as_name());
416 let ast_id = self.source_ast_id_map.ast_id(&m);
417
418 // FIXME: cfg_attr
419 let export_attr = attrs.by_key("macro_export");
420
421 let export = export_attr.exists();
422 let local_inner = if export {
423 export_attr.tt_values().map(|it| &it.token_trees).flatten().any(|it| match it {
424 tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => {
425 ident.text.contains("local_inner_macros")
426 }
427 _ => false,
428 })
429 } else {
430 false
431 };
432
433 let builtin = attrs.by_key("rustc_builtin_macro").exists();
434
435 let m = self.raw_items.macros.alloc(MacroData {
436 ast_id,
437 path,
438 name,
439 export,
440 local_inner,
441 builtin,
442 });
443 self.push_item(current_module, attrs, RawItemKind::Macro(m));
444 }
445
446 fn add_impl(&mut self, current_module: Option<Idx<ModuleData>>, imp: ast::ImplDef) {
447 let attrs = self.parse_attrs(&imp);
448 let ast_id = self.source_ast_id_map.ast_id(&imp);
449 let imp = self.raw_items.impls.alloc(ImplData { ast_id });
450 self.push_item(current_module, attrs, RawItemKind::Impl(imp))
451 }
452
453 fn push_import(
454 &mut self,
455 current_module: Option<Idx<ModuleData>>,
456 attrs: Attrs,
457 data: ImportData,
458 ) {
459 let import = self.raw_items.imports.alloc(data);
460 self.push_item(current_module, attrs, RawItemKind::Import(import))
461 }
462
463 fn push_item(
464 &mut self,
465 current_module: Option<Idx<ModuleData>>,
466 attrs: Attrs,
467 kind: RawItemKind,
468 ) {
469 match current_module {
470 Some(module) => match &mut self.raw_items.modules[module] {
471 ModuleData::Definition { items, .. } => items,
472 ModuleData::Declaration { .. } => unreachable!(),
473 },
474 None => &mut self.raw_items.items,
475 }
476 .push(RawItem { attrs, kind })
477 }
478
479 fn parse_attrs(&self, item: &impl ast::AttrsOwner) -> Attrs {
480 Attrs::new(item, &self.hygiene)
481 }
482}
diff --git a/crates/ra_hir_def/src/nameres/tests/incremental.rs b/crates/ra_hir_def/src/nameres/tests/incremental.rs
index 87165ac33..0c288a108 100644
--- a/crates/ra_hir_def/src/nameres/tests/incremental.rs
+++ b/crates/ra_hir_def/src/nameres/tests/incremental.rs
@@ -58,44 +58,6 @@ fn typing_inside_a_function_should_not_invalidate_def_map() {
58} 58}
59 59
60#[test] 60#[test]
61fn adding_inner_items_should_not_invalidate_def_map() {
62 check_def_map_is_not_recomputed(
63 r"
64 //- /lib.rs
65 struct S { a: i32}
66 enum E { A }
67 trait T {
68 fn a() {}
69 }
70 mod foo;<|>
71 impl S {
72 fn a() {}
73 }
74 use crate::foo::bar::Baz;
75 //- /foo/mod.rs
76 pub mod bar;
77
78 //- /foo/bar.rs
79 pub struct Baz;
80 ",
81 r"
82 struct S { a: i32, b: () }
83 enum E { A, B }
84 trait T {
85 fn a() {}
86 fn b() {}
87 }
88 mod foo;<|>
89 impl S {
90 fn a() {}
91 fn b() {}
92 }
93 use crate::foo::bar::Baz;
94 ",
95 );
96}
97
98#[test]
99fn typing_inside_a_macro_should_not_invalidate_def_map() { 61fn typing_inside_a_macro_should_not_invalidate_def_map() {
100 let (mut db, pos) = TestDB::with_position( 62 let (mut db, pos) = TestDB::with_position(
101 r" 63 r"
diff --git a/crates/ra_hir_def/src/nameres/tests/mod_resolution.rs b/crates/ra_hir_def/src/nameres/tests/mod_resolution.rs
index b43b294ca..e9a5e4cba 100644
--- a/crates/ra_hir_def/src/nameres/tests/mod_resolution.rs
+++ b/crates/ra_hir_def/src/nameres/tests/mod_resolution.rs
@@ -20,8 +20,11 @@ fn name_res_works_for_broken_modules() {
20 ", 20 ",
21 ); 21 );
22 assert_snapshot!(map, @r###" 22 assert_snapshot!(map, @r###"
23 ⋮crate 23crate
24 ⋮Baz: _ 24Baz: _
25foo: t
26
27crate::foo
25 "###); 28 "###);
26} 29}
27 30
@@ -719,10 +722,7 @@ fn unresolved_module_diagnostics() {
719 ), 722 ),
720 ), 723 ),
721 ), 724 ),
722 value: FileAstId { 725 value: FileAstId::<ra_syntax::ast::generated::nodes::Module>(1),
723 raw: Idx::<SyntaxNodePtr>(1),
724 _ty: PhantomData,
725 },
726 }, 726 },
727 candidate: "bar.rs", 727 candidate: "bar.rs",
728 }, 728 },