diff options
| author | J. Ryan Stinnett <jryans@gmail.com> | 2020-12-31 02:23:19 +0000 |
|---|---|---|
| committer | J. Ryan Stinnett <jryans@gmail.com> | 2021-01-08 07:15:03 +0000 |
| commit | fd0ad03902402d5bc23f9d04307388f3d6235961 (patch) | |
| tree | 5f7eed10fa3499f48b6c24d770620f1bd85437f1 | |
| parent | 61c8aae0a9222b7d2481704094f85a0d8f6b333e (diff) | |
| download | rust-fd0ad03902402d5bc23f9d04307388f3d6235961.tar.gz rust-fd0ad03902402d5bc23f9d04307388f3d6235961.zip | |
Recursively document methods via `Deref` traits
| -rw-r--r-- | src/librustdoc/html/render/mod.rs | 36 | ||||
| -rw-r--r-- | src/test/rustdoc-ui/deref-recursive-cycle.rs | 17 | ||||
| -rw-r--r-- | src/test/rustdoc/deref-recursive.rs | 40 |
3 files changed, 90 insertions, 3 deletions
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 8d304b45590..28e638a58a8 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -3548,9 +3548,6 @@ fn render_assoc_items( ); } } - if let AssocItemRender::DerefFor { .. } = what { - return; - } if !traits.is_empty() { let deref_impl = traits.iter().find(|t| t.inner_impl().trait_.def_id() == cache.deref_trait_did); @@ -3560,6 +3557,12 @@ fn render_assoc_items( render_deref_methods(w, cx, impl_, containing_item, has_deref_mut, cache); } + // If we were already one level into rendering deref methods, we don't want to render + // anything after recursing into any further deref methods above. + if let AssocItemRender::DerefFor { .. } = what { + return; + } + let (synthetic, concrete): (Vec<&&Impl>, Vec<&&Impl>) = traits.iter().partition(|t| t.inner_impl().synthetic); let (blanket_impl, concrete): (Vec<&&Impl>, _) = @@ -3631,6 +3634,13 @@ fn render_deref_methods( let what = AssocItemRender::DerefFor { trait_: deref_type, type_: real_target, deref_mut_: deref_mut }; if let Some(did) = target.def_id() { + if let Some(type_did) = impl_.inner_impl().for_.def_id() { + // `impl Deref<Target = S> for S` + if did == type_did { + // Avoid infinite cycles + return; + } + } render_assoc_items(w, cx, container_item, did, what, cache); } else { if let Some(prim) = target.primitive_type() { @@ -4417,6 +4427,26 @@ fn sidebar_deref_methods(impl_: &Impl, v: &Vec<Impl>) -> String { out.push_str(&format!("<div class=\"sidebar-links\">{}</div>", ret.join(""))); } } + + // Recurse into any further impls that might exist for `target` + if let Some(target_did) = target.def_id() { + if let Some(target_impls) = c.impls.get(&target_did) { + if let Some(target_deref_impl) = target_impls + .iter() + .filter(|i| i.inner_impl().trait_.is_some()) + .find(|i| i.inner_impl().trait_.def_id() == c.deref_trait_did) + { + if let Some(type_did) = impl_.inner_impl().for_.def_id() { + // `impl Deref<Target = S> for S` + if target_did == type_did { + // Avoid infinite cycles + return out; + } + } + out.push_str(&sidebar_deref_methods(target_deref_impl, target_impls)); + } + } + } } out diff --git a/src/test/rustdoc-ui/deref-recursive-cycle.rs b/src/test/rustdoc-ui/deref-recursive-cycle.rs new file mode 100644 index 00000000000..4cb518cbbbd --- /dev/null +++ b/src/test/rustdoc-ui/deref-recursive-cycle.rs @@ -0,0 +1,17 @@ +// check-pass +// #26207: Ensure `Deref` cycles are properly handled without errors. + +#[derive(Copy, Clone)] +struct S; + +impl std::ops::Deref for S { + type Target = S; + + fn deref(&self) -> &S { + self + } +} + +fn main() { + let s: S = *******S; +} diff --git a/src/test/rustdoc/deref-recursive.rs b/src/test/rustdoc/deref-recursive.rs new file mode 100644 index 00000000000..0d1e7fb1932 --- /dev/null +++ b/src/test/rustdoc/deref-recursive.rs @@ -0,0 +1,40 @@ +// #26207: Show all methods reachable via Deref impls, recursing through multiple dereferencing +// levels if needed. + +// @has 'foo/struct.Foo.html' +// @has '-' '//*[@id="deref-methods"]' 'Methods from Deref<Target = Bar>' +// @has '-' '//*[@class="impl-items"]//*[@id="method.bar"]' 'pub fn bar(&self)' +// @has '-' '//*[@id="deref-methods"]' 'Methods from Deref<Target = Baz>' +// @has '-' '//*[@class="impl-items"]//*[@id="method.baz"]' 'pub fn baz(&self)' +// @has '-' '//*[@class="sidebar-title"]' 'Methods from Deref<Target=Bar>' +// @has '-' '//*[@class="sidebar-links"]/a[@href="#method.bar"]' 'bar' +// @has '-' '//*[@class="sidebar-title"]' 'Methods from Deref<Target=Baz>' +// @has '-' '//*[@class="sidebar-links"]/a[@href="#method.baz"]' 'baz' + +#![crate_name = "foo"] + +use std::ops::Deref; + +pub struct Foo(Bar); +pub struct Bar(Baz); +pub struct Baz; + +impl Deref for Foo { + type Target = Bar; + fn deref(&self) -> &Bar { &self.0 } +} + +impl Deref for Bar { + type Target = Baz; + fn deref(&self) -> &Baz { &self.0 } +} + +impl Bar { + /// This appears under `Foo` methods + pub fn bar(&self) {} +} + +impl Baz { + /// This should also appear in `Foo` methods when recursing + pub fn baz(&self) {} +} |
