diff --git a/src/librustc/middle/privacy.rs b/src/librustc/middle/privacy.rs index 416dc9d7237..e3d5c93ea7c 100644 --- a/src/librustc/middle/privacy.rs +++ b/src/librustc/middle/privacy.rs @@ -22,7 +22,7 @@ use middle::typeck::{method_static, method_object}; use syntax::ast; use syntax::ast_map; -use syntax::ast_util::{is_local, def_id_of_def}; +use syntax::ast_util::{is_local, def_id_of_def, local_def}; use syntax::attr; use syntax::codemap::Span; use syntax::parse::token; @@ -35,15 +35,16 @@ type Context<'self> = (&'self method_map, &'self resolve::ExportMap2); /// A set of AST nodes exported by the crate. pub type ExportedItems = HashSet; -// This visitor is used to determine the parent of all nodes in question when it -// comes to privacy. This is used to determine later on if a usage is actually -// valid or not. -struct ParentVisitor<'self> { - parents: &'self mut HashMap, +//////////////////////////////////////////////////////////////////////////////// +/// The parent visitor, used to determine what's the parent of what (node-wise) +//////////////////////////////////////////////////////////////////////////////// + +struct ParentVisitor { + parents: HashMap, curparent: ast::NodeId, } -impl<'self> Visitor<()> for ParentVisitor<'self> { +impl Visitor<()> for ParentVisitor { fn visit_item(&mut self, item: @ast::item, _: ()) { self.parents.insert(item.id, self.curparent); @@ -138,34 +139,37 @@ impl<'self> Visitor<()> for ParentVisitor<'self> { } } -// This visitor is used to determine which items of the ast are embargoed, -// otherwise known as not exported. +//////////////////////////////////////////////////////////////////////////////// +/// The embargo visitor, used to determine the exports of the ast +//////////////////////////////////////////////////////////////////////////////// + struct EmbargoVisitor<'self> { tcx: ty::ctxt, - // A set of all nodes in the ast which can be considered "publicly - // exported" in the sense that they are accessible from anywhere - // in any hierarchy. They are public items whose ancestors are all - // public. - path_all_public_items: &'self mut ExportedItems, - // A set of all nodes in the ast that can be reached via a public - // path. This includes everything in `path_all_public_items` as - // well as re-exported private nodes (`pub use`ing a private - // path). - exported_items: &'self mut ExportedItems, exp_map2: &'self resolve::ExportMap2, - path_all_public: bool, -} -impl<'self> EmbargoVisitor<'self> { - fn add_path_all_public_item(&mut self, id: ast::NodeId) { - self.path_all_public_items.insert(id); - self.exported_items.insert(id); - } + // This flag is an indicator of whether the previous item in the + // hierarchical chain was exported or not. This is the indicator of whether + // children should be exported as well. Note that this can flip from false + // to true if a reexported module is entered (or an action similar). + prev_exported: bool, + + // This is a list of all exported items in the AST. An exported item is any + // function/method/item which is usable by external crates. This essentially + // means that the result is "public all the way down", but the "path down" + // may jump across private boundaries through reexport statements. + exported_items: ExportedItems, + + // This sets contains all the destination nodes which are publicly + // re-exported. This is *not* a set of all reexported nodes, only a set of + // all nodes which are reexported *and* reachable from external crates. This + // means that the destination of the reexport is exported, and hence the + // destination must also be exported. + reexports: HashSet, } impl<'self> Visitor<()> for EmbargoVisitor<'self> { fn visit_item(&mut self, item: @ast::item, _: ()) { - let orig_all_pub = self.path_all_public; + let orig_all_pub = self.prev_exported; match item.node { // impls/extern blocks do not break the "public chain" because they // cannot have visibility qualifiers on them anyway @@ -174,67 +178,99 @@ impl<'self> Visitor<()> for EmbargoVisitor<'self> { // Private by default, hence we only retain the "public chain" if // `pub` is explicitly listed. _ => { - self.path_all_public = orig_all_pub && item.vis == ast::public; + self.prev_exported = + (orig_all_pub && item.vis == ast::public) || + self.reexports.contains(&item.id); } } - if self.path_all_public { - self.add_path_all_public_item(item.id); - } + let public_first = self.prev_exported && + self.exported_items.insert(item.id); match item.node { // Enum variants inherit from their parent, so if the enum is // public all variants are public unless they're explicitly priv - ast::item_enum(ref def, _) if self.path_all_public => { + ast::item_enum(ref def, _) if public_first => { for variant in def.variants.iter() { if variant.node.vis != ast::private { - self.add_path_all_public_item(variant.node.id); + self.exported_items.insert(variant.node.id); } } } - // Methods which are public at the source are totally public. - ast::item_impl(_, None, _, ref methods) => { - for method in methods.iter() { - let public = match method.explicit_self.node { - ast::sty_static => self.path_all_public, - _ => true, - } && method.vis == ast::public; - if public { - self.add_path_all_public_item(method.id); + // Implementations are a little tricky to determine what's exported + // out of them. Here's a few cases which are currently defined: + // + // * Impls for private types do not need to export their methods + // (either public or private methods) + // + // * Impls for public types only have public methods exported + // + // * Public trait impls for public types must have all methods + // exported. + // + // * Private trait impls for public types can be ignored + // + // * Public trait impls for private types have their methods + // exported. I'm not entirely certain that this is the correct + // thing to do, but I have seen use cases of where this will cause + // undefined symbols at linkage time if this case is not handled. + // + // * Private trait impls for private types can be completely ignored + ast::item_impl(_, _, ref ty, ref methods) => { + let public_ty = match ty.node { + ast::ty_path(_, _, id) => { + match self.tcx.def_map.get_copy(&id) { + ast::DefPrimTy(*) => true, + def => { + let did = def_id_of_def(def); + !is_local(did) || + self.exported_items.contains(&did.node) + } + } + } + _ => true, + }; + let tr = ty::impl_trait_ref(self.tcx, local_def(item.id)); + let public_trait = do tr.map_default(false) |tr| { + !is_local(tr.def_id) || + self.exported_items.contains(&tr.def_id.node) + }; + + if public_ty || public_trait { + for method in methods.iter() { + let meth_public = match method.explicit_self.node { + ast::sty_static => public_ty, + _ => true, + } && method.vis == ast::public; + if meth_public || public_trait { + self.exported_items.insert(method.id); + } } } } - // Trait implementation methods are all completely public - ast::item_impl(_, Some(*), _, ref methods) => { - for method in methods.iter() { - debug!("exporting: {}", method.id); - self.add_path_all_public_item(method.id); - } - } - - // Default methods on traits are all public so long as the trait is - // public - ast::item_trait(_, _, ref methods) if self.path_all_public => { + // Default methods on traits are all public so long as the trait + // is public + ast::item_trait(_, _, ref methods) if public_first => { for method in methods.iter() { match *method { ast::provided(ref m) => { debug!("provided {}", m.id); - self.add_path_all_public_item(m.id); + self.exported_items.insert(m.id); } ast::required(ref m) => { debug!("required {}", m.id); - self.add_path_all_public_item(m.id); + self.exported_items.insert(m.id); } } } } // Struct constructors are public if the struct is all public. - ast::item_struct(ref def, _) if self.path_all_public => { + ast::item_struct(ref def, _) if public_first => { match def.ctor_id { - Some(id) => { self.add_path_all_public_item(id); } + Some(id) => { self.exported_items.insert(id); } None => {} } } @@ -244,44 +280,40 @@ impl<'self> Visitor<()> for EmbargoVisitor<'self> { visit::walk_item(self, item, ()); - self.path_all_public = orig_all_pub; + self.prev_exported = orig_all_pub; } fn visit_foreign_item(&mut self, a: @ast::foreign_item, _: ()) { - if self.path_all_public && a.vis == ast::public { - self.add_path_all_public_item(a.id); + if self.prev_exported && a.vis == ast::public { + self.exported_items.insert(a.id); } } - fn visit_mod(&mut self, m: &ast::_mod, sp: Span, id: ast::NodeId, _: ()) { + fn visit_mod(&mut self, m: &ast::_mod, _sp: Span, id: ast::NodeId, _: ()) { // This code is here instead of in visit_item so that the // crate module gets processed as well. - if self.path_all_public { - match self.exp_map2.find(&id) { - Some(exports) => { - for export in exports.iter() { - if is_local(export.def_id) && export.reexport { - self.exported_items.insert(export.def_id.node); - } - } + if self.prev_exported { + assert!(self.exp_map2.contains_key(&id), "wut {:?}", id); + for export in self.exp_map2.get(&id).iter() { + if is_local(export.def_id) && export.reexport { + self.reexports.insert(export.def_id.node); } - None => self.tcx.sess.span_bug(sp, "missing exp_map2 entry \ - for module"), } } visit::walk_mod(self, m, ()) } } +//////////////////////////////////////////////////////////////////////////////// +/// The privacy visitor, where privacy checks take place (violations reported) +//////////////////////////////////////////////////////////////////////////////// + struct PrivacyVisitor<'self> { tcx: ty::ctxt, curitem: ast::NodeId, in_fn: bool, - - // See comments on the same field in `EmbargoVisitor`. - path_all_public_items: &'self ExportedItems, method_map: &'self method_map, - parents: &'self HashMap, + parents: HashMap, external_exports: resolve::ExternalExports, last_private_map: resolve::LastPrivateMap, } @@ -339,9 +371,6 @@ impl<'self> PrivacyVisitor<'self> { ExternallyDenied } }; - } else if self.path_all_public_items.contains(&did.node) { - debug!("privacy - exported item {}", self.nodestr(did.node)); - return Allowable; } debug!("privacy - local {:?} not public all the way down", did); @@ -357,8 +386,36 @@ impl<'self> PrivacyVisitor<'self> { loop { debug!("privacy - examining {}", self.nodestr(closest_private_id)); let vis = match self.tcx.items.find(&closest_private_id) { + // If this item is a method, then we know for sure that it's an + // actual method and not a static method. The reason for this is + // that these cases are only hit in the ExprMethodCall + // expression, and ExprCall will have its path checked later + // (the path of the trait/impl) if it's a static method. + // + // With this information, then we can completely ignore all + // trait methods. The privacy violation would be if the trait + // couldn't get imported, not if the method couldn't be used + // (all trait methods are public). + // + // However, if this is an impl method, then we dictate this + // decision solely based on the privacy of the method + // invocation. + // FIXME(#10573) is this the right behavior? Why not consider + // where the method was defined? + Some(&ast_map::node_method(ref m, imp, _)) => { + match ty::impl_trait_ref(self.tcx, imp) { + Some(*) => return Allowable, + _ if m.vis == ast::public => return Allowable, + _ => m.vis + } + } + Some(&ast_map::node_trait_method(*)) => { + return Allowable; + } + + // This is not a method call, extract the visibility as one + // would normally look at it Some(&ast_map::node_item(it, _)) => it.vis, - Some(&ast_map::node_method(ref m, _, _)) => m.vis, Some(&ast_map::node_foreign_item(_, _, v, _)) => v, Some(&ast_map::node_variant(ref v, _, _)) => { // sadly enum variants still inherit visibility, so only @@ -369,11 +426,14 @@ impl<'self> PrivacyVisitor<'self> { _ => ast::public, }; if vis != ast::public { break } + // if we've reached the root, then everything was allowable and this + // access is public. + if closest_private_id == ast::CRATE_NODE_ID { return Allowable } closest_private_id = *self.parents.get(&closest_private_id); - // If we reached the top, then we should have been public all the - // way down in the first place... - assert!(closest_private_id != ast::DUMMY_NODE_ID); + // If we reached the top, then we were public all the way down and + // we can allow this access. + if closest_private_id == ast::DUMMY_NODE_ID { return Allowable } } debug!("privacy - closest priv {}", self.nodestr(closest_private_id)); if self.private_accessible(closest_private_id) { @@ -530,53 +590,226 @@ impl<'self> PrivacyVisitor<'self> { method_static(method_id) => { self.check_static_method(span, method_id, &ident) } - method_param(method_param { - trait_id: trait_id, - method_num: method_num, - _ - }) | - method_object(method_object { - trait_id: trait_id, - method_num: method_num, - _ - }) => { - if !self.ensure_public(span, trait_id, None, "source trait") { - return + // Trait methods are always all public. The only controlling factor + // is whether the trait itself is accessible or not. + method_param(method_param { trait_id: trait_id, _ }) | + method_object(method_object { trait_id: trait_id, _ }) => { + self.ensure_public(span, trait_id, None, "source trait"); + } + } + } +} + +impl<'self> Visitor<()> for PrivacyVisitor<'self> { + fn visit_item(&mut self, item: @ast::item, _: ()) { + // Do not check privacy inside items with the resolve_unexported + // attribute. This is used for the test runner. + if attr::contains_name(item.attrs, "!resolve_unexported") { + return; + } + + let orig_curitem = util::replace(&mut self.curitem, item.id); + visit::walk_item(self, item, ()); + self.curitem = orig_curitem; + } + + fn visit_expr(&mut self, expr: @ast::Expr, _: ()) { + match expr.node { + ast::ExprField(base, ident, _) => { + // Method calls are now a special syntactic form, + // so `a.b` should always be a field. + assert!(!self.method_map.contains_key(&expr.id)); + + // With type_autoderef, make sure we don't + // allow pointers to violate privacy + let t = ty::type_autoderef(self.tcx, + ty::expr_ty(self.tcx, base)); + match ty::get(t).sty { + ty::ty_struct(id, _) => self.check_field(expr.span, id, ident), + _ => {} } - match self.tcx.items.find(&trait_id.node) { - Some(&ast_map::node_item(item, _)) => { - match item.node { - ast::item_trait(_, _, ref methods) => { - match methods[method_num] { - ast::provided(ref method) => { - let def = ast::DefId { - node: method.id, - crate: trait_id.crate, - }; - self.ensure_public(span, def, None, - format!("method `{}`", - token::ident_to_str( - &method.ident))); - } - ast::required(_) => { - // Required methods can't be private. - } - } + } + ast::ExprMethodCall(_, base, ident, _, _, _) => { + // see above + let t = ty::type_autoderef(self.tcx, + ty::expr_ty(self.tcx, base)); + match ty::get(t).sty { + ty::ty_enum(_, _) | ty::ty_struct(_, _) => { + let entry = match self.method_map.find(&expr.id) { + None => { + self.tcx.sess.span_bug(expr.span, + "method call not in \ + method map"); + } + Some(entry) => entry + }; + debug!("(privacy checking) checking impl method"); + self.check_method(expr.span, &entry.origin, ident); + } + _ => {} + } + } + ast::ExprPath(ref path) => { + self.check_path(expr.span, expr.id, path); + } + ast::ExprStruct(_, ref fields, _) => { + match ty::get(ty::expr_ty(self.tcx, expr)).sty { + ty::ty_struct(id, _) => { + for field in (*fields).iter() { + self.check_field(expr.span, id, field.ident.node); + } + } + ty::ty_enum(_, _) => { + match self.tcx.def_map.get_copy(&expr.id) { + ast::DefVariant(_, variant_id, _) => { + for field in fields.iter() { + self.check_field(expr.span, variant_id, + field.ident.node); + } + } + _ => self.tcx.sess.span_bug(expr.span, + "resolve didn't \ + map enum struct \ + constructor to a \ + variant def"), + } + } + _ => self.tcx.sess.span_bug(expr.span, "struct expr \ + didn't have \ + struct type?!"), + } + } + ast::ExprUnary(_, ast::UnDeref, operand) => { + // In *e, we need to check that if e's type is an + // enum type t, then t's first variant is public or + // privileged. (We can assume it has only one variant + // since typeck already happened.) + match ty::get(ty::expr_ty(self.tcx, operand)).sty { + ty::ty_enum(id, _) => { + self.check_variant(expr.span, id); + } + _ => { /* No check needed */ } + } + } + _ => {} + } + + visit::walk_expr(self, expr, ()); + } + + fn visit_ty(&mut self, t: &ast::Ty, _: ()) { + match t.node { + ast::ty_path(ref path, _, id) => self.check_path(t.span, id, path), + _ => {} + } + visit::walk_ty(self, t, ()); + } + + fn visit_view_item(&mut self, a: &ast::view_item, _: ()) { + match a.node { + ast::view_item_extern_mod(*) => {} + ast::view_item_use(ref uses) => { + for vpath in uses.iter() { + match vpath.node { + ast::view_path_simple(_, ref path, id) | + ast::view_path_glob(ref path, id) => { + debug!("privacy - glob/simple {}", id); + self.check_path(vpath.span, id, path); + } + ast::view_path_list(_, ref list, _) => { + for pid in list.iter() { + debug!("privacy - list {}", pid.node.id); + let seg = ast::PathSegment { + identifier: pid.node.name, + lifetimes: opt_vec::Empty, + types: opt_vec::Empty, + }; + let segs = ~[seg]; + let path = ast::Path { + global: false, + span: pid.span, + segments: segs, + }; + self.check_path(pid.span, pid.node.id, &path); } - _ => self.tcx.sess.span_bug(span, "trait wasn't \ - actually a trait?!"), } } - Some(_) => self.tcx.sess.span_bug(span, "trait wasn't an \ - item?!"), - None => self.tcx.sess.span_bug(span, "trait item wasn't \ - found in the AST \ - map?!"), } } } } + fn visit_pat(&mut self, pattern: @ast::Pat, _: ()) { + match pattern.node { + ast::PatStruct(_, ref fields, _) => { + match ty::get(ty::pat_ty(self.tcx, pattern)).sty { + ty::ty_struct(id, _) => { + for field in fields.iter() { + self.check_field(pattern.span, id, field.ident); + } + } + ty::ty_enum(_, _) => { + match self.tcx.def_map.find(&pattern.id) { + Some(&ast::DefVariant(_, variant_id, _)) => { + for field in fields.iter() { + self.check_field(pattern.span, variant_id, + field.ident); + } + } + _ => self.tcx.sess.span_bug(pattern.span, + "resolve didn't \ + map enum struct \ + pattern to a \ + variant def"), + } + } + _ => self.tcx.sess.span_bug(pattern.span, + "struct pattern didn't have \ + struct type?!"), + } + } + _ => {} + } + + visit::walk_pat(self, pattern, ()); + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// The privacy sanity check visitor, ensures unnecessary visibility isn't here +//////////////////////////////////////////////////////////////////////////////// + +struct SanePrivacyVisitor { + tcx: ty::ctxt, + in_fn: bool, +} + +impl Visitor<()> for SanePrivacyVisitor { + fn visit_item(&mut self, item: @ast::item, _: ()) { + if self.in_fn { + self.check_all_inherited(item); + } else { + self.check_sane_privacy(item); + } + + let orig_in_fn = util::replace(&mut self.in_fn, match item.node { + ast::item_mod(*) => false, // modules turn privacy back on + _ => self.in_fn, // otherwise we inherit + }); + visit::walk_item(self, item, ()); + self.in_fn = orig_in_fn; + } + + fn visit_fn(&mut self, fk: &visit::fn_kind, fd: &ast::fn_decl, + b: &ast::Block, s: Span, n: ast::NodeId, _: ()) { + // This catches both functions and methods + let orig_in_fn = util::replace(&mut self.in_fn, true); + visit::walk_fn(self, fk, fd, b, s, n, ()); + self.in_fn = orig_in_fn; + } +} + +impl SanePrivacyVisitor { /// Validates all of the visibility qualifers placed on the item given. This /// ensures that there are no extraneous qualifiers that don't actually do /// anything. In theory these qualifiers wouldn't parse, but that may happen @@ -749,249 +982,57 @@ impl<'self> PrivacyVisitor<'self> { } } -impl<'self> Visitor<()> for PrivacyVisitor<'self> { - fn visit_item(&mut self, item: @ast::item, _: ()) { - // Do not check privacy inside items with the resolve_unexported - // attribute. This is used for the test runner. - if attr::contains_name(item.attrs, "!resolve_unexported") { - return; - } - - // Disallow unnecessary visibility qualifiers - if self.in_fn { - self.check_all_inherited(item); - } else { - self.check_sane_privacy(item); - } - - let orig_curitem = util::replace(&mut self.curitem, item.id); - let orig_in_fn = util::replace(&mut self.in_fn, match item.node { - ast::item_mod(*) => false, // modules turn privacy back on - _ => self.in_fn, // otherwise we inherit - }); - visit::walk_item(self, item, ()); - self.curitem = orig_curitem; - self.in_fn = orig_in_fn; - } - - fn visit_fn(&mut self, fk: &visit::fn_kind, fd: &ast::fn_decl, - b: &ast::Block, s: Span, n: ast::NodeId, _: ()) { - // This catches both functions and methods - let orig_in_fn = util::replace(&mut self.in_fn, true); - visit::walk_fn(self, fk, fd, b, s, n, ()); - self.in_fn = orig_in_fn; - } - - fn visit_expr(&mut self, expr: @ast::Expr, _: ()) { - match expr.node { - ast::ExprField(base, ident, _) => { - // Method calls are now a special syntactic form, - // so `a.b` should always be a field. - assert!(!self.method_map.contains_key(&expr.id)); - - // With type_autoderef, make sure we don't - // allow pointers to violate privacy - let t = ty::type_autoderef(self.tcx, - ty::expr_ty(self.tcx, base)); - match ty::get(t).sty { - ty::ty_struct(id, _) => self.check_field(expr.span, id, ident), - _ => {} - } - } - ast::ExprMethodCall(_, base, ident, _, _, _) => { - // see above - let t = ty::type_autoderef(self.tcx, - ty::expr_ty(self.tcx, base)); - match ty::get(t).sty { - ty::ty_enum(_, _) | ty::ty_struct(_, _) => { - let entry = match self.method_map.find(&expr.id) { - None => { - self.tcx.sess.span_bug(expr.span, - "method call not in \ - method map"); - } - Some(entry) => entry - }; - debug!("(privacy checking) checking impl method"); - self.check_method(expr.span, &entry.origin, ident); - } - _ => {} - } - } - ast::ExprPath(ref path) => { - self.check_path(expr.span, expr.id, path); - } - ast::ExprStruct(_, ref fields, _) => { - match ty::get(ty::expr_ty(self.tcx, expr)).sty { - ty::ty_struct(id, _) => { - for field in (*fields).iter() { - self.check_field(expr.span, id, field.ident.node); - } - } - ty::ty_enum(_, _) => { - match self.tcx.def_map.get_copy(&expr.id) { - ast::DefVariant(_, variant_id, _) => { - for field in fields.iter() { - self.check_field(expr.span, variant_id, - field.ident.node); - } - } - _ => self.tcx.sess.span_bug(expr.span, - "resolve didn't \ - map enum struct \ - constructor to a \ - variant def"), - } - } - _ => self.tcx.sess.span_bug(expr.span, "struct expr \ - didn't have \ - struct type?!"), - } - } - ast::ExprUnary(_, ast::UnDeref, operand) => { - // In *e, we need to check that if e's type is an - // enum type t, then t's first variant is public or - // privileged. (We can assume it has only one variant - // since typeck already happened.) - match ty::get(ty::expr_ty(self.tcx, operand)).sty { - ty::ty_enum(id, _) => { - self.check_variant(expr.span, id); - } - _ => { /* No check needed */ } - } - } - _ => {} - } - - visit::walk_expr(self, expr, ()); - } - - fn visit_ty(&mut self, t: &ast::Ty, _: ()) { - match t.node { - ast::ty_path(ref path, _, id) => self.check_path(t.span, id, path), - _ => {} - } - visit::walk_ty(self, t, ()); - } - - fn visit_view_item(&mut self, a: &ast::view_item, _: ()) { - match a.node { - ast::view_item_extern_mod(*) => {} - ast::view_item_use(ref uses) => { - for vpath in uses.iter() { - match vpath.node { - ast::view_path_simple(_, ref path, id) | - ast::view_path_glob(ref path, id) => { - debug!("privacy - glob/simple {}", id); - self.check_path(vpath.span, id, path); - } - ast::view_path_list(_, ref list, _) => { - for pid in list.iter() { - debug!("privacy - list {}", pid.node.id); - let seg = ast::PathSegment { - identifier: pid.node.name, - lifetimes: opt_vec::Empty, - types: opt_vec::Empty, - }; - let segs = ~[seg]; - let path = ast::Path { - global: false, - span: pid.span, - segments: segs, - }; - self.check_path(pid.span, pid.node.id, &path); - } - } - } - } - } - } - } - - fn visit_pat(&mut self, pattern: @ast::Pat, _: ()) { - match pattern.node { - ast::PatStruct(_, ref fields, _) => { - match ty::get(ty::pat_ty(self.tcx, pattern)).sty { - ty::ty_struct(id, _) => { - for field in fields.iter() { - self.check_field(pattern.span, id, field.ident); - } - } - ty::ty_enum(_, _) => { - match self.tcx.def_map.find(&pattern.id) { - Some(&ast::DefVariant(_, variant_id, _)) => { - for field in fields.iter() { - self.check_field(pattern.span, variant_id, - field.ident); - } - } - _ => self.tcx.sess.span_bug(pattern.span, - "resolve didn't \ - map enum struct \ - pattern to a \ - variant def"), - } - } - _ => self.tcx.sess.span_bug(pattern.span, - "struct pattern didn't have \ - struct type?!"), - } - } - _ => {} - } - - visit::walk_pat(self, pattern, ()); - } -} - pub fn check_crate(tcx: ty::ctxt, method_map: &method_map, exp_map2: &resolve::ExportMap2, external_exports: resolve::ExternalExports, last_private_map: resolve::LastPrivateMap, crate: &ast::Crate) -> ExportedItems { - let mut parents = HashMap::new(); - let mut path_all_public_items = HashSet::new(); - let mut exported_items = HashSet::new(); + // Figure out who everyone's parent is + let mut visitor = ParentVisitor { + parents: HashMap::new(), + curparent: ast::DUMMY_NODE_ID, + }; + visit::walk_crate(&mut visitor, crate, ()); - // First, figure out who everyone's parent is - { - let mut visitor = ParentVisitor { - parents: &mut parents, - curparent: ast::DUMMY_NODE_ID, - }; + // Use the parent map to check the privacy of everything + let mut visitor = PrivacyVisitor { + curitem: ast::DUMMY_NODE_ID, + in_fn: false, + tcx: tcx, + parents: visitor.parents, + method_map: method_map, + external_exports: external_exports, + last_private_map: last_private_map, + }; + visit::walk_crate(&mut visitor, crate, ()); + + // Sanity check to make sure that all privacy usage and controls are + // reasonable. + let mut visitor = SanePrivacyVisitor { + in_fn: false, + tcx: tcx, + }; + visit::walk_crate(&mut visitor, crate, ()); + + tcx.sess.abort_if_errors(); + + // Build up a set of all exported items in the AST. This is a set of all + // items which are reachable from external crates based on visibility. + let mut visitor = EmbargoVisitor { + tcx: tcx, + exported_items: HashSet::new(), + reexports: HashSet::new(), + exp_map2: exp_map2, + prev_exported: true, + }; + loop { + let before = visitor.exported_items.len(); visit::walk_crate(&mut visitor, crate, ()); + if before == visitor.exported_items.len() { + break + } } - // Next, build up the list of all exported items from this crate - { - let mut visitor = EmbargoVisitor { - tcx: tcx, - path_all_public_items: &mut path_all_public_items, - exported_items: &mut exported_items, - exp_map2: exp_map2, - path_all_public: true, // start out as public - }; - // Initialize the exported items with resolve's id for the "root crate" - // to resolve references to `super` leading to the root and such. - visitor.add_path_all_public_item(ast::CRATE_NODE_ID); - visit::walk_crate(&mut visitor, crate, ()); - } - - // And then actually check the privacy of everything. - { - let mut visitor = PrivacyVisitor { - curitem: ast::DUMMY_NODE_ID, - in_fn: false, - tcx: tcx, - path_all_public_items: &path_all_public_items, - parents: &parents, - method_map: method_map, - external_exports: external_exports, - last_private_map: last_private_map, - }; - visit::walk_crate(&mut visitor, crate, ()); - } - - return exported_items; + return visitor.exported_items; } diff --git a/src/librustc/middle/reachable.rs b/src/librustc/middle/reachable.rs index 0d6f6de47be..02ac4c52084 100644 --- a/src/librustc/middle/reachable.rs +++ b/src/librustc/middle/reachable.rs @@ -22,7 +22,7 @@ use middle::privacy; use std::hashmap::HashSet; use syntax::ast; use syntax::ast_map; -use syntax::ast_util::{def_id_of_def, is_local, local_def}; +use syntax::ast_util::{def_id_of_def, is_local}; use syntax::attr; use syntax::parse::token; use syntax::visit::Visitor; @@ -310,47 +310,13 @@ impl ReachableContext { } } - // Implementations of exported structs/enums need to get - // added to the worklist (as all their methods should be - // accessible) - ast::item_struct(*) | ast::item_enum(*) => { - let def = local_def(item.id); - let impls = match self.tcx.inherent_impls.find(&def) { - Some(&impls) => impls, - None => return - }; - for imp in impls.iter() { - if is_local(imp.did) { - self.worklist.push(imp.did.node); - } - } - } - - // Propagate through this impl - ast::item_impl(_, _, _, ref methods) => { - for method in methods.iter() { - self.worklist.push(method.id); - } - } - - // Default methods of exported traits need to all be - // accessible. - ast::item_trait(_, _, ref methods) => { - for method in methods.iter() { - match *method { - ast::required(*) => {} - ast::provided(ref method) => { - self.worklist.push(method.id); - } - } - } - } - // These are normal, nothing reachable about these // inherently and their children are already in the - // worklist + // worklist, as determined by the privacy pass ast::item_static(*) | ast::item_ty(*) | - ast::item_mod(*) | ast::item_foreign_mod(*) => {} + ast::item_mod(*) | ast::item_foreign_mod(*) | + ast::item_impl(*) | ast::item_trait(*) | + ast::item_struct(*) | ast::item_enum(*) => {} _ => { self.tcx.sess.span_bug(item.span, diff --git a/src/test/auxiliary/privacy_reexport.rs b/src/test/auxiliary/privacy_reexport.rs new file mode 100644 index 00000000000..7e0f7f3abfe --- /dev/null +++ b/src/test/auxiliary/privacy_reexport.rs @@ -0,0 +1,15 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub use bar = foo; + +mod foo { + pub fn frob() {} +} diff --git a/src/test/compile-fail/lint-missing-doc.rs b/src/test/compile-fail/lint-missing-doc.rs index c866462a3e9..10c27adde22 100644 --- a/src/test/compile-fail/lint-missing-doc.rs +++ b/src/test/compile-fail/lint-missing-doc.rs @@ -57,6 +57,11 @@ pub trait C { //~ ERROR: missing documentation #[allow(missing_doc)] pub trait D {} impl Foo { + pub fn foo() {} + fn bar() {} +} + +impl PubFoo { pub fn foo() {} //~ ERROR: missing documentation /// dox pub fn foo1() {} diff --git a/src/test/compile-fail/privacy1.rs b/src/test/compile-fail/privacy1.rs index a17e689f444..a2f8e78590f 100644 --- a/src/test/compile-fail/privacy1.rs +++ b/src/test/compile-fail/privacy1.rs @@ -31,6 +31,12 @@ mod bar { fn bar2(&self) {} } + trait B { + fn foo() -> Self; + } + + impl B for int { fn foo() -> int { 3 } } + pub enum Enum { priv Priv, Pub @@ -108,6 +114,10 @@ mod foo { ::bar::baz::A.bar2(); //~ ERROR: struct `A` is inaccessible //~^ ERROR: method `bar2` is private //~^^ NOTE: module `baz` is private + + let _: int = + ::bar::B::foo(); //~ ERROR: method `foo` is inaccessible + //~^ NOTE: trait `B` is private ::lol(); ::bar::Priv; //~ ERROR: variant `Priv` is private diff --git a/src/test/run-pass/privacy-reexport.rs b/src/test/run-pass/privacy-reexport.rs new file mode 100644 index 00000000000..eedc47ca0ad --- /dev/null +++ b/src/test/run-pass/privacy-reexport.rs @@ -0,0 +1,18 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// xfail-fast +// aux-build:privacy_reexport.rs + +extern mod privacy_reexport; + +fn main() { + privacy_reexport::bar::frob(); +}