blob: e64d2eba06bb7872bd794e399f22e164a6837a6d [file] [log] [blame]
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![doc(rust_logo)]
#![feature(rustdoc_internals)]
#![allow(internal_features)]
#![feature(associated_type_defaults)]
#![feature(try_blocks)]
#![feature(let_chains)]
mod errors;
use rustc_ast::visit::{try_visit, VisitorResult};
use rustc_ast::MacroDef;
use rustc_attr as attr;
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::intern::Interned;
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, ItemKind, PatKind};
use rustc_middle::middle::privacy::{EffectiveVisibilities, EffectiveVisibility, Level};
use rustc_middle::query::Providers;
use rustc_middle::ty::GenericArgs;
use rustc_middle::ty::{self, Const, GenericParamDefKind};
use rustc_middle::ty::{TraitRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor};
use rustc_middle::{bug, span_bug};
use rustc_session::lint;
use rustc_span::hygiene::Transparency;
use rustc_span::symbol::{kw, sym, Ident};
use rustc_span::Span;
use tracing::debug;
use std::fmt;
use std::marker::PhantomData;
use std::ops::ControlFlow;
use errors::{
FieldIsPrivate, FieldIsPrivateLabel, FromPrivateDependencyInPublicInterface, InPublicInterface,
ItemIsPrivate, PrivateInterfacesOrBoundsLint, ReportEffectiveVisibility, UnnameableTypesLint,
UnnamedItemIsPrivate,
};
rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
////////////////////////////////////////////////////////////////////////////////
/// Generic infrastructure used to implement specific visitors below.
////////////////////////////////////////////////////////////////////////////////
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))
}
}
/// Implemented to visit all `DefId`s in a type.
/// Visiting `DefId`s is useful because visibilities and reachabilities are attached to them.
/// The idea is to visit "all components of a type", as documented in
/// <https://github.com/rust-lang/rfcs/blob/master/text/2145-type-privacy.md#how-to-determine-visibility-of-a-type>.
/// The default type visitor (`TypeVisitor`) does most of the job, but it has some shortcomings.
/// First, it doesn't have overridable `fn visit_trait_ref`, so we have to catch trait `DefId`s
/// manually. Second, it doesn't visit some type components like signatures of fn types, or traits
/// in `impl Trait`, see individual comments in `DefIdVisitorSkeleton::visit_ty`.
pub trait DefIdVisitor<'tcx> {
type Result: VisitorResult = ();
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)
-> Self::Result;
/// Not overridden, but used to actually visit types and traits.
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>>) -> Self::Result {
ty_fragment.visit_with(&mut self.skeleton())
}
fn visit_trait(&mut self, trait_ref: TraitRef<'tcx>) -> Self::Result {
self.skeleton().visit_trait(trait_ref)
}
fn visit_predicates(&mut self, predicates: ty::GenericPredicates<'tcx>) -> Self::Result {
self.skeleton().visit_clauses(predicates.predicates)
}
fn visit_clauses(&mut self, clauses: &[(ty::Clause<'tcx>, Span)]) -> Self::Result {
self.skeleton().visit_clauses(clauses)
}
}
pub 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>) -> V::Result {
let TraitRef { def_id, args, .. } = trait_ref;
try_visit!(self.def_id_visitor.visit_def_id(
def_id,
"trait",
&trait_ref.print_only_trait_path()
));
if V::SHALLOW { V::Result::output() } else { args.visit_with(self) }
}
fn visit_projection_ty(&mut self, projection: ty::AliasTy<'tcx>) -> V::Result {
let tcx = self.def_id_visitor.tcx();
let (trait_ref, assoc_args) = projection.trait_ref_and_own_args(tcx);
try_visit!(self.visit_trait(trait_ref));
if V::SHALLOW {
V::Result::output()
} else {
V::Result::from_branch(
assoc_args.iter().try_for_each(|arg| arg.visit_with(self).branch()),
)
}
}
fn visit_clause(&mut self, clause: ty::Clause<'tcx>) -> V::Result {
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 }) => {
try_visit!(term.visit_with(self));
self.visit_projection_ty(projection_ty)
}
ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty, _region)) => ty.visit_with(self),
ty::ClauseKind::RegionOutlives(..) => V::Result::output(),
ty::ClauseKind::ConstArgHasType(ct, ty) => {
try_visit!(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)]) -> V::Result {
for &(clause, _) in clauses {
try_visit!(self.visit_clause(clause));
}
V::Result::output()
}
}
impl<'tcx, V> TypeVisitor<TyCtxt<'tcx>> for DefIdVisitorSkeleton<'_, 'tcx, V>
where
V: DefIdVisitor<'tcx> + ?Sized,
{
type Result = V::Result;
fn visit_predicate(&mut self, p: ty::Predicate<'tcx>) -> Self::Result {
self.visit_clause(p.as_clause().unwrap())
}
fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
let tcx = self.def_id_visitor.tcx();
// GenericArgs are not visited here because they are visited below
// in `super_visit_with`.
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::CoroutineClosure(def_id, ..)
| ty::Coroutine(def_id, ..) => {
try_visit!(self.def_id_visitor.visit_def_id(def_id, "type", &ty));
if V::SHALLOW {
return V::Result::output();
}
// Default type visitor doesn't visit signatures of fn types.
// Something like `fn() -> Priv {my_func}` is considered a private type even if
// `my_func` is public, so we need to visit signatures.
if let ty::FnDef(..) = ty.kind() {
// FIXME: this should probably use `args` from `FnDef`
try_visit!(tcx.fn_sig(def_id).instantiate_identity().visit_with(self));
}
// Inherent static methods don't have self type in args.
// Something like `fn() {my_method}` type of the method
// `impl Pub<Priv> { pub fn my_method() {} }` is considered a private type,
// so we need to visit the self type additionally.
if let Some(assoc_item) = tcx.opt_associated_item(def_id) {
if let Some(impl_def_id) = assoc_item.impl_container(tcx) {
try_visit!(
tcx.type_of(impl_def_id).instantiate_identity().visit_with(self)
);
}
}
}
ty::Alias(kind @ (ty::Inherent | ty::Weak | ty::Projection), data) => {
if V::SKIP_ASSOC_TYS {
// Visitors searching for minimal visibility/reachability want to
// conservatively approximate associated types like `Type::Alias`
// as visible/reachable even if `Type` is private.
// Ideally, associated types should be instantiated in the same way as
// free type aliases, but this isn't done yet.
return V::Result::output();
}
try_visit!(self.def_id_visitor.visit_def_id(
data.def_id,
match kind {
ty::Inherent | ty::Projection => "associated type",
ty::Weak => "type alias",
ty::Opaque => unreachable!(),
},
&LazyDefPathStr { def_id: data.def_id, tcx },
));
// This will also visit args if necessary, so we don't need to recurse.
return if V::SHALLOW {
V::Result::output()
} else if kind == ty::Projection {
self.visit_projection_ty(data)
} else {
V::Result::from_branch(
data.args.iter().try_for_each(|arg| arg.visit_with(self).branch()),
)
};
}
ty::Dynamic(predicates, ..) => {
// All traits in the list are considered the "primary" part of the type
// and are visited by shallow visitors.
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;
try_visit!(self.def_id_visitor.visit_def_id(def_id, "trait", &trait_ref));
}
}
ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) => {
// Skip repeated `Opaque`s to avoid infinite recursion.
if self.visited_opaque_tys.insert(def_id) {
// The intent is to treat `impl Trait1 + Trait2` identically to
// `dyn Trait1 + Trait2`. Therefore we ignore def-id of the opaque type itself
// (it either has no visibility, or its visibility is insignificant, like
// visibilities of type aliases) and recurse into bounds instead to go
// through the trait list (default type visitor doesn't visit those traits).
// All traits in the list are considered the "primary" part of the type
// and are visited by shallow visitors.
try_visit!(self.visit_clauses(tcx.explicit_item_bounds(def_id).skip_binder()));
}
}
// These types don't have their own def-ids (but may have subcomponents
// with def-ids that should be visited recursively).
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::Pat(..)
| ty::FnPtr(..)
| ty::Param(..)
| ty::Bound(..)
| ty::Error(_)
| ty::CoroutineWitness(..) => {}
ty::Placeholder(..) | ty::Infer(..) => {
bug!("unexpected type: {:?}", ty)
}
}
if V::SHALLOW { V::Result::output() } else { ty.super_visit_with(self) }
}
fn visit_const(&mut self, c: Const<'tcx>) -> Self::Result {
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 }
}
////////////////////////////////////////////////////////////////////////////////
/// Visitor used to determine impl visibility and reachability.
////////////////////////////////////////////////////////////////////////////////
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) {
if let Some(def_id) = def_id.as_local() {
self.min = VL::new_min(self, def_id);
}
}
}
trait VisibilityLike: Sized {
const MAX: Self;
fn new_min<const SHALLOW: bool>(
find: &FindMin<'_, '_, Self, SHALLOW>,
def_id: LocalDefId,
) -> Self;
// Returns an over-approximation (`SKIP_ASSOC_TYS` = true) of visibility due to
// associated types for which we can't determine visibility precisely.
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)
}
}
////////////////////////////////////////////////////////////////////////////////
/// The embargo visitor, used to determine the exports of the AST.
////////////////////////////////////////////////////////////////////////////////
struct EmbargoVisitor<'tcx> {
tcx: TyCtxt<'tcx>,
/// Effective visibilities for reachable nodes.
effective_visibilities: EffectiveVisibilities,
/// A set of pairs corresponding to modules, where the first module is
/// reachable via a macro that's defined in the second module. This cannot
/// be represented as reachable because it can't handle the following case:
///
/// pub mod n { // Should be `Public`
/// pub(crate) mod p { // Should *not* be accessible
/// pub fn f() -> i32 { 12 } // Must be `Reachable`
/// }
/// }
/// pub macro m() {
/// n::p::f()
/// }
macro_reachable: FxHashSet<(LocalModDefId, LocalModDefId)>,
/// Preliminary pass for marking all underlying types of `impl Trait`s as reachable.
impl_trait_pass: bool,
/// Has something changed in the level map?
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()
}
// Updates node effective visibility.
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,
) {
// FIXME(typed_def_id): Make `Visibility::Restricted` use a `LocalModDefId` by default.
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,
}
}
// We have to make sure that the items that macros might reference
// are reachable, since they might be exported transitively.
fn update_reachability_from_macro(
&mut self,
local_def_id: LocalDefId,
md: &MacroDef,
macro_ev: EffectiveVisibility,
) {
// Non-opaque macros cannot make other items more accessible than they already are.
let hir_id = self.tcx.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.def_kind(macro_module_def_id) != DefKind::Mod {
// The macro's parent doesn't correspond to a `mod`, return early (#63164, #65252).
return;
}
// FIXME(typed_def_id): Introduce checked constructors that check def_kind.
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;
}
// Since we are starting from an externally visible module,
// all the parents in the loop below are also guaranteed to be modules.
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));
}
}
/// Updates the item as being reachable through a macro defined in the given
/// module. Returns `true` if the level has changed.
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)) {
for child in self.tcx.module_children_local(module_def_id.to_local_def_id()) {
if let Res::Def(def_kind, def_id) = child.res
&& let Some(def_id) = def_id.as_local()
&& child.vis.is_accessible_from(defining_mod, self.tcx)
{
let vis = self.tcx.local_visibility(def_id);
self.update_macro_reachable_def(def_id, def_kind, vis, defining_mod, macro_ev);
}
}
true
} else {
false
}
}
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 {
// No type privacy, so can be directly marked as reachable.
DefKind::Const | DefKind::Static { .. } | DefKind::TraitAlias | DefKind::TyAlias => {
if vis.is_accessible_from(module, self.tcx) {
self.update(def_id, macro_ev, Level::Reachable);
}
}
// Hygiene isn't really implemented for `macro_rules!` macros at the
// moment. Accordingly, marking them as reachable is unwise. `macro` macros
// have normal hygiene, so we can treat them like other items without type
// privacy and mark them 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);
}
}
}
// We can't use a module name as the final segment of a path, except
// in use statements. Since re-export checking doesn't consider
// hygiene these don't need to be marked reachable. The contents of
// the module, however may be 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 => {
// While structs and unions have type privacy, their fields do not.
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);
}
}
// These have type privacy, so are not reachable unless they're
// public, or are not namespaced at all.
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 => (),
}
}
}
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(opaque) = item.kind
&& !opaque.in_trait
{
// FIXME: This is some serious pessimization intended to workaround deficiencies
// in the reachability pass (`middle/reachable.rs`). Types are marked as link-time
// reachable if they are returned via `impl Trait`, even from private functions.
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;
}
// Update levels of nested things and mark all items
// in interfaces of reachable items as reachable.
let item_ev = self.get(item.owner_id.def_id);
match item.kind {
// The interface is empty, and no nested items.
hir::ItemKind::Use(..)
| hir::ItemKind::ExternCrate(..)
| hir::ItemKind::GlobalAsm(..) => {}
// The interface is empty, and all nested items are processed by `visit_item`.
hir::ItemKind::Mod(..) | hir::ItemKind::OpaqueTy(..) => {}
hir::ItemKind::Macro(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()
{
// No type to visit.
} 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(impl_) => {
// Type inference is very smart sometimes. It can make an impl reachable even some
// components of its type or trait are unreachable. E.g. methods of
// `impl ReachableTrait<UnreachableTy> for ReachableTy<UnreachableTy> { ... }`
// can be usable from other crates (#57264). So we skip args when calculating
// reachability and consider an impl reachable if its "shallow" type and trait are
// reachable.
//
// The assumption we make here is that type-inference won't let you use an impl
// without knowing both "shallow" version of its self type and "shallow" version of
// its trait if it exists (which require reaching the `DefId`s in them).
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();
}
// Corner case: if the variant is reachable, but its
// enum is not, make the enum reachable as well.
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).own_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) {
if let Some(def_id) = def_id.as_local() {
// All effective visibilities except `reachable_through_impl_trait` are limited to
// nominal visibility. If any type or trait is leaked farther than that, it will
// produce type privacy errors on any use, so we don't consider it leaked.
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);
}
}
}
////////////////////////////////////////////////////////////////////////////////
/// Visitor, used for EffectiveVisibilities table checking
////////////////////////////////////////////////////////////////////////////////
pub struct TestReachabilityVisitor<'tcx, 'a> {
tcx: TyCtxt<'tcx>,
effective_visibilities: &'a EffectiveVisibilities,
}
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 = effective_vis.at_level(level).to_string(def_id, 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.dcx().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);
}
}
//////////////////////////////////////////////////////////////////////////////////////
/// Name privacy visitor, checks privacy and reports violations.
/// Most of name privacy checks are performed during the main resolution phase,
/// or later in type checking when field accesses and associated items are resolved.
/// This pass performs remaining checks for fields in struct expressions and patterns.
//////////////////////////////////////////////////////////////////////////////////////
struct NamePrivacyVisitor<'tcx> {
tcx: TyCtxt<'tcx>,
maybe_typeck_results: Option<&'tcx ty::TypeckResults<'tcx>>,
}
impl<'tcx> NamePrivacyVisitor<'tcx> {
/// Gets the type-checking results for the current body.
/// As this will ICE if called outside bodies, only call when working with
/// `Expr` or `Pat` nodes (they are guaranteed to be found only in bodies).
#[track_caller]
fn typeck_results(&self) -> &'tcx ty::TypeckResults<'tcx> {
self.maybe_typeck_results
.expect("`NamePrivacyVisitor::typeck_results` called outside of body")
}
// Checks that a field in a struct constructor (expression or pattern) is accessible.
fn check_field(
&mut self,
hir_id: hir::HirId, // ID of the field use
use_ctxt: Span, // syntax context of the field name at the use site
span: Span, // span of the field pattern, e.g., `x: 0`
def: ty::AdtDef<'tcx>, // definition of the struct or enum
field: &'tcx ty::FieldDef,
in_update_syntax: bool,
) {
if def.is_enum() {
return;
}
// definition of the field
let ident = Ident::new(kw::Empty, use_ctxt);
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.dcx().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> {
fn visit_nested_body(&mut self, body_id: hir::BodyId) {
let old_maybe_typeck_results =
self.maybe_typeck_results.replace(self.tcx.typeck_body(body_id));
self.visit_body(self.tcx.hir().body(body_id));
self.maybe_typeck_results = old_maybe_typeck_results;
}
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 {
// If the expression uses FRU we need to make sure all the unmentioned fields
// are checked for privacy (RFC 736). Rather than computing the set of
// unmentioned fields, just check them all.
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 (hir_id, use_ctxt, span) = match field {
Some(field) => (field.hir_id, field.ident.span, field.span),
None => (base.hir_id, base.span, base.span),
};
self.check_field(hir_id, use_ctxt, span, adt, variant_field, true);
}
} else {
for field in fields {
let (hir_id, use_ctxt, span) = (field.hir_id, field.ident.span, field.span);
let index = self.typeck_results().field_index(field.hir_id);
self.check_field(hir_id, use_ctxt, 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 (hir_id, use_ctxt, span) = (field.hir_id, field.ident.span, field.span);
let index = self.typeck_results().field_index(field.hir_id);
self.check_field(hir_id, use_ctxt, span, adt, &variant.fields[index], false);
}
}
intravisit::walk_pat(self, pat);
}
}
////////////////////////////////////////////////////////////////////////////////////////////
/// Type privacy visitor, checks types for privacy and reports violations.
/// Both explicitly written types and inferred types of expressions and patterns are checked.
/// Checks are performed on "semantic" types regardless of names and their hygiene.
////////////////////////////////////////////////////////////////////////////////////////////
struct TypePrivacyVisitor<'tcx> {
tcx: TyCtxt<'tcx>,
module_def_id: LocalModDefId,
maybe_typeck_results: Option<&'tcx ty::TypeckResults<'tcx>>,
span: Span,
}
impl<'tcx> TypePrivacyVisitor<'tcx> {
fn item_is_accessible(&self, did: DefId) -> bool {
self.tcx.visibility(did).is_accessible_from(self.module_def_id, self.tcx)
}
// Take node-id of an expression or pattern and check its type for privacy.
fn check_expr_pat_type(&mut self, id: hir::HirId, span: Span) -> bool {
self.span = span;
let typeck_results = self
.maybe_typeck_results
.unwrap_or_else(|| span_bug!(span, "`hir::Expr` or `hir::Pat` outside of a body"));
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.dcx().emit_err(ItemIsPrivate { span: self.span, kind, descr: descr.into() });
}
is_error
}
}
impl<'tcx> rustc_ty_utils::sig_types::SpannedTypeVisitor<'tcx> for TypePrivacyVisitor<'tcx> {
type Result = ControlFlow<()>;
fn visit(&mut self, span: Span, value: impl TypeVisitable<TyCtxt<'tcx>>) -> Self::Result {
self.span = span;
value.visit_with(&mut self.skeleton())
}
}
impl<'tcx> Visitor<'tcx> for TypePrivacyVisitor<'tcx> {
fn visit_nested_body(&mut self, body_id: hir::BodyId) {
let old_maybe_typeck_results =
self.maybe_typeck_results.replace(self.tcx.typeck_body(body_id));
self.visit_body(self.tcx.hir().body(body_id));
self.maybe_typeck_results = old_maybe_typeck_results;
}
fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'tcx>) {
self.span = hir_ty.span;
if self
.visit(
self.maybe_typeck_results
.unwrap_or_else(|| span_bug!(hir_ty.span, "`hir::Ty` outside of a body"))
.node_type(hir_ty.hir_id),
)
.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(ty) = self
.maybe_typeck_results
.unwrap_or_else(|| span_bug!(inf.span, "`hir::InferArg` outside of a body"))
.node_type_opt(inf.hir_id)
{
if self.visit(ty).is_break() {
return;
}
} else {
// FIXME: check types of const infers here.
}
intravisit::walk_inf(self, inf);
}
// Check types of expressions
fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
if self.check_expr_pat_type(expr.hir_id, expr.span) {
// Do not check nested expressions if the error already happened.
return;
}
match expr.kind {
hir::ExprKind::Assign(_, rhs, _) | hir::ExprKind::Match(rhs, ..) => {
// Do not report duplicate errors for `x = y` and `match x { ... }`.
if self.check_expr_pat_type(rhs.hir_id, rhs.span) {
return;
}
}
hir::ExprKind::MethodCall(segment, ..) => {
// Method calls have to be checked specially.
self.span = segment.ident.span;
let typeck_results = self
.maybe_typeck_results
.unwrap_or_else(|| span_bug!(self.span, "`hir::Expr` outside of a body"));
if let Some(def_id) = 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
.dcx()
.span_delayed_bug(expr.span, "no type-dependent def for method call");
}
}
_ => {}
}
intravisit::walk_expr(self, expr);
}
// Prohibit access to associated items with insufficient nominal visibility.
//
// Additionally, until better reachability analysis for macros 2.0 is available,
// we prohibit access to private statics from other crates, this allows to give
// more code internal visibility at link time. (Access to private functions
// is already prohibited by type privacy for function types.)
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(..) => {
match self.maybe_typeck_results {
Some(typeck_results) => typeck_results.type_dependent_def(id),
// FIXME: Check type-relative associated types in signatures.
None => None,
}
}
};
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.dcx().emit_err(ItemIsPrivate { span, kind, descr: (&name).into() })
}
None => sess.dcx().emit_err(UnnamedItemIsPrivate { span, kind }),
};
return;
}
}
intravisit::walk_qpath(self, qpath, id);
}
// Check types of patterns.
fn visit_pat(&mut self, pattern: &'tcx hir::Pat<'tcx>) {
if self.check_expr_pat_type(pattern.hir_id, pattern.span) {
// Do not check nested patterns if the error already happened.
return;
}
intravisit::walk_pat(self, pattern);
}
fn visit_local(&mut self, local: &'tcx hir::LetStmt<'tcx>) {
if let Some(init) = local.init {
if self.check_expr_pat_type(init.hir_id, init.span) {
// Do not report duplicate errors for `let x = y`.
return;
}
}
intravisit::walk_local(self, local);
}
}
impl<'tcx> DefIdVisitor<'tcx> for TypePrivacyVisitor<'tcx> {
type Result = ControlFlow<()>;
fn tcx(&self) -> TyCtxt<'tcx> {
self.tcx
}
fn visit_def_id(
&mut self,
def_id: DefId,
kind: &str,
descr: &dyn fmt::Display,
) -> Self::Result {
if self.check_def_id(def_id, kind, descr) {
ControlFlow::Break(())
} else {
ControlFlow::Continue(())
}
}
}
///////////////////////////////////////////////////////////////////////////////
/// SearchInterfaceForPrivateItemsVisitor traverses an item's interface and
/// finds any private components in it.
/// PrivateItemsInPublicInterfacesVisitor ensures there are no private types
/// and traits in public interfaces.
///////////////////////////////////////////////////////////////////////////////
struct SearchInterfaceForPrivateItemsVisitor<'tcx> {
tcx: TyCtxt<'tcx>,
item_def_id: LocalDefId,
/// The visitor checks that each component type is at least this visible.
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).own_params {
match param.kind {
GenericParamDefKind::Lifetime => {}
GenericParamDefKind::Type { has_default, .. } => {
if has_default {
self.visit(self.tcx.type_of(param.def_id).instantiate_identity());
}
}
// FIXME(generic_const_exprs): May want to look inside const here
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;
// N.B., we use `explicit_predicates_of` and not `predicates_of`
// because we don't want to report privacy errors due to where
// clauses that the compiler inferred. We only want to
// consider the ones that the user wrote. This is important
// for the inferred outlives rules; see
// `tests/ui/rfc-2093-infer-outlives/privacy.rs`.
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_node_span_lint(
lint::builtin::EXPORTED_PRIVATE_DEPENDENCIES,
self.tcx.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.dcx().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_node_span_lint(
lint,
self.tcx.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: &reachable_at_vis.to_string(self.item_def_id, self.tcx),
ty_span: vis_span,
ty_kind: kind,
ty_descr: descr.into(),
ty_vis_descr: &vis.to_string(local_def_id, self.tcx),
},
);
}
false
}
/// An item is 'leaked' from a private dependency if all
/// of the following are true:
/// 1. It's contained within a public type
/// 2. It comes from a private crate
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> {
type Result = ControlFlow<()>;
fn tcx(&self) -> TyCtxt<'tcx> {
self.tcx
}
fn visit_def_id(
&mut self,
def_id: DefId,
kind: &str,
descr: &dyn fmt::Display,
) -> Self::Result {
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.local_def_id_to_hir_id(def_id);
let span = self.tcx.def_span(def_id.to_def_id());
self.tcx.emit_node_span_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: &reachable_at_vis.to_string(def_id, self.tcx),
reexported_vis: &reexported_at_vis.to_string(def_id, 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 => {
// `ty()` for opaque types is the underlying type,
// it's not a part of interface, so we skip it.
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();
}
}
}
}
// Subitems of foreign modules have their own publicity.
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();
}
}
}
// Subitems of structs and unions have their own publicity.
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();
}
}
}
// An inherent impl is public when its type is public
// Subitems of inherent impls have their own publicity.
// A trait impl is public when both its type and its trait are public
// Subitems of trait impls have inherited publicity.
DefKind::Impl { .. } => {
let item = tcx.hir().item(id);
if let hir::ItemKind::Impl(impl_) = item.kind {
let impl_vis = ty::Visibility::of_impl::<false>(
item.owner_id.def_id,
tcx,
&Default::default(),
);
// We are using the non-shallow version here, unlike when building the
// effective visisibilities table to avoid large number of false positives.
// For example in
//
// impl From<Priv> for Pub {
// fn from(_: Priv) -> Pub {...}
// }
//
// lints shouldn't be emitted even if `from` effective visibility
// is larger than `Priv` nominal visibility and if `Priv` can leak
// in some scenarios due to type inference.
let impl_ev = EffectiveVisibility::of_impl::<false>(
item.owner_id.def_id,
tcx,
self.effective_visibilities,
);
// check that private components do not appear in the generics or predicates of inherent impls
// this check is intentionally NOT performed for impls of traits, per #90586
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 {
effective_visibilities,
check_private_in_public,
check_mod_privacy,
..*providers
};
}
fn check_mod_privacy(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) {
// Check privacy of names not checked in previous compilation stages.
let mut visitor = NamePrivacyVisitor { tcx, maybe_typeck_results: None };
tcx.hir().visit_item_likes_in_module(module_def_id, &mut visitor);
// Check privacy of explicitly written types and traits as well as
// inferred types of expressions and patterns.
let span = tcx.def_span(module_def_id);
let mut visitor = TypePrivacyVisitor { tcx, module_def_id, maybe_typeck_results: None, span };
let module = tcx.hir_module_items(module_def_id);
for def_id in module.definitions() {
rustc_ty_utils::sig_types::walk_types(tcx, def_id, &mut visitor);
if let Some(body_id) = tcx.hir().maybe_body_owned_by(def_id) {
visitor.visit_nested_body(body_id);
}
}
for id in module.free_items() {
if let ItemKind::Impl(i) = tcx.hir().item(id).kind {
if let Some(item) = i.of_trait {
let trait_ref = tcx.impl_trait_ref(id.owner_id.def_id).unwrap();
let trait_ref = trait_ref.instantiate_identity();
visitor.span = item.path.span;
visitor.visit_def_id(trait_ref.def_id, "trait", &trait_ref.print_only_trait_path());
}
}
}
}
fn effective_visibilities(tcx: TyCtxt<'_>, (): ()) -> &EffectiveVisibilities {
// Build up a set of all exported items in the AST. This is a set of all
// items which are reachable from external crates based on visibility.
let mut visitor = EmbargoVisitor {
tcx,
effective_visibilities: tcx.resolutions(()).effective_visibilities.clone(),
macro_reachable: Default::default(),
// HACK(jynelson): trying to infer the type of `impl Trait` breaks `async-std` (and
// `pub async fn` in general). Since rustdoc never needs to do codegen and doesn't
// care about link-time reachability, keep them unreachable (issue #75100).
impl_trait_pass: !tcx.sess.opts.actually_rustdoc,
changed: false,
};
visitor.effective_visibilities.check_invariants(tcx);
if visitor.impl_trait_pass {
// Underlying types of `impl Trait`s are marked as reachable unconditionally,
// so this pass doesn't need to be a part of the fixed point iteration below.
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(());
// Check for private types in public interfaces.
let mut checker = PrivateItemsInPublicInterfacesChecker { tcx, effective_visibilities };
for id in tcx.hir().items() {
checker.check_item(id);
}
}