Skip to content

Commit

Permalink
Rollup merge of rust-lang#105848 - lukas-code:backticks, r=GuillaumeG…
Browse files Browse the repository at this point in the history
…omez,jyn514,notriddle

rustdoc: Add a new lint for broken inline code

This patch adds `rustdoc::unescaped_backticks`, a new rustdoc lint that will detect broken inline code nodes.

The lint woks by finding stray backticks and with some heuristics tries to guess where the second backtick might be missing.

Here is how it looks:
```rust
#![warn(rustdoc::unescaped_backticks)]

/// `add(a, b) is the same as `add(b, a)`.
pub fn add(a: i32, b: i32) -> i32 { a + b }
```
```text
warning: unescaped backtick
 --> src/lib.rs:3:41
  |
3 | /// `add(a, b) is the same as `add(b, a)`.
  |                                         ^
  |
help: a previous inline code might be longer than expected
  |
3 | /// `add(a, b)` is the same as `add(b, a)`.
  |               +
help: if you meant to use a literal backtick, escape it
  |
3 | /// `add(a, b) is the same as `add(b, a)\`.
  |                                         +
```

If we can't get proper spans, for example if the doc comment comes from a macro expansion, we print the suggestion in help messages instead. Here's a [real-world example](https://docs.rs/tracing-subscriber/0.3.17/tracing_subscriber/layer/trait.Filter.html#method.max_level_hint):

```text
warning: unescaped backtick
    --> /tracing-subscriber-0.3.17/src/layer/mod.rs:1400:9
     |
1400 | /         /// Returns an optional hint of the highest [verbosity level][level] that
1401 | |         /// this `Filter` will enable.
1402 | |         ///
1403 | |         /// If this method returns a [`LevelFilter`], it will be used as a hint to
...    |
1427 | |         /// [`Interest`]: tracing_core::subscriber::Interest
1428 | |         /// [rebuild]: tracing_core::callsite::rebuild_interest_cache
     | |_____________________________________________________________________^
     |
     = help: a previous inline code might be longer than expected
              change: Therefore, if the `Filter will change the value returned by this
             to this: Therefore, if the `Filter` will change the value returned by this
     = help: if you meant to use a literal backtick, escape it
              change: [`rebuild_interest_cache`][rebuild] is called after the value of the max
             to this: [`rebuild_interest_cache\`][rebuild] is called after the value of the max
```

You can find more examples [here](https://gist.github.com/lukas-code/7678ddf5c608aee97b3a669de80d3465).

A limitation of the current implementation is, that it cannot suggest removing misplaced backticks, for example [here](https://docs.rs/tikv-jemalloc-sys/0.5.3+5.3.0-patched/tikv_jemalloc_sys/fn.mallctl.html).

The lint is allowed by default ~~and nightly-only~~ for now, ~~but without a feature gate. This is similar to how `rustdoc::invalid_html_tags` and `rustdoc::bare_urls` were handled.~~
  • Loading branch information
matthiaskrgr authored Apr 29, 2023
2 parents f229949 + 4f15a77 commit 4cfaead
Show file tree
Hide file tree
Showing 7 changed files with 1,775 additions and 5 deletions.
38 changes: 38 additions & 0 deletions src/doc/rustdoc/src/lints.md
Original file line number Diff line number Diff line change
Expand Up @@ -374,3 +374,41 @@ warning: this URL is not a hyperlink
warning: 2 warnings emitted
```

## `unescaped_backticks`

This lint is **allowed by default**. It detects backticks (\`) that are not escaped.
This usually means broken inline code. For example:

```rust
#![warn(rustdoc::unescaped_backticks)]

/// `add(a, b) is the same as `add(b, a)`.
pub fn add(a: i32, b: i32) -> i32 { a + b }
```

Which will give:

```text
warning: unescaped backtick
--> src/lib.rs:3:41
|
3 | /// `add(a, b) is the same as `add(b, a)`.
| ^
|
note: the lint level is defined here
--> src/lib.rs:1:9
|
1 | #![warn(rustdoc::unescaped_backticks)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: a previous inline code might be longer than expected
|
3 | /// `add(a, b)` is the same as `add(b, a)`.
| +
help: if you meant to use a literal backtick, escape it
|
3 | /// `add(a, b) is the same as `add(b, a)\`.
| +
warning: 1 warning emitted
```
11 changes: 6 additions & 5 deletions src/librustdoc/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@
#![feature(assert_matches)]
#![feature(box_patterns)]
#![feature(drain_filter)]
#![feature(impl_trait_in_assoc_type)]
#![feature(iter_intersperse)]
#![feature(lazy_cell)]
#![feature(let_chains)]
#![feature(test)]
#![feature(never_type)]
#![feature(lazy_cell)]
#![feature(type_ascription)]
#![feature(iter_intersperse)]
#![feature(round_char_boundary)]
#![feature(test)]
#![feature(type_alias_impl_trait)]
#![feature(impl_trait_in_assoc_type)]
#![feature(type_ascription)]
#![recursion_limit = "256"]
#![warn(rustc::internal)]
#![allow(clippy::collapsible_if, clippy::collapsible_else_if)]
Expand Down
12 changes: 12 additions & 0 deletions src/librustdoc/lint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,17 @@ declare_rustdoc_lint! {
"codeblock could not be parsed as valid Rust or is empty"
}

declare_rustdoc_lint! {
/// The `unescaped_backticks` lint detects unescaped backticks (\`), which usually
/// mean broken inline code. This is a `rustdoc` only lint, see the documentation
/// in the [rustdoc book].
///
/// [rustdoc book]: ../../../rustdoc/lints.html#unescaped_backticks
UNESCAPED_BACKTICKS,
Allow,
"detects unescaped backticks in doc comments"
}

pub(crate) static RUSTDOC_LINTS: Lazy<Vec<&'static Lint>> = Lazy::new(|| {
vec![
BROKEN_INTRA_DOC_LINKS,
Expand All @@ -185,6 +196,7 @@ pub(crate) static RUSTDOC_LINTS: Lazy<Vec<&'static Lint>> = Lazy::new(|| {
INVALID_HTML_TAGS,
BARE_URLS,
MISSING_CRATE_LEVEL_DOCS,
UNESCAPED_BACKTICKS,
]
});

Expand Down
2 changes: 2 additions & 0 deletions src/librustdoc/passes/lint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
mod bare_urls;
mod check_code_block_syntax;
mod html_tags;
mod unescaped_backticks;

use super::Pass;
use crate::clean::*;
Expand All @@ -27,6 +28,7 @@ impl<'a, 'tcx> DocVisitor for Linter<'a, 'tcx> {
bare_urls::visit_item(self.cx, item);
check_code_block_syntax::visit_item(self.cx, item);
html_tags::visit_item(self.cx, item);
unescaped_backticks::visit_item(self.cx, item);

self.visit_item_recur(item)
}
Expand Down
Loading

0 comments on commit 4cfaead

Please sign in to comment.