]>
Commit | Line | Data |
---|---|---|
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. | |
487cf647 | 4 | use crate::ty::{DefIdTree, TyCtxt, Visibility}; |
dfeec247 | 5 | use rustc_data_structures::fx::FxHashMap; |
c295e0f8 | 6 | use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; |
532ac7d7 | 7 | use rustc_macros::HashStable; |
5e7ed085 | 8 | use rustc_query_system::ich::StableHashingContext; |
487cf647 | 9 | use rustc_span::def_id::LocalDefId; |
dfeec247 | 10 | use 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 |
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. | |
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 | ||
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 | } | |
92a42be0 SL |
75 | } |
76 | ||
2b03887a | 77 | /// Holds a map of effective visibilities for reachable HIR nodes. |
487cf647 | 78 | #[derive(Clone, Debug)] |
2b03887a FG |
79 | pub struct EffectiveVisibilities<Id = LocalDefId> { |
80 | map: FxHashMap<Id, EffectiveVisibility>, | |
92a42be0 SL |
81 | } |
82 | ||
487cf647 FG |
83 | impl 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 | ||
487cf647 FG |
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 | }; | |
2b03887a FG |
134 | } |
135 | ||
136 | pub fn set_public_at_level( | |
137 | &mut self, | |
487cf647 FG |
138 | id: LocalDefId, |
139 | lazy_private_vis: impl FnOnce() -> Visibility, | |
2b03887a FG |
140 | level: Level, |
141 | ) { | |
142 | let mut effective_vis = self | |
143 | .effective_vis(id) | |
144 | .copied() | |
487cf647 | 145 | .unwrap_or_else(|| EffectiveVisibility::from_vis(lazy_private_vis())); |
2b03887a FG |
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 | } | |
487cf647 FG |
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 | ||
487cf647 FG |
197 | impl<Id: Eq + Hash> EffectiveVisibilities<Id> { |
198 | pub fn iter(&self) -> impl Iterator<Item = (&Id, &EffectiveVisibility)> { | |
199 | self.map.iter() | |
200 | } | |
201 | ||
202 | pub fn effective_vis(&self, id: Id) -> Option<&EffectiveVisibility> { | |
203 | self.map.get(&id) | |
204 | } | |
205 | ||
206 | // FIXME: Share code with `fn update`. | |
207 | pub fn effective_vis_or_private( | |
208 | &mut self, | |
209 | id: Id, | |
210 | lazy_private_vis: impl FnOnce() -> Visibility, | |
211 | ) -> &EffectiveVisibility { | |
212 | self.map.entry(id).or_insert_with(|| EffectiveVisibility::from_vis(lazy_private_vis())) | |
213 | } | |
214 | ||
9ffffee4 | 215 | pub fn update( |
2b03887a FG |
216 | &mut self, |
217 | id: Id, | |
218 | nominal_vis: Visibility, | |
9ffffee4 | 219 | lazy_private_vis: impl FnOnce() -> Visibility, |
487cf647 | 220 | inherited_effective_vis: EffectiveVisibility, |
2b03887a | 221 | level: Level, |
9ffffee4 | 222 | tree: impl DefIdTree, |
2b03887a FG |
223 | ) -> bool { |
224 | let mut changed = false; | |
9ffffee4 FG |
225 | let mut current_effective_vis = self |
226 | .map | |
227 | .get(&id) | |
228 | .copied() | |
229 | .unwrap_or_else(|| EffectiveVisibility::from_vis(lazy_private_vis())); | |
487cf647 FG |
230 | |
231 | let mut inherited_effective_vis_at_prev_level = *inherited_effective_vis.at_level(level); | |
232 | let mut calculated_effective_vis = inherited_effective_vis_at_prev_level; | |
233 | for l in Level::all_levels() { | |
234 | if level >= l { | |
235 | let inherited_effective_vis_at_level = *inherited_effective_vis.at_level(l); | |
236 | let current_effective_vis_at_level = current_effective_vis.at_level_mut(l); | |
237 | // effective visibility for id shouldn't be recalculated if | |
238 | // inherited from parent_id effective visibility isn't changed at next level | |
239 | if !(inherited_effective_vis_at_prev_level == inherited_effective_vis_at_level | |
240 | && level != l) | |
241 | { | |
242 | calculated_effective_vis = | |
243 | if nominal_vis.is_at_least(inherited_effective_vis_at_level, tree) { | |
244 | inherited_effective_vis_at_level | |
245 | } else { | |
246 | nominal_vis | |
247 | }; | |
248 | } | |
249 | // effective visibility can't be decreased at next update call for the | |
250 | // same id | |
251 | if *current_effective_vis_at_level != calculated_effective_vis | |
252 | && calculated_effective_vis.is_at_least(*current_effective_vis_at_level, tree) | |
253 | { | |
254 | changed = true; | |
255 | *current_effective_vis_at_level = calculated_effective_vis; | |
2b03887a | 256 | } |
487cf647 | 257 | inherited_effective_vis_at_prev_level = inherited_effective_vis_at_level; |
2b03887a FG |
258 | } |
259 | } | |
487cf647 | 260 | |
2b03887a FG |
261 | self.map.insert(id, current_effective_vis); |
262 | changed | |
92a42be0 SL |
263 | } |
264 | } | |
265 | ||
2b03887a | 266 | impl<Id> Default for EffectiveVisibilities<Id> { |
92a42be0 | 267 | fn default() -> Self { |
2b03887a | 268 | EffectiveVisibilities { map: Default::default() } |
92a42be0 SL |
269 | } |
270 | } | |
c295e0f8 | 271 | |
2b03887a | 272 | impl<'a> HashStable<StableHashingContext<'a>> for EffectiveVisibilities { |
c295e0f8 | 273 | fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { |
2b03887a | 274 | let EffectiveVisibilities { ref map } = *self; |
5e7ed085 | 275 | map.hash_stable(hcx, hasher); |
c295e0f8 XL |
276 | } |
277 | } |