about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-07-02 21:30:36 +0000
committerbors <bors@rust-lang.org>2024-07-02 21:30:36 +0000
commit3af20058eb9f2e9fc34a65b1444668b0a0180156 (patch)
tree74b2caa42d1ab23e4414e3f05e03a0baab846889
parent6e6683b15ee32fd875ff682d7c9c35b7a61c5151 (diff)
parent70c8579e210d2fc8dae3bd0ae14c2210326eab5f (diff)
downloadrust-3af20058eb9f2e9fc34a65b1444668b0a0180156.tar.gz
rust-3af20058eb9f2e9fc34a65b1444668b0a0180156.zip
Auto merge of #13010 - notriddle:notriddle/unbalanced-ticks-backslash, r=blyxyas
doc_markdown: detect escaped `` ` `` when checking unmatched

```
changelog: [`doc_markdown`]: correctly detect backslash-escaped `` ` ``
```
-rw-r--r--clippy_lints/src/doc/mod.rs13
-rw-r--r--tests/ui/doc/unbalanced_ticks.rs17
-rw-r--r--tests/ui/doc/unbalanced_ticks.stderr18
3 files changed, 46 insertions, 2 deletions
diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs
index 5faa00b7c97..357ed08c7c7 100644
--- a/clippy_lints/src/doc/mod.rs
+++ b/clippy_lints/src/doc/mod.rs
@@ -769,7 +769,18 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
             TaskListMarker(_) | Code(_) | Rule => (),
             FootnoteReference(text) | Text(text) => {
                 paragraph_range.end = range.end;
-                ticks_unbalanced |= text.contains('`') && !in_code;
+                let range_ = range.clone();
+                ticks_unbalanced |= text.contains('`')
+                    && !in_code
+                    && doc[range.clone()].bytes().enumerate().any(|(i, c)| {
+                        // scan the markdown source code bytes for backquotes that aren't preceded by backslashes
+                        // - use bytes, instead of chars, to avoid utf8 decoding overhead (special chars are ascii)
+                        // - relevant backquotes are within doc[range], but backslashes are not, because they're not
+                        //   actually part of the rendered text (pulldown-cmark doesn't emit any events for escapes)
+                        // - if `range_.start + i == 0`, then `range_.start + i - 1 == -1`, and since we're working in
+                        //   usize, that would underflow and maybe panic
+                        c == b'`' && (range_.start + i == 0 || doc.as_bytes().get(range_.start + i - 1) != Some(&b'\\'))
+                    });
                 if Some(&text) == in_link.as_ref() || ticks_unbalanced {
                     // Probably a link of the form `<http://example.com>`
                     // Which are represented as a link to "http://example.com" with
diff --git a/tests/ui/doc/unbalanced_ticks.rs b/tests/ui/doc/unbalanced_ticks.rs
index 6f7bab72040..04446787b6c 100644
--- a/tests/ui/doc/unbalanced_ticks.rs
+++ b/tests/ui/doc/unbalanced_ticks.rs
@@ -49,3 +49,20 @@ fn other_markdown() {}
 ///   pub struct Struct;
 ///   ```
 fn issue_7421() {}
+
+/// `
+//~^ ERROR: backticks are unbalanced
+fn escape_0() {}
+
+/// Escaped \` backticks don't count.
+fn escape_1() {}
+
+/// Escaped \` \` backticks don't count.
+fn escape_2() {}
+
+/// Escaped \` ` backticks don't count, but unescaped backticks do.
+//~^ ERROR: backticks are unbalanced
+fn escape_3() {}
+
+/// Backslashes ` \` within code blocks don't count.
+fn escape_4() {}
diff --git a/tests/ui/doc/unbalanced_ticks.stderr b/tests/ui/doc/unbalanced_ticks.stderr
index 56ef2913623..50324010e97 100644
--- a/tests/ui/doc/unbalanced_ticks.stderr
+++ b/tests/ui/doc/unbalanced_ticks.stderr
@@ -78,5 +78,21 @@ help: try
 LL | /// - This item needs `backticks_here`
    |                       ~~~~~~~~~~~~~~~~
 
-error: aborting due to 8 previous errors
+error: backticks are unbalanced
+  --> tests/ui/doc/unbalanced_ticks.rs:53:5
+   |
+LL | /// `
+   |     ^
+   |
+   = help: a backtick may be missing a pair
+
+error: backticks are unbalanced
+  --> tests/ui/doc/unbalanced_ticks.rs:63:5
+   |
+LL | /// Escaped \` ` backticks don't count, but unescaped backticks do.
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: a backtick may be missing a pair
+
+error: aborting due to 10 previous errors