From 2599fb7e71f472b13f87c6d39d09f3df5ff55434 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 25 Sep 2026 23:49:09 +0200 Subject: [PATCH] Correctly handle `rustc_allow_incoherent_impl` on primitive methods --- src/librustdoc/html/format.rs | 16 +++++++++++- .../auxiliary/incoherent_impl_primitive.rs | 7 ++++++ .../jump-to-def/incoherent_impl-primitive.rs | 25 +++++++++++++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 tests/rustdoc-html/jump-to-def/auxiliary/incoherent_impl_primitive.rs create mode 100644 tests/rustdoc-html/jump-to-def/incoherent_impl-primitive.rs diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index da88172c688f5..af7c6410a55b3 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -21,7 +21,7 @@ use rustc_hir::{ConstStability, StabilityLevel, StableSince}; use rustc_metadata::creader::CStore; use rustc_middle::ty::{self, Ty, TyCtxt, TypingMode}; use rustc_span::symbol::kw; -use rustc_span::{Ident, Symbol}; +use rustc_span::{Ident, Symbol, sym}; use tracing::{debug, trace}; use super::url_parts_builder::UrlPartsBuilder; @@ -537,11 +537,25 @@ fn generate_item_def_id_path( && !def_id.is_local() && def_id.krate != original_def_id.krate; } else { + // This hack is because primitive types are only documented in `core` and `std`. + // However, with `#[rustc_allow_incoherent_impl]`, a lot of primitive methods are + // implemented in `core`. Some of them are documented in `core` (like `[]::sort`) while + // others aren't (like `[]::to_vec`). So in case we're not documenting `core`, we link + // to `std`. + if !of_trait && crate_name != sym::core { + if ![sym::alloc, sym::std].contains(&crate_name) { + // We cannot link to this primitive's associated item as it's not part of + // `core`, `alloc` or `std` so returning early. + return Err(HrefError::UnnamableItem); + } + maybe_have_impl_not_in_def_crate = true; + } prim = PrimitiveType::from_ty(ty); } } let (shortty, fqp) = if let Some(prim) = prim { + let crate_name = if maybe_have_impl_not_in_def_crate { sym::std } else { crate_name }; (ItemType::Primitive, vec![crate_name, prim.as_sym()]) } else { ( diff --git a/tests/rustdoc-html/jump-to-def/auxiliary/incoherent_impl_primitive.rs b/tests/rustdoc-html/jump-to-def/auxiliary/incoherent_impl_primitive.rs new file mode 100644 index 0000000000000..98e9a5129417c --- /dev/null +++ b/tests/rustdoc-html/jump-to-def/auxiliary/incoherent_impl_primitive.rs @@ -0,0 +1,7 @@ +#![feature(rustc_attrs)] +#![allow(internal_features)] + +impl [T] { + #[rustc_allow_incoherent_impl] + pub fn f(&self) {} +} diff --git a/tests/rustdoc-html/jump-to-def/incoherent_impl-primitive.rs b/tests/rustdoc-html/jump-to-def/incoherent_impl-primitive.rs new file mode 100644 index 0000000000000..9a4609953a178 --- /dev/null +++ b/tests/rustdoc-html/jump-to-def/incoherent_impl-primitive.rs @@ -0,0 +1,25 @@ +// This test checks that outside of `core`/`alloc`/`std`, we don't link to incoherent method impl +// because there is no page to link to since primitive are only documented in `core` and `std`. + +//@ aux-build: incoherent_impl_primitive.rs +//@ build-aux-docs +//@ compile-flags: -Zunstable-options --generate-link-to-definition + +#![crate_name = "foo"] + +extern crate incoherent_impl_primitive; + +//@ has 'src/foo/incoherent_impl-primitive.rs.html' + +// It should contain only the link to "first". Sadly, because of the limit, we cannot test +// if a link doesn't have an attribute so for now, we simply ensure that the number of `` +// (including both line numbers and jump to def links) is superior by one to the number of +// line ``. If so, we know there is only one jump to def link. +//@ count - '//pre/code/a' 7 +//@ count - '//pre/code/a[@id]' 6 +//@ has - '//pre/code/a[@href="{{channel}}/core/primitive.slice.html#method.first"]' 'first' + +fn foo() { + let x = ["a"].f(); + ["a"].first(); +}