aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_def/src/item_scope.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/hir_def/src/item_scope.rs')
-rw-r--r--crates/hir_def/src/item_scope.rs341
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
4use std::collections::hash_map::Entry;
5
6use base_db::CrateId;
7use hir_expand::name::Name;
8use once_cell::sync::Lazy;
9use rustc_hash::{FxHashMap, FxHashSet};
10use test_utils::mark;
11
12use 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)]
18pub(crate) enum ImportType {
19 Glob,
20 Named,
21}
22
23#[derive(Debug, Default)]
24pub 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)]
31pub 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
56pub(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)]
65pub(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.
74impl 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
270impl 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)]
296pub enum ItemInNs {
297 Types(ModuleDefId),
298 Values(ModuleDefId),
299 Macros(MacroDefId),
300}
301
302impl 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}