Skip to content

Commit 599df16

Browse files
committed
rustc_privacy: Expand public node set, build exported node set more correctly
1 parent d2f41bd commit 599df16

File tree

3 files changed

+149
-92
lines changed

3 files changed

+149
-92
lines changed

src/librustc_front/hir.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1159,6 +1159,12 @@ impl StructFieldKind {
11591159
NamedField(..) => false,
11601160
}
11611161
}
1162+
1163+
pub fn visibility(&self) -> Visibility {
1164+
match *self {
1165+
NamedField(_, vis) | UnnamedField(vis) => vis
1166+
}
1167+
}
11621168
}
11631169

11641170
/// Fields and Ids of enum variants and structs

src/librustc_privacy/lib.rs

Lines changed: 137 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ struct EmbargoVisitor<'a, 'tcx: 'a> {
162162
// This is a list of all exported items in the AST. An exported item is any
163163
// function/method/item which is usable by external crates. This essentially
164164
// means that the result is "public all the way down", but the "path down"
165-
// may jump across private boundaries through reexport statements.
165+
// may jump across private boundaries through reexport statements or type aliases.
166166
exported_items: ExportedItems,
167167

168168
// This sets contains all the destination nodes which are publicly
@@ -172,9 +172,12 @@ struct EmbargoVisitor<'a, 'tcx: 'a> {
172172
// destination must also be exported.
173173
reexports: NodeSet,
174174

175+
// Items that are directly public without help of reexports or type aliases.
175176
// These two fields are closely related to one another in that they are only
176-
// used for generation of the 'PublicItems' set, not for privacy checking at
177-
// all
177+
// used for generation of the `public_items` set, not for privacy checking at
178+
// all. Public items are mostly a subset of exported items with exception of
179+
// fields and exported macros - they are public, but not exported.
180+
// FIXME: Make fields and exported macros exported as well (requires fixing resulting ICEs)
178181
public_items: PublicItems,
179182
prev_public: bool,
180183
}
@@ -194,58 +197,103 @@ impl<'a, 'tcx> EmbargoVisitor<'a, 'tcx> {
194197
fn exported_trait(&self, _id: ast::NodeId) -> bool {
195198
true
196199
}
200+
201+
fn is_public_exported_ty(&self, ty: &hir::Ty) -> (bool, bool) {
202+
if let hir::TyPath(..) = ty.node {
203+
match self.tcx.def_map.borrow().get(&ty.id).unwrap().full_def() {
204+
def::DefPrimTy(..) | def::DefSelfTy(..) => (true, true),
205+
def => {
206+
if let Some(node_id) = self.tcx.map.as_local_node_id(def.def_id()) {
207+
(self.public_items.contains(&node_id),
208+
self.exported_items.contains(&node_id))
209+
} else {
210+
(true, true)
211+
}
212+
}
213+
}
214+
} else {
215+
(true, true)
216+
}
217+
}
218+
219+
fn is_public_exported_trait(&self, trait_ref: &hir::TraitRef) -> (bool, bool) {
220+
let did = self.tcx.trait_ref_to_def_id(trait_ref);
221+
if let Some(node_id) = self.tcx.map.as_local_node_id(did) {
222+
(self.public_items.contains(&node_id), self.exported_items.contains(&node_id))
223+
} else {
224+
(true, true)
225+
}
226+
}
227+
228+
fn maybe_insert_id(&mut self, id: ast::NodeId) {
229+
if self.prev_public { self.public_items.insert(id); }
230+
if self.prev_exported { self.exported_items.insert(id); }
231+
}
197232
}
198233

