diff --git a/crates/hir_def/src/nameres.rs b/crates/hir_def/src/nameres.rs index 34ff07f3c4d..f92232eb32d 100644 --- a/crates/hir_def/src/nameres.rs +++ b/crates/hir_def/src/nameres.rs @@ -337,6 +337,12 @@ pub fn with_ancestor_maps( None } + /// If this `DefMap` is for a block expression, returns the module containing the block (which + /// might again be a block, or a module inside a block). + pub fn parent(&self) -> Option { + Some(self.block?.parent) + } + // FIXME: this can use some more human-readable format (ideally, an IR // even), as this should be a great debugging aid. pub fn dump(&self, db: &dyn DefDatabase) -> String { diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs index 6bd41bc0821..9996a080721 100644 --- a/crates/hir_def/src/nameres/collector.rs +++ b/crates/hir_def/src/nameres/collector.rs @@ -608,7 +608,7 @@ fn record_resolved_import(&mut self, directive: &ImportDirective) { ( n, res.filter_visibility(|v| { - v.is_visible_from_def_map(&self.def_map, module_id) + v.is_visible_from_def_map(self.db, &self.def_map, module_id) }), ) }) @@ -761,7 +761,7 @@ fn update_recursive( .filter(|(glob_importing_module, _)| { // we know all resolutions have the same visibility (`vis`), so we // just need to check that once - vis.is_visible_from_def_map(&self.def_map, *glob_importing_module) + vis.is_visible_from_def_map(self.db, &self.def_map, *glob_importing_module) }) .cloned() .collect::>(); diff --git a/crates/hir_def/src/visibility.rs b/crates/hir_def/src/visibility.rs index 38da3132bf7..0e39519100f 100644 --- a/crates/hir_def/src/visibility.rs +++ b/crates/hir_def/src/visibility.rs @@ -103,7 +103,7 @@ pub fn is_visible_from(self, db: &dyn DefDatabase, from_module: ModuleId) -> boo return false; } let def_map = from_module.def_map(db); - self.is_visible_from_def_map(&def_map, from_module.local_id) + self.is_visible_from_def_map(db, &def_map, from_module.local_id) } pub(crate) fn is_visible_from_other_crate(self) -> bool { @@ -115,19 +115,41 @@ pub(crate) fn is_visible_from_other_crate(self) -> bool { pub(crate) fn is_visible_from_def_map( self, + db: &dyn DefDatabase, def_map: &DefMap, - from_module: crate::LocalModuleId, + mut from_module: crate::LocalModuleId, ) -> bool { let to_module = match self { Visibility::Module(m) => m, Visibility::Public => return true, }; + // from_module needs to be a descendant of to_module - let mut ancestors = std::iter::successors(Some(from_module), |m| { - let parent_id = def_map[*m].parent?; - Some(parent_id) - }); - ancestors.any(|m| m == to_module.local_id) + let mut def_map = def_map; + let mut parent_arc; + loop { + if def_map.module_id(from_module) == to_module { + return true; + } + match def_map[from_module].parent { + Some(parent) => { + from_module = parent; + } + None => { + match def_map.parent() { + Some(module) => { + parent_arc = module.def_map(db); + def_map = &*parent_arc; + from_module = module.local_id; + } + None => { + // Reached the root module, nothing left to check. + return false; + } + } + } + } + } } /// Returns the most permissive visibility of `self` and `other`.