1212
1313use 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 } ;
1717use rustc_hir:: HirId ;
1818use rustc_lint_defs:: Applicability ;
1919use 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