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_hir::def::DefKind;
8 use rustc_macros::HashStable;
9 use rustc_query_system::ich::StableHashingContext;
10 use rustc_span::def_id::LocalDefId;
13 /// Represents the levels of effective visibility an item can have.
15 /// The variants are sorted in ascending order of directness.
16 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, HashStable)]
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
25 /// Item is accessible either directly, or with help of `use` reexports.
27 /// Item is directly accessible, without help of reexports.
32 pub fn all_levels() -> [Level; 4] {
33 [Level::Direct, Level::Reexported, Level::Reachable, Level::ReachableThroughImplTrait]
37 #[derive(Clone, Copy, PartialEq, Eq, Debug, HashStable)]
38 pub struct EffectiveVisibility {
40 reexported: Visibility,
41 reachable: Visibility,
42 reachable_through_impl_trait: Visibility,
45 impl EffectiveVisibility {
46 pub fn at_level(&self, level: Level) -> &Visibility {
48 Level::Direct => &self.direct,
49 Level::Reexported => &self.reexported,
50 Level::Reachable => &self.reachable,
51 Level::ReachableThroughImplTrait => &self.reachable_through_impl_trait,
55 fn at_level_mut(&mut self, level: Level) -> &mut Visibility {
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,
64 pub fn is_public_at_level(&self, level: Level) -> bool {
65 self.at_level(level).is_public()
68 pub fn from_vis(vis: Visibility) -> EffectiveVisibility {
73 reachable_through_impl_trait: vis,
78 /// Holds a map of effective visibilities for reachable HIR nodes.
79 #[derive(Clone, Debug)]
80 pub struct EffectiveVisibilities<Id = LocalDefId> {
81 map: FxHashMap<Id, EffectiveVisibility>,
84 impl EffectiveVisibilities {
85 pub fn is_public_at_level(&self, id: LocalDefId, level: Level) -> bool {
86 self.effective_vis(id)
87 .map_or(false, |effective_vis| effective_vis.is_public_at_level(level))
90 /// See `Level::Reachable`.
91 pub fn is_reachable(&self, id: LocalDefId) -> bool {
92 self.is_public_at_level(id, Level::Reachable)
95 /// See `Level::Reexported`.
96 pub fn is_exported(&self, id: LocalDefId) -> bool {
97 self.is_public_at_level(id, Level::Reexported)
100 /// See `Level::Direct`.
101 pub fn is_directly_public(&self, id: LocalDefId) -> bool {
102 self.is_public_at_level(id, Level::Direct)
105 pub fn public_at_level(&self, id: LocalDefId) -> Option<Level> {
106 self.effective_vis(id).and_then(|effective_vis| {
107 for level in Level::all_levels() {
108 if effective_vis.is_public_at_level(level) {
116 // FIXME: Share code with `fn update`.
117 pub fn update_eff_vis(
120 eff_vis: &EffectiveVisibility,
121 tree: impl DefIdTree,
123 use std::collections::hash_map::Entry;
124 match self.map.entry(def_id) {
125 Entry::Occupied(mut occupied) => {
126 let old_eff_vis = occupied.get_mut();
127 for l in Level::all_levels() {
128 let vis_at_level = eff_vis.at_level(l);
129 let old_vis_at_level = old_eff_vis.at_level_mut(l);
130 if vis_at_level != old_vis_at_level
131 && vis_at_level.is_at_least(*old_vis_at_level, tree)
133 *old_vis_at_level = *vis_at_level
138 Entry::Vacant(vacant) => vacant.insert(*eff_vis),
142 pub fn set_public_at_level(
145 lazy_private_vis: impl FnOnce() -> Visibility,
148 let mut effective_vis = self
151 .unwrap_or_else(|| EffectiveVisibility::from_vis(lazy_private_vis()));
152 for l in Level::all_levels() {
154 *effective_vis.at_level_mut(l) = Visibility::Public;
157 self.map.insert(id, effective_vis);
160 pub fn check_invariants(&self, tcx: TyCtxt<'_>, early: bool) {
161 if !cfg!(debug_assertions) {
164 for (&def_id, ev) in &self.map {
165 // More direct visibility levels can never go farther than less direct ones,
166 // neither of effective visibilities can go farther than nominal visibility,
167 // and all effective visibilities are larger or equal than private visibility.
168 let private_vis = Visibility::Restricted(tcx.parent_module_from_def_id(def_id));
169 let span = tcx.def_span(def_id.to_def_id());
170 if !ev.direct.is_at_least(private_vis, tcx) {
171 span_bug!(span, "private {:?} > direct {:?}", private_vis, ev.direct);
173 if !ev.reexported.is_at_least(ev.direct, tcx) {
174 span_bug!(span, "direct {:?} > reexported {:?}", ev.direct, ev.reexported);
176 if !ev.reachable.is_at_least(ev.reexported, tcx) {
177 span_bug!(span, "reexported {:?} > reachable {:?}", ev.reexported, ev.reachable);
179 if !ev.reachable_through_impl_trait.is_at_least(ev.reachable, tcx) {
182 "reachable {:?} > reachable_through_impl_trait {:?}",
184 ev.reachable_through_impl_trait
187 let nominal_vis = tcx.visibility(def_id);
188 let def_kind = tcx.opt_def_kind(def_id);
189 // FIXME: `rustc_privacy` is not yet updated for the new logic and can set
190 // effective visibilities that are larger than the nominal one.
191 if !nominal_vis.is_at_least(ev.reachable_through_impl_trait, tcx) && early {
194 "{:?}: reachable_through_impl_trait {:?} > nominal {:?}",
196 ev.reachable_through_impl_trait,
200 // Fully private items are never put into the table, this is important for performance.
201 // FIXME: Fully private `mod` items are currently put into the table.
202 if ev.reachable_through_impl_trait == private_vis && def_kind != Some(DefKind::Mod) {
203 span_bug!(span, "fully private item in the table {:?}: {:?}", def_id, ev.direct);
209 pub trait IntoDefIdTree {
210 type Tree: DefIdTree;
211 fn tree(self) -> Self::Tree;
214 impl<Id: Eq + Hash> EffectiveVisibilities<Id> {
215 pub fn iter(&self) -> impl Iterator<Item = (&Id, &EffectiveVisibility)> {
219 pub fn effective_vis(&self, id: Id) -> Option<&EffectiveVisibility> {
223 // `parent_id` is not necessarily a parent in source code tree,
224 // it is the node from which the maximum effective visibility is inherited.
225 pub fn update<T: IntoDefIdTree>(
228 nominal_vis: Visibility,
229 lazy_private_vis: impl FnOnce(T) -> (Visibility, T),
230 inherited_eff_vis: Option<EffectiveVisibility>,
234 let mut changed = false;
235 let mut current_effective_vis = match self.map.get(&id).copied() {
236 Some(eff_vis) => eff_vis,
239 (private_vis, into_tree) = lazy_private_vis(into_tree);
240 EffectiveVisibility::from_vis(private_vis)
243 let tree = into_tree.tree();
245 if let Some(inherited_effective_vis) = inherited_eff_vis {
246 let mut inherited_effective_vis_at_prev_level =
247 *inherited_effective_vis.at_level(level);
248 let mut calculated_effective_vis = inherited_effective_vis_at_prev_level;
249 for l in Level::all_levels() {
251 let inherited_effective_vis_at_level = *inherited_effective_vis.at_level(l);
252 let current_effective_vis_at_level = current_effective_vis.at_level_mut(l);
253 // effective visibility for id shouldn't be recalculated if
254 // inherited from parent_id effective visibility isn't changed at next level
255 if !(inherited_effective_vis_at_prev_level == inherited_effective_vis_at_level
258 calculated_effective_vis =
259 if nominal_vis.is_at_least(inherited_effective_vis_at_level, tree) {
260 inherited_effective_vis_at_level
265 // effective visibility can't be decreased at next update call for the
267 if *current_effective_vis_at_level != calculated_effective_vis
268 && calculated_effective_vis
269 .is_at_least(*current_effective_vis_at_level, tree)
272 *current_effective_vis_at_level = calculated_effective_vis;
274 inherited_effective_vis_at_prev_level = inherited_effective_vis_at_level;
278 self.map.insert(id, current_effective_vis);
283 impl<Id> Default for EffectiveVisibilities<Id> {
284 fn default() -> Self {
285 EffectiveVisibilities { map: Default::default() }
289 impl<'a> HashStable<StableHashingContext<'a>> for EffectiveVisibilities {
290 fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
291 let EffectiveVisibilities { ref map } = *self;
292 map.hash_stable(hcx, hasher);