diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 493fdc6fb1b33..92a7bedb60d04 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -10,6 +10,7 @@ use std::cmp::Ordering; use std::fmt::{self, Display, Write}; use std::iter::{self, once}; +use std::ops::ControlFlow; use std::slice; use itertools::{Either, Itertools}; @@ -21,7 +22,7 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::{ConstStability, StabilityLevel, StableSince}; use rustc_metadata::creader::{CStore, LoadedMacro}; -use rustc_middle::ty::{self, TyCtxt, TypingMode}; +use rustc_middle::ty::{self, ImplSubject, TyCtxt, TypingMode}; use rustc_span::symbol::kw; use rustc_span::{Symbol, sym}; use tracing::{debug, trace}; @@ -483,22 +484,80 @@ fn generate_item_def_id_path( /// Checks if the given defid refers to an item that is unnamable, such as one defined in a const block. fn is_unnamable(tcx: TyCtxt<'_>, did: DefId) -> bool { + traverse_parent(tcx, did, false, true, &|_| ControlFlow::Continue(())) +} + +pub(crate) fn traverse_parent( + tcx: TyCtxt<'_>, + did: DefId, + if_noparent: bool, + if_unnamable: bool, + callback: &dyn Fn(DefId) -> ControlFlow, +) -> bool { let mut cur_did = did; while let Some(parent) = tcx.opt_parent(cur_did) { + if let ControlFlow::Break(x) = callback(cur_did) { + return x; + } match tcx.def_kind(parent) { // items defined in these can be linked to, as long as they are visible - DefKind::Mod | DefKind::ForeignMod => cur_did = parent, - // items in impls can be linked to, - // as long as we can link to the item the impl is on. - // since associated traits are not a thing, - // it should not be possible to refer to an impl item if - // the base type is not namable. - DefKind::Impl { .. } => return false, + DefKind::Mod | DefKind::ForeignMod | DefKind::Trait => cur_did = parent, + // this serves both to future-proof is_unnamable for associated trait aliases, + // and traverses upwards to see if the trait or type is doc(hidden) + DefKind::Impl { .. } => { + match tcx.impl_subject(parent).as_ref().skip_binder() { + ImplSubject::Trait(trait_ref) => { + // first, check if the trait fufills the criteria + if traverse_parent( + tcx, + trait_ref.def_id, + if_noparent, + if_unnamable, + callback, + ) { + return true; + } + + // for trait impls on local types, we also check if the type matches the criteria + if let Some(hir::Node::Item(hir::Item { + kind: hir::ItemKind::Impl(hir::Impl { self_ty, .. }), + .. + })) = tcx.hir_get_if_local(parent) + && let hir::Ty { + kind: + hir::TyKind::Path(hir::QPath::Resolved( + _, + hir::Path { + res: hir::def::Res::Def(_, self_ty_def_id), + .. + }, + )), + .. + } = self_ty.peel_refs() + /*&& let Some(local_def_id) = parent.as_local() + && let Some(self_ty_def_id) = tcx.typeck(local_def_id).type_dependent_def_id(self_ty.hir_id)*/ + { + cur_did = *self_ty_def_id; + continue; + } + return if_noparent; + } + ImplSubject::Inherent(ty) => { + if let Some(def_id) = + rustc_middle::ty::print::characteristic_def_id_of_type(ty.peel_refs()) + { + cur_did = def_id; + } else { + return if_noparent; + } + } + } + } // everything else does not have docs generated for it - _ => return true, + _ => return if_unnamable, } } - return false; + return if_noparent; } fn to_module_fqp(shortty: ItemType, fqp: &[Symbol]) -> &[Symbol] { diff --git a/src/librustdoc/lint.rs b/src/librustdoc/lint.rs index b09ea05688595..a2e70c725d811 100644 --- a/src/librustdoc/lint.rs +++ b/src/librustdoc/lint.rs @@ -97,6 +97,17 @@ declare_rustdoc_lint! { "linking from a public item to a private one" } +declare_rustdoc_lint! { + /// This is a subset of `broken_intra_doc_links` that warns when linking from + /// a non-hidden item to a hidden one. This is a `rustdoc` only lint, see the + /// documentation in the [rustdoc book]. + /// + /// [rustdoc book]: ../../../rustdoc/lints.html#hidden_intra_doc_links + HIDDEN_INTRA_DOC_LINKS, + Warn, + "linking from a non-hidden item to a hidden one" +} + declare_rustdoc_lint! { /// The `invalid_codeblock_attributes` lint detects code block attributes /// in documentation examples that have potentially mis-typed values. This @@ -199,6 +210,7 @@ declare_rustdoc_lint! { pub(crate) static RUSTDOC_LINTS: Lazy> = Lazy::new(|| { vec![ BROKEN_INTRA_DOC_LINKS, + HIDDEN_INTRA_DOC_LINKS, PRIVATE_INTRA_DOC_LINKS, MISSING_DOC_CODE_EXAMPLES, PRIVATE_DOC_TESTS, diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 5a9aa2a94c8b4..8371fb39011fd 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -5,7 +5,7 @@ use std::borrow::Cow; use std::fmt::Display; use std::mem; -use std::ops::Range; +use std::ops::{ControlFlow, Range}; use pulldown_cmark::LinkType; use rustc_ast::util::comments::may_have_doc_links; @@ -34,7 +34,7 @@ use crate::clean::utils::find_nearest_parent_module; use crate::clean::{self, Crate, Item, ItemId, ItemLink, PrimitiveType}; use crate::core::DocContext; use crate::html::markdown::{MarkdownLink, MarkdownLinkRange, markdown_links}; -use crate::lint::{BROKEN_INTRA_DOC_LINKS, PRIVATE_INTRA_DOC_LINKS}; +use crate::lint::{BROKEN_INTRA_DOC_LINKS, HIDDEN_INTRA_DOC_LINKS, PRIVATE_INTRA_DOC_LINKS}; use crate::passes::Pass; use crate::visit::DocVisitor; @@ -1341,13 +1341,19 @@ impl LinkCollector<'_, '_> { } } + let src_def_id = diag_info.item.item_id.expect_def_id(); // item can be non-local e.g. when using `#[rustc_doc_primitive = "pointer"]` if let Some(dst_id) = id.as_local() - && let Some(src_id) = diag_info.item.item_id.expect_def_id().as_local() + && let Some(src_id) = src_def_id.as_local() && self.cx.tcx.effective_visibilities(()).is_exported(src_id) && !self.cx.tcx.effective_visibilities(()).is_exported(dst_id) { - privacy_error(self.cx, diag_info, path_str); + privacy_error(self.cx, diag_info, path_str, PrivacyErrorKind::Private); + } + + if is_effectively_hidden(self.cx.tcx, id) && !is_effectively_hidden(self.cx.tcx, src_def_id) + { + privacy_error(self.cx, diag_info, path_str, PrivacyErrorKind::Hidden); } Some(()) @@ -1591,6 +1597,13 @@ fn range_between_backticks(ori_link_range: &MarkdownLinkRange, dox: &str) -> Mar ) } +/// Is this item or any of its ancestors doc(hidden)? +fn is_effectively_hidden(tcx: TyCtxt<'_>, id: DefId) -> bool { + crate::html::format::traverse_parent(tcx, id, false, false, &|id| { + if tcx.is_doc_hidden(id) { ControlFlow::Break(true) } else { ControlFlow::Continue(()) } + }) +} + /// Returns true if we should ignore `link` due to it being unlikely /// that it is an intra-doc link. `link` should still have disambiguators /// if there were any. @@ -2334,8 +2347,20 @@ fn suggest_disambiguator( } } +#[derive(Copy, Clone)] +enum PrivacyErrorKind { + Private, + Hidden, +} + /// Report a link from a public item to a private one. -fn privacy_error(cx: &DocContext<'_>, diag_info: &DiagnosticInfo<'_>, path_str: &str) { +fn privacy_error( + cx: &DocContext<'_>, + diag_info: &DiagnosticInfo<'_>, + path_str: &str, + kind: PrivacyErrorKind, +) { + use PrivacyErrorKind::*; let sym; let item_name = match diag_info.item.name { Some(name) => { @@ -2344,17 +2369,22 @@ fn privacy_error(cx: &DocContext<'_>, diag_info: &DiagnosticInfo<'_>, path_str: } None => "", }; - let msg = format!("public documentation for `{item_name}` links to private item `{path_str}`"); + let (public, private, flag, lint) = match kind { + Private => ("public", "private", "--document-private-items", PRIVATE_INTRA_DOC_LINKS), + Hidden => ("non-hidden", "hidden", "--document-hidden-items", HIDDEN_INTRA_DOC_LINKS), + }; + let msg = + format!("{public} documentation for `{item_name}` links to {private} item `{path_str}`"); - report_diagnostic(cx.tcx, PRIVATE_INTRA_DOC_LINKS, msg, diag_info, |diag, sp, _link_range| { + report_diagnostic(cx.tcx, lint, msg, diag_info, |diag, sp, _link_range| { if let Some(sp) = sp { - diag.span_label(sp, "this item is private"); + diag.span_label(sp, format!("this item is {private}")); } let note_msg = if cx.render_options.document_private { - "this link resolves only because you passed `--document-private-items`, but will break without" + format!("this link resolves only because you passed `{flag}`, but will break without") } else { - "this link will resolve properly if you pass `--document-private-items`" + format!("this link will resolve properly if you pass `{flag}`") }; diag.note(note_msg); }); diff --git a/tests/rustdoc-ui/intra-doc/auxiliary/hidden.rs b/tests/rustdoc-ui/intra-doc/auxiliary/hidden.rs new file mode 100644 index 0000000000000..3deefa7a294f7 --- /dev/null +++ b/tests/rustdoc-ui/intra-doc/auxiliary/hidden.rs @@ -0,0 +1,9 @@ +#[doc(hidden)] +pub fn public_hidden_fn() {} + +#[doc(hidden)] +fn private_hidden_fn() {} + +pub fn public_non_hidden_fn() {} + +fn private_non_hidden_fn() {} diff --git a/tests/rustdoc-ui/intra-doc/link-to-hidden-144664.doc-both.stderr b/tests/rustdoc-ui/intra-doc/link-to-hidden-144664.doc-both.stderr new file mode 100644 index 0000000000000..1d0b7a98cb9d1 --- /dev/null +++ b/tests/rustdoc-ui/intra-doc/link-to-hidden-144664.doc-both.stderr @@ -0,0 +1,92 @@ +error: non-hidden documentation for `link_to_hidden_144664` links to hidden item `crate::local_public_hidden_fn` + --> $DIR/link-to-hidden-144664.rs:14:8 + | +LL | //! * [crate::local_public_hidden_fn] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this item is hidden + | + = note: this link resolves only because you passed `--document-hidden-items`, but will break without +note: the lint level is defined here + --> $DIR/link-to-hidden-144664.rs:10:9 + | +LL | #![deny(rustdoc::hidden_intra_doc_links)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: public documentation for `link_to_hidden_144664` links to private item `crate::local_private_hidden_fn` + --> $DIR/link-to-hidden-144664.rs:18:8 + | +LL | //! * [crate::local_private_hidden_fn] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this item is private + | + = note: this link resolves only because you passed `--document-private-items`, but will break without +note: the lint level is defined here + --> $DIR/link-to-hidden-144664.rs:9:9 + | +LL | #![deny(rustdoc::private_intra_doc_links)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: non-hidden documentation for `link_to_hidden_144664` links to hidden item `crate::local_private_hidden_fn` + --> $DIR/link-to-hidden-144664.rs:18:8 + | +LL | //! * [crate::local_private_hidden_fn] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this item is hidden + | + = note: this link resolves only because you passed `--document-hidden-items`, but will break without + +error: public documentation for `link_to_hidden_144664` links to private item `crate::local_private_non_hidden_fn` + --> $DIR/link-to-hidden-144664.rs:22:8 + | +LL | //! * [crate::local_private_non_hidden_fn] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this item is private + | + = note: this link resolves only because you passed `--document-private-items`, but will break without + +error: non-hidden documentation for `link_to_hidden_144664` links to hidden item `nonlocal::public_hidden_fn` + --> $DIR/link-to-hidden-144664.rs:24:8 + | +LL | //! * [nonlocal::public_hidden_fn] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ this item is hidden + | + = note: this link resolves only because you passed `--document-hidden-items`, but will break without + +error: unresolved link to `nonlocal::private_hidden_fn` + --> $DIR/link-to-hidden-144664.rs:28:8 + | +LL | //! * [nonlocal::private_hidden_fn] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ no item named `private_hidden_fn` in module `hidden` + | +note: the lint level is defined here + --> $DIR/link-to-hidden-144664.rs:11:9 + | +LL | #![deny(rustdoc::broken_intra_doc_links)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: unresolved link to `nonlocal::private_non_hidden_fn` + --> $DIR/link-to-hidden-144664.rs:30:8 + | +LL | //! * [nonlocal::private_non_hidden_fn] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no item named `private_non_hidden_fn` in module `hidden` + +error: unresolved link to `crate::hidden_mod::a` + --> $DIR/link-to-hidden-144664.rs:32:8 + | +LL | //! * [crate::hidden_mod::a] + | ^^^^^^^^^^^^^^^^^^^^ no item named `a` in module `hidden_mod` + +error: non-hidden documentation for `link_to_hidden_144664` links to hidden item `crate::hidden_mod::b` + --> $DIR/link-to-hidden-144664.rs:34:8 + | +LL | //! * [crate::hidden_mod::b] + | ^^^^^^^^^^^^^^^^^^^^ this item is hidden + | + = note: this link resolves only because you passed `--document-hidden-items`, but will break without + +error: non-hidden documentation for `link_to_hidden_144664` links to hidden item `crate::hidden_mod::c` + --> $DIR/link-to-hidden-144664.rs:36:8 + | +LL | //! * [crate::hidden_mod::c] + | ^^^^^^^^^^^^^^^^^^^^ this item is hidden + | + = note: this link resolves only because you passed `--document-hidden-items`, but will break without + +error: aborting due to 10 previous errors + diff --git a/tests/rustdoc-ui/intra-doc/link-to-hidden-144664.doc-hidden.stderr b/tests/rustdoc-ui/intra-doc/link-to-hidden-144664.doc-hidden.stderr new file mode 100644 index 0000000000000..c3f699b61ac2b --- /dev/null +++ b/tests/rustdoc-ui/intra-doc/link-to-hidden-144664.doc-hidden.stderr @@ -0,0 +1,92 @@ +error: non-hidden documentation for `link_to_hidden_144664` links to hidden item `crate::local_public_hidden_fn` + --> $DIR/link-to-hidden-144664.rs:14:8 + | +LL | //! * [crate::local_public_hidden_fn] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this item is hidden + | + = note: this link will resolve properly if you pass `--document-hidden-items` +note: the lint level is defined here + --> $DIR/link-to-hidden-144664.rs:10:9 + | +LL | #![deny(rustdoc::hidden_intra_doc_links)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: public documentation for `link_to_hidden_144664` links to private item `crate::local_private_hidden_fn` + --> $DIR/link-to-hidden-144664.rs:18:8 + | +LL | //! * [crate::local_private_hidden_fn] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this item is private + | + = note: this link will resolve properly if you pass `--document-private-items` +note: the lint level is defined here + --> $DIR/link-to-hidden-144664.rs:9:9 + | +LL | #![deny(rustdoc::private_intra_doc_links)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: non-hidden documentation for `link_to_hidden_144664` links to hidden item `crate::local_private_hidden_fn` + --> $DIR/link-to-hidden-144664.rs:18:8 + | +LL | //! * [crate::local_private_hidden_fn] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this item is hidden + | + = note: this link will resolve properly if you pass `--document-hidden-items` + +error: public documentation for `link_to_hidden_144664` links to private item `crate::local_private_non_hidden_fn` + --> $DIR/link-to-hidden-144664.rs:22:8 + | +LL | //! * [crate::local_private_non_hidden_fn] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this item is private + | + = note: this link will resolve properly if you pass `--document-private-items` + +error: non-hidden documentation for `link_to_hidden_144664` links to hidden item `nonlocal::public_hidden_fn` + --> $DIR/link-to-hidden-144664.rs:24:8 + | +LL | //! * [nonlocal::public_hidden_fn] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ this item is hidden + | + = note: this link will resolve properly if you pass `--document-hidden-items` + +error: unresolved link to `nonlocal::private_hidden_fn` + --> $DIR/link-to-hidden-144664.rs:28:8 + | +LL | //! * [nonlocal::private_hidden_fn] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ no item named `private_hidden_fn` in module `hidden` + | +note: the lint level is defined here + --> $DIR/link-to-hidden-144664.rs:11:9 + | +LL | #![deny(rustdoc::broken_intra_doc_links)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: unresolved link to `nonlocal::private_non_hidden_fn` + --> $DIR/link-to-hidden-144664.rs:30:8 + | +LL | //! * [nonlocal::private_non_hidden_fn] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no item named `private_non_hidden_fn` in module `hidden` + +error: unresolved link to `crate::hidden_mod::a` + --> $DIR/link-to-hidden-144664.rs:32:8 + | +LL | //! * [crate::hidden_mod::a] + | ^^^^^^^^^^^^^^^^^^^^ no item named `a` in module `hidden_mod` + +error: non-hidden documentation for `link_to_hidden_144664` links to hidden item `crate::hidden_mod::b` + --> $DIR/link-to-hidden-144664.rs:34:8 + | +LL | //! * [crate::hidden_mod::b] + | ^^^^^^^^^^^^^^^^^^^^ this item is hidden + | + = note: this link will resolve properly if you pass `--document-hidden-items` + +error: non-hidden documentation for `link_to_hidden_144664` links to hidden item `crate::hidden_mod::c` + --> $DIR/link-to-hidden-144664.rs:36:8 + | +LL | //! * [crate::hidden_mod::c] + | ^^^^^^^^^^^^^^^^^^^^ this item is hidden + | + = note: this link will resolve properly if you pass `--document-hidden-items` + +error: aborting due to 10 previous errors + diff --git a/tests/rustdoc-ui/intra-doc/link-to-hidden-144664.doc-priv.stderr b/tests/rustdoc-ui/intra-doc/link-to-hidden-144664.doc-priv.stderr new file mode 100644 index 0000000000000..1d0b7a98cb9d1 --- /dev/null +++ b/tests/rustdoc-ui/intra-doc/link-to-hidden-144664.doc-priv.stderr @@ -0,0 +1,92 @@ +error: non-hidden documentation for `link_to_hidden_144664` links to hidden item `crate::local_public_hidden_fn` + --> $DIR/link-to-hidden-144664.rs:14:8 + | +LL | //! * [crate::local_public_hidden_fn] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this item is hidden + | + = note: this link resolves only because you passed `--document-hidden-items`, but will break without +note: the lint level is defined here + --> $DIR/link-to-hidden-144664.rs:10:9 + | +LL | #![deny(rustdoc::hidden_intra_doc_links)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: public documentation for `link_to_hidden_144664` links to private item `crate::local_private_hidden_fn` + --> $DIR/link-to-hidden-144664.rs:18:8 + | +LL | //! * [crate::local_private_hidden_fn] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this item is private + | + = note: this link resolves only because you passed `--document-private-items`, but will break without +note: the lint level is defined here + --> $DIR/link-to-hidden-144664.rs:9:9 + | +LL | #![deny(rustdoc::private_intra_doc_links)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: non-hidden documentation for `link_to_hidden_144664` links to hidden item `crate::local_private_hidden_fn` + --> $DIR/link-to-hidden-144664.rs:18:8 + | +LL | //! * [crate::local_private_hidden_fn] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this item is hidden + | + = note: this link resolves only because you passed `--document-hidden-items`, but will break without + +error: public documentation for `link_to_hidden_144664` links to private item `crate::local_private_non_hidden_fn` + --> $DIR/link-to-hidden-144664.rs:22:8 + | +LL | //! * [crate::local_private_non_hidden_fn] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this item is private + | + = note: this link resolves only because you passed `--document-private-items`, but will break without + +error: non-hidden documentation for `link_to_hidden_144664` links to hidden item `nonlocal::public_hidden_fn` + --> $DIR/link-to-hidden-144664.rs:24:8 + | +LL | //! * [nonlocal::public_hidden_fn] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ this item is hidden + | + = note: this link resolves only because you passed `--document-hidden-items`, but will break without + +error: unresolved link to `nonlocal::private_hidden_fn` + --> $DIR/link-to-hidden-144664.rs:28:8 + | +LL | //! * [nonlocal::private_hidden_fn] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ no item named `private_hidden_fn` in module `hidden` + | +note: the lint level is defined here + --> $DIR/link-to-hidden-144664.rs:11:9 + | +LL | #![deny(rustdoc::broken_intra_doc_links)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: unresolved link to `nonlocal::private_non_hidden_fn` + --> $DIR/link-to-hidden-144664.rs:30:8 + | +LL | //! * [nonlocal::private_non_hidden_fn] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no item named `private_non_hidden_fn` in module `hidden` + +error: unresolved link to `crate::hidden_mod::a` + --> $DIR/link-to-hidden-144664.rs:32:8 + | +LL | //! * [crate::hidden_mod::a] + | ^^^^^^^^^^^^^^^^^^^^ no item named `a` in module `hidden_mod` + +error: non-hidden documentation for `link_to_hidden_144664` links to hidden item `crate::hidden_mod::b` + --> $DIR/link-to-hidden-144664.rs:34:8 + | +LL | //! * [crate::hidden_mod::b] + | ^^^^^^^^^^^^^^^^^^^^ this item is hidden + | + = note: this link resolves only because you passed `--document-hidden-items`, but will break without + +error: non-hidden documentation for `link_to_hidden_144664` links to hidden item `crate::hidden_mod::c` + --> $DIR/link-to-hidden-144664.rs:36:8 + | +LL | //! * [crate::hidden_mod::c] + | ^^^^^^^^^^^^^^^^^^^^ this item is hidden + | + = note: this link resolves only because you passed `--document-hidden-items`, but will break without + +error: aborting due to 10 previous errors + diff --git a/tests/rustdoc-ui/intra-doc/link-to-hidden-144664.noflags.stderr b/tests/rustdoc-ui/intra-doc/link-to-hidden-144664.noflags.stderr new file mode 100644 index 0000000000000..c3f699b61ac2b --- /dev/null +++ b/tests/rustdoc-ui/intra-doc/link-to-hidden-144664.noflags.stderr @@ -0,0 +1,92 @@ +error: non-hidden documentation for `link_to_hidden_144664` links to hidden item `crate::local_public_hidden_fn` + --> $DIR/link-to-hidden-144664.rs:14:8 + | +LL | //! * [crate::local_public_hidden_fn] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this item is hidden + | + = note: this link will resolve properly if you pass `--document-hidden-items` +note: the lint level is defined here + --> $DIR/link-to-hidden-144664.rs:10:9 + | +LL | #![deny(rustdoc::hidden_intra_doc_links)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: public documentation for `link_to_hidden_144664` links to private item `crate::local_private_hidden_fn` + --> $DIR/link-to-hidden-144664.rs:18:8 + | +LL | //! * [crate::local_private_hidden_fn] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this item is private + | + = note: this link will resolve properly if you pass `--document-private-items` +note: the lint level is defined here + --> $DIR/link-to-hidden-144664.rs:9:9 + | +LL | #![deny(rustdoc::private_intra_doc_links)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: non-hidden documentation for `link_to_hidden_144664` links to hidden item `crate::local_private_hidden_fn` + --> $DIR/link-to-hidden-144664.rs:18:8 + | +LL | //! * [crate::local_private_hidden_fn] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this item is hidden + | + = note: this link will resolve properly if you pass `--document-hidden-items` + +error: public documentation for `link_to_hidden_144664` links to private item `crate::local_private_non_hidden_fn` + --> $DIR/link-to-hidden-144664.rs:22:8 + | +LL | //! * [crate::local_private_non_hidden_fn] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this item is private + | + = note: this link will resolve properly if you pass `--document-private-items` + +error: non-hidden documentation for `link_to_hidden_144664` links to hidden item `nonlocal::public_hidden_fn` + --> $DIR/link-to-hidden-144664.rs:24:8 + | +LL | //! * [nonlocal::public_hidden_fn] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ this item is hidden + | + = note: this link will resolve properly if you pass `--document-hidden-items` + +error: unresolved link to `nonlocal::private_hidden_fn` + --> $DIR/link-to-hidden-144664.rs:28:8 + | +LL | //! * [nonlocal::private_hidden_fn] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ no item named `private_hidden_fn` in module `hidden` + | +note: the lint level is defined here + --> $DIR/link-to-hidden-144664.rs:11:9 + | +LL | #![deny(rustdoc::broken_intra_doc_links)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: unresolved link to `nonlocal::private_non_hidden_fn` + --> $DIR/link-to-hidden-144664.rs:30:8 + | +LL | //! * [nonlocal::private_non_hidden_fn] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no item named `private_non_hidden_fn` in module `hidden` + +error: unresolved link to `crate::hidden_mod::a` + --> $DIR/link-to-hidden-144664.rs:32:8 + | +LL | //! * [crate::hidden_mod::a] + | ^^^^^^^^^^^^^^^^^^^^ no item named `a` in module `hidden_mod` + +error: non-hidden documentation for `link_to_hidden_144664` links to hidden item `crate::hidden_mod::b` + --> $DIR/link-to-hidden-144664.rs:34:8 + | +LL | //! * [crate::hidden_mod::b] + | ^^^^^^^^^^^^^^^^^^^^ this item is hidden + | + = note: this link will resolve properly if you pass `--document-hidden-items` + +error: non-hidden documentation for `link_to_hidden_144664` links to hidden item `crate::hidden_mod::c` + --> $DIR/link-to-hidden-144664.rs:36:8 + | +LL | //! * [crate::hidden_mod::c] + | ^^^^^^^^^^^^^^^^^^^^ this item is hidden + | + = note: this link will resolve properly if you pass `--document-hidden-items` + +error: aborting due to 10 previous errors + diff --git a/tests/rustdoc-ui/intra-doc/link-to-hidden-144664.rs b/tests/rustdoc-ui/intra-doc/link-to-hidden-144664.rs new file mode 100644 index 0000000000000..4917d27caf9cd --- /dev/null +++ b/tests/rustdoc-ui/intra-doc/link-to-hidden-144664.rs @@ -0,0 +1,82 @@ +// ignore-tidy-linelength +//@ revisions: noflags doc-priv doc-hidden doc-both +//@ aux-build: hidden.rs +//@ build-aux-docs +//@[doc-priv]compile-flags: --document-private-items +//@[doc-hidden]compile-flags: -Z unstable-options --document-hidden-items +//@[doc-both]compile-flags: -Z unstable-options --document-hidden-items --document-private-items + +#![deny(rustdoc::private_intra_doc_links)] +#![deny(rustdoc::hidden_intra_doc_links)] +#![deny(rustdoc::broken_intra_doc_links)] + +//! All these links are broken and should produce warnings: +//! * [crate::local_public_hidden_fn] +//~^ ERROR: non-hidden documentation for `link_to_hidden_144664` links to hidden item `crate::local_public_hidden_fn` + +// for local items that are hidden and private, both lints are emmitted +//! * [crate::local_private_hidden_fn] +//~^ ERROR: public documentation for `link_to_hidden_144664` links to private item `crate::local_private_hidden_fn` +//~| ERROR: non-hidden documentation for `link_to_hidden_144664` links to hidden item `crate::local_private_hidden_fn` + +//! * [crate::local_private_non_hidden_fn] +//~^ ERROR: public documentation for `link_to_hidden_144664` links to private item `crate::local_private_non_hidden_fn` +//! * [nonlocal::public_hidden_fn] +//~^ ERROR: non-hidden documentation for `link_to_hidden_144664` links to hidden item `nonlocal::public_hidden_fn` + +// for cross-crate private items, rustdoc doesn't even know if they exist, so we get a generic error. +//! * [nonlocal::private_hidden_fn] +//~^ ERROR: unresolved link +//! * [nonlocal::private_non_hidden_fn] +//~^ ERROR: unresolved link +//! * [crate::hidden_mod::a] +//~^ ERROR: unresolved link +//! * [crate::hidden_mod::b] +//~^ ERROR: non-hidden documentation for `link_to_hidden_144664` links to hidden item `crate::hidden_mod::b` +//! * [crate::hidden_mod::c] +//~^ ERROR: non-hidden documentation for `link_to_hidden_144664` links to hidden item `crate::hidden_mod::c` +extern crate hidden as nonlocal; + +#[doc(hidden)] pub fn local_public_hidden_fn() {} +/// [Private hidden to public hidden is fine](crate::local_public_hidden_fn) +#[doc(hidden)] fn local_private_hidden_fn() {} +pub fn local_public_non_hidden_fn() {} +fn local_private_non_hidden_fn() {} + +pub struct PublicStruct; + +#[doc(hidden)] +pub mod hidden_mod { + /// [This link is fine, both are indirectly hidden](self::b) + fn a() {} + /// [This link also fine, both are hidden, doesn't matter that only one is indirect](self::c) + pub fn b() {} + /// [Hidden docs can link to non-hidden](crate::local_public_non_hidden_fn) + #[doc(hidden)] + pub fn c() {} + + #[doc(hidden)] + pub struct HiddenStruct; + + + impl crate::PublicNonHiddenTrait for HiddenStruct { + /// This method is actually hidden, + /// as the struct is hidden and thus it will not show up in docs. + /// [This link is ok](self::c) + fn non_hidden_trait_method(&self) {} + } + + pub trait PublicHiddenTrait { + fn hidden_trait_method(&self); + } +} + +pub trait PublicNonHiddenTrait { + fn non_hidden_trait_method(&self); +} + +impl crate::hidden_mod::PublicHiddenTrait for PublicStruct { + /// This method is hidden as the trait is hidden. + /// [This link is ok](crate::local_public_hidden_fn) + fn hidden_trait_method(&self) {} +}