Skip to content

Commit 48fa1dc

Browse files
committed
Auto merge of rust-lang#7357 - ebobrow:unbalanced-tick, r=xFrednet,flip1995
check for unbalanced tick pairs in doc-markdown lint fixes rust-lang#6753 changelog: check for unbalanced tick pairs in doc-markdown lint
2 parents a8b374f + 20cb1bc commit 48fa1dc

File tree

6 files changed

+151
-15
lines changed

6 files changed

+151
-15
lines changed

clippy_lints/src/doc.rs

+50-14
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
use clippy_utils::diagnostics::{span_lint, span_lint_and_note};
1+
use clippy_utils::diagnostics::{span_lint, span_lint_and_help, 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,8 @@ 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 elements and their
42+
/// content are not linted.
4143
///
4244
/// In addition, when writing documentation comments, including `[]` brackets
4345
/// inside a link text would trip the parser. Therfore, documenting link with
@@ -469,11 +471,11 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
469471
spans: &[(usize, Span)],
470472
) -> DocHeaders {
471473
// true if a safety header was found
472-
use pulldown_cmark::CodeBlockKind;
473474
use pulldown_cmark::Event::{
474475
Code, End, FootnoteReference, HardBreak, Html, Rule, SoftBreak, Start, TaskListMarker, Text,
475476
};
476-
use pulldown_cmark::Tag::{CodeBlock, Heading, Link};
477+
use pulldown_cmark::Tag::{CodeBlock, Heading, Item, Link, Paragraph};
478+
use pulldown_cmark::{CodeBlockKind, CowStr};
477479

478480
let mut headers = DocHeaders {
479481
safety: false,
@@ -485,6 +487,9 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
485487
let mut in_heading = false;
486488
let mut is_rust = false;
487489
let mut edition = None;
490+
let mut ticks_unbalanced = false;
491+
let mut text_to_check: Vec<(CowStr<'_>, Span)> = Vec::new();
492+
let mut paragraph_span = spans.get(0).expect("function isn't called if doc comment is empty").1;
488493
for (event, range) in events {
489494
match event {
490495
Start(CodeBlock(ref kind)) => {
@@ -510,13 +515,42 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
510515
},
511516
Start(Link(_, url, _)) => in_link = Some(url),
512517
End(Link(..)) => in_link = None,
513-
Start(Heading(_)) => in_heading = true,
514-
End(Heading(_)) => in_heading = false,
518+
Start(Heading(_) | Paragraph | Item) => {
519+
if let Start(Heading(_)) = event {
520+
in_heading = true;
521+
}
522+
ticks_unbalanced = false;
523+
let (_, span) = get_current_span(spans, range.start);
524+
paragraph_span = first_line_of_span(cx, span);
525+
},
526+
End(Heading(_) | Paragraph | Item) => {
527+
if let End(Heading(_)) = event {
528+
in_heading = false;
529+
}
530+
if ticks_unbalanced {
531+
span_lint_and_help(
532+
cx,
533+
DOC_MARKDOWN,
534+
paragraph_span,
535+
"backticks are unbalanced",
536+
None,
537+
"a backtick may be missing a pair",
538+
);
539+
} else {
540+
for (text, span) in text_to_check {
541+
check_text(cx, valid_idents, &text, span);
542+
}
543+
}
544+
text_to_check = Vec::new();
545+
},
515546
Start(_tag) | End(_tag) => (), // We don't care about other tags
516547
Html(_html) => (), // HTML is weird, just ignore it
517548
SoftBreak | HardBreak | TaskListMarker(_) | Code(_) | Rule => (),
518549
FootnoteReference(text) | Text(text) => {
519-
if Some(&text) == in_link.as_ref() {
550+
let (begin, span) = get_current_span(spans, range.start);
551+
paragraph_span = paragraph_span.with_hi(span.hi());
552+
ticks_unbalanced |= text.contains('`');
553+
if Some(&text) == in_link.as_ref() || ticks_unbalanced {
520554
// Probably a link of the form `<http://example.com>`
521555
// Which are represented as a link to "http://example.com" with
522556
// text "http://example.com" by pulldown-cmark
@@ -525,11 +559,6 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
525559
headers.safety |= in_heading && text.trim() == "Safety";
526560
headers.errors |= in_heading && text.trim() == "Errors";
527561
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];
533562
if in_code {
534563
if is_rust {
535564
let edition = edition.unwrap_or_else(|| cx.tcx.sess.edition());
@@ -538,15 +567,22 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
538567
} else {
539568
// Adjust for the beginning of the current `Event`
540569
let span = span.with_lo(span.lo() + BytePos::from_usize(range.start - begin));
541-
542-
check_text(cx, valid_idents, &text, span);
570+
text_to_check.push((text, span));
543571
}
544572
},
545573
}
546574
}
547575
headers
548576
}
549577

578+
fn get_current_span(spans: &[(usize, Span)], idx: usize) -> (usize, Span) {
579+
let index = match spans.binary_search_by(|c| c.0.cmp(&idx)) {
580+
Ok(o) => o,
581+
Err(e) => e - 1,
582+
};
583+
spans[index]
584+
}
585+
550586
fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) {
551587
fn has_needless_main(code: &str, edition: Edition) -> bool {
552588
rustc_driver::catch_fatal_errors(|| {

clippy_lints/src/if_let_mutex.rs

+1-1
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>>,
File renamed without changes.
File renamed without changes.

tests/ui/doc/unbalanced_ticks.rs

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//! This file tests for the `DOC_MARKDOWN` lint, specifically cases
2+
//! where ticks are unbalanced (see issue #6753).
3+
4+
#![allow(dead_code)]
5+
#![warn(clippy::doc_markdown)]
6+
7+
/// This is a doc comment with `unbalanced_tick marks and several words that
8+
/// should be `encompassed_by` tick marks because they `contain_underscores`.
9+
/// Because of the initial `unbalanced_tick` pair, the error message is
10+
/// very `confusing_and_misleading`.
11+
fn main() {}
12+
13+
/// This paragraph has `unbalanced_tick marks and should stop_linting.
14+
///
15+
/// This paragraph is fine and should_be linted normally.
16+
///
17+
/// Double unbalanced backtick from ``here to here` should lint.
18+
///
19+
/// Double balanced back ticks ``start end`` is fine.
20+
fn multiple_paragraphs() {}
21+
22+
/// ```
23+
/// // Unbalanced tick mark in code block shouldn't warn:
24+
/// `
25+
/// ```
26+
fn in_code_block() {}
27+
28+
/// # `Fine`
29+
///
30+
/// ## not_fine
31+
///
32+
/// ### `unbalanced
33+
///
34+
/// - This `item has unbalanced tick marks
35+
/// - This item needs backticks_here
36+
fn other_markdown() {}

tests/ui/doc/unbalanced_ticks.stderr

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
error: backticks are unbalanced
2+
--> $DIR/unbalanced_ticks.rs:7:1
3+
|
4+
LL | / /// This is a doc comment with `unbalanced_tick marks and several words that
5+
LL | | /// should be `encompassed_by` tick marks because they `contain_underscores`.
6+
LL | | /// Because of the initial `unbalanced_tick` pair, the error message is
7+
LL | | /// very `confusing_and_misleading`.
8+
| |____________________________________^
9+
|
10+
= note: `-D clippy::doc-markdown` implied by `-D warnings`
11+
= help: a backtick may be missing a pair
12+
13+
error: backticks are unbalanced
14+
--> $DIR/unbalanced_ticks.rs:13:1
15+
|
16+
LL | /// This paragraph has `unbalanced_tick marks and should stop_linting.
17+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
18+
|
19+
= help: a backtick may be missing a pair
20+
21+
error: you should put `should_be` between ticks in the documentation
22+
--> $DIR/unbalanced_ticks.rs:15:32
23+
|
24+
LL | /// This paragraph is fine and should_be linted normally.
25+
| ^^^^^^^^^
26+
27+
error: backticks are unbalanced
28+
--> $DIR/unbalanced_ticks.rs:17:1
29+
|
30+
LL | /// Double unbalanced backtick from ``here to here` should lint.
31+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
32+
|
33+
= help: a backtick may be missing a pair
34+
35+
error: you should put `not_fine` between ticks in the documentation
36+
--> $DIR/unbalanced_ticks.rs:30:8
37+
|
38+
LL | /// ## not_fine
39+
| ^^^^^^^^
40+
41+
error: backticks are unbalanced
42+
--> $DIR/unbalanced_ticks.rs:32:1
43+
|
44+
LL | /// ### `unbalanced
45+
| ^^^^^^^^^^^^^^^^^^^
46+
|
47+
= help: a backtick may be missing a pair
48+
49+
error: backticks are unbalanced
50+
--> $DIR/unbalanced_ticks.rs:34:1
51+
|
52+
LL | /// - This `item has unbalanced tick marks
53+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
54+
|
55+
= help: a backtick may be missing a pair
56+
57+
error: you should put `backticks_here` between ticks in the documentation
58+
--> $DIR/unbalanced_ticks.rs:35:23
59+
|
60+
LL | /// - This item needs backticks_here
61+
| ^^^^^^^^^^^^^^
62+
63+
error: aborting due to 8 previous errors
64+

0 commit comments

Comments
 (0)