rustdoc/
lint.rs

1use std::sync::LazyLock as Lazy;
2
3use rustc_data_structures::fx::FxHashMap;
4use rustc_lint::LintStore;
5use rustc_lint_defs::{Lint, LintId, declare_tool_lint};
6use rustc_session::{Session, lint};
7
8/// This function is used to setup the lint initialization. By default, in rustdoc, everything
9/// is "allowed". Depending if we run in test mode or not, we want some of them to be at their
10/// default level. For example, the "INVALID_CODEBLOCK_ATTRIBUTES" lint is activated in both
11/// modes.
12///
13/// A little detail easy to forget is that there is a way to set the lint level for all lints
14/// through the "WARNINGS" lint. To prevent this to happen, we set it back to its "normal" level
15/// inside this function.
16///
17/// It returns a tuple containing:
18///  * Vector of tuples of lints' name and their associated "max" level
19///  * HashMap of lint id with their associated "max" level
20pub(crate) fn init_lints<F>(
21    mut allowed_lints: Vec<String>,
22    lint_opts: Vec<(String, lint::Level)>,
23    filter_call: F,
24) -> (Vec<(String, lint::Level)>, FxHashMap<lint::LintId, lint::Level>)
25where
26    F: Fn(&lint::Lint) -> Option<(String, lint::Level)>,
27{
28    let warnings_lint_name = lint::builtin::WARNINGS.name;
29
30    allowed_lints.push(warnings_lint_name.to_owned());
31    allowed_lints.extend(lint_opts.iter().map(|(lint, _)| lint).cloned());
32
33    let lints = || {
34        lint::builtin::HardwiredLints::lint_vec()
35            .into_iter()
36            .chain(rustc_lint::SoftLints::lint_vec())
37    };
38
39    let lint_opts = lints()
40        .filter_map(|lint| {
41            // Permit feature-gated lints to avoid feature errors when trying to
42            // allow all lints.
43            if lint.feature_gate.is_some() || allowed_lints.iter().any(|l| lint.name == l) {
44                None
45            } else {
46                filter_call(lint)
47            }
48        })
49        .chain(lint_opts)
50        .collect::<Vec<_>>();
51
52    let lint_caps = lints()
53        .filter_map(|lint| {
54            // We don't want to allow *all* lints so let's ignore
55            // those ones.
56            if allowed_lints.iter().any(|l| lint.name == l) {
57                None
58            } else {
59                Some((lint::LintId::of(lint), lint::Allow))
60            }
61        })
62        .collect();
63    (lint_opts, lint_caps)
64}
65
66macro_rules! declare_rustdoc_lint {
67    (
68        $(#[$attr:meta])* $name: ident, $level: ident, $descr: literal $(,)?
69        $(@feature_gate = $gate:ident;)?
70    ) => {
71        declare_tool_lint! {
72            $(#[$attr])* pub rustdoc::$name, $level, $descr
73            $(, @feature_gate = $gate;)?
74        }
75    }
76}
77
78declare_rustdoc_lint! {
79    /// The `broken_intra_doc_links` lint detects failures in resolving
80    /// intra-doc link targets. This is a `rustdoc` only lint, see the
81    /// documentation in the [rustdoc book].
82    ///
83    /// [rustdoc book]: ../../../rustdoc/lints.html#broken_intra_doc_links
84    BROKEN_INTRA_DOC_LINKS,
85    Warn,
86    "failures in resolving intra-doc link targets"
87}
88
89declare_rustdoc_lint! {
90    /// This is a subset of `broken_intra_doc_links` that warns when linking from
91    /// a public item to a private one. This is a `rustdoc` only lint, see the
92    /// documentation in the [rustdoc book].
93    ///
94    /// [rustdoc book]: ../../../rustdoc/lints.html#private_intra_doc_links
95    PRIVATE_INTRA_DOC_LINKS,
96    Warn,
97    "linking from a public item to a private one"
98}
99
100declare_rustdoc_lint! {
101    /// The `invalid_codeblock_attributes` lint detects code block attributes
102    /// in documentation examples that have potentially mis-typed values. This
103    /// is a `rustdoc` only lint, see the documentation in the [rustdoc book].
104    ///
105    /// [rustdoc book]: ../../../rustdoc/lints.html#invalid_codeblock_attributes
106    INVALID_CODEBLOCK_ATTRIBUTES,
107    Warn,
108    "codeblock attribute looks a lot like a known one"
109}
110
111declare_rustdoc_lint! {
112    /// The `missing_crate_level_docs` lint detects if documentation is
113    /// missing at the crate root. This is a `rustdoc` only lint, see the
114    /// documentation in the [rustdoc book].
115    ///
116    /// [rustdoc book]: ../../../rustdoc/lints.html#missing_crate_level_docs
117    MISSING_CRATE_LEVEL_DOCS,
118    Allow,
119    "detects crates with no crate-level documentation"
120}
121
122declare_rustdoc_lint! {
123    /// The `missing_doc_code_examples` lint detects publicly-exported items
124    /// without code samples in their documentation. This is a `rustdoc` only
125    /// lint, see the documentation in the [rustdoc book].
126    ///
127    /// [rustdoc book]: ../../../rustdoc/lints.html#missing_doc_code_examples
128    MISSING_DOC_CODE_EXAMPLES,
129    Allow,
130    "detects publicly-exported items without code samples in their documentation",
131    @feature_gate = rustdoc_missing_doc_code_examples;
132}
133
134declare_rustdoc_lint! {
135    /// The `private_doc_tests` lint detects code samples in docs of private
136    /// items not documented by `rustdoc`. This is a `rustdoc` only lint, see
137    /// the documentation in the [rustdoc book].
138    ///
139    /// [rustdoc book]: ../../../rustdoc/lints.html#private_doc_tests
140    PRIVATE_DOC_TESTS,
141    Allow,
142    "detects code samples in docs of private items not documented by rustdoc"
143}
144
145declare_rustdoc_lint! {
146    /// The `invalid_html_tags` lint detects invalid HTML tags. This is a
147    /// `rustdoc` only lint, see the documentation in the [rustdoc book].
148    ///
149    /// [rustdoc book]: ../../../rustdoc/lints.html#invalid_html_tags
150    INVALID_HTML_TAGS,
151    Warn,
152    "detects invalid HTML tags in doc comments"
153}
154
155declare_rustdoc_lint! {
156    /// The `bare_urls` lint detects when a URL is not a hyperlink.
157    /// This is a `rustdoc` only lint, see the documentation in the [rustdoc book].
158    ///
159    /// [rustdoc book]: ../../../rustdoc/lints.html#bare_urls
160    BARE_URLS,
161    Warn,
162    "detects URLs that are not hyperlinks"
163}
164
165declare_rustdoc_lint! {
166   /// The `invalid_rust_codeblocks` lint detects Rust code blocks in
167   /// documentation examples that are invalid (e.g. empty, not parsable as
168   /// Rust code). This is a `rustdoc` only lint, see the documentation in the
169   /// [rustdoc book].
170   ///
171   /// [rustdoc book]: ../../../rustdoc/lints.html#invalid_rust_codeblocks
172   INVALID_RUST_CODEBLOCKS,
173   Warn,
174   "codeblock could not be parsed as valid Rust or is empty"
175}
176
177declare_rustdoc_lint! {
178   /// The `unescaped_backticks` lint detects unescaped backticks (\`), which usually
179   /// mean broken inline code. This is a `rustdoc` only lint, see the documentation
180   /// in the [rustdoc book].
181   ///
182   /// [rustdoc book]: ../../../rustdoc/lints.html#unescaped_backticks
183   UNESCAPED_BACKTICKS,
184   Allow,
185   "detects unescaped backticks in doc comments"
186}
187
188declare_rustdoc_lint! {
189    /// This lint is **warn-by-default**. It detects explicit links that are the same
190    /// as computed automatic links. This usually means the explicit links are removable.
191    /// This is a `rustdoc` only lint, see the documentation in the [rustdoc book].
192    ///
193    /// [rustdoc book]: ../../../rustdoc/lints.html#redundant_explicit_links
194    REDUNDANT_EXPLICIT_LINKS,
195    Warn,
196    "detects redundant explicit links in doc comments"
197}
198
199declare_rustdoc_lint! {
200    /// This compatibility lint checks for Markdown syntax that works in the old engine but not
201    /// the new one.
202    UNPORTABLE_MARKDOWN,
203    Warn,
204    "detects markdown that is interpreted differently in different parser"
205}
206
207pub(crate) static RUSTDOC_LINTS: Lazy<Vec<&'static Lint>> = Lazy::new(|| {
208    vec![
209        BROKEN_INTRA_DOC_LINKS,
210        PRIVATE_INTRA_DOC_LINKS,
211        MISSING_DOC_CODE_EXAMPLES,
212        PRIVATE_DOC_TESTS,
213        INVALID_CODEBLOCK_ATTRIBUTES,
214        INVALID_RUST_CODEBLOCKS,
215        INVALID_HTML_TAGS,
216        BARE_URLS,
217        MISSING_CRATE_LEVEL_DOCS,
218        UNESCAPED_BACKTICKS,
219        REDUNDANT_EXPLICIT_LINKS,
220        UNPORTABLE_MARKDOWN,
221    ]
222});
223
224pub(crate) fn register_lints(_sess: &Session, lint_store: &mut LintStore) {
225    lint_store.register_lints(&RUSTDOC_LINTS);
226    lint_store.register_group(
227        true,
228        "rustdoc::all",
229        Some("rustdoc"),
230        RUSTDOC_LINTS
231            .iter()
232            .filter(|lint| lint.feature_gate.is_none()) // only include stable lints
233            .map(|&lint| LintId::of(lint))
234            .collect(),
235    );
236    for lint in &*RUSTDOC_LINTS {
237        let name = lint.name_lower();
238        lint_store.register_renamed(&name.replace("rustdoc::", ""), &name);
239    }
240    lint_store
241        .register_renamed("intra_doc_link_resolution_failure", "rustdoc::broken_intra_doc_links");
242    lint_store.register_renamed("non_autolinks", "rustdoc::bare_urls");
243    lint_store.register_renamed("rustdoc::non_autolinks", "rustdoc::bare_urls");
244}