about summary refs log tree commit diff
path: root/src/librustdoc/visit_lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/librustdoc/visit_lib.rs')
-rw-r--r--src/librustdoc/visit_lib.rs108
1 files changed, 108 insertions, 0 deletions
diff --git a/src/librustdoc/visit_lib.rs b/src/librustdoc/visit_lib.rs
new file mode 100644
index 00000000000..f56f4c306f9
--- /dev/null
+++ b/src/librustdoc/visit_lib.rs
@@ -0,0 +1,108 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use rustc::middle::cstore::{CrateStore, ChildItem, DefLike};
+use rustc::middle::privacy::{AccessLevels, AccessLevel};
+use rustc::hir::def::Def;
+use rustc::hir::def_id::{CRATE_DEF_INDEX, DefId};
+use rustc::ty::Visibility;
+use syntax::ast;
+
+use std::cell::RefMut;
+
+use clean::{Attributes, Clean};
+
+// FIXME: this may not be exhaustive, but is sufficient for rustdocs current uses
+
+/// Similar to `librustc_privacy::EmbargoVisitor`, but also takes
+/// specific rustdoc annotations into account (i.e. `doc(hidden)`)
+pub struct LibEmbargoVisitor<'a, 'b: 'a, 'tcx: 'b> {
+    cx: &'a ::core::DocContext<'b, 'tcx>,
+    cstore: &'a CrateStore<'tcx>,
+    // Accessibility levels for reachable nodes
+    access_levels: RefMut<'a, AccessLevels<DefId>>,
+    // Previous accessibility level, None means unreachable
+    prev_level: Option<AccessLevel>,
+}
+
+impl<'a, 'b, 'tcx> LibEmbargoVisitor<'a, 'b, 'tcx> {
+    pub fn new(cx: &'a ::core::DocContext<'b, 'tcx>) -> LibEmbargoVisitor<'a, 'b, 'tcx> {
+        LibEmbargoVisitor {
+            cx: cx,
+            cstore: &*cx.sess().cstore,
+            access_levels: cx.access_levels.borrow_mut(),
+            prev_level: Some(AccessLevel::Public),
+        }
+    }
+
+    pub fn visit_lib(&mut self, cnum: ast::CrateNum) {
+        let did = DefId { krate: cnum, index: CRATE_DEF_INDEX };
+        self.visit_mod(did);
+    }
+
+    // Updates node level and returns the updated level
+    fn update(&mut self, did: DefId, level: Option<AccessLevel>) -> Option<AccessLevel> {
+        let attrs: Vec<_> = self.cx.tcx().get_attrs(did).iter()
+                                                        .map(|a| a.clean(self.cx))
+                                                        .collect();
+        let is_hidden = attrs.list("doc").has_word("hidden");
+
+        let old_level = self.access_levels.map.get(&did).cloned();
+        // Accessibility levels can only grow
+        if level > old_level && !is_hidden {
+            self.access_levels.map.insert(did, level.unwrap());
+            level
+        } else {
+            old_level
+        }
+    }
+
+    pub fn visit_mod(&mut self, did: DefId) {
+        for item in self.cstore.item_children(did) {
+            if let DefLike::DlDef(def) = item.def {
+                match def {
+                    Def::Mod(did) |
+                    Def::ForeignMod(did) |
+                    Def::Trait(did) |
+                    Def::Struct(did) |
+                    Def::Enum(did) |
+                    Def::TyAlias(did) |
+                    Def::Fn(did) |
+                    Def::Method(did) |
+                    Def::Static(did, _) |
+                    Def::Const(did) => self.visit_item(did, item),
+                    _ => {}
+                }
+            }
+        }
+    }
+
+    fn visit_item(&mut self, did: DefId, item: ChildItem) {
+        let inherited_item_level = match item.def {
+            DefLike::DlImpl(..) | DefLike::DlField => unreachable!(),
+            DefLike::DlDef(def) => {
+                match def {
+                    Def::ForeignMod(..) => self.prev_level,
+                    _ => if item.vis == Visibility::Public { self.prev_level } else { None }
+                }
+            }
+        };
+
+        let item_level = self.update(did, inherited_item_level);
+
+        if let DefLike::DlDef(Def::Mod(did)) = item.def {
+            let orig_level = self.prev_level;
+
+            self.prev_level = item_level;
+            self.visit_mod(did);
+            self.prev_level = orig_level;
+        }
+    }
+}