Skip to main content

rustdoc/
lint.rs

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