about summary refs log tree commit diff
diff options
context:
space:
mode:
authorGuillaume Gomez <guillaume1.gomez@gmail.com>2021-10-07 16:24:53 +0200
committerGitHub <noreply@github.com>2021-10-07 16:24:53 +0200
commite32328bdc5a749d4b7d35297c85337522a07bddf (patch)
tree2df540b61011010c62665cb486e085b4e468bd59
parentde0b4f9444f870bf500f4b5f5ceae0acedf85e4a (diff)
parent09c76884bc6ba1957dc6d71d79b797de2cc40b2d (diff)
downloadrust-e32328bdc5a749d4b7d35297c85337522a07bddf.tar.gz
rust-e32328bdc5a749d4b7d35297c85337522a07bddf.zip
Rollup merge of #89596 - GuillaumeGomez:implicit-doc-cfg, r=jyn514
Make cfg imply doc(cfg)

This is a reopening of #79341, rebased and modified a bit (we made a lot of refactoring in rustdoc's types so they needed to be reflected in this PR as well):

 * `hidden_cfg` is now in the `Cache` instead of `DocContext` because `cfg` information isn't stored anymore on `clean::Attributes` type but instead computed on-demand, so we need this information in later parts of rustdoc.
 * I removed the `bool_to_options` feature (which makes the code a bit simpler to read for `SingleExt` trait implementation.
 * I updated the version for the feature.

There is only one thing I couldn't figure out: [this comment](https://github.com/rust-lang/rust/pull/79341#discussion_r561855624)

> I think I'll likely scrap the whole `SingleExt` extension trait as the diagnostics for 0 and >1 items should be different.

How/why should they differ?

EDIT: this part has been solved, the current code was fine, just needed a little simplification.

cc `@Nemo157`
r? `@jyn514`

Original PR description:

This is only active when the `doc_cfg` feature is active.

The implicit cfg can be overridden via `#[doc(cfg(...))]`, so e.g. to hide a `#[cfg]` you can use something like:

```rust
#[cfg(unix)]
#[doc(cfg(all()))]
pub struct Unix;
```

By adding `#![doc(cfg_hide(foobar))]` to the crate attributes the cfg `#[cfg(foobar)]` (and _only_ that _exact_ cfg) will not be implicitly treated as a `doc(cfg)` to render a message in the documentation.
-rw-r--r--compiler/rustc_ast_passes/src/feature_gate.rs1
-rw-r--r--compiler/rustc_feature/src/active.rs3
-rw-r--r--compiler/rustc_passes/src/check_attr.rs1
-rw-r--r--compiler/rustc_span/src/symbol.rs2
-rw-r--r--library/alloc/src/lib.rs6
-rw-r--r--library/core/src/lib.rs25
-rw-r--r--library/std/src/lib.rs2
-rw-r--r--library/std/src/os/raw/mod.rs6
-rw-r--r--library/std/src/os/windows/raw.rs2
-rw-r--r--src/librustdoc/clean/inline.rs4
-rw-r--r--src/librustdoc/clean/mod.rs2
-rw-r--r--src/librustdoc/clean/types.rs43
-rw-r--r--src/librustdoc/doctest.rs2
-rw-r--r--src/librustdoc/formats/cache.rs2
-rw-r--r--src/librustdoc/html/render/print_item.rs2
-rw-r--r--src/librustdoc/visit_ast.rs28
-rw-r--r--src/test/rustdoc-ui/doc-cfg.rs9
-rw-r--r--src/test/rustdoc-ui/doc-cfg.stderr26
-rw-r--r--src/test/rustdoc-ui/feature-gate-doc_cfg_hide.rs7
-rw-r--r--src/test/rustdoc-ui/feature-gate-doc_cfg_hide.stderr14
-rw-r--r--src/test/rustdoc/doc-cfg-hide.rs32
-rw-r--r--src/test/rustdoc/doc-cfg-implicit-gate.rs7
-rw-r--r--src/test/rustdoc/doc-cfg-implicit.rs31
23 files changed, 247 insertions, 10 deletions
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index 06e9d9ed329..bce5cd8174d 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -279,6 +279,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
 
                 gate_doc!(
                     cfg => doc_cfg
+                    cfg_hide => doc_cfg_hide
                     masked => doc_masked
                     notable_trait => doc_notable_trait
                     keyword => doc_keyword
diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs
index eae7f7854ec..ec2c703ad49 100644
--- a/compiler/rustc_feature/src/active.rs
+++ b/compiler/rustc_feature/src/active.rs
@@ -675,6 +675,9 @@ declare_features! (
     /// Allows `#[track_caller]` on closures and generators.
     (active, closure_track_caller, "1.57.0", Some(87417), None),
 
+    /// Allows `#[doc(cfg_hide(...))]`.
+    (active, doc_cfg_hide, "1.57.0", Some(43781), None),
+
     // -------------------------------------------------------------------------
     // feature-group-end: actual feature gates
     // -------------------------------------------------------------------------
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index e7b2a018680..1815302101f 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -938,6 +938,7 @@ impl CheckAttrVisitor<'tcx> {
                         // plugins: removed, but rustdoc warns about it itself
                         sym::alias
                         | sym::cfg
+                        | sym::cfg_hide
                         | sym::hidden
                         | sym::html_favicon_url
                         | sym::html_logo_url
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 77baf7d7381..382dbc377d6 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -399,6 +399,7 @@ symbols! {
         cfg_attr_multi,
         cfg_doctest,
         cfg_eval,
+        cfg_hide,
         cfg_panic,
         cfg_sanitize,
         cfg_target_abi,
@@ -547,6 +548,7 @@ symbols! {
         doc,
         doc_alias,
         doc_cfg,
+        doc_cfg_hide,
         doc_keyword,
         doc_masked,
         doc_notable_trait,
diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs
index ca41ce975e4..72fe84222de 100644
--- a/library/alloc/src/lib.rs
+++ b/library/alloc/src/lib.rs
@@ -67,6 +67,10 @@
     issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/",
     test(no_crate_inject, attr(allow(unused_variables), deny(warnings)))
 )]
+#![cfg_attr(
+    not(bootstrap),
+    doc(cfg_hide(not(test), not(any(test, bootstrap)), target_has_atomic = "ptr"))
+)]
 #![no_std]
 #![needs_allocator]
 #![warn(deprecated_in_future)]
@@ -146,6 +150,8 @@
 #![feature(associated_type_bounds)]
 #![feature(slice_group_by)]
 #![feature(decl_macro)]
+#![feature(doc_cfg)]
+#![cfg_attr(not(bootstrap), feature(doc_cfg_hide))]
 // Allow testing this library
 
 #[cfg(test)]
diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs
index f9d517ce8c0..630876445ba 100644
--- a/library/core/src/lib.rs
+++ b/library/core/src/lib.rs
@@ -60,6 +60,30 @@
     test(no_crate_inject, attr(deny(warnings))),
     test(attr(allow(dead_code, deprecated, unused_variables, unused_mut)))
 )]
