Skip to content

Commit 181ccb1

Browse files
Rollup merge of rust-lang#157233 - nnethercote:rustdoc-impl-order, r=GuillaumeGomez
rustdoc: Fix trait impl ordering For types, rustdoc produces different impl orderings in the sidebar vs. the main section. E.g. for `std::cell::UnsafeCell` the "Trait Implementations" sidebar order is this: ``` !Freeze !RefUnwindSafe !Sync CoerceUnsized<UnsafeCell<U>> Debug Default DispatchFromDyn<UnsafeCell<U>> From<T> ``` The primary sort is on polarity, and the secondary sort is alphabetical. Makes sense. The main section order is this: ``` impl<T> Debug for UnsafeCell<T> impl<T> Default for UnsafeCell<T> impl<T> From<T> for UnsafeCell<T> impl<T, U> CoerceUnsized<UnsafeCell<U>> for UnsafeCell<T> impl<T, U> DispatchFromDyn<UnsafeCell<U>> for UnsafeCell<T> impl<T> !Freeze for UnsafeCell<T> impl<T> !RefUnwindSafe for UnsafeCell<T> impl<T> !Sync for UnsafeCell<T> ``` This strange ordering occurs because the entries are sorted on the generated HTML. Impls with methods (the first three) come first because they start with a `<details>` tag while the others start with a `<section>` tag. After that, the order depends on a section id of the form `impl-{trait}-for-{type}` that doesn't include the polarity, which is why the secondary ordering is alphabetical but ignores the `!`. The sidebar ordering is the better of the two. This commit changes the main section to use the same ordering as the sidebar. This involves creating a new function `impl_trait_key` shared between the two parts. r? @GuillaumeGomez
2 parents 6b4dd89 + eef3ae7 commit 181ccb1

6 files changed

Lines changed: 62 additions & 17 deletions

File tree

