aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_def/src/visibility.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/hir_def/src/visibility.rs')
-rw-r--r--crates/hir_def/src/visibility.rs171
1 files changed, 171 insertions, 0 deletions
diff --git a/crates/hir_def/src/visibility.rs b/crates/hir_def/src/visibility.rs
new file mode 100644
index 000000000..e6e0853a3
--- /dev/null
+++ b/crates/hir_def/src/visibility.rs
@@ -0,0 +1,171 @@
1//! Defines hir-level representation of visibility (e.g. `pub` and `pub(crate)`).
2
3use hir_expand::{hygiene::Hygiene, InFile};
4use syntax::ast;
5
6use crate::{
7 db::DefDatabase,
8 nameres::CrateDefMap,
9 path::{ModPath, PathKind},
10 ModuleId,
11};
12
13/// Visibility of an item, not yet resolved.
14#[derive(Debug, Clone, PartialEq, Eq)]
15pub enum RawVisibility {
16 /// `pub(in module)`, `pub(crate)` or `pub(super)`. Also private, which is
17 /// equivalent to `pub(self)`.
18 Module(ModPath),
19 /// `pub`.
20 Public,
21}
22
23impl RawVisibility {
24 pub(crate) const fn private() -> RawVisibility {
25 let path = ModPath { kind: PathKind::Super(0), segments: Vec::new() };
26 RawVisibility::Module(path)
27 }
28
29 pub(crate) fn from_ast(
30 db: &dyn DefDatabase,
31 node: InFile<Option<ast::Visibility>>,
32 ) -> RawVisibility {
33 Self::from_ast_with_hygiene(node.value, &Hygiene::new(db.upcast(), node.file_id))
34 }
35
36 pub(crate) fn from_ast_with_hygiene(
37 node: Option<ast::Visibility>,
38 hygiene: &Hygiene,
39 ) -> RawVisibility {
40 Self::from_ast_with_hygiene_and_default(node, RawVisibility::private(), hygiene)
41 }
42
43 pub(crate) fn from_ast_with_hygiene_and_default(
44 node: Option<ast::Visibility>,
45 default: RawVisibility,
46 hygiene: &Hygiene,
47 ) -> RawVisibility {
48 let node = match node {
49 None => return default,
50 Some(node) => node,
51 };
52 match node.kind() {
53 ast::VisibilityKind::In(path) => {
54 let path = ModPath::from_src(path, hygiene);
55 let path = match path {
56 None => return RawVisibility::private(),
57 Some(path) => path,
58 };
59 RawVisibility::Module(path)
60 }
61 ast::VisibilityKind::PubCrate => {
62 let path = ModPath { kind: PathKind::Crate, segments: Vec::new() };
63 RawVisibility::Module(path)
64 }
65 ast::VisibilityKind::PubSuper => {
66 let path = ModPath { kind: PathKind::Super(1), segments: Vec::new() };
67 RawVisibility::Module(path)
68 }
69 ast::VisibilityKind::PubSelf => {
70 let path = ModPath { kind: PathKind::Plain, segments: Vec::new() };
71 RawVisibility::Module(path)
72 }
73 ast::VisibilityKind::Pub => RawVisibility::Public,
74 }
75 }
76
77 pub fn resolve(
78 &self,
79 db: &dyn DefDatabase,
80 resolver: &crate::resolver::Resolver,
81 ) -> Visibility {
82 // we fall back to public visibility (i.e. fail open) if the path can't be resolved
83 resolver.resolve_visibility(db, self).unwrap_or(Visibility::Public)
84 }
85}
86
87/// Visibility of an item, with the path resolved.
88#[derive(Debug, Copy, Clone, PartialEq, Eq)]
89pub enum Visibility {
90 /// Visibility is restricted to a certain module.
91 Module(ModuleId),
92 /// Visibility is unrestricted.
93 Public,
94}
95
96impl Visibility {
97 pub fn is_visible_from(self, db: &dyn DefDatabase, from_module: ModuleId) -> bool {
98 let to_module = match self {
99 Visibility::Module(m) => m,
100 Visibility::Public => return true,
101 };
102 // if they're not in the same crate, it can't be visible
103 if from_module.krate != to_module.krate {
104 return false;
105 }
106 let def_map = db.crate_def_map(from_module.krate);
107 self.is_visible_from_def_map(&def_map, from_module.local_id)
108 }
109
110 pub(crate) fn is_visible_from_other_crate(self) -> bool {
111 match self {
112 Visibility::Module(_) => false,
113 Visibility::Public => true,
114 }
115 }
116
117 pub(crate) fn is_visible_from_def_map(
118 self,
119 def_map: &CrateDefMap,
120 from_module: crate::LocalModuleId,
121 ) -> bool {
122 let to_module = match self {
123 Visibility::Module(m) => m,
124 Visibility::Public => return true,
125 };
126 // from_module needs to be a descendant of to_module
127 let mut ancestors = std::iter::successors(Some(from_module), |m| {
128 let parent_id = def_map[*m].parent?;
129 Some(parent_id)
130 });
131 ancestors.any(|m| m == to_module.local_id)
132 }
133
134 /// Returns the most permissive visibility of `self` and `other`.
135 ///
136 /// If there is no subset relation between `self` and `other`, returns `None` (ie. they're only
137 /// visible in unrelated modules).
138 pub(crate) fn max(self, other: Visibility, def_map: &CrateDefMap) -> Option<Visibility> {
139 match (self, other) {
140 (Visibility::Module(_), Visibility::Public)
141 | (Visibility::Public, Visibility::Module(_))
142 | (Visibility::Public, Visibility::Public) => Some(Visibility::Public),
143 (Visibility::Module(mod_a), Visibility::Module(mod_b)) => {
144 if mod_a.krate != mod_b.krate {
145 return None;
146 }
147
148 let mut a_ancestors = std::iter::successors(Some(mod_a.local_id), |m| {
149 let parent_id = def_map[*m].parent?;
150 Some(parent_id)
151 });
152 let mut b_ancestors = std::iter::successors(Some(mod_b.local_id), |m| {
153 let parent_id = def_map[*m].parent?;
154 Some(parent_id)
155 });
156
157 if a_ancestors.any(|m| m == mod_b.local_id) {
158 // B is above A
159 return Some(Visibility::Module(mod_b));
160 }
161
162 if b_ancestors.any(|m| m == mod_a.local_id) {
163 // A is above B
164 return Some(Visibility::Module(mod_a));
165 }
166
167 None
168 }
169 }
170 }
171}