From 7e9d00700ef3f2e44277e2d537e662ad1aec9146 Mon Sep 17 00:00:00 2001 From: Matthew Martin Date: Thu, 27 Nov 2025 10:18:17 -0600 Subject: [PATCH 1/2] Refactor doc header matching --- clippy_lints/src/doc/mod.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index 120da92da944..23e945701c3d 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -1276,12 +1276,14 @@ fn check_doc<'a, Events: Iterator, Range headers.safety = true, + "Errors" => headers.errors = true, + "Panics" => headers.panics = true, + _ => {} + } + } if let Some(tags) = code { if tags.rust && !tags.compile_fail && !tags.ignore { From 304e0be28088caec91fe4d83afe844bb1acc0562 Mon Sep 17 00:00:00 2001 From: Matthew Martin Date: Wed, 26 Nov 2025 09:08:06 -0600 Subject: [PATCH 2/2] Add missing_examples_doc lint --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/doc/missing_headers.rs | 8 ++++++- clippy_lints/src/doc/mod.rs | 31 ++++++++++++++++++++++++ tests/ui/doc_examples.rs | 32 +++++++++++++++++++++++++ tests/ui/doc_examples.stderr | 17 +++++++++++++ 6 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 tests/ui/doc_examples.rs create mode 100644 tests/ui/doc_examples.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 78b81b5b74d6..7a01c3bd829d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6622,6 +6622,7 @@ Released 2018-09-13 [`missing_docs_in_private_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items [`missing_enforced_import_renames`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_enforced_import_renames [`missing_errors_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_errors_doc +[`missing_examples_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_examples_doc [`missing_fields_in_debug`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_fields_in_debug [`missing_inline_in_public_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_inline_in_public_items [`missing_panics_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_panics_doc diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 4a350dca2993..5bec80032bfe 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -122,6 +122,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::doc::DOC_SUSPICIOUS_FOOTNOTES_INFO, crate::doc::EMPTY_DOCS_INFO, crate::doc::MISSING_ERRORS_DOC_INFO, + crate::doc::MISSING_EXAMPLES_DOC_INFO, crate::doc::MISSING_PANICS_DOC_INFO, crate::doc::MISSING_SAFETY_DOC_INFO, crate::doc::NEEDLESS_DOCTEST_MAIN_INFO, diff --git a/clippy_lints/src/doc/missing_headers.rs b/clippy_lints/src/doc/missing_headers.rs index b164a9a99782..a88b75902cca 100644 --- a/clippy_lints/src/doc/missing_headers.rs +++ b/clippy_lints/src/doc/missing_headers.rs @@ -1,4 +1,7 @@ -use super::{DocHeaders, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, MISSING_SAFETY_DOC, UNNECESSARY_SAFETY_DOC}; +use super::{ + DocHeaders, MISSING_ERRORS_DOC, MISSING_EXAMPLES_DOC, MISSING_PANICS_DOC, MISSING_SAFETY_DOC, + UNNECESSARY_SAFETY_DOC, +}; use clippy_utils::diagnostics::{span_lint, span_lint_and_note}; use clippy_utils::macros::{is_panic, root_macro_call_first_node}; use clippy_utils::res::MaybeDef; @@ -34,6 +37,9 @@ pub fn check( } let span = cx.tcx.def_span(owner_id); + if !headers.examples { + span_lint(cx, MISSING_EXAMPLES_DOC, span, "docs missing `# Examples` section"); + } match (headers.safety, sig.header.safety()) { (false, Safety::Unsafe) => span_lint( cx, diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index 23e945701c3d..a79641340dda 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -211,6 +211,33 @@ declare_clippy_lint! { "`pub fn` may panic without `# Panics` in doc comment" } +declare_clippy_lint! { + /// ### What it does + /// Checks the doc comments of publicly visible functions and warns if + /// there is no `# Examples` section. + /// + /// ### Why is this bad? + /// Examples help readers better understand how and why to use the function. + /// + /// ### Examples + /// The following function has an `# Examples` section in its doc comment: + /// + /// ``` + /// /// # Examples + /// /// + /// /// ``` + /// /// assert_eq!(bikeshed_color(), "blue"); + /// /// ``` + /// pub fn bikeshed_color() -> &'static str { + /// "blue" + /// } + /// ``` + #[clippy::version = "1.93.0"] + pub MISSING_EXAMPLES_DOC, + restriction, + "`pub fn` without `# Examples` in doc comment" +} + declare_clippy_lint! { /// ### What it does /// Checks for `fn main() { .. }` in doctests @@ -721,6 +748,7 @@ impl_lint_pass!(Documentation => [ MISSING_SAFETY_DOC, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, + MISSING_EXAMPLES_DOC, NEEDLESS_DOCTEST_MAIN, TEST_ATTR_IN_DOCTEST, UNNECESSARY_SAFETY_DOC, @@ -832,10 +860,12 @@ impl Fragments<'_> { } #[derive(Copy, Clone, Default)] +#[expect(clippy::struct_excessive_bools)] struct DocHeaders { safety: bool, errors: bool, panics: bool, + examples: bool, first_paragraph_len: usize, } @@ -1281,6 +1311,7 @@ fn check_doc<'a, Events: Iterator, Range headers.safety = true, "Errors" => headers.errors = true, "Panics" => headers.panics = true, + "Examples" => headers.examples = true, _ => {} } } diff --git a/tests/ui/doc_examples.rs b/tests/ui/doc_examples.rs new file mode 100644 index 000000000000..505de2256fa6 --- /dev/null +++ b/tests/ui/doc_examples.rs @@ -0,0 +1,32 @@ +#![warn(clippy::missing_examples_doc)] + +pub fn pub_fn_missing_docs() { + //~^ missing_examples_doc + unimplemented!(); +} + +/// Docs without examples +pub fn pub_fn_missing_examples() { + //~^ missing_examples_doc + unimplemented!(); +} + +/// Docs with examples +/// +/// # Examples +/// +/// ``` +/// pub_fn_missing_examples() +/// ``` +pub fn pub_fn_with_examples() { + unimplemented!(); +} + +fn priv_fn_missing_docs() { + unimplemented!(); +} + +#[doc(hidden)] +pub fn hidden_fn_missing_docs() { + unimplemented!(); +} diff --git a/tests/ui/doc_examples.stderr b/tests/ui/doc_examples.stderr new file mode 100644 index 000000000000..4630b7b51bd9 --- /dev/null +++ b/tests/ui/doc_examples.stderr @@ -0,0 +1,17 @@ +error: docs missing `# Examples` section + --> tests/ui/doc_examples.rs:3:1 + | +LL | pub fn pub_fn_missing_docs() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::missing-examples-doc` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::missing_examples_doc)]` + +error: docs missing `# Examples` section + --> tests/ui/doc_examples.rs:9:1 + | +LL | pub fn pub_fn_missing_examples() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors +