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
45 changes: 28 additions & 17 deletions src/librustdoc/formats/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,30 @@ impl Cache {
}
}

impl CacheBuilder<'_, '_> {
/// Extends `dids` with ones that an impl should be associated with for a type appearing in its
/// `Self` type or trait generic arguments, accounting for references and `#[fundamental]`
/// wrappers.
///
/// This ensures that impls like `impl Trait<Box<Local>> for Foreign`, `impl Trait for
/// Box<Local>`, and other variations of these, are documented on `Local`'s page.
fn extend_with_fundamental_dids(&self, ty: &clean::Type, dids: &mut FxIndexSet<DefId>) {
dids.extend(ty.def_id(self.cache));
// without_borrowed_ref allows cases like `impl Trait<&Box<Local>> for Foreign` to be
// handled by this function. (This is rare in practice, but easy to handle here.)
if let clean::Type::Path { path } = ty.without_borrowed_ref()
&& let Some(generics) = path.generics()
&& let ty::Adt(adt, _) =
self.tcx.type_of(path.def_id()).instantiate_identity().skip_norm_wip().kind()
&& adt.is_fundamental()
{
for inner in generics {
self.extend_with_fundamental_dids(inner, dids);
}
}
}
}

impl DocFolder for CacheBuilder<'_, '_> {
fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
if item.item_id.is_local() {
Expand Down Expand Up @@ -418,22 +442,9 @@ impl DocFolder for CacheBuilder<'_, '_> {
// Note: matching twice to restrict the lifetime of the `i` borrow.
let mut dids = FxIndexSet::default();
match i.for_ {
clean::Type::Path { ref path }
| clean::BorrowedRef { type_: clean::Type::Path { ref path }, .. } => {
dids.insert(path.def_id());
if let Some(generics) = path.generics()
&& let ty::Adt(adt, _) = self
.tcx
.type_of(path.def_id())
.instantiate_identity()
.skip_norm_wip()
.kind()
&& adt.is_fundamental()
{
for ty in generics {
dids.extend(ty.def_id(self.cache));
}
}
clean::Type::Path { .. }
| clean::BorrowedRef { type_: clean::Type::Path { .. }, .. } => {
self.extend_with_fundamental_dids(&i.for_, &mut dids);
}
clean::DynTrait(ref bounds, _)
| clean::BorrowedRef { type_: clean::DynTrait(ref bounds, _), .. } => {
Expand All @@ -452,7 +463,7 @@ impl DocFolder for CacheBuilder<'_, '_> {
&& let Some(generics) = trait_.generics()
{
for bound in generics {
dids.extend(bound.def_id(self.cache));
self.extend_with_fundamental_dids(bound, &mut dids);
}
}
let impl_item = Impl { impl_item: item };
Expand Down
58 changes: 58 additions & 0 deletions tests/rustdoc-html/impl/impl-fundamental-nesting.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Followup to https://github.com/rust-lang/rust/issues/92940 and impl-box.rs.
//
// Show traits implemented on fundamental types that wrap local ones: nested edition.

#![crate_name = "foo"]

use std::pin::Pin;

pub struct Local;

//@ has 'foo/struct.Local.html'

// Nested fundamental + foreign Self.
//@ has '-' '//*[@id="impl-From%3CBox%3CLocal%3E%3E-for-String"]' 'impl From<Box<Local>> for String'
impl From<Box<Local>> for String {
fn from(_: Box<Local>) -> String {
String::new()
}
}

// Also test with Pin.
//@ has '-' '//*[@id="impl-From%3CPin%3CLocal%3E%3E-for-u8"]' 'impl From<Pin<Local>> for u8'
impl From<Pin<Local>> for u8 {
fn from(_: Pin<Local>) -> u8 {
0
}
}

// Reference to a fundamental wrapper.
//@ has '-' '//*[@id="impl-From%3C%26Box%3CLocal%3E%3E-for-u16"]' "impl<'a> From<&'a Box<Local>> for u16"
impl<'a> From<&'a Box<Local>> for u16 {
fn from(_: &'a Box<Local>) -> u16 {
0
}
}

// Nested two levels deep in Self.
//@ has '-' '//*[@id="impl-From%3Cu32%3E-for-Box%3CBox%3CLocal%3E%3E"]' 'impl From<u32> for Box<Box<Local>>'
impl From<u32> for Box<Box<Local>> {
fn from(_: u32) -> Box<Box<Local>> {
Box::new(Box::new(Local))
}
}

// Mixed fundamental wrappers in Self.
//@ has '-' '//*[@id="impl-From%3Cu64%3E-for-Pin%3CBox%3CLocal%3E%3E"]' 'impl From<u64> for Pin<Box<Local>>'
impl From<u64> for Pin<Box<Local>> {
fn from(_: u64) -> Pin<Box<Local>> {
Pin::new(Box::new(Local))
}
}

// A non-fundamental wrapper must not show up on Local's page, but it should still be listed on the
// trait's own page.
pub trait Marker {}
//@ has 'foo/trait.Marker.html' '//*[@id="impl-Marker-for-Vec%3CLocal%3E"]' 'impl Marker for Vec<Local>'
//@ !has 'foo/struct.Local.html' '//*[@id="impl-Marker-for-Vec%3CLocal%3E"]' 'impl Marker for Vec<Local>'
impl Marker for Vec<Local> {}
57 changes: 57 additions & 0 deletions tests/rustdoc-json/impls/fundamental_nesting.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Companion to tests/rustdoc-html/impl/impl-fundamental-nesting.rs.
//
// Show traits implemented on fundamental types that wrap local ones: nested edition.

use std::pin::Pin;

pub struct Local;

// Nested fundamental + foreign Self.
/// from box local
impl From<Box<Local>> for String {
fn from(_: Box<Local>) -> String {
String::new()
}
}
//@ set from_box_local = "$.index[?(@.docs=='from box local')].id"
//@ has "$.index[?(@.name=='Local')].inner.struct.impls[*]" $from_box_local

// Reference to a fundamental wrapper.
/// from ref box local
impl<'a> From<&'a Box<Local>> for u16 {
fn from(_: &'a Box<Local>) -> u16 {
0
}
}
//@ set from_ref_box_local = "$.index[?(@.docs=='from ref box local')].id"
//@ has "$.index[?(@.name=='Local')].inner.struct.impls[*]" $from_ref_box_local

// Nested two levels deep in Self.
/// u32 for box box local
impl From<u32> for Box<Box<Local>> {
fn from(_: u32) -> Box<Box<Local>> {
Box::new(Box::new(Local))
}
}
//@ set u32_for_box_box_local = "$.index[?(@.docs=='u32 for box box local')].id"
//@ has "$.index[?(@.name=='Local')].inner.struct.impls[*]" $u32_for_box_box_local

// Mixed fundamental wrappers in Self.
/// u64 for pin box local
impl From<u64> for Pin<Box<Local>> {
fn from(_: u64) -> Pin<Box<Local>> {
Pin::new(Box::new(Local))
}
}
//@ set u64_for_pin_box_local = "$.index[?(@.docs=='u64 for pin box local')].id"
//@ has "$.index[?(@.name=='Local')].inner.struct.impls[*]" $u64_for_pin_box_local

// A non-fundamental wrapper must not associate the impl with Local, but the impl must still be
// listed on the trait itself.
pub trait Marker {}

/// marker for vec local
impl Marker for Vec<Local> {}
//@ set marker_for_vec_local = "$.index[?(@.docs=='marker for vec local')].id"
//@ !has "$.index[?(@.name=='Local')].inner.struct.impls[*]" $marker_for_vec_local
//@ has "$.index[?(@.name=='Marker')].inner.trait.implementations[*]" $marker_for_vec_local
Loading