@@ -16,6 +16,7 @@ use std::mem::replace;
1616use collections:: { HashSet , HashMap } ;
1717
1818use metadata:: csearch;
19+ use middle:: lint;
1920use middle:: resolve;
2021use middle:: ty;
2122use 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+
11721418pub 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}
0 commit comments