mirror of https://github.com/rust-lang/rust.git
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 =
|
let reachable_map =
|
||||||
time(time_passes, "reachability checking", (), |_|
|
time(time_passes, "reachability checking", (), |_|
|
||||||
reachable::find_reachable(ty_cx, method_map, exp_map2,
|
reachable::find_reachable(ty_cx, method_map, &exported_items));
|
||||||
&exported_items));
|
|
||||||
|
|
||||||
time(time_passes, "lint checking", (), |_|
|
time(time_passes, "lint checking", (), |_|
|
||||||
lint::check_crate(ty_cx, crate));
|
lint::check_crate(ty_cx, &exported_items, crate));
|
||||||
|
|
||||||
CrateAnalysis {
|
CrateAnalysis {
|
||||||
exp_map2: exp_map2,
|
exp_map2: exp_map2,
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
//! Context itself, span_lint should be used instead of add_lint.
|
//! Context itself, span_lint should be used instead of add_lint.
|
||||||
|
|
||||||
use driver::session;
|
use driver::session;
|
||||||
|
use middle::privacy;
|
||||||
use middle::trans::adt; // for `adt::is_ffi_safe`
|
use middle::trans::adt; // for `adt::is_ffi_safe`
|
||||||
use middle::ty;
|
use middle::ty;
|
||||||
use middle::pat_util;
|
use middle::pat_util;
|
||||||
|
@ -317,13 +318,20 @@ pub fn get_lint_dict() -> LintDict {
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Context {
|
struct Context<'self> {
|
||||||
// All known lint modes (string versions)
|
// All known lint modes (string versions)
|
||||||
dict: @LintDict,
|
dict: @LintDict,
|
||||||
// Current levels of each lint warning
|
// Current levels of each lint warning
|
||||||
cur: SmallIntMap<(level, LintSource)>,
|
cur: SmallIntMap<(level, LintSource)>,
|
||||||
// context we're checking in (used to access fields like sess)
|
// context we're checking in (used to access fields like sess)
|
||||||
tcx: ty::ctxt,
|
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
|
// When recursing into an attributed node of the ast which modifies lint
|
||||||
// levels, this stack keeps track of the previous lint levels of whatever
|
// levels, this stack keeps track of the previous lint levels of whatever
|
||||||
|
@ -331,7 +339,7 @@ struct Context {
|
||||||
lint_stack: ~[(lint, level, LintSource)],
|
lint_stack: ~[(lint, level, LintSource)],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Context {
|
impl<'self> Context<'self> {
|
||||||
fn get_level(&self, lint: lint) -> level {
|
fn get_level(&self, lint: lint) -> level {
|
||||||
match self.cur.find(&(lint as uint)) {
|
match self.cur.find(&(lint as uint)) {
|
||||||
Some(&(lvl, _)) => lvl,
|
Some(&(lvl, _)) => lvl,
|
||||||
|
@ -440,9 +448,16 @@ impl Context {
|
||||||
true
|
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);
|
f(self);
|
||||||
|
|
||||||
// rollback
|
// rollback
|
||||||
|
self.is_doc_hidden = old_is_doc_hidden;
|
||||||
do pushed.times {
|
do pushed.times {
|
||||||
let (lint, lvl, src) = self.lint_stack.pop();
|
let (lint, lvl, src) = self.lint_stack.pop();
|
||||||
self.set_level(lint, lvl, src);
|
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 {
|
// `#[doc(hidden)]` disables missing_doc check.
|
||||||
fn check_attrs(&self, attrs: &[ast::Attribute], id: ast::NodeId,
|
if cx.is_doc_hidden { return }
|
||||||
sp: Span, msg: ~str) {
|
|
||||||
if !attrs.iter().any(|a| a.node.is_sugared_doc) {
|
|
||||||
self.sess.add_lint(missing_doc, id, sp, msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_struct(&self, sdef: &ast::struct_def) {
|
// Only check publicly-visible items, using the result from the
|
||||||
for field in sdef.fields.iter() {
|
// privacy pass.
|
||||||
match field.node.kind {
|
if !cx.exported_items.contains(&id) { return }
|
||||||
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(*) => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn doc_hidden(&self, attrs: &[ast::Attribute]) -> bool {
|
if !attrs.iter().any(|a| a.node.is_sugared_doc) {
|
||||||
do attrs.iter().any |attr| {
|
cx.span_lint(missing_doc, sp,
|
||||||
"doc" == attr.name() &&
|
format!("missing documentation for {}", desc));
|
||||||
match attr.meta_item_list() {
|
|
||||||
Some(l) => attr::contains_name(l, "hidden"),
|
|
||||||
None => false // not of the form #[doc(...)]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Visitor<()> for MissingDocLintVisitor {
|
fn check_missing_doc_item(cx: &mut Context, it: &ast::item) { // XXX doesn't need to be mut
|
||||||
fn visit_ty_method(&mut self, m:&ast::TypeMethod, _: ()) {
|
let desc = match it.node {
|
||||||
if self.doc_hidden(m.attrs) { return }
|
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
|
fn check_missing_doc_method(cx: &Context, m: &ast::method) {
|
||||||
// trait (no visibility)
|
let did = ast::DefId {
|
||||||
self.check_attrs(m.attrs, m.id, m.span,
|
crate: ast::LOCAL_CRATE,
|
||||||
~"missing documentation for a method");
|
node: m.id
|
||||||
visit::walk_ty_method(self, m, ());
|
};
|
||||||
}
|
match cx.tcx.methods.find(&did) {
|
||||||
|
None => cx.tcx.sess.span_bug(m.span, "missing method descriptor?!"),
|
||||||
fn visit_fn(&mut self, fk: &visit::fn_kind, d: &ast::fn_decl,
|
Some(md) => {
|
||||||
b: &ast::Block, sp: Span, id: ast::NodeId, _: ()) {
|
match md.container {
|
||||||
// Only warn about explicitly public methods.
|
// Always check default methods defined on traits.
|
||||||
match *fk {
|
ty::TraitContainer(*) => {}
|
||||||
visit::fk_method(_, _, m) => {
|
// For methods defined on impls, it depends on whether
|
||||||
if self.doc_hidden(m.attrs) {
|
// it is an implementation for a trait or is a plain
|
||||||
return;
|
// impl.
|
||||||
}
|
ty::ImplContainer(cid) => {
|
||||||
// If we're in a trait implementation, no need to duplicate
|
match ty::impl_trait_ref(cx.tcx, cid) {
|
||||||
// documentation
|
Some(*) => return, // impl for trait: don't doc
|
||||||
if m.vis == ast::public {
|
None => {} // plain impl: doc according to privacy
|
||||||
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);
|
|
||||||
}
|
|
||||||
_ => ()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
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
|
/// 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);
|
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, _: ()) {
|
fn visit_item(&mut self, it: @ast::item, _: ()) {
|
||||||
do self.with_lint_attrs(it.attrs) |cx| {
|
do self.with_lint_attrs(it.attrs) |cx| {
|
||||||
check_item_ctypes(cx, it);
|
check_item_ctypes(cx, it);
|
||||||
check_item_non_camel_case_types(cx, it);
|
check_item_non_camel_case_types(cx, it);
|
||||||
check_item_non_uppercase_statics(cx, it);
|
check_item_non_uppercase_statics(cx, it);
|
||||||
check_heap_item(cx, it);
|
check_heap_item(cx, it);
|
||||||
|
check_missing_doc_item(cx, it);
|
||||||
|
|
||||||
do cx.visit_ids |v| {
|
do cx.visit_ids |v| {
|
||||||
v.visit_item(it, ());
|
v.visit_item(it, ());
|
||||||
|
@ -1111,6 +1085,8 @@ impl Visitor<()> for Context {
|
||||||
match *fk {
|
match *fk {
|
||||||
visit::fk_method(_, _, m) => {
|
visit::fk_method(_, _, m) => {
|
||||||
do self.with_lint_attrs(m.attrs) |cx| {
|
do self.with_lint_attrs(m.attrs) |cx| {
|
||||||
|
check_missing_doc_method(cx, m);
|
||||||
|
|
||||||
do cx.visit_ids |v| {
|
do cx.visit_ids |v| {
|
||||||
v.visit_fn(fk, decl, body, span, id, ());
|
v.visit_fn(fk, decl, body, span, id, ());
|
||||||
}
|
}
|
||||||
|
@ -1120,9 +1096,45 @@ impl Visitor<()> for Context {
|
||||||
_ => recurse(self),
|
_ => 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) {
|
fn visit_id(&self, id: ast::NodeId) {
|
||||||
match self.tcx.sess.lints.pop(&id) {
|
match self.tcx.sess.lints.pop(&id) {
|
||||||
None => {}
|
None => {}
|
||||||
|
@ -1135,17 +1147,16 @@ impl ast_util::IdVisitingOperation for Context {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_crate(tcx: ty::ctxt, crate: &ast::Crate) {
|
pub fn check_crate(tcx: ty::ctxt,
|
||||||
// This visitor contains more state than is currently maintained in Context,
|
exported_items: &privacy::ExportedItems,
|
||||||
// and there's no reason for the Context to keep track of this information
|
crate: &ast::Crate) {
|
||||||
// really
|
|
||||||
let mut dox = MissingDocLintVisitor(tcx);
|
|
||||||
visit::walk_crate(&mut dox, crate, ());
|
|
||||||
|
|
||||||
let mut cx = Context {
|
let mut cx = Context {
|
||||||
dict: @get_lint_dict(),
|
dict: @get_lint_dict(),
|
||||||
cur: SmallIntMap::new(),
|
cur: SmallIntMap::new(),
|
||||||
tcx: tcx,
|
tcx: tcx,
|
||||||
|
exported_items: exported_items,
|
||||||
|
cur_struct_def_id: -1,
|
||||||
|
is_doc_hidden: false,
|
||||||
lint_stack: ~[],
|
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
|
// file at the top-level directory of this distribution and at
|
||||||
// http://rust-lang.org/COPYRIGHT.
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
//
|
//
|
||||||
|
@ -31,8 +31,7 @@ use syntax::visit::Visitor;
|
||||||
|
|
||||||
type Context<'self> = (&'self method_map, &'self resolve::ExportMap2);
|
type Context<'self> = (&'self method_map, &'self resolve::ExportMap2);
|
||||||
|
|
||||||
// A set of all nodes in the ast which can be considered "publicly exported" in
|
/// A set of AST nodes exported by the crate.
|
||||||
// the sense that they are accessible from anywhere in any hierarchy.
|
|
||||||
pub type ExportedItems = HashSet<ast::NodeId>;
|
pub type ExportedItems = HashSet<ast::NodeId>;
|
||||||
|
|
||||||
// This visitor is used to determine the parent of all nodes in question when it
|
// 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,
|
// This visitor is used to determine which items of the ast are embargoed,
|
||||||
// otherwise known as not exported.
|
// otherwise known as not exported.
|
||||||
struct EmbargoVisitor<'self> {
|
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,
|
exported_items: &'self mut ExportedItems,
|
||||||
exp_map2: &'self resolve::ExportMap2,
|
exp_map2: &'self resolve::ExportMap2,
|
||||||
path_all_public: bool,
|
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> {
|
impl<'self> Visitor<()> for EmbargoVisitor<'self> {
|
||||||
fn visit_item(&mut self, item: @ast::item, _: ()) {
|
fn visit_item(&mut self, item: @ast::item, _: ()) {
|
||||||
let orig_all_pub = self.path_all_public;
|
let orig_all_pub = self.path_all_public;
|
||||||
|
@ -162,7 +178,7 @@ impl<'self> Visitor<()> for EmbargoVisitor<'self> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.path_all_public {
|
if self.path_all_public {
|
||||||
self.exported_items.insert(item.id);
|
self.add_path_all_public_item(item.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
match item.node {
|
match item.node {
|
||||||
|
@ -171,7 +187,7 @@ impl<'self> Visitor<()> for EmbargoVisitor<'self> {
|
||||||
ast::item_enum(ref def, _) if self.path_all_public => {
|
ast::item_enum(ref def, _) if self.path_all_public => {
|
||||||
for variant in def.variants.iter() {
|
for variant in def.variants.iter() {
|
||||||
if variant.node.vis != ast::private {
|
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,
|
_ => true,
|
||||||
} && method.vis == ast::public;
|
} && method.vis == ast::public;
|
||||||
if 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) => {
|
ast::item_impl(_, Some(*), _, ref methods) => {
|
||||||
for method in methods.iter() {
|
for method in methods.iter() {
|
||||||
debug!("exporting: {}", method.id);
|
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 {
|
match *method {
|
||||||
ast::provided(ref m) => {
|
ast::provided(ref m) => {
|
||||||
debug!("provided {}", m.id);
|
debug!("provided {}", m.id);
|
||||||
self.exported_items.insert(m.id);
|
self.add_path_all_public_item(m.id);
|
||||||
}
|
}
|
||||||
ast::required(ref m) => {
|
ast::required(ref m) => {
|
||||||
debug!("required {}", m.id);
|
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
|
// Struct constructors are public if the struct is all public.
|
||||||
// public
|
|
||||||
ast::item_struct(ref def, _) if self.path_all_public => {
|
ast::item_struct(ref def, _) if self.path_all_public => {
|
||||||
match def.ctor_id {
|
match def.ctor_id {
|
||||||
Some(id) => { self.exported_items.insert(id); }
|
Some(id) => { self.add_path_all_public_item(id); }
|
||||||
None => {}
|
None => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -233,17 +248,36 @@ impl<'self> Visitor<()> for EmbargoVisitor<'self> {
|
||||||
|
|
||||||
fn visit_foreign_item(&mut self, a: @ast::foreign_item, _: ()) {
|
fn visit_foreign_item(&mut self, a: @ast::foreign_item, _: ()) {
|
||||||
if self.path_all_public && a.vis == ast::public {
|
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> {
|
struct PrivacyVisitor<'self> {
|
||||||
tcx: ty::ctxt,
|
tcx: ty::ctxt,
|
||||||
curitem: ast::NodeId,
|
curitem: ast::NodeId,
|
||||||
|
|
||||||
// Results of previous analyses necessary for privacy checking.
|
// See comments on the same field in `EmbargoVisitor`.
|
||||||
exported_items: &'self ExportedItems,
|
path_all_public_items: &'self ExportedItems,
|
||||||
method_map: &'self method_map,
|
method_map: &'self method_map,
|
||||||
parents: &'self HashMap<ast::NodeId, ast::NodeId>,
|
parents: &'self HashMap<ast::NodeId, ast::NodeId>,
|
||||||
external_exports: resolve::ExternalExports,
|
external_exports: resolve::ExternalExports,
|
||||||
|
@ -303,7 +337,7 @@ impl<'self> PrivacyVisitor<'self> {
|
||||||
ExternallyDenied
|
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));
|
debug!("privacy - exported item {}", self.nodestr(did.node));
|
||||||
return Allowable;
|
return Allowable;
|
||||||
}
|
}
|
||||||
|
@ -842,6 +876,7 @@ pub fn check_crate(tcx: ty::ctxt,
|
||||||
last_private_map: resolve::LastPrivateMap,
|
last_private_map: resolve::LastPrivateMap,
|
||||||
crate: &ast::Crate) -> ExportedItems {
|
crate: &ast::Crate) -> ExportedItems {
|
||||||
let mut parents = HashMap::new();
|
let mut parents = HashMap::new();
|
||||||
|
let mut path_all_public_items = HashSet::new();
|
||||||
let mut exported_items = HashSet::new();
|
let mut exported_items = HashSet::new();
|
||||||
|
|
||||||
// First, figure out who everyone's parent is
|
// 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
|
// 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 {
|
let mut visitor = EmbargoVisitor {
|
||||||
|
tcx: tcx,
|
||||||
|
path_all_public_items: &mut path_all_public_items,
|
||||||
exported_items: &mut exported_items,
|
exported_items: &mut exported_items,
|
||||||
exp_map2: exp_map2,
|
exp_map2: exp_map2,
|
||||||
path_all_public: true, // start out as public
|
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, ());
|
visit::walk_crate(&mut visitor, crate, ());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -871,7 +908,7 @@ pub fn check_crate(tcx: ty::ctxt,
|
||||||
let mut visitor = PrivacyVisitor {
|
let mut visitor = PrivacyVisitor {
|
||||||
curitem: ast::DUMMY_NODE_ID,
|
curitem: ast::DUMMY_NODE_ID,
|
||||||
tcx: tcx,
|
tcx: tcx,
|
||||||
exported_items: &exported_items,
|
path_all_public_items: &path_all_public_items,
|
||||||
parents: &parents,
|
parents: &parents,
|
||||||
method_map: method_map,
|
method_map: method_map,
|
||||||
external_exports: external_exports,
|
external_exports: external_exports,
|
||||||
|
@ -879,5 +916,6 @@ pub fn check_crate(tcx: ty::ctxt,
|
||||||
};
|
};
|
||||||
visit::walk_crate(&mut visitor, crate, ());
|
visit::walk_crate(&mut visitor, crate, ());
|
||||||
}
|
}
|
||||||
|
|
||||||
return exported_items;
|
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
|
// file at the top-level directory of this distribution and at
|
||||||
// http://rust-lang.org/COPYRIGHT.
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
//
|
//
|
||||||
|
@ -18,7 +18,6 @@
|
||||||
use middle::ty;
|
use middle::ty;
|
||||||
use middle::typeck;
|
use middle::typeck;
|
||||||
use middle::privacy;
|
use middle::privacy;
|
||||||
use middle::resolve;
|
|
||||||
|
|
||||||
use std::hashmap::HashSet;
|
use std::hashmap::HashSet;
|
||||||
use syntax::ast;
|
use syntax::ast;
|
||||||
|
@ -105,8 +104,6 @@ struct ReachableContext {
|
||||||
// A worklist of item IDs. Each item ID in this worklist will be inlined
|
// A worklist of item IDs. Each item ID in this worklist will be inlined
|
||||||
// and will be scanned for further references.
|
// and will be scanned for further references.
|
||||||
worklist: @mut ~[ast::NodeId],
|
worklist: @mut ~[ast::NodeId],
|
||||||
// Known reexports of modules
|
|
||||||
exp_map2: resolve::ExportMap2,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct MarkSymbolVisitor {
|
struct MarkSymbolVisitor {
|
||||||
|
@ -173,14 +170,12 @@ impl Visitor<()> for MarkSymbolVisitor {
|
||||||
|
|
||||||
impl ReachableContext {
|
impl ReachableContext {
|
||||||
// Creates a new reachability computation context.
|
// Creates a new reachability computation context.
|
||||||
fn new(tcx: ty::ctxt, method_map: typeck::method_map,
|
fn new(tcx: ty::ctxt, method_map: typeck::method_map) -> ReachableContext {
|
||||||
exp_map2: resolve::ExportMap2) -> ReachableContext {
|
|
||||||
ReachableContext {
|
ReachableContext {
|
||||||
tcx: tcx,
|
tcx: tcx,
|
||||||
method_map: method_map,
|
method_map: method_map,
|
||||||
reachable_symbols: @mut HashSet::new(),
|
reachable_symbols: @mut HashSet::new(),
|
||||||
worklist: @mut ~[],
|
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.
|
// Step 2: Mark all symbols that the symbols on the worklist touch.
|
||||||
fn propagate(&self) {
|
fn propagate(&self) {
|
||||||
let mut visitor = self.init_visitor();
|
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
|
// Implementations of exported structs/enums need to get
|
||||||
// added to the worklist (as all their methods should be
|
// added to the worklist (as all their methods should be
|
||||||
// accessible)
|
// accessible)
|
||||||
|
@ -339,7 +314,7 @@ impl ReachableContext {
|
||||||
// inherently and their children are already in the
|
// inherently and their children are already in the
|
||||||
// worklist
|
// worklist
|
||||||
ast::item_static(*) | ast::item_ty(*) |
|
ast::item_static(*) | ast::item_ty(*) |
|
||||||
ast::item_foreign_mod(*) => {}
|
ast::item_mod(*) | ast::item_foreign_mod(*) => {}
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
self.tcx.sess.span_bug(item.span,
|
self.tcx.sess.span_bug(item.span,
|
||||||
|
@ -376,9 +351,7 @@ impl ReachableContext {
|
||||||
worklist: {}",
|
worklist: {}",
|
||||||
desc))
|
desc))
|
||||||
}
|
}
|
||||||
None if search_item == ast::CRATE_NODE_ID => {
|
None if search_item == ast::CRATE_NODE_ID => {}
|
||||||
self.propagate_mod(search_item);
|
|
||||||
}
|
|
||||||
None => {
|
None => {
|
||||||
self.tcx.sess.bug(format!("found unmapped ID in worklist: \
|
self.tcx.sess.bug(format!("found unmapped ID in worklist: \
|
||||||
{}",
|
{}",
|
||||||
|
@ -404,10 +377,9 @@ impl ReachableContext {
|
||||||
|
|
||||||
pub fn find_reachable(tcx: ty::ctxt,
|
pub fn find_reachable(tcx: ty::ctxt,
|
||||||
method_map: typeck::method_map,
|
method_map: typeck::method_map,
|
||||||
exp_map2: resolve::ExportMap2,
|
|
||||||
exported_items: &privacy::ExportedItems)
|
exported_items: &privacy::ExportedItems)
|
||||||
-> @mut HashSet<ast::NodeId> {
|
-> @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
|
// Step 1: Seed the worklist with all nodes which were found to be public as
|
||||||
// a result of the privacy pass
|
// a result of the privacy pass
|
||||||
|
|
|
@ -13,14 +13,13 @@ use rustc::{driver, middle};
|
||||||
use rustc::middle::privacy;
|
use rustc::middle::privacy;
|
||||||
|
|
||||||
use syntax::ast;
|
use syntax::ast;
|
||||||
use syntax::ast_util::is_local;
|
|
||||||
use syntax::diagnostic;
|
use syntax::diagnostic;
|
||||||
use syntax::parse;
|
use syntax::parse;
|
||||||
use syntax;
|
use syntax;
|
||||||
|
|
||||||
use std::os;
|
use std::os;
|
||||||
use std::local_data;
|
use std::local_data;
|
||||||
use std::hashmap::{HashMap,HashSet};
|
use std::hashmap::{HashSet};
|
||||||
|
|
||||||
use visit_ast::RustdocVisitor;
|
use visit_ast::RustdocVisitor;
|
||||||
use clean;
|
use clean;
|
||||||
|
@ -34,7 +33,6 @@ pub struct DocContext {
|
||||||
|
|
||||||
pub struct CrateAnalysis {
|
pub struct CrateAnalysis {
|
||||||
exported_items: privacy::ExportedItems,
|
exported_items: privacy::ExportedItems,
|
||||||
reexports: HashMap<ast::NodeId, ~[ast::NodeId]>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses, resolves, and typechecks the given crate
|
/// 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);
|
let mut crate = phase_1_parse_input(sess, cfg.clone(), &input);
|
||||||
crate = phase_2_configure_and_expand(sess, cfg, crate);
|
crate = phase_2_configure_and_expand(sess, cfg, crate);
|
||||||
let driver::driver::CrateAnalysis {
|
let driver::driver::CrateAnalysis {
|
||||||
exported_items, ty_cx, exp_map2, _
|
exported_items, ty_cx, _
|
||||||
} = phase_3_run_analysis_passes(sess, &crate);
|
} = 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);
|
debug!("crate: {:?}", crate);
|
||||||
return (DocContext { crate: crate, tycx: ty_cx, sess: sess },
|
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) {
|
pub fn run_core (libs: HashSet<Path>, path: &Path) -> (clean::Crate, CrateAnalysis) {
|
||||||
|
|
|
@ -16,7 +16,6 @@ use std::local_data;
|
||||||
|
|
||||||
use syntax::ast;
|
use syntax::ast;
|
||||||
|
|
||||||
use core;
|
|
||||||
use clean;
|
use clean;
|
||||||
use clean::Item;
|
use clean::Item;
|
||||||
use plugins;
|
use plugins;
|
||||||
|
@ -59,17 +58,7 @@ pub fn strip_private(crate: clean::Crate) -> plugins::PluginResult {
|
||||||
let mut retained = HashSet::new();
|
let mut retained = HashSet::new();
|
||||||
let crate = Cell::new(crate);
|
let crate = Cell::new(crate);
|
||||||
let exported_items = do local_data::get(super::analysiskey) |analysis| {
|
let exported_items = do local_data::get(super::analysiskey) |analysis| {
|
||||||
let analysis = analysis.unwrap();
|
analysis.unwrap().exported_items.clone()
|
||||||
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
|
|
||||||
};
|
};
|
||||||
let mut crate = crate.take();
|
let mut crate = crate.take();
|
||||||
|
|
||||||
|
@ -90,32 +79,6 @@ pub fn strip_private(crate: clean::Crate) -> plugins::PluginResult {
|
||||||
(crate, None)
|
(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> {
|
struct Stripper<'self> {
|
||||||
retained: &'self mut HashSet<ast::NodeId>,
|
retained: &'self mut HashSet<ast::NodeId>,
|
||||||
exported_items: &'self 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)
|
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_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,
|
fn visit_opt_lifetime_ref(&mut self,
|
||||||
_span: Span,
|
_span: Span,
|
||||||
opt_lifetime: &Option<Lifetime>,
|
opt_lifetime: &Option<Lifetime>,
|
||||||
|
@ -234,20 +235,27 @@ pub fn walk_enum_def<E:Clone, V:Visitor<E>>(visitor: &mut V,
|
||||||
generics: &Generics,
|
generics: &Generics,
|
||||||
env: E) {
|
env: E) {
|
||||||
for variant in enum_definition.variants.iter() {
|
for variant in enum_definition.variants.iter() {
|
||||||
match variant.node.kind {
|
visitor.visit_variant(variant, generics, env.clone());
|
||||||
tuple_variant_kind(ref variant_arguments) => {
|
}
|
||||||
for variant_argument in variant_arguments.iter() {
|
}
|
||||||
visitor.visit_ty(&variant_argument.ty, env.clone())
|
|
||||||
}
|
pub fn walk_variant<E:Clone, V:Visitor<E>>(visitor:&mut V,
|
||||||
}
|
variant: &variant,
|
||||||
struct_variant_kind(struct_definition) => {
|
generics: &Generics,
|
||||||
visitor.visit_struct_def(struct_definition,
|
env: E) {
|
||||||
variant.node.name,
|
match variant.node.kind {
|
||||||
generics,
|
tuple_variant_kind(ref variant_arguments) => {
|
||||||
variant.node.id,
|
for variant_argument in variant_arguments.iter() {
|
||||||
env.clone())
|
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
|
// When denying at the crate level, be sure to not get random warnings from the
|
||||||
// injected intrinsics by the compiler.
|
// injected intrinsics by the compiler.
|
||||||
#[feature(struct_variant)];
|
#[feature(struct_variant)];
|
||||||
|
#[feature(globs)];
|
||||||
#[deny(missing_doc)];
|
#[deny(missing_doc)];
|
||||||
|
|
||||||
struct Foo {
|
struct Foo {
|
||||||
|
@ -39,16 +40,21 @@ fn foo3() {}
|
||||||
#[allow(missing_doc)] pub fn foo4() {}
|
#[allow(missing_doc)] pub fn foo4() {}
|
||||||
|
|
||||||
/// dox
|
/// dox
|
||||||
pub trait A {}
|
pub trait A {
|
||||||
trait B {}
|
/// dox
|
||||||
pub trait C {} //~ ERROR: missing documentation
|
|
||||||
#[allow(missing_doc)] pub trait D {}
|
|
||||||
|
|
||||||
trait Bar {
|
|
||||||
fn foo();
|
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 {
|
impl Foo {
|
||||||
pub fn foo() {} //~ ERROR: missing documentation
|
pub fn foo() {} //~ ERROR: missing documentation
|
||||||
|
@ -120,4 +126,26 @@ pub enum PubBaz3 {
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub fn baz() {}
|
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() {}
|
fn main() {}
|
||||||
|
|
Loading…
Reference in New Issue