about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustdoc/passes/strip_hidden.rs8
-rw-r--r--src/librustdoc/passes/strip_private.rs10
-rw-r--r--src/librustdoc/passes/stripper.rs53
3 files changed, 54 insertions, 17 deletions
diff --git a/src/librustdoc/passes/strip_hidden.rs b/src/librustdoc/passes/strip_hidden.rs
index 533e2ce46dd..9914edf3036 100644
--- a/src/librustdoc/passes/strip_hidden.rs
+++ b/src/librustdoc/passes/strip_hidden.rs
@@ -17,6 +17,7 @@ pub(crate) const STRIP_HIDDEN: Pass = Pass {
 /// Strip items marked `#[doc(hidden)]`
 pub(crate) fn strip_hidden(krate: clean::Crate, cx: &mut DocContext<'_>) -> clean::Crate {
     let mut retained = ItemIdSet::default();
+    let is_json_output = cx.output_format.is_json() && !cx.show_coverage;
 
     // strip all #[doc(hidden)] items
     let krate = {
@@ -25,7 +26,12 @@ pub(crate) fn strip_hidden(krate: clean::Crate, cx: &mut DocContext<'_>) -> clea
     };
 
     // strip all impls referencing stripped items
-    let mut stripper = ImplStripper { retained: &retained, cache: &cx.cache };
+    let mut stripper = ImplStripper {
+        retained: &retained,
+        cache: &cx.cache,
+        is_json_output,
+        document_private: cx.render_options.document_private,
+    };
     stripper.fold_crate(krate)
 }
 
diff --git a/src/librustdoc/passes/strip_private.rs b/src/librustdoc/passes/strip_private.rs
index 9ba841a31cf..f3aa3c7ce24 100644
--- a/src/librustdoc/passes/strip_private.rs
+++ b/src/librustdoc/passes/strip_private.rs
@@ -17,6 +17,7 @@ pub(crate) const STRIP_PRIVATE: Pass = Pass {
 pub(crate) fn strip_private(mut krate: clean::Crate, cx: &mut DocContext<'_>) -> clean::Crate {
     // This stripper collects all *retained* nodes.
     let mut retained = ItemIdSet::default();
+    let is_json_output = cx.output_format.is_json() && !cx.show_coverage;
 
     // strip all private items
     {
@@ -24,12 +25,17 @@ pub(crate) fn strip_private(mut krate: clean::Crate, cx: &mut DocContext<'_>) ->
             retained: &mut retained,
             access_levels: &cx.cache.access_levels,
             update_retained: true,
-            is_json_output: cx.output_format.is_json() && !cx.show_coverage,
+            is_json_output,
         };
         krate = ImportStripper.fold_crate(stripper.fold_crate(krate));
     }
 
     // strip all impls referencing private items
-    let mut stripper = ImplStripper { retained: &retained, cache: &cx.cache };
+    let mut stripper = ImplStripper {
+        retained: &retained,
+        cache: &cx.cache,
+        is_json_output,
+        document_private: cx.render_options.document_private,
+    };
     stripper.fold_crate(krate)
 }
diff --git a/src/librustdoc/passes/stripper.rs b/src/librustdoc/passes/stripper.rs
index 0d419042a10..3f069e8393f 100644
--- a/src/librustdoc/passes/stripper.rs
+++ b/src/librustdoc/passes/stripper.rs
@@ -14,17 +14,19 @@ pub(crate) struct Stripper<'a> {
     pub(crate) is_json_output: bool,
 }
 
-impl<'a> Stripper<'a> {
-    // We need to handle this differently for the JSON output because some non exported items could
-    // be used in public API. And so, we need these items as well. `is_exported` only checks if they
-    // are in the public API, which is not enough.
-    #[inline]
-    fn is_item_reachable(&self, item_id: ItemId) -> bool {
-        if self.is_json_output {
-            self.access_levels.is_reachable(item_id.expect_def_id())
-        } else {
-            self.access_levels.is_exported(item_id.expect_def_id())
-        }
+// We need to handle this differently for the JSON output because some non exported items could
+// be used in public API. And so, we need these items as well. `is_exported` only checks if they
+// are in the public API, which is not enough.
+#[inline]
+fn is_item_reachable(
+    is_json_output: bool,
+    access_levels: &AccessLevels<DefId>,
+    item_id: ItemId,
+) -> bool {
+    if is_json_output {
+        access_levels.is_reachable(item_id.expect_def_id())
+    } else {
+        access_levels.is_exported(item_id.expect_def_id())
     }
 }
 
@@ -61,7 +63,9 @@ impl<'a> DocFolder for Stripper<'a> {
             | clean::MacroItem(..)
             | clean::ForeignTypeItem => {
                 let item_id = i.item_id;
-                if item_id.is_local() && !self.is_item_reachable(item_id) {
+                if item_id.is_local()
+                    && !is_item_reachable(self.is_json_output, self.access_levels, item_id)
+                {
                     debug!("Stripper: stripping {:?} {:?}", i.type_(), i.name);
                     return None;
                 }
@@ -133,6 +137,8 @@ impl<'a> DocFolder for Stripper<'a> {
 pub(crate) struct ImplStripper<'a> {
     pub(crate) retained: &'a ItemIdSet,
     pub(crate) cache: &'a Cache,
+    pub(crate) is_json_output: bool,
+    pub(crate) document_private: bool,
 }
 
 impl<'a> DocFolder for ImplStripper<'a> {
@@ -140,8 +146,27 @@ impl<'a> DocFolder for ImplStripper<'a> {
         if let clean::ImplItem(ref imp) = *i.kind {
             // Impl blocks can be skipped if they are: empty; not a trait impl; and have no
             // documentation.
-            if imp.trait_.is_none() && imp.items.is_empty() && i.doc_value().is_none() {
-                return None;
+            //
+            // There is one special case: if the impl block contains only private items.
+            if imp.trait_.is_none() {
+                // If the only items present are private ones and we're not rendering private items,
+                // we don't document it.
+                if !imp.items.is_empty()
+                    && !self.document_private
+                    && imp.items.iter().all(|i| {
+                        let item_id = i.item_id;
+                        item_id.is_local()
+                            && !is_item_reachable(
+                                self.is_json_output,
+                                &self.cache.access_levels,
+                                item_id,
+                            )
+                    })
+                {
+                    return None;
+                } else if imp.items.is_empty() && i.doc_value().is_none() {
+                    return None;
+                }
             }
             if let Some(did) = imp.for_.def_id(self.cache) {
                 if did.is_local() && !imp.for_.is_assoc_ty() && !self.retained.contains(&did.into())