Skip to content

Commit fd0ad03

Browse files
committed
Recursively document methods via Deref traits
1 parent 61c8aae commit fd0ad03

File tree

3 files changed

+90
-3
lines changed

3 files changed

+90
-3
lines changed

src/librustdoc/html/render/mod.rs

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3548,9 +3548,6 @@ fn render_assoc_items(
35483548
);
35493549
}
35503550
}
3551-
if let AssocItemRender::DerefFor { .. } = what {
3552-
return;
3553-
}
35543551
if !traits.is_empty() {
35553552
let deref_impl =
35563553
traits.iter().find(|t| t.inner_impl().trait_.def_id() == cache.deref_trait_did);
@@ -3560,6 +3557,12 @@ fn render_assoc_items(
35603557
render_deref_methods(w, cx, impl_, containing_item, has_deref_mut, cache);
35613558
}
35623559

3560+
// If we were already one level into rendering deref methods, we don't want to render
3561+
// anything after recursing into any further deref methods above.
3562+
if let AssocItemRender::DerefFor { .. } = what {
3563+
return;
3564+
}
3565+
35633566
let (synthetic, concrete): (Vec<&&Impl>, Vec<&&Impl>) =
35643567
traits.iter().partition(|t| t.inner_impl().synthetic);
35653568
let (blanket_impl, concrete): (Vec<&&Impl>, _) =
@@ -3631,6 +3634,13 @@ fn render_deref_methods(
36313634
let what =
36323635
AssocItemRender::DerefFor { trait_: deref_type, type_: real_target, deref_mut_: deref_mut };
36333636
if let Some(did) = target.def_id() {
3637+
if let Some(type_did) = impl_.inner_impl().for_.def_id() {
3638+
// `impl Deref<Target = S> for S`
3639+
if did == type_did {
3640+
// Avoid infinite cycles
3641+
return;
3642+
}
3643+
}
36343644
render_assoc_items(w, cx, container_item, did, what, cache);
36353645
} else {
36363646
if let Some(prim) = target.primitive_type() {
@@ -4417,6 +4427,26 @@ fn sidebar_deref_methods(impl_: &Impl, v: &Vec<Impl>) -> String {
44174427
out.push_str(&format!("<div class=\"sidebar-links\">{}</div>", ret.join("")));
44184428
}
44194429
}
4430+
4431+
// Recurse into any further impls that might exist for `target`
4432+
if let Some(target_did) = target.def_id() {
4433+
if let Some(target_impls) = c.impls.get(&target_did) {
4434+
if let Some(target_deref_impl) = target_impls
4435+
.iter()
4436+
.filter(|i| i.inner_impl().trait_.is_some())
4437+
.find(|i| i.inner_impl().trait_.def_id() == c.deref_trait_did)
4438+
{
4439+
if let Some(type_did) = impl_.inner_impl().for_.def_id() {
4440+
// `impl Deref<Target = S> for S`
4441+
if target_did == type_did {
4442+
// Avoid infinite cycles
4443+
return out;
4444+
}
4445+
}
4446+
out.push_str(&sidebar_deref_methods(target_deref_impl, target_impls));
4447+
}
4448+
}
4449+
}
44204450
}
44214451

44224452
out
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// check-pass
2+
// #26207: Ensure `Deref` cycles are properly handled without errors.
3+
4+
#[derive(Copy, Clone)]
5+
struct S;
6+
7+
impl std::ops::Deref for S {
8+
type Target = S;
9+
10+
fn deref(&self) -> &S {
11+
self
12+
}
13+
}
14+
15+
fn main() {
16+
let s: S = *******S;
17+
}

src/test/rustdoc/deref-recursive.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// #26207: Show all methods reachable via Deref impls, recursing through multiple dereferencing
2+
// levels if needed.
3+
4+
// @has 'foo/struct.Foo.html'
5+
// @has '-' '//*[@id="deref-methods"]' 'Methods from Deref<Target = Bar>'
6+
// @has '-' '//*[@class="impl-items"]//*[@id="method.bar"]' 'pub fn bar(&self)'
7+
// @has '-' '//*[@id="deref-methods"]' 'Methods from Deref<Target = Baz>'
8+
// @has '-' '//*[@class="impl-items"]//*[@id="method.baz"]' 'pub fn baz(&self)'
9+
// @has '-' '//*[@class="sidebar-title"]' 'Methods from Deref<Target=Bar>'
10+
// @has '-' '//*[@class="sidebar-links"]/a[@href="#method.bar"]' 'bar'
11+
// @has '-' '//*[@class="sidebar-title"]' 'Methods from Deref<Target=Baz>'
12+
// @has '-' '//*[@class="sidebar-links"]/a[@href="#method.baz"]' 'baz'
13+
14+
#![crate_name = "foo"]
15+
16+
use std::ops::Deref;
17+
18+
pub struct Foo(Bar);
19+
pub struct Bar(Baz);
20+
pub struct Baz;
21+
22+
impl Deref for Foo {
23+
type Target = Bar;
24+
fn deref(&self) -> &Bar { &self.0 }
25+
}
26+
27+
impl Deref for Bar {
28+
type Target = Baz;
29+
fn deref(&self) -> &Baz { &self.0 }
30+
}
31+
32+
impl Bar {
33+
/// This appears under `Foo` methods
34+
pub fn bar(&self) {}
35+
}
36+
37+
impl Baz {
38+
/// This should also appear in `Foo` methods when recursing
39+
pub fn baz(&self) {}
40+
}

0 commit comments

Comments
 (0)