Skip to content

Commit

Permalink
Add new lint doc_overindented_list_item
Browse files Browse the repository at this point in the history
Add a new lint `doc_overindented_list_item` to detect and fix list items
in docs that are overindented.

For example,

```rs
/// - first line
///      second line
fn foo() {}
```

this would be fixed to:

```rs
/// - first line
///   second line
fn foo() {}
```

This lint improves readabiliy and consistency in doc.
  • Loading branch information
ohno418 committed Dec 23, 2024
1 parent 592fd34 commit b3cda74
Show file tree
Hide file tree
Showing 10 changed files with 244 additions and 15 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,11 +159,11 @@ line. (You can swap `clippy::all` with the specific lint category you are target
You can add options to your code to `allow`/`warn`/`deny` Clippy lints:

* the whole set of `Warn` lints using the `clippy` lint group (`#![deny(clippy::all)]`).
Note that `rustc` has additional [lint groups](https://doc.rust-lang.org/rustc/lints/groups.html).
Note that `rustc` has additional [lint groups](https://doc.rust-lang.org/rustc/lints/groups.html).

* all lints using both the `clippy` and `clippy::pedantic` lint groups (`#![deny(clippy::all)]`,
`#![deny(clippy::pedantic)]`). Note that `clippy::pedantic` contains some very aggressive
lints prone to false positives.
`#![deny(clippy::pedantic)]`). Note that `clippy::pedantic` contains some very aggressive
lints prone to false positives.

* only some lints (`#![deny(clippy::single_match, clippy::box_vec)]`, etc.)

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 @@ -139,6 +139,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
crate::disallowed_types::DISALLOWED_TYPES_INFO,
crate::doc::DOC_INCLUDE_WITHOUT_CFG_INFO,
crate::doc::DOC_LAZY_CONTINUATION_INFO,
crate::doc::DOC_OVERINDENTED_LIST_ITEM_INFO,
crate::doc::DOC_LINK_WITH_QUOTES_INFO,
crate::doc::DOC_MARKDOWN_INFO,
crate::doc::DOC_NESTED_REFDEFS_INFO,
Expand Down
45 changes: 44 additions & 1 deletion clippy_lints/src/doc/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#![allow(clippy::lint_without_lint_pass)]

mod lazy_continuation;
mod overindented_list_item;
mod too_long_first_doc_paragraph;

use clippy_config::Conf;
Expand Down Expand Up @@ -429,6 +430,39 @@ declare_clippy_lint! {
"require every line of a paragraph to be indented and marked"
}

declare_clippy_lint! {
/// ### What it does
///
/// Detects overindented list items in doc comments where the continuation
/// lines are indented more than necessary.
///
/// ### Why is this bad?
///
/// Overindented list items in doc comments can lead to inconsistent and
/// poorly formatted documentation when rendered. Excessive indentation may
/// cause the text to be misinterpreted as a nested list item or code block,
/// affecting readability and the overall structure of the documentation.
///
/// ### Example
///
/// ```no_run
/// /// - This is the first item in a list
/// /// and this line is overindented.
/// # fn foo() {}
/// ```
///
/// Fixes this into:
/// ```no_run
/// /// - This is the first item in a list
/// /// and this line is overindented.
/// # fn foo() {}
/// ```
#[clippy::version = "1.80.0"]
pub DOC_OVERINDENTED_LIST_ITEM,
style,
"ensure list items are not overindented"
}

declare_clippy_lint! {
/// ### What it does
/// Checks if the first paragraph in the documentation of items listed in the module page is too long.
Expand Down Expand Up @@ -618,6 +652,7 @@ impl_lint_pass!(Documentation => [
SUSPICIOUS_DOC_COMMENTS,
EMPTY_DOCS,
DOC_LAZY_CONTINUATION,
DOC_OVERINDENTED_LIST_ITEM,
EMPTY_LINE_AFTER_OUTER_ATTR,
EMPTY_LINE_AFTER_DOC_COMMENTS,
TOO_LONG_FIRST_DOC_PARAGRAPH,
Expand Down Expand Up @@ -1004,11 +1039,19 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
&& !in_footnote_definition
&& !matches!(next_event, End(_))
{
let span = Span::new(span.hi(), next_span.lo(), span.ctxt(), span.parent());
lazy_continuation::check(
cx,
doc,
range.end..next_range.start,
Span::new(span.hi(), next_span.lo(), span.ctxt(), span.parent()),
span,
&containers[..],
);
overindented_list_item::check(
cx,
doc,
range.end..next_range.start,
span,
&containers[..],
);
}
Expand Down
51 changes: 51 additions & 0 deletions clippy_lints/src/doc/overindented_list_item.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
use clippy_utils::diagnostics::span_lint_and_then;
use itertools::Itertools;
use rustc_errors::Applicability;
use rustc_lint::LateContext;
use rustc_span::Span;
use std::ops::Range;

use super::DOC_OVERINDENTED_LIST_ITEM;

pub(super) fn check(cx: &LateContext<'_>, doc: &str, range: Range<usize>, span: Span, containers: &[super::Container]) {
if doc[range.clone()].contains('\t') {
// We don't do tab stops correctly.
return;
}

let is_blockquote = containers.iter().any(|c| matches!(c, super::Container::Blockquote));
if is_blockquote {
// If this doc is a blockquote, we don't go further.
return;
}

let leading_spaces = doc[range].chars().filter(|c| *c == ' ').count();
let list_indentation = containers
.iter()
.map(|c| {
if let super::Container::List(indent) = c {
*indent
} else {
0
}
})
.sum();

if leading_spaces > list_indentation {
span_lint_and_then(
cx,
DOC_OVERINDENTED_LIST_ITEM,
span,
"doc list item overindented",
|diag| {
diag.span_suggestion_verbose(
span,
"indent this line",
std::iter::repeat(" ").take(list_indentation).join(""),
Applicability::MaybeIncorrect,
);
diag.help("if this is supposed to be its own paragraph, add a blank line");
},
);
}
}
1 change: 1 addition & 0 deletions tests/ui/doc/doc_lazy_list.fixed
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#![warn(clippy::doc_lazy_continuation)]
#![allow(clippy::doc_overindented_list_item)]

/// 1. nest here
/// lazy continuation
Expand Down
1 change: 1 addition & 0 deletions tests/ui/doc/doc_lazy_list.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#![warn(clippy::doc_lazy_continuation)]
#![allow(clippy::doc_overindented_list_item)]

/// 1. nest here
/// lazy continuation
Expand Down
22 changes: 11 additions & 11 deletions tests/ui/doc/doc_lazy_list.stderr
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error: doc list item without indentation
--> tests/ui/doc/doc_lazy_list.rs:4:5
--> tests/ui/doc/doc_lazy_list.rs:5:5
|
LL | /// lazy continuation
| ^
Expand All @@ -13,7 +13,7 @@ LL | /// lazy continuation
| +++

error: doc list item without indentation
--> tests/ui/doc/doc_lazy_list.rs:9:5
--> tests/ui/doc/doc_lazy_list.rs:10:5
|
LL | /// lazy list continuations don't make warnings with this lint
| ^
Expand All @@ -25,7 +25,7 @@ LL | /// lazy list continuations don't make warnings with this lint
| +++

error: doc list item without indentation
--> tests/ui/doc/doc_lazy_list.rs:11:5
--> tests/ui/doc/doc_lazy_list.rs:12:5
|
LL | /// because they don't have the
| ^
Expand All @@ -37,7 +37,7 @@ LL | /// because they don't have the
| +++

error: doc list item without indentation
--> tests/ui/doc/doc_lazy_list.rs:16:5
--> tests/ui/doc/doc_lazy_list.rs:17:5
|
LL | /// lazy continuation
| ^
Expand All @@ -49,7 +49,7 @@ LL | /// lazy continuation
| ++++

error: doc list item without indentation
--> tests/ui/doc/doc_lazy_list.rs:21:5
--> tests/ui/doc/doc_lazy_list.rs:22:5
|
LL | /// lazy list continuations don't make warnings with this lint
| ^
Expand All @@ -61,7 +61,7 @@ LL | /// lazy list continuations don't make warnings with this lint
| ++++

error: doc list item without indentation
--> tests/ui/doc/doc_lazy_list.rs:23:5
--> tests/ui/doc/doc_lazy_list.rs:24:5
|
LL | /// because they don't have the
| ^
Expand All @@ -73,7 +73,7 @@ LL | /// because they don't have the
| ++++

error: doc list item without indentation
--> tests/ui/doc/doc_lazy_list.rs:28:5
--> tests/ui/doc/doc_lazy_list.rs:29:5
|
LL | /// lazy continuation
| ^
Expand All @@ -85,7 +85,7 @@ LL | /// lazy continuation
| ++++

error: doc list item without indentation
--> tests/ui/doc/doc_lazy_list.rs:33:5
--> tests/ui/doc/doc_lazy_list.rs:34:5
|
LL | /// this will warn on the lazy continuation
| ^
Expand All @@ -97,7 +97,7 @@ LL | /// this will warn on the lazy continuation
| ++++++

error: doc list item without indentation
--> tests/ui/doc/doc_lazy_list.rs:35:5
--> tests/ui/doc/doc_lazy_list.rs:36:5
|
LL | /// and so should this
| ^^^^
Expand All @@ -109,7 +109,7 @@ LL | /// and so should this
| ++

error: doc list item without indentation
--> tests/ui/doc/doc_lazy_list.rs:56:5
--> tests/ui/doc/doc_lazy_list.rs:57:5
|
LL | /// 'protocol_descriptors': [
| ^
Expand All @@ -121,7 +121,7 @@ LL | /// 'protocol_descriptors': [
| +

error: doc list item without indentation
--> tests/ui/doc/doc_lazy_list.rs:75:5
--> tests/ui/doc/doc_lazy_list.rs:76:5
|
LL | /// ]
| ^
Expand Down
28 changes: 28 additions & 0 deletions tests/ui/doc/doc_overindented_list_item.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#![warn(clippy::doc_overindented_list_item)]

#[rustfmt::skip]
/// - first list item
/// overindented line
//~^ ERROR: doc list item overindented
/// this is overindented line too
//~^ ERROR: doc list item overindented
/// - second list item
fn foo() {}

#[rustfmt::skip]
/// - first list item
/// overindented line
//~^ ERROR: doc list item overindented
/// this is overindented line too
//~^ ERROR: doc list item overindented
/// - second list item
fn bar() {}

#[rustfmt::skip]
/// * first list item
/// overindented line
//~^ ERROR: doc list item overindented
/// this is overindented line too
//~^ ERROR: doc list item overindented
/// * second list item
fn baz() {}
28 changes: 28 additions & 0 deletions tests/ui/doc/doc_overindented_list_item.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#![warn(clippy::doc_overindented_list_item)]

#[rustfmt::skip]
/// - first list item
/// overindented line
//~^ ERROR: doc list item overindented
/// this is overindented line too
//~^ ERROR: doc list item overindented
/// - second list item
fn foo() {}

#[rustfmt::skip]
/// - first list item
/// overindented line
//~^ ERROR: doc list item overindented
/// this is overindented line too
//~^ ERROR: doc list item overindented
/// - second list item
fn bar() {}

#[rustfmt::skip]
/// * first list item
/// overindented line
//~^ ERROR: doc list item overindented
/// this is overindented line too
//~^ ERROR: doc list item overindented
/// * second list item
fn baz() {}
76 changes: 76 additions & 0 deletions tests/ui/doc/doc_overindented_list_item.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
error: doc list item overindented
--> tests/ui/doc/doc_overindented_list_item.rs:5:5
|
LL | /// overindented line
| ^^^^^^^
|
= help: if this is supposed to be its own paragraph, add a blank line
= note: `-D clippy::doc-overindented-list-item` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::doc_overindented_list_item)]`
help: indent this line
|
LL | /// overindented line
| ++

error: doc list item overindented
--> tests/ui/doc/doc_overindented_list_item.rs:7:5
|
LL | /// this is overindented line too
| ^^^^^
|
= help: if this is supposed to be its own paragraph, add a blank line
help: indent this line
|
LL | /// this is overindented line too
| ++

error: doc list item overindented
--> tests/ui/doc/doc_overindented_list_item.rs:14:7
|
LL | /// overindented line
| ^^^^^
|
= help: if this is supposed to be its own paragraph, add a blank line
help: indent this line
|
LL | /// overindented line
| ++

error: doc list item overindented
--> tests/ui/doc/doc_overindented_list_item.rs:16:7
|
LL | /// this is overindented line too
| ^^^
|
= help: if this is supposed to be its own paragraph, add a blank line
help: indent this line
|
LL | /// this is overindented line too
| ++

error: doc list item overindented
--> tests/ui/doc/doc_overindented_list_item.rs:23:5
|
LL | /// overindented line
| ^^^^^^^
|
= help: if this is supposed to be its own paragraph, add a blank line
help: indent this line
|
LL | /// overindented line
| ++

error: doc list item overindented
--> tests/ui/doc/doc_overindented_list_item.rs:25:5
|
LL | /// this is overindented line too
| ^^^^^
|
= help: if this is supposed to be its own paragraph, add a blank line
help: indent this line
|
LL | /// this is overindented line too
| ++

error: aborting due to 6 previous errors

0 comments on commit b3cda74

Please sign in to comment.