diff options
Diffstat (limited to 'crates/ra_hir_def')
-rw-r--r-- | crates/ra_hir_def/src/nameres/collector.rs | 301 |
1 files changed, 193 insertions, 108 deletions
diff --git a/crates/ra_hir_def/src/nameres/collector.rs b/crates/ra_hir_def/src/nameres/collector.rs index 034f27410..b899a5fb3 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 | ||
6 | use hir_expand::{ | 6 | use 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,14 +20,14 @@ use test_utils::mark; | |||
19 | use crate::{ | 20 | use crate::{ |
20 | attr::Attrs, | 21 | attr::Attrs, |
21 | db::DefDatabase, | 22 | db::DefDatabase, |
22 | item_tree::{Import, ItemTree, Mod, ModItem}, | 23 | item_tree::{Import, ItemTree, MacroCall, Mod, ModItem, ModKind, StructDefKind}, |
23 | nameres::{ | 24 | nameres::{ |
24 | diagnostics::DefDiagnostic, mod_resolution::ModDir, path_resolution::ReachedFixedPoint, | 25 | diagnostics::DefDiagnostic, mod_resolution::ModDir, path_resolution::ReachedFixedPoint, |
25 | BuiltinShadowMode, CrateDefMap, ModuleData, ModuleOrigin, ResolveMode, | 26 | BuiltinShadowMode, CrateDefMap, ModuleData, ModuleOrigin, ResolveMode, |
26 | }, | 27 | }, |
27 | path::{ImportAlias, ModPath, PathKind}, | 28 | path::{ImportAlias, ModPath, PathKind}, |
28 | per_ns::PerNs, | 29 | per_ns::PerNs, |
29 | visibility::Visibility, | 30 | visibility::{RawVisibility, Visibility}, |
30 | AdtId, AsMacroCall, AstId, AstIdWithPath, ConstLoc, ContainerId, EnumLoc, EnumVariantId, | 31 | AdtId, AsMacroCall, AstId, AstIdWithPath, ConstLoc, ContainerId, EnumLoc, EnumVariantId, |
31 | FunctionLoc, ImplLoc, Intern, LocalModuleId, ModuleDefId, ModuleId, StaticLoc, StructLoc, | 32 | FunctionLoc, ImplLoc, Intern, LocalModuleId, ModuleDefId, ModuleId, StaticLoc, StructLoc, |
32 | TraitLoc, TypeAliasLoc, UnionLoc, | 33 | TraitLoc, TypeAliasLoc, UnionLoc, |
@@ -125,6 +126,13 @@ struct DeriveDirective { | |||
125 | ast_id: AstIdWithPath<ast::ModuleItem>, | 126 | ast_id: AstIdWithPath<ast::ModuleItem>, |
126 | } | 127 | } |
127 | 128 | ||
129 | struct DefData<'a> { | ||
130 | id: ModuleDefId, | ||
131 | name: &'a Name, | ||
132 | visibility: &'a RawVisibility, | ||
133 | has_constructor: bool, | ||
134 | } | ||
135 | |||
128 | /// Walks the tree of module recursively | 136 | /// Walks the tree of module recursively |
129 | struct DefCollector<'a> { | 137 | struct DefCollector<'a> { |
130 | db: &'a dyn DefDatabase, | 138 | db: &'a dyn DefDatabase, |
@@ -693,9 +701,9 @@ impl ModCollector<'_, '_> { | |||
693 | // `#[macro_use] extern crate` is hoisted to imports macros before collecting | 701 | // `#[macro_use] extern crate` is hoisted to imports macros before collecting |
694 | // any other items. | 702 | // any other items. |
695 | for item in items { | 703 | for item in items { |
696 | if self.is_cfg_enabled(&item.attrs) { | 704 | if self.is_cfg_enabled(self.item_tree.attrs(*item)) { |
697 | if let raw::RawItemKind::Import(import_id) = item.kind { | 705 | if let ModItem::Import(import_id) = item { |
698 | let import = self.item_tree[import_id].clone(); | 706 | let import = self.item_tree[*import_id].clone(); |
699 | if import.is_extern_crate && import.is_macro_use { | 707 | if import.is_extern_crate && import.is_macro_use { |
700 | self.def_collector.import_macros_from_extern_crate(self.module_id, &import); | 708 | self.def_collector.import_macros_from_extern_crate(self.module_id, &import); |
701 | } | 709 | } |
@@ -703,13 +711,17 @@ impl ModCollector<'_, '_> { | |||
703 | } | 711 | } |
704 | } | 712 | } |
705 | 713 | ||
706 | for item in items { | 714 | for &item in items { |
707 | if self.is_cfg_enabled(&item.attrs) { | 715 | let attrs = self.item_tree.attrs(item); |
708 | match item.kind { | 716 | if self.is_cfg_enabled(attrs) { |
709 | raw::RawItemKind::Module(m) => { | 717 | let module = |
710 | self.collect_module(&self.item_tree[m], &item.attrs) | 718 | ModuleId { krate: self.def_collector.def_map.krate, local_id: self.module_id }; |
711 | } | 719 | let container = ContainerId::ModuleId(module); |
712 | raw::RawItemKind::Import(import_id) => { | 720 | |
721 | let mut def = None; | ||
722 | match item { | ||
723 | ModItem::Mod(m) => self.collect_module(&self.item_tree[m], attrs), | ||
724 | ModItem::Import(import_id) => { | ||
713 | self.def_collector.unresolved_imports.push(ImportDirective { | 725 | self.def_collector.unresolved_imports.push(ImportDirective { |
714 | module_id: self.module_id, | 726 | module_id: self.module_id, |
715 | import_id, | 727 | import_id, |
@@ -717,11 +729,8 @@ impl ModCollector<'_, '_> { | |||
717 | status: PartialResolvedImport::Unresolved, | 729 | status: PartialResolvedImport::Unresolved, |
718 | }) | 730 | }) |
719 | } | 731 | } |
720 | raw::RawItemKind::Def(def) => { | 732 | ModItem::MacroCall(mac) => self.collect_macro(&self.item_tree[mac]), |
721 | self.define_def(&self.item_tree[def], &item.attrs) | 733 | ModItem::Impl(imp) => { |
722 | } | ||
723 | raw::RawItemKind::Macro(mac) => self.collect_macro(&self.item_tree[mac]), | ||
724 | raw::RawItemKind::Impl(imp) => { | ||
725 | let module = ModuleId { | 734 | let module = ModuleId { |
726 | krate: self.def_collector.def_map.krate, | 735 | krate: self.def_collector.def_map.krate, |
727 | local_id: self.module_id, | 736 | local_id: self.module_id, |
@@ -735,6 +744,147 @@ impl ModCollector<'_, '_> { | |||
735 | .scope | 744 | .scope |
736 | .define_impl(impl_id) | 745 | .define_impl(impl_id) |
737 | } | 746 | } |
747 | ModItem::Function(it) => { | ||
748 | let it = &self.item_tree[it]; | ||
749 | def = Some(DefData { | ||
750 | id: FunctionLoc { | ||
751 | container: container.into(), | ||
752 | ast_id: AstId::new(self.file_id, it.ast_id), | ||
753 | } | ||
754 | .intern(self.def_collector.db) | ||
755 | .into(), | ||
756 | name: &it.name, | ||
757 | visibility: &it.visibility, | ||
758 | has_constructor: false, | ||
759 | }); | ||
760 | } | ||
761 | ModItem::Struct(it) => { | ||
762 | let it = &self.item_tree[it]; | ||
763 | |||
764 | // FIXME: check attrs to see if this is an attribute macro invocation; | ||
765 | // in which case we don't add the invocation, just a single attribute | ||
766 | // macro invocation | ||
767 | self.collect_derives(attrs, it.ast_id.upcast()); | ||
768 | |||
769 | def = Some(DefData { | ||
770 | id: StructLoc { | ||
771 | container, | ||
772 | ast_id: AstId::new(self.file_id, it.ast_id), | ||
773 | } | ||
774 | .intern(self.def_collector.db) | ||
775 | .into(), | ||
776 | name: &it.name, | ||
777 | visibility: &it.visibility, | ||
778 | has_constructor: it.kind != StructDefKind::Record, | ||
779 | }); | ||
780 | } | ||
781 | ModItem::Union(it) => { | ||
782 | let it = &self.item_tree[it]; | ||
783 | |||
784 | // FIXME: check attrs to see if this is an attribute macro invocation; | ||
785 | // in which case we don't add the invocation, just a single attribute | ||
786 | // macro invocation | ||
787 | self.collect_derives(attrs, it.ast_id.upcast()); | ||
788 | |||
789 | def = Some(DefData { | ||
790 | id: UnionLoc { container, ast_id: AstId::new(self.file_id, it.ast_id) } | ||
791 | .intern(self.def_collector.db) | ||
792 | .into(), | ||
793 | name: &it.name, | ||
794 | visibility: &it.visibility, | ||
795 | has_constructor: false, | ||
796 | }); | ||
797 | } | ||
798 | ModItem::Enum(it) => { | ||
799 | let it = &self.item_tree[it]; | ||
800 | |||
801 | // FIXME: check attrs to see if this is an attribute macro invocation; | ||
802 | // in which case we don't add the invocation, just a single attribute | ||
803 | // macro invocation | ||
804 | self.collect_derives(attrs, it.ast_id.upcast()); | ||
805 | |||
806 | def = Some(DefData { | ||
807 | id: EnumLoc { container, ast_id: AstId::new(self.file_id, it.ast_id) } | ||
808 | .intern(self.def_collector.db) | ||
809 | .into(), | ||
810 | name: &it.name, | ||
811 | visibility: &it.visibility, | ||
812 | has_constructor: false, | ||
813 | }); | ||
814 | } | ||
815 | ModItem::Const(it) => { | ||
816 | let it = &self.item_tree[it]; | ||
817 | |||
818 | if let Some(name) = &it.name { | ||
819 | def = Some(DefData { | ||
820 | id: ConstLoc { | ||
821 | container: container.into(), | ||
822 | ast_id: AstId::new(self.file_id, it.ast_id), | ||
823 | } | ||
824 | .intern(self.def_collector.db) | ||
825 | .into(), | ||
826 | name, | ||
827 | visibility: &it.visibility, | ||
828 | has_constructor: false, | ||
829 | }); | ||
830 | } | ||
831 | } | ||
832 | ModItem::Static(it) => { | ||
833 | let it = &self.item_tree[it]; | ||
834 | |||
835 | def = Some(DefData { | ||
836 | id: StaticLoc { | ||
837 | container, | ||
838 | ast_id: AstId::new(self.file_id, it.ast_id), | ||
839 | } | ||
840 | .intern(self.def_collector.db) | ||
841 | .into(), | ||
842 | name: &it.name, | ||
843 | visibility: &it.visibility, | ||
844 | has_constructor: false, | ||
845 | }); | ||
846 | } | ||
847 | ModItem::Trait(it) => { | ||
848 | let it = &self.item_tree[it]; | ||
849 | |||
850 | def = Some(DefData { | ||
851 | id: TraitLoc { container, ast_id: AstId::new(self.file_id, it.ast_id) } | ||
852 | .intern(self.def_collector.db) | ||
853 | .into(), | ||
854 | name: &it.name, | ||
855 | visibility: &it.visibility, | ||
856 | has_constructor: false, | ||
857 | }); | ||
858 | } | ||
859 | ModItem::TypeAlias(it) => { | ||
860 | let it = &self.item_tree[it]; | ||
861 | |||
862 | def = Some(DefData { | ||
863 | id: TypeAliasLoc { | ||
864 | container: container.into(), | ||
865 | ast_id: AstId::new(self.file_id, it.ast_id), | ||
866 | } | ||
867 | .intern(self.def_collector.db) | ||
868 | .into(), | ||
869 | name: &it.name, | ||
870 | visibility: &it.visibility, | ||
871 | has_constructor: false, | ||
872 | }); | ||
873 | } | ||
874 | } | ||
875 | |||
876 | if let Some(DefData { id, name, visibility, has_constructor }) = def { | ||
877 | self.def_collector.def_map.modules[self.module_id].scope.define_def(id); | ||
878 | let vis = self | ||
879 | .def_collector | ||
880 | .def_map | ||
881 | .resolve_visibility(self.def_collector.db, self.module_id, visibility) | ||
882 | .unwrap_or(Visibility::Public); | ||
883 | self.def_collector.update( | ||
884 | self.module_id, | ||
885 | &[(name.clone(), PerNs::from_def(id, vis, has_constructor))], | ||
886 | vis, | ||
887 | ) | ||
738 | } | 888 | } |
739 | } | 889 | } |
740 | } | 890 | } |
@@ -743,14 +893,14 @@ impl ModCollector<'_, '_> { | |||
743 | fn collect_module(&mut self, module: &Mod, attrs: &Attrs) { | 893 | fn collect_module(&mut self, module: &Mod, attrs: &Attrs) { |
744 | let path_attr = attrs.by_key("path").string_value(); | 894 | let path_attr = attrs.by_key("path").string_value(); |
745 | let is_macro_use = attrs.by_key("macro_use").exists(); | 895 | let is_macro_use = attrs.by_key("macro_use").exists(); |
746 | match module { | 896 | match &module.kind { |
747 | // inline module, just recurse | 897 | // inline module, just recurse |
748 | raw::ModuleData::Definition { name, visibility, items, ast_id } => { | 898 | ModKind::Inline { items } => { |
749 | let module_id = self.push_child_module( | 899 | let module_id = self.push_child_module( |
750 | name.clone(), | 900 | module.name.clone(), |
751 | AstId::new(self.file_id, *ast_id), | 901 | AstId::new(self.file_id, module.ast_id), |
752 | None, | 902 | None, |
753 | &visibility, | 903 | &module.visibility, |
754 | ); | 904 | ); |
755 | 905 | ||
756 | ModCollector { | 906 | ModCollector { |
@@ -759,7 +909,7 @@ impl ModCollector<'_, '_> { | |||
759 | module_id, | 909 | module_id, |
760 | file_id: self.file_id, | 910 | file_id: self.file_id, |
761 | item_tree: self.item_tree, | 911 | item_tree: self.item_tree, |
762 | mod_dir: self.mod_dir.descend_into_definition(name, path_attr), | 912 | mod_dir: self.mod_dir.descend_into_definition(&module.name, path_attr), |
763 | } | 913 | } |
764 | .collect(&*items); | 914 | .collect(&*items); |
765 | if is_macro_use { | 915 | if is_macro_use { |
@@ -767,31 +917,31 @@ impl ModCollector<'_, '_> { | |||
767 | } | 917 | } |
768 | } | 918 | } |
769 | // out of line module, resolve, parse and recurse | 919 | // out of line module, resolve, parse and recurse |
770 | raw::ModuleData::Declaration { name, visibility, ast_id } => { | 920 | ModKind::Outline {} => { |
771 | let ast_id = AstId::new(self.file_id, *ast_id); | 921 | let ast_id = AstId::new(self.file_id, module.ast_id); |
772 | match self.mod_dir.resolve_declaration( | 922 | match self.mod_dir.resolve_declaration( |
773 | self.def_collector.db, | 923 | self.def_collector.db, |
774 | self.file_id, | 924 | self.file_id, |
775 | name, | 925 | &module.name, |
776 | path_attr, | 926 | path_attr, |
777 | ) { | 927 | ) { |
778 | Ok((file_id, is_mod_rs, mod_dir)) => { | 928 | Ok((file_id, is_mod_rs, mod_dir)) => { |
779 | let module_id = self.push_child_module( | 929 | let module_id = self.push_child_module( |
780 | name.clone(), | 930 | module.name.clone(), |
781 | ast_id, | 931 | ast_id, |
782 | Some((file_id, is_mod_rs)), | 932 | Some((file_id, is_mod_rs)), |
783 | &visibility, | 933 | &module.visibility, |
784 | ); | 934 | ); |
785 | let raw_items = self.def_collector.db.raw_items(file_id.into()); | 935 | let item_tree = self.def_collector.db.item_tree(file_id.into()); |
786 | ModCollector { | 936 | ModCollector { |
787 | def_collector: &mut *self.def_collector, | 937 | def_collector: &mut *self.def_collector, |
788 | macro_depth: self.macro_depth, | 938 | macro_depth: self.macro_depth, |
789 | module_id, | 939 | module_id, |
790 | file_id: file_id.into(), | 940 | file_id: file_id.into(), |
791 | item_tree: &raw_items, | 941 | item_tree: &item_tree, |
792 | mod_dir, | 942 | mod_dir, |
793 | } | 943 | } |
794 | .collect(raw_items.items()); | 944 | .collect(item_tree.top_level_items()); |
795 | if is_macro_use { | 945 | if is_macro_use { |
796 | self.import_all_legacy_macros(module_id); | 946 | self.import_all_legacy_macros(module_id); |
797 | } | 947 | } |
@@ -840,77 +990,7 @@ impl ModCollector<'_, '_> { | |||
840 | res | 990 | res |
841 | } | 991 | } |
842 | 992 | ||
843 | fn define_def(&mut self, def: &raw::DefData, attrs: &Attrs) { | 993 | fn collect_derives(&mut self, attrs: &Attrs, ast_id: FileAstId<ast::ModuleItem>) { |
844 | let module = ModuleId { krate: self.def_collector.def_map.krate, local_id: self.module_id }; | ||
845 | // FIXME: check attrs to see if this is an attribute macro invocation; | ||
846 | // in which case we don't add the invocation, just a single attribute | ||
847 | // macro invocation | ||
848 | self.collect_derives(attrs, def); | ||
849 | |||
850 | let name = def.name.clone(); | ||
851 | let container = ContainerId::ModuleId(module); | ||
852 | let vis = &def.visibility; | ||
853 | let mut has_constructor = false; | ||
854 | |||
855 | let def: ModuleDefId = match def.kind { | ||
856 | raw::DefKind::Function(ast_id) => FunctionLoc { | ||
857 | container: container.into(), | ||
858 | ast_id: AstId::new(self.file_id, ast_id), | ||
859 | } | ||
860 | .intern(self.def_collector.db) | ||
861 | .into(), | ||
862 | raw::DefKind::Struct(ast_id, mode) => { | ||
863 | has_constructor = mode != raw::StructDefKind::Record; | ||
864 | StructLoc { container, ast_id: AstId::new(self.file_id, ast_id) } | ||
865 | .intern(self.def_collector.db) | ||
866 | .into() | ||
867 | } | ||
868 | raw::DefKind::Union(ast_id) => { | ||
869 | UnionLoc { container, ast_id: AstId::new(self.file_id, ast_id) } | ||
870 | .intern(self.def_collector.db) | ||
871 | .into() | ||
872 | } | ||
873 | raw::DefKind::Enum(ast_id) => { | ||
874 | EnumLoc { container, ast_id: AstId::new(self.file_id, ast_id) } | ||
875 | .intern(self.def_collector.db) | ||
876 | .into() | ||
877 | } | ||
878 | raw::DefKind::Const(ast_id) => { | ||
879 | ConstLoc { container: container.into(), ast_id: AstId::new(self.file_id, ast_id) } | ||
880 | .intern(self.def_collector.db) | ||
881 | .into() | ||
882 | } | ||
883 | raw::DefKind::Static(ast_id) => { | ||
884 | StaticLoc { container, ast_id: AstId::new(self.file_id, ast_id) } | ||
885 | .intern(self.def_collector.db) | ||
886 | .into() | ||
887 | } | ||
888 | raw::DefKind::Trait(ast_id) => { | ||
889 | TraitLoc { container, ast_id: AstId::new(self.file_id, ast_id) } | ||
890 | .intern(self.def_collector.db) | ||
891 | .into() | ||
892 | } | ||
893 | raw::DefKind::TypeAlias(ast_id) => TypeAliasLoc { | ||
894 | container: container.into(), | ||
895 | ast_id: AstId::new(self.file_id, ast_id), | ||
896 | } | ||
897 | .intern(self.def_collector.db) | ||
898 | .into(), | ||
899 | }; | ||
900 | self.def_collector.def_map.modules[self.module_id].scope.define_def(def); | ||
901 | let vis = self | ||
902 | .def_collector | ||
903 | .def_map | ||
904 | .resolve_visibility(self.def_collector.db, self.module_id, vis) | ||
905 | .unwrap_or(Visibility::Public); | ||
906 | self.def_collector.update( | ||
907 | self.module_id, | ||
908 | &[(name, PerNs::from_def(def, vis, has_constructor))], | ||
909 | vis, | ||
910 | ) | ||
911 | } | ||
912 | |||
913 | fn collect_derives(&mut self, attrs: &Attrs, def: &raw::DefData) { | ||
914 | for derive_subtree in attrs.by_key("derive").tt_values() { | 994 | for derive_subtree in attrs.by_key("derive").tt_values() { |
915 | // for #[derive(Copy, Clone)], `derive_subtree` is the `(Copy, Clone)` subtree | 995 | // for #[derive(Copy, Clone)], `derive_subtree` is the `(Copy, Clone)` subtree |
916 | for tt in &derive_subtree.token_trees { | 996 | for tt in &derive_subtree.token_trees { |
@@ -921,7 +1001,7 @@ impl ModCollector<'_, '_> { | |||
921 | }; | 1001 | }; |
922 | let path = ModPath::from_tt_ident(ident); | 1002 | let path = ModPath::from_tt_ident(ident); |
923 | 1003 | ||
924 | let ast_id = AstIdWithPath::new(self.file_id, def.kind.ast_id(), path); | 1004 | let ast_id = AstIdWithPath::new(self.file_id, ast_id, path); |
925 | self.def_collector | 1005 | self.def_collector |
926 | .unexpanded_attribute_macros | 1006 | .unexpanded_attribute_macros |
927 | .push(DeriveDirective { module_id: self.module_id, ast_id }); | 1007 | .push(DeriveDirective { module_id: self.module_id, ast_id }); |
@@ -929,11 +1009,11 @@ impl ModCollector<'_, '_> { | |||
929 | } | 1009 | } |
930 | } | 1010 | } |
931 | 1011 | ||
932 | fn collect_macro(&mut self, mac: &raw::MacroData) { | 1012 | fn collect_macro(&mut self, mac: &MacroCall) { |
933 | let mut ast_id = AstIdWithPath::new(self.file_id, mac.ast_id, mac.path.clone()); | 1013 | let mut ast_id = AstIdWithPath::new(self.file_id, mac.ast_id, mac.path.clone()); |
934 | 1014 | ||
935 | // Case 0: builtin macros | 1015 | // Case 0: builtin macros |
936 | if mac.builtin { | 1016 | if mac.is_builtin { |
937 | if let Some(name) = &mac.name { | 1017 | if let Some(name) = &mac.name { |
938 | let krate = self.def_collector.def_map.krate; | 1018 | let krate = self.def_collector.def_map.krate; |
939 | if let Some(macro_id) = find_builtin_macro(name, krate, ast_id.ast_id) { | 1019 | if let Some(macro_id) = find_builtin_macro(name, krate, ast_id.ast_id) { |
@@ -941,7 +1021,7 @@ impl ModCollector<'_, '_> { | |||
941 | self.module_id, | 1021 | self.module_id, |
942 | name.clone(), | 1022 | name.clone(), |
943 | macro_id, | 1023 | macro_id, |
944 | mac.export, | 1024 | mac.is_export, |
945 | ); | 1025 | ); |
946 | return; | 1026 | return; |
947 | } | 1027 | } |
@@ -955,9 +1035,14 @@ impl ModCollector<'_, '_> { | |||
955 | ast_id: Some(ast_id.ast_id), | 1035 | ast_id: Some(ast_id.ast_id), |
956 | krate: Some(self.def_collector.def_map.krate), | 1036 | krate: Some(self.def_collector.def_map.krate), |
957 | kind: MacroDefKind::Declarative, | 1037 | kind: MacroDefKind::Declarative, |
958 | local_inner: mac.local_inner, | 1038 | local_inner: mac.is_local_inner, |
959 | }; | 1039 | }; |
960 | self.def_collector.define_macro(self.module_id, name.clone(), macro_id, mac.export); | 1040 | self.def_collector.define_macro( |
1041 | self.module_id, | ||
1042 | name.clone(), | ||
1043 | macro_id, | ||
1044 | mac.is_export, | ||
1045 | ); | ||
961 | } | 1046 | } |
962 | return; | 1047 | return; |
963 | } | 1048 | } |