Skip to content

Commit 1bc04a2

Browse files
Add new unused_footnote_definition rustdoc lint
1 parent d8266c2 commit 1bc04a2

File tree

2 files changed

+35
-2
lines changed

2 files changed

+35
-2
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/footnotes.rs

+27-2
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@
1212
1313
use std::ops::Range;
1414

15-
use pulldown_cmark::{Event, Options, Parser};
16-
use rustc_data_structures::fx::FxHashSet;
15+
use pulldown_cmark::{Event, Options, Parser, Tag};
16+
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
1717
use rustc_hir::HirId;
1818
use rustc_lint_defs::Applicability;
1919
use rustc_resolve::rustdoc::source_span_for_markdown_range;
@@ -25,6 +25,8 @@ pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item, hir_id: HirId, dox: &
2525
let tcx = cx.tcx;
2626

2727
let mut missing_footnote_references = FxHashSet::default();
28+
let mut footnote_references = FxHashSet::default();
29+
let mut footnote_definitions = FxHashMap::default();
2830

2931
let options = Options::ENABLE_FOOTNOTES;
3032
let mut parser = Parser::new_ext(dox, options).into_offset_iter().peekable();
@@ -40,10 +42,33 @@ pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item, hir_id: HirId, dox: &
4042
{
4143
missing_footnote_references.insert(Range { start: span.start, end: end_span.end });
4244
}
45+
Event::FootnoteReference(label) => {
46+
footnote_references.insert(label);
47+
}
48+
Event::Start(Tag::FootnoteDefinition(label)) => {
49+
footnote_definitions.insert(label, span.start + 1);
50+
}
4351
_ => {}
4452
}
4553
}
4654

55+
#[allow(rustc::potential_query_instability)]
56+
for (footnote, span) in footnote_definitions {
57+
if !footnote_references.contains(&footnote) {
58+
let span = source_span_for_markdown_range(
59+
tcx,
60+
dox,
61+
&(span..span + 1),
62+
&item.attrs.doc_strings,
63+
)
64+
.unwrap_or_else(|| item.attr_span(tcx));
65+
66+
tcx.node_span_lint(crate::lint::UNUSED_FOOTNOTE_DEFINITION, hir_id, span, |lint| {
67+
lint.primary_message("unused footnote definition");
68+
});
69+
}
70+
}
71+
4772
#[allow(rustc::potential_query_instability)]
4873
for span in missing_footnote_references {
4974
let (ref_span, precise) =

0 commit comments

Comments
 (0)