Skip to content

Commit b80b659

Browse files
committed
Auto merge of #42125 - petrochenkov:privty, r=nikomatsakis
Check types for privacy This PR implements late post factum checking of type privacy, as opposed to early preventive "private-in-public" checking. This will allow to turn private-in-public checks into a lint and make them more heuristic-based, and more aligned with what people may expect (e.g. reachability-based behavior). Types are privacy-checked if they are written explicitly, and also if they are inferred as expression or pattern types. This PR checks "semantic" types and does it unhygienically, this significantly restricts what macros 2.0 (as implemented in #40847) can do (sorry @jseyfried) - they still can use private *names*, but can't use private *types*. This is the most conservative solution, but hopefully it's temporary and can be relaxed in the future, probably using macro contexts of expression/pattern spans. Traits are also checked in preparation for [trait aliases](#41517), which will be able to leak private traits, and macros 2.0 which will be able to leak pretty much anything. This is a [breaking-change], but the code that is not contrived and can be broken by this patch should be guarded by `private_in_public` lint. [Previous crater run](#34537 (comment)) discovered a few abandoned crates that weren't updated since `private_in_public` has been introduced in 2015. cc #34537 https://internals.rust-lang.org/t/lang-team-minutes-private-in-public-rules/4504 Fixes #30476 Fixes #33479 cc @nikomatsakis r? @eddyb
2 parents d2ebb12 + a27f8cf commit b80b659

12 files changed

+665
-8
lines changed

src/libproc_macro/quote.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ impl ProcMacro for Quoter {
8787
let mut info = cx.current_expansion.mark.expn_info().unwrap();
8888
info.callee.allow_internal_unstable = true;
8989
cx.current_expansion.mark.set_expn_info(info);
90-
::__internal::set_sess(cx, || quote!(::TokenStream((quote stream))))
90+
::__internal::set_sess(cx, || quote!(::TokenStream { 0: (quote stream) }))
9191
}
9292
}
9393

src/librustc_privacy/lib.rs

+350-2
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@
1818

1919
#![feature(rustc_diagnostic_macros)]
2020

21-
extern crate rustc;
21+
#[macro_use] extern crate rustc;
2222
#[macro_use] extern crate syntax;
2323
extern crate syntax_pos;
2424

2525
use rustc::hir::{self, PatKind};
2626
use rustc::hir::def::Def;
27-
use rustc::hir::def_id::{LOCAL_CRATE, CrateNum, DefId};
27+
use rustc::hir::def_id::{CRATE_DEF_INDEX, LOCAL_CRATE, CrateNum, DefId};
2828
use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap};
2929
use rustc::hir::itemlikevisit::DeepVisitor;
3030
use rustc::lint;
@@ -537,6 +537,344 @@ impl<'a, 'tcx> Visitor<'tcx> for NamePrivacyVisitor<'a, 'tcx> {
537537
}
538538
}
539539

