1 // Copyright 2012-2014 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.
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.
11 #![crate_name = "rustc_privacy"]
12 #![unstable(feature = "rustc_private", issue = "27812")]
13 #![crate_type = "dylib"]
14 #![crate_type = "rlib"]
15 #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
16 html_favicon_url
= "https://doc.rust-lang.org/favicon.ico",
17 html_root_url
= "https://doc.rust-lang.org/nightly/")]
20 #![feature(rustc_diagnostic_macros)]
21 #![feature(rustc_private)]
22 #![feature(staged_api)]
25 #[macro_use] extern crate syntax;
26 extern crate syntax_pos
;
28 use rustc
::dep_graph
::DepNode
;
29 use rustc
::hir
::{self, PatKind}
;
30 use rustc
::hir
::def
::{self, Def, CtorKind}
;
31 use rustc
::hir
::def_id
::{CRATE_DEF_INDEX, DefId}
;
32 use rustc
::hir
::intravisit
::{self, Visitor, NestedVisitorMap}
;
33 use rustc
::hir
::itemlikevisit
::DeepVisitor
;
34 use rustc
::hir
::pat_util
::EnumerateAndAdjustIterator
;
36 use rustc
::middle
::privacy
::{AccessLevel, AccessLevels}
;
37 use rustc
::ty
::{self, TyCtxt, Ty, TypeFoldable}
;
38 use rustc
::ty
::fold
::TypeVisitor
;
39 use rustc
::util
::nodemap
::NodeSet
;
44 use std
::mem
::replace
;
48 ////////////////////////////////////////////////////////////////////////////////
49 /// The embargo visitor, used to determine the exports of the ast
50 ////////////////////////////////////////////////////////////////////////////////
52 struct EmbargoVisitor
<'a
, 'tcx
: 'a
> {
53 tcx
: TyCtxt
<'a
, 'tcx
, 'tcx
>,
54 export_map
: &'a def
::ExportMap
,
56 // Accessibility levels for reachable nodes
57 access_levels
: AccessLevels
,
58 // Previous accessibility level, None means unreachable
59 prev_level
: Option
<AccessLevel
>,
60 // Have something changed in the level map?
64 struct ReachEverythingInTheInterfaceVisitor
<'b
, 'a
: 'b
, 'tcx
: 'a
> {
66 ev
: &'b
mut EmbargoVisitor
<'a
, 'tcx
>,
69 impl<'a
, 'tcx
> EmbargoVisitor
<'a
, 'tcx
> {
70 fn item_ty_level(&self, item_def_id
: DefId
) -> Option
<AccessLevel
> {
71 let ty_def_id
= match self.tcx
.item_type(item_def_id
).sty
{
72 ty
::TyAdt(adt
, _
) => adt
.did
,
73 ty
::TyDynamic(ref obj
, ..) if obj
.principal().is_some() =>
74 obj
.principal().unwrap().def_id(),
75 ty
::TyProjection(ref proj
) => proj
.trait_ref
.def_id
,
76 _
=> return Some(AccessLevel
::Public
)
78 if let Some(node_id
) = self.tcx
.hir
.as_local_node_id(ty_def_id
) {
81 Some(AccessLevel
::Public
)
85 fn impl_trait_level(&self, impl_def_id
: DefId
) -> Option
<AccessLevel
> {
86 if let Some(trait_ref
) = self.tcx
.impl_trait_ref(impl_def_id
) {
87 if let Some(node_id
) = self.tcx
.hir
.as_local_node_id(trait_ref
.def_id
) {
88 return self.get(node_id
);
91 Some(AccessLevel
::Public
)
94 fn get(&self, id
: ast
::NodeId
) -> Option
<AccessLevel
> {
95 self.access_levels
.map
.get(&id
).cloned()
98 // Updates node level and returns the updated level
99 fn update(&mut self, id
: ast
::NodeId
, level
: Option
<AccessLevel
>) -> Option
<AccessLevel
> {
100 let old_level
= self.get(id
);
101 // Accessibility levels can only grow
102 if level
> old_level
{
103 self.access_levels
.map
.insert(id
, level
.unwrap());
111 fn reach
<'b
>(&'b
mut self, item_id
: ast
::NodeId
)
112 -> ReachEverythingInTheInterfaceVisitor
<'b
, 'a
, 'tcx
> {
113 ReachEverythingInTheInterfaceVisitor
{
114 item_def_id
: self.tcx
.hir
.local_def_id(item_id
),
120 impl<'a
, 'tcx
> Visitor
<'tcx
> for EmbargoVisitor
<'a
, 'tcx
> {
121 /// We want to visit items in the context of their containing
122 /// module and so forth, so supply a crate for doing a deep walk.
123 fn nested_visit_map
<'this
>(&'this
mut self) -> NestedVisitorMap
<'this
, 'tcx
> {
124 NestedVisitorMap
::All(&self.tcx
.hir
)
127 fn visit_item(&mut self, item
: &'tcx hir
::Item
) {
128 let inherited_item_level
= match item
.node
{
129 // Impls inherit level from their types and traits
130 hir
::ItemImpl(..) => {
131 let def_id
= self.tcx
.hir
.local_def_id(item
.id
);
132 cmp
::min(self.item_ty_level(def_id
), self.impl_trait_level(def_id
))
134 hir
::ItemDefaultImpl(..) => {
135 let def_id
= self.tcx
.hir
.local_def_id(item
.id
);
136 self.impl_trait_level(def_id
)
138 // Foreign mods inherit level from parents
139 hir
::ItemForeignMod(..) => {
142 // Other `pub` items inherit levels from parents
144 if item
.vis
== hir
::Public { self.prev_level }
else { None }
148 // Update level of the item itself
149 let item_level
= self.update(item
.id
, inherited_item_level
);
151 // Update levels of nested things
153 hir
::ItemEnum(ref def
, _
) => {
154 for variant
in &def
.variants
{
155 let variant_level
= self.update(variant
.node
.data
.id(), item_level
);
156 for field
in variant
.node
.data
.fields() {
157 self.update(field
.id
, variant_level
);
161 hir
::ItemImpl(.., None
, _
, ref impl_item_refs
) => {
162 for impl_item_ref
in impl_item_refs
{
163 if impl_item_ref
.vis
== hir
::Public
{
164 self.update(impl_item_ref
.id
.node_id
, item_level
);
168 hir
::ItemImpl(.., Some(_
), _
, ref impl_item_refs
) => {
169 for impl_item_ref
in impl_item_refs
{
170 self.update(impl_item_ref
.id
.node_id
, item_level
);
173 hir
::ItemTrait(.., ref trait_item_refs
) => {
174 for trait_item_ref
in trait_item_refs
{
175 self.update(trait_item_ref
.id
.node_id
, item_level
);
178 hir
::ItemStruct(ref def
, _
) | hir
::ItemUnion(ref def
, _
) => {
179 if !def
.is_struct() {
180 self.update(def
.id(), item_level
);
182 for field
in def
.fields() {
183 if field
.vis
== hir
::Public
{
184 self.update(field
.id
, item_level
);
188 hir
::ItemForeignMod(ref foreign_mod
) => {
189 for foreign_item
in &foreign_mod
.items
{
190 if foreign_item
.vis
== hir
::Public
{
191 self.update(foreign_item
.id
, item_level
);
198 // Mark all items in interfaces of reachable items as reachable
200 // The interface is empty
201 hir
::ItemExternCrate(..) => {}
202 // All nested items are checked by visit_item
203 hir
::ItemMod(..) => {}
204 // Reexports are handled in visit_mod
205 hir
::ItemUse(..) => {}
206 // The interface is empty
207 hir
::ItemDefaultImpl(..) => {}
209 hir
::ItemConst(..) | hir
::ItemStatic(..) |
210 hir
::ItemFn(..) | hir
::ItemTy(..) => {
211 if item_level
.is_some() {
212 self.reach(item
.id
).generics().predicates().item_type();
215 hir
::ItemTrait(.., ref trait_item_refs
) => {
216 if item_level
.is_some() {
217 self.reach(item
.id
).generics().predicates();
219 for trait_item_ref
in trait_item_refs
{
220 let mut reach
= self.reach(trait_item_ref
.id
.node_id
);
221 reach
.generics().predicates();
223 if trait_item_ref
.kind
== hir
::AssociatedItemKind
::Type
&&
224 !trait_item_ref
.defaultness
.has_value() {
232 // Visit everything except for private impl items
233 hir
::ItemImpl(.., ref trait_ref
, _
, ref impl_item_refs
) => {
234 if item_level
.is_some() {
235 self.reach(item
.id
).generics().predicates().impl_trait_ref();
237 for impl_item_ref
in impl_item_refs
{
238 let id
= impl_item_ref
.id
.node_id
;
239 if trait_ref
.is_some() || self.get(id
).is_some() {
240 self.reach(id
).generics().predicates().item_type();
246 // Visit everything, but enum variants have their own levels
247 hir
::ItemEnum(ref def
, _
) => {
248 if item_level
.is_some() {
249 self.reach(item
.id
).generics().predicates();
251 for variant
in &def
.variants
{
252 if self.get(variant
.node
.data
.id()).is_some() {
253 for field
in variant
.node
.data
.fields() {
254 self.reach(field
.id
).item_type();
256 // Corner case: if the variant is reachable, but its
257 // enum is not, make the enum reachable as well.
258 self.update(item
.id
, Some(AccessLevel
::Reachable
));
262 // Visit everything, but foreign items have their own levels
263 hir
::ItemForeignMod(ref foreign_mod
) => {
264 for foreign_item
in &foreign_mod
.items
{
265 if self.get(foreign_item
.id
).is_some() {
266 self.reach(foreign_item
.id
).generics().predicates().item_type();
270 // Visit everything except for private fields
271 hir
::ItemStruct(ref struct_def
, _
) |
272 hir
::ItemUnion(ref struct_def
, _
) => {
273 if item_level
.is_some() {
274 self.reach(item
.id
).generics().predicates();
275 for field
in struct_def
.fields() {
276 if self.get(field
.id
).is_some() {
277 self.reach(field
.id
).item_type();
284 let orig_level
= self.prev_level
;
285 self.prev_level
= item_level
;
287 intravisit
::walk_item(self, item
);
289 self.prev_level
= orig_level
;
292 fn visit_block(&mut self, b
: &'tcx hir
::Block
) {
293 let orig_level
= replace(&mut self.prev_level
, None
);
295 // Blocks can have public items, for example impls, but they always
296 // start as completely private regardless of publicity of a function,
297 // constant, type, field, etc. in which this block resides
298 intravisit
::walk_block(self, b
);
300 self.prev_level
= orig_level
;
303 fn visit_mod(&mut self, m
: &'tcx hir
::Mod
, _sp
: Span
, id
: ast
::NodeId
) {
304 // This code is here instead of in visit_item so that the
305 // crate module gets processed as well.
306 if self.prev_level
.is_some() {
307 if let Some(exports
) = self.export_map
.get(&id
) {
308 for export
in exports
{
309 if let Some(node_id
) = self.tcx
.hir
.as_local_node_id(export
.def
.def_id()) {
310 self.update(node_id
, Some(AccessLevel
::Exported
));
316 intravisit
::walk_mod(self, m
, id
);
319 fn visit_macro_def(&mut self, md
: &'tcx hir
::MacroDef
) {
320 self.update(md
.id
, Some(AccessLevel
::Public
));
323 fn visit_ty(&mut self, ty
: &'tcx hir
::Ty
) {
324 if let hir
::TyImplTrait(..) = ty
.node
{
325 if self.get(ty
.id
).is_some() {
326 // Reach the (potentially private) type and the API being exposed.
327 self.reach(ty
.id
).item_type().predicates();
331 intravisit
::walk_ty(self, ty
);
335 impl<'b
, 'a
, 'tcx
> ReachEverythingInTheInterfaceVisitor
<'b
, 'a
, 'tcx
> {
336 fn generics(&mut self) -> &mut Self {
337 self.ev
.tcx
.item_generics(self.item_def_id
).visit_with(self);
341 fn predicates(&mut self) -> &mut Self {
342 self.ev
.tcx
.item_predicates(self.item_def_id
).visit_with(self);
346 fn item_type(&mut self) -> &mut Self {
347 self.ev
.tcx
.item_type(self.item_def_id
).visit_with(self);
351 fn impl_trait_ref(&mut self) -> &mut Self {
352 self.ev
.tcx
.impl_trait_ref(self.item_def_id
).visit_with(self);
357 impl<'b
, 'a
, 'tcx
> TypeVisitor
<'tcx
> for ReachEverythingInTheInterfaceVisitor
<'b
, 'a
, 'tcx
> {
358 fn visit_ty(&mut self, ty
: Ty
<'tcx
>) -> bool
{
359 let ty_def_id
= match ty
.sty
{
360 ty
::TyAdt(adt
, _
) => Some(adt
.did
),
361 ty
::TyDynamic(ref obj
, ..) => obj
.principal().map(|p
| p
.def_id()),
362 ty
::TyProjection(ref proj
) => Some(proj
.trait_ref
.def_id
),
363 ty
::TyFnDef(def_id
, ..) |
364 ty
::TyAnon(def_id
, _
) => Some(def_id
),
368 if let Some(def_id
) = ty_def_id
{
369 if let Some(node_id
) = self.ev
.tcx
.hir
.as_local_node_id(def_id
) {
370 self.ev
.update(node_id
, Some(AccessLevel
::Reachable
));
374 ty
.super_visit_with(self)
377 fn visit_trait_ref(&mut self, trait_ref
: ty
::TraitRef
<'tcx
>) -> bool
{
378 if let Some(node_id
) = self.ev
.tcx
.hir
.as_local_node_id(trait_ref
.def_id
) {
379 let item
= self.ev
.tcx
.hir
.expect_item(node_id
);
380 self.ev
.update(item
.id
, Some(AccessLevel
::Reachable
));
383 trait_ref
.super_visit_with(self)
387 ////////////////////////////////////////////////////////////////////////////////
388 /// The privacy visitor, where privacy checks take place (violations reported)
389 ////////////////////////////////////////////////////////////////////////////////
391 struct PrivacyVisitor
<'a
, 'tcx
: 'a
> {
392 tcx
: TyCtxt
<'a
, 'tcx
, 'tcx
>,
395 tables
: &'a ty
::TypeckTables
<'tcx
>,
398 impl<'a
, 'tcx
> PrivacyVisitor
<'a
, 'tcx
> {
399 fn item_is_accessible(&self, did
: DefId
) -> bool
{
400 match self.tcx
.hir
.as_local_node_id(did
) {
402 ty
::Visibility
::from_hir(&self.tcx
.hir
.expect_item(node_id
).vis
, node_id
, self.tcx
),
403 None
=> self.tcx
.sess
.cstore
.visibility(did
),
404 }.is_accessible_from(self.curitem
, self.tcx
)
407 // Checks that a field is in scope.
408 fn check_field(&mut self, span
: Span
, def
: &'tcx ty
::AdtDef
, field
: &'tcx ty
::FieldDef
) {
409 if !def
.is_enum() && !field
.vis
.is_accessible_from(self.curitem
, self.tcx
) {
410 struct_span_err
!(self.tcx
.sess
, span
, E0451
, "field `{}` of {} `{}` is private",
411 field
.name
, def
.variant_descr(), self.tcx
.item_path_str(def
.did
))
412 .span_label(span
, &format
!("field `{}` is private", field
.name
))
417 // Checks that a method is in scope.
418 fn check_method(&mut self, span
: Span
, method_def_id
: DefId
) {
419 match self.tcx
.associated_item(method_def_id
).container
{
420 // Trait methods are always all public. The only controlling factor
421 // is whether the trait itself is accessible or not.
422 ty
::TraitContainer(trait_def_id
) if !self.item_is_accessible(trait_def_id
) => {
423 let msg
= format
!("source trait `{}` is private",
424 self.tcx
.item_path_str(trait_def_id
));
425 self.tcx
.sess
.span_err(span
, &msg
);
432 impl<'a
, 'tcx
> Visitor
<'tcx
> for PrivacyVisitor
<'a
, 'tcx
> {
433 /// We want to visit items in the context of their containing
434 /// module and so forth, so supply a crate for doing a deep walk.
435 fn nested_visit_map
<'this
>(&'this
mut self) -> NestedVisitorMap
<'this
, 'tcx
> {
436 NestedVisitorMap
::All(&self.tcx
.hir
)
439 fn visit_nested_body(&mut self, body
: hir
::BodyId
) {
440 let old_tables
= self.tables
;
441 self.tables
= self.tcx
.body_tables(body
);
442 let body
= self.tcx
.hir
.body(body
);
443 self.visit_body(body
);
444 self.tables
= old_tables
;
447 fn visit_item(&mut self, item
: &'tcx hir
::Item
) {
448 let orig_curitem
= replace(&mut self.curitem
, self.tcx
.hir
.local_def_id(item
.id
));
449 intravisit
::walk_item(self, item
);
450 self.curitem
= orig_curitem
;
453 fn visit_expr(&mut self, expr
: &'tcx hir
::Expr
) {
455 hir
::ExprMethodCall(..) => {
456 let method_call
= ty
::MethodCall
::expr(expr
.id
);
457 let method
= self.tables
.method_map
[&method_call
];
458 self.check_method(expr
.span
, method
.def_id
);
460 hir
::ExprStruct(ref qpath
, ref expr_fields
, _
) => {
461 let def
= self.tables
.qpath_def(qpath
, expr
.id
);
462 let adt
= self.tables
.expr_ty(expr
).ty_adt_def().unwrap();
463 let variant
= adt
.variant_of_def(def
);
464 // RFC 736: ensure all unmentioned fields are visible.
465 // Rather than computing the set of unmentioned fields
466 // (i.e. `all_fields - fields`), just check them all,
467 // unless the ADT is a union, then unmentioned fields
470 for expr_field
in expr_fields
{
471 self.check_field(expr
.span
, adt
, variant
.field_named(expr_field
.name
.node
));
474 for field
in &variant
.fields
{
475 let expr_field
= expr_fields
.iter().find(|f
| f
.name
.node
== field
.name
);
476 let span
= if let Some(f
) = expr_field { f.span }
else { expr.span }
;
477 self.check_field(span
, adt
, field
);
481 hir
::ExprPath(hir
::QPath
::Resolved(_
, ref path
)) => {
482 if let Def
::StructCtor(_
, CtorKind
::Fn
) = path
.def
{
483 let adt_def
= self.tcx
.expect_variant_def(path
.def
);
484 let private_indexes
= adt_def
.fields
.iter().enumerate().filter(|&(_
, field
)| {
485 !field
.vis
.is_accessible_from(self.curitem
, self.tcx
)
486 }).map(|(i
, _
)| i
).collect
::<Vec
<_
>>();
488 if !private_indexes
.is_empty() {
489 let mut error
= struct_span_err
!(self.tcx
.sess
, expr
.span
, E0450
,
490 "cannot invoke tuple struct constructor \
491 with private fields");
492 error
.span_label(expr
.span
,
493 &format
!("cannot construct with a private field"));
495 if let Some(node_id
) = self.tcx
.hir
.as_local_node_id(adt_def
.did
) {
496 let node
= self.tcx
.hir
.find(node_id
);
497 if let Some(hir
::map
::NodeStructCtor(vdata
)) = node
{
498 for i
in private_indexes
{
499 error
.span_label(vdata
.fields()[i
].span
,
500 &format
!("private field declared here"));
511 intravisit
::walk_expr(self, expr
);
514 fn visit_pat(&mut self, pattern
: &'tcx hir
::Pat
) {
515 // Foreign functions do not have their patterns mapped in the def_map,
516 // and there's nothing really relevant there anyway, so don't bother
517 // checking privacy. If you can name the type then you can pass it to an
518 // external C function anyway.
519 if self.in_foreign { return }
522 PatKind
::Struct(ref qpath
, ref fields
, _
) => {
523 let def
= self.tables
.qpath_def(qpath
, pattern
.id
);
524 let adt
= self.tables
.pat_ty(pattern
).ty_adt_def().unwrap();
525 let variant
= adt
.variant_of_def(def
);
526 for field
in fields
{
527 self.check_field(field
.span
, adt
, variant
.field_named(field
.node
.name
));
530 PatKind
::TupleStruct(_
, ref fields
, ddpos
) => {
531 match self.tables
.pat_ty(pattern
).sty
{
532 // enum fields have no privacy at this time
533 ty
::TyAdt(def
, _
) if !def
.is_enum() => {
534 let expected_len
= def
.struct_variant().fields
.len();
535 for (i
, field
) in fields
.iter().enumerate_and_adjust(expected_len
, ddpos
) {
536 if let PatKind
::Wild
= field
.node
{
539 self.check_field(field
.span
, def
, &def
.struct_variant().fields
[i
]);
548 intravisit
::walk_pat(self, pattern
);
551 fn visit_foreign_item(&mut self, fi
: &'tcx hir
::ForeignItem
) {
552 self.in_foreign
= true;
553 intravisit
::walk_foreign_item(self, fi
);
554 self.in_foreign
= false;
558 ///////////////////////////////////////////////////////////////////////////////
559 /// Obsolete visitors for checking for private items in public interfaces.
560 /// These visitors are supposed to be kept in frozen state and produce an
561 /// "old error node set". For backward compatibility the new visitor reports
562 /// warnings instead of hard errors when the erroneous node is not in this old set.
563 ///////////////////////////////////////////////////////////////////////////////
565 struct ObsoleteVisiblePrivateTypesVisitor
<'a
, 'tcx
: 'a
> {
566 tcx
: TyCtxt
<'a
, 'tcx
, 'tcx
>,
567 access_levels
: &'a AccessLevels
,
569 // set of errors produced by this obsolete visitor
570 old_error_set
: NodeSet
,
573 struct ObsoleteCheckTypeForPrivatenessVisitor
<'a
, 'b
: 'a
, 'tcx
: 'b
> {
574 inner
: &'a ObsoleteVisiblePrivateTypesVisitor
<'b
, 'tcx
>,
575 /// whether the type refers to private types.
576 contains_private
: bool
,
577 /// whether we've recurred at all (i.e. if we're pointing at the
578 /// first type on which visit_ty was called).
580 // whether that first type is a public path.
581 outer_type_is_public_path
: bool
,
584 impl<'a
, 'tcx
> ObsoleteVisiblePrivateTypesVisitor
<'a
, 'tcx
> {
585 fn path_is_private_type(&self, path
: &hir
::Path
) -> bool
{
586 let did
= match path
.def
{
587 Def
::PrimTy(..) | Def
::SelfTy(..) => return false,
591 // A path can only be private if:
592 // it's in this crate...
593 if let Some(node_id
) = self.tcx
.hir
.as_local_node_id(did
) {
594 // .. and it corresponds to a private type in the AST (this returns
595 // None for type parameters)
596 match self.tcx
.hir
.find(node_id
) {
597 Some(hir
::map
::NodeItem(ref item
)) => item
.vis
!= hir
::Public
,
598 Some(_
) | None
=> false,
605 fn trait_is_public(&self, trait_id
: ast
::NodeId
) -> bool
{
606 // FIXME: this would preferably be using `exported_items`, but all
607 // traits are exported currently (see `EmbargoVisitor.exported_trait`)
608 self.access_levels
.is_public(trait_id
)
611 fn check_ty_param_bound(&mut self,
612 ty_param_bound
: &hir
::TyParamBound
) {
613 if let hir
::TraitTyParamBound(ref trait_ref
, _
) = *ty_param_bound
{
614 if self.path_is_private_type(&trait_ref
.trait_ref
.path
) {
615 self.old_error_set
.insert(trait_ref
.trait_ref
.ref_id
);
620 fn item_is_public(&self, id
: &ast
::NodeId
, vis
: &hir
::Visibility
) -> bool
{
621 self.access_levels
.is_reachable(*id
) || *vis
== hir
::Public
625 impl<'a
, 'b
, 'tcx
, 'v
> Visitor
<'v
> for ObsoleteCheckTypeForPrivatenessVisitor
<'a
, 'b
, 'tcx
> {
626 fn nested_visit_map
<'this
>(&'this
mut self) -> NestedVisitorMap
<'this
, 'v
> {
627 NestedVisitorMap
::None
630 fn visit_ty(&mut self, ty
: &hir
::Ty
) {
631 if let hir
::TyPath(hir
::QPath
::Resolved(_
, ref path
)) = ty
.node
{
632 if self.inner
.path_is_private_type(path
) {
633 self.contains_private
= true;
634 // found what we're looking for so let's stop
639 if let hir
::TyPath(_
) = ty
.node
{
640 if self.at_outer_type
{
641 self.outer_type_is_public_path
= true;
644 self.at_outer_type
= false;
645 intravisit
::walk_ty(self, ty
)
648 // don't want to recurse into [, .. expr]
649 fn visit_expr(&mut self, _
: &hir
::Expr
) {}
652 impl<'a
, 'tcx
> Visitor
<'tcx
> for ObsoleteVisiblePrivateTypesVisitor
<'a
, 'tcx
> {
653 /// We want to visit items in the context of their containing
654 /// module and so forth, so supply a crate for doing a deep walk.
655 fn nested_visit_map
<'this
>(&'this
mut self) -> NestedVisitorMap
<'this
, 'tcx
> {
656 NestedVisitorMap
::All(&self.tcx
.hir
)
659 fn visit_item(&mut self, item
: &'tcx hir
::Item
) {
661 // contents of a private mod can be reexported, so we need
662 // to check internals.
663 hir
::ItemMod(_
) => {}
665 // An `extern {}` doesn't introduce a new privacy
666 // namespace (the contents have their own privacies).
667 hir
::ItemForeignMod(_
) => {}
669 hir
::ItemTrait(.., ref bounds
, _
) => {
670 if !self.trait_is_public(item
.id
) {
674 for bound
in bounds
.iter() {
675 self.check_ty_param_bound(bound
)
679 // impls need some special handling to try to offer useful
680 // error messages without (too many) false positives
681 // (i.e. we could just return here to not check them at
682 // all, or some worse estimation of whether an impl is
683 // publicly visible).
684 hir
::ItemImpl(.., ref g
, ref trait_ref
, ref self_
, ref impl_item_refs
) => {
685 // `impl [... for] Private` is never visible.
686 let self_contains_private
;
687 // impl [... for] Public<...>, but not `impl [... for]
688 // Vec<Public>` or `(Public,)` etc.
689 let self_is_public_path
;
691 // check the properties of the Self type:
693 let mut visitor
= ObsoleteCheckTypeForPrivatenessVisitor
{
695 contains_private
: false,
697 outer_type_is_public_path
: false,
699 visitor
.visit_ty(&self_
);
700 self_contains_private
= visitor
.contains_private
;
701 self_is_public_path
= visitor
.outer_type_is_public_path
;
704 // miscellaneous info about the impl
706 // `true` iff this is `impl Private for ...`.
707 let not_private_trait
=
708 trait_ref
.as_ref().map_or(true, // no trait counts as public trait
710 let did
= tr
.path
.def
.def_id();
712 if let Some(node_id
) = self.tcx
.hir
.as_local_node_id(did
) {
713 self.trait_is_public(node_id
)
715 true // external traits must be public
719 // `true` iff this is a trait impl or at least one method is public.
721 // `impl Public { $( fn ...() {} )* }` is not visible.
723 // This is required over just using the methods' privacy
724 // directly because we might have `impl<T: Foo<Private>> ...`,
725 // and we shouldn't warn about the generics if all the methods
726 // are private (because `T` won't be visible externally).
727 let trait_or_some_public_method
=
728 trait_ref
.is_some() ||
729 impl_item_refs
.iter()
730 .any(|impl_item_ref
| {
731 let impl_item
= self.tcx
.hir
.impl_item(impl_item_ref
.id
);
732 match impl_item
.node
{
733 hir
::ImplItemKind
::Const(..) |
734 hir
::ImplItemKind
::Method(..) => {
735 self.access_levels
.is_reachable(impl_item
.id
)
737 hir
::ImplItemKind
::Type(_
) => false,
741 if !self_contains_private
&&
743 trait_or_some_public_method
{
745 intravisit
::walk_generics(self, g
);
749 for impl_item_ref
in impl_item_refs
{
750 // This is where we choose whether to walk down
751 // further into the impl to check its items. We
752 // should only walk into public items so that we
753 // don't erroneously report errors for private
754 // types in private items.
755 let impl_item
= self.tcx
.hir
.impl_item(impl_item_ref
.id
);
756 match impl_item
.node
{
757 hir
::ImplItemKind
::Const(..) |
758 hir
::ImplItemKind
::Method(..)
759 if self.item_is_public(&impl_item
.id
, &impl_item
.vis
) =>
761 intravisit
::walk_impl_item(self, impl_item
)
763 hir
::ImplItemKind
::Type(..) => {
764 intravisit
::walk_impl_item(self, impl_item
)
771 // Any private types in a trait impl fall into three
773 // 1. mentioned in the trait definition
774 // 2. mentioned in the type params/generics
775 // 3. mentioned in the associated types of the impl
777 // Those in 1. can only occur if the trait is in
778 // this crate and will've been warned about on the
779 // trait definition (there's no need to warn twice
780 // so we don't check the methods).
782 // Those in 2. are warned via walk_generics and this
784 intravisit
::walk_path(self, &tr
.path
);
786 // Those in 3. are warned with this call.
787 for impl_item_ref
in impl_item_refs
{
788 let impl_item
= self.tcx
.hir
.impl_item(impl_item_ref
.id
);
789 if let hir
::ImplItemKind
::Type(ref ty
) = impl_item
.node
{
795 } else if trait_ref
.is_none() && self_is_public_path
{
796 // impl Public<Private> { ... }. Any public static
797 // methods will be visible as `Public::foo`.
798 let mut found_pub_static
= false;
799 for impl_item_ref
in impl_item_refs
{
800 if self.item_is_public(&impl_item_ref
.id
.node_id
, &impl_item_ref
.vis
) {
801 let impl_item
= self.tcx
.hir
.impl_item(impl_item_ref
.id
);
802 match impl_item_ref
.kind
{
803 hir
::AssociatedItemKind
::Const
=> {
804 found_pub_static
= true;
805 intravisit
::walk_impl_item(self, impl_item
);
807 hir
::AssociatedItemKind
::Method { has_self: false }
=> {
808 found_pub_static
= true;
809 intravisit
::walk_impl_item(self, impl_item
);
815 if found_pub_static
{
816 intravisit
::walk_generics(self, g
)
822 // `type ... = ...;` can contain private types, because
823 // we're introducing a new name.
824 hir
::ItemTy(..) => return,
826 // not at all public, so we don't care
827 _
if !self.item_is_public(&item
.id
, &item
.vis
) => {
834 // We've carefully constructed it so that if we're here, then
835 // any `visit_ty`'s will be called on things that are in
836 // public signatures, i.e. things that we're interested in for
838 intravisit
::walk_item(self, item
);
841 fn visit_generics(&mut self, generics
: &'tcx hir
::Generics
) {
842 for ty_param
in generics
.ty_params
.iter() {
843 for bound
in ty_param
.bounds
.iter() {
844 self.check_ty_param_bound(bound
)
847 for predicate
in &generics
.where_clause
.predicates
{
849 &hir
::WherePredicate
::BoundPredicate(ref bound_pred
) => {
850 for bound
in bound_pred
.bounds
.iter() {
851 self.check_ty_param_bound(bound
)
854 &hir
::WherePredicate
::RegionPredicate(_
) => {}
855 &hir
::WherePredicate
::EqPredicate(ref eq_pred
) => {
856 self.visit_ty(&eq_pred
.rhs_ty
);
862 fn visit_foreign_item(&mut self, item
: &'tcx hir
::ForeignItem
) {
863 if self.access_levels
.is_reachable(item
.id
) {
864 intravisit
::walk_foreign_item(self, item
)
868 fn visit_ty(&mut self, t
: &'tcx hir
::Ty
) {
869 if let hir
::TyPath(hir
::QPath
::Resolved(_
, ref path
)) = t
.node
{
870 if self.path_is_private_type(path
) {
871 self.old_error_set
.insert(t
.id
);
874 intravisit
::walk_ty(self, t
)
877 fn visit_variant(&mut self,
878 v
: &'tcx hir
::Variant
,
879 g
: &'tcx hir
::Generics
,
880 item_id
: ast
::NodeId
) {
881 if self.access_levels
.is_reachable(v
.node
.data
.id()) {
882 self.in_variant
= true;
883 intravisit
::walk_variant(self, v
, g
, item_id
);
884 self.in_variant
= false;
888 fn visit_struct_field(&mut self, s
: &'tcx hir
::StructField
) {
889 if s
.vis
== hir
::Public
|| self.in_variant
{
890 intravisit
::walk_struct_field(self, s
);
894 // we don't need to introspect into these at all: an
895 // expression/block context can't possibly contain exported things.
896 // (Making them no-ops stops us from traversing the whole AST without
897 // having to be super careful about our `walk_...` calls above.)
898 fn visit_block(&mut self, _
: &'tcx hir
::Block
) {}
899 fn visit_expr(&mut self, _
: &'tcx hir
::Expr
) {}
902 ///////////////////////////////////////////////////////////////////////////////
903 /// SearchInterfaceForPrivateItemsVisitor traverses an item's interface and
904 /// finds any private components in it.
905 /// PrivateItemsInPublicInterfacesVisitor ensures there are no private types
906 /// and traits in public interfaces.
907 ///////////////////////////////////////////////////////////////////////////////
909 struct SearchInterfaceForPrivateItemsVisitor
<'a
, 'tcx
: 'a
> {
910 tcx
: TyCtxt
<'a
, 'tcx
, 'tcx
>,
913 /// The visitor checks that each component type is at least this visible
914 required_visibility
: ty
::Visibility
,
915 /// The visibility of the least visible component that has been visited
916 min_visibility
: ty
::Visibility
,
917 has_old_errors
: bool
,
920 impl<'a
, 'tcx
: 'a
> SearchInterfaceForPrivateItemsVisitor
<'a
, 'tcx
> {
921 fn generics(&mut self) -> &mut Self {
922 self.tcx
.item_generics(self.item_def_id
).visit_with(self);
926 fn predicates(&mut self) -> &mut Self {
927 self.tcx
.item_predicates(self.item_def_id
).visit_with(self);
931 fn item_type(&mut self) -> &mut Self {
932 self.tcx
.item_type(self.item_def_id
).visit_with(self);
936 fn impl_trait_ref(&mut self) -> &mut Self {
937 self.tcx
.impl_trait_ref(self.item_def_id
).visit_with(self);
942 impl<'a
, 'tcx
: 'a
> TypeVisitor
<'tcx
> for SearchInterfaceForPrivateItemsVisitor
<'a
, 'tcx
> {
943 fn visit_ty(&mut self, ty
: Ty
<'tcx
>) -> bool
{
944 let ty_def_id
= match ty
.sty
{
945 ty
::TyAdt(adt
, _
) => Some(adt
.did
),
946 ty
::TyDynamic(ref obj
, ..) => obj
.principal().map(|p
| p
.def_id()),
947 ty
::TyProjection(ref proj
) => {
948 if self.required_visibility
== ty
::Visibility
::Invisible
{
949 // Conservatively approximate the whole type alias as public without
950 // recursing into its components when determining impl publicity.
951 // For example, `impl <Type as Trait>::Alias {...}` may be a public impl
952 // even if both `Type` and `Trait` are private.
953 // Ideally, associated types should be substituted in the same way as
954 // free type aliases, but this isn't done yet.
958 Some(proj
.trait_ref
.def_id
)
963 if let Some(def_id
) = ty_def_id
{
964 // Non-local means public (private items can't leave their crate, modulo bugs)
965 if let Some(node_id
) = self.tcx
.hir
.as_local_node_id(def_id
) {
966 let item
= self.tcx
.hir
.expect_item(node_id
);
967 let vis
= ty
::Visibility
::from_hir(&item
.vis
, node_id
, self.tcx
);
969 if !vis
.is_at_least(self.min_visibility
, self.tcx
) {
970 self.min_visibility
= vis
;
972 if !vis
.is_at_least(self.required_visibility
, self.tcx
) {
973 if self.tcx
.sess
.features
.borrow().pub_restricted
|| self.has_old_errors
{
974 let mut err
= struct_span_err
!(self.tcx
.sess
, self.span
, E0446
,
975 "private type `{}` in public interface", ty
);
976 err
.span_label(self.span
, &format
!("can't leak private type"));
979 self.tcx
.sess
.add_lint(lint
::builtin
::PRIVATE_IN_PUBLIC
,
982 format
!("private type `{}` in public \
983 interface (error E0446)", ty
));
989 if let ty
::TyProjection(ref proj
) = ty
.sty
{
990 // Avoid calling `visit_trait_ref` below on the trait,
991 // as we have already checked the trait itself above.
992 proj
.trait_ref
.super_visit_with(self)
994 ty
.super_visit_with(self)
998 fn visit_trait_ref(&mut self, trait_ref
: ty
::TraitRef
<'tcx
>) -> bool
{
999 // Non-local means public (private items can't leave their crate, modulo bugs)
1000 if let Some(node_id
) = self.tcx
.hir
.as_local_node_id(trait_ref
.def_id
) {
1001 let item
= self.tcx
.hir
.expect_item(node_id
);
1002 let vis
= ty
::Visibility
::from_hir(&item
.vis
, node_id
, self.tcx
);
1004 if !vis
.is_at_least(self.min_visibility
, self.tcx
) {
1005 self.min_visibility
= vis
;
1007 if !vis
.is_at_least(self.required_visibility
, self.tcx
) {
1008 if self.tcx
.sess
.features
.borrow().pub_restricted
|| self.has_old_errors
{
1009 struct_span_err
!(self.tcx
.sess
, self.span
, E0445
,
1010 "private trait `{}` in public interface", trait_ref
)
1011 .span_label(self.span
, &format
!(
1012 "private trait can't be public"))
1015 self.tcx
.sess
.add_lint(lint
::builtin
::PRIVATE_IN_PUBLIC
,
1018 format
!("private trait `{}` in public \
1019 interface (error E0445)", trait_ref
));
1024 trait_ref
.super_visit_with(self)
1028 struct PrivateItemsInPublicInterfacesVisitor
<'a
, 'tcx
: 'a
> {
1029 tcx
: TyCtxt
<'a
, 'tcx
, 'tcx
>,
1030 old_error_set
: &'a NodeSet
,
1031 inner_visibility
: ty
::Visibility
,
1034 impl<'a
, 'tcx
> PrivateItemsInPublicInterfacesVisitor
<'a
, 'tcx
> {
1035 fn check(&self, item_id
: ast
::NodeId
, required_visibility
: ty
::Visibility
)
1036 -> SearchInterfaceForPrivateItemsVisitor
<'a
, 'tcx
> {
1037 let mut has_old_errors
= false;
1039 // Slow path taken only if there any errors in the crate.
1040 for &id
in self.old_error_set
{
1041 // Walk up the nodes until we find `item_id` (or we hit a root).
1045 has_old_errors
= true;
1048 let parent
= self.tcx
.hir
.get_parent_node(id
);
1060 SearchInterfaceForPrivateItemsVisitor
{
1062 item_def_id
: self.tcx
.hir
.local_def_id(item_id
),
1063 span
: self.tcx
.hir
.span(item_id
),
1064 min_visibility
: ty
::Visibility
::Public
,
1065 required_visibility
: required_visibility
,
1066 has_old_errors
: has_old_errors
,
1071 impl<'a
, 'tcx
> Visitor
<'tcx
> for PrivateItemsInPublicInterfacesVisitor
<'a
, 'tcx
> {
1072 fn nested_visit_map
<'this
>(&'this
mut self) -> NestedVisitorMap
<'this
, 'tcx
> {
1073 NestedVisitorMap
::OnlyBodies(&self.tcx
.hir
)
1076 fn visit_item(&mut self, item
: &'tcx hir
::Item
) {
1078 let min
= |vis1
: ty
::Visibility
, vis2
| {
1079 if vis1
.is_at_least(vis2
, tcx
) { vis2 }
else { vis1 }
1082 let item_visibility
= ty
::Visibility
::from_hir(&item
.vis
, item
.id
, tcx
);
1085 // Crates are always public
1086 hir
::ItemExternCrate(..) => {}
1087 // All nested items are checked by visit_item
1088 hir
::ItemMod(..) => {}
1089 // Checked in resolve
1090 hir
::ItemUse(..) => {}
1091 // Subitems of these items have inherited publicity
1092 hir
::ItemConst(..) | hir
::ItemStatic(..) | hir
::ItemFn(..) |
1093 hir
::ItemTy(..) => {
1094 self.check(item
.id
, item_visibility
).generics().predicates().item_type();
1096 // Recurse for e.g. `impl Trait` (see `visit_ty`).
1097 self.inner_visibility
= item_visibility
;
1098 intravisit
::walk_item(self, item
);
1100 hir
::ItemTrait(.., ref trait_item_refs
) => {
1101 self.check(item
.id
, item_visibility
).generics().predicates();
1103 for trait_item_ref
in trait_item_refs
{
1104 let mut check
= self.check(trait_item_ref
.id
.node_id
, item_visibility
);
1105 check
.generics().predicates();
1107 if trait_item_ref
.kind
== hir
::AssociatedItemKind
::Type
&&
1108 !trait_item_ref
.defaultness
.has_value() {
1109 // No type to visit.
1115 hir
::ItemEnum(ref def
, _
) => {
1116 self.check(item
.id
, item_visibility
).generics().predicates();
1118 for variant
in &def
.variants
{
1119 for field
in variant
.node
.data
.fields() {
1120 self.check(field
.id
, item_visibility
).item_type();
1124 // Subitems of foreign modules have their own publicity
1125 hir
::ItemForeignMod(ref foreign_mod
) => {
1126 for foreign_item
in &foreign_mod
.items
{
1127 let vis
= ty
::Visibility
::from_hir(&foreign_item
.vis
, item
.id
, tcx
);
1128 self.check(foreign_item
.id
, vis
).generics().predicates().item_type();
1131 // Subitems of structs and unions have their own publicity
1132 hir
::ItemStruct(ref struct_def
, _
) |
1133 hir
::ItemUnion(ref struct_def
, _
) => {
1134 self.check(item
.id
, item_visibility
).generics().predicates();
1136 for field
in struct_def
.fields() {
1137 let field_visibility
= ty
::Visibility
::from_hir(&field
.vis
, item
.id
, tcx
);
1138 self.check(field
.id
, min(item_visibility
, field_visibility
)).item_type();
1141 // The interface is empty
1142 hir
::ItemDefaultImpl(..) => {}
1143 // An inherent impl is public when its type is public
1144 // Subitems of inherent impls have their own publicity
1145 hir
::ItemImpl(.., None
, _
, ref impl_item_refs
) => {
1147 self.check(item
.id
, ty
::Visibility
::Invisible
).item_type().min_visibility
;
1148 self.check(item
.id
, ty_vis
).generics().predicates();
1150 for impl_item_ref
in impl_item_refs
{
1151 let impl_item
= self.tcx
.hir
.impl_item(impl_item_ref
.id
);
1153 ty
::Visibility
::from_hir(&impl_item
.vis
, item
.id
, tcx
);
1154 self.check(impl_item
.id
, min(impl_item_vis
, ty_vis
))
1155 .generics().predicates().item_type();
1157 // Recurse for e.g. `impl Trait` (see `visit_ty`).
1158 self.inner_visibility
= impl_item_vis
;
1159 intravisit
::walk_impl_item(self, impl_item
);
1162 // A trait impl is public when both its type and its trait are public
1163 // Subitems of trait impls have inherited publicity
1164 hir
::ItemImpl(.., Some(_
), _
, ref impl_item_refs
) => {
1165 let vis
= self.check(item
.id
, ty
::Visibility
::Invisible
)
1166 .item_type().impl_trait_ref().min_visibility
;
1167 self.check(item
.id
, vis
).generics().predicates();
1168 for impl_item_ref
in impl_item_refs
{
1169 let impl_item
= self.tcx
.hir
.impl_item(impl_item_ref
.id
);
1170 self.check(impl_item
.id
, vis
).generics().predicates().item_type();
1172 // Recurse for e.g. `impl Trait` (see `visit_ty`).
1173 self.inner_visibility
= vis
;
1174 intravisit
::walk_impl_item(self, impl_item
);
1180 fn visit_impl_item(&mut self, _impl_item
: &'tcx hir
::ImplItem
) {
1181 // handled in `visit_item` above
1184 fn visit_ty(&mut self, ty
: &'tcx hir
::Ty
) {
1185 if let hir
::TyImplTrait(..) = ty
.node
{
1186 // Check the traits being exposed, as they're separate,
1187 // e.g. `impl Iterator<Item=T>` has two predicates,
1188 // `X: Iterator` and `<X as Iterator>::Item == T`,
1189 // where `X` is the `impl Iterator<Item=T>` itself,
1190 // stored in `item_predicates`, not in the `Ty` itself.
1191 self.check(ty
.id
, self.inner_visibility
).predicates();
1194 intravisit
::walk_ty(self, ty
);
1197 // Don't recurse into expressions in array sizes or const initializers
1198 fn visit_expr(&mut self, _
: &'tcx hir
::Expr
) {}
1199 // Don't recurse into patterns in function arguments
1200 fn visit_pat(&mut self, _
: &'tcx hir
::Pat
) {}
1203 pub fn check_crate
<'a
, 'tcx
>(tcx
: TyCtxt
<'a
, 'tcx
, 'tcx
>,
1204 export_map
: &def
::ExportMap
)
1206 let _task
= tcx
.dep_graph
.in_task(DepNode
::Privacy
);
1208 let krate
= tcx
.hir
.krate();
1210 // Use the parent map to check the privacy of everything
1211 let mut visitor
= PrivacyVisitor
{
1212 curitem
: DefId
::local(CRATE_DEF_INDEX
),
1215 tables
: &ty
::TypeckTables
::empty(),
1217 intravisit
::walk_crate(&mut visitor
, krate
);
1219 tcx
.sess
.abort_if_errors();
1221 // Build up a set of all exported items in the AST. This is a set of all
1222 // items which are reachable from external crates based on visibility.
1223 let mut visitor
= EmbargoVisitor
{
1225 export_map
: export_map
,
1226 access_levels
: Default
::default(),
1227 prev_level
: Some(AccessLevel
::Public
),
1231 intravisit
::walk_crate(&mut visitor
, krate
);
1232 if visitor
.changed
{
1233 visitor
.changed
= false;
1238 visitor
.update(ast
::CRATE_NODE_ID
, Some(AccessLevel
::Public
));
1241 let mut visitor
= ObsoleteVisiblePrivateTypesVisitor
{
1243 access_levels
: &visitor
.access_levels
,
1245 old_error_set
: NodeSet(),
1247 intravisit
::walk_crate(&mut visitor
, krate
);
1249 // Check for private types and traits in public interfaces
1250 let mut visitor
= PrivateItemsInPublicInterfacesVisitor
{
1252 old_error_set
: &visitor
.old_error_set
,
1253 inner_visibility
: ty
::Visibility
::Public
,
1255 krate
.visit_all_item_likes(&mut DeepVisitor
::new(&mut visitor
));
1258 visitor
.access_levels
1261 __build_diagnostic_array
! { librustc_privacy, DIAGNOSTICS }