From 73efff4c8dc5824bc42010f551b3fc5e5678ad96 Mon Sep 17 00:00:00 2001 From: binarycat Date: Fri, 11 Jul 2025 13:45:51 -0500 Subject: [PATCH 1/4] add regression test for RUST-143222 --- .../reexport/auxiliary/wrap-unnamable-type.rs | 12 ++++++++++++ .../rustdoc/reexport/wrapped-unnamble-type-143222.rs | 12 ++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 tests/rustdoc/reexport/auxiliary/wrap-unnamable-type.rs create mode 100644 tests/rustdoc/reexport/wrapped-unnamble-type-143222.rs diff --git a/tests/rustdoc/reexport/auxiliary/wrap-unnamable-type.rs b/tests/rustdoc/reexport/auxiliary/wrap-unnamable-type.rs new file mode 100644 index 0000000000000..8ce0a0de54484 --- /dev/null +++ b/tests/rustdoc/reexport/auxiliary/wrap-unnamable-type.rs @@ -0,0 +1,12 @@ +pub trait Assoc { + type Ty; +} + +pub struct Foo(::Ty); + +const _: () = { + impl crate::Assoc for Foo { + type Ty = Bar; + } + pub struct Bar; +}; diff --git a/tests/rustdoc/reexport/wrapped-unnamble-type-143222.rs b/tests/rustdoc/reexport/wrapped-unnamble-type-143222.rs new file mode 100644 index 0000000000000..005a5c0b98add --- /dev/null +++ b/tests/rustdoc/reexport/wrapped-unnamble-type-143222.rs @@ -0,0 +1,12 @@ +//@ compile-flags: -Z normalize-docs --document-private-items -Zunstable-options --show-type-layout +//@ aux-build:wrap-unnamable-type.rs +//@ build-aux-docs + +#![crate_name = "foo"] + +extern crate wrap_unnamable_type as helper; +//extern crate helper; +//@ has 'foo/struct.Foo.html' +//@ !hasraw - '_/struct.Bar.html' +#[doc(inline)] +pub use helper::Foo; From 950634d90cead84cbb19d3d05a51fe810d56bde6 Mon Sep 17 00:00:00 2001 From: binarycat Date: Sat, 12 Jul 2025 15:05:30 -0500 Subject: [PATCH 2/4] rustdoc: never try to link to unnamable types --- src/librustdoc/html/format.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index bcb3e57c84428..aff6a4ddabae8 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -367,6 +367,8 @@ pub(crate) enum HrefError { Private, // Not in external cache, href link should be in same page NotInExternalCache, + /// Refers to an unnamable item, such as one defined within a function or const block. + UnnamableItem, } // Panics if `syms` is empty. @@ -509,7 +511,13 @@ fn url_parts( builder.extend(module_fqp.iter().copied()); Ok(builder) } - ExternalLocation::Local => Ok(href_relative_parts(module_fqp, relative_to)), + ExternalLocation::Local => { + if module_fqp.iter().any(|sym| sym.as_str() == "_") { + Err(HrefError::UnnamableItem) + } else { + Ok(href_relative_parts(module_fqp, relative_to)) + } + } ExternalLocation::Unknown => Err(HrefError::DocumentationNotBuilt), } } From daff4c3a26a5a3a6ab92152993d63a2a8f0fb48e Mon Sep 17 00:00:00 2001 From: binarycat Date: Mon, 14 Jul 2025 12:31:58 -0500 Subject: [PATCH 3/4] fix regression test --- tests/rustdoc/reexport/auxiliary/wrap-unnamable-type.rs | 2 +- tests/rustdoc/reexport/wrapped-unnamble-type-143222.rs | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/rustdoc/reexport/auxiliary/wrap-unnamable-type.rs b/tests/rustdoc/reexport/auxiliary/wrap-unnamable-type.rs index 8ce0a0de54484..7f8e346c8beac 100644 --- a/tests/rustdoc/reexport/auxiliary/wrap-unnamable-type.rs +++ b/tests/rustdoc/reexport/auxiliary/wrap-unnamable-type.rs @@ -4,7 +4,7 @@ pub trait Assoc { pub struct Foo(::Ty); -const _: () = { +const _X: () = { impl crate::Assoc for Foo { type Ty = Bar; } diff --git a/tests/rustdoc/reexport/wrapped-unnamble-type-143222.rs b/tests/rustdoc/reexport/wrapped-unnamble-type-143222.rs index 005a5c0b98add..9a8893786f5f5 100644 --- a/tests/rustdoc/reexport/wrapped-unnamble-type-143222.rs +++ b/tests/rustdoc/reexport/wrapped-unnamble-type-143222.rs @@ -2,11 +2,15 @@ //@ aux-build:wrap-unnamable-type.rs //@ build-aux-docs +// regression test for https://github.com/rust-lang/rust/issues/143222 +// makes sure normalizing docs does not cause us to link to unnamable types +// in cross-crate reexports. + #![crate_name = "foo"] extern crate wrap_unnamable_type as helper; -//extern crate helper; + //@ has 'foo/struct.Foo.html' -//@ !hasraw - '_/struct.Bar.html' +//@ !hasraw - 'struct.Bar.html' #[doc(inline)] pub use helper::Foo; From 09520221ce0463feec3cfaff4ef9321c79bccded Mon Sep 17 00:00:00 2001 From: binarycat Date: Tue, 15 Jul 2025 16:06:03 -0500 Subject: [PATCH 4/4] rustdoc: actually never link to unnamable types --- src/librustdoc/html/format.rs | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index aff6a4ddabae8..944a6b5de899d 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -492,6 +492,20 @@ fn generate_item_def_id_path( Ok((url_parts, shortty, fqp)) } +/// Checks if the given defid refers to an item that is unnamable, such as one defined in a const block. +fn is_unnamable(tcx: TyCtxt<'_>, did: DefId) -> bool { + let mut cur_did = did; + while let Some(parent) = tcx.opt_parent(cur_did) { + match tcx.def_kind(parent) { + // items defined in these can be linked to + DefKind::Mod | DefKind::Impl { .. } | DefKind::ForeignMod => cur_did = parent, + // everything else does not have docs generated for it + _ => return true, + } + } + return false; +} + fn to_module_fqp(shortty: ItemType, fqp: &[Symbol]) -> &[Symbol] { if shortty == ItemType::Module { fqp } else { &fqp[..fqp.len() - 1] } } @@ -511,13 +525,7 @@ fn url_parts( builder.extend(module_fqp.iter().copied()); Ok(builder) } - ExternalLocation::Local => { - if module_fqp.iter().any(|sym| sym.as_str() == "_") { - Err(HrefError::UnnamableItem) - } else { - Ok(href_relative_parts(module_fqp, relative_to)) - } - } + ExternalLocation::Local => Ok(href_relative_parts(module_fqp, relative_to)), ExternalLocation::Unknown => Err(HrefError::DocumentationNotBuilt), } } @@ -571,6 +579,9 @@ pub(crate) fn href_with_root_path( } _ => original_did, }; + if is_unnamable(cx.tcx(), did) { + return Err(HrefError::UnnamableItem); + } let cache = cx.cache(); let relative_to = &cx.current;