From 439e2770be6aec41a3961235305787a78d47fbdd Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Sat, 5 Oct 2013 14:37:39 -0700 Subject: [PATCH] Extract privacy checking from name resolution This commit is the culmination of my recent effort to refine Rust's notion of privacy and visibility among crates. The major goals of this commit were to remove privacy checking from resolve for the sake of sane error messages, and to attempt a much more rigid and well-tested implementation of visibility throughout rust. The implemented rules for name visibility are: 1. Everything pub from the root namespace is visible to anyone 2. You may access any private item of your ancestors. "Accessing a private item" depends on what the item is, so for a function this means that you can call it, but for a module it means that you can look inside of it. Once you look inside a private module, any accessed item must be "pub from the root" where the new root is the private module that you looked into. These rules required some more analysis results to get propagated from trans to privacy in the form of a few hash tables. I added a new test in which my goal was to showcase all of the privacy nuances of the language, and I hope to place any new bugs into this file to prevent regressions. Overall, I was unable to completely remove the notion of privacy from resolve. One use of privacy is for dealing with glob imports. Essentially a glob import can only import *public* items from the destination, and because this must be done at namespace resolution time, resolve must maintain the notion of "what items are public in a module". There are some sad approximations of privacy, but I unfortunately can't see clear methods to extract them outside. The other use case of privacy in resolve now is one that must stick around regardless of glob imports. When dealing with privacy, checking a private path needs to know "what the last private thing was" when looking at a path. Resolve is the only compiler pass which knows the answer to this question, so it maintains the answer on a per-path resolution basis (works similarly to the def_map generated). Closes #8215 --- src/librustc/driver/driver.rs | 13 +- src/librustc/metadata/decoder.rs | 5 +- src/librustc/metadata/encoder.rs | 13 +- src/librustc/middle/privacy.rs | 1390 +++++++++++++----------- src/librustc/middle/resolve.rs | 965 ++++++++-------- src/librustc/middle/trans/base.rs | 2 - src/librustc/middle/trans/context.rs | 3 - src/libsyntax/ast_util.rs | 26 - src/test/compile-fail/glob-resolve1.rs | 45 + src/test/compile-fail/privacy1.rs | 168 +++ src/test/compile-fail/privacy2.rs | 37 + src/test/compile-fail/privacy3.rs | 32 + src/test/compile-fail/privacy4.rs | 31 + 13 files changed, 1543 insertions(+), 1187 deletions(-) create mode 100644 src/test/compile-fail/glob-resolve1.rs create mode 100644 src/test/compile-fail/privacy1.rs create mode 100644 src/test/compile-fail/privacy2.rs create mode 100644 src/test/compile-fail/privacy3.rs create mode 100644 src/test/compile-fail/privacy4.rs diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs index 5534342f3c7..4681c37fce8 100644 --- a/src/librustc/driver/driver.rs +++ b/src/librustc/driver/driver.rs @@ -199,7 +199,6 @@ pub fn phase_2_configure_and_expand(sess: Session, pub struct CrateAnalysis { exp_map2: middle::resolve::ExportMap2, - exported_items: @middle::privacy::ExportedItems, ty_cx: ty::ctxt, maps: astencode::Maps, reachable: @mut HashSet @@ -229,7 +228,9 @@ pub fn phase_3_run_analysis_passes(sess: Session, let middle::resolve::CrateMap { def_map: def_map, exp_map2: exp_map2, - trait_map: trait_map + trait_map: trait_map, + external_exports: external_exports, + last_private_map: last_private_map } = time(time_passes, "resolution", (), |_| middle::resolve::resolve_crate(sess, lang_items, crate)); @@ -261,9 +262,10 @@ pub fn phase_3_run_analysis_passes(sess: Session, middle::check_const::check_crate(sess, crate, ast_map, def_map, method_map, ty_cx)); - let exported_items = - time(time_passes, "privacy checking", (), |_| - middle::privacy::check_crate(ty_cx, &method_map, &exp_map2, crate)); + let maps = (external_exports, last_private_map); + time(time_passes, "privacy checking", maps, |(a, b)| + middle::privacy::check_crate(ty_cx, &method_map, &exp_map2, + a, b, crate)); time(time_passes, "effect checking", (), |_| middle::effect::check_crate(ty_cx, method_map, crate)); @@ -305,7 +307,6 @@ pub fn phase_3_run_analysis_passes(sess: Session, CrateAnalysis { exp_map2: exp_map2, - exported_items: @exported_items, ty_cx: ty_cx, maps: astencode::Maps { root_map: root_map, diff --git a/src/librustc/metadata/decoder.rs b/src/librustc/metadata/decoder.rs index 5c6a7c4f3b7..be01d3cbfc9 100644 --- a/src/librustc/metadata/decoder.rs +++ b/src/librustc/metadata/decoder.rs @@ -837,8 +837,9 @@ fn each_child_of_item_or_crate(intr: @ident_interner, let def_like = item_to_def_like(child_item_doc, child_def_id, cdata.cnum); - callback(def_like, token::str_to_ident(name), - item_visibility(child_item_doc)); + // These items have a public visibility because they're part of + // a public re-export. + callback(def_like, token::str_to_ident(name), ast::public); } } diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs index e88ee701051..ad190dfcfb1 100644 --- a/src/librustc/metadata/encoder.rs +++ b/src/librustc/metadata/encoder.rs @@ -58,7 +58,6 @@ pub struct EncodeParams<'self> { diag: @mut span_handler, tcx: ty::ctxt, reexports2: middle::resolve::ExportMap2, - exported_items: @middle::privacy::ExportedItems, item_symbols: &'self HashMap, discrim_symbols: &'self HashMap, non_inlineable_statics: &'self HashSet, @@ -89,7 +88,6 @@ pub struct EncodeContext<'self> { tcx: ty::ctxt, stats: @mut Stats, reexports2: middle::resolve::ExportMap2, - exported_items: @middle::privacy::ExportedItems, item_symbols: &'self HashMap, discrim_symbols: &'self HashMap, non_inlineable_statics: &'self HashSet, @@ -1277,12 +1275,7 @@ fn my_visit_item(i:@item, items: ast_map::map, ebml_w:&writer::Encoder, let mut ebml_w = ebml_w.clone(); // See above let ecx : &EncodeContext = unsafe { cast::transmute(ecx_ptr) }; - let vis = if ecx.exported_items.contains(&i.id) { - ast::public - } else { - ast::inherited - }; - encode_info_for_item(ecx, &mut ebml_w, i, index, *pt, vis); + encode_info_for_item(ecx, &mut ebml_w, i, index, *pt, i.vis); } _ => fail2!("bad item") } @@ -1628,7 +1621,7 @@ impl<'self> Visitor<()> for ImplVisitor<'self> { // Load eagerly if this is an implementation of the Drop trait // or if the trait is not defined in this crate. - if def_id == self.ecx.tcx.lang_items.drop_trait().unwrap() || + if Some(def_id) == self.ecx.tcx.lang_items.drop_trait() || def_id.crate != LOCAL_CRATE { self.ebml_w.start_tag(tag_impls_impl); encode_def_id(self.ebml_w, local_def(item.id)); @@ -1744,7 +1737,6 @@ pub fn encode_metadata(parms: EncodeParams, crate: &Crate) -> ~[u8] { diag, tcx, reexports2, - exported_items, discrim_symbols, cstore, encode_inlined_item, @@ -1760,7 +1752,6 @@ pub fn encode_metadata(parms: EncodeParams, crate: &Crate) -> ~[u8] { tcx: tcx, stats: stats, reexports2: reexports2, - exported_items: exported_items, item_symbols: item_symbols, discrim_symbols: discrim_symbols, non_inlineable_statics: non_inlineable_statics, diff --git a/src/librustc/middle/privacy.rs b/src/librustc/middle/privacy.rs index 7088091e192..f395231124b 100644 --- a/src/librustc/middle/privacy.rs +++ b/src/librustc/middle/privacy.rs @@ -12,384 +12,126 @@ //! outside their scopes. This pass will also generate a set of exported items //! which are available for use externally when compiled as a library. -use std::hashmap::HashSet; +use std::hashmap::{HashSet, HashMap}; -use metadata::csearch; -use middle::resolve::ExportMap2; -use middle::ty::{ty_struct, ty_enum}; +use middle::resolve; use middle::ty; use middle::typeck::{method_map, method_origin, method_param}; use middle::typeck::{method_static, method_object}; -use std::util::ignore; -use syntax::ast::{DeclItem, Def, DefFn, DefId, DefStaticMethod}; -use syntax::ast::{DefVariant, ExprField, ExprMethodCall, ExprPath}; -use syntax::ast::{ExprStruct, ExprUnary, Ident, inherited, item_enum}; -use syntax::ast::{item_foreign_mod, item_fn, item_impl, item_struct}; -use syntax::ast::{item_trait, LOCAL_CRATE, NodeId, PatStruct, Path}; -use syntax::ast::{private, provided, public, required, StmtDecl, visibility}; use syntax::ast; -use syntax::ast_map::{node_foreign_item, node_item, node_method}; -use syntax::ast_map::{node_trait_method}; use syntax::ast_map; -use syntax::ast_util::{Private, Public, is_local}; -use syntax::ast_util::{variant_visibility_to_privacy, visibility_to_privacy}; +use syntax::ast_util::is_local; use syntax::attr; use syntax::codemap::Span; use syntax::parse::token; +use syntax::opt_vec; use syntax::visit; use syntax::visit::Visitor; -use syntax::ast::{_mod,Expr,item,Block,Pat}; -// This set is a set of all item nodes which can be used by external crates if -// we're building a library. The necessary qualifications for this are that all -// items leading down to the current item (excluding an `impl`) must be `pub`. -pub type ExportedItems = HashSet; +type Context<'self> = (&'self method_map, &'self resolve::ExportMap2); -type Context<'self> = (&'self method_map, &'self ExportMap2); +// 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, + curparent: ast::NodeId, +} -struct PrivacyVisitor { - tcx: ty::ctxt, - privileged_items: @mut ~[NodeId], +impl<'self> Visitor<()> for ParentVisitor<'self> { + fn visit_item(&mut self, item: @ast::item, _: ()) { + self.parents.insert(item.id, self.curparent); - // A set of all items which are re-exported to be used across crates - exported_items: ExportedItems, + let prev = self.curparent; + match item.node { + ast::item_mod(*) => { self.curparent = item.id; } + // Enum variants are parented to the enum definition itself beacuse + // they inherit privacy + ast::item_enum(ref def, _) => { + for variant in def.variants.iter() { + // If variants are private, then their logical "parent" is + // the enclosing module because everyone in the enclosing + // module can still use the private variant + if variant.node.vis == ast::private { + self.parents.insert(variant.node.id, self.curparent); - // A flag as to whether the current path is public all the way down to the - // current point or not + // Otherwise, if the variant is public, then the parent is + // considered the enclosing enum because the enum will + // dictate the privacy visibility of this variant instead. + } else { + self.parents.insert(variant.node.id, item.id); + } + } + } + _ => {} + } + visit::walk_item(self, item, ()); + self.curparent = prev; + } + + fn visit_trait_method(&mut self, m: &ast::trait_method, _: ()) { + match *m { + ast::provided(ref m) => self.parents.insert(m.id, self.curparent), + ast::required(ref m) => self.parents.insert(m.id, self.curparent), + }; + visit::walk_trait_method(self, m, ()); + } + + fn visit_foreign_item(&mut self, a: @ast::foreign_item, _: ()) { + self.parents.insert(a.id, self.curparent); + visit::walk_foreign_item(self, a, ()); + } + + fn visit_fn(&mut self, a: &visit::fn_kind, b: &ast::fn_decl, + c: &ast::Block, d: Span, id: ast::NodeId, _: ()) { + self.parents.insert(id, self.curparent); + visit::walk_fn(self, a, b, c, d, id, ()); + } + + fn visit_struct_def(&mut self, s: @ast::struct_def, i: ast::Ident, + g: &ast::Generics, n: ast::NodeId, _: ()) { + // Struct constructors are parented to their struct definitions because + // they essentially are the struct definitions. + match s.ctor_id { + Some(id) => { self.parents.insert(id, n); } + None => {} + } + + // While we have the id of the struct definition, go ahead and parent + // all the fields. + for field in s.fields.iter() { + let vis = match field.node.kind { + ast::named_field(_, vis) => vis, + ast::unnamed_field => continue + }; + + // Private fields are scoped to this module, so parent them directly + // to the module instead of the struct. This is similar to the case + // of private enum variants. + if vis == ast::private { + self.parents.insert(field.node.id, self.curparent); + + // Otherwise public fields are scoped to the visibility of the + // struct itself + } else { + self.parents.insert(field.node.id, n); + } + } + visit::walk_struct_def(self, s, i, g, n, ()) + } +} + +// This visitor is used to determine which items of the ast are embargoed, +// otherwise known as not exported. +struct EmbargoVisitor<'self> { + exported_items: &'self mut HashSet, + exp_map2: &'self resolve::ExportMap2, path_all_public: bool, } -impl PrivacyVisitor { - // Adds an item to its scope. - fn add_privileged_item(&mut self, item: @ast::item, count: &mut uint) { - match item.node { - item_struct(*) | item_trait(*) | item_enum(*) | - item_fn(*) => { - self.privileged_items.push(item.id); - *count += 1; - } - item_impl(_, _, _, ref methods) => { - for method in methods.iter() { - self.privileged_items.push(method.id); - *count += 1; - } - self.privileged_items.push(item.id); - *count += 1; - } - item_foreign_mod(ref foreign_mod) => { - for foreign_item in foreign_mod.items.iter() { - self.privileged_items.push(foreign_item.id); - *count += 1; - } - } - _ => {} - } - } - - // Adds items that are privileged to this scope. - fn add_privileged_items(&mut self, items: &[@ast::item]) -> uint { - let mut count = 0; - for &item in items.iter() { - self.add_privileged_item(item, &mut count); - } - count - } - - // Checks that an enum variant is in scope - fn check_variant(&mut self, span: Span, enum_id: ast::DefId) { - let variant_info = ty::enum_variants(self.tcx, enum_id)[0]; - let parental_privacy = if is_local(enum_id) { - let parent_vis = ast_map::node_item_query(self.tcx.items, - enum_id.node, - |it| { it.vis }, - ~"unbound enum parent when checking \ - dereference of enum type"); - visibility_to_privacy(parent_vis) - } - else { - // WRONG - Public - }; - debug2!("parental_privacy = {:?}", parental_privacy); - debug2!("vis = {:?}, priv = {:?}", - variant_info.vis, - visibility_to_privacy(variant_info.vis)) - // inherited => privacy of the enum item - if variant_visibility_to_privacy(variant_info.vis, - parental_privacy == Public) - == Private { - self.tcx.sess.span_err(span, - "can only dereference enums \ - with a single, public variant"); - } - } - - // Returns true if a crate-local method is private and false otherwise. - fn method_is_private(&mut self, span: Span, method_id: NodeId) -> bool { - let check = |vis: visibility, container_id: DefId| { - let mut is_private = false; - if vis == private { - is_private = true; - } else if vis == public { - is_private = false; - } else { - // Look up the enclosing impl. - if container_id.crate != LOCAL_CRATE { - self.tcx.sess.span_bug(span, - "local method isn't in local \ - impl?!"); - } - - match self.tcx.items.find(&container_id.node) { - Some(&node_item(item, _)) => { - match item.node { - item_impl(_, None, _, _) - if item.vis != public => { - is_private = true; - } - _ => {} - } - } - Some(_) => { - self.tcx.sess.span_bug(span, "impl wasn't an item?!"); - } - None => { - self.tcx.sess.span_bug(span, "impl wasn't in AST map?!"); - } - } - } - - is_private - }; - - match self.tcx.items.find(&method_id) { - Some(&node_method(method, impl_id, _)) => { - check(method.vis, impl_id) - } - Some(&node_trait_method(trait_method, trait_id, _)) => { - match *trait_method { - required(_) => check(public, trait_id), - provided(method) => check(method.vis, trait_id), - } - } - Some(_) => { - self.tcx.sess.span_bug(span, - format!("method_is_private: method was a {}?!", - ast_map::node_id_to_str( - self.tcx.items, - method_id, - token::get_ident_interner()))); - } - None => { - self.tcx.sess.span_bug(span, "method not found in \ - AST map?!"); - } - } - } - - // Returns true if the given local item is private and false otherwise. - fn local_item_is_private(&mut self, span: Span, item_id: NodeId) -> bool { - let mut f: &fn(NodeId) -> bool = |_| false; - f = |item_id| { - match self.tcx.items.find(&item_id) { - Some(&node_item(item, _)) => item.vis != public, - Some(&node_foreign_item(*)) => false, - Some(&node_method(method, impl_did, _)) => { - match method.vis { - private => true, - public => false, - inherited => f(impl_did.node) - } - } - Some(&node_trait_method(_, trait_did, _)) => f(trait_did.node), - Some(_) => { - self.tcx.sess.span_bug(span, - format!("local_item_is_private: item was \ - a {}?!", - ast_map::node_id_to_str( - self.tcx.items, - item_id, - token::get_ident_interner()))); - } - None => { - self.tcx.sess.span_bug(span, "item not found in AST map?!"); - } - } - }; - f(item_id) - } - - // Checks that a private field is in scope. - // FIXME #6993: change type (and name) from Ident to Name - fn check_field(&mut self, span: Span, id: ast::DefId, ident: ast::Ident) { - let fields = ty::lookup_struct_fields(self.tcx, id); - for field in fields.iter() { - if field.name != ident.name { continue; } - if field.vis == private { - self.tcx.sess.span_err(span, format!("field `{}` is private", - token::ident_to_str(&ident))); - } - break; - } - } - - // Given the ID of a method, checks to ensure it's in scope. - fn check_method_common(&mut self, span: Span, method_id: DefId, name: &Ident) { - // If the method is a default method, we need to use the def_id of - // the default implementation. - // Having to do this this is really unfortunate. - let method_id = ty::method(self.tcx, method_id).provided_source.unwrap_or(method_id); - - if method_id.crate == LOCAL_CRATE { - let is_private = self.method_is_private(span, method_id.node); - let container_id = ty::method(self.tcx, method_id).container_id(); - if is_private && - (container_id.crate != LOCAL_CRATE || - !self.privileged_items.iter().any(|x| x == &(container_id.node))) { - self.tcx.sess.span_err(span, - format!("method `{}` is private", - token::ident_to_str(name))); - } - } else { - let visibility = - csearch::get_item_visibility(self.tcx.sess.cstore, method_id); - if visibility != public { - self.tcx.sess.span_err(span, - format!("method `{}` is private", - token::ident_to_str(name))); - } - } - } - - // Checks that a private path is in scope. - fn check_path(&mut self, span: Span, def: Def, path: &Path) { - debug2!("checking path"); - match def { - DefStaticMethod(method_id, _, _) => { - debug2!("found static method def, checking it"); - self.check_method_common(span, - method_id, - &path.segments.last().identifier) - } - DefFn(def_id, _) => { - if def_id.crate == LOCAL_CRATE { - if self.local_item_is_private(span, def_id.node) && - !self.privileged_items.iter().any(|x| x == &def_id.node) { - self.tcx.sess.span_err(span, - format!("function `{}` is private", - token::ident_to_str( - &path.segments - .last() - .identifier))); - } - //} else if csearch::get_item_visibility(self.tcx.sess.cstore, - // def_id) != public { - // self.tcx.sess.span_err(span, - // format!("function `{}` is private", - // token::ident_to_str( - // &path.segments - // .last() - // .identifier))); - } - // If this is a function from a non-local crate, then the - // privacy check is enforced during resolve. All public items - // will be tagged as such in the crate metadata and then usage - // of the private items will be blocked during resolve. Hence, - // if this isn't from the local crate, nothing to check. - } - _ => {} - } - } - - // Checks that a private method is in scope. - fn check_method(&mut self, span: Span, origin: &method_origin, ident: ast::Ident) { - match *origin { - method_static(method_id) => { - self.check_method_common(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 trait_id.crate == LOCAL_CRATE { - match self.tcx.items.find(&trait_id.node) { - Some(&node_item(item, _)) => { - match item.node { - item_trait(_, _, ref methods) => { - if method_num >= (*methods).len() { - self.tcx.sess.span_bug(span, - "method number out of range?!"); - } - match (*methods)[method_num] { - provided(method) - if method.vis == private && - !self.privileged_items.iter() - .any(|x| x == &(trait_id.node)) => { - self.tcx.sess.span_err(span, - format!("method `{}` is private", - token::ident_to_str(&method - .ident))); - } - provided(_) | required(_) => { - // Required methods can't be - // private. - } - } - } - _ => { - 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?!"); - } - } - } else { - // FIXME #4732: External crates. - } - } - } - } -} - -impl<'self> Visitor> for PrivacyVisitor { - - fn visit_mod(&mut self, the_module:&_mod, _:Span, _:NodeId, - cx: Context<'self>) { - - let n_added = self.add_privileged_items(the_module.items); - - visit::walk_mod(self, the_module, cx); - - do n_added.times { - ignore(self.privileged_items.pop()); - } - } - - fn visit_item(&mut self, item:@item, cx: Context<'self>) { - - // 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 - check_sane_privacy(self.tcx, item); - - // Keep track of whether this item is available for export or not. +impl<'self> Visitor<()> for EmbargoVisitor<'self> { + fn visit_item(&mut self, item: @ast::item, _: ()) { let orig_all_pub = self.path_all_public; match item.node { // impls/extern blocks do not break the "public chain" because they @@ -402,342 +144,672 @@ impl<'self> Visitor> for PrivacyVisitor { self.path_all_public = orig_all_pub && item.vis == ast::public; } } - debug2!("public path at {}: {}", item.id, self.path_all_public); if self.path_all_public { - debug2!("all the way public {}", item.id); self.exported_items.insert(item.id); - - // All re-exported items in a module which is public should also be - // public (in terms of how they should get encoded) - match item.node { - ast::item_mod(*) => { - let (_, exp_map2) = cx; - match exp_map2.find(&item.id) { - Some(exports) => { - for export in exports.iter() { - if export.reexport && is_local(export.def_id) { - debug2!("found reexported {:?}", export); - let id = export.def_id.node; - self.exported_items.insert(id); - } - } - } - None => {} - } - } - _ => {} - } } - visit::walk_item(self, item, cx); + 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 => { + for variant in def.variants.iter() { + if variant.node.vis != ast::private { + 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.exported_items.insert(method.id); + } + } + } + + // Trait implementation methods are all completely public + ast::item_impl(_, Some(*), _, ref methods) => { + for method in methods.iter() { + debug2!("exporting: {}", method.id); + self.exported_items.insert(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 => { + for method in methods.iter() { + match *method { + ast::provided(ref m) => { + debug2!("provided {}", m.id); + self.exported_items.insert(m.id); + } + ast::required(ref m) => { + debug2!("required {}", m.id); + self.exported_items.insert(m.id); + } + } + } + } + + // Default methods on traits are all public so long as the trait is + // public + ast::item_struct(ref def, _) if self.path_all_public => { + match def.ctor_id { + Some(id) => { self.exported_items.insert(id); } + None => {} + } + } + + _ => {} + } + + visit::walk_item(self, item, ()); self.path_all_public = orig_all_pub; } - fn visit_block(&mut self, block:&Block, cx: Context<'self>) { + fn visit_foreign_item(&mut self, a: @ast::foreign_item, _: ()) { + if self.path_all_public && a.vis == ast::public { + self.exported_items.insert(a.id); + } + } +} - // Gather up all the privileged items. - let mut n_added = 0; - for stmt in block.stmts.iter() { - match stmt.node { - StmtDecl(decl, _) => { - match decl.node { - DeclItem(item) => { - self.add_privileged_item(item, &mut n_added); - } - _ => {} +struct PrivacyVisitor<'self> { + tcx: ty::ctxt, + curitem: ast::NodeId, + + // Results of previous analyses necessary for privacy checking. + exported_items: &'self HashSet, + method_map: &'self method_map, + parents: &'self HashMap, + external_exports: resolve::ExternalExports, + last_private_map: resolve::LastPrivateMap, +} + +impl<'self> PrivacyVisitor<'self> { + // used when debugging + fn nodestr(&self, id: ast::NodeId) -> ~str { + ast_map::node_id_to_str(self.tcx.items, id, token::get_ident_interner()) + } + + // Determines whether the given definition is public from the point of view + // of the current item. + fn def_public(&self, did: ast::DefId) -> bool { + if !is_local(did) { + if self.external_exports.contains(&did) { + debug2!("privacy - {:?} was externally exported", did); + return true; + } + debug2!("privacy - is {:?} a public method", did); + return match self.tcx.methods.find(&did) { + Some(meth) => { + debug2!("privacy - well at least it's a method: {:?}", meth); + match meth.container { + ty::TraitContainer(id) => { + debug2!("privacy - recursing on trait {:?}", id); + self.def_public(id) } + ty::ImplContainer(id) => { + match ty::impl_trait_ref(self.tcx, id) { + Some(t) => { + debug2!("privacy - impl of trait {:?}", id); + self.def_public(t.def_id) + } + None => { + debug2!("privacy - found a method {:?}", + meth.vis); + meth.vis == ast::public + } + } + } + } + } + None => { + debug2!("privacy - nope, not even a method"); + false + } + }; + } else if self.exported_items.contains(&did.node) { + debug2!("privacy - exported item {}", self.nodestr(did.node)); + return true; + } + + debug2!("privacy - local {:?} not public all the way down", did); + // return quickly for things in the same module + if self.parents.find(&did.node) == self.parents.find(&self.curitem) { + debug2!("privacy - same parent, we're done here"); + return true; + } + + // We now know that there is at least one private member between the + // destination and the root. + let mut closest_private_id = did.node; + loop { + debug2!("privacy - examining {}", self.nodestr(closest_private_id)); + let vis = match self.tcx.items.find(&closest_private_id) { + 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 + // break out of this is explicitly private + if v.node.vis == ast::private { break } + ast::public // need to move up a level (to the enum) + } + _ => ast::public, + }; + if vis != ast::public { break } + 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); + } + debug2!("privacy - closest priv {}", self.nodestr(closest_private_id)); + return self.private_accessible(closest_private_id); + } + + /// For a local private node in the AST, this function will determine + /// whether the node is accessible by the current module that iteration is + /// inside. + fn private_accessible(&self, id: ast::NodeId) -> bool { + let parent = *self.parents.get(&id); + debug2!("privacy - accessible parent {}", self.nodestr(parent)); + + // After finding `did`'s closest private member, we roll ourselves back + // to see if this private member's parent is anywhere in our ancestry. + // By the privacy rules, we can access all of our ancestor's private + // members, so that's why we test the parent, and not the did itself. + let mut cur = self.curitem; + loop { + debug2!("privacy - questioning {}", self.nodestr(cur)); + match cur { + // If the relevant parent is in our history, then we're allowed + // to look inside any of our ancestor's immediate private items, + // so this access is valid. + x if x == parent => return true, + + // If we've reached the root, then we couldn't access this item + // in the first place + ast::DUMMY_NODE_ID => return false, + + // Keep going up + _ => {} + } + + cur = *self.parents.get(&cur); + } + } + + // Checks that a dereference of a univariant enum can occur. + fn check_variant(&self, span: Span, enum_id: ast::DefId) { + let variant_info = ty::enum_variants(self.tcx, enum_id)[0]; + if !self.def_public(variant_info.id) { + self.tcx.sess.span_err(span, "can only dereference enums \ + with a single, public variant"); + } + } + + // Checks that a field is in scope. + // FIXME #6993: change type (and name) from Ident to Name + fn check_field(&mut self, span: Span, id: ast::DefId, ident: ast::Ident) { + let fields = ty::lookup_struct_fields(self.tcx, id); + for field in fields.iter() { + if field.name != ident.name { continue; } + // public fields are public everywhere + if field.vis != ast::private { break } + if !is_local(field.id) || + !self.private_accessible(field.id.node) { + self.tcx.sess.span_err(span, format!("field `{}` is private", + token::ident_to_str(&ident))); + } + break; + } + } + + // Given the ID of a method, checks to ensure it's in scope. + fn check_static_method(&mut self, span: Span, method_id: ast::DefId, + name: &ast::Ident) { + // If the method is a default method, we need to use the def_id of + // the default implementation. + let method_id = ty::method(self.tcx, method_id).provided_source + .unwrap_or(method_id); + + if !self.def_public(method_id) { + debug2!("private: {:?}", method_id); + self.tcx.sess.span_err(span, format!("method `{}` is private", + token::ident_to_str(name))); + } + } + + // Checks that a path is in scope. + fn check_path(&mut self, span: Span, path_id: ast::NodeId, path: &ast::Path) { + debug2!("privacy - path {}", self.nodestr(path_id)); + let ck = |tyname: &str| { + let last_private = *self.last_private_map.get(&path_id); + debug2!("privacy - {:?}", last_private); + let public = match last_private { + resolve::AllPublic => true, + resolve::DependsOn(def) => self.def_public(def), + }; + if !public { + debug2!("denying {:?}", path); + let name = token::ident_to_str(&path.segments.last() + .identifier); + self.tcx.sess.span_err(span, + format!("{} `{}` is private", tyname, name)); + } + }; + match self.tcx.def_map.get_copy(&path_id) { + ast::DefStaticMethod(*) => ck("static method"), + ast::DefFn(*) => ck("function"), + ast::DefStatic(*) => ck("static"), + ast::DefVariant(*) => ck("variant"), + ast::DefTy(*) => ck("type"), + ast::DefTrait(*) => ck("trait"), + ast::DefStruct(*) => ck("struct"), + ast::DefMethod(_, Some(*)) => ck("trait method"), + ast::DefMethod(*) => ck("method"), + ast::DefMod(*) => ck("module"), + _ => {} + } + } + + // Checks that a method is in scope. + fn check_method(&mut self, span: Span, origin: &method_origin, + ident: ast::Ident) { + match *origin { + 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.def_public(trait_id) { + self.tcx.sess.span_err(span, "source trait is private"); + return; + } + 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, + }; + if self.def_public(def) { return } + let msg = format!("method `{}` is \ + private", + token::ident_to_str( + &method.ident)); + self.tcx.sess.span_err(span, msg); + } + ast::required(_) => { + // Required methods can't be private. + } + } + } + _ => 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?!"), + } + } + } + } + + /// 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 + /// later on down the road... + fn check_sane_privacy(&self, item: @ast::item) { + let tcx = self.tcx; + let check_inherited = |sp: Span, vis: ast::visibility, note: &str| { + if vis != ast::inherited { + tcx.sess.span_err(sp, "unnecessary visibility qualifier"); + if note.len() > 0 { + tcx.sess.span_note(sp, note); + } + } + }; + let check_not_priv = |sp: Span, vis: ast::visibility, note: &str| { + if vis == ast::private { + tcx.sess.span_err(sp, "unnecessary `priv` qualifier"); + if note.len() > 0 { + tcx.sess.span_note(sp, note); + } + } + }; + match item.node { + // implementations of traits don't need visibility qualifiers because + // that's controlled by having the trait in scope. + ast::item_impl(_, Some(*), _, ref methods) => { + check_inherited(item.span, item.vis, + "visibility qualifiers have no effect on trait \ + impls"); + for m in methods.iter() { + check_inherited(m.span, m.vis, ""); + } + } + + ast::item_impl(_, _, _, ref methods) => { + check_inherited(item.span, item.vis, + "place qualifiers on individual methods instead"); + for i in methods.iter() { + check_not_priv(i.span, i.vis, "functions are private by \ + default"); + } + } + ast::item_foreign_mod(ref fm) => { + check_inherited(item.span, item.vis, + "place qualifiers on individual functions \ + instead"); + for i in fm.items.iter() { + check_not_priv(i.span, i.vis, "functions are private by \ + default"); + } + } + + ast::item_enum(ref def, _) => { + for v in def.variants.iter() { + match v.node.vis { + ast::public => { + if item.vis == ast::public { + tcx.sess.span_err(v.span, "unnecessary `pub` \ + visibility"); + } + } + ast::private => { + if item.vis != ast::public { + tcx.sess.span_err(v.span, "unnecessary `priv` \ + visibility"); + } + } + ast::inherited => {} + } + } + } + + ast::item_struct(ref def, _) => { + for f in def.fields.iter() { + match f.node.kind { + ast::named_field(_, ast::public) => { + tcx.sess.span_err(f.span, "unnecessary `pub` \ + visibility"); + } + ast::named_field(_, ast::private) => { + // Fields should really be private by default... + } + ast::named_field(*) | ast::unnamed_field => {} + } + } + } + + ast::item_trait(_, _, ref methods) => { + for m in methods.iter() { + match *m { + ast::provided(ref m) => { + check_inherited(m.span, m.vis, + "unnecessary visibility"); + } + ast::required(*) => {} + } + } + } + + ast::item_static(*) | + ast::item_fn(*) | ast::item_mod(*) | ast::item_ty(*) | + ast::item_mac(*) => { + check_not_priv(item.span, item.vis, "items are private by \ + default"); + } + } + } +} + +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 + self.check_sane_privacy(item); + + let orig_curitem = self.curitem; + 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), + _ => {} + } + } + 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 + }; + debug2!("(privacy checking) checking impl method"); + self.check_method(expr.span, &entry.origin, ident); } _ => {} } } - - visit::walk_block(self, block, cx); - - do n_added.times { - ignore(self.privileged_items.pop()); + ast::ExprPath(ref path) => { + self.check_path(expr.span, expr.id, path); } - - } - - fn visit_expr(&mut self, expr:@Expr, cx: Context<'self>) { - let (method_map, _) = cx; - match expr.node { - ExprField(base, ident, _) => { - // Method calls are now a special syntactic form, - // so `a.b` should always be a field. - assert!(!method_map.contains_key(&expr.id)); - - // With type_autoderef, make sure we don't - // allow pointers to violate privacy - match ty::get(ty::type_autoderef(self.tcx, ty::expr_ty(self.tcx, - base))).sty { - ty_struct(id, _) - if id.crate != LOCAL_CRATE || !self.privileged_items.iter() - .any(|x| x == &(id.node)) => { - debug2!("(privacy checking) checking field access"); - self.check_field(expr.span, id, ident); - } - _ => {} - } - } - ExprMethodCall(_, base, ident, _, _, _) => { - // Ditto - match ty::get(ty::type_autoderef(self.tcx, ty::expr_ty(self.tcx, - base))).sty { - ty_enum(id, _) | - ty_struct(id, _) - if id.crate != LOCAL_CRATE || - !self.privileged_items.iter().any(|x| x == &(id.node)) => { - match method_map.find(&expr.id) { - None => { - self.tcx.sess.span_bug(expr.span, - "method call not in \ - method map"); - } - Some(ref entry) => { - debug2!("(privacy checking) checking \ - impl method"); - self.check_method(expr.span, &entry.origin, ident); - } - } - } - _ => {} - } - } - ExprPath(ref path) => { - self.check_path(expr.span, self.tcx.def_map.get_copy(&expr.id), path); - } - ExprStruct(_, ref fields, _) => { - match ty::get(ty::expr_ty(self.tcx, expr)).sty { - ty_struct(id, _) => { - if id.crate != LOCAL_CRATE || - !self.privileged_items.iter().any(|x| x == &(id.node)) { - for field in (*fields).iter() { - debug2!("(privacy checking) checking \ - field in struct literal"); - self.check_field(expr.span, id, field.ident); - } - } - } - ty_enum(id, _) => { - if id.crate != LOCAL_CRATE || - !self.privileged_items.iter().any(|x| x == &(id.node)) { - match self.tcx.def_map.get_copy(&expr.id) { - DefVariant(_, variant_id, _) => { - for field in (*fields).iter() { - debug2!("(privacy checking) \ - checking field in \ - struct variant \ - literal"); - self.check_field(expr.span, variant_id, field.ident); - } - } - _ => { - 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::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); } } - } - 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_enum(id, _) => { - if id.crate != LOCAL_CRATE || - !self.privileged_items.iter().any(|x| x == &(id.node)) { - self.check_variant(expr.span, id); - } - } - _ => { /* No check needed */ } - } - } - _ => {} - } - - visit::walk_expr(self, expr, cx); - - } - - fn visit_pat(&mut self, pattern:@Pat, cx: Context<'self>) { - - match pattern.node { - PatStruct(_, ref fields, _) => { - match ty::get(ty::pat_ty(self.tcx, pattern)).sty { - ty_struct(id, _) => { - if id.crate != LOCAL_CRATE || - !self.privileged_items.iter().any(|x| x == &(id.node)) { + ty::ty_enum(_, _) => { + match self.tcx.def_map.get_copy(&expr.id) { + ast::DefVariant(_, variant_id, _) => { for field in fields.iter() { - debug2!("(privacy checking) checking \ - struct pattern"); - self.check_field(pattern.span, id, field.ident); + self.check_field(expr.span, variant_id, + field.ident); } } + _ => self.tcx.sess.span_bug(expr.span, + "resolve didn't \ + map enum struct \ + constructor to a \ + variant def"), } - ty_enum(enum_id, _) => { - if enum_id.crate != LOCAL_CRATE || - !self.privileged_items.iter().any(|x| x == &enum_id.node) { - match self.tcx.def_map.find(&pattern.id) { - Some(&DefVariant(_, variant_id, _)) => { - for field in fields.iter() { - debug2!("(privacy checking) \ - checking field in \ - struct variant pattern"); - 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(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) => { + debug2!("privacy - glob/simple {}", id); + self.check_path(vpath.span, id, path); + } + ast::view_path_list(_, ref list, _) => { + for pid in list.iter() { + debug2!("privacy - list {}", pid.node.id); + let seg = ast::PathSegment { + identifier: pid.node.name, + lifetime: None, + 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(pattern.span, - "struct pattern didn't have \ - struct type?!"); - } } } - _ => {} } + } + } - visit::walk_pat(self, pattern, cx); + 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: &ExportMap2, - crate: &ast::Crate) -> ExportedItems { - let privileged_items = @mut ~[]; + exp_map2: &resolve::ExportMap2, + external_exports: resolve::ExternalExports, + last_private_map: resolve::LastPrivateMap, + crate: &ast::Crate) { + let mut parents = HashMap::new(); + let mut exported_items = HashSet::new(); - let mut visitor = PrivacyVisitor { - tcx: tcx, - privileged_items: privileged_items, - exported_items: HashSet::new(), - path_all_public: true, // start out as public - }; - visit::walk_crate(&mut visitor, crate, (method_map, exp_map2)); - return visitor.exported_items; -} + // First, figure out who everyone's parent is + { + let mut visitor = ParentVisitor { + parents: &mut parents, + curparent: ast::DUMMY_NODE_ID, + }; + visit::walk_crate(&mut visitor, crate, ()); + } -/// 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 -/// later on down the road... -fn check_sane_privacy(tcx: ty::ctxt, item: @ast::item) { - let check_inherited = |sp: Span, vis: ast::visibility, note: &str| { - if vis != ast::inherited { - tcx.sess.span_err(sp, "unnecessary visibility qualifier"); - if note.len() > 0 { - tcx.sess.span_note(sp, note); - } - } - }; - let check_not_priv = |sp: Span, vis: ast::visibility, note: &str| { - if vis == ast::private { - tcx.sess.span_err(sp, "unnecessary `priv` qualifier"); - if note.len() > 0 { - tcx.sess.span_note(sp, note); - } - } - }; - match item.node { - // implementations of traits don't need visibility qualifiers because - // that's controlled by having the trait in scope. - ast::item_impl(_, Some(*), _, ref methods) => { - check_inherited(item.span, item.vis, - "visibility qualifiers have no effect on trait impls"); - for m in methods.iter() { - check_inherited(m.span, m.vis, ""); - } - } + // Next, build up the list of all exported items from this crate + { + // Initialize the exported items with resolve's id for the "root crate" + // to resolve references to `super` leading to the root and such. + exported_items.insert(0); + let mut visitor = EmbargoVisitor { + exported_items: &mut exported_items, + exp_map2: exp_map2, + path_all_public: true, // start out as public + }; + visit::walk_crate(&mut visitor, crate, ()); + } - ast::item_impl(_, _, _, ref methods) => { - check_inherited(item.span, item.vis, - "place qualifiers on individual methods instead"); - for i in methods.iter() { - check_not_priv(i.span, i.vis, "functions are private by default"); - } - } - ast::item_foreign_mod(ref fm) => { - check_inherited(item.span, item.vis, - "place qualifiers on individual functions instead"); - for i in fm.items.iter() { - check_not_priv(i.span, i.vis, "functions are private by default"); - } - } - - ast::item_enum(ref def, _) => { - for v in def.variants.iter() { - match v.node.vis { - ast::public => { - if item.vis == ast::public { - tcx.sess.span_err(v.span, "unnecessary `pub` \ - visibility"); - } - } - ast::private => { - if item.vis != ast::public { - tcx.sess.span_err(v.span, "unnecessary `priv` \ - visibility"); - } - } - ast::inherited => {} - } - } - } - - ast::item_struct(ref def, _) => { - for f in def.fields.iter() { - match f.node.kind { - ast::named_field(_, ast::public) => { - tcx.sess.span_err(f.span, "unnecessary `pub` \ - visibility"); - } - ast::named_field(_, ast::private) => { - // Fields should really be private by default... - } - ast::named_field(*) | ast::unnamed_field => {} - } - } - } - - ast::item_trait(_, _, ref methods) => { - for m in methods.iter() { - match *m { - ast::provided(ref m) => { - check_inherited(m.span, m.vis, - "unnecessary visibility"); - } - ast::required(*) => {} - } - } - } - - ast::item_static(*) | - ast::item_fn(*) | ast::item_mod(*) | ast::item_ty(*) | - ast::item_mac(*) => { - check_not_priv(item.span, item.vis, "items are private by default"); - } + // And then actually check the privacy of everything. + { + let mut visitor = PrivacyVisitor { + curitem: ast::DUMMY_NODE_ID, + tcx: tcx, + exported_items: &exported_items, + parents: &parents, + method_map: method_map, + external_exports: external_exports, + last_private_map: last_private_map, + }; + visit::walk_crate(&mut visitor, crate, ()); } } diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs index 646c80a0a76..447f1075585 100644 --- a/src/librustc/middle/resolve.rs +++ b/src/librustc/middle/resolve.rs @@ -25,8 +25,6 @@ use syntax::ast::*; use syntax::ast; use syntax::ast_util::{def_id_of_def, local_def, mtwt_resolve}; use syntax::ast_util::{path_to_ident, walk_pat, trait_method_to_ty_method}; -use syntax::ast_util::{Privacy, Public, Private}; -use syntax::ast_util::{variant_visibility_to_privacy, visibility_to_privacy}; use syntax::attr; use syntax::parse::token; use syntax::parse::token::{ident_interner, interner_get}; @@ -71,6 +69,27 @@ pub struct Export2 { reexport: bool, // Whether this is a reexport. } +// This set contains all exported definitions from external crates. The set does +// not contain any entries from local crates. +pub type ExternalExports = HashSet; + +// XXX: dox +pub type LastPrivateMap = HashMap; + +pub enum LastPrivate { + AllPublic, + DependsOn(DefId), +} + +impl LastPrivate { + fn or(self, other: LastPrivate) -> LastPrivate { + match (self, other) { + (me, AllPublic) => me, + (_, other) => other, + } + } +} + #[deriving(Eq)] enum PatternBindingMode { RefutableMode, @@ -119,8 +138,8 @@ impl NamespaceResult { enum NameDefinition { NoNameDefinition, //< The name was unbound. - ChildNameDefinition(Def), //< The name identifies an immediate child. - ImportNameDefinition(Def) //< The name identifies an import. + ChildNameDefinition(Def, LastPrivate), //< The name identifies an immediate child. + ImportNameDefinition(Def, LastPrivate) //< The name identifies an import. } #[deriving(Eq)] @@ -275,21 +294,13 @@ enum NameSearchType { ImportSearch, /// We're doing a name search in order to resolve a path type, a path - /// expression, or a path pattern. We can select public or private - /// names. - /// - /// XXX: This should be ripped out of resolve and handled later, in - /// the privacy checking phase. - PathPublicOrPrivateSearch, - - /// We're doing a name search in order to resolve a path type, a path - /// expression, or a path pattern. Allow only public names to be selected. - PathPublicOnlySearch, + /// expression, or a path pattern. + PathSearch, } enum BareIdentifierPatternResolution { - FoundStructOrEnumVariant(Def), - FoundConst(Def), + FoundStructOrEnumVariant(Def, LastPrivate), + FoundConst(Def, LastPrivate), BareIdentifierPatternUnresolved } @@ -323,26 +334,26 @@ impl Rib { /// One import directive. struct ImportDirective { - privacy: Privacy, module_path: ~[Ident], subclass: @ImportDirectiveSubclass, span: Span, id: NodeId, + is_public: bool, // see note in ImportResolution about how to use this } impl ImportDirective { - fn new(privacy: Privacy, - module_path: ~[Ident], - subclass: @ImportDirectiveSubclass, - span: Span, - id: NodeId) - -> ImportDirective { + fn new(module_path: ~[Ident], + subclass: @ImportDirectiveSubclass, + span: Span, + id: NodeId, + is_public: bool) + -> ImportDirective { ImportDirective { - privacy: privacy, module_path: module_path, subclass: subclass, span: span, - id: id + id: id, + is_public: is_public, } } } @@ -366,9 +377,11 @@ impl Target { /// An ImportResolution represents a particular `use` directive. struct ImportResolution { - /// The privacy of this `use` directive (whether it's `use` or - /// `pub use`. - privacy: Privacy, + /// Whether this resolution came from a `use` or a `pub use`. Note that this + /// should *not* be used whenever resolution is being performed, this is + /// only looked at for glob imports statements currently. Privacy testing + /// occurs during a later phase of compilation. + is_public: bool, // The number of outstanding references to this name. When this reaches // zero, outside modules can count on the targets being correct. Before @@ -389,15 +402,14 @@ struct ImportResolution { } impl ImportResolution { - fn new(privacy: Privacy, - id: NodeId) -> ImportResolution { + fn new(id: NodeId, is_public: bool) -> ImportResolution { ImportResolution { - privacy: privacy, type_id: id, value_id: id, outstanding_references: 0, value_target: None, type_target: None, + is_public: is_public, } } @@ -439,6 +451,7 @@ struct Module { parent_link: ParentLink, def_id: Option, kind: ModuleKind, + is_public: bool, children: @mut HashMap, imports: @mut ~[@ImportDirective], @@ -480,14 +493,16 @@ struct Module { impl Module { fn new(parent_link: ParentLink, - def_id: Option, - kind: ModuleKind, - external: bool) + def_id: Option, + kind: ModuleKind, + external: bool, + is_public: bool) -> Module { Module { parent_link: parent_link, def_id: def_id, kind: kind, + is_public: is_public, children: @mut HashMap::new(), imports: @mut ~[], external_module_children: @mut HashMap::new(), @@ -507,7 +522,7 @@ impl Module { // Records a possibly-private type definition. struct TypeNsDef { - privacy: Privacy, + is_public: bool, // see note in ImportResolution about how to use this module_def: Option<@mut Module>, type_def: Option, type_span: Option @@ -515,7 +530,7 @@ struct TypeNsDef { // Records a possibly-private value definition. struct ValueNsDef { - privacy: Privacy, + is_public: bool, // see note in ImportResolution about how to use this def: Def, value_span: Option, } @@ -537,18 +552,19 @@ enum TraitReferenceType { impl NameBindings { /// Creates a new module in this set of name bindings. fn define_module(&mut self, - privacy: Privacy, - parent_link: ParentLink, - def_id: Option, - kind: ModuleKind, - external: bool, - sp: Span) { + parent_link: ParentLink, + def_id: Option, + kind: ModuleKind, + external: bool, + is_public: bool, + sp: Span) { // Merges the module with the existing type def or creates a new one. - let module_ = @mut Module::new(parent_link, def_id, kind, external); + let module_ = @mut Module::new(parent_link, def_id, kind, external, + is_public); match self.type_def { None => { self.type_def = Some(TypeNsDef { - privacy: privacy, + is_public: is_public, module_def: Some(module_), type_def: None, type_span: Some(sp) @@ -556,7 +572,7 @@ impl NameBindings { } Some(type_def) => { self.type_def = Some(TypeNsDef { - privacy: privacy, + is_public: is_public, module_def: Some(module_), type_span: Some(sp), type_def: type_def.type_def @@ -567,17 +583,18 @@ impl NameBindings { /// Sets the kind of the module, creating a new one if necessary. fn set_module_kind(&mut self, - privacy: Privacy, - parent_link: ParentLink, - def_id: Option, - kind: ModuleKind, - external: bool, - _sp: Span) { + parent_link: ParentLink, + def_id: Option, + kind: ModuleKind, + external: bool, + is_public: bool, + _sp: Span) { match self.type_def { None => { - let module = @mut Module::new(parent_link, def_id, kind, external); + let module = @mut Module::new(parent_link, def_id, kind, + external, is_public); self.type_def = Some(TypeNsDef { - privacy: privacy, + is_public: is_public, module_def: Some(module), type_def: None, type_span: None, @@ -587,11 +604,12 @@ impl NameBindings { match type_def.module_def { None => { let module = @mut Module::new(parent_link, - def_id, - kind, - external); + def_id, + kind, + external, + is_public); self.type_def = Some(TypeNsDef { - privacy: privacy, + is_public: is_public, module_def: Some(module), type_def: type_def.type_def, type_span: None, @@ -604,31 +622,32 @@ impl NameBindings { } /// Records a type definition. - fn define_type(&mut self, privacy: Privacy, def: Def, sp: Span) { + fn define_type(&mut self, def: Def, sp: Span, is_public: bool) { // Merges the type with the existing type def or creates a new one. match self.type_def { None => { self.type_def = Some(TypeNsDef { - privacy: privacy, module_def: None, type_def: Some(def), - type_span: Some(sp) + type_span: Some(sp), + is_public: is_public, }); } Some(type_def) => { self.type_def = Some(TypeNsDef { - privacy: privacy, type_def: Some(def), type_span: Some(sp), - module_def: type_def.module_def + module_def: type_def.module_def, + is_public: is_public, }); } } } /// Records a value definition. - fn define_value(&mut self, privacy: Privacy, def: Def, sp: Span) { - self.value_def = Some(ValueNsDef { privacy: privacy, def: def, value_span: Some(sp) }); + fn define_value(&mut self, def: Def, sp: Span, is_public: bool) { + self.value_def = Some(ValueNsDef { def: def, value_span: Some(sp), + is_public: is_public }); } /// Returns the module node if applicable. @@ -663,12 +682,10 @@ impl NameBindings { fn defined_in_public_namespace(&self, namespace: Namespace) -> bool { match namespace { TypeNS => match self.type_def { - Some(def) => def.privacy != Private, - None => false + Some(def) => def.is_public, None => false }, ValueNS => match self.value_def { - Some(def) => def.privacy != Private, - None => false + Some(def) => def.is_public, None => false } } } @@ -705,24 +722,6 @@ impl NameBindings { } } - fn privacy_for_namespace(&self, namespace: Namespace) - -> Option { - match namespace { - TypeNS => { - match self.type_def { - None => None, - Some(ref type_def) => Some((*type_def).privacy) - } - } - ValueNS => { - match self.value_def { - None => None, - Some(value_def) => Some(value_def.privacy) - } - } - } - } - fn span_for_namespace(&self, namespace: Namespace) -> Option { if self.defined_in_namespace(namespace) { match namespace { @@ -800,15 +799,15 @@ fn namespace_error_to_str(ns: NamespaceError) -> &'static str { } fn Resolver(session: Session, - lang_items: LanguageItems, - crate_span: Span) -> Resolver { + lang_items: LanguageItems, + crate_span: Span) -> Resolver { let graph_root = @mut NameBindings(); - graph_root.define_module(Public, - NoParentLink, + graph_root.define_module(NoParentLink, Some(DefId { crate: 0, node: 0 }), NormalModuleKind, false, + true, crate_span); let current_module = graph_root.get_module(); @@ -846,6 +845,8 @@ fn Resolver(session: Session, export_map2: @mut HashMap::new(), trait_map: HashMap::new(), used_imports: HashSet::new(), + external_exports: HashSet::new(), + last_private: HashMap::new(), emit_errors: true, intr: session.intr() @@ -903,6 +904,8 @@ struct Resolver { def_map: DefMap, export_map2: ExportMap2, trait_map: TraitMap, + external_exports: ExternalExports, + last_private: LastPrivateMap, // Whether or not to print error messages. Can be set to true // when getting additional info for error message suggestions, @@ -1161,7 +1164,7 @@ impl Resolver { { let ident = item.ident; let sp = item.span; - let privacy = visibility_to_privacy(item.vis); + let is_public = item.vis == ast::public; match item.node { item_mod(*) => { @@ -1170,11 +1173,11 @@ impl Resolver { let parent_link = self.get_parent_link(new_parent, ident); let def_id = DefId { crate: 0, node: item.id }; - name_bindings.define_module(privacy, - parent_link, + name_bindings.define_module(parent_link, Some(def_id), NormalModuleKind, false, + item.vis == ast::public, sp); ModuleReducedGraphParent(name_bindings.get_module()) @@ -1190,11 +1193,11 @@ impl Resolver { let parent_link = self.get_parent_link(new_parent, ident); let def_id = DefId { crate: 0, node: item.id }; - name_bindings.define_module(privacy, - parent_link, + name_bindings.define_module(parent_link, Some(def_id), ExternModuleKind, false, + true, sp); ModuleReducedGraphParent(name_bindings.get_module()) @@ -1213,7 +1216,7 @@ impl Resolver { let mutbl = m == ast::MutMutable; name_bindings.define_value - (privacy, DefStatic(local_def(item.id), mutbl), sp); + (DefStatic(local_def(item.id), mutbl), sp, is_public); parent } item_fn(_, purity, _, _, _) => { @@ -1221,7 +1224,7 @@ impl Resolver { self.add_child(ident, parent, ForbidDuplicateValues, sp); let def = DefFn(local_def(item.id), purity); - name_bindings.define_value(privacy, def, sp); + name_bindings.define_value(def, sp, is_public); new_parent } @@ -1231,7 +1234,7 @@ impl Resolver { self.add_child(ident, parent, ForbidDuplicateTypes, sp); name_bindings.define_type - (privacy, DefTy(local_def(item.id)), sp); + (DefTy(local_def(item.id)), sp, is_public); parent } @@ -1240,16 +1243,14 @@ impl Resolver { self.add_child(ident, parent, ForbidDuplicateTypes, sp); name_bindings.define_type - (privacy, DefTy(local_def(item.id)), sp); + (DefTy(local_def(item.id)), sp, is_public); for variant in (*enum_definition).variants.iter() { self.build_reduced_graph_for_variant( variant, local_def(item.id), - // inherited => privacy of the enum item - variant_visibility_to_privacy(variant.node.vis, - privacy == Public), - new_parent); + new_parent, + is_public); } parent } @@ -1265,12 +1266,13 @@ impl Resolver { let (name_bindings, new_parent) = self.add_child(ident, parent, forbid, sp); // Define a name in the type namespace. - name_bindings.define_type(privacy, DefTy(local_def(item.id)), sp); + name_bindings.define_type(DefTy(local_def(item.id)), sp, is_public); // If this is a newtype or unit-like struct, define a name // in the value namespace as well do ctor_id.while_some |cid| { - name_bindings.define_value(privacy, DefStruct(local_def(cid)), sp); + name_bindings.define_value(DefStruct(local_def(cid)), sp, + is_public); None } @@ -1316,11 +1318,11 @@ impl Resolver { let parent_link = self.get_parent_link(new_parent, ident); let def_id = local_def(item.id); - name_bindings.define_module(Public, - parent_link, + name_bindings.define_module(parent_link, Some(def_id), ImplModuleKind, false, + true, sp); ModuleReducedGraphParent( @@ -1353,9 +1355,10 @@ impl Resolver { } }; - method_name_bindings.define_value(Public, - def, - method.span); + let is_public = method.vis == ast::public; + method_name_bindings.define_value(def, + method.span, + is_public); } } _ => {} @@ -1372,11 +1375,11 @@ impl Resolver { // Add all the methods within to a new module. let parent_link = self.get_parent_link(parent, ident); - name_bindings.define_module(privacy, - parent_link, + name_bindings.define_module(parent_link, Some(local_def(item.id)), TraitModuleKind, false, + item.vis == ast::public, sp); let module_parent = ModuleReducedGraphParent(name_bindings. get_module()); @@ -1408,7 +1411,7 @@ impl Resolver { module_parent, ForbidDuplicateValues, ty_m.span); - method_name_bindings.define_value(Public, def, ty_m.span); + method_name_bindings.define_value(def, ty_m.span, true); // Add it to the trait info if not static. match ty_m.explicit_self.node { @@ -1430,7 +1433,7 @@ impl Resolver { } } - name_bindings.define_type(privacy, DefTrait(def_id), sp); + name_bindings.define_type(DefTrait(def_id), sp, is_public); new_parent } @@ -1443,35 +1446,30 @@ impl Resolver { // Constructs the reduced graph for one variant. Variants exist in the // type and/or value namespaces. fn build_reduced_graph_for_variant(&mut self, - variant: &variant, - item_id: DefId, - parent_privacy: Privacy, - parent: ReducedGraphParent) { + variant: &variant, + item_id: DefId, + parent: ReducedGraphParent, + parent_public: bool) { let ident = variant.node.name; - - let privacy = - match variant.node.vis { - public => Public, - private => Private, - inherited => parent_privacy - }; + // XXX: this is unfortunate to have to do this privacy calculation + // here. This should be living in middle::privacy, but it's + // necessary to keep around in some form becaues of glob imports... + let is_public = parent_public && variant.node.vis != ast::private; match variant.node.kind { tuple_variant_kind(_) => { let (child, _) = self.add_child(ident, parent, ForbidDuplicateValues, variant.span); - child.define_value(privacy, - DefVariant(item_id, - local_def(variant.node.id), false), - variant.span); + child.define_value(DefVariant(item_id, + local_def(variant.node.id), false), + variant.span, is_public); } struct_variant_kind(_) => { let (child, _) = self.add_child(ident, parent, ForbidDuplicateTypesAndValues, variant.span); - child.define_type(privacy, - DefVariant(item_id, - local_def(variant.node.id), true), - variant.span); + child.define_type(DefVariant(item_id, + local_def(variant.node.id), true), + variant.span, is_public); self.structs.insert(local_def(variant.node.id)); } } @@ -1482,7 +1480,6 @@ impl Resolver { fn build_reduced_graph_for_view_item(&mut self, view_item: &view_item, parent: ReducedGraphParent) { - let privacy = visibility_to_privacy(view_item.vis); match view_item.node { view_item_use(ref view_paths) => { for view_path in view_paths.iter() { @@ -1515,39 +1512,40 @@ impl Resolver { // Build up the import directives. let module_ = self.get_module_from_parent(parent); + let is_public = view_item.vis == ast::public; match view_path.node { view_path_simple(binding, ref full_path, id) => { let source_ident = full_path.segments.last().identifier; let subclass = @SingleImport(binding, source_ident); - self.build_import_directive(privacy, - module_, + self.build_import_directive(module_, module_path, subclass, view_path.span, - id); + id, + is_public); } view_path_list(_, ref source_idents, _) => { for source_ident in source_idents.iter() { let name = source_ident.node.name; let subclass = @SingleImport(name, name); self.build_import_directive( - privacy, module_, module_path.clone(), subclass, source_ident.span, - source_ident.node.id); + source_ident.node.id, + is_public); } } view_path_glob(_, id) => { - self.build_import_directive(privacy, - module_, + self.build_import_directive(module_, module_path, @GlobImport, view_path.span, - id); + id, + is_public); } } } @@ -1559,12 +1557,14 @@ impl Resolver { node_id) { Some(crate_id) => { let def_id = DefId { crate: crate_id, node: 0 }; + self.external_exports.insert(def_id); let parent_link = ModuleParentLink (self.get_module_from_parent(parent), name); let external_module = @mut Module::new(parent_link, Some(def_id), NormalModuleKind, - false); + false, + true); parent.external_module_children.insert( name.name, @@ -1586,6 +1586,7 @@ impl Resolver { f: &fn(&mut Resolver, ReducedGraphParent)) { let name = foreign_item.ident; + let is_public = foreign_item.vis == ast::public; let (name_bindings, new_parent) = self.add_child(name, parent, ForbidDuplicateValues, foreign_item.span); @@ -1593,7 +1594,7 @@ impl Resolver { match foreign_item.node { foreign_item_fn(_, ref generics) => { let def = DefFn(local_def(foreign_item.id), unsafe_fn); - name_bindings.define_value(Public, def, foreign_item.span); + name_bindings.define_value(def, foreign_item.span, is_public); do self.with_type_parameter_rib( HasTypeParameters( @@ -1604,7 +1605,7 @@ impl Resolver { } foreign_item_static(_, m) => { let def = DefStatic(local_def(foreign_item.id), m); - name_bindings.define_value(Public, def, foreign_item.span); + name_bindings.define_value(def, foreign_item.span, is_public); f(self, new_parent) } @@ -1628,6 +1629,7 @@ impl Resolver { BlockParentLink(parent_module, block_id), None, AnonymousModuleKind, + false, false); parent_module.anonymous_children.insert(block_id, new_module); ModuleReducedGraphParent(new_module) @@ -1638,15 +1640,18 @@ impl Resolver { fn handle_external_def(&mut self, def: Def, - visibility: ast::visibility, + vis: visibility, child_name_bindings: @mut NameBindings, final_ident: &str, ident: Ident, new_parent: ReducedGraphParent) { - let privacy = visibility_to_privacy(visibility); debug2!("(building reduced graph for \ external crate) building external def, priv {:?}", - privacy); + vis); + let is_public = vis == ast::public; + if is_public { + self.external_exports.insert(def_id_of_def(def)); + } match def { DefMod(def_id) | DefForeignMod(def_id) | DefStruct(def_id) | DefTy(def_id) => { @@ -1662,11 +1667,11 @@ impl Resolver { {}", final_ident); let parent_link = self.get_parent_link(new_parent, ident); - child_name_bindings.define_module(privacy, - parent_link, + child_name_bindings.define_module(parent_link, Some(def_id), NormalModuleKind, true, + is_public, dummy_sp()); } } @@ -1681,20 +1686,20 @@ impl Resolver { variant {}", final_ident); // We assume the parent is visible, or else we wouldn't have seen - // it. - let privacy = variant_visibility_to_privacy(visibility, true); + // it. Also variants are public-by-default if the parent was also + // public. + let is_public = vis != ast::private; if is_struct { - child_name_bindings.define_type(privacy, def, dummy_sp()); + child_name_bindings.define_type(def, dummy_sp(), is_public); self.structs.insert(variant_id); - } - else { - child_name_bindings.define_value(privacy, def, dummy_sp()); + } else { + child_name_bindings.define_value(def, dummy_sp(), is_public); } } DefFn(*) | DefStaticMethod(*) | DefStatic(*) => { debug2!("(building reduced graph for external \ crate) building value (fn/static) {}", final_ident); - child_name_bindings.define_value(privacy, def, dummy_sp()); + child_name_bindings.define_value(def, dummy_sp(), is_public); } DefTrait(def_id) => { debug2!("(building reduced graph for external \ @@ -1720,6 +1725,9 @@ impl Resolver { if explicit_self != sty_static { interned_method_names.insert(method_name.name); } + if is_public { + self.external_exports.insert(method_def_id); + } } for name in interned_method_names.iter() { if !self.method_map.contains_key(name) { @@ -1731,35 +1739,37 @@ impl Resolver { } } - child_name_bindings.define_type(privacy, def, dummy_sp()); + child_name_bindings.define_type(def, dummy_sp(), is_public); // Define a module if necessary. let parent_link = self.get_parent_link(new_parent, ident); - child_name_bindings.set_module_kind(privacy, - parent_link, + child_name_bindings.set_module_kind(parent_link, Some(def_id), TraitModuleKind, true, + is_public, dummy_sp()) } DefTy(_) => { debug2!("(building reduced graph for external \ crate) building type {}", final_ident); - child_name_bindings.define_type(privacy, def, dummy_sp()); + child_name_bindings.define_type(def, dummy_sp(), is_public); } DefStruct(def_id) => { debug2!("(building reduced graph for external \ crate) building type and value for {}", final_ident); - child_name_bindings.define_type(privacy, def, dummy_sp()); + child_name_bindings.define_type(def, dummy_sp(), is_public); if get_struct_fields(self.session.cstore, def_id).len() == 0 { - child_name_bindings.define_value(privacy, def, dummy_sp()); + child_name_bindings.define_value(def, dummy_sp(), is_public); } self.structs.insert(def_id); } DefMethod(*) => { - // Ignored; handled elsewhere. + debug2!("(building reduced graph for external crate) \ + ignoring {:?}", def); + // Ignored; handled elsewhere. } DefSelf(*) | DefArg(*) | DefLocal(*) | DefPrimTy(*) | DefTyParam(*) | DefBinding(*) | @@ -1854,11 +1864,11 @@ impl Resolver { self.get_parent_link(new_parent, final_ident); child_name_bindings.define_module( - Public, parent_link, Some(def), ImplModuleKind, true, + true, dummy_sp()); type_module = child_name_bindings. @@ -1886,10 +1896,9 @@ impl Resolver { static_method_info.def_id, static_method_info.purity); - let p = visibility_to_privacy( - static_method_info.vis); method_name_bindings.define_value( - p, def, dummy_sp()); + def, dummy_sp(), + visibility == ast::public); } } @@ -1956,14 +1965,15 @@ impl Resolver { /// Creates and adds an import directive to the given module. fn build_import_directive(&mut self, - privacy: Privacy, - module_: @mut Module, - module_path: ~[Ident], - subclass: @ImportDirectiveSubclass, - span: Span, - id: NodeId) { - let directive = @ImportDirective::new(privacy, module_path, - subclass, span, id); + module_: @mut Module, + module_path: ~[Ident], + subclass: @ImportDirectiveSubclass, + span: Span, + id: NodeId, + is_public: bool) { + let directive = @ImportDirective::new(module_path, + subclass, span, id, + is_public); module_.imports.push(directive); // Bump the reference count on the name. Or, if this is a glob, set @@ -1972,8 +1982,7 @@ impl Resolver { match *subclass { SingleImport(target, _) => { debug2!("(building import directive) building import \ - directive: privacy {:?} {}::{}", - privacy, + directive: {}::{}", self.idents_to_str(directive.module_path), self.session.str_of(target)); @@ -1984,13 +1993,12 @@ impl Resolver { resolution.outstanding_references += 1; // the source of this name is different now - resolution.privacy = privacy; resolution.type_id = id; resolution.value_id = id; } None => { debug2!("(building import directive) creating new"); - let resolution = @mut ImportResolution::new(privacy, id); + let resolution = @mut ImportResolution::new(id, is_public); resolution.outstanding_references = 1; module_.import_resolutions.insert(target.name, resolution); } @@ -2153,8 +2161,8 @@ impl Resolver { /// currently-unresolved imports, or success if we know the name exists. /// If successful, the resolved bindings are written into the module. fn resolve_import_for_module(&mut self, - module_: @mut Module, - import_directive: @ImportDirective) + module_: @mut Module, + import_directive: @ImportDirective) -> ResolveResult<()> { let mut resolution_result = Failed; let module_path = &import_directive.module_path; @@ -2165,9 +2173,9 @@ impl Resolver { self.module_to_str(module_)); // First, resolve the module path for the directive, if necessary. - let containing_module = if module_path.len() == 0 { + let container = if module_path.len() == 0 { // Use the crate root. - Some(self.graph_root.get_module()) + Some((self.graph_root.get_module(), AllPublic)) } else { match self.resolve_module_path(module_, *module_path, @@ -2180,13 +2188,13 @@ impl Resolver { resolution_result = Indeterminate; None } - Success(containing_module) => Some(containing_module), + Success(container) => Some(container), } }; - match containing_module { + match container { None => {} - Some(containing_module) => { + Some((containing_module, lp)) => { // We found the module that the target is contained // within. Attempt to resolve the import within it. @@ -2197,15 +2205,16 @@ impl Resolver { containing_module, target, source, - import_directive); + import_directive, + lp); } GlobImport => { - let privacy = import_directive.privacy; resolution_result = - self.resolve_glob_import(privacy, - module_, + self.resolve_glob_import(module_, containing_module, - import_directive.id); + import_directive.id, + import_directive.is_public, + lp); } } } @@ -2246,7 +2255,7 @@ impl Resolver { -> NameBindings { NameBindings { type_def: Some(TypeNsDef { - privacy: Public, + is_public: false, module_def: Some(module), type_def: None, type_span: None @@ -2256,18 +2265,21 @@ impl Resolver { } fn resolve_single_import(&mut self, - module_: @mut Module, - containing_module: @mut Module, - target: Ident, - source: Ident, - directive: &ImportDirective) + module_: @mut Module, + containing_module: @mut Module, + target: Ident, + source: Ident, + directive: &ImportDirective, + lp: LastPrivate) -> ResolveResult<()> { debug2!("(resolving single import) resolving `{}` = `{}::{}` from \ - `{}`", + `{}` id {}, last private {:?}", self.session.str_of(target), self.module_to_str(containing_module), self.session.str_of(source), - self.module_to_str(module_)); + self.module_to_str(module_), + directive.id, + lp); // We need to resolve both namespaces for this to succeed. // @@ -2295,6 +2307,7 @@ impl Resolver { // Unless we managed to find a result in both namespaces (unlikely), // search imports as well. + let mut used_reexport = false; match (value_result, type_result) { (BoundResult(*), BoundResult(*)) => {} // Continue. _ => { @@ -2337,7 +2350,7 @@ impl Resolver { // Import resolutions must be declared with "pub" // in order to be exported. - if import_resolution.privacy == Private { + if !import_resolution.is_public { return UnboundResult; } @@ -2360,11 +2373,14 @@ impl Resolver { if value_result.is_unknown() { value_result = get_binding(self, *import_resolution, ValueNS); + used_reexport = import_resolution.is_public; } if type_result.is_unknown() { type_result = get_binding(self, *import_resolution, TypeNS); + used_reexport = import_resolution.is_public; } + } Some(_) => { // The import is unresolved. Bail out. @@ -2378,6 +2394,7 @@ impl Resolver { // If we didn't find a result in the type namespace, search the // external modules. + let mut used_public = false; match type_result { BoundResult(*) => {} _ => { @@ -2390,6 +2407,7 @@ impl Resolver { *module); type_result = BoundResult(containing_module, name_bindings); + used_public = true; } } } @@ -2405,6 +2423,7 @@ impl Resolver { import_resolution.value_target = Some(Target::new(target_module, name_bindings)); import_resolution.value_id = directive.id; + used_public = name_bindings.defined_in_public_namespace(ValueNS); } UnboundResult => { /* Continue. */ } UnknownResult => { @@ -2418,6 +2437,7 @@ impl Resolver { import_resolution.type_target = Some(Target::new(target_module, name_bindings)); import_resolution.type_id = directive.id; + used_public = name_bindings.defined_in_public_namespace(TypeNS); } UnboundResult => { /* Continue. */ } UnknownResult => { @@ -2425,61 +2445,16 @@ impl Resolver { } } - let i = import_resolution; - let mut resolve_fail = false; - let mut priv_fail = false; - match (i.value_target, i.type_target) { - // If this name wasn't found in either namespace, it's definitely - // unresolved. - (None, None) => { resolve_fail = true; } - // If it's private, it's also unresolved. - (Some(t), None) | (None, Some(t)) => { - let bindings = &mut *t.bindings; - match bindings.type_def { - Some(ref type_def) => { - if type_def.privacy == Private { - priv_fail = true; - } - } - _ => () - } - match bindings.value_def { - Some(ref value_def) => { - if value_def.privacy == Private { - priv_fail = true; - } - } - _ => () - } - } - // It's also an error if there's both a type and a value with this - // name, but both are private - (Some(val), Some(ty)) => { - match (val.bindings.value_def, ty.bindings.value_def) { - (Some(ref value_def), Some(ref type_def)) => - if value_def.privacy == Private - && type_def.privacy == Private { - priv_fail = true; - }, - _ => () - } - } - } - - let span = directive.span; - if resolve_fail { - let msg = format!("unresolved import: there is no `{}` in `{}`", + if import_resolution.value_target.is_none() && + import_resolution.type_target.is_none() { + let msg = format!("unresolved import: there is no \ + `{}` in `{}`", self.session.str_of(source), self.module_to_str(containing_module)); - self.resolve_error(span, msg); - return Failed; - } else if priv_fail { - let msg = format!("unresolved import: found `{}` in `{}` but it is \ - private", self.session.str_of(source), - self.module_to_str(containing_module)); - self.resolve_error(span, msg); + self.resolve_error(directive.span, msg); return Failed; } + let used_public = used_reexport || used_public; assert!(import_resolution.outstanding_references >= 1); import_resolution.outstanding_references -= 1; @@ -2487,19 +2462,23 @@ impl Resolver { // record what this import resolves to for later uses in documentation, // this may resolve to either a value or a type, but for documentation // purposes it's good enough to just favor one over the other. - match i.value_target { + match import_resolution.value_target { Some(target) => { - self.def_map.insert(i.value_id, - target.bindings.value_def.get_ref().def); + let def = target.bindings.def_for_namespace(ValueNS).unwrap(); + self.def_map.insert(directive.id, def); + let did = def_id_of_def(def); + self.last_private.insert(directive.id, + if used_public {lp} else {DependsOn(did)}); } None => {} } - match i.type_target { + match import_resolution.type_target { Some(target) => { - match target.bindings.type_def.get_ref().type_def { - Some(def) => { self.def_map.insert(i.type_id, def); } - None => {} - } + let def = target.bindings.def_for_namespace(TypeNS).unwrap(); + self.def_map.insert(directive.id, def); + let did = def_id_of_def(def); + self.last_private.insert(directive.id, + if used_public {lp} else {DependsOn(did)}); } None => {} } @@ -2512,15 +2491,16 @@ impl Resolver { // succeeds or bails out (as importing * from an empty module or a module // that exports nothing is valid). fn resolve_glob_import(&mut self, - privacy: Privacy, - module_: @mut Module, - containing_module: @mut Module, - id: NodeId) - -> ResolveResult<()> { + module_: @mut Module, + containing_module: @mut Module, + id: NodeId, + is_public: bool, + lp: LastPrivate) + -> ResolveResult<()> { // This function works in a highly imperative manner; it eagerly adds // everything it can to the list of import resolutions of the module // node. - debug2!("(resolving glob import) resolving {:?} glob import", privacy); + debug2!("(resolving glob import) resolving glob import {}", id); // We must bail out if the node has unresolved imports of any kind // (including globs). @@ -2540,12 +2520,17 @@ impl Resolver { target_import_resolution.type_target.is_none(), self.module_to_str(module_)); + if !target_import_resolution.is_public { + debug2!("(resolving glob import) nevermind, just kidding"); + continue + } + // Here we merge two import resolutions. match module_.import_resolutions.find(ident) { - None if target_import_resolution.privacy == Public => { + None => { // Simple: just copy the old import resolution. let new_import_resolution = - @mut ImportResolution::new(privacy, id); + @mut ImportResolution::new(id, is_public); new_import_resolution.value_target = target_import_resolution.value_target; new_import_resolution.type_target = @@ -2554,7 +2539,6 @@ impl Resolver { module_.import_resolutions.insert (*ident, new_import_resolution); } - None => { /* continue ... */ } Some(&dest_import_resolution) => { // Merge the two import resolutions at a finer-grained // level. @@ -2577,6 +2561,7 @@ impl Resolver { Some(type_target); } } + dest_import_resolution.is_public = is_public; } } } @@ -2587,7 +2572,8 @@ impl Resolver { match module_.import_resolutions.find(&name) { None => { // Create a new import resolution from this child. - dest_import_resolution = @mut ImportResolution::new(privacy, id); + dest_import_resolution = @mut ImportResolution::new(id, + is_public); module_.import_resolutions.insert (name, dest_import_resolution); } @@ -2597,11 +2583,10 @@ impl Resolver { } debug2!("(resolving glob import) writing resolution `{}` in `{}` \ - to `{}`, privacy={:?}", + to `{}`", interner_get(name), self.module_to_str(containing_module), - self.module_to_str(module_), - dest_import_resolution.privacy); + self.module_to_str(module_)); // Merge the child item into the import resolution. if name_bindings.defined_in_public_namespace(ValueNS) { @@ -2616,6 +2601,7 @@ impl Resolver { Some(Target::new(containing_module, name_bindings)); dest_import_resolution.type_id = id; } + dest_import_resolution.is_public = is_public; }; // Add all children from the containing module. @@ -2635,6 +2621,7 @@ impl Resolver { match containing_module.def_id { Some(did) => { self.def_map.insert(id, DefMod(did)); + self.last_private.insert(id, lp); } None => {} } @@ -2645,15 +2632,17 @@ impl Resolver { /// Resolves the given module path from the given root `module_`. fn resolve_module_path_from_root(&mut self, - module_: @mut Module, - module_path: &[Ident], - index: uint, - span: Span, - mut name_search_type: NameSearchType) - -> ResolveResult<@mut Module> { + module_: @mut Module, + module_path: &[Ident], + index: uint, + span: Span, + name_search_type: NameSearchType, + lp: LastPrivate) + -> ResolveResult<(@mut Module, LastPrivate)> { let mut search_module = module_; let mut index = index; let module_path_len = module_path.len(); + let mut closest_private = lp; // Resolve the module part of the path. This does not involve looking // upward though scope chains; we simply resolve names directly in @@ -2690,7 +2679,7 @@ impl Resolver { self.session.str_of(name)); return Indeterminate; } - Success(target) => { + Success((target, used_proxy)) => { // Check to see whether there are type bindings, and, if // so, whether there is a module within. match target.bindings.type_def { @@ -2720,7 +2709,23 @@ impl Resolver { or type implementation"); return Failed; } - (_, _) => search_module = module_def, + (_, _) => { + search_module = module_def; + + // Keep track of the closest + // private module used when + // resolving this import chain. + if !used_proxy && + !search_module.is_public { + match search_module.def_id { + Some(did) => { + closest_private = + DependsOn(did); + } + None => {} + } + } + } } } } @@ -2738,28 +2743,23 @@ impl Resolver { } index += 1; - - // After the first element of the path, allow searching only - // through public identifiers. - // - // XXX: Rip this out and move it to the privacy checker. - if name_search_type == PathPublicOrPrivateSearch { - name_search_type = PathPublicOnlySearch - } } - return Success(search_module); + return Success((search_module, closest_private)); } /// Attempts to resolve the module part of an import directive or path /// rooted at the given module. + /// + /// On success, returns the resolved module, and the closest *private* + /// module found to the destination when resolving this path. fn resolve_module_path(&mut self, - module_: @mut Module, - module_path: &[Ident], - use_lexical_scope: UseLexicalScopeFlag, - span: Span, - name_search_type: NameSearchType) - -> ResolveResult<@mut Module> { + module_: @mut Module, + module_path: &[Ident], + use_lexical_scope: UseLexicalScopeFlag, + span: Span, + name_search_type: NameSearchType) + -> ResolveResult<(@mut Module, LastPrivate)> { let module_path_len = module_path.len(); assert!(module_path_len > 0); @@ -2774,6 +2774,7 @@ impl Resolver { let search_module; let start_index; + let last_private; match module_prefix_result { Failed => { let mpath = self.idents_to_str(module_path); @@ -2805,6 +2806,7 @@ impl Resolver { // resolution process at index zero. search_module = self.graph_root.get_module(); start_index = 0; + last_private = AllPublic; } UseLexicalScope => { // This is not a crate-relative path. We resolve the @@ -2826,6 +2828,7 @@ impl Resolver { Success(containing_module) => { search_module = containing_module; start_index = 1; + last_private = AllPublic; } } } @@ -2834,6 +2837,7 @@ impl Resolver { Success(PrefixFound(containing_module, index)) => { search_module = containing_module; start_index = index; + last_private = DependsOn(containing_module.def_id.unwrap()); } } @@ -2841,18 +2845,19 @@ impl Resolver { module_path, start_index, span, - name_search_type) + name_search_type, + last_private) } /// Invariant: This must only be called during main resolution, not during /// import resolution. fn resolve_item_in_lexical_scope(&mut self, - module_: @mut Module, - name: Ident, - namespace: Namespace, - search_through_modules: - SearchThroughModulesFlag) - -> ResolveResult { + module_: @mut Module, + name: Ident, + namespace: Namespace, + search_through_modules: + SearchThroughModulesFlag) + -> ResolveResult<(Target, bool)> { debug2!("(resolving item in lexical scope) resolving `{}` in \ namespace {:?} in `{}`", self.session.str_of(name), @@ -2865,7 +2870,8 @@ impl Resolver { match module_.children.find(&name.name) { Some(name_bindings) if name_bindings.defined_in_namespace(namespace) => { - return Success(Target::new(module_, *name_bindings)); + debug2!("top name bindings succeeded"); + return Success((Target::new(module_, *name_bindings), false)); } Some(_) | None => { /* Not found; continue. */ } } @@ -2890,7 +2896,7 @@ impl Resolver { debug2!("(resolving item in lexical scope) using \ import resolution"); self.used_imports.insert(import_resolution.id(namespace)); - return Success(target); + return Success((target, false)); } } } @@ -2904,7 +2910,8 @@ impl Resolver { let name_bindings = @mut Resolver::create_name_bindings_from_module( *module); - return Success(Target::new(module_, name_bindings)); + debug2!("lower name bindings succeeded"); + return Success((Target::new(module_, name_bindings), false)); } } } @@ -2954,7 +2961,7 @@ impl Resolver { match self.resolve_name_in_module(search_module, name, namespace, - PathPublicOrPrivateSearch) { + PathSearch) { Failed => { // Continue up the search chain. } @@ -2966,9 +2973,11 @@ impl Resolver { higher scope; bailing"); return Indeterminate; } - Success(target) => { + Success((target, used_reexport)) => { // We found the module. - return Success(target); + debug2!("(resolving item in lexical scope) found name \ + in module, done"); + return Success((target, used_reexport)); } } } @@ -2976,15 +2985,15 @@ impl Resolver { /// Resolves a module name in the current lexical scope. fn resolve_module_in_lexical_scope(&mut self, - module_: @mut Module, - name: Ident) - -> ResolveResult<@mut Module> { + module_: @mut Module, + name: Ident) + -> ResolveResult<@mut Module> { // If this module is an anonymous module, resolve the item in the // lexical scope. Otherwise, resolve the item from the crate root. let resolve_result = self.resolve_item_in_lexical_scope( module_, name, TypeNS, DontSearchThroughModules); match resolve_result { - Success(target) => { + Success((target, _)) => { let bindings = &mut *target.bindings; match bindings.type_def { Some(ref type_def) => { @@ -3064,8 +3073,8 @@ impl Resolver { /// (b) some chain of `super::`. /// grammar: (SELF MOD_SEP ) ? (SUPER MOD_SEP) * fn resolve_module_prefix(&mut self, - module_: @mut Module, - module_path: &[Ident]) + module_: @mut Module, + module_path: &[Ident]) -> ResolveResult { // Start at the current module if we see `self` or `super`, or at the // top of the crate otherwise. @@ -3106,12 +3115,15 @@ impl Resolver { /// Attempts to resolve the supplied name in the given module for the /// given namespace. If successful, returns the target corresponding to /// the name. + /// + /// The boolean returned on success is an indicator of whether this lookup + /// passed through a public re-export proxy. fn resolve_name_in_module(&mut self, - module_: @mut Module, - name: Ident, - namespace: Namespace, - name_search_type: NameSearchType) - -> ResolveResult { + module_: @mut Module, + name: Ident, + namespace: Namespace, + name_search_type: NameSearchType) + -> ResolveResult<(Target, bool)> { debug2!("(resolving name in module) resolving `{}` in `{}`", self.session.str_of(name), self.module_to_str(module_)); @@ -3122,7 +3134,7 @@ impl Resolver { Some(name_bindings) if name_bindings.defined_in_namespace(namespace) => { debug2!("(resolving name in module) found node as child"); - return Success(Target::new(module_, *name_bindings)); + return Success((Target::new(module_, *name_bindings), false)); } Some(_) | None => { // Continue. @@ -3133,39 +3145,30 @@ impl Resolver { // If this is a search of all imports, we should be done with glob // resolution at this point. - if name_search_type == PathPublicOrPrivateSearch || - name_search_type == PathPublicOnlySearch { + if name_search_type == PathSearch { assert_eq!(module_.glob_count, 0); } // Check the list of resolved imports. match module_.import_resolutions.find(&name.name) { Some(import_resolution) => { - if import_resolution.privacy == Public && + if import_resolution.is_public && import_resolution.outstanding_references != 0 { debug2!("(resolving name in module) import \ - unresolved; bailing out"); + unresolved; bailing out"); return Indeterminate; } - match import_resolution.target_for_namespace(namespace) { None => { debug2!("(resolving name in module) name found, \ but not in namespace {:?}", namespace); } - Some(target) - if name_search_type == - PathPublicOrPrivateSearch || - import_resolution.privacy == Public => { + Some(target) => { debug2!("(resolving name in module) resolved to \ import"); self.used_imports.insert(import_resolution.id(namespace)); - return Success(target); - } - Some(_) => { - debug2!("(resolving name in module) name found, \ - but not public"); + return Success((target, true)); } } } @@ -3180,7 +3183,7 @@ impl Resolver { let name_bindings = @mut Resolver::create_name_bindings_from_module( *module); - return Success(Target::new(module_, name_bindings)); + return Success((Target::new(module_, name_bindings), false)); } } } @@ -3299,14 +3302,13 @@ impl Resolver { } fn add_exports_of_namebindings(&mut self, - exports2: &mut ~[Export2], - name: Name, - namebindings: @mut NameBindings, - ns: Namespace, - reexport: bool) { - match (namebindings.def_for_namespace(ns), - namebindings.privacy_for_namespace(ns)) { - (Some(d), Some(Public)) => { + exports2: &mut ~[Export2], + name: Name, + namebindings: @mut NameBindings, + ns: Namespace, + reexport: bool) { + match namebindings.def_for_namespace(ns) { + Some(d) => { debug2!("(computing exports) YES: {} '{}' => {:?}", if reexport { ~"reexport" } else { ~"export"}, interner_get(name), @@ -3317,34 +3319,27 @@ impl Resolver { def_id: def_id_of_def(d) }); } - (Some(_), Some(privacy)) => { - debug2!("(computing reexports) NO: privacy {:?}", privacy); - } - (d_opt, p_opt) => { - debug2!("(computing reexports) NO: {:?}, {:?}", d_opt, p_opt); + d_opt => { + debug2!("(computing reexports) NO: {:?}", d_opt); } } } fn add_exports_for_module(&mut self, - exports2: &mut ~[Export2], - module_: @mut Module) { + exports2: &mut ~[Export2], + module_: @mut Module) { for (name, importresolution) in module_.import_resolutions.iter() { - if importresolution.privacy != Public { - debug2!("(computing exports) not reexporting private `{}`", - interner_get(*name)); - continue; - } + if !importresolution.is_public { continue } let xs = [TypeNS, ValueNS]; - for ns in xs.iter() { - match importresolution.target_for_namespace(*ns) { + for &ns in xs.iter() { + match importresolution.target_for_namespace(ns) { Some(target) => { debug2!("(computing exports) maybe reexport '{}'", interner_get(*name)); - self.add_exports_of_namebindings(&mut *exports2, + self.add_exports_of_namebindings(exports2, *name, target.bindings, - *ns, + ns, true) } _ => () @@ -3753,7 +3748,7 @@ impl Resolver { // Associate this type parameter with // the item that bound it self.record_def(type_parameter.id, - DefTyParamBinder(node_id)); + (DefTyParamBinder(node_id), AllPublic)); // plain insert (no renaming) function_type_rib.bindings.insert(ident.name, def_like); } @@ -4192,7 +4187,7 @@ impl Resolver { Some(&primitive_type) => { result_def = - Some(DefPrimTy(primitive_type)); + Some((DefPrimTy(primitive_type), AllPublic)); if path.segments .iter() @@ -4218,10 +4213,7 @@ impl Resolver { match result_def { None => { - match self.resolve_path(ty.id, - path, - TypeNS, - true) { + match self.resolve_path(ty.id, path, TypeNS, true) { Some(def) => { debug2!("(resolving type) resolved `{}` to \ type {:?}", @@ -4279,12 +4271,12 @@ impl Resolver { } fn resolve_pattern(&mut self, - pattern: @Pat, - mode: PatternBindingMode, - mutability: Mutability, - // Maps idents to the node ID for the (outermost) - // pattern that binds them - bindings_list: Option<@mut HashMap>) { + pattern: @Pat, + mode: PatternBindingMode, + mutability: Mutability, + // Maps idents to the node ID for the (outermost) + // pattern that binds them + bindings_list: Option<@mut HashMap>) { let pat_id = pattern.id; do walk_pat(pattern) |pattern| { match pattern.node { @@ -4304,7 +4296,7 @@ impl Resolver { let renamed = mtwt_resolve(ident); match self.resolve_bare_identifier_pattern(ident) { - FoundStructOrEnumVariant(def) + FoundStructOrEnumVariant(def, lp) if mode == RefutableMode => { debug2!("(resolving pattern) resolving `{}` to \ struct or enum variant", @@ -4314,9 +4306,9 @@ impl Resolver { pattern, binding_mode, "an enum variant"); - self.record_def(pattern.id, def); + self.record_def(pattern.id, (def, lp)); } - FoundStructOrEnumVariant(_) => { + FoundStructOrEnumVariant(*) => { self.resolve_error(pattern.span, format!("declaration of `{}` \ shadows an enum \ @@ -4324,7 +4316,7 @@ impl Resolver { struct in scope", interner_get(renamed))); } - FoundConst(def) if mode == RefutableMode => { + FoundConst(def, lp) if mode == RefutableMode => { debug2!("(resolving pattern) resolving `{}` to \ constant", interner_get(renamed)); @@ -4333,9 +4325,9 @@ impl Resolver { pattern, binding_mode, "a constant"); - self.record_def(pattern.id, def); + self.record_def(pattern.id, (def, lp)); } - FoundConst(_) => { + FoundConst(*) => { self.resolve_error(pattern.span, "only irrefutable patterns \ allowed here"); @@ -4367,7 +4359,7 @@ impl Resolver { // will be able to distinguish variants from // locals in patterns. - self.record_def(pattern.id, def); + self.record_def(pattern.id, (def, AllPublic)); // Add the binding to the local ribs, if it // doesn't already exist in the bindings list. (We @@ -4420,11 +4412,11 @@ impl Resolver { PatIdent(binding_mode, ref path, _) => { // This must be an enum variant, struct, or constant. match self.resolve_path(pat_id, path, ValueNS, false) { - Some(def @ DefVariant(*)) | - Some(def @ DefStruct(*)) => { + Some(def @ (DefVariant(*), _)) | + Some(def @ (DefStruct(*), _)) => { self.record_def(pattern.id, def); } - Some(def @ DefStatic(*)) => { + Some(def @ (DefStatic(*), _)) => { self.enforce_default_binding_mode( pattern, binding_mode, @@ -4455,10 +4447,10 @@ impl Resolver { PatEnum(ref path, _) => { // This must be an enum variant, struct or const. match self.resolve_path(pat_id, path, ValueNS, false) { - Some(def @ DefFn(*)) | - Some(def @ DefVariant(*)) | - Some(def @ DefStruct(*)) | - Some(def @ DefStatic(*)) => { + Some(def @ (DefFn(*), _)) | + Some(def @ (DefVariant(*), _)) | + Some(def @ (DefStruct(*), _)) | + Some(def @ (DefStatic(*), _)) => { self.record_def(pattern.id, def); } Some(_) => { @@ -4500,16 +4492,16 @@ impl Resolver { PatStruct(ref path, _, _) => { match self.resolve_path(pat_id, path, TypeNS, false) { - Some(DefTy(class_id)) + Some((DefTy(class_id), lp)) if self.structs.contains(&class_id) => { let class_def = DefStruct(class_id); - self.record_def(pattern.id, class_def); + self.record_def(pattern.id, (class_def, lp)); } - Some(definition @ DefStruct(class_id)) => { + Some(definition @ (DefStruct(class_id), _)) => { assert!(self.structs.contains(&class_id)); self.record_def(pattern.id, definition); } - Some(definition @ DefVariant(_, variant_id, _)) + Some(definition @ (DefVariant(_, variant_id, _), _)) if self.structs.contains(&variant_id) => { self.record_def(pattern.id, definition); } @@ -4538,19 +4530,25 @@ impl Resolver { name, ValueNS, SearchThroughModules) { - Success(target) => { + Success((target, _)) => { + debug2!("(resolve bare identifier pattern) succeeded in \ + finding {} at {:?}", + self.session.str_of(name), target.bindings.value_def); match target.bindings.value_def { None => { fail2!("resolved name in the value namespace to a \ set of name bindings with no def?!"); } Some(def) => { + // For the two success cases, this lookup can be + // considered as not having a private component because + // the lookup happened only within the current module. match def.def { def @ DefVariant(*) | def @ DefStruct(*) => { - return FoundStructOrEnumVariant(def); + return FoundStructOrEnumVariant(def, AllPublic); } def @ DefStatic(_, false) => { - return FoundConst(def); + return FoundConst(def, AllPublic); } _ => { return BareIdentifierPatternUnresolved; @@ -4565,6 +4563,8 @@ impl Resolver { } Failed => { + debug2!("(resolve bare identifier pattern) failed to find {}", + self.session.str_of(name)); return BareIdentifierPatternUnresolved; } } @@ -4573,35 +4573,31 @@ impl Resolver { /// If `check_ribs` is true, checks the local definitions first; i.e. /// doesn't skip straight to the containing module. fn resolve_path(&mut self, - id: NodeId, - path: &Path, - namespace: Namespace, - check_ribs: bool) - -> Option { + id: NodeId, + path: &Path, + namespace: Namespace, + check_ribs: bool) -> Option<(Def, LastPrivate)> { // First, resolve the types. for ty in path.segments.iter().flat_map(|s| s.types.iter()) { self.resolve_type(ty); } if path.global { - return self.resolve_crate_relative_path(path, - self.xray_context, - namespace); + return self.resolve_crate_relative_path(path, namespace); } - let unqualified_def = self.resolve_identifier(path.segments - .last() - .identifier, - namespace, - check_ribs, - path.span); + let unqualified_def = + self.resolve_identifier(path.segments + .last() + .identifier, + namespace, + check_ribs, + path.span); if path.segments.len() > 1 { - let def = self.resolve_module_relative_path(path, - self.xray_context, - namespace); + let def = self.resolve_module_relative_path(path, namespace); match (def, unqualified_def) { - (Some(d), Some(ud)) if d == ud => { + (Some((d, _)), Some((ud, _))) if d == ud => { self.session.add_lint(unnecessary_qualification, id, path.span, @@ -4622,13 +4618,13 @@ impl Resolver { namespace: Namespace, check_ribs: bool, span: Span) - -> Option { + -> Option<(Def, LastPrivate)> { if check_ribs { match self.resolve_identifier_in_local_ribs(identifier, namespace, span) { Some(def) => { - return Some(def); + return Some((def, AllPublic)); } None => { // Continue. @@ -4642,51 +4638,43 @@ impl Resolver { // FIXME #4952: Merge me with resolve_name_in_module? fn resolve_definition_of_name_in_module(&mut self, - containing_module: @mut Module, - name: Ident, - namespace: Namespace, - xray: XrayFlag) + containing_module: @mut Module, + name: Ident, + namespace: Namespace) -> NameDefinition { // First, search children. self.populate_module_if_necessary(containing_module); match containing_module.children.find(&name.name) { Some(child_name_bindings) => { - match (child_name_bindings.def_for_namespace(namespace), - child_name_bindings.privacy_for_namespace(namespace)) { - (Some(def), Some(Public)) => { + match child_name_bindings.def_for_namespace(namespace) { + Some(def) => { // Found it. Stop the search here. - return ChildNameDefinition(def); - } - (Some(def), _) if xray == Xray => { - // Found it. Stop the search here. - return ChildNameDefinition(def); - } - (Some(_), _) | (None, _) => { - // Continue. + let p = child_name_bindings.defined_in_public_namespace( + namespace); + let lp = if p {AllPublic} else { + DependsOn(def_id_of_def(def)) + }; + return ChildNameDefinition(def, lp); } + None => {} } } - None => { - // Continue. - } + None => {} } // Next, search import resolutions. match containing_module.import_resolutions.find(&name.name) { - Some(import_resolution) if import_resolution.privacy == Public || - xray == Xray => { + Some(import_resolution) if import_resolution.is_public => { match (*import_resolution).target_for_namespace(namespace) { Some(target) => { - match (target.bindings.def_for_namespace(namespace), - target.bindings.privacy_for_namespace( - namespace)) { - (Some(def), Some(Public)) => { + match target.bindings.def_for_namespace(namespace) { + Some(def) => { // Found it. let id = import_resolution.id(namespace); self.used_imports.insert(id); - return ImportNameDefinition(def); + return ImportNameDefinition(def, AllPublic); } - (Some(_), _) | (None, _) => { + None => { // This can happen with external impls, due to // the imperfect way we read the metadata. } @@ -4695,7 +4683,7 @@ impl Resolver { None => {} } } - Some(_) | None => {} // Continue. + Some(*) | None => {} // Continue. } // Finally, search through external children. @@ -4706,7 +4694,10 @@ impl Resolver { match module.def_id { None => {} // Continue. Some(def_id) => { - return ChildNameDefinition(DefMod(def_id)); + let lp = if module.is_public {AllPublic} else { + DependsOn(def_id) + }; + return ChildNameDefinition(DefMod(def_id), lp); } } } @@ -4719,17 +4710,17 @@ impl Resolver { // resolve a "module-relative" path, e.g. a::b::c fn resolve_module_relative_path(&mut self, path: &Path, - xray: XrayFlag, namespace: Namespace) - -> Option { + -> Option<(Def, LastPrivate)> { let module_path_idents = path.segments.init().map(|ps| ps.identifier); let containing_module; + let last_private; match self.resolve_module_path(self.current_module, module_path_idents, UseLexicalScope, path.span, - PathPublicOnlySearch) { + PathSearch) { Failed => { let msg = format!("use of undeclared module `{}`", self.idents_to_str(module_path_idents)); @@ -4741,22 +4732,22 @@ impl Resolver { fail2!("indeterminate unexpected"); } - Success(resulting_module) => { + Success((resulting_module, resulting_last_private)) => { containing_module = resulting_module; + last_private = resulting_last_private; } } let ident = path.segments.last().identifier; let def = match self.resolve_definition_of_name_in_module(containing_module, ident, - namespace, - xray) { + namespace) { NoNameDefinition => { // We failed to resolve the name. Report an error. return None; } - ChildNameDefinition(def) | ImportNameDefinition(def) => { - def + ChildNameDefinition(def, lp) | ImportNameDefinition(def, lp) => { + (def, last_private.or(lp)) } }; match containing_module.kind { @@ -4783,20 +4774,21 @@ impl Resolver { /// Invariant: This must be called only during main resolution, not during /// import resolution. fn resolve_crate_relative_path(&mut self, - path: &Path, - xray: XrayFlag, - namespace: Namespace) - -> Option { + path: &Path, + namespace: Namespace) + -> Option<(Def, LastPrivate)> { let module_path_idents = path.segments.init().map(|ps| ps.identifier); let root_module = self.graph_root.get_module(); let containing_module; + let last_private; match self.resolve_module_path_from_root(root_module, module_path_idents, 0, path.span, - PathPublicOrPrivateSearch) { + PathSearch, + AllPublic) { Failed => { let msg = format!("use of undeclared module `::{}`", self.idents_to_str(module_path_idents)); @@ -4808,22 +4800,22 @@ impl Resolver { fail2!("indeterminate unexpected"); } - Success(resulting_module) => { + Success((resulting_module, resulting_last_private)) => { containing_module = resulting_module; + last_private = resulting_last_private; } } let name = path.segments.last().identifier; match self.resolve_definition_of_name_in_module(containing_module, name, - namespace, - xray) { + namespace) { NoNameDefinition => { // We failed to resolve the name. Report an error. return None; } - ChildNameDefinition(def) | ImportNameDefinition(def) => { - return Some(def); + ChildNameDefinition(def, lp) | ImportNameDefinition(def, lp) => { + return Some((def, last_private.or(lp))); } } } @@ -4896,26 +4888,32 @@ impl Resolver { } fn resolve_item_by_identifier_in_lexical_scope(&mut self, - ident: Ident, - namespace: Namespace) - -> Option { + ident: Ident, + namespace: Namespace) + -> Option<(Def, LastPrivate)> { // Check the items. match self.resolve_item_in_lexical_scope(self.current_module, ident, namespace, DontSearchThroughModules) { - Success(target) => { + Success((target, _)) => { match (*target.bindings).def_for_namespace(namespace) { None => { // This can happen if we were looking for a type and // found a module instead. Modules don't have defs. + debug2!("(resolving item path by identifier in lexical \ + scope) failed to resolve {} after success...", + self.session.str_of(ident)); return None; } Some(def) => { debug2!("(resolving item path in lexical scope) \ resolved `{}` to item", self.session.str_of(ident)); - return Some(def); + // This lookup is "all public" because it only searched + // for one identifier in the current module (couldn't + // have passed through reexports or anything like that. + return Some((def, AllPublic)); } } } @@ -4923,6 +4921,8 @@ impl Resolver { fail2!("unexpected indeterminate result"); } Failed => { + debug2!("(resolving item path by identifier in lexical scope) \ + failed to resolve {}", self.session.str_of(ident)); return None; } } @@ -5005,7 +5005,7 @@ impl Resolver { // First-class methods are not supported yet; error // out here. match def { - DefMethod(*) => { + (DefMethod(*), _) => { self.resolve_error(expr.span, "first-class methods \ are not supported"); @@ -5027,7 +5027,7 @@ impl Resolver { // structs, which wouldn't result in this error.) match self.with_no_errors(|this| this.resolve_path(expr.id, path, TypeNS, false)) { - Some(DefTy(struct_id)) + Some((DefTy(struct_id), _)) if self.structs.contains(&struct_id) => { self.resolve_error(expr.span, format!("`{}` is a structure name, but \ @@ -5075,12 +5075,12 @@ impl Resolver { ExprStruct(ref path, _, _) => { // Resolve the path to the structure it goes to. match self.resolve_path(expr.id, path, TypeNS, false) { - Some(DefTy(class_id)) | Some(DefStruct(class_id)) + Some((DefTy(class_id), lp)) | Some((DefStruct(class_id), lp)) if self.structs.contains(&class_id) => { let class_def = DefStruct(class_id); - self.record_def(expr.id, class_def); + self.record_def(expr.id, (class_def, lp)); } - Some(definition @ DefVariant(_, class_id, _)) + Some(definition @ (DefVariant(_, class_id, _), _)) if self.structs.contains(&class_id) => { self.record_def(expr.id, definition); } @@ -5118,7 +5118,8 @@ impl Resolver { `{}`", interner_get(label))), Some(DlDef(def @ DefLabel(_))) => { - self.record_def(expr.id, def) + // XXX: is AllPublic correct? + self.record_def(expr.id, (def, AllPublic)) } Some(_) => { self.session.span_bug(expr.span, @@ -5135,7 +5136,7 @@ impl Resolver { "`self` is not allowed in \ this context") } - Some(def) => self.record_def(expr.id, def), + Some(def) => self.record_def(expr.id, (def, AllPublic)), } } @@ -5352,8 +5353,10 @@ impl Resolver { } } - fn record_def(&mut self, node_id: NodeId, def: Def) { - debug2!("(recording def) recording {:?} for {:?}", def, node_id); + fn record_def(&mut self, node_id: NodeId, (def, lp): (Def, LastPrivate)) { + debug2!("(recording def) recording {:?} for {:?}, last private {:?}", + def, node_id, lp); + self.last_private.insert(node_id, lp); do self.def_map.insert_or_update_with(node_id, def) |_, old_value| { // Resolve appears to "resolve" the same ID multiple // times, so here is a sanity check it at least comes to @@ -5393,7 +5396,7 @@ impl Resolver { } fn check_for_item_unused_imports(&self, vi: &view_item) { - // Ignore public import statements because there's no way to be sure + // Ignore is_public import statements because there's no way to be sure // whether they're used or not. Also ignore imports with a dummy span // because this means that they were generated in some fashion by the // compiler and we don't need to consider them. @@ -5499,7 +5502,9 @@ impl Resolver { pub struct CrateMap { def_map: DefMap, exp_map2: ExportMap2, - trait_map: TraitMap + trait_map: TraitMap, + external_exports: ExternalExports, + last_private_map: LastPrivateMap, } /// Entry point to crate resolution. @@ -5509,9 +5514,13 @@ pub fn resolve_crate(session: Session, -> CrateMap { let mut resolver = Resolver(session, lang_items, crate.span); resolver.resolve(crate); + let Resolver { def_map, export_map2, trait_map, last_private, + external_exports, _ } = resolver; CrateMap { - def_map: resolver.def_map, - exp_map2: resolver.export_map2, - trait_map: resolver.trait_map.clone(), + def_map: def_map, + exp_map2: export_map2, + trait_map: trait_map, + external_exports: external_exports, + last_private_map: last_private, } } diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index 16ac802ee15..73b197ec361 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -3034,7 +3034,6 @@ pub fn crate_ctxt_to_encode_parms<'r>(cx: &'r CrateContext, ie: encoder::encode_ diag: diag, tcx: cx.tcx, reexports2: cx.exp_map2, - exported_items: cx.exported_items, item_symbols: item_symbols, discrim_symbols: discrim_symbols, non_inlineable_statics: &cx.non_inlineable_statics, @@ -3116,7 +3115,6 @@ pub fn trans_crate(sess: session::Session, llmod_id, analysis.ty_cx, analysis.exp_map2, - analysis.exported_items, analysis.maps, symbol_hasher, link_meta, diff --git a/src/librustc/middle/trans/context.rs b/src/librustc/middle/trans/context.rs index 3125155d0c7..2b3874e0bf0 100644 --- a/src/librustc/middle/trans/context.rs +++ b/src/librustc/middle/trans/context.rs @@ -49,7 +49,6 @@ pub struct CrateContext { intrinsics: HashMap<&'static str, ValueRef>, item_vals: HashMap, exp_map2: resolve::ExportMap2, - exported_items: @privacy::ExportedItems, reachable: @mut HashSet, item_symbols: HashMap, link_meta: LinkMeta, @@ -125,7 +124,6 @@ impl CrateContext { name: &str, tcx: ty::ctxt, emap2: resolve::ExportMap2, - exported_items: @privacy::ExportedItems, maps: astencode::Maps, symbol_hasher: hash::State, link_meta: LinkMeta, @@ -185,7 +183,6 @@ impl CrateContext { intrinsics: intrinsics, item_vals: HashMap::new(), exp_map2: emap2, - exported_items: exported_items, reachable: reachable, item_symbols: HashMap::new(), link_meta: link_meta, diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs index 2c01f246c5f..926d1997465 100644 --- a/src/libsyntax/ast_util.rs +++ b/src/libsyntax/ast_util.rs @@ -702,32 +702,6 @@ pub fn struct_def_is_tuple_like(struct_def: &ast::struct_def) -> bool { struct_def.ctor_id.is_some() } -pub fn visibility_to_privacy(visibility: visibility) -> Privacy { - match visibility { - public => Public, - inherited | private => Private - } -} - -pub fn variant_visibility_to_privacy(visibility: visibility, - enclosing_is_public: bool) - -> Privacy { - if enclosing_is_public { - match visibility { - public | inherited => Public, - private => Private - } - } else { - visibility_to_privacy(visibility) - } -} - -#[deriving(Eq)] -pub enum Privacy { - Private, - Public -} - /// Returns true if the given pattern consists solely of an identifier /// and false otherwise. pub fn pat_is_ident(pat: @ast::Pat) -> bool { diff --git a/src/test/compile-fail/glob-resolve1.rs b/src/test/compile-fail/glob-resolve1.rs new file mode 100644 index 00000000000..a0004f98ecf --- /dev/null +++ b/src/test/compile-fail/glob-resolve1.rs @@ -0,0 +1,45 @@ +// 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. + +// Make sure that globs only bring in public things. + +use bar::*; + +mod bar { + use import = self::fpriv; + fn fpriv() {} + extern { + fn epriv(); + } + enum A { A1 } + pub enum B { B1 } + + struct C; + + type D = int; +} + +fn foo() {} + +fn main() { + fpriv(); //~ ERROR: unresolved + epriv(); //~ ERROR: unresolved + A1; //~ ERROR: unresolved + B1; + C; //~ ERROR: unresolved + import(); //~ ERROR: unresolved + + foo::(); //~ ERROR: undeclared + //~^ ERROR: undeclared + foo::(); //~ ERROR: undeclared + //~^ ERROR: undeclared + foo::(); //~ ERROR: undeclared + //~^ ERROR: undeclared +} diff --git a/src/test/compile-fail/privacy1.rs b/src/test/compile-fail/privacy1.rs new file mode 100644 index 00000000000..2e2b53331ca --- /dev/null +++ b/src/test/compile-fail/privacy1.rs @@ -0,0 +1,168 @@ +// 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. + +#[no_std]; // makes debugging this test *a lot* easier (during resolve) + +mod bar { + // shouln't bring in too much + pub use self::glob::*; + + // can't publicly re-export private items + pub use self::baz::{foo, bar}; + //~^ ERROR: function `bar` is private + + pub use self::private::ppriv; + //~^ ERROR: function `ppriv` is private + + pub struct A; + impl A { + pub fn foo() {} + fn bar() {} + + pub fn foo2(&self) {} + fn bar2(&self) {} + } + + pub enum Enum { + priv Priv, + Pub + } + + mod baz { + pub struct A; + impl A { + pub fn foo() {} + fn bar() {} + + pub fn foo2(&self) {} + fn bar2(&self) {} + } + + // both of these are re-exported by `bar`, but only one should be + // validly re-exported + pub fn foo() {} + fn bar() {} + } + + extern { + fn epriv(); + pub fn epub(); + } + + fn test() { + self::Priv; + self::Pub; + unsafe { + epriv(); + epub(); + } + self::baz::A; + self::baz::A::foo(); + self::baz::A::bar(); //~ ERROR: method `bar` is private + self::baz::A.foo2(); + self::baz::A.bar2(); //~ ERROR: method `bar2` is private + + // this used to cause an ICE in privacy traversal. + super::gpub(); + } + + mod glob { + pub fn gpub() {} + fn gpriv() {} + } + + mod private { + fn ppriv() {} + } +} + +pub fn gpub() {} + +fn lol() { + bar::A; + bar::A::foo(); + bar::A::bar(); //~ ERROR: method `bar` is private + bar::A.foo2(); + bar::A.bar2(); //~ ERROR: method `bar2` is private +} + +mod foo { + fn test() { + ::bar::A::foo(); + ::bar::A::bar(); //~ ERROR: method `bar` is private + ::bar::A.foo2(); + ::bar::A.bar2(); //~ ERROR: method `bar2` is private + ::bar::baz::A::foo(); //~ ERROR: method `foo` is private + ::bar::baz::A::bar(); //~ ERROR: method `bar` is private + ::bar::baz::A.foo2(); //~ ERROR: struct `A` is private + ::bar::baz::A.bar2(); //~ ERROR: struct `A` is private + //~^ ERROR: method `bar2` is private + ::lol(); + + ::bar::Priv; //~ ERROR: variant `Priv` is private + ::bar::Pub; + + unsafe { + ::bar::epriv(); //~ ERROR: function `epriv` is private + ::bar::epub(); + } + + ::bar::foo(); + ::bar::bar(); + + ::bar::gpub(); + + ::bar::baz::foo(); //~ ERROR: function `foo` is private + ::bar::baz::bar(); //~ ERROR: function `bar` is private + } + + fn test2() { + use bar::baz::{foo, bar}; + //~^ ERROR: function `foo` is private + //~^^ ERROR: function `bar` is private + foo(); + bar(); + } + + fn test3() { + use bar::baz; + //~^ ERROR: module `baz` is private + } + + fn test4() { + use bar::{foo, bar}; + foo(); + bar(); + } + + fn test5() { + use bar; + bar::foo(); + bar::bar(); + } +} + +pub mod mytest { + // Even though the inner `A` struct is a publicly exported item (usable from + // external crates through `foo::foo`, it should not be accessible through + // its definition path (which has the private `i` module). + use self::foo::foo; + use self::foo::i::A; //~ ERROR: type `A` is private + + pub mod foo { + pub use foo = self::i::A; + + mod i { + pub struct A; + } + } +} + +#[start] fn main(_: int, _: **u8) -> int { 3 } diff --git a/src/test/compile-fail/privacy2.rs b/src/test/compile-fail/privacy2.rs new file mode 100644 index 00000000000..e8e21021ccc --- /dev/null +++ b/src/test/compile-fail/privacy2.rs @@ -0,0 +1,37 @@ +// 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. + +#[no_std]; // makes debugging this test *a lot* easier (during resolve) + +// Test to make sure that globs don't leak in regular `use` statements. + +mod bar { + pub use self::glob::*; + + mod glob { + use foo; + } +} + +pub fn foo() {} + +fn test1() { + use bar::foo; //~ ERROR: unresolved import + //~^ ERROR: failed to resolve +} + +fn test2() { + use bar::glob::foo; + //~^ ERROR: there is no + //~^^ ERROR: failed to resolve +} + +#[start] fn main(_: int, _: **u8) -> int { 3 } + diff --git a/src/test/compile-fail/privacy3.rs b/src/test/compile-fail/privacy3.rs new file mode 100644 index 00000000000..523d4cd4b8d --- /dev/null +++ b/src/test/compile-fail/privacy3.rs @@ -0,0 +1,32 @@ +// 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. + +#[no_std]; // makes debugging this test *a lot* easier (during resolve) + +// Test to make sure that private items imported through globs remain private +// when they're used. + +mod bar { + pub use self::glob::*; + + mod glob { + fn gpriv() {} + } +} + +pub fn foo() {} + +fn test1() { + use bar::gpriv; //~ ERROR: unresolved import + //~^ ERROR: failed to resolve + gpriv(); +} + +#[start] fn main(_: int, _: **u8) -> int { 3 } diff --git a/src/test/compile-fail/privacy4.rs b/src/test/compile-fail/privacy4.rs new file mode 100644 index 00000000000..88a9b2f3058 --- /dev/null +++ b/src/test/compile-fail/privacy4.rs @@ -0,0 +1,31 @@ +// 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. + +#[no_std]; // makes debugging this test *a lot* easier (during resolve) + +// Test to make sure that private items imported through globs remain private +// when they're used. + +mod bar { + pub use self::glob::*; + + mod glob { + fn gpriv() {} + } +} + +pub fn foo() {} + +fn test2() { + use bar::glob::gpriv; //~ ERROR: function `gpriv` is private + gpriv(); +} + +#[start] fn main(_: int, _: **u8) -> int { 3 }