From 2f24e740db3365afac56aad3e8a533340369ef7d Mon Sep 17 00:00:00 2001
From: Florian Diebold <flodiebold@gmail.com>
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/src')

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<ModuleDef>,
@@ -154,6 +154,8 @@ struct Resolver<'a, DB> {
     krate: Crate,
     module_tree: Arc<ModuleTree>,
     processed_imports: FxHashSet<(ModuleId, ImportId)>,
+    /// If module `a` has `use b::*`, then this contains the mapping b -> a (and the import)
+    glob_imports: FxHashMap<ModuleId, Vec<(ModuleId, ImportId)>>,
     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::<Vec<_>>();
+                        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::<Vec<_>>();
+                        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::<Vec<_>>();
-                    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<ImportId>,
+        resolutions: &[(Name, Resolution)],
+    ) {
+        self.update_recursive(module_id, import, resolutions, 0)
+    }
+
+    fn update_recursive(
+        &mut self,
+        module_id: ModuleId,
+        import: Option<ImportId>,
+        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::<Vec<_>>();
+        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