#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![cfg_attr(not(bootstrap), doc(rust_logo))]
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
#![cfg_attr(not(bootstrap), allow(internal_features))]
#![feature(associated_type_defaults)]
#![feature(rustc_private)]
#![feature(try_blocks)]
#![feature(let_chains)]
#![recursion_limit = "256"]
#![deny(rustc::untranslatable_diagnostic)]
#![deny(rustc::diagnostic_outside_of_impl)]
#[macro_use]
extern crate tracing;
mod errors;
use rustc_ast::MacroDef;
use rustc_attr as attr;
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::intern::Interned;
use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage};
use rustc_fluent_macro::fluent_messages;
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId, CRATE_DEF_ID};
use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::{AssocItemKind, ForeignItemKind, ItemId, Node, PatKind};
use rustc_middle::bug;
use rustc_middle::hir::nested_filter;
use rustc_middle::middle::privacy::{EffectiveVisibilities, EffectiveVisibility, Level};
use rustc_middle::query::Providers;
use rustc_middle::span_bug;
use rustc_middle::ty::GenericArgs;
use rustc_middle::ty::{self, Const, GenericParamDefKind};
use rustc_middle::ty::{TraitRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor};
use rustc_session::lint;
use rustc_span::hygiene::Transparency;
use rustc_span::symbol::{kw, sym, Ident};
use rustc_span::Span;
use std::marker::PhantomData;
use std::ops::ControlFlow;
use std::{fmt, mem};
use errors::{
FieldIsPrivate, FieldIsPrivateLabel, FromPrivateDependencyInPublicInterface, InPublicInterface,
ItemIsPrivate, PrivateInterfacesOrBoundsLint, ReportEffectiveVisibility, UnnameableTypesLint,
UnnamedItemIsPrivate,
};
fluent_messages! { "../messages.ftl" }
struct LazyDefPathStr<'tcx> {
def_id: DefId,
tcx: TyCtxt<'tcx>,
}
impl<'tcx> fmt::Display for LazyDefPathStr<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.tcx.def_path_str(self.def_id))
}
}
trait DefIdVisitor<'tcx> {
type BreakTy = ();
const SHALLOW: bool = false;
const SKIP_ASSOC_TYS: bool = false;
fn tcx(&self) -> TyCtxt<'tcx>;
fn visit_def_id(
&mut self,
def_id: DefId,
kind: &str,
descr: &dyn fmt::Display,
) -> ControlFlow<Self::BreakTy>;
fn skeleton(&mut self) -> DefIdVisitorSkeleton<'_, 'tcx, Self> {
DefIdVisitorSkeleton {
def_id_visitor: self,
visited_opaque_tys: Default::default(),
dummy: Default::default(),
}
}
fn visit(
&mut self,
ty_fragment: impl TypeVisitable<TyCtxt<'tcx>>,
) -> ControlFlow<Self::BreakTy> {
ty_fragment.visit_with(&mut self.skeleton())
}
fn visit_trait(&mut self, trait_ref: TraitRef<'tcx>) -> ControlFlow<Self::BreakTy> {
self.skeleton().visit_trait(trait_ref)
}
fn visit_projection_ty(&mut self, projection: ty::AliasTy<'tcx>) -> ControlFlow<Self::BreakTy> {
self.skeleton().visit_projection_ty(projection)
}
fn visit_predicates(
&mut self,
predicates: ty::GenericPredicates<'tcx>,
) -> ControlFlow<Self::BreakTy> {
self.skeleton().visit_clauses(predicates.predicates)
}
fn visit_clauses(
&mut self,
clauses: &[(ty::Clause<'tcx>, Span)],
) -> ControlFlow<Self::BreakTy> {
self.skeleton().visit_clauses(clauses)
}
}
struct DefIdVisitorSkeleton<'v, 'tcx, V: ?Sized> {
def_id_visitor: &'v mut V,
visited_opaque_tys: FxHashSet<DefId>,
dummy: PhantomData<TyCtxt<'tcx>>,
}
impl<'tcx, V> DefIdVisitorSkeleton<'_, 'tcx, V>
where
V: DefIdVisitor<'tcx> + ?Sized,
{
fn visit_trait(&mut self, trait_ref: TraitRef<'tcx>) -> ControlFlow<V::BreakTy> {
let TraitRef { def_id, args, .. } = trait_ref;
self.def_id_visitor.visit_def_id(def_id, "trait", &trait_ref.print_only_trait_path())?;
if V::SHALLOW { ControlFlow::Continue(()) } else { args.visit_with(self) }
}
fn visit_projection_ty(&mut self, projection: ty::AliasTy<'tcx>) -> ControlFlow<V::BreakTy> {
let tcx = self.def_id_visitor.tcx();
let (trait_ref, assoc_args) = projection.trait_ref_and_own_args(tcx);
self.visit_trait(trait_ref)?;
if V::SHALLOW {
ControlFlow::Continue(())
} else {
assoc_args.iter().try_for_each(|subst| subst.visit_with(self))
}
}
fn visit_clause(&mut self, clause: ty::Clause<'tcx>) -> ControlFlow<V::BreakTy> {
match clause.kind().skip_binder() {
ty::ClauseKind::Trait(ty::TraitPredicate { trait_ref, polarity: _ }) => {
self.visit_trait(trait_ref)
}
ty::ClauseKind::Projection(ty::ProjectionPredicate { projection_ty, term }) => {
term.visit_with(self)?;
self.visit_projection_ty(projection_ty)
}
ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty, _region)) => ty.visit_with(self),
ty::ClauseKind::RegionOutlives(..) => ControlFlow::Continue(()),
ty::ClauseKind::ConstArgHasType(ct, ty) => {
ct.visit_with(self)?;
ty.visit_with(self)
}
ty::ClauseKind::ConstEvaluatable(ct) => ct.visit_with(self),
ty::ClauseKind::WellFormed(arg) => arg.visit_with(self),
}
}
fn visit_clauses(&mut self, clauses: &[(ty::Clause<'tcx>, Span)]) -> ControlFlow<V::BreakTy> {
clauses.into_iter().try_for_each(|&(clause, _span)| self.visit_clause(clause))
}
}
impl<'tcx, V> TypeVisitor<TyCtxt<'tcx>> for DefIdVisitorSkeleton<'_, 'tcx, V>
where
V: DefIdVisitor<'tcx> + ?Sized,
{
type BreakTy = V::BreakTy;
fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<V::BreakTy> {
let tcx = self.def_id_visitor.tcx();
match *ty.kind() {
ty::Adt(ty::AdtDef(Interned(&ty::AdtDefData { did: def_id, .. }, _)), ..)
| ty::Foreign(def_id)
| ty::FnDef(def_id, ..)
| ty::Closure(def_id, ..)
| ty::Generator(def_id, ..) => {
self.def_id_visitor.visit_def_id(def_id, "type", &ty)?;
if V::SHALLOW {
return ControlFlow::Continue(());
}
if let ty::FnDef(..) = ty.kind() {
tcx.fn_sig(def_id).instantiate_identity().visit_with(self)?;
}
if let Some(assoc_item) = tcx.opt_associated_item(def_id) {
if let Some(impl_def_id) = assoc_item.impl_container(tcx) {
tcx.type_of(impl_def_id).instantiate_identity().visit_with(self)?;
}
}
}
ty::Alias(ty::Weak, alias) => {
self.def_id_visitor.visit_def_id(alias.def_id, "type alias", &ty);
}
ty::Alias(ty::Projection, proj) => {
if V::SKIP_ASSOC_TYS {
return ControlFlow::Continue(());
}
return self.visit_projection_ty(proj);
}
ty::Alias(ty::Inherent, data) => {
if V::SKIP_ASSOC_TYS {
return ControlFlow::Continue(());
}
self.def_id_visitor.visit_def_id(
data.def_id,
"associated type",
&LazyDefPathStr { def_id: data.def_id, tcx },
)?;
return if V::SHALLOW {
ControlFlow::Continue(())
} else {
data.args.iter().try_for_each(|subst| subst.visit_with(self))
};
}
ty::Dynamic(predicates, ..) => {
for predicate in predicates {
let trait_ref = match predicate.skip_binder() {
ty::ExistentialPredicate::Trait(trait_ref) => trait_ref,
ty::ExistentialPredicate::Projection(proj) => proj.trait_ref(tcx),
ty::ExistentialPredicate::AutoTrait(def_id) => {
ty::ExistentialTraitRef { def_id, args: GenericArgs::empty() }
}
};
let ty::ExistentialTraitRef { def_id, args: _ } = trait_ref;
self.def_id_visitor.visit_def_id(def_id, "trait", &trait_ref)?;
}
}
ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) => {
if self.visited_opaque_tys.insert(def_id) {
self.visit_clauses(tcx.explicit_item_bounds(def_id).skip_binder())?;
}
}
ty::Bool
| ty::Char
| ty::Int(..)
| ty::Uint(..)
| ty::Float(..)
| ty::Str
| ty::Never
| ty::Array(..)
| ty::Slice(..)
| ty::Tuple(..)
| ty::RawPtr(..)
| ty::Ref(..)
| ty::FnPtr(..)
| ty::Param(..)
| ty::Bound(..)
| ty::Error(_)
| ty::GeneratorWitness(..) => {}
ty::Placeholder(..) | ty::Infer(..) => {
bug!("unexpected type: {:?}", ty)
}
}
if V::SHALLOW { ControlFlow::Continue(()) } else { ty.super_visit_with(self) }
}
fn visit_const(&mut self, c: Const<'tcx>) -> ControlFlow<Self::BreakTy> {
let tcx = self.def_id_visitor.tcx();
tcx.expand_abstract_consts(c).super_visit_with(self)
}
}
fn min(vis1: ty::Visibility, vis2: ty::Visibility, tcx: TyCtxt<'_>) -> ty::Visibility {
if vis1.is_at_least(vis2, tcx) { vis2 } else { vis1 }
}
struct FindMin<'a, 'tcx, VL: VisibilityLike, const SHALLOW: bool> {
tcx: TyCtxt<'tcx>,
effective_visibilities: &'a EffectiveVisibilities,
min: VL,
}
impl<'a, 'tcx, VL: VisibilityLike, const SHALLOW: bool> DefIdVisitor<'tcx>
for FindMin<'a, 'tcx, VL, SHALLOW>
{
const SHALLOW: bool = SHALLOW;
const SKIP_ASSOC_TYS: bool = true;
fn tcx(&self) -> TyCtxt<'tcx> {
self.tcx
}
fn visit_def_id(
&mut self,
def_id: DefId,
_kind: &str,
_descr: &dyn fmt::Display,
) -> ControlFlow<Self::BreakTy> {
if let Some(def_id) = def_id.as_local() {
self.min = VL::new_min(self, def_id);
}
ControlFlow::Continue(())
}
}
trait VisibilityLike: Sized {
const MAX: Self;
fn new_min<const SHALLOW: bool>(
find: &FindMin<'_, '_, Self, SHALLOW>,
def_id: LocalDefId,
) -> Self;
fn of_impl<const SHALLOW: bool>(
def_id: LocalDefId,
tcx: TyCtxt<'_>,
effective_visibilities: &EffectiveVisibilities,
) -> Self {
let mut find = FindMin::<_, SHALLOW> { tcx, effective_visibilities, min: Self::MAX };
find.visit(tcx.type_of(def_id).instantiate_identity());
if let Some(trait_ref) = tcx.impl_trait_ref(def_id) {
find.visit_trait(trait_ref.instantiate_identity());
}
find.min
}
}
impl VisibilityLike for ty::Visibility {
const MAX: Self = ty::Visibility::Public;
fn new_min<const SHALLOW: bool>(
find: &FindMin<'_, '_, Self, SHALLOW>,
def_id: LocalDefId,
) -> Self {
min(find.tcx.local_visibility(def_id), find.min, find.tcx)
}
}
impl VisibilityLike for EffectiveVisibility {
const MAX: Self = EffectiveVisibility::from_vis(ty::Visibility::Public);
fn new_min<const SHALLOW: bool>(
find: &FindMin<'_, '_, Self, SHALLOW>,
def_id: LocalDefId,
) -> Self {
let effective_vis =
find.effective_visibilities.effective_vis(def_id).copied().unwrap_or_else(|| {
let private_vis = ty::Visibility::Restricted(
find.tcx.parent_module_from_def_id(def_id).to_local_def_id(),
);
EffectiveVisibility::from_vis(private_vis)
});
effective_vis.min(find.min, find.tcx)
}
}
struct EmbargoVisitor<'tcx> {
tcx: TyCtxt<'tcx>,
effective_visibilities: EffectiveVisibilities,
macro_reachable: FxHashSet<(LocalModDefId, LocalModDefId)>,
impl_trait_pass: bool,
changed: bool,
}
struct ReachEverythingInTheInterfaceVisitor<'a, 'tcx> {
effective_vis: EffectiveVisibility,
item_def_id: LocalDefId,
ev: &'a mut EmbargoVisitor<'tcx>,
level: Level,
}
impl<'tcx> EmbargoVisitor<'tcx> {
fn get(&self, def_id: LocalDefId) -> Option<EffectiveVisibility> {
self.effective_visibilities.effective_vis(def_id).copied()
}
fn update(
&mut self,
def_id: LocalDefId,
inherited_effective_vis: EffectiveVisibility,
level: Level,
) {
let nominal_vis = self.tcx.local_visibility(def_id);
self.update_eff_vis(def_id, inherited_effective_vis, Some(nominal_vis), level);
}
fn update_eff_vis(
&mut self,
def_id: LocalDefId,
inherited_effective_vis: EffectiveVisibility,
max_vis: Option<ty::Visibility>,
level: Level,
) {
let private_vis =
ty::Visibility::Restricted(self.tcx.parent_module_from_def_id(def_id).into());
if max_vis != Some(private_vis) {
self.changed |= self.effective_visibilities.update(
def_id,
max_vis,
|| private_vis,
inherited_effective_vis,
level,
self.tcx,
);
}
}
fn reach(
&mut self,
def_id: LocalDefId,
effective_vis: EffectiveVisibility,
) -> ReachEverythingInTheInterfaceVisitor<'_, 'tcx> {
ReachEverythingInTheInterfaceVisitor {
effective_vis,
item_def_id: def_id,
ev: self,
level: Level::Reachable,
}
}
fn reach_through_impl_trait(
&mut self,
def_id: LocalDefId,
effective_vis: EffectiveVisibility,
) -> ReachEverythingInTheInterfaceVisitor<'_, 'tcx> {
ReachEverythingInTheInterfaceVisitor {
effective_vis,
item_def_id: def_id,
ev: self,
level: Level::ReachableThroughImplTrait,
}
}
fn update_reachability_from_macro(
&mut self,
local_def_id: LocalDefId,
md: &MacroDef,
macro_ev: EffectiveVisibility,
) {
let hir_id = self.tcx.hir().local_def_id_to_hir_id(local_def_id);
let attrs = self.tcx.hir().attrs(hir_id);
if attr::find_transparency(attrs, md.macro_rules).0 != Transparency::Opaque {
return;
}
let macro_module_def_id = self.tcx.local_parent(local_def_id);
if self.tcx.opt_def_kind(macro_module_def_id) != Some(DefKind::Mod) {
return;
}
let macro_module_def_id = LocalModDefId::new_unchecked(macro_module_def_id);
if self.effective_visibilities.public_at_level(local_def_id).is_none() {
return;
}
let mut module_def_id = macro_module_def_id;
loop {
let changed_reachability =
self.update_macro_reachable(module_def_id, macro_module_def_id, macro_ev);
if changed_reachability || module_def_id == LocalModDefId::CRATE_DEF_ID {
break;
}
module_def_id = LocalModDefId::new_unchecked(self.tcx.local_parent(module_def_id));
}
}
fn update_macro_reachable(
&mut self,
module_def_id: LocalModDefId,
defining_mod: LocalModDefId,
macro_ev: EffectiveVisibility,
) -> bool {
if self.macro_reachable.insert((module_def_id, defining_mod)) {
self.update_macro_reachable_mod(module_def_id, defining_mod, macro_ev);
true
} else {
false
}
}
fn update_macro_reachable_mod(
&mut self,
module_def_id: LocalModDefId,
defining_mod: LocalModDefId,
macro_ev: EffectiveVisibility,
) {
let module = self.tcx.hir().get_module(module_def_id).0;
for item_id in module.item_ids {
let def_kind = self.tcx.def_kind(item_id.owner_id);
let vis = self.tcx.local_visibility(item_id.owner_id.def_id);
self.update_macro_reachable_def(
item_id.owner_id.def_id,
def_kind,
vis,
defining_mod,
macro_ev,
);
}
for child in self.tcx.module_children_local(module_def_id.to_local_def_id()) {
if !child.reexport_chain.is_empty()
&& child.vis.is_accessible_from(defining_mod, self.tcx)
&& let Res::Def(def_kind, def_id) = child.res
&& let Some(def_id) = def_id.as_local() {
let vis = self.tcx.local_visibility(def_id);
self.update_macro_reachable_def(def_id, def_kind, vis, defining_mod, macro_ev);
}
}
}
fn update_macro_reachable_def(
&mut self,
def_id: LocalDefId,
def_kind: DefKind,
vis: ty::Visibility,
module: LocalModDefId,
macro_ev: EffectiveVisibility,
) {
self.update(def_id, macro_ev, Level::Reachable);
match def_kind {
DefKind::Const | DefKind::Static(_) | DefKind::TraitAlias | DefKind::TyAlias => {
if vis.is_accessible_from(module, self.tcx) {
self.update(def_id, macro_ev, Level::Reachable);
}
}
DefKind::Macro(_) => {
let item = self.tcx.hir().expect_item(def_id);
if let hir::ItemKind::Macro(MacroDef { macro_rules: false, .. }, _) = item.kind {
if vis.is_accessible_from(module, self.tcx) {
self.update(def_id, macro_ev, Level::Reachable);
}
}
}
DefKind::Mod => {
if vis.is_accessible_from(module, self.tcx) {
self.update_macro_reachable(
LocalModDefId::new_unchecked(def_id),
module,
macro_ev,
);
}
}
DefKind::Struct | DefKind::Union => {
let item = self.tcx.hir().expect_item(def_id);
if let hir::ItemKind::Struct(ref struct_def, _)
| hir::ItemKind::Union(ref struct_def, _) = item.kind
{
for field in struct_def.fields() {
let field_vis = self.tcx.local_visibility(field.def_id);
if field_vis.is_accessible_from(module, self.tcx) {
self.reach(field.def_id, macro_ev).ty();
}
}
} else {
bug!("item {:?} with DefKind {:?}", item, def_kind);
}
}
DefKind::AssocConst
| DefKind::AssocTy
| DefKind::ConstParam
| DefKind::Ctor(_, _)
| DefKind::Enum
| DefKind::ForeignTy
| DefKind::Fn
| DefKind::OpaqueTy
| DefKind::AssocFn
| DefKind::Trait
| DefKind::TyParam
| DefKind::Variant
| DefKind::LifetimeParam
| DefKind::ExternCrate
| DefKind::Use
| DefKind::ForeignMod
| DefKind::AnonConst
| DefKind::InlineConst
| DefKind::Field
| DefKind::GlobalAsm
| DefKind::Impl { .. }
| DefKind::Closure
| DefKind::Generator => (),
}
}
}
impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> {
fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
if self.impl_trait_pass
&& let hir::ItemKind::OpaqueTy(ref opaque) = item.kind
&& !opaque.in_trait {
let pub_ev = EffectiveVisibility::from_vis(ty::Visibility::Public);
self.reach_through_impl_trait(item.owner_id.def_id, pub_ev)
.generics()
.predicates()
.ty();
return;
}
let item_ev = self.get(item.owner_id.def_id);
match item.kind {
hir::ItemKind::Use(..)
| hir::ItemKind::ExternCrate(..)
| hir::ItemKind::GlobalAsm(..) => {}
hir::ItemKind::Mod(..) | hir::ItemKind::OpaqueTy(..) => {}
hir::ItemKind::Macro(ref macro_def, _) => {
if let Some(item_ev) = item_ev {
self.update_reachability_from_macro(item.owner_id.def_id, macro_def, item_ev);
}
}
hir::ItemKind::Const(..)
| hir::ItemKind::Static(..)
| hir::ItemKind::Fn(..)
| hir::ItemKind::TyAlias(..) => {
if let Some(item_ev) = item_ev {
self.reach(item.owner_id.def_id, item_ev).generics().predicates().ty();
}
}
hir::ItemKind::Trait(.., trait_item_refs) => {
if let Some(item_ev) = item_ev {
self.reach(item.owner_id.def_id, item_ev).generics().predicates();
for trait_item_ref in trait_item_refs {
self.update(trait_item_ref.id.owner_id.def_id, item_ev, Level::Reachable);
let tcx = self.tcx;
let mut reach = self.reach(trait_item_ref.id.owner_id.def_id, item_ev);
reach.generics().predicates();
if trait_item_ref.kind == AssocItemKind::Type
&& !tcx.defaultness(trait_item_ref.id.owner_id).has_value()
{
} else {
reach.ty();
}
}
}
}
hir::ItemKind::TraitAlias(..) => {
if let Some(item_ev) = item_ev {
self.reach(item.owner_id.def_id, item_ev).generics().predicates();
}
}
hir::ItemKind::Impl(ref impl_) => {
let item_ev = EffectiveVisibility::of_impl::<true>(
item.owner_id.def_id,
self.tcx,
&self.effective_visibilities,
);
self.update_eff_vis(item.owner_id.def_id, item_ev, None, Level::Direct);
self.reach(item.owner_id.def_id, item_ev).generics().predicates().ty().trait_ref();
for impl_item_ref in impl_.items {
let def_id = impl_item_ref.id.owner_id.def_id;
let max_vis =
impl_.of_trait.is_none().then(|| self.tcx.local_visibility(def_id));
self.update_eff_vis(def_id, item_ev, max_vis, Level::Direct);
if let Some(impl_item_ev) = self.get(def_id) {
self.reach(def_id, impl_item_ev).generics().predicates().ty();
}
}
}
hir::ItemKind::Enum(ref def, _) => {
if let Some(item_ev) = item_ev {
self.reach(item.owner_id.def_id, item_ev).generics().predicates();
}
for variant in def.variants {
if let Some(item_ev) = item_ev {
self.update(variant.def_id, item_ev, Level::Reachable);
}
if let Some(variant_ev) = self.get(variant.def_id) {
if let Some(ctor_def_id) = variant.data.ctor_def_id() {
self.update(ctor_def_id, variant_ev, Level::Reachable);
}
for field in variant.data.fields() {
self.update(field.def_id, variant_ev, Level::Reachable);
self.reach(field.def_id, variant_ev).ty();
}
self.reach(item.owner_id.def_id, variant_ev).ty();
}
if let Some(ctor_def_id) = variant.data.ctor_def_id() {
if let Some(ctor_ev) = self.get(ctor_def_id) {
self.reach(item.owner_id.def_id, ctor_ev).ty();
}
}
}
}
hir::ItemKind::ForeignMod { items, .. } => {
for foreign_item in items {
if let Some(foreign_item_ev) = self.get(foreign_item.id.owner_id.def_id) {
self.reach(foreign_item.id.owner_id.def_id, foreign_item_ev)
.generics()
.predicates()
.ty();
}
}
}
hir::ItemKind::Struct(ref struct_def, _) | hir::ItemKind::Union(ref struct_def, _) => {
if let Some(item_ev) = item_ev {
self.reach(item.owner_id.def_id, item_ev).generics().predicates();
for field in struct_def.fields() {
self.update(field.def_id, item_ev, Level::Reachable);
if let Some(field_ev) = self.get(field.def_id) {
self.reach(field.def_id, field_ev).ty();
}
}
}
if let Some(ctor_def_id) = struct_def.ctor_def_id() {
if let Some(item_ev) = item_ev {
self.update(ctor_def_id, item_ev, Level::Reachable);
}
if let Some(ctor_ev) = self.get(ctor_def_id) {
self.reach(item.owner_id.def_id, ctor_ev).ty();
}
}
}
}
}
}
impl ReachEverythingInTheInterfaceVisitor<'_, '_> {
fn generics(&mut self) -> &mut Self {
for param in &self.ev.tcx.generics_of(self.item_def_id).params {
match param.kind {
GenericParamDefKind::Lifetime => {}
GenericParamDefKind::Type { has_default, .. } => {
if has_default {
self.visit(self.ev.tcx.type_of(param.def_id).instantiate_identity());
}
}
GenericParamDefKind::Const { has_default, .. } => {
self.visit(self.ev.tcx.type_of(param.def_id).instantiate_identity());
if has_default {
self.visit(
self.ev.tcx.const_param_default(param.def_id).instantiate_identity(),
);
}
}
}
}
self
}
fn predicates(&mut self) -> &mut Self {
self.visit_predicates(self.ev.tcx.predicates_of(self.item_def_id));
self
}
fn ty(&mut self) -> &mut Self {
self.visit(self.ev.tcx.type_of(self.item_def_id).instantiate_identity());
self
}
fn trait_ref(&mut self) -> &mut Self {
if let Some(trait_ref) = self.ev.tcx.impl_trait_ref(self.item_def_id) {
self.visit_trait(trait_ref.instantiate_identity());
}
self
}
}
impl<'tcx> DefIdVisitor<'tcx> for ReachEverythingInTheInterfaceVisitor<'_, 'tcx> {
fn tcx(&self) -> TyCtxt<'tcx> {
self.ev.tcx
}
fn visit_def_id(
&mut self,
def_id: DefId,
_kind: &str,
_descr: &dyn fmt::Display,
) -> ControlFlow<Self::BreakTy> {
if let Some(def_id) = def_id.as_local() {
let max_vis = (self.level != Level::ReachableThroughImplTrait)
.then(|| self.ev.tcx.local_visibility(def_id));
self.ev.update_eff_vis(def_id, self.effective_vis, max_vis, self.level);
}
ControlFlow::Continue(())
}
}
pub struct TestReachabilityVisitor<'tcx, 'a> {
tcx: TyCtxt<'tcx>,
effective_visibilities: &'a EffectiveVisibilities,
}
fn vis_to_string<'tcx>(def_id: LocalDefId, vis: ty::Visibility, tcx: TyCtxt<'tcx>) -> String {
match vis {
ty::Visibility::Restricted(restricted_id) => {
if restricted_id.is_top_level_module() {
"pub(crate)".to_string()
} else if restricted_id == tcx.parent_module_from_def_id(def_id).to_local_def_id() {
"pub(self)".to_string()
} else {
format!("pub({})", tcx.item_name(restricted_id.to_def_id()))
}
}
ty::Visibility::Public => "pub".to_string(),
}
}
impl<'tcx, 'a> TestReachabilityVisitor<'tcx, 'a> {
fn effective_visibility_diagnostic(&mut self, def_id: LocalDefId) {
if self.tcx.has_attr(def_id, sym::rustc_effective_visibility) {
let mut error_msg = String::new();
let span = self.tcx.def_span(def_id.to_def_id());
if let Some(effective_vis) = self.effective_visibilities.effective_vis(def_id) {
for level in Level::all_levels() {
let vis_str = vis_to_string(def_id, *effective_vis.at_level(level), self.tcx);
if level != Level::Direct {
error_msg.push_str(", ");
}
error_msg.push_str(&format!("{level:?}: {vis_str}"));
}
} else {
error_msg.push_str("not in the table");
}
self.tcx.sess.emit_err(ReportEffectiveVisibility { span, descr: error_msg });
}
}
}
impl<'tcx, 'a> Visitor<'tcx> for TestReachabilityVisitor<'tcx, 'a> {
fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
self.effective_visibility_diagnostic(item.owner_id.def_id);
match item.kind {
hir::ItemKind::Enum(ref def, _) => {
for variant in def.variants.iter() {
self.effective_visibility_diagnostic(variant.def_id);
if let Some(ctor_def_id) = variant.data.ctor_def_id() {
self.effective_visibility_diagnostic(ctor_def_id);
}
for field in variant.data.fields() {
self.effective_visibility_diagnostic(field.def_id);
}
}
}
hir::ItemKind::Struct(ref def, _) | hir::ItemKind::Union(ref def, _) => {
if let Some(ctor_def_id) = def.ctor_def_id() {
self.effective_visibility_diagnostic(ctor_def_id);
}
for field in def.fields() {
self.effective_visibility_diagnostic(field.def_id);
}
}
_ => {}
}
}
fn visit_trait_item(&mut self, item: &'tcx hir::TraitItem<'tcx>) {
self.effective_visibility_diagnostic(item.owner_id.def_id);
}
fn visit_impl_item(&mut self, item: &'tcx hir::ImplItem<'tcx>) {
self.effective_visibility_diagnostic(item.owner_id.def_id);
}
fn visit_foreign_item(&mut self, item: &'tcx hir::ForeignItem<'tcx>) {
self.effective_visibility_diagnostic(item.owner_id.def_id);
}
}
struct NamePrivacyVisitor<'tcx> {
tcx: TyCtxt<'tcx>,
maybe_typeck_results: Option<&'tcx ty::TypeckResults<'tcx>>,
current_item: LocalDefId,
}
impl<'tcx> NamePrivacyVisitor<'tcx> {
#[track_caller]
fn typeck_results(&self) -> &'tcx ty::TypeckResults<'tcx> {
self.maybe_typeck_results
.expect("`NamePrivacyVisitor::typeck_results` called outside of body")
}
fn check_field(
&mut self,
use_ctxt: Span, span: Span, def: ty::AdtDef<'tcx>, field: &'tcx ty::FieldDef,
in_update_syntax: bool,
) {
if def.is_enum() {
return;
}
let ident = Ident::new(kw::Empty, use_ctxt);
let hir_id = self.tcx.hir().local_def_id_to_hir_id(self.current_item);
let def_id = self.tcx.adjust_ident_and_get_scope(ident, def.did(), hir_id).1;
if !field.vis.is_accessible_from(def_id, self.tcx) {
self.tcx.sess.emit_err(FieldIsPrivate {
span,
field_name: field.name,
variant_descr: def.variant_descr(),
def_path_str: self.tcx.def_path_str(def.did()),
label: if in_update_syntax {
FieldIsPrivateLabel::IsUpdateSyntax { span, field_name: field.name }
} else {
FieldIsPrivateLabel::Other { span }
},
});
}
}
}
impl<'tcx> Visitor<'tcx> for NamePrivacyVisitor<'tcx> {
type NestedFilter = nested_filter::All;
fn nested_visit_map(&mut self) -> Self::Map {
self.tcx.hir()
}
fn visit_mod(&mut self, _m: &'tcx hir::Mod<'tcx>, _s: Span, _n: hir::HirId) {
}
fn visit_nested_body(&mut self, body: hir::BodyId) {
let old_maybe_typeck_results =
self.maybe_typeck_results.replace(self.tcx.typeck_body(body));
let body = self.tcx.hir().body(body);
self.visit_body(body);
self.maybe_typeck_results = old_maybe_typeck_results;
}
fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
let orig_current_item = mem::replace(&mut self.current_item, item.owner_id.def_id);
intravisit::walk_item(self, item);
self.current_item = orig_current_item;
}
fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
if let hir::ExprKind::Struct(qpath, fields, ref base) = expr.kind {
let res = self.typeck_results().qpath_res(qpath, expr.hir_id);
let adt = self.typeck_results().expr_ty(expr).ty_adt_def().unwrap();
let variant = adt.variant_of_res(res);
if let Some(base) = *base {
for (vf_index, variant_field) in variant.fields.iter_enumerated() {
let field = fields
.iter()
.find(|f| self.typeck_results().field_index(f.hir_id) == vf_index);
let (use_ctxt, span) = match field {
Some(field) => (field.ident.span, field.span),
None => (base.span, base.span),
};
self.check_field(use_ctxt, span, adt, variant_field, true);
}
} else {
for field in fields {
let use_ctxt = field.ident.span;
let index = self.typeck_results().field_index(field.hir_id);
self.check_field(use_ctxt, field.span, adt, &variant.fields[index], false);
}
}
}
intravisit::walk_expr(self, expr);
}
fn visit_pat(&mut self, pat: &'tcx hir::Pat<'tcx>) {
if let PatKind::Struct(ref qpath, fields, _) = pat.kind {
let res = self.typeck_results().qpath_res(qpath, pat.hir_id);
let adt = self.typeck_results().pat_ty(pat).ty_adt_def().unwrap();
let variant = adt.variant_of_res(res);
for field in fields {
let use_ctxt = field.ident.span;
let index = self.typeck_results().field_index(field.hir_id);
self.check_field(use_ctxt, field.span, adt, &variant.fields[index], false);
}
}
intravisit::walk_pat(self, pat);
}
}
struct TypePrivacyVisitor<'tcx> {
tcx: TyCtxt<'tcx>,
maybe_typeck_results: Option<&'tcx ty::TypeckResults<'tcx>>,
current_item: LocalDefId,
span: Span,
}
impl<'tcx> TypePrivacyVisitor<'tcx> {
#[track_caller]
fn typeck_results(&self) -> &'tcx ty::TypeckResults<'tcx> {
self.maybe_typeck_results
.expect("`TypePrivacyVisitor::typeck_results` called outside of body")
}
fn item_is_accessible(&self, did: DefId) -> bool {
self.tcx.visibility(did).is_accessible_from(self.current_item, self.tcx)
}
fn check_expr_pat_type(&mut self, id: hir::HirId, span: Span) -> bool {
self.span = span;
let typeck_results = self.typeck_results();
let result: ControlFlow<()> = try {
self.visit(typeck_results.node_type(id))?;
self.visit(typeck_results.node_args(id))?;
if let Some(adjustments) = typeck_results.adjustments().get(id) {
adjustments.iter().try_for_each(|adjustment| self.visit(adjustment.target))?;
}
};
result.is_break()
}
fn check_def_id(&mut self, def_id: DefId, kind: &str, descr: &dyn fmt::Display) -> bool {
let is_error = !self.item_is_accessible(def_id);
if is_error {
self.tcx.sess.emit_err(ItemIsPrivate { span: self.span, kind, descr: descr.into() });
}
is_error
}
}
impl<'tcx> Visitor<'tcx> for TypePrivacyVisitor<'tcx> {
type NestedFilter = nested_filter::All;
fn nested_visit_map(&mut self) -> Self::Map {
self.tcx.hir()
}
fn visit_mod(&mut self, _m: &'tcx hir::Mod<'tcx>, _s: Span, _n: hir::HirId) {
}
fn visit_nested_body(&mut self, body: hir::BodyId) {
let old_maybe_typeck_results =
self.maybe_typeck_results.replace(self.tcx.typeck_body(body));
let body = self.tcx.hir().body(body);
self.visit_body(body);
self.maybe_typeck_results = old_maybe_typeck_results;
}
fn visit_generic_arg(&mut self, generic_arg: &'tcx hir::GenericArg<'tcx>) {
match generic_arg {
hir::GenericArg::Type(t) => self.visit_ty(t),
hir::GenericArg::Infer(inf) => self.visit_infer(inf),
hir::GenericArg::Lifetime(_) | hir::GenericArg::Const(_) => {}
}
}
fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'tcx>) {
self.span = hir_ty.span;
if let Some(typeck_results) = self.maybe_typeck_results {
if self.visit(typeck_results.node_type(hir_ty.hir_id)).is_break() {
return;
}
} else {
if self.visit(rustc_hir_analysis::hir_ty_to_ty(self.tcx, hir_ty)).is_break() {
return;
}
}
intravisit::walk_ty(self, hir_ty);
}
fn visit_infer(&mut self, inf: &'tcx hir::InferArg) {
self.span = inf.span;
if let Some(typeck_results) = self.maybe_typeck_results {
if let Some(ty) = typeck_results.node_type_opt(inf.hir_id) {
if self.visit(ty).is_break() {
return;
}
} else {
}
} else {
bug!("visit_infer without typeck_results");
}
intravisit::walk_inf(self, inf);
}
fn visit_trait_ref(&mut self, trait_ref: &'tcx hir::TraitRef<'tcx>) {
self.span = trait_ref.path.span;
if self.maybe_typeck_results.is_none() {
let bounds = rustc_hir_analysis::hir_trait_to_predicates(
self.tcx,
trait_ref,
self.tcx.types.never,
);
for (clause, _) in bounds.clauses() {
match clause.kind().skip_binder() {
ty::ClauseKind::Trait(trait_predicate) => {
if self.visit_trait(trait_predicate.trait_ref).is_break() {
return;
}
}
ty::ClauseKind::Projection(proj_predicate) => {
let term = self.visit(proj_predicate.term);
if term.is_break()
|| self.visit_projection_ty(proj_predicate.projection_ty).is_break()
{
return;
}
}
_ => {}
}
}
}
intravisit::walk_trait_ref(self, trait_ref);
}
fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
if self.check_expr_pat_type(expr.hir_id, expr.span) {
return;
}
match expr.kind {
hir::ExprKind::Assign(_, rhs, _) | hir::ExprKind::Match(rhs, ..) => {
if self.check_expr_pat_type(rhs.hir_id, rhs.span) {
return;
}
}
hir::ExprKind::MethodCall(segment, ..) => {
self.span = segment.ident.span;
if let Some(def_id) = self.typeck_results().type_dependent_def_id(expr.hir_id) {
if self.visit(self.tcx.type_of(def_id).instantiate_identity()).is_break() {
return;
}
} else {
self.tcx
.sess
.delay_span_bug(expr.span, "no type-dependent def for method call");
}
}
_ => {}
}
intravisit::walk_expr(self, expr);
}
fn visit_qpath(&mut self, qpath: &'tcx hir::QPath<'tcx>, id: hir::HirId, span: Span) {
let def = match qpath {
hir::QPath::Resolved(_, path) => match path.res {
Res::Def(kind, def_id) => Some((kind, def_id)),
_ => None,
},
hir::QPath::TypeRelative(..) | hir::QPath::LangItem(..) => self
.maybe_typeck_results
.and_then(|typeck_results| typeck_results.type_dependent_def(id)),
};
let def = def.filter(|(kind, _)| {
matches!(
kind,
DefKind::AssocFn | DefKind::AssocConst | DefKind::AssocTy | DefKind::Static(_)
)
});
if let Some((kind, def_id)) = def {
let is_local_static =
if let DefKind::Static(_) = kind { def_id.is_local() } else { false };
if !self.item_is_accessible(def_id) && !is_local_static {
let name = match *qpath {
hir::QPath::LangItem(it, ..) => {
self.tcx.lang_items().get(it).map(|did| self.tcx.def_path_str(did))
}
hir::QPath::Resolved(_, path) => Some(self.tcx.def_path_str(path.res.def_id())),
hir::QPath::TypeRelative(_, segment) => Some(segment.ident.to_string()),
};
let kind = self.tcx.def_descr(def_id);
let sess = self.tcx.sess;
let _ = match name {
Some(name) => {
sess.emit_err(ItemIsPrivate { span, kind, descr: (&name).into() })
}
None => sess.emit_err(UnnamedItemIsPrivate { span, kind }),
};
return;
}
}
intravisit::walk_qpath(self, qpath, id);
}
fn visit_pat(&mut self, pattern: &'tcx hir::Pat<'tcx>) {
if self.check_expr_pat_type(pattern.hir_id, pattern.span) {
return;
}
intravisit::walk_pat(self, pattern);
}
fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) {
if let Some(init) = local.init {
if self.check_expr_pat_type(init.hir_id, init.span) {
return;
}
}
intravisit::walk_local(self, local);
}
fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
let orig_current_item = mem::replace(&mut self.current_item, item.owner_id.def_id);
let old_maybe_typeck_results = self.maybe_typeck_results.take();
intravisit::walk_item(self, item);
self.maybe_typeck_results = old_maybe_typeck_results;
self.current_item = orig_current_item;
}
}
impl<'tcx> DefIdVisitor<'tcx> for TypePrivacyVisitor<'tcx> {
fn tcx(&self) -> TyCtxt<'tcx> {
self.tcx
}
fn visit_def_id(
&mut self,
def_id: DefId,
kind: &str,
descr: &dyn fmt::Display,
) -> ControlFlow<Self::BreakTy> {
if self.check_def_id(def_id, kind, descr) {
ControlFlow::Break(())
} else {
ControlFlow::Continue(())
}
}
}
struct SearchInterfaceForPrivateItemsVisitor<'tcx> {
tcx: TyCtxt<'tcx>,
item_def_id: LocalDefId,
required_visibility: ty::Visibility,
required_effective_vis: Option<EffectiveVisibility>,
in_assoc_ty: bool,
in_primary_interface: bool,
}
impl SearchInterfaceForPrivateItemsVisitor<'_> {
fn generics(&mut self) -> &mut Self {
self.in_primary_interface = true;
for param in &self.tcx.generics_of(self.item_def_id).params {
match param.kind {
GenericParamDefKind::Lifetime => {}
GenericParamDefKind::Type { has_default, .. } => {
if has_default {
self.visit(self.tcx.type_of(param.def_id).instantiate_identity());
}
}
GenericParamDefKind::Const { .. } => {
self.visit(self.tcx.type_of(param.def_id).instantiate_identity());
}
}
}
self
}
fn predicates(&mut self) -> &mut Self {
self.in_primary_interface = false;
self.visit_predicates(self.tcx.explicit_predicates_of(self.item_def_id));
self
}
fn bounds(&mut self) -> &mut Self {
self.in_primary_interface = false;
self.visit_clauses(self.tcx.explicit_item_bounds(self.item_def_id).skip_binder());
self
}
fn ty(&mut self) -> &mut Self {
self.in_primary_interface = true;
self.visit(self.tcx.type_of(self.item_def_id).instantiate_identity());
self
}
fn check_def_id(&mut self, def_id: DefId, kind: &str, descr: &dyn fmt::Display) -> bool {
if self.leaks_private_dep(def_id) {
self.tcx.emit_spanned_lint(
lint::builtin::EXPORTED_PRIVATE_DEPENDENCIES,
self.tcx.hir().local_def_id_to_hir_id(self.item_def_id),
self.tcx.def_span(self.item_def_id.to_def_id()),
FromPrivateDependencyInPublicInterface {
kind,
descr: descr.into(),
krate: self.tcx.crate_name(def_id.krate),
},
);
}
let Some(local_def_id) = def_id.as_local() else {
return false;
};
let vis = self.tcx.local_visibility(local_def_id);
let span = self.tcx.def_span(self.item_def_id.to_def_id());
let vis_span = self.tcx.def_span(def_id);
if self.in_assoc_ty && !vis.is_at_least(self.required_visibility, self.tcx) {
let vis_descr = match vis {
ty::Visibility::Public => "public",
ty::Visibility::Restricted(vis_def_id) => {
if vis_def_id
== self.tcx.parent_module_from_def_id(local_def_id).to_local_def_id()
{
"private"
} else if vis_def_id.is_top_level_module() {
"crate-private"
} else {
"restricted"
}
}
};
self.tcx.sess.emit_err(InPublicInterface {
span,
vis_descr,
kind,
descr: descr.into(),
vis_span,
});
return false;
}
let Some(effective_vis) = self.required_effective_vis else {
return false;
};
let reachable_at_vis = *effective_vis.at_level(Level::Reachable);
if !vis.is_at_least(reachable_at_vis, self.tcx) {
let lint = if self.in_primary_interface {
lint::builtin::PRIVATE_INTERFACES
} else {
lint::builtin::PRIVATE_BOUNDS
};
self.tcx.emit_spanned_lint(
lint,
self.tcx.hir().local_def_id_to_hir_id(self.item_def_id),
span,
PrivateInterfacesOrBoundsLint {
item_span: span,
item_kind: self.tcx.def_descr(self.item_def_id.to_def_id()),
item_descr: (&LazyDefPathStr {
def_id: self.item_def_id.to_def_id(),
tcx: self.tcx,
})
.into(),
item_vis_descr: &vis_to_string(self.item_def_id, reachable_at_vis, self.tcx),
ty_span: vis_span,
ty_kind: kind,
ty_descr: descr.into(),
ty_vis_descr: &vis_to_string(local_def_id, vis, self.tcx),
},
);
}
false
}
fn leaks_private_dep(&self, item_id: DefId) -> bool {
let ret = self.required_visibility.is_public() && self.tcx.is_private_dep(item_id.krate);
debug!("leaks_private_dep(item_id={:?})={}", item_id, ret);
ret
}
}
impl<'tcx> DefIdVisitor<'tcx> for SearchInterfaceForPrivateItemsVisitor<'tcx> {
fn tcx(&self) -> TyCtxt<'tcx> {
self.tcx
}
fn visit_def_id(
&mut self,
def_id: DefId,
kind: &str,
descr: &dyn fmt::Display,
) -> ControlFlow<Self::BreakTy> {
if self.check_def_id(def_id, kind, descr) {
ControlFlow::Break(())
} else {
ControlFlow::Continue(())
}
}
}
struct PrivateItemsInPublicInterfacesChecker<'tcx, 'a> {
tcx: TyCtxt<'tcx>,
effective_visibilities: &'a EffectiveVisibilities,
}
impl<'tcx> PrivateItemsInPublicInterfacesChecker<'tcx, '_> {
fn check(
&self,
def_id: LocalDefId,
required_visibility: ty::Visibility,
required_effective_vis: Option<EffectiveVisibility>,
) -> SearchInterfaceForPrivateItemsVisitor<'tcx> {
SearchInterfaceForPrivateItemsVisitor {
tcx: self.tcx,
item_def_id: def_id,
required_visibility,
required_effective_vis,
in_assoc_ty: false,
in_primary_interface: true,
}
}
fn check_unnameable(&self, def_id: LocalDefId, effective_vis: Option<EffectiveVisibility>) {
let Some(effective_vis) = effective_vis else {
return;
};
let reexported_at_vis = effective_vis.at_level(Level::Reexported);
let reachable_at_vis = effective_vis.at_level(Level::Reachable);
if reachable_at_vis.is_public() && reexported_at_vis != reachable_at_vis {
let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id);
let span = self.tcx.def_span(def_id.to_def_id());
self.tcx.emit_spanned_lint(
lint::builtin::UNNAMEABLE_TYPES,
hir_id,
span,
UnnameableTypesLint {
span,
kind: self.tcx.def_descr(def_id.to_def_id()),
descr: (&LazyDefPathStr { def_id: def_id.to_def_id(), tcx: self.tcx }).into(),
reachable_vis: &vis_to_string(def_id, *reachable_at_vis, self.tcx),
reexported_vis: &vis_to_string(def_id, *reexported_at_vis, self.tcx),
},
);
}
}
fn check_assoc_item(
&self,
def_id: LocalDefId,
assoc_item_kind: AssocItemKind,
vis: ty::Visibility,
effective_vis: Option<EffectiveVisibility>,
) {
let mut check = self.check(def_id, vis, effective_vis);
let (check_ty, is_assoc_ty) = match assoc_item_kind {
AssocItemKind::Const | AssocItemKind::Fn { .. } => (true, false),
AssocItemKind::Type => (self.tcx.defaultness(def_id).has_value(), true),
};
check.in_assoc_ty = is_assoc_ty;
check.generics().predicates();
if check_ty {
check.ty();
}
}
fn get(&self, def_id: LocalDefId) -> Option<EffectiveVisibility> {
self.effective_visibilities.effective_vis(def_id).copied()
}
pub fn check_item(&mut self, id: ItemId) {
let tcx = self.tcx;
let def_id = id.owner_id.def_id;
let item_visibility = tcx.local_visibility(def_id);
let effective_vis = self.get(def_id);
let def_kind = tcx.def_kind(def_id);
match def_kind {
DefKind::Const | DefKind::Static(_) | DefKind::Fn | DefKind::TyAlias => {
if let DefKind::TyAlias = def_kind {
self.check_unnameable(def_id, effective_vis);
}
self.check(def_id, item_visibility, effective_vis).generics().predicates().ty();
}
DefKind::OpaqueTy => {
self.check(def_id, item_visibility, effective_vis).generics().bounds();
}
DefKind::Trait => {
let item = tcx.hir().item(id);
if let hir::ItemKind::Trait(.., trait_item_refs) = item.kind {
self.check_unnameable(item.owner_id.def_id, effective_vis);
self.check(item.owner_id.def_id, item_visibility, effective_vis)
.generics()
.predicates();
for trait_item_ref in trait_item_refs {
self.check_assoc_item(
trait_item_ref.id.owner_id.def_id,
trait_item_ref.kind,
item_visibility,
effective_vis,
);
if let AssocItemKind::Type = trait_item_ref.kind {
self.check(
trait_item_ref.id.owner_id.def_id,
item_visibility,
effective_vis,
)
.bounds();
}
}
}
}
DefKind::TraitAlias => {
self.check(def_id, item_visibility, effective_vis).generics().predicates();
}
DefKind::Enum => {
let item = tcx.hir().item(id);
if let hir::ItemKind::Enum(ref def, _) = item.kind {
self.check_unnameable(item.owner_id.def_id, effective_vis);
self.check(item.owner_id.def_id, item_visibility, effective_vis)
.generics()
.predicates();
for variant in def.variants {
for field in variant.data.fields() {
self.check(field.def_id, item_visibility, effective_vis).ty();
}
}
}
}
DefKind::ForeignMod => {
let item = tcx.hir().item(id);
if let hir::ItemKind::ForeignMod { items, .. } = item.kind {
for foreign_item in items {
let foreign_item = tcx.hir().foreign_item(foreign_item.id);
let ev = self.get(foreign_item.owner_id.def_id);
let vis = tcx.local_visibility(foreign_item.owner_id.def_id);
if let ForeignItemKind::Type = foreign_item.kind {
self.check_unnameable(foreign_item.owner_id.def_id, ev);
}
self.check(foreign_item.owner_id.def_id, vis, ev)
.generics()
.predicates()
.ty();
}
}
}
DefKind::Struct | DefKind::Union => {
let item = tcx.hir().item(id);
if let hir::ItemKind::Struct(ref struct_def, _)
| hir::ItemKind::Union(ref struct_def, _) = item.kind
{
self.check_unnameable(item.owner_id.def_id, effective_vis);
self.check(item.owner_id.def_id, item_visibility, effective_vis)
.generics()
.predicates();
for field in struct_def.fields() {
let field_visibility = tcx.local_visibility(field.def_id);
let field_ev = self.get(field.def_id);
self.check(
field.def_id,
min(item_visibility, field_visibility, tcx),
field_ev,
)
.ty();
}
}
}
DefKind::Impl { .. } => {
let item = tcx.hir().item(id);
if let hir::ItemKind::Impl(ref impl_) = item.kind {
let impl_vis = ty::Visibility::of_impl::<false>(
item.owner_id.def_id,
tcx,
&Default::default(),
);
let impl_ev = EffectiveVisibility::of_impl::<false>(
item.owner_id.def_id,
tcx,
self.effective_visibilities,
);
if impl_.of_trait.is_none() {
self.check(item.owner_id.def_id, impl_vis, Some(impl_ev))
.generics()
.predicates();
}
for impl_item_ref in impl_.items {
let impl_item_vis = if impl_.of_trait.is_none() {
min(
tcx.local_visibility(impl_item_ref.id.owner_id.def_id),
impl_vis,
tcx,
)
} else {
impl_vis
};
let impl_item_ev = if impl_.of_trait.is_none() {
self.get(impl_item_ref.id.owner_id.def_id)
.map(|ev| ev.min(impl_ev, self.tcx))
} else {
Some(impl_ev)
};
self.check_assoc_item(
impl_item_ref.id.owner_id.def_id,
impl_item_ref.kind,
impl_item_vis,
impl_item_ev,
);
}
}
}
_ => {}
}
}
}
pub fn provide(providers: &mut Providers) {
*providers = Providers {
visibility,
effective_visibilities,
check_private_in_public,
check_mod_privacy,
..*providers
};
}
fn visibility(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Visibility<DefId> {
local_visibility(tcx, def_id).to_def_id()
}
fn local_visibility(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Visibility {
match tcx.resolutions(()).visibilities.get(&def_id) {
Some(vis) => *vis,
None => {
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
match tcx.hir().get(hir_id) {
Node::Expr(hir::Expr { kind: hir::ExprKind::Closure{..}, .. })
| Node::Item(hir::Item {
kind: hir::ItemKind::Use(_, hir::UseKind::ListStem)
| hir::ItemKind::OpaqueTy(..),
..
}) => ty::Visibility::Restricted(tcx.parent_module(hir_id).to_local_def_id()),
Node::ImplItem(impl_item) => {
match tcx.hir().get_by_def_id(tcx.hir().get_parent_item(hir_id).def_id) {
Node::Item(hir::Item {
kind: hir::ItemKind::Impl(hir::Impl { of_trait: Some(tr), .. }),
..
}) => tr.path.res.opt_def_id().map_or_else(
|| {
tcx.sess.delay_span_bug(tr.path.span, "trait without a def-id");
ty::Visibility::Public
},
|def_id| tcx.visibility(def_id).expect_local(),
),
_ => span_bug!(impl_item.span, "the parent is not a trait impl"),
}
}
_ => span_bug!(
tcx.def_span(def_id),
"visibility table unexpectedly missing a def-id: {:?}",
def_id,
),
}
}
}
}
fn check_mod_privacy(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) {
let mut visitor = NamePrivacyVisitor {
tcx,
maybe_typeck_results: None,
current_item: module_def_id.to_local_def_id(),
};
let (module, span, hir_id) = tcx.hir().get_module(module_def_id);
intravisit::walk_mod(&mut visitor, module, hir_id);
let mut visitor = TypePrivacyVisitor {
tcx,
maybe_typeck_results: None,
current_item: module_def_id.to_local_def_id(),
span,
};
intravisit::walk_mod(&mut visitor, module, hir_id);
}
fn effective_visibilities(tcx: TyCtxt<'_>, (): ()) -> &EffectiveVisibilities {
let mut visitor = EmbargoVisitor {
tcx,
effective_visibilities: tcx.resolutions(()).effective_visibilities.clone(),
macro_reachable: Default::default(),
impl_trait_pass: !tcx.sess.opts.actually_rustdoc,
changed: false,
};
visitor.effective_visibilities.check_invariants(tcx);
if visitor.impl_trait_pass {
tcx.hir().visit_all_item_likes_in_crate(&mut visitor);
visitor.impl_trait_pass = false;
visitor.changed = false;
}
loop {
tcx.hir().visit_all_item_likes_in_crate(&mut visitor);
if visitor.changed {
visitor.changed = false;
} else {
break;
}
}
visitor.effective_visibilities.check_invariants(tcx);
let mut check_visitor =
TestReachabilityVisitor { tcx, effective_visibilities: &visitor.effective_visibilities };
check_visitor.effective_visibility_diagnostic(CRATE_DEF_ID);
tcx.hir().visit_all_item_likes_in_crate(&mut check_visitor);
tcx.arena.alloc(visitor.effective_visibilities)
}
fn check_private_in_public(tcx: TyCtxt<'_>, (): ()) {
let effective_visibilities = tcx.effective_visibilities(());
let mut checker = PrivateItemsInPublicInterfacesChecker { tcx, effective_visibilities };
for id in tcx.hir().items() {
checker.check_item(id);
}
}