Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 46 additions & 2 deletions src/librustdoc/clean/cfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,44 @@ impl Cfg {
}
}

/// Recursively sorts the configuration tree to ensure deterministic rendering.
///
/// Sorting groups predicates logically: Targets first, then Target Features,
/// then Crate Features, and finally nested Any/All/Not groupings.
/// Within each group, a fallback alphabetical sort is applied.
pub(crate) fn sort_for_rendering(&mut self) {
fn sort_cfg_entry(cfg: &mut CfgEntry) {
match cfg {
CfgEntry::Any(sub_cfgs, _) | CfgEntry::All(sub_cfgs, _) => {
for sub_cfg in sub_cfgs.iter_mut() {
sort_cfg_entry(sub_cfg);
}

sub_cfgs.sort_by_cached_key(|a| {
(
cfg_category(a),
Display(a, Format::LongPlain).to_string().to_ascii_lowercase(),
)
});
}
CfgEntry::Not(box_cfg, _) => sort_cfg_entry(box_cfg),
_ => {}
}
}

fn cfg_category(cfg: &CfgEntry) -> u8 {
match cfg {
CfgEntry::NameValue { name, .. } if *name == sym::feature => 2,
CfgEntry::NameValue { name, .. } if *name == sym::target_feature => 1,
CfgEntry::NameValue { .. } | CfgEntry::Bool(..) => 0,
CfgEntry::Any(..) | CfgEntry::All(..) | CfgEntry::Not(..) => 3,
_ => 4,
}
}

sort_cfg_entry(&mut self.0);
}

fn omit_preposition(&self) -> bool {
matches!(self.0, CfgEntry::Bool(..))
}
Expand Down Expand Up @@ -843,14 +881,20 @@ pub(crate) fn extract_cfg_from_attrs<'a, I: Iterator<Item = &'a hir::Attribute>
if matches!(cfg_info.current_cfg.0, CfgEntry::Bool(true, _)) {
None
} else {
Some(Arc::new(cfg_info.current_cfg.clone()))
let mut cfg = cfg_info.current_cfg.clone();
cfg.sort_for_rendering();
Some(Arc::new(cfg))
}
} else {
// If `doc(auto_cfg)` feature is enabled, we want to collect all `cfg` items, we remove the
// hidden ones afterward.
match strip_hidden(&cfg_info.current_cfg.0, &cfg_info.hidden_cfg) {
None | Some(CfgEntry::Bool(true, _)) => None,
Some(cfg) => Some(Arc::new(Cfg(cfg))),
Some(cfg_entry) => {
let mut cfg = Cfg(cfg_entry);
cfg.sort_for_rendering();
Some(Arc::new(cfg))
}
}
}
}
31 changes: 31 additions & 0 deletions src/librustdoc/clean/cfg/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -418,3 +418,34 @@ fn test_simplify_with() {
assert_eq!(foobar.simplify_with(&foobarbaz), None);
});
}

#[test]
fn test_sort_for_rendering() {
create_default_session_globals_then(|| {
let mut cfg = cfg_any(thin_vec![
name_value_cfg_e("feature", "sync"),
name_value_cfg_e("target_os", "linux"),
cfg_all_e(thin_vec![word_cfg_e("unix")]),
name_value_cfg_e("target_feature", "sse2"),
name_value_cfg_e("target_os", "android"),
name_value_cfg_e("feature", "alloc"),
]);

cfg.sort_for_rendering();

let expected = cfg_any(thin_vec![
// Category 0: Targets (Sorted Alphabetically: Android -> Linux)
name_value_cfg_e("target_os", "android"),
name_value_cfg_e("target_os", "linux"),
// Category 1: Target Features
name_value_cfg_e("target_feature", "sse2"),
// Category 2: Crate Features (Sorted Alphabetically: alloc -> sync)
name_value_cfg_e("feature", "alloc"),
name_value_cfg_e("feature", "sync"),
// Category 3: Nested logic pushed to the end
cfg_all_e(thin_vec![word_cfg_e("unix")]),
]);

assert_eq!(cfg, expected);
});
}
4 changes: 2 additions & 2 deletions tests/rustdoc-gui/item-info-overflow.goml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ assert-property: (".item-info", {"scrollWidth": "940"})
// Just to be sure we're comparing the correct "item-info":
assert-text: (
".item-info",
"Available on Android or Linux or Emscripten or DragonFly BSD or FreeBSD or NetBSD or OpenBSD",
"Available on Android or DragonFly BSD or Emscripten or FreeBSD or Linux or NetBSD or OpenBSD",
STARTS_WITH,
)

