diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 8108316a856ba..00a20fdca6e31 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -79,6 +79,7 @@ use crate::html::format::{ use crate::html::markdown::{ HeadingOffset, IdMap, Markdown, MarkdownItemInfo, MarkdownSummaryLine, short_markdown_summary, }; +use crate::html::render::print_item::compare_names; use crate::html::render::search_index::get_function_type_for_search; use crate::html::static_files::SCRAPE_EXAMPLES_HELP_MD; use crate::html::{highlight, sources}; @@ -941,6 +942,16 @@ fn short_item_info( extra_info } +// Prints the polarity and path of an impl's trait, if it has one, e.g. `Send`, `!Sync`. +fn impl_trait_key(cx: &Context<'_>, i: &Impl) -> Option { + let trait_ = i.inner_impl().trait_.as_ref()?; + let prefix = match i.inner_impl().polarity { + ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => "", + ty::ImplPolarity::Negative => "!", + }; + Some(format!("{prefix}{:#}", print_path(trait_, cx))) +} + // Render the list of items inside one of the sections "Trait Implementations", // "Auto Trait Implementations," "Blanket Trait Implementations" (on struct/enum pages). fn render_impls( @@ -950,7 +961,9 @@ fn render_impls( containing_item: &clean::Item, toggle_open_by_default: bool, ) -> fmt::Result { - let mut rendered_impls = impls + // Render each impl alongside its `impl_trait_key`, which is used as the primary sorting key + // to match the impl order in the sidebar. + let mut keyed_rendered_impls = impls .iter() .map(|i| { let did = i.trait_did().unwrap(); @@ -971,11 +984,16 @@ fn render_impls( toggle_open_by_default, }, ); - imp.to_string() + (impl_trait_key(cx, i).unwrap(), imp.to_string()) }) .collect::>(); - rendered_impls.sort(); - w.write_str(&rendered_impls.join("")) + + // Sort and then remove the `impl_trait_key`s, which are no longer needed after sorting. + keyed_rendered_impls + .sort_by(|(k1, h1), (k2, h2)| compare_names(k1, k2).then_with(|| h1.cmp(h2))); + let joined: String = keyed_rendered_impls.into_iter().map(|a| a.1).collect(); + + w.write_str(&joined) } /// Build a (possibly empty) `href` attribute (a key-value pair) for the given associated item. diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs index 44c8013664cae..beddac0f2613f 100644 --- a/src/librustdoc/html/render/sidebar.rs +++ b/src/librustdoc/html/render/sidebar.rs @@ -6,10 +6,10 @@ use askama::Template; use rustc_data_structures::fx::FxHashSet; use rustc_hir::def::CtorKind; use rustc_hir::def_id::{DefIdMap, DefIdSet}; -use rustc_middle::ty::{self, TyCtxt}; +use rustc_middle::ty::TyCtxt; use tracing::debug; -use super::{Context, ItemSection, item_ty_to_section}; +use super::{Context, ItemSection, impl_trait_key, item_ty_to_section}; use crate::clean; use crate::formats::Impl; use crate::formats::item_type::ItemType; @@ -707,15 +707,9 @@ fn sidebar_render_assoc_items( let mut ret = impls .iter() - .filter_map(|it| { - let trait_ = it.inner_impl().trait_.as_ref()?; - let encoded = id_map.derive(super::get_id_for_impl(cx.tcx(), it.impl_item.item_id)); - - let prefix = match it.inner_impl().polarity { - ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => "", - ty::ImplPolarity::Negative => "!", - }; - let generated = Link::new(encoded, format!("{prefix}{:#}", print_path(trait_, cx))); + .filter_map(|i| { + let encoded = id_map.derive(super::get_id_for_impl(cx.tcx(), i.impl_item.item_id)); + let generated = Link::new(encoded, impl_trait_key(cx, i)?); if links.insert(generated.clone()) { Some(generated) } else { None } }) .collect::>>(); diff --git a/tests/rustdoc-gui/search-tab.goml b/tests/rustdoc-gui/search-tab.goml index 0a3cfc231e50a..34daf498b5b69 100644 --- a/tests/rustdoc-gui/search-tab.goml +++ b/tests/rustdoc-gui/search-tab.goml @@ -81,7 +81,7 @@ set-window-size: (851, 600) // Check the size and count in tabs assert-text: ("#search-tabs > button:nth-child(1) > .count", " (25) ") assert-text: ("#search-tabs > button:nth-child(2) > .count", " (7)  ") -assert-text: ("#search-tabs > button:nth-child(3) > .count", " (0)  ") +assert-text: ("#search-tabs > button:nth-child(3) > .count", " (1)  ") store-property: ("#search-tabs > button:nth-child(1)", {"offsetWidth": buttonWidth}) assert-property: ("#search-tabs > button:nth-child(2)", {"offsetWidth": |buttonWidth|}) assert-property: ("#search-tabs > button:nth-child(3)", {"offsetWidth": |buttonWidth|}) diff --git a/tests/rustdoc-gui/sidebar-foreign-impl-sort.goml b/tests/rustdoc-gui/sidebar-foreign-impl-sort.goml index f09f09713514d..31b35f5152327 100644 --- a/tests/rustdoc-gui/sidebar-foreign-impl-sort.goml +++ b/tests/rustdoc-gui/sidebar-foreign-impl-sort.goml @@ -1,4 +1,4 @@ -// Checks sidebar resizing close the Settings popover +// Checks sidebar foreign impl ordering go-to: "file://" + |DOC_PATH| + "/test_docs/SidebarSort/trait.Sort.html#foreign-impls" // Check that the sidebar contains the expected foreign implementations diff --git a/tests/rustdoc-gui/src/test_docs/lib.rs b/tests/rustdoc-gui/src/test_docs/lib.rs index f219291970450..49ac8bd31a49e 100644 --- a/tests/rustdoc-gui/src/test_docs/lib.rs +++ b/tests/rustdoc-gui/src/test_docs/lib.rs @@ -10,6 +10,7 @@ #![feature(associated_type_defaults)] #![feature(macro_attr)] #![feature(macro_derive)] +#![feature(negative_impls)] /*! Enable the feature some-feature to enjoy @@ -89,6 +90,19 @@ impl AsRef for Foo { } } +unsafe impl Send for Foo {} +impl !Sync for Foo {} + +impl From for Foo { + fn from(value: u8) -> Self { todo!(); } +} +impl From for Foo { + fn from(value: u16) -> Self { todo!(); } +} +impl From for Foo { + fn from(value: u32) -> Self { todo!(); } +} + ///
I have warnings!
pub struct WarningStruct; diff --git a/tests/rustdoc-gui/trait-impl-sort.goml b/tests/rustdoc-gui/trait-impl-sort.goml new file mode 100644 index 0000000000000..2d67bdf5bd58f --- /dev/null +++ b/tests/rustdoc-gui/trait-impl-sort.goml @@ -0,0 +1,19 @@ +// Check that trait impls in the sidebar and the main section have the same +// ordering. + +go-to: "file://" + |DOC_PATH| + "/test_docs/struct.Foo.html" + +// .sidebar .trait-implementation li:nth-child(3) a +assert-text: (".sidebar-elems .trait-implementation li:nth-child(1) a", "!Sync") +assert-text: (".sidebar-elems .trait-implementation li:nth-child(2) a", "AsRef") +assert-text: (".sidebar-elems .trait-implementation li:nth-child(3) a", "From") +assert-text: (".sidebar-elems .trait-implementation li:nth-child(4) a", "From") +assert-text: (".sidebar-elems .trait-implementation li:nth-child(5) a", "From") +assert-text: (".sidebar-elems .trait-implementation li:nth-child(6) a", "Send") + +assert-text: ("#trait-implementations-list section:nth-child(1) .code-header", "impl !Sync for Foo") +assert-text: ("#trait-implementations-list details:nth-child(2) .code-header", "impl AsRef for Foo") +assert-text: ("#trait-implementations-list details:nth-child(3) .code-header", "impl From for Foo") +assert-text: ("#trait-implementations-list details:nth-child(4) .code-header", "impl From for Foo") +assert-text: ("#trait-implementations-list details:nth-child(5) .code-header", "impl From for Foo") +assert-text: ("#trait-implementations-list section:nth-child(6) .code-header", "impl Send for Foo")