1use std::iter;
4
5use base_db::Crate;
6use hir_expand::{InFile, Lookup};
7use la_arena::ArenaMap;
8use syntax::ast::{self, HasVisibility};
9use triomphe::Arc;
10
11use crate::{
12 AssocItemId, HasModule, ItemContainerId, LocalFieldId, LocalModuleId, ModuleId, TraitId,
13 VariantId, db::DefDatabase, nameres::DefMap, resolver::HasResolver, src::HasSource,
14};
15
16pub use crate::item_tree::{RawVisibility, VisibilityExplicitness};
17
18#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
20pub enum Visibility {
21 Module(ModuleId, VisibilityExplicitness),
23 PubCrate(Crate),
25 Public,
27}
28
29impl Visibility {
30 pub fn resolve(
31 db: &dyn DefDatabase,
32 resolver: &crate::resolver::Resolver<'_>,
33 raw_vis: &RawVisibility,
34 ) -> Self {
35 resolver.resolve_visibility(db, raw_vis).unwrap_or(Visibility::Public)
37 }
38
39 pub(crate) fn is_visible_from_other_crate(self) -> bool {
40 matches!(self, Visibility::Public)
41 }
42
43 #[tracing::instrument(skip_all)]
44 pub fn is_visible_from(self, db: &dyn DefDatabase, from_module: ModuleId) -> bool {
45 let to_module = match self {
46 Visibility::Module(m, _) => m,
47 Visibility::PubCrate(krate) => return from_module.krate == krate,
48 Visibility::Public => return true,
49 };
50 if from_module == to_module {
51 return true;
53 }
54 if from_module.krate != to_module.krate {
56 return false;
57 }
58 let def_map = from_module.def_map(db);
59 Self::is_visible_from_def_map_(db, def_map, to_module, from_module.local_id)
60 }
61
62 pub(crate) fn is_visible_from_def_map(
63 self,
64 db: &dyn DefDatabase,
65 def_map: &DefMap,
66 from_module: LocalModuleId,
67 ) -> bool {
68 let to_module = match self {
69 Visibility::Module(m, _) => m,
70 Visibility::PubCrate(krate) => return def_map.krate() == krate,
71 Visibility::Public => return true,
72 };
73 if def_map.krate() != to_module.krate {
75 return false;
76 }
77
78 if from_module == to_module.local_id && def_map.block_id() == to_module.block {
79 return true;
81 }
82 Self::is_visible_from_def_map_(db, def_map, to_module, from_module)
83 }
84
85 fn is_visible_from_def_map_(
86 db: &dyn DefDatabase,
87 def_map: &DefMap,
88 mut to_module: ModuleId,
89 mut from_module: LocalModuleId,
90 ) -> bool {
91 debug_assert_eq!(to_module.krate, def_map.krate());
92 let def_map_block = def_map.block_id();
100 loop {
101 match (to_module.block, def_map_block) {
102 (None, _) => (),
104 (Some(a), Some(b)) if a == b => {}
106 _ => {
107 if let Some(parent) = to_module.def_map(db).parent() {
108 to_module = parent;
109 continue;
110 }
111 }
112 }
113 break;
114 }
115
116 let mut def_map = def_map;
118 let mut parent_arc;
119 loop {
120 if def_map.module_id(from_module) == to_module {
121 return true;
122 }
123 match def_map[from_module].parent {
124 Some(parent) => from_module = parent,
125 None => {
126 match def_map.parent() {
127 Some(module) => {
128 parent_arc = module.def_map(db);
129 def_map = parent_arc;
130 from_module = module.local_id;
131 }
132 None => return false,
134 }
135 }
136 }
137 }
138 }
139
140 pub(crate) fn max(self, other: Visibility, def_map: &DefMap) -> Option<Visibility> {
145 match (self, other) {
146 (_, Visibility::Public) | (Visibility::Public, _) => Some(Visibility::Public),
147 (Visibility::PubCrate(krate), Visibility::PubCrate(krateb)) => {
148 if krate == krateb {
149 Some(Visibility::PubCrate(krate))
150 } else {
151 None
152 }
153 }
154 (Visibility::Module(mod_, _), Visibility::PubCrate(krate))
155 | (Visibility::PubCrate(krate), Visibility::Module(mod_, _)) => {
156 if mod_.krate == krate {
157 Some(Visibility::PubCrate(krate))
158 } else {
159 None
160 }
161 }
162 (Visibility::Module(mod_a, expl_a), Visibility::Module(mod_b, expl_b)) => {
163 if mod_a == mod_b {
164 return Some(Visibility::Module(
167 mod_a,
168 match (expl_a, expl_b) {
169 (VisibilityExplicitness::Explicit, _)
170 | (_, VisibilityExplicitness::Explicit) => {
171 VisibilityExplicitness::Explicit
172 }
173 _ => VisibilityExplicitness::Implicit,
174 },
175 ));
176 }
177
178 if mod_a.krate() != def_map.krate() || mod_b.krate() != def_map.krate() {
179 return None;
180 }
181
182 let def_block = def_map.block_id();
183 if mod_a.containing_block() != def_block || mod_b.containing_block() != def_block {
184 return None;
185 }
186
187 let mut a_ancestors =
188 iter::successors(Some(mod_a.local_id), |&m| def_map[m].parent);
189
190 if a_ancestors.any(|m| m == mod_b.local_id) {
191 return Some(Visibility::Module(mod_b, expl_b));
193 }
194
195 let mut b_ancestors =
196 iter::successors(Some(mod_b.local_id), |&m| def_map[m].parent);
197 if b_ancestors.any(|m| m == mod_a.local_id) {
198 return Some(Visibility::Module(mod_a, expl_a));
200 }
201
202 None
203 }
204 }
205 }
206
207 pub(crate) fn min(self, other: Visibility, def_map: &DefMap) -> Option<Visibility> {
212 match (self, other) {
213 (vis, Visibility::Public) | (Visibility::Public, vis) => Some(vis),
214 (Visibility::PubCrate(krate), Visibility::PubCrate(krateb)) => {
215 if krate == krateb {
216 Some(Visibility::PubCrate(krate))
217 } else {
218 None
219 }
220 }
221 (Visibility::Module(mod_, exp), Visibility::PubCrate(krate))
222 | (Visibility::PubCrate(krate), Visibility::Module(mod_, exp)) => {
223 if mod_.krate == krate { Some(Visibility::Module(mod_, exp)) } else { None }
224 }
225 (Visibility::Module(mod_a, expl_a), Visibility::Module(mod_b, expl_b)) => {
226 if mod_a == mod_b {
227 return Some(Visibility::Module(
230 mod_a,
231 match (expl_a, expl_b) {
232 (VisibilityExplicitness::Explicit, _)
233 | (_, VisibilityExplicitness::Explicit) => {
234 VisibilityExplicitness::Explicit
235 }
236 _ => VisibilityExplicitness::Implicit,
237 },
238 ));
239 }
240
241 if mod_a.krate() != def_map.krate() || mod_b.krate() != def_map.krate() {
242 return None;
243 }
244
245 let def_block = def_map.block_id();
246 if mod_a.containing_block() != def_block || mod_b.containing_block() != def_block {
247 return None;
248 }
249
250 let mut a_ancestors =
251 iter::successors(Some(mod_a.local_id), |&m| def_map[m].parent);
252
253 if a_ancestors.any(|m| m == mod_b.local_id) {
254 return Some(Visibility::Module(mod_a, expl_a));
256 }
257
258 let mut b_ancestors =
259 iter::successors(Some(mod_b.local_id), |&m| def_map[m].parent);
260 if b_ancestors.any(|m| m == mod_a.local_id) {
261 return Some(Visibility::Module(mod_b, expl_b));
263 }
264
265 None
266 }
267 }
268 }
269}
270
271pub(crate) fn field_visibilities_query(
273 db: &dyn DefDatabase,
274 variant_id: VariantId,
275) -> Arc<ArenaMap<LocalFieldId, Visibility>> {
276 let variant_fields = variant_id.fields(db);
277 let fields = variant_fields.fields();
278 if fields.is_empty() {
279 return Arc::default();
280 }
281 let resolver = variant_id.module(db).resolver(db);
282 let mut res = ArenaMap::default();
283 for (field_id, field_data) in fields.iter() {
284 res.insert(field_id, Visibility::resolve(db, &resolver, &field_data.visibility));
285 }
286 res.shrink_to_fit();
287 Arc::new(res)
288}
289
290pub fn visibility_from_ast(
291 db: &dyn DefDatabase,
292 has_resolver: impl HasResolver,
293 ast_vis: InFile<Option<ast::Visibility>>,
294) -> Visibility {
295 let mut span_map = None;
296 let raw_vis = crate::item_tree::visibility_from_ast(db, ast_vis.value, &mut |range| {
297 span_map.get_or_insert_with(|| db.span_map(ast_vis.file_id)).span_for_range(range).ctx
298 });
299 if raw_vis == RawVisibility::Public {
300 return Visibility::Public;
301 }
302
303 Visibility::resolve(db, &has_resolver.resolver(db), &raw_vis)
304}
305
306pub(crate) fn assoc_visibility_query(db: &dyn DefDatabase, def: AssocItemId) -> Visibility {
308 match def {
309 AssocItemId::FunctionId(function_id) => {
310 let loc = function_id.lookup(db);
311 trait_item_visibility(db, loc.container).unwrap_or_else(|| {
312 let source = loc.source(db);
313 visibility_from_ast(db, function_id, source.map(|src| src.visibility()))
314 })
315 }
316 AssocItemId::ConstId(const_id) => {
317 let loc = const_id.lookup(db);
318 trait_item_visibility(db, loc.container).unwrap_or_else(|| {
319 let source = loc.source(db);
320 visibility_from_ast(db, const_id, source.map(|src| src.visibility()))
321 })
322 }
323 AssocItemId::TypeAliasId(type_alias_id) => {
324 let loc = type_alias_id.lookup(db);
325 trait_item_visibility(db, loc.container).unwrap_or_else(|| {
326 let source = loc.source(db);
327 visibility_from_ast(db, type_alias_id, source.map(|src| src.visibility()))
328 })
329 }
330 }
331}
332
333fn trait_item_visibility(db: &dyn DefDatabase, container: ItemContainerId) -> Option<Visibility> {
334 match container {
335 ItemContainerId::TraitId(trait_) => Some(trait_visibility(db, trait_)),
336 _ => None,
337 }
338}
339
340fn trait_visibility(db: &dyn DefDatabase, def: TraitId) -> Visibility {
341 let loc = def.lookup(db);
342 let source = loc.source(db);
343 visibility_from_ast(db, def, source.map(|src| src.visibility()))
344}