about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorGuillaume Gomez <guillaume1.gomez@gmail.com>2025-10-02 15:24:04 +0200
committerGuillaume Gomez <guillaume1.gomez@gmail.com>2025-10-02 15:24:04 +0200
commit6275a19255afb2a09b932986928e81cf431a7358 (patch)
treedd2a72aba04f07e319d0bf987d1f56111c5b4183 /src
parent4b9c62b4da3e17cee99d3d2052f1c576b188e2a8 (diff)
downloadrust-6275a19255afb2a09b932986928e81cf431a7358.tar.gz
rust-6275a19255afb2a09b932986928e81cf431a7358.zip
Move doc_cfg-specific code into `cfg.rs`
Diffstat (limited to 'src')
-rw-r--r--src/librustdoc/clean/cfg.rs267
-rw-r--r--src/librustdoc/clean/mod.rs1
-rw-r--r--src/librustdoc/clean/types.rs264
3 files changed, 268 insertions, 264 deletions
diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs
index aa614b73dea..881a81b22f0 100644
--- a/src/librustdoc/clean/cfg.rs
+++ b/src/librustdoc/clean/cfg.rs
@@ -3,14 +3,18 @@
 // FIXME: Once the portability lint RFC is implemented (see tracking issue #41619),
 // switch to use those structures instead.
 
+use std::sync::Arc;
 use std::{fmt, mem, ops};
 
 use itertools::Either;
 use rustc_ast::{LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit};
-use rustc_data_structures::fx::FxHashSet;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_hir::attrs::AttributeKind;
+use rustc_middle::ty::TyCtxt;
 use rustc_session::parse::ParseSess;
 use rustc_span::Span;
 use rustc_span::symbol::{Symbol, sym};
+use {rustc_ast as ast, rustc_hir as hir};
 
 use crate::display::{Joined as _, MaybeDisplay, Wrapped};
 use crate::html::escape::Escape;
@@ -600,3 +604,264 @@ impl fmt::Display for Display<'_> {
         }
     }
 }
