about summary refs log tree commit diff
diff options
context:
space:
mode:
authorVadim Petrochenkov <vadim.petrochenkov@gmail.com>2023-02-11 19:14:25 +0400
committerVadim Petrochenkov <vadim.petrochenkov@gmail.com>2023-03-23 16:19:59 +0400
commitbec4eab3f972495df32b4861a3117ef16d73a018 (patch)
tree44961ca125f7762b35742ce9536d2057774192e8
parent84dd6dfd9d19176cc3c94bc1448a841e44d57890 (diff)
downloadrust-bec4eab3f972495df32b4861a3117ef16d73a018.tar.gz
rust-bec4eab3f972495df32b4861a3117ef16d73a018.zip
rustdoc: Skip doc link resolution for non-exported items
-rw-r--r--compiler/rustc_resolve/src/late.rs5
-rw-r--r--compiler/rustc_resolve/src/rustdoc.rs16
-rw-r--r--src/librustdoc/core.rs9
-rw-r--r--src/librustdoc/passes/collect_intra_doc_links.rs13
-rw-r--r--tests/rustdoc-ui/intra-doc/reachable-non-exported.rs13
5 files changed, 45 insertions, 11 deletions
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index 6af9dc89e56..4ca54bab31a 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -4236,7 +4236,10 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
             {
                 return;
             }
-            ResolveDocLinks::Exported if !maybe_exported.eval(self.r) => {
+            ResolveDocLinks::Exported
+                if !maybe_exported.eval(self.r)
+                    && !rustdoc::has_primitive_or_keyword_docs(attrs) =>
+            {
                 return;
             }
             ResolveDocLinks::ExportedMetadata
diff --git a/compiler/rustc_resolve/src/rustdoc.rs b/compiler/rustc_resolve/src/rustdoc.rs
index 0e40f794f18..44a27bbc175 100644
--- a/compiler/rustc_resolve/src/rustdoc.rs
+++ b/compiler/rustc_resolve/src/rustdoc.rs
@@ -3,7 +3,7 @@ use rustc_ast as ast;
 use rustc_ast::util::comments::beautify_doc_string;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_span::def_id::DefId;
-use rustc_span::symbol::{kw, Symbol};
+use rustc_span::symbol::{kw, sym, Symbol};
 use rustc_span::Span;
 use std::{cmp, mem};
 
@@ -339,6 +339,20 @@ pub fn inner_docs(attrs: &[ast::Attribute]) -> bool {
     attrs.iter().find(|a| a.doc_str().is_some()).map_or(true, |a| a.style == ast::AttrStyle::Inner)
 }
 
+/// Has `#[doc(primitive)]` or `#[doc(keyword)]`.
+pub fn has_primitive_or_keyword_docs(attrs: &[ast::Attribute]) -> bool {
+    for attr in attrs {
+        if attr.has_name(sym::doc) && let Some(items) = attr.meta_item_list() {
+            for item in items {
+                if item.has_name(sym::primitive) || item.has_name(sym::keyword) {
+                    return true;
+                }
+            }
+        }
+    }
+    false
+}
+
 /// Simplified version of the corresponding function in rustdoc.
 /// If the rustdoc version returns a successful result, this function must return the same result.
 /// Otherwise this function may return anything.
diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs
index fbfc58a436b..28458f32903 100644
--- a/src/librustdoc/core.rs
+++ b/src/librustdoc/core.rs
@@ -217,13 +217,8 @@ pub(crate) fn create_config(
 
     let crate_types =
         if proc_macro_crate { vec![CrateType::ProcMacro] } else { vec![CrateType::Rlib] };
-    let resolve_doc_links = if *document_private {
-        ResolveDocLinks::All
-    } else {
-        // Should be `ResolveDocLinks::Exported` in theory, but for some reason rustdoc
-        // still tries to request resolutions for links on private items.
-        ResolveDocLinks::All
-    };
+    let resolve_doc_links =
+        if *document_private { ResolveDocLinks::All } else { ResolveDocLinks::Exported };
     let test = scrape_examples_options.map(|opts| opts.scrape_tests).unwrap_or(false);
     // plays with error output here!
     let sessopts = config::Options {
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index 789523c561e..d98cf251e97 100644
--- a/src/librustdoc/passes/collect_intra_doc_links.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links.rs
@@ -15,8 +15,8 @@ use rustc_hir::def_id::{DefId, CRATE_DEF_ID};
 use rustc_hir::Mutability;
 use rustc_middle::ty::{fast_reject::TreatProjections, Ty, TyCtxt};
 use rustc_middle::{bug, ty};
-use rustc_resolve::rustdoc::MalformedGenerics;
-use rustc_resolve::rustdoc::{prepare_to_doc_link_resolution, strip_generics_from_path};
+use rustc_resolve::rustdoc::{has_primitive_or_keyword_docs, prepare_to_doc_link_resolution};
+use rustc_resolve::rustdoc::{strip_generics_from_path, MalformedGenerics};
 use rustc_session::lint::Lint;
 use rustc_span::hygiene::MacroKind;
 use rustc_span::symbol::{sym, Ident, Symbol};
@@ -899,6 +899,15 @@ fn preprocessed_markdown_links(s: &str) -> Vec<PreprocessedMarkdownLink> {
 
 impl LinkCollector<'_, '_> {
     fn resolve_links(&mut self, item: &Item) {
+        if !self.cx.render_options.document_private
+            && let Some(def_id) = item.item_id.as_def_id()
+            && let Some(def_id) = def_id.as_local()
+            && !self.cx.tcx.effective_visibilities(()).is_exported(def_id)
+            && !has_primitive_or_keyword_docs(&item.attrs.other_attrs) {
+            // Skip link resolution for non-exported items.
+            return;
+        }
+
         // We want to resolve in the lexical scope of the documentation.
         // In the presence of re-exports, this is not the same as the module of the item.
         // Rather than merging all documentation into one, resolve it one attribute at a time
diff --git a/tests/rustdoc-ui/intra-doc/reachable-non-exported.rs b/tests/rustdoc-ui/intra-doc/reachable-non-exported.rs
new file mode 100644
index 00000000000..6afcad4f921
--- /dev/null
+++ b/tests/rustdoc-ui/intra-doc/reachable-non-exported.rs
@@ -0,0 +1,13 @@
+// The structure is reachable, but not exported, so rustdoc
+// doesn't attempt to request doc link resolutions on it.
+
+// check-pass
+
+mod private {
+    /// [core::str::FromStr]
+    pub struct ReachableButNotExported;
+}
+
+pub fn foo() -> private::ReachableButNotExported {
+    private::ReachableButNotExported
+}