Skip to content

Commit 0f25712

Browse files
Joshua Nelsonjyn514
Joshua Nelson
authored andcommitted
Revert "Cleanup markdown span handling"
This caused a diagnostic regression, originally it was: ``` warning: unresolved link to `std::process::Comman` --> link.rs:3:10 | 3 | //! [a]: std::process::Comman | ^^^^^^^^^^^^^^^^^^^^ no item named `Comman` in module `process` | = note: `#[warn(broken_intra_doc_links)]` on by default ``` but after that PR rustdoc now displays ``` warning: unresolved link to `std::process::Comman` --> link.rs:1:14 | 1 | //! Links to [a] [link][a] | ^^^ no item named `Comman` in module `process` | = note: `#[warn(broken_intra_doc_links)]` on by default ``` which IMO is much less clear.
1 parent 1f5beec commit 0f25712

File tree

4 files changed

+126
-89
lines changed

4 files changed

+126
-89
lines changed

src/librustdoc/html/markdown.rs

+65-61
Original file line numberDiff line numberDiff line change
@@ -418,7 +418,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for LinkReplacer<'a, I> {
418418
struct HeadingLinks<'a, 'b, 'ids, I> {
419419
inner: I,
420420
toc: Option<&'b mut TocBuilder>,
421-
buf: VecDeque<(Event<'a>, Range<usize>)>,
421+
buf: VecDeque<Event<'a>>,
422422
id_map: &'ids mut IdMap,
423423
}
424424

@@ -428,48 +428,48 @@ impl<'a, 'b, 'ids, I> HeadingLinks<'a, 'b, 'ids, I> {
428428
}
429429
}
430430

