Skip to content

Commit

Permalink
auto merge of #12595 : huonw/rust/pub-vis-typ, r=alexcrichton
Browse files Browse the repository at this point in the history
These are types that are in exported type signatures, but are not
exported themselves, e.g.

    struct Foo { ... }

    pub fn bar() -> Foo { ... }

will warn about the Foo.

Such types are not listed in documentation, and cannot be named outside
the crate in which they are declared, which is very user-unfriendly.

cc #10573.
  • Loading branch information
bors committed Feb 28, 2014
2 parents b99a8ff + 218eae0 commit 2e51e8d
Show file tree
Hide file tree
Showing 20 changed files with 428 additions and 21 deletions.
1 change: 1 addition & 0 deletions src/libextra/workcache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
// except according to those terms.

#[allow(missing_doc)];
#[allow(visible_private_types)];

use serialize::json;
use serialize::json::ToJson;
Expand Down
1 change: 1 addition & 0 deletions src/libgreen/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@

// NB this does *not* include globs, please keep it that way.
#[feature(macro_rules)];
#[allow(visible_private_types)];

use std::mem::replace;
use std::os;
Expand Down
1 change: 1 addition & 0 deletions src/libnative/io/timer_other.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ struct Inner {
id: uint,
}

#[allow(visible_private_types)]
pub enum Req {
// Add a new timer to the helper thread.
NewTimer(~Inner),
Expand Down
1 change: 1 addition & 0 deletions src/libnative/io/timer_timerfd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ pub struct Timer {
priv on_worker: bool,
}

#[allow(visible_private_types)]
pub enum Req {
NewTimer(libc::c_int, Chan<()>, bool, imp::itimerspec),
RemoveTimer(libc::c_int, Chan<()>),
Expand Down
2 changes: 2 additions & 0 deletions src/librustc/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ This API is completely unstable and subject to change.
#[feature(macro_rules, globs, struct_variant, managed_boxes)];
#[feature(quote)];

#[allow(visible_private_types)];

extern crate extra;
extern crate flate;
extern crate arena;
Expand Down
7 changes: 7 additions & 0 deletions src/librustc/middle/lint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ pub enum Lint {
UnusedMut,
UnnecessaryAllocation,
DeadCode,
VisiblePrivateTypes,
UnnecessaryTypecast,

MissingDoc,
Expand Down Expand Up @@ -312,6 +313,12 @@ static lint_table: &'static [(&'static str, LintSpec)] = &[
desc: "detect piece of code that will never be used",
default: warn
}),
("visible_private_types",
LintSpec {
lint: VisiblePrivateTypes,
desc: "detect use of private types in exported type signatures",
default: warn
}),

("missing_doc",
LintSpec {
Expand Down
255 changes: 255 additions & 0 deletions src/librustc/middle/privacy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use std::mem::replace;
use collections::{HashSet, HashMap};

use metadata::csearch;
use middle::lint;
use middle::resolve;
use middle::ty;
use middle::typeck::{MethodMap, MethodOrigin, MethodParam};
Expand Down Expand Up @@ -1169,6 +1170,251 @@ impl SanePrivacyVisitor {
}
}

struct VisiblePrivateTypesVisitor<'a> {
tcx: ty::ctxt,
exported_items: &'a ExportedItems,
public_items: &'a PublicItems,
}

struct CheckTypeForPrivatenessVisitor<'a, 'b> {
inner: &'b VisiblePrivateTypesVisitor<'a>,
/// whether the type refers to private types.
contains_private: bool,
/// whether we've recurred at all (i.e. if we're pointing at the
/// first type on which visit_ty was called).
at_outer_type: bool,
// whether that first type is a public path.
outer_type_is_public_path: bool,
}

impl<'a> VisiblePrivateTypesVisitor<'a> {
fn path_is_private_type(&self, path_id: ast::NodeId) -> bool {
let did = match self.tcx.def_map.borrow().get().find_copy(&path_id) {
// `int` etc. (None doesn't seem to occur.)
None | Some(ast::DefPrimTy(..)) => return false,
Some(def) => def_id_of_def(def)
};
// A path can only be private if:
// it's in this crate...
is_local(did) &&
// ... it's not exported (obviously) ...
!self.exported_items.contains(&did.node) &&
// .. and it corresponds to a type in the AST (this returns None for
// type parameters)
self.tcx.map.find(did.node).is_some()
}

fn trait_is_public(&self, trait_id: ast::NodeId) -> bool {
// FIXME: this would preferably be using `exported_items`, but all
// traits are exported currently (see `EmbargoVisitor.exported_trait`)
self.public_items.contains(&trait_id)
}
}

