• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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::{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, CRATE_DEF_ID};
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 {
all_levels() -> [Level; 4]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 {
at_level(&self, level: Level) -> &Visibility46     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 
at_level_mut(&mut self, level: Level) -> &mut Visibility55     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 
is_public_at_level(&self, level: Level) -> bool64     pub fn is_public_at_level(&self, level: Level) -> bool {
65         self.at_level(level).is_public()
66     }
67 
from_vis(vis: Visibility) -> EffectiveVisibility68     pub const 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     #[must_use]
min(mut self, lhs: EffectiveVisibility, tcx: TyCtxt<'_>) -> Self78     pub fn min(mut self, lhs: EffectiveVisibility, tcx: TyCtxt<'_>) -> Self {
79         for l in Level::all_levels() {
80             let rhs_vis = self.at_level_mut(l);
81             let lhs_vis = *lhs.at_level(l);
82             if rhs_vis.is_at_least(lhs_vis, tcx) {
83                 *rhs_vis = lhs_vis;
84             };
85         }
86         self
87     }
88 }
89 
90 /// Holds a map of effective visibilities for reachable HIR nodes.
91 #[derive(Clone, Debug)]
92 pub struct EffectiveVisibilities<Id = LocalDefId> {
93     map: FxHashMap<Id, EffectiveVisibility>,
94 }
95 
96 impl EffectiveVisibilities {
is_public_at_level(&self, id: LocalDefId, level: Level) -> bool97     pub fn is_public_at_level(&self, id: LocalDefId, level: Level) -> bool {
98         self.effective_vis(id).is_some_and(|effective_vis| effective_vis.is_public_at_level(level))
99     }
100 
101     /// See `Level::Reachable`.
is_reachable(&self, id: LocalDefId) -> bool102     pub fn is_reachable(&self, id: LocalDefId) -> bool {
103         self.is_public_at_level(id, Level::Reachable)
104     }
105 
106     /// See `Level::Reexported`.
is_exported(&self, id: LocalDefId) -> bool107     pub fn is_exported(&self, id: LocalDefId) -> bool {
108         self.is_public_at_level(id, Level::Reexported)
109     }
110 
111     /// See `Level::Direct`.
is_directly_public(&self, id: LocalDefId) -> bool112     pub fn is_directly_public(&self, id: LocalDefId) -> bool {
113         self.is_public_at_level(id, Level::Direct)
114     }
115 
public_at_level(&self, id: LocalDefId) -> Option<Level>116     pub fn public_at_level(&self, id: LocalDefId) -> Option<Level> {
117         self.effective_vis(id).and_then(|effective_vis| {
118             Level::all_levels().into_iter().find(|&level| effective_vis.is_public_at_level(level))
119         })
120     }
121 
update_root(&mut self)122     pub fn update_root(&mut self) {
123         self.map.insert(CRATE_DEF_ID, EffectiveVisibility::from_vis(Visibility::Public));
124     }
125 
126     // FIXME: Share code with `fn update`.
update_eff_vis( &mut self, def_id: LocalDefId, eff_vis: &EffectiveVisibility, tcx: TyCtxt<'_>, )127     pub fn update_eff_vis(
128         &mut self,
129         def_id: LocalDefId,
130         eff_vis: &EffectiveVisibility,
131         tcx: TyCtxt<'_>,
132     ) {
133         use std::collections::hash_map::Entry;
134         match self.map.entry(def_id) {
135             Entry::Occupied(mut occupied) => {
136                 let old_eff_vis = occupied.get_mut();
137                 for l in Level::all_levels() {
138                     let vis_at_level = eff_vis.at_level(l);
139                     let old_vis_at_level = old_eff_vis.at_level_mut(l);
140                     if vis_at_level != old_vis_at_level
141                         && vis_at_level.is_at_least(*old_vis_at_level, tcx)
142                     {
143                         *old_vis_at_level = *vis_at_level
144                     }
145                 }
146                 old_eff_vis
147             }
148             Entry::Vacant(vacant) => vacant.insert(*eff_vis),
149         };
150     }
151 
check_invariants(&self, tcx: TyCtxt<'_>)152     pub fn check_invariants(&self, tcx: TyCtxt<'_>) {
153         if !cfg!(debug_assertions) {
154             return;
155         }
156         for (&def_id, ev) in &self.map {
157             // More direct visibility levels can never go farther than less direct ones,
158             // and all effective visibilities are larger or equal than private visibility.
159             let private_vis = Visibility::Restricted(tcx.parent_module_from_def_id(def_id));
160             let span = tcx.def_span(def_id.to_def_id());
161             if !ev.direct.is_at_least(private_vis, tcx) {
162                 span_bug!(span, "private {:?} > direct {:?}", private_vis, ev.direct);
163             }
164             if !ev.reexported.is_at_least(ev.direct, tcx) {
165                 span_bug!(span, "direct {:?} > reexported {:?}", ev.direct, ev.reexported);
166             }
167             if !ev.reachable.is_at_least(ev.reexported, tcx) {
168                 span_bug!(span, "reexported {:?} > reachable {:?}", ev.reexported, ev.reachable);
169             }
170             if !ev.reachable_through_impl_trait.is_at_least(ev.reachable, tcx) {
171                 span_bug!(
172                     span,
173                     "reachable {:?} > reachable_through_impl_trait {:?}",
174                     ev.reachable,
175                     ev.reachable_through_impl_trait
176                 );
177             }
178             // All effective visibilities except `reachable_through_impl_trait` are limited to
179             // nominal visibility. For some items nominal visibility doesn't make sense so we
180             // don't check this condition for them.
181             if !matches!(tcx.def_kind(def_id), DefKind::Impl { .. }) {
182                 let nominal_vis = tcx.visibility(def_id);
183                 if !nominal_vis.is_at_least(ev.reachable, tcx) {
184                     span_bug!(
185                         span,
186                         "{:?}: reachable {:?} > nominal {:?}",
187                         def_id,
188                         ev.reachable,
189                         nominal_vis
190                     );
191                 }
192             }
193         }
194     }
195 }
196 
197 impl<Id: Eq + Hash> EffectiveVisibilities<Id> {
iter(&self) -> impl Iterator<Item = (&Id, &EffectiveVisibility)>198     pub fn iter(&self) -> impl Iterator<Item = (&Id, &EffectiveVisibility)> {
199         self.map.iter()
200     }
201 
effective_vis(&self, id: Id) -> Option<&EffectiveVisibility>202     pub fn effective_vis(&self, id: Id) -> Option<&EffectiveVisibility> {
203         self.map.get(&id)
204     }
205 
206     // FIXME: Share code with `fn update`.
effective_vis_or_private( &mut self, id: Id, lazy_private_vis: impl FnOnce() -> Visibility, ) -> &EffectiveVisibility207     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 
update( &mut self, id: Id, max_vis: Option<Visibility>, lazy_private_vis: impl FnOnce() -> Visibility, inherited_effective_vis: EffectiveVisibility, level: Level, tcx: TyCtxt<'_>, ) -> bool215     pub fn update(
216         &mut self,
217         id: Id,
218         max_vis: Option<Visibility>,
219         lazy_private_vis: impl FnOnce() -> Visibility,
220         inherited_effective_vis: EffectiveVisibility,
221         level: Level,
222         tcx: TyCtxt<'_>,
223     ) -> bool {
224         let mut changed = false;
225         let mut current_effective_vis = self
226             .map
227             .get(&id)
228             .copied()
229             .unwrap_or_else(|| EffectiveVisibility::from_vis(lazy_private_vis()));
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 = if let Some(max_vis) = max_vis && !max_vis.is_at_least(inherited_effective_vis_at_level, tcx) {
243                         max_vis
244                     } else {
245                         inherited_effective_vis_at_level
246                     }
247                 }
248                 // effective visibility can't be decreased at next update call for the
249                 // same id
250                 if *current_effective_vis_at_level != calculated_effective_vis
251                     && calculated_effective_vis.is_at_least(*current_effective_vis_at_level, tcx)
252                 {
253                     changed = true;
254                     *current_effective_vis_at_level = calculated_effective_vis;
255                 }
256                 inherited_effective_vis_at_prev_level = inherited_effective_vis_at_level;
257             }
258         }
259 
260         self.map.insert(id, current_effective_vis);
261         changed
262     }
263 }
264 
265 impl<Id> Default for EffectiveVisibilities<Id> {
default() -> Self266     fn default() -> Self {
267         EffectiveVisibilities { map: Default::default() }
268     }
269 }
270 
271 impl<'a> HashStable<StableHashingContext<'a>> for EffectiveVisibilities {
hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher)272     fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
273         let EffectiveVisibilities { ref map } = *self;
274         map.hash_stable(hcx, hasher);
275     }
276 }
277