rustdoc/passes/
check_doc_cfg.rs

1use rustc_hir::HirId;
2use rustc_hir::def_id::LocalDefId;
3use rustc_middle::ty::TyCtxt;
4use rustc_span::sym;
5
6use super::Pass;
7use crate::clean::{Attributes, Crate, Item};
8use crate::core::DocContext;
9use crate::visit::DocVisitor;
10
11pub(crate) const CHECK_DOC_CFG: Pass = Pass {
12    name: "check-doc-cfg",
13    run: Some(check_doc_cfg),
14    description: "checks `#[doc(cfg(...))]` for stability feature and unexpected cfgs",
15};
16
17pub(crate) fn check_doc_cfg(krate: Crate, cx: &mut DocContext<'_>) -> Crate {
18    let mut checker = DocCfgChecker { cx };
19    checker.visit_crate(&krate);
20    krate
21}
22
23struct RustdocCfgMatchesLintEmitter<'a>(TyCtxt<'a>, HirId);
24
25impl<'a> rustc_attr_parsing::CfgMatchesLintEmitter for RustdocCfgMatchesLintEmitter<'a> {
26    fn emit_span_lint(
27        &self,
28        sess: &rustc_session::Session,
29        lint: &'static rustc_lint::Lint,
30        sp: rustc_span::Span,
31        builtin_diag: rustc_lint_defs::BuiltinLintDiag,
32    ) {
33        self.0.node_span_lint(lint, self.1, sp, |diag| {
34            rustc_lint::decorate_builtin_lint(sess, Some(self.0), builtin_diag, diag)
35        });
36    }
37}
38
39struct DocCfgChecker<'a, 'tcx> {
40    cx: &'a mut DocContext<'tcx>,
41}
42
43impl DocCfgChecker<'_, '_> {
44    fn check_attrs(&mut self, attrs: &Attributes, did: LocalDefId) {
45        let doc_cfgs = attrs
46            .other_attrs
47            .iter()
48            .filter(|attr| attr.has_name(sym::doc))
49            .flat_map(|attr| attr.meta_item_list().unwrap_or_default())
50            .filter(|attr| attr.has_name(sym::cfg));
51
52        for doc_cfg in doc_cfgs {
53            if let Some([cfg_mi]) = doc_cfg.meta_item_list() {
54                let _ = rustc_attr_parsing::cfg_matches(
55                    cfg_mi,
56                    &self.cx.tcx.sess,
57                    RustdocCfgMatchesLintEmitter(
58                        self.cx.tcx,
59                        self.cx.tcx.local_def_id_to_hir_id(did),
60                    ),
61                    Some(self.cx.tcx.features()),
62                );
63            }
64        }
65    }
66}
67
68impl DocVisitor<'_> for DocCfgChecker<'_, '_> {
69    fn visit_item(&mut self, item: &'_ Item) {
70        if let Some(Some(local_did)) = item.def_id().map(|did| did.as_local()) {
71            self.check_attrs(&item.attrs, local_did);
72        }
73
74        self.visit_item_recur(item);
75    }
76}