diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index 9dbb3f9d322c0..6d623b82c1cfe 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -109,7 +109,7 @@ impl *mut T { /// with [`cast_mut`] on `*const T` and may have documentation value if used instead of implicit /// coercion. /// - /// [`cast_mut`]: #method.cast_mut + /// [`cast_mut`]: pointer::cast_mut #[stable(feature = "ptr_const_cast", since = "1.65.0")] #[rustc_const_stable(feature = "ptr_const_cast", since = "1.65.0")] #[rustc_diagnostic_item = "ptr_cast_const"] @@ -121,7 +121,7 @@ impl *mut T { /// Casts a pointer to its raw bits. /// /// This is equivalent to `as usize`, but is more specific to enhance readability. - /// The inverse method is [`from_bits`](#method.from_bits-1). + /// The inverse method is [`from_bits`](pointer#method.from_bits-1). /// /// In particular, `*p as usize` and `p as usize` will both compile for /// pointers to numeric types but do very different things, so using this @@ -157,7 +157,7 @@ impl *mut T { /// Creates a pointer from its raw bits. /// /// This is equivalent to `as *mut T`, but is more specific to enhance readability. - /// The inverse method is [`to_bits`](#method.to_bits-1). + /// The inverse method is [`to_bits`](pointer#method.to_bits-1). /// /// # Examples /// @@ -307,7 +307,7 @@ impl *mut T { /// /// For the mutable counterpart see [`as_mut`]. /// - /// [`as_uninit_ref`]: #method.as_uninit_ref-1 + /// [`as_uninit_ref`]: pointer#method.as_uninit_ref-1 /// [`as_mut`]: #method.as_mut /// /// # Safety @@ -373,7 +373,7 @@ impl *mut T { /// /// For the mutable counterpart see [`as_uninit_mut`]. /// - /// [`as_ref`]: #method.as_ref-1 + /// [`as_ref`]: pointer#method.as_ref-1 /// [`as_uninit_mut`]: #method.as_uninit_mut /// /// # Safety @@ -628,7 +628,7 @@ impl *mut T { /// For the shared counterpart see [`as_ref`]. /// /// [`as_uninit_mut`]: #method.as_uninit_mut - /// [`as_ref`]: #method.as_ref-1 + /// [`as_ref`]: pointer#method.as_ref-1 /// /// # Safety /// @@ -693,7 +693,7 @@ impl *mut T { /// For the shared counterpart see [`as_uninit_ref`]. /// /// [`as_mut`]: #method.as_mut - /// [`as_uninit_ref`]: #method.as_uninit_ref-1 + /// [`as_uninit_ref`]: pointer#method.as_uninit_ref-1 /// /// # Safety /// @@ -783,7 +783,7 @@ impl *mut T { /// /// This function is the inverse of [`offset`]. /// - /// [`offset`]: #method.offset-1 + /// [`offset`]: pointer#method.offset-1 /// /// # Safety /// @@ -2064,7 +2064,7 @@ impl *mut [T] { /// /// For the mutable counterpart see [`as_uninit_slice_mut`]. /// - /// [`as_ref`]: #method.as_ref-1 + /// [`as_ref`]: pointer#method.as_ref-1 /// [`as_uninit_slice_mut`]: #method.as_uninit_slice_mut /// /// # Safety diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index aef8f1a74fb5a..a85e8356c2f21 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -54,6 +54,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::def_id::{DefId, DefIdSet}; use rustc_hir::Mutability; use rustc_middle::middle::stability; +use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; use rustc_middle::ty::TyCtxt; use rustc_span::{ symbol::{sym, Symbol}, @@ -62,6 +63,7 @@ use rustc_span::{ use serde::ser::{SerializeMap, SerializeSeq}; use serde::{Serialize, Serializer}; +use crate::clean::types::TypeAliasItem; use crate::clean::{self, ItemId, RenderedLink, SelfTy}; use crate::error::Error; use crate::formats::cache::Cache; @@ -1139,8 +1141,40 @@ fn render_assoc_items_inner( info!("Documenting associated items of {:?}", containing_item.name); let shared = Rc::clone(&cx.shared); let cache = &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 tcx = cx.tcx(); + let av = if let TypeAliasItem(ait) = &*containing_item.kind && + let aliased_clean_type = ait.item_type.as_ref().unwrap_or(&ait.type_) && + let Some(aliased_type_defid) = aliased_clean_type.def_id(cache) && + let Some(mut av) = cache.impls.get(&aliased_type_defid).cloned() && + let Some(alias_def_id) = containing_item.item_id.as_def_id() + { + // This branch of the compiler compares types structually, but does + // not check trait bounds. That's probably fine, since type aliases + // don't normally constrain on them anyway. + // https://github.com/rust-lang/rust/issues/21903 + // + // FIXME(lazy_type_alias): Once the feature is complete or stable, rewrite this to use type unification. + // Be aware of `tests/rustdoc/issue-112515-impl-ty-alias.rs` which might regress. + let aliased_ty = tcx.type_of(alias_def_id).skip_binder(); + let reject_cx = DeepRejectCtxt { + treat_obligation_params: TreatParams::AsCandidateKey, + }; + av.retain(|impl_| { + if let Some(impl_def_id) = impl_.impl_item.item_id.as_def_id() { + reject_cx.types_may_unify(aliased_ty, tcx.type_of(impl_def_id).skip_binder()) + } else { + false + } + }); + av + } else { + Vec::new() + }; + let blank = Vec::new(); + let v = cache.impls.get(&it).unwrap_or(&blank); + let (non_trait, traits): (Vec<_>, _) = + v.iter().chain(&av[..]).partition(|i| i.inner_impl().trait_.is_none()); + let mut saw_impls = FxHashSet::default(); if !non_trait.is_empty() { let mut tmp_buf = Buffer::html(); let (render_mode, id, class_html) = match what { @@ -1169,6 +1203,9 @@ fn render_assoc_items_inner( }; let mut impls_buf = Buffer::html(); for i in &non_trait { + if !saw_impls.insert(i.def_id()) { + continue; + } render_impl( &mut impls_buf, cx, @@ -1214,8 +1251,10 @@ fn render_assoc_items_inner( let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) = traits.into_iter().partition(|t| t.inner_impl().kind.is_auto()); - let (blanket_impl, concrete): (Vec<&Impl>, _) = - concrete.into_iter().partition(|t| t.inner_impl().kind.is_blanket()); + let (blanket_impl, concrete): (Vec<&Impl>, _) = concrete + .into_iter() + .filter(|t| saw_impls.insert(t.def_id())) + .partition(|t| t.inner_impl().kind.is_blanket()); render_all_impls(w, cx, containing_item, &concrete, &synthetic, &blanket_impl); } diff --git a/tests/rustdoc/issue-32077-type-alias-impls.rs b/tests/rustdoc/issue-32077-type-alias-impls.rs new file mode 100644 index 0000000000000..555d0579bee79 --- /dev/null +++ b/tests/rustdoc/issue-32077-type-alias-impls.rs @@ -0,0 +1,59 @@ +// Regression test for . + +#![crate_name = "foo"] + +pub struct GenericStruct(T); + +impl GenericStruct { + pub fn on_gen(arg: T) {} +} + +impl GenericStruct { + pub fn on_u32(arg: u32) {} +} + +pub trait Foo {} +pub trait Bar {} + +impl Foo for GenericStruct {} +impl Bar for GenericStruct {} + +// @has 'foo/type.TypedefStruct.html' +// We check that we have the implementation of the type alias itself. +// @has - '//*[@id="impl-TypedefStruct"]/h3' 'impl TypedefStruct' +// @has - '//*[@id="method.on_alias"]/h4' 'pub fn on_alias()' +// @has - '//*[@id="impl-GenericStruct%3CT%3E"]/h3' 'impl GenericStruct' +// @has - '//*[@id="method.on_gen"]/h4' 'pub fn on_gen(arg: T)' +// @has - '//*[@id="impl-Foo-for-GenericStruct%3CT%3E"]/h3' 'impl Foo for GenericStruct' +// This trait implementation doesn't match the type alias parameters so shouldn't appear in docs. +// @!has - '//h3' 'impl Bar for GenericStruct {}' +// Same goes for the `Deref` impl. +// @!has - '//h2' 'Methods from Deref' +pub type TypedefStruct = GenericStruct; + +impl TypedefStruct { + pub fn on_alias() {} +} + +impl std::ops::Deref for GenericStruct { + type Target = u32; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +pub struct Wrap(GenericStruct); + +// @has 'foo/type.Alias.html' +// @has - '//h2' 'Methods from Deref' +// @has - '//*[@id="impl-Deref-for-Wrap%3CT%3E"]/h3' 'impl Deref for Wrap' +pub type Alias = Wrap; + +impl std::ops::Deref for Wrap { + type Target = GenericStruct; + + fn deref(&self) -> &Self::Target { + &self.0 + } +}