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, ModuleId, TraitId, VariantId,
13 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(db) == krate,
48 Visibility::Public => return true,
49 };
50 if from_module == to_module {
51 return true;
53 }
54 if from_module.krate(db) != to_module.krate(db) {
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)
60 }
61
62 pub(crate) fn is_visible_from_def_map(
63 self,
64 db: &dyn DefDatabase,
65 def_map: &DefMap,
66 from_module: ModuleId,
67 ) -> bool {
68 if cfg!(debug_assertions) {
69 _ = def_map.modules[from_module];
70 }
71 let to_module = match self {
72 Visibility::Module(m, _) => m,
73 Visibility::PubCrate(krate) => return from_module.krate(db) == krate,
74 Visibility::Public => return true,
75 };
76 if from_module == to_module {
77 return true;
79 }
80 if def_map.krate() != to_module.krate(db) {
82 return false;
83 }
84
85 if from_module == to_module && def_map.block_id() == to_module.block(db) {
86 return true;
88 }
89 Self::is_visible_from_def_map_(db, def_map, to_module, from_module)
90 }
91
92 fn is_visible_from_def_map_(
93 db: &dyn DefDatabase,
94 def_map: &DefMap,
95 mut to_module: ModuleId,
96 mut from_module: ModuleId,
97 ) -> bool {
98 debug_assert_eq!(to_module.krate(db), def_map.krate());
99 let def_map_block = def_map.block_id();
107 loop {
108 match (to_module.block(db), def_map_block) {
109 (None, _) => (),
111 (Some(a), Some(b)) if a == b => {}
113 _ => {
114 if let Some(parent) = to_module.def_map(db).parent() {
115 to_module = parent;
116 continue;
117 }
118 }
119 }
120 break;
121 }
122
123 let mut def_map = def_map;
125 let mut parent_arc;
126 loop {
127 if from_module == to_module {
128 return true;
129 }
130 match def_map[from_module].parent {
131 Some(parent) => from_module = parent,
132 None => {
133 match def_map.parent() {
134 Some(module) => {
135 parent_arc = module.def_map(db);
136 def_map = parent_arc;
137 from_module = module;
138 }
139 None => return false,
141 }
142 }
143 }
144 }
145 }
146
147 pub(crate) fn max(
152 self,
153 db: &dyn DefDatabase,
154 other: Visibility,
155 def_map: &DefMap,
156 ) -> Option<Visibility> {
157 match (self, other) {
158 (_, Visibility::Public) | (Visibility::Public, _) => Some(Visibility::Public),
159 (Visibility::PubCrate(krate), Visibility::PubCrate(krateb)) => {
160 if krate == krateb {
161 Some(Visibility::PubCrate(krate))
162 } else {
163 None
164 }
165 }
166 (Visibility::Module(mod_, _), Visibility::PubCrate(krate))
167 | (Visibility::PubCrate(krate), Visibility::Module(mod_, _)) => {
168 if mod_.krate(db) == krate { Some(Visibility::PubCrate(krate)) } else { None }
169 }
170 (Visibility::Module(mod_a, expl_a), Visibility::Module(mod_b, expl_b)) => {
171 if mod_a == mod_b {
172 return Some(Visibility::Module(
175 mod_a,
176 match (expl_a, expl_b) {
177 (VisibilityExplicitness::Explicit, _)
178 | (_, VisibilityExplicitness::Explicit) => {
179 VisibilityExplicitness::Explicit
180 }
181 _ => VisibilityExplicitness::Implicit,
182 },
183 ));
184 }
185
186 if mod_a.krate(db) != def_map.krate() || mod_b.krate(db) != def_map.krate() {
187 return None;
188 }
189
190 let def_block = def_map.block_id();
191 if mod_a.block(db) != def_block || mod_b.block(db) != def_block {
192 return None;
193 }
194
195 let mut a_ancestors = iter::successors(Some(mod_a), |&m| def_map[m].parent);
196
197 if a_ancestors.any(|m| m == mod_b) {
198 return Some(Visibility::Module(mod_b, expl_b));
200 }
201
202 let mut b_ancestors = iter::successors(Some(mod_b), |&m| def_map[m].parent);
203 if b_ancestors.any(|m| m == mod_a) {
204 return Some(Visibility::Module(mod_a, expl_a));
206 }
207
208 None
209 }
210 }
211 }
212
213 pub(crate) fn min(
218 self,
219 db: &dyn DefDatabase,
220 other: Visibility,
221 def_map: &DefMap,
222 ) -> Option<Visibility> {
223 match (self, other) {
224 (vis, Visibility::Public) | (Visibility::Public, vis) => Some(vis),
225 (Visibility::PubCrate(krate), Visibility::PubCrate(krateb)) => {
226 if krate == krateb {
227 Some(Visibility::PubCrate(krate))
228 } else {
229 None
230 }
231 }
232 (Visibility::Module(mod_, exp), Visibility::PubCrate(krate))
233 | (Visibility::PubCrate(krate), Visibility::Module(mod_, exp)) => {
234 if mod_.krate(db) == krate { Some(Visibility::Module(mod_, exp)) } else { None }
235 }
236 (Visibility::Module(mod_a, expl_a), Visibility::Module(mod_b, expl_b)) => {
237 if mod_a.krate(db) != mod_b.krate(db) {
238 return Some(Visibility::Module(
241 mod_a,
242 match (expl_a, expl_b) {
243 (VisibilityExplicitness::Explicit, _)
244 | (_, VisibilityExplicitness::Explicit) => {
245 VisibilityExplicitness::Explicit
246 }
247 _ => VisibilityExplicitness::Implicit,
248 },
249 ));
250 }
251
252 if mod_a.krate(db) != def_map.krate() || mod_b.krate(db) != def_map.krate() {
253 return None;
254 }
255
256 let def_block = def_map.block_id();
257 if mod_a.block(db) != def_block || mod_b.block(db) != def_block {
258 return None;
259 }
260
261 let mut a_ancestors = iter::successors(Some(mod_a), |&m| def_map[m].parent);
262
263 if a_ancestors.any(|m| m == mod_b) {
264 return Some(Visibility::Module(mod_a, expl_a));
266 }
267
268 let mut b_ancestors = iter::successors(Some(mod_b), |&m| def_map[m].parent);
269 if b_ancestors.any(|m| m == mod_a) {
270 return Some(Visibility::Module(mod_b, expl_b));
272 }
273
274 None
275 }
276 }
277 }
278}
279
280pub(crate) fn field_visibilities_query(
282 db: &dyn DefDatabase,
283 variant_id: VariantId,
284) -> Arc<ArenaMap<LocalFieldId, Visibility>> {
285 let variant_fields = variant_id.fields(db);
286 let fields = variant_fields.fields();
287 if fields.is_empty() {
288 return Arc::default();
289 }
290 let resolver = variant_id.module(db).resolver(db);
291 let mut res = ArenaMap::default();
292 for (field_id, field_data) in fields.iter() {
293 res.insert(field_id, Visibility::resolve(db, &resolver, &field_data.visibility));
294 }
295 res.shrink_to_fit();
296 Arc::new(res)
297}
298
299pub fn visibility_from_ast(
300 db: &dyn DefDatabase,
301 has_resolver: impl HasResolver + HasModule,
302 ast_vis: InFile<Option<ast::Visibility>>,
303) -> Visibility {
304 let mut span_map = None;
305 let raw_vis = crate::item_tree::visibility_from_ast(db, ast_vis.value, &mut |range| {
306 span_map.get_or_insert_with(|| db.span_map(ast_vis.file_id)).span_for_range(range).ctx
307 });
308 match raw_vis {
309 RawVisibility::PubSelf(explicitness) => {
310 Visibility::Module(has_resolver.module(db), explicitness)
311 }
312 RawVisibility::PubCrate => Visibility::PubCrate(has_resolver.krate(db)),
313 RawVisibility::Public => Visibility::Public,
314 RawVisibility::Module(..) => Visibility::resolve(db, &has_resolver.resolver(db), &raw_vis),
315 }
316}
317
318pub(crate) fn assoc_visibility_query(db: &dyn DefDatabase, def: AssocItemId) -> Visibility {
320 match def {
321 AssocItemId::FunctionId(function_id) => {
322 let loc = function_id.lookup(db);
323 trait_item_visibility(db, loc.container).unwrap_or_else(|| {
324 let source = loc.source(db);
325 visibility_from_ast(db, function_id, source.map(|src| src.visibility()))
326 })
327 }
328 AssocItemId::ConstId(const_id) => {
329 let loc = const_id.lookup(db);
330 trait_item_visibility(db, loc.container).unwrap_or_else(|| {
331 let source = loc.source(db);
332 visibility_from_ast(db, const_id, source.map(|src| src.visibility()))
333 })
334 }
335 AssocItemId::TypeAliasId(type_alias_id) => {
336 let loc = type_alias_id.lookup(db);
337 trait_item_visibility(db, loc.container).unwrap_or_else(|| {
338 let source = loc.source(db);
339 visibility_from_ast(db, type_alias_id, source.map(|src| src.visibility()))
340 })
341 }
342 }
343}
344
345fn trait_item_visibility(db: &dyn DefDatabase, container: ItemContainerId) -> Option<Visibility> {
346 match container {
347 ItemContainerId::TraitId(trait_) => Some(trait_visibility(db, trait_)),
348 _ => None,
349 }
350}
351
352fn trait_visibility(db: &dyn DefDatabase, def: TraitId) -> Visibility {
353 let loc = def.lookup(db);
354 let source = loc.source(db);
355 visibility_from_ast(db, def, source.map(|src| src.visibility()))
356}