Expand All @@ -26,6 +26,6 @@ assert-property: (
// Just to be sure we're comparing the correct "item-info":
assert-text: (
"#impl-SimpleTrait-for-LongItemInfo2 .item-info",
"Available on Android or Linux or Emscripten or DragonFly BSD or FreeBSD or NetBSD or OpenBSD",
"Available on Android or DragonFly BSD or Emscripten or FreeBSD or Linux or NetBSD or OpenBSD",
STARTS_WITH,
)
4 changes: 2 additions & 2 deletions tests/rustdoc-gui/item-info.goml
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ store-position: (
"//*[@class='stab portability']//code[normalize-space()='Win32_System_Diagnostics']",
{"x": second_line_x, "y": second_line_y},
)
assert: |first_line_x| != |second_line_x| && |first_line_x| == 521 && |second_line_x| == 277
assert: |first_line_y| != |second_line_y| && |first_line_y| == 676 && |second_line_y| == 699
assert: |first_line_x| != |second_line_x| && |first_line_x| == 509 && |second_line_x| == 277
assert: |first_line_y| == |second_line_y| && |first_line_y| == 699

// Now we ensure that they're not rendered on the same line.
set-window-size: (1100, 800)
Expand Down
39 changes: 20 additions & 19 deletions tests/rustdoc-html/doc-cfg/all-targets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

//@ has all_targets/fn.foo.html \
// '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \
// 'Available on GNU or Catalyst or Managarm C Library or MSVC or musl or Newlib or \
// Neutrino 7.0 or Neutrino 7.1 or Neutrino 7.1 with io-sock or Neutrino 8.0 or \
// OpenHarmony or relibc or SGX or Simulator or WASIp1 or WASIp2 or WASIp3 or \
// uClibc or V5 or target_env=fake_env only.'
// 'Available on target_env=fake_env or Catalyst or GNU or Managarm C Library \
// or MSVC or musl or Neutrino 7.0 or Neutrino 7.1 or Neutrino 7.1 with io-sock \
// or Neutrino 8.0 or Newlib or OpenHarmony or relibc or SGX or Simulator or \
// uClibc or V5 or WASIp1 or WASIp2 or WASIp3 only.'
#[doc(cfg(any(
target_env = "gnu",
target_env = "macabi",
Expand All @@ -32,12 +32,12 @@ pub fn foo() {}

//@ has all_targets/fn.bar.html \
// '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \
// 'Available on AArch64 or AMD GPU or ARM or ARM64EC or AVR or BPF or C-SKY or \
// Hexagon or LoongArch32 or LoongArch64 or Motorola 680x0 or MIPS or MIPS release \
// 6 or MIPS-64 or MIPS-64 release 6 or MSP430 or NVidia GPU or PowerPC or \
// PowerPC64 or RISC-V RV32 or RISC-V RV64 or s390x or SPARC or SPARC-64 or SPIR-V \
// or WebAssembly or WebAssembly or x86 or x86-64 or Xtensa or \
// target_arch=fake_arch only.'
// 'Available on target_arch=fake_arch or AArch64 or AMD GPU or ARM or \
// ARM64EC or AVR or BPF or C-SKY or Hexagon or LoongArch32 or LoongArch64 \
// or MIPS or MIPS release 6 or MIPS-64 or MIPS-64 release 6 or Motorola 680x0 \
// or MSP430 or NVidia GPU or PowerPC or PowerPC64 or RISC-V RV32 or RISC-V RV64 \
// or s390x or SPARC or SPARC-64 or SPIR-V or WebAssembly or WebAssembly or x86 \
// or x86-64 or Xtensa only.'
#[doc(cfg(any(
target_arch = "aarch64",
target_arch = "amdgpu",
Expand Down Expand Up @@ -75,15 +75,16 @@ pub fn bar() {}

//@ has all_targets/fn.baz.html \
// '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \
// 'Available on AIX and AMD HSA and Android and CUDA and Cygwin and DragonFly \
// BSD and Emscripten and ESP-IDF and FreeBSD and Fuchsia and Haiku and HelenOS \
// and Hermit and Horizon and GNU/Hurd and illumos and iOS and L4Re and Linux \
// and LynxOS-178 and macOS and Managarm and Motor OS and NetBSD and bare-metal \
// and QNX Neutrino and NuttX and OpenBSD and Play Station Portable and Play \
// Station 1 and QuRT and Redox OS and RTEMS OS and Solaris and SOLID ASP3 and \
// TEEOS and Trusty and tvOS and UEFI and VEXos and visionOS and Play Station \
// Vita and VxWorks and WASI and watchOS and Windows and Xous and zero knowledge \
// Virtual Machine and target_os=unknown and target_os=fake_os only.'
// 'Available on target_os=fake_os and target_os=unknown and AIX and AMD HSA \
// and Android and bare-metal and CUDA and Cygwin and DragonFly BSD and \
// Emscripten and ESP-IDF and FreeBSD and Fuchsia and GNU/Hurd and Haiku \
// and HelenOS and Hermit and Horizon and illumos and iOS and L4Re and Linux \
// and LynxOS-178 and macOS and Managarm and Motor OS and NetBSD and NuttX \
// and OpenBSD and Play Station 1 and Play Station Portable and Play Station Vita \
// and QNX Neutrino and QuRT and Redox OS and RTEMS OS and Solaris and \
// SOLID ASP3 and TEEOS and Trusty and tvOS and UEFI and VEXos and visionOS \
// and VxWorks and WASI and watchOS and Windows and Xous and zero knowledge \
// Virtual Machine only.'
#[doc(cfg(all(
target_os = "aix",
target_os = "amdhsa",
Expand Down
6 changes: 3 additions & 3 deletions tests/rustdoc-html/doc-cfg/doc-cfg-simplification.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ pub mod ratel {

//@ has 'globuliferous/ratel/static.NUNCIATIVE.html'
//@ count - '//*[@class="stab portability"]' 1
//@ matches - '//*[@class="stab portability"]' 'crate features ratel and nunciative'
//@ matches - '//*[@class="stab portability"]' 'crate features nunciative and ratel'
#[doc(cfg(feature = "nunciative"))]
pub static NUNCIATIVE: () = ();

Expand Down Expand Up @@ -80,7 +80,7 @@ pub mod ratel {

//@ has 'globuliferous/ratel/enum.Cosmotellurian.html'
//@ count - '//*[@class="stab portability"]' 10
//@ matches - '//*[@class="stab portability"]' 'crate features ratel and cosmotellurian'
//@ matches - '//*[@class="stab portability"]' 'crate features cosmotellurian and ratel'
//@ matches - '//*[@class="stab portability"]' 'crate feature biotaxy'
//@ matches - '//*[@class="stab portability"]' 'crate feature xiphopagus'
//@ matches - '//*[@class="stab portability"]' 'crate feature juxtapositive'
Expand Down Expand Up @@ -158,7 +158,7 @@ pub mod ratel {

//@ has 'globuliferous/ratel/trait.Aposiopesis.html'
//@ count - '//*[@class="stab portability"]' 4
//@ matches - '//*[@class="stab portability"]' 'crate features ratel and aposiopesis'
//@ matches - '//*[@class="stab portability"]' 'crate features aposiopesis and ratel'
//@ matches - '//*[@class="stab portability"]' 'crate feature umbracious'
//@ matches - '//*[@class="stab portability"]' 'crate feature uakari'
//@ matches - '//*[@class="stab portability"]' 'crate feature rotograph'
Expand Down
4 changes: 2 additions & 2 deletions tests/rustdoc-html/doc-cfg/doc-cfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
//@ has doc_cfg/struct.Portable.html
//@ !has - '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' ''
//@ has - '//*[@id="method.unix_and_arm_only_function"]' 'fn unix_and_arm_only_function()'
//@ has - '//*[@class="stab portability"]' 'Available on Unix and ARM only.'
//@ has - '//*[@class="stab portability"]' 'Available on ARM and Unix only.'
//@ has - '//*[@id="method.wasi_and_wasm32_only_function"]' 'fn wasi_and_wasm32_only_function()'
//@ has - '//*[@class="stab portability"]' 'Available on WASI and WebAssembly only.'
pub struct Portable;
Expand All @@ -25,7 +25,7 @@ pub mod unix_only {

//@ has doc_cfg/unix_only/trait.ArmOnly.html \
// '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \
// 'Available on Unix and ARM only.'
// 'Available on ARM and Unix only.'
//@ count - '//*[@class="stab portability"]' 1
#[doc(cfg(target_arch = "arm"))]
pub trait ArmOnly {
Expand Down
10 changes: 5 additions & 5 deletions tests/rustdoc-html/doc-cfg/duplicate-cfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ pub mod bar {
}

//@ has 'foo/baz/index.html'
//@ has '-' '//*[@class="stab portability"]' 'Available on crate features sync and send only.'
//@ has '-' '//*[@class="stab portability"]' 'Available on crate features send and sync only.'
#[doc(cfg(all(feature = "sync", feature = "send")))]
pub mod baz {
//@ has 'foo/baz/struct.Baz.html'
//@ has '-' '//*[@class="stab portability"]' 'Available on crate features sync and send only.'
//@ has '-' '//*[@class="stab portability"]' 'Available on crate features send and sync only.'
#[doc(cfg(feature = "sync"))]
pub struct Baz;
}
Expand All @@ -37,17 +37,17 @@ pub mod baz {
#[doc(cfg(feature = "sync"))]
pub mod qux {
//@ has 'foo/qux/struct.Qux.html'
//@ has '-' '//*[@class="stab portability"]' 'Available on crate features sync and send only.'
//@ has '-' '//*[@class="stab portability"]' 'Available on crate features send and sync only.'
#[doc(cfg(all(feature = "sync", feature = "send")))]
pub struct Qux;
}

//@ has 'foo/quux/index.html'
//@ has '-' '//*[@class="stab portability"]' 'Available on crate feature sync and crate feature send and foo only.'
//@ has '-' '//*[@class="stab portability"]' 'Available on foo and crate feature send and crate feature sync only.'
#[doc(cfg(all(feature = "sync", feature = "send", foo)))]
pub mod quux {
//@ has 'foo/quux/struct.Quux.html'
//@ has '-' '//*[@class="stab portability"]' 'Available on crate feature sync and crate feature send and foo and bar only.'
//@ has '-' '//*[@class="stab portability"]' 'Available on bar and foo and crate feature send and crate feature sync only.'
#[doc(cfg(all(feature = "send", feature = "sync", bar)))]
pub struct Quux;
}
42 changes: 42 additions & 0 deletions tests/rustdoc-html/doc-cfg/sort.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Tests that `doc(cfg)` badges and auto-detected `cfg` badges are sorted deterministically.
//@ edition:2021
//@ compile-flags: --cfg target_os="linux"

#![crate_name = "foo"]
#![feature(doc_cfg)]

// TEST 1: Explicit `#[doc(cfg(...))]`
// Tests that OS targets are sorted alphabetically.
//@ has 'foo/fn.foo.html'
//@ has - '//*[@class="stab portability"]' 'Available on Android or Apple or Cygwin \
// or DragonFly BSD or FreeBSD or Linux or NetBSD or OpenBSD or QNX Neutrino only.'
#[doc(cfg(any(
target_os = "android",
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
target_os = "nto",
target_vendor = "apple",
target_os = "cygwin"
)))]
pub fn foo() {}

// TEST 2: Implicit `#[cfg(...)]` via auto-detection
// Tests that targets are sorted alphabetically just like explicit `doc(cfg)`.
//@ has 'foo/fn.bar.html'
//@ has - '//*[@class="stab portability"]' 'Available on Android or Apple or Cygwin \
// or DragonFly BSD or FreeBSD or Linux or NetBSD or OpenBSD or QNX Neutrino only.'
#[cfg(any(
target_os = "android",
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
target_os = "nto",
target_vendor = "apple",
target_os = "cygwin"
))]
pub fn bar() {}
4 changes: 2 additions & 2 deletions tests/rustdoc-html/inline_cross/doc-auto-cfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@ pub mod pre {
pub mod post {
// issue: <https://github.com/rust-lang/rust/issues/113982>
//@ has 'it/post/index.html' '//*[@class="stab portability"]' 'extra'
//@ has - '//*[@class="stab portability"]' 'extra and extension'
//@ has - '//*[@class="stab portability"]' 'extension and extra'
//@ has 'it/post/struct.Type.html' '//*[@class="stab portability"]' \
// 'Available on crate feature extra only.'
//@ has 'it/post/fn.compute.html' '//*[@class="stab portability"]' \
// 'Available on crate feature extra and extension only.'
// 'Available on extension and crate feature extra only.'
#[cfg(feature = "extra")]
pub use doc_auto_cfg::*;

Expand Down
4 changes: 2 additions & 2 deletions tests/rustdoc-html/target-feature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,13 @@ pub unsafe fn f2_not_safe() {}

//@ has 'foo/fn.f3_multifeatures_in_attr.html'
//@ has - '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \
// 'Available on target features popcnt and avx2 only.'
// 'Available on target features avx2 and popcnt only.'
#[target_feature(enable = "popcnt", enable = "avx2")]
pub fn f3_multifeatures_in_attr() {}

//@ has 'foo/fn.f4_multi_attrs.html'
//@ has - '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \
// 'Available on target features popcnt and avx2 only.'
// 'Available on target features avx2 and popcnt only.'
#[target_feature(enable = "popcnt")]
#[target_feature(enable = "avx2")]
pub fn f4_multi_attrs() {}
Loading