1use 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#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, HashStable)]
20pub enum Level {
21 ReachableThroughImplTrait,
23 Reachable,
28 Reexported,
30 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#[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 pub fn is_reachable(&self, id: LocalDefId) -> bool {
106 self.is_public_at_level(id, Level::Reachable)
107 }
108
109 pub fn is_exported(&self, id: LocalDefId) -> bool {
111 self.is_public_at_level(id, Level::Reexported)
112 }
113
114 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 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 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 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 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 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 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}