Skip to content

Commit 8adc781

Browse files
authored
Rollup merge of #72771 - jyn514:rustdoc, r=Manishearth
Warn if linking to a private item Closes #72769 r? @GuillaumeGomez
2 parents 7750c3d + 6742382 commit 8adc781

10 files changed

+98
-23
lines changed

src/librustdoc/config.rs

+6-8
Original file line numberDiff line numberDiff line change
@@ -123,10 +123,6 @@ pub struct Options {
123123
///
124124
/// Be aware: This option can come both from the CLI and from crate attributes!
125125
pub default_passes: DefaultPassOption,
126-
/// Document items that have lower than `pub` visibility.
127-
pub document_private: bool,
128-
/// Document items that have `doc(hidden)`.
129-
pub document_hidden: bool,
130126
/// Any passes manually selected by the user.
131127
///
132128
/// Be aware: This option can come both from the CLI and from crate attributes!
@@ -177,8 +173,6 @@ impl fmt::Debug for Options {
177173
.field("test_args", &self.test_args)
178174
.field("persist_doctests", &self.persist_doctests)
179175
.field("default_passes", &self.default_passes)
180-
.field("document_private", &self.document_private)
181-
.field("document_hidden", &self.document_hidden)
182176
.field("manual_passes", &self.manual_passes)
183177
.field("display_warnings", &self.display_warnings)
184178
.field("show_coverage", &self.show_coverage)
@@ -250,6 +244,10 @@ pub struct RenderOptions {
250244
pub generate_search_filter: bool,
251245
/// Option (disabled by default) to generate files used by RLS and some other tools.
252246
pub generate_redirect_pages: bool,
247+
/// Document items that have lower than `pub` visibility.
248+
pub document_private: bool,
249+
/// Document items that have `doc(hidden)`.
250+
pub document_hidden: bool,
253251
}
254252

255253
impl Options {
@@ -567,8 +565,6 @@ impl Options {
567565
should_test,
568566
test_args,
569567
default_passes,
570-
document_private,
571-
document_hidden,
572568
manual_passes,
573569
display_warnings,
574570
show_coverage,
@@ -597,6 +593,8 @@ impl Options {
597593
markdown_playground_url,
598594
generate_search_filter,
599595
generate_redirect_pages,
596+
document_private,
597+
document_hidden,
600598
},
601599
output_format,
602600
})

src/librustdoc/core.rs

+8-7
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ pub struct DocContext<'tcx> {
6262
// FIXME(eddyb) make this a `ty::TraitRef<'tcx>` set.
6363
pub generated_synthetics: RefCell<FxHashSet<(Ty<'tcx>, DefId)>>,
6464
pub auto_traits: Vec<DefId>,
65+
/// The options given to rustdoc that could be relevant to a pass.
66+
pub render_options: RenderOptions,
6567
}
6668

6769
impl<'tcx> DocContext<'tcx> {
@@ -281,8 +283,6 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt
281283
describe_lints,
282284
lint_cap,
283285
mut default_passes,
284-
mut document_private,
285-
document_hidden,
286286
mut manual_passes,
287287
display_warnings,
288288
render_options,
@@ -448,6 +448,7 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt
448448
.cloned()
449449
.filter(|trait_def_id| tcx.trait_is_auto(*trait_def_id))
450450
.collect(),
451+
render_options,
451452
};
452453
debug!("crate: {:?}", tcx.hir().krate());
453454

@@ -524,7 +525,7 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt
524525
}
525526

526527
if attr.is_word() && name == sym::document_private_items {
527-
document_private = true;
528+
ctxt.render_options.document_private = true;
528529
}
529530
}
530531

