1 //! Looks for items missing (or incorrectly having) doctests.
3 //! This pass is overloaded and runs two different lints.
5 //! - MISSING_DOC_CODE_EXAMPLES: this lint is **UNSTABLE** and looks for public items missing doctests.
6 //! - PRIVATE_DOC_TESTS: this lint is **STABLE** and looks for private items with doctests.
11 use crate::core
::DocContext
;
12 use crate::html
::markdown
::{find_testable_code, ErrorCodes, Ignore, LangString}
;
13 use crate::visit
::DocVisitor
;
14 use crate::visit_ast
::inherits_doc_hidden
;
16 use rustc_middle
::lint
::LintLevelSource
;
17 use rustc_session
::lint
;
18 use rustc_span
::symbol
::sym
;
20 pub(crate) const CHECK_DOC_TEST_VISIBILITY
: Pass
= Pass
{
21 name
: "check_doc_test_visibility",
22 run
: check_doc_test_visibility
,
23 description
: "run various visibility-related lints on doctests",
26 struct DocTestVisibilityLinter
<'a
, 'tcx
> {
27 cx
: &'a
mut DocContext
<'tcx
>,
30 pub(crate) fn check_doc_test_visibility(krate
: Crate
, cx
: &mut DocContext
<'_
>) -> Crate
{
31 let mut coll
= DocTestVisibilityLinter { cx }
;
32 coll
.visit_crate(&krate
);
36 impl<'a
, 'tcx
> DocVisitor
for DocTestVisibilityLinter
<'a
, 'tcx
> {
37 fn visit_item(&mut self, item
: &Item
) {
38 let dox
= item
.attrs
.collapsed_doc_value().unwrap_or_default();
40 look_for_tests(self.cx
, &dox
, item
);
42 self.visit_item_recur(item
)
46 pub(crate) struct Tests
{
47 pub(crate) found_tests
: usize,
50 impl crate::doctest
::Tester
for Tests
{
51 fn add_test(&mut self, _
: String
, config
: LangString
, _
: usize) {
52 if config
.rust
&& config
.ignore
== Ignore
::None
{
53 self.found_tests
+= 1;
58 pub(crate) fn should_have_doc_example(cx
: &DocContext
<'_
>, item
: &clean
::Item
) -> bool
{
59 if !cx
.cache
.access_levels
.is_public(item
.item_id
.expect_def_id())
62 clean
::StructFieldItem(_
)
63 | clean
::VariantItem(_
)
64 | clean
::AssocConstItem(..)
65 | clean
::AssocTypeItem(..)
66 | clean
::TypedefItem(_
)
67 | clean
::StaticItem(_
)
68 | clean
::ConstantItem(_
)
69 | clean
::ExternCrateItem { .. }
70 | clean
::ImportItem(_
)
71 | clean
::PrimitiveItem(_
)
73 // check for trait impl
74 | clean
::ImplItem(box clean
::Impl { trait_: Some(_), .. }
)
80 // The `expect_def_id()` should be okay because `local_def_id_to_hir_id`
81 // would presumably panic if a fake `DefIndex` were passed.
82 let hir_id
= cx
.tcx
.hir().local_def_id_to_hir_id(item
.item_id
.expect_def_id().expect_local());
84 // check if parent is trait impl
85 if let Some(parent_hir_id
) = cx
.tcx
.hir().find_parent_node(hir_id
) {
86 if let Some(parent_node
) = cx
.tcx
.hir().find(parent_hir_id
) {
89 hir
::Node
::Item(hir
::Item
{
90 kind
: hir
::ItemKind
::Impl(hir
::Impl { of_trait: Some(_), .. }
),
99 if cx
.tcx
.hir().attrs(hir_id
).lists(sym
::doc
).has_word(sym
::hidden
)
100 || inherits_doc_hidden(cx
.tcx
, hir_id
)
101 || cx
.tcx
.hir().span(hir_id
).in_derive_expansion()
105 let (level
, source
) = cx
.tcx
.lint_level_at_node(crate::lint
::MISSING_DOC_CODE_EXAMPLES
, hir_id
);
106 level
!= lint
::Level
::Allow
|| matches
!(source
, LintLevelSource
::Default
)
109 pub(crate) fn look_for_tests
<'tcx
>(cx
: &DocContext
<'tcx
>, dox
: &str, item
: &Item
) {
110 let Some(hir_id
) = DocContext
::as_local_hir_id(cx
.tcx
, item
.item_id
)
112 // If non-local, no need to check anything.
116 let mut tests
= Tests { found_tests: 0 }
;
118 find_testable_code(dox
, &mut tests
, ErrorCodes
::No
, false, None
);
120 if tests
.found_tests
== 0 && cx
.tcx
.features().rustdoc_missing_doc_code_examples
{
121 if should_have_doc_example(cx
, item
) {
122 debug
!("reporting error for {:?} (hir_id={:?})", item
, hir_id
);
123 let sp
= item
.attr_span(cx
.tcx
);
124 cx
.tcx
.struct_span_lint_hir(
125 crate::lint
::MISSING_DOC_CODE_EXAMPLES
,
129 lint
.build("missing code example in this documentation").emit();
133 } else if tests
.found_tests
> 0
134 && !cx
.cache
.access_levels
.is_exported(item
.item_id
.expect_def_id())
136 cx
.tcx
.struct_span_lint_hir(
137 crate::lint
::PRIVATE_DOC_TESTS
,
139 item
.attr_span(cx
.tcx
),
141 lint
.build("documentation test in private item").emit();