]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_middle/src/middle/privacy.rs
Auto merge of #105613 - Nilstrieb:rename-assert_uninit_valid, r=RalfJung
[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             Level::all_levels().into_iter().find(|&level| effective_vis.is_public_at_level(level))
107         })
108     }
109
110     // FIXME: Share code with `fn update`.
111     pub fn update_eff_vis(
112         &mut self,
113         def_id: LocalDefId,
114         eff_vis: &EffectiveVisibility,
115         tree: impl DefIdTree,
116     ) {
117         use std::collections::hash_map::Entry;
118         match self.map.entry(def_id) {
119             Entry::Occupied(mut occupied) => {
120                 let old_eff_vis = occupied.get_mut();
121                 for l in Level::all_levels() {
122                     let vis_at_level = eff_vis.at_level(l);
123                     let old_vis_at_level = old_eff_vis.at_level_mut(l);
124                     if vis_at_level != old_vis_at_level
125                         && vis_at_level.is_at_least(*old_vis_at_level, tree)
126                     {
127                         *old_vis_at_level = *vis_at_level
128                     }
129                 }
130                 old_eff_vis
131             }
132             Entry::Vacant(vacant) => vacant.insert(*eff_vis),
133         };
134     }
135
136     pub fn set_public_at_level(
137         &mut self,
138         id: LocalDefId,
139         lazy_private_vis: impl FnOnce() -> Visibility,
140         level: Level,
141     ) {
142         let mut effective_vis = self
143             .effective_vis(id)
144             .copied()
145             .unwrap_or_else(|| EffectiveVisibility::from_vis(lazy_private_vis()));
146         for l in Level::all_levels() {
147             if l <= level {
148                 *effective_vis.at_level_mut(l) = Visibility::Public;
149             }
150         }
151         self.map.insert(id, effective_vis);
152     }
153
154     pub fn check_invariants(&self, tcx: TyCtxt<'_>, early: bool) {
155         if !cfg!(debug_assertions) {
156             return;
157         }
158         for (&def_id, ev) in &self.map {
159             // More direct visibility levels can never go farther than less direct ones,
160             // neither of effective visibilities can go farther than nominal visibility,
161             // and all effective visibilities are larger or equal than private visibility.
162             let private_vis = Visibility::Restricted(tcx.parent_module_from_def_id(def_id));
163             let span = tcx.def_span(def_id.to_def_id());
164             if !ev.direct.is_at_least(private_vis, tcx) {
165                 span_bug!(span, "private {:?} > direct {:?}", private_vis, ev.direct);
166             }
167             if !ev.reexported.is_at_least(ev.direct, tcx) {
168                 span_bug!(span, "direct {:?} > reexported {:?}", ev.direct, ev.reexported);
169             }
170             if !ev.reachable.is_at_least(ev.reexported, tcx) {
171                 span_bug!(span, "reexported {:?} > reachable {:?}", ev.reexported, ev.reachable);
172             }
173             if !ev.reachable_through_impl_trait.is_at_least(ev.reachable, tcx) {
174                 span_bug!(
175                     span,
176                     "reachable {:?} > reachable_through_impl_trait {:?}",
177                     ev.reachable,
178                     ev.reachable_through_impl_trait
179                 );
180             }
181             let nominal_vis = tcx.visibility(def_id);
182             // FIXME: `rustc_privacy` is not yet updated for the new logic and can set
183             // effective visibilities that are larger than the nominal one.
184             if !nominal_vis.is_at_least(ev.reachable_through_impl_trait, tcx) && early {
185                 span_bug!(
186                     span,
187                     "{:?}: reachable_through_impl_trait {:?} > nominal {:?}",
188                     def_id,
189                     ev.reachable_through_impl_trait,
190                     nominal_vis
191                 );
192             }
193         }
194     }
195 }
196
197 pub trait IntoDefIdTree {
198     type Tree: DefIdTree;
199     fn tree(self) -> Self::Tree;
200 }
201
202 impl<Id: Eq + Hash> EffectiveVisibilities<Id> {
203     pub fn iter(&self) -> impl Iterator<Item = (&Id, &EffectiveVisibility)> {
204         self.map.iter()
205     }
206
207     pub fn effective_vis(&self, id: Id) -> Option<&EffectiveVisibility> {
208         self.map.get(&id)
209     }
210
211     // FIXME: Share code with `fn update`.
212     pub fn effective_vis_or_private(
213         &mut self,
214         id: Id,
215         lazy_private_vis: impl FnOnce() -> Visibility,
216     ) -> &EffectiveVisibility {
217         self.map.entry(id).or_insert_with(|| EffectiveVisibility::from_vis(lazy_private_vis()))
218     }
219
220     pub fn update<T: IntoDefIdTree>(
221         &mut self,
222         id: Id,
223         nominal_vis: Visibility,
224         lazy_private_vis: impl FnOnce(T) -> (Visibility, T),
225         inherited_effective_vis: EffectiveVisibility,
226         level: Level,
227         mut into_tree: T,
228     ) -> bool {
229         let mut changed = false;
230         let mut current_effective_vis = match self.map.get(&id).copied() {
231             Some(eff_vis) => eff_vis,
232             None => {
233                 let private_vis;
234                 (private_vis, into_tree) = lazy_private_vis(into_tree);
235                 EffectiveVisibility::from_vis(private_vis)
236             }
237         };
238         let tree = into_tree.tree();
239
240         let mut inherited_effective_vis_at_prev_level = *inherited_effective_vis.at_level(level);
241         let mut calculated_effective_vis = inherited_effective_vis_at_prev_level;
242         for l in Level::all_levels() {
243             if level >= l {
244                 let inherited_effective_vis_at_level = *inherited_effective_vis.at_level(l);
245                 let current_effective_vis_at_level = current_effective_vis.at_level_mut(l);
246                 // effective visibility for id shouldn't be recalculated if
247                 // inherited from parent_id effective visibility isn't changed at next level
248                 if !(inherited_effective_vis_at_prev_level == inherited_effective_vis_at_level
249                     && level != l)
250                 {
251                     calculated_effective_vis =
252                         if nominal_vis.is_at_least(inherited_effective_vis_at_level, tree) {
253                             inherited_effective_vis_at_level
254                         } else {
255                             nominal_vis
256                         };
257                 }
258                 // effective visibility can't be decreased at next update call for the
259                 // same id
260                 if *current_effective_vis_at_level != calculated_effective_vis
261                     && calculated_effective_vis.is_at_least(*current_effective_vis_at_level, tree)
262                 {
263                     changed = true;
264                     *current_effective_vis_at_level = calculated_effective_vis;
265                 }
266                 inherited_effective_vis_at_prev_level = inherited_effective_vis_at_level;
267             }
268         }
269
270         self.map.insert(id, current_effective_vis);
271         changed
272     }
273 }
274
275 impl<Id> Default for EffectiveVisibilities<Id> {
276     fn default() -> Self {
277         EffectiveVisibilities { map: Default::default() }
278     }
279 }
280
281 impl<'a> HashStable<StableHashingContext<'a>> for EffectiveVisibilities {
282     fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
283         let EffectiveVisibilities { ref map } = *self;
284         map.hash_stable(hcx, hasher);
285     }
286 }