diff options
Diffstat (limited to 'crates/ra_hir_def/src/item_scope.rs')
-rw-r--r-- | crates/ra_hir_def/src/item_scope.rs | 174 |
1 files changed, 146 insertions, 28 deletions
diff --git a/crates/ra_hir_def/src/item_scope.rs b/crates/ra_hir_def/src/item_scope.rs index fc15948ad..beeb98559 100644 --- a/crates/ra_hir_def/src/item_scope.rs +++ b/crates/ra_hir_def/src/item_scope.rs | |||
@@ -1,18 +1,39 @@ | |||
1 | //! Describes items defined or visible (ie, imported) in a certain scope. | 1 | //! Describes items defined or visible (ie, imported) in a certain scope. |
2 | //! This is shared between modules and blocks. | 2 | //! This is shared between modules and blocks. |
3 | 3 | ||
4 | use std::collections::hash_map::Entry; | ||
5 | |||
4 | use hir_expand::name::Name; | 6 | use hir_expand::name::Name; |
5 | use once_cell::sync::Lazy; | 7 | use once_cell::sync::Lazy; |
6 | use rustc_hash::FxHashMap; | 8 | use ra_db::CrateId; |
9 | use rustc_hash::{FxHashMap, FxHashSet}; | ||
10 | use test_utils::mark; | ||
7 | 11 | ||
8 | use crate::{ | 12 | use crate::{ |
9 | per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, ImplId, MacroDefId, ModuleDefId, | 13 | db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, HasModule, ImplId, |
10 | TraitId, | 14 | LocalModuleId, Lookup, MacroDefId, ModuleDefId, TraitId, |
11 | }; | 15 | }; |
12 | 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 | |||
13 | #[derive(Debug, Default, PartialEq, Eq)] | 30 | #[derive(Debug, Default, PartialEq, Eq)] |
14 | pub struct ItemScope { | 31 | pub struct ItemScope { |
15 | visible: FxHashMap<Name, PerNs>, | 32 | types: FxHashMap<Name, (ModuleDefId, Visibility)>, |
33 | values: FxHashMap<Name, (ModuleDefId, Visibility)>, | ||
34 | macros: FxHashMap<Name, (MacroDefId, Visibility)>, | ||
35 | unresolved: FxHashSet<Name>, | ||
36 | |||
16 | defs: Vec<ModuleDefId>, | 37 | defs: Vec<ModuleDefId>, |
17 | impls: Vec<ImplId>, | 38 | impls: Vec<ImplId>, |
18 | /// Macros visible in current module in legacy textual scope | 39 | /// Macros visible in current module in legacy textual scope |
@@ -50,14 +71,16 @@ pub(crate) enum BuiltinShadowMode { | |||
50 | /// Other methods will only resolve values, types and module scoped macros only. | 71 | /// Other methods will only resolve values, types and module scoped macros only. |
51 | impl ItemScope { | 72 | impl ItemScope { |
52 | pub fn entries<'a>(&'a self) -> impl Iterator<Item = (&'a Name, PerNs)> + 'a { | 73 | pub fn entries<'a>(&'a self) -> impl Iterator<Item = (&'a Name, PerNs)> + 'a { |
53 | //FIXME: shadowing | 74 | // FIXME: shadowing |
54 | self.visible.iter().map(|(n, def)| (n, *def)) | 75 | let keys: FxHashSet<_> = self |
55 | } | 76 | .types |
77 | .keys() | ||
78 | .chain(self.values.keys()) | ||
79 | .chain(self.macros.keys()) | ||
80 | .chain(self.unresolved.iter()) | ||
81 | .collect(); | ||
56 | 82 | ||
57 | pub fn entries_without_primitives<'a>( | 83 | keys.into_iter().map(move |name| (name, self.get(name))) |
58 | &'a self, | ||
59 | ) -> impl Iterator<Item = (&'a Name, PerNs)> + 'a { | ||
60 | self.visible.iter().map(|(n, def)| (n, *def)) | ||
61 | } | 84 | } |
62 | 85 | ||
63 | pub fn declarations(&self) -> impl Iterator<Item = ModuleDefId> + '_ { | 86 | pub fn declarations(&self) -> impl Iterator<Item = ModuleDefId> + '_ { |
@@ -76,7 +99,7 @@ impl ItemScope { | |||
76 | 99 | ||
77 | /// Iterate over all module scoped macros | 100 | /// Iterate over all module scoped macros |
78 | pub(crate) fn macros<'a>(&'a self) -> impl Iterator<Item = (&'a Name, MacroDefId)> + 'a { | 101 | pub(crate) fn macros<'a>(&'a self) -> impl Iterator<Item = (&'a Name, MacroDefId)> + 'a { |
79 | self.visible.iter().filter_map(|(name, def)| def.take_macros().map(|macro_| (name, macro_))) | 102 | self.entries().filter_map(|(name, def)| def.take_macros().map(|macro_| (name, macro_))) |
80 | } | 103 | } |
81 | 104 | ||
82 | /// Iterate over all legacy textual scoped macros visible at the end of the module | 105 | /// Iterate over all legacy textual scoped macros visible at the end of the module |
@@ -86,12 +109,16 @@ impl ItemScope { | |||
86 | 109 | ||
87 | /// Get a name from current module scope, legacy macros are not included | 110 | /// Get a name from current module scope, legacy macros are not included |
88 | pub(crate) fn get(&self, name: &Name) -> PerNs { | 111 | pub(crate) fn get(&self, name: &Name) -> PerNs { |
89 | self.visible.get(name).copied().unwrap_or_else(PerNs::none) | 112 | PerNs { |
113 | types: self.types.get(name).copied(), | ||
114 | values: self.values.get(name).copied(), | ||
115 | macros: self.macros.get(name).copied(), | ||
116 | } | ||
90 | } | 117 | } |
91 | 118 | ||
92 | pub(crate) fn name_of(&self, item: ItemInNs) -> Option<(&Name, Visibility)> { | 119 | pub(crate) fn name_of(&self, item: ItemInNs) -> Option<(&Name, Visibility)> { |
93 | for (name, per_ns) in &self.visible { | 120 | for (name, per_ns) in self.entries() { |
94 | if let Some(vis) = item.match_with(*per_ns) { | 121 | if let Some(vis) = item.match_with(per_ns) { |
95 | return Some((name, vis)); | 122 | return Some((name, vis)); |
96 | } | 123 | } |
97 | } | 124 | } |
@@ -99,8 +126,8 @@ impl ItemScope { | |||
99 | } | 126 | } |
100 | 127 | ||
101 | pub(crate) fn traits<'a>(&'a self) -> impl Iterator<Item = TraitId> + 'a { | 128 | pub(crate) fn traits<'a>(&'a self) -> impl Iterator<Item = TraitId> + 'a { |
102 | self.visible.values().filter_map(|def| match def.take_types() { | 129 | self.types.values().filter_map(|(def, _)| match def { |
103 | Some(ModuleDefId::TraitId(t)) => Some(t), | 130 | ModuleDefId::TraitId(t) => Some(*t), |
104 | _ => None, | 131 | _ => None, |
105 | }) | 132 | }) |
106 | } | 133 | } |
@@ -123,26 +150,99 @@ impl ItemScope { | |||
123 | 150 | ||
124 | pub(crate) fn push_res(&mut self, name: Name, def: PerNs) -> bool { | 151 | pub(crate) fn push_res(&mut self, name: Name, def: PerNs) -> bool { |
125 | let mut changed = false; | 152 | let mut changed = false; |
126 | let existing = self.visible.entry(name).or_default(); | ||
127 | 153 | ||
128 | if existing.types.is_none() && def.types.is_some() { | 154 | if let Some(types) = def.types { |
129 | existing.types = def.types; | 155 | self.types.entry(name.clone()).or_insert_with(|| { |
130 | changed = true; | 156 | changed = true; |
157 | types | ||
158 | }); | ||
131 | } | 159 | } |
132 | if existing.values.is_none() && def.values.is_some() { | 160 | if let Some(values) = def.values { |
133 | existing.values = def.values; | 161 | self.values.entry(name.clone()).or_insert_with(|| { |
134 | changed = true; | 162 | changed = true; |
163 | values | ||
164 | }); | ||
165 | } | ||
166 | if let Some(macros) = def.macros { | ||
167 | self.macros.entry(name.clone()).or_insert_with(|| { | ||
168 | changed = true; | ||
169 | macros | ||
170 | }); | ||
171 | } | ||
172 | |||
173 | if def.is_none() { | ||
174 | if self.unresolved.insert(name) { | ||
175 | changed = true; | ||
176 | } | ||
177 | } | ||
178 | |||
179 | changed | ||
180 | } | ||
181 | |||
182 | pub(crate) fn push_res_with_import( | ||
183 | &mut self, | ||
184 | glob_imports: &mut PerNsGlobImports, | ||
185 | lookup: (LocalModuleId, Name), | ||
186 | def: PerNs, | ||
187 | def_import_type: ImportType, | ||
188 | ) -> bool { | ||
189 | let mut changed = false; | ||
190 | |||
191 | macro_rules! check_changed { | ||
192 | ( | ||
193 | $changed:ident, | ||
194 | ( $this:ident / $def:ident ) . $field:ident, | ||
195 | $glob_imports:ident [ $lookup:ident ], | ||
196 | $def_import_type:ident | ||
197 | ) => {{ | ||
198 | let existing = $this.$field.entry($lookup.1.clone()); | ||
199 | match (existing, $def.$field) { | ||
200 | (Entry::Vacant(entry), Some(_)) => { | ||
201 | match $def_import_type { | ||
202 | ImportType::Glob => { | ||
203 | $glob_imports.$field.insert($lookup.clone()); | ||
204 | } | ||
205 | ImportType::Named => { | ||
206 | $glob_imports.$field.remove(&$lookup); | ||
207 | } | ||
208 | } | ||
209 | |||
210 | if let Some(fld) = $def.$field { | ||
211 | entry.insert(fld); | ||
212 | } | ||
213 | $changed = true; | ||
214 | } | ||
215 | (Entry::Occupied(mut entry), Some(_)) | ||
216 | if $glob_imports.$field.contains(&$lookup) | ||
217 | && matches!($def_import_type, ImportType::Named) => | ||
218 | { | ||
219 | mark::hit!(import_shadowed); | ||
220 | $glob_imports.$field.remove(&$lookup); | ||
221 | if let Some(fld) = $def.$field { | ||
222 | entry.insert(fld); | ||
223 | } | ||
224 | $changed = true; | ||
225 | } | ||
226 | _ => {} | ||
227 | } | ||
228 | }}; | ||
135 | } | 229 | } |
136 | if existing.macros.is_none() && def.macros.is_some() { | 230 | |
137 | existing.macros = def.macros; | 231 | check_changed!(changed, (self / def).types, glob_imports[lookup], def_import_type); |
138 | changed = true; | 232 | check_changed!(changed, (self / def).values, glob_imports[lookup], def_import_type); |
233 | check_changed!(changed, (self / def).macros, glob_imports[lookup], def_import_type); | ||
234 | |||
235 | if def.is_none() { | ||
236 | if self.unresolved.insert(lookup.1) { | ||
237 | changed = true; | ||
238 | } | ||
139 | } | 239 | } |
140 | 240 | ||
141 | changed | 241 | changed |
142 | } | 242 | } |
143 | 243 | ||
144 | pub(crate) fn resolutions<'a>(&'a self) -> impl Iterator<Item = (Name, PerNs)> + 'a { | 244 | pub(crate) fn resolutions<'a>(&'a self) -> impl Iterator<Item = (Name, PerNs)> + 'a { |
145 | self.visible.iter().map(|(name, res)| (name.clone(), *res)) | 245 | self.entries().map(|(name, res)| (name.clone(), res)) |
146 | } | 246 | } |
147 | 247 | ||
148 | pub(crate) fn collect_legacy_macros(&self) -> FxHashMap<Name, MacroDefId> { | 248 | pub(crate) fn collect_legacy_macros(&self) -> FxHashMap<Name, MacroDefId> { |
@@ -203,4 +303,22 @@ impl ItemInNs { | |||
203 | ItemInNs::Macros(_) => None, | 303 | ItemInNs::Macros(_) => None, |
204 | } | 304 | } |
205 | } | 305 | } |
306 | |||
307 | /// Returns the crate defining this item (or `None` if `self` is built-in). | ||
308 | pub fn krate(&self, db: &dyn DefDatabase) -> Option<CrateId> { | ||
309 | Some(match self { | ||
310 | ItemInNs::Types(did) | ItemInNs::Values(did) => match did { | ||
311 | ModuleDefId::ModuleId(id) => id.krate, | ||
312 | ModuleDefId::FunctionId(id) => id.lookup(db).module(db).krate, | ||
313 | ModuleDefId::AdtId(id) => id.module(db).krate, | ||
314 | ModuleDefId::EnumVariantId(id) => id.parent.lookup(db).container.module(db).krate, | ||
315 | ModuleDefId::ConstId(id) => id.lookup(db).container.module(db).krate, | ||
316 | ModuleDefId::StaticId(id) => id.lookup(db).container.module(db).krate, | ||
317 | ModuleDefId::TraitId(id) => id.lookup(db).container.module(db).krate, | ||
318 | ModuleDefId::TypeAliasId(id) => id.lookup(db).module(db).krate, | ||
319 | ModuleDefId::BuiltinType(_) => return None, | ||
320 | }, | ||
321 | ItemInNs::Macros(id) => return id.krate, | ||
322 | }) | ||
323 | } | ||
206 | } | 324 | } |