Skip to content

Commit 6e6e41c

Browse files
authored
Unrolled build for rust-lang#133000
Rollup merge of rust-lang#133000 - GuillaumeGomez:footnote-ids, r=notriddle [rustdoc] Fix duplicated footnote IDs Fixes rust-lang#131901. Footnote IDs were increased locally (ie, on the docblock) and not globally (ie, on the whole item page). cc `@aDotInTheVoid` r? `@notriddle`
2 parents 8adb4b3 + 7a8257d commit 6e6e41c

File tree

4 files changed

+92
-32
lines changed

4 files changed

+92
-32
lines changed

src/librustdoc/html/markdown.rs

+30-16
Original file line numberDiff line numberDiff line change
@@ -1333,12 +1333,14 @@ impl Markdown<'_> {
13331333

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

1336-
let p = HeadingLinks::new(p, None, ids, heading_offset);
1337-
let p = footnotes::Footnotes::new(p);
1338-
let p = LinkReplacer::new(p.map(|(ev, _)| ev), links);
1339-
let p = TableWrapper::new(p);
1340-
let p = CodeBlocks::new(p, codes, edition, playground);
1341-
html::push_html(&mut s, p);
1336+
ids.handle_footnotes(|ids, existing_footnotes| {
1337+
let p = HeadingLinks::new(p, None, ids, heading_offset);
1338+
let p = footnotes::Footnotes::new(p, existing_footnotes);
1339+
let p = LinkReplacer::new(p.map(|(ev, _)| ev), links);
1340+
let p = TableWrapper::new(p);
1341+
let p = CodeBlocks::new(p, codes, edition, playground);
1342+
html::push_html(&mut s, p);
1343+
});
13421344

13431345
s
13441346
}
@@ -1367,13 +1369,13 @@ impl MarkdownWithToc<'_> {
13671369

13681370
let mut toc = TocBuilder::new();
13691371

1370-
{
1372+
ids.handle_footnotes(|ids, existing_footnotes| {
13711373
let p = HeadingLinks::new(p, Some(&mut toc), ids, HeadingOffset::H1);
1372-
let p = footnotes::Footnotes::new(p);
1374+
let p = footnotes::Footnotes::new(p, existing_footnotes);
13731375
let p = TableWrapper::new(p.map(|(ev, _)| ev));
13741376
let p = CodeBlocks::new(p, codes, edition, playground);
13751377
html::push_html(&mut s, p);
1376-
}
1378+
});
13771379

13781380
(toc.into_toc(), s)
13791381
}
@@ -1401,13 +1403,15 @@ impl MarkdownItemInfo<'_> {
14011403

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

