]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_middle/src/middle/privacy.rs
New upstream version 1.70.0+dfsg1
[rustc.git] / compiler / rustc_middle / src / middle / privacy.rs
CommitLineData
1a4d82fc
JJ
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.
353b0b11 4use crate::ty::{TyCtxt, Visibility};
dfeec247 5use rustc_data_structures::fx::FxHashMap;
c295e0f8 6use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
532ac7d7 7use rustc_macros::HashStable;
5e7ed085 8use rustc_query_system::ich::StableHashingContext;
353b0b11 9use rustc_span::def_id::{LocalDefId, CRATE_DEF_ID};
dfeec247 10use std::hash::Hash;
92a42be0 11
2b03887a 12/// Represents the levels of effective visibility an item can have.
fc512014 13///
2b03887a 14/// The variants are sorted in ascending order of directness.
532ac7d7 15#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, HashStable)]
2b03887a
FG
16pub 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.
92a42be0 23 Reachable,
2b03887a
FG
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
30impl 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)]
37pub struct EffectiveVisibility {
38 direct: Visibility,
39 reexported: Visibility,
40 reachable: Visibility,
41 reachable_through_impl_trait: Visibility,
42}
43
44impl 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 }
92a42be0
SL
75}
76
2b03887a 77/// Holds a map of effective visibilities for reachable HIR nodes.
487cf647 78#[derive(Clone, Debug)]
2b03887a
FG
79pub struct EffectiveVisibilities<Id = LocalDefId> {
80 map: FxHashMap<Id, EffectiveVisibility>,
92a42be0
SL
81}
82
487cf647
FG
83impl EffectiveVisibilities {
84 pub fn is_public_at_level(&self, id: LocalDefId, level: Level) -> bool {
2b03887a
FG
85 self.effective_vis(id)
86 .map_or(false, |effective_vis| effective_vis.is_public_at_level(level))
87 }
88
89 /// See `Level::Reachable`.
487cf647 90 pub fn is_reachable(&self, id: LocalDefId) -> bool {
2b03887a 91 self.is_public_at_level(id, Level::Reachable)
92a42be0 92 }
9fa01778 93
2b03887a 94 /// See `Level::Reexported`.
487cf647 95 pub fn is_exported(&self, id: LocalDefId) -> bool {
2b03887a
FG
96 self.is_public_at_level(id, Level::Reexported)
97 }
98
99 /// See `Level::Direct`.
487cf647 100 pub fn is_directly_public(&self, id: LocalDefId) -> bool {
2b03887a
FG
101 self.is_public_at_level(id, Level::Direct)
102 }
103
487cf647 104 pub fn public_at_level(&self, id: LocalDefId) -> Option<Level> {
2b03887a 105 self.effective_vis(id).and_then(|effective_vis| {
9c376795 106 Level::all_levels().into_iter().find(|&level| effective_vis.is_public_at_level(level))
2b03887a
FG
107 })
108 }
109
353b0b11
FG
110 pub fn update_root(&mut self) {
111 self.map.insert(CRATE_DEF_ID, EffectiveVisibility::from_vis(Visibility::Public));
112 }
113
487cf647
FG
114 // FIXME: Share code with `fn update`.
115 pub fn update_eff_vis(
116 &mut self,
117 def_id: LocalDefId,
118 eff_vis: &EffectiveVisibility,
353b0b11 119 tcx: TyCtxt<'_>,
487cf647
FG
120 ) {
121 use std::collections::hash_map::Entry;
122 match self.map.entry(def_id) {
123 Entry::Occupied(mut occupied) => {
124 let old_eff_vis = occupied.get_mut();
125 for l in Level::all_levels() {
126 let vis_at_level = eff_vis.at_level(l);
127 let old_vis_at_level = old_eff_vis.at_level_mut(l);
128 if vis_at_level != old_vis_at_level
353b0b11 129 && vis_at_level.is_at_least(*old_vis_at_level, tcx)
487cf647
FG
130 {
131 *old_vis_at_level = *vis_at_level
132 }
133 }
134 old_eff_vis
135 }
136 Entry::Vacant(vacant) => vacant.insert(*eff_vis),
137 };
2b03887a
FG
138 }
139
140 pub fn set_public_at_level(
141 &mut self,
487cf647
FG
142 id: LocalDefId,
143 lazy_private_vis: impl FnOnce() -> Visibility,
2b03887a
FG
144 level: Level,
145 ) {
146 let mut effective_vis = self
147 .effective_vis(id)
148 .copied()
487cf647 149 .unwrap_or_else(|| EffectiveVisibility::from_vis(lazy_private_vis()));
2b03887a
FG
150 for l in Level::all_levels() {
151 if l <= level {
152 *effective_vis.at_level_mut(l) = Visibility::Public;
153 }
154 }
155 self.map.insert(id, effective_vis);
156 }
487cf647
FG
157
158 pub fn check_invariants(&self, tcx: TyCtxt<'_>, early: bool) {
159 if !cfg!(debug_assertions) {
160 return;
161 }
162 for (&def_id, ev) in &self.map {
163 // More direct visibility levels can never go farther than less direct ones,
164 // neither of effective visibilities can go farther than nominal visibility,
165 // and all effective visibilities are larger or equal than private visibility.
166 let private_vis = Visibility::Restricted(tcx.parent_module_from_def_id(def_id));
167 let span = tcx.def_span(def_id.to_def_id());
168 if !ev.direct.is_at_least(private_vis, tcx) {
169 span_bug!(span, "private {:?} > direct {:?}", private_vis, ev.direct);
170 }
171 if !ev.reexported.is_at_least(ev.direct, tcx) {
172 span_bug!(span, "direct {:?} > reexported {:?}", ev.direct, ev.reexported);
173 }
174 if !ev.reachable.is_at_least(ev.reexported, tcx) {
175 span_bug!(span, "reexported {:?} > reachable {:?}", ev.reexported, ev.reachable);
176 }
177 if !ev.reachable_through_impl_trait.is_at_least(ev.reachable, tcx) {
178 span_bug!(
179 span,
180 "reachable {:?} > reachable_through_impl_trait {:?}",
181 ev.reachable,
182 ev.reachable_through_impl_trait
183 );
184 }
185 let nominal_vis = tcx.visibility(def_id);
186 // FIXME: `rustc_privacy` is not yet updated for the new logic and can set
187 // effective visibilities that are larger than the nominal one.
188 if !nominal_vis.is_at_least(ev.reachable_through_impl_trait, tcx) && early {
189 span_bug!(
190 span,
191 "{:?}: reachable_through_impl_trait {:?} > nominal {:?}",
192 def_id,
193 ev.reachable_through_impl_trait,
194 nominal_vis
195 );
196 }
197 }
198 }
199}
200
487cf647
FG
201impl<Id: Eq + Hash> EffectiveVisibilities<Id> {
202 pub fn iter(&self) -> impl Iterator<Item = (&Id, &EffectiveVisibility)> {
203 self.map.iter()
204 }
205
206 pub fn effective_vis(&self, id: Id) -> Option<&EffectiveVisibility> {
207 self.map.get(&id)
208 }
209
210 // FIXME: Share code with `fn update`.
211 pub fn effective_vis_or_private(
212 &mut self,
213 id: Id,
214 lazy_private_vis: impl FnOnce() -> Visibility,
215 ) -> &EffectiveVisibility {
216 self.map.entry(id).or_insert_with(|| EffectiveVisibility::from_vis(lazy_private_vis()))
217 }
218
9ffffee4 219 pub fn update(
2b03887a
FG
220 &mut self,
221 id: Id,
222 nominal_vis: Visibility,
9ffffee4 223 lazy_private_vis: impl FnOnce() -> Visibility,
487cf647 224 inherited_effective_vis: EffectiveVisibility,
2b03887a 225 level: Level,
353b0b11 226 tcx: TyCtxt<'_>,
2b03887a
FG
227 ) -> bool {
228 let mut changed = false;
9ffffee4
FG
229 let mut current_effective_vis = self
230 .map
231 .get(&id)
232 .copied()
233 .unwrap_or_else(|| EffectiveVisibility::from_vis(lazy_private_vis()));
487cf647
FG
234
235 let mut inherited_effective_vis_at_prev_level = *inherited_effective_vis.at_level(level);
236 let mut calculated_effective_vis = inherited_effective_vis_at_prev_level;
237 for l in Level::all_levels() {
238 if level >= l {
239 let inherited_effective_vis_at_level = *inherited_effective_vis.at_level(l);
240 let current_effective_vis_at_level = current_effective_vis.at_level_mut(l);
241 // effective visibility for id shouldn't be recalculated if
242 // inherited from parent_id effective visibility isn't changed at next level
243 if !(inherited_effective_vis_at_prev_level == inherited_effective_vis_at_level
244 && level != l)
245 {
246 calculated_effective_vis =
353b0b11 247 if nominal_vis.is_at_least(inherited_effective_vis_at_level, tcx) {
487cf647
FG
248 inherited_effective_vis_at_level
249 } else {
250 nominal_vis
251 };
252 }
253 // effective visibility can't be decreased at next update call for the
254 // same id
255 if *current_effective_vis_at_level != calculated_effective_vis
353b0b11 256 && calculated_effective_vis.is_at_least(*current_effective_vis_at_level, tcx)
487cf647
FG
257 {
258 changed = true;
259 *current_effective_vis_at_level = calculated_effective_vis;
2b03887a 260 }
487cf647 261 inherited_effective_vis_at_prev_level = inherited_effective_vis_at_level;
2b03887a
FG
262 }
263 }
487cf647 264
2b03887a
FG
265 self.map.insert(id, current_effective_vis);
266 changed
92a42be0
SL
267 }
268}
269
2b03887a 270impl<Id> Default for EffectiveVisibilities<Id> {
92a42be0 271 fn default() -> Self {
2b03887a 272 EffectiveVisibilities { map: Default::default() }
92a42be0
SL
273 }
274}
c295e0f8 275
2b03887a 276impl<'a> HashStable<StableHashingContext<'a>> for EffectiveVisibilities {
c295e0f8 277 fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
2b03887a 278 let EffectiveVisibilities { ref map } = *self;
5e7ed085 279 map.hash_stable(hcx, hasher);
c295e0f8
XL
280 }
281}