make missing_doc lint respect the visibility rules
Previously, the `exported_items` set created by the privacy pass was incomplete. Specifically, it did not include items that had been defined at a private path but then `pub use`d at a public path. This commit finds all crate exports during the privacy pass. Consequently, some code in the reachable pass and in rustdoc is no longer necessary. This commit then removes the separate `MissingDocLintVisitor` lint pass, opting to check missing_doc lint in the same pass as the other lint checkers using the visibility result computed by the privacy pass. Fixes #9777.
This commit is contained in:
parent
4d9b95fada
commit
1f7eb4f9aa
@ -305,11 +305,10 @@ pub fn phase_3_run_analysis_passes(sess: Session,
|
||||
|
||||
let reachable_map =
|
||||
time(time_passes, "reachability checking", (), |_|
|
||||
reachable::find_reachable(ty_cx, method_map, exp_map2,
|
||||
&exported_items));
|
||||
reachable::find_reachable(ty_cx, method_map, &exported_items));
|
||||
|
||||
time(time_passes, "lint checking", (), |_|
|
||||
lint::check_crate(ty_cx, crate));
|
||||
lint::check_crate(ty_cx, &exported_items, crate));
|
||||
|
||||
CrateAnalysis {
|
||||
exp_map2: exp_map2,
|
||||
|
@ -34,6 +34,7 @@
|
||||
//! Context itself, span_lint should be used instead of add_lint.
|
||||
|
||||
use driver::session;
|
||||
use middle::privacy;
|
||||
use middle::trans::adt; // for `adt::is_ffi_safe`
|
||||
use middle::ty;
|
||||
use middle::pat_util;
|
||||
@ -317,13 +318,20 @@ pub fn get_lint_dict() -> LintDict {
|
||||
return map;
|
||||
}
|
||||
|
||||
struct Context {
|
||||
struct Context<'self> {
|
||||
// All known lint modes (string versions)
|
||||
dict: @LintDict,
|
||||
// Current levels of each lint warning
|
||||
cur: SmallIntMap<(level, LintSource)>,
|
||||
// context we're checking in (used to access fields like sess)
|
||||
tcx: ty::ctxt,
|
||||
// Items exported by the crate; used by the missing_doc lint.
|
||||
exported_items: &'self privacy::ExportedItems,
|
||||
// The id of the current `ast::struct_def` being walked.
|
||||
cur_struct_def_id: ast::NodeId,
|
||||
// Whether some ancestor of the current node was marked
|
||||
// #[doc(hidden)].
|
||||
is_doc_hidden: bool,
|
||||
|
||||
// When recursing into an attributed node of the ast which modifies lint
|
||||
// levels, this stack keeps track of the previous lint levels of whatever
|
||||
@ -331,7 +339,7 @@ struct Context {
|
||||
lint_stack: ~[(lint, level, LintSource)],
|
||||
}
|
||||
|
||||
impl Context {
|
||||
impl<'self> Context<'self> {
|
||||
fn get_level(&self, lint: lint) -> level {
|
||||
match self.cur.find(&(lint as uint)) {
|
||||
Some(&(lvl, _)) => lvl,
|
||||
@ -440,9 +448,16 @@ impl Context {
|
||||
true
|
||||
};
|
||||
|
||||
let old_is_doc_hidden = self.is_doc_hidden;
|
||||
self.is_doc_hidden = self.is_doc_hidden ||
|
||||
attrs.iter().any(|attr| ("doc" == attr.name() && match attr.meta_item_list()
|
||||
{ None => false,
|
||||
Some(l) => attr::contains_name(l, "hidden") }));
|
||||
|
||||
f(self);
|
||||
|
||||
// rollback
|
||||
self.is_doc_hidden = old_is_doc_hidden;
|
||||
do pushed.times {
|
||||
let (lint, lvl, src) = self.lint_stack.pop();
|
||||
self.set_level(lint, lvl, src);
|
||||
@ -870,123 +885,81 @@ fn check_unnecessary_allocation(cx: &Context, e: &ast::Expr) {
|
||||
}
|
||||
}
|
||||
|
||||
struct MissingDocLintVisitor(ty::ctxt);
|
||||
fn check_missing_doc_attrs(cx: &Context,
|
||||
id: ast::NodeId,
|
||||
attrs: &[ast::Attribute],
|
||||
sp: Span,
|
||||
desc: &'static str) {
|
||||
// If we're building a test harness, then warning about
|
||||
// documentation is probably not really relevant right now.
|
||||
if cx.tcx.sess.opts.test { return }
|
||||
|
||||
impl MissingDocLintVisitor {
|
||||
fn check_attrs(&self, attrs: &[ast::Attribute], id: ast::NodeId,
|
||||
sp: Span, msg: ~str) {
|
||||
if !attrs.iter().any(|a| a.node.is_sugared_doc) {
|
||||
self.sess.add_lint(missing_doc, id, sp, msg);
|
||||
}
|
||||
}
|
||||
// `#[doc(hidden)]` disables missing_doc check.
|
||||
if cx.is_doc_hidden { return }
|
||||
|
||||
fn check_struct(&self, sdef: &ast::struct_def) {
|
||||
for field in sdef.fields.iter() {
|
||||
match field.node.kind {
|
||||
ast::named_field(_, vis) if vis != ast::private => {
|
||||
self.check_attrs(field.node.attrs, field.node.id, field.span,
|
||||
~"missing documentation for a field");
|
||||
}
|
||||
ast::unnamed_field | ast::named_field(*) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Only check publicly-visible items, using the result from the
|
||||
// privacy pass.
|
||||
if !cx.exported_items.contains(&id) { return }
|
||||
|
||||
fn doc_hidden(&self, attrs: &[ast::Attribute]) -> bool {
|
||||
do attrs.iter().any |attr| {
|
||||
"doc" == attr.name() &&
|
||||
match attr.meta_item_list() {
|
||||
Some(l) => attr::contains_name(l, "hidden"),
|
||||
None => false // not of the form #[doc(...)]
|
||||
}
|
||||
}
|
||||
if !attrs.iter().any(|a| a.node.is_sugared_doc) {
|
||||
cx.span_lint(missing_doc, sp,
|
||||
format!("missing documentation for {}", desc));
|
||||
}
|
||||
}
|
||||
|
||||
impl Visitor<()> for MissingDocLintVisitor {
|
||||
fn visit_ty_method(&mut self, m:&ast::TypeMethod, _: ()) {
|
||||
if self.doc_hidden(m.attrs) { return }
|
||||
fn check_missing_doc_item(cx: &mut Context, it: &ast::item) { // XXX doesn't need to be mut
|
||||
let desc = match it.node {
|
||||
ast::item_fn(*) => "a function",
|
||||
ast::item_mod(*) => "a module",
|
||||
ast::item_enum(*) => "an enum",
|
||||
ast::item_struct(*) => "a struct",
|
||||
ast::item_trait(*) => "a trait",
|
||||
_ => return
|
||||
};
|
||||
check_missing_doc_attrs(cx, it.id, it.attrs, it.span, desc);
|
||||
}
|
||||
|
||||
// All ty_method objects are linted about because they're part of a
|
||||
// trait (no visibility)
|
||||
self.check_attrs(m.attrs, m.id, m.span,
|
||||
~"missing documentation for a method");
|
||||
visit::walk_ty_method(self, m, ());
|
||||
}
|
||||
|
||||
fn visit_fn(&mut self, fk: &visit::fn_kind, d: &ast::fn_decl,
|
||||
b: &ast::Block, sp: Span, id: ast::NodeId, _: ()) {
|
||||
// Only warn about explicitly public methods.
|
||||
match *fk {
|
||||
visit::fk_method(_, _, m) => {
|
||||
if self.doc_hidden(m.attrs) {
|
||||
return;
|
||||
}
|
||||
// If we're in a trait implementation, no need to duplicate
|
||||
// documentation
|
||||
if m.vis == ast::public {
|
||||
self.check_attrs(m.attrs, id, sp,
|
||||
~"missing documentation for a method");
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
visit::walk_fn(self, fk, d, b, sp, id, ());
|
||||
}
|
||||
|
||||
fn visit_item(&mut self, it: @ast::item, _: ()) {
|
||||
// If we're building a test harness, then warning about documentation is
|
||||
// probably not really relevant right now
|
||||
if self.sess.opts.test { return }
|
||||
if self.doc_hidden(it.attrs) { return }
|
||||
|
||||
match it.node {
|
||||
ast::item_struct(sdef, _) if it.vis == ast::public => {
|
||||
self.check_attrs(it.attrs, it.id, it.span,
|
||||
~"missing documentation for a struct");
|
||||
self.check_struct(sdef);
|
||||
}
|
||||
|
||||
// Skip implementations because they inherit documentation from the
|
||||
// trait (which was already linted)
|
||||
ast::item_impl(_, Some(*), _, _) => return,
|
||||
|
||||
ast::item_trait(*) if it.vis != ast::public => return,
|
||||
ast::item_trait(*) => self.check_attrs(it.attrs, it.id, it.span,
|
||||
~"missing documentation for a trait"),
|
||||
|
||||
ast::item_fn(*) if it.vis == ast::public => {
|
||||
self.check_attrs(it.attrs, it.id, it.span,
|
||||
~"missing documentation for a function");
|
||||
}
|
||||
|
||||
ast::item_mod(*) if it.vis == ast::public => {
|
||||
self.check_attrs(it.attrs, it.id, it.span,
|
||||
~"missing documentation for a module");
|
||||
}
|
||||
|
||||
ast::item_enum(ref edef, _) if it.vis == ast::public => {
|
||||
self.check_attrs(it.attrs, it.id, it.span,
|
||||
~"missing documentation for an enum");
|
||||
for variant in edef.variants.iter() {
|
||||
if variant.node.vis == ast::private { continue; }
|
||||
|
||||
self.check_attrs(variant.node.attrs, variant.node.id,
|
||||
variant.span,
|
||||
~"missing documentation for a variant");
|
||||
match variant.node.kind {
|
||||
ast::struct_variant_kind(sdef) => {
|
||||
self.check_struct(sdef);
|
||||
}
|
||||
_ => ()
|
||||
fn check_missing_doc_method(cx: &Context, m: &ast::method) {
|
||||
let did = ast::DefId {
|
||||
crate: ast::LOCAL_CRATE,
|
||||
node: m.id
|
||||
};
|
||||
match cx.tcx.methods.find(&did) {
|
||||
None => cx.tcx.sess.span_bug(m.span, "missing method descriptor?!"),
|
||||
Some(md) => {
|
||||
match md.container {
|
||||
// Always check default methods defined on traits.
|
||||
ty::TraitContainer(*) => {}
|
||||
// For methods defined on impls, it depends on whether
|
||||
// it is an implementation for a trait or is a plain
|
||||
// impl.
|
||||
ty::ImplContainer(cid) => {
|
||||
match ty::impl_trait_ref(cx.tcx, cid) {
|
||||
Some(*) => return, // impl for trait: don't doc
|
||||
None => {} // plain impl: doc according to privacy
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
visit::walk_item(self, it, ());
|
||||
}
|
||||
check_missing_doc_attrs(cx, m.id, m.attrs, m.span, "a method");
|
||||
}
|
||||
|
||||
fn check_missing_doc_ty_method(cx: &Context, tm: &ast::TypeMethod) {
|
||||
check_missing_doc_attrs(cx, tm.id, tm.attrs, tm.span, "a type method");
|
||||
}
|
||||
|
||||
fn check_missing_doc_struct_field(cx: &Context, sf: &ast::struct_field) {
|
||||
match sf.node.kind {
|
||||
ast::named_field(_, vis) if vis != ast::private =>
|
||||
check_missing_doc_attrs(cx, cx.cur_struct_def_id, sf.node.attrs,
|
||||
sf.span, "a struct field"),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_missing_doc_variant(cx: &Context, v: &ast::variant) {
|
||||
check_missing_doc_attrs(cx, v.node.id, v.node.attrs, v.span, "a variant");
|
||||
}
|
||||
|
||||
/// Checks for use of items with #[deprecated], #[experimental] and
|
||||
@ -1062,13 +1035,14 @@ fn check_stability(cx: &Context, e: &ast::Expr) {
|
||||
cx.span_lint(lint, e.span, msg);
|
||||
}
|
||||
|
||||
impl Visitor<()> for Context {
|
||||
impl<'self> Visitor<()> for Context<'self> {
|
||||
fn visit_item(&mut self, it: @ast::item, _: ()) {
|
||||
do self.with_lint_attrs(it.attrs) |cx| {
|
||||
check_item_ctypes(cx, it);
|
||||
check_item_non_camel_case_types(cx, it);
|
||||
check_item_non_uppercase_statics(cx, it);
|
||||
check_heap_item(cx, it);
|
||||
check_missing_doc_item(cx, it);
|
||||
|
||||
do cx.visit_ids |v| {
|
||||
v.visit_item(it, ());
|
||||
@ -1111,6 +1085,8 @@ impl Visitor<()> for Context {
|
||||
match *fk {
|
||||
visit::fk_method(_, _, m) => {
|
||||
do self.with_lint_attrs(m.attrs) |cx| {
|
||||
check_missing_doc_method(cx, m);
|
||||
|
||||
do cx.visit_ids |v| {
|
||||
v.visit_fn(fk, decl, body, span, id, ());
|
||||
}
|
||||
@ -1120,9 +1096,45 @@ impl Visitor<()> for Context {
|
||||
_ => recurse(self),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_ty_method(&mut self, t: &ast::TypeMethod, _: ()) {
|
||||
do self.with_lint_attrs(t.attrs) |cx| {
|
||||
check_missing_doc_ty_method(cx, t);
|
||||
|
||||
visit::walk_ty_method(cx, t, ());
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_struct_def(&mut self,
|
||||
s: @ast::struct_def,
|
||||
i: ast::Ident,
|
||||
g: &ast::Generics,
|
||||
id: ast::NodeId,
|
||||
_: ()) {
|
||||
let old_id = self.cur_struct_def_id;
|
||||
self.cur_struct_def_id = id;
|
||||
visit::walk_struct_def(self, s, i, g, id, ());
|
||||
self.cur_struct_def_id = old_id;
|
||||
}
|
||||
|
||||
fn visit_struct_field(&mut self, s: @ast::struct_field, _: ()) {
|
||||
do self.with_lint_attrs(s.node.attrs) |cx| {
|
||||
check_missing_doc_struct_field(cx, s);
|
||||
|
||||
visit::walk_struct_field(cx, s, ());
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_variant(&mut self, v: &ast::variant, g: &ast::Generics, _: ()) {
|
||||
do self.with_lint_attrs(v.node.attrs) |cx| {
|
||||
check_missing_doc_variant(cx, v);
|
||||
|
||||
visit::walk_variant(cx, v, g, ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ast_util::IdVisitingOperation for Context {
|
||||
impl<'self> ast_util::IdVisitingOperation for Context<'self> {
|
||||
fn visit_id(&self, id: ast::NodeId) {
|
||||
match self.tcx.sess.lints.pop(&id) {
|
||||
None => {}
|
||||
@ -1135,17 +1147,16 @@ impl ast_util::IdVisitingOperation for Context {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_crate(tcx: ty::ctxt, crate: &ast::Crate) {
|
||||
// This visitor contains more state than is currently maintained in Context,
|
||||
// and there's no reason for the Context to keep track of this information
|
||||
// really
|
||||
let mut dox = MissingDocLintVisitor(tcx);
|
||||
visit::walk_crate(&mut dox, crate, ());
|
||||
|
||||
pub fn check_crate(tcx: ty::ctxt,
|
||||
exported_items: &privacy::ExportedItems,
|
||||
crate: &ast::Crate) {
|
||||
let mut cx = Context {
|
||||
dict: @get_lint_dict(),
|
||||
cur: SmallIntMap::new(),
|
||||
tcx: tcx,
|
||||
exported_items: exported_items,
|
||||
cur_struct_def_id: -1,
|
||||
is_doc_hidden: false,
|
||||
lint_stack: ~[],
|
||||
};
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
|
||||
// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
@ -31,8 +31,7 @@ use syntax::visit::Visitor;
|
||||
|
||||
type Context<'self> = (&'self method_map, &'self resolve::ExportMap2);
|
||||
|
||||
// A set of all nodes in the ast which can be considered "publicly exported" in
|
||||
// the sense that they are accessible from anywhere in any hierarchy.
|
||||
/// A set of AST nodes exported by the crate.
|
||||
pub type ExportedItems = HashSet<ast::NodeId>;
|
||||
|
||||
// This visitor is used to determine the parent of all nodes in question when it
|
||||
@ -141,11 +140,28 @@ impl<'self> Visitor<()> for ParentVisitor<'self> {
|
||||
// This visitor is used to determine which items of the ast are embargoed,
|
||||
// otherwise known as not exported.
|
||||
struct EmbargoVisitor<'self> {
|
||||
tcx: ty::ctxt,
|
||||
// A set of all nodes in the ast which can be considered "publicly
|
||||
// exported" in the sense that they are accessible from anywhere
|
||||
// in any hierarchy. They are public items whose ancestors are all
|
||||
// public.
|
||||
path_all_public_items: &'self mut ExportedItems,
|
||||
// A set of all nodes in the ast that can be reached via a public
|
||||
// path. This includes everything in `path_all_public_items` as
|
||||
// well as re-exported private nodes (`pub use`ing a private
|
||||
// path).
|
||||
exported_items: &'self mut ExportedItems,
|
||||
exp_map2: &'self resolve::ExportMap2,
|
||||
path_all_public: bool,
|
||||
}
|
||||
|
||||
impl<'self> EmbargoVisitor<'self> {
|
||||
fn add_path_all_public_item(&mut self, id: ast::NodeId) {
|
||||
self.path_all_public_items.insert(id);
|
||||
self.exported_items.insert(id);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'self> Visitor<()> for EmbargoVisitor<'self> {
|
||||
fn visit_item(&mut self, item: @ast::item, _: ()) {
|
||||
let orig_all_pub = self.path_all_public;
|
||||
@ -162,7 +178,7 @@ impl<'self> Visitor<()> for EmbargoVisitor<'self> {
|
||||
}
|
||||
|
||||
if self.path_all_public {
|
||||
self.exported_items.insert(item.id);
|
||||
self.add_path_all_public_item(item.id);
|
||||
}
|
||||
|
||||
match item.node {
|
||||
@ -171,7 +187,7 @@ impl<'self> Visitor<()> for EmbargoVisitor<'self> {
|
||||
ast::item_enum(ref def, _) if self.path_all_public => {
|
||||
for variant in def.variants.iter() {
|
||||
if variant.node.vis != ast::private {
|
||||
self.exported_items.insert(variant.node.id);
|
||||
self.add_path_all_public_item(variant.node.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -184,7 +200,7 @@ impl<'self> Visitor<()> for EmbargoVisitor<'self> {
|
||||
_ => true,
|
||||
} && method.vis == ast::public;
|
||||
if public {
|
||||
self.exported_items.insert(method.id);
|
||||
self.add_path_all_public_item(method.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -193,7 +209,7 @@ impl<'self> Visitor<()> for EmbargoVisitor<'self> {
|
||||
ast::item_impl(_, Some(*), _, ref methods) => {
|
||||
for method in methods.iter() {
|
||||
debug!("exporting: {}", method.id);
|
||||
self.exported_items.insert(method.id);
|
||||
self.add_path_all_public_item(method.id);
|
||||
}
|
||||
}
|
||||
|
||||
@ -204,21 +220,20 @@ impl<'self> Visitor<()> for EmbargoVisitor<'self> {
|
||||
match *method {
|
||||
ast::provided(ref m) => {
|
||||
debug!("provided {}", m.id);
|
||||
self.exported_items.insert(m.id);
|
||||
self.add_path_all_public_item(m.id);
|
||||
}
|
||||
ast::required(ref m) => {
|
||||
debug!("required {}", m.id);
|
||||
self.exported_items.insert(m.id);
|
||||
self.add_path_all_public_item(m.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Default methods on traits are all public so long as the trait is
|
||||
// public
|
||||
// Struct constructors are public if the struct is all public.
|
||||
ast::item_struct(ref def, _) if self.path_all_public => {
|
||||
match def.ctor_id {
|
||||
Some(id) => { self.exported_items.insert(id); }
|
||||
Some(id) => { self.add_path_all_public_item(id); }
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
@ -233,17 +248,36 @@ impl<'self> Visitor<()> for EmbargoVisitor<'self> {
|
||||
|
||||
fn visit_foreign_item(&mut self, a: @ast::foreign_item, _: ()) {
|
||||
if self.path_all_public && a.vis == ast::public {
|
||||
self.exported_items.insert(a.id);
|
||||
self.add_path_all_public_item(a.id);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_mod(&mut self, m: &ast::_mod, sp: Span, id: ast::NodeId, _: ()) {
|
||||
// This code is here instead of in visit_item so that the
|
||||
// crate module gets processed as well.
|
||||
if self.path_all_public {
|
||||
match self.exp_map2.find(&id) {
|
||||
Some(exports) => {
|
||||
for export in exports.iter() {
|
||||
if is_local(export.def_id) && export.reexport {
|
||||
self.exported_items.insert(export.def_id.node);
|
||||
}
|
||||
}
|
||||
}
|
||||
None => self.tcx.sess.span_bug(sp, "missing exp_map2 entry \
|
||||
for module"),
|
||||
}
|
||||
}
|
||||
visit::walk_mod(self, m, ())
|
||||
}
|
||||
}
|
||||
|
||||
struct PrivacyVisitor<'self> {
|
||||
tcx: ty::ctxt,
|
||||
curitem: ast::NodeId,
|
||||
|
||||
// Results of previous analyses necessary for privacy checking.
|
||||
exported_items: &'self ExportedItems,
|
||||
// See comments on the same field in `EmbargoVisitor`.
|
||||
path_all_public_items: &'self ExportedItems,
|
||||
method_map: &'self method_map,
|
||||
parents: &'self HashMap<ast::NodeId, ast::NodeId>,
|
||||
external_exports: resolve::ExternalExports,
|
||||
@ -303,7 +337,7 @@ impl<'self> PrivacyVisitor<'self> {
|
||||
ExternallyDenied
|
||||
}
|
||||
};
|
||||
} else if self.exported_items.contains(&did.node) {
|
||||
} else if self.path_all_public_items.contains(&did.node) {
|
||||
debug!("privacy - exported item {}", self.nodestr(did.node));
|
||||
return Allowable;
|
||||
}
|
||||
@ -842,6 +876,7 @@ pub fn check_crate(tcx: ty::ctxt,
|
||||
last_private_map: resolve::LastPrivateMap,
|
||||
crate: &ast::Crate) -> ExportedItems {
|
||||
let mut parents = HashMap::new();
|
||||
let mut path_all_public_items = HashSet::new();
|
||||
let mut exported_items = HashSet::new();
|
||||
|
||||
// First, figure out who everyone's parent is
|
||||
@ -855,14 +890,16 @@ pub fn check_crate(tcx: ty::ctxt,
|
||||
|
||||
// Next, build up the list of all exported items from this crate
|
||||
{
|
||||
// Initialize the exported items with resolve's id for the "root crate"
|
||||
// to resolve references to `super` leading to the root and such.
|
||||
exported_items.insert(ast::CRATE_NODE_ID);
|
||||
let mut visitor = EmbargoVisitor {
|
||||
tcx: tcx,
|
||||
path_all_public_items: &mut path_all_public_items,
|
||||
exported_items: &mut exported_items,
|
||||
exp_map2: exp_map2,
|
||||
path_all_public: true, // start out as public
|
||||
};
|
||||
// Initialize the exported items with resolve's id for the "root crate"
|
||||
// to resolve references to `super` leading to the root and such.
|
||||
visitor.add_path_all_public_item(ast::CRATE_NODE_ID);
|
||||
visit::walk_crate(&mut visitor, crate, ());
|
||||
}
|
||||
|
||||
@ -871,7 +908,7 @@ pub fn check_crate(tcx: ty::ctxt,
|
||||
let mut visitor = PrivacyVisitor {
|
||||
curitem: ast::DUMMY_NODE_ID,
|
||||
tcx: tcx,
|
||||
exported_items: &exported_items,
|
||||
path_all_public_items: &path_all_public_items,
|
||||
parents: &parents,
|
||||
method_map: method_map,
|
||||
external_exports: external_exports,
|
||||
@ -879,5 +916,6 @@ pub fn check_crate(tcx: ty::ctxt,
|
||||
};
|
||||
visit::walk_crate(&mut visitor, crate, ());
|
||||
}
|
||||
|
||||
return exported_items;
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
|
||||
// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
@ -18,7 +18,6 @@
|
||||
use middle::ty;
|
||||
use middle::typeck;
|
||||
use middle::privacy;
|
||||
use middle::resolve;
|
||||
|
||||
use std::hashmap::HashSet;
|
||||
use syntax::ast;
|
||||
@ -105,8 +104,6 @@ struct ReachableContext {
|
||||
// A worklist of item IDs. Each item ID in this worklist will be inlined
|
||||
// and will be scanned for further references.
|
||||
worklist: @mut ~[ast::NodeId],
|
||||
// Known reexports of modules
|
||||
exp_map2: resolve::ExportMap2,
|
||||
}
|
||||
|
||||
struct MarkSymbolVisitor {
|
||||
@ -173,14 +170,12 @@ impl Visitor<()> for MarkSymbolVisitor {
|
||||
|
||||
impl ReachableContext {
|
||||
// Creates a new reachability computation context.
|
||||
fn new(tcx: ty::ctxt, method_map: typeck::method_map,
|
||||
exp_map2: resolve::ExportMap2) -> ReachableContext {
|
||||
fn new(tcx: ty::ctxt, method_map: typeck::method_map) -> ReachableContext {
|
||||
ReachableContext {
|
||||
tcx: tcx,
|
||||
method_map: method_map,
|
||||
reachable_symbols: @mut HashSet::new(),
|
||||
worklist: @mut ~[],
|
||||
exp_map2: exp_map2,
|
||||
}
|
||||
}
|
||||
|
||||
@ -255,19 +250,6 @@ impl ReachableContext {
|
||||
}
|
||||
}
|
||||
|
||||
fn propagate_mod(&self, id: ast::NodeId) {
|
||||
match self.exp_map2.find(&id) {
|
||||
Some(l) => {
|
||||
for reexport in l.iter() {
|
||||
if reexport.reexport && is_local(reexport.def_id) {
|
||||
self.worklist.push(reexport.def_id.node);
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
|
||||
// Step 2: Mark all symbols that the symbols on the worklist touch.
|
||||
fn propagate(&self) {
|
||||
let mut visitor = self.init_visitor();
|
||||
@ -292,13 +274,6 @@ impl ReachableContext {
|
||||
}
|
||||
}
|
||||
|
||||
// Our recursion into modules involves looking up their
|
||||
// public reexports and the destinations of those
|
||||
// exports. Privacy will put them in the worklist, but
|
||||
// we won't find them in the ast_map, so this is where
|
||||
// we deal with publicly re-exported items instead.
|
||||
ast::item_mod(*) => self.propagate_mod(item.id),
|
||||
|
||||
// Implementations of exported structs/enums need to get
|
||||
// added to the worklist (as all their methods should be
|
||||
// accessible)
|
||||
@ -339,7 +314,7 @@ impl ReachableContext {
|
||||
// inherently and their children are already in the
|
||||
// worklist
|
||||
ast::item_static(*) | ast::item_ty(*) |
|
||||
ast::item_foreign_mod(*) => {}
|
||||
ast::item_mod(*) | ast::item_foreign_mod(*) => {}
|
||||
|
||||
_ => {
|
||||
self.tcx.sess.span_bug(item.span,
|
||||
@ -376,9 +351,7 @@ impl ReachableContext {
|
||||
worklist: {}",
|
||||
desc))
|
||||
}
|
||||
None if search_item == ast::CRATE_NODE_ID => {
|
||||
self.propagate_mod(search_item);
|
||||
}
|
||||
None if search_item == ast::CRATE_NODE_ID => {}
|
||||
None => {
|
||||
self.tcx.sess.bug(format!("found unmapped ID in worklist: \
|
||||
{}",
|
||||
@ -404,10 +377,9 @@ impl ReachableContext {
|
||||
|
||||
pub fn find_reachable(tcx: ty::ctxt,
|
||||
method_map: typeck::method_map,
|
||||
exp_map2: resolve::ExportMap2,
|
||||
exported_items: &privacy::ExportedItems)
|
||||
-> @mut HashSet<ast::NodeId> {
|
||||
let reachable_context = ReachableContext::new(tcx, method_map, exp_map2);
|
||||
let reachable_context = ReachableContext::new(tcx, method_map);
|
||||
|
||||
// Step 1: Seed the worklist with all nodes which were found to be public as
|
||||
// a result of the privacy pass
|
||||
|
@ -13,14 +13,13 @@ use rustc::{driver, middle};
|
||||
use rustc::middle::privacy;
|
||||
|
||||
use syntax::ast;
|
||||
use syntax::ast_util::is_local;
|
||||
use syntax::diagnostic;
|
||||
use syntax::parse;
|
||||
use syntax;
|
||||
|
||||
use std::os;
|
||||
use std::local_data;
|
||||
use std::hashmap::{HashMap,HashSet};
|
||||
use std::hashmap::{HashSet};
|
||||
|
||||
use visit_ast::RustdocVisitor;
|
||||
use clean;
|
||||
@ -34,7 +33,6 @@ pub struct DocContext {
|
||||
|
||||
pub struct CrateAnalysis {
|
||||
exported_items: privacy::ExportedItems,
|
||||
reexports: HashMap<ast::NodeId, ~[ast::NodeId]>,
|
||||
}
|
||||
|
||||
/// Parses, resolves, and typechecks the given crate
|
||||
@ -73,20 +71,12 @@ fn get_ast_and_resolve(cpath: &Path,
|
||||
let mut crate = phase_1_parse_input(sess, cfg.clone(), &input);
|
||||
crate = phase_2_configure_and_expand(sess, cfg, crate);
|
||||
let driver::driver::CrateAnalysis {
|
||||
exported_items, ty_cx, exp_map2, _
|
||||
exported_items, ty_cx, _
|
||||
} = phase_3_run_analysis_passes(sess, &crate);
|
||||
|
||||
let mut reexports = HashMap::new();
|
||||
for (&module, nodes) in exp_map2.iter() {
|
||||
reexports.insert(module, nodes.iter()
|
||||
.filter(|e| e.reexport && is_local(e.def_id))
|
||||
.map(|e| e.def_id.node)
|
||||
.to_owned_vec());
|
||||
}
|
||||
|
||||
debug!("crate: {:?}", crate);
|
||||
return (DocContext { crate: crate, tycx: ty_cx, sess: sess },
|
||||
CrateAnalysis { reexports: reexports, exported_items: exported_items });
|
||||
CrateAnalysis { exported_items: exported_items });
|
||||
}
|
||||
|
||||
pub fn run_core (libs: HashSet<Path>, path: &Path) -> (clean::Crate, CrateAnalysis) {
|
||||
|
@ -16,7 +16,6 @@ use std::local_data;
|
||||
|
||||
use syntax::ast;
|
||||
|
||||
use core;
|
||||
use clean;
|
||||
use clean::Item;
|
||||
use plugins;
|
||||
@ -59,17 +58,7 @@ pub fn strip_private(crate: clean::Crate) -> plugins::PluginResult {
|
||||
let mut retained = HashSet::new();
|
||||
let crate = Cell::new(crate);
|
||||
let exported_items = do local_data::get(super::analysiskey) |analysis| {
|
||||
let analysis = analysis.unwrap();
|
||||
let mut exported_items = analysis.exported_items.clone();
|
||||
{
|
||||
let mut finder = ExportedItemsFinder {
|
||||
exported_items: &mut exported_items,
|
||||
analysis: analysis,
|
||||
};
|
||||
let c = finder.fold_crate(crate.take());
|
||||
crate.put_back(c);
|
||||
}
|
||||
exported_items
|
||||
analysis.unwrap().exported_items.clone()
|
||||
};
|
||||
let mut crate = crate.take();
|
||||
|
||||
@ -90,32 +79,6 @@ pub fn strip_private(crate: clean::Crate) -> plugins::PluginResult {
|
||||
(crate, None)
|
||||
}
|
||||
|
||||
struct ExportedItemsFinder<'self> {
|
||||
exported_items: &'self mut HashSet<ast::NodeId>,
|
||||
analysis: &'self core::CrateAnalysis,
|
||||
}
|
||||
|
||||
impl<'self> fold::DocFolder for ExportedItemsFinder<'self> {
|
||||
fn fold_item(&mut self, i: Item) -> Option<Item> {
|
||||
match i.inner {
|
||||
clean::ModuleItem(*) => {
|
||||
if self.analysis.exported_items.contains(&i.id) {
|
||||
match self.analysis.reexports.find(&i.id) {
|
||||
Some(l) => {
|
||||
for &id in l.iter() {
|
||||
self.exported_items.insert(id);
|
||||
}
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
return self.fold_item_recur(i);
|
||||
}
|
||||
}
|
||||
|
||||
struct Stripper<'self> {
|
||||
retained: &'self mut HashSet<ast::NodeId>,
|
||||
exported_items: &'self HashSet<ast::NodeId>,
|
||||
|
@ -90,6 +90,7 @@ pub trait Visitor<E:Clone> {
|
||||
walk_struct_def(self, s, i, g, n, e)
|
||||
}
|
||||
fn visit_struct_field(&mut self, s:@struct_field, e:E) { walk_struct_field(self, s, e) }
|
||||
fn visit_variant(&mut self, v:&variant, g:&Generics, e:E) { walk_variant(self, v, g, e) }
|
||||
fn visit_opt_lifetime_ref(&mut self,
|
||||
_span: Span,
|
||||
opt_lifetime: &Option<Lifetime>,
|
||||
@ -234,20 +235,27 @@ pub fn walk_enum_def<E:Clone, V:Visitor<E>>(visitor: &mut V,
|
||||
generics: &Generics,
|
||||
env: E) {
|
||||
for variant in enum_definition.variants.iter() {
|
||||
match variant.node.kind {
|
||||
tuple_variant_kind(ref variant_arguments) => {
|
||||
for variant_argument in variant_arguments.iter() {
|
||||
visitor.visit_ty(&variant_argument.ty, env.clone())
|
||||
}
|
||||
}
|
||||
struct_variant_kind(struct_definition) => {
|
||||
visitor.visit_struct_def(struct_definition,
|
||||
variant.node.name,
|
||||
generics,
|
||||
variant.node.id,
|
||||
env.clone())
|
||||
visitor.visit_variant(variant, generics, env.clone());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn walk_variant<E:Clone, V:Visitor<E>>(visitor:&mut V,
|
||||
variant: &variant,
|
||||
generics: &Generics,
|
||||
env: E) {
|
||||
match variant.node.kind {
|
||||
tuple_variant_kind(ref variant_arguments) => {
|
||||
for variant_argument in variant_arguments.iter() {
|
||||
visitor.visit_ty(&variant_argument.ty, env.clone())
|
||||
}
|
||||
}
|
||||
struct_variant_kind(struct_definition) => {
|
||||
visitor.visit_struct_def(struct_definition,
|
||||
variant.node.name,
|
||||
generics,
|
||||
variant.node.id,
|
||||
env.clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,7 @@
|
||||
// When denying at the crate level, be sure to not get random warnings from the
|
||||
// injected intrinsics by the compiler.
|
||||
#[feature(struct_variant)];
|
||||
#[feature(globs)];
|
||||
#[deny(missing_doc)];
|
||||
|
||||
struct Foo {
|
||||
@ -39,16 +40,21 @@ fn foo3() {}
|
||||
#[allow(missing_doc)] pub fn foo4() {}
|
||||
|
||||
/// dox
|
||||
pub trait A {}
|
||||
trait B {}
|
||||
pub trait C {} //~ ERROR: missing documentation
|
||||
#[allow(missing_doc)] pub trait D {}
|
||||
|
||||
trait Bar {
|
||||
pub trait A {
|
||||
/// dox
|
||||
fn foo();
|
||||
fn foo_with_impl() {
|
||||
}
|
||||
/// dox
|
||||
fn foo_with_impl() {}
|
||||
}
|
||||
trait B {
|
||||
fn foo();
|
||||
fn foo_with_impl() {}
|
||||
}
|
||||
pub trait C { //~ ERROR: missing documentation
|
||||
fn foo(); //~ ERROR: missing documentation
|
||||
fn foo_with_impl() {} //~ ERROR: missing documentation
|
||||
}
|
||||
#[allow(missing_doc)] pub trait D {}
|
||||
|
||||
impl Foo {
|
||||
pub fn foo() {} //~ ERROR: missing documentation
|
||||
@ -120,4 +126,26 @@ pub enum PubBaz3 {
|
||||
#[doc(hidden)]
|
||||
pub fn baz() {}
|
||||
|
||||
mod internal_impl {
|
||||
/// dox
|
||||
pub fn documented() {}
|
||||
pub fn undocumented1() {} //~ ERROR: missing documentation
|
||||
pub fn undocumented2() {} //~ ERROR: missing documentation
|
||||
fn undocumented3() {}
|
||||
/// dox
|
||||
pub mod globbed {
|
||||
/// dox
|
||||
pub fn also_documented() {}
|
||||
pub fn also_undocumented1() {} //~ ERROR: missing documentation
|
||||
fn also_undocumented2() {}
|
||||
}
|
||||
}
|
||||
/// dox
|
||||
pub mod public_interface {
|
||||
pub use foo = internal_impl::documented;
|
||||
pub use bar = internal_impl::undocumented1;
|
||||
pub use internal_impl::{documented, undocumented2};
|
||||
pub use internal_impl::globbed::*;
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
Loading…
Reference in New Issue
Block a user