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