Improve PrivateItemsInPublicInterfacesVisitor

This commit is contained in:
Jeffrey Seyfried 2016-04-01 20:16:31 +00:00
parent 0d34c5dd8a
commit 35204025be
2 changed files with 109 additions and 85 deletions

View File

@ -302,7 +302,7 @@ impl Visibility {
Visibility::Restricted(module) => module, Visibility::Restricted(module) => module,
}; };
let mut block_ancestor = map.get_module_parent(block); let mut block_ancestor = block;
loop { loop {
if block_ancestor == restriction { return true } if block_ancestor == restriction { return true }
let block_ancestor_parent = map.get_module_parent(block_ancestor); let block_ancestor_parent = map.get_module_parent(block_ancestor);
@ -310,6 +310,17 @@ impl Visibility {
block_ancestor = block_ancestor_parent; block_ancestor = block_ancestor_parent;
} }
} }
/// Returns true if this visibility is at least as accessible as the given visibility
pub fn is_at_least(self, vis: Visibility, map: &ast_map::Map) -> bool {
let vis_restriction = match vis {
Visibility::Public => return self == Visibility::Public,
Visibility::PrivateExternal => return true,
Visibility::Restricted(module) => module,
};
self.is_accessible_from(vis_restriction, map)
}
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]

View File

