1 //! This pass is overloaded and runs two different lints.
3 //! - MISSING_DOC_CODE_EXAMPLES: this lint is **UNSTABLE** and looks for public items missing doctests
4 //! - PRIVATE_DOC_TESTS: this lint is **STABLE** and looks for private items with doctests.
9 use crate::core
::DocContext
;
10 use crate::fold
::DocFolder
;
11 use crate::html
::markdown
::{find_testable_code, ErrorCodes, Ignore, LangString}
;
12 use crate::visit_ast
::inherits_doc_hidden
;
13 use rustc_middle
::lint
::LintLevelSource
;
14 use rustc_session
::lint
;
15 use rustc_span
::symbol
::sym
;
17 crate const CHECK_PRIVATE_ITEMS_DOC_TESTS
: Pass
= Pass
{
18 name
: "check-private-items-doc-tests",
19 run
: check_private_items_doc_tests
,
20 description
: "check private items doc tests",
23 struct PrivateItemDocTestLinter
<'a
, 'tcx
> {
24 cx
: &'a
mut DocContext
<'tcx
>,
27 crate fn check_private_items_doc_tests(krate
: Crate
, cx
: &mut DocContext
<'_
>) -> Crate
{
28 let mut coll
= PrivateItemDocTestLinter { cx }
;
30 coll
.fold_crate(krate
)
33 impl<'a
, 'tcx
> DocFolder
for PrivateItemDocTestLinter
<'a
, 'tcx
> {
34 fn fold_item(&mut self, item
: Item
) -> Option
<Item
> {
35 let dox
= item
.attrs
.collapsed_doc_value().unwrap_or_else(String
::new
);
37 look_for_tests(self.cx
, &dox
, &item
);
39 Some(self.fold_item_recur(item
))
43 pub(crate) struct Tests
{
44 pub(crate) found_tests
: usize,
47 impl crate::doctest
::Tester
for Tests
{
48 fn add_test(&mut self, _
: String
, config
: LangString
, _
: usize) {
49 if config
.rust
&& config
.ignore
== Ignore
::None
{
50 self.found_tests
+= 1;
55 crate fn should_have_doc_example(cx
: &DocContext
<'_
>, item
: &clean
::Item
) -> bool
{
56 if !cx
.cache
.access_levels
.is_public(item
.def_id
.expect_real())
59 clean
::StructFieldItem(_
)
60 | clean
::VariantItem(_
)
61 | clean
::AssocConstItem(_
, _
)
62 | clean
::AssocTypeItem(_
, _
)
63 | clean
::TypedefItem(_
, _
)
64 | clean
::StaticItem(_
)
65 | clean
::ConstantItem(_
)
66 | clean
::ExternCrateItem { .. }
67 | clean
::ImportItem(_
)
68 | clean
::PrimitiveItem(_
)
69 | clean
::KeywordItem(_
)
74 // The `expect_real()` should be okay because `local_def_id_to_hir_id`
75 // would presumably panic if a fake `DefIndex` were passed.
76 let hir_id
= cx
.tcx
.hir().local_def_id_to_hir_id(item
.def_id
.expect_real().expect_local());
77 if cx
.tcx
.hir().attrs(hir_id
).lists(sym
::doc
).has_word(sym
::hidden
)
78 || inherits_doc_hidden(cx
.tcx
, hir_id
)
82 let (level
, source
) = cx
.tcx
.lint_level_at_node(crate::lint
::MISSING_DOC_CODE_EXAMPLES
, hir_id
);
83 level
!= lint
::Level
::Allow
|| matches
!(source
, LintLevelSource
::Default
)
86 crate fn look_for_tests
<'tcx
>(cx
: &DocContext
<'tcx
>, dox
: &str, item
: &Item
) {
87 let hir_id
= match DocContext
::as_local_hir_id(cx
.tcx
, item
.def_id
) {
88 Some(hir_id
) => hir_id
,
90 // If non-local, no need to check anything.
95 let mut tests
= Tests { found_tests: 0 }
;
97 find_testable_code(&dox
, &mut tests
, ErrorCodes
::No
, false, None
);
99 if tests
.found_tests
== 0 && cx
.tcx
.sess
.is_nightly_build() {
100 if should_have_doc_example(cx
, &item
) {
101 debug
!("reporting error for {:?} (hir_id={:?})", item
, hir_id
);
102 let sp
= item
.attr_span(cx
.tcx
);
103 cx
.tcx
.struct_span_lint_hir(
104 crate::lint
::MISSING_DOC_CODE_EXAMPLES
,
107 |lint
| lint
.build("missing code example in this documentation").emit(),
110 } else if tests
.found_tests
> 0 && !cx
.cache
.access_levels
.is_public(item
.def_id
.expect_real())
112 cx
.tcx
.struct_span_lint_hir(
113 crate::lint
::PRIVATE_DOC_TESTS
,
115 item
.attr_span(cx
.tcx
),
116 |lint
| lint
.build("documentation test in private item").emit(),