From 1ce809d0fa59ade71b13c200870b1fd5f74ceff4 Mon Sep 17 00:00:00 2001
From: Florian Diebold <flodiebold@gmail.com>
Date: Tue, 24 Dec 2019 21:23:22 +0100
Subject: Add logic for resolving and checking visibility

---
 crates/ra_hir_def/src/resolver.rs   | 21 +++++++++++++++++++
 crates/ra_hir_def/src/visibility.rs | 40 ++++++++++++++++++++++++++++++++++++-
 2 files changed, 60 insertions(+), 1 deletion(-)

(limited to 'crates/ra_hir_def/src')

diff --git a/crates/ra_hir_def/src/resolver.rs b/crates/ra_hir_def/src/resolver.rs
index cf3c33d78..d509dc3dd 100644
--- a/crates/ra_hir_def/src/resolver.rs
+++ b/crates/ra_hir_def/src/resolver.rs
@@ -19,6 +19,7 @@ use crate::{
     nameres::CrateDefMap,
     path::{ModPath, PathKind},
     per_ns::PerNs,
+    visibility::{ResolvedVisibility, Visibility},
     AdtId, AssocContainerId, ConstId, ContainerId, DefWithBodyId, EnumId, EnumVariantId,
     FunctionId, GenericDefId, HasModule, ImplId, LocalModuleId, Lookup, ModuleDefId, ModuleId,
     StaticId, StructId, TraitId, TypeAliasId, TypeParamId, VariantId,
@@ -231,6 +232,26 @@ impl Resolver {
         Some(res)
     }
 
+    pub fn resolve_visibility(
+        &self,
+        db: &impl DefDatabase,
+        visibility: &Visibility,
+    ) -> Option<ResolvedVisibility> {
+        match visibility {
+            Visibility::Module(mod_path) => {
+                let resolved = self.resolve_module_path_in_items(db, &mod_path).take_types()?;
+                match resolved {
+                    ModuleDefId::ModuleId(m) => Some(ResolvedVisibility::Module(m)),
+                    _ => {
+                        // error: visibility needs to refer to module
+                        None
+                    }
+                }
+            }
+            Visibility::Public => Some(ResolvedVisibility::Public),
+        }
+    }
+
     pub fn resolve_path_in_value_ns(
         &self,
         db: &impl DefDatabase,
diff --git a/crates/ra_hir_def/src/visibility.rs b/crates/ra_hir_def/src/visibility.rs
index 7d881911d..901bc7191 100644
--- a/crates/ra_hir_def/src/visibility.rs
+++ b/crates/ra_hir_def/src/visibility.rs
@@ -9,7 +9,7 @@ use crate::{
     db::DefDatabase,
     path::{ModPath, PathKind},
     src::{HasChildSource, HasSource},
-    AdtId, Lookup, VisibilityDefId,
+    AdtId, Lookup, ModuleId, VisibilityDefId,
 };
 
 /// Visibility of an item, not yet resolved.
@@ -89,6 +89,44 @@ impl Visibility {
             ast::VisibilityKind::Pub => Visibility::Public,
         }
     }
+
+    pub fn resolve(
+        &self,
+        db: &impl DefDatabase,
+        resolver: &crate::resolver::Resolver,
+    ) -> ResolvedVisibility {
+        // we fall back to public visibility (i.e. fail open) if the path can't be resolved
+        resolver.resolve_visibility(db, self).unwrap_or(ResolvedVisibility::Public)
+    }
+}
+
+/// Visibility of an item, with the path resolved.
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub enum ResolvedVisibility {
+    /// Visibility is restricted to a certain module.
+    Module(ModuleId),
+    /// Visibility is unrestricted.
+    Public,
+}
+
+impl ResolvedVisibility {
+    pub fn visible_from(self, db: &impl DefDatabase, from_module: ModuleId) -> bool {
+        let to_module = match self {
+            ResolvedVisibility::Module(m) => m,
+            ResolvedVisibility::Public => return true,
+        };
+        // if they're not in the same crate, it can't be visible
+        if from_module.krate != to_module.krate {
+            return false;
+        }
+        // from_module needs to be a descendant of to_module
+        let def_map = db.crate_def_map(from_module.krate);
+        let mut ancestors = std::iter::successors(Some(from_module), |m| {
+            let parent_id = def_map[m.local_id].parent?;
+            Some(ModuleId { local_id: parent_id, ..*m })
+        });
+        ancestors.any(|m| m == to_module)
+    }
 }
 
 fn visibility_from_loc<T>(node: T, db: &impl DefDatabase) -> Visibility
-- 
cgit v1.2.3