about summary refs log tree commit diff
diff options
context:
space:
mode:
authorGuillaume Gomez <guillaume1.gomez@gmail.com>2025-05-29 18:39:08 +0200
committerGuillaume Gomez <guillaume1.gomez@gmail.com>2025-09-27 11:29:49 +0200
commit553308b11503eafac6341b82f345bd62b09ba317 (patch)
tree0149dbb52ae9088cde28314584b7556227ae58aa
parent1561efe41afe40c1afaf3d11316ef754d9a8f9a9 (diff)
downloadrust-553308b11503eafac6341b82f345bd62b09ba317.tar.gz
rust-553308b11503eafac6341b82f345bd62b09ba317.zip
Improve code and better check `doc(cfg(...))` attributes
-rw-r--r--compiler/rustc_ast_passes/src/feature_gate.rs1
-rw-r--r--compiler/rustc_passes/messages.ftl2
-rw-r--r--compiler/rustc_passes/src/check_attr.rs23
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--src/librustdoc/clean/inline.rs4
-rw-r--r--src/librustdoc/clean/types.rs89
-rw-r--r--tests/rustdoc-ui/doc-cfg.rs9
-rw-r--r--tests/rustdoc-ui/doc-cfg.stderr66
-rw-r--r--tests/rustdoc-ui/feature-gate-doc_cfg.rs6
-rw-r--r--tests/rustdoc-ui/feature-gate-doc_cfg.stderr63
-rw-r--r--tests/rustdoc-ui/feature-gate-doc_cfg_hide.rs8
-rw-r--r--tests/rustdoc-ui/feature-gate-doc_cfg_hide.stderr10
-rw-r--r--tests/rustdoc-ui/lints/doc_cfg_hide.stderr2
13 files changed, 209 insertions, 75 deletions
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index 35531378784..608ccfefeb6 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -183,6 +183,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
                 gate_doc!(
                     "experimental" {
                         cfg => doc_cfg
+                        auto_cfg => doc_cfg
                         masked => doc_masked
                         notable_trait => doc_notable_trait
                     }
diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl
index a9a0f6d8b51..a5ff169e638 100644
--- a/compiler/rustc_passes/messages.ftl
+++ b/compiler/rustc_passes/messages.ftl
@@ -150,7 +150,7 @@ passes_doc_auto_cfg_hide_show_expects_list =
     `#![doc(auto_cfg({$attr_name}(...)))]` expects a list of items
 
 passes_doc_auto_cfg_hide_show_unexpected_item =
-    `#![doc(auto_cfg({$attr_name}(...)))]` only accepts identifiers or key/values items
+    `#![doc(auto_cfg({$attr_name}(...)))]` only accepts identifiers or key/value items
 
 passes_doc_auto_cfg_wrong_literal =
     `expected boolean for #[doc(auto_cfg = ...)]`
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index c521467b556..88b49c781e7 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -1176,7 +1176,15 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
             }
             MetaItemKind::List(list) => {
                 for item in list {
-                    let Some(attr_name) = item.name() else { continue };
+                    let Some(attr_name) = item.name() else {
+                        self.tcx.emit_node_span_lint(
+                            INVALID_DOC_ATTRIBUTES,
+                            hir_id,
+                            meta.span,
+                            errors::DocAutoCfgExpectsHideOrShow,
+                        );
+                        continue;
+                    };
                     if attr_name != sym::hide && attr_name != sym::show {
                         self.tcx.emit_node_span_lint(
                             INVALID_DOC_ATTRIBUTES,
@@ -1195,6 +1203,19 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                                         attr_name: attr_name.as_str(),
                                     },
                                 );
+                            } else if match item {
+                                MetaItemInner::Lit(_) => true,
+                                // We already checked above that it's not a list.
+                                MetaItemInner::MetaItem(meta) => meta.path.segments.len() != 1,
+                            } {
+                                self.tcx.emit_node_span_lint(
+                                    INVALID_DOC_ATTRIBUTES,
+                                    hir_id,
+                                    item.span(),
+                                    errors::DocAutoCfgHideShowUnexpectedItem {
+                                        attr_name: attr_name.as_str(),
+                                    },
+                                );
                             }
                         }
                     } else {
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index e80a98994db..18c3faed932 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -1150,7 +1150,6 @@ symbols! {
         hashset_iter_ty,
         hexagon_target_feature,
         hidden,
-        hidden_cfg,
         hide,
         hint,
         homogeneous_aggregate,
diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs
index 8ffa6033c9b..8beea0580de 100644
--- a/src/librustdoc/clean/inline.rs
+++ b/src/librustdoc/clean/inline.rs
@@ -605,6 +605,10 @@ pub(crate) fn build_impl(
         });
     }
 