src/librustdoc/html/render/mod.rs

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ use crate::html::format::{
7979
use crate::html::markdown::{
8080
HeadingOffset, IdMap, Markdown, MarkdownItemInfo, MarkdownSummaryLine, short_markdown_summary,
8181
};
82+
use crate::html::render::print_item::compare_names;
8283
use crate::html::render::search_index::get_function_type_for_search;
8384
use crate::html::static_files::SCRAPE_EXAMPLES_HELP_MD;
8485
use crate::html::{highlight, sources};
@@ -941,6 +942,16 @@ fn short_item_info(
941942
extra_info
942943
}
943944

945+
// Prints the polarity and path of an impl's trait, if it has one, e.g. `Send`, `!Sync`.
946+
fn impl_trait_key(cx: &Context<'_>, i: &Impl) -> Option<String> {
947+
let trait_ = i.inner_impl().trait_.as_ref()?;
948+
let prefix = match i.inner_impl().polarity {
949+
ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => "",
950+
ty::ImplPolarity::Negative => "!",
951+
};
952+
Some(format!("{prefix}{:#}", print_path(trait_, cx)))
953+
}
954+
944955
// Render the list of items inside one of the sections "Trait Implementations",
945956
// "Auto Trait Implementations," "Blanket Trait Implementations" (on struct/enum pages).
946957
fn render_impls(
@@ -950,7 +961,9 @@ fn render_impls(
950961
containing_item: &clean::Item,
951962
toggle_open_by_default: bool,
952963
) -> fmt::Result {
953-
let mut rendered_impls = impls
964+
// Render each impl alongside its `impl_trait_key`, which is used as the primary sorting key
965+
// to match the impl order in the sidebar.
966+
let mut keyed_rendered_impls = impls
954967
.iter()
955968
.map(|i| {
956969
let did = i.trait_did().unwrap();
@@ -971,11 +984,16 @@ fn render_impls(
971984
toggle_open_by_default,
972985
},
973986
);
974-
imp.to_string()
987+
(impl_trait_key(cx, i).unwrap(), imp.to_string())
975988
})
976989
.collect::<Vec<_>>();
977-
rendered_impls.sort();
978-
w.write_str(&rendered_impls.join(""))
990+
991+
// Sort and then remove the `impl_trait_key`s, which are no longer needed after sorting.
992+
keyed_rendered_impls
993+
.sort_by(|(k1, h1), (k2, h2)| compare_names(k1, k2).then_with(|| h1.cmp(h2)));
994+
let joined: String = keyed_rendered_impls.into_iter().map(|a| a.1).collect();
995+
996+
w.write_str(&joined)
979997
}
980998

981999
/// Build a (possibly empty) `href` attribute (a key-value pair) for the given associated item.

src/librustdoc/html/render/sidebar.rs

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ use askama::Template;
66
use rustc_data_structures::fx::FxHashSet;
77
use rustc_hir::def::CtorKind;
88
use rustc_hir::def_id::{DefIdMap, DefIdSet};
9-
use rustc_middle::ty::{self, TyCtxt};
9+
use rustc_middle::ty::TyCtxt;
1010
use tracing::debug;
1111

12-
use super::{Context, ItemSection, item_ty_to_section};
12+
use super::{Context, ItemSection, impl_trait_key, item_ty_to_section};
1313
use crate::clean;
1414
use crate::formats::Impl;
1515
use crate::formats::item_type::ItemType;
@@ -707,15 +707,9 @@ fn sidebar_render_assoc_items(
707707

708708
let mut ret = impls
709709
.iter()
710-
.filter_map(|it| {
711-
let trait_ = it.inner_impl().trait_.as_ref()?;
712-
let encoded = id_map.derive(super::get_id_for_impl(cx.tcx(), it.impl_item.item_id));
713-
714-
let prefix = match it.inner_impl().polarity {
715-
ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => "",
716-
ty::ImplPolarity::Negative => "!",
717-
};
718-
let generated = Link::new(encoded, format!("{prefix}{:#}", print_path(trait_, cx)));
710+
.filter_map(|i| {
711+
let encoded = id_map.derive(super::get_id_for_impl(cx.tcx(), i.impl_item.item_id));
712+
let generated = Link::new(encoded, impl_trait_key(cx, i)?);
719713
if links.insert(generated.clone()) { Some(generated) } else { None }
720714
})
721715
.collect::<Vec<Link<'static>>>();

tests/rustdoc-gui/search-tab.goml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ set-window-size: (851, 600)
8181
// Check the size and count in tabs
8282
assert-text: ("#search-tabs > button:nth-child(1) > .count", " (25) ")
8383
assert-text: ("#search-tabs > button:nth-child(2) > .count", " (7)  ")
84-
assert-text: ("#search-tabs > button:nth-child(3) > .count", " (0)  ")
84+
assert-text: ("#search-tabs > button:nth-child(3) > .count", " (1)  ")
8585
store-property: ("#search-tabs > button:nth-child(1)", {"offsetWidth": buttonWidth})
8686
assert-property: ("#search-tabs > button:nth-child(2)", {"offsetWidth": |buttonWidth|})
8787
assert-property: ("#search-tabs > button:nth-child(3)", {"offsetWidth": |buttonWidth|})

tests/rustdoc-gui/sidebar-foreign-impl-sort.goml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Checks sidebar resizing close the Settings popover
1+
// Checks sidebar foreign impl ordering
22
go-to: "file://" + |DOC_PATH| + "/test_docs/SidebarSort/trait.Sort.html#foreign-impls"
33

44
// Check that the sidebar contains the expected foreign implementations

tests/rustdoc-gui/src/test_docs/lib.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#![feature(associated_type_defaults)]
1111
#![feature(macro_attr)]
1212
#![feature(macro_derive)]
13+
#![feature(negative_impls)]
1314

1415
/*!
1516
Enable the feature <span class="stab portability"><code>some-feature</code></span> to enjoy
@@ -89,6 +90,19 @@ impl AsRef<str> for Foo {
8990
}
9091
}
9192

93+
unsafe impl Send for Foo {}
94+
impl !Sync for Foo {}
95+
96+
impl From<u8> for Foo {
97+
fn from(value: u8) -> Self { todo!(); }
98+
}
99+
impl From<u16> for Foo {
100+
fn from(value: u16) -> Self { todo!(); }
101+
}
102+
impl From<u32> for Foo {
103+
fn from(value: u32) -> Self { todo!(); }
104+
}
105+
92106
/// <div id="doc-warning-0" class="warning">I have warnings!</div>
93107
pub struct WarningStruct;
94108

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Check that trait impls in the sidebar and the main section have the same
2+
// ordering.
3+
4+
go-to: "file://" + |DOC_PATH| + "/test_docs/struct.Foo.html"
5+
6+
// .sidebar .trait-implementation li:nth-child(3) a
7+
assert-text: (".sidebar-elems .trait-implementation li:nth-child(1) a", "!Sync")
8+
assert-text: (".sidebar-elems .trait-implementation li:nth-child(2) a", "AsRef<str>")
9+
assert-text: (".sidebar-elems .trait-implementation li:nth-child(3) a", "From<u8>")
10+
assert-text: (".sidebar-elems .trait-implementation li:nth-child(4) a", "From<u16>")
11+
assert-text: (".sidebar-elems .trait-implementation li:nth-child(5) a", "From<u32>")
12+
assert-text: (".sidebar-elems .trait-implementation li:nth-child(6) a", "Send")
13+
14+
assert-text: ("#trait-implementations-list section:nth-child(1) .code-header", "impl !Sync for Foo")
15+
assert-text: ("#trait-implementations-list details:nth-child(2) .code-header", "impl AsRef<str> for Foo")
16+
assert-text: ("#trait-implementations-list details:nth-child(3) .code-header", "impl From<u8> for Foo")
17+
assert-text: ("#trait-implementations-list details:nth-child(4) .code-header", "impl From<u16> for Foo")
18+
assert-text: ("#trait-implementations-list details:nth-child(5) .code-header", "impl From<u32> for Foo")
19+
assert-text: ("#trait-implementations-list section:nth-child(6) .code-header", "impl Send for Foo")

0 commit comments

Comments
 (0)