Skip to content

Commit

Permalink
privacy: Check effective visibility invariants
Browse files Browse the repository at this point in the history
  • Loading branch information
petrochenkov committed Nov 5, 2022
1 parent bb401bd commit 448261a
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 1 deletion.
51 changes: 50 additions & 1 deletion compiler/rustc_middle/src/middle/privacy.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
//! A pass that checks to make sure private fields and methods aren't used
//! outside their scopes. This pass will also generate a set of exported items
//! which are available for use externally when compiled as a library.
use crate::ty::{DefIdTree, Visibility};
use crate::ty::{DefIdTree, TyCtxt, Visibility};
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_hir::def::DefKind;
use rustc_macros::HashStable;
use rustc_query_system::ich::StableHashingContext;
use rustc_span::def_id::LocalDefId;
Expand Down Expand Up @@ -133,6 +134,54 @@ impl EffectiveVisibilities {
}
self.map.insert(id, effective_vis);
}

pub fn check_invariants(&self, tcx: TyCtxt<'_>, early: bool) {
if !cfg!(debug_assertions) {
return;
}
for (&def_id, ev) in &self.map {
// More direct visibility levels can never go farther than less direct ones,
// neither of effective visibilities can go farther than nominal visibility,
// and all effective visibilities are larger or equal than private visibility.
let private_vis = Visibility::Restricted(tcx.parent_module_from_def_id(def_id));
let span = tcx.def_span(def_id.to_def_id());
if !ev.direct.is_at_least(private_vis, tcx) {
span_bug!(span, "private {:?} > direct {:?}", private_vis, ev.direct);
}
if !ev.reexported.is_at_least(ev.direct, tcx) {
span_bug!(span, "direct {:?} > reexported {:?}", ev.direct, ev.reexported);
}
if !ev.reachable.is_at_least(ev.reexported, tcx) {
span_bug!(span, "reexported {:?} > reachable {:?}", ev.reexported, ev.reachable);
}
if !ev.reachable_through_impl_trait.is_at_least(ev.reachable, tcx) {
span_bug!(
span,
"reachable {:?} > reachable_through_impl_trait {:?}",
ev.reachable,
ev.reachable_through_impl_trait
);
}
let nominal_vis = tcx.visibility(def_id);
let def_kind = tcx.opt_def_kind(def_id);
// FIXME: `rustc_privacy` is not yet updated for the new logic and can set
// effective visibilities that are larger than the nominal one.
if !nominal_vis.is_at_least(ev.reachable_through_impl_trait, tcx) && early {
span_bug!(
span,
"{:?}: reachable_through_impl_trait {:?} > nominal {:?}",
def_id,
ev.reachable_through_impl_trait,
nominal_vis
);
}
// Fully private items are never put into the table, this is important for performance.
// FIXME: Fully private `mod` items are currently put into the table.
if ev.reachable_through_impl_trait == private_vis && def_kind != Some(DefKind::Mod) {
span_bug!(span, "fully private item in the table {:?}: {:?}", def_id, ev.direct);
}
}
}
}

impl<Id: Eq + Hash> EffectiveVisibilities<Id> {
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_privacy/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2139,6 +2139,7 @@ fn effective_visibilities(tcx: TyCtxt<'_>, (): ()) -> &EffectiveVisibilities {
changed: false,
};

visitor.effective_visibilities.check_invariants(tcx, true);
loop {
tcx.hir().walk_toplevel_module(&mut visitor);
if visitor.changed {
Expand All @@ -2147,6 +2148,7 @@ fn effective_visibilities(tcx: TyCtxt<'_>, (): ()) -> &EffectiveVisibilities {
break;
}
}
visitor.effective_visibilities.check_invariants(tcx, false);

let mut check_visitor =
TestReachabilityVisitor { tcx, effective_visibilities: &visitor.effective_visibilities };
Expand Down

0 comments on commit 448261a

Please sign in to comment.