diff options
author | Zac Pullar-Strecker <[email protected]> | 2020-08-24 10:19:53 +0100 |
---|---|---|
committer | Zac Pullar-Strecker <[email protected]> | 2020-08-24 10:20:13 +0100 |
commit | 7bbca7a1b3f9293d2f5cc5745199bc5f8396f2f0 (patch) | |
tree | bdb47765991cb973b2cd5481a088fac636bd326c /crates/hir_def/src/item_scope.rs | |
parent | ca464650eeaca6195891199a93f4f76cf3e7e697 (diff) | |
parent | e65d48d1fb3d4d91d9dc1148a7a836ff5c9a3c87 (diff) |
Merge remote-tracking branch 'upstream/master' into 503-hover-doc-links
Diffstat (limited to 'crates/hir_def/src/item_scope.rs')
-rw-r--r-- | crates/hir_def/src/item_scope.rs | 341 |
1 files changed, 341 insertions, 0 deletions
diff --git a/crates/hir_def/src/item_scope.rs b/crates/hir_def/src/item_scope.rs new file mode 100644 index 000000000..f1e9dfd5b --- /dev/null +++ b/crates/hir_def/src/item_scope.rs | |||
@@ -0,0 +1,341 @@ | |||
1 | //! Describes items defined or visible (ie, imported) in a certain scope. | ||
2 | //! This is shared between modules and blocks. | ||
3 | |||
4 | use std::collections::hash_map::Entry; | ||
5 | |||
6 | use base_db::CrateId; | ||
7 | use hir_expand::name::Name; | ||
8 | use once_cell::sync::Lazy; | ||
9 | use rustc_hash::{FxHashMap, FxHashSet}; | ||
10 | use test_utils::mark; | ||
11 | |||
12 | use crate::{ | ||
13 | db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, HasModule, ImplId, | ||
14 | LocalModuleId, Lookup, MacroDefId, ModuleDefId, TraitId, | ||
15 | }; | ||
16 | |||
17 | #[derive(Copy, Clone)] | ||
18 | pub(crate) enum ImportType { | ||
19 | Glob, | ||
20 | Named, | ||
21 | } | ||
22 | |||
23 | #[derive(Debug, Default)] | ||
24 | pub struct PerNsGlobImports { | ||
25 | types: FxHashSet<(LocalModuleId, Name)>, | ||
26 | values: FxHashSet<(LocalModuleId, Name)>, | ||
27 | macros: FxHashSet<(LocalModuleId, Name)>, | ||
28 | } | ||
29 | |||
30 | #[derive(Debug, Default, PartialEq, Eq)] | ||
31 | pub struct ItemScope { | ||
32 | types: FxHashMap<Name, (ModuleDefId, Visibility)>, | ||
33 | values: FxHashMap<Name, (ModuleDefId, Visibility)>, | ||
34 | macros: FxHashMap<Name, (MacroDefId, Visibility)>, | ||
35 | unresolved: FxHashSet<Name>, | ||
36 | |||
37 | defs: Vec<ModuleDefId>, | ||
38 | impls: Vec<ImplId>, | ||
39 | /// Traits imported via `use Trait as _;`. | ||
40 | unnamed_trait_imports: FxHashMap<TraitId, Visibility>, | ||
41 | /// Macros visible in current module in legacy textual scope | ||
42 | /// | ||
43 | /// For macros invoked by an unqualified identifier like `bar!()`, `legacy_macros` will be searched in first. | ||
44 | /// If it yields no result, then it turns to module scoped `macros`. | ||
45 | /// It macros with name qualified with a path like `crate::foo::bar!()`, `legacy_macros` will be skipped, | ||
46 | /// and only normal scoped `macros` will be searched in. | ||
47 | /// | ||
48 | /// Note that this automatically inherit macros defined textually before the definition of module itself. | ||
49 | /// | ||
50 | /// Module scoped macros will be inserted into `items` instead of here. | ||
51 | // FIXME: Macro shadowing in one module is not properly handled. Non-item place macros will | ||
52 | // be all resolved to the last one defined if shadowing happens. | ||
53 | legacy_macros: FxHashMap<Name, MacroDefId>, | ||
54 | } | ||
55 | |||
56 | pub(crate) static BUILTIN_SCOPE: Lazy<FxHashMap<Name, PerNs>> = Lazy::new(|| { | ||
57 | BuiltinType::ALL | ||
58 | .iter() | ||
59 | .map(|(name, ty)| (name.clone(), PerNs::types(ty.clone().into(), Visibility::Public))) | ||
60 | .collect() | ||
61 | }); | ||
62 | |||
63 | /// Shadow mode for builtin type which can be shadowed by module. | ||
64 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] | ||
65 | pub(crate) enum BuiltinShadowMode { | ||
66 | /// Prefer user-defined modules (or other types) over builtins. | ||
67 | Module, | ||
68 | /// Prefer builtins over user-defined modules (but not other types). | ||
69 | Other, | ||
70 | } | ||
71 | |||
72 | /// Legacy macros can only be accessed through special methods like `get_legacy_macros`. | ||
73 | /// Other methods will only resolve values, types and module scoped macros only. | ||
74 | impl ItemScope { | ||
75 | pub fn entries<'a>(&'a self) -> impl Iterator<Item = (&'a Name, PerNs)> + 'a { | ||
76 | // FIXME: shadowing | ||
77 | let keys: FxHashSet<_> = self | ||
78 | .types | ||
79 | .keys() | ||
80 | .chain(self.values.keys()) | ||
81 | .chain(self.macros.keys()) | ||
82 | .chain(self.unresolved.iter()) | ||
83 | .collect(); | ||
84 | |||
85 | keys.into_iter().map(move |name| (name, self.get(name))) | ||
86 | } | ||
87 | |||
88 | pub fn declarations(&self) -> impl Iterator<Item = ModuleDefId> + '_ { | ||
89 | self.defs.iter().copied() | ||
90 | } | ||
91 | |||
92 | pub fn impls(&self) -> impl Iterator<Item = ImplId> + ExactSizeIterator + '_ { | ||
93 | self.impls.iter().copied() | ||
94 | } | ||
95 | |||
96 | pub fn visibility_of(&self, def: ModuleDefId) -> Option<Visibility> { | ||
97 | self.name_of(ItemInNs::Types(def)) | ||
98 | .or_else(|| self.name_of(ItemInNs::Values(def))) | ||
99 | .map(|(_, v)| v) | ||
100 | } | ||
101 | |||
102 | /// Iterate over all module scoped macros | ||
103 | pub(crate) fn macros<'a>(&'a self) -> impl Iterator<Item = (&'a Name, MacroDefId)> + 'a { | ||
104 | self.entries().filter_map(|(name, def)| def.take_macros().map(|macro_| (name, macro_))) | ||
105 | } | ||
106 | |||
107 | /// Iterate over all legacy textual scoped macros visible at the end of the module | ||
108 | pub(crate) fn legacy_macros<'a>(&'a self) -> impl Iterator<Item = (&'a Name, MacroDefId)> + 'a { | ||
109 | self.legacy_macros.iter().map(|(name, def)| (name, *def)) | ||
110 | } | ||
111 | |||
112 | /// Get a name from current module scope, legacy macros are not included | ||
113 | pub(crate) fn get(&self, name: &Name) -> PerNs { | ||
114 | PerNs { | ||
115 | types: self.types.get(name).copied(), | ||
116 | values: self.values.get(name).copied(), | ||
117 | macros: self.macros.get(name).copied(), | ||
118 | } | ||
119 | } | ||
120 | |||
121 | pub(crate) fn name_of(&self, item: ItemInNs) -> Option<(&Name, Visibility)> { | ||
122 | for (name, per_ns) in self.entries() { | ||
123 | if let Some(vis) = item.match_with(per_ns) { | ||
124 | return Some((name, vis)); | ||
125 | } | ||
126 | } | ||
127 | None | ||
128 | } | ||
129 | |||
130 | pub(crate) fn traits<'a>(&'a self) -> impl Iterator<Item = TraitId> + 'a { | ||
131 | self.types | ||
132 | .values() | ||
133 | .filter_map(|(def, _)| match def { | ||
134 | ModuleDefId::TraitId(t) => Some(*t), | ||
135 | _ => None, | ||
136 | }) | ||
137 | .chain(self.unnamed_trait_imports.keys().copied()) | ||
138 | } | ||
139 | |||
140 | pub(crate) fn define_def(&mut self, def: ModuleDefId) { | ||
141 | self.defs.push(def) | ||
142 | } | ||
143 | |||
144 | pub(crate) fn get_legacy_macro(&self, name: &Name) -> Option<MacroDefId> { | ||
145 | self.legacy_macros.get(name).copied() | ||
146 | } | ||
147 | |||
148 | pub(crate) fn define_impl(&mut self, imp: ImplId) { | ||
149 | self.impls.push(imp) | ||
150 | } | ||
151 | |||
152 | pub(crate) fn define_legacy_macro(&mut self, name: Name, mac: MacroDefId) { | ||
153 | self.legacy_macros.insert(name, mac); | ||
154 | } | ||
155 | |||
156 | pub(crate) fn unnamed_trait_vis(&self, tr: TraitId) -> Option<Visibility> { | ||
157 | self.unnamed_trait_imports.get(&tr).copied() | ||
158 | } | ||
159 | |||
160 | pub(crate) fn push_unnamed_trait(&mut self, tr: TraitId, vis: Visibility) { | ||
161 | self.unnamed_trait_imports.insert(tr, vis); | ||
162 | } | ||
163 | |||
164 | pub(crate) fn push_res(&mut self, name: Name, def: PerNs) -> bool { | ||
165 | let mut changed = false; | ||
166 | |||
167 | if let Some(types) = def.types { | ||
168 | self.types.entry(name.clone()).or_insert_with(|| { | ||
169 | changed = true; | ||
170 | types | ||
171 | }); | ||
172 | } | ||
173 | if let Some(values) = def.values { | ||
174 | self.values.entry(name.clone()).or_insert_with(|| { | ||
175 | changed = true; | ||
176 | values | ||
177 | }); | ||
178 | } | ||
179 | if let Some(macros) = def.macros { | ||
180 | self.macros.entry(name.clone()).or_insert_with(|| { | ||
181 | changed = true; | ||
182 | macros | ||
183 | }); | ||
184 | } | ||
185 | |||
186 | if def.is_none() { | ||
187 | if self.unresolved.insert(name) { | ||
188 | changed = true; | ||
189 | } | ||
190 | } | ||
191 | |||
192 | changed | ||
193 | } | ||
194 | |||
195 | pub(crate) fn push_res_with_import( | ||
196 | &mut self, | ||
197 | glob_imports: &mut PerNsGlobImports, | ||
198 | lookup: (LocalModuleId, Name), | ||
199 | def: PerNs, | ||
200 | def_import_type: ImportType, | ||
201 | ) -> bool { | ||
202 | let mut changed = false; | ||
203 | |||
204 | macro_rules! check_changed { | ||
205 | ( | ||
206 | $changed:ident, | ||
207 | ( $this:ident / $def:ident ) . $field:ident, | ||
208 | $glob_imports:ident [ $lookup:ident ], | ||
209 | $def_import_type:ident | ||
210 | ) => {{ | ||
211 | let existing = $this.$field.entry($lookup.1.clone()); | ||
212 | match (existing, $def.$field) { | ||
213 | (Entry::Vacant(entry), Some(_)) => { | ||
214 | match $def_import_type { | ||
215 | ImportType::Glob => { | ||
216 | $glob_imports.$field.insert($lookup.clone()); | ||
217 | } | ||
218 | ImportType::Named => { | ||
219 | $glob_imports.$field.remove(&$lookup); | ||
220 | } | ||
221 | } | ||
222 | |||
223 | if let Some(fld) = $def.$field { | ||
224 | entry.insert(fld); | ||
225 | } | ||
226 | $changed = true; | ||
227 | } | ||
228 | (Entry::Occupied(mut entry), Some(_)) | ||
229 | if $glob_imports.$field.contains(&$lookup) | ||
230 | && matches!($def_import_type, ImportType::Named) => | ||
231 | { | ||
232 | mark::hit!(import_shadowed); | ||
233 | $glob_imports.$field.remove(&$lookup); | ||
234 | if let Some(fld) = $def.$field { | ||
235 | entry.insert(fld); | ||
236 | } | ||
237 | $changed = true; | ||
238 | } | ||
239 | _ => {} | ||
240 | } | ||
241 | }}; | ||
242 | } | ||
243 | |||
244 | check_changed!(changed, (self / def).types, glob_imports[lookup], def_import_type); | ||
245 | check_changed!(changed, (self / def).values, glob_imports[lookup], def_import_type); | ||
246 | check_changed!(changed, (self / def).macros, glob_imports[lookup], def_import_type); | ||
247 | |||
248 | if def.is_none() { | ||
249 | if self.unresolved.insert(lookup.1) { | ||
250 | changed = true; | ||
251 | } | ||
252 | } | ||
253 | |||
254 | changed | ||
255 | } | ||
256 | |||
257 | pub(crate) fn resolutions<'a>(&'a self) -> impl Iterator<Item = (Option<Name>, PerNs)> + 'a { | ||
258 | self.entries().map(|(name, res)| (Some(name.clone()), res)).chain( | ||
259 | self.unnamed_trait_imports | ||
260 | .iter() | ||
261 | .map(|(tr, vis)| (None, PerNs::types(ModuleDefId::TraitId(*tr), *vis))), | ||
262 | ) | ||
263 | } | ||
264 | |||
265 | pub(crate) fn collect_legacy_macros(&self) -> FxHashMap<Name, MacroDefId> { | ||
266 | self.legacy_macros.clone() | ||
267 | } | ||
268 | } | ||
269 | |||
270 | impl PerNs { | ||
271 | pub(crate) fn from_def(def: ModuleDefId, v: Visibility, has_constructor: bool) -> PerNs { | ||
272 | match def { | ||
273 | ModuleDefId::ModuleId(_) => PerNs::types(def, v), | ||
274 | ModuleDefId::FunctionId(_) => PerNs::values(def, v), | ||
275 | ModuleDefId::AdtId(adt) => match adt { | ||
276 | AdtId::UnionId(_) => PerNs::types(def, v), | ||
277 | AdtId::EnumId(_) => PerNs::types(def, v), | ||
278 | AdtId::StructId(_) => { | ||
279 | if has_constructor { | ||
280 | PerNs::both(def, def, v) | ||
281 | } else { | ||
282 | PerNs::types(def, v) | ||
283 | } | ||
284 | } | ||
285 | }, | ||
286 | ModuleDefId::EnumVariantId(_) => PerNs::both(def, def, v), | ||
287 | ModuleDefId::ConstId(_) | ModuleDefId::StaticId(_) => PerNs::values(def, v), | ||
288 | ModuleDefId::TraitId(_) => PerNs::types(def, v), | ||
289 | ModuleDefId::TypeAliasId(_) => PerNs::types(def, v), | ||
290 | ModuleDefId::BuiltinType(_) => PerNs::types(def, v), | ||
291 | } | ||
292 | } | ||
293 | } | ||
294 | |||
295 | #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] | ||
296 | pub enum ItemInNs { | ||
297 | Types(ModuleDefId), | ||
298 | Values(ModuleDefId), | ||
299 | Macros(MacroDefId), | ||
300 | } | ||
301 | |||
302 | impl ItemInNs { | ||
303 | fn match_with(self, per_ns: PerNs) -> Option<Visibility> { | ||
304 | match self { | ||
305 | ItemInNs::Types(def) => { | ||
306 | per_ns.types.filter(|(other_def, _)| *other_def == def).map(|(_, vis)| vis) | ||
307 | } | ||
308 | ItemInNs::Values(def) => { | ||
309 | per_ns.values.filter(|(other_def, _)| *other_def == def).map(|(_, vis)| vis) | ||
310 | } | ||
311 | ItemInNs::Macros(def) => { | ||
312 | per_ns.macros.filter(|(other_def, _)| *other_def == def).map(|(_, vis)| vis) | ||
313 | } | ||
314 | } | ||
315 | } | ||
316 | |||
317 | pub fn as_module_def_id(self) -> Option<ModuleDefId> { | ||
318 | match self { | ||
319 | ItemInNs::Types(id) | ItemInNs::Values(id) => Some(id), | ||
320 | ItemInNs::Macros(_) => None, | ||
321 | } | ||
322 | } | ||
323 | |||
324 | /// Returns the crate defining this item (or `None` if `self` is built-in). | ||
325 | pub fn krate(&self, db: &dyn DefDatabase) -> Option<CrateId> { | ||
326 | Some(match self { | ||
327 | ItemInNs::Types(did) | ItemInNs::Values(did) => match did { | ||
328 | ModuleDefId::ModuleId(id) => id.krate, | ||
329 | ModuleDefId::FunctionId(id) => id.lookup(db).module(db).krate, | ||
330 | ModuleDefId::AdtId(id) => id.module(db).krate, | ||
331 | ModuleDefId::EnumVariantId(id) => id.parent.lookup(db).container.module(db).krate, | ||
332 | ModuleDefId::ConstId(id) => id.lookup(db).container.module(db).krate, | ||
333 | ModuleDefId::StaticId(id) => id.lookup(db).container.module(db).krate, | ||
334 | ModuleDefId::TraitId(id) => id.lookup(db).container.module(db).krate, | ||
335 | ModuleDefId::TypeAliasId(id) => id.lookup(db).module(db).krate, | ||
336 | ModuleDefId::BuiltinType(_) => return None, | ||
337 | }, | ||
338 | ItemInNs::Macros(id) => return id.krate, | ||
339 | }) | ||
340 | } | ||
341 | } | ||