1use std::iter;
4
5use base_db::{Crate, SourceDatabase};
6use hir_expand::{InFile, Lookup};
7use la_arena::ArenaMap;
8use syntax::ast::{self, HasVisibility};
9
10use crate::{
11 AssocItemId, HasModule, ItemContainerId, LocalFieldId, ModuleId, ModuleIdLt, TraitId,
12 VariantId, nameres::DefMap, resolver::HasResolver, signatures::VariantFields, src::HasSource,
13};
14
15pub use crate::item_tree::{RawVisibility, VisibilityExplicitness};
16
17#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
19pub enum Visibility {
20 Module(ModuleId, VisibilityExplicitness),
22 PubCrate(Crate),
24 Public,
26}
27
28impl Visibility {
29 pub fn resolve(
30 db: &dyn SourceDatabase,
31 resolver: &crate::resolver::Resolver<'_>,
32 raw_vis: &RawVisibility,
33 ) -> Self {
34 resolver.resolve_visibility(db, raw_vis).unwrap_or(Visibility::Public)
36 }
37
38 pub(crate) fn is_visible_from_other_crate(self) -> bool {
39 matches!(self, Visibility::Public)
40 }
41
42 #[tracing::instrument(skip_all)]
43 pub fn is_visible_from<'db>(
44 self,
45 db: &'db dyn SourceDatabase,
46 from_module: ModuleIdLt<'db>,
47 ) -> bool {
48 let to_module = match self {
49 Visibility::Module(m, _) => unsafe { m.to_db(db) },
50 Visibility::PubCrate(krate) => return from_module.krate(db) == krate,
51 Visibility::Public => return true,
52 };
53 if from_module == to_module {
54 return true;
56 }
57 if from_module.krate(db) != to_module.krate(db) {
59 return false;
60 }
61 let def_map = from_module.def_map(db);
62 Self::is_visible_from_def_map_(db, def_map, to_module, from_module)
63 }
64
65 pub(crate) fn is_visible_from_def_map<'db>(
66 self,
67 db: &'db dyn SourceDatabase,
68 def_map: &'db DefMap,
69 from_module: ModuleIdLt<'db>,
70 ) -> bool {
71 if cfg!(debug_assertions) {
72 _ = def_map.modules[from_module];
73 }
74 let to_module = match self {
75 Visibility::Module(m, _) => m,
76 Visibility::PubCrate(krate) => return from_module.krate(db) == krate,
77 Visibility::Public => return true,
78 };
79 if from_module == to_module {
80 return true;
82 }
83 if def_map.krate() != to_module.krate(db) {
85 return false;
86 }
87
88 if from_module == to_module && def_map.block_id() == to_module.block(db) {
89 return true;
91 }
92 Self::is_visible_from_def_map_(db, def_map, to_module, from_module)
93 }
94
95 fn is_visible_from_def_map_<'db>(
96 db: &'db dyn SourceDatabase,
97 def_map: &'db DefMap,
98 mut to_module: ModuleIdLt<'db>,
99 mut from_module: ModuleIdLt<'db>,
100 ) -> bool {
101 debug_assert_eq!(to_module.krate(db), def_map.krate());
102 let def_map_block = def_map.block_id();
110 loop {
111 match (to_module.block(db), def_map_block) {
112 (None, _) => (),
114 (Some(a), Some(b)) if a == b => {}
116 _ => {
117 if let Some(parent) = to_module.def_map(db).parent() {
118 to_module = parent;
119 continue;
120 }
121 }
122 }
123 break;
124 }
125
126 let mut def_map = def_map;
128 let mut parent_arc;
129 loop {
130 if from_module == to_module {
131 return true;
132 }
133 match def_map[from_module].parent {
134 Some(parent) => from_module = parent,
135 None => {
136 match def_map.parent() {
137 Some(module) => {
138 parent_arc = module.def_map(db);
139 def_map = parent_arc;
140 from_module = module;
141 }
142 None => return false,
144 }
145 }
146 }
147 }
148 }
149
150 pub(crate) fn max(
155 self,
156 db: &dyn SourceDatabase,
157 other: Visibility,
158 def_map: &DefMap,
159 ) -> Option<Visibility> {
160 match (self, other) {
161 (_, Visibility::Public) | (Visibility::Public, _) => Some(Visibility::Public),
162 (Visibility::PubCrate(krate), Visibility::PubCrate(krateb)) => {
163 if krate == krateb {
164 Some(Visibility::PubCrate(krate))
165 } else {
166 None
167 }
168 }
169 (Visibility::Module(mod_, _), Visibility::PubCrate(krate))
170 | (Visibility::PubCrate(krate), Visibility::Module(mod_, _)) => {
171 if mod_.krate(db) == krate { Some(Visibility::PubCrate(krate)) } else { None }
172 }
173 (Visibility::Module(mod_a, expl_a), Visibility::Module(mod_b, expl_b)) => {
174 if mod_a == mod_b {
175 return Some(Visibility::Module(
178 mod_a,
179 match (expl_a, expl_b) {
180 (VisibilityExplicitness::Explicit, _)
181 | (_, VisibilityExplicitness::Explicit) => {
182 VisibilityExplicitness::Explicit
183 }
184 _ => VisibilityExplicitness::Implicit,
185 },
186 ));
187 }
188
189 if mod_a.krate(db) != def_map.krate() || mod_b.krate(db) != def_map.krate() {
190 return None;
191 }
192
193 let def_block = def_map.block_id();
194 if mod_a.block(db) != def_block || mod_b.block(db) != def_block {
195 return None;
196 }
197
198 let mut a_ancestors = iter::successors(Some(mod_a), |&m| def_map[m].parent);
199
200 if a_ancestors.any(|m| m == mod_b) {
201 return Some(Visibility::Module(mod_b, expl_b));
203 }
204
205 let mut b_ancestors = iter::successors(Some(mod_b), |&m| def_map[m].parent);
206 if b_ancestors.any(|m| m == mod_a) {
207 return Some(Visibility::Module(mod_a, expl_a));
209 }
210
211 None
212 }
213 }
214 }
215
216 pub(crate) fn min(
221 self,
222 db: &dyn SourceDatabase,
223 other: Visibility,
224 def_map: &DefMap,
225 ) -> Option<Visibility> {
226 match (self, other) {
227 (vis, Visibility::Public) | (Visibility::Public, vis) => Some(vis),
228 (Visibility::PubCrate(krate), Visibility::PubCrate(krateb)) => {
229 if krate == krateb {
230 Some(Visibility::PubCrate(krate))
231 } else {
232 None
233 }
234 }
235 (Visibility::Module(mod_, exp), Visibility::PubCrate(krate))
236 | (Visibility::PubCrate(krate), Visibility::Module(mod_, exp)) => {
237 if mod_.krate(db) == krate { Some(Visibility::Module(mod_, exp)) } else { None }
238 }
239 (Visibility::Module(mod_a, expl_a), Visibility::Module(mod_b, expl_b)) => {
240 if mod_a == mod_b {
241 return Some(Visibility::Module(
244 mod_a,
245 match (expl_a, expl_b) {
246 (VisibilityExplicitness::Explicit, _)
247 | (_, VisibilityExplicitness::Explicit) => {
248 VisibilityExplicitness::Explicit
249 }
250 _ => VisibilityExplicitness::Implicit,
251 },
252 ));
253 }
254
255 if mod_a.krate(db) != def_map.krate() || mod_b.krate(db) != def_map.krate() {
256 return None;
257 }
258
259 let def_block = def_map.block_id();
260 if mod_a.block(db) != def_block || mod_b.block(db) != def_block {
261 return None;
262 }
263
264 let mut a_ancestors = iter::successors(Some(mod_a), |&m| def_map[m].parent);
265
266 if a_ancestors.any(|m| m == mod_b) {
267 return Some(Visibility::Module(mod_a, expl_a));
269 }
270
271 let mut b_ancestors = iter::successors(Some(mod_b), |&m| def_map[m].parent);
272 if b_ancestors.any(|m| m == mod_a) {
273 return Some(Visibility::Module(mod_b, expl_b));
275 }
276
277 None
278 }
279 }
280 }
281
282 #[inline]
283 pub fn is_public(&self) -> bool {
284 matches!(self, Visibility::Public)
285 }
286}
287
288#[salsa::tracked]
289impl VariantFields {
290 #[salsa::tracked(returns(ref))]
292 pub fn field_visibilities(
293 db: &dyn SourceDatabase,
294 variant_id: VariantId,
295 ) -> ArenaMap<LocalFieldId, Visibility> {
296 let variant_fields = variant_id.fields(db);
297 let fields = variant_fields.fields();
298 if fields.is_empty() {
299 return ArenaMap::default();
300 }
301 let resolver = variant_id.module(db).resolver(db);
302 let mut res = ArenaMap::with_capacity(fields.len());
303 for (field_id, field_data) in fields.iter() {
304 res.insert(field_id, Visibility::resolve(db, &resolver, &field_data.visibility));
305 }
306 res
307 }
308}
309
310pub fn visibility_from_ast(
311 db: &dyn SourceDatabase,
312 has_resolver: impl HasResolver + HasModule,
313 ast_vis: InFile<Option<ast::Visibility>>,
314) -> Visibility {
315 let mut span_map = None;
316 let raw_vis = crate::item_tree::visibility_from_ast(db, ast_vis.value, &mut |range| {
317 span_map.get_or_insert_with(|| ast_vis.file_id.span_map(db)).span_for_range(range).ctx
318 });
319 match raw_vis {
320 RawVisibility::PubSelf(explicitness) => {
321 Visibility::Module(has_resolver.module(db), explicitness)
322 }
323 RawVisibility::PubCrate => Visibility::PubCrate(has_resolver.krate(db)),
324 RawVisibility::Public => Visibility::Public,
325 RawVisibility::Module(..) => Visibility::resolve(db, &has_resolver.resolver(db), &raw_vis),
326 }
327}
328
329#[salsa::tracked]
330impl AssocItemId {
331 #[salsa::tracked(returns(copy))]
333 pub fn assoc_visibility(self, db: &dyn SourceDatabase) -> Visibility {
334 match self {
335 AssocItemId::FunctionId(function_id) => {
336 let loc = function_id.lookup(db);
337 trait_item_visibility(db, loc.container).unwrap_or_else(|| {
338 let source = loc.source(db);
339 visibility_from_ast(db, function_id, source.map(|src| src.visibility()))
340 })
341 }
342 AssocItemId::ConstId(const_id) => {
343 let loc = const_id.lookup(db);
344 trait_item_visibility(db, loc.container).unwrap_or_else(|| {
345 let source = loc.source(db);
346 visibility_from_ast(db, const_id, source.map(|src| src.visibility()))
347 })
348 }
349 AssocItemId::TypeAliasId(type_alias_id) => {
350 let loc = type_alias_id.lookup(db);
351 trait_item_visibility(db, loc.container).unwrap_or_else(|| {
352 let source = loc.source(db);
353 visibility_from_ast(db, type_alias_id, source.map(|src| src.visibility()))
354 })
355 }
356 }
357 }
358}
359
360fn trait_item_visibility(
361 db: &dyn SourceDatabase,
362 container: ItemContainerId,
363) -> Option<Visibility> {
364 match container {
365 ItemContainerId::TraitId(trait_) => Some(trait_visibility(db, trait_)),
366 _ => None,
367 }
368}
369
370fn trait_visibility(db: &dyn SourceDatabase, def: TraitId) -> Visibility {
371 let loc = def.lookup(db);
372 let source = loc.source(db);
373 visibility_from_ast(db, def, source.map(|src| src.visibility()))
374}