Skip to content

Commit 85fb9e0

Browse files
committed
Check types for privacy
1 parent cd72f2e commit 85fb9e0

11 files changed

+581
-7
lines changed

src/librustc_privacy/lib.rs

+330-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,324 @@ 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+
fn check_expr_pat_type(&mut self, id: ast::NodeId, span: Span) -> bool {
594+
if let Some(ty) = self.tables.node_id_to_type_opt(id) {
595+
self.span = span;
596+
ty.visit_with(self)
597+
} else {
598+
false
599+
}
600+
}
601+
602+
fn check_item(&mut self, item_id: ast::NodeId) -> &mut Self {
603+
self.current_item = self.tcx.hir.local_def_id(item_id);
604+
self.span = self.tcx.hir.span(item_id);
605+
self
606+
}
607+
608+
// Convenience methods for checking item interfaces
609+
fn ty(&mut self) -> &mut Self {
610+
self.tcx.type_of(self.current_item).visit_with(self);
611+
self
612+
}
613+
614+
fn generics(&mut self) -> &mut Self {
615+
for def in &self.tcx.generics_of(self.current_item).types {
616+
if def.has_default {
617+
self.tcx.type_of(def.def_id).visit_with(self);
618+
}
619+
}
620+
self
621+
}
622+
623+
fn predicates(&mut self) -> &mut Self {
624+
self.tcx.predicates_of(self.current_item).visit_with(self);
625+
self
626+
}
627+
628+
fn impl_trait_ref(&mut self) -> &mut Self {
629+
self.tcx.impl_trait_ref(self.current_item).visit_with(self);
630+
self
631+
}
632+
}
633+
634+
impl<'a, 'tcx> Visitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> {
635+
/// We want to visit items in the context of their containing
636+
/// module and so forth, so supply a crate for doing a deep walk.
637+
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
638+
NestedVisitorMap::All(&self.tcx.hir)
639+
}
640+
641+
fn visit_nested_body(&mut self, body: hir::BodyId) {
642+
let orig_tables = replace(&mut self.tables, self.tcx.body_tables(body));
643+
let body = self.tcx.hir.body(body);
644+
self.visit_body(body);
645+
self.tables = orig_tables;
646+
}
647+
648+
// Check types of expressions
649+
fn visit_expr(&mut self, expr: &'tcx hir::Expr) {
650+
if self.check_expr_pat_type(expr.id, expr.span) {
651+
// Do not check nested expressions if the error already happened.
652+
return;
653+
}
654+
match expr.node {
655+
hir::ExprAssign(.., ref rhs) | hir::ExprMatch(ref rhs, ..) => {
656+
// Do not report duplicate errors for `x = y` and `match x { ... }`.
657+
if self.check_expr_pat_type(rhs.id, rhs.span) {
658+
return;
659+
}
660+
}
661+
hir::ExprMethodCall(name, ..) => {
662+
// Method calls have to be checked specially.
663+
if let Some(method) = self.tables.method_map.get(&ty::MethodCall::expr(expr.id)) {
664+
self.span = name.span;
665+
if method.ty.visit_with(self) || method.substs.visit_with(self) {
666+
return;
667+
}
668+
}
669+
}
670+
_ => {}
671+
}
672+
673+
intravisit::walk_expr(self, expr);
674+
}
675+
676+
// Check types of patterns
677+
fn visit_pat(&mut self, pattern: &'tcx hir::Pat) {
678+
if self.check_expr_pat_type(pattern.id, pattern.span) {
679+
// Do not check nested patterns if the error already happened.
680+
return;
681+
}
682+
683+
intravisit::walk_pat(self, pattern);
684+
}
685+
686+
fn visit_local(&mut self, local: &'tcx hir::Local) {
687+
if let Some(ref init) = local.init {
688+
if self.check_expr_pat_type(init.id, init.span) {
689+
// Do not report duplicate errors for `let x = y`.
690+
return;
691+
}
692+
}
693+
694+
intravisit::walk_local(self, local);
695+
}
696+
697+
// Check types in item interfaces
698+
fn visit_item(&mut self, item: &'tcx hir::Item) {
699+
let orig_current_item = self.current_item;
700+
701+
match item.node {
702+
hir::ItemExternCrate(..) | hir::ItemMod(..) |
703+
hir::ItemUse(..) | hir::ItemGlobalAsm(..) => {}
704+
hir::ItemConst(..) | hir::ItemStatic(..) |
705+
hir::ItemTy(..) | hir::ItemFn(..) => {
706+
self.check_item(item.id).generics().predicates().ty();
707+
}
708+
hir::ItemTrait(.., ref trait_item_refs) => {
709+
self.check_item(item.id).generics().predicates();
710+
for trait_item_ref in trait_item_refs {
711+
let mut check = self.check_item(trait_item_ref.id.node_id);
712+
check.generics().predicates();
713+
if trait_item_ref.kind != hir::AssociatedItemKind::Type ||
714+
trait_item_ref.defaultness.has_value() {
715+
check.ty();
716+
}
717+
}
718+
}
719+
hir::ItemEnum(ref def, _) => {
720+
self.check_item(item.id).generics().predicates();
721+
for variant in &def.variants {
722+
for field in variant.node.data.fields() {
723+
self.check_item(field.id).ty();
724+
}
725+
}
726+
}
727+
hir::ItemForeignMod(ref foreign_mod) => {
728+
for foreign_item in &foreign_mod.items {
729+
self.check_item(foreign_item.id).generics().predicates().ty();
730+
}
731+
}
732+
hir::ItemStruct(ref struct_def, _) |
733+
hir::ItemUnion(ref struct_def, _) => {
734+
self.check_item(item.id).generics().predicates();
735+
for field in struct_def.fields() {
736+
self.check_item(field.id).ty();
737+
}
738+
}
739+
hir::ItemDefaultImpl(..) => {
740+
self.check_item(item.id).impl_trait_ref();
741+
}
742+
hir::ItemImpl(.., ref trait_ref, _, ref impl_item_refs) => {
743+
{
744+
let mut check = self.check_item(item.id);
745+
check.ty().generics().predicates();
746+
if trait_ref.is_some() {
747+
check.impl_trait_ref();
748+
}
749+
}
750+
for impl_item_ref in impl_item_refs {
751+
let impl_item = self.tcx.hir.impl_item(impl_item_ref.id);
752+
self.check_item(impl_item.id).generics().predicates().ty();
753+
}
754+
}
755+
}
756+
757+
self.current_item = self.tcx.hir.local_def_id(item.id);
758+
intravisit::walk_item(self, item);
759+
self.current_item = orig_current_item;
760+
}
761+
}
762+
763+
impl<'a, 'tcx> TypeVisitor<'tcx> for TypePrivacyVisitor<'a, 'tcx> {
764+
fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool {
765+
match ty.sty {
766+
ty::TyAdt(&ty::AdtDef { did: def_id, .. }, ..) | ty::TyFnDef(def_id, ..) => {
767+
if !self.item_is_accessible(def_id) {
768+
let msg = format!("type `{}` is private", ty);
769+
self.tcx.sess.span_err(self.span, &msg);
770+
return true;
771+
}
772+
if let ty::TyFnDef(..) = ty.sty {
773+
// Inherent static methods don't have self type in substs,
774+
// we have to check it additionally.
775+
let mut impl_def_id = None;
776+
if let Some(node_id) = self.tcx.hir.as_local_node_id(def_id) {
777+
if let hir::map::NodeImplItem(..) = self.tcx.hir.get(node_id) {
778+
impl_def_id = Some(self.tcx.hir.get_parent_did(node_id));
779+
}
780+
} else if let Some(Def::Method(..)) = self.tcx.describe_def(def_id) {
781+
let candidate_impl_def_id = self.tcx.parent_def_id(def_id)
782+
.expect("no parent for method def_id");
783+
// `is_none` means it's an impl, not a trait
784+
if self.tcx.describe_def(candidate_impl_def_id).is_none() {
785+
impl_def_id = Some(candidate_impl_def_id)
786+
}
787+
}
788+
if let Some(impl_def_id) = impl_def_id {
789+
let self_ty = self.tcx.type_of(impl_def_id);
790+
if self_ty.visit_with(self) {
791+
return true;
792+
}
793+
}
794+
}
795+
}
796+
ty::TyDynamic(ref predicates, ..) => {
797+
let is_private = predicates.skip_binder().iter().any(|predicate| {
798+
let def_id = match *predicate {
799+
ty::ExistentialPredicate::Trait(trait_ref) => trait_ref.def_id,
800+
ty::ExistentialPredicate::Projection(proj) => proj.trait_ref.def_id,
801+
ty::ExistentialPredicate::AutoTrait(def_id) => def_id,
802+
};
803+
!self.item_is_accessible(def_id)
804+
});
805+
if is_private {
806+
let msg = format!("type `{}` is private", ty);
807+
self.tcx.sess.span_err(self.span, &msg);
808+
return true;
809+
}
810+
}
811+
ty::TyAnon(def_id, ..) => {
812+
for predicate in &self.tcx.predicates_of(def_id).predicates {
813+
let trait_ref = match *predicate {
814+
ty::Predicate::Trait(ref poly_trait_predicate) => {
815+
Some(poly_trait_predicate.skip_binder().trait_ref)
816+
}
817+
ty::Predicate::Projection(ref poly_projection_predicate) => {
818+
if poly_projection_predicate.skip_binder().ty.visit_with(self) {
819+
return true;
820+
}
821+
Some(poly_projection_predicate.skip_binder().projection_ty.trait_ref)
822+
}
823+
ty::Predicate::TypeOutlives(..) => None,
824+
_ => bug!("unexpected predicate: {:?}", predicate),
825+
};
826+
if let Some(trait_ref) = trait_ref {
827+
if !self.item_is_accessible(trait_ref.def_id) {
828+
let msg = format!("trait `{}` is private", trait_ref);
829+
self.tcx.sess.span_err(self.span, &msg);
830+
return true;
831+
}
832+
// Skip `Self` to avoid infinite recursion
833+
for subst in trait_ref.substs.iter().skip(1) {
834+
if subst.visit_with(self) {
835+
return true;
836+
}
837+
}
838+
}
839+
}
840+
}
841+
_ => {}
842+
}
843+
844+
ty.super_visit_with(self)
845+
}
846+
847+
fn visit_trait_ref(&mut self, trait_ref: ty::TraitRef<'tcx>) -> bool {
848+
if !self.item_is_accessible(trait_ref.def_id) {
849+
let msg = format!("trait `{}` is private", trait_ref);
850+
self.tcx.sess.span_err(self.span, &msg);
851+
return true;
852+
}
853+
854+
trait_ref.super_visit_with(self)
855+
}
856+
}
857+
540858
///////////////////////////////////////////////////////////////////////////////
541859
/// Obsolete visitors for checking for private items in public interfaces.
542860
/// These visitors are supposed to be kept in frozen state and produce an
@@ -1225,6 +1543,16 @@ fn privacy_access_levels<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
12251543
};
12261544
intravisit::walk_crate(&mut visitor, krate);
12271545

