Skip to content

Commit 54467ca

Browse files
committed
check for unbalanced tick pairs in doc-markdown
1 parent 6bf8772 commit 54467ca

File tree

4 files changed

+92
-43
lines changed

4 files changed

+92
-43
lines changed

clippy_lints/src/doc.rs

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use clippy_utils::diagnostics::{span_lint, span_lint_and_note};
2+
use clippy_utils::source::first_line_of_span;
23
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
34
use clippy_utils::{is_entrypoint_fn, is_expn_of, match_panic_def_id, method_chain_args, return_ty};
45
use if_chain::if_chain;
@@ -37,7 +38,7 @@ declare_clippy_lint! {
3738
/// consider that.
3839
///
3940
/// **Known problems:** Lots of bad docs won’t be fixed, what the lint checks
40-
/// for is limited, and there are still false positives.
41+
/// for is limited, and there are still false positives. HTML is not linted.
4142
///
4243
/// In addition, when writing documentation comments, including `[]` brackets
4344
/// inside a link text would trip the parser. Therfore, documenting link with
@@ -469,11 +470,11 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
469470
spans: &[(usize, Span)],
470471
) -> DocHeaders {
471472
// true if a safety header was found
472-
use pulldown_cmark::CodeBlockKind;
473473
use pulldown_cmark::Event::{
474474
Code, End, FootnoteReference, HardBreak, Html, Rule, SoftBreak, Start, TaskListMarker, Text,
475475
};
476-
use pulldown_cmark::Tag::{CodeBlock, Heading, Link};
476+
use pulldown_cmark::Tag::{CodeBlock, Heading, Link, Paragraph};
477+
use pulldown_cmark::{CodeBlockKind, CowStr};
477478

478479
let mut headers = DocHeaders {
479480
safety: false,
@@ -485,6 +486,9 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
485486
let mut in_heading = false;
486487
let mut is_rust = false;
487488
let mut edition = None;
489+
let mut ticks_unbalanced = false;
490+
let mut text_to_check: Vec<(CowStr<'_>, Span)> = Vec::new();
491+
let mut paragraph_span = spans[0].1; // First line
488492
for (event, range) in events {
489493
match event {
490494
Start(CodeBlock(ref kind)) => {
@@ -512,24 +516,45 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
512516
End(Link(..)) => in_link = None,
513517
Start(Heading(_)) => in_heading = true,
514518
End(Heading(_)) => in_heading = false,
519+
Start(Paragraph) => {
520+
ticks_unbalanced = false;
521+
let (_, span) = get_current_span(spans, range.start);
522+
paragraph_span = first_line_of_span(cx, span);
523+
},
524+
End(Paragraph) => {
525+
if ticks_unbalanced {
526+
span_lint(
527+
cx,
528+
DOC_MARKDOWN,
529+
paragraph_span,
530+
"backticks are unbalanced; one may be missing a pair",
531+
);
532+
} else {
533+
for (text, span) in text_to_check {
534+
check_text(cx, valid_idents, &text, span);
535+
}
536+
}
537+
text_to_check = Vec::new();
538+
},
515539
Start(_tag) | End(_tag) => (), // We don't care about other tags
516540
Html(_html) => (), // HTML is weird, just ignore it
517541
SoftBreak | HardBreak | TaskListMarker(_) | Code(_) | Rule => (),
518542
FootnoteReference(text) | Text(text) => {
519-
if Some(&text) == in_link.as_ref() {
543+
let (begin, span) = get_current_span(spans, range.start);
544+
paragraph_span = paragraph_span.with_hi(span.hi());
545+
if Some(&text) == in_link.as_ref() || ticks_unbalanced {
520546
// Probably a link of the form `<http://example.com>`
521547
// Which are represented as a link to "http://example.com" with
522548
// text "http://example.com" by pulldown-cmark
523549
continue;
524550
}
551+
if text.contains('`') && !in_code {
552+
ticks_unbalanced = true;
553+
continue;
554+
}
525555
headers.safety |= in_heading && text.trim() == "Safety";
526556
headers.errors |= in_heading && text.trim() == "Errors";
527557
headers.panics |= in_heading && text.trim() == "Panics";
528-
let index = match spans.binary_search_by(|c| c.0.cmp(&range.start)) {
529-
Ok(o) => o,
530-
Err(e) => e - 1,
531-
};
532-
let (begin, span) = spans[index];
533558
if in_code {
534559
if is_rust {
535560
let edition = edition.unwrap_or_else(|| cx.tcx.sess.edition());
@@ -538,15 +563,22 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
538563
} else {
539564
// Adjust for the beginning of the current `Event`
540565
let span = span.with_lo(span.lo() + BytePos::from_usize(range.start - begin));
541-
542-
check_text(cx, valid_idents, &text, span);
566+
text_to_check.push((text, span));
543567
}
544568
},
545569
}
546570
}
547571
headers
548572
}
549573

574+
fn get_current_span(spans: &[(usize, Span)], idx: usize) -> (usize, Span) {
575+
let index = match spans.binary_search_by(|c| c.0.cmp(&idx)) {
576+
Ok(o) => o,
577+
Err(e) => e - 1,
578+
};
579+
spans[index]
580+
}
581+
550582
fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) {
551583
fn has_needless_main(code: &str, edition: Edition) -> bool {
552584
rustc_driver::catch_fatal_errors(|| {

clippy_lints/src/if_let_mutex.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ impl<'tcx> LateLintPass<'tcx> for IfLetMutex {
8383
}
8484
}
8585

86-
/// Checks if `Mutex::lock` is called in the `if let _ = expr.
86+
/// Checks if `Mutex::lock` is called in the `if let` expr.
8787
pub struct OppVisitor<'a, 'tcx> {
8888
mutex_lock_called: bool,
8989
found_mutex: Option<&'tcx Expr<'tcx>>,

tests/ui/doc.rs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,6 @@ fn main() {
9696
/// ## CamelCaseThing
9797
/// Talks about `CamelCaseThing`. Titles should be ignored; see issue #897.
9898
///
99-
/// # CamelCaseThing
100-
///
10199
/// Not a title #897 CamelCaseThing
102100
/// be_sure_we_got_to_the_end_of_it
103101
fn issue897() {
@@ -185,7 +183,6 @@ fn issue_1920() {}
185183

186184
/// Ok: <http://www.unicode.org/reports/tr9/#Reordering_Resolved_Levels>
187185
///
188-
/// Not ok: http://www.unicode.org
189186
/// Not ok: https://www.unicode.org
190187
/// Not ok: http://www.unicode.org/
191188
/// Not ok: http://www.unicode.org/reports/tr9/#Reordering_Resolved_Levels
@@ -203,6 +200,20 @@ fn issue_2343() {}
203200
/// __|_ _|__||_|
204201
fn pulldown_cmark_crash() {}
205202

203+
/// This has `unbalanced_tick marks so it should_not_lint elsewhere.
204+
fn issue_6753() {}
205+
206+
/// This paragraph has `unbalanced_tick marks and should throw an error.
207+
///
208+
/// This paragraph is fine and should_be linted normally.
209+
fn unbalanced_ticks() {}
210+
211+
/// ```
212+
/// // Unbalanced tick mark in code block shouldn't warn:
213+
/// `
214+
/// ```
215+
fn unbalanced_ticks_in_code() {}
216+
206217
// issue #7033 - const_evaluatable_checked ICE
207218
struct S<T, const N: usize>
208219
where [(); N.checked_next_power_of_two().unwrap()]: {

tests/ui/doc.stderr

Lines changed: 34 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -85,106 +85,112 @@ LL | /// ## CamelCaseThing
8585
| ^^^^^^^^^^^^^^
8686

8787
error: you should put `CamelCaseThing` between ticks in the documentation
88-
--> $DIR/doc.rs:99:7
89-
|
90-
LL | /// # CamelCaseThing
91-
| ^^^^^^^^^^^^^^
92-
93-
error: you should put `CamelCaseThing` between ticks in the documentation
94-
--> $DIR/doc.rs:101:22
88+
--> $DIR/doc.rs:99:22
9589
|
9690
LL | /// Not a title #897 CamelCaseThing
9791
| ^^^^^^^^^^^^^^
9892

9993
error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
100-
--> $DIR/doc.rs:102:5
94+
--> $DIR/doc.rs:100:5
10195
|
10296
LL | /// be_sure_we_got_to_the_end_of_it
10397
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
10498

10599
error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
106-
--> $DIR/doc.rs:109:5
100+
--> $DIR/doc.rs:107:5
107101
|
108102
LL | /// be_sure_we_got_to_the_end_of_it
109103
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
110104

111105
error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
112-
--> $DIR/doc.rs:122:5
106+
--> $DIR/doc.rs:120:5
113107
|
114108
LL | /// be_sure_we_got_to_the_end_of_it
115109
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
116110

117111
error: you should put `FooBar` between ticks in the documentation
118-
--> $DIR/doc.rs:133:43
112+
--> $DIR/doc.rs:131:43
119113
|
120114
LL | /** E.g., serialization of an empty list: FooBar
121115
| ^^^^^^
122116

123117
error: you should put `BarQuz` between ticks in the documentation
124-
--> $DIR/doc.rs:138:5
118+
--> $DIR/doc.rs:136:5
125119
|
126120
LL | And BarQuz too.
127121
| ^^^^^^
128122

129123
error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
130-
--> $DIR/doc.rs:139:1
124+
--> $DIR/doc.rs:137:1
131125
|
132126
LL | be_sure_we_got_to_the_end_of_it
133127
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
134128

135129
error: you should put `FooBar` between ticks in the documentation
136-
--> $DIR/doc.rs:144:43
130+
--> $DIR/doc.rs:142:43
137131
|
138132
LL | /** E.g., serialization of an empty list: FooBar
139133
| ^^^^^^
140134

141135
error: you should put `BarQuz` between ticks in the documentation
142-
--> $DIR/doc.rs:149:5
136+
--> $DIR/doc.rs:147:5
143137
|
144138
LL | And BarQuz too.
145139
| ^^^^^^
146140

147141
error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
148-
--> $DIR/doc.rs:150:1
142+
--> $DIR/doc.rs:148:1
149143
|
150144
LL | be_sure_we_got_to_the_end_of_it
151145
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
152146

153147
error: you should put `be_sure_we_got_to_the_end_of_it` between ticks in the documentation
154-
--> $DIR/doc.rs:161:5
148+
--> $DIR/doc.rs:159:5
155149
|
156150
LL | /// be_sure_we_got_to_the_end_of_it
157151
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
158152

159153
error: you should put bare URLs between `<`/`>` or make a proper Markdown link
160-
--> $DIR/doc.rs:188:13
161-
|
162-
LL | /// Not ok: http://www.unicode.org
163-
| ^^^^^^^^^^^^^^^^^^^^^^
164-
165-
error: you should put bare URLs between `<`/`>` or make a proper Markdown link
166-
--> $DIR/doc.rs:189:13
154+
--> $DIR/doc.rs:186:13
167155
|
168156
LL | /// Not ok: https://www.unicode.org
169157
| ^^^^^^^^^^^^^^^^^^^^^^^
170158

171159
error: you should put bare URLs between `<`/`>` or make a proper Markdown link
172-
--> $DIR/doc.rs:190:13
160+
--> $DIR/doc.rs:187:13
173161
|
174162
LL | /// Not ok: http://www.unicode.org/
175163
| ^^^^^^^^^^^^^^^^^^^^^^
176164

177165
error: you should put bare URLs between `<`/`>` or make a proper Markdown link
178-
--> $DIR/doc.rs:191:13
166+
--> $DIR/doc.rs:188:13
179167
|
180168
LL | /// Not ok: http://www.unicode.org/reports/tr9/#Reordering_Resolved_Levels
181169
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
182170

183171
error: you should put `mycrate::Collection` between ticks in the documentation
184-
--> $DIR/doc.rs:194:22
172+
--> $DIR/doc.rs:191:22
185173
|
186174
LL | /// An iterator over mycrate::Collection's values.
187175
| ^^^^^^^^^^^^^^^^^^^
188176

189-
error: aborting due to 31 previous errors
177+
error: backticks are unbalanced; one may be missing a pair
178+
--> $DIR/doc.rs:203:1
179+
|
180+
LL | /// This has `unbalanced_tick marks so it should_not_lint elsewhere.
181+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
182+
183+
error: backticks are unbalanced; one may be missing a pair
184+
--> $DIR/doc.rs:206:1
185+
|
186+
LL | /// This paragraph has `unbalanced_tick marks and should throw an error.
187+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
188+
189+
error: you should put `should_be` between ticks in the documentation
190+
--> $DIR/doc.rs:208:32
191+
|
192+
LL | /// This paragraph is fine and should_be linted normally.
193+
| ^^^^^^^^^
194+
195+
error: aborting due to 32 previous errors
190196

0 commit comments

Comments
 (0)