rustc_middle/middle/
privacy.rs

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
5use std::hash::Hash;
6
7use rustc_data_structures::fx::{FxIndexMap, IndexEntry};
8use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
9use rustc_hir::def::DefKind;
10use rustc_macros::HashStable;
11use rustc_query_system::ich::StableHashingContext;
12use rustc_span::def_id::{CRATE_DEF_ID, LocalDefId};
13
14use crate::ty::{TyCtxt, Visibility};
15
16/// Represents the levels of effective visibility an item can have.
17///
18/// The variants are sorted in ascending order of directness.
19#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, HashStable)]
20pub enum Level {
21    /// Superset of `Reachable` including items leaked through return position `impl Trait`.
22    ReachableThroughImplTrait,
23    /// Item is either reexported, or leaked through any kind of interface.
24    /// For example, if function `fn f() -> T {...}` is directly public, then type `T` is publicly
25    /// reachable and its values can be obtained by other crates even if the type itself is not
26    /// nameable.
27    Reachable,
28    /// Item is accessible either directly, or with help of `use` reexports.
29    Reexported,
30    /// Item is directly accessible, without help of reexports.
31    Direct,
32}
33
34impl Level {
35    pub fn all_levels() -> [Level; 4] {
36        [Level::Direct, Level::Reexported, Level::Reachable, Level::ReachableThroughImplTrait]
37    }
38}
39
40#[derive(Clone, Copy, PartialEq, Eq, Debug, HashStable)]
41pub struct EffectiveVisibility {
42    direct: Visibility,
43    reexported: Visibility,
44    reachable: Visibility,
45    reachable_through_impl_trait: Visibility,
46}
47
48impl EffectiveVisibility {
49    pub fn at_level(&self, level: Level) -> &Visibility {
50        match level {
51            Level::Direct => &self.direct,
52            Level::Reexported => &self.reexported,
53            Level::Reachable => &self.reachable,
54            Level::ReachableThroughImplTrait => &self.reachable_through_impl_trait,
55        }
56    }
57
58    fn at_level_mut(&mut self, level: Level) -> &mut Visibility {
59        match level {
60            Level::Direct => &mut self.direct,
61            Level::Reexported => &mut self.reexported,
62            Level::Reachable => &mut self.reachable,
63            Level::ReachableThroughImplTrait => &mut self.reachable_through_impl_trait,
64        }
65    }
66
67    pub fn is_public_at_level(&self, level: Level) -> bool {
68        self.at_level(level).is_public()
69    }
70
71    pub const fn from_vis(vis: Visibility) -> EffectiveVisibility {
72        EffectiveVisibility {
73            direct: vis,
74            reexported: vis,
75            reachable: vis,
76            reachable_through_impl_trait: vis,
77        }
78    }
79
80    #[must_use]
81    pub fn min(mut self, lhs: EffectiveVisibility, tcx: TyCtxt<'_>) -> Self {
82        for l in Level::all_levels() {
83            let rhs_vis = self.at_level_mut(l);
84            let lhs_vis = *lhs.at_level(l);
85            if rhs_vis.is_at_least(lhs_vis, tcx) {
86                *rhs_vis = lhs_vis;
87            };
88        }
89        self
90    }
91}
92
93/// Holds a map of effective visibilities for reachable HIR nodes.
94#[derive(Clone, Debug)]
95pub struct EffectiveVisibilities<Id = LocalDefId> {
96    map: FxIndexMap<Id, EffectiveVisibility>,
97}
98
99impl EffectiveVisibilities {
100    pub fn is_public_at_level(&self, id: LocalDefId, level: Level) -> bool {
101        self.effective_vis(id).is_some_and(|effective_vis| effective_vis.is_public_at_level(level))
102    }
103
104    /// See `Level::Reachable`.
105    pub fn is_reachable(&self, id: LocalDefId) -> bool {
106        self.is_public_at_level(id, Level::Reachable)
107    }
108
109    /// See `Level::Reexported`.
110    pub fn is_exported(&self, id: LocalDefId) -> bool {
111        self.is_public_at_level(id, Level::Reexported)
112    }
113
114    /// See `Level::Direct`.
115    pub fn is_directly_public(&self, id: LocalDefId) -> bool {
116        self.is_public_at_level(id, Level::Direct)
117    }
118
119    pub fn public_at_level(&self, id: LocalDefId) -> Option<Level> {
120        self.effective_vis(id).and_then(|effective_vis| {
121            Level::all_levels().into_iter().find(|&level| effective_vis.is_public_at_level(level))
122        })
123    }
124
125    pub fn update_root(&mut self) {
126        self.map.insert(CRATE_DEF_ID, EffectiveVisibility::from_vis(Visibility::Public));
127    }
128
129    // FIXME: Share code with `fn update`.
130    pub fn update_eff_vis(
131        &mut self,
132        def_id: LocalDefId,
133        eff_vis: &EffectiveVisibility,
134        tcx: TyCtxt<'_>,
135    ) {
136        match self.map.entry(def_id) {
137            IndexEntry::Occupied(mut occupied) => {
138                let old_eff_vis = occupied.get_mut();
139                for l in Level::all_levels() {
140                    let vis_at_level = eff_vis.at_level(l);
141                    let old_vis_at_level = old_eff_vis.at_level_mut(l);
142                    if vis_at_level != old_vis_at_level
143                        && vis_at_level.is_at_least(*old_vis_at_level, tcx)
144                    {
145                        *old_vis_at_level = *vis_at_level
146                    }
147                }
148                old_eff_vis
149            }
150            IndexEntry::Vacant(vacant) => vacant.insert(*eff_vis),
151        };
152    }
153
154    pub fn check_invariants(&self, tcx: TyCtxt<'_>) {
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            // and all effective visibilities are larger or equal than private visibility.
161            let private_vis = Visibility::Restricted(tcx.parent_module_from_def_id(def_id));
162            let span = tcx.def_span(def_id.to_def_id());
163            if !ev.direct.is_at_least(private_vis, tcx) {
164                span_bug!(span, "private {:?} > direct {:?}", private_vis, ev.direct);
165            }
166            if !ev.reexported.is_at_least(ev.direct, tcx) {
167                span_bug!(span, "direct {:?} > reexported {:?}", ev.direct, ev.reexported);
168            }
169            if !ev.reachable.is_at_least(ev.reexported, tcx) {
170                span_bug!(span, "reexported {:?} > reachable {:?}", ev.reexported, ev.reachable);
171            }
172            if !ev.reachable_through_impl_trait.is_at_least(ev.reachable, tcx) {
173                span_bug!(
174                    span,
175                    "reachable {:?} > reachable_through_impl_trait {:?}",
176                    ev.reachable,
177                    ev.reachable_through_impl_trait
178                );
179            }
180            // All effective visibilities except `reachable_through_impl_trait` are limited to
181            // nominal visibility. For some items nominal visibility doesn't make sense so we
182            // don't check this condition for them.
183            let is_impl = matches!(tcx.def_kind(def_id), DefKind::Impl { .. });
184            let is_associated_item_in_trait_impl = tcx
185                .impl_of_method(def_id.to_def_id())
186                .and_then(|impl_id| tcx.trait_id_of_impl(impl_id))
187                .is_some();
188            if !is_impl && !is_associated_item_in_trait_impl {
189                let nominal_vis = tcx.visibility(def_id);
190                if !nominal_vis.is_at_least(ev.reachable, tcx) {
191                    span_bug!(
192                        span,
193                        "{:?}: reachable {:?} > nominal {:?}",
194                        def_id,
195                        ev.reachable,
196                        nominal_vis,
197                    );
198                }
199            }
200        }
201    }
202}
203
204impl<Id: Eq + Hash> EffectiveVisibilities<Id> {
205    pub fn iter(&self) -> impl Iterator<Item = (&Id, &EffectiveVisibility)> {
206        self.map.iter()
207    }
208
209    pub fn effective_vis(&self, id: Id) -> Option<&EffectiveVisibility> {
210        self.map.get(&id)
211    }
212
213    // FIXME: Share code with `fn update`.
214    pub fn effective_vis_or_private(
215        &mut self,
216        id: Id,
217        lazy_private_vis: impl FnOnce() -> Visibility,
218    ) -> &EffectiveVisibility {
219        self.map.entry(id).or_insert_with(|| EffectiveVisibility::from_vis(lazy_private_vis()))
220    }
221
222    pub fn update(
223        &mut self,
224        id: Id,
225        max_vis: Option<Visibility>,
226        lazy_private_vis: impl FnOnce() -> Visibility,
227        inherited_effective_vis: EffectiveVisibility,
228        level: Level,
229        tcx: TyCtxt<'_>,
230    ) -> bool {
231        let mut changed = false;
232        let mut current_effective_vis = self
233            .map
234            .get(&id)
235            .copied()
236            .unwrap_or_else(|| EffectiveVisibility::from_vis(lazy_private_vis()));
237
238        let mut inherited_effective_vis_at_prev_level = *inherited_effective_vis.at_level(level);
239        let mut calculated_effective_vis = inherited_effective_vis_at_prev_level;
240        for l in Level::all_levels() {
241            if level >= l {
242                let inherited_effective_vis_at_level = *inherited_effective_vis.at_level(l);
243                let current_effective_vis_at_level = current_effective_vis.at_level_mut(l);
244                // effective visibility for id shouldn't be recalculated if
245                // inherited from parent_id effective visibility isn't changed at next level
246                if !(inherited_effective_vis_at_prev_level == inherited_effective_vis_at_level
247                    && level != l)
248                {
249                    calculated_effective_vis = if let Some(max_vis) = max_vis
250                        && !max_vis.is_at_least(inherited_effective_vis_at_level, tcx)
251                    {
252                        max_vis
253                    } else {
254                        inherited_effective_vis_at_level
255                    }
256                }
257                // effective visibility can't be decreased at next update call for the
258                // same id
259                if *current_effective_vis_at_level != calculated_effective_vis
260                    && calculated_effective_vis.is_at_least(*current_effective_vis_at_level, tcx)
261                {
262                    changed = true;
263                    *current_effective_vis_at_level = calculated_effective_vis;
264                }
265                inherited_effective_vis_at_prev_level = inherited_effective_vis_at_level;
266            }
267        }
268
269        self.map.insert(id, current_effective_vis);
270        changed
271    }
272}
273
274impl<Id> Default for EffectiveVisibilities<Id> {
275    fn default() -> Self {
276        EffectiveVisibilities { map: Default::default() }
277    }
278}
279
280impl<'a> HashStable<StableHashingContext<'a>> for EffectiveVisibilities {
281    fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
282        let EffectiveVisibilities { ref map } = *self;
283        map.hash_stable(hcx, hasher);
284    }
285}