//! Defines hir-level representation of visibility (e.g. `pub` and `pub(crate)`).
+use std::sync::Arc;
+
use hir_expand::{hygiene::Hygiene, InFile};
+use la_arena::ArenaMap;
use syntax::ast;
use crate::{
db::DefDatabase,
nameres::DefMap,
path::{ModPath, PathKind},
- ModuleId,
+ resolver::HasResolver,
+ FunctionId, HasModule, LocalFieldId, ModuleId, VariantId,
};
/// Visibility of an item, not yet resolved.
}
impl RawVisibility {
- pub(crate) const fn private() -> RawVisibility {
- let path = ModPath { kind: PathKind::Super(0), segments: Vec::new() };
- RawVisibility::Module(path)
+ pub(crate) fn private() -> RawVisibility {
+ RawVisibility::Module(ModPath::from_kind(PathKind::Super(0)))
}
pub(crate) fn from_ast(
db: &dyn DefDatabase,
node: InFile<Option<ast::Visibility>>,
) -> RawVisibility {
- Self::from_ast_with_hygiene(node.value, &Hygiene::new(db.upcast(), node.file_id))
+ Self::from_ast_with_hygiene(db, node.value, &Hygiene::new(db.upcast(), node.file_id))
}
pub(crate) fn from_ast_with_hygiene(
+ db: &dyn DefDatabase,
node: Option<ast::Visibility>,
hygiene: &Hygiene,
) -> RawVisibility {
- Self::from_ast_with_hygiene_and_default(node, RawVisibility::private(), hygiene)
+ Self::from_ast_with_hygiene_and_default(db, node, RawVisibility::private(), hygiene)
}
pub(crate) fn from_ast_with_hygiene_and_default(
+ db: &dyn DefDatabase,
node: Option<ast::Visibility>,
default: RawVisibility,
hygiene: &Hygiene,
};
match node.kind() {
ast::VisibilityKind::In(path) => {
- let path = ModPath::from_src(path, hygiene);
+ let path = ModPath::from_src(db.upcast(), path, hygiene);
let path = match path {
None => return RawVisibility::private(),
Some(path) => path,
RawVisibility::Module(path)
}
ast::VisibilityKind::PubCrate => {
- let path = ModPath { kind: PathKind::Crate, segments: Vec::new() };
+ let path = ModPath::from_kind(PathKind::Crate);
RawVisibility::Module(path)
}
ast::VisibilityKind::PubSuper => {
- let path = ModPath { kind: PathKind::Super(1), segments: Vec::new() };
+ let path = ModPath::from_kind(PathKind::Super(1));
RawVisibility::Module(path)
}
ast::VisibilityKind::PubSelf => {
- let path = ModPath { kind: PathKind::Plain, segments: Vec::new() };
+ let path = ModPath::from_kind(PathKind::Plain);
RawVisibility::Module(path)
}
ast::VisibilityKind::Pub => RawVisibility::Public,
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 {
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 {
+ let mut to_module = match self {
Visibility::Module(m) => m,
Visibility::Public => return true,
};
+
+ // `to_module` might be the root module of a block expression. Those have the same
+ // visibility as the containing module (even though no items are directly nameable from
+ // there, getting this right is important for method resolution).
+ // In that case, we adjust the visibility of `to_module` to point to the containing module.
+ // Additional complication: `to_module` might be in `from_module`'s `DefMap`, which we're
+ // currently computing, so we must not call the `def_map` query for it.
+ let arc;
+ let to_module_def_map =
+ if to_module.krate == def_map.krate() && to_module.block == def_map.block_id() {
+ cov_mark::hit!(is_visible_from_same_block_def_map);
+ def_map
+ } else {
+ arc = to_module.def_map(db);
+ &arc
+ };
+ let is_block_root = match to_module.block {
+ Some(_) => to_module_def_map[to_module.local_id].parent.is_none(),
+ None => false,
+ };
+ if is_block_root {
+ to_module = to_module_def_map.containing_module(to_module.local_id).unwrap();
+ }
+
// 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`.
/// visible in unrelated modules).
pub(crate) fn max(self, other: Visibility, def_map: &DefMap) -> Option<Visibility> {
match (self, other) {
- (Visibility::Module(_), Visibility::Public)
- | (Visibility::Public, Visibility::Module(_))
- | (Visibility::Public, Visibility::Public) => Some(Visibility::Public),
+ (Visibility::Module(_) | Visibility::Public, Visibility::Public)
+ | (Visibility::Public, Visibility::Module(_)) => Some(Visibility::Public),
(Visibility::Module(mod_a), Visibility::Module(mod_b)) => {
if mod_a.krate != mod_b.krate {
return None;
}
}
}
+
+/// Resolve visibility of all specific fields of a struct or union variant.
+pub(crate) fn field_visibilities_query(
+ db: &dyn DefDatabase,
+ variant_id: VariantId,
+) -> Arc<ArenaMap<LocalFieldId, Visibility>> {
+ let var_data = match variant_id {
+ VariantId::StructId(it) => db.struct_data(it).variant_data.clone(),
+ VariantId::UnionId(it) => db.union_data(it).variant_data.clone(),
+ VariantId::EnumVariantId(it) => {
+ db.enum_data(it.parent).variants[it.local_id].variant_data.clone()
+ }
+ };
+ let resolver = variant_id.module(db).resolver(db);
+ let mut res = ArenaMap::default();
+ for (field_id, field_data) in var_data.fields().iter() {
+ res.insert(field_id, field_data.visibility.resolve(db, &resolver))
+ }
+ Arc::new(res)
+}
+
+/// Resolve visibility of a function.
+pub(crate) fn function_visibility_query(db: &dyn DefDatabase, def: FunctionId) -> Visibility {
+ let resolver = def.resolver(db);
+ db.function_data(def).visibility.resolve(db, &resolver)
+}