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
36 changes: 35 additions & 1 deletion src/librustdoc/clean/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down Expand Up @@ -1758,6 +1758,40 @@ impl PrimitiveType {
}
}

pub(crate) fn from_ty(ty: Ty<'_>) -> Option<Self> {
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(int) => Some(Self::from(*int)),
ty::Uint(uint) => Some(Self::from(*uint)),
ty::Float(float) => Some(Self::from(*float)),
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};
Expand Down
35 changes: 20 additions & 15 deletions src/librustdoc/html/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@

use std::cmp::Ordering;
use std::fmt::{self, Display, Write};
use std::iter::{self, once};
use std::slice;
use std::{iter, slice};

use itertools::{Either, Itertools};
use rustc_abi::ExternAbi;
Expand Down Expand Up @@ -434,27 +433,33 @@ 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
let ty = tcx.type_of(def_id);
Copy link
Copy Markdown
Member

@fmease fmease May 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you use the normalized type for both the ADT check and the primitive one? So set ty to the normalized type? Something like the following (pseudo code):

let ty = tcx.type_of(def_id);
let ty =
    ...query_normalize(...ty...)
    .map(...resolve_vars_if_possible...)
    .unwrap_or(ty);

That's because I'm relatively sure we're currently failing at finding the definition if the impl contains free alias types (unlikely scenario but normalizing is simply the correct thing to do).

Consider:

  • rustc a.rs --crate-type=lib:
    #![feature(lazy_type_alias, rustc_attrs)]
    #![rustc_coherence_is_core]
    
    type Identity<T> = T;
    
    impl Identity<u8> {
        pub fn inherent(self) {}
    }
  • rustdoc b.rs -L. --generate-link-to-definition -Zunstable-options:
    fn scope() {
        extern crate a;
        0u8.inherent();
    }

So under your PR I presume inherent still 404s but please double-check that locally.

View changes since the review

let ty = infcx
.at(&ObligationCause::dummy(), tcx.param_env(def_id))
.query_normalize(ty::Binder::dummy(
tcx.type_of(def_id).instantiate_identity().skip_norm_wip(),
))
.map(|resolved| infcx.resolve_vars_if_possible(resolved.value))
.ok()
.and_then(|normalized| normalized.skip_binder().ty_adt_def())
.map(|adt| adt.did())
.unwrap_or(def_id);
.query_normalize(ty::Binder::dummy(ty.instantiate_identity().skip_norm_wip()))
.map(|resolved| infcx.resolve_vars_if_possible(resolved.value).skip_binder())
.unwrap_or(ty.skip_binder());
if let Some(new_def_id) = ty.ty_adt_def().map(|adt| adt.did()) {
def_id = new_def_id;
} else {
prim = PrimitiveType::from_ty(ty);
}
}

let relative = clean::inline::item_relative_path(tcx, def_id);
let fqp: Vec<Symbol> = once(crate_name).chain(relative).collect();

let shortty = ItemType::from_def_id(def_id, tcx);
let mut fqp = vec![crate_name];
let shortty = if let Some(prim) = prim {
fqp.push(prim.as_sym());
ItemType::Primitive
} else {
fqp.append(&mut clean::inline::item_relative_path(tcx, def_id));
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)?;
Expand Down
16 changes: 16 additions & 0 deletions tests/rustdoc-html/jump-to-def/prim-method.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Checks that links to primitive types methods work.
// Regression test for <https://github.com/rust-lang/rust/issues/156707>.

// 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(());
}
Loading