blob: 3284da77a02225e51f821931cb8c11373cca9d02 [file] [log] [blame]
use rustc_hir::HirId;
use rustc_hir::def_id::LocalDefId;
use rustc_middle::ty::TyCtxt;
use rustc_span::sym;
use super::Pass;
use crate::clean::{Attributes, Crate, Item};
use crate::core::DocContext;
use crate::visit::DocVisitor;
pub(crate) const CHECK_DOC_CFG: Pass = Pass {
name: "check-doc-cfg",
run: Some(check_doc_cfg),
description: "checks `#[doc(cfg(...))]` for stability feature and unexpected cfgs",
};
pub(crate) fn check_doc_cfg(krate: Crate, cx: &mut DocContext<'_>) -> Crate {
let mut checker = DocCfgChecker { cx };
checker.visit_crate(&krate);
krate
}
struct RustdocCfgMatchesLintEmitter<'a>(TyCtxt<'a>, HirId);
impl<'a> rustc_attr_parsing::CfgMatchesLintEmitter for RustdocCfgMatchesLintEmitter<'a> {
fn emit_span_lint(
&self,
sess: &rustc_session::Session,
lint: &'static rustc_lint::Lint,
sp: rustc_span::Span,
builtin_diag: rustc_lint_defs::BuiltinLintDiag,
) {
self.0.node_span_lint(lint, self.1, sp, |diag| {
rustc_lint::decorate_builtin_lint(sess, Some(self.0), builtin_diag, diag)
});
}
}
struct DocCfgChecker<'a, 'tcx> {
cx: &'a mut DocContext<'tcx>,
}
impl DocCfgChecker<'_, '_> {
fn check_attrs(&mut self, attrs: &Attributes, did: LocalDefId) {
let doc_cfgs = attrs
.other_attrs
.iter()
.filter(|attr| attr.has_name(sym::doc))
.flat_map(|attr| attr.meta_item_list().unwrap_or_default())
.filter(|attr| attr.has_name(sym::cfg));
for doc_cfg in doc_cfgs {
if let Some([cfg_mi]) = doc_cfg.meta_item_list() {
let _ = rustc_attr_parsing::cfg_matches(
cfg_mi,
&self.cx.tcx.sess,
RustdocCfgMatchesLintEmitter(
self.cx.tcx,
self.cx.tcx.local_def_id_to_hir_id(did),
),
Some(self.cx.tcx.features()),
);
}
}
}
}
impl DocVisitor<'_> for DocCfgChecker<'_, '_> {
fn visit_item(&mut self, item: &'_ Item) {
if let Some(Some(local_did)) = item.def_id().map(|did| did.as_local()) {
self.check_attrs(&item.attrs, local_did);
}
self.visit_item_recur(item);
}
}