Rollup merge of #112429 - GuillaumeGomez:ty-alias-impls, r=notriddle,lcnr
[rustdoc] List matching impls on type aliases Fixes #32077. Thanks a lot to ``@lcnr`` who helped me a lot with this fix! cc ``@notriddle`` r? ``@lcnr``
This commit is contained in:
commit
f83c8e4317
4 changed files with 178 additions and 37 deletions
|
@ -106,7 +106,7 @@ impl<T: ?Sized> *mut T {
|
||||||
/// with [`cast_mut`] on `*const T` and may have documentation value if used instead of implicit
|
/// with [`cast_mut`] on `*const T` and may have documentation value if used instead of implicit
|
||||||
/// coercion.
|
/// coercion.
|
||||||
///
|
///
|
||||||
/// [`cast_mut`]: #method.cast_mut
|
/// [`cast_mut`]: pointer::cast_mut
|
||||||
#[stable(feature = "ptr_const_cast", since = "1.65.0")]
|
#[stable(feature = "ptr_const_cast", since = "1.65.0")]
|
||||||
#[rustc_const_stable(feature = "ptr_const_cast", since = "1.65.0")]
|
#[rustc_const_stable(feature = "ptr_const_cast", since = "1.65.0")]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
@ -117,7 +117,7 @@ impl<T: ?Sized> *mut T {
|
||||||
/// Casts a pointer to its raw bits.
|
/// Casts a pointer to its raw bits.
|
||||||
///
|
///
|
||||||
/// This is equivalent to `as usize`, but is more specific to enhance readability.
|
/// 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
|
/// 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
|
/// pointers to numeric types but do very different things, so using this
|
||||||
|
@ -153,7 +153,7 @@ impl<T: ?Sized> *mut T {
|
||||||
/// Creates a pointer from its raw bits.
|
/// Creates a pointer from its raw bits.
|
||||||
///
|
///
|
||||||
/// This is equivalent to `as *mut T`, but is more specific to enhance readability.
|
/// 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
|
/// # Examples
|
||||||
///
|
///
|
||||||
|
@ -303,7 +303,7 @@ impl<T: ?Sized> *mut T {
|
||||||
///
|
///
|
||||||
/// For the mutable counterpart see [`as_mut`].
|
/// 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
|
/// [`as_mut`]: #method.as_mut
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
|
@ -369,7 +369,7 @@ impl<T: ?Sized> *mut T {
|
||||||
///
|
///
|
||||||
/// For the mutable counterpart see [`as_uninit_mut`].
|
/// 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
|
/// [`as_uninit_mut`]: #method.as_uninit_mut
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
|
@ -624,7 +624,7 @@ impl<T: ?Sized> *mut T {
|
||||||
/// For the shared counterpart see [`as_ref`].
|
/// For the shared counterpart see [`as_ref`].
|
||||||
///
|
///
|
||||||
/// [`as_uninit_mut`]: #method.as_uninit_mut
|
/// [`as_uninit_mut`]: #method.as_uninit_mut
|
||||||
/// [`as_ref`]: #method.as_ref-1
|
/// [`as_ref`]: pointer#method.as_ref-1
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
|
@ -689,7 +689,7 @@ impl<T: ?Sized> *mut T {
|
||||||
/// For the shared counterpart see [`as_uninit_ref`].
|
/// For the shared counterpart see [`as_uninit_ref`].
|
||||||
///
|
///
|
||||||
/// [`as_mut`]: #method.as_mut
|
/// [`as_mut`]: #method.as_mut
|
||||||
/// [`as_uninit_ref`]: #method.as_uninit_ref-1
|
/// [`as_uninit_ref`]: pointer#method.as_uninit_ref-1
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
|
@ -779,7 +779,7 @@ impl<T: ?Sized> *mut T {
|
||||||
///
|
///
|
||||||
/// This function is the inverse of [`offset`].
|
/// This function is the inverse of [`offset`].
|
||||||
///
|
///
|
||||||
/// [`offset`]: #method.offset-1
|
/// [`offset`]: pointer#method.offset-1
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
|
@ -2051,7 +2051,7 @@ impl<T> *mut [T] {
|
||||||
///
|
///
|
||||||
/// For the mutable counterpart see [`as_uninit_slice_mut`].
|
/// 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
|
/// [`as_uninit_slice_mut`]: #method.as_uninit_slice_mut
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
|
|
|
@ -53,12 +53,15 @@ use rustc_data_structures::captures::Captures;
|
||||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||||
use rustc_hir::def_id::{DefId, DefIdSet};
|
use rustc_hir::def_id::{DefId, DefIdSet};
|
||||||
use rustc_hir::Mutability;
|
use rustc_hir::Mutability;
|
||||||
|
use rustc_infer::infer::TyCtxtInferExt;
|
||||||
|
use rustc_infer::traits::{Obligation, ObligationCause};
|
||||||
use rustc_middle::middle::stability;
|
use rustc_middle::middle::stability;
|
||||||
use rustc_middle::ty::TyCtxt;
|
use rustc_middle::ty::{ParamEnv, TyCtxt};
|
||||||
use rustc_span::{
|
use rustc_span::{
|
||||||
symbol::{sym, Symbol},
|
symbol::{sym, Symbol},
|
||||||
BytePos, FileName, RealFileName,
|
BytePos, FileName, RealFileName,
|
||||||
};
|
};
|
||||||
|
use rustc_trait_selection::traits::ObligationCtxt;
|
||||||
use serde::ser::{SerializeMap, SerializeSeq};
|
use serde::ser::{SerializeMap, SerializeSeq};
|
||||||
use serde::{Serialize, Serializer};
|
use serde::{Serialize, Serializer};
|
||||||
|
|
||||||
|
@ -1112,15 +1115,47 @@ fn render_assoc_items<'a, 'cx: 'a>(
|
||||||
containing_item: &'a clean::Item,
|
containing_item: &'a clean::Item,
|
||||||
it: DefId,
|
it: DefId,
|
||||||
what: AssocItemRender<'a>,
|
what: AssocItemRender<'a>,
|
||||||
|
aliased_type: Option<DefId>,
|
||||||
) -> impl fmt::Display + 'a + Captures<'cx> {
|
) -> impl fmt::Display + 'a + Captures<'cx> {
|
||||||
let mut derefs = DefIdSet::default();
|
let mut derefs = DefIdSet::default();
|
||||||
derefs.insert(it);
|
derefs.insert(it);
|
||||||
display_fn(move |f| {
|
display_fn(move |f| {
|
||||||
render_assoc_items_inner(f, cx, containing_item, it, what, &mut derefs);
|
render_assoc_items_inner(f, cx, containing_item, it, what, &mut derefs, aliased_type);
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check whether `impl_def_id` may apply to *some instantiation* of `item_def_id`.
|
||||||
|
fn is_valid_impl_for(tcx: TyCtxt<'_>, item_def_id: DefId, impl_def_id: DefId) -> bool {
|
||||||
|
let infcx = tcx.infer_ctxt().intercrate(true).build();
|
||||||
|
let ocx = ObligationCtxt::new(&infcx);
|
||||||
|
let param_env = ParamEnv::empty();
|
||||||
|
|
||||||
|
let alias_substs = infcx.fresh_substs_for_item(rustc_span::DUMMY_SP, item_def_id);
|
||||||
|
let alias_ty = tcx.type_of(item_def_id).subst(tcx, alias_substs);
|
||||||
|
let alias_bounds = tcx.predicates_of(item_def_id).instantiate(tcx, alias_substs);
|
||||||
|
|
||||||
|
let impl_substs = infcx.fresh_substs_for_item(rustc_span::DUMMY_SP, impl_def_id);
|
||||||
|
let impl_self_ty = tcx.type_of(impl_def_id).subst(tcx, impl_substs);
|
||||||
|
let impl_bounds = tcx.predicates_of(impl_def_id).instantiate(tcx, impl_substs);
|
||||||
|
|
||||||
|
if ocx.eq(&ObligationCause::dummy(), param_env, impl_self_ty, alias_ty).is_err() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ocx.register_obligations(
|
||||||
|
alias_bounds
|
||||||
|
.iter()
|
||||||
|
.chain(impl_bounds)
|
||||||
|
.map(|(p, _)| Obligation::new(tcx, ObligationCause::dummy(), param_env, p)),
|
||||||
|
);
|
||||||
|
|
||||||
|
let errors = ocx.select_where_possible();
|
||||||
|
errors.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
// If `aliased_type` is `Some`, it means `it` is a type alias and `aliased_type` is the "actual"
|
||||||
|
// type aliased behind `it`. It is used to check whether or not the implementation of the aliased
|
||||||
|
// type can be displayed on the alias doc page.
|
||||||
fn render_assoc_items_inner(
|
fn render_assoc_items_inner(
|
||||||
mut w: &mut dyn fmt::Write,
|
mut w: &mut dyn fmt::Write,
|
||||||
cx: &mut Context<'_>,
|
cx: &mut Context<'_>,
|
||||||
|
@ -1128,12 +1163,28 @@ fn render_assoc_items_inner(
|
||||||
it: DefId,
|
it: DefId,
|
||||||
what: AssocItemRender<'_>,
|
what: AssocItemRender<'_>,
|
||||||
derefs: &mut DefIdSet,
|
derefs: &mut DefIdSet,
|
||||||
|
aliased_type: Option<DefId>,
|
||||||
) {
|
) {
|
||||||
info!("Documenting associated items of {:?}", containing_item.name);
|
info!("Documenting associated items of {:?}", containing_item.name);
|
||||||
let shared = Rc::clone(&cx.shared);
|
let shared = Rc::clone(&cx.shared);
|
||||||
let cache = &shared.cache;
|
let cache = &shared.cache;
|
||||||
let Some(v) = cache.impls.get(&it) else { return };
|
let empty = Vec::new();
|
||||||
let (non_trait, traits): (Vec<_>, _) = v.iter().partition(|i| i.inner_impl().trait_.is_none());
|
let v = match cache.impls.get(&it) {
|
||||||
|
Some(v) => v,
|
||||||
|
None => &empty,
|
||||||
|
};
|
||||||
|
let v2 = match aliased_type {
|
||||||
|
Some(aliased_type) => cache.impls.get(&aliased_type).unwrap_or(&empty),
|
||||||
|
None => &empty,
|
||||||
|
};
|
||||||
|
if v.is_empty() && v2.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let mut saw_impls = FxHashSet::default();
|
||||||
|
let (non_trait, traits): (Vec<_>, _) =
|
||||||
|
v.iter().chain(v2).partition(|i| i.inner_impl().trait_.is_none());
|
||||||
|
let tcx = cx.tcx();
|
||||||
|
let is_alias = aliased_type.is_some();
|
||||||
if !non_trait.is_empty() {
|
if !non_trait.is_empty() {
|
||||||
let mut tmp_buf = Buffer::html();
|
let mut tmp_buf = Buffer::html();
|
||||||
let (render_mode, id, class_html) = match what {
|
let (render_mode, id, class_html) = match what {
|
||||||
|
@ -1165,6 +1216,12 @@ fn render_assoc_items_inner(
|
||||||
};
|
};
|
||||||
let mut impls_buf = Buffer::html();
|
let mut impls_buf = Buffer::html();
|
||||||
for i in &non_trait {
|
for i in &non_trait {
|
||||||
|
if !saw_impls.insert(i.def_id()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if is_alias && !is_valid_impl_for(tcx, it, i.def_id()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
render_impl(
|
render_impl(
|
||||||
&mut impls_buf,
|
&mut impls_buf,
|
||||||
cx,
|
cx,
|
||||||
|
@ -1193,9 +1250,14 @@ fn render_assoc_items_inner(
|
||||||
if !traits.is_empty() {
|
if !traits.is_empty() {
|
||||||
let deref_impl =
|
let deref_impl =
|
||||||
traits.iter().find(|t| t.trait_did() == cx.tcx().lang_items().deref_trait());
|
traits.iter().find(|t| t.trait_did() == cx.tcx().lang_items().deref_trait());
|
||||||
if let Some(impl_) = deref_impl {
|
if let Some(impl_) = deref_impl &&
|
||||||
|
(!is_alias || is_valid_impl_for(tcx, it, impl_.def_id()))
|
||||||
|
{
|
||||||
let has_deref_mut =
|
let has_deref_mut =
|
||||||
traits.iter().any(|t| t.trait_did() == cx.tcx().lang_items().deref_mut_trait());
|
traits.iter().any(|t| {
|
||||||
|
t.trait_did() == cx.tcx().lang_items().deref_mut_trait() &&
|
||||||
|
(!is_alias || is_valid_impl_for(tcx, it, t.def_id()))
|
||||||
|
});
|
||||||
render_deref_methods(&mut w, cx, impl_, containing_item, has_deref_mut, derefs);
|
render_deref_methods(&mut w, cx, impl_, containing_item, has_deref_mut, derefs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1205,10 +1267,14 @@ fn render_assoc_items_inner(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
|
let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) = traits
|
||||||
traits.into_iter().partition(|t| t.inner_impl().kind.is_auto());
|
.into_iter()
|
||||||
let (blanket_impl, concrete): (Vec<&Impl>, _) =
|
.filter(|t| saw_impls.insert(t.def_id()))
|
||||||
concrete.into_iter().partition(|t| t.inner_impl().kind.is_blanket());
|
.partition(|t| t.inner_impl().kind.is_auto());
|
||||||
|
let (blanket_impl, concrete): (Vec<&Impl>, _) = concrete
|
||||||
|
.into_iter()
|
||||||
|
.filter(|t| !is_alias || is_valid_impl_for(tcx, it, t.def_id()))
|
||||||
|
.partition(|t| t.inner_impl().kind.is_blanket());
|
||||||
|
|
||||||
render_all_impls(w, cx, containing_item, &concrete, &synthetic, &blanket_impl);
|
render_all_impls(w, cx, containing_item, &concrete, &synthetic, &blanket_impl);
|
||||||
}
|
}
|
||||||
|
@ -1247,10 +1313,10 @@ fn render_deref_methods(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs);
|
render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs, None);
|
||||||
} else if let Some(prim) = target.primitive_type() {
|
} else if let Some(prim) = target.primitive_type() {
|
||||||
if let Some(&did) = cache.primitive_locations.get(&prim) {
|
if let Some(&did) = cache.primitive_locations.get(&prim) {
|
||||||
render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs);
|
render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs, None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -260,7 +260,7 @@ fn item_template_render_assoc_items<'a: 'b, 'b, 'cx: 'a>(
|
||||||
display_fn(move |f| {
|
display_fn(move |f| {
|
||||||
let (item, mut cx) = templ.item_and_mut_cx();
|
let (item, mut cx) = templ.item_and_mut_cx();
|
||||||
let def_id = item.item_id.expect_def_id();
|
let def_id = item.item_id.expect_def_id();
|
||||||
let v = render_assoc_items(*cx, item, def_id, AssocItemRender::All);
|
let v = render_assoc_items(*cx, item, def_id, AssocItemRender::All, None);
|
||||||
write!(f, "{v}")
|
write!(f, "{v}")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -893,7 +893,11 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there are methods directly on this trait object, render them here.
|
// If there are methods directly on this trait object, render them here.
|
||||||
write!(w, "{}", render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All));
|
write!(
|
||||||
|
w,
|
||||||
|
"{}",
|
||||||
|
render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All, None)
|
||||||
|
);
|
||||||
|
|
||||||
let cloned_shared = Rc::clone(&cx.shared);
|
let cloned_shared = Rc::clone(&cx.shared);
|
||||||
let cache = &cloned_shared.cache;
|
let cache = &cloned_shared.cache;
|
||||||
|
@ -1125,7 +1129,11 @@ fn item_trait_alias(
|
||||||
// won't be visible anywhere in the docs. It would be nice to also show
|
// won't be visible anywhere in the docs. It would be nice to also show
|
||||||
// associated items from the aliased type (see discussion in #32077), but
|
// associated items from the aliased type (see discussion in #32077), but
|
||||||
// we need #14072 to make sense of the generics.
|
// we need #14072 to make sense of the generics.
|
||||||
write!(w, "{}", render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All))
|
write!(
|
||||||
|
w,
|
||||||
|
"{}",
|
||||||
|
render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All, None)
|
||||||
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1154,7 +1162,11 @@ fn item_opaque_ty(
|
||||||
// won't be visible anywhere in the docs. It would be nice to also show
|
// won't be visible anywhere in the docs. It would be nice to also show
|
||||||
// associated items from the aliased type (see discussion in #32077), but
|
// associated items from the aliased type (see discussion in #32077), but
|
||||||
// we need #14072 to make sense of the generics.
|
// we need #14072 to make sense of the generics.
|
||||||
write!(w, "{}", render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All))
|
write!(
|
||||||
|
w,
|
||||||
|
"{}",
|
||||||
|
render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All, None)
|
||||||
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1179,11 +1191,11 @@ fn item_typedef(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clea
|
||||||
write!(w, "{}", document(cx, it, None, HeadingOffset::H2));
|
write!(w, "{}", document(cx, it, None, HeadingOffset::H2));
|
||||||
|
|
||||||
let def_id = it.item_id.expect_def_id();
|
let def_id = it.item_id.expect_def_id();
|
||||||
// Render any items associated directly to this alias, as otherwise they
|
write!(
|
||||||
// won't be visible anywhere in the docs. It would be nice to also show
|
w,
|
||||||
// associated items from the aliased type (see discussion in #32077), but
|
"{}",
|
||||||
// we need #14072 to make sense of the generics.
|
render_assoc_items(cx, it, def_id, AssocItemRender::All, t.type_.def_id(&cx.cache()))
|
||||||
write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All));
|
);
|
||||||
write!(w, "{}", document_type_layout(cx, def_id));
|
write!(w, "{}", document_type_layout(cx, def_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1423,7 +1435,7 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::
|
||||||
write!(w, "</div>");
|
write!(w, "</div>");
|
||||||
}
|
}
|
||||||
let def_id = it.item_id.expect_def_id();
|
let def_id = it.item_id.expect_def_id();
|
||||||
write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All));
|
write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All, None));
|
||||||
write!(w, "{}", document_type_layout(cx, def_id));
|
write!(w, "{}", document_type_layout(cx, def_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1466,7 +1478,7 @@ fn item_primitive(w: &mut impl fmt::Write, cx: &mut Context<'_>, it: &clean::Ite
|
||||||
let def_id = it.item_id.expect_def_id();
|
let def_id = it.item_id.expect_def_id();
|
||||||
write!(w, "{}", document(cx, it, None, HeadingOffset::H2)).unwrap();
|
write!(w, "{}", document(cx, it, None, HeadingOffset::H2)).unwrap();
|
||||||
if it.name.map(|n| n.as_str() != "reference").unwrap_or(false) {
|
if it.name.map(|n| n.as_str() != "reference").unwrap_or(false) {
|
||||||
write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All)).unwrap();
|
write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All, None)).unwrap();
|
||||||
} else {
|
} else {
|
||||||
// We handle the "reference" primitive type on its own because we only want to list
|
// We handle the "reference" primitive type on its own because we only want to list
|
||||||
// implementations on generic types.
|
// implementations on generic types.
|
||||||
|
@ -1571,7 +1583,7 @@ fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let def_id = it.item_id.expect_def_id();
|
let def_id = it.item_id.expect_def_id();
|
||||||
write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All));
|
write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All, None));
|
||||||
write!(w, "{}", document_type_layout(cx, def_id));
|
write!(w, "{}", document_type_layout(cx, def_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1606,7 +1618,11 @@ fn item_foreign_type(w: &mut impl fmt::Write, cx: &mut Context<'_>, it: &clean::
|
||||||
});
|
});
|
||||||
|
|
||||||
write!(w, "{}", document(cx, it, None, HeadingOffset::H2)).unwrap();
|
write!(w, "{}", document(cx, it, None, HeadingOffset::H2)).unwrap();
|
||||||
write!(w, "{}", render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All))
|
write!(
|
||||||
|
w,
|
||||||
|
"{}",
|
||||||
|
render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All, None)
|
||||||
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
59
tests/rustdoc/issue-32077-type-alias-impls.rs
Normal file
59
tests/rustdoc/issue-32077-type-alias-impls.rs
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
// Regression test for <https://github.com/rust-lang/rust/issues/32077>.
|
||||||
|
|
||||||
|
#![crate_name = "foo"]
|
||||||
|
|
||||||
|
pub struct GenericStruct<T>(T);
|
||||||
|
|
||||||
|
impl<T> GenericStruct<T> {
|
||||||
|
pub fn on_gen(arg: T) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GenericStruct<u32> {
|
||||||
|
pub fn on_u32(arg: u32) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Foo {}
|
||||||
|
pub trait Bar {}
|
||||||
|
|
||||||
|
impl<T> Foo for GenericStruct<T> {}
|
||||||
|
impl Bar for GenericStruct<u32> {}
|
||||||
|
|
||||||
|
// @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<T> GenericStruct<T>'
|
||||||
|
// @has - '//*[@id="method.on_gen"]/h4' 'pub fn on_gen(arg: T)'
|
||||||
|
// @has - '//*[@id="impl-Foo-for-GenericStruct%3CT%3E"]/h3' 'impl<T> Foo for GenericStruct<T>'
|
||||||
|
// This trait implementation doesn't match the type alias parameters so shouldn't appear in docs.
|
||||||
|
// @!has - '//h3' 'impl Bar for GenericStruct<u32> {}'
|
||||||
|
// Same goes for the `Deref` impl.
|
||||||
|
// @!has - '//h2' 'Methods from Deref<Target = u32>'
|
||||||
|
pub type TypedefStruct = GenericStruct<u8>;
|
||||||
|
|
||||||
|
impl TypedefStruct {
|
||||||
|
pub fn on_alias() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Deref for GenericStruct<u32> {
|
||||||
|
type Target = u32;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Wrap<T>(GenericStruct<T>);
|
||||||
|
|
||||||
|
// @has 'foo/type.Alias.html'
|
||||||
|
// @has - '//h2' 'Methods from Deref<Target = u32>'
|
||||||
|
// @has - '//*[@id="impl-Deref-for-Wrap%3CT%3E"]/h3' 'impl<T> Deref for Wrap<T>'
|
||||||
|
pub type Alias = Wrap<u32>;
|
||||||
|
|
||||||
|
impl<T> std::ops::Deref for Wrap<T> {
|
||||||
|
type Target = GenericStruct<T>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue