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, MethodObject};
25 use util::nodemap::{NodeMap, NodeSet};
29 use syntax::ast_util::{is_local, local_def};
31 use syntax::codemap::Span;
32 use syntax::parse::token;
33 use syntax::owned_slice::OwnedSlice;
35 use syntax::visit::Visitor;
37 type Context<'a> = (&'a MethodMap, &'a resolve::ExportMap2);
39 /// A set of AST nodes exported by the crate.
40 pub type ExportedItems = NodeSet;
42 /// A set of AST nodes that are fully public in the crate. This map is used for
43 /// documentation purposes (reexporting a private struct inlines the doc,
44 /// reexporting a public struct doesn't inline the doc).
45 pub type PublicItems = NodeSet;
47 /// Result of a checking operation - None => no errors were found. Some => an
48 /// error and contains the span and message for reporting that error and
49 /// optionally the same for a note about the error.
50 type CheckResult = Option<(Span, String, Option<(Span, String)>)>;
52 ////////////////////////////////////////////////////////////////////////////////
53 /// The parent visitor, used to determine what's the parent of what (node-wise)
54 ////////////////////////////////////////////////////////////////////////////////
56 struct ParentVisitor {
57 parents: NodeMap<ast::NodeId>,
58 curparent: ast::NodeId,
61 impl Visitor<()> for ParentVisitor {
62 fn visit_item(&mut self, item: &ast::Item, _: ()) {
63 self.parents.insert(item.id, self.curparent);
65 let prev = self.curparent;
67 ast::ItemMod(..) => { self.curparent = item.id; }
68 // Enum variants are parented to the enum definition itself because
69 // they inherit privacy
70 ast::ItemEnum(ref def, _) => {
71 for variant in def.variants.iter() {
72 // The parent is considered the enclosing enum because the
73 // enum will dictate the privacy visibility of this variant
75 self.parents.insert(variant.node.id, item.id);
79 // Trait methods are always considered "public", but if the trait is
80 // private then we need some private item in the chain from the
81 // method to the root. In this case, if the trait is private, then
82 // parent all the methods to the trait to indicate that they're
84 ast::ItemTrait(_, _, _, ref methods) if item.vis != ast::Public => {
85 for m in methods.iter() {
87 ast::Provided(ref m) => self.parents.insert(m.id, item.id),
88 ast::Required(ref m) => self.parents.insert(m.id, item.id),
95 visit::walk_item(self, item, ());
96 self.curparent = prev;
99 fn visit_foreign_item(&mut self, a: &ast::ForeignItem, _: ()) {
100 self.parents.insert(a.id, self.curparent);
101 visit::walk_foreign_item(self, a, ());
104 fn visit_fn(&mut self, a: &visit::FnKind, b: &ast::FnDecl,
105 c: &ast::Block, d: Span, id: ast::NodeId, _: ()) {
106 // We already took care of some trait methods above, otherwise things
107 // like impl methods and pub trait methods are parented to the
108 // containing module, not the containing trait.
109 if !self.parents.contains_key(&id) {
110 self.parents.insert(id, self.curparent);
112 visit::walk_fn(self, a, b, c, d, ());
115 fn visit_struct_def(&mut self, s: &ast::StructDef, _: ast::Ident,
116 _: &ast::Generics, n: ast::NodeId, _: ()) {
117 // Struct constructors are parented to their struct definitions because
118 // they essentially are the struct definitions.
120 Some(id) => { self.parents.insert(id, n); }
124 // While we have the id of the struct definition, go ahead and parent
126 for field in s.fields.iter() {
127 self.parents.insert(field.node.id, self.curparent);
129 visit::walk_struct_def(self, s, ())
133 ////////////////////////////////////////////////////////////////////////////////
134 /// The embargo visitor, used to determine the exports of the ast
135 ////////////////////////////////////////////////////////////////////////////////
137 struct EmbargoVisitor<'a> {
139 exp_map2: &'a resolve::ExportMap2,
141 // This flag is an indicator of whether the previous item in the
142 // hierarchical chain was exported or not. This is the indicator of whether
143 // children should be exported as well. Note that this can flip from false
144 // to true if a reexported module is entered (or an action similar).
147 // This is a list of all exported items in the AST. An exported item is any
148 // function/method/item which is usable by external crates. This essentially
149 // means that the result is "public all the way down", but the "path down"
150 // may jump across private boundaries through reexport statements.
151 exported_items: ExportedItems,
153 // This sets contains all the destination nodes which are publicly
154 // re-exported. This is *not* a set of all reexported nodes, only a set of
155 // all nodes which are reexported *and* reachable from external crates. This
156 // means that the destination of the reexport is exported, and hence the
157 // destination must also be exported.
160 // These two fields are closely related to one another in that they are only
161 // used for generation of the 'PublicItems' set, not for privacy checking at
163 public_items: PublicItems,
167 impl<'a> EmbargoVisitor<'a> {
168 // There are checks inside of privacy which depend on knowing whether a
169 // trait should be exported or not. The two current consumers of this are:
171 // 1. Should default methods of a trait be exported?
172 // 2. Should the methods of an implementation of a trait be exported?
174 // The answer to both of these questions partly rely on whether the trait
175 // itself is exported or not. If the trait is somehow exported, then the
176 // answers to both questions must be yes. Right now this question involves
177 // more analysis than is currently done in rustc, so we conservatively
178 // answer "yes" so that all traits need to be exported.
179 fn exported_trait(&self, _id: ast::NodeId) -> bool {
184 impl<'a> Visitor<()> for EmbargoVisitor<'a> {
185 fn visit_item(&mut self, item: &ast::Item, _: ()) {
186 let orig_all_pub = self.prev_public;
187 self.prev_public = orig_all_pub && item.vis == ast::Public;
188 if self.prev_public {
189 self.public_items.insert(item.id);
192 let orig_all_exported = self.prev_exported;
194 // impls/extern blocks do not break the "public chain" because they
195 // cannot have visibility qualifiers on them anyway
196 ast::ItemImpl(..) | ast::ItemForeignMod(..) => {}
198 // Traits are a little special in that even if they themselves are
199 // not public they may still be exported.
200 ast::ItemTrait(..) => {
201 self.prev_exported = self.exported_trait(item.id);
204 // Private by default, hence we only retain the "public chain" if
205 // `pub` is explicitly listed.
208 (orig_all_exported && item.vis == ast::Public) ||
209 self.reexports.contains(&item.id);
213 let public_first = self.prev_exported &&
214 self.exported_items.insert(item.id);
217 // Enum variants inherit from their parent, so if the enum is
218 // public all variants are public unless they're explicitly priv
219 ast::ItemEnum(ref def, _) if public_first => {
220 for variant in def.variants.iter() {
221 self.exported_items.insert(variant.node.id);
225 // Implementations are a little tricky to determine what's exported
226 // out of them. Here's a few cases which are currently defined:
228 // * Impls for private types do not need to export their methods
229 // (either public or private methods)
231 // * Impls for public types only have public methods exported
233 // * Public trait impls for public types must have all methods
236 // * Private trait impls for public types can be ignored
238 // * Public trait impls for private types have their methods
239 // exported. I'm not entirely certain that this is the correct
240 // thing to do, but I have seen use cases of where this will cause
241 // undefined symbols at linkage time if this case is not handled.
243 // * Private trait impls for private types can be completely ignored
244 ast::ItemImpl(_, _, ref ty, ref methods) => {
245 let public_ty = match ty.node {
246 ast::TyPath(_, _, id) => {
247 match self.tcx.def_map.borrow().get_copy(&id) {
248 def::DefPrimTy(..) => true,
250 let did = def.def_id();
252 self.exported_items.contains(&did.node)
258 let tr = ty::impl_trait_ref(self.tcx, local_def(item.id));
259 let public_trait = tr.clone().map_or(false, |tr| {
260 !is_local(tr.def_id) ||
261 self.exported_items.contains(&tr.def_id.node)
264 if public_ty || public_trait {
265 for method in methods.iter() {
266 let meth_public = match method.explicit_self.node {
267 ast::SelfStatic => public_ty,
269 } && method.vis == ast::Public;
270 if meth_public || tr.is_some() {
271 self.exported_items.insert(method.id);
277 // Default methods on traits are all public so long as the trait
279 ast::ItemTrait(_, _, _, ref methods) if public_first => {
280 for method in methods.iter() {
282 ast::Provided(ref m) => {
283 debug!("provided {}", m.id);
284 self.exported_items.insert(m.id);
286 ast::Required(ref m) => {
287 debug!("required {}", m.id);
288 self.exported_items.insert(m.id);
294 // Struct constructors are public if the struct is all public.
295 ast::ItemStruct(ref def, _) if public_first => {
297 Some(id) => { self.exported_items.insert(id); }
302 ast::ItemTy(ref ty, _) if public_first => {
304 ast::TyPath(_, _, id) => {
305 match self.tcx.def_map.borrow().get_copy(&id) {
306 def::DefPrimTy(..) | def::DefTyParam(..) => {},
308 let did = def.def_id();
310 self.exported_items.insert(did.node);
322 visit::walk_item(self, item, ());
324 self.prev_exported = orig_all_exported;
325 self.prev_public = orig_all_pub;
328 fn visit_foreign_item(&mut self, a: &ast::ForeignItem, _: ()) {
329 if self.prev_exported && a.vis == ast::Public {
330 self.exported_items.insert(a.id);
334 fn visit_mod(&mut self, m: &ast::Mod, _sp: Span, id: ast::NodeId, _: ()) {
335 // This code is here instead of in visit_item so that the
336 // crate module gets processed as well.
337 if self.prev_exported {
338 let exp_map2 = self.exp_map2.borrow();
339 assert!(exp_map2.contains_key(&id), "wut {:?}", id);
340 for export in exp_map2.get(&id).iter() {
341 if is_local(export.def_id) {
342 self.reexports.insert(export.def_id.node);
346 visit::walk_mod(self, m, ())
350 ////////////////////////////////////////////////////////////////////////////////
351 /// The privacy visitor, where privacy checks take place (violations reported)
352 ////////////////////////////////////////////////////////////////////////////////
354 struct PrivacyVisitor<'a> {
356 curitem: ast::NodeId,
358 parents: NodeMap<ast::NodeId>,
359 external_exports: resolve::ExternalExports,
360 last_private_map: resolve::LastPrivateMap,
366 DisallowedBy(ast::NodeId),
370 UnnamedField(uint), // index
371 // FIXME #6993: change type (and name) from Ident to Name
372 NamedField(ast::Ident),
375 impl<'a> PrivacyVisitor<'a> {
376 // used when debugging
377 fn nodestr(&self, id: ast::NodeId) -> String {
378 self.tcx.map.node_to_str(id).to_string()
381 // Determines whether the given definition is public from the point of view
382 // of the current item.
383 fn def_privacy(&self, did: ast::DefId) -> PrivacyResult {
385 if self.external_exports.contains(&did) {
386 debug!("privacy - {:?} was externally exported", did);
389 debug!("privacy - is {:?} a public method", did);
391 return match self.tcx.methods.borrow().find(&did) {
393 debug!("privacy - well at least it's a method: {:?}", meth);
394 match meth.container {
395 ty::TraitContainer(id) => {
396 debug!("privacy - recursing on trait {:?}", id);
399 ty::ImplContainer(id) => {
400 match ty::impl_trait_ref(self.tcx, id) {
402 debug!("privacy - impl of trait {:?}", id);
403 self.def_privacy(t.def_id)
406 debug!("privacy - found a method {:?}",
408 if meth.vis == ast::Public {
419 debug!("privacy - nope, not even a method");
425 debug!("privacy - local {} not public all the way down",
426 self.tcx.map.node_to_str(did.node));
427 // return quickly for things in the same module
428 if self.parents.find(&did.node) == self.parents.find(&self.curitem) {
429 debug!("privacy - same parent, we're done here");
433 // We now know that there is at least one private member between the
434 // destination and the root.
435 let mut closest_private_id = did.node;
437 debug!("privacy - examining {}", self.nodestr(closest_private_id));
438 let vis = match self.tcx.map.find(closest_private_id) {
439 // If this item is a method, then we know for sure that it's an
440 // actual method and not a static method. The reason for this is
441 // that these cases are only hit in the ExprMethodCall
442 // expression, and ExprCall will have its path checked later
443 // (the path of the trait/impl) if it's a static method.
445 // With this information, then we can completely ignore all
446 // trait methods. The privacy violation would be if the trait
447 // couldn't get imported, not if the method couldn't be used
448 // (all trait methods are public).
450 // However, if this is an impl method, then we dictate this
451 // decision solely based on the privacy of the method
453 // FIXME(#10573) is this the right behavior? Why not consider
454 // where the method was defined?
455 Some(ast_map::NodeMethod(ref m)) => {
456 let imp = self.tcx.map.get_parent_did(closest_private_id);
457 match ty::impl_trait_ref(self.tcx, imp) {
458 Some(..) => return Allowable,
459 _ if m.vis == ast::Public => return Allowable,
463 Some(ast_map::NodeTraitMethod(_)) => {
467 // This is not a method call, extract the visibility as one
468 // would normally look at it
469 Some(ast_map::NodeItem(it)) => it.vis,
470 Some(ast_map::NodeForeignItem(_)) => {
471 self.tcx.map.get_foreign_vis(closest_private_id)
473 Some(ast_map::NodeVariant(..)) => {
474 ast::Public // need to move up a level (to the enum)
478 if vis != ast::Public { break }
479 // if we've reached the root, then everything was allowable and this
481 if closest_private_id == ast::CRATE_NODE_ID { return Allowable }
482 closest_private_id = *self.parents.get(&closest_private_id);
484 // If we reached the top, then we were public all the way down and
485 // we can allow this access.
486 if closest_private_id == ast::DUMMY_NODE_ID { return Allowable }
488 debug!("privacy - closest priv {}", self.nodestr(closest_private_id));
489 if self.private_accessible(closest_private_id) {
492 DisallowedBy(closest_private_id)
496 /// For a local private node in the AST, this function will determine
497 /// whether the node is accessible by the current module that iteration is
499 fn private_accessible(&self, id: ast::NodeId) -> bool {
500 let parent = *self.parents.get(&id);
501 debug!("privacy - accessible parent {}", self.nodestr(parent));
503 // After finding `did`'s closest private member, we roll ourselves back
504 // to see if this private member's parent is anywhere in our ancestry.
505 // By the privacy rules, we can access all of our ancestor's private
506 // members, so that's why we test the parent, and not the did itself.
507 let mut cur = self.curitem;
509 debug!("privacy - questioning {}, {:?}", self.nodestr(cur), cur);
511 // If the relevant parent is in our history, then we're allowed
512 // to look inside any of our ancestor's immediate private items,
513 // so this access is valid.
514 x if x == parent => return true,
516 // If we've reached the root, then we couldn't access this item
517 // in the first place
518 ast::DUMMY_NODE_ID => return false,
524 cur = *self.parents.get(&cur);
528 fn report_error(&self, result: CheckResult) -> bool {
531 Some((span, msg, note)) => {
532 self.tcx.sess.span_err(span, msg.as_slice());
534 Some((span, msg)) => {
535 self.tcx.sess.span_note(span, msg.as_slice())
544 /// Guarantee that a particular definition is public. Returns a CheckResult
545 /// which contains any errors found. These can be reported using `report_error`.
546 /// If the result is `None`, no errors were found.
547 fn ensure_public(&self, span: Span, to_check: ast::DefId,
548 source_did: Option<ast::DefId>, msg: &str) -> CheckResult {
549 let id = match self.def_privacy(to_check) {
550 ExternallyDenied => {
551 return Some((span, format!("{} is private", msg), None))
553 Allowable => return None,
554 DisallowedBy(id) => id,
557 // If we're disallowed by a particular id, then we attempt to give a
558 // nice error message to say why it was disallowed. It was either
559 // because the item itself is private or because its parent is private
560 // and its parent isn't in our ancestry.
561 let (err_span, err_msg) = if id == source_did.unwrap_or(to_check).node {
562 return Some((span, format!("{} is private", msg), None));
564 (span, format!("{} is inaccessible", msg))
566 let item = match self.tcx.map.find(id) {
567 Some(ast_map::NodeItem(item)) => {
569 // If an impl disallowed this item, then this is resolve's
570 // way of saying that a struct/enum's static method was
571 // invoked, and the struct/enum itself is private. Crawl
572 // back up the chains to find the relevant struct/enum that
574 ast::ItemImpl(_, _, ref ty, _) => {
575 let id = match ty.node {
576 ast::TyPath(_, _, id) => id,
577 _ => return Some((err_span, err_msg, None)),
579 let def = self.tcx.def_map.borrow().get_copy(&id);
580 let did = def.def_id();
581 assert!(is_local(did));
582 match self.tcx.map.get(did.node) {
583 ast_map::NodeItem(item) => item,
584 _ => self.tcx.sess.span_bug(item.span,
585 "path is not an item")
591 Some(..) | None => return Some((err_span, err_msg, None)),
593 let desc = match item.node {
594 ast::ItemMod(..) => "module",
595 ast::ItemTrait(..) => "trait",
596 ast::ItemStruct(..) => "struct",
597 ast::ItemEnum(..) => "enum",
598 _ => return Some((err_span, err_msg, None))
600 let msg = format!("{} `{}` is private", desc,
601 token::get_ident(item.ident));
602 Some((err_span, err_msg, Some((span, msg))))
605 // Checks that a field is in scope.
606 fn check_field(&mut self,
610 let fields = ty::lookup_struct_fields(self.tcx, id);
611 let field = match name {
612 NamedField(ident) => {
613 debug!("privacy - check named field {} in struct {}", ident.name, id);
614 fields.iter().find(|f| f.name == ident.name).unwrap()
616 UnnamedField(idx) => fields.get(idx)
618 if field.vis == ast::Public ||
619 (is_local(field.id) && self.private_accessible(field.id.node)) {
623 let struct_type = ty::lookup_item_type(self.tcx, id).ty;
624 let struct_desc = match ty::get(struct_type).sty {
625 ty::ty_struct(_, _) => format!("struct `{}`", ty::item_path_str(self.tcx, id)),
626 ty::ty_bare_fn(ty::BareFnTy { sig: ty::FnSig { output, .. }, .. }) => {
627 // Struct `id` is really a struct variant of an enum,
628 // and we're really looking at the variant's constructor
629 // function. So get the return type for a detailed error
631 let enum_id = match ty::get(output).sty {
632 ty::ty_enum(id, _) => id,
633 _ => self.tcx.sess.span_bug(span, "enum variant doesn't \
636 format!("variant `{}` of enum `{}`",
637 ty::with_path(self.tcx, id, |mut p| p.last().unwrap()),
638 ty::item_path_str(self.tcx, enum_id))
640 _ => self.tcx.sess.span_bug(span, "can't find struct for field")
642 let msg = match name {
643 NamedField(name) => format!("field `{}` of {} is private",
644 token::get_ident(name), struct_desc),
645 UnnamedField(idx) => format!("field #{} of {} is private",
646 idx + 1, struct_desc),
648 self.tcx.sess.span_err(span, msg.as_slice());
651 // Given the ID of a method, checks to ensure it's in scope.
652 fn check_static_method(&mut self, span: Span, method_id: ast::DefId,
654 // If the method is a default method, we need to use the def_id of
655 // the default implementation.
656 let method_id = ty::method(self.tcx, method_id).provided_source
657 .unwrap_or(method_id);
659 let string = token::get_ident(name);
660 self.report_error(self.ensure_public(span,
663 format!("method `{}`",
664 string).as_slice()));
667 // Checks that a path is in scope.
668 fn check_path(&mut self, span: Span, path_id: ast::NodeId, path: &ast::Path) {
669 debug!("privacy - path {}", self.nodestr(path_id));
670 let orig_def = self.tcx.def_map.borrow().get_copy(&path_id);
671 let ck = |tyname: &str| {
672 let ck_public = |def: ast::DefId| {
673 let name = token::get_ident(path.segments
677 let origdid = orig_def.def_id();
678 self.ensure_public(span,
686 match *self.last_private_map.get(&path_id) {
687 resolve::LastMod(resolve::AllPublic) => {},
688 resolve::LastMod(resolve::DependsOn(def)) => {
689 self.report_error(ck_public(def));
691 resolve::LastImport{value_priv: value_priv,
692 value_used: check_value,
693 type_priv: type_priv,
694 type_used: check_type} => {
695 // This dance with found_error is because we don't want to report
696 // a privacy error twice for the same directive.
697 let found_error = match (type_priv, check_type) {
698 (Some(resolve::DependsOn(def)), resolve::Used) => {
699 !self.report_error(ck_public(def))
704 match (value_priv, check_value) {
705 (Some(resolve::DependsOn(def)), resolve::Used) => {
706 self.report_error(ck_public(def));
711 // If an import is not used in either namespace, we still
712 // want to check that it could be legal. Therefore we check
713 // in both namespaces and only report an error if both would
714 // be illegal. We only report one error, even if it is
715 // illegal to import from both namespaces.
716 match (value_priv, check_value, type_priv, check_type) {
717 (Some(p), resolve::Unused, None, _) |
718 (None, _, Some(p), resolve::Unused) => {
720 resolve::AllPublic => None,
721 resolve::DependsOn(def) => ck_public(def),
724 self.report_error(p);
727 (Some(v), resolve::Unused, Some(t), resolve::Unused) => {
729 resolve::AllPublic => None,
730 resolve::DependsOn(def) => ck_public(def),
733 resolve::AllPublic => None,
734 resolve::DependsOn(def) => ck_public(def),
737 (Some(_), Some(t)) => {
738 self.report_error(Some(t));
748 // FIXME(#12334) Imports can refer to definitions in both the type and
749 // value namespaces. The privacy information is aware of this, but the
750 // def map is not. Therefore the names we work out below will not always
751 // be accurate and we can get slightly wonky error messages (but type
752 // checking is always correct).
753 match self.tcx.def_map.borrow().get_copy(&path_id) {
754 def::DefStaticMethod(..) => ck("static method"),
755 def::DefFn(..) => ck("function"),
756 def::DefStatic(..) => ck("static"),
757 def::DefVariant(..) => ck("variant"),
758 def::DefTy(..) => ck("type"),
759 def::DefTrait(..) => ck("trait"),
760 def::DefStruct(..) => ck("struct"),
761 def::DefMethod(_, Some(..)) => ck("trait method"),
762 def::DefMethod(..) => ck("method"),
763 def::DefMod(..) => ck("module"),
768 // Checks that a method is in scope.
769 fn check_method(&mut self, span: Span, origin: MethodOrigin,
772 MethodStatic(method_id) => {
773 self.check_static_method(span, method_id, ident)
775 // Trait methods are always all public. The only controlling factor
776 // is whether the trait itself is accessible or not.
777 MethodParam(MethodParam { trait_id: trait_id, .. }) |
778 MethodObject(MethodObject { trait_id: trait_id, .. }) => {
779 self.report_error(self.ensure_public(span, trait_id, None,
786 impl<'a> Visitor<()> for PrivacyVisitor<'a> {
787 fn visit_item(&mut self, item: &ast::Item, _: ()) {
788 // Do not check privacy inside items with the resolve_unexported
789 // attribute. This is used for the test runner.
790 if attr::contains_name(item.attrs.as_slice(), "!resolve_unexported") {
794 let orig_curitem = replace(&mut self.curitem, item.id);
795 visit::walk_item(self, item, ());
796 self.curitem = orig_curitem;
799 fn visit_expr(&mut self, expr: &ast::Expr, _: ()) {
801 ast::ExprField(ref base, ident, _) => {
802 match ty::get(ty::expr_ty_adjusted(self.tcx, &**base)).sty {
803 ty::ty_struct(id, _) => {
804 self.check_field(expr.span, id, NamedField(ident.node));
809 ast::ExprMethodCall(ident, _, _) => {
810 let method_call = MethodCall::expr(expr.id);
811 match self.tcx.method_map.borrow().find(&method_call) {
813 self.tcx.sess.span_bug(expr.span,
814 "method call not in \
818 debug!("(privacy checking) checking impl method");
819 self.check_method(expr.span, method.origin, ident.node);
823 ast::ExprStruct(_, ref fields, _) => {
824 match ty::get(ty::expr_ty(self.tcx, expr)).sty {
825 ty::ty_struct(id, _) => {
826 for field in (*fields).iter() {
827 self.check_field(expr.span, id,
828 NamedField(field.ident.node));
831 ty::ty_enum(_, _) => {
832 match self.tcx.def_map.borrow().get_copy(&expr.id) {
833 def::DefVariant(_, variant_id, _) => {
834 for field in fields.iter() {
835 self.check_field(expr.span, variant_id,
836 NamedField(field.ident.node));
839 _ => self.tcx.sess.span_bug(expr.span,
846 _ => self.tcx.sess.span_bug(expr.span, "struct expr \
851 ast::ExprPath(..) => {
852 let guard = |did: ast::DefId| {
853 let fields = ty::lookup_struct_fields(self.tcx, did);
854 let any_priv = fields.iter().any(|f| {
855 f.vis != ast::Public && (
857 !self.private_accessible(f.id.node))
860 self.tcx.sess.span_err(expr.span,
861 "cannot invoke tuple struct constructor \
862 with private fields");
865 match self.tcx.def_map.borrow().find(&expr.id) {
866 Some(&def::DefStruct(did)) => {
867 guard(if is_local(did) {
868 local_def(self.tcx.map.get_parent(did.node))
870 // "tuple structs" with zero fields (such as
871 // `pub struct Foo;`) don't have a ctor_id, hence
872 // the unwrap_or to the same struct id.
874 csearch::get_tuple_struct_definition_if_ctor(
875 &self.tcx.sess.cstore, did);
876 maybe_did.unwrap_or(did)
879 // Tuple struct constructors across crates are identified as
880 // DefFn types, so we explicitly handle that case here.
881 Some(&def::DefFn(did, _)) if !is_local(did) => {
882 match csearch::get_tuple_struct_definition_if_ctor(
883 &self.tcx.sess.cstore, did) {
884 Some(did) => guard(did),
894 visit::walk_expr(self, expr, ());
897 fn visit_view_item(&mut self, a: &ast::ViewItem, _: ()) {
899 ast::ViewItemExternCrate(..) => {}
900 ast::ViewItemUse(ref vpath) => {
902 ast::ViewPathSimple(..) | ast::ViewPathGlob(..) => {}
903 ast::ViewPathList(_, ref list, _) => {
904 for pid in list.iter() {
905 debug!("privacy - list {}", pid.node.id);
906 let seg = ast::PathSegment {
907 identifier: pid.node.name,
908 lifetimes: Vec::new(),
909 types: OwnedSlice::empty(),
911 let segs = vec!(seg);
912 let path = ast::Path {
917 self.check_path(pid.span, pid.node.id, &path);
923 visit::walk_view_item(self, a, ());
926 fn visit_pat(&mut self, pattern: &ast::Pat, _: ()) {
927 // Foreign functions do not have their patterns mapped in the def_map,
928 // and there's nothing really relevant there anyway, so don't bother
929 // checking privacy. If you can name the type then you can pass it to an
930 // external C function anyway.
931 if self.in_foreign { return }
934 ast::PatStruct(_, ref fields, _) => {
935 match ty::get(ty::pat_ty(self.tcx, pattern)).sty {
936 ty::ty_struct(id, _) => {
937 for field in fields.iter() {
938 self.check_field(pattern.span, id,
939 NamedField(field.ident));
942 ty::ty_enum(_, _) => {
943 match self.tcx.def_map.borrow().find(&pattern.id) {
944 Some(&def::DefVariant(_, variant_id, _)) => {
945 for field in fields.iter() {
946 self.check_field(pattern.span, variant_id,
947 NamedField(field.ident));
950 _ => self.tcx.sess.span_bug(pattern.span,
957 _ => self.tcx.sess.span_bug(pattern.span,
958 "struct pattern didn't have \
963 // Patterns which bind no fields are allowable (the path is check
965 ast::PatEnum(_, Some(ref fields)) => {
966 match ty::get(ty::pat_ty(self.tcx, pattern)).sty {
967 ty::ty_struct(id, _) => {
968 for (i, field) in fields.iter().enumerate() {
970 ast::PatWild(..) => continue,
973 self.check_field(field.span, id, UnnamedField(i));
977 // enum fields have no privacy at this time
986 visit::walk_pat(self, pattern, ());
989 fn visit_foreign_item(&mut self, fi: &ast::ForeignItem, _: ()) {
990 self.in_foreign = true;
991 visit::walk_foreign_item(self, fi, ());
992 self.in_foreign = false;
995 fn visit_path(&mut self, path: &ast::Path, id: ast::NodeId, _: ()) {
996 self.check_path(path.span, id, path);
997 visit::walk_path(self, path, ());
1001 ////////////////////////////////////////////////////////////////////////////////
1002 /// The privacy sanity check visitor, ensures unnecessary visibility isn't here
1003 ////////////////////////////////////////////////////////////////////////////////
1005 struct SanePrivacyVisitor<'a> {
1010 impl<'a> Visitor<()> for SanePrivacyVisitor<'a> {
1011 fn visit_item(&mut self, item: &ast::Item, _: ()) {
1013 self.check_all_inherited(item);
1015 self.check_sane_privacy(item);
1018 let in_fn = self.in_fn;
1019 let orig_in_fn = replace(&mut self.in_fn, match item.node {
1020 ast::ItemMod(..) => false, // modules turn privacy back on
1021 _ => in_fn, // otherwise we inherit
1023 visit::walk_item(self, item, ());
1024 self.in_fn = orig_in_fn;
1027 fn visit_fn(&mut self, fk: &visit::FnKind, fd: &ast::FnDecl,
1028 b: &ast::Block, s: Span, _: ast::NodeId, _: ()) {
1029 // This catches both functions and methods
1030 let orig_in_fn = replace(&mut self.in_fn, true);
1031 visit::walk_fn(self, fk, fd, b, s, ());
1032 self.in_fn = orig_in_fn;
1035 fn visit_view_item(&mut self, i: &ast::ViewItem, _: ()) {
1037 ast::Inherited => {}
1040 self.tcx.sess.span_err(i.span, "unnecessary `pub`, imports \
1041 in functions are never \
1045 ast::ViewItemExternCrate(..) => {
1046 self.tcx.sess.span_err(i.span, "`pub` visibility \
1054 visit::walk_view_item(self, i, ());
1058 impl<'a> SanePrivacyVisitor<'a> {
1059 /// Validates all of the visibility qualifiers placed on the item given. This
1060 /// ensures that there are no extraneous qualifiers that don't actually do
1061 /// anything. In theory these qualifiers wouldn't parse, but that may happen
1062 /// later on down the road...
1063 fn check_sane_privacy(&self, item: &ast::Item) {
1065 let check_inherited = |sp: Span, vis: ast::Visibility, note: &str| {
1066 if vis != ast::Inherited {
1067 tcx.sess.span_err(sp, "unnecessary visibility qualifier");
1069 tcx.sess.span_note(sp, note);
1074 // implementations of traits don't need visibility qualifiers because
1075 // that's controlled by having the trait in scope.
1076 ast::ItemImpl(_, Some(..), _, ref methods) => {
1077 check_inherited(item.span, item.vis,
1078 "visibility qualifiers have no effect on trait \
1080 for m in methods.iter() {
1081 check_inherited(m.span, m.vis, "");
1085 ast::ItemImpl(..) => {
1086 check_inherited(item.span, item.vis,
1087 "place qualifiers on individual methods instead");
1089 ast::ItemForeignMod(..) => {
1090 check_inherited(item.span, item.vis,
1091 "place qualifiers on individual functions \
1095 ast::ItemEnum(ref def, _) => {
1096 for v in def.variants.iter() {
1099 if item.vis == ast::Public {
1100 tcx.sess.span_err(v.span, "unnecessary `pub` \
1104 ast::Inherited => {}
1109 ast::ItemTrait(_, _, _, ref methods) => {
1110 for m in methods.iter() {
1112 ast::Provided(ref m) => {
1113 check_inherited(m.span, m.vis,
1114 "unnecessary visibility");
1116 ast::Required(ref m) => {
1117 check_inherited(m.span, m.vis,
1118 "unnecessary visibility");
1124 ast::ItemStatic(..) | ast::ItemStruct(..) |
1125 ast::ItemFn(..) | ast::ItemMod(..) | ast::ItemTy(..) |
1126 ast::ItemMac(..) => {}
1130 /// When inside of something like a function or a method, visibility has no
1131 /// control over anything so this forbids any mention of any visibility
1132 fn check_all_inherited(&self, item: &ast::Item) {
1134 fn check_inherited(tcx: &ty::ctxt, sp: Span, vis: ast::Visibility) {
1135 if vis != ast::Inherited {
1136 tcx.sess.span_err(sp, "visibility has no effect inside functions");
1139 let check_struct = |def: &Gc<ast::StructDef>| {
1140 for f in def.fields.iter() {
1142 ast::NamedField(_, p) => check_inherited(tcx, f.span, p),
1143 ast::UnnamedField(..) => {}
1147 check_inherited(tcx, item.span, item.vis);
1149 ast::ItemImpl(_, _, _, ref methods) => {
1150 for m in methods.iter() {
1151 check_inherited(tcx, m.span, m.vis);
1154 ast::ItemForeignMod(ref fm) => {
1155 for i in fm.items.iter() {
1156 check_inherited(tcx, i.span, i.vis);
1159 ast::ItemEnum(ref def, _) => {
1160 for v in def.variants.iter() {
1161 check_inherited(tcx, v.span, v.node.vis);
1164 ast::StructVariantKind(ref s) => check_struct(s),
1165 ast::TupleVariantKind(..) => {}
1170 ast::ItemStruct(ref def, _) => check_struct(def),
1172 ast::ItemTrait(_, _, _, ref methods) => {
1173 for m in methods.iter() {
1175 ast::Required(..) => {}
1176 ast::Provided(ref m) => check_inherited(tcx, m.span,
1182 ast::ItemStatic(..) |
1183 ast::ItemFn(..) | ast::ItemMod(..) | ast::ItemTy(..) |
1184 ast::ItemMac(..) => {}
1189 struct VisiblePrivateTypesVisitor<'a> {
1191 exported_items: &'a ExportedItems,
1192 public_items: &'a PublicItems,
1195 struct CheckTypeForPrivatenessVisitor<'a, 'b> {
1196 inner: &'b VisiblePrivateTypesVisitor<'a>,
1197 /// whether the type refers to private types.
1198 contains_private: bool,
1199 /// whether we've recurred at all (i.e. if we're pointing at the
1200 /// first type on which visit_ty was called).
1201 at_outer_type: bool,
1202 // whether that first type is a public path.
1203 outer_type_is_public_path: bool,
1206 impl<'a> VisiblePrivateTypesVisitor<'a> {
1207 fn path_is_private_type(&self, path_id: ast::NodeId) -> bool {
1208 let did = match self.tcx.def_map.borrow().find_copy(&path_id) {
1209 // `int` etc. (None doesn't seem to occur.)
1210 None | Some(def::DefPrimTy(..)) => return false,
1211 Some(def) => def.def_id()
1213 // A path can only be private if:
1214 // it's in this crate...
1216 // ... it's not exported (obviously) ...
1217 !self.exported_items.contains(&did.node) &&
1218 // .. and it corresponds to a type in the AST (this returns None for
1220 self.tcx.map.find(did.node).is_some()
1223 fn trait_is_public(&self, trait_id: ast::NodeId) -> bool {
1224 // FIXME: this would preferably be using `exported_items`, but all
1225 // traits are exported currently (see `EmbargoVisitor.exported_trait`)
1226 self.public_items.contains(&trait_id)
1230 impl<'a, 'b> Visitor<()> for CheckTypeForPrivatenessVisitor<'a, 'b> {
1231 fn visit_ty(&mut self, ty: &ast::Ty, _: ()) {
1233 ast::TyPath(_, _, path_id) => {
1234 if self.inner.path_is_private_type(path_id) {
1235 self.contains_private = true;
1236 // found what we're looking for so let's stop
1239 } else if self.at_outer_type {
1240 self.outer_type_is_public_path = true;
1245 self.at_outer_type = false;
1246 visit::walk_ty(self, ty, ())
1249 // don't want to recurse into [, .. expr]
1250 fn visit_expr(&mut self, _: &ast::Expr, _: ()) {}
1253 impl<'a> Visitor<()> for VisiblePrivateTypesVisitor<'a> {
1254 fn visit_item(&mut self, item: &ast::Item, _: ()) {
1256 // contents of a private mod can be reexported, so we need
1257 // to check internals.
1258 ast::ItemMod(_) => {}
1260 // An `extern {}` doesn't introduce a new privacy
1261 // namespace (the contents have their own privacies).
1262 ast::ItemForeignMod(_) => {}
1264 ast::ItemTrait(..) if !self.trait_is_public(item.id) => return,
1266 // impls need some special handling to try to offer useful
1267 // error messages without (too many) false positives
1268 // (i.e. we could just return here to not check them at
1269 // all, or some worse estimation of whether an impl is
1270 // publically visible.
1271 ast::ItemImpl(ref g, ref trait_ref, self_, ref methods) => {
1272 // `impl [... for] Private` is never visible.
1273 let self_contains_private;
1274 // impl [... for] Public<...>, but not `impl [... for]
1275 // ~[Public]` or `(Public,)` etc.
1276 let self_is_public_path;
1278 // check the properties of the Self type:
1280 let mut visitor = CheckTypeForPrivatenessVisitor {
1282 contains_private: false,
1283 at_outer_type: true,
1284 outer_type_is_public_path: false,
1286 visitor.visit_ty(&*self_, ());
1287 self_contains_private = visitor.contains_private;
1288 self_is_public_path = visitor.outer_type_is_public_path;
1291 // miscellaneous info about the impl
1293 // `true` iff this is `impl Private for ...`.
1294 let not_private_trait =
1295 trait_ref.as_ref().map_or(true, // no trait counts as public trait
1297 let did = ty::trait_ref_to_def_id(self.tcx, tr);
1299 !is_local(did) || self.trait_is_public(did.node)
1302 // `true` iff this is a trait impl or at least one method is public.
1304 // `impl Public { $( fn ...() {} )* }` is not visible.
1306 // This is required over just using the methods' privacy
1307 // directly because we might have `impl<T: Foo<Private>> ...`,
1308 // and we shouldn't warn about the generics if all the methods
1309 // are private (because `T` won't be visible externally).
1310 let trait_or_some_public_method =
1311 trait_ref.is_some() ||
1312 methods.iter().any(|m| self.exported_items.contains(&m.id));
1314 if !self_contains_private &&
1315 not_private_trait &&
1316 trait_or_some_public_method {
1318 visit::walk_generics(self, g, ());
1322 for method in methods.iter() {
1323 visit::walk_method_helper(self, &**method, ())
1327 // Any private types in a trait impl fall into two
1329 // 1. mentioned in the trait definition
1330 // 2. mentioned in the type params/generics
1332 // Those in 1. can only occur if the trait is in
1333 // this crate and will've been warned about on the
1334 // trait definition (there's no need to warn twice
1335 // so we don't check the methods).
1337 // Those in 2. are warned via walk_generics and this
1339 visit::walk_trait_ref_helper(self, tr, ())
1342 } else if trait_ref.is_none() && self_is_public_path {
1343 // impl Public<Private> { ... }. Any public static
1344 // methods will be visible as `Public::foo`.
1345 let mut found_pub_static = false;
1346 for method in methods.iter() {
1347 if method.explicit_self.node == ast::SelfStatic &&
1348 self.exported_items.contains(&method.id) {
1349 found_pub_static = true;
1350 visit::walk_method_helper(self, &**method, ());
1353 if found_pub_static {
1354 visit::walk_generics(self, g, ())
1360 // `type ... = ...;` can contain private types, because
1361 // we're introducing a new name.
1362 ast::ItemTy(..) => return,
1364 // not at all public, so we don't care
1365 _ if !self.exported_items.contains(&item.id) => return,
1370 // we've carefully constructed it so that if we're here, then
1371 // any `visit_ty`'s will be called on things that are in
1372 // public signatures, i.e. things that we're interested in for
1374 visit::walk_item(self, item, ());
1377 fn visit_foreign_item(&mut self, item: &ast::ForeignItem, _: ()) {
1378 if self.exported_items.contains(&item.id) {
1379 visit::walk_foreign_item(self, item, ())
1383 fn visit_fn(&mut self,
1384 fk: &visit::FnKind, fd: &ast::FnDecl, b: &ast::Block, s: Span, id: ast::NodeId,
1386 // needs special handling for methods.
1387 if self.exported_items.contains(&id) {
1388 visit::walk_fn(self, fk, fd, b, s, ());
1392 fn visit_ty(&mut self, t: &ast::Ty, _: ()) {
1394 ast::TyPath(ref p, _, path_id) => {
1395 if self.path_is_private_type(path_id) {
1396 self.tcx.sess.add_lint(
1397 lint::VisiblePrivateTypes,
1399 "private type in exported type \
1400 signature".to_string());
1405 visit::walk_ty(self, t, ())
1408 fn visit_variant(&mut self, v: &ast::Variant, g: &ast::Generics, _: ()) {
1409 if self.exported_items.contains(&v.node.id) {
1410 visit::walk_variant(self, v, g, ());
1414 fn visit_struct_field(&mut self, s: &ast::StructField, _: ()) {
1416 ast::NamedField(_, ast::Public) => {
1417 visit::walk_struct_field(self, s, ());
1424 // we don't need to introspect into these at all: an
1425 // expression/block context can't possibly contain exported
1426 // things, and neither do view_items. (Making them no-ops stops us
1427 // from traversing the whole AST without having to be super
1428 // careful about our `walk_...` calls above.)
1429 fn visit_view_item(&mut self, _: &ast::ViewItem, _: ()) {}
1430 fn visit_block(&mut self, _: &ast::Block, _: ()) {}
1431 fn visit_expr(&mut self, _: &ast::Expr, _: ()) {}
1434 pub fn check_crate(tcx: &ty::ctxt,
1435 exp_map2: &resolve::ExportMap2,
1436 external_exports: resolve::ExternalExports,
1437 last_private_map: resolve::LastPrivateMap,
1438 krate: &ast::Crate) -> (ExportedItems, PublicItems) {
1439 // Figure out who everyone's parent is
1440 let mut visitor = ParentVisitor {
1441 parents: NodeMap::new(),
1442 curparent: ast::DUMMY_NODE_ID,
1444 visit::walk_crate(&mut visitor, krate, ());
1446 // Use the parent map to check the privacy of everything
1447 let mut visitor = PrivacyVisitor {
1448 curitem: ast::DUMMY_NODE_ID,
1451 parents: visitor.parents,
1452 external_exports: external_exports,
1453 last_private_map: last_private_map,
1455 visit::walk_crate(&mut visitor, krate, ());
1457 // Sanity check to make sure that all privacy usage and controls are
1459 let mut visitor = SanePrivacyVisitor {
1463 visit::walk_crate(&mut visitor, krate, ());
1465 tcx.sess.abort_if_errors();
1467 // Build up a set of all exported items in the AST. This is a set of all
1468 // items which are reachable from external crates based on visibility.
1469 let mut visitor = EmbargoVisitor {
1471 exported_items: NodeSet::new(),
1472 public_items: NodeSet::new(),
1473 reexports: NodeSet::new(),
1475 prev_exported: true,
1479 let before = visitor.exported_items.len();
1480 visit::walk_crate(&mut visitor, krate, ());
1481 if before == visitor.exported_items.len() {
1486 let EmbargoVisitor { exported_items, public_items, .. } = visitor;
1489 let mut visitor = VisiblePrivateTypesVisitor {
1491 exported_items: &exported_items,
1492 public_items: &public_items
1494 visit::walk_crate(&mut visitor, krate, ());
1496 return (exported_items, public_items);