@@ -10,50 +10,50 @@ const MSG: &str = "doc comments should end with a terminal punctuation mark";
1010const PUNCTUATION_SUGGESTION : char = '.' ;
1111
1212pub 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}
0 commit comments