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 use crate::ty::{TyCtxt, Visibility}; 5 use rustc_data_structures::fx::FxHashMap; 6 use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; 7 use rustc_hir::def::DefKind; 8 use rustc_macros::HashStable; 9 use rustc_query_system::ich::StableHashingContext; 10 use rustc_span::def_id::{LocalDefId, CRATE_DEF_ID}; 11 use std::hash::Hash; 12 13 /// Represents the levels of effective visibility an item can have. 14 /// 15 /// The variants are sorted in ascending order of directness. 16 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, HashStable)] 17 pub enum Level { 18 /// Superset of `Reachable` including items leaked through return position `impl Trait`. 19 ReachableThroughImplTrait, 20 /// Item is either reexported, or leaked through any kind of interface. 21 /// For example, if function `fn f() -> T {...}` is directly public, then type `T` is publicly 22 /// reachable and its values can be obtained by other crates even if the type itself is not 23 /// nameable. 24 Reachable, 25 /// Item is accessible either directly, or with help of `use` reexports. 26 Reexported, 27 /// Item is directly accessible, without help of reexports. 28 Direct, 29 } 30 31 impl Level { all_levels() -> [Level; 4]32 pub fn all_levels() -> [Level; 4] { 33 [Level::Direct, Level::Reexported, Level::Reachable, Level::ReachableThroughImplTrait] 34 } 35 } 36 37 #[derive(Clone, Copy, PartialEq, Eq, Debug, HashStable)] 38 pub struct EffectiveVisibility { 39 direct: Visibility, 40 reexported: Visibility, 41 reachable: Visibility, 42 reachable_through_impl_trait: Visibility, 43 } 44 45 impl EffectiveVisibility { at_level(&self, level: Level) -> &Visibility46 pub fn at_level(&self, level: Level) -> &Visibility { 47 match level { 48 Level::Direct => &self.direct, 49 Level::Reexported => &self.reexported, 50 Level::Reachable => &self.reachable, 51 Level::ReachableThroughImplTrait => &self.reachable_through_impl_trait, 52 } 53 } 54 at_level_mut(&mut self, level: Level) -> &mut Visibility55 fn at_level_mut(&mut self, level: Level) -> &mut Visibility { 56 match level { 57 Level::Direct => &mut self.direct, 58 Level::Reexported => &mut self.reexported, 59 Level::Reachable => &mut self.reachable, 60 Level::ReachableThroughImplTrait => &mut self.reachable_through_impl_trait, 61 } 62 } 63 is_public_at_level(&self, level: Level) -> bool64 pub fn is_public_at_level(&self, level: Level) -> bool { 65 self.at_level(level).is_public() 66 } 67 from_vis(vis: Visibility) -> EffectiveVisibility68 pub const fn from_vis(vis: Visibility) -> EffectiveVisibility { 69 EffectiveVisibility { 70 direct: vis, 71 reexported: vis, 72 reachable: vis, 73 reachable_through_impl_trait: vis, 74 } 75 } 76 77 #[must_use] min(mut self, lhs: EffectiveVisibility, tcx: TyCtxt<'_>) -> Self78 pub fn min(mut self, lhs: EffectiveVisibility, tcx: TyCtxt<'_>) -> Self { 79 for l in Level::all_levels() { 80 let rhs_vis = self.at_level_mut(l); 81 let lhs_vis = *lhs.at_level(l); 82 if rhs_vis.is_at_least(lhs_vis, tcx) { 83 *rhs_vis = lhs_vis; 84 }; 85 } 86 self 87 } 88 } 89 90 /// Holds a map of effective visibilities for reachable HIR nodes. 91 #[derive(Clone, Debug)] 92 pub struct EffectiveVisibilities<Id = LocalDefId> { 93 map: FxHashMap<Id, EffectiveVisibility>, 94 } 95 96 impl EffectiveVisibilities { is_public_at_level(&self, id: LocalDefId, level: Level) -> bool97 pub fn is_public_at_level(&self, id: LocalDefId, level: Level) -> bool { 98 self.effective_vis(id).is_some_and(|effective_vis| effective_vis.is_public_at_level(level)) 99 } 100 101 /// See `Level::Reachable`. is_reachable(&self, id: LocalDefId) -> bool102 pub fn is_reachable(&self, id: LocalDefId) -> bool { 103 self.is_public_at_level(id, Level::Reachable) 104 } 105 106 /// See `Level::Reexported`. is_exported(&self, id: LocalDefId) -> bool107 pub fn is_exported(&self, id: LocalDefId) -> bool { 108 self.is_public_at_level(id, Level::Reexported) 109 } 110 111 /// See `Level::Direct`. is_directly_public(&self, id: LocalDefId) -> bool112 pub fn is_directly_public(&self, id: LocalDefId) -> bool { 113 self.is_public_at_level(id, Level::Direct) 114 } 115 public_at_level(&self, id: LocalDefId) -> Option<Level>116 pub fn public_at_level(&self, id: LocalDefId) -> Option<Level> { 117 self.effective_vis(id).and_then(|effective_vis| { 118 Level::all_levels().into_iter().find(|&level| effective_vis.is_public_at_level(level)) 119 }) 120 } 121 update_root(&mut self)122 pub fn update_root(&mut self) { 123 self.map.insert(CRATE_DEF_ID, EffectiveVisibility::from_vis(Visibility::Public)); 124 } 125 126 // FIXME: Share code with `fn update`. update_eff_vis( &mut self, def_id: LocalDefId, eff_vis: &EffectiveVisibility, tcx: TyCtxt<'_>, )127 pub fn update_eff_vis( 128 &mut self, 129 def_id: LocalDefId, 130 eff_vis: &EffectiveVisibility, 131 tcx: TyCtxt<'_>, 132 ) { 133 use std::collections::hash_map::Entry; 134 match self.map.entry(def_id) { 135 Entry::Occupied(mut occupied) => { 136 let old_eff_vis = occupied.get_mut(); 137 for l in Level::all_levels() { 138 let vis_at_level = eff_vis.at_level(l); 139 let old_vis_at_level = old_eff_vis.at_level_mut(l); 140 if vis_at_level != old_vis_at_level 141 && vis_at_level.is_at_least(*old_vis_at_level, tcx) 142 { 143 *old_vis_at_level = *vis_at_level 144 } 145 } 146 old_eff_vis 147 } 148 Entry::Vacant(vacant) => vacant.insert(*eff_vis), 149 }; 150 } 151 check_invariants(&self, tcx: TyCtxt<'_>)152 pub fn check_invariants(&self, tcx: TyCtxt<'_>) { 153 if !cfg!(debug_assertions) { 154 return; 155 } 156 for (&def_id, ev) in &self.map { 157 // More direct visibility levels can never go farther than less direct ones, 158 // and all effective visibilities are larger or equal than private visibility. 159 let private_vis = Visibility::Restricted(tcx.parent_module_from_def_id(def_id)); 160 let span = tcx.def_span(def_id.to_def_id()); 161 if !ev.direct.is_at_least(private_vis, tcx) { 162 span_bug!(span, "private {:?} > direct {:?}", private_vis, ev.direct); 163 } 164 if !ev.reexported.is_at_least(ev.direct, tcx) { 165 span_bug!(span, "direct {:?} > reexported {:?}", ev.direct, ev.reexported); 166 } 167 if !ev.reachable.is_at_least(ev.reexported, tcx) { 168 span_bug!(span, "reexported {:?} > reachable {:?}", ev.reexported, ev.reachable); 169 } 170 if !ev.reachable_through_impl_trait.is_at_least(ev.reachable, tcx) { 171 span_bug!( 172 span, 173 "reachable {:?} > reachable_through_impl_trait {:?}", 174 ev.reachable, 175 ev.reachable_through_impl_trait 176 ); 177 } 178 // All effective visibilities except `reachable_through_impl_trait` are limited to 179 // nominal visibility. For some items nominal visibility doesn't make sense so we 180 // don't check this condition for them. 181 if !matches!(tcx.def_kind(def_id), DefKind::Impl { .. }) { 182 let nominal_vis = tcx.visibility(def_id); 183 if !nominal_vis.is_at_least(ev.reachable, tcx) { 184 span_bug!( 185 span, 186 "{:?}: reachable {:?} > nominal {:?}", 187 def_id, 188 ev.reachable, 189 nominal_vis 190 ); 191 } 192 } 193 } 194 } 195 } 196 197 impl<Id: Eq + Hash> EffectiveVisibilities<Id> { iter(&self) -> impl Iterator<Item = (&Id, &EffectiveVisibility)>198 pub fn iter(&self) -> impl Iterator<Item = (&Id, &EffectiveVisibility)> { 199 self.map.iter() 200 } 201 effective_vis(&self, id: Id) -> Option<&EffectiveVisibility>202 pub fn effective_vis(&self, id: Id) -> Option<&EffectiveVisibility> { 203 self.map.get(&id) 204 } 205 206 // FIXME: Share code with `fn update`. effective_vis_or_private( &mut self, id: Id, lazy_private_vis: impl FnOnce() -> Visibility, ) -> &EffectiveVisibility207 pub fn effective_vis_or_private( 208 &mut self, 209 id: Id, 210 lazy_private_vis: impl FnOnce() -> Visibility, 211 ) -> &EffectiveVisibility { 212 self.map.entry(id).or_insert_with(|| EffectiveVisibility::from_vis(lazy_private_vis())) 213 } 214 update( &mut self, id: Id, max_vis: Option<Visibility>, lazy_private_vis: impl FnOnce() -> Visibility, inherited_effective_vis: EffectiveVisibility, level: Level, tcx: TyCtxt<'_>, ) -> bool215 pub fn update( 216 &mut self, 217 id: Id, 218 max_vis: Option<Visibility>, 219 lazy_private_vis: impl FnOnce() -> Visibility, 220 inherited_effective_vis: EffectiveVisibility, 221 level: Level, 222 tcx: TyCtxt<'_>, 223 ) -> bool { 224 let mut changed = false; 225 let mut current_effective_vis = self 226 .map 227 .get(&id) 228 .copied() 229 .unwrap_or_else(|| EffectiveVisibility::from_vis(lazy_private_vis())); 230 231 let mut inherited_effective_vis_at_prev_level = *inherited_effective_vis.at_level(level); 232 let mut calculated_effective_vis = inherited_effective_vis_at_prev_level; 233 for l in Level::all_levels() { 234 if level >= l { 235 let inherited_effective_vis_at_level = *inherited_effective_vis.at_level(l); 236 let current_effective_vis_at_level = current_effective_vis.at_level_mut(l); 237 // effective visibility for id shouldn't be recalculated if 238 // inherited from parent_id effective visibility isn't changed at next level 239 if !(inherited_effective_vis_at_prev_level == inherited_effective_vis_at_level 240 && level != l) 241 { 242 calculated_effective_vis = if let Some(max_vis) = max_vis && !max_vis.is_at_least(inherited_effective_vis_at_level, tcx) { 243 max_vis 244 } else { 245 inherited_effective_vis_at_level 246 } 247 } 248 // effective visibility can't be decreased at next update call for the 249 // same id 250 if *current_effective_vis_at_level != calculated_effective_vis 251 && calculated_effective_vis.is_at_least(*current_effective_vis_at_level, tcx) 252 { 253 changed = true; 254 *current_effective_vis_at_level = calculated_effective_vis; 255 } 256 inherited_effective_vis_at_prev_level = inherited_effective_vis_at_level; 257 } 258 } 259 260 self.map.insert(id, current_effective_vis); 261 changed 262 } 263 } 264 265 impl<Id> Default for EffectiveVisibilities<Id> { default() -> Self266 fn default() -> Self { 267 EffectiveVisibilities { map: Default::default() } 268 } 269 } 270 271 impl<'a> HashStable<StableHashingContext<'a>> for EffectiveVisibilities { hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher)272 fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { 273 let EffectiveVisibilities { ref map } = *self; 274 map.hash_stable(hcx, hasher); 275 } 276 } 277