about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustdoc/passes/stripper.rs30
-rw-r--r--src/test/rustdoc-json/reexport/reexport_method_from_private_module.rs28
2 files changed, 53 insertions, 5 deletions
diff --git a/src/librustdoc/passes/stripper.rs b/src/librustdoc/passes/stripper.rs
index f293a6fcc6d..0089ce63d07 100644
--- a/src/librustdoc/passes/stripper.rs
+++ b/src/librustdoc/passes/stripper.rs
@@ -1,9 +1,11 @@
 //! A collection of utility functions for the `strip_*` passes.
 use rustc_hir::def_id::DefId;
 use rustc_middle::middle::privacy::EffectiveVisibilities;
+use rustc_span::symbol::sym;
+
 use std::mem;
 
-use crate::clean::{self, Item, ItemId, ItemIdSet};
+use crate::clean::{self, Item, ItemId, ItemIdSet, NestedAttributesExt};
 use crate::fold::{strip_item, DocFolder};
 use crate::formats::cache::Cache;
 
@@ -151,6 +153,22 @@ pub(crate) struct ImplStripper<'a> {
     pub(crate) document_private: bool,
 }
 
+impl<'a> ImplStripper<'a> {
+    #[inline]
+    fn should_keep_impl(&self, item: &Item, for_def_id: DefId) -> bool {
+        if !for_def_id.is_local() || self.retained.contains(&for_def_id.into()) {
+            true
+        } else if self.is_json_output {
+            // If the "for" item is exported and the impl block isn't `#[doc(hidden)]`, then we
+            // need to keep it.
+            self.cache.effective_visibilities.is_exported(for_def_id)
+                && !item.attrs.lists(sym::doc).has_word(sym::hidden)
+        } else {
+            false
+        }
+    }
+}
+
 impl<'a> DocFolder for ImplStripper<'a> {
     fn fold_item(&mut self, i: Item) -> Option<Item> {
         if let clean::ImplItem(ref imp) = *i.kind {
@@ -178,15 +196,17 @@ impl<'a> DocFolder for ImplStripper<'a> {
                     return None;
                 }
             }
+            // Because we don't inline in `maybe_inline_local` if the output format is JSON,
+            // we need to make a special check for JSON output: we want to keep it unless it has
+            // a `#[doc(hidden)]` attribute if the `for_` type is exported.
             if let Some(did) = imp.for_.def_id(self.cache) {
-                if did.is_local() && !imp.for_.is_assoc_ty() && !self.retained.contains(&did.into())
-                {
+                if !imp.for_.is_assoc_ty() && !self.should_keep_impl(&i, did) {
                     debug!("ImplStripper: impl item for stripped type; removing");
                     return None;
                 }
             }
             if let Some(did) = imp.trait_.as_ref().map(|t| t.def_id()) {
-                if did.is_local() && !self.retained.contains(&did.into()) {
+                if !self.should_keep_impl(&i, did) {
                     debug!("ImplStripper: impl item for stripped trait; removing");
                     return None;
                 }
@@ -194,7 +214,7 @@ impl<'a> DocFolder for ImplStripper<'a> {
             if let Some(generics) = imp.trait_.as_ref().and_then(|t| t.generics()) {
                 for typaram in generics {
                     if let Some(did) = typaram.def_id(self.cache) {
-                        if did.is_local() && !self.retained.contains(&did.into()) {
+                        if !self.should_keep_impl(&i, did) {
                             debug!(
                                 "ImplStripper: stripped item in trait's generics; removing impl"
                             );
diff --git a/src/test/rustdoc-json/reexport/reexport_method_from_private_module.rs b/src/test/rustdoc-json/reexport/reexport_method_from_private_module.rs
new file mode 100644
index 00000000000..239b1a23b43
--- /dev/null
+++ b/src/test/rustdoc-json/reexport/reexport_method_from_private_module.rs
@@ -0,0 +1,28 @@
+// Regression test for <https://github.com/rust-lang/rust/issues/102583>.
+
+// @set impl_S = "$.index[*][?(@.docs=='impl S')].id"
+// @has "$.index[*][?(@.name=='S')].inner.impls[*]" $impl_S
+// @set is_present = "$.index[*][?(@.name=='is_present')].id"
+// @is "$.index[*][?(@.docs=='impl S')].inner.items[*]" $is_present
+// @!has "$.index[*][?(@.name=='hidden_impl')]"
+// @!has "$.index[*][?(@.name=='hidden_fn')]"
+
+#![no_std]
+
+mod private_mod {
+    pub struct S;
+
+    /// impl S
+    impl S {
+        pub fn is_present() {}
+        #[doc(hidden)]
+        pub fn hidden_fn() {}
+    }
+
+    #[doc(hidden)]
+    impl S {
+        pub fn hidden_impl() {}
+    }
+}
+
+pub use private_mod::*;