diff --git a/src/librustc/hir/intravisit.rs b/src/librustc/hir/intravisit.rs index 55fd58da86635..6e6f845abd368 100644 --- a/src/librustc/hir/intravisit.rs +++ b/src/librustc/hir/intravisit.rs @@ -203,6 +203,9 @@ pub trait Visitor<'v> : Sized { fn visit_macro_def(&mut self, macro_def: &'v MacroDef) { walk_macro_def(self, macro_def) } + fn visit_vis(&mut self, vis: &'v Visibility) { + walk_vis(self, vis) + } } pub fn walk_opt_name<'v, V: Visitor<'v>>(visitor: &mut V, span: Span, opt_name: Option) { @@ -288,6 +291,7 @@ pub fn walk_trait_ref<'v, V>(visitor: &mut V, trait_ref: &'v TraitRef) } pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item) { + visitor.visit_vis(&item.vis); visitor.visit_name(item.span, item.name); match item.node { ItemExternCrate(opt_name) => { @@ -529,6 +533,7 @@ pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat) { } pub fn walk_foreign_item<'v, V: Visitor<'v>>(visitor: &mut V, foreign_item: &'v ForeignItem) { + visitor.visit_vis(&foreign_item.vis); visitor.visit_name(foreign_item.span, foreign_item.name); match foreign_item.node { @@ -662,6 +667,7 @@ pub fn walk_trait_item<'v, V: Visitor<'v>>(visitor: &mut V, trait_item: &'v Trai } pub fn walk_impl_item<'v, V: Visitor<'v>>(visitor: &mut V, impl_item: &'v ImplItem) { + visitor.visit_vis(&impl_item.vis); visitor.visit_name(impl_item.span, impl_item.name); walk_list!(visitor, visit_attribute, &impl_item.attrs); match impl_item.node { @@ -690,6 +696,7 @@ pub fn walk_struct_def<'v, V: Visitor<'v>>(visitor: &mut V, struct_definition: & } pub fn walk_struct_field<'v, V: Visitor<'v>>(visitor: &mut V, struct_field: &'v StructField) { + visitor.visit_vis(&struct_field.vis); visitor.visit_name(struct_field.span, struct_field.name); visitor.visit_ty(&struct_field.ty); walk_list!(visitor, visit_attribute, &struct_field.attrs); @@ -839,6 +846,12 @@ pub fn walk_arm<'v, V: Visitor<'v>>(visitor: &mut V, arm: &'v Arm) { walk_list!(visitor, visit_attribute, &arm.attrs); } +pub fn walk_vis<'v, V: Visitor<'v>>(visitor: &mut V, vis: &'v Visibility) { + if let Visibility::Restricted { ref path, id } = *vis { + visitor.visit_path(path, id) + } +} + #[derive(Copy, Clone, RustcEncodable, RustcDecodable, Debug)] pub struct IdRange { pub min: NodeId, diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 738a04dea585d..7c47665704118 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -1708,8 +1708,10 @@ pub fn lower_capture_clause(_lctx: &LoweringContext, c: CaptureBy) -> hir::Captu pub fn lower_visibility(lctx: &LoweringContext, v: &Visibility) -> hir::Visibility { match *v { Visibility::Public => hir::Public, + Visibility::Crate(_) => hir::Visibility::Crate, + Visibility::Restricted { ref path, id } => + hir::Visibility::Restricted { path: P(lower_path(lctx, path)), id: id }, Visibility::Inherited => hir::Inherited, - _ => panic!(lctx.diagnostic().fatal("pub(restricted) is not implemented yet!")) } } diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index 8e748875b93f1..4e7520035238f 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -30,7 +30,7 @@ pub use self::TyParamBound::*; pub use self::UnOp::*; pub use self::UnsafeSource::*; pub use self::ViewPath_::*; -pub use self::Visibility::*; +pub use self::Visibility::{Public, Inherited}; pub use self::PathParameters::*; use hir::def::Def; @@ -1434,6 +1434,8 @@ pub struct PolyTraitRef { #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] pub enum Visibility { Public, + Crate, + Restricted { path: P, id: NodeId }, Inherited, } diff --git a/src/librustc/hir/print.rs b/src/librustc/hir/print.rs index e9ed0ed574eaf..e595c619e859b 100644 --- a/src/librustc/hir/print.rs +++ b/src/librustc/hir/print.rs @@ -325,6 +325,8 @@ pub fn arg_to_string(arg: &hir::Arg) -> String { pub fn visibility_qualified(vis: &hir::Visibility, s: &str) -> String { match *vis { hir::Public => format!("pub {}", s), + hir::Visibility::Crate => format!("pub(crate) {}", s), + hir::Visibility::Restricted { ref path, .. } => format!("pub({}) {}", path, s), hir::Inherited => s.to_string(), } } @@ -898,6 +900,9 @@ impl<'a> State<'a> { pub fn print_visibility(&mut self, vis: &hir::Visibility) -> io::Result<()> { match *vis { hir::Public => self.word_nbsp("pub"), + hir::Visibility::Crate => self.word_nbsp("pub(crate)"), + hir::Visibility::Restricted { ref path, .. } => + self.word_nbsp(&format!("pub({})", path)), hir::Inherited => Ok(()), } } diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index eaba5d2a8602c..0377ef92a6f2f 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -287,6 +287,15 @@ impl Visibility { pub fn from_hir(visibility: &hir::Visibility, id: NodeId, tcx: &TyCtxt) -> Self { match *visibility { hir::Public => Visibility::Public, + hir::Visibility::Crate => Visibility::Restricted(ast::CRATE_NODE_ID), + hir::Visibility::Restricted { id, .. } => match tcx.def_map.borrow().get(&id) { + Some(resolution) => Visibility::Restricted({ + tcx.map.as_local_node_id(resolution.base_def.def_id()).unwrap() + }), + // If there is no resolution, `resolve` will have already reported an error, so + // assume that the visibility is public to avoid reporting more privacy errors. + None => Visibility::Public, + }, hir::Inherited => Visibility::Restricted(tcx.map.get_module_parent(id)), } } diff --git a/src/librustc_privacy/lib.rs b/src/librustc_privacy/lib.rs index a6ce4cc3ee41a..d55d0d53b8c48 100644 --- a/src/librustc_privacy/lib.rs +++ b/src/librustc_privacy/lib.rs @@ -1023,7 +1023,8 @@ impl<'a, 'tcx: 'a, 'v> Visitor<'v> for SearchInterfaceForPrivateItemsVisitor<'a, self.min_visibility = vis; } if !vis.is_at_least(self.required_visibility, &self.tcx.map) { - if self.old_error_set.contains(&ty.id) { + if self.tcx.sess.features.borrow().pub_restricted || + self.old_error_set.contains(&ty.id) { span_err!(self.tcx.sess, ty.span, E0446, "private type in public interface"); } else { @@ -1053,7 +1054,8 @@ impl<'a, 'tcx: 'a, 'v> Visitor<'v> for SearchInterfaceForPrivateItemsVisitor<'a, self.min_visibility = vis; } if !vis.is_at_least(self.required_visibility, &self.tcx.map) { - if self.old_error_set.contains(&trait_ref.ref_id) { + if self.tcx.sess.features.borrow().pub_restricted || + self.old_error_set.contains(&trait_ref.ref_id) { span_err!(self.tcx.sess, trait_ref.path.span, E0445, "private trait in public interface"); } else { diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs index 003450cd6fd79..2bec7725b76b7 100644 --- a/src/librustc_resolve/build_reduced_graph.rs +++ b/src/librustc_resolve/build_reduced_graph.rs @@ -53,10 +53,10 @@ impl<'a> ToNameBinding<'a> for (Module<'a>, Span) { } } -impl<'a> ToNameBinding<'a> for (Def, Span, DefModifiers) { +impl<'a> ToNameBinding<'a> for (Def, Span, DefModifiers, ty::Visibility) { fn to_name_binding(self) -> NameBinding<'a> { let kind = NameBindingKind::Def(self.0); - NameBinding { modifiers: self.2, kind: kind, span: Some(self.1) } + NameBinding { modifiers: self.2, kind: kind, span: Some(self.1), vis: self.3 } } } @@ -105,12 +105,9 @@ impl<'b, 'tcx:'b> Resolver<'b, 'tcx> { let parent = *parent_ref; let name = item.name; let sp = item.span; - let is_public = item.vis == hir::Public; - let modifiers = if is_public { - DefModifiers::PUBLIC - } else { - DefModifiers::empty() - } | DefModifiers::IMPORTABLE; + let modifiers = DefModifiers::IMPORTABLE; + self.current_module = parent; + let vis = self.resolve_visibility(&item.vis); match item.node { ItemUse(ref view_path) => { @@ -172,7 +169,7 @@ impl<'b, 'tcx:'b> Resolver<'b, 'tcx> { subclass, view_path.span, item.id, - is_public, + vis, is_prelude); } ViewPathList(_, ref source_items) => { @@ -223,7 +220,7 @@ impl<'b, 'tcx:'b> Resolver<'b, 'tcx> { subclass, source_item.span, source_item.node.id(), - is_public, + vis, is_prelude); } } @@ -233,7 +230,7 @@ impl<'b, 'tcx:'b> Resolver<'b, 'tcx> { GlobImport, view_path.span, item.id, - is_public, + vis, is_prelude); } } @@ -249,7 +246,7 @@ impl<'b, 'tcx:'b> Resolver<'b, 'tcx> { }; let parent_link = ModuleParentLink(parent, name); let def = Def::Mod(def_id); - let module = self.new_extern_crate_module(parent_link, def, is_public, item.id); + let module = self.new_extern_crate_module(parent_link, def, vis, item.id); self.define(parent, name, TypeNS, (module, sp)); self.build_reduced_graph_for_external_crate(module); @@ -259,7 +256,7 @@ impl<'b, 'tcx:'b> Resolver<'b, 'tcx> { ItemMod(..) => { let parent_link = ModuleParentLink(parent, name); let def = Def::Mod(self.ast_map.local_def_id(item.id)); - let module = self.new_module(parent_link, Some(def), false, is_public); + let module = self.new_module(parent_link, Some(def), false, vis); self.define(parent, name, TypeNS, (module, sp)); parent.module_children.borrow_mut().insert(item.id, module); *parent_ref = module; @@ -271,38 +268,32 @@ impl<'b, 'tcx:'b> Resolver<'b, 'tcx> { ItemStatic(_, m, _) => { let mutbl = m == hir::MutMutable; let def = Def::Static(self.ast_map.local_def_id(item.id), mutbl); - self.define(parent, name, ValueNS, (def, sp, modifiers)); + self.define(parent, name, ValueNS, (def, sp, modifiers, vis)); } ItemConst(_, _) => { let def = Def::Const(self.ast_map.local_def_id(item.id)); - self.define(parent, name, ValueNS, (def, sp, modifiers)); + self.define(parent, name, ValueNS, (def, sp, modifiers, vis)); } ItemFn(_, _, _, _, _, _) => { let def = Def::Fn(self.ast_map.local_def_id(item.id)); - self.define(parent, name, ValueNS, (def, sp, modifiers)); + self.define(parent, name, ValueNS, (def, sp, modifiers, vis)); } // These items live in the type namespace. ItemTy(..) => { let def = Def::TyAlias(self.ast_map.local_def_id(item.id)); - self.define(parent, name, TypeNS, (def, sp, modifiers)); + self.define(parent, name, TypeNS, (def, sp, modifiers, vis)); } ItemEnum(ref enum_definition, _) => { let parent_link = ModuleParentLink(parent, name); let def = Def::Enum(self.ast_map.local_def_id(item.id)); - let module = self.new_module(parent_link, Some(def), false, is_public); + let module = self.new_module(parent_link, Some(def), false, vis); self.define(parent, name, TypeNS, (module, sp)); - let variant_modifiers = if is_public { - DefModifiers::empty() - } else { - DefModifiers::PRIVATE_VARIANT - }; for variant in &(*enum_definition).variants { let item_def_id = self.ast_map.local_def_id(item.id); - self.build_reduced_graph_for_variant(variant, item_def_id, - module, variant_modifiers); + self.build_reduced_graph_for_variant(variant, item_def_id, module); } } @@ -310,20 +301,20 @@ impl<'b, 'tcx:'b> Resolver<'b, 'tcx> { ItemStruct(ref struct_def, _) => { // Define a name in the type namespace. let def = Def::Struct(self.ast_map.local_def_id(item.id)); - self.define(parent, name, TypeNS, (def, sp, modifiers)); + self.define(parent, name, TypeNS, (def, sp, modifiers, vis)); // If this is a newtype or unit-like struct, define a name // in the value namespace as well if !struct_def.is_struct() { let def = Def::Struct(self.ast_map.local_def_id(struct_def.id())); - self.define(parent, name, ValueNS, (def, sp, modifiers)); + self.define(parent, name, ValueNS, (def, sp, modifiers, vis)); } // Record the def ID and fields of this struct. - let field_names = struct_def.fields() - .iter() - .map(|f| f.name) - .collect(); + let field_names = struct_def.fields().iter().map(|field| { + self.resolve_visibility(&field.vis); + field.name + }).collect(); let item_def_id = self.ast_map.local_def_id(item.id); self.structs.insert(item_def_id, field_names); } @@ -336,7 +327,7 @@ impl<'b, 'tcx:'b> Resolver<'b, 'tcx> { // Add all the items within to a new module. let parent_link = ModuleParentLink(parent, name); let def = Def::Trait(def_id); - let module_parent = self.new_module(parent_link, Some(def), false, is_public); + let module_parent = self.new_module(parent_link, Some(def), false, vis); self.define(parent, name, TypeNS, (module_parent, sp)); // Add the names of all the items to the trait info. @@ -348,8 +339,8 @@ impl<'b, 'tcx:'b> Resolver<'b, 'tcx> { hir::TypeTraitItem(..) => (Def::AssociatedTy(def_id, item_def_id), TypeNS), }; - let modifiers = DefModifiers::PUBLIC; // NB: not DefModifiers::IMPORTABLE - self.define(module_parent, item.name, ns, (def, item.span, modifiers)); + let modifiers = DefModifiers::empty(); // NB: not DefModifiers::IMPORTABLE + self.define(module_parent, item.name, ns, (def, item.span, modifiers, vis)); self.trait_item_map.insert((item.name, def_id), item_def_id); } @@ -362,8 +353,7 @@ impl<'b, 'tcx:'b> Resolver<'b, 'tcx> { fn build_reduced_graph_for_variant(&mut self, variant: &Variant, item_id: DefId, - parent: Module<'b>, - variant_modifiers: DefModifiers) { + parent: Module<'b>) { let name = variant.node.name; if variant.node.data.is_struct() { // Not adding fields for variants as they are not accessed with a self receiver @@ -373,11 +363,11 @@ impl<'b, 'tcx:'b> Resolver<'b, 'tcx> { // Variants are always treated as importable to allow them to be glob used. // All variants are defined in both type and value namespaces as future-proofing. - let modifiers = DefModifiers::PUBLIC | DefModifiers::IMPORTABLE | variant_modifiers; + let modifiers = DefModifiers::IMPORTABLE; let def = Def::Variant(item_id, self.ast_map.local_def_id(variant.node.data.id())); - self.define(parent, name, ValueNS, (def, variant.span, modifiers)); - self.define(parent, name, TypeNS, (def, variant.span, modifiers)); + self.define(parent, name, ValueNS, (def, variant.span, modifiers, parent.vis)); + self.define(parent, name, TypeNS, (def, variant.span, modifiers, parent.vis)); } /// Constructs the reduced graph for one foreign item. @@ -385,12 +375,7 @@ impl<'b, 'tcx:'b> Resolver<'b, 'tcx> { foreign_item: &ForeignItem, parent: Module<'b>) { let name = foreign_item.name; - let is_public = foreign_item.vis == hir::Public; - let modifiers = if is_public { - DefModifiers::PUBLIC - } else { - DefModifiers::empty() - } | DefModifiers::IMPORTABLE; + let modifiers = DefModifiers::IMPORTABLE; let def = match foreign_item.node { ForeignItemFn(..) => { @@ -400,7 +385,9 @@ impl<'b, 'tcx:'b> Resolver<'b, 'tcx> { Def::Static(self.ast_map.local_def_id(foreign_item.id), m) } }; - self.define(parent, name, ValueNS, (def, foreign_item.span, modifiers)); + self.current_module = parent; + let vis = self.resolve_visibility(&foreign_item.vis); + self.define(parent, name, ValueNS, (def, foreign_item.span, modifiers, vis)); } fn build_reduced_graph_for_block(&mut self, block: &Block, parent: &mut Module<'b>) { @@ -412,7 +399,7 @@ impl<'b, 'tcx:'b> Resolver<'b, 'tcx> { block_id); let parent_link = BlockParentLink(parent, block_id); - let new_module = self.new_module(parent_link, None, false, false); + let new_module = self.new_module(parent_link, None, false, parent.vis); parent.module_children.borrow_mut().insert(block_id, new_module); *parent = new_module; } @@ -434,32 +421,27 @@ impl<'b, 'tcx:'b> Resolver<'b, 'tcx> { } let name = xcdef.name; - let is_public = xcdef.vis == ty::Visibility::Public || parent.is_trait(); - - let mut modifiers = DefModifiers::empty(); - if is_public { - modifiers = modifiers | DefModifiers::PUBLIC; - } - if parent.is_normal() { - modifiers = modifiers | DefModifiers::IMPORTABLE; - } + let vis = if parent.is_trait() { ty::Visibility::Public } else { xcdef.vis }; + let modifiers = match parent.is_normal() { + true => DefModifiers::IMPORTABLE, + false => DefModifiers::empty(), + }; match def { Def::Mod(_) | Def::ForeignMod(_) | Def::Enum(..) => { - debug!("(building reduced graph for external crate) building module {} {}", - name, - is_public); + debug!("(building reduced graph for external crate) building module {} {:?}", + name, vis); let parent_link = ModuleParentLink(parent, name); - let module = self.new_module(parent_link, Some(def), true, is_public); + let module = self.new_module(parent_link, Some(def), true, vis); self.try_define(parent, name, TypeNS, (module, DUMMY_SP)); } Def::Variant(_, variant_id) => { debug!("(building reduced graph for external crate) building variant {}", name); // Variants are always treated as importable to allow them to be glob used. // All variants are defined in both type and value namespaces as future-proofing. - let modifiers = DefModifiers::PUBLIC | DefModifiers::IMPORTABLE; - self.try_define(parent, name, TypeNS, (def, DUMMY_SP, modifiers)); - self.try_define(parent, name, ValueNS, (def, DUMMY_SP, modifiers)); + let modifiers = DefModifiers::IMPORTABLE; + self.try_define(parent, name, TypeNS, (def, DUMMY_SP, modifiers, vis)); + self.try_define(parent, name, ValueNS, (def, DUMMY_SP, modifiers, vis)); if self.session.cstore.variant_kind(variant_id) == Some(VariantKind::Struct) { // Not adding fields for variants as they are not accessed with a self receiver self.structs.insert(variant_id, Vec::new()); @@ -472,7 +454,7 @@ impl<'b, 'tcx:'b> Resolver<'b, 'tcx> { Def::Method(..) => { debug!("(building reduced graph for external crate) building value (fn/static) {}", name); - self.try_define(parent, name, ValueNS, (def, DUMMY_SP, modifiers)); + self.try_define(parent, name, ValueNS, (def, DUMMY_SP, modifiers, vis)); } Def::Trait(def_id) => { debug!("(building reduced graph for external crate) building type {}", name); @@ -493,21 +475,21 @@ impl<'b, 'tcx:'b> Resolver<'b, 'tcx> { } let parent_link = ModuleParentLink(parent, name); - let module = self.new_module(parent_link, Some(def), true, is_public); + let module = self.new_module(parent_link, Some(def), true, vis); self.try_define(parent, name, TypeNS, (module, DUMMY_SP)); } Def::TyAlias(..) | Def::AssociatedTy(..) => { debug!("(building reduced graph for external crate) building type {}", name); - self.try_define(parent, name, TypeNS, (def, DUMMY_SP, modifiers)); + self.try_define(parent, name, TypeNS, (def, DUMMY_SP, modifiers, vis)); } Def::Struct(def_id) if self.session.cstore.tuple_struct_definition_if_ctor(def_id).is_none() => { debug!("(building reduced graph for external crate) building type and value for {}", name); - self.try_define(parent, name, TypeNS, (def, DUMMY_SP, modifiers)); + self.try_define(parent, name, TypeNS, (def, DUMMY_SP, modifiers, vis)); if let Some(ctor_def_id) = self.session.cstore.struct_ctor_def_id(def_id) { let def = Def::Struct(ctor_def_id); - self.try_define(parent, name, ValueNS, (def, DUMMY_SP, modifiers)); + self.try_define(parent, name, ValueNS, (def, DUMMY_SP, modifiers, vis)); } // Record the def ID and fields of this struct. diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 5f4244caa629c..2147331d441d9 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -55,6 +55,7 @@ use rustc::middle::cstore::CrateStore; use rustc::hir::def::*; use rustc::hir::def_id::DefId; use rustc::hir::pat_util::pat_bindings; +use rustc::ty; use rustc::ty::subst::{ParamSpace, FnSpace, TypeSpace}; use rustc::hir::{Freevar, FreevarMap, TraitMap, GlobMap}; use rustc::util::nodemap::{NodeMap, FnvHashMap, FnvHashSet}; @@ -820,7 +821,7 @@ enum ParentLink<'a> { pub struct ModuleS<'a> { parent_link: ParentLink<'a>, def: Option, - is_public: bool, + vis: ty::Visibility, // If the module is an extern crate, `def` is root of the external crate and `extern_crate_id` // is the NodeId of the local `extern crate` item (otherwise, `extern_crate_id` is None). @@ -864,12 +865,12 @@ impl<'a> ModuleS<'a> { fn new(parent_link: ParentLink<'a>, def: Option, external: bool, - is_public: bool, + vis: ty::Visibility, arenas: &'a ResolverArenas<'a>) -> Self { ModuleS { parent_link: parent_link, def: def, - is_public: is_public, + vis: vis, extern_crate_id: None, resolutions: RefCell::new(HashMap::new()), unresolved_imports: RefCell::new(Vec::new()), @@ -892,9 +893,10 @@ impl<'a> ModuleS<'a> { self.def.as_ref().map(Def::def_id) } + // `self` resolves to the first module ancestor that `is_normal`. fn is_normal(&self) -> bool { match self.def { - Some(Def::Mod(_)) | Some(Def::ForeignMod(_)) => true, + Some(Def::Mod(_)) => true, _ => false, } } @@ -905,40 +907,18 @@ impl<'a> ModuleS<'a> { _ => false, } } - - fn is_ancestor_of(&self, module: Module<'a>) -> bool { - if self.def_id() == module.def_id() { return true } - match module.parent_link { - ParentLink::BlockParentLink(parent, _) | - ParentLink::ModuleParentLink(parent, _) => self.is_ancestor_of(parent), - _ => false, - } - } } impl<'a> fmt::Debug for ModuleS<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, - "{:?}, {}", - self.def, - if self.is_public { - "public" - } else { - "private" - }) + write!(f, "{:?}, {:?}", self.def, self.vis) } } bitflags! { #[derive(Debug)] flags DefModifiers: u8 { - // Enum variants are always considered `PUBLIC`, this is needed for `use Enum::Variant` - // or `use Enum::*` to work on private enums. - const PUBLIC = 1 << 0, const IMPORTABLE = 1 << 1, - // Variants are considered `PUBLIC`, but some of them live in private enums. - // We need to track them to prohibit reexports like `pub use PrivEnum::Variant`. - const PRIVATE_VARIANT = 1 << 2, const GLOB_IMPORTED = 1 << 3, } } @@ -949,6 +929,7 @@ pub struct NameBinding<'a> { modifiers: DefModifiers, kind: NameBindingKind<'a>, span: Option, + vis: ty::Visibility, } #[derive(Clone, Debug)] @@ -968,13 +949,12 @@ struct PrivacyError<'a>(Span, Name, &'a NameBinding<'a>); impl<'a> NameBinding<'a> { fn create_from_module(module: Module<'a>, span: Option) -> Self { - let modifiers = if module.is_public { - DefModifiers::PUBLIC - } else { - DefModifiers::empty() - } | DefModifiers::IMPORTABLE; - - NameBinding { modifiers: modifiers, kind: NameBindingKind::Module(module), span: span } + NameBinding { + modifiers: DefModifiers::IMPORTABLE, + kind: NameBindingKind::Module(module), + span: span, + vis: module.vis, + } } fn module(&self) -> Option> { @@ -997,8 +977,20 @@ impl<'a> NameBinding<'a> { self.modifiers.contains(modifiers) } - fn is_public(&self) -> bool { - self.defined_with(DefModifiers::PUBLIC) + fn is_pseudo_public(&self) -> bool { + self.pseudo_vis() == ty::Visibility::Public + } + + // We sometimes need to treat variants as `pub` for backwards compatibility + fn pseudo_vis(&self) -> ty::Visibility { + if self.is_variant() { ty::Visibility::Public } else { self.vis } + } + + fn is_variant(&self) -> bool { + match self.kind { + NameBindingKind::Def(Def::Variant(..)) => true, + _ => false, + } } fn is_extern_crate(&self) -> bool { @@ -1148,8 +1140,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { arenas: &'a ResolverArenas<'a>) -> Resolver<'a, 'tcx> { let root_def_id = ast_map.local_def_id(CRATE_NODE_ID); + let vis = ty::Visibility::Public; let graph_root = - ModuleS::new(NoParentLink, Some(Def::Mod(root_def_id)), false, true, arenas); + ModuleS::new(NoParentLink, Some(Def::Mod(root_def_id)), false, vis, arenas); let graph_root = arenas.alloc_module(graph_root); Resolver { @@ -1209,17 +1202,17 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { parent_link: ParentLink<'a>, def: Option, external: bool, - is_public: bool) -> Module<'a> { - self.arenas.alloc_module(ModuleS::new(parent_link, def, external, is_public, self.arenas)) + vis: ty::Visibility) -> Module<'a> { + self.arenas.alloc_module(ModuleS::new(parent_link, def, external, vis, self.arenas)) } fn new_extern_crate_module(&self, parent_link: ParentLink<'a>, def: Def, - is_public: bool, + vis: ty::Visibility, local_node_id: NodeId) -> Module<'a> { - let mut module = ModuleS::new(parent_link, Some(def), false, is_public, self.arenas); + let mut module = ModuleS::new(parent_link, Some(def), false, vis, self.arenas); module.extern_crate_id = Some(local_node_id); self.arenas.modules.alloc(module) } @@ -1335,7 +1328,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { // Check to see whether there are type bindings, and, if // so, whether there is a module within. if let Some(module_def) = binding.module() { - self.check_privacy(search_module, name, binding, span); + self.check_privacy(name, binding, span); search_module = module_def; } else { let msg = format!("Not a module `{}`", name); @@ -1466,7 +1459,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } /// Returns the nearest normal module parent of the given module. - fn get_nearest_normal_module_parent(&mut self, module_: Module<'a>) -> Option> { + fn get_nearest_normal_module_parent(&self, module_: Module<'a>) -> Option> { let mut module_ = module_; loop { match module_.parent_link { @@ -1485,7 +1478,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { /// Returns the nearest normal module parent of the given module, or the /// module itself if it is a normal module. - fn get_nearest_normal_module_parent_or_self(&mut self, module_: Module<'a>) -> Module<'a> { + fn get_nearest_normal_module_parent_or_self(&self, module_: Module<'a>) -> Module<'a> { if module_.is_normal() { return module_; } @@ -1617,7 +1610,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { fn resolve_crate(&mut self, krate: &hir::Crate) { debug!("(resolving crate) starting"); - + self.current_module = self.graph_root; intravisit::walk_crate(self, krate); } @@ -1980,6 +1973,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { this.with_self_rib(Def::SelfTy(trait_id, Some((item_id, self_type.id))), |this| { this.with_current_self_type(self_type, |this| { for impl_item in impl_items { + this.resolve_visibility(&impl_item.vis); match impl_item.node { hir::ImplItemKind::Const(..) => { // If this is a trait impl, ensure the const @@ -2772,7 +2766,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let name = segments.last().unwrap().identifier.name; let result = self.resolve_name_in_module(containing_module, name, namespace, false, true); result.success().map(|binding| { - self.check_privacy(containing_module, name, binding, span); + self.check_privacy(name, binding, span); binding.def().unwrap() }).ok_or(false) } @@ -2822,7 +2816,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let name = segments.last().unwrap().identifier.name; let result = self.resolve_name_in_module(containing_module, name, namespace, false, true); result.success().map(|binding| { - self.check_privacy(containing_module, name, binding, span); + self.check_privacy(name, binding, span); binding.def().unwrap() }).ok_or(false) } @@ -3314,7 +3308,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { // only if both the module is public and the entity is // declared as public (due to pruning, we don't explore // outside crate private modules => no need to check this) - if !in_module_is_extern || name_binding.is_public() { + if !in_module_is_extern || name_binding.vis == ty::Visibility::Public { lookup_results.push(path); } } @@ -3339,7 +3333,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { _ => bug!(), }; - if !in_module_is_extern || name_binding.is_public() { + if !in_module_is_extern || name_binding.vis == ty::Visibility::Public { // add the module to the lookup let is_extern = in_module_is_extern || name_binding.is_extern_crate(); worklist.push((module, path_segments, is_extern)); @@ -3379,16 +3373,51 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } } - fn is_visible(&self, binding: &'a NameBinding<'a>, parent: Module<'a>) -> bool { - binding.is_public() || parent.is_ancestor_of(self.current_module) + fn resolve_visibility(&mut self, vis: &hir::Visibility) -> ty::Visibility { + let (path, id) = match *vis { + hir::Public => return ty::Visibility::Public, + hir::Visibility::Crate => return ty::Visibility::Restricted(ast::CRATE_NODE_ID), + hir::Visibility::Restricted { ref path, id } => (path, id), + hir::Inherited => { + let current_module = + self.get_nearest_normal_module_parent_or_self(self.current_module); + let id = self.ast_map.as_local_node_id(current_module.def_id().unwrap()).unwrap(); + return ty::Visibility::Restricted(id); + } + }; + + let segments: Vec<_> = path.segments.iter().map(|seg| seg.identifier.name).collect(); + let vis = match self.resolve_module_path(&segments, DontUseLexicalScope, path.span) { + Success(module) => { + let def = module.def.unwrap(); + let path_resolution = PathResolution { base_def: def, depth: 0 }; + self.def_map.borrow_mut().insert(id, path_resolution); + ty::Visibility::Restricted(self.ast_map.as_local_node_id(def.def_id()).unwrap()) + } + Failed(Some((span, msg))) => { + self.session.span_err(span, &format!("failed to resolve module path. {}", msg)); + ty::Visibility::Public + } + _ => { + self.session.span_err(path.span, "unresolved module path"); + ty::Visibility::Public + } + }; + if !self.is_accessible(vis) { + let msg = format!("visibilities can only be restricted to ancestor modules"); + self.session.span_err(path.span, &msg); + } + vis + } + + fn is_accessible(&self, vis: ty::Visibility) -> bool { + let current_module = self.get_nearest_normal_module_parent_or_self(self.current_module); + let node_id = self.ast_map.as_local_node_id(current_module.def_id().unwrap()).unwrap(); + vis.is_accessible_from(node_id, &self.ast_map) } - fn check_privacy(&mut self, - module: Module<'a>, - name: Name, - binding: &'a NameBinding<'a>, - span: Span) { - if !self.is_visible(binding, module) { + fn check_privacy(&mut self, name: Name, binding: &'a NameBinding<'a>, span: Span) { + if !self.is_accessible(binding.vis) { self.privacy_errors.push(PrivacyError(span, name, binding)); } } diff --git a/src/librustc_resolve/resolve_imports.rs b/src/librustc_resolve/resolve_imports.rs index ff684d37653cb..e712dbdcbf7af 100644 --- a/src/librustc_resolve/resolve_imports.rs +++ b/src/librustc_resolve/resolve_imports.rs @@ -21,6 +21,7 @@ use UseLexicalScopeFlag::DontUseLexicalScope; use {names_to_string, module_to_string}; use {resolve_error, ResolutionError}; +use rustc::ty; use rustc::lint; use rustc::hir::def::*; @@ -63,7 +64,7 @@ pub struct ImportDirective<'a> { subclass: ImportDirectiveSubclass, span: Span, id: NodeId, - is_public: bool, // see note in ImportResolutionPerNamespace about how to use this + vis: ty::Visibility, // see note in ImportResolutionPerNamespace about how to use this is_prelude: bool, } @@ -72,10 +73,7 @@ impl<'a> ImportDirective<'a> { // this returns the binding for the name this directive defines in that namespace. fn import(&self, binding: &'a NameBinding<'a>, privacy_error: Option>>) -> NameBinding<'a> { - let mut modifiers = match self.is_public { - true => DefModifiers::PUBLIC | DefModifiers::IMPORTABLE, - false => DefModifiers::empty(), - }; + let mut modifiers = DefModifiers::IMPORTABLE; if let GlobImport = self.subclass { modifiers = modifiers | DefModifiers::GLOB_IMPORTED; } @@ -88,6 +86,7 @@ impl<'a> ImportDirective<'a> { }, span: Some(self.span), modifiers: modifiers, + vis: self.vis, } } } @@ -184,8 +183,8 @@ impl<'a> NameResolution<'a> { // If (1) we don't allow private imports, (2) no public single import can define // the name, and (3) no public glob has defined the name, the resolution depends // on whether more globs can define the name. - if !allow_private_imports && !directive.is_public && - !self.binding.map(NameBinding::is_public).unwrap_or(false) { + if !allow_private_imports && directive.vis != ty::Visibility::Public && + !self.binding.map(NameBinding::is_pseudo_public).unwrap_or(false) { return None; } @@ -243,14 +242,15 @@ impl<'a> ::ModuleS<'a> { if let Some(result) = resolution.try_result(ns, allow_private_imports) { // If the resolution doesn't depend on glob definability, check privacy and return. return result.and_then(|binding| { - let allowed = allow_private_imports || !binding.is_import() || binding.is_public(); + let allowed = allow_private_imports || !binding.is_import() || + binding.is_pseudo_public(); if allowed { Success(binding) } else { Failed(None) } }); } // Check if the globs are determined for directive in self.globs.borrow().iter() { - if !allow_private_imports && !directive.is_public { continue } + if !allow_private_imports && directive.vis != ty::Visibility::Public { continue } match directive.target_module.get() { None => return Indeterminate, Some(target_module) => match target_module.resolve_name(name, ns, false) { @@ -285,7 +285,7 @@ impl<'a> ::ModuleS<'a> { subclass: ImportDirectiveSubclass, span: Span, id: NodeId, - is_public: bool, + vis: ty::Visibility, is_prelude: bool) { let directive = self.arenas.alloc_import_directive(ImportDirective { module_path: module_path, @@ -293,7 +293,7 @@ impl<'a> ::ModuleS<'a> { subclass: subclass, span: span, id: id, - is_public: is_public, + vis: vis, is_prelude: is_prelude, }); @@ -337,7 +337,7 @@ impl<'a> ::ModuleS<'a> { } fn define_in_glob_importers(&self, name: Name, ns: Namespace, binding: &'a NameBinding<'a>) { - if !binding.defined_with(DefModifiers::PUBLIC | DefModifiers::IMPORTABLE) { return } + if !binding.defined_with(DefModifiers::IMPORTABLE) || !binding.is_pseudo_public() { return } for &(importer, directive) in self.glob_importers.borrow_mut().iter() { let _ = importer.try_define_child(name, ns, directive.import(binding, None)); } @@ -413,6 +413,7 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> { modifiers: DefModifiers::GLOB_IMPORTED, kind: NameBindingKind::Def(Def::Err), span: None, + vis: ty::Visibility::Public, }); let dummy_binding = e.import_directive.import(dummy_binding, None); @@ -521,7 +522,7 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> { span_err!(self.resolver.session, directive.span, E0253, "{}", &msg); } - let privacy_error = if !self.resolver.is_visible(binding, target_module) { + let privacy_error = if !self.resolver.is_accessible(binding.vis) { Some(Box::new(PrivacyError(directive.span, source, binding))) } else { None @@ -567,10 +568,10 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> { _ => (), } + let ast_map = self.resolver.ast_map; match (&value_result, &type_result) { - (&Success(name_binding), _) if !name_binding.is_import() && - directive.is_public && - !name_binding.is_public() => { + (&Success(binding), _) if !binding.pseudo_vis().is_at_least(directive.vis, ast_map) && + self.resolver.is_accessible(binding.vis) => { let msg = format!("`{}` is private, and cannot be reexported", source); let note_msg = format!("consider marking `{}` as `pub` in the imported module", source); @@ -579,10 +580,9 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> { .emit(); } - (_, &Success(name_binding)) if !name_binding.is_import() && - directive.is_public && - !name_binding.is_public() => { - if name_binding.is_extern_crate() { + (_, &Success(binding)) if !binding.pseudo_vis().is_at_least(directive.vis, ast_map) && + self.resolver.is_accessible(binding.vis) => { + if binding.is_extern_crate() { let msg = format!("extern crate `{}` is private, and cannot be reexported \ (error E0364), consider declaring with `pub`", source); @@ -662,7 +662,7 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> { resolution.borrow().binding().map(|binding| (*name, binding)) }).collect::>(); for ((name, ns), binding) in bindings { - if binding.defined_with(DefModifiers::IMPORTABLE | DefModifiers::PUBLIC) { + if binding.defined_with(DefModifiers::IMPORTABLE) && binding.is_pseudo_public() { let _ = module_.try_define_child(name, ns, directive.import(binding, None)); } } @@ -698,15 +698,16 @@ impl<'a, 'b:'a, 'tcx:'b> ImportResolver<'a, 'b, 'tcx> { None => continue, }; - if binding.is_public() && (binding.is_import() || binding.is_extern_crate()) { + if binding.vis == ty::Visibility::Public && + (binding.is_import() || binding.is_extern_crate()) { if let Some(def) = binding.def() { reexports.push(Export { name: name, def_id: def.def_id() }); } } if let NameBindingKind::Import { binding: orig_binding, id, .. } = binding.kind { - if ns == TypeNS && binding.is_public() && - orig_binding.defined_with(DefModifiers::PRIVATE_VARIANT) { + if ns == TypeNS && orig_binding.is_variant() && + !orig_binding.vis.is_at_least(binding.vis, &self.resolver.ast_map) { let msg = format!("variant `{}` is private, and cannot be reexported \ (error E0364), consider declaring its enum as `pub`", name); diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 850975498269b..1e9796c5f66f9 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -122,7 +122,7 @@ fn try_inline_def(cx: &DocContext, tcx: &TyCtxt, name: Some(tcx.item_name(did).to_string()), attrs: load_attrs(cx, tcx, did), inner: inner, - visibility: Some(hir::Public), + visibility: Some(clean::Public), stability: stability::lookup_stability(tcx, did).clean(cx), deprecation: stability::lookup_deprecation(tcx, did).clean(cx), def_id: did, @@ -323,7 +323,7 @@ pub fn build_impl(cx: &DocContext, source: clean::Span::empty(), name: None, attrs: attrs, - visibility: Some(hir::Inherited), + visibility: Some(clean::Inherited), stability: stability::lookup_stability(tcx, did).clean(cx), deprecation: stability::lookup_deprecation(tcx, did).clean(cx), def_id: did, @@ -444,7 +444,7 @@ pub fn build_impl(cx: &DocContext, source: clean::Span::empty(), name: None, attrs: attrs, - visibility: Some(hir::Inherited), + visibility: Some(clean::Inherited), stability: stability::lookup_stability(tcx, did).clean(cx), deprecation: stability::lookup_deprecation(tcx, did).clean(cx), def_id: did, diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index e2189e003ae9d..42db93e180344 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -22,6 +22,7 @@ pub use self::Attribute::*; pub use self::TyParamBound::*; pub use self::SelfTy::*; pub use self::FunctionRetTy::*; +pub use self::Visibility::*; use syntax; use syntax::abi::Abi; @@ -183,7 +184,7 @@ impl<'a, 'tcx> Clean for visit_ast::RustdocVisitor<'a, 'tcx> { source: Span::empty(), name: Some(prim.to_url_str().to_string()), attrs: child.attrs.clone(), - visibility: Some(hir::Public), + visibility: Some(Public), stability: None, deprecation: None, def_id: DefId::local(prim.to_def_index()), @@ -1391,7 +1392,7 @@ impl<'tcx> Clean for ty::Method<'tcx> { Item { name: Some(self.name.clean(cx)), - visibility: Some(hir::Inherited), + visibility: Some(Inherited), stability: get_stability(cx, self.def_id), deprecation: get_deprecation(cx, self.def_id), def_id: self.def_id, @@ -1777,17 +1778,21 @@ impl<'tcx> Clean for ty::FieldDefData<'tcx, 'static> { } } -pub type Visibility = hir::Visibility; +#[derive(Clone, PartialEq, Eq, RustcDecodable, RustcEncodable, Debug)] +pub enum Visibility { + Public, + Inherited, +} impl Clean> for hir::Visibility { fn clean(&self, _: &DocContext) -> Option { - Some(self.clone()) + Some(if *self == hir::Visibility::Public { Public } else { Inherited }) } } impl Clean> for ty::Visibility { fn clean(&self, _: &DocContext) -> Option { - Some(if *self == ty::Visibility::Public { hir::Public } else { hir::Inherited }) + Some(if *self == ty::Visibility::Public { Public } else { Inherited }) } } @@ -1919,7 +1924,7 @@ impl<'tcx> Clean for ty::VariantDefData<'tcx, 'static> { name: Some(self.name.clean(cx)), attrs: inline::load_attrs(cx, cx.tcx(), self.did), source: Span::empty(), - visibility: Some(hir::Inherited), + visibility: Some(Inherited), def_id: self.did, inner: VariantItem(Variant { kind: kind }), stability: get_stability(cx, self.did), @@ -2341,7 +2346,7 @@ impl Clean for doctree::DefaultImpl { attrs: self.attrs.clean(cx), source: self.whence.clean(cx), def_id: cx.map.local_def_id(self.id), - visibility: Some(hir::Public), + visibility: Some(Public), stability: None, deprecation: None, inner: DefaultImplItem(DefaultImpl { @@ -2700,7 +2705,7 @@ impl Clean for doctree::Macro { name: Some(name.clone()), attrs: self.attrs.clean(cx), source: self.whence.clean(cx), - visibility: hir::Public.clean(cx), + visibility: Some(Public), stability: self.stab.clean(cx), deprecation: self.depr.clean(cx), def_id: cx.map.local_def_id(self.id), diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index ce20ad05acb0e..d2b43ae5bda15 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -31,7 +31,7 @@ use html::render::{cache, CURRENT_LOCATION_KEY}; /// Helper to render an optional visibility with a space after it (if the /// visibility is preset) #[derive(Copy, Clone)] -pub struct VisSpace<'a>(pub &'a Option); +pub struct VisSpace<'a>(pub &'a Option); /// Similarly to VisSpace, this structure is used to render a function style with a /// space after it. #[derive(Copy, Clone)] @@ -57,7 +57,7 @@ pub struct CommaSep<'a, T: 'a>(pub &'a [T]); pub struct AbiSpace(pub Abi); impl<'a> VisSpace<'a> { - pub fn get(self) -> &'a Option { + pub fn get(self) -> &'a Option { let VisSpace(v) = self; v } } @@ -639,8 +639,8 @@ impl<'a> fmt::Display for Method<'a> { impl<'a> fmt::Display for VisSpace<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self.get() { - Some(hir::Public) => write!(f, "pub "), - Some(hir::Inherited) | None => Ok(()) + Some(clean::Public) => write!(f, "pub "), + Some(clean::Inherited) | None => Ok(()) } } } diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index c5850089578cd..92e014e81eaf2 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -1408,7 +1408,8 @@ impl Context { match it.inner { clean::StrippedItem(..) => true, clean::ModuleItem(ref m) => { - it.doc_value().is_none() && m.items.is_empty() && it.visibility != Some(hir::Public) + it.doc_value().is_none() && m.items.is_empty() + && it.visibility != Some(clean::Public) }, _ => false, } diff --git a/src/librustdoc/passes.rs b/src/librustdoc/passes.rs index adc39b69986d6..0042afb1fd514 100644 --- a/src/librustdoc/passes.rs +++ b/src/librustdoc/passes.rs @@ -14,7 +14,6 @@ use rustc::util::nodemap::DefIdSet; use std::cmp; use std::string::String; use std::usize; -use rustc::hir; use clean::{self, Attributes, GetDefId}; use clean::Item; @@ -133,13 +132,13 @@ impl<'a> fold::DocFolder for Stripper<'a> { } clean::StructFieldItem(..) => { - if i.visibility != Some(hir::Public) { + if i.visibility != Some(clean::Public) { return Strip(i).fold(); } } clean::ModuleItem(..) => { - if i.def_id.is_local() && i.visibility != Some(hir::Public) { + if i.def_id.is_local() && i.visibility != Some(clean::Public) { return Strip(self.fold_item_recur(i).unwrap()).fold() } } @@ -226,7 +225,7 @@ impl fold::DocFolder for ImportStripper { fn fold_item(&mut self, i: Item) -> Option { match i.inner { clean::ExternCrateItem(..) | - clean::ImportItem(..) if i.visibility != Some(hir::Public) => None, + clean::ImportItem(..) if i.visibility != Some(clean::Public) => None, _ => self.fold_item_recur(i) } } diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 16d4ed53b5b19..df9f935446d71 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -1887,7 +1887,7 @@ pub struct PolyTraitRef { #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] pub enum Visibility { Public, - Crate, + Crate(Span), Restricted { path: P, id: NodeId }, Inherited, } diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index fc18ef407ab2f..9960e88e7fb5a 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -256,6 +256,9 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Option, Status // impl specialization (RFC 1210) ("specialization", "1.7.0", Some(31844), Active), + + // pub(restricted) visibilities (RFC 1422) + ("pub_restricted", "1.9.0", Some(32409), Active), ]; // (changing above list without updating src/doc/reference.md makes @cmr sad) @@ -608,6 +611,7 @@ pub struct Features { pub deprecated: bool, pub question_mark: bool, pub specialization: bool, + pub pub_restricted: bool, } impl Features { @@ -644,6 +648,7 @@ impl Features { deprecated: false, question_mark: false, specialization: false, + pub_restricted: false, } } } @@ -1159,6 +1164,15 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> { } visit::walk_impl_item(self, ii); } + + fn visit_vis(&mut self, vis: &'v ast::Visibility) { + let span = match *vis { + ast::Visibility::Crate(span) => span, + ast::Visibility::Restricted { ref path, .. } => path.span, + _ => return, + }; + self.gate_feature("pub_restricted", span, "`pub(restricted)` syntax is experimental"); + } } fn check_crate_inner(cm: &CodeMap, span_handler: &Handler, @@ -1256,6 +1270,7 @@ fn check_crate_inner(cm: &CodeMap, span_handler: &Handler, deprecated: cx.has_feature("deprecated"), question_mark: cx.has_feature("question_mark"), specialization: cx.has_feature("specialization"), + pub_restricted: cx.has_feature("pub_restricted"), } } diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 5d378763bef63..89451e795503f 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -850,7 +850,7 @@ pub fn noop_fold_struct_field(f: StructField, fld: &mut T) -> StructF span: fld.new_span(f.span), id: fld.new_id(f.id), ident: f.ident.map(|ident| fld.fold_ident(ident)), - vis: f.vis, + vis: fld.fold_vis(f.vis), ty: fld.fold_ty(f.ty), attrs: fold_attrs(f.attrs, fld), } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index c4997348537f4..e0e72a152820e 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -70,6 +70,7 @@ bitflags! { const RESTRICTION_STMT_EXPR = 1 << 0, const RESTRICTION_NO_STRUCT_LITERAL = 1 << 1, const NO_NONINLINE_MOD = 1 << 2, + const ALLOW_MODULE_PATHS = 1 << 3, } } @@ -560,7 +561,9 @@ impl<'a> Parser<'a> { } pub fn parse_ident(&mut self) -> PResult<'a, ast::Ident> { - self.check_strict_keywords(); + if !self.restrictions.contains(Restrictions::ALLOW_MODULE_PATHS) { + self.check_strict_keywords(); + } self.check_reserved_keywords(); match self.token { token::Ident(i, _) => { @@ -4938,7 +4941,7 @@ impl<'a> Parser<'a> { let mut attrs = self.parse_outer_attributes()?; let lo = self.span.lo; - let vis = self.parse_visibility()?; + let vis = self.parse_visibility(true)?; let defaultness = self.parse_defaultness()?; let (name, node) = if self.eat_keyword(keywords::Type) { let name = self.parse_ident()?; @@ -5250,7 +5253,7 @@ impl<'a> Parser<'a> { |p| { let attrs = p.parse_outer_attributes()?; let lo = p.span.lo; - let vis = p.parse_visibility()?; + let vis = p.parse_visibility(false)?; let ty = p.parse_ty_sum()?; Ok(StructField { span: mk_sp(lo, p.span.hi), @@ -5290,20 +5293,26 @@ impl<'a> Parser<'a> { /// Parse an element of a struct definition fn parse_struct_decl_field(&mut self) -> PResult<'a, StructField> { - let attrs = self.parse_outer_attributes()?; - - if self.eat_keyword(keywords::Pub) { - return self.parse_single_struct_field(Visibility::Public, attrs); - } - - return self.parse_single_struct_field(Visibility::Inherited, attrs); + let vis = self.parse_visibility(true)?; + self.parse_single_struct_field(vis, attrs) } - /// Parse visibility: PUB or nothing - fn parse_visibility(&mut self) -> PResult<'a, Visibility> { - if self.eat_keyword(keywords::Pub) { Ok(Visibility::Public) } - else { Ok(Visibility::Inherited) } + fn parse_visibility(&mut self, allow_restricted: bool) -> PResult<'a, Visibility> { + if !self.eat_keyword(keywords::Pub) { + Ok(Visibility::Inherited) + } else if !allow_restricted || !self.eat(&token::OpenDelim(token::Paren)) { + Ok(Visibility::Public) + } else if self.eat_keyword(keywords::Crate) { + let span = self.last_span; + self.expect(&token::CloseDelim(token::Paren))?; + Ok(Visibility::Crate(span)) + } else { + let path = self.with_res(Restrictions::ALLOW_MODULE_PATHS, + |this| this.parse_path(NoTypesAllowed))?; + self.expect(&token::CloseDelim(token::Paren))?; + Ok(Visibility::Restricted { path: P(path), id: ast::DUMMY_NODE_ID }) + } } /// Parse defaultness: DEFAULT or nothing @@ -5765,7 +5774,7 @@ impl<'a> Parser<'a> { let lo = self.span.lo; - let visibility = self.parse_visibility()?; + let visibility = self.parse_visibility(true)?; if self.eat_keyword(keywords::Use) { // USE ITEM @@ -6015,7 +6024,7 @@ impl<'a> Parser<'a> { fn parse_foreign_item(&mut self) -> PResult<'a, Option> { let attrs = self.parse_outer_attributes()?; let lo = self.span.lo; - let visibility = self.parse_visibility()?; + let visibility = self.parse_visibility(true)?; if self.check_keyword(keywords::Static) { // FOREIGN STATIC ITEM diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index fcd83b4104130..95f1b63168b47 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -435,7 +435,7 @@ pub fn mac_to_string(arg: &ast::Mac) -> String { pub fn visibility_qualified(vis: &ast::Visibility, s: &str) -> String { match *vis { ast::Visibility::Public => format!("pub {}", s), - ast::Visibility::Crate => format!("pub(crate) {}", s), + ast::Visibility::Crate(_) => format!("pub(crate) {}", s), ast::Visibility::Restricted { ref path, .. } => format!("pub({}) {}", path, s), ast::Visibility::Inherited => s.to_string() } @@ -1386,7 +1386,7 @@ impl<'a> State<'a> { pub fn print_visibility(&mut self, vis: &ast::Visibility) -> io::Result<()> { match *vis { ast::Visibility::Public => self.word_nbsp("pub"), - ast::Visibility::Crate => self.word_nbsp("pub(crate)"), + ast::Visibility::Crate(_) => self.word_nbsp("pub(crate)"), ast::Visibility::Restricted { ref path, .. } => self.word_nbsp(&format!("pub({})", path)), ast::Visibility::Inherited => Ok(()) diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index 839bbf4805df2..413017c727177 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -231,6 +231,7 @@ pub fn walk_trait_ref<'v,V>(visitor: &mut V, } pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item) { + visitor.visit_vis(&item.vis); visitor.visit_ident(item.span, item.ident); match item.node { ItemKind::ExternCrate(opt_name) => { @@ -470,6 +471,7 @@ pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat) { pub fn walk_foreign_item<'v, V: Visitor<'v>>(visitor: &mut V, foreign_item: &'v ForeignItem) { + visitor.visit_vis(&foreign_item.vis); visitor.visit_ident(foreign_item.span, foreign_item.ident); match foreign_item.node { @@ -592,6 +594,7 @@ pub fn walk_trait_item<'v, V: Visitor<'v>>(visitor: &mut V, trait_item: &'v Trai } pub fn walk_impl_item<'v, V: Visitor<'v>>(visitor: &mut V, impl_item: &'v ImplItem) { + visitor.visit_vis(&impl_item.vis); visitor.visit_ident(impl_item.span, impl_item.ident); walk_list!(visitor, visit_attribute, &impl_item.attrs); match impl_item.node { @@ -619,6 +622,7 @@ pub fn walk_struct_def<'v, V: Visitor<'v>>(visitor: &mut V, pub fn walk_struct_field<'v, V: Visitor<'v>>(visitor: &mut V, struct_field: &'v StructField) { + visitor.visit_vis(&struct_field.vis); walk_opt_ident(visitor, struct_field.span, struct_field.ident); visitor.visit_ty(&struct_field.ty); walk_list!(visitor, visit_attribute, &struct_field.attrs); @@ -812,8 +816,7 @@ pub fn walk_arm<'v, V: Visitor<'v>>(visitor: &mut V, arm: &'v Arm) { } pub fn walk_vis<'v, V: Visitor<'v>>(visitor: &mut V, vis: &'v Visibility) { - match *vis { - Visibility::Restricted { ref path, id } => visitor.visit_path(path, id), - _ => {} + if let Visibility::Restricted { ref path, id } = *vis { + visitor.visit_path(path, id); } } diff --git a/src/test/auxiliary/pub_restricted.rs b/src/test/auxiliary/pub_restricted.rs new file mode 100644 index 0000000000000..b1c88ce6ce55c --- /dev/null +++ b/src/test/auxiliary/pub_restricted.rs @@ -0,0 +1,23 @@ +// Copyright 2016 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. + +#![feature(pub_restricted)] + +pub(crate) struct Crate; +#[derive(Default)] +pub struct Universe { + pub x: i32, + pub(crate) y: i32 +} + +impl Universe { + pub fn f(&self) {} + pub(crate) fn g(&self) {} +} diff --git a/src/test/compile-fail/privacy/restricted/feature-gate.rs b/src/test/compile-fail/privacy/restricted/feature-gate.rs new file mode 100644 index 0000000000000..53ae439867f98 --- /dev/null +++ b/src/test/compile-fail/privacy/restricted/feature-gate.rs @@ -0,0 +1,25 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub(crate) //~ ERROR experimental +mod foo {} + +pub(self) //~ ERROR experimental +mod bar {} + +struct S { + pub(self) x: i32, //~ ERROR experimental +} +impl S { + pub(self) fn f() {} //~ ERROR experimental +} +extern { + pub(self) fn f(); //~ ERROR experimental +} diff --git a/src/test/compile-fail/privacy/restricted/lookup-ignores-private.rs b/src/test/compile-fail/privacy/restricted/lookup-ignores-private.rs new file mode 100644 index 0000000000000..4e2a69cb79e19 --- /dev/null +++ b/src/test/compile-fail/privacy/restricted/lookup-ignores-private.rs @@ -0,0 +1,44 @@ +// Copyright 2016 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. + +#![feature(rustc_attrs, pub_restricted)] +#![allow(warnings)] + +mod foo { + pub use foo::bar::S; + mod bar { + #[derive(Default)] + pub struct S { + pub(foo) x: i32, + } + impl S { + pub(foo) fn f(&self) -> i32 { 0 } + } + + pub struct S2 { + pub(crate) x: bool, + } + impl S2 { + pub(crate) fn f(&self) -> bool { false } + } + + impl ::std::ops::Deref for S { + type Target = S2; + fn deref(&self) -> &S2 { unimplemented!() } + } + } +} + +#[rustc_error] +fn main() { //~ ERROR compilation successful + let s = foo::S::default(); + let _: bool = s.x; + let _: bool = s.f(); +} diff --git a/src/test/compile-fail/privacy/restricted/private-in-public.rs b/src/test/compile-fail/privacy/restricted/private-in-public.rs new file mode 100644 index 0000000000000..f8e7e6283a0af --- /dev/null +++ b/src/test/compile-fail/privacy/restricted/private-in-public.rs @@ -0,0 +1,20 @@ +// Copyright 2016 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. + +#![feature(pub_restricted)] + +mod foo { + struct Priv; + mod bar { + use foo::Priv; + pub(super) fn f(_: Priv) {} + pub(crate) fn f(_: Priv) {} //~ ERROR private + } +} diff --git a/src/test/compile-fail/privacy/restricted/struct-literal-field.rs b/src/test/compile-fail/privacy/restricted/struct-literal-field.rs new file mode 100644 index 0000000000000..e254e005656c1 --- /dev/null +++ b/src/test/compile-fail/privacy/restricted/struct-literal-field.rs @@ -0,0 +1,31 @@ +// Copyright 2016 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. + +#![feature(pub_restricted)] +#![deny(private_in_public)] +#![allow(warnings)] + +mod foo { + pub mod bar { + pub struct S { + pub(foo) x: i32, + } + } + + fn f() { + use foo::bar::S; + S { x: 0 }; // ok + } +} + +fn main() { + use foo::bar::S; + S { x: 0 }; //~ ERROR private +} diff --git a/src/test/compile-fail/privacy/restricted/test.rs b/src/test/compile-fail/privacy/restricted/test.rs new file mode 100644 index 0000000000000..3e1bb7666229c --- /dev/null +++ b/src/test/compile-fail/privacy/restricted/test.rs @@ -0,0 +1,62 @@ +// Copyright 2016 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. + +// aux-build:pub_restricted.rs + +#![feature(pub_restricted)] +#![deny(private_in_public)] +#![allow(warnings)] +extern crate pub_restricted; + +mod foo { + pub mod bar { + pub(super) fn f() {} + #[derive(Default)] + pub struct S { + pub(super) x: i32, + } + impl S { + pub(super) fn f(&self) {} + pub(super) fn g() {} + } + } + fn f() { + use foo::bar::S; + pub(self) use foo::bar::f; // ok + pub(super) use foo::bar::f as g; //~ ERROR cannot be reexported + S::default().x; // ok + S::default().f(); // ok + S::g(); // ok + } +} + +fn f() { + use foo::bar::S; + use foo::bar::f; //~ ERROR private + S::default().x; //~ ERROR private + S::default().f(); //~ ERROR private + S::g(); //~ ERROR private +} + +fn main() { + use pub_restricted::Universe; + use pub_restricted::Crate; //~ ERROR private + + let u = Universe::default(); + let _ = u.x; + let _ = u.y; //~ ERROR private + u.f(); + u.g(); //~ ERROR private +} + +mod pathological { + pub(bad::path) mod m1 {} //~ ERROR failed to resolve module path + pub(foo) mod m2 {} //~ ERROR visibilities can only be restricted to ancestor modules +}