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