Skip to content

Commit 7494aef

Browse files
authored
Rollup merge of rust-lang#80381 - rust-lang:revert-80244-spans, r=GuillaumeGomez
Revert "Cleanup markdown span handling" Reverts rust-lang#80244. 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. cc `@bugadani,` thanks for catching this in rust-lang#77859. r? `@GuillaumeGomez`
2 parents 18ac1ec + 0f25712 commit 7494aef

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
@@ -245,7 +245,7 @@ struct DiagnosticInfo<'a> {
245245
item: &'a Item,
246246
dox: &'a str,
247247
ori_link: &'a str,
248-
link_range: Range<usize>,
248+
link_range: Option<Range<usize>>,
249249
}
250250

251251
#[derive(Clone, Debug, Hash)]
@@ -982,7 +982,7 @@ impl LinkCollector<'_, '_> {
982982
parent_node: Option<DefId>,
983983
krate: CrateNum,
984984
ori_link: String,
985-
link_range: Range<usize>,
985+
link_range: Option<Range<usize>>,
986986
) -> Option<ItemLink> {
987987
trace!("considering link '{}'", ori_link);
988988

@@ -1628,7 +1628,7 @@ fn report_diagnostic(
16281628
msg: &str,
16291629
item: &Item,
16301630
dox: &str,
1631-
link_range: &Range<usize>,
1631+
link_range: &Option<Range<usize>>,
16321632
decorate: impl FnOnce(&mut DiagnosticBuilder<'_>, Option<rustc_span::Span>),
16331633
) {
16341634
let hir_id = match cx.as_local_hir_id(item.def_id) {
@@ -1646,26 +1646,31 @@ fn report_diagnostic(
16461646
cx.tcx.struct_span_lint_hir(lint, hir_id, sp, |lint| {
16471647
let mut diag = lint.build(msg);
16481648

1649-
let span = super::source_span_for_markdown_range(cx, dox, link_range, attrs);
1650-
if let Some(sp) = span {
1651-
diag.set_span(sp);
1652-
} else {
1653-
// blah blah blah\nblah\nblah [blah] blah blah\nblah blah
1654-
// ^ ~~~~
1655-
// | link_range
1656-
// last_new_line_offset
1657-
let last_new_line_offset = dox[..link_range.start].rfind('\n').map_or(0, |n| n + 1);
1658-
let line = dox[last_new_line_offset..].lines().next().unwrap_or("");
1659-
1660-
// Print the line containing the `link_range` and manually mark it with '^'s.
1661-
diag.note(&format!(
1662-
"the link appears in this line:\n\n{line}\n\
1663-
{indicator: <before$}{indicator:^<found$}",
1664-
line = line,
1665-
indicator = "",
1666-
before = link_range.start - last_new_line_offset,
1667-
found = link_range.len(),
1668-
));
1649+
let span = link_range
1650+
.as_ref()
1651+
.and_then(|range| super::source_span_for_markdown_range(cx, dox, range, attrs));
1652+
1653+
if let Some(link_range) = link_range {
1654+
if let Some(sp) = span {
1655+
diag.set_span(sp);
1656+
} else {
1657+
// blah blah blah\nblah\nblah [blah] blah blah\nblah blah
1658+
// ^ ~~~~
1659+
// | link_range
1660+
// last_new_line_offset
1661+
let last_new_line_offset = dox[..link_range.start].rfind('\n').map_or(0, |n| n + 1);
1662+
let line = dox[last_new_line_offset..].lines().next().unwrap_or("");
1663+
1664+
// Print the line containing the `link_range` and manually mark it with '^'s.
1665+
diag.note(&format!(
1666+
"the link appears in this line:\n\n{line}\n\
1667+
{indicator: <before$}{indicator:^<found$}",
1668+
line = line,
1669+
indicator = "",
1670+
before = link_range.start - last_new_line_offset,
1671+
found = link_range.len(),
1672+
));
1673+
}
16691674
}
16701675

16711676
decorate(&mut diag, span);
@@ -1685,7 +1690,7 @@ fn resolution_failure(
16851690
path_str: &str,
16861691
disambiguator: Option<Disambiguator>,
16871692
dox: &str,
1688-
link_range: Range<usize>,
1693+
link_range: Option<Range<usize>>,
16891694
kinds: SmallVec<[ResolutionFailure<'_>; 3]>,
16901695
) {
16911696
let tcx = collector.cx.tcx;
@@ -1909,7 +1914,7 @@ fn anchor_failure(
19091914
item: &Item,
19101915
path_str: &str,
19111916
dox: &str,
1912-
link_range: Range<usize>,
1917+
link_range: Option<Range<usize>>,
19131918
failure: AnchorFailure,
19141919
) {
19151920
let msg = match failure {
@@ -1934,7 +1939,7 @@ fn ambiguity_error(
19341939
item: &Item,
19351940
path_str: &str,
19361941
dox: &str,
1937-
link_range: Range<usize>,
1942+
link_range: Option<Range<usize>>,
19381943
candidates: Vec<Res>,
19391944
) {
19401945
let mut msg = format!("`{}` is ", path_str);
@@ -1983,12 +1988,13 @@ fn suggest_disambiguator(
19831988
path_str: &str,
19841989
dox: &str,
19851990
sp: Option<rustc_span::Span>,
1986-
link_range: &Range<usize>,
1991+
link_range: &Option<Range<usize>>,
19871992
) {
19881993
let suggestion = disambiguator.suggestion();
19891994
let help = format!("to link to the {}, {}", disambiguator.descr(), suggestion.descr());
19901995

19911996
if let Some(sp) = sp {
1997+
let link_range = link_range.as_ref().expect("must have a link range if we have a span");
19921998
let msg = if dox.bytes().nth(link_range.start) == Some(b'`') {
19931999
format!("`{}`", suggestion.as_help(path_str))
19942000
} else {
@@ -2007,7 +2013,7 @@ fn privacy_error(
20072013
item: &Item,
20082014
path_str: &str,
20092015
dox: &str,
2010-
link_range: Range<usize>,
2016+
link_range: Option<Range<usize>>,
20112017
) {
20122018
let sym;
20132019
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)