|
| 1 | +//! Detects specific markdown syntax that's different between pulldown-cmark |
| 2 | +//! 0.9 and 0.11. |
| 3 | +//! |
| 4 | +//! This is a mitigation for old parser bugs that affected some |
| 5 | +//! real crates' docs. The old parser claimed to comply with CommonMark, |
| 6 | +//! but it did not. These warnings will eventually be removed, |
| 7 | +//! though some of them may become Clippy lints. |
| 8 | +//! |
| 9 | +//! <https://github.com/rust-lang/rust/pull/121659#issuecomment-1992752820> |
| 10 | +//! |
| 11 | +//! <https://rustc-dev-guide.rust-lang.org/bug-fix-procedure.html#add-the-lint-to-the-list-of-removed-lists> |
| 12 | +
|
| 13 | +use std::ops::Range; |
| 14 | + |
| 15 | +use pulldown_cmark::{Event, Options, Parser}; |
| 16 | +use rustc_data_structures::fx::FxHashSet; |
| 17 | +use rustc_hir::HirId; |
| 18 | +use rustc_lint_defs::Applicability; |
| 19 | +use rustc_resolve::rustdoc::source_span_for_markdown_range; |
| 20 | + |
| 21 | +use crate::clean::Item; |
| 22 | +use crate::core::DocContext; |
| 23 | + |
| 24 | +pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item, hir_id: HirId, dox: &str) { |
| 25 | + let tcx = cx.tcx; |
| 26 | + |
| 27 | + let mut missing_footnote_references = FxHashSet::default(); |
| 28 | + |
| 29 | + let options = Options::ENABLE_FOOTNOTES; |
| 30 | + let mut parser = Parser::new_ext(dox, options).into_offset_iter().peekable(); |
| 31 | + while let Some((event, span)) = parser.next() { |
| 32 | + match event { |
| 33 | + Event::Text(text) |
| 34 | + if &*text == "[" |
| 35 | + && let Some((Event::Text(text), _)) = parser.peek() |
| 36 | + && text.trim_start().starts_with('^') |
| 37 | + && parser.next().is_some() |
| 38 | + && let Some((Event::Text(text), end_span)) = parser.peek() |
| 39 | + && &**text == "]" => |
| 40 | + { |
| 41 | + missing_footnote_references.insert(Range { start: span.start, end: end_span.end }); |
| 42 | + } |
| 43 | + _ => {} |
| 44 | + } |
| 45 | + } |
| 46 | + |
| 47 | + #[allow(rustc::potential_query_instability)] |
| 48 | + for span in missing_footnote_references { |
| 49 | + let (ref_span, precise) = |
| 50 | + source_span_for_markdown_range(tcx, dox, &span, &item.attrs.doc_strings) |
| 51 | + .map(|span| (span, true)) |
| 52 | + .unwrap_or_else(|| (item.attr_span(tcx), false)); |
| 53 | + |
| 54 | + if precise { |
| 55 | + tcx.node_span_lint(crate::lint::BROKEN_FOOTNOTE, hir_id, ref_span, |lint| { |
| 56 | + lint.primary_message("no footnote definition matching this footnote"); |
| 57 | + lint.span_suggestion( |
| 58 | + ref_span.shrink_to_lo(), |
| 59 | + "if it should not be a footnote, escape it", |
| 60 | + "\\", |
| 61 | + Applicability::MaybeIncorrect, |
| 62 | + ); |
| 63 | + }); |
| 64 | + } |
| 65 | + } |
| 66 | +} |
0 commit comments