diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index 5187839423d..418238181e9 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -23,7 +23,7 @@ use crate::clean::*; use crate::core::DocContext; use crate::fold::DocFolder; use crate::html::markdown::markdown_links; -use crate::passes::{look_for_tests, Pass}; +use crate::passes::Pass; use super::span_of_attrs; @@ -508,8 +508,6 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> { let dox = item.attrs.collapsed_doc_value().unwrap_or_else(String::new); trace!("got documentation '{}'", dox); - look_for_tests(&cx, &dox, &item, true); - // find item's parent to resolve `Self` in item's docs below let parent_name = self.cx.as_local_hir_id(item.def_id).and_then(|item_hir| { let parent_hir = self.cx.tcx.hir().get_parent_item(item_hir); diff --git a/src/librustdoc/passes/doc_test_lints.rs b/src/librustdoc/passes/doc_test_lints.rs new file mode 100644 index 00000000000..aced7d55281 --- /dev/null +++ b/src/librustdoc/passes/doc_test_lints.rs @@ -0,0 +1,97 @@ +//! This pass is overloaded and runs two different lints. +//! +//! - MISSING_DOC_CODE_EXAMPLES: this looks for public items missing doc-tests +//! - PRIVATE_DOC_TESTS: this looks for private items with doc-tests. + +use super::{span_of_attrs, Pass}; +use crate::clean::*; +use crate::core::DocContext; +use crate::fold::DocFolder; +use crate::html::markdown::{find_testable_code, ErrorCodes, LangString}; +use rustc_session::lint; + +pub const CHECK_PRIVATE_ITEMS_DOC_TESTS: Pass = Pass { + name: "check-private-items-doc-tests", + run: check_private_items_doc_tests, + description: "check private items doc tests", +}; + +struct PrivateItemDocTestLinter<'a, 'tcx> { + cx: &'a DocContext<'tcx>, +} + +impl<'a, 'tcx> PrivateItemDocTestLinter<'a, 'tcx> { + fn new(cx: &'a DocContext<'tcx>) -> Self { + PrivateItemDocTestLinter { cx } + } +} + +pub fn check_private_items_doc_tests(krate: Crate, cx: &DocContext<'_>) -> Crate { + let mut coll = PrivateItemDocTestLinter::new(cx); + + coll.fold_crate(krate) +} + +impl<'a, 'tcx> DocFolder for PrivateItemDocTestLinter<'a, 'tcx> { + fn fold_item(&mut self, item: Item) -> Option { + let cx = self.cx; + let dox = item.attrs.collapsed_doc_value().unwrap_or_else(String::new); + + look_for_tests(&cx, &dox, &item); + + self.fold_item_recur(item) + } +} + +pub fn look_for_tests<'tcx>(cx: &DocContext<'tcx>, dox: &str, item: &Item) { + let hir_id = match cx.as_local_hir_id(item.def_id) { + Some(hir_id) => hir_id, + None => { + // If non-local, no need to check anything. + return; + } + }; + + struct Tests { + found_tests: usize, + } + + impl crate::test::Tester for Tests { + fn add_test(&mut self, _: String, _: LangString, _: usize) { + self.found_tests += 1; + } + } + + let mut tests = Tests { found_tests: 0 }; + + find_testable_code(&dox, &mut tests, ErrorCodes::No, false, None); + + if tests.found_tests == 0 { + use ItemEnum::*; + + let should_report = match item.inner { + ExternCrateItem(_, _) | ImportItem(_) | PrimitiveItem(_) | KeywordItem(_) => false, + _ => true, + }; + if should_report { + debug!("reporting error for {:?} (hir_id={:?})", item, hir_id); + let sp = span_of_attrs(&item.attrs).unwrap_or(item.source.span()); + cx.tcx.struct_span_lint_hir( + lint::builtin::MISSING_DOC_CODE_EXAMPLES, + hir_id, + sp, + |lint| lint.build("missing code example in this documentation").emit(), + ); + } + } else if rustc_feature::UnstableFeatures::from_environment().is_nightly_build() + && tests.found_tests > 0 + && !cx.renderinfo.borrow().access_levels.is_public(item.def_id) + { + cx.tcx.struct_span_lint_hir( + lint::builtin::PRIVATE_DOC_TESTS, + hir_id, + span_of_attrs(&item.attrs).unwrap_or(item.source.span()), + |lint| lint.build("documentation test in private item").emit(), + ); + } +} diff --git a/src/librustdoc/passes/mod.rs b/src/librustdoc/passes/mod.rs index 70366c90139..0eebdbd87ed 100644 --- a/src/librustdoc/passes/mod.rs +++ b/src/librustdoc/passes/mod.rs @@ -3,7 +3,6 @@ use rustc_hir::def_id::{DefId, DefIdSet}; use rustc_middle::middle::privacy::AccessLevels; -use rustc_session::lint; use rustc_span::{InnerSpan, Span, DUMMY_SP}; use std::mem; use std::ops::Range; @@ -12,7 +11,6 @@ use self::Condition::*; use crate::clean::{self, GetDefId, Item}; use crate::core::DocContext; use crate::fold::{DocFolder, StripItem}; -use crate::html::markdown::{find_testable_code, ErrorCodes, LangString}; mod collapse_docs; pub use self::collapse_docs::COLLAPSE_DOCS; @@ -35,8 +33,8 @@ pub use self::propagate_doc_cfg::PROPAGATE_DOC_CFG; mod collect_intra_doc_links; pub use self::collect_intra_doc_links::COLLECT_INTRA_DOC_LINKS; -mod private_items_doc_tests; -pub use self::private_items_doc_tests::CHECK_PRIVATE_ITEMS_DOC_TESTS; +mod doc_test_lints; +pub use self::doc_test_lints::CHECK_PRIVATE_ITEMS_DOC_TESTS; mod collect_trait_impls; pub use self::collect_trait_impls::COLLECT_TRAIT_IMPLS; @@ -312,52 +310,6 @@ impl DocFolder for ImportStripper { } } -pub fn look_for_tests<'tcx>( - cx: &DocContext<'tcx>, - dox: &str, - item: &Item, - check_missing_code: bool, -) { - let hir_id = match cx.as_local_hir_id(item.def_id) { - Some(hir_id) => hir_id, - None => { - // If non-local, no need to check anything. - return; - } - }; - - struct Tests { - found_tests: usize, - } - - impl crate::test::Tester for Tests { - fn add_test(&mut self, _: String, _: LangString, _: usize) { - self.found_tests += 1; - } - } - - let mut tests = Tests { found_tests: 0 }; - - find_testable_code(&dox, &mut tests, ErrorCodes::No, false, None); - - if check_missing_code && tests.found_tests == 0 { - let sp = span_of_attrs(&item.attrs).unwrap_or(item.source.span()); - cx.tcx.struct_span_lint_hir(lint::builtin::MISSING_DOC_CODE_EXAMPLES, hir_id, sp, |lint| { - lint.build("missing code example in this documentation").emit() - }); - } else if !check_missing_code - && tests.found_tests > 0 - && !cx.renderinfo.borrow().access_levels.is_public(item.def_id) - { - cx.tcx.struct_span_lint_hir( - lint::builtin::PRIVATE_DOC_TESTS, - hir_id, - span_of_attrs(&item.attrs).unwrap_or(item.source.span()), - |lint| lint.build("documentation test in private item").emit(), - ); - } -} - /// Returns a span encompassing all the given attributes. crate fn span_of_attrs(attrs: &clean::Attributes) -> Option { if attrs.doc_strings.is_empty() { diff --git a/src/librustdoc/passes/private_items_doc_tests.rs b/src/librustdoc/passes/private_items_doc_tests.rs deleted file mode 100644 index aec5a6bd4e2..00000000000 --- a/src/librustdoc/passes/private_items_doc_tests.rs +++ /dev/null @@ -1,37 +0,0 @@ -use crate::clean::*; -use crate::core::DocContext; -use crate::fold::DocFolder; -use crate::passes::{look_for_tests, Pass}; - -pub const CHECK_PRIVATE_ITEMS_DOC_TESTS: Pass = Pass { - name: "check-private-items-doc-tests", - run: check_private_items_doc_tests, - description: "check private items doc tests", -}; - -struct PrivateItemDocTestLinter<'a, 'tcx> { - cx: &'a DocContext<'tcx>, -} - -impl<'a, 'tcx> PrivateItemDocTestLinter<'a, 'tcx> { - fn new(cx: &'a DocContext<'tcx>) -> Self { - PrivateItemDocTestLinter { cx } - } -} - -pub fn check_private_items_doc_tests(krate: Crate, cx: &DocContext<'_>) -> Crate { - let mut coll = PrivateItemDocTestLinter::new(cx); - - coll.fold_crate(krate) -} - -impl<'a, 'tcx> DocFolder for PrivateItemDocTestLinter<'a, 'tcx> { - fn fold_item(&mut self, item: Item) -> Option { - let cx = self.cx; - let dox = item.attrs.collapsed_doc_value().unwrap_or_else(String::new); - - look_for_tests(&cx, &dox, &item, false); - - self.fold_item_recur(item) - } -} diff --git a/src/test/rustdoc-ui/lint-group.stderr b/src/test/rustdoc-ui/lint-group.stderr index 14d72e9aad3..ad923c714da 100644 --- a/src/test/rustdoc-ui/lint-group.stderr +++ b/src/test/rustdoc-ui/lint-group.stderr @@ -1,3 +1,16 @@ +error: missing code example in this documentation + --> $DIR/lint-group.rs:16:1 + | +LL | /// wait, this doesn't have a doctest? + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/lint-group.rs:7:9 + | +LL | #![deny(rustdoc)] + | ^^^^^^^ + = note: `#[deny(missing_doc_code_examples)]` implied by `#[deny(rustdoc)]` + error: documentation test in private item --> $DIR/lint-group.rs:19:1 | @@ -29,18 +42,5 @@ LL | #![deny(rustdoc)] = note: `#[deny(intra_doc_link_resolution_failure)]` implied by `#[deny(rustdoc)]` = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]` -error: missing code example in this documentation - --> $DIR/lint-group.rs:16:1 - | -LL | /// wait, this doesn't have a doctest? - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: the lint level is defined here - --> $DIR/lint-group.rs:7:9 - | -LL | #![deny(rustdoc)] - | ^^^^^^^ - = note: `#[deny(missing_doc_code_examples)]` implied by `#[deny(rustdoc)]` - error: aborting due to 3 previous errors