1546+
// Check privacy of explicitly written types and traits as well as
1547+
// inferred types of expressions and patterns.
1548+
let mut visitor = TypePrivacyVisitor {
1549+
tcx: tcx,
1550+
tables: &ty::TypeckTables::empty(),
1551+
current_item: DefId::local(CRATE_DEF_INDEX),
1552+
span: krate.span,
1553+
};
1554+
intravisit::walk_crate(&mut visitor, krate);
1555+
12281556
// Build up a set of all exported items in the AST. This is a set of all
12291557
// items which are reachable from external crates based on visibility.
12301558
let mut visitor = EmbargoVisitor {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(decl_macro)]
12+
13+
fn priv_fn() {}
14+
enum PrivEnum { Variant }
15+
pub enum PubEnum { Variant }
16+
trait PrivTrait { fn method() {} }
17+
impl PrivTrait for u8 {}
18+
pub trait PubTrait { fn method() {} }
19+
impl PubTrait for u8 {}
20+
struct PrivTupleStruct(u8);
21+
pub struct PubTupleStruct(u8);
22+
impl PubTupleStruct { fn method() {} }
23+
24+
struct Priv;
25+
pub type Alias = Priv;
26+
pub struct Pub<T = Alias>(pub T);
27+
28+
impl Pub<Priv> {
29+
pub fn static_method() {}
30+
}
31+
32+
pub macro m() {
33+
priv_fn;
34+
PrivEnum::Variant;
35+
PubEnum::Variant;
36+
<u8 as PrivTrait>::method;
37+
<u8 as PubTrait>::method;
38+
PrivTupleStruct;
39+
PubTupleStruct;
40+
Pub::static_method;
41+
}

0 commit comments

Comments
 (0)