Skip to content

Commit c743e12

Browse files
committed
Check types for privacy
1 parent 4225019 commit c743e12

11 files changed

+581
-7
lines changed

src/librustc_privacy/lib.rs

+330-2
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,13 @@
2222
#![cfg_attr(stage0, feature(rustc_private))]
2323
#![cfg_attr(stage0, feature(staged_api))]
2424

25-
extern crate rustc;
25+
#[macro_use] extern crate rustc;
2626
#[macro_use] extern crate syntax;
2727
extern crate syntax_pos;
2828

2929
use rustc::hir::{self, PatKind};
3030
use rustc::hir::def::Def;
31-
use rustc::hir::def_id::{LOCAL_CRATE, CrateNum, DefId};
31+
use rustc::hir::def_id::{CRATE_DEF_INDEX, LOCAL_CRATE, CrateNum, DefId};
3232
use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap};
3333
use rustc::hir::itemlikevisit::DeepVisitor;
3434
use rustc::lint;
@@ -535,6 +535,324 @@ impl<'a, 'tcx> Visitor<'tcx> for NamePrivacyVisitor<'a, 'tcx> {
535535
}
536536
}
537537

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

1538+
// Check privacy of explicitly written types and traits as well as
1539+
// inferred types of expressions and patterns.
1540+
let mut visitor = TypePrivacyVisitor {
1541+
tcx: tcx,
1542+
tables: &ty::TypeckTables::empty(),
1543+
current_item: DefId::local(CRATE_DEF_INDEX),
1544+
span: krate.span,
1545+
};
1546+
intravisit::walk_crate(&mut visitor, krate);
1547+
12201548
// Build up a set of all exported items in the AST. This is a set of all
12211549
// items which are reachable from external crates based on visibility.
12221550
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)