about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJacob Pratt <jacob@jhpratt.dev>2025-03-23 20:44:10 -0400
committerGitHub <noreply@github.com>2025-03-23 20:44:10 -0400
commitabc67849a8a5782c90b252274096115630bfecc3 (patch)
tree43e61f6f229d6e31d930b64d55e22395ddb1bc78
parent66f2a19676565f69eeccb174151e08889e916f1e (diff)
parentb46412f6d7fde0c24780b0808aa7aefbc18d9e1e (diff)
downloadrust-abc67849a8a5782c90b252274096115630bfecc3.tar.gz
rust-abc67849a8a5782c90b252274096115630bfecc3.zip
Rollup merge of #138574 - lolbinarycat:rustdoc-deref-24686-v2, r=GuillaumeGomez
rustdoc: be more strict about "Methods from Deref"

fixes #137083
fixes #24686

Currently done:
* [x] fix `render_assoc_items_inner
* [x] fix sidebar logic
* [x] port test from https://github.com/rust-lang/rust/pull/137564
* [x] add test for sidebar items

Note that this does not yet fix the sidebar logic.
-rw-r--r--src/librustdoc/clean/types.rs14
-rw-r--r--src/librustdoc/html/render/mod.rs16
-rw-r--r--src/librustdoc/html/render/sidebar.rs5
-rw-r--r--tests/rustdoc/deref/deref-methods-24686-target.rs27
4 files changed, 59 insertions, 3 deletions
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index c991d32ea22..5f80aded9d0 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -1552,6 +1552,10 @@ impl Type {
         matches!(self, Type::BorrowedRef { .. })
     }
 
+    fn is_type_alias(&self) -> bool {
+        matches!(self, Type::Path { path: Path { res: Res::Def(DefKind::TyAlias, _), .. } })
+    }
+
     /// Check if two types are "the same" for documentation purposes.
     ///
     /// This is different from `Eq`, because it knows that things like
@@ -1580,6 +1584,16 @@ impl Type {
         } else {
             (self, other)
         };
+
+        // FIXME: `Cache` does not have the data required to unwrap type aliases,
+        // so we just assume they are equal.
+        // This is only remotely acceptable because we were previously
+        // assuming all types were equal when used
+        // as a generic parameter of a type in `Deref::Target`.
+        if self_cleared.is_type_alias() || other_cleared.is_type_alias() {
+            return true;
+        }
+
         match (self_cleared, other_cleared) {
             // Recursive cases.
             (Type::Tuple(a), Type::Tuple(b)) => {
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 8dfde1679fe..2237e0f987b 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -89,7 +89,7 @@ pub(crate) fn ensure_trailing_slash(v: &str) -> impl fmt::Display {
 
 /// Specifies whether rendering directly implemented trait items or ones from a certain Deref
 /// impl.
-#[derive(Copy, Clone)]
+#[derive(Copy, Clone, Debug)]
 pub(crate) enum AssocItemRender<'a> {
     All,
     DerefFor { trait_: &'a clean::Path, type_: &'a clean::Type, deref_mut_: bool },
@@ -1296,7 +1296,8 @@ fn render_assoc_items_inner(
     info!("Documenting associated items of {:?}", containing_item.name);
     let cache = &cx.shared.cache;
     let Some(v) = cache.impls.get(&it) else { return };
-    let (non_trait, traits): (Vec<_>, _) = v.iter().partition(|i| i.inner_impl().trait_.is_none());
+    let (mut non_trait, traits): (Vec<_>, _) =
+        v.iter().partition(|i| i.inner_impl().trait_.is_none());
     if !non_trait.is_empty() {
         let mut close_tags = <Vec<&str>>::with_capacity(1);
         let mut tmp_buf = String::new();
@@ -1314,6 +1315,16 @@ fn render_assoc_items_inner(
             AssocItemRender::DerefFor { trait_, type_, deref_mut_ } => {
                 let id =
                     cx.derive_id(small_url_encode(format!("deref-methods-{:#}", type_.print(cx))));
+                // the `impls.get` above only looks at the outermost type,
+                // and the Deref impl may only be implemented for certain
+                // values of generic parameters.
+                // for example, if an item impls `Deref<[u8]>`,
+                // we should not show methods from `[MaybeUninit<u8>]`.
+                // this `retain` filters out any instances where
+                // the types do not line up perfectly.
+                non_trait.retain(|impl_| {
+                    type_.is_doc_subtype_of(&impl_.inner_impl().for_, &cx.shared.cache)
+                });
                 let derived_id = cx.derive_id(&id);
                 close_tags.push("</details>");
                 write_str(
@@ -1392,6 +1403,7 @@ fn render_assoc_items_inner(
     }
 }
 
+/// `derefs` is the set of all deref targets that have already been handled.
 fn render_deref_methods(
     mut w: impl Write,
     cx: &Context<'_>,
diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs
index 3130815af0b..9c78dcdc571 100644
--- a/src/librustdoc/html/render/sidebar.rs
+++ b/src/librustdoc/html/render/sidebar.rs
@@ -533,7 +533,10 @@ fn sidebar_deref_methods<'a>(
             debug!("found inner_impl: {impls:?}");
             let mut ret = impls
                 .iter()
-                .filter(|i| i.inner_impl().trait_.is_none())
+                .filter(|i| {
+                    i.inner_impl().trait_.is_none()
+                        && real_target.is_doc_subtype_of(&i.inner_impl().for_, &c)
+                })
                 .flat_map(|i| get_methods(i.inner_impl(), true, used_links, deref_mut, cx.tcx()))
                 .collect::<Vec<_>>();
             if !ret.is_empty() {
diff --git a/tests/rustdoc/deref/deref-methods-24686-target.rs b/tests/rustdoc/deref/deref-methods-24686-target.rs
new file mode 100644
index 00000000000..e019488ca80
--- /dev/null
+++ b/tests/rustdoc/deref/deref-methods-24686-target.rs
@@ -0,0 +1,27 @@
+#![crate_name = "foo"]
+
+// test for https://github.com/rust-lang/rust/issues/24686
+use std::ops::Deref;
+
+pub struct Foo<T>(T);
+impl Foo<i32> {
+    pub fn get_i32(&self) -> i32 { self.0 }
+}
+impl Foo<u32> {
+    pub fn get_u32(&self) -> u32 { self.0 }
+}
+
+// Note that the same href is used both on the method itself,
+// and on the sidebar items.
+//@ has foo/struct.Bar.html
+//@ has - '//a[@href="#method.get_i32"]' 'get_i32'
+//@ !has - '//a[@href="#method.get_u32"]' 'get_u32'
+//@ count - '//ul[@class="block deref-methods"]//a' 1
+//@ count - '//a[@href="#method.get_i32"]' 2
+pub struct Bar(Foo<i32>);
+impl Deref for Bar {
+    type Target = Foo<i32>;
+    fn deref(&self) -> &Foo<i32> {
+        &self.0
+    }
+}