+#![cfg_attr(
+    not(bootstrap),
+    doc(cfg_hide(
+        not(test),
+        target_pointer_width = "16",
+        target_pointer_width = "32",
+        target_pointer_width = "64",
+        target_has_atomic = "8",
+        target_has_atomic = "16",
+        target_has_atomic = "32",
+        target_has_atomic = "64",
+        target_has_atomic = "ptr",
+        target_has_atomic_equal_alignment = "8",
+        target_has_atomic_equal_alignment = "16",
+        target_has_atomic_equal_alignment = "32",
+        target_has_atomic_equal_alignment = "64",
+        target_has_atomic_equal_alignment = "ptr",
+        target_has_atomic_load_store = "8",
+        target_has_atomic_load_store = "16",
+        target_has_atomic_load_store = "32",
+        target_has_atomic_load_store = "64",
+        target_has_atomic_load_store = "ptr",
+    ))
+)]
 #![no_core]
 //
 // Lints:
@@ -133,6 +157,7 @@
 #![feature(doc_notable_trait)]
 #![feature(doc_primitive)]
 #![feature(exhaustive_patterns)]
+#![cfg_attr(not(bootstrap), feature(doc_cfg_hide))]
 #![feature(extern_types)]
 #![feature(fundamental)]
 #![feature(if_let_guard)]
diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs
index a7516bf4ffd..b16436a18f0 100644
--- a/library/std/src/lib.rs
+++ b/library/std/src/lib.rs
@@ -195,6 +195,7 @@
     test(no_crate_inject, attr(deny(warnings))),
     test(attr(allow(dead_code, deprecated, unused_variables, unused_mut)))
 )]
+#![cfg_attr(not(bootstrap), doc(cfg_hide(not(test), not(any(test, bootstrap)))))]
 // Don't link to std. We are std.
 #![no_std]
 #![warn(deprecated_in_future)]
@@ -263,6 +264,7 @@
 #![feature(custom_test_frameworks)]
 #![feature(decl_macro)]
 #![feature(doc_cfg)]
+#![cfg_attr(not(bootstrap), feature(doc_cfg_hide))]
 #![feature(doc_keyword)]
 #![feature(doc_masked)]
 #![feature(doc_notable_trait)]
diff --git a/library/std/src/os/raw/mod.rs b/library/std/src/os/raw/mod.rs
index 1e220ea30ab..30eeac14b43 100644
--- a/library/std/src/os/raw/mod.rs
+++ b/library/std/src/os/raw/mod.rs
@@ -46,6 +46,7 @@ macro_rules! type_alias {
 }
 
 type_alias! { "char.md", c_char = u8, NonZero_c_char = NonZeroU8;
+#[doc(cfg(all()))]
 #[cfg(any(
     all(
         target_os = "linux",
@@ -88,6 +89,7 @@ type_alias! { "char.md", c_char = u8, NonZero_c_char = NonZeroU8;
     all(target_os = "fuchsia", target_arch = "aarch64")
 ))]}
 type_alias! { "char.md", c_char = i8, NonZero_c_char = NonZeroI8;
+#[doc(cfg(all()))]
 #[cfg(not(any(
     all(
         target_os = "linux",
@@ -136,12 +138,16 @@ type_alias! { "ushort.md", c_ushort = u16, NonZero_c_ushort = NonZeroU16; }
 type_alias! { "int.md", c_int = i32, NonZero_c_int = NonZeroI32; }
 type_alias! { "uint.md", c_uint = u32, NonZero_c_uint = NonZeroU32; }
 type_alias! { "long.md", c_long = i32, NonZero_c_long = NonZeroI32;
+#[doc(cfg(all()))]
 #[cfg(any(target_pointer_width = "32", windows))] }
 type_alias! { "ulong.md", c_ulong = u32, NonZero_c_ulong = NonZeroU32;
+#[doc(cfg(all()))]
 #[cfg(any(target_pointer_width = "32", windows))] }
 type_alias! { "long.md", c_long = i64, NonZero_c_long = NonZeroI64;
+#[doc(cfg(all()))]
 #[cfg(all(target_pointer_width = "64", not(windows)))] }
 type_alias! { "ulong.md", c_ulong = u64, NonZero_c_ulong = NonZeroU64;
+#[doc(cfg(all()))]
 #[cfg(all(target_pointer_width = "64", not(windows)))] }
 type_alias! { "longlong.md", c_longlong = i64, NonZero_c_longlong = NonZeroI64; }
 type_alias! { "ulonglong.md", c_ulonglong = u64, NonZero_c_ulonglong = NonZeroU64; }
diff --git a/library/std/src/os/windows/raw.rs b/library/std/src/os/windows/raw.rs
index 5014e008eb5..0ef3adade5c 100644
--- a/library/std/src/os/windows/raw.rs
+++ b/library/std/src/os/windows/raw.rs
@@ -7,8 +7,10 @@ use crate::os::raw::c_void;
 #[stable(feature = "raw_ext", since = "1.1.0")]
 pub type HANDLE = *mut c_void;
 #[cfg(target_pointer_width = "32")]
+#[doc(cfg(all()))]
 #[stable(feature = "raw_ext", since = "1.1.0")]
 pub type SOCKET = u32;
 #[cfg(target_pointer_width = "64")]
+#[doc(cfg(all()))]
 #[stable(feature = "raw_ext", since = "1.1.0")]
 pub type SOCKET = u64;
diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs
index 4a888b22332..b463c1dc714 100644
--- a/src/librustdoc/clean/inline.rs
+++ b/src/librustdoc/clean/inline.rs
@@ -318,10 +318,10 @@ fn merge_attrs(
             } else {
                 Attributes::from_ast(&both, None)
             },
-            both.cfg(cx.sess()),
+            both.cfg(cx.tcx, &cx.cache.hidden_cfg),
         )
     } else {
-        (old_attrs.clean(cx), old_attrs.cfg(cx.sess()))
+        (old_attrs.clean(cx), old_attrs.cfg(cx.tcx, &cx.cache.hidden_cfg))
     }
 }
 
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index a55d85f5841..969d15dd6a1 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -1973,7 +1973,7 @@ fn clean_extern_crate(
         def_id: crate_def_id.into(),
         visibility: krate.vis.clean(cx),
         kind: box ExternCrateItem { src: orig_name },
-        cfg: attrs.cfg(cx.sess()),
+        cfg: attrs.cfg(cx.tcx, &cx.cache.hidden_cfg),
     }]
 }
 
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index 0e78fe7aec3..5b722175f98 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -421,7 +421,7 @@ impl Item {
             kind,
             box ast_attrs.clean(cx),
             cx,
-            ast_attrs.cfg(cx.sess()),
+            ast_attrs.cfg(cx.tcx, &cx.cache.hidden_cfg),
         )
     }
 
