]> git.proxmox.com Git - rustc.git/blobdiff - src/librustdoc/passes/check_doc_test_visibility.rs
New upstream version 1.70.0+dfsg1
[rustc.git] / src / librustdoc / passes / check_doc_test_visibility.rs
index 7d3010cf3325b327c03f2b67b93d2c7190de34eb..6b13e6c9581ae62dfecc0da772c3bec5a01807b7 100644 (file)
@@ -1,3 +1,5 @@
+//! Looks for items missing (or incorrectly having) doctests.
+//!
 //! This pass is overloaded and runs two different lints.
 //!
 //! - MISSING_DOC_CODE_EXAMPLES: this lint is **UNSTABLE** and looks for public items missing doctests.
@@ -13,9 +15,8 @@ use crate::visit_ast::inherits_doc_hidden;
 use rustc_hir as hir;
 use rustc_middle::lint::LintLevelSource;
 use rustc_session::lint;
-use rustc_span::symbol::sym;
 
-crate const CHECK_DOC_TEST_VISIBILITY: Pass = Pass {
+pub(crate) const CHECK_DOC_TEST_VISIBILITY: Pass = Pass {
     name: "check_doc_test_visibility",
     run: check_doc_test_visibility,
     description: "run various visibility-related lints on doctests",
@@ -25,7 +26,7 @@ struct DocTestVisibilityLinter<'a, 'tcx> {
     cx: &'a mut DocContext<'tcx>,
 }
 
-crate fn check_doc_test_visibility(krate: Crate, cx: &mut DocContext<'_>) -> Crate {
+pub(crate) fn check_doc_test_visibility(krate: Crate, cx: &mut DocContext<'_>) -> Crate {
     let mut coll = DocTestVisibilityLinter { cx };
     coll.visit_crate(&krate);
     krate
@@ -33,9 +34,9 @@ crate fn check_doc_test_visibility(krate: Crate, cx: &mut DocContext<'_>) -> Cra
 
 impl<'a, 'tcx> DocVisitor for DocTestVisibilityLinter<'a, 'tcx> {
     fn visit_item(&mut self, item: &Item) {
-        let dox = item.attrs.collapsed_doc_value().unwrap_or_else(String::new);
+        let dox = item.attrs.collapsed_doc_value().unwrap_or_default();
 
-        look_for_tests(self.cx, &dox, &item);
+        look_for_tests(self.cx, &dox, item);
 
         self.visit_item_recur(item)
     }
@@ -53,23 +54,23 @@ impl crate::doctest::Tester for Tests {
     }
 }
 
-crate fn should_have_doc_example(cx: &DocContext<'_>, item: &clean::Item) -> bool {
-    if !cx.cache.access_levels.is_public(item.def_id.expect_def_id())
+pub(crate) fn should_have_doc_example(cx: &DocContext<'_>, item: &clean::Item) -> bool {
+    if !cx.cache.effective_visibilities.is_directly_public(cx.tcx, item.item_id.expect_def_id())
         || matches!(
             *item.kind,
             clean::StructFieldItem(_)
                 | clean::VariantItem(_)
-                | clean::AssocConstItem(_, _)
-                | clean::AssocTypeItem(_, _)
-                | clean::TypedefItem(_, _)
+                | clean::AssocConstItem(..)
+                | clean::AssocTypeItem(..)
+                | clean::TypedefItem(_)
                 | clean::StaticItem(_)
                 | clean::ConstantItem(_)
                 | clean::ExternCrateItem { .. }
                 | clean::ImportItem(_)
                 | clean::PrimitiveItem(_)
-                | clean::KeywordItem(_)
+                | clean::KeywordItem
                 // check for trait impl
-                | clean::ImplItem(clean::Impl { trait_: Some(_), .. })
+                | clean::ImplItem(box clean::Impl { trait_: Some(_), .. })
         )
     {
         return false;
@@ -77,47 +78,47 @@ crate fn should_have_doc_example(cx: &DocContext<'_>, item: &clean::Item) -> boo
 
     // The `expect_def_id()` should be okay because `local_def_id_to_hir_id`
     // would presumably panic if a fake `DefIndex` were passed.
-    let hir_id = cx.tcx.hir().local_def_id_to_hir_id(item.def_id.expect_def_id().expect_local());
+    let def_id = item.item_id.expect_def_id().expect_local();
 
     // check if parent is trait impl
-    if let Some(parent_hir_id) = cx.tcx.hir().find_parent_node(hir_id) {
-        if let Some(parent_node) = cx.tcx.hir().find(parent_hir_id) {
-            if matches!(
-                parent_node,
-                hir::Node::Item(hir::Item {
-                    kind: hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }),
-                    ..
-                })
-            ) {
-                return false;
-            }
-        }
+    if let Some(parent_def_id) = cx.tcx.opt_local_parent(def_id) &&
+        let Some(parent_node) = cx.tcx.hir().find_by_def_id(parent_def_id) &&
+        matches!(
+            parent_node,
+            hir::Node::Item(hir::Item {
+                kind: hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }),
+                ..
+            })
+        )
+    {
+        return false;
     }
 
-    if cx.tcx.hir().attrs(hir_id).lists(sym::doc).has_word(sym::hidden)
-        || inherits_doc_hidden(cx.tcx, hir_id)
-        || cx.tcx.hir().span(hir_id).in_derive_expansion()
+    if cx.tcx.is_doc_hidden(def_id.to_def_id())
+        || inherits_doc_hidden(cx.tcx, def_id)
+        || cx.tcx.def_span(def_id.to_def_id()).in_derive_expansion()
     {
         return false;
     }
-    let (level, source) = cx.tcx.lint_level_at_node(crate::lint::MISSING_DOC_CODE_EXAMPLES, hir_id);
+    let (level, source) = cx.tcx.lint_level_at_node(
+        crate::lint::MISSING_DOC_CODE_EXAMPLES,
+        cx.tcx.hir().local_def_id_to_hir_id(def_id),
+    );
     level != lint::Level::Allow || matches!(source, LintLevelSource::Default)
 }
 
-crate fn look_for_tests<'tcx>(cx: &DocContext<'tcx>, dox: &str, item: &Item) {
-    let hir_id = match DocContext::as_local_hir_id(cx.tcx, item.def_id) {
-        Some(hir_id) => hir_id,
-        None => {
-            // If non-local, no need to check anything.
-            return;
-        }
+pub(crate) fn look_for_tests<'tcx>(cx: &DocContext<'tcx>, dox: &str, item: &Item) {
+    let Some(hir_id) = DocContext::as_local_hir_id(cx.tcx, item.item_id)
+    else {
+        // If non-local, no need to check anything.
+        return;
     };
 
     let mut tests = Tests { found_tests: 0 };
 
     find_testable_code(dox, &mut tests, ErrorCodes::No, false, None);
 
-    if tests.found_tests == 0 && cx.tcx.sess.is_nightly_build() {
+    if tests.found_tests == 0 && cx.tcx.features().rustdoc_missing_doc_code_examples {
         if should_have_doc_example(cx, item) {
             debug!("reporting error for {:?} (hir_id={:?})", item, hir_id);
             let sp = item.attr_span(cx.tcx);
@@ -125,17 +126,19 @@ crate fn look_for_tests<'tcx>(cx: &DocContext<'tcx>, dox: &str, item: &Item) {
                 crate::lint::MISSING_DOC_CODE_EXAMPLES,
                 hir_id,
                 sp,
-                |lint| lint.build("missing code example in this documentation").emit(),
+                "missing code example in this documentation",
+                |lint| lint,
             );
         }
     } else if tests.found_tests > 0
-        && !cx.cache.access_levels.is_public(item.def_id.expect_def_id())
+        && !cx.cache.effective_visibilities.is_exported(cx.tcx, item.item_id.expect_def_id())
     {
         cx.tcx.struct_span_lint_hir(
             crate::lint::PRIVATE_DOC_TESTS,
             hir_id,
             item.attr_span(cx.tcx),
-            |lint| lint.build("documentation test in private item").emit(),
+            "documentation test in private item",
+            |lint| lint,
         );
     }
 }