+    // In here, we pass an empty `CfgInfo` because the computation of `cfg` happens later, so it
+    // doesn't matter at this point.
+    //
+    // We need to pass this empty `CfgInfo` because `merge_attrs` is used when computing the `cfg`.
     let (merged_attrs, cfg) = merge_attrs(cx, load_attrs(cx, did), attrs, &mut CfgInfo::default());
     trace!("merged_attrs={merged_attrs:?}");
 
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index 545f1ef6da4..605b77109a0 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -970,8 +970,10 @@ fn show_hide_show_conflict_error(
     diag.emit();
 }
 
-/// This function checks if a same `cfg` is present in both `auto_cfg(hide(...))` and
-/// `auto_cfg(show(...))` on the same item. If so, it emits an error.
+/// This functions updates the `hidden_cfg` field of the provided `cfg_info` argument.
+///
+/// It also checks if a same `cfg` is present in both `auto_cfg(hide(...))` and
+/// `auto_cfg(show(...))` on the same item and emits an error if it's the case.
 ///
 /// Because we go through a list of `cfg`s, we keep track of the `cfg`s we saw in `new_show_attrs`
 /// and in `new_hide_attrs` arguments.
@@ -1023,6 +1025,31 @@ pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator<Item = &'a hir::Attribute>
         Some(item)
     }
 
+    fn check_changed_auto_active_status(
+        changed_auto_active_status: &mut Option<rustc_span::Span>,
+        attr: &ast::MetaItem,
+        cfg_info: &mut CfgInfo,
+        tcx: TyCtxt<'_>,
+        new_value: bool,
+    ) -> bool {
+        if let Some(first_change) = changed_auto_active_status {
+            if cfg_info.auto_cfg_active != new_value {
+                tcx.sess
+                    .dcx()
+                    .struct_span_err(
+                        vec![*first_change, attr.span],
+                        "`auto_cfg` was disabled and enabled more than once on the same item",
+                    )
+                    .emit();
+                return true;
+            }
+        } else {
+            *changed_auto_active_status = Some(attr.span);
+        }
+        cfg_info.auto_cfg_active = new_value;
+        false
+    }
+
     let mut new_show_attrs = FxHashMap::default();
     let mut new_hide_attrs = FxHashMap::default();
 
