diff options
Diffstat (limited to 'crates/ra_hir_def/src/item_scope.rs')
-rw-r--r-- | crates/ra_hir_def/src/item_scope.rs | 172 |
1 files changed, 172 insertions, 0 deletions
diff --git a/crates/ra_hir_def/src/item_scope.rs b/crates/ra_hir_def/src/item_scope.rs new file mode 100644 index 000000000..b0288ee8d --- /dev/null +++ b/crates/ra_hir_def/src/item_scope.rs | |||
@@ -0,0 +1,172 @@ | |||
1 | //! Describes items defined or visible (ie, imported) in a certain scope. | ||
2 | //! This is shared between modules and blocks. | ||
3 | |||
4 | use hir_expand::name::Name; | ||
5 | use once_cell::sync::Lazy; | ||
6 | use rustc_hash::FxHashMap; | ||
7 | |||
8 | use crate::{per_ns::PerNs, AdtId, BuiltinType, ImplId, MacroDefId, ModuleDefId, TraitId}; | ||
9 | |||
10 | #[derive(Debug, Default, PartialEq, Eq)] | ||
11 | pub struct ItemScope { | ||
12 | visible: FxHashMap<Name, PerNs>, | ||
13 | defs: Vec<ModuleDefId>, | ||
14 | impls: Vec<ImplId>, | ||
15 | /// Macros visible in current module in legacy textual scope | ||
16 | /// | ||
17 | /// For macros invoked by an unqualified identifier like `bar!()`, `legacy_macros` will be searched in first. | ||
18 | /// If it yields no result, then it turns to module scoped `macros`. | ||
19 | /// It macros with name qualified with a path like `crate::foo::bar!()`, `legacy_macros` will be skipped, | ||
20 | /// and only normal scoped `macros` will be searched in. | ||
21 | /// | ||
22 | /// Note that this automatically inherit macros defined textually before the definition of module itself. | ||
23 | /// | ||
24 | /// Module scoped macros will be inserted into `items` instead of here. | ||
25 | // FIXME: Macro shadowing in one module is not properly handled. Non-item place macros will | ||
26 | // be all resolved to the last one defined if shadowing happens. | ||
27 | legacy_macros: FxHashMap<Name, MacroDefId>, | ||
28 | } | ||
29 | |||
30 | static BUILTIN_SCOPE: Lazy<FxHashMap<Name, PerNs>> = Lazy::new(|| { | ||
31 | BuiltinType::ALL | ||
32 | .iter() | ||
33 | .map(|(name, ty)| (name.clone(), PerNs::types(ty.clone().into()))) | ||
34 | .collect() | ||
35 | }); | ||
36 | |||
37 | /// Shadow mode for builtin type which can be shadowed by module. | ||
38 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] | ||
39 | pub(crate) enum BuiltinShadowMode { | ||
40 | // Prefer Module | ||
41 | Module, | ||
42 | // Prefer Other Types | ||
43 | Other, | ||
44 | } | ||
45 | |||
46 | /// Legacy macros can only be accessed through special methods like `get_legacy_macros`. | ||
47 | /// Other methods will only resolve values, types and module scoped macros only. | ||
48 | impl ItemScope { | ||
49 | pub fn entries<'a>(&'a self) -> impl Iterator<Item = (&'a Name, PerNs)> + 'a { | ||
50 | //FIXME: shadowing | ||
51 | self.visible.iter().chain(BUILTIN_SCOPE.iter()).map(|(n, def)| (n, *def)) | ||
52 | } | ||
53 | |||
54 | pub fn entries_without_primitives<'a>( | ||
55 | &'a self, | ||
56 | ) -> impl Iterator<Item = (&'a Name, PerNs)> + 'a { | ||
57 | self.visible.iter().map(|(n, def)| (n, *def)) | ||
58 | } | ||
59 | |||
60 | pub fn declarations(&self) -> impl Iterator<Item = ModuleDefId> + '_ { | ||
61 | self.defs.iter().copied() | ||
62 | } | ||
63 | |||
64 | pub fn impls(&self) -> impl Iterator<Item = ImplId> + ExactSizeIterator + '_ { | ||
65 | self.impls.iter().copied() | ||
66 | } | ||
67 | |||
68 | /// Iterate over all module scoped macros | ||
69 | pub(crate) fn macros<'a>(&'a self) -> impl Iterator<Item = (&'a Name, MacroDefId)> + 'a { | ||
70 | self.visible.iter().filter_map(|(name, def)| def.take_macros().map(|macro_| (name, macro_))) | ||
71 | } | ||
72 | |||
73 | /// Iterate over all legacy textual scoped macros visible at the end of the module | ||
74 | pub(crate) fn legacy_macros<'a>(&'a self) -> impl Iterator<Item = (&'a Name, MacroDefId)> + 'a { | ||
75 | self.legacy_macros.iter().map(|(name, def)| (name, *def)) | ||
76 | } | ||
77 | |||
78 | /// Get a name from current module scope, legacy macros are not included | ||
79 | pub(crate) fn get(&self, name: &Name, shadow: BuiltinShadowMode) -> PerNs { | ||
80 | match shadow { | ||
81 | BuiltinShadowMode::Module => self | ||
82 | .visible | ||
83 | .get(name) | ||
84 | .or_else(|| BUILTIN_SCOPE.get(name)) | ||
85 | .copied() | ||
86 | .unwrap_or_else(PerNs::none), | ||
87 | BuiltinShadowMode::Other => { | ||
88 | let item = self.visible.get(name).copied(); | ||
89 | if let Some(def) = item { | ||
90 | if let Some(ModuleDefId::ModuleId(_)) = def.take_types() { | ||
91 | return BUILTIN_SCOPE | ||
92 | .get(name) | ||
93 | .copied() | ||
94 | .or(item) | ||
95 | .unwrap_or_else(PerNs::none); | ||
96 | } | ||
97 | } | ||
98 | |||
99 | item.or_else(|| BUILTIN_SCOPE.get(name).copied()).unwrap_or_else(PerNs::none) | ||
100 | } | ||
101 | } | ||
102 | } | ||
103 | |||
104 | pub(crate) fn traits<'a>(&'a self) -> impl Iterator<Item = TraitId> + 'a { | ||
105 | self.visible.values().filter_map(|def| match def.take_types() { | ||
106 | Some(ModuleDefId::TraitId(t)) => Some(t), | ||
107 | _ => None, | ||
108 | }) | ||
109 | } | ||
110 | |||
111 | pub(crate) fn define_def(&mut self, def: ModuleDefId) { | ||
112 | self.defs.push(def) | ||
113 | } | ||
114 | |||
115 | pub(crate) fn get_legacy_macro(&self, name: &Name) -> Option<MacroDefId> { | ||
116 | self.legacy_macros.get(name).copied() | ||
117 | } | ||
118 | |||
119 | pub(crate) fn define_impl(&mut self, imp: ImplId) { | ||
120 | self.impls.push(imp) | ||
121 | } | ||
122 | |||
123 | pub(crate) fn define_legacy_macro(&mut self, name: Name, mac: MacroDefId) { | ||
124 | self.legacy_macros.insert(name, mac); | ||
125 | } | ||
126 | |||
127 | pub(crate) fn push_res(&mut self, name: Name, def: PerNs) -> bool { | ||
128 | let mut changed = false; | ||
129 | let existing = self.visible.entry(name.clone()).or_default(); | ||
130 | |||
131 | if existing.types.is_none() && def.types.is_some() { | ||
132 | existing.types = def.types; | ||
133 | changed = true; | ||
134 | } | ||
135 | if existing.values.is_none() && def.values.is_some() { | ||
136 | existing.values = def.values; | ||
137 | changed = true; | ||
138 | } | ||
139 | if existing.macros.is_none() && def.macros.is_some() { | ||
140 | existing.macros = def.macros; | ||
141 | changed = true; | ||
142 | } | ||
143 | |||
144 | changed | ||
145 | } | ||
146 | |||
147 | pub(crate) fn collect_resolutions(&self) -> Vec<(Name, PerNs)> { | ||
148 | self.visible.iter().map(|(name, res)| (name.clone(), res.clone())).collect() | ||
149 | } | ||
150 | |||
151 | pub(crate) fn collect_legacy_macros(&self) -> FxHashMap<Name, MacroDefId> { | ||
152 | self.legacy_macros.clone() | ||
153 | } | ||
154 | } | ||
155 | |||
156 | impl From<ModuleDefId> for PerNs { | ||
157 | fn from(def: ModuleDefId) -> PerNs { | ||
158 | match def { | ||
159 | ModuleDefId::ModuleId(_) => PerNs::types(def), | ||
160 | ModuleDefId::FunctionId(_) => PerNs::values(def), | ||
161 | ModuleDefId::AdtId(adt) => match adt { | ||
162 | AdtId::StructId(_) | AdtId::UnionId(_) => PerNs::both(def, def), | ||
163 | AdtId::EnumId(_) => PerNs::types(def), | ||
164 | }, | ||
165 | ModuleDefId::EnumVariantId(_) => PerNs::both(def, def), | ||
166 | ModuleDefId::ConstId(_) | ModuleDefId::StaticId(_) => PerNs::values(def), | ||
167 | ModuleDefId::TraitId(_) => PerNs::types(def), | ||
168 | ModuleDefId::TypeAliasId(_) => PerNs::types(def), | ||
169 | ModuleDefId::BuiltinType(_) => PerNs::types(def), | ||
170 | } | ||
171 | } | ||
172 | } | ||