Generate docs for links to private items when passed --document-private

- Pass around document_private a lot more
- Add tests
  + Add tests for intra-doc links to private items
  + Add ignored tests for warnings in reference links
This commit is contained in:
Joshua Nelson 2020-05-30 11:35:35 -04:00
parent 6f8bec9399
commit 20552c811a
11 changed files with 78 additions and 27 deletions

View File

@ -123,10 +123,6 @@ pub struct Options {
/// ///
/// Be aware: This option can come both from the CLI and from crate attributes! /// Be aware: This option can come both from the CLI and from crate attributes!
pub default_passes: DefaultPassOption, pub default_passes: DefaultPassOption,
/// Document items that have lower than `pub` visibility.
pub document_private: bool,
/// Document items that have `doc(hidden)`.
pub document_hidden: bool,
/// Any passes manually selected by the user. /// Any passes manually selected by the user.
/// ///
/// Be aware: This option can come both from the CLI and from crate attributes! /// Be aware: This option can come both from the CLI and from crate attributes!
@ -177,8 +173,6 @@ impl fmt::Debug for Options {
.field("test_args", &self.test_args) .field("test_args", &self.test_args)
.field("persist_doctests", &self.persist_doctests) .field("persist_doctests", &self.persist_doctests)
.field("default_passes", &self.default_passes) .field("default_passes", &self.default_passes)
.field("document_private", &self.document_private)
.field("document_hidden", &self.document_hidden)
.field("manual_passes", &self.manual_passes) .field("manual_passes", &self.manual_passes)
.field("display_warnings", &self.display_warnings) .field("display_warnings", &self.display_warnings)
.field("show_coverage", &self.show_coverage) .field("show_coverage", &self.show_coverage)
@ -250,6 +244,10 @@ pub struct RenderOptions {
pub generate_search_filter: bool, pub generate_search_filter: bool,
/// Option (disabled by default) to generate files used by RLS and some other tools. /// Option (disabled by default) to generate files used by RLS and some other tools.
pub generate_redirect_pages: bool, pub generate_redirect_pages: bool,
/// Document items that have lower than `pub` visibility.
pub document_private: bool,
/// Document items that have `doc(hidden)`.
pub document_hidden: bool,
} }
impl Options { impl Options {
@ -567,8 +565,6 @@ impl Options {
should_test, should_test,
test_args, test_args,
default_passes, default_passes,
document_private,
document_hidden,
manual_passes, manual_passes,
display_warnings, display_warnings,
show_coverage, show_coverage,
@ -597,6 +593,8 @@ impl Options {
markdown_playground_url, markdown_playground_url,
generate_search_filter, generate_search_filter,
generate_redirect_pages, generate_redirect_pages,
document_private,
document_hidden,
}, },
output_format, output_format,
}) })

View File

@ -62,6 +62,8 @@ pub struct DocContext<'tcx> {
// FIXME(eddyb) make this a `ty::TraitRef<'tcx>` set. // FIXME(eddyb) make this a `ty::TraitRef<'tcx>` set.
pub generated_synthetics: RefCell<FxHashSet<(Ty<'tcx>, DefId)>>, pub generated_synthetics: RefCell<FxHashSet<(Ty<'tcx>, DefId)>>,
pub auto_traits: Vec<DefId>, pub auto_traits: Vec<DefId>,
/// The options given to rustdoc that could be relevant to a pass.
pub render_options: RenderOptions,
} }
impl<'tcx> DocContext<'tcx> { impl<'tcx> DocContext<'tcx> {
@ -281,8 +283,6 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt
describe_lints, describe_lints,
lint_cap, lint_cap,
mut default_passes, mut default_passes,
mut document_private,
document_hidden,
mut manual_passes, mut manual_passes,
display_warnings, display_warnings,
render_options, render_options,
@ -448,6 +448,7 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt
.cloned() .cloned()
.filter(|trait_def_id| tcx.trait_is_auto(*trait_def_id)) .filter(|trait_def_id| tcx.trait_is_auto(*trait_def_id))
.collect(), .collect(),
render_options,
}; };
debug!("crate: {:?}", tcx.hir().krate()); debug!("crate: {:?}", tcx.hir().krate());
@ -524,7 +525,7 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt
} }
if attr.is_word() && name == sym::document_private_items { if attr.is_word() && name == sym::document_private_items {
document_private = true; ctxt.render_options.document_private = true;
} }
} }
@ -544,9 +545,9 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt
for p in passes { for p in passes {
let run = match p.condition { let run = match p.condition {
Always => true, Always => true,
WhenDocumentPrivate => document_private, WhenDocumentPrivate => ctxt.render_options.document_private,
WhenNotDocumentPrivate => !document_private, WhenNotDocumentPrivate => !ctxt.render_options.document_private,
WhenNotDocumentHidden => !document_hidden, WhenNotDocumentHidden => !ctxt.render_options.document_hidden,
}; };
if run { if run {
debug!("running pass {}", p.pass.name); debug!("running pass {}", p.pass.name);
@ -556,7 +557,7 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt
ctxt.sess().abort_if_errors(); ctxt.sess().abort_if_errors();
(krate, ctxt.renderinfo.into_inner(), render_options) (krate, ctxt.renderinfo.into_inner(), ctxt.render_options)
}) })
}) })
}) })

