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}