540+
////////////////////////////////////////////////////////////////////////////////////////////
541+
/// Type privacy visitor, checks types for privacy and reports violations.
542+
/// Both explicitly written types and inferred types of expressions and patters are checked.
543+
/// Checks are performed on "semantic" types regardless of names and their hygiene.
544+
////////////////////////////////////////////////////////////////////////////////////////////
545+
546+
struct TypePrivacyVisitor<'a, 'tcx: 'a> {
547+
tcx: TyCtxt<'a, 'tcx, 'tcx>,
548+
tables: &'a ty::TypeckTables<'tcx>,
549+
current_item: DefId,
550+
span: Span,
551+
}
552+
553+
impl<'a, 'tcx> TypePrivacyVisitor<'a, 'tcx> {
554+
fn def_id_visibility(&self, did: DefId) -> ty::Visibility {
555+
match self.tcx.hir.as_local_node_id(did) {
556+
Some(node_id) => {
557+
let vis = match self.tcx.hir.get(node_id) {
558+
hir::map::NodeItem(item) => &item.vis,
559+
hir::map::NodeForeignItem(foreign_item) => &foreign_item.vis,
560+
hir::map::NodeImplItem(impl_item) => &impl_item.vis,
561+
hir::map::NodeTraitItem(..) |
562+
hir::map::NodeVariant(..) => {
563+
return self.def_id_visibility(self.tcx.hir.get_parent_did(node_id));
564+
}
565+
hir::map::NodeStructCtor(vdata) => {
566+
let struct_node_id = self.tcx.hir.get_parent(node_id);
567+
let struct_vis = match self.tcx.hir.get(struct_node_id) {
568+
hir::map::NodeItem(item) => &item.vis,
569+
node => bug!("unexpected node kind: {:?}", node),
570+
};
571+
let mut ctor_vis
572+
= ty::Visibility::from_hir(struct_vis, struct_node_id, self.tcx);
573+
for field in vdata.fields() {
574+
let field_vis = ty::Visibility::from_hir(&field.vis, node_id, self.tcx);
575+
if ctor_vis.is_at_least(field_vis, self.tcx) {
576+
ctor_vis = field_vis;
577+
}
578+
}
579+
return ctor_vis;
580+
}
581+
node => bug!("unexpected node kind: {:?}", node)
582+
};
583+
ty::Visibility::from_hir(vis, node_id, self.tcx)
584+
}
585+
None => self.tcx.sess.cstore.visibility(did),
586+
}
587+
}
588+
589+
fn item_is_accessible(&self, did: DefId) -> bool {
590+
self.def_id_visibility(did).is_accessible_from(self.current_item, self.tcx)
591+
}
592+
593+
// Take node ID of an expression or pattern and check its type for privacy.
594+
fn check_expr_pat_type(&mut self, id: ast::NodeId, span: Span) -> bool {
595+
self.span = span;
596+
if let Some(ty) = self.tables.node_id_to_type_opt(id) {
597+
if ty.visit_with(self) {
598+
return true;
599+
}
600+
}
601+
if self.tables.node_substs(id).visit_with(self) {
602+
return true;
603+
}
604+
if let Some(adjustments) = self.tables.adjustments.get(&id) {
605+
for adjustment in adjustments {
606+
if adjustment.target.visit_with(self) {
607+
return true;
608+
}
609+
}
610+
}
611+
false
612+
}
613+
614+
fn check_item(&mut self, item_id: ast::NodeId) -> &mut Self {
615+
self.current_item = self.tcx.hir.local_def_id(item_id);
616+
self.span = self.tcx.hir.span(item_id);
617+
self
618+
}
619+
620+
// Convenience methods for checking item interfaces
621+
fn ty(&mut self) -> &mut Self {
622+
self.tcx.type_of(self.current_item).visit_with(self);
623+
self
624+
}
625+
626+
fn generics(&mut self) -> &mut Self {
627+
for def in &self.tcx.generics_of(self.current_item).types {
628+
if def.has_default {
629+
self.tcx.type_of(def.def_id).visit_with(self);
630+
}
631+
}
632+
self
633+
}
634+
635+
fn predicates(&mut self) -> &mut Self {
636+
self.tcx.predicates_of(self.current_item).visit_with(self);
637+
self
638+
}
639+
640+
fn impl_trait_ref(&mut self) -> &mut Self {
641+
self.tcx.impl_trait_ref(self.current_item).visit_with(self);
642+
self
643+
}
644+
}
645+
646+
impl<'a, 'tcx> Visitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> {
647+
/// We want to visit items in the context of their containing
648+
/// module and so forth, so supply a crate for doing a deep walk.
649+
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
650+
NestedVisitorMap::All(&self.tcx.hir)
651+
}
652+
653+
fn visit_nested_body(&mut self, body: hir::BodyId) {
654+
let orig_tables = replace(&mut self.tables, self.tcx.body_tables(body));
655+
let body = self.tcx.hir.body(body);
656+
self.visit_body(body);
657+
self.tables = orig_tables;
658+
}
659+
660+
// Check types of expressions
661+
fn visit_expr(&mut self, expr: &'tcx hir::Expr) {
662+
if self.check_expr_pat_type(expr.id, expr.span) {
663+
// Do not check nested expressions if the error already happened.
664+
return;
665+
}
666+
match expr.node {
667+
hir::ExprAssign(.., ref rhs) | hir::ExprMatch(ref rhs, ..) => {
668+
// Do not report duplicate errors for `x = y` and `match x { ... }`.
669+
if self.check_expr_pat_type(rhs.id, rhs.span) {
670+
return;
671+
}
672+
}
673+
hir::ExprMethodCall(name, ..) => {
674+
// Method calls have to be checked specially.
675+
let def_id = self.tables.type_dependent_defs[&expr.id].def_id();
676+
self.span = name.span;
677+
if self.tcx.type_of(def_id).visit_with(self) {
678+
return;
679+
}
680+
}
681+
_ => {}
682+
}
683+
684+
intravisit::walk_expr(self, expr);
685+
}
686+
687+
fn visit_qpath(&mut self, qpath: &'tcx hir::QPath, id: ast::NodeId, span: Span) {
688+
// Inherent associated constants don't have self type in substs,
689+
// we have to check it additionally.
690+
if let hir::QPath::TypeRelative(..) = *qpath {
691+
if let Some(def) = self.tables.type_dependent_defs.get(&id).cloned() {
692+
if let Some(assoc_item) = self.tcx.opt_associated_item(def.def_id()) {
693+
if let ty::ImplContainer(impl_def_id) = assoc_item.container {
694+
if self.tcx.type_of(impl_def_id).visit_with(self) {
695+
return;
696+
}
697+
}
698+
}
699+
}
700+
}
701+
702+
intravisit::walk_qpath(self, qpath, id, span);
703+
}
704+
705+
// Check types of patterns
706+
fn visit_pat(&mut self, pattern: &'tcx hir::Pat) {
707+
if self.check_expr_pat_type(pattern.id, pattern.span) {
708+
// Do not check nested patterns if the error already happened.
709+
return;
710+
}
711+
712+
intravisit::walk_pat(self, pattern);
713+
}
714+
715+
fn visit_local(&mut self, local: &'tcx hir::Local) {
716+
if let Some(ref init) = local.init {
717+
if self.check_expr_pat_type(init.id, init.span) {
718+
// Do not report duplicate errors for `let x = y`.
719+
return;
720+
}
721+
}
722+
723+
intravisit::walk_local(self, local);
724+
}
725+
726+
// Check types in item interfaces
727+
fn visit_item(&mut self, item: &'tcx hir::Item) {
728+
let orig_current_item = self.current_item;
729+
730+
match item.node {
731+
hir::ItemExternCrate(..) | hir::ItemMod(..) |
732+
hir::ItemUse(..) | hir::ItemGlobalAsm(..) => {}
733+
hir::ItemConst(..) | hir::ItemStatic(..) |
734+
hir::ItemTy(..) | hir::ItemFn(..) => {
735+
self.check_item(item.id).generics().predicates().ty();
736+
}
737+
hir::ItemTrait(.., ref trait_item_refs) => {
738+
self.check_item(item.id).generics().predicates();
739+
for trait_item_ref in trait_item_refs {
740+
let mut check = self.check_item(trait_item_ref.id.node_id);
741+
check.generics().predicates();
742+
if trait_item_ref.kind != hir::AssociatedItemKind::Type ||
743+
trait_item_ref.defaultness.has_value() {
744+
check.ty();
745+
}
746+
}
747+
}
748+
hir::ItemEnum(ref def, _) => {
749+
self.check_item(item.id).generics().predicates();
750+
for variant in &def.variants {
751+
for field in variant.node.data.fields() {
752+
self.check_item(field.id).ty();
753+
}
754+
}
755+
}
756+
hir::ItemForeignMod(ref foreign_mod) => {
757+
for foreign_item in &foreign_mod.items {
758+
self.check_item(foreign_item.id).generics().predicates().ty();
759+
}
760+
}
761+
hir::ItemStruct(ref struct_def, _) |
762+
hir::ItemUnion(ref struct_def, _) => {
763+
self.check_item(item.id).generics().predicates();
764+
for field in struct_def.fields() {
765+
self.check_item(field.id).ty();
766+
}
767+
}
768+
hir::ItemDefaultImpl(..) => {
769+
self.check_item(item.id).impl_trait_ref();
770+
}
771+
hir::ItemImpl(.., ref trait_ref, _, ref impl_item_refs) => {
772+
{
773+
let mut check = self.check_item(item.id);
774+
check.ty().generics().predicates();
775+
if trait_ref.is_some() {
776+
check.impl_trait_ref();
777+
}
778+
}
779+
for impl_item_ref in impl_item_refs {
780+
let impl_item = self.tcx.hir.impl_item(impl_item_ref.id);
781+
self.check_item(impl_item.id).generics().predicates().ty();
782+
}
783+
}
784+
}
785+
786+
self.current_item = self.tcx.hir.local_def_id(item.id);
787+
intravisit::walk_item(self, item);
788+
self.current_item = orig_current_item;
789+
}
790+
}
791+
792+
impl<'a, 'tcx> TypeVisitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> {
793+
fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool {
794+
match ty.sty {
795+
ty::TyAdt(&ty::AdtDef { did: def_id, .. }, ..) | ty::TyFnDef(def_id, ..) => {
796+
if !self.item_is_accessible(def_id) {
797+
let msg = format!("type `{}` is private", ty);
798+
self.tcx.sess.span_err(self.span, &msg);
799+
return true;
800+
}
801+
if let ty::TyFnDef(..) = ty.sty {
802+
if self.tcx.fn_sig(def_id).visit_with(self) {
803+
return true;
804+
}
805+
}
806+
// Inherent static methods don't have self type in substs,
807+
// we have to check it additionally.
808+
if let Some(assoc_item) = self.tcx.opt_associated_item(def_id) {
809+
if let ty::ImplContainer(impl_def_id) = assoc_item.container {
810+
if self.tcx.type_of(impl_def_id).visit_with(self) {
811+
return true;
812+
}
813+
}
814+
}
815+
}
816+
ty::TyDynamic(ref predicates, ..) => {
817+
let is_private = predicates.skip_binder().iter().any(|predicate| {
818+
let def_id = match *predicate {
819+
ty::ExistentialPredicate::Trait(trait_ref) => trait_ref.def_id,
820+
ty::ExistentialPredicate::Projection(proj) => proj.trait_ref.def_id,
821+
ty::ExistentialPredicate::AutoTrait(def_id) => def_id,
822+
};
823+
!self.item_is_accessible(def_id)
824+
});
825+
if is_private {
826+
let msg = format!("type `{}` is private", ty);
827+
self.tcx.sess.span_err(self.span, &msg);
828+
return true;
829+
}
830+
}
831+
ty::TyAnon(def_id, ..) => {
832+
for predicate in &self.tcx.predicates_of(def_id).predicates {
833+
let trait_ref = match *predicate {
834+
ty::Predicate::Trait(ref poly_trait_predicate) => {
835+
Some(poly_trait_predicate.skip_binder().trait_ref)
836+
}
837+
ty::Predicate::Projection(ref poly_projection_predicate) => {
838+
if poly_projection_predicate.skip_binder().ty.visit_with(self) {
839+
return true;
840+
}
841+
Some(poly_projection_predicate.skip_binder().projection_ty.trait_ref)
842+
}
843+
ty::Predicate::TypeOutlives(..) => None,
844+
_ => bug!("unexpected predicate: {:?}", predicate),
845+
};
846+
if let Some(trait_ref) = trait_ref {
847+
if !self.item_is_accessible(trait_ref.def_id) {
848+
let msg = format!("trait `{}` is private", trait_ref);
849+
self.tcx.sess.span_err(self.span, &msg);
850+
return true;
851+
}
852+
// `Self` here is the same `TyAnon`, so skip it to avoid infinite recursion
853+
for subst in trait_ref.substs.iter().skip(1) {
854+
if subst.visit_with(self) {
855+
return true;
856+
}
857+
}
858+
}
859+
}
860+
}
861+
_ => {}
862+
}
863+
864+
ty.super_visit_with(self)
865+
}
866+
867+
fn visit_trait_ref(&mut self, trait_ref: ty::TraitRef<'tcx>) -> bool {
868+
if !self.item_is_accessible(trait_ref.def_id) {
869+
let msg = format!("trait `{}` is private", trait_ref);
870+
self.tcx.sess.span_err(self.span, &msg);
871+
return true;
872+
}
873+
874+
trait_ref.super_visit_with(self)
875+
}
876+
}
877+
540878
///////////////////////////////////////////////////////////////////////////////
541879
/// Obsolete visitors for checking for private items in public interfaces.
542880
/// These visitors are supposed to be kept in frozen state and produce an
@@ -1225,6 +1563,16 @@ fn privacy_access_levels<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
12251563
};
12261564
intravisit::walk_crate(&mut visitor, krate);
12271565

1566+
// Check privacy of explicitly written types and traits as well as
1567+
// inferred types of expressions and patterns.
1568+
let mut visitor = TypePrivacyVisitor {
1569+
tcx: tcx,
1570+
tables: &ty::TypeckTables::empty(),
1571+
current_item: DefId::local(CRATE_DEF_INDEX),
1572+
span: krate.span,
1573+
};
1574+
intravisit::walk_crate(&mut visitor, krate);
1575+
12281576
// Build up a set of all exported items in the AST. This is a set of all
12291577
// items which are reachable from external crates based on visibility.
12301578
let mut visitor = EmbargoVisitor {

0 commit comments

Comments
 (0)