@ -936,27 +936,41 @@ impl<'a, 'tcx, 'v> Visitor<'v> for ObsoleteVisiblePrivateTypesVisitor<'a, 'tcx>
struct SearchInterfaceForPrivateItemsVisitor<'a, 'tcx: 'a> { struct SearchInterfaceForPrivateItemsVisitor<'a, 'tcx: 'a> {
tcx: &'a TyCtxt<'tcx>, tcx: &'a TyCtxt<'tcx>,
// Do not report an error when a private type is found /// The visitor checks that each component type is at least this visible
is_quiet: bool, required_visibility: ty::Visibility,
// Is private component found? /// The visibility of the least visible component that has been visited
is_public: bool, min_visibility: ty::Visibility,
old_error_set: &'a NodeSet, old_error_set: &'a NodeSet,
} }
impl<'a, 'tcx: 'a> SearchInterfaceForPrivateItemsVisitor<'a, 'tcx> { impl<'a, 'tcx: 'a> SearchInterfaceForPrivateItemsVisitor<'a, 'tcx> {
// Check if the type alias contain private types when substituted fn new(tcx: &'a TyCtxt<'tcx>, old_error_set: &'a NodeSet) -> Self {
fn is_public_type_alias(&self, item: &hir::Item, path: &hir::Path) -> bool { SearchInterfaceForPrivateItemsVisitor {
tcx: tcx,
min_visibility: ty::Visibility::Public,
required_visibility: ty::Visibility::PrivateExternal,
old_error_set: old_error_set,
}
}
}
impl<'a, 'tcx: 'a> SearchInterfaceForPrivateItemsVisitor<'a, 'tcx> {
// Return the visibility of the type alias's least visible component type when substituted
fn substituted_alias_visibility(&self, item: &hir::Item, path: &hir::Path)
-> Option<ty::Visibility> {
// We substitute type aliases only when determining impl publicity // We substitute type aliases only when determining impl publicity
// FIXME: This will probably change and all type aliases will be substituted, // FIXME: This will probably change and all type aliases will be substituted,
// requires an amendment to RFC 136. // requires an amendment to RFC 136.
if !self.is_quiet { if self.required_visibility != ty::Visibility::PrivateExternal {
return false return None;
} }
// Type alias is considered public if the aliased type is // Type alias is considered public if the aliased type is
// public, even if the type alias itself is private. So, something // public, even if the type alias itself is private. So, something
// like `type A = u8; pub fn f() -> A {...}` doesn't cause an error. // like `type A = u8; pub fn f() -> A {...}` doesn't cause an error.
if let hir::ItemTy(ref ty, ref generics) = item.node { if let hir::ItemTy(ref ty, ref generics) = item.node {
let mut check = SearchInterfaceForPrivateItemsVisitor { is_public: true, ..*self }; let mut check = SearchInterfaceForPrivateItemsVisitor {
min_visibility: ty::Visibility::Public, ..*self
};
check.visit_ty(ty); check.visit_ty(ty);
// If a private type alias with default type parameters is used in public // If a private type alias with default type parameters is used in public
// interface we must ensure, that the defaults are public if they are actually used. // interface we must ensure, that the defaults are public if they are actually used.
@ -970,26 +984,23 @@ impl<'a, 'tcx: 'a> SearchInterfaceForPrivateItemsVisitor<'a, 'tcx> {
check.visit_ty(default_ty); check.visit_ty(default_ty);
} }
} }
check.is_public Some(check.min_visibility)
} else { } else {
false None
} }
} }
} }
impl<'a, 'tcx: 'a, 'v> Visitor<'v> for SearchInterfaceForPrivateItemsVisitor<'a, 'tcx> { impl<'a, 'tcx: 'a, 'v> Visitor<'v> for SearchInterfaceForPrivateItemsVisitor<'a, 'tcx> {
fn visit_ty(&mut self, ty: &hir::Ty) { fn visit_ty(&mut self, ty: &hir::Ty) {
if self.is_quiet && !self.is_public {
// We are in quiet mode and a private type is already found, no need to proceed
return
}
if let hir::TyPath(_, ref path) = ty.node { if let hir::TyPath(_, ref path) = ty.node {
let def = self.tcx.def_map.borrow().get(&ty.id).unwrap().full_def(); let def = self.tcx.def_map.borrow().get(&ty.id).unwrap().full_def();
match def { match def {
Def::PrimTy(..) | Def::SelfTy(..) | Def::TyParam(..) => { Def::PrimTy(..) | Def::SelfTy(..) | Def::TyParam(..) => {
// Public // Public
} }
Def::AssociatedTy(..) if self.is_quiet => { Def::AssociatedTy(..)
if self.required_visibility == ty::Visibility::PrivateExternal => {
// Conservatively approximate the whole type alias as public without // Conservatively approximate the whole type alias as public without
// recursing into its components when determining impl publicity. // recursing into its components when determining impl publicity.
// For example, `impl <Type as Trait>::Alias {...}` may be a public impl // For example, `impl <Type as Trait>::Alias {...}` may be a public impl
@ -1003,21 +1014,24 @@ impl<'a, 'tcx: 'a, 'v> Visitor<'v> for SearchInterfaceForPrivateItemsVisitor<'a,
// Non-local means public (private items can't leave their crate, modulo bugs) // Non-local means public (private items can't leave their crate, modulo bugs)
if let Some(node_id) = self.tcx.map.as_local_node_id(def_id) { if let Some(node_id) = self.tcx.map.as_local_node_id(def_id) {
let item = self.tcx.map.expect_item(node_id); let item = self.tcx.map.expect_item(node_id);
if item.vis != hir::Public && !self.is_public_type_alias(item, path) { let vis = match self.substituted_alias_visibility(item, path) {
if !self.is_quiet { Some(vis) => vis,
if self.old_error_set.contains(&ty.id) { None => ty::Visibility::from_hir(&item.vis, node_id, &self.tcx),
span_err!(self.tcx.sess, ty.span, E0446, };
"private type in public interface");
} else { if !vis.is_at_least(self.min_visibility, &self.tcx.map) {
self.tcx.sess.add_lint ( self.min_visibility = vis;
lint::builtin::PRIVATE_IN_PUBLIC, }
node_id, if !vis.is_at_least(self.required_visibility, &self.tcx.map) {
ty.span, if self.old_error_set.contains(&ty.id) {
format!("private type in public interface"), span_err!(self.tcx.sess, ty.span, E0446,
); "private type in public interface");
} } else {
self.tcx.sess.add_lint(lint::builtin::PRIVATE_IN_PUBLIC,
node_id,
ty.span,
format!("private type in public interface"));
} }
self.is_public = false;
} }
} }
} }
@ -1029,28 +1043,26 @@ impl<'a, 'tcx: 'a, 'v> Visitor<'v> for SearchInterfaceForPrivateItemsVisitor<'a,
} }
fn visit_trait_ref(&mut self, trait_ref: &hir::TraitRef) { fn visit_trait_ref(&mut self, trait_ref: &hir::TraitRef) {
if self.is_quiet && !self.is_public {
// We are in quiet mode and a private type is already found, no need to proceed
return
}
// Non-local means public (private items can't leave their crate, modulo bugs) // Non-local means public (private items can't leave their crate, modulo bugs)
let def_id = self.tcx.trait_ref_to_def_id(trait_ref); let def_id = self.tcx.trait_ref_to_def_id(trait_ref);
if let Some(node_id) = self.tcx.map.as_local_node_id(def_id) { if let Some(node_id) = self.tcx.map.as_local_node_id(def_id) {
let item = self.tcx.map.expect_item(node_id); let item = self.tcx.map.expect_item(node_id);
if item.vis != hir::Public { let vis = ty::Visibility::from_hir(&item.vis, node_id, &self.tcx);
if !self.is_quiet {
if self.old_error_set.contains(&trait_ref.ref_id) { if !vis.is_at_least(self.min_visibility, &self.tcx.map) {
span_err!(self.tcx.sess, trait_ref.path.span, E0445, self.min_visibility = vis;
"private trait in public interface"); }
} else { if !vis.is_at_least(self.required_visibility, &self.tcx.map) {
self.tcx.sess.add_lint(lint::builtin::PRIVATE_IN_PUBLIC, if self.old_error_set.contains(&trait_ref.ref_id) {
node_id, span_err!(self.tcx.sess, trait_ref.path.span, E0445,
trait_ref.path.span, "private trait in public interface");
"private trait in public interface (error E0445)" } else {
.to_string()); self.tcx.sess.add_lint(lint::builtin::PRIVATE_IN_PUBLIC,
} node_id,
trait_ref.path.span,
"private trait in public interface (error E0445)"
.to_string());
} }
self.is_public = false;
} }
} }
@ -1072,29 +1084,29 @@ struct PrivateItemsInPublicInterfacesVisitor<'a, 'tcx: 'a> {
impl<'a, 'tcx> PrivateItemsInPublicInterfacesVisitor<'a, 'tcx> { impl<'a, 'tcx> PrivateItemsInPublicInterfacesVisitor<'a, 'tcx> {
// A type is considered public if it doesn't contain any private components // A type is considered public if it doesn't contain any private components
fn is_public_ty(&self, ty: &hir::Ty) -> bool { fn ty_visibility(&self, ty: &hir::Ty) -> ty::Visibility {
let mut check = SearchInterfaceForPrivateItemsVisitor { let mut check = SearchInterfaceForPrivateItemsVisitor::new(self.tcx, self.old_error_set);
tcx: self.tcx, is_quiet: true, is_public: true, old_error_set: self.old_error_set
};
check.visit_ty(ty); check.visit_ty(ty);
check.is_public check.min_visibility
} }
// A trait reference is considered public if it doesn't contain any private components // A trait reference is considered public if it doesn't contain any private components
fn is_public_trait_ref(&self, trait_ref: &hir::TraitRef) -> bool { fn trait_ref_visibility(&self, trait_ref: &hir::TraitRef) -> ty::Visibility {
let mut check = SearchInterfaceForPrivateItemsVisitor { let mut check = SearchInterfaceForPrivateItemsVisitor::new(self.tcx, self.old_error_set);
tcx: self.tcx, is_quiet: true, is_public: true, old_error_set: self.old_error_set
};
check.visit_trait_ref(trait_ref); check.visit_trait_ref(trait_ref);
check.is_public check.min_visibility
} }
} }
impl<'a, 'tcx, 'v> Visitor<'v> for PrivateItemsInPublicInterfacesVisitor<'a, 'tcx> { impl<'a, 'tcx, 'v> Visitor<'v> for PrivateItemsInPublicInterfacesVisitor<'a, 'tcx> {
fn visit_item(&mut self, item: &hir::Item) { fn visit_item(&mut self, item: &hir::Item) {
let mut check = SearchInterfaceForPrivateItemsVisitor { let min = |vis1: ty::Visibility, vis2| {
tcx: self.tcx, is_quiet: false, is_public: true, old_error_set: self.old_error_set if vis1.is_at_least(vis2, &self.tcx.map) { vis2 } else { vis1 }
}; };
let mut check = SearchInterfaceForPrivateItemsVisitor::new(self.tcx, self.old_error_set);
let item_visibility = ty::Visibility::from_hir(&item.vis, item.id, &self.tcx);
match item.node { match item.node {
// Crates are always public // Crates are always public
hir::ItemExternCrate(..) => {} hir::ItemExternCrate(..) => {}
@ -1105,27 +1117,26 @@ impl<'a, 'tcx, 'v> Visitor<'v> for PrivateItemsInPublicInterfacesVisitor<'a, 'tc
// Subitems of these items have inherited publicity // Subitems of these items have inherited publicity
hir::ItemConst(..) | hir::ItemStatic(..) | hir::ItemFn(..) | hir::ItemConst(..) | hir::ItemStatic(..) | hir::ItemFn(..) |
hir::ItemEnum(..) | hir::ItemTrait(..) | hir::ItemTy(..) => { hir::ItemEnum(..) | hir::ItemTrait(..) | hir::ItemTy(..) => {
if item.vis == hir::Public { check.required_visibility = item_visibility;
check.visit_item(item); check.visit_item(item);
}
} }
// Subitems of foreign modules have their own publicity // Subitems of foreign modules have their own publicity
hir::ItemForeignMod(ref foreign_mod) => { hir::ItemForeignMod(ref foreign_mod) => {
for foreign_item in &foreign_mod.items { for foreign_item in &foreign_mod.items {
if foreign_item.vis == hir::Public { check.required_visibility =
check.visit_foreign_item(foreign_item); ty::Visibility::from_hir(&foreign_item.vis, item.id, &self.tcx);
} check.visit_foreign_item(foreign_item);
} }
} }
// Subitems of structs have their own publicity // Subitems of structs have their own publicity
hir::ItemStruct(ref struct_def, ref generics) => { hir::ItemStruct(ref struct_def, ref generics) => {
if item.vis == hir::Public { check.required_visibility = item_visibility;
check.visit_generics(generics); check.visit_generics(generics);
for field in struct_def.fields() {
if field.vis == hir::Public { for field in struct_def.fields() {
check.visit_struct_field(field); let field_visibility = ty::Visibility::from_hir(&field.vis, item.id, &self.tcx);
} check.required_visibility = min(item_visibility, field_visibility);
} check.visit_struct_field(field);
} }
} }
// The interface is empty // The interface is empty
@ -1133,23 +1144,25 @@ impl<'a, 'tcx, 'v> Visitor<'v> for PrivateItemsInPublicInterfacesVisitor<'a, 'tc
// An inherent impl is public when its type is public // An inherent impl is public when its type is public
// Subitems of inherent impls have their own publicity // Subitems of inherent impls have their own publicity
hir::ItemImpl(_, _, ref generics, None, ref ty, ref impl_items) => { hir::ItemImpl(_, _, ref generics, None, ref ty, ref impl_items) => {
if self.is_public_ty(ty) { let ty_vis = self.ty_visibility(ty);
check.visit_generics(generics); check.required_visibility = ty_vis;
for impl_item in impl_items { check.visit_generics(generics);
if impl_item.vis == hir::Public {
check.visit_impl_item(impl_item); for impl_item in impl_items {
} let impl_item_vis =
} ty::Visibility::from_hir(&impl_item.vis, item.id, &self.tcx);
check.required_visibility = min(impl_item_vis, ty_vis);
check.visit_impl_item(impl_item);
} }
} }
// A trait impl is public when both its type and its trait are public // A trait impl is public when both its type and its trait are public
// Subitems of trait impls have inherited publicity // Subitems of trait impls have inherited publicity
hir::ItemImpl(_, _, ref generics, Some(ref trait_ref), ref ty, ref impl_items) => { hir::ItemImpl(_, _, ref generics, Some(ref trait_ref), ref ty, ref impl_items) => {
if self.is_public_ty(ty) && self.is_public_trait_ref(trait_ref) { let vis = min(self.ty_visibility(ty), self.trait_ref_visibility(trait_ref));
check.visit_generics(generics); check.required_visibility = vis;
for impl_item in impl_items { check.visit_generics(generics);
check.visit_impl_item(impl_item); for impl_item in impl_items {
} check.visit_impl_item(impl_item);
} }
} }
} }