Skip to content

Commit 2e51e8d

Browse files
committed
auto merge of #12595 : huonw/rust/pub-vis-typ, r=alexcrichton
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.
2 parents b99a8ff + 218eae0 commit 2e51e8d

File tree

20 files changed

+428
-21
lines changed

20 files changed

+428
-21
lines changed

src/libextra/workcache.rs

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
// except according to those terms.
1010

1111
#[allow(missing_doc)];
12+
#[allow(visible_private_types)];
1213

1314
use serialize::json;
1415
use serialize::json::ToJson;

src/libgreen/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@
173173

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

177178
use std::mem::replace;
178179
use std::os;

src/libnative/io/timer_other.rs

+1
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ struct Inner {
7171
id: uint,
7272
}
7373

74+
#[allow(visible_private_types)]
7475
pub enum Req {
7576
// Add a new timer to the helper thread.
7677
NewTimer(~Inner),

src/libnative/io/timer_timerfd.rs

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ pub struct Timer {
4444
priv on_worker: bool,
4545
}
4646

47+
#[allow(visible_private_types)]
4748
pub enum Req {
4849
NewTimer(libc::c_int, Chan<()>, bool, imp::itimerspec),
4950
RemoveTimer(libc::c_int, Chan<()>),

src/librustc/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ This API is completely unstable and subject to change.
3030
#[feature(macro_rules, globs, struct_variant, managed_boxes)];
3131
#[feature(quote)];
3232

33+
#[allow(visible_private_types)];
34+
3335
extern crate extra;
3436
extern crate flate;
3537
extern crate arena;

src/librustc/middle/lint.rs

+7
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ pub enum Lint {
9898
UnusedMut,
9999
UnnecessaryAllocation,
100100
DeadCode,
101+
VisiblePrivateTypes,
101102
UnnecessaryTypecast,
102103

103104
MissingDoc,
@@ -312,6 +313,12 @@ static lint_table: &'static [(&'static str, LintSpec)] = &[
312313
desc: "detect piece of code that will never be used",
313314
default: warn
314315
}),
316+
("visible_private_types",
317+
LintSpec {
318+
lint: VisiblePrivateTypes,
319+
desc: "detect use of private types in exported type signatures",
320+
default: warn
321+
}),
315322