1404-
let p = HeadingLinks::new(p, None, ids, HeadingOffset::H1);
1405-
let p = footnotes::Footnotes::new(p);
1406-
let p = TableWrapper::new(p.map(|(ev, _)| ev));
1407-
let p = p.filter(|event| {
1408-
!matches!(event, Event::Start(Tag::Paragraph) | Event::End(TagEnd::Paragraph))
1406+
ids.handle_footnotes(|ids, existing_footnotes| {
1407+
let p = HeadingLinks::new(p, None, ids, HeadingOffset::H1);
1408+
let p = footnotes::Footnotes::new(p, existing_footnotes);
1409+
let p = TableWrapper::new(p.map(|(ev, _)| ev));
1410+
let p = p.filter(|event| {
1411+
!matches!(event, Event::Start(Tag::Paragraph) | Event::End(TagEnd::Paragraph))
1412+
});
1413+
html::push_html(&mut s, p);
14091414
});
1410-
html::push_html(&mut s, p);
14111415

14121416
s
14131417
}
@@ -1882,6 +1886,7 @@ pub(crate) fn rust_code_blocks(md: &str, extra_info: &ExtraInfo<'_>) -> Vec<Rust
18821886
#[derive(Clone, Default, Debug)]
18831887
pub struct IdMap {
18841888
map: FxHashMap<Cow<'static, str>, usize>,
1889+
existing_footnotes: usize,
18851890
}
18861891

18871892
// The map is pre-initialized and cloned each time to avoid reinitializing it repeatedly.
@@ -1943,7 +1948,7 @@ fn init_id_map() -> FxHashMap<Cow<'static, str>, usize> {
19431948

19441949
impl IdMap {
19451950
pub fn new() -> Self {
1946-
IdMap { map: DEFAULT_ID_MAP.get_or_init(init_id_map).clone() }
1951+
IdMap { map: DEFAULT_ID_MAP.get_or_init(init_id_map).clone(), existing_footnotes: 0 }
19471952
}
19481953

19491954
pub(crate) fn derive<S: AsRef<str> + ToString>(&mut self, candidate: S) -> String {
@@ -1959,4 +1964,13 @@ impl IdMap {
19591964
self.map.insert(id.clone().into(), 1);
19601965
id
19611966
}
1967+
1968+
/// Method to handle `existing_footnotes` increment automatically (to prevent forgetting
1969+
/// about it).
1970+
pub(crate) fn handle_footnotes<F: FnOnce(&mut Self, &mut usize)>(&mut self, closure: F) {
1971+
let mut existing_footnotes = self.existing_footnotes;
1972+
1973+
closure(self, &mut existing_footnotes);
1974+
self.existing_footnotes = existing_footnotes;
1975+
}
19621976
}

src/librustdoc/html/markdown/footnotes.rs

+19-14
Original file line numberDiff line numberDiff line change
@@ -8,36 +8,35 @@ use super::SpannedEvent;
88

99
/// Moves all footnote definitions to the end and add back links to the
1010
/// references.
11-
pub(super) struct Footnotes<'a, I> {
11+
pub(super) struct Footnotes<'a, 'b, I> {
1212
inner: I,
1313
footnotes: FxIndexMap<String, FootnoteDef<'a>>,
14+
existing_footnotes: &'b mut usize,
1415
}
1516

1617
/// The definition of a single footnote.
1718
struct FootnoteDef<'a> {
1819
content: Vec<Event<'a>>,
1920
/// The number that appears in the footnote reference and list.
20-
id: u16,
21+
id: usize,
2122
}
2223

23-
impl<'a, I> Footnotes<'a, I> {
24-
pub(super) fn new(iter: I) -> Self {
25-
Footnotes { inner: iter, footnotes: FxIndexMap::default() }
24+
impl<'a, 'b, I> Footnotes<'a, 'b, I> {
25+
pub(super) fn new(iter: I, existing_footnotes: &'b mut usize) -> Self {
26+
Footnotes { inner: iter, footnotes: FxIndexMap::default(), existing_footnotes }
2627
}
2728

28-
fn get_entry(&mut self, key: &str) -> (&mut Vec<Event<'a>>, u16) {
29-
let new_id = self.footnotes.len() + 1;
29+
fn get_entry(&mut self, key: &str) -> (&mut Vec<Event<'a>>, usize) {
30+
let new_id = self.footnotes.len() + 1 + *self.existing_footnotes;
3031
let key = key.to_owned();
31-
let FootnoteDef { content, id } = self
32-
.footnotes
33-
.entry(key)
34-
.or_insert(FootnoteDef { content: Vec::new(), id: new_id as u16 });
32+
let FootnoteDef { content, id } =
33+
self.footnotes.entry(key).or_insert(FootnoteDef { content: Vec::new(), id: new_id });
3534
// Don't allow changing the ID of existing entrys, but allow changing the contents.
3635
(content, *id)
3736
}
3837
}
3938

40-
impl<'a, I: Iterator<Item = SpannedEvent<'a>>> Iterator for Footnotes<'a, I> {
39+
impl<'a, 'b, I: Iterator<Item = SpannedEvent<'a>>> Iterator for Footnotes<'a, 'b, I> {
4140
type Item = SpannedEvent<'a>;
4241

4342
fn next(&mut self) -> Option<Self::Item> {
@@ -47,8 +46,13 @@ impl<'a, I: Iterator<Item = SpannedEvent<'a>>> Iterator for Footnotes<'a, I> {
4746
// When we see a reference (to a footnote we may not know) the definition of,
4847
// reserve a number for it, and emit a link to that number.
4948
let (_, id) = self.get_entry(reference);
50-
let reference =
51-
format!("<sup id=\"fnref{0}\"><a href=\"#fn{0}\">{0}</a></sup>", id);
49+
let reference = format!(
50+
"<sup id=\"fnref{0}\"><a href=\"#fn{0}\">{1}</a></sup>",
51+
id,
52+
// Although the ID count is for the whole page, the footnote reference
53+
// are local to the item so we make this ID "local" when displayed.
54+
id - *self.existing_footnotes
55+
);
5256
return Some((Event::Html(reference.into()), range));
5357
}
5458
Some((Event::Start(Tag::FootnoteDefinition(def)), _)) => {
@@ -64,6 +68,7 @@ impl<'a, I: Iterator<Item = SpannedEvent<'a>>> Iterator for Footnotes<'a, I> {
6468
// After all the markdown is emmited, emit an <hr> then all the footnotes
6569
// in a list.
6670
let defs: Vec<_> = self.footnotes.drain(..).map(|(_, x)| x).collect();
71+
*self.existing_footnotes += defs.len();
6772
let defs_html = render_footnotes_defs(defs);
6873
return Some((Event::Html(defs_html.into()), 0..0));
6974
} else {

src/librustdoc/html/render/context.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,9 @@ pub(crate) struct Context<'tcx> {
7676

7777
// `Context` is cloned a lot, so we don't want the size to grow unexpectedly.
7878
#[cfg(all(not(windows), target_pointer_width = "64"))]
79-
rustc_data_structures::static_assert_size!(Context<'_>, 184);
80-
#[cfg(all(windows, target_pointer_width = "64"))]
8179
rustc_data_structures::static_assert_size!(Context<'_>, 192);
80+
#[cfg(all(windows, target_pointer_width = "64"))]
81+
rustc_data_structures::static_assert_size!(Context<'_>, 200);
8282

8383
/// Shared mutable state used in [`Context`] and elsewhere.
8484
pub(crate) struct SharedContext<'tcx> {

tests/rustdoc/footnote-ids.rs

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// This test ensures that footnotes ID are not duplicated across an item page.
2+
// This is a regression test for <https://github.com/rust-lang/rust/issues/131901>.
3+
4+
#![crate_name = "foo"]
5+
6+
//@ has 'foo/struct.Foo.html'
7+
8+
pub struct Foo;
9+
10+
impl Foo {
11+
//@ has - '//a[@href="#fn1"]' '1'
12+
//@ has - '//li[@id="fn1"]' 'Hiya'
13+
//@ has - '//a[@href="#fn2"]' '2'
14+
//@ has - '//li[@id="fn2"]' 'Tiya'
15+
/// Link 1 [^1]
16+
/// Link 1.1 [^2]
17+
///
18+
/// [^1]: Hiya
19+
/// [^2]: Tiya
20+
pub fn l1(){}
21+
22+
//@ has - '//a[@href="#fn3"]' '1'
23+
//@ has - '//li[@id="fn3"]' 'Yiya'
24+
//@ has - '//a[@href="#fn4"]' '2'
25+
//@ has - '//li[@id="fn4"]' 'Biya'
26+
/// Link 2 [^1]
27+
/// Link 3 [^2]
28+
///
29+
/// [^1]: Yiya
30+
/// [^2]: Biya
31+
pub fn l2() {}
32+
}
33+
34+
impl Foo {
35+
//@ has - '//a[@href="#fn5"]' '1'
36+
//@ has - '//li[@id="fn5"]' 'Ciya'
37+
/// Link 3 [^1]
38+
///
39+
/// [^1]: Ciya
40+
pub fn l3(){}
41+
}

0 commit comments

Comments
 (0)