1 // Copyright 2012-2013 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 //! A pass that checks to make sure private fields and methods aren't used
12 //! outside their scopes. This pass will also generate a set of exported items
13 //! which are available for use externally when compiled as a library.
16 use std::mem::replace;
18 use metadata::csearch;
23 use middle::typeck::{MethodCall, MethodMap, MethodOrigin, MethodParam};
24 use middle::typeck::{MethodStatic, MethodStaticUnboxedClosure, MethodObject};
25 use util::nodemap::{NodeMap, NodeSet};
29 use syntax::ast_util::{is_local, local_def, PostExpansionMethod};
30 use syntax::codemap::Span;
31 use syntax::parse::token;
32 use syntax::owned_slice::OwnedSlice;
34 use syntax::visit::Visitor;
36 type Context<'a> = (&'a MethodMap, &'a resolve::ExportMap2);
38 /// A set of AST nodes exported by the crate.
39 pub type ExportedItems = NodeSet;
41 /// A set of AST nodes that are fully public in the crate. This map is used for
42 /// documentation purposes (reexporting a private struct inlines the doc,
43 /// reexporting a public struct doesn't inline the doc).
44 pub type PublicItems = NodeSet;
46 /// Result of a checking operation - None => no errors were found. Some => an
47 /// error and contains the span and message for reporting that error and
48 /// optionally the same for a note about the error.
49 type CheckResult = Option<(Span, String, Option<(Span, String)>)>;
51 ////////////////////////////////////////////////////////////////////////////////
52 /// The parent visitor, used to determine what's the parent of what (node-wise)
53 ////////////////////////////////////////////////////////////////////////////////
55 struct ParentVisitor {
56 parents: NodeMap<ast::NodeId>,
57 curparent: ast::NodeId,
60 impl Visitor<()> for ParentVisitor {
61 fn visit_item(&mut self, item: &ast::Item, _: ()) {
62 self.parents.insert(item.id, self.curparent);
64 let prev = self.curparent;
66 ast::ItemMod(..) => { self.curparent = item.id; }
67 // Enum variants are parented to the enum definition itself because
68 // they inherit privacy
69 ast::ItemEnum(ref def, _) => {
70 for variant in def.variants.iter() {
71 // The parent is considered the enclosing enum because the
72 // enum will dictate the privacy visibility of this variant
74 self.parents.insert(variant.node.id, item.id);
78 // Trait methods are always considered "public", but if the trait is
79 // private then we need some private item in the chain from the
80 // method to the root. In this case, if the trait is private, then
81 // parent all the methods to the trait to indicate that they're
83 ast::ItemTrait(_, _, _, ref methods) if item.vis != ast::Public => {
84 for m in methods.iter() {
86 ast::ProvidedMethod(ref m) => self.parents.insert(m.id, item.id),
87 ast::RequiredMethod(ref m) => self.parents.insert(m.id, item.id),
94 visit::walk_item(self, item, ());
95 self.curparent = prev;
98 fn visit_foreign_item(&mut self, a: &ast::ForeignItem, _: ()) {
99 self.parents.insert(a.id, self.curparent);
100 visit::walk_foreign_item(self, a, ());
103 fn visit_fn(&mut self, a: &visit::FnKind, b: &ast::FnDecl,
104 c: &ast::Block, d: Span, id: ast::NodeId, _: ()) {
105 // We already took care of some trait methods above, otherwise things
106 // like impl methods and pub trait methods are parented to the
107 // containing module, not the containing trait.
108 if !self.parents.contains_key(&id) {
109 self.parents.insert(id, self.curparent);
111 visit::walk_fn(self, a, b, c, d, ());
114 fn visit_struct_def(&mut self, s: &ast::StructDef, _: ast::Ident,
115 _: &ast::Generics, n: ast::NodeId, _: ()) {
116 // Struct constructors are parented to their struct definitions because
117 // they essentially are the struct definitions.
119 Some(id) => { self.parents.insert(id, n); }
123 // While we have the id of the struct definition, go ahead and parent
125 for field in s.fields.iter() {
126 self.parents.insert(field.node.id, self.curparent);
128 visit::walk_struct_def(self, s, ())
132 ////////////////////////////////////////////////////////////////////////////////
133 /// The embargo visitor, used to determine the exports of the ast
134 ////////////////////////////////////////////////////////////////////////////////
136 struct EmbargoVisitor<'a, 'tcx: 'a> {
137 tcx: &'a ty::ctxt<'tcx>,
138 exp_map2: &'a resolve::ExportMap2,
140 // This flag is an indicator of whether the previous item in the
141 // hierarchical chain was exported or not. This is the indicator of whether
142 // children should be exported as well. Note that this can flip from false
143 // to true if a reexported module is entered (or an action similar).
146 // This is a list of all exported items in the AST. An exported item is any
147 // function/method/item which is usable by external crates. This essentially
148 // means that the result is "public all the way down", but the "path down"
149 // may jump across private boundaries through reexport statements.
150 exported_items: ExportedItems,
152 // This sets contains all the destination nodes which are publicly
153 // re-exported. This is *not* a set of all reexported nodes, only a set of
154 // all nodes which are reexported *and* reachable from external crates. This
155 // means that the destination of the reexport is exported, and hence the
156 // destination must also be exported.
159 // These two fields are closely related to one another in that they are only
160 // used for generation of the 'PublicItems' set, not for privacy checking at
162 public_items: PublicItems,
166 impl<'a, 'tcx> EmbargoVisitor<'a, 'tcx> {
167 // There are checks inside of privacy which depend on knowing whether a
168 // trait should be exported or not. The two current consumers of this are:
170 // 1. Should default methods of a trait be exported?
171 // 2. Should the methods of an implementation of a trait be exported?
173 // The answer to both of these questions partly rely on whether the trait
174 // itself is exported or not. If the trait is somehow exported, then the
175 // answers to both questions must be yes. Right now this question involves
176 // more analysis than is currently done in rustc, so we conservatively
177 // answer "yes" so that all traits need to be exported.
178 fn exported_trait(&self, _id: ast::NodeId) -> bool {
183 impl<'a, 'tcx> Visitor<()> for EmbargoVisitor<'a, 'tcx> {
184 fn visit_item(&mut self, item: &ast::Item, _: ()) {
185 let orig_all_pub = self.prev_public;
186 self.prev_public = orig_all_pub && item.vis == ast::Public;
187 if self.prev_public {
188 self.public_items.insert(item.id);
191 let orig_all_exported = self.prev_exported;
193 // impls/extern blocks do not break the "public chain" because they
194 // cannot have visibility qualifiers on them anyway
195 ast::ItemImpl(..) | ast::ItemForeignMod(..) => {}
197 // Traits are a little special in that even if they themselves are
198 // not public they may still be exported.
199 ast::ItemTrait(..) => {
200 self.prev_exported = self.exported_trait(item.id);
203 // Private by default, hence we only retain the "public chain" if
204 // `pub` is explicitly listed.
207 (orig_all_exported && item.vis == ast::Public) ||
208 self.reexports.contains(&item.id);
212 let public_first = self.prev_exported &&
213 self.exported_items.insert(item.id);
216 // Enum variants inherit from their parent, so if the enum is
217 // public all variants are public unless they're explicitly priv
218 ast::ItemEnum(ref def, _) if public_first => {
219 for variant in def.variants.iter() {
220 self.exported_items.insert(variant.node.id);
224 // Implementations are a little tricky to determine what's exported
225 // out of them. Here's a few cases which are currently defined:
227 // * Impls for private types do not need to export their methods
228 // (either public or private methods)
230 // * Impls for public types only have public methods exported
232 // * Public trait impls for public types must have all methods
235 // * Private trait impls for public types can be ignored
237 // * Public trait impls for private types have their methods
238 // exported. I'm not entirely certain that this is the correct
239 // thing to do, but I have seen use cases of where this will cause
240 // undefined symbols at linkage time if this case is not handled.
242 // * Private trait impls for private types can be completely ignored
243 ast::ItemImpl(_, _, ref ty, ref impl_items) => {
244 let public_ty = match ty.node {
245 ast::TyPath(_, _, id) => {
246 match self.tcx.def_map.borrow().get_copy(&id) {
247 def::DefPrimTy(..) => true,
249 let did = def.def_id();
251 self.exported_items.contains(&did.node)
257 let tr = ty::impl_trait_ref(self.tcx, local_def(item.id));
258 let public_trait = tr.clone().map_or(false, |tr| {
259 !is_local(tr.def_id) ||
260 self.exported_items.contains(&tr.def_id.node)
263 if public_ty || public_trait {
264 for impl_item in impl_items.iter() {
266 ast::MethodImplItem(method) => {
268 match method.pe_explicit_self().node {
269 ast::SelfStatic => public_ty,
271 } && method.pe_vis() == ast::Public;
272 if meth_public || tr.is_some() {
273 self.exported_items.insert(method.id);
281 // Default methods on traits are all public so long as the trait
283 ast::ItemTrait(_, _, _, ref methods) if public_first => {
284 for method in methods.iter() {
286 ast::ProvidedMethod(ref m) => {
287 debug!("provided {}", m.id);
288 self.exported_items.insert(m.id);
290 ast::RequiredMethod(ref m) => {
291 debug!("required {}", m.id);
292 self.exported_items.insert(m.id);
298 // Struct constructors are public if the struct is all public.
299 ast::ItemStruct(ref def, _) if public_first => {
301 Some(id) => { self.exported_items.insert(id); }
306 ast::ItemTy(ref ty, _) if public_first => {
308 ast::TyPath(_, _, id) => {
309 match self.tcx.def_map.borrow().get_copy(&id) {
310 def::DefPrimTy(..) | def::DefTyParam(..) => {},
312 let did = def.def_id();
314 self.exported_items.insert(did.node);
326 visit::walk_item(self, item, ());
328 self.prev_exported = orig_all_exported;
329 self.prev_public = orig_all_pub;
332 fn visit_foreign_item(&mut self, a: &ast::ForeignItem, _: ()) {
333 if (self.prev_exported && a.vis == ast::Public) || self.reexports.contains(&a.id) {
334 self.exported_items.insert(a.id);
338 fn visit_mod(&mut self, m: &ast::Mod, _sp: Span, id: ast::NodeId, _: ()) {
339 // This code is here instead of in visit_item so that the
340 // crate module gets processed as well.
341 if self.prev_exported {
342 let exp_map2 = self.exp_map2.borrow();
343 assert!(exp_map2.contains_key(&id), "wut {:?}", id);
344 for export in exp_map2.get(&id).iter() {
345 if is_local(export.def_id) {
346 self.reexports.insert(export.def_id.node);
350 visit::walk_mod(self, m, ())
354 ////////////////////////////////////////////////////////////////////////////////
355 /// The privacy visitor, where privacy checks take place (violations reported)
356 ////////////////////////////////////////////////////////////////////////////////
358 struct PrivacyVisitor<'a, 'tcx: 'a> {
359 tcx: &'a ty::ctxt<'tcx>,
360 curitem: ast::NodeId,
362 parents: NodeMap<ast::NodeId>,
363 external_exports: resolve::ExternalExports,
364 last_private_map: resolve::LastPrivateMap,
370 DisallowedBy(ast::NodeId),
374 UnnamedField(uint), // index
375 // FIXME #6993: change type (and name) from Ident to Name
376 NamedField(ast::Ident),
379 impl<'a, 'tcx> PrivacyVisitor<'a, 'tcx> {
380 // used when debugging
381 fn nodestr(&self, id: ast::NodeId) -> String {
382 self.tcx.map.node_to_string(id).to_string()
385 // Determines whether the given definition is public from the point of view
386 // of the current item.
387 fn def_privacy(&self, did: ast::DefId) -> PrivacyResult {
389 if self.external_exports.contains(&did) {
390 debug!("privacy - {:?} was externally exported", did);
393 debug!("privacy - is {:?} a public method", did);
395 return match self.tcx.impl_or_trait_items.borrow().find(&did) {
396 Some(&ty::MethodTraitItem(ref meth)) => {
397 debug!("privacy - well at least it's a method: {:?}",
399 match meth.container {
400 ty::TraitContainer(id) => {
401 debug!("privacy - recursing on trait {:?}", id);
404 ty::ImplContainer(id) => {
405 match ty::impl_trait_ref(self.tcx, id) {
407 debug!("privacy - impl of trait {:?}", id);
408 self.def_privacy(t.def_id)
411 debug!("privacy - found a method {:?}",
413 if meth.vis == ast::Public {
424 debug!("privacy - nope, not even a method");
430 debug!("privacy - local {} not public all the way down",
431 self.tcx.map.node_to_string(did.node));
432 // return quickly for things in the same module
433 if self.parents.find(&did.node) == self.parents.find(&self.curitem) {
434 debug!("privacy - same parent, we're done here");
438 // We now know that there is at least one private member between the
439 // destination and the root.
440 let mut closest_private_id = did.node;
442 debug!("privacy - examining {}", self.nodestr(closest_private_id));
443 let vis = match self.tcx.map.find(closest_private_id) {
444 // If this item is a method, then we know for sure that it's an
445 // actual method and not a static method. The reason for this is
446 // that these cases are only hit in the ExprMethodCall
447 // expression, and ExprCall will have its path checked later
448 // (the path of the trait/impl) if it's a static method.
450 // With this information, then we can completely ignore all
451 // trait methods. The privacy violation would be if the trait
452 // couldn't get imported, not if the method couldn't be used
453 // (all trait methods are public).
455 // However, if this is an impl method, then we dictate this
456 // decision solely based on the privacy of the method
458 // FIXME(#10573) is this the right behavior? Why not consider
459 // where the method was defined?
460 Some(ast_map::NodeImplItem(ref ii)) => {
462 ast::MethodImplItem(m) => {
465 .get_parent_did(closest_private_id);
466 match ty::impl_trait_ref(self.tcx, imp) {
467 Some(..) => return Allowable,
468 _ if m.pe_vis() == ast::Public => {
476 Some(ast_map::NodeTraitItem(_)) => {
480 // This is not a method call, extract the visibility as one
481 // would normally look at it
482 Some(ast_map::NodeItem(it)) => it.vis,
483 Some(ast_map::NodeForeignItem(_)) => {
484 self.tcx.map.get_foreign_vis(closest_private_id)
486 Some(ast_map::NodeVariant(..)) => {
487 ast::Public // need to move up a level (to the enum)
491 if vis != ast::Public { break }
492 // if we've reached the root, then everything was allowable and this
494 if closest_private_id == ast::CRATE_NODE_ID { return Allowable }
495 closest_private_id = *self.parents.get(&closest_private_id);
497 // If we reached the top, then we were public all the way down and
498 // we can allow this access.
499 if closest_private_id == ast::DUMMY_NODE_ID { return Allowable }
501 debug!("privacy - closest priv {}", self.nodestr(closest_private_id));
502 if self.private_accessible(closest_private_id) {
505 DisallowedBy(closest_private_id)
509 /// For a local private node in the AST, this function will determine
510 /// whether the node is accessible by the current module that iteration is
512 fn private_accessible(&self, id: ast::NodeId) -> bool {
513 let parent = *self.parents.get(&id);
514 debug!("privacy - accessible parent {}", self.nodestr(parent));
516 // After finding `did`'s closest private member, we roll ourselves back
517 // to see if this private member's parent is anywhere in our ancestry.
518 // By the privacy rules, we can access all of our ancestor's private
519 // members, so that's why we test the parent, and not the did itself.
520 let mut cur = self.curitem;
522 debug!("privacy - questioning {}, {:?}", self.nodestr(cur), cur);
524 // If the relevant parent is in our history, then we're allowed
525 // to look inside any of our ancestor's immediate private items,
526 // so this access is valid.
527 x if x == parent => return true,
529 // If we've reached the root, then we couldn't access this item
530 // in the first place
531 ast::DUMMY_NODE_ID => return false,
537 cur = *self.parents.get(&cur);
541 fn report_error(&self, result: CheckResult) -> bool {
544 Some((span, msg, note)) => {
545 self.tcx.sess.span_err(span, msg.as_slice());
547 Some((span, msg)) => {
548 self.tcx.sess.span_note(span, msg.as_slice())
557 /// Guarantee that a particular definition is public. Returns a CheckResult
558 /// which contains any errors found. These can be reported using `report_error`.
559 /// If the result is `None`, no errors were found.
560 fn ensure_public(&self, span: Span, to_check: ast::DefId,
561 source_did: Option<ast::DefId>, msg: &str) -> CheckResult {
562 let id = match self.def_privacy(to_check) {
563 ExternallyDenied => {
564 return Some((span, format!("{} is private", msg), None))
566 Allowable => return None,
567 DisallowedBy(id) => id,
570 // If we're disallowed by a particular id, then we attempt to give a
571 // nice error message to say why it was disallowed. It was either
572 // because the item itself is private or because its parent is private
573 // and its parent isn't in our ancestry.
574 let (err_span, err_msg) = if id == source_did.unwrap_or(to_check).node {
575 return Some((span, format!("{} is private", msg), None));
577 (span, format!("{} is inaccessible", msg))
579 let item = match self.tcx.map.find(id) {
580 Some(ast_map::NodeItem(item)) => {
582 // If an impl disallowed this item, then this is resolve's
583 // way of saying that a struct/enum's static method was
584 // invoked, and the struct/enum itself is private. Crawl
585 // back up the chains to find the relevant struct/enum that
587 ast::ItemImpl(_, _, ref ty, _) => {
588 let id = match ty.node {
589 ast::TyPath(_, _, id) => id,
590 _ => return Some((err_span, err_msg, None)),
592 let def = self.tcx.def_map.borrow().get_copy(&id);
593 let did = def.def_id();
594 assert!(is_local(did));
595 match self.tcx.map.get(did.node) {
596 ast_map::NodeItem(item) => item,
597 _ => self.tcx.sess.span_bug(item.span,
598 "path is not an item")
604 Some(..) | None => return Some((err_span, err_msg, None)),
606 let desc = match item.node {
607 ast::ItemMod(..) => "module",
608 ast::ItemTrait(..) => "trait",
609 ast::ItemStruct(..) => "struct",
610 ast::ItemEnum(..) => "enum",
611 _ => return Some((err_span, err_msg, None))
613 let msg = format!("{} `{}` is private", desc,
614 token::get_ident(item.ident));
615 Some((err_span, err_msg, Some((span, msg))))
618 // Checks that a field is in scope.
619 fn check_field(&mut self,
623 let fields = ty::lookup_struct_fields(self.tcx, id);
624 let field = match name {
625 NamedField(ident) => {
626 debug!("privacy - check named field {} in struct {}", ident.name, id);
627 fields.iter().find(|f| f.name == ident.name).unwrap()
629 UnnamedField(idx) => fields.get(idx)
631 if field.vis == ast::Public ||
632 (is_local(field.id) && self.private_accessible(field.id.node)) {
636 let struct_type = ty::lookup_item_type(self.tcx, id).ty;
637 let struct_desc = match ty::get(struct_type).sty {
638 ty::ty_struct(_, _) => format!("struct `{}`", ty::item_path_str(self.tcx, id)),
639 ty::ty_bare_fn(ty::BareFnTy { sig: ty::FnSig { output, .. }, .. }) => {
640 // Struct `id` is really a struct variant of an enum,
641 // and we're really looking at the variant's constructor
642 // function. So get the return type for a detailed error
644 let enum_id = match ty::get(output).sty {
645 ty::ty_enum(id, _) => id,
646 _ => self.tcx.sess.span_bug(span, "enum variant doesn't \
649 format!("variant `{}` of enum `{}`",
650 ty::with_path(self.tcx, id, |mut p| p.last().unwrap()),
651 ty::item_path_str(self.tcx, enum_id))
653 _ => self.tcx.sess.span_bug(span, "can't find struct for field")
655 let msg = match name {
656 NamedField(name) => format!("field `{}` of {} is private",
657 token::get_ident(name), struct_desc),
658 UnnamedField(idx) => format!("field #{} of {} is private",
659 idx + 1, struct_desc),
661 self.tcx.sess.span_err(span, msg.as_slice());
664 // Given the ID of a method, checks to ensure it's in scope.
665 fn check_static_method(&mut self,
667 method_id: ast::DefId,
669 // If the method is a default method, we need to use the def_id of
670 // the default implementation.
671 let method_id = match ty::impl_or_trait_item(self.tcx, method_id) {
672 ty::MethodTraitItem(method_type) => {
673 method_type.provided_source.unwrap_or(method_id)
677 let string = token::get_ident(name);
678 self.report_error(self.ensure_public(span,
681 format!("method `{}`",
682 string).as_slice()));
685 // Checks that a path is in scope.
686 fn check_path(&mut self, span: Span, path_id: ast::NodeId, path: &ast::Path) {
687 debug!("privacy - path {}", self.nodestr(path_id));
688 let orig_def = self.tcx.def_map.borrow().get_copy(&path_id);
689 let ck = |tyname: &str| {
690 let ck_public = |def: ast::DefId| {
691 let name = token::get_ident(path.segments
695 let origdid = orig_def.def_id();
696 self.ensure_public(span,
704 match *self.last_private_map.get(&path_id) {
705 resolve::LastMod(resolve::AllPublic) => {},
706 resolve::LastMod(resolve::DependsOn(def)) => {
707 self.report_error(ck_public(def));
709 resolve::LastImport{value_priv: value_priv,
710 value_used: check_value,
711 type_priv: type_priv,
712 type_used: check_type} => {
713 // This dance with found_error is because we don't want to report
714 // a privacy error twice for the same directive.
715 let found_error = match (type_priv, check_type) {
716 (Some(resolve::DependsOn(def)), resolve::Used) => {
717 !self.report_error(ck_public(def))
722 match (value_priv, check_value) {
723 (Some(resolve::DependsOn(def)), resolve::Used) => {
724 self.report_error(ck_public(def));
729 // If an import is not used in either namespace, we still
730 // want to check that it could be legal. Therefore we check
731 // in both namespaces and only report an error if both would
732 // be illegal. We only report one error, even if it is
733 // illegal to import from both namespaces.
734 match (value_priv, check_value, type_priv, check_type) {
735 (Some(p), resolve::Unused, None, _) |
736 (None, _, Some(p), resolve::Unused) => {
738 resolve::AllPublic => None,
739 resolve::DependsOn(def) => ck_public(def),
742 self.report_error(p);
745 (Some(v), resolve::Unused, Some(t), resolve::Unused) => {
747 resolve::AllPublic => None,
748 resolve::DependsOn(def) => ck_public(def),
751 resolve::AllPublic => None,
752 resolve::DependsOn(def) => ck_public(def),
755 (Some(_), Some(t)) => {
756 self.report_error(Some(t));
766 // FIXME(#12334) Imports can refer to definitions in both the type and
767 // value namespaces. The privacy information is aware of this, but the
768 // def map is not. Therefore the names we work out below will not always
769 // be accurate and we can get slightly wonky error messages (but type
770 // checking is always correct).
771 match self.tcx.def_map.borrow().get_copy(&path_id) {
772 def::DefStaticMethod(..) => ck("static method"),
773 def::DefFn(..) => ck("function"),
774 def::DefStatic(..) => ck("static"),
775 def::DefVariant(..) => ck("variant"),
776 def::DefTy(..) => ck("type"),
777 def::DefTrait(..) => ck("trait"),
778 def::DefStruct(..) => ck("struct"),
779 def::DefMethod(_, Some(..)) => ck("trait method"),
780 def::DefMethod(..) => ck("method"),
781 def::DefMod(..) => ck("module"),
786 // Checks that a method is in scope.
787 fn check_method(&mut self, span: Span, origin: MethodOrigin,
790 MethodStatic(method_id) => {
791 self.check_static_method(span, method_id, ident)
793 MethodStaticUnboxedClosure(_) => {}
794 // Trait methods are always all public. The only controlling factor
795 // is whether the trait itself is accessible or not.
796 MethodParam(MethodParam { trait_id: trait_id, .. }) |
797 MethodObject(MethodObject { trait_id: trait_id, .. }) => {
798 self.report_error(self.ensure_public(span, trait_id, None,
805 impl<'a, 'tcx> Visitor<()> for PrivacyVisitor<'a, 'tcx> {
806 fn visit_item(&mut self, item: &ast::Item, _: ()) {
807 let orig_curitem = replace(&mut self.curitem, item.id);
808 visit::walk_item(self, item, ());
809 self.curitem = orig_curitem;
812 fn visit_expr(&mut self, expr: &ast::Expr, _: ()) {
814 ast::ExprField(ref base, ident, _) => {
815 match ty::get(ty::expr_ty_adjusted(self.tcx, &**base)).sty {
816 ty::ty_struct(id, _) => {
817 self.check_field(expr.span, id, NamedField(ident.node));
822 ast::ExprTupField(ref base, idx, _) => {
823 match ty::get(ty::expr_ty_adjusted(self.tcx, &**base)).sty {
824 ty::ty_struct(id, _) => {
825 self.check_field(expr.span, id, UnnamedField(idx.node));
830 ast::ExprMethodCall(ident, _, _) => {
831 let method_call = MethodCall::expr(expr.id);
832 match self.tcx.method_map.borrow().find(&method_call) {
834 self.tcx.sess.span_bug(expr.span,
835 "method call not in \
839 debug!("(privacy checking) checking impl method");
840 self.check_method(expr.span, method.origin, ident.node);
844 ast::ExprStruct(_, ref fields, _) => {
845 match ty::get(ty::expr_ty(self.tcx, expr)).sty {
846 ty::ty_struct(id, _) => {
847 for field in (*fields).iter() {
848 self.check_field(expr.span, id,
849 NamedField(field.ident.node));
852 ty::ty_enum(_, _) => {
853 match self.tcx.def_map.borrow().get_copy(&expr.id) {
854 def::DefVariant(_, variant_id, _) => {
855 for field in fields.iter() {
856 self.check_field(expr.span, variant_id,
857 NamedField(field.ident.node));
860 _ => self.tcx.sess.span_bug(expr.span,
867 _ => self.tcx.sess.span_bug(expr.span, "struct expr \
872 ast::ExprPath(..) => {
873 let guard = |did: ast::DefId| {
874 let fields = ty::lookup_struct_fields(self.tcx, did);
875 let any_priv = fields.iter().any(|f| {
876 f.vis != ast::Public && (
878 !self.private_accessible(f.id.node))
881 self.tcx.sess.span_err(expr.span,
882 "cannot invoke tuple struct constructor \
883 with private fields");
886 match self.tcx.def_map.borrow().find(&expr.id) {
887 Some(&def::DefStruct(did)) => {
888 guard(if is_local(did) {
889 local_def(self.tcx.map.get_parent(did.node))
891 // "tuple structs" with zero fields (such as
892 // `pub struct Foo;`) don't have a ctor_id, hence
893 // the unwrap_or to the same struct id.
895 csearch::get_tuple_struct_definition_if_ctor(
896 &self.tcx.sess.cstore, did);
897 maybe_did.unwrap_or(did)
900 // Tuple struct constructors across crates are identified as
901 // DefFn types, so we explicitly handle that case here.
902 Some(&def::DefFn(did, _)) if !is_local(did) => {
903 match csearch::get_tuple_struct_definition_if_ctor(
904 &self.tcx.sess.cstore, did) {
905 Some(did) => guard(did),
915 visit::walk_expr(self, expr, ());
918 fn visit_view_item(&mut self, a: &ast::ViewItem, _: ()) {
920 ast::ViewItemExternCrate(..) => {}
921 ast::ViewItemUse(ref vpath) => {
923 ast::ViewPathSimple(..) | ast::ViewPathGlob(..) => {}
924 ast::ViewPathList(ref prefix, ref list, _) => {
925 for pid in list.iter() {
927 ast::PathListIdent { id, name } => {
928 debug!("privacy - ident item {}", id);
929 let seg = ast::PathSegment {
931 lifetimes: Vec::new(),
932 types: OwnedSlice::empty(),
934 let segs = vec![seg];
935 let path = ast::Path {
940 self.check_path(pid.span, id, &path);
942 ast::PathListMod { id } => {
943 debug!("privacy - mod item {}", id);
944 self.check_path(pid.span, id, prefix);
952 visit::walk_view_item(self, a, ());
955 fn visit_pat(&mut self, pattern: &ast::Pat, _: ()) {
956 // Foreign functions do not have their patterns mapped in the def_map,
957 // and there's nothing really relevant there anyway, so don't bother
958 // checking privacy. If you can name the type then you can pass it to an
959 // external C function anyway.
960 if self.in_foreign { return }
963 ast::PatStruct(_, ref fields, _) => {
964 match ty::get(ty::pat_ty(self.tcx, pattern)).sty {
965 ty::ty_struct(id, _) => {
966 for field in fields.iter() {
967 self.check_field(pattern.span, id,
968 NamedField(field.ident));
971 ty::ty_enum(_, _) => {
972 match self.tcx.def_map.borrow().find(&pattern.id) {
973 Some(&def::DefVariant(_, variant_id, _)) => {
974 for field in fields.iter() {
975 self.check_field(pattern.span, variant_id,
976 NamedField(field.ident));
979 _ => self.tcx.sess.span_bug(pattern.span,
986 _ => self.tcx.sess.span_bug(pattern.span,
987 "struct pattern didn't have \
992 // Patterns which bind no fields are allowable (the path is check
994 ast::PatEnum(_, Some(ref fields)) => {
995 match ty::get(ty::pat_ty(self.tcx, pattern)).sty {
996 ty::ty_struct(id, _) => {
997 for (i, field) in fields.iter().enumerate() {
999 ast::PatWild(..) => continue,
1002 self.check_field(field.span, id, UnnamedField(i));
1005 ty::ty_enum(..) => {
1006 // enum fields have no privacy at this time
1015 visit::walk_pat(self, pattern, ());
1018 fn visit_foreign_item(&mut self, fi: &ast::ForeignItem, _: ()) {
1019 self.in_foreign = true;
1020 visit::walk_foreign_item(self, fi, ());
1021 self.in_foreign = false;
1024 fn visit_path(&mut self, path: &ast::Path, id: ast::NodeId, _: ()) {
1025 self.check_path(path.span, id, path);
1026 visit::walk_path(self, path, ());
1030 ////////////////////////////////////////////////////////////////////////////////
1031 /// The privacy sanity check visitor, ensures unnecessary visibility isn't here
1032 ////////////////////////////////////////////////////////////////////////////////
1034 struct SanePrivacyVisitor<'a, 'tcx: 'a> {
1035 tcx: &'a ty::ctxt<'tcx>,
1039 impl<'a, 'tcx> Visitor<()> for SanePrivacyVisitor<'a, 'tcx> {
1040 fn visit_item(&mut self, item: &ast::Item, _: ()) {
1042 self.check_all_inherited(item);
1044 self.check_sane_privacy(item);
1047 let in_fn = self.in_fn;
1048 let orig_in_fn = replace(&mut self.in_fn, match item.node {
1049 ast::ItemMod(..) => false, // modules turn privacy back on
1050 _ => in_fn, // otherwise we inherit
1052 visit::walk_item(self, item, ());
1053 self.in_fn = orig_in_fn;
1056 fn visit_fn(&mut self, fk: &visit::FnKind, fd: &ast::FnDecl,
1057 b: &ast::Block, s: Span, _: ast::NodeId, _: ()) {
1058 // This catches both functions and methods
1059 let orig_in_fn = replace(&mut self.in_fn, true);
1060 visit::walk_fn(self, fk, fd, b, s, ());
1061 self.in_fn = orig_in_fn;
1064 fn visit_view_item(&mut self, i: &ast::ViewItem, _: ()) {
1066 ast::Inherited => {}
1069 self.tcx.sess.span_err(i.span, "unnecessary `pub`, imports \
1070 in functions are never \
1074 ast::ViewItemExternCrate(..) => {
1075 self.tcx.sess.span_err(i.span, "`pub` visibility \
1083 visit::walk_view_item(self, i, ());
1087 impl<'a, 'tcx> SanePrivacyVisitor<'a, 'tcx> {
1088 /// Validates all of the visibility qualifiers placed on the item given. This
1089 /// ensures that there are no extraneous qualifiers that don't actually do
1090 /// anything. In theory these qualifiers wouldn't parse, but that may happen
1091 /// later on down the road...
1092 fn check_sane_privacy(&self, item: &ast::Item) {
1094 let check_inherited = |sp: Span, vis: ast::Visibility, note: &str| {
1095 if vis != ast::Inherited {
1096 tcx.sess.span_err(sp, "unnecessary visibility qualifier");
1098 tcx.sess.span_note(sp, note);
1103 // implementations of traits don't need visibility qualifiers because
1104 // that's controlled by having the trait in scope.
1105 ast::ItemImpl(_, Some(..), _, ref impl_items) => {
1106 check_inherited(item.span, item.vis,
1107 "visibility qualifiers have no effect on trait \
1109 for impl_item in impl_items.iter() {
1111 ast::MethodImplItem(m) => {
1112 check_inherited(m.span, m.pe_vis(), "");
1118 ast::ItemImpl(..) => {
1119 check_inherited(item.span, item.vis,
1120 "place qualifiers on individual methods instead");
1122 ast::ItemForeignMod(..) => {
1123 check_inherited(item.span, item.vis,
1124 "place qualifiers on individual functions \
1128 ast::ItemEnum(ref def, _) => {
1129 for v in def.variants.iter() {
1132 if item.vis == ast::Public {
1133 tcx.sess.span_err(v.span, "unnecessary `pub` \
1137 ast::Inherited => {}
1142 ast::ItemTrait(_, _, _, ref methods) => {
1143 for m in methods.iter() {
1145 ast::ProvidedMethod(ref m) => {
1146 check_inherited(m.span, m.pe_vis(),
1147 "unnecessary visibility");
1149 ast::RequiredMethod(ref m) => {
1150 check_inherited(m.span, m.vis,
1151 "unnecessary visibility");
1157 ast::ItemStatic(..) | ast::ItemStruct(..) |
1158 ast::ItemFn(..) | ast::ItemMod(..) | ast::ItemTy(..) |
1159 ast::ItemMac(..) => {}
1163 /// When inside of something like a function or a method, visibility has no
1164 /// control over anything so this forbids any mention of any visibility
1165 fn check_all_inherited(&self, item: &ast::Item) {
1167 fn check_inherited(tcx: &ty::ctxt, sp: Span, vis: ast::Visibility) {
1168 if vis != ast::Inherited {
1169 tcx.sess.span_err(sp, "visibility has no effect inside functions");
1172 let check_struct = |def: &Gc<ast::StructDef>| {
1173 for f in def.fields.iter() {
1175 ast::NamedField(_, p) => check_inherited(tcx, f.span, p),
1176 ast::UnnamedField(..) => {}
1180 check_inherited(tcx, item.span, item.vis);
1182 ast::ItemImpl(_, _, _, ref impl_items) => {
1183 for impl_item in impl_items.iter() {
1185 ast::MethodImplItem(m) => {
1186 check_inherited(tcx, m.span, m.pe_vis());
1191 ast::ItemForeignMod(ref fm) => {
1192 for i in fm.items.iter() {
1193 check_inherited(tcx, i.span, i.vis);
1196 ast::ItemEnum(ref def, _) => {
1197 for v in def.variants.iter() {
1198 check_inherited(tcx, v.span, v.node.vis);
1201 ast::StructVariantKind(ref s) => check_struct(s),
1202 ast::TupleVariantKind(..) => {}
1207 ast::ItemStruct(ref def, _) => check_struct(def),
1209 ast::ItemTrait(_, _, _, ref methods) => {
1210 for m in methods.iter() {
1212 ast::RequiredMethod(..) => {}
1213 ast::ProvidedMethod(ref m) => check_inherited(tcx, m.span,
1219 ast::ItemStatic(..) |
1220 ast::ItemFn(..) | ast::ItemMod(..) | ast::ItemTy(..) |
1221 ast::ItemMac(..) => {}
1226 struct VisiblePrivateTypesVisitor<'a, 'tcx: 'a> {
1227 tcx: &'a ty::ctxt<'tcx>,
1228 exported_items: &'a ExportedItems,
1229 public_items: &'a PublicItems,
1232 struct CheckTypeForPrivatenessVisitor<'a, 'b: 'a, 'tcx: 'b> {
1233 inner: &'a VisiblePrivateTypesVisitor<'b, 'tcx>,
1234 /// whether the type refers to private types.
1235 contains_private: bool,
1236 /// whether we've recurred at all (i.e. if we're pointing at the
1237 /// first type on which visit_ty was called).
1238 at_outer_type: bool,
1239 // whether that first type is a public path.
1240 outer_type_is_public_path: bool,
1243 impl<'a, 'tcx> VisiblePrivateTypesVisitor<'a, 'tcx> {
1244 fn path_is_private_type(&self, path_id: ast::NodeId) -> bool {
1245 let did = match self.tcx.def_map.borrow().find_copy(&path_id) {
1246 // `int` etc. (None doesn't seem to occur.)
1247 None | Some(def::DefPrimTy(..)) => return false,
1248 Some(def) => def.def_id()
1250 // A path can only be private if:
1251 // it's in this crate...
1253 // ... it's not exported (obviously) ...
1254 !self.exported_items.contains(&did.node) &&
1255 // .. and it corresponds to a type in the AST (this returns None for
1257 self.tcx.map.find(did.node).is_some()
1260 fn trait_is_public(&self, trait_id: ast::NodeId) -> bool {
1261 // FIXME: this would preferably be using `exported_items`, but all
1262 // traits are exported currently (see `EmbargoVisitor.exported_trait`)
1263 self.public_items.contains(&trait_id)
1267 impl<'a, 'b, 'tcx> Visitor<()> for CheckTypeForPrivatenessVisitor<'a, 'b, 'tcx> {
1268 fn visit_ty(&mut self, ty: &ast::Ty, _: ()) {
1270 ast::TyPath(_, _, path_id) => {
1271 if self.inner.path_is_private_type(path_id) {
1272 self.contains_private = true;
1273 // found what we're looking for so let's stop
1276 } else if self.at_outer_type {
1277 self.outer_type_is_public_path = true;
1282 self.at_outer_type = false;
1283 visit::walk_ty(self, ty, ())
1286 // don't want to recurse into [, .. expr]
1287 fn visit_expr(&mut self, _: &ast::Expr, _: ()) {}
1290 impl<'a, 'tcx> Visitor<()> for VisiblePrivateTypesVisitor<'a, 'tcx> {
1291 fn visit_item(&mut self, item: &ast::Item, _: ()) {
1293 // contents of a private mod can be reexported, so we need
1294 // to check internals.
1295 ast::ItemMod(_) => {}
1297 // An `extern {}` doesn't introduce a new privacy
1298 // namespace (the contents have their own privacies).
1299 ast::ItemForeignMod(_) => {}
1301 ast::ItemTrait(..) if !self.trait_is_public(item.id) => return,
1303 // impls need some special handling to try to offer useful
1304 // error messages without (too many) false positives
1305 // (i.e. we could just return here to not check them at
1306 // all, or some worse estimation of whether an impl is
1307 // publicly visible.
1308 ast::ItemImpl(ref g, ref trait_ref, self_, ref impl_items) => {
1309 // `impl [... for] Private` is never visible.
1310 let self_contains_private;
1311 // impl [... for] Public<...>, but not `impl [... for]
1312 // ~[Public]` or `(Public,)` etc.
1313 let self_is_public_path;
1315 // check the properties of the Self type:
1317 let mut visitor = CheckTypeForPrivatenessVisitor {
1319 contains_private: false,
1320 at_outer_type: true,
1321 outer_type_is_public_path: false,
1323 visitor.visit_ty(&*self_, ());
1324 self_contains_private = visitor.contains_private;
1325 self_is_public_path = visitor.outer_type_is_public_path;
1328 // miscellaneous info about the impl
1330 // `true` iff this is `impl Private for ...`.
1331 let not_private_trait =
1332 trait_ref.as_ref().map_or(true, // no trait counts as public trait
1334 let did = ty::trait_ref_to_def_id(self.tcx, tr);
1336 !is_local(did) || self.trait_is_public(did.node)
1339 // `true` iff this is a trait impl or at least one method is public.
1341 // `impl Public { $( fn ...() {} )* }` is not visible.
1343 // This is required over just using the methods' privacy
1344 // directly because we might have `impl<T: Foo<Private>> ...`,
1345 // and we shouldn't warn about the generics if all the methods
1346 // are private (because `T` won't be visible externally).
1347 let trait_or_some_public_method =
1348 trait_ref.is_some() ||
1352 ast::MethodImplItem(m) => {
1353 self.exported_items.contains(&m.id)
1358 if !self_contains_private &&
1359 not_private_trait &&
1360 trait_or_some_public_method {
1362 visit::walk_generics(self, g, ());
1366 for impl_item in impl_items.iter() {
1368 ast::MethodImplItem(method) => {
1369 visit::walk_method_helper(self,
1377 // Any private types in a trait impl fall into two
1379 // 1. mentioned in the trait definition
1380 // 2. mentioned in the type params/generics
1382 // Those in 1. can only occur if the trait is in
1383 // this crate and will've been warned about on the
1384 // trait definition (there's no need to warn twice
1385 // so we don't check the methods).
1387 // Those in 2. are warned via walk_generics and this
1389 visit::walk_trait_ref_helper(self, tr, ())
1392 } else if trait_ref.is_none() && self_is_public_path {
1393 // impl Public<Private> { ... }. Any public static
1394 // methods will be visible as `Public::foo`.
1395 let mut found_pub_static = false;
1396 for impl_item in impl_items.iter() {
1398 ast::MethodImplItem(method) => {
1399 if method.pe_explicit_self().node ==
1402 .contains(&method.id) {
1403 found_pub_static = true;
1404 visit::walk_method_helper(self,
1411 if found_pub_static {
1412 visit::walk_generics(self, g, ())
1418 // `type ... = ...;` can contain private types, because
1419 // we're introducing a new name.
1420 ast::ItemTy(..) => return,
1422 // not at all public, so we don't care
1423 _ if !self.exported_items.contains(&item.id) => return,
1428 // we've carefully constructed it so that if we're here, then
1429 // any `visit_ty`'s will be called on things that are in
1430 // public signatures, i.e. things that we're interested in for
1432 visit::walk_item(self, item, ());
1435 fn visit_foreign_item(&mut self, item: &ast::ForeignItem, _: ()) {
1436 if self.exported_items.contains(&item.id) {
1437 visit::walk_foreign_item(self, item, ())
1441 fn visit_fn(&mut self,
1442 fk: &visit::FnKind, fd: &ast::FnDecl, b: &ast::Block, s: Span, id: ast::NodeId,
1444 // needs special handling for methods.
1445 if self.exported_items.contains(&id) {
1446 visit::walk_fn(self, fk, fd, b, s, ());
1450 fn visit_ty(&mut self, t: &ast::Ty, _: ()) {
1452 ast::TyPath(ref p, _, path_id) => {
1453 if self.path_is_private_type(path_id) {
1454 self.tcx.sess.add_lint(
1455 lint::builtin::VISIBLE_PRIVATE_TYPES,
1457 "private type in exported type \
1458 signature".to_string());
1463 visit::walk_ty(self, t, ())
1466 fn visit_variant(&mut self, v: &ast::Variant, g: &ast::Generics, _: ()) {
1467 if self.exported_items.contains(&v.node.id) {
1468 visit::walk_variant(self, v, g, ());
1472 fn visit_struct_field(&mut self, s: &ast::StructField, _: ()) {
1474 ast::NamedField(_, ast::Public) => {
1475 visit::walk_struct_field(self, s, ());
1482 // we don't need to introspect into these at all: an
1483 // expression/block context can't possibly contain exported
1484 // things, and neither do view_items. (Making them no-ops stops us
1485 // from traversing the whole AST without having to be super
1486 // careful about our `walk_...` calls above.)
1487 fn visit_view_item(&mut self, _: &ast::ViewItem, _: ()) {}
1488 fn visit_block(&mut self, _: &ast::Block, _: ()) {}
1489 fn visit_expr(&mut self, _: &ast::Expr, _: ()) {}
1492 pub fn check_crate(tcx: &ty::ctxt,
1493 exp_map2: &resolve::ExportMap2,
1494 external_exports: resolve::ExternalExports,
1495 last_private_map: resolve::LastPrivateMap,
1496 krate: &ast::Crate) -> (ExportedItems, PublicItems) {
1497 // Figure out who everyone's parent is
1498 let mut visitor = ParentVisitor {
1499 parents: NodeMap::new(),
1500 curparent: ast::DUMMY_NODE_ID,
1502 visit::walk_crate(&mut visitor, krate, ());
1504 // Use the parent map to check the privacy of everything
1505 let mut visitor = PrivacyVisitor {
1506 curitem: ast::DUMMY_NODE_ID,
1509 parents: visitor.parents,
1510 external_exports: external_exports,
1511 last_private_map: last_private_map,
1513 visit::walk_crate(&mut visitor, krate, ());
1515 // Sanity check to make sure that all privacy usage and controls are
1517 let mut visitor = SanePrivacyVisitor {
1521 visit::walk_crate(&mut visitor, krate, ());
1523 tcx.sess.abort_if_errors();
1525 // Build up a set of all exported items in the AST. This is a set of all
1526 // items which are reachable from external crates based on visibility.
1527 let mut visitor = EmbargoVisitor {
1529 exported_items: NodeSet::new(),
1530 public_items: NodeSet::new(),
1531 reexports: NodeSet::new(),
1533 prev_exported: true,
1537 let before = visitor.exported_items.len();
1538 visit::walk_crate(&mut visitor, krate, ());
1539 if before == visitor.exported_items.len() {
1544 let EmbargoVisitor { exported_items, public_items, .. } = visitor;
1547 let mut visitor = VisiblePrivateTypesVisitor {
1549 exported_items: &exported_items,
1550 public_items: &public_items
1552 visit::walk_crate(&mut visitor, krate, ());
1554 return (exported_items, public_items);