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
15 changes: 14 additions & 1 deletion src/librustdoc/html/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,7 @@ fn generate_item_def_id_path(
root_path: Option<&str>,
) -> Result<HrefInfo, HrefError> {
use rustc_middle::traits::ObligationCause;
use rustc_middle::ty;
use rustc_trait_selection::infer::TyCtxtInferExt;
use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt;

Expand All @@ -434,7 +435,19 @@ fn generate_item_def_id_path(
.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()) {
// If this is a dyn trait, we want to get the actual trait from which the method comes from.
// Since a `dyn trait` (as of 2026) can only be composed of a trait plus auto traits, we
// look for the trait and ignore auto traits.
if let ty::Dynamic(traits, _) = ty.kind()
&& let Some(trait_def_id) =
traits.iter().find_map(|trait_| match trait_.skip_binder() {
ty::ExistentialPredicate::Trait(t) => Some(t.def_id),
ty::ExistentialPredicate::Projection(p) => Some(p.trait_ref(tcx).def_id),
ty::ExistentialPredicate::AutoTrait(_) => None,
})
{
def_id = trait_def_id;
} else 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);
Expand Down
9 changes: 5 additions & 4 deletions src/librustdoc/html/span_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@ use rustc_span::{BytePos, ExpnKind};
use crate::clean::{self, PrimitiveType, rustc_span};
use crate::html::sources;

/// This is a stripped down version of [`rustc_span::Span`] that only contains the start and end byte positions of the span.
/// This is a stripped down version of [`rustc_span::Span`] that only contains the start and end
/// byte positions of the span.
///
/// Profiling showed that the `Span` interner was taking up a lot of the run-time when highlighting, and since we
/// never actually use the context and parent that are stored in a normal `Span`, we can replace its usages with this
/// one, which is much cheaper to construct.
/// Profiling showed that the `Span` interner was taking up a lot of the run-time when highlighting,
/// and since we never actually use the context and parent that are stored in a normal `Span`, we
/// can replace its usages with this one, which is much cheaper to construct.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) struct Span {
lo: BytePos,
Expand Down
35 changes: 35 additions & 0 deletions tests/rustdoc-html/jump-to-def/dyn.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// This test ensures that `dyn` traits methods are correctly linked in the
// source code (and not to "foreigntype").

//@ compile-flags: -Zunstable-options --generate-link-to-definition

#![crate_name = "foo"]

//@ has 'src/foo/dyn.rs.html'
//@ has - '//a[@href="{{channel}}/core/any/trait.Any.html#method.is"]' 'is'
// There should be two of them.
//@ count - '//a[@href="{{channel}}/core/any/trait.Any.html#method.is"]' 2

pub struct X<T: ?Sized>(std::boxed::Box<T>);

impl X<dyn std::any::Any> {
pub fn downcast<T: std::any::Any>(self) -> Result<X<T>, Self> {
if self.0.is::<T>() {
Ok(unsafe { std::mem::transmute(self) })
} else {
Err(self)
}
}
}

pub struct Y<T: ?Sized>(std::boxed::Box<T>);

impl Y<dyn std::any::Any + Send> {
pub fn downcast<T: std::any::Any + Send>(self) -> Result<Y<T>, Self> {
if self.0.is::<T>() {
Ok(unsafe { std::mem::transmute(self) })
} else {
Err(self)
}
}
}
Loading