]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_middle/src/middle/privacy.rs
effective visibility: Satisfy borrow checker to use resolver lazily from a closure
[rust.git] / compiler / rustc_middle / src / middle / privacy.rs
1 //! A pass that checks to make sure private fields and methods aren't used
2 //! outside their scopes. This pass will also generate a set of exported items
3 //! which are available for use externally when compiled as a library.
4 use crate::ty::{DefIdTree, TyCtxt, Visibility};
5 use rustc_data_structures::fx::FxHashMap;
6 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
7 use rustc_hir::def::DefKind;
8 use rustc_macros::HashStable;
9 use rustc_query_system::ich::StableHashingContext;
10 use rustc_span::def_id::LocalDefId;
11 use std::hash::Hash;
12
13 /// Represents the levels of effective visibility an item can have.
14 ///
15 /// The variants are sorted in ascending order of directness.
16 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, HashStable)]
17 pub enum Level {
18     /// Superset of `Reachable` including items leaked through return position `impl Trait`.
19     ReachableThroughImplTrait,
20     /// Item is either reexported, or leaked through any kind of interface.
21     /// For example, if function `fn f() -> T {...}` is directly public, then type `T` is publicly
22     /// reachable and its values can be obtained by other crates even if the type itself is not
23     /// nameable.
24     Reachable,
25     /// Item is accessible either directly, or with help of `use` reexports.
26     Reexported,
27     /// Item is directly accessible, without help of reexports.
28     Direct,
29 }
30
31 impl Level {
32     pub fn all_levels() -> [Level; 4] {
33         [Level::Direct, Level::Reexported, Level::Reachable, Level::ReachableThroughImplTrait]
34     }
35 }
36
37 #[derive(Clone, Copy, PartialEq, Eq, Debug, HashStable)]
38 pub struct EffectiveVisibility {
39     direct: Visibility,
40     reexported: Visibility,
41     reachable: Visibility,
42     reachable_through_impl_trait: Visibility,
43 }
44
45 impl EffectiveVisibility {
46     pub fn at_level(&self, level: Level) -> &Visibility {
47         match level {
48             Level::Direct => &self.direct,
49             Level::Reexported => &self.reexported,
50             Level::Reachable => &self.reachable,
51             Level::ReachableThroughImplTrait => &self.reachable_through_impl_trait,
52         }
53     }
54
55     fn at_level_mut(&mut self, level: Level) -> &mut Visibility {
56         match level {
57             Level::Direct => &mut self.direct,
58             Level::Reexported => &mut self.reexported,
59             Level::Reachable => &mut self.reachable,
60             Level::ReachableThroughImplTrait => &mut self.reachable_through_impl_trait,
61         }
62     }
63
64     pub fn is_public_at_level(&self, level: Level) -> bool {
65         self.at_level(level).is_public()
66     }
67
68     pub fn from_vis(vis: Visibility) -> EffectiveVisibility {
69         EffectiveVisibility {
70             direct: vis,
71             reexported: vis,
72             reachable: vis,
73             reachable_through_impl_trait: vis,
74         }
75     }
76 }
77
78 /// Holds a map of effective visibilities for reachable HIR nodes.
79 #[derive(Clone, Debug)]
80 pub struct EffectiveVisibilities<Id = LocalDefId> {
81     map: FxHashMap<Id, EffectiveVisibility>,
82 }
83
84 impl EffectiveVisibilities {
85     pub fn is_public_at_level(&self, id: LocalDefId, level: Level) -> bool {
86         self.effective_vis(id)
87             .map_or(false, |effective_vis| effective_vis.is_public_at_level(level))
88     }
89
90     /// See `Level::Reachable`.
91     pub fn is_reachable(&self, id: LocalDefId) -> bool {
92         self.is_public_at_level(id, Level::Reachable)
93     }
94
95     /// See `Level::Reexported`.
96     pub fn is_exported(&self, id: LocalDefId) -> bool {
97         self.is_public_at_level(id, Level::Reexported)
98     }
99
100     /// See `Level::Direct`.
101     pub fn is_directly_public(&self, id: LocalDefId) -> bool {
102         self.is_public_at_level(id, Level::Direct)
103     }
104
105     pub fn public_at_level(&self, id: LocalDefId) -> Option<Level> {
106         self.effective_vis(id).and_then(|effective_vis| {
107             for level in Level::all_levels() {
108                 if effective_vis.is_public_at_level(level) {
109                     return Some(level);
110                 }
111             }
112             None
113         })
114     }
115
116     // FIXME: Share code with `fn update`.
117     pub fn update_eff_vis(
118         &mut self,
119         def_id: LocalDefId,
120         eff_vis: &EffectiveVisibility,
121         tree: impl DefIdTree,
122     ) {
123         use std::collections::hash_map::Entry;
124         match self.map.entry(def_id) {
125             Entry::Occupied(mut occupied) => {
126                 let old_eff_vis = occupied.get_mut();
127                 for l in Level::all_levels() {
128                     let vis_at_level = eff_vis.at_level(l);
129                     let old_vis_at_level = old_eff_vis.at_level_mut(l);
130                     if vis_at_level != old_vis_at_level
131                         && vis_at_level.is_at_least(*old_vis_at_level, tree)
132                     {
133                         *old_vis_at_level = *vis_at_level
134                     }
135                 }
136                 old_eff_vis
137             }
138             Entry::Vacant(vacant) => vacant.insert(*eff_vis),
139         };
140     }
141
142     pub fn set_public_at_level(
143         &mut self,
144         id: LocalDefId,
145         lazy_private_vis: impl FnOnce() -> Visibility,
146         level: Level,
147     ) {
148         let mut effective_vis = self
149             .effective_vis(id)
150             .copied()
151             .unwrap_or_else(|| EffectiveVisibility::from_vis(lazy_private_vis()));
152         for l in Level::all_levels() {
153             if l <= level {
154                 *effective_vis.at_level_mut(l) = Visibility::Public;
155             }
156         }
157         self.map.insert(id, effective_vis);
158     }
159
160     pub fn check_invariants(&self, tcx: TyCtxt<'_>, early: bool) {
161         if !cfg!(debug_assertions) {
162             return;
163         }
164         for (&def_id, ev) in &self.map {
165             // More direct visibility levels can never go farther than less direct ones,
166             // neither of effective visibilities can go farther than nominal visibility,
167             // and all effective visibilities are larger or equal than private visibility.
168             let private_vis = Visibility::Restricted(tcx.parent_module_from_def_id(def_id));
169             let span = tcx.def_span(def_id.to_def_id());
170             if !ev.direct.is_at_least(private_vis, tcx) {
171                 span_bug!(span, "private {:?} > direct {:?}", private_vis, ev.direct);
172             }
173             if !ev.reexported.is_at_least(ev.direct, tcx) {
174                 span_bug!(span, "direct {:?} > reexported {:?}", ev.direct, ev.reexported);
175             }
176             if !ev.reachable.is_at_least(ev.reexported, tcx) {
177                 span_bug!(span, "reexported {:?} > reachable {:?}", ev.reexported, ev.reachable);
178             }
179             if !ev.reachable_through_impl_trait.is_at_least(ev.reachable, tcx) {
180                 span_bug!(
181                     span,
182                     "reachable {:?} > reachable_through_impl_trait {:?}",
183                     ev.reachable,
184                     ev.reachable_through_impl_trait
185                 );
186             }
187             let nominal_vis = tcx.visibility(def_id);
188             let def_kind = tcx.opt_def_kind(def_id);
189             // FIXME: `rustc_privacy` is not yet updated for the new logic and can set
190             // effective visibilities that are larger than the nominal one.
191             if !nominal_vis.is_at_least(ev.reachable_through_impl_trait, tcx) && early {
192                 span_bug!(
193                     span,
194                     "{:?}: reachable_through_impl_trait {:?} > nominal {:?}",
195                     def_id,
196                     ev.reachable_through_impl_trait,
197                     nominal_vis
198                 );
199             }
200             // Fully private items are never put into the table, this is important for performance.
201             // FIXME: Fully private `mod` items are currently put into the table.
202             if ev.reachable_through_impl_trait == private_vis && def_kind != Some(DefKind::Mod) {
203                 span_bug!(span, "fully private item in the table {:?}: {:?}", def_id, ev.direct);
204             }
205         }
206     }
207 }
208
209 pub trait IntoDefIdTree {
210     type Tree: DefIdTree;
211     fn tree(self) -> Self::Tree;
212 }
213
214 impl<Id: Eq + Hash> EffectiveVisibilities<Id> {
215     pub fn iter(&self) -> impl Iterator<Item = (&Id, &EffectiveVisibility)> {
216         self.map.iter()
217     }
218
219     pub fn effective_vis(&self, id: Id) -> Option<&EffectiveVisibility> {
220         self.map.get(&id)
221     }
222
223     // `parent_id` is not necessarily a parent in source code tree,
224     // it is the node from which the maximum effective visibility is inherited.
225     pub fn update<T: IntoDefIdTree>(
226         &mut self,
227         id: Id,
228         nominal_vis: Visibility,
229         lazy_private_vis: impl FnOnce(T) -> (Visibility, T),
230         inherited_eff_vis: Option<EffectiveVisibility>,
231         level: Level,
232         mut into_tree: T,
233     ) -> bool {
234         let mut changed = false;
235         let mut current_effective_vis = match self.map.get(&id).copied() {
236             Some(eff_vis) => eff_vis,
237             None => {
238                 let private_vis;
239                 (private_vis, into_tree) = lazy_private_vis(into_tree);
240                 EffectiveVisibility::from_vis(private_vis)
241             }
242         };
243         let tree = into_tree.tree();
244
245         if let Some(inherited_effective_vis) = inherited_eff_vis {
246             let mut inherited_effective_vis_at_prev_level =
247                 *inherited_effective_vis.at_level(level);
248             let mut calculated_effective_vis = inherited_effective_vis_at_prev_level;
249             for l in Level::all_levels() {
250                 if level >= l {
251                     let inherited_effective_vis_at_level = *inherited_effective_vis.at_level(l);
252                     let current_effective_vis_at_level = current_effective_vis.at_level_mut(l);
253                     // effective visibility for id shouldn't be recalculated if
254                     // inherited from parent_id effective visibility isn't changed at next level
255                     if !(inherited_effective_vis_at_prev_level == inherited_effective_vis_at_level
256                         && level != l)
257                     {
258                         calculated_effective_vis =
259                             if nominal_vis.is_at_least(inherited_effective_vis_at_level, tree) {
260                                 inherited_effective_vis_at_level
261                             } else {
262                                 nominal_vis
263                             };
264                     }
265                     // effective visibility can't be decreased at next update call for the
266                     // same id
267                     if *current_effective_vis_at_level != calculated_effective_vis
268                         && calculated_effective_vis
269                             .is_at_least(*current_effective_vis_at_level, tree)
270                     {
271                         changed = true;
272                         *current_effective_vis_at_level = calculated_effective_vis;
273                     }
274                     inherited_effective_vis_at_prev_level = inherited_effective_vis_at_level;
275                 }
276             }
277         }
278         self.map.insert(id, current_effective_vis);
279         changed
280     }
281 }
282
283 impl<Id> Default for EffectiveVisibilities<Id> {
284     fn default() -> Self {
285         EffectiveVisibilities { map: Default::default() }
286     }
287 }
288
289 impl<'a> HashStable<StableHashingContext<'a>> for EffectiveVisibilities {
290     fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
291         let EffectiveVisibilities { ref map } = *self;
292         map.hash_stable(hcx, hasher);
293     }
294 }