diff options
Diffstat (limited to 'crates/ra_hir/src/nameres.rs')
-rw-r--r-- | crates/ra_hir/src/nameres.rs | 127 |
1 files changed, 116 insertions, 11 deletions
diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs index b7382d9c3..94f7db024 100644 --- a/crates/ra_hir/src/nameres.rs +++ b/crates/ra_hir/src/nameres.rs | |||
@@ -61,7 +61,7 @@ impl ModuleScope { | |||
61 | 61 | ||
62 | /// `Resolution` is basically `DefId` atm, but it should account for stuff like | 62 | /// `Resolution` is basically `DefId` atm, but it should account for stuff like |
63 | /// multiple namespaces, ambiguity and errors. | 63 | /// multiple namespaces, ambiguity and errors. |
64 | #[derive(Debug, Clone, PartialEq, Eq)] | 64 | #[derive(Debug, Clone, PartialEq, Eq, Default)] |
65 | pub struct Resolution { | 65 | pub struct Resolution { |
66 | /// None for unresolved | 66 | /// None for unresolved |
67 | pub def: PerNs<ModuleDef>, | 67 | pub def: PerNs<ModuleDef>, |
@@ -154,6 +154,8 @@ struct Resolver<'a, DB> { | |||
154 | krate: Crate, | 154 | krate: Crate, |
155 | module_tree: Arc<ModuleTree>, | 155 | module_tree: Arc<ModuleTree>, |
156 | processed_imports: FxHashSet<(ModuleId, ImportId)>, | 156 | processed_imports: FxHashSet<(ModuleId, ImportId)>, |
157 | /// If module `a` has `use b::*`, then this contains the mapping b -> a (and the import) | ||
158 | glob_imports: FxHashMap<ModuleId, Vec<(ModuleId, ImportId)>>, | ||
157 | result: ItemMap, | 159 | result: ItemMap, |
158 | } | 160 | } |
159 | 161 | ||
@@ -173,6 +175,7 @@ where | |||
173 | krate, | 175 | krate, |
174 | module_tree, | 176 | module_tree, |
175 | processed_imports: FxHashSet::default(), | 177 | processed_imports: FxHashSet::default(), |
178 | glob_imports: FxHashMap::default(), | ||
176 | result: ItemMap::default(), | 179 | result: ItemMap::default(), |
177 | } | 180 | } |
178 | } | 181 | } |
@@ -264,14 +267,72 @@ where | |||
264 | import: &ImportData, | 267 | import: &ImportData, |
265 | ) -> ReachedFixedPoint { | 268 | ) -> ReachedFixedPoint { |
266 | log::debug!("resolving import: {:?}", import); | 269 | log::debug!("resolving import: {:?}", import); |
267 | if import.is_glob { | ||
268 | return ReachedFixedPoint::Yes; | ||
269 | }; | ||
270 | let original_module = Module { krate: self.krate, module_id }; | 270 | let original_module = Module { krate: self.krate, module_id }; |
271 | let (def, reached_fixedpoint) = | 271 | let (def, reached_fixedpoint) = |
272 | self.result.resolve_path_fp(self.db, original_module, &import.path); | 272 | self.result.resolve_path_fp(self.db, original_module, &import.path); |
273 | 273 | ||
274 | if reached_fixedpoint == ReachedFixedPoint::Yes { | 274 | if reached_fixedpoint != ReachedFixedPoint::Yes { |
275 | return reached_fixedpoint; | ||
276 | } | ||
277 | |||
278 | if import.is_glob { | ||
279 | log::debug!("glob import: {:?}", import); | ||
280 | match def.take_types() { | ||
281 | Some(ModuleDef::Module(m)) => { | ||
282 | if m.krate != self.krate { | ||
283 | tested_by!(glob_across_crates); | ||
284 | // glob import from other crate => we can just import everything once | ||
285 | let item_map = self.db.item_map(m.krate); | ||
286 | let scope = &item_map[m.module_id]; | ||
287 | let items = scope | ||
288 | .items | ||
289 | .iter() | ||
290 | .map(|(name, res)| (name.clone(), res.clone())) | ||
291 | .collect::<Vec<_>>(); | ||
292 | self.update(module_id, Some(import_id), &items); | ||
293 | } else { | ||
294 | // glob import from same crate => we do an initial | ||
295 | // import, and then need to propagate any further | ||
296 | // additions | ||
297 | let scope = &self.result[m.module_id]; | ||
298 | let items = scope | ||
299 | .items | ||
300 | .iter() | ||
301 | .map(|(name, res)| (name.clone(), res.clone())) | ||
302 | .collect::<Vec<_>>(); | ||
303 | self.update(module_id, Some(import_id), &items); | ||
304 | // record the glob import in case we add further items | ||
305 | self.glob_imports | ||
306 | .entry(m.module_id) | ||
307 | .or_default() | ||
308 | .push((module_id, import_id)); | ||
309 | } | ||
310 | } | ||
311 | Some(ModuleDef::Enum(e)) => { | ||
312 | tested_by!(glob_enum); | ||
313 | // glob import from enum => just import all the variants | ||
314 | let variants = e.variants(self.db); | ||
315 | let resolutions = variants | ||
316 | .into_iter() | ||
317 | .filter_map(|variant| { | ||
318 | let res = Resolution { | ||
319 | def: PerNs::both(variant.into(), e.into()), | ||
320 | import: Some(import_id), | ||
321 | }; | ||
322 | let name = variant.name(self.db)?; | ||
323 | Some((name, res)) | ||
324 | }) | ||
325 | .collect::<Vec<_>>(); | ||
326 | self.update(module_id, Some(import_id), &resolutions); | ||
327 | } | ||
328 | Some(d) => { | ||
329 | log::debug!("glob import {:?} from non-module/enum {:?}", import, d); | ||
330 | } | ||
331 | None => { | ||
332 | log::debug!("glob import {:?} didn't resolve as type", import); | ||
333 | } | ||
334 | } | ||
335 | } else { | ||
275 | let last_segment = import.path.segments.last().unwrap(); | 336 | let last_segment = import.path.segments.last().unwrap(); |
276 | let name = import.alias.clone().unwrap_or_else(|| last_segment.name.clone()); | 337 | let name = import.alias.clone().unwrap_or_else(|| last_segment.name.clone()); |
277 | log::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def); | 338 | log::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def); |
@@ -284,17 +345,61 @@ where | |||
284 | } | 345 | } |
285 | } | 346 | } |
286 | } | 347 | } |
287 | self.update(module_id, |items| { | 348 | let resolution = Resolution { def, import: Some(import_id) }; |
288 | let res = Resolution { def, import: Some(import_id) }; | 349 | self.update(module_id, None, &[(name, resolution)]); |
289 | items.items.insert(name, res); | ||
290 | }); | ||
291 | } | 350 | } |
292 | reached_fixedpoint | 351 | reached_fixedpoint |
293 | } | 352 | } |
294 | 353 | ||
295 | fn update(&mut self, module_id: ModuleId, f: impl FnOnce(&mut ModuleScope)) { | 354 | fn update( |
355 | &mut self, | ||
356 | module_id: ModuleId, | ||
357 | import: Option<ImportId>, | ||
358 | resolutions: &[(Name, Resolution)], | ||
359 | ) { | ||
360 | self.update_recursive(module_id, import, resolutions, 0) | ||
361 | } | ||
362 | |||
363 | fn update_recursive( | ||
364 | &mut self, | ||
365 | module_id: ModuleId, | ||
366 | import: Option<ImportId>, | ||
367 | resolutions: &[(Name, Resolution)], | ||
368 | depth: usize, | ||
369 | ) { | ||
370 | if depth > 100 { | ||
371 | // prevent stack overflows (but this shouldn't be possible) | ||
372 | panic!("infinite recursion in glob imports!"); | ||
373 | } | ||
296 | let module_items = self.result.per_module.get_mut(module_id).unwrap(); | 374 | let module_items = self.result.per_module.get_mut(module_id).unwrap(); |
297 | f(module_items) | 375 | let mut changed = false; |
376 | for (name, res) in resolutions { | ||
377 | let existing = module_items.items.entry(name.clone()).or_default(); | ||
378 | if existing.def.types.is_none() && res.def.types.is_some() { | ||
379 | existing.def.types = res.def.types; | ||
380 | existing.import = import.or(res.import); | ||
381 | changed = true; | ||
382 | } | ||
383 | if existing.def.values.is_none() && res.def.values.is_some() { | ||
384 | existing.def.values = res.def.values; | ||
385 | existing.import = import.or(res.import); | ||
386 | changed = true; | ||
387 | } | ||
388 | } | ||
389 | if !changed { | ||
390 | return; | ||
391 | } | ||
392 | let glob_imports = self | ||
393 | .glob_imports | ||
394 | .get(&module_id) | ||
395 | .into_iter() | ||
396 | .flat_map(|v| v.iter()) | ||
397 | .cloned() | ||
398 | .collect::<Vec<_>>(); | ||
399 | for (glob_importing_module, glob_import) in glob_imports { | ||
400 | // We pass the glob import so that the tracked import in those modules is that glob import | ||
401 | self.update_recursive(glob_importing_module, Some(glob_import), resolutions, depth + 1); | ||
402 | } | ||
298 | } | 403 | } |
299 | } | 404 | } |
300 | 405 | ||