From 2f24e740db3365afac56aad3e8a533340369ef7d Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 10 Feb 2019 16:19:50 +0100 Subject: Implement glob imports within the same crate Fixes #231. --- crates/ra_hir/src/nameres.rs | 93 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 77 insertions(+), 16 deletions(-) (limited to 'crates/ra_hir') diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs index 2442e754e..94f7db024 100644 --- a/crates/ra_hir/src/nameres.rs +++ b/crates/ra_hir/src/nameres.rs @@ -61,7 +61,7 @@ impl ModuleScope { /// `Resolution` is basically `DefId` atm, but it should account for stuff like /// multiple namespaces, ambiguity and errors. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Default)] pub struct Resolution { /// None for unresolved pub def: PerNs, @@ -154,6 +154,8 @@ struct Resolver<'a, DB> { krate: Crate, module_tree: Arc, processed_imports: FxHashSet<(ModuleId, ImportId)>, + /// If module `a` has `use b::*`, then this contains the mapping b -> a (and the import) + glob_imports: FxHashMap>, result: ItemMap, } @@ -173,6 +175,7 @@ where krate, module_tree, processed_imports: FxHashSet::default(), + glob_imports: FxHashMap::default(), result: ItemMap::default(), } } @@ -281,12 +284,28 @@ where // glob import from other crate => we can just import everything once let item_map = self.db.item_map(m.krate); let scope = &item_map[m.module_id]; - self.update(module_id, |items| { - // TODO: handle shadowing and visibility - items.items.extend( - scope.items.iter().map(|(name, res)| (name.clone(), res.clone())), - ); - }); + let items = scope + .items + .iter() + .map(|(name, res)| (name.clone(), res.clone())) + .collect::>(); + self.update(module_id, Some(import_id), &items); + } else { + // glob import from same crate => we do an initial + // import, and then need to propagate any further + // additions + let scope = &self.result[m.module_id]; + let items = scope + .items + .iter() + .map(|(name, res)| (name.clone(), res.clone())) + .collect::>(); + self.update(module_id, Some(import_id), &items); + // record the glob import in case we add further items + self.glob_imports + .entry(m.module_id) + .or_default() + .push((module_id, import_id)); } } Some(ModuleDef::Enum(e)) => { @@ -304,9 +323,7 @@ where Some((name, res)) }) .collect::>(); - self.update(module_id, |items| { - items.items.extend(resolutions); - }); + self.update(module_id, Some(import_id), &resolutions); } Some(d) => { log::debug!("glob import {:?} from non-module/enum {:?}", import, d); @@ -328,17 +345,61 @@ where } } } - self.update(module_id, |items| { - let res = Resolution { def, import: Some(import_id) }; - items.items.insert(name, res); - }); + let resolution = Resolution { def, import: Some(import_id) }; + self.update(module_id, None, &[(name, resolution)]); } reached_fixedpoint } - fn update(&mut self, module_id: ModuleId, f: impl FnOnce(&mut ModuleScope)) { + fn update( + &mut self, + module_id: ModuleId, + import: Option, + resolutions: &[(Name, Resolution)], + ) { + self.update_recursive(module_id, import, resolutions, 0) + } + + fn update_recursive( + &mut self, + module_id: ModuleId, + import: Option, + resolutions: &[(Name, Resolution)], + depth: usize, + ) { + if depth > 100 { + // prevent stack overflows (but this shouldn't be possible) + panic!("infinite recursion in glob imports!"); + } let module_items = self.result.per_module.get_mut(module_id).unwrap(); - f(module_items) + let mut changed = false; + for (name, res) in resolutions { + let existing = module_items.items.entry(name.clone()).or_default(); + if existing.def.types.is_none() && res.def.types.is_some() { + existing.def.types = res.def.types; + existing.import = import.or(res.import); + changed = true; + } + if existing.def.values.is_none() && res.def.values.is_some() { + existing.def.values = res.def.values; + existing.import = import.or(res.import); + changed = true; + } + } + if !changed { + return; + } + let glob_imports = self + .glob_imports + .get(&module_id) + .into_iter() + .flat_map(|v| v.iter()) + .cloned() + .collect::>(); + for (glob_importing_module, glob_import) in glob_imports { + // We pass the glob import so that the tracked import in those modules is that glob import + self.update_recursive(glob_importing_module, Some(glob_import), resolutions, depth + 1); + } } } -- cgit v1.2.3