1212
1313use std:: collections:: { BTreeMap , BTreeSet } ;
1414
15+ use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
1516use rustc_hir:: HirId ;
1617use rustc_lint_defs:: Applicability ;
1718use 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