Skip to content

Commit c3864be

Browse files
committed
rustdoc: show impl Trait<Box<Local>> for Foreign, etc on Local's docs
This is a generalization of PR 92940: that PR handled cases like `impl Foreign for Box<Local>`, but was missing handling for a few other closely related cases. My particular interest was with showing `impl From<Box<Utf8Path>> for Box<Path>` in camino's documentation. But I ended up handling a bunch of related cases along the way. I'm new to rustdoc so please let me know if I got anything wrong :) took a bit to fully understand how this worked.
1 parent 1676160 commit c3864be

3 files changed

Lines changed: 143 additions & 17 deletions

File tree

src/librustdoc/formats/cache.rs

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,30 @@ impl Cache {
233233
}
234234
}
235235

236+
impl CacheBuilder<'_, '_> {
237+
/// Extends `dids` with ones that an impl should be associated with for a type appearing in its
238+
/// `Self` type or trait generic arguments, accounting for references and `#[fundamental]`
239+
/// wrappers.
240+
///
241+
/// This ensures that impls like `impl Trait<Box<Local>> for Foreign`, `impl Trait for
242+
/// Box<Local>`, and other variations of these, are documented on `Local`'s page.
243+
fn extend_with_fundamental_dids(&self, ty: &clean::Type, dids: &mut FxIndexSet<DefId>) {
244+
dids.extend(ty.def_id(self.cache));
245+
// without_borrowed_ref allows cases like `impl Trait<&Box<Local>> for Foreign` to be
246+
// handled by this function. (This is rare in practice, but easy to handle here.)
247+
if let clean::Type::Path { path } = ty.without_borrowed_ref()
248+
&& let Some(generics) = path.generics()
249+
&& let ty::Adt(adt, _) =
250+
self.tcx.type_of(path.def_id()).instantiate_identity().skip_norm_wip().kind()
251+
&& adt.is_fundamental()
252+
{
253+
for inner in generics {
254+
self.extend_with_fundamental_dids(inner, dids);
255+
}
256+
}
257+
}
258+
}
259+
236260
impl DocFolder for CacheBuilder<'_, '_> {
237261
fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
238262
if item.item_id.is_local() {
@@ -418,22 +442,9 @@ impl DocFolder for CacheBuilder<'_, '_> {
418442
// Note: matching twice to restrict the lifetime of the `i` borrow.
419443
let mut dids = FxIndexSet::default();
420444
match i.for_ {
421-
clean::Type::Path { ref path }
422-
| clean::BorrowedRef { type_: clean::Type::Path { ref path }, .. } => {
423-
dids.insert(path.def_id());
424-
if let Some(generics) = path.generics()
425-
&& let ty::Adt(adt, _) = self
426-
.tcx
427-
.type_of(path.def_id())
428-
.instantiate_identity()
429-
.skip_norm_wip()
430-
.kind()
431-
&& adt.is_fundamental()
432-
{
433-
for ty in generics {
434-
dids.extend(ty.def_id(self.cache));
435-
}
436-
}
445+
clean::Type::Path { .. }
446+
| clean::BorrowedRef { type_: clean::Type::Path { .. }, .. } => {
447+
self.extend_with_fundamental_dids(&i.for_, &mut dids);
437448
}
438449
clean::DynTrait(ref bounds, _)
439450
| clean::BorrowedRef { type_: clean::DynTrait(ref bounds, _), .. } => {
@@ -452,7 +463,7 @@ impl DocFolder for CacheBuilder<'_, '_> {
452463
&& let Some(generics) = trait_.generics()
453464
{
454465
for bound in generics {
455-
dids.extend(bound.def_id(self.cache));
466+
self.extend_with_fundamental_dids(bound, &mut dids);
456467
}
457468
}
458469
let impl_item = Impl { impl_item: item };
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// Followup to https://github.com/rust-lang/rust/issues/92940 and impl-box.rs.
2+
//
3+
// Show traits implemented on fundamental types that wrap local ones: nested edition.
4+
5+
#![crate_name = "foo"]
6+
7+
use std::pin::Pin;
8+
9+
pub struct Local;
10+
11+
//@ has 'foo/struct.Local.html'
12+
13+
// Nested fundamental + foreign Self.
14+
//@ has '-' '//*[@id="impl-From%3CBox%3CLocal%3E%3E-for-String"]' 'impl From<Box<Local>> for String'
15+
impl From<Box<Local>> for String {
16+
fn from(_: Box<Local>) -> String {
17+
String::new()
18+
}
19+
}
20+
21+
// Also test with Pin.
22+
//@ has '-' '//*[@id="impl-From%3CPin%3CLocal%3E%3E-for-u8"]' 'impl From<Pin<Local>> for u8'
23+
impl From<Pin<Local>> for u8 {
24+
fn from(_: Pin<Local>) -> u8 {
25+
0
26+
}
27+
}
28+
29+
// Reference to a fundamental wrapper.
30+
//@ has '-' '//*[@id="impl-From%3C%26Box%3CLocal%3E%3E-for-u16"]' "impl<'a> From<&'a Box<Local>> for u16"
31+
impl<'a> From<&'a Box<Local>> for u16 {
32+
fn from(_: &'a Box<Local>) -> u16 {
33+
0
34+
}
35+
}
36+
37+
// Nested two levels deep in Self.
38+
//@ has '-' '//*[@id="impl-From%3Cu32%3E-for-Box%3CBox%3CLocal%3E%3E"]' 'impl From<u32> for Box<Box<Local>>'
39+
impl From<u32> for Box<Box<Local>> {
40+
fn from(_: u32) -> Box<Box<Local>> {
41+
Box::new(Box::new(Local))
42+
}
43+
}
44+
45+
// Mixed fundamental wrappers in Self.
46+
//@ has '-' '//*[@id="impl-From%3Cu64%3E-for-Pin%3CBox%3CLocal%3E%3E"]' 'impl From<u64> for Pin<Box<Local>>'
47+
impl From<u64> for Pin<Box<Local>> {
48+
fn from(_: u64) -> Pin<Box<Local>> {
49+
Pin::new(Box::new(Local))
50+
}
51+
}
52+
53+
// A non-fundamental wrapper must not show up on Local's page, but it should still be listed on the
54+
// trait's own page.
55+
pub trait Marker {}
56+
//@ has 'foo/trait.Marker.html' '//*[@id="impl-Marker-for-Vec%3CLocal%3E"]' 'impl Marker for Vec<Local>'
57+
//@ !has 'foo/struct.Local.html' '//*[@id="impl-Marker-for-Vec%3CLocal%3E"]' 'impl Marker for Vec<Local>'
58+
impl Marker for Vec<Local> {}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Companion to tests/rustdoc-html/impl/impl-fundamental-nesting.rs.
2+
//
3+
// Show traits implemented on fundamental types that wrap local ones: nested edition.
4+
5+
use std::pin::Pin;
6+
7+
pub struct Local;
8+
9+
// Nested fundamental + foreign Self.
10+
/// from box local
11+
impl From<Box<Local>> for String {
12+
fn from(_: Box<Local>) -> String {
13+
String::new()
14+
}
15+
}
16+
//@ set from_box_local = "$.index[?(@.docs=='from box local')].id"
17+
//@ has "$.index[?(@.name=='Local')].inner.struct.impls[*]" $from_box_local
18+
19+
// Reference to a fundamental wrapper.
20+
/// from ref box local
21+
impl<'a> From<&'a Box<Local>> for u16 {
22+
fn from(_: &'a Box<Local>) -> u16 {
23+
0
24+
}
25+
}
26+
//@ set from_ref_box_local = "$.index[?(@.docs=='from ref box local')].id"
27+
//@ has "$.index[?(@.name=='Local')].inner.struct.impls[*]" $from_ref_box_local
28+
29+
// Nested two levels deep in Self.
30+
/// u32 for box box local
31+
impl From<u32> for Box<Box<Local>> {
32+
fn from(_: u32) -> Box<Box<Local>> {
33+
Box::new(Box::new(Local))
34+
}
35+
}
36+
//@ set u32_for_box_box_local = "$.index[?(@.docs=='u32 for box box local')].id"
37+
//@ has "$.index[?(@.name=='Local')].inner.struct.impls[*]" $u32_for_box_box_local
38+
39+
// Mixed fundamental wrappers in Self.
40+
/// u64 for pin box local
41+
impl From<u64> for Pin<Box<Local>> {
42+
fn from(_: u64) -> Pin<Box<Local>> {
43+
Pin::new(Box::new(Local))
44+
}
45+
}
46+
//@ set u64_for_pin_box_local = "$.index[?(@.docs=='u64 for pin box local')].id"
47+
//@ has "$.index[?(@.name=='Local')].inner.struct.impls[*]" $u64_for_pin_box_local
48+
49+
// A non-fundamental wrapper must not associate the impl with Local, but the impl must still be
50+
// listed on the trait itself.
51+
pub trait Marker {}
52+
53+
/// marker for vec local
54+
impl Marker for Vec<Local> {}
55+
//@ set marker_for_vec_local = "$.index[?(@.docs=='marker for vec local')].id"
56+
//@ !has "$.index[?(@.name=='Local')].inner.struct.impls[*]" $marker_for_vec_local
57+
//@ has "$.index[?(@.name=='Marker')].inner.trait.implementations[*]" $marker_for_vec_local

0 commit comments

Comments
 (0)