From 069bf55cca1e1be1f6cdd28b638f691e059858dc Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Tue, 24 Dec 2019 20:32:42 +0100 Subject: Add infrastructure for visibility on syntax and hir_def level --- crates/ra_hir_def/src/db.rs | 6 +- crates/ra_hir_def/src/lib.rs | 25 ++++++++ crates/ra_hir_def/src/visibility.rs | 101 +++++++++++++++++++++++++++++++++ crates/ra_syntax/src/ast.rs | 4 +- crates/ra_syntax/src/ast/extensions.rs | 29 ++++++++++ 5 files changed, 163 insertions(+), 2 deletions(-) create mode 100644 crates/ra_hir_def/src/visibility.rs diff --git a/crates/ra_hir_def/src/db.rs b/crates/ra_hir_def/src/db.rs index c55fd4111..1761e2187 100644 --- a/crates/ra_hir_def/src/db.rs +++ b/crates/ra_hir_def/src/db.rs @@ -14,9 +14,10 @@ use crate::{ generics::GenericParams, lang_item::{LangItemTarget, LangItems}, nameres::{raw::RawItems, CrateDefMap}, + visibility::Visibility, AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, FunctionId, FunctionLoc, GenericDefId, ImplId, ImplLoc, ModuleId, StaticId, StaticLoc, StructId, StructLoc, TraitId, - TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, + TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, VisibilityDefId, }; #[salsa::query_group(InternDatabaseStorage)] @@ -90,6 +91,9 @@ pub trait DefDatabase: InternDatabase + AstDatabase { #[salsa::invoke(Attrs::attrs_query)] fn attrs(&self, def: AttrDefId) -> Attrs; + #[salsa::invoke(Visibility::visibility_query)] + fn visibility(&self, def: VisibilityDefId) -> Visibility; + #[salsa::invoke(LangItems::module_lang_items_query)] fn module_lang_items(&self, module: ModuleId) -> Option>; diff --git a/crates/ra_hir_def/src/lib.rs b/crates/ra_hir_def/src/lib.rs index f6c7f38d1..72a59d867 100644 --- a/crates/ra_hir_def/src/lib.rs +++ b/crates/ra_hir_def/src/lib.rs @@ -36,6 +36,8 @@ pub mod nameres; pub mod src; pub mod child_by_source; +pub mod visibility; + #[cfg(test)] mod test_db; #[cfg(test)] @@ -323,6 +325,29 @@ impl_froms!( ImplId ); +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub enum VisibilityDefId { + ModuleId(ModuleId), + StructFieldId(StructFieldId), + AdtId(AdtId), + FunctionId(FunctionId), + StaticId(StaticId), + ConstId(ConstId), + TraitId(TraitId), + TypeAliasId(TypeAliasId), +} + +impl_froms!( + VisibilityDefId: ModuleId, + StructFieldId, + AdtId(StructId, EnumId, UnionId), + StaticId, + ConstId, + FunctionId, + TraitId, + TypeAliasId +); + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum VariantId { EnumVariantId(EnumVariantId), diff --git a/crates/ra_hir_def/src/visibility.rs b/crates/ra_hir_def/src/visibility.rs new file mode 100644 index 000000000..7d881911d --- /dev/null +++ b/crates/ra_hir_def/src/visibility.rs @@ -0,0 +1,101 @@ +use std::sync::Arc; + +use either::Either; + +use hir_expand::InFile; +use ra_syntax::ast::{self, VisibilityOwner}; + +use crate::{ + db::DefDatabase, + path::{ModPath, PathKind}, + src::{HasChildSource, HasSource}, + AdtId, Lookup, VisibilityDefId, +}; + +/// Visibility of an item, not yet resolved. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Visibility { + // FIXME: We could avoid the allocation in many cases by special-casing + // pub(crate), pub(super) and private. Alternatively, `ModPath` could be + // made to contain an Arc<[Segment]> instead of a Vec? + /// `pub(in module)`, `pub(crate)` or `pub(super)`. Also private, which is + /// equivalent to `pub(self)`. + Module(Arc), + /// `pub`. + Public, +} + +impl Visibility { + pub(crate) fn visibility_query(db: &impl DefDatabase, def: VisibilityDefId) -> Visibility { + match def { + VisibilityDefId::ModuleId(module) => { + let def_map = db.crate_def_map(module.krate); + let src = match def_map[module.local_id].declaration_source(db) { + Some(it) => it, + None => return Visibility::private(), + }; + Visibility::from_ast(db, src.map(|it| it.visibility())) + } + VisibilityDefId::StructFieldId(it) => { + let src = it.parent.child_source(db); + // TODO: enum variant fields should be public by default + let vis_node = src.map(|m| match &m[it.local_id] { + Either::Left(tuple) => tuple.visibility(), + Either::Right(record) => record.visibility(), + }); + Visibility::from_ast(db, vis_node) + } + VisibilityDefId::AdtId(it) => match it { + AdtId::StructId(it) => visibility_from_loc(it.lookup(db), db), + AdtId::EnumId(it) => visibility_from_loc(it.lookup(db), db), + AdtId::UnionId(it) => visibility_from_loc(it.lookup(db), db), + }, + VisibilityDefId::TraitId(it) => visibility_from_loc(it.lookup(db), db), + VisibilityDefId::ConstId(it) => visibility_from_loc(it.lookup(db), db), + VisibilityDefId::StaticId(it) => visibility_from_loc(it.lookup(db), db), + VisibilityDefId::FunctionId(it) => visibility_from_loc(it.lookup(db), db), + VisibilityDefId::TypeAliasId(it) => visibility_from_loc(it.lookup(db), db), + } + } + + fn private() -> Visibility { + let path = ModPath { kind: PathKind::Super(0), segments: Vec::new() }; + Visibility::Module(Arc::new(path)) + } + + fn from_ast(db: &impl DefDatabase, node: InFile>) -> Visibility { + let file_id = node.file_id; + let node = match node.value { + None => return Visibility::private(), + Some(node) => node, + }; + match node.kind() { + ast::VisibilityKind::In(path) => { + let path = ModPath::from_src(path, &hir_expand::hygiene::Hygiene::new(db, file_id)); + let path = match path { + None => return Visibility::private(), + Some(path) => path, + }; + Visibility::Module(Arc::new(path)) + } + ast::VisibilityKind::PubCrate => { + let path = ModPath { kind: PathKind::Crate, segments: Vec::new() }; + Visibility::Module(Arc::new(path)) + } + ast::VisibilityKind::PubSuper => { + let path = ModPath { kind: PathKind::Super(1), segments: Vec::new() }; + Visibility::Module(Arc::new(path)) + } + ast::VisibilityKind::Pub => Visibility::Public, + } + } +} + +fn visibility_from_loc(node: T, db: &impl DefDatabase) -> Visibility +where + T: HasSource, + T::Value: ast::VisibilityOwner, +{ + let src = node.source(db); + Visibility::from_ast(db, src.map(|n| n.visibility())) +} diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs index 277532a8c..89cb9a9f3 100644 --- a/crates/ra_syntax/src/ast.rs +++ b/crates/ra_syntax/src/ast.rs @@ -17,7 +17,9 @@ use crate::{ pub use self::{ expr_extensions::{ArrayExprKind, BinOp, ElseBranch, LiteralKind, PrefixOp, RangeOp}, - extensions::{FieldKind, PathSegmentKind, SelfParamKind, StructKind, TypeBoundKind}, + extensions::{ + FieldKind, PathSegmentKind, SelfParamKind, StructKind, TypeBoundKind, VisibilityKind, + }, generated::*, tokens::*, traits::*, diff --git a/crates/ra_syntax/src/ast/extensions.rs b/crates/ra_syntax/src/ast/extensions.rs index baaef3023..d9666cdca 100644 --- a/crates/ra_syntax/src/ast/extensions.rs +++ b/crates/ra_syntax/src/ast/extensions.rs @@ -413,3 +413,32 @@ impl ast::TraitDef { self.syntax().children_with_tokens().any(|t| t.kind() == T![auto]) } } + +pub enum VisibilityKind { + In(ast::Path), + PubCrate, + PubSuper, + Pub, +} + +impl ast::Visibility { + pub fn kind(&self) -> VisibilityKind { + if let Some(path) = children(self).next() { + VisibilityKind::In(path) + } else if self.is_pub_crate() { + VisibilityKind::PubCrate + } else if self.is_pub_super() { + VisibilityKind::PubSuper + } else { + VisibilityKind::Pub + } + } + + fn is_pub_crate(&self) -> bool { + self.syntax().children_with_tokens().any(|it| it.kind() == T![crate]) + } + + fn is_pub_super(&self) -> bool { + self.syntax().children_with_tokens().any(|it| it.kind() == T![super]) + } +} -- cgit v1.2.3