about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJ. Ryan Stinnett <jryans@gmail.com>2020-12-31 02:23:19 +0000
committerJ. Ryan Stinnett <jryans@gmail.com>2021-01-08 07:15:03 +0000
commitfd0ad03902402d5bc23f9d04307388f3d6235961 (patch)
tree5f7eed10fa3499f48b6c24d770620f1bd85437f1
parent61c8aae0a9222b7d2481704094f85a0d8f6b333e (diff)
downloadrust-fd0ad03902402d5bc23f9d04307388f3d6235961.tar.gz
rust-fd0ad03902402d5bc23f9d04307388f3d6235961.zip
Recursively document methods via `Deref` traits
-rw-r--r--src/librustdoc/html/render/mod.rs36
-rw-r--r--src/test/rustdoc-ui/deref-recursive-cycle.rs17
-rw-r--r--src/test/rustdoc/deref-recursive.rs40
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) {}
+}