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;
12 /// Represents the levels of effective visibility an item can have.
14 /// The variants are sorted in ascending order of directness.
15 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, HashStable)]
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
24 /// Item is accessible either directly, or with help of `use` reexports.
26 /// Item is directly accessible, without help of reexports.
31 pub fn all_levels() -> [Level; 4] {
32 [Level::Direct, Level::Reexported, Level::Reachable, Level::ReachableThroughImplTrait]
36 #[derive(Clone, Copy, PartialEq, Eq, Debug, HashStable)]
37 pub struct EffectiveVisibility {
39 reexported: Visibility,
40 reachable: Visibility,
41 reachable_through_impl_trait: Visibility,
44 impl EffectiveVisibility {
45 pub fn at_level(&self, level: Level) -> &Visibility {
47 Level::Direct => &self.direct,
48 Level::Reexported => &self.reexported,
49 Level::Reachable => &self.reachable,
50 Level::ReachableThroughImplTrait => &self.reachable_through_impl_trait,
54 fn at_level_mut(&mut self, level: Level) -> &mut Visibility {
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,
63 pub fn is_public_at_level(&self, level: Level) -> bool {
64 self.at_level(level).is_public()
67 pub fn from_vis(vis: Visibility) -> EffectiveVisibility {
72 reachable_through_impl_trait: vis,
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>,
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))
89 /// See `Level::Reachable`.
90 pub fn is_reachable(&self, id: LocalDefId) -> bool {
91 self.is_public_at_level(id, Level::Reachable)
94 /// See `Level::Reexported`.
95 pub fn is_exported(&self, id: LocalDefId) -> bool {
96 self.is_public_at_level(id, Level::Reexported)
99 /// See `Level::Direct`.
100 pub fn is_directly_public(&self, id: LocalDefId) -> bool {
101 self.is_public_at_level(id, Level::Direct)
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))
110 // FIXME: Share code with `fn update`.
111 pub fn update_eff_vis(
114 eff_vis: &EffectiveVisibility,
115 tree: impl DefIdTree,
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)
127 *old_vis_at_level = *vis_at_level
132 Entry::Vacant(vacant) => vacant.insert(*eff_vis),
136 pub fn set_public_at_level(
139 lazy_private_vis: impl FnOnce() -> Visibility,
142 let mut effective_vis = self
145 .unwrap_or_else(|| EffectiveVisibility::from_vis(lazy_private_vis()));
146 for l in Level::all_levels() {
148 *effective_vis.at_level_mut(l) = Visibility::Public;
151 self.map.insert(id, effective_vis);
154 pub fn check_invariants(&self, tcx: TyCtxt<'_>, early: bool) {
155 if !cfg!(debug_assertions) {
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);
167 if !ev.reexported.is_at_least(ev.direct, tcx) {
168 span_bug!(span, "direct {:?} > reexported {:?}", ev.direct, ev.reexported);
170 if !ev.reachable.is_at_least(ev.reexported, tcx) {
171 span_bug!(span, "reexported {:?} > reachable {:?}", ev.reexported, ev.reachable);
173 if !ev.reachable_through_impl_trait.is_at_least(ev.reachable, tcx) {
176 "reachable {:?} > reachable_through_impl_trait {:?}",
178 ev.reachable_through_impl_trait
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 {
187 "{:?}: reachable_through_impl_trait {:?} > nominal {:?}",
189 ev.reachable_through_impl_trait,
197 pub trait IntoDefIdTree {
198 type Tree: DefIdTree;
199 fn tree(self) -> Self::Tree;
202 impl<Id: Eq + Hash> EffectiveVisibilities<Id> {
203 pub fn iter(&self) -> impl Iterator<Item = (&Id, &EffectiveVisibility)> {
207 pub fn effective_vis(&self, id: Id) -> Option<&EffectiveVisibility> {
211 // FIXME: Share code with `fn update`.
212 pub fn effective_vis_or_private(
215 lazy_private_vis: impl FnOnce() -> Visibility,
216 ) -> &EffectiveVisibility {
217 self.map.entry(id).or_insert_with(|| EffectiveVisibility::from_vis(lazy_private_vis()))
220 pub fn update<T: IntoDefIdTree>(
223 nominal_vis: Visibility,
224 lazy_private_vis: impl FnOnce(T) -> (Visibility, T),
225 inherited_effective_vis: EffectiveVisibility,
229 let mut changed = false;
230 let mut current_effective_vis = match self.map.get(&id).copied() {
231 Some(eff_vis) => eff_vis,
234 (private_vis, into_tree) = lazy_private_vis(into_tree);
235 EffectiveVisibility::from_vis(private_vis)
238 let tree = into_tree.tree();
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() {
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
251 calculated_effective_vis =
252 if nominal_vis.is_at_least(inherited_effective_vis_at_level, tree) {
253 inherited_effective_vis_at_level
258 // effective visibility can't be decreased at next update call for the
260 if *current_effective_vis_at_level != calculated_effective_vis
261 && calculated_effective_vis.is_at_least(*current_effective_vis_at_level, tree)
264 *current_effective_vis_at_level = calculated_effective_vis;
266 inherited_effective_vis_at_prev_level = inherited_effective_vis_at_level;
270 self.map.insert(id, current_effective_vis);
275 impl<Id> Default for EffectiveVisibilities<Id> {
276 fn default() -> Self {
277 EffectiveVisibilities { map: Default::default() }
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);