@@ -544,9 +545,9 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt
544545
for p in passes {
545546
let run = match p.condition {
546547
Always => true,
547-
WhenDocumentPrivate => document_private,
548-
WhenNotDocumentPrivate => !document_private,
549-
WhenNotDocumentHidden => !document_hidden,
548+
WhenDocumentPrivate => ctxt.render_options.document_private,
549+
WhenNotDocumentPrivate => !ctxt.render_options.document_private,
550+
WhenNotDocumentHidden => !ctxt.render_options.document_hidden,
550551
};
551552
if run {
552553
debug!("running pass {}", p.pass.name);
@@ -556,7 +557,7 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt
556557

557558
ctxt.sess().abort_if_errors();
558559

559-
(krate, ctxt.renderinfo.into_inner(), render_options)
560+
(krate, ctxt.renderinfo.into_inner(), ctxt.render_options)
560561
})
561562
})
562563
})

src/librustdoc/html/format.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -468,7 +468,7 @@ impl clean::Path {
468468

469469
pub fn href(did: DefId) -> Option<(String, ItemType, Vec<String>)> {
470470
let cache = cache();
471-
if !did.is_local() && !cache.access_levels.is_public(did) {
471+
if !did.is_local() && !cache.access_levels.is_public(did) && !cache.document_private {
472472
return None;
473473
}
474474

src/librustdoc/html/render.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,7 @@ pub fn run(
469469
static_root_path,
470470
generate_search_filter,
471471
generate_redirect_pages,
472+
document_private,
472473
..
473474
} = options;
474475

@@ -546,7 +547,7 @@ pub fn run(
546547
scx.ensure_dir(&dst)?;
547548
krate = sources::render(&dst, &mut scx, krate)?;
548549
let (new_crate, index, cache) =
549-
Cache::from_krate(renderinfo, &extern_html_root_urls, &dst, krate);
550+
Cache::from_krate(renderinfo, document_private, &extern_html_root_urls, &dst, krate);
550551
krate = new_crate;
551552
let cache = Arc::new(cache);
552553
let mut cx = Context {

src/librustdoc/html/render/cache.rs

+6
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,10 @@ crate struct Cache {
9191
/// The version of the crate being documented, if given from the `--crate-version` flag.
9292
pub crate_version: Option<String>,
9393

94+
/// Whether to document private items.
95+
/// This is stored in `Cache` so it doesn't need to be passed through all rustdoc functions.
96+
pub document_private: bool,
97+
9498
// Private fields only used when initially crawling a crate to build a cache
9599
stack: Vec<String>,
96100
parent_stack: Vec<DefId>,
@@ -126,6 +130,7 @@ crate struct Cache {
126130
impl Cache {
127131
pub fn from_krate(
128132
renderinfo: RenderInfo,
133+
document_private: bool,
129134
extern_html_root_urls: &BTreeMap<String, String>,
130135
dst: &Path,
131136
mut krate: clean::Crate,
@@ -160,6 +165,7 @@ impl Cache {
160165
stripped_mod: false,
161166
access_levels,
162167
crate_version: krate.version.take(),
168+
document_private,
163169
orphan_impl_items: Vec::new(),
164170
orphan_trait_impls: Vec::new(),
165171
traits: krate.external_traits.replace(Default::default()),

src/librustdoc/passes/collect_intra_doc_links.rs

+39-6
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
178178
let result = cx.enter_resolver(|resolver| {
179179
resolver.resolve_str_path_error(DUMMY_SP, &path_str, ns, module_id)
180180
});
181+
debug!("{} resolved to {:?} in namespace {:?}", path_str, result, ns);
181182
let result = match result {
182183
Ok((_, Res::Err)) => Err(ErrorKind::ResolutionFailure),
183184
_ => result.map_err(|_| ErrorKind::ResolutionFailure),
@@ -202,7 +203,13 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
202203
}
203204
return Ok((res, Some(path_str.to_owned())));
204205
}
205-
_ => return Ok((res, extra_fragment.clone())),
206+
other => {
207+
debug!(
208+
"failed to resolve {} in namespace {:?} (got {:?})",
209+
path_str, ns, other
210+
);
211+
return Ok((res, extra_fragment.clone()));
212+
}
206213
};
207214

208215
if value != (ns == ValueNS) {
@@ -555,12 +562,13 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
555562
} else {
556563
(parts[0].to_owned(), None)
557564
};
565+
let resolved_self;
566+
let mut path_str;
558567
let (res, fragment) = {
559568
let mut kind = None;
560-
let mut path_str = if let Some(prefix) =
561-
["struct@", "enum@", "type@", "trait@", "union@"]
562-
.iter()
563-
.find(|p| link.starts_with(**p))
569+
path_str = if let Some(prefix) = ["struct@", "enum@", "type@", "trait@", "union@"]
570+
.iter()
571+
.find(|p| link.starts_with(**p))
564572
{
565573
kind = Some(TypeNS);
566574
link.trim_start_matches(prefix)
@@ -614,7 +622,6 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
614622
let base_node =
615623
if item.is_mod() && item.attrs.inner_docs { None } else { parent_node };
616624

617-
let resolved_self;
618625
// replace `Self` with suitable item's parent name
619626
if path_str.starts_with("Self::") {
620627
if let Some(ref name) = parent_name {
@@ -760,6 +767,32 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
760767
if let Res::PrimTy(_) = res {
761768
item.attrs.links.push((ori_link, None, fragment));
762769
} else {
770+
debug!("intra-doc link to {} resolved to {:?}", path_str, res);
771+
if let Some(local) = res.opt_def_id().and_then(|def_id| def_id.as_local()) {
772+
use rustc_hir::def_id::LOCAL_CRATE;
773+
774+
let hir_id = self.cx.tcx.hir().as_local_hir_id(local);
775+
if !self.cx.tcx.privacy_access_levels(LOCAL_CRATE).is_exported(hir_id)
776+
&& !self.cx.render_options.document_private
777+
{
778+
let item_name = item.name.as_deref().unwrap_or("<unknown>");
779+
let err_msg = format!(
780+
"public documentation for `{}` links to a private item",
781+
item_name
782+
);
783+
build_diagnostic(
784+
cx,
785+
&item,
786+
path_str,
787+
&dox,
788+
link_range,
789+
&err_msg,
790+
"this item is private",
791+
None,
792+
);
793+
continue;
794+
}
795+
}
763796
let id = register_res(cx, res);
764797
item.attrs.links.push((ori_link, Some(id), fragment));
765798
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
warning: `[DontDocMe]` public documentation for `DocMe` links to a private item
2+
--> $DIR/intra-links-private.rs:6:11
3+
|
4+
LL | /// docs [DontDocMe]
5+
| ^^^^^^^^^ this item is private
6+
|
7+
= note: `#[warn(intra_doc_link_resolution_failure)]` on by default
8+
9+
warning: 1 warning emitted
10+
+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// check-pass
2+
// revisions: public private
3+
// [private]compile-flags: --document-private-items
4+
#![cfg_attr(private, deny(intra_doc_resolution_failure))]
5+
6+
/// docs [DontDocMe]
7+
//[public]~^ WARNING `[DontDocMe]` public documentation for `DocMe` links to a private item
8+
// FIXME: for [private] we should also make sure the link was actually generated
9+
pub struct DocMe;
10+
struct DontDocMe;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// ignore-test
2+
// check-pass
3+
4+
/// docs [label][with#anchor#error]
5+
//~^ WARNING has an issue with the link anchor
6+
pub struct S;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
warning: `[with#anchor#error]` has an issue with the link anchor.
2+
--> $DIR/reference-link-has-one-warning.rs:3:18
3+
|
4+
LL | /// docs [label][with#anchor#error]
5+
| ^^^^^^^^^^^^^^^^^ only one `#` is allowed in a link
6+
|
7+
= note: `#[warn(intra_doc_link_resolution_failure)]` on by default
8+
9+
warning: 1 warning emitted
10+

0 commit comments

Comments
 (0)