@@ -747,7 +747,7 @@ crate trait AttributesExt {
 
     fn other_attrs(&self) -> Vec<ast::Attribute>;
 
-    fn cfg(&self, sess: &Session) -> Option<Arc<Cfg>>;
+    fn cfg(&self, tcx: TyCtxt<'_>, hidden_cfg: &FxHashSet<Cfg>) -> Option<Arc<Cfg>>;
 }
 
 impl AttributesExt for [ast::Attribute] {
@@ -772,8 +772,41 @@ impl AttributesExt for [ast::Attribute] {
         self.iter().filter(|attr| attr.doc_str().is_none()).cloned().collect()
     }
 
-    fn cfg(&self, sess: &Session) -> Option<Arc<Cfg>> {
-        let mut cfg = Cfg::True;
+    fn cfg(&self, tcx: TyCtxt<'_>, hidden_cfg: &FxHashSet<Cfg>) -> Option<Arc<Cfg>> {
+        let sess = tcx.sess;
+        let doc_cfg_active = tcx.features().doc_cfg;
+
+        fn single<T: IntoIterator>(it: T) -> Option<T::Item> {
+            let mut iter = it.into_iter();
+            let item = iter.next()?;
+            if iter.next().is_some() {
+                return None;
+            }
+            Some(item)
+        }
+
+        let mut cfg = if doc_cfg_active {
+            let mut doc_cfg = self
+                .iter()
+                .filter(|attr| attr.has_name(sym::doc))
+                .flat_map(|attr| attr.meta_item_list().unwrap_or_else(Vec::new))
+                .filter(|attr| attr.has_name(sym::cfg))
+                .peekable();
+            if doc_cfg.peek().is_some() {
+                doc_cfg
+                    .filter_map(|attr| Cfg::parse(attr.meta_item()?).ok())
+                    .fold(Cfg::True, |cfg, new_cfg| cfg & new_cfg)
+            } else {
+                self.iter()
+                    .filter(|attr| attr.has_name(sym::cfg))
+                    .filter_map(|attr| single(attr.meta_item_list()?))
+                    .filter_map(|attr| Cfg::parse(attr.meta_item()?).ok())
+                    .filter(|cfg| !hidden_cfg.contains(cfg))
+                    .fold(Cfg::True, |cfg, new_cfg| cfg & new_cfg)
+            }
+        } else {
+            Cfg::True
+        };
 
         for attr in self.iter() {
             // #[doc]
@@ -800,6 +833,8 @@ impl AttributesExt for [ast::Attribute] {
             }
         }
 
+        // treat #[target_feature(enable = "feat")] attributes as if they were
+        // #[doc(cfg(target_feature = "feat"))] attributes as well
         for attr in self.lists(sym::target_feature) {
             if attr.has_name(sym::enable) {
                 if let Some(feat) = attr.value_str() {
diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs
index 43abcf095d8..9e64d200b43 100644
--- a/src/librustdoc/doctest.rs
+++ b/src/librustdoc/doctest.rs
@@ -1123,7 +1123,7 @@ impl<'a, 'hir, 'tcx> HirCollector<'a, 'hir, 'tcx> {
         let ast_attrs = self.tcx.hir().attrs(hir_id);
         let mut attrs = Attributes::from_ast(ast_attrs, None);
 
-        if let Some(ref cfg) = ast_attrs.cfg(self.sess) {
+        if let Some(ref cfg) = ast_attrs.cfg(self.tcx, &FxHashSet::default()) {
             if !cfg.matches(&self.sess.parse_sess, Some(&self.sess.features_untracked())) {
                 return;
             }
diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs
index bcfcc3d7039..cc9e081add1 100644
--- a/src/librustdoc/formats/cache.rs
+++ b/src/librustdoc/formats/cache.rs
@@ -119,6 +119,8 @@ crate struct Cache {
     ///
     /// Links are indexed by the DefId of the item they document.
     crate intra_doc_links: FxHashMap<ItemId, Vec<clean::ItemLink>>,
+    /// Cfg that have been hidden via #![doc(cfg_hide(...))]
+    crate hidden_cfg: FxHashSet<clean::cfg::Cfg>,
 }
 
 /// This struct is used to wrap the `cache` and `tcx` in order to run `DocFolder`.
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index 1275fa4e156..cab3fca708b 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -323,7 +323,7 @@ fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[cl
                     let import_item = clean::Item {
                         def_id: import_def_id.into(),
                         attrs: import_attrs,
-                        cfg: ast_attrs.cfg(cx.sess()),
+                        cfg: ast_attrs.cfg(cx.tcx(), &cx.cache().hidden_cfg),
                         ..myitem.clone()
                     };
 
diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs
index 4f5495a176d..36b1a14f6c1 100644
--- a/src/librustdoc/visit_ast.rs
+++ b/src/librustdoc/visit_ast.rs
@@ -6,6 +6,7 @@ use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::DefId;
 use rustc_hir::Node;
+use rustc_hir::CRATE_HIR_ID;
 use rustc_middle::middle::privacy::AccessLevel;
 use rustc_middle::ty::TyCtxt;
 use rustc_span;
@@ -15,7 +16,7 @@ use rustc_span::symbol::{kw, sym, Symbol};
 
 use std::mem;
 
-use crate::clean::{self, AttributesExt, NestedAttributesExt};
+use crate::clean::{self, cfg::Cfg, AttributesExt, NestedAttributesExt};
 use crate::core;
 use crate::doctree::*;
 
@@ -97,6 +98,31 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
                 }
             }
         }
+
+        self.cx.cache.hidden_cfg = self
+            .cx
+            .tcx
+            .hir()
+            .attrs(CRATE_HIR_ID)
+            .iter()
+            .filter(|attr| attr.has_name(sym::doc))
+            .flat_map(|attr| attr.meta_item_list().into_iter().flatten())
+            .filter(|attr| attr.has_name(sym::cfg_hide))
+            .flat_map(|attr| {
+                attr.meta_item_list()
+                    .unwrap_or(&[])
+                    .iter()
+                    .filter_map(|attr| {
+                        Some(
+                            Cfg::parse(attr.meta_item()?)
+                                .map_err(|e| self.cx.sess().diagnostic().span_err(e.span, e.msg))
+                                .ok()?,
+                        )
+                    })
+                    .collect::<Vec<_>>()
+            })
+            .collect();
+
         self.cx.cache.exact_paths = self.exact_paths;
         top_level_module
     }
diff --git a/src/test/rustdoc-ui/doc-cfg.rs b/src/test/rustdoc-ui/doc-cfg.rs
new file mode 100644
index 00000000000..354d76bc3c4
--- /dev/null
+++ b/src/test/rustdoc-ui/doc-cfg.rs
@@ -0,0 +1,9 @@
+#![feature(doc_cfg)]
+
+#[doc(cfg(), cfg(foo, bar))]
+//~^ ERROR
+//~^^ ERROR
+#[doc(cfg(foo), cfg(bar))] // ok!
+#[doc(cfg())] //~ ERROR
+#[doc(cfg(foo, bar))] //~ ERROR
+pub fn foo() {}
diff --git a/src/test/rustdoc-ui/doc-cfg.stderr b/src/test/rustdoc-ui/doc-cfg.stderr
new file mode 100644
index 00000000000..b379f6febe2
--- /dev/null
+++ b/src/test/rustdoc-ui/doc-cfg.stderr
@@ -0,0 +1,26 @@
+error: `cfg` predicate is not specified
+  --> $DIR/doc-cfg.rs:3:7
+   |
+LL | #[doc(cfg(), cfg(foo, bar))]
+   |       ^^^^^
+
+error: multiple `cfg` predicates are specified
+  --> $DIR/doc-cfg.rs:3:23
+   |
+LL | #[doc(cfg(), cfg(foo, bar))]
+   |                       ^^^
+
+error: `cfg` predicate is not specified
+  --> $DIR/doc-cfg.rs:7:7
+   |
+LL | #[doc(cfg())]
+   |       ^^^^^
+
+error: multiple `cfg` predicates are specified
+  --> $DIR/doc-cfg.rs:8:16
+   |
+LL | #[doc(cfg(foo, bar))]
+   |                ^^^
+
+error: aborting due to 4 previous errors
+
diff --git a/src/test/rustdoc-ui/feature-gate-doc_cfg_hide.rs b/src/test/rustdoc-ui/feature-gate-doc_cfg_hide.rs
new file mode 100644
index 00000000000..17812018b9b
--- /dev/null
+++ b/src/test/rustdoc-ui/feature-gate-doc_cfg_hide.rs
@@ -0,0 +1,7 @@
+#![doc(cfg_hide(test))]
+//~^ ERROR `#[doc(cfg_hide)]` is experimental
+
+#[cfg(not(test))]
+pub fn public_fn() {}
+#[cfg(test)]
+pub fn internal_use_only() {}
diff --git a/src/test/rustdoc-ui/feature-gate-doc_cfg_hide.stderr b/src/test/rustdoc-ui/feature-gate-doc_cfg_hide.stderr
new file mode 100644
index 00000000000..ba42c7bbb05
--- /dev/null
+++ b/src/test/rustdoc-ui/feature-gate-doc_cfg_hide.stderr
@@ -0,0 +1,14 @@
+error[E0658]: `#[doc(cfg_hide)]` is experimental
+  --> $DIR/feature-gate-doc_cfg_hide.rs:1:1
+   |
+LL | #![doc(cfg_hide(test))]
+   | ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #43781 <https://github.com/rust-lang/rust/issues/43781> for more information
+   = help: add `#![feature(doc_cfg_hide)]` to the crate attributes to enable
+
+error: Compilation failed, aborting rustdoc
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/rustdoc/doc-cfg-hide.rs b/src/test/rustdoc/doc-cfg-hide.rs
new file mode 100644
index 00000000000..b9d0d323137
--- /dev/null
+++ b/src/test/rustdoc/doc-cfg-hide.rs
@@ -0,0 +1,32 @@
+#![crate_name = "oud"]
+#![feature(doc_cfg, doc_cfg_hide)]
+
+#![doc(cfg_hide(feature = "solecism"))]
+
+// @has 'oud/struct.Solecism.html'
+// @count   - '//*[@class="stab portability"]' 0
+// compile-flags:--cfg feature="solecism"
+#[cfg(feature = "solecism")]
+pub struct Solecism;
+
+// @has 'oud/struct.Scribacious.html'
+// @count   - '//*[@class="stab portability"]' 1
+// @matches - '//*[@class="stab portability"]' 'crate feature solecism'
+#[cfg(feature = "solecism")]
+#[doc(cfg(feature = "solecism"))]
+pub struct Scribacious;
+
+// @has 'oud/struct.Hyperdulia.html'
+// @count   - '//*[@class="stab portability"]' 1
+// @matches - '//*[@class="stab portability"]' 'crate feature hyperdulia'
+// compile-flags:--cfg feature="hyperdulia"
+#[cfg(feature = "solecism")]
+#[cfg(feature = "hyperdulia")]
+pub struct Hyperdulia;
+
+// @has 'oud/struct.Oystercatcher.html'
+// @count   - '//*[@class="stab portability"]' 1
+// @matches - '//*[@class="stab portability"]' 'crate features solecism and oystercatcher'
+// compile-flags:--cfg feature="oystercatcher"
+#[cfg(all(feature = "solecism", feature = "oystercatcher"))]
+pub struct Oystercatcher;
diff --git a/src/test/rustdoc/doc-cfg-implicit-gate.rs b/src/test/rustdoc/doc-cfg-implicit-gate.rs
new file mode 100644
index 00000000000..92804d3729b
--- /dev/null
+++ b/src/test/rustdoc/doc-cfg-implicit-gate.rs
@@ -0,0 +1,7 @@
+// compile-flags:--cfg feature="worricow"
+#![crate_name = "xenogenous"]
+
+// @has 'xenogenous/struct.Worricow.html'
+// @count   - '//*[@class="stab portability"]' 0
+#[cfg(feature = "worricow")]
+pub struct Worricow;
diff --git a/src/test/rustdoc/doc-cfg-implicit.rs b/src/test/rustdoc/doc-cfg-implicit.rs
new file mode 100644
index 00000000000..36c2025785d
--- /dev/null
+++ b/src/test/rustdoc/doc-cfg-implicit.rs
@@ -0,0 +1,31 @@
+#![crate_name = "funambulism"]
+#![feature(doc_cfg)]
+
+// @has 'funambulism/struct.Disorbed.html'
+// @count   - '//*[@class="stab portability"]' 1
+// @matches - '//*[@class="stab portability"]' 'crate feature disorbed'
+// compile-flags:--cfg feature="disorbed"
+#[cfg(feature = "disorbed")]
+pub struct Disorbed;
+
+// @has 'funambulism/struct.Aesthesia.html'
+// @count   - '//*[@class="stab portability"]' 1
+// @matches - '//*[@class="stab portability"]' 'crate feature aesthesia'
+// compile-flags:--cfg feature="aesthesia"
+#[doc(cfg(feature = "aesthesia"))]
+pub struct Aesthesia;
+
+// @has 'funambulism/struct.Pliothermic.html'
+// @count   - '//*[@class="stab portability"]' 1
+// @matches - '//*[@class="stab portability"]' 'crate feature pliothermic'
+// compile-flags:--cfg feature="epopoeist"
+#[cfg(feature = "epopoeist")]
+#[doc(cfg(feature = "pliothermic"))]
+pub struct Pliothermic;
+
+// @has 'funambulism/struct.Simillimum.html'
+// @count   - '//*[@class="stab portability"]' 0
+// compile-flags:--cfg feature="simillimum"
+#[cfg(feature = "simillimum")]
+#[doc(cfg(all()))]
+pub struct Simillimum;