]>
Commit | Line | Data |
---|---|---|
a2a8927a XL |
1 | //! Looks for items missing (or incorrectly having) doctests. |
2 | //! | |
3dfed10e XL |
3 | //! This pass is overloaded and runs two different lints. |
4 | //! | |
c295e0f8 | 5 | //! - MISSING_DOC_CODE_EXAMPLES: this lint is **UNSTABLE** and looks for public items missing doctests. |
5869c6ff | 6 | //! - PRIVATE_DOC_TESTS: this lint is **STABLE** and looks for private items with doctests. |
3dfed10e | 7 | |
cdc7bbd5 | 8 | use super::Pass; |
3dfed10e XL |
9 | use crate::clean; |
10 | use crate::clean::*; | |
11 | use crate::core::DocContext; | |
1b1a35ee | 12 | use crate::html::markdown::{find_testable_code, ErrorCodes, Ignore, LangString}; |
3c0e092e | 13 | use crate::visit::DocVisitor; |
6a06907d | 14 | use crate::visit_ast::inherits_doc_hidden; |
c295e0f8 | 15 | use rustc_hir as hir; |
fc512014 | 16 | use rustc_middle::lint::LintLevelSource; |
3dfed10e | 17 | use rustc_session::lint; |
6a06907d | 18 | use rustc_span::symbol::sym; |
3dfed10e | 19 | |
923072b8 | 20 | pub(crate) const CHECK_DOC_TEST_VISIBILITY: Pass = Pass { |
c295e0f8 XL |
21 | name: "check_doc_test_visibility", |
22 | run: check_doc_test_visibility, | |
23 | description: "run various visibility-related lints on doctests", | |
3dfed10e XL |
24 | }; |
25 | ||
c295e0f8 | 26 | struct DocTestVisibilityLinter<'a, 'tcx> { |
6a06907d | 27 | cx: &'a mut DocContext<'tcx>, |
3dfed10e XL |
28 | } |
29 | ||
923072b8 | 30 | pub(crate) fn check_doc_test_visibility(krate: Crate, cx: &mut DocContext<'_>) -> Crate { |
c295e0f8 | 31 | let mut coll = DocTestVisibilityLinter { cx }; |
3c0e092e XL |
32 | coll.visit_crate(&krate); |
33 | krate | |
3dfed10e XL |
34 | } |
35 | ||
3c0e092e XL |
36 | impl<'a, 'tcx> DocVisitor for DocTestVisibilityLinter<'a, 'tcx> { |
37 | fn visit_item(&mut self, item: &Item) { | |
923072b8 | 38 | let dox = item.attrs.collapsed_doc_value().unwrap_or_default(); |
3dfed10e | 39 | |
923072b8 | 40 | look_for_tests(self.cx, &dox, item); |
3dfed10e | 41 | |
3c0e092e | 42 | self.visit_item_recur(item) |
3dfed10e XL |
43 | } |
44 | } | |
45 | ||
46 | pub(crate) struct Tests { | |
47 | pub(crate) found_tests: usize, | |
48 | } | |
49 | ||
1b1a35ee XL |
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; | |
54 | } | |
3dfed10e XL |
55 | } |
56 | } | |
57 | ||
923072b8 | 58 | pub(crate) fn should_have_doc_example(cx: &DocContext<'_>, item: &clean::Item) -> bool { |
487cf647 | 59 | if !cx.cache.effective_visibilities.is_directly_public(cx.tcx, item.item_id.expect_def_id()) |
6a06907d XL |
60 | || matches!( |
61 | *item.kind, | |
62 | clean::StructFieldItem(_) | |
63 | | clean::VariantItem(_) | |
04454e1e | 64 | | clean::AssocConstItem(..) |
5e7ed085 | 65 | | clean::AssocTypeItem(..) |
04454e1e | 66 | | clean::TypedefItem(_) |
6a06907d XL |
67 | | clean::StaticItem(_) |
68 | | clean::ConstantItem(_) | |
69 | | clean::ExternCrateItem { .. } | |
70 | | clean::ImportItem(_) | |
71 | | clean::PrimitiveItem(_) | |
064997fb | 72 | | clean::KeywordItem |
c295e0f8 | 73 | // check for trait impl |
064997fb | 74 | | clean::ImplItem(box clean::Impl { trait_: Some(_), .. }) |
6a06907d XL |
75 | ) |
76 | { | |
29967ef6 XL |
77 | return false; |
78 | } | |
c295e0f8 | 79 | |
136023e0 | 80 | // The `expect_def_id()` should be okay because `local_def_id_to_hir_id` |
17df50a5 | 81 | // would presumably panic if a fake `DefIndex` were passed. |
04454e1e | 82 | let hir_id = cx.tcx.hir().local_def_id_to_hir_id(item.item_id.expect_def_id().expect_local()); |
c295e0f8 XL |
83 | |
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) { | |
87 | if matches!( | |
88 | parent_node, | |
89 | hir::Node::Item(hir::Item { | |
90 | kind: hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }), | |
91 | .. | |
92 | }) | |
93 | ) { | |
94 | return false; | |
95 | } | |
96 | } | |
97 | } | |
98 | ||
6a06907d XL |
99 | if cx.tcx.hir().attrs(hir_id).lists(sym::doc).has_word(sym::hidden) |
100 | || inherits_doc_hidden(cx.tcx, hir_id) | |
c295e0f8 | 101 | || cx.tcx.hir().span(hir_id).in_derive_expansion() |
6a06907d XL |
102 | { |
103 | return false; | |
104 | } | |
105 | let (level, source) = cx.tcx.lint_level_at_node(crate::lint::MISSING_DOC_CODE_EXAMPLES, hir_id); | |
fc512014 | 106 | level != lint::Level::Allow || matches!(source, LintLevelSource::Default) |
3dfed10e XL |
107 | } |
108 | ||
923072b8 | 109 | pub(crate) fn look_for_tests<'tcx>(cx: &DocContext<'tcx>, dox: &str, item: &Item) { |
04454e1e | 110 | let Some(hir_id) = DocContext::as_local_hir_id(cx.tcx, item.item_id) |
5e7ed085 FG |
111 | else { |
112 | // If non-local, no need to check anything. | |
113 | return; | |
3dfed10e XL |
114 | }; |
115 | ||
1b1a35ee | 116 | let mut tests = Tests { found_tests: 0 }; |
3dfed10e | 117 | |
3c0e092e | 118 | find_testable_code(dox, &mut tests, ErrorCodes::No, false, None); |
3dfed10e | 119 | |
f2b60f7d | 120 | if tests.found_tests == 0 && cx.tcx.features().rustdoc_missing_doc_code_examples { |
3c0e092e | 121 | if should_have_doc_example(cx, item) { |
3dfed10e | 122 | debug!("reporting error for {:?} (hir_id={:?})", item, hir_id); |
cdc7bbd5 | 123 | let sp = item.attr_span(cx.tcx); |
3dfed10e | 124 | cx.tcx.struct_span_lint_hir( |
6a06907d | 125 | crate::lint::MISSING_DOC_CODE_EXAMPLES, |
3dfed10e XL |
126 | hir_id, |
127 | sp, | |
2b03887a FG |
128 | "missing code example in this documentation", |
129 | |lint| lint, | |
3dfed10e XL |
130 | ); |
131 | } | |
136023e0 | 132 | } else if tests.found_tests > 0 |
487cf647 | 133 | && !cx.cache.effective_visibilities.is_exported(cx.tcx, item.item_id.expect_def_id()) |
17df50a5 | 134 | { |
3dfed10e | 135 | cx.tcx.struct_span_lint_hir( |
6a06907d | 136 | crate::lint::PRIVATE_DOC_TESTS, |
3dfed10e | 137 | hir_id, |
cdc7bbd5 | 138 | item.attr_span(cx.tcx), |
2b03887a FG |
139 | "documentation test in private item", |
140 | |lint| lint, | |
3dfed10e XL |
141 | ); |
142 | } | |
143 | } |