impl<'a, 'b> Visitor<()> for CheckTypeForPrivatenessVisitor<'a, 'b> {
fn visit_ty(&mut self, ty: &ast::Ty, _: ()) {
match ty.node {
ast::TyPath(_, _, path_id) => {
if self.inner.path_is_private_type(path_id) {
self.contains_private = true;
// found what we're looking for so let's stop
// working.
return
} else if self.at_outer_type {
self.outer_type_is_public_path = true;
}
}
_ => {}
}
self.at_outer_type = false;
visit::walk_ty(self, ty, ())
}

// don't want to recurse into [, .. expr]
fn visit_expr(&mut self, _: &ast::Expr, _: ()) {}
}

impl<'a> Visitor<()> for VisiblePrivateTypesVisitor<'a> {
fn visit_item(&mut self, item: &ast::Item, _: ()) {
match item.node {
// contents of a private mod can be reexported, so we need
// to check internals.
ast::ItemMod(_) => {}

// An `extern {}` doesn't introduce a new privacy
// namespace (the contents have their own privacies).
ast::ItemForeignMod(_) => {}

ast::ItemTrait(..) if !self.trait_is_public(item.id) => return,

// impls need some special handling to try to offer useful
// error messages without (too many) false positives
// (i.e. we could just return here to not check them at
// all, or some worse estimation of whether an impl is
// publically visible.
ast::ItemImpl(ref g, ref trait_ref, self_, ref methods) => {
// `impl [... for] Private` is never visible.
let self_contains_private;
// impl [... for] Public<...>, but not `impl [... for]
// ~[Public]` or `(Public,)` etc.
let self_is_public_path;

// check the properties of the Self type:
{
let mut visitor = CheckTypeForPrivatenessVisitor {
inner: self,
contains_private: false,
at_outer_type: true,
outer_type_is_public_path: false,
};
visitor.visit_ty(self_, ());
self_contains_private = visitor.contains_private;
self_is_public_path = visitor.outer_type_is_public_path;
}

// miscellanous info about the impl

// `true` iff this is `impl Private for ...`.
let not_private_trait =
trait_ref.as_ref().map_or(true, // no trait counts as public trait
|tr| {
let did = ty::trait_ref_to_def_id(self.tcx, tr);

!is_local(did) || self.trait_is_public(did.node)
});

// `true` iff this is a trait impl or at least one method is public.
//
// `impl Public { $( fn ...() {} )* }` is not visible.
//
// This is required over just using the methods' privacy
// directly because we might have `impl<T: Foo<Private>> ...`,
// and we shouldn't warn about the generics if all the methods
// are private (because `T` won't be visible externally).
let trait_or_some_public_method =
trait_ref.is_some() ||
methods.iter().any(|m| self.exported_items.contains(&m.id));

if !self_contains_private &&
not_private_trait &&
trait_or_some_public_method {

visit::walk_generics(self, g, ());

match *trait_ref {
None => {
for method in methods.iter() {
visit::walk_method_helper(self, *method, ())
}
}
Some(ref tr) => {
// Any private types in a trait impl fall into two
// categories.
// 1. mentioned in the trait definition
// 2. mentioned in the type params/generics
//
// Those in 1. can only occur if the trait is in
// this crate and will've been warned about on the
// trait definition (there's no need to warn twice
// so we don't check the methods).
//
// Those in 2. are warned via walk_generics and this
// call here.
visit::walk_trait_ref_helper(self, tr, ())
}
}
} else if trait_ref.is_none() && self_is_public_path {
// impl Public<Private> { ... }. Any public static
// methods will be visible as `Public::foo`.
let mut found_pub_static = false;
for method in methods.iter() {
if method.explicit_self.node == ast::SelfStatic &&
self.exported_items.contains(&method.id) {
found_pub_static = true;
visit::walk_method_helper(self, *method, ());
}
}
if found_pub_static {
visit::walk_generics(self, g, ())
}
}
return
}

// `type ... = ...;` can contain private types, because
// we're introducing a new name.
ast::ItemTy(..) => return,

// not at all public, so we don't care
_ if !self.exported_items.contains(&item.id) => return,

_ => {}
}

// we've carefully constructed it so that if we're here, then
// any `visit_ty`'s will be called on things that are in
// public signatures, i.e. things that we're interested in for
// this visitor.
visit::walk_item(self, item, ());
}

fn visit_foreign_item(&mut self, item: &ast::ForeignItem, _: ()) {
if self.exported_items.contains(&item.id) {
visit::walk_foreign_item(self, item, ())
}
}

fn visit_fn(&mut self,
fk: &visit::FnKind, fd: &ast::FnDecl, b: &ast::Block, s: Span, id: ast::NodeId,
_: ()) {
// needs special handling for methods.
if self.exported_items.contains(&id) {
visit::walk_fn(self, fk, fd, b, s, id, ());
}
}

fn visit_ty(&mut self, t: &ast::Ty, _: ()) {
match t.node {
ast::TyPath(ref p, _, path_id) => {
if self.path_is_private_type(path_id) {
self.tcx.sess.add_lint(lint::VisiblePrivateTypes,
path_id, p.span,
~"private type in exported type signature");
}
}
_ => {}
}
visit::walk_ty(self, t, ())
}

fn visit_variant(&mut self, v: &ast::Variant, g: &ast::Generics, _: ()) {
if self.exported_items.contains(&v.node.id) {
visit::walk_variant(self, v, g, ());
}
}

fn visit_struct_field(&mut self, s: &ast::StructField, _: ()) {
match s.node.kind {
// the only way to get here is by being inside a public
// struct/enum variant, so the only way to have a private
// field is with an explicit `priv`.
ast::NamedField(_, ast::Private) => {}

_ => visit::walk_struct_field(self, s, ())
}
}


// we don't need to introspect into these at all: an
// expression/block context can't possibly contain exported
// things, and neither do view_items. (Making them no-ops stops us
// from traversing the whole AST without having to be super
// careful about our `walk_...` calls above.)
fn visit_view_item(&mut self, _: &ast::ViewItem, _: ()) {}
fn visit_block(&mut self, _: &ast::Block, _: ()) {}
fn visit_expr(&mut self, _: &ast::Expr, _: ()) {}
}

