summaryrefslogtreecommitdiffstats
path: root/src/tools/clippy/clippy_lints/src/doc_link_with_quotes.rs
blob: cb07f57e87006ff95368b8619b4bb559f983bc8a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
use clippy_utils::diagnostics::span_lint;
use itertools::Itertools;
use rustc_ast::{AttrKind, Attribute};
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};

declare_clippy_lint! {
    /// ### What it does
    /// Detects the syntax `['foo']` in documentation comments (notice quotes instead of backticks)
    /// outside of code blocks
    /// ### Why is this bad?
    /// It is likely a typo when defining an intra-doc link
    ///
    /// ### Example
    /// ```rust
    /// /// See also: ['foo']
    /// fn bar() {}
    /// ```
    /// Use instead:
    /// ```rust
    /// /// See also: [`foo`]
    /// fn bar() {}
    /// ```
    #[clippy::version = "1.60.0"]
    pub DOC_LINK_WITH_QUOTES,
    pedantic,
    "possible typo for an intra-doc link"
}
declare_lint_pass!(DocLinkWithQuotes => [DOC_LINK_WITH_QUOTES]);

impl EarlyLintPass for DocLinkWithQuotes {
    fn check_attribute(&mut self, ctx: &EarlyContext<'_>, attr: &Attribute) {
        if let AttrKind::DocComment(_, symbol) = attr.kind {
            if contains_quote_link(symbol.as_str()) {
                span_lint(
                    ctx,
                    DOC_LINK_WITH_QUOTES,
                    attr.span,
                    "possible intra-doc link using quotes instead of backticks",
                );
            }
        }
    }
}

fn contains_quote_link(s: &str) -> bool {
    let mut in_backticks = false;
    let mut found_opening = false;

    for c in s.chars().tuple_windows::<(char, char)>() {
        match c {
            ('`', _) => in_backticks = !in_backticks,
            ('[', '\'') if !in_backticks => found_opening = true,
            ('\'', ']') if !in_backticks && found_opening => return true,
            _ => {},
        }
    }

    false
}