+
+/// This type keeps track of (doc) cfg information as we go down the item tree.
+#[derive(Clone, Debug)]
+pub(crate) struct CfgInfo {
+    /// List of currently active `doc(auto_cfg(hide(...)))` cfgs, minus currently active
+    /// `doc(auto_cfg(show(...)))` cfgs.
+    hidden_cfg: FxHashSet<Cfg>,
+    /// Current computed `cfg`. Each time we enter a new item, this field is updated as well while
+    /// taking into account the `hidden_cfg` information.
+    current_cfg: Cfg,
+    /// Whether the `doc(auto_cfg())` feature is enabled or not at this point.
+    auto_cfg_active: bool,
+    /// If the parent item used `doc(cfg(...))`, then we don't want to overwrite `current_cfg`,
+    /// instead we will concatenate with it. However, if it's not the case, we need to overwrite
+    /// `current_cfg`.
+    parent_is_doc_cfg: bool,
+}
+
+impl Default for CfgInfo {
+    fn default() -> Self {
+        Self {
+            hidden_cfg: FxHashSet::from_iter([
+                Cfg::Cfg(sym::test, None),
+                Cfg::Cfg(sym::doc, None),
+                Cfg::Cfg(sym::doctest, None),
+            ]),
+            current_cfg: Cfg::True,
+            auto_cfg_active: true,
+            parent_is_doc_cfg: false,
+        }
+    }
+}
+
+fn show_hide_show_conflict_error(
+    tcx: TyCtxt<'_>,
+    item_span: rustc_span::Span,
+    previous: rustc_span::Span,
+) {
+    let mut diag = tcx.sess.dcx().struct_span_err(
+        item_span,
+        format!(
+            "same `cfg` was in `auto_cfg(hide(...))` and `auto_cfg(show(...))` on the same item"
+        ),
+    );
+    diag.span_note(previous, "first change was here");
+    diag.emit();
+}
+
+/// 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.
+fn handle_auto_cfg_hide_show(
+    tcx: TyCtxt<'_>,
+    cfg_info: &mut CfgInfo,
+    sub_attr: &MetaItemInner,
+    is_show: bool,
+    new_show_attrs: &mut FxHashMap<(Symbol, Option<Symbol>), rustc_span::Span>,
+    new_hide_attrs: &mut FxHashMap<(Symbol, Option<Symbol>), rustc_span::Span>,
+) {
+    if let MetaItemInner::MetaItem(item) = sub_attr
+        && let MetaItemKind::List(items) = &item.kind
+    {
+        for item in items {
+            // FIXME: Report in case `Cfg::parse` reports an error?
+            if let Ok(Cfg::Cfg(key, value)) = Cfg::parse(item) {
+                if is_show {
+                    if let Some(span) = new_hide_attrs.get(&(key, value)) {
+                        show_hide_show_conflict_error(tcx, item.span(), *span);
+                    } else {
+                        new_show_attrs.insert((key, value), item.span());
+                    }
+                    cfg_info.hidden_cfg.remove(&Cfg::Cfg(key, value));
+                } else {
+                    if let Some(span) = new_show_attrs.get(&(key, value)) {
+                        show_hide_show_conflict_error(tcx, item.span(), *span);
+                    } else {
+                        new_hide_attrs.insert((key, value), item.span());
+                    }
+                    cfg_info.hidden_cfg.insert(Cfg::Cfg(key, value));
+                }
+            }
+        }
+    }
+}
+
+pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator<Item = &'a hir::Attribute> + Clone>(
+    attrs: I,
+    tcx: TyCtxt<'_>,
+    cfg_info: &mut CfgInfo,
+) -> Option<Arc<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)
+    }
+
+    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();
+
+    let mut doc_cfg = attrs
+        .clone()
+        .filter(|attr| attr.has_name(sym::doc))
+        .flat_map(|attr| attr.meta_item_list().unwrap_or_default())
+        .filter(|attr| attr.has_name(sym::cfg))
+        .peekable();
+    // If the item uses `doc(cfg(...))`, then we ignore the other `cfg(...)` attributes.
+    if doc_cfg.peek().is_some() {
+        let sess = tcx.sess;
+        // We overwrite existing `cfg`.
+        if !cfg_info.parent_is_doc_cfg {
+            cfg_info.current_cfg = Cfg::True;
+            cfg_info.parent_is_doc_cfg = true;
+        }
+        for attr in doc_cfg {
+            if let Some(cfg_mi) =
+                attr.meta_item().and_then(|attr| rustc_expand::config::parse_cfg(attr, sess))
+            {
+                match Cfg::parse(cfg_mi) {
+                    Ok(new_cfg) => cfg_info.current_cfg &= new_cfg,
+                    Err(e) => {
+                        sess.dcx().span_err(e.span, e.msg);
+                    }
+                }
+            }
+        }
+    } else {
+        cfg_info.parent_is_doc_cfg = false;
+    }
+
+    let mut changed_auto_active_status = None;
+
+    // We get all `doc(auto_cfg)`, `cfg` and `target_feature` attributes.
+    for attr in attrs {
+        if let Some(ident) = attr.ident()
+            && ident.name == sym::doc
+            && let Some(attrs) = attr.meta_item_list()
+        {
+            for attr in attrs.iter().filter(|attr| attr.has_name(sym::auto_cfg)) {
+                let MetaItemInner::MetaItem(attr) = attr else {
+                    continue;
+                };
+                match &attr.kind {
+                    MetaItemKind::Word => {
+                        if check_changed_auto_active_status(
+                            &mut changed_auto_active_status,
+                            attr,
+                            cfg_info,
+                            tcx,
+                            true,
+                        ) {
+                            return None;
+                        }
+                    }
+                    MetaItemKind::NameValue(lit) => {
+                        if let LitKind::Bool(value) = lit.kind {
+                            if check_changed_auto_active_status(
+                                &mut changed_auto_active_status,
+                                attr,
+                                cfg_info,
+                                tcx,
+                                value,
+                            ) {
+                                return None;
+                            }
+                        }
+                    }
+                    MetaItemKind::List(sub_attrs) => {
+                        if check_changed_auto_active_status(
+                            &mut changed_auto_active_status,
+                            attr,
+                            cfg_info,
+                            tcx,
+                            true,
+                        ) {
+                            return None;
+                        }
+                        for sub_attr in sub_attrs.iter() {
+                            if let Some(ident) = sub_attr.ident()
+                                && (ident.name == sym::show || ident.name == sym::hide)
+                            {
+                                handle_auto_cfg_hide_show(
+                                    tcx,
+                                    cfg_info,
+                                    &sub_attr,
+                                    ident.name == sym::show,
+                                    &mut new_show_attrs,
+                                    &mut new_hide_attrs,
+                                );
+                            }
+                        }
+                    }
+                }
+            }
+        } else if let hir::Attribute::Parsed(AttributeKind::TargetFeature { features, .. }) = attr {
+            // Treat `#[target_feature(enable = "feat")]` attributes as if they were
+            // `#[doc(cfg(target_feature = "feat"))]` attributes as well.
+            for (feature, _) in features {
+                cfg_info.current_cfg &= Cfg::Cfg(sym::target_feature, Some(*feature));
+            }
+            continue;
+        } else if !cfg_info.parent_is_doc_cfg
+            && let Some(ident) = attr.ident()
+            && matches!(ident.name, sym::cfg | sym::cfg_trace)
+            && let Some(attr) = single(attr.meta_item_list()?)
+            && let Ok(new_cfg) = Cfg::parse(&attr)
+        {
+            cfg_info.current_cfg &= new_cfg;
+        }
+    }
+
+    // If `doc(auto_cfg)` feature is disabled and `doc(cfg())` wasn't used, there is nothing
+    // to be done here.
+    if !cfg_info.auto_cfg_active && !cfg_info.parent_is_doc_cfg {
+        None
+    } else if cfg_info.parent_is_doc_cfg {
+        if cfg_info.current_cfg == Cfg::True {
+            None
+        } else {
+            Some(Arc::new(cfg_info.current_cfg.clone()))
+        }
+    } else {
+        // If `doc(auto_cfg)` feature is enabled, we want to collect all `cfg` items, we remove the
+        // hidden ones afterward.
+        match cfg_info.current_cfg.strip_hidden(&cfg_info.hidden_cfg) {
+            None | Some(Cfg::True) => None,
+            Some(cfg) => Some(Arc::new(cfg)),
+        }
+    }
+}
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index c6339dd4755..4fd8d245089 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -58,6 +58,7 @@ use tracing::{debug, instrument};
 use utils::*;
 use {rustc_ast as ast, rustc_hir as hir};
 