View File

@ -468,7 +468,7 @@ impl clean::Path {
pub fn href(did: DefId) -> Option<(String, ItemType, Vec<String>)> { pub fn href(did: DefId) -> Option<(String, ItemType, Vec<String>)> {
let cache = cache(); let cache = cache();
if !did.is_local() && !cache.access_levels.is_public(did) { if !did.is_local() && !cache.access_levels.is_public(did) && !cache.document_private {
return None; return None;
} }

View File

@ -469,6 +469,7 @@ pub fn run(
static_root_path, static_root_path,
generate_search_filter, generate_search_filter,
generate_redirect_pages, generate_redirect_pages,
document_private,
.. ..
} = options; } = options;
@ -546,7 +547,7 @@ pub fn run(
scx.ensure_dir(&dst)?; scx.ensure_dir(&dst)?;
krate = sources::render(&dst, &mut scx, krate)?; krate = sources::render(&dst, &mut scx, krate)?;
let (new_crate, index, cache) = let (new_crate, index, cache) =
Cache::from_krate(renderinfo, &extern_html_root_urls, &dst, krate); Cache::from_krate(renderinfo, document_private, &extern_html_root_urls, &dst, krate);
krate = new_crate; krate = new_crate;
let cache = Arc::new(cache); let cache = Arc::new(cache);
let mut cx = Context { let mut cx = Context {

View File

@ -91,6 +91,10 @@ crate struct Cache {
/// The version of the crate being documented, if given from the `--crate-version` flag. /// The version of the crate being documented, if given from the `--crate-version` flag.
pub crate_version: Option<String>, pub crate_version: Option<String>,
/// Whether to document private items.
/// This is stored in `Cache` so it doesn't need to be passed through all rustdoc functions.
pub document_private: bool,
// Private fields only used when initially crawling a crate to build a cache // Private fields only used when initially crawling a crate to build a cache
stack: Vec<String>, stack: Vec<String>,
parent_stack: Vec<DefId>, parent_stack: Vec<DefId>,
@ -126,6 +130,7 @@ crate struct Cache {
impl Cache { impl Cache {
pub fn from_krate( pub fn from_krate(
renderinfo: RenderInfo, renderinfo: RenderInfo,
document_private: bool,
extern_html_root_urls: &BTreeMap<String, String>, extern_html_root_urls: &BTreeMap<String, String>,
dst: &Path, dst: &Path,
mut krate: clean::Crate, mut krate: clean::Crate,
@ -160,6 +165,7 @@ impl Cache {
stripped_mod: false, stripped_mod: false,
access_levels, access_levels,
crate_version: krate.version.take(), crate_version: krate.version.take(),
document_private,
orphan_impl_items: Vec::new(), orphan_impl_items: Vec::new(),
orphan_trait_impls: Vec::new(), orphan_trait_impls: Vec::new(),
traits: krate.external_traits.replace(Default::default()), traits: krate.external_traits.replace(Default::default()),

View File

@ -34,7 +34,6 @@ extern crate rustc_metadata;
extern crate rustc_middle; extern crate rustc_middle;
extern crate rustc_mir; extern crate rustc_mir;
extern crate rustc_parse; extern crate rustc_parse;
extern crate rustc_privacy;
extern crate rustc_resolve; extern crate rustc_resolve;
extern crate rustc_session; extern crate rustc_session;
extern crate rustc_span as rustc_span; extern crate rustc_span as rustc_span;

View File

@ -204,7 +204,10 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
return Ok((res, Some(path_str.to_owned()))); return Ok((res, Some(path_str.to_owned())));
} }
other => { other => {
debug!("failed to resolve {} in namespace {:?} (got {:?})", path_str, ns, other); debug!(
"failed to resolve {} in namespace {:?} (got {:?})",
path_str, ns, other
);
debug!("extra_fragment is {:?}", extra_fragment); debug!("extra_fragment is {:?}", extra_fragment);
return Ok((res, extra_fragment.clone())); return Ok((res, extra_fragment.clone()));
} }
@ -564,8 +567,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
let mut path_str; let mut path_str;
let (res, fragment) = { let (res, fragment) = {
let mut kind = None; let mut kind = None;
path_str = if let Some(prefix) = path_str = if let Some(prefix) = ["struct@", "enum@", "type@", "trait@", "union@"]
["struct@", "enum@", "type@", "trait@", "union@"]
.iter() .iter()
.find(|p| link.starts_with(**p)) .find(|p| link.starts_with(**p))
{ {
@ -766,22 +768,30 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
if let Res::PrimTy(_) = res { if let Res::PrimTy(_) = res {
item.attrs.links.push((ori_link, None, fragment)); item.attrs.links.push((ori_link, None, fragment));
} else { } else {
// ~~WRONG: TODO: I think this happens too late and we need to instead put this in `self.resolve`~~ debug!("linked item {} resolved to {:?}", path_str, res);
debug!("item {:?} resolved to {:?}", item, res);
if let Some(local) = res.opt_def_id().and_then(|def_id| def_id.as_local()) { if let Some(local) = res.opt_def_id().and_then(|def_id| def_id.as_local()) {
use rustc_hir::def_id::LOCAL_CRATE;
let hir_id = self.cx.tcx.hir().as_local_hir_id(local); let hir_id = self.cx.tcx.hir().as_local_hir_id(local);
if !self.cx.tcx.privacy_access_levels(rustc_hir::def_id::LOCAL_CRATE).is_exported(hir_id) { if !self.cx.tcx.privacy_access_levels(LOCAL_CRATE).is_exported(hir_id)
&& !self.cx.render_options.document_private
{
let item_name = item.name.as_deref().unwrap_or("<unknown>"); let item_name = item.name.as_deref().unwrap_or("<unknown>");
let err_msg = format!(
"public documentation for `{}` links to a private item",
item_name
);
build_diagnostic( build_diagnostic(
cx, cx,
&item, &item,
path_str, path_str,
&dox, &dox,
link_range, link_range,
&format!("public documentation for `{}` links to a private item", item_name), &err_msg,
"this item is private", "this item is private",
None, None,
); );
continue;
} }
} }
let id = register_res(cx, res); let id = register_res(cx, res);

View File

@ -0,0 +1,10 @@
warning: `[DontDocMe]` public documentation for `DocMe` links to a private item
--> $DIR/intra-links-private.rs:6:11
|
LL | /// docs [DontDocMe]
| ^^^^^^^^^ this item is private
|
= note: `#[warn(intra_doc_link_resolution_failure)]` on by default
warning: 1 warning emitted

View File

@ -0,0 +1,10 @@
// check-pass
// revisions: public private
// [private]compile-flags: --document-private-items
#![cfg_attr(private, deny(intra_doc_resolution_failure))]
/// docs [DontDocMe]
//[public]~^ WARNING `[DontDocMe]` public documentation for `DocMe` links to a private item
// FIXME: for [private] we should also make sure the link was actually generated
pub struct DocMe;
struct DontDocMe;

View File

@ -0,0 +1,6 @@
// ignore-test
// check-pass
/// docs [label][with#anchor#error]
//~^ WARNING has an issue with the link anchor
pub struct S;

View File

@ -0,0 +1,10 @@
warning: `[with#anchor#error]` has an issue with the link anchor.
--> $DIR/reference-link-has-one-warning.rs:3:18
|
LL | /// docs [label][with#anchor#error]
| ^^^^^^^^^^^^^^^^^ only one `#` is allowed in a link
|
= note: `#[warn(intra_doc_link_resolution_failure)]` on by default
warning: 1 warning emitted