Skip to main content

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