Skip to content

Commit c38fcd1

Browse files
doc_comments_missing_terminal_punctuation: check every paragraph
1 parent 49be55f commit c38fcd1

File tree

5 files changed

+112
-73
lines changed

5 files changed

+112
-73
lines changed

clippy_lints/src/doc/doc_comments_missing_terminal_punctuation.rs

Lines changed: 51 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -10,50 +10,50 @@ const MSG: &str = "doc comments should end with a terminal punctuation mark";
1010
const PUNCTUATION_SUGGESTION: char = '.';
1111

1212
pub fn check(cx: &LateContext<'_>, doc: &str, fragments: Fragments<'_>) {
13-
match is_missing_punctuation(doc) {
14-
IsMissingPunctuation::Fixable(offset) => {
15-
// This ignores `#[doc]` attributes, which we do not handle.
16-
if let Some(span) = fragments.span(cx, offset..offset) {
17-
clippy_utils::diagnostics::span_lint_and_sugg(
18-
cx,
19-
DOC_COMMENTS_MISSING_TERMINAL_PUNCTUATION,
20-
span,
21-
MSG,
22-
"end the doc comment with some punctuation",
23-
PUNCTUATION_SUGGESTION.to_string(),
24-
Applicability::MaybeIncorrect,
25-
);
26-
}
27-
},
28-
IsMissingPunctuation::Unfixable(offset) => {
29-
// This ignores `#[doc]` attributes, which we do not handle.
30-
if let Some(span) = fragments.span(cx, offset..offset) {
31-
clippy_utils::diagnostics::span_lint_and_help(
32-
cx,
33-
DOC_COMMENTS_MISSING_TERMINAL_PUNCTUATION,
34-
span,
35-
MSG,
36-
None,
37-
"end the doc comment with some punctuation",
38-
);
39-
}
40-
},
41-
IsMissingPunctuation::No => {},
13+
for missing_punctuation in is_missing_punctuation(doc) {
14+
match missing_punctuation {
15+
MissingPunctuation::Fixable(offset) => {
16+
// This ignores `#[doc]` attributes, which we do not handle.
17+
if let Some(span) = fragments.span(cx, offset..offset) {
18+
clippy_utils::diagnostics::span_lint_and_sugg(
19+
cx,
20+
DOC_COMMENTS_MISSING_TERMINAL_PUNCTUATION,
21+
span,
22+
MSG,
23+
"end the doc comment with some punctuation",
24+
PUNCTUATION_SUGGESTION.to_string(),
25+
Applicability::MaybeIncorrect,
26+
);
27+
}
28+
},
29+
MissingPunctuation::Unfixable(offset) => {
30+
// This ignores `#[doc]` attributes, which we do not handle.
31+
if let Some(span) = fragments.span(cx, offset..offset) {
32+
clippy_utils::diagnostics::span_lint_and_help(
33+
cx,
34+
DOC_COMMENTS_MISSING_TERMINAL_PUNCTUATION,
35+
span,
36+
MSG,
37+
None,
38+
"end the doc comment with some punctuation",
39+
);
40+
}
41+
},
42+
}
4243
}
4344
}
4445

4546
#[must_use]
4647
/// If punctuation is missing, returns the offset where new punctuation should be inserted.
47-
fn is_missing_punctuation(doc_string: &str) -> IsMissingPunctuation {
48-
const TERMINAL_PUNCTUATION_MARKS: &[char] = &['.', '?', '!', '…'];
49-
50-
// Short-circuit in simple, common cases to avoid Markdown parsing.
51-
if doc_string.trim_end().ends_with(TERMINAL_PUNCTUATION_MARKS) {
52-
return IsMissingPunctuation::No;
53-
}
48+
fn is_missing_punctuation(doc_string: &str) -> Vec<MissingPunctuation> {
49+
// The colon is not exactly a terminal punctuation mark, but this is required for paragraphs that
50+
// introduce a table or a list for example.
51+
const TERMINAL_PUNCTUATION_MARKS: &[char] = &['.', '?', '!', '…', ':'];
5452

5553
let mut no_report_depth = 0;
56-
let mut missing_punctuation = IsMissingPunctuation::No;
54+
let mut missing_punctuation = Vec::new();
55+
let mut current_paragraph = None;
56+
5757
for (event, offset) in
5858
Parser::new_ext(doc_string, main_body_opts() - Options::ENABLE_SMART_PUNCTUATION).into_offset_iter()
5959
{
@@ -75,10 +75,15 @@ fn is_missing_punctuation(doc_string: &str) -> IsMissingPunctuation {
7575
TagEnd::CodeBlock | TagEnd::Heading(_) | TagEnd::HtmlBlock | TagEnd::List(_) | TagEnd::Table,
7676
) => {
7777
no_report_depth -= 1;
78-
missing_punctuation = IsMissingPunctuation::No;
78+
current_paragraph = None;
7979
},
8080
Event::InlineHtml(_) | Event::Start(Tag::Image { .. }) | Event::End(TagEnd::Image) => {
81-
missing_punctuation = IsMissingPunctuation::No;
81+
current_paragraph = None;
82+
},
83+
Event::End(TagEnd::Paragraph) => {
84+
if let Some(mp) = current_paragraph {
85+
missing_punctuation.push(mp);
86+
}
8287
},
8388
Event::Code(..) | Event::Start(Tag::Link { .. }) | Event::End(TagEnd::Link)
8489
if no_report_depth == 0 && !offset.is_empty() =>
@@ -87,24 +92,24 @@ fn is_missing_punctuation(doc_string: &str) -> IsMissingPunctuation {
8792
.trim_end()
8893
.ends_with(TERMINAL_PUNCTUATION_MARKS)
8994
{
90-
missing_punctuation = IsMissingPunctuation::No;
95+
current_paragraph = None;
9196
} else {
92-
missing_punctuation = IsMissingPunctuation::Fixable(offset.end);
97+
current_paragraph = Some(MissingPunctuation::Fixable(offset.end));
9398
}
9499
},
95100
Event::Text(..) if no_report_depth == 0 && !offset.is_empty() => {
96101
let trimmed = doc_string[..offset.end].trim_end();
97102
if trimmed.ends_with(TERMINAL_PUNCTUATION_MARKS) {
98-
missing_punctuation = IsMissingPunctuation::No;
103+
current_paragraph = None;
99104
} else if let Some(t) = trimmed.strip_suffix(|c| c == ')' || c == '"') {
100105
if t.ends_with(TERMINAL_PUNCTUATION_MARKS) {
101106
// Avoid false positives.
102-
missing_punctuation = IsMissingPunctuation::No;
107+
current_paragraph = None;
103108
} else {
104-
missing_punctuation = IsMissingPunctuation::Unfixable(offset.end);
109+
current_paragraph = Some(MissingPunctuation::Unfixable(offset.end));
105110
}
106111
} else {
107-
missing_punctuation = IsMissingPunctuation::Fixable(offset.end);
112+
current_paragraph = Some(MissingPunctuation::Fixable(offset.end));
108113
}
109114
},
110115
_ => {},
@@ -115,8 +120,7 @@ fn is_missing_punctuation(doc_string: &str) -> IsMissingPunctuation {
115120
}
116121

117122
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
118-
enum IsMissingPunctuation {
123+
enum MissingPunctuation {
119124
Fixable(usize),
120125
Unfixable(usize),
121-
No,
122126
}

clippy_lints/src/doc/mod.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -671,19 +671,24 @@ declare_clippy_lint! {
671671

672672
declare_clippy_lint! {
673673
/// ### What it does
674-
/// Checks for doc comments that do not end with a period or another punctuation mark.
674+
/// Checks for doc comments whose paragraphs do not end with a period or another punctuation mark.
675675
/// Various Markdowns constructs are taken into account to avoid false positives.
676676
///
677677
/// ### Why is this bad?
678-
/// A project may wish to enforce consistent doc comments by making sure they end with a punctuation mark.
678+
/// A project may wish to enforce consistent doc comments by making sure paragraphs end with a
679+
/// punctuation mark.
679680
///
680681
/// ### Example
681682
/// ```no_run
682-
/// /// Returns the Answer to the Ultimate Question of Life, the Universe, and Everything
683+
/// /// Returns a random number
684+
/// ///
685+
/// /// It was chosen by a fair dice roll
683686
/// ```
684687
/// Use instead:
685688
/// ```no_run
686-
/// /// Returns the Answer to the Ultimate Question of Life, the Universe, and Everything.
689+
/// /// Returns a random number.
690+
/// ///
691+
/// /// It was chosen by a fair dice roll.
687692
/// ```
688693
#[clippy::version = "1.92.0"]
689694
pub DOC_COMMENTS_MISSING_TERMINAL_PUNCTUATION,

tests/ui/doc/doc_comments_missing_terminal_punctuation.fixed

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ enum Exceptions {
4646
/// | -------------- | ----- |
4747
/// | Markdown table | A-ok |
4848
MarkdownTable,
49-
/// Here is a snippet
49+
/// Here is a snippet.
50+
//~^ doc_comments_missing_terminal_punctuation
5051
///
5152
/// ```
5253
/// // Code blocks are no issues.
@@ -134,12 +135,17 @@ enum OrderedLists {
134135
///
135136
struct TrailingBlankLine;
136137

137-
/// The first paragraph is not checked
138+
/// This doc comment has multiple paragraph.
139+
/// This first paragraph is missing punctuation.
140+
//~^ doc_comments_missing_terminal_punctuation
141+
///
142+
/// The second one as well
143+
/// And it has multiple sentences.
144+
//~^ doc_comments_missing_terminal_punctuation
138145
///
139-
/// Other sentences are not either
140-
/// Only the last sentence is.
146+
/// Same for this third and last one.
141147
//~^ doc_comments_missing_terminal_punctuation
142-
struct OnlyLastSentence;
148+
struct MultiParagraphDocComment;
143149

144150
/// ```
145151
struct IncompleteBlockCode;

tests/ui/doc/doc_comments_missing_terminal_punctuation.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ enum Exceptions {
4747
/// | Markdown table | A-ok |
4848
MarkdownTable,
4949
/// Here is a snippet
50+
//~^ doc_comments_missing_terminal_punctuation
5051
///
5152
/// ```
5253
/// // Code blocks are no issues.
@@ -134,12 +135,17 @@ enum OrderedLists {
134135
///
135136
struct TrailingBlankLine;
136137

137-
/// The first paragraph is not checked
138+
/// This doc comment has multiple paragraph.
139+
/// This first paragraph is missing punctuation
140+
//~^ doc_comments_missing_terminal_punctuation
141+
///
142+
/// The second one as well
143+
/// And it has multiple sentences
144+
//~^ doc_comments_missing_terminal_punctuation
138145
///
139-
/// Other sentences are not either
140-
/// Only the last sentence is
146+
/// Same for this third and last one
141147
//~^ doc_comments_missing_terminal_punctuation
142-
struct OnlyLastSentence;
148+
struct MultiParagraphDocComment;
143149

144150
/// ```
145151
struct IncompleteBlockCode;

tests/ui/doc/doc_comments_missing_terminal_punctuation.stderr

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -32,64 +32,82 @@ LL | /// <https://spec.commonmark.org/0.31.2/#autolinks>
3232
| ^ help: end the doc comment with some punctuation: `.`
3333

3434
error: doc comments should end with a terminal punctuation mark
35-
--> tests/ui/doc/doc_comments_missing_terminal_punctuation.rs:71:15
35+
--> tests/ui/doc/doc_comments_missing_terminal_punctuation.rs:49:26
36+
|
37+
LL | /// Here is a snippet
38+
| ^ help: end the doc comment with some punctuation: `.`
39+
40+
error: doc comments should end with a terminal punctuation mark
41+
--> tests/ui/doc/doc_comments_missing_terminal_punctuation.rs:72:15
3642
|
3743
LL | /// U+0001
3844
| ^ help: end the doc comment with some punctuation: `.`
3945

4046
error: doc comments should end with a terminal punctuation mark
41-
--> tests/ui/doc/doc_comments_missing_terminal_punctuation.rs:78:29
47+
--> tests/ui/doc/doc_comments_missing_terminal_punctuation.rs:79:29
4248
|
4349
LL | //! inner attributes too
4450
| ^ help: end the doc comment with some punctuation: `.`
4551

4652
error: doc comments should end with a terminal punctuation mark
47-
--> tests/ui/doc/doc_comments_missing_terminal_punctuation.rs:89:47
53+
--> tests/ui/doc/doc_comments_missing_terminal_punctuation.rs:90:47
4854
|
4955
LL | /// **But sometimes it is missing a period**
5056
| ^ help: end the doc comment with some punctuation: `.`
5157

5258
error: doc comments should end with a terminal punctuation mark
53-
--> tests/ui/doc/doc_comments_missing_terminal_punctuation.rs:94:46
59+
--> tests/ui/doc/doc_comments_missing_terminal_punctuation.rs:95:46
5460
|
5561
LL | /// _But sometimes it is missing a period_
5662
| ^ help: end the doc comment with some punctuation: `.`
5763

5864
error: doc comments should end with a terminal punctuation mark
59-
--> tests/ui/doc/doc_comments_missing_terminal_punctuation.rs:103:56
65+
--> tests/ui/doc/doc_comments_missing_terminal_punctuation.rs:104:56
6066
|
6167
LL | /// Doc comments can end with an [inline link](#anchor)
6268
| ^ help: end the doc comment with some punctuation: `.`
6369

6470
error: doc comments should end with a terminal punctuation mark
65-
--> tests/ui/doc/doc_comments_missing_terminal_punctuation.rs:107:65
71+
--> tests/ui/doc/doc_comments_missing_terminal_punctuation.rs:108:65
6672
|
6773
LL | /// Some doc comments contain [link reference definitions][spec]
6874
| ^ help: end the doc comment with some punctuation: `.`
6975

7076
error: doc comments should end with a terminal punctuation mark
71-
--> tests/ui/doc/doc_comments_missing_terminal_punctuation.rs:132:57
77+
--> tests/ui/doc/doc_comments_missing_terminal_punctuation.rs:133:57
7278
|
7379
LL | /// Doc comments with trailing blank lines are supported
7480
| ^ help: end the doc comment with some punctuation: `.`
7581

7682
error: doc comments should end with a terminal punctuation mark
77-
--> tests/ui/doc/doc_comments_missing_terminal_punctuation.rs:140:30
83+
--> tests/ui/doc/doc_comments_missing_terminal_punctuation.rs:139:48
84+
|
85+
LL | /// This first paragraph is missing punctuation
86+
| ^ help: end the doc comment with some punctuation: `.`
87+
88+
error: doc comments should end with a terminal punctuation mark
89+
--> tests/ui/doc/doc_comments_missing_terminal_punctuation.rs:143:34
90+
|
91+
LL | /// And it has multiple sentences
92+
| ^ help: end the doc comment with some punctuation: `.`
93+
94+
error: doc comments should end with a terminal punctuation mark
95+
--> tests/ui/doc/doc_comments_missing_terminal_punctuation.rs:146:37
7896
|
79-
LL | /// Only the last sentence is
80-
| ^ help: end the doc comment with some punctuation: `.`
97+
LL | /// Same for this third and last one
98+
| ^ help: end the doc comment with some punctuation: `.`
8199

82100
error: doc comments should end with a terminal punctuation mark
83-
--> tests/ui/doc/doc_comments_missing_terminal_punctuation.rs:147:33
101+
--> tests/ui/doc/doc_comments_missing_terminal_punctuation.rs:153:33
84102
|
85103
LL | /// This ends with a code `span`
86104
| ^ help: end the doc comment with some punctuation: `.`
87105

88106
error: doc comments should end with a terminal punctuation mark
89-
--> tests/ui/doc/doc_comments_missing_terminal_punctuation.rs:156:27
107+
--> tests/ui/doc/doc_comments_missing_terminal_punctuation.rs:162:27
90108
|
91109
LL | * Block doc comments work
92110
| ^ help: end the doc comment with some punctuation: `.`
93111

94-
error: aborting due to 15 previous errors
112+
error: aborting due to 18 previous errors
95113

0 commit comments

Comments
 (0)