316323
("missing_doc",
317324
LintSpec {

src/librustc/middle/privacy.rs

+255
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use std::mem::replace;
1616
use collections::{HashSet, HashMap};
1717

1818
use metadata::csearch;
19+
use middle::lint;
1920
use middle::resolve;
2021
use middle::ty;
2122
use middle::typeck::{MethodMap, MethodOrigin, MethodParam};
@@ -1169,6 +1170,251 @@ impl SanePrivacyVisitor {
11691170
}
11701171
}
11711172

1173+
struct VisiblePrivateTypesVisitor<'a> {
1174+
tcx: ty::ctxt,
1175+
exported_items: &'a ExportedItems,
1176+
public_items: &'a PublicItems,
1177+
}
1178+
1179+
struct CheckTypeForPrivatenessVisitor<'a, 'b> {
1180+
inner: &'b VisiblePrivateTypesVisitor<'a>,
1181+
/// whether the type refers to private types.
1182+
contains_private: bool,
1183+
/// whether we've recurred at all (i.e. if we're pointing at the
1184+
/// first type on which visit_ty was called).
1185+
at_outer_type: bool,
1186+
// whether that first type is a public path.
1187+
outer_type_is_public_path: bool,
1188+
}
1189+
1190+
impl<'a> VisiblePrivateTypesVisitor<'a> {
1191+
fn path_is_private_type(&self, path_id: ast::NodeId) -> bool {
1192+
let did = match self.tcx.def_map.borrow().get().find_copy(&path_id) {
1193+
// `int` etc. (None doesn't seem to occur.)
1194+
None | Some(ast::DefPrimTy(..)) => return false,
1195+
Some(def) => def_id_of_def(def)
1196+
};
1197+
// A path can only be private if:
1198+
// it's in this crate...
1199+
is_local(did) &&
1200+
// ... it's not exported (obviously) ...
1201+
!self.exported_items.contains(&did.node) &&
1202+
// .. and it corresponds to a type in the AST (this returns None for
1203+
// type parameters)
1204+
self.tcx.map.find(did.node).is_some()
1205+
}
1206+
1207+
fn trait_is_public(&self, trait_id: ast::NodeId) -> bool {
1208+
// FIXME: this would preferably be using `exported_items`, but all
1209+
// traits are exported currently (see `EmbargoVisitor.exported_trait`)
1210+
self.public_items.contains(&trait_id)
1211+
}
1212+
}
1213+
1214+
impl<'a, 'b> Visitor<()> for CheckTypeForPrivatenessVisitor<'a, 'b> {
1215+
fn visit_ty(&mut self, ty: &ast::Ty, _: ()) {
1216+
match ty.node {
1217+
ast::TyPath(_, _, path_id) => {
1218+
if self.inner.path_is_private_type(path_id) {
1219+
self.contains_private = true;
1220+
// found what we're looking for so let's stop
1221+
// working.
1222+
return
1223+
} else if self.at_outer_type {
1224+
self.outer_type_is_public_path = true;
1225+
}
1226+
}
1227+
_ => {}
1228+
}
1229+
self.at_outer_type = false;
1230+
visit::walk_ty(self, ty, ())
1231+
}
1232+
1233+
// don't want to recurse into [, .. expr]
1234+
fn visit_expr(&mut self, _: &ast::Expr, _: ()) {}
1235+
}
1236+
1237+
impl<'a> Visitor<()> for VisiblePrivateTypesVisitor<'a> {
1238+
fn visit_item(&mut self, item: &ast::Item, _: ()) {
1239+
match item.node {
1240+
// contents of a private mod can be reexported, so we need
1241+
// to check internals.
1242+
ast::ItemMod(_) => {}
1243+
1244+
// An `extern {}` doesn't introduce a new privacy
1245+
// namespace (the contents have their own privacies).
1246+
ast::ItemForeignMod(_) => {}
1247+
1248+
ast::ItemTrait(..) if !self.trait_is_public(item.id) => return,
1249+
1250+
// impls need some special handling to try to offer useful
1251+
// error messages without (too many) false positives
1252+
// (i.e. we could just return here to not check them at
1253+
// all, or some worse estimation of whether an impl is
1254+
// publically visible.
1255+
ast::ItemImpl(ref g, ref trait_ref, self_, ref methods) => {
1256+
// `impl [... for] Private` is never visible.
1257+
let self_contains_private;
1258+
// impl [... for] Public<...>, but not `impl [... for]
1259+
// ~[Public]` or `(Public,)` etc.
1260+
let self_is_public_path;
1261+
1262+
// check the properties of the Self type:
1263+
{
1264+
let mut visitor = CheckTypeForPrivatenessVisitor {
1265+
inner: self,
1266+
contains_private: false,
1267+
at_outer_type: true,
1268+
outer_type_is_public_path: false,
1269+
};
1270+
visitor.visit_ty(self_, ());
1271+
self_contains_private = visitor.contains_private;
1272+
self_is_public_path = visitor.outer_type_is_public_path;
1273+
}
1274+
1275+
// miscellanous info about the impl
1276+
1277+
// `true` iff this is `impl Private for ...`.
1278+
let not_private_trait =
1279+
trait_ref.as_ref().map_or(true, // no trait counts as public trait
1280+
|tr| {
1281+
let did = ty::trait_ref_to_def_id(self.tcx, tr);
1282+
1283+
!is_local(did) || self.trait_is_public(did.node)
1284+
});
1285+
1286+
// `true` iff this is a trait impl or at least one method is public.
1287+
//
1288+
// `impl Public { $( fn ...() {} )* }` is not visible.
1289+
//
1290+
// This is required over just using the methods' privacy
1291+
// directly because we might have `impl<T: Foo<Private>> ...`,
1292+
// and we shouldn't warn about the generics if all the methods
1293+
// are private (because `T` won't be visible externally).
1294+
let trait_or_some_public_method =
1295+
trait_ref.is_some() ||
1296+
methods.iter().any(|m| self.exported_items.contains(&m.id));
1297+
1298+
if !self_contains_private &&
1299+
not_private_trait &&
1300+
trait_or_some_public_method {
1301+
1302+
visit::walk_generics(self, g, ());
1303+
1304+
match *trait_ref {
1305+
None => {
1306+
for method in methods.iter() {
1307+
visit::walk_method_helper(self, *method, ())
1308+
}
1309+
}
1310+
Some(ref tr) => {
1311+
// Any private types in a trait impl fall into two
1312+
// categories.
1313+
// 1. mentioned in the trait definition
1314+
// 2. mentioned in the type params/generics
1315+
//
1316+
// Those in 1. can only occur if the trait is in
1317+
// this crate and will've been warned about on the
1318+
// trait definition (there's no need to warn twice
1319+
// so we don't check the methods).
1320+
//
1321+
// Those in 2. are warned via walk_generics and this
1322+
// call here.
1323+
visit::walk_trait_ref_helper(self, tr, ())
1324+
}
1325+
}
1326+
} else if trait_ref.is_none() && self_is_public_path {
1327+
// impl Public<Private> { ... }. Any public static
1328+
// methods will be visible as `Public::foo`.
1329+
let mut found_pub_static = false;
1330+
for method in methods.iter() {
1331+
if method.explicit_self.node == ast::SelfStatic &&
1332+
self.exported_items.contains(&method.id) {
1333+
found_pub_static = true;
1334+
visit::walk_method_helper(self, *method, ());
1335+
}
1336+
}
1337+
if found_pub_static {
1338+
visit::walk_generics(self, g, ())
1339+
}
1340+
}
1341+
return
1342+
}
1343+
1344+
// `type ... = ...;` can contain private types, because
1345+
// we're introducing a new name.
1346+
ast::ItemTy(..) => return,
1347+
1348+
// not at all public, so we don't care
1349+
_ if !self.exported_items.contains(&item.id) => return,
1350+
1351+
_ => {}
1352+
}
1353+
1354+
// we've carefully constructed it so that if we're here, then
1355+
// any `visit_ty`'s will be called on things that are in
1356+
// public signatures, i.e. things that we're interested in for
1357+
// this visitor.
1358+
visit::walk_item(self, item, ());
1359+
}
1360+
1361+
fn visit_foreign_item(&mut self, item: &ast::ForeignItem, _: ()) {
1362+
if self.exported_items.contains(&item.id) {
1363+
visit::walk_foreign_item(self, item, ())
1364+
}
1365+
}
1366+
1367+
fn visit_fn(&mut self,
1368+
fk: &visit::FnKind, fd: &ast::FnDecl, b: &ast::Block, s: Span, id: ast::NodeId,
1369+
_: ()) {
1370+
// needs special handling for methods.
1371+
if self.exported_items.contains(&id) {
1372+
visit::walk_fn(self, fk, fd, b, s, id, ());
1373+
}
1374+
}
1375+
1376+
fn visit_ty(&mut self, t: &ast::Ty, _: ()) {
1377+
match t.node {
1378+
ast::TyPath(ref p, _, path_id) => {
1379+
if self.path_is_private_type(path_id) {
1380+
self.tcx.sess.add_lint(lint::VisiblePrivateTypes,
1381+
path_id, p.span,
1382+
~"private type in exported type signature");
1383+
}
1384+
}
1385+
_ => {}
1386+
}
1387+
visit::walk_ty(self, t, ())
1388+
}
1389+
1390+
fn visit_variant(&mut self, v: &ast::Variant, g: &ast::Generics, _: ()) {
1391+
if self.exported_items.contains(&v.node.id) {
1392+
visit::walk_variant(self, v, g, ());
1393+
}
1394+
}
1395+
1396+
fn visit_struct_field(&mut self, s: &ast::StructField, _: ()) {
1397+
match s.node.kind {
1398+
// the only way to get here is by being inside a public
1399+
// struct/enum variant, so the only way to have a private
1400+
// field is with an explicit `priv`.
1401+
ast::NamedField(_, ast::Private) => {}
1402+
1403+
_ => visit::walk_struct_field(self, s, ())
1404+
}
1405+
}
1406+
1407+
1408+
// we don't need to introspect into these at all: an
1409+
// expression/block context can't possibly contain exported
1410+
// things, and neither do view_items. (Making them no-ops stops us
1411+
// from traversing the whole AST without having to be super
1412+
// careful about our `walk_...` calls above.)
1413+
fn visit_view_item(&mut self, _: &ast::ViewItem, _: ()) {}
1414+
fn visit_block(&mut self, _: &ast::Block, _: ()) {}
1415+
fn visit_expr(&mut self, _: &ast::Expr, _: ()) {}
1416+
}
1417+
11721418
pub fn check_crate(tcx: ty::ctxt,
11731419
method_map: &MethodMap,
11741420
exp_map2: &resolve::ExportMap2,
@@ -1225,5 +1471,14 @@ pub fn check_crate(tcx: ty::ctxt,
12251471
}
12261472

12271473
let EmbargoVisitor { exported_items, public_items, .. } = visitor;
1474+
1475+
{
1476+
let mut visitor = VisiblePrivateTypesVisitor {
1477+
tcx: tcx,
1478+
exported_items: &exported_items,
1479+
public_items: &public_items
1480+
};
1481+
visit::walk_crate(&mut visitor, krate, ());
1482+
}
12281483
return (exported_items, public_items);
12291484
}

