Skip to content

Commit 448261a

Browse files
committed
privacy: Check effective visibility invariants
1 parent bb401bd commit 448261a

File tree

2 files changed

+52
-1
lines changed

2 files changed

+52
-1
lines changed

compiler/rustc_middle/src/middle/privacy.rs

+50-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
//! A pass that checks to make sure private fields and methods aren't used
22
//! outside their scopes. This pass will also generate a set of exported items
33
//! which are available for use externally when compiled as a library.
4-
use crate::ty::{DefIdTree, Visibility};
4+
use crate::ty::{DefIdTree, TyCtxt, Visibility};
55
use rustc_data_structures::fx::FxHashMap;
66
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
7+
use rustc_hir::def::DefKind;
78
use rustc_macros::HashStable;
89
use rustc_query_system::ich::StableHashingContext;
910
use rustc_span::def_id::LocalDefId;
@@ -133,6 +134,54 @@ impl EffectiveVisibilities {
133134
}
134135
self.map.insert(id, effective_vis);
135136
}
137+
138+
pub fn check_invariants(&self, tcx: TyCtxt<'_>, early: bool) {
139+
if !cfg!(debug_assertions) {
140+
return;
141+
}
142+
for (&def_id, ev) in &self.map {
143+
// More direct visibility levels can never go farther than less direct ones,
144+
// neither of effective visibilities can go farther than nominal visibility,
145+
// and all effective visibilities are larger or equal than private visibility.
146+
let private_vis = Visibility::Restricted(tcx.parent_module_from_def_id(def_id));
147+
let span = tcx.def_span(def_id.to_def_id());
148+
if !ev.direct.is_at_least(private_vis, tcx) {
149+
span_bug!(span, "private {:?} > direct {:?}", private_vis, ev.direct);
150+
}
151+
if !ev.reexported.is_at_least(ev.direct, tcx) {
152+
span_bug!(span, "direct {:?} > reexported {:?}", ev.direct, ev.reexported);
153+
}
154+
if !ev.reachable.is_at_least(ev.reexported, tcx) {
155+
span_bug!(span, "reexported {:?} > reachable {:?}", ev.reexported, ev.reachable);
156+
}
157+
if !ev.reachable_through_impl_trait.is_at_least(ev.reachable, tcx) {
158+
span_bug!(
159+
span,
160+
"reachable {:?} > reachable_through_impl_trait {:?}",
161+
ev.reachable,
162+
ev.reachable_through_impl_trait
163+
);
164+
}
165+
let nominal_vis = tcx.visibility(def_id);
166+
let def_kind = tcx.opt_def_kind(def_id);
167+
// FIXME: `rustc_privacy` is not yet updated for the new logic and can set
168+
// effective visibilities that are larger than the nominal one.
169+
if !nominal_vis.is_at_least(ev.reachable_through_impl_trait, tcx) && early {
170+
span_bug!(
171+
span,
172+
"{:?}: reachable_through_impl_trait {:?} > nominal {:?}",
173+
def_id,
174+
ev.reachable_through_impl_trait,
175+
nominal_vis
176+
);
177+
}
178+
// Fully private items are never put into the table, this is important for performance.
179+
// FIXME: Fully private `mod` items are currently put into the table.
180+
if ev.reachable_through_impl_trait == private_vis && def_kind != Some(DefKind::Mod) {
181+
span_bug!(span, "fully private item in the table {:?}: {:?}", def_id, ev.direct);
182+
}
183+
}
184+
}
136185
}
137186

138187
impl<Id: Eq + Hash> EffectiveVisibilities<Id> {

compiler/rustc_privacy/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -2139,6 +2139,7 @@ fn effective_visibilities(tcx: TyCtxt<'_>, (): ()) -> &EffectiveVisibilities {
21392139
changed: false,
21402140
};
21412141

2142+
visitor.effective_visibilities.check_invariants(tcx, true);
21422143
loop {
21432144
tcx.hir().walk_toplevel_module(&mut visitor);
21442145
if visitor.changed {
@@ -2147,6 +2148,7 @@ fn effective_visibilities(tcx: TyCtxt<'_>, (): ()) -> &EffectiveVisibilities {
21472148
break;
21482149
}
21492150
}
2151+
visitor.effective_visibilities.check_invariants(tcx, false);
21502152

21512153
let mut check_visitor =
21522154
TestReachabilityVisitor { tcx, effective_visibilities: &visitor.effective_visibilities };

0 commit comments

Comments
 (0)