199234
impl<'a, 'tcx, 'v> Visitor<'v> for EmbargoVisitor<'a, 'tcx> {
200235
fn visit_item(&mut self, item: &hir::Item) {
201-
let orig_all_pub = self.prev_public;
202-
self.prev_public = orig_all_pub && item.vis == hir::Public;
203-
if self.prev_public {
204-
self.public_items.insert(item.id);
205-
}
206-
236+
let orig_all_public = self.prev_public;
207237
let orig_all_exported = self.prev_exported;
208238
match item.node {
209239
// impls/extern blocks do not break the "public chain" because they
210-
// cannot have visibility qualifiers on them anyway
240+
// cannot have visibility qualifiers on them anyway. They are also not
241+
// added to public/exported sets based on inherited publicity.
211242
hir::ItemImpl(..) | hir::ItemDefaultImpl(..) | hir::ItemForeignMod(..) => {}
212243

213244
// Traits are a little special in that even if they themselves are
214245
// not public they may still be exported.
215246
hir::ItemTrait(..) => {
247+
self.prev_public = self.prev_public && item.vis == hir::Public;
216248
self.prev_exported = self.exported_trait(item.id);
249+
250+
self.maybe_insert_id(item.id);
217251
}
218252

219253
// Private by default, hence we only retain the "public chain" if
220254
// `pub` is explicitly listed.
221255
_ => {
222-
self.prev_exported =
223-
(orig_all_exported && item.vis == hir::Public) ||
224-
self.reexports.contains(&item.id);
256+
self.prev_public = self.prev_public && item.vis == hir::Public;
257+
self.prev_exported = self.prev_exported && item.vis == hir::Public ||
258+
self.reexports.contains(&item.id);
259+
260+
self.maybe_insert_id(item.id);
225261
}
226262
}
227263

228-
let public_first = self.prev_exported &&
229-
self.exported_items.insert(item.id);
230-
231264
match item.node {
232265
// Enum variants inherit from their parent, so if the enum is
233-
// public all variants are public unless they're explicitly priv
234-
hir::ItemEnum(ref def, _) if public_first => {
266+
// public all variants are public
267+
hir::ItemEnum(ref def, _) => {
235268
for variant in &def.variants {
236-
self.exported_items.insert(variant.node.data.id());
237-
self.public_items.insert(variant.node.data.id());
269+
self.maybe_insert_id(variant.node.data.id());
270+
for field in variant.node.data.fields() {
271+
// Variant fields are always public
272+
if self.prev_public { self.public_items.insert(field.node.id); }
273+
// FIXME: Make fields exported (requires fixing resulting ICEs)
274+
// if self.prev_exported { self.exported_items.insert(field.node.id); }
275+
}
276+
}
277+
}
278+
279+
// * Inherent impls for public types only have public methods exported
280+
// * Inherent impls for private types do not need to export their methods
281+
// * Inherent impls themselves are not exported, they are nothing more than
282+
// containers for other items
283+
hir::ItemImpl(_, _, _, None, ref ty, ref impl_items) => {
284+
let (public_ty, exported_ty) = self.is_public_exported_ty(&ty);
285+
286+
for impl_item in impl_items {
287+
if impl_item.vis == hir::Public {
288+
if public_ty { self.public_items.insert(impl_item.id); }
289+
if exported_ty { self.exported_items.insert(impl_item.id); }
290+
}
238291
}
239292
}
240293

241294
// Implementations are a little tricky to determine what's exported
242295
// out of them. Here's a few cases which are currently defined:
243296
//
244-
// * Impls for private types do not need to export their methods
245-
// (either public or private methods)
246-
//
247-
// * Impls for public types only have public methods exported
248-
//
249297
// * Public trait impls for public types must have all methods
250298
// exported.
251299
//
@@ -257,83 +305,51 @@ impl<'a, 'tcx, 'v> Visitor<'v> for EmbargoVisitor<'a, 'tcx> {
257305
// undefined symbols at linkage time if this case is not handled.
258306
//
259307
// * Private trait impls for private types can be completely ignored
260-
hir::ItemImpl(_, _, _, _, ref ty, ref impl_items) => {
261-
let public_ty = match ty.node {
262-
hir::TyPath(..) => {
263-
match self.tcx.def_map.borrow().get(&ty.id).unwrap().full_def() {
264-
def::DefPrimTy(..) => true,
265-
def::DefSelfTy(..) => true,
266-
def => {
267-
let did = def.def_id();
268-
if let Some(node_id) = self.tcx.map.as_local_node_id(did) {
269-
self.exported_items.contains(&node_id)
270-
} else {
271-
true
272-
}
273-
}
274-
}
275-
}
276-
_ => true,
277-
};
278-
let tr = self.tcx.impl_trait_ref(self.tcx.map.local_def_id(item.id));
279-
let public_trait = tr.clone().map_or(false, |tr| {
280-
if let Some(node_id) = self.tcx.map.as_local_node_id(tr.def_id) {
281-
self.exported_items.contains(&node_id)
282-
} else {
283-
true
284-
}
285-
});
308+
hir::ItemImpl(_, _, _, Some(ref trait_ref), ref ty, ref impl_items) => {
309+
let (public_ty, _exported_ty) = self.is_public_exported_ty(&ty);
310+
let (public_trait, exported_trait) = self.is_public_exported_trait(trait_ref);
286311

287-
if public_ty || public_trait {
288-
for impl_item in impl_items {
289-
match impl_item.node {
290-
hir::ConstImplItem(..) => {
291-
if (public_ty && impl_item.vis == hir::Public)
292-
|| tr.is_some() {
293-
self.exported_items.insert(impl_item.id);
294-
}
295-
}
296-
hir::MethodImplItem(ref sig, _) => {
297-
let meth_public = match sig.explicit_self.node {
298-
hir::SelfStatic => public_ty,
299-
_ => true,
300-
} && impl_item.vis == hir::Public;
301-
if meth_public || tr.is_some() {
302-
self.exported_items.insert(impl_item.id);
303-
}
304-
}
305-
hir::TypeImplItem(_) => {}
306-
}
307-
}
312+
if public_ty && public_trait { self.public_items.insert(item.id); }
313+
if exported_trait { self.exported_items.insert(item.id); }
314+
315+
for impl_item in impl_items {
316+
if public_ty && public_trait { self.public_items.insert(impl_item.id); }
317+
if exported_trait { self.exported_items.insert(impl_item.id); }
308318
}
309319
}
310320

321+
// Default trait impls are exported for public traits
322+
hir::ItemDefaultImpl(_, ref trait_ref) => {
323+
let (public_trait, exported_trait) = self.is_public_exported_trait(trait_ref);
324+
325+
if public_trait { self.public_items.insert(item.id); }
326+
if exported_trait { self.exported_items.insert(item.id); }
327+
}
328+
311329
// Default methods on traits are all public so long as the trait
312330
// is public
313-
hir::ItemTrait(_, _, _, ref trait_items) if public_first => {
331+
hir::ItemTrait(_, _, _, ref trait_items) => {
314332
for trait_item in trait_items {
315-
debug!("trait item {}", trait_item.id);
316-
self.exported_items.insert(trait_item.id);
333+
self.maybe_insert_id(trait_item.id);
317334
}
318335
}
319336

320337
// Struct constructors are public if the struct is all public.
321-
hir::ItemStruct(ref def, _) if public_first => {
338+
hir::ItemStruct(ref def, _) => {
322339
if !def.is_struct() {
323-
self.exported_items.insert(def.id());
340+
self.maybe_insert_id(def.id());
324341
}
325-
// fields can be public or private, so lets check
326342
for field in def.fields() {
327-
let vis = match field.node.kind {
328-
hir::NamedField(_, vis) | hir::UnnamedField(vis) => vis
329-
};
330-
if vis == hir::Public {
331-
self.public_items.insert(field.node.id);
343+
// Struct fields can be public or private, so lets check
344+
if field.node.kind.visibility() == hir::Public {
345+
if self.prev_public { self.public_items.insert(field.node.id); }
346+
// FIXME: Make fields exported (requires fixing resulting ICEs)
347+
// if self.prev_exported { self.exported_items.insert(field.node.id); }
332348
}
333349
}
334350
}
335351

336-
hir::ItemTy(ref ty, _) if public_first => {
352+
hir::ItemTy(ref ty, _) if self.prev_exported => {
337353
if let hir::TyPath(..) = ty.node {
338354
match self.tcx.def_map.borrow().get(&ty.id).unwrap().full_def() {
339355
def::DefPrimTy(..) | def::DefSelfTy(..) | def::DefTyParam(..) => {},
@@ -347,19 +363,37 @@ impl<'a, 'tcx, 'v> Visitor<'v> for EmbargoVisitor<'a, 'tcx> {
347363
}
348364
}
349365

366+
hir::ItemForeignMod(ref foreign_mod) => {
367+
for foreign_item in &foreign_mod.items {
368+
let public = self.prev_public && foreign_item.vis == hir::Public;
369+
let exported = self.prev_exported && foreign_item.vis == hir::Public ||
370+
self.reexports.contains(&foreign_item.id);
371+
372+
if public { self.public_items.insert(foreign_item.id); }
373+
if exported { self.exported_items.insert(foreign_item.id); }
374+
}
375+
}
376+
350377
_ => {}
351378
}
352379

353380
visit::walk_item(self, item);
354381

382+
self.prev_public = orig_all_public;
355383
self.prev_exported = orig_all_exported;
356-
self.prev_public = orig_all_pub;
357384
}
358385

359-
fn visit_foreign_item(&mut self, a: &hir::ForeignItem) {
360-
if (self.prev_exported && a.vis == hir::Public) || self.reexports.contains(&a.id) {
361-
self.exported_items.insert(a.id);
362-
}
386+
fn visit_block(&mut self, b: &'v hir::Block) {
387+
let orig_all_public = replace(&mut self.prev_public, false);
388+
let orig_all_exported = replace(&mut self.prev_exported, false);
389+
390+
// Blocks can have exported and public items, for example impls, but they always
391+
// start as non-public and non-exported regardless of publicity of a function,
392+
// constant, type, field, etc. in which this block resides
393+
visit::walk_block(self, b);
394+
395+
self.prev_public = orig_all_public;
396+
self.prev_exported = orig_all_exported;
363397
}
364398

365399
fn visit_mod(&mut self, m: &hir::Mod, _sp: Span, id: ast::NodeId) {
@@ -375,6 +409,12 @@ impl<'a, 'tcx, 'v> Visitor<'v> for EmbargoVisitor<'a, 'tcx> {
375409
}
376410
visit::walk_mod(self, m)
377411
}
412+
413+
fn visit_macro_def(&mut self, md: &'v hir::MacroDef) {
414+
self.public_items.insert(md.id);
415+
// FIXME: Make exported macros exported (requires fixing resulting ICEs)
416+
// self.exported_items.insert(md.id);
417+
}
378418
}
379419

380420
////////////////////////////////////////////////////////////////////////////////
@@ -1447,11 +1487,13 @@ impl<'a, 'tcx, 'v> Visitor<'v> for VisiblePrivateTypesVisitor<'a, 'tcx> {
14471487
}
14481488
}
14491489

1450-
14511490
// we don't need to introspect into these at all: an
14521491
// expression/block context can't possibly contain exported things.
14531492
// (Making them no-ops stops us from traversing the whole AST without
14541493
// having to be super careful about our `walk_...` calls above.)
1494+
// FIXME: Unfortunately this ^^^ is not true, blocks can contain
1495+
// exported items (e.g. impls) and actual code in rustc itself breaks
1496+
// if we don't traverse blocks in `EmbargoVisitor`
14551497
fn visit_block(&mut self, _: &hir::Block) {}
14561498
fn visit_expr(&mut self, _: &hir::Expr) {}
14571499
}
@@ -1501,9 +1543,12 @@ pub fn check_crate(tcx: &ty::ctxt,
15011543
prev_public: true,
15021544
};
15031545
loop {
1504-
let before = visitor.exported_items.len();
1546+
let before = (visitor.exported_items.len(), visitor.public_items.len(),
1547+
visitor.reexports.len());
15051548
visit::walk_crate(&mut visitor, krate);
1506-
if before == visitor.exported_items.len() {
1549+
let after = (visitor.exported_items.len(), visitor.public_items.len(),
1550+
visitor.reexports.len());
1551+
if after == before {
15071552
break
15081553
}
15091554
}

src/libsyntax/ast.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1737,6 +1737,12 @@ impl StructFieldKind {
17371737
NamedField(..) => false,
17381738
}
17391739
}
1740+
1741+
pub fn visibility(&self) -> Visibility {
1742+
match *self {
1743+
NamedField(_, vis) | UnnamedField(vis) => vis
1744+
}
1745+
}
17401746
}
17411747

17421748
/// Fields and Ids of enum variants and structs

0 commit comments

Comments
 (0)