src/librustdoc/html/render.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ pub enum ExternalLocation {
9999
}
100100

101101
/// Different ways an implementor of a trait can be rendered.
102-
enum Implementor {
102+
pub enum Implementor {
103103
/// Paths are displayed specially by omitting the `impl XX for` cruft
104104
PathType(clean::Type),
105105
/// This is the generic representation of a trait implementor, used for

src/librustuv/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ via `close` and `delete` methods.
4141

4242
#[feature(macro_rules)];
4343
#[deny(unused_result, unused_must_use)];
44+
#[allow(visible_private_types)];
4445

4546
#[cfg(test)] extern crate green;
4647

src/libstd/libc.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1130,7 +1130,7 @@ pub mod types {
11301130
Data4: [BYTE, ..8],
11311131
}
11321132

1133-
struct WSAPROTOCOLCHAIN {
1133+
pub struct WSAPROTOCOLCHAIN {
11341134
ChainLen: c_int,
11351135
ChainEntries: [DWORD, ..MAX_PROTOCOL_CHAIN],
11361136
}

src/libstd/rt/local.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ pub trait Local<Borrowed> {
2424
unsafe fn try_unsafe_borrow() -> Option<*mut Self>;
2525
}
2626

27+
#[allow(visible_private_types)]
2728
impl Local<local_ptr::Borrowed<Task>> for Task {
2829
#[inline]
2930
fn put(value: ~Task) { unsafe { local_ptr::put(value) } }
@@ -127,4 +128,3 @@ mod test {
127128
}
128129

129130
}
130-

src/libstd/rt/local_ptr.rs

+1
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,7 @@ pub mod native {
366366

367367
#[inline]
368368
#[cfg(not(test))]
369+
#[allow(visible_private_types)]
369370
pub fn maybe_tls_key() -> Option<tls::Key> {
370371
unsafe {
371372
// NB: This is a little racy because, while the key is

src/libstd/rt/unwind.rs

+2
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,7 @@ fn rust_exception_class() -> uw::_Unwind_Exception_Class {
280280

281281
#[cfg(not(target_arch = "arm"), not(test))]
282282
#[doc(hidden)]
283+
#[allow(visible_private_types)]
283284
pub mod eabi {
284285
use uw = super::libunwind;
285286
use libc::c_int;
@@ -333,6 +334,7 @@ pub mod eabi {
333334
// ARM EHABI uses a slightly different personality routine signature,
334335
// but otherwise works the same.
335336
#[cfg(target_arch = "arm", not(test))]
337+
#[allow(visible_private_types)]
336338
pub mod eabi {
337339
use uw = super::libunwind;
338340
use libc::c_int;

0 commit comments

Comments
 (0)