+pub(crate) use self::cfg::{CfgInfo, extract_cfg_from_attrs};
 pub(crate) use self::types::*;
 pub(crate) use self::utils::{krate, register_res, synthesize_auto_trait_and_blanket_impls};
 use crate::core::DocContext;
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index 6e3dfa0ac22..f3662a67bbe 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -6,8 +6,7 @@ use std::{fmt, iter};
 use arrayvec::ArrayVec;
 use itertools::Either;
 use rustc_abi::{ExternAbi, VariantIdx};
-use rustc_ast::ast::{LitKind, MetaItemInner, MetaItemKind};
-use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet};
+use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
 use rustc_hir::attrs::{AttributeKind, DeprecatedSince, Deprecation};
 use rustc_hir::def::{CtorKind, DefKind, Res};
 use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId};
@@ -922,267 +921,6 @@ pub(crate) fn hir_attr_lists<'a, I: IntoIterator<Item = &'a hir::Attribute>>(
         .flatten()
 }
 
-/// This type keeps track of (doc) cfg information as we go down the item tree.
-#[derive(Clone, Debug)]
-pub(crate) struct CfgInfo {
-    /// List of currently active `doc(auto_cfg(hide(...)))` cfgs, minus currently active
-    /// `doc(auto_cfg(show(...)))` cfgs.
-    hidden_cfg: FxHashSet<Cfg>,
-    /// Current computed `cfg`. Each time we enter a new item, this field is updated as well while
-    /// taking into account the `hidden_cfg` information.
-    current_cfg: Cfg,
-    /// Whether the `doc(auto_cfg())` feature is enabled or not at this point.
-    auto_cfg_active: bool,
-    /// If the parent item used `doc(cfg(...))`, then we don't want to overwrite `current_cfg`,
-    /// instead we will concatenate with it. However, if it's not the case, we need to overwrite
-    /// `current_cfg`.
-    parent_is_doc_cfg: bool,
-}
-
-impl Default for CfgInfo {
-    fn default() -> Self {
-        Self {
-            hidden_cfg: FxHashSet::from_iter([
-                Cfg::Cfg(sym::test, None),
-                Cfg::Cfg(sym::doc, None),
-                Cfg::Cfg(sym::doctest, None),
-            ]),
-            current_cfg: Cfg::True,
-            auto_cfg_active: true,
-            parent_is_doc_cfg: false,
-        }
-    }
-}
-
-fn show_hide_show_conflict_error(
-    tcx: TyCtxt<'_>,
-    item_span: rustc_span::Span,
-    previous: rustc_span::Span,
-) {
-    let mut diag = tcx.sess.dcx().struct_span_err(
-        item_span,
-        format!(
-            "same `cfg` was in `auto_cfg(hide(...))` and `auto_cfg(show(...))` on the same item"
-        ),
-    );
-    diag.span_note(previous, "first change was here");
-    diag.emit();
-}
-
-/// 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.
-fn handle_auto_cfg_hide_show(
-    tcx: TyCtxt<'_>,
-    cfg_info: &mut CfgInfo,
-    sub_attr: &MetaItemInner,
-    is_show: bool,
-    new_show_attrs: &mut FxHashMap<(Symbol, Option<Symbol>), rustc_span::Span>,
-    new_hide_attrs: &mut FxHashMap<(Symbol, Option<Symbol>), rustc_span::Span>,
-) {
-    if let MetaItemInner::MetaItem(item) = sub_attr
-        && let MetaItemKind::List(items) = &item.kind
-    {
-        for item in items {
-            // FIXME: Report in case `Cfg::parse` reports an error?
-            if let Ok(Cfg::Cfg(key, value)) = Cfg::parse(item) {
-                if is_show {
-                    if let Some(span) = new_hide_attrs.get(&(key, value)) {
-                        show_hide_show_conflict_error(tcx, item.span(), *span);
-                    } else {
-                        new_show_attrs.insert((key, value), item.span());
-                    }
-                    cfg_info.hidden_cfg.remove(&Cfg::Cfg(key, value));
-                } else {
-                    if let Some(span) = new_show_attrs.get(&(key, value)) {
-                        show_hide_show_conflict_error(tcx, item.span(), *span);
-                    } else {
-                        new_hide_attrs.insert((key, value), item.span());
-                    }
-                    cfg_info.hidden_cfg.insert(Cfg::Cfg(key, value));
-                }
-            }
-        }
-    }
-}
-
-pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator<Item = &'a hir::Attribute> + Clone>(
-    attrs: I,
-    tcx: TyCtxt<'_>,
-    cfg_info: &mut CfgInfo,
-) -> Option<Arc<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)
-    }
-
-    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();
-
-    let mut doc_cfg = attrs
-        .clone()
-        .filter(|attr| attr.has_name(sym::doc))
-        .flat_map(|attr| attr.meta_item_list().unwrap_or_default())
-        .filter(|attr| attr.has_name(sym::cfg))
-        .peekable();
-    // If the item uses `doc(cfg(...))`, then we ignore the other `cfg(...)` attributes.
-    if doc_cfg.peek().is_some() {
-        let sess = tcx.sess;
-        // We overwrite existing `cfg`.
-        if !cfg_info.parent_is_doc_cfg {
-            cfg_info.current_cfg = Cfg::True;
-            cfg_info.parent_is_doc_cfg = true;
-        }
-        for attr in doc_cfg {
-            if let Some(cfg_mi) =
-                attr.meta_item().and_then(|attr| rustc_expand::config::parse_cfg(attr, sess))
-            {
-                match Cfg::parse(cfg_mi) {
-                    Ok(new_cfg) => cfg_info.current_cfg &= new_cfg,
-                    Err(e) => {
-                        sess.dcx().span_err(e.span, e.msg);
-                    }
-                }
-            }
-        }
-    } else {
-        cfg_info.parent_is_doc_cfg = false;
-    }
-
-    let mut changed_auto_active_status = None;
-
-    // We get all `doc(auto_cfg)`, `cfg` and `target_feature` attributes.
-    for attr in attrs {
-        if let Some(ident) = attr.ident()
-            && ident.name == sym::doc
-            && let Some(attrs) = attr.meta_item_list()
-        {
-            for attr in attrs.iter().filter(|attr| attr.has_name(sym::auto_cfg)) {
-                let MetaItemInner::MetaItem(attr) = attr else {
-                    continue;
-                };
-                match &attr.kind {
-                    MetaItemKind::Word => {
-                        if check_changed_auto_active_status(
-                            &mut changed_auto_active_status,
-                            attr,
-                            cfg_info,
-                            tcx,
-                            true,
-                        ) {
-                            return None;
-                        }
-                    }
-                    MetaItemKind::NameValue(lit) => {
-                        if let LitKind::Bool(value) = lit.kind {
-                            if check_changed_auto_active_status(
-                                &mut changed_auto_active_status,
-                                attr,
-                                cfg_info,
-                                tcx,
-                                value,
-                            ) {
-                                return None;
-                            }
-                        }
-                    }
-                    MetaItemKind::List(sub_attrs) => {
-                        if check_changed_auto_active_status(
-                            &mut changed_auto_active_status,
-                            attr,
-                            cfg_info,
-                            tcx,
-                            true,
-                        ) {
-                            return None;
-                        }
-                        for sub_attr in sub_attrs.iter() {
-                            if let Some(ident) = sub_attr.ident()
-                                && (ident.name == sym::show || ident.name == sym::hide)
-                            {
-                                handle_auto_cfg_hide_show(
-                                    tcx,
-                                    cfg_info,
-                                    &sub_attr,
-                                    ident.name == sym::show,
-                                    &mut new_show_attrs,
-                                    &mut new_hide_attrs,
-                                );
-                            }
-                        }
-                    }
-                }
-            }
-        } else if let hir::Attribute::Parsed(AttributeKind::TargetFeature { features, .. }) = attr {
-            // Treat `#[target_feature(enable = "feat")]` attributes as if they were
-            // `#[doc(cfg(target_feature = "feat"))]` attributes as well.
-            for (feature, _) in features {
-                cfg_info.current_cfg &= Cfg::Cfg(sym::target_feature, Some(*feature));
-            }
-            continue;
-        } else if !cfg_info.parent_is_doc_cfg
-            && let Some(ident) = attr.ident()
-            && matches!(ident.name, sym::cfg | sym::cfg_trace)
-            && let Some(attr) = single(attr.meta_item_list()?)
-            && let Ok(new_cfg) = Cfg::parse(&attr)
-        {
-            cfg_info.current_cfg &= new_cfg;
-        }
-    }
-
-    // If `doc(auto_cfg)` feature is disabled and `doc(cfg())` wasn't used, there is nothing
-    // to be done here.
-    if !cfg_info.auto_cfg_active && !cfg_info.parent_is_doc_cfg {
-        None
-    } else if cfg_info.parent_is_doc_cfg {
-        if cfg_info.current_cfg == Cfg::True {
-            None
-        } else {
-            Some(Arc::new(cfg_info.current_cfg.clone()))
-        }
-    } else {
-        // If `doc(auto_cfg)` feature is enabled, we want to collect all `cfg` items, we remove the
-        // hidden ones afterward.
-        match cfg_info.current_cfg.strip_hidden(&cfg_info.hidden_cfg) {
-            None | Some(Cfg::True) => None,
-            Some(cfg) => Some(Arc::new(cfg)),
-        }
-    }
-}
-
 pub(crate) trait NestedAttributesExt {
     /// Returns `true` if the attribute list contains a specific `word`
     fn has_word(self, word: Symbol) -> bool