@@ -1070,49 +1097,39 @@ pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator<Item = &'a hir::Attribute>
                 };
                 match &attr.kind {
                     MetaItemKind::Word => {
-                        if let Some(first_change) = changed_auto_active_status {
-                            if !cfg_info.auto_cfg_active {
-                                tcx.sess.dcx().struct_span_err(
-                                    vec![first_change, attr.span],
-                                    "`auto_cfg` was disabled and enabled more than once on the same item",
-                                ).emit();
-                                return None;
-                            }
-                        } else {
-                            changed_auto_active_status = Some(attr.span);
+                        if check_changed_auto_active_status(
+                            &mut changed_auto_active_status,
+                            attr,
+                            cfg_info,
+                            tcx,
+                            true,
+                        ) {
+                            return None;
                         }
-                        cfg_info.auto_cfg_active = true;
                     }
                     MetaItemKind::NameValue(lit) => {
                         if let LitKind::Bool(value) = lit.kind {
-                            if let Some(first_change) = changed_auto_active_status {
-                                if cfg_info.auto_cfg_active != value {
-                                    tcx.sess.dcx().struct_span_err(
-                                        vec![first_change, attr.span],
-                                        "`auto_cfg` was disabled and enabled more than once on the same item",
-                                    ).emit();
-                                    return None;
-                                }
-                            } else {
-                                changed_auto_active_status = Some(attr.span);
+                            if check_changed_auto_active_status(
+                                &mut changed_auto_active_status,
+                                attr,
+                                cfg_info,
+                                tcx,
+                                value,
+                            ) {
+                                return None;
                             }
-                            cfg_info.auto_cfg_active = value;
                         }
                     }
                     MetaItemKind::List(sub_attrs) => {
-                        if let Some(first_change) = changed_auto_active_status {
-                            if !cfg_info.auto_cfg_active {
-                                tcx.sess.dcx().struct_span_err(
-                                    vec![first_change, attr.span],
-                                    "`auto_cfg` was disabled and enabled more than once on the same item",
-                                ).emit();
-                                return None;
-                            }
-                        } else {
-                            changed_auto_active_status = Some(attr.span);
+                        if check_changed_auto_active_status(
+                            &mut changed_auto_active_status,
+                            attr,
+                            cfg_info,
+                            tcx,
+                            true,
+                        ) {
+                            return None;
                         }
-                        // Whatever happens next, the feature is enabled again.
-                        cfg_info.auto_cfg_active = true;
                         for sub_attr in sub_attrs.iter() {
                             if let Some(ident) = sub_attr.ident()
                                 && (ident.name == sym::show || ident.name == sym::hide)
diff --git a/tests/rustdoc-ui/doc-cfg.rs b/tests/rustdoc-ui/doc-cfg.rs
index 14943bbc341..9840c305290 100644
--- a/tests/rustdoc-ui/doc-cfg.rs
+++ b/tests/rustdoc-ui/doc-cfg.rs
@@ -8,4 +8,13 @@
 //~^^ WARN unexpected `cfg` condition name: `bar`
 #[doc(cfg())] //~ ERROR
 #[doc(cfg(foo, bar))] //~ ERROR
+#[doc(auto_cfg(42))] //~ ERROR
+#[doc(auto_cfg(hide(true)))] //~ ERROR
+#[doc(auto_cfg(hide(42)))] //~ ERROR
+#[doc(auto_cfg(hide("a")))] //~ ERROR
+#[doc(auto_cfg(hide(foo::bar)))] //~ ERROR
+// Shouldn't lint
+#[doc(auto_cfg(hide(windows)))]
+#[doc(auto_cfg(hide(feature = "windows")))]
+#[doc(auto_cfg(hide(foo)))]
 pub fn foo() {}
diff --git a/tests/rustdoc-ui/doc-cfg.stderr b/tests/rustdoc-ui/doc-cfg.stderr
index 1233ee010de..36ca18eed8f 100644
--- a/tests/rustdoc-ui/doc-cfg.stderr
+++ b/tests/rustdoc-ui/doc-cfg.stderr
@@ -1,26 +1,34 @@
-error: `cfg` predicate is not specified
-  --> $DIR/doc-cfg.rs:3:7
+error: `only "hide" or "show" are allowed in "#[doc(auto_cfg(...))]"`
+  --> $DIR/doc-cfg.rs:11:7
    |
-LL | #[doc(cfg(), cfg(foo, bar))]
-   |       ^^^^^ help: expected syntax is: `cfg(/* predicate */)`
+LL | #[doc(auto_cfg(42))]
+   |       ^^^^^^^^^^^^
+   |
+   = note: `#[deny(invalid_doc_attributes)]` on by default
 
-error: multiple `cfg` predicates are specified
-  --> $DIR/doc-cfg.rs:3:23
+error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or key/value items
+  --> $DIR/doc-cfg.rs:12:21
    |
-LL | #[doc(cfg(), cfg(foo, bar))]
-   |                       ^^^
+LL | #[doc(auto_cfg(hide(true)))]
+   |                     ^^^^
 
-error: `cfg` predicate is not specified
-  --> $DIR/doc-cfg.rs:9:7
+error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or key/value items
+  --> $DIR/doc-cfg.rs:13:21
    |
-LL | #[doc(cfg())]
-   |       ^^^^^ help: expected syntax is: `cfg(/* predicate */)`
+LL | #[doc(auto_cfg(hide(42)))]
+   |                     ^^
 
-error: multiple `cfg` predicates are specified
-  --> $DIR/doc-cfg.rs:10:16
+error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or key/value items
+  --> $DIR/doc-cfg.rs:14:21
    |
-LL | #[doc(cfg(foo, bar))]
-   |                ^^^
+LL | #[doc(auto_cfg(hide("a")))]
+   |                     ^^^
+
+error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or key/value items
+  --> $DIR/doc-cfg.rs:15:21
+   |
+LL | #[doc(auto_cfg(hide(foo::bar)))]
+   |                     ^^^^^^^^
 
 warning: unexpected `cfg` condition name: `foo`
   --> $DIR/doc-cfg.rs:6:11
@@ -42,5 +50,29 @@ LL | #[doc(cfg(foo), cfg(bar))]
    = help: to expect this configuration use `--check-cfg=cfg(bar)`
    = note: see <https://doc.rust-lang.org/nightly/rustc/check-cfg.html> for more information about checking conditional configuration
 
-error: aborting due to 4 previous errors; 2 warnings emitted
+error: `cfg` predicate is not specified
+  --> $DIR/doc-cfg.rs:3:7
+   |
+LL | #[doc(cfg(), cfg(foo, bar))]
+   |       ^^^^^ help: expected syntax is: `cfg(/* predicate */)`
+
+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:9:7
+   |
+LL | #[doc(cfg())]
+   |       ^^^^^ help: expected syntax is: `cfg(/* predicate */)`
+
+error: multiple `cfg` predicates are specified
+  --> $DIR/doc-cfg.rs:10:16
+   |
+LL | #[doc(cfg(foo, bar))]
+   |                ^^^
+
+error: aborting due to 9 previous errors; 2 warnings emitted
 
diff --git a/tests/rustdoc-ui/feature-gate-doc_cfg.rs b/tests/rustdoc-ui/feature-gate-doc_cfg.rs
new file mode 100644
index 00000000000..b474a1524bc
--- /dev/null
+++ b/tests/rustdoc-ui/feature-gate-doc_cfg.rs
@@ -0,0 +1,6 @@
+#![doc(auto_cfg)] //~ ERROR
+#![doc(auto_cfg(false))] //~ ERROR
+#![doc(auto_cfg(true))] //~ ERROR
+#![doc(auto_cfg(hide(feature = "solecism")))] //~ ERROR
+#![doc(auto_cfg(show(feature = "bla")))] //~ ERROR
+#![doc(cfg(feature = "solecism"))] //~ ERROR
diff --git a/tests/rustdoc-ui/feature-gate-doc_cfg.stderr b/tests/rustdoc-ui/feature-gate-doc_cfg.stderr
new file mode 100644
index 00000000000..68a86c1abb7
--- /dev/null
+++ b/tests/rustdoc-ui/feature-gate-doc_cfg.stderr
@@ -0,0 +1,63 @@
+error[E0658]: `#[doc(auto_cfg)]` is experimental
+  --> $DIR/feature-gate-doc_cfg.rs:1:1
+   |
+LL | #![doc(auto_cfg)]
+   | ^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #43781 <https://github.com/rust-lang/rust/issues/43781> for more information
+   = help: add `#![feature(doc_cfg)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: `#[doc(auto_cfg)]` is experimental
+  --> $DIR/feature-gate-doc_cfg.rs:2:1
+   |
+LL | #![doc(auto_cfg(false))]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #43781 <https://github.com/rust-lang/rust/issues/43781> for more information
+   = help: add `#![feature(doc_cfg)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: `#[doc(auto_cfg)]` is experimental
+  --> $DIR/feature-gate-doc_cfg.rs:3:1
+   |
+LL | #![doc(auto_cfg(true))]
+   | ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #43781 <https://github.com/rust-lang/rust/issues/43781> for more information
+   = help: add `#![feature(doc_cfg)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: `#[doc(auto_cfg)]` is experimental
+  --> $DIR/feature-gate-doc_cfg.rs:4:1
+   |
+LL | #![doc(auto_cfg(hide(feature = "solecism")))]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #43781 <https://github.com/rust-lang/rust/issues/43781> for more information
+   = help: add `#![feature(doc_cfg)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: `#[doc(auto_cfg)]` is experimental
+  --> $DIR/feature-gate-doc_cfg.rs:5:1
+   |
+LL | #![doc(auto_cfg(show(feature = "bla")))]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #43781 <https://github.com/rust-lang/rust/issues/43781> for more information
+   = help: add `#![feature(doc_cfg)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: `#[doc(cfg)]` is experimental
+  --> $DIR/feature-gate-doc_cfg.rs:6:1
+   |
+LL | #![doc(cfg(feature = "solecism"))]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #43781 <https://github.com/rust-lang/rust/issues/43781> for more information
+   = help: add `#![feature(doc_cfg)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error: aborting due to 6 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/rustdoc-ui/feature-gate-doc_cfg_hide.rs b/tests/rustdoc-ui/feature-gate-doc_cfg_hide.rs
deleted file mode 100644
index e49285a01b8..00000000000
--- a/tests/rustdoc-ui/feature-gate-doc_cfg_hide.rs
+++ /dev/null
@@ -1,8 +0,0 @@
-// FIXME: Remove this file once feature is removed
-
-#![doc(cfg_hide(test))] //~ ERROR
-
-#[cfg(not(test))]
-pub fn public_fn() {}
-#[cfg(test)]
-pub fn internal_use_only() {}
diff --git a/tests/rustdoc-ui/feature-gate-doc_cfg_hide.stderr b/tests/rustdoc-ui/feature-gate-doc_cfg_hide.stderr
deleted file mode 100644
index b3eee2af7f7..00000000000
--- a/tests/rustdoc-ui/feature-gate-doc_cfg_hide.stderr
+++ /dev/null
@@ -1,10 +0,0 @@
-error: unknown `doc` attribute `cfg_hide`
-  --> $DIR/feature-gate-doc_cfg_hide.rs:3:8
-   |
-LL | #![doc(cfg_hide(test))]
-   |        ^^^^^^^^^^^^^^
-   |
-   = note: `#[deny(invalid_doc_attributes)]` on by default
-
-error: aborting due to 1 previous error
-
diff --git a/tests/rustdoc-ui/lints/doc_cfg_hide.stderr b/tests/rustdoc-ui/lints/doc_cfg_hide.stderr
index 9e820a77b5e..c63c8d607fa 100644
--- a/tests/rustdoc-ui/lints/doc_cfg_hide.stderr
+++ b/tests/rustdoc-ui/lints/doc_cfg_hide.stderr
@@ -12,7 +12,7 @@ error: `#![doc(auto_cfg(hide(...)))]` expects a list of items
 LL | #![doc(auto_cfg(hide))]
    |        ^^^^^^^^^^^^^^
 
-error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or key/values items
+error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or key/value items
   --> $DIR/doc_cfg_hide.rs:4:22
    |
 LL | #![doc(auto_cfg(hide(not(windows))))]