Skip to content

Commit 3edf5da

Browse files
Add new unused_footnote_definition rustdoc lint
1 parent c9627d0 commit 3edf5da

File tree

2 files changed

+41
-6
lines changed

2 files changed

+41
-6
lines changed

src/librustdoc/lint.rs

+8
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,13 @@ declare_rustdoc_lint! {
211211
"footnote reference with no associated definition"
212212
}
213213

214+
declare_rustdoc_lint! {
215+
/// This lint checks if all footnote definitions are used.
216+
UNUSED_FOOTNOTE_DEFINITION,
217+
Warn,
218+
"unused footnote definition"
219+
}
220+
214221
pub(crate) static RUSTDOC_LINTS: Lazy<Vec<&'static Lint>> = Lazy::new(|| {
215222
vec![
216223
BROKEN_INTRA_DOC_LINKS,
@@ -226,6 +233,7 @@ pub(crate) static RUSTDOC_LINTS: Lazy<Vec<&'static Lint>> = Lazy::new(|| {
226233
REDUNDANT_EXPLICIT_LINKS,
227234
UNPORTABLE_MARKDOWN,
228235
BROKEN_FOOTNOTE,
236+
UNUSED_FOOTNOTE_DEFINITION,
229237
]
230238
});
231239

src/librustdoc/passes/lint/unportable_markdown.rs

+33-6
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
1313
use std::collections::{BTreeMap, BTreeSet};
1414

15+
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
1516
use rustc_hir::HirId;
1617
use rustc_lint_defs::Applicability;
1718
use rustc_resolve::rustdoc::source_span_for_markdown_range;
@@ -39,6 +40,8 @@ pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item, hir_id: HirId, dox: &
3940
// the new parser and old parser.
4041
let mut missing_footnote_references = BTreeMap::new();
4142
let mut found_footnote_references = BTreeSet::new();
43+
let mut footnote_references = FxHashSet::default();
44+
let mut footnote_definitions = FxHashMap::default();
4245

4346
// populate problem cases from new parser
4447
{
@@ -51,13 +54,20 @@ pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item, hir_id: HirId, dox: &
5154
}
5255
let parser_new = cmarkn::Parser::new_ext(dox, main_body_opts_new()).into_offset_iter();
5356
for (event, span) in parser_new {
54-
if let cmarkn::Event::Start(cmarkn::Tag::BlockQuote(_)) = event {
55-
if !dox[span.clone()].starts_with("> ") {
56-
spaceless_block_quotes.insert(span.start);
57+
match event {
58+
cmarkn::Event::Start(cmarkn::Tag::BlockQuote(_)) => {
59+
if !dox[span.clone()].starts_with("> ") {
60+
spaceless_block_quotes.insert(span.start);
61+
}
5762
}
58-
}
59-
if let cmarkn::Event::FootnoteReference(_) = event {
60-
found_footnote_references.insert(span.start + 1);
63+
cmarkn::Event::FootnoteReference(label) => {
64+
found_footnote_references.insert(span.start + 1);
65+
footnote_references.insert(label);
66+
}
67+
cmarkn::Event::Start(cmarkn::Tag::FootnoteDefinition(label)) => {
68+
footnote_definitions.insert(label, span.start + 1);
69+
}
70+
_ => {}
6171
}
6272
}
6373
}
@@ -86,6 +96,23 @@ pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item, hir_id: HirId, dox: &
8696
}
8797
}
8898

99+
#[allow(rustc::potential_query_instability)]
100+
for (footnote, span) in footnote_definitions {
101+
if !footnote_references.contains(&footnote) {
102+
let span = source_span_for_markdown_range(
103+
tcx,
104+
dox,
105+
&(span..span + 1),
106+
&item.attrs.doc_strings,
107+
)
108+
.unwrap_or_else(|| item.attr_span(tcx));
109+
110+
tcx.node_span_lint(crate::lint::UNUSED_FOOTNOTE_DEFINITION, hir_id, span, |lint| {
111+
lint.primary_message("unused footnote definition");
112+
});
113+
}
114+
}
115+
89116
for start in spaceless_block_quotes {
90117
let (span, precise) =
91118
source_span_for_markdown_range(tcx, dox, &(start..start + 1), &item.attrs.doc_strings)

0 commit comments

Comments
 (0)