431-
impl<'a, 'b, 'ids, I: Iterator<Item = (Event<'a>, Range<usize>)>> Iterator
432-
for HeadingLinks<'a, 'b, 'ids, I>
433-
{
434-
type Item = (Event<'a>, Range<usize>);
431+
impl<'a, 'b, 'ids, I: Iterator<Item = Event<'a>>> Iterator for HeadingLinks<'a, 'b, 'ids, I> {
432+
type Item = Event<'a>;
435433

436434
fn next(&mut self) -> Option<Self::Item> {
437435
if let Some(e) = self.buf.pop_front() {
438436
return Some(e);
439437
}
440438

441439
let event = self.inner.next();
442-
if let Some((Event::Start(Tag::Heading(level)), _)) = event {
440+
if let Some(Event::Start(Tag::Heading(level))) = event {
443441
let mut id = String::new();
444442
for event in &mut self.inner {
445-
match &event.0 {
443+
match &event {
446444
Event::End(Tag::Heading(..)) => break,
447-
Event::Start(Tag::Link(_, _, _)) | Event::End(Tag::Link(..)) => {}
448445
Event::Text(text) | Event::Code(text) => {
449446
id.extend(text.chars().filter_map(slugify));
450-
self.buf.push_back(event);
451447
}
452-
_ => self.buf.push_back(event),
448+
_ => {}
449+
}
450+
match event {
451+
Event::Start(Tag::Link(_, _, _)) | Event::End(Tag::Link(..)) => {}
452+
event => self.buf.push_back(event),
453453
}
454454
}
455455
let id = self.id_map.derive(id);
456456

457457
if let Some(ref mut builder) = self.toc {
458458
let mut html_header = String::new();
459-
html::push_html(&mut html_header, self.buf.iter().map(|(ev, _)| ev.clone()));
459+
html::push_html(&mut html_header, self.buf.iter().cloned());
460460
let sec = builder.push(level as u32, html_header, id.clone());
461-
self.buf.push_front((Event::Html(format!("{} ", sec).into()), 0..0));
461+
self.buf.push_front(Event::Html(format!("{} ", sec).into()));
462462
}
463463

464-
self.buf.push_back((Event::Html(format!("</a></h{}>", level).into()), 0..0));
464+
self.buf.push_back(Event::Html(format!("</a></h{}>", level).into()));
465465

466466
let start_tags = format!(
467467
"<h{level} id=\"{id}\" class=\"section-header\">\
468468
<a href=\"#{id}\">",
469469
id = id,
470470
level = level
471471
);
472-
return Some((Event::Html(start_tags.into()), 0..0));
472+
return Some(Event::Html(start_tags.into()));
473473
}
474474
event
475475
}
@@ -560,23 +560,23 @@ impl<'a, I> Footnotes<'a, I> {
560560
}
561561
}
562562

563-
impl<'a, I: Iterator<Item = (Event<'a>, Range<usize>)>> Iterator for Footnotes<'a, I> {
564-
type Item = (Event<'a>, Range<usize>);
563+
impl<'a, I: Iterator<Item = Event<'a>>> Iterator for Footnotes<'a, I> {
564+
type Item = Event<'a>;
565565

566566
fn next(&mut self) -> Option<Self::Item> {
567567
loop {
568568
match self.inner.next() {
569-
Some((Event::FootnoteReference(ref reference), range)) => {
569+
Some(Event::FootnoteReference(ref reference)) => {
570570
let entry = self.get_entry(&reference);
571571
let reference = format!(
572572
"<sup id=\"fnref{0}\"><a href=\"#fn{0}\">{0}</a></sup>",
573573
(*entry).1
574574
);
575-
return Some((Event::Html(reference.into()), range));
575+
return Some(Event::Html(reference.into()));
576576
}
577-
Some((Event::Start(Tag::FootnoteDefinition(def)), _)) => {
577+
Some(Event::Start(Tag::FootnoteDefinition(def))) => {
578578
let mut content = Vec::new();
579-
for (event, _) in &mut self.inner {
579+
for event in &mut self.inner {
580580
if let Event::End(Tag::FootnoteDefinition(..)) = event {
581581
break;
582582
}
@@ -607,7 +607,7 @@ impl<'a, I: Iterator<Item = (Event<'a>, Range<usize>)>> Iterator for Footnotes<'
607607
ret.push_str("</li>");
608608
}
609609
ret.push_str("</ol></div>");
610-
return Some((Event::Html(ret.into()), 0..0));
610+
return Some(Event::Html(ret.into()));
611611
} else {
612612
return None;
613613
}
@@ -917,14 +917,13 @@ impl Markdown<'_> {
917917
};
918918

919919
let p = Parser::new_with_broken_link_callback(md, opts(), Some(&mut replacer));
920-
let p = p.into_offset_iter();
921920

922921
let mut s = String::with_capacity(md.len() * 3 / 2);
923922

924923
let p = HeadingLinks::new(p, None, &mut ids);
925-
let p = Footnotes::new(p);
926-
let p = LinkReplacer::new(p.map(|(ev, _)| ev), links);
924+
let p = LinkReplacer::new(p, links);
927925
let p = CodeBlocks::new(p, codes, edition, playground);
926+
let p = Footnotes::new(p);
928927
html::push_html(&mut s, p);
929928

930929
s
@@ -935,16 +934,16 @@ impl MarkdownWithToc<'_> {
935934
crate fn into_string(self) -> String {
936935
let MarkdownWithToc(md, mut ids, codes, edition, playground) = self;
937936

938-
let p = Parser::new_ext(md, opts()).into_offset_iter();
937+
let p = Parser::new_ext(md, opts());
939938

940939
let mut s = String::with_capacity(md.len() * 3 / 2);
941940

942941
let mut toc = TocBuilder::new();
943942

944943
{
945944
let p = HeadingLinks::new(p, Some(&mut toc), &mut ids);
945+
let p = CodeBlocks::new(p, codes, edition, playground);
946946
let p = Footnotes::new(p);
947-
let p = CodeBlocks::new(p.map(|(ev, _)| ev), codes, edition, playground);
948947
html::push_html(&mut s, p);
949948
}
950949

@@ -960,19 +959,19 @@ impl MarkdownHtml<'_> {
960959
if md.is_empty() {
961960
return String::new();
962961
}
963-
let p = Parser::new_ext(md, opts()).into_offset_iter();
962+
let p = Parser::new_ext(md, opts());
964963

965964
// Treat inline HTML as plain text.
966-
let p = p.map(|event| match event.0 {
967-
Event::Html(text) => (Event::Text(text), event.1),
965+
let p = p.map(|event| match event {
966+
Event::Html(text) => Event::Text(text),
968967
_ => event,
969968
});
970969

971970
let mut s = String::with_capacity(md.len() * 3 / 2);
972971

973972
let p = HeadingLinks::new(p, None, &mut ids);
973+
let p = CodeBlocks::new(p, codes, edition, playground);
974974
let p = Footnotes::new(p);
975-
let p = CodeBlocks::new(p.map(|(ev, _)| ev), codes, edition, playground);
976975
html::push_html(&mut s, p);
977976

978977
s
@@ -1125,45 +1124,50 @@ crate fn plain_text_summary(md: &str) -> String {
11251124
s
11261125
}
11271126

1128-
crate fn markdown_links(md: &str) -> Vec<(String, Range<usize>)> {
1127+
crate fn markdown_links(md: &str) -> Vec<(String, Option<Range<usize>>)> {
11291128
if md.is_empty() {
11301129
return vec![];
11311130
}
11321131

11331132
let mut links = vec![];
1134-
// Used to avoid mutable borrow issues in the `push` closure
1135-
// Probably it would be more efficient to use a `RefCell` but it doesn't seem worth the churn.
11361133
let mut shortcut_links = vec![];
11371134

1138-
let span_for_link = |link: &str, span: Range<usize>| {
1139-
// Pulldown includes the `[]` as well as the URL. Only highlight the relevant span.
1140-
// NOTE: uses `rfind` in case the title and url are the same: `[Ok][Ok]`
1141-
match md[span.clone()].rfind(link) {
1142-
Some(start) => {
1143-
let start = span.start + start;
1144-
start..start + link.len()
1135+
{
1136+
let locate = |s: &str| unsafe {
1137+
let s_start = s.as_ptr();
1138+
let s_end = s_start.add(s.len());
1139+
let md_start = md.as_ptr();
1140+
let md_end = md_start.add(md.len());
1141+
if md_start <= s_start && s_end <= md_end {
1142+
let start = s_start.offset_from(md_start) as usize;
1143+
let end = s_end.offset_from(md_start) as usize;
1144+
Some(start..end)
1145+
} else {
1146+
None
1147+
}
1148+
};
1149+
1150+
let mut push = |link: BrokenLink<'_>| {
1151+
// FIXME: use `link.span` instead of `locate`
1152+
// (doing it now includes the `[]` as well as the text)
1153+
shortcut_links.push((link.reference.to_owned(), locate(link.reference)));
1154+
None
1155+
};
1156+
let p = Parser::new_with_broken_link_callback(md, opts(), Some(&mut push));
1157+
1158+
// There's no need to thread an IdMap through to here because
1159+
// the IDs generated aren't going to be emitted anywhere.
1160+
let mut ids = IdMap::new();
1161+
let iter = Footnotes::new(HeadingLinks::new(p, None, &mut ids));
1162+
1163+
for ev in iter {
1164+
if let Event::Start(Tag::Link(_, dest, _)) = ev {
1165+
debug!("found link: {}", dest);
1166+
links.push(match dest {
1167+
CowStr::Borrowed(s) => (s.to_owned(), locate(s)),
1168+
s @ (CowStr::Boxed(..) | CowStr::Inlined(..)) => (s.into_string(), None),
1169+
});
11451170
}
1146-
// This can happen for things other than intra-doc links, like `#1` expanded to `https://github.com/rust-lang/rust/issues/1`.
1147-
None => span,
1148-
}
1149-
};
1150-
let mut push = |link: BrokenLink<'_>| {
1151-
let span = span_for_link(link.reference, link.span);
1152-
shortcut_links.push((link.reference.to_owned(), span));
1153-
None
1154-
};
1155-
let p = Parser::new_with_broken_link_callback(md, opts(), Some(&mut push));
1156-
1157-
// There's no need to thread an IdMap through to here because
1158-
// the IDs generated aren't going to be emitted anywhere.
1159-
let mut ids = IdMap::new();
1160-
let iter = Footnotes::new(HeadingLinks::new(p.into_offset_iter(), None, &mut ids));
1161-
1162-
for ev in iter {
1163-
if let Event::Start(Tag::Link(_, dest, _)) = ev.0 {
1164-
debug!("found link: {}", dest);
1165-
let span = span_for_link(&dest, ev.1);
1166-
links.push((dest.into_string(), span));
11671171
}
11681172
}
11691173

src/librustdoc/passes/collect_intra_doc_links.rs

+34-28
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ struct DiagnosticInfo<'a> {
180180
item: &'a Item,
181181
dox: &'a str,
182182
ori_link: &'a str,
183-
link_range: Range<usize>,
183+
link_range: Option<Range<usize>>,
184184
}
185185

186186
#[derive(Clone, Debug, Hash)]
@@ -920,7 +920,7 @@ impl LinkCollector<'_, '_> {
920920
parent_node: Option<DefId>,
921921
krate: CrateNum,
922922
ori_link: String,
923-
link_range: Range<usize>,
923+
link_range: Option<Range<usize>>,
924924
) -> Option<ItemLink> {
925925
trace!("considering link '{}'", ori_link);
926926

@@ -1566,7 +1566,7 @@ fn report_diagnostic(
15661566
msg: &str,
15671567
item: &Item,
15681568
dox: &str,
1569-
link_range: &Range<usize>,
1569+
link_range: &Option<Range<usize>>,
15701570
decorate: impl FnOnce(&mut DiagnosticBuilder<'_>, Option<rustc_span::Span>),
15711571
) {
15721572
let hir_id = match cx.as_local_hir_id(item.def_id) {
@@ -1584,26 +1584,31 @@ fn report_diagnostic(
15841584
cx.tcx.struct_span_lint_hir(lint, hir_id, sp, |lint| {
15851585
let mut diag = lint.build(msg);
15861586

1587-
let span = super::source_span_for_markdown_range(cx, dox, link_range, attrs);
1588-
if let Some(sp) = span {
1589-
diag.set_span(sp);
1590-
} else {
1591-
// blah blah blah\nblah\nblah [blah] blah blah\nblah blah
1592-
// ^ ~~~~
1593-
// | link_range
1594-
// last_new_line_offset
1595-
let last_new_line_offset = dox[..link_range.start].rfind('\n').map_or(0, |n| n + 1);
1596-
let line = dox[last_new_line_offset..].lines().next().unwrap_or("");
1597-
1598-
// Print the line containing the `link_range` and manually mark it with '^'s.
1599-
diag.note(&format!(
1600-
"the link appears in this line:\n\n{line}\n\
1601-
{indicator: <before$}{indicator:^<found$}",
1602-
line = line,
1603-
indicator = "",
1604-
before = link_range.start - last_new_line_offset,
1605-
found = link_range.len(),
1606-
));
1587+
let span = link_range
1588+
.as_ref()
1589+
.and_then(|range| super::source_span_for_markdown_range(cx, dox, range, attrs));
1590+
1591+
if let Some(link_range) = link_range {
1592+
if let Some(sp) = span {
1593+
diag.set_span(sp);
1594+
} else {
1595+
// blah blah blah\nblah\nblah [blah] blah blah\nblah blah
1596+
// ^ ~~~~
1597+
// | link_range
1598+
// last_new_line_offset
1599+
let last_new_line_offset = dox[..link_range.start].rfind('\n').map_or(0, |n| n + 1);
1600+
let line = dox[last_new_line_offset..].lines().next().unwrap_or("");
1601+
1602+
// Print the line containing the `link_range` and manually mark it with '^'s.
1603+
diag.note(&format!(
1604+
"the link appears in this line:\n\n{line}\n\
1605+
{indicator: <before$}{indicator:^<found$}",
1606+
line = line,
1607+
indicator = "",
1608+
before = link_range.start - last_new_line_offset,
1609+
found = link_range.len(),
1610+
));
1611+
}
16071612
}
16081613

16091614
decorate(&mut diag, span);
@@ -1623,7 +1628,7 @@ fn resolution_failure(
16231628
path_str: &str,
16241629
disambiguator: Option<Disambiguator>,
16251630
dox: &str,
1626-
link_range: Range<usize>,
1631+
link_range: Option<Range<usize>>,
16271632
kinds: SmallVec<[ResolutionFailure<'_>; 3]>,
16281633
) {
16291634
report_diagnostic(
@@ -1857,7 +1862,7 @@ fn anchor_failure(
18571862
item: &Item,
18581863
path_str: &str,
18591864
dox: &str,
1860-
link_range: Range<usize>,
1865+
link_range: Option<Range<usize>>,
18611866
failure: AnchorFailure,
18621867
) {
18631868
let msg = match failure {
@@ -1882,7 +1887,7 @@ fn ambiguity_error(
18821887
item: &Item,
18831888
path_str: &str,
18841889
dox: &str,
1885-
link_range: Range<usize>,
1890+
link_range: Option<Range<usize>>,
18861891
candidates: Vec<Res>,
18871892
) {
18881893
let mut msg = format!("`{}` is ", path_str);
@@ -1931,12 +1936,13 @@ fn suggest_disambiguator(
19311936
path_str: &str,
19321937
dox: &str,
19331938
sp: Option<rustc_span::Span>,
1934-
link_range: &Range<usize>,
1939+
link_range: &Option<Range<usize>>,
19351940
) {
19361941
let suggestion = disambiguator.suggestion();
19371942
let help = format!("to link to the {}, {}", disambiguator.descr(), suggestion.descr());
19381943

19391944
if let Some(sp) = sp {
1945+
let link_range = link_range.as_ref().expect("must have a link range if we have a span");
19401946
let msg = if dox.bytes().nth(link_range.start) == Some(b'`') {
19411947
format!("`{}`", suggestion.as_help(path_str))
19421948
} else {
@@ -1955,7 +1961,7 @@ fn privacy_error(
19551961
item: &Item,
19561962
path_str: &str,
19571963
dox: &str,
1958-
link_range: Range<usize>,
1964+
link_range: Option<Range<usize>>,
19591965
) {
19601966
let sym;
19611967
let item_name = match item.name {
+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// Test that errors point to the reference, not to the title text.
2+
#![deny(broken_intra_doc_links)]
3+
//! Links to [a] [link][a]
4+
//!
5+
//! [a]: std::process::Comman
6+
//~^ ERROR unresolved
7+
//~| ERROR unresolved

0 commit comments

Comments
 (0)