privacy: Check effective visibility invariants
This commit is contained in:
parent
bb401bd04d
commit
448261a78a
@ -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;
|
||||
@ -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> {
|
||||
|
@ -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 {
|
||||
@ -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 };
|
||||
|
Loading…
x
Reference in New Issue
Block a user