pub fn check_crate(tcx: ty::ctxt,
method_map: &MethodMap,
exp_map2: &resolve::ExportMap2,
Expand Down Expand Up @@ -1225,5 +1471,14 @@ pub fn check_crate(tcx: ty::ctxt,
}

let EmbargoVisitor { exported_items, public_items, .. } = visitor;

{
let mut visitor = VisiblePrivateTypesVisitor {
tcx: tcx,
exported_items: &exported_items,
public_items: &public_items
};
visit::walk_crate(&mut visitor, krate, ());
}
return (exported_items, public_items);
}
2 changes: 1 addition & 1 deletion src/librustdoc/html/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ pub enum ExternalLocation {
}

/// Different ways an implementor of a trait can be rendered.
enum Implementor {
pub enum Implementor {
/// Paths are displayed specially by omitting the `impl XX for` cruft
PathType(clean::Type),
/// This is the generic representation of a trait implementor, used for
Expand Down
1 change: 1 addition & 0 deletions src/librustuv/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ via `close` and `delete` methods.

#[feature(macro_rules)];
#[deny(unused_result, unused_must_use)];
#[allow(visible_private_types)];

#[cfg(test)] extern crate green;

Expand Down
2 changes: 1 addition & 1 deletion src/libstd/libc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1130,7 +1130,7 @@ pub mod types {
Data4: [BYTE, ..8],
}

struct WSAPROTOCOLCHAIN {
pub struct WSAPROTOCOLCHAIN {
ChainLen: c_int,
ChainEntries: [DWORD, ..MAX_PROTOCOL_CHAIN],
}
Expand Down
2 changes: 1 addition & 1 deletion src/libstd/rt/local.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ pub trait Local<Borrowed> {
unsafe fn try_unsafe_borrow() -> Option<*mut Self>;
}

#[allow(visible_private_types)]
impl Local<local_ptr::Borrowed<Task>> for Task {
#[inline]
fn put(value: ~Task) { unsafe { local_ptr::put(value) } }
Expand Down Expand Up @@ -127,4 +128,3 @@ mod test {
}

}

1 change: 1 addition & 0 deletions src/libstd/rt/local_ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,7 @@ pub mod native {

#[inline]
#[cfg(not(test))]
#[allow(visible_private_types)]
pub fn maybe_tls_key() -> Option<tls::Key> {
unsafe {
// NB: This is a little racy because, while the key is
Expand Down
2 changes: 2 additions & 0 deletions src/libstd/rt/unwind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@ fn rust_exception_class() -> uw::_Unwind_Exception_Class {

#[cfg(not(target_arch = "arm"), not(test))]
#[doc(hidden)]
#[allow(visible_private_types)]
pub mod eabi {
use uw = super::libunwind;
use libc::c_int;
Expand Down Expand Up @@ -333,6 +334,7 @@ pub mod eabi {
// ARM EHABI uses a slightly different personality routine signature,
// but otherwise works the same.
#[cfg(target_arch = "arm", not(test))]
#[allow(visible_private_types)]
pub mod eabi {
use uw = super::libunwind;
use libc::c_int;
Expand Down
Loading

0 comments on commit 2e51e8d

Please sign in to comment.