• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! Defines hir-level representation of visibility (e.g. `pub` and `pub(crate)`).
2 
3 use std::iter;
4 
5 use hir_expand::{hygiene::Hygiene, InFile};
6 use la_arena::ArenaMap;
7 use syntax::ast;
8 use triomphe::Arc;
9 
10 use crate::{
11     db::DefDatabase,
12     nameres::DefMap,
13     path::{ModPath, PathKind},
14     resolver::HasResolver,
15     ConstId, FunctionId, HasModule, LocalFieldId, LocalModuleId, ModuleId, VariantId,
16 };
17 
18 /// Visibility of an item, not yet resolved.
19 #[derive(Debug, Clone, PartialEq, Eq)]
20 pub enum RawVisibility {
21     /// `pub(in module)`, `pub(crate)` or `pub(super)`. Also private, which is
22     /// equivalent to `pub(self)`.
23     Module(ModPath),
24     /// `pub`.
25     Public,
26 }
27 
28 impl RawVisibility {
private() -> RawVisibility29     pub(crate) const fn private() -> RawVisibility {
30         RawVisibility::Module(ModPath::from_kind(PathKind::Super(0)))
31     }
32 
from_ast( db: &dyn DefDatabase, node: InFile<Option<ast::Visibility>>, ) -> RawVisibility33     pub(crate) fn from_ast(
34         db: &dyn DefDatabase,
35         node: InFile<Option<ast::Visibility>>,
36     ) -> RawVisibility {
37         Self::from_ast_with_hygiene(db, node.value, &Hygiene::new(db.upcast(), node.file_id))
38     }
39 
from_ast_with_hygiene( db: &dyn DefDatabase, node: Option<ast::Visibility>, hygiene: &Hygiene, ) -> RawVisibility40     pub(crate) fn from_ast_with_hygiene(
41         db: &dyn DefDatabase,
42         node: Option<ast::Visibility>,
43         hygiene: &Hygiene,
44     ) -> RawVisibility {
45         Self::from_ast_with_hygiene_and_default(db, node, RawVisibility::private(), hygiene)
46     }
47 
from_ast_with_hygiene_and_default( db: &dyn DefDatabase, node: Option<ast::Visibility>, default: RawVisibility, hygiene: &Hygiene, ) -> RawVisibility48     pub(crate) fn from_ast_with_hygiene_and_default(
49         db: &dyn DefDatabase,
50         node: Option<ast::Visibility>,
51         default: RawVisibility,
52         hygiene: &Hygiene,
53     ) -> RawVisibility {
54         let node = match node {
55             None => return default,
56             Some(node) => node,
57         };
58         match node.kind() {
59             ast::VisibilityKind::In(path) => {
60                 let path = ModPath::from_src(db.upcast(), path, hygiene);
61                 let path = match path {
62                     None => return RawVisibility::private(),
63                     Some(path) => path,
64                 };
65                 RawVisibility::Module(path)
66             }
67             ast::VisibilityKind::PubCrate => {
68                 let path = ModPath::from_kind(PathKind::Crate);
69                 RawVisibility::Module(path)
70             }
71             ast::VisibilityKind::PubSuper => {
72                 let path = ModPath::from_kind(PathKind::Super(1));
73                 RawVisibility::Module(path)
74             }
75             ast::VisibilityKind::PubSelf => {
76                 let path = ModPath::from_kind(PathKind::Plain);
77                 RawVisibility::Module(path)
78             }
79             ast::VisibilityKind::Pub => RawVisibility::Public,
80         }
81     }
82 
resolve( &self, db: &dyn DefDatabase, resolver: &crate::resolver::Resolver, ) -> Visibility83     pub fn resolve(
84         &self,
85         db: &dyn DefDatabase,
86         resolver: &crate::resolver::Resolver,
87     ) -> Visibility {
88         // we fall back to public visibility (i.e. fail open) if the path can't be resolved
89         resolver.resolve_visibility(db, self).unwrap_or(Visibility::Public)
90     }
91 }
92 
93 /// Visibility of an item, with the path resolved.
94 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
95 pub enum Visibility {
96     /// Visibility is restricted to a certain module.
97     Module(ModuleId),
98     /// Visibility is unrestricted.
99     Public,
100 }
101 
102 impl Visibility {
is_visible_from(self, db: &dyn DefDatabase, from_module: ModuleId) -> bool103     pub fn is_visible_from(self, db: &dyn DefDatabase, from_module: ModuleId) -> bool {
104         let to_module = match self {
105             Visibility::Module(m) => m,
106             Visibility::Public => return true,
107         };
108         // if they're not in the same crate, it can't be visible
109         if from_module.krate != to_module.krate {
110             return false;
111         }
112         let def_map = from_module.def_map(db);
113         self.is_visible_from_def_map(db, &def_map, from_module.local_id)
114     }
115 
is_visible_from_other_crate(self) -> bool116     pub(crate) fn is_visible_from_other_crate(self) -> bool {
117         matches!(self, Visibility::Public)
118     }
119 
is_visible_from_def_map( self, db: &dyn DefDatabase, def_map: &DefMap, mut from_module: LocalModuleId, ) -> bool120     pub(crate) fn is_visible_from_def_map(
121         self,
122         db: &dyn DefDatabase,
123         def_map: &DefMap,
124         mut from_module: LocalModuleId,
125     ) -> bool {
126         let mut to_module = match self {
127             Visibility::Module(m) => m,
128             Visibility::Public => return true,
129         };
130 
131         // `to_module` might be the root module of a block expression. Those have the same
132         // visibility as the containing module (even though no items are directly nameable from
133         // there, getting this right is important for method resolution).
134         // In that case, we adjust the visibility of `to_module` to point to the containing module.
135 
136         // Additional complication: `to_module` might be in `from_module`'s `DefMap`, which we're
137         // currently computing, so we must not call the `def_map` query for it.
138         let mut arc;
139         loop {
140             let to_module_def_map =
141                 if to_module.krate == def_map.krate() && to_module.block == def_map.block_id() {
142                     cov_mark::hit!(is_visible_from_same_block_def_map);
143                     def_map
144                 } else {
145                     arc = to_module.def_map(db);
146                     &arc
147                 };
148             match to_module_def_map.parent() {
149                 Some(parent) => to_module = parent,
150                 None => break,
151             }
152         }
153 
154         // from_module needs to be a descendant of to_module
155         let mut def_map = def_map;
156         let mut parent_arc;
157         loop {
158             if def_map.module_id(from_module) == to_module {
159                 return true;
160             }
161             match def_map[from_module].parent {
162                 Some(parent) => from_module = parent,
163                 None => {
164                     match def_map.parent() {
165                         Some(module) => {
166                             parent_arc = module.def_map(db);
167                             def_map = &*parent_arc;
168                             from_module = module.local_id;
169                         }
170                         // Reached the root module, nothing left to check.
171                         None => return false,
172                     }
173                 }
174             }
175         }
176     }
177 
178     /// Returns the most permissive visibility of `self` and `other`.
179     ///
180     /// If there is no subset relation between `self` and `other`, returns `None` (ie. they're only
181     /// visible in unrelated modules).
max(self, other: Visibility, def_map: &DefMap) -> Option<Visibility>182     pub(crate) fn max(self, other: Visibility, def_map: &DefMap) -> Option<Visibility> {
183         match (self, other) {
184             (Visibility::Module(_) | Visibility::Public, Visibility::Public)
185             | (Visibility::Public, Visibility::Module(_)) => Some(Visibility::Public),
186             (Visibility::Module(mod_a), Visibility::Module(mod_b)) => {
187                 if mod_a.krate != mod_b.krate {
188                     return None;
189                 }
190 
191                 let mut a_ancestors = iter::successors(Some(mod_a.local_id), |&m| {
192                     let parent_id = def_map[m].parent?;
193                     Some(parent_id)
194                 });
195                 let mut b_ancestors = iter::successors(Some(mod_b.local_id), |&m| {
196                     let parent_id = def_map[m].parent?;
197                     Some(parent_id)
198                 });
199 
200                 if a_ancestors.any(|m| m == mod_b.local_id) {
201                     // B is above A
202                     return Some(Visibility::Module(mod_b));
203                 }
204 
205                 if b_ancestors.any(|m| m == mod_a.local_id) {
206                     // A is above B
207                     return Some(Visibility::Module(mod_a));
208                 }
209 
210                 None
211             }
212         }
213     }
214 }
215 
216 /// Resolve visibility of all specific fields of a struct or union variant.
field_visibilities_query( db: &dyn DefDatabase, variant_id: VariantId, ) -> Arc<ArenaMap<LocalFieldId, Visibility>>217 pub(crate) fn field_visibilities_query(
218     db: &dyn DefDatabase,
219     variant_id: VariantId,
220 ) -> Arc<ArenaMap<LocalFieldId, Visibility>> {
221     let var_data = match variant_id {
222         VariantId::StructId(it) => db.struct_data(it).variant_data.clone(),
223         VariantId::UnionId(it) => db.union_data(it).variant_data.clone(),
224         VariantId::EnumVariantId(it) => {
225             db.enum_data(it.parent).variants[it.local_id].variant_data.clone()
226         }
227     };
228     let resolver = variant_id.module(db).resolver(db);
229     let mut res = ArenaMap::default();
230     for (field_id, field_data) in var_data.fields().iter() {
231         res.insert(field_id, field_data.visibility.resolve(db, &resolver));
232     }
233     Arc::new(res)
234 }
235 
236 /// Resolve visibility of a function.
function_visibility_query(db: &dyn DefDatabase, def: FunctionId) -> Visibility237 pub(crate) fn function_visibility_query(db: &dyn DefDatabase, def: FunctionId) -> Visibility {
238     let resolver = def.resolver(db);
239     db.function_data(def).visibility.resolve(db, &resolver)
240 }
241 
242 /// Resolve visibility of a const.
const_visibility_query(db: &dyn DefDatabase, def: ConstId) -> Visibility243 pub(crate) fn const_visibility_query(db: &dyn DefDatabase, def: ConstId) -> Visibility {
244     let resolver = def.resolver(db);
245     db.const_data(def).visibility.resolve(db, &resolver)
246 }
247