Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions clippy_lints/src/declared_lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
8 changes: 7 additions & 1 deletion clippy_lints/src/doc/missing_headers.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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,
Expand Down
45 changes: 39 additions & 6 deletions clippy_lints/src/doc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
}

Expand Down Expand Up @@ -1276,12 +1306,15 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
continue;
}
let trimmed_text = text.trim();
headers.safety |= in_heading && trimmed_text == "Safety";
headers.safety |= in_heading && trimmed_text == "SAFETY";
headers.safety |= in_heading && trimmed_text == "Implementation safety";
headers.safety |= in_heading && trimmed_text == "Implementation Safety";
headers.errors |= in_heading && trimmed_text == "Errors";
headers.panics |= in_heading && trimmed_text == "Panics";
if in_heading {
match trimmed_text {
"Safety" | "SAFETY" | "Implementation safety" | "Implementation Safety" => headers.safety = true,
"Errors" => headers.errors = true,
"Panics" => headers.panics = true,
"Examples" => headers.examples = true,
_ => {}
}
}

if let Some(tags) = code {
if tags.rust && !tags.compile_fail && !tags.ignore {
Expand Down
32 changes: 32 additions & 0 deletions tests/ui/doc_examples.rs
Original file line number Diff line number Diff line change
@@ -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!();
}
17 changes: 17 additions & 0 deletions tests/ui/doc_examples.stderr
Original file line number Diff line number Diff line change
@@ -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