From 260409af1c5956827c23eadc6b9154552f282172 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 19 May 2026 04:03:55 +0200 Subject: [PATCH] Fix jump to def link generation on primitive type associated methods --- src/librustdoc/clean/types.rs | 50 ++++++++++++++++++- src/librustdoc/html/format.rs | 22 +++++--- src/librustdoc/html/span_map.rs | 8 +++ tests/rustdoc-html/jump-to-def/prim-method.rs | 16 ++++++ 4 files changed, 89 insertions(+), 7 deletions(-) create mode 100644 tests/rustdoc-html/jump-to-def/prim-method.rs diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 2fe0c8d8776dd..385ed40c12592 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -20,7 +20,7 @@ use rustc_index::IndexVec; use rustc_metadata::rendered_const; use rustc_middle::span_bug; use rustc_middle::ty::fast_reject::SimplifiedType; -use rustc_middle::ty::{self, TyCtxt, Visibility}; +use rustc_middle::ty::{self, Ty, TyCtxt, Visibility}; use rustc_resolve::rustdoc::{ DocFragment, add_doc_fragment, attrs_to_doc_fragments, inner_docs, span_of_fragments, }; @@ -1758,6 +1758,54 @@ impl PrimitiveType { } } + pub(crate) fn from_ty(ty: Ty<'_>) -> Option { + use ty::{FloatTy, IntTy, UintTy}; + match ty.kind() { + ty::Array(..) => Some(Self::Array), + ty::Bool => Some(Self::Bool), + ty::Char => Some(Self::Char), + ty::FnDef(..) | ty::FnPtr(..) => Some(Self::Fn), + ty::Int(IntTy::Isize) => Some(Self::Isize), + ty::Int(IntTy::I8) => Some(Self::I8), + ty::Int(IntTy::I16) => Some(Self::I16), + ty::Int(IntTy::I32) => Some(Self::I32), + ty::Int(IntTy::I64) => Some(Self::I64), + ty::Int(IntTy::I128) => Some(Self::I128), + ty::Uint(UintTy::Usize) => Some(Self::Usize), + ty::Uint(UintTy::U8) => Some(Self::U8), + ty::Uint(UintTy::U16) => Some(Self::U16), + ty::Uint(UintTy::U32) => Some(Self::U32), + ty::Uint(UintTy::U64) => Some(Self::U64), + ty::Uint(UintTy::U128) => Some(Self::U128), + ty::Float(FloatTy::F16) => Some(Self::F16), + ty::Float(FloatTy::F32) => Some(Self::F32), + ty::Float(FloatTy::F64) => Some(Self::F64), + ty::Float(FloatTy::F128) => Some(Self::F128), + ty::Never => Some(Self::Never), + ty::Pat(..) => Some(Self::Pat), + ty::RawPtr(..) => Some(Self::RawPointer), + ty::Ref(..) => Some(Self::Reference), + ty::Slice(..) => Some(Self::Slice), + ty::Str => Some(Self::Str), + ty::Tuple(elems) if elems.is_empty() => Some(Self::Unit), + ty::Tuple(_) => Some(Self::Tuple), + ty::Adt(..) + | ty::Alias(..) + | ty::Bound(..) + | ty::Closure(..) + | ty::Coroutine(..) + | ty::CoroutineClosure(..) + | ty::CoroutineWitness(..) + | ty::Dynamic(..) + | ty::Error(..) + | ty::Foreign(..) + | ty::Infer(..) + | ty::Param(..) + | ty::Placeholder(..) + | ty::UnsafeBinder(..) => None, + } + } + pub(crate) fn simplified_types() -> &'static SimplifiedTypes { use PrimitiveType::*; use ty::{FloatTy, IntTy, UintTy}; diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 35212d480cfdd..86b41167f544f 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -434,12 +434,13 @@ fn generate_item_def_id_path( let tcx = cx.tcx(); let crate_name = tcx.crate_name(def_id.krate); + let mut prim = None; // No need to try to infer the actual parent item if it's not an associated item from the `impl` // block. if def_id != original_def_id && matches!(tcx.def_kind(def_id), DefKind::Impl { .. }) { let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); - def_id = infcx + if let Some(new_def_id) = infcx .at(&ObligationCause::dummy(), tcx.param_env(def_id)) .query_normalize(ty::Binder::dummy( tcx.type_of(def_id).instantiate_identity().skip_norm_wip(), @@ -448,13 +449,22 @@ fn generate_item_def_id_path( .ok() .and_then(|normalized| normalized.skip_binder().ty_adt_def()) .map(|adt| adt.did()) - .unwrap_or(def_id); + { + def_id = new_def_id; + } else { + // If the parent is an impl of a primitive type, the inference seems to fail, so instead + // we use `type_of` to get the actual primitive type. + prim = PrimitiveType::from_ty(tcx.type_of(def_id).skip_binder()); + } } - let relative = clean::inline::item_relative_path(tcx, def_id); - let fqp: Vec = once(crate_name).chain(relative).collect(); - - let shortty = ItemType::from_def_id(def_id, tcx); + let (fqp, shortty): (Vec, ItemType) = match prim { + Some(prim) => (vec![crate_name, prim.as_sym()], ItemType::Primitive), + None => { + let relative = clean::inline::item_relative_path(tcx, def_id); + (once(crate_name).chain(relative).collect(), ItemType::from_def_id(def_id, tcx)) + } + }; let module_fqp = to_module_fqp(shortty, &fqp); let (parts, is_absolute) = url_parts(cx.cache(), def_id, module_fqp, &cx.current)?; diff --git a/src/librustdoc/html/span_map.rs b/src/librustdoc/html/span_map.rs index 517b538d1bfd1..beaa3856c9957 100644 --- a/src/librustdoc/html/span_map.rs +++ b/src/librustdoc/html/span_map.rs @@ -127,6 +127,14 @@ impl<'tcx> SpanMapVisitor<'tcx> { fn link_for_def(&self, def_id: DefId) -> LinkFromSrc { if def_id.is_local() { LinkFromSrc::Local(rustc_span(def_id, self.tcx)) + // // If this is the `DefId` of a function... + // } else if self.tcx.type_of(def_id).skip_binder().is_fn() + // // coming from an impl block... + // && let Some(parent_def_id) = self.tcx.opt_parent(def_id) + // // implemented on a primitive, then it's not an external item but a primitive. + // && self.tcx.type_of(parent_def_id).skip_binder().is_primitive() + // { + // LinkFromSrc::Primitive(def_id) } else { LinkFromSrc::External(def_id) } diff --git a/tests/rustdoc-html/jump-to-def/prim-method.rs b/tests/rustdoc-html/jump-to-def/prim-method.rs new file mode 100644 index 0000000000000..43f6592de5390 --- /dev/null +++ b/tests/rustdoc-html/jump-to-def/prim-method.rs @@ -0,0 +1,16 @@ +// Checks that links to primitive types methods work. +// Regression test for . + +// ignore-tidy-linelength +//@ compile-flags: -Zunstable-options --generate-link-to-definition + +#![crate_name = "foo"] + +//@ has 'src/foo/prim-method.rs.html' + +fn scope() { + //@ has - '//a[@href="{{channel}}/core/primitive.usize.html#method.saturating_add"]' 'saturating_add' + let _ = 0usize.saturating_add(1); + //@ has - '//a[@href="{{channel}}/core/primitive.bool.html#method.then_some"]' 'then_some' + let _ = false.then_some(()); +}