Skip to content

Commit 4b1d13d

Browse files
List matching impls on type aliases
1 parent a77659a commit 4b1d13d

File tree

2 files changed

+110
-28
lines changed

2 files changed

+110
-28
lines changed

src/librustdoc/html/render/mod.rs

+78-12
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,15 @@ use rustc_data_structures::captures::Captures;
5353
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
5454
use rustc_hir::def_id::{DefId, DefIdSet};
5555
use rustc_hir::Mutability;
56+
use rustc_infer::infer::TyCtxtInferExt;
57+
use rustc_infer::traits::{Obligation, ObligationCause};
5658
use rustc_middle::middle::stability;
57-
use rustc_middle::ty::TyCtxt;
59+
use rustc_middle::ty::{ParamEnv, TyCtxt};
5860
use rustc_span::{
5961
symbol::{sym, Symbol},
6062
BytePos, FileName, RealFileName,
6163
};
64+
use rustc_trait_selection::traits::ObligationCtxt;
6265
use serde::ser::{SerializeMap, SerializeSeq};
6366
use serde::{Serialize, Serializer};
6467

@@ -1112,28 +1115,76 @@ fn render_assoc_items<'a, 'cx: 'a>(
11121115
containing_item: &'a clean::Item,
11131116
it: DefId,
11141117
what: AssocItemRender<'a>,
1118+
aliased_type: Option<DefId>,
11151119
) -> impl fmt::Display + 'a + Captures<'cx> {
11161120
let mut derefs = DefIdSet::default();
11171121
derefs.insert(it);
11181122
display_fn(move |f| {
1119-
render_assoc_items_inner(f, cx, containing_item, it, what, &mut derefs);
1123+
render_assoc_items_inner(f, cx, containing_item, it, what, &mut derefs, aliased_type);
11201124
Ok(())
11211125
})
11221126
}
11231127

1128+
/// Check whether `impl_def_id` may apply to *some instantiation* of `item_def_id`.
1129+
fn is_valid_impl_for(tcx: TyCtxt<'_>, item_def_id: DefId, impl_def_id: DefId) -> bool {
1130+
let infcx = tcx.infer_ctxt().intercrate(true).build();
1131+
let ocx = ObligationCtxt::new(&infcx);
1132+
let param_env = ParamEnv::empty();
1133+
1134+
let alias_substs = infcx.fresh_substs_for_item(rustc_span::DUMMY_SP, item_def_id);
1135+
let alias_ty = tcx.type_of(item_def_id).subst(tcx, alias_substs);
1136+
let alias_bounds = tcx.predicates_of(item_def_id).instantiate(tcx, alias_substs);
1137+
1138+
let impl_substs = infcx.fresh_substs_for_item(rustc_span::DUMMY_SP, impl_def_id);
1139+
let impl_self_ty = tcx.type_of(impl_def_id).subst(tcx, impl_substs);
1140+
let impl_bounds = tcx.predicates_of(impl_def_id).instantiate(tcx, impl_substs);
1141+
1142+
if ocx.eq(&ObligationCause::dummy(), param_env, impl_self_ty, alias_ty).is_err() {
1143+
return false;
1144+
}
1145+
ocx.register_obligations(
1146+
alias_bounds
1147+
.iter()
1148+
.chain(impl_bounds)
1149+
.map(|(p, _)| Obligation::new(tcx, ObligationCause::dummy(), param_env, p)),
1150+
);
1151+
1152+
let errors = ocx.select_where_possible();
1153+
errors.is_empty()
1154+
}
1155+
1156+
// If `aliased_type` is `Some`, it means `it` is a type alias and `aliased_type` is the "actual"
1157+
// type aliased behind `it`. It is used to check whether or not the implementation of the aliased
1158+
// type can be displayed on the alias doc page.
11241159
fn render_assoc_items_inner(
11251160
mut w: &mut dyn fmt::Write,
11261161
cx: &mut Context<'_>,
11271162
containing_item: &clean::Item,
11281163
it: DefId,
11291164
what: AssocItemRender<'_>,
11301165
derefs: &mut DefIdSet,
1166+
aliased_type: Option<DefId>,
11311167
) {
11321168
info!("Documenting associated items of {:?}", containing_item.name);
11331169
let shared = Rc::clone(&cx.shared);
11341170
let cache = &shared.cache;
1135-
let Some(v) = cache.impls.get(&it) else { return };
1136-
let (non_trait, traits): (Vec<_>, _) = v.iter().partition(|i| i.inner_impl().trait_.is_none());
1171+
let empty = Vec::new();
1172+
let v = match cache.impls.get(&it) {
1173+
Some(v) => v,
1174+
None => &empty,
1175+
};
1176+
let v2 = match aliased_type {
1177+
Some(aliased_type) => cache.impls.get(&aliased_type).unwrap_or(&empty),
1178+
None => &empty,
1179+
};
1180+
if v.is_empty() && v2.is_empty() {
1181+
return;
1182+
}
1183+
let mut saw_impls = FxHashSet::default();
1184+
let (non_trait, traits): (Vec<_>, _) =
1185+
v.iter().chain(v2).partition(|i| i.inner_impl().trait_.is_none());
1186+
let tcx = cx.tcx();
1187+
let is_alias = aliased_type.is_some();
11371188
if !non_trait.is_empty() {
11381189
let mut tmp_buf = Buffer::html();
11391190
let (render_mode, id, class_html) = match what {
@@ -1165,6 +1216,12 @@ fn render_assoc_items_inner(
11651216
};
11661217
let mut impls_buf = Buffer::html();
11671218
for i in &non_trait {
1219+
if !saw_impls.insert(i.def_id()) {
1220+
continue;
1221+
}
1222+
if is_alias && !is_valid_impl_for(tcx, it, i.def_id()) {
1223+
continue;
1224+
}
11681225
render_impl(
11691226
&mut impls_buf,
11701227
cx,
@@ -1193,9 +1250,14 @@ fn render_assoc_items_inner(
11931250
if !traits.is_empty() {
11941251
let deref_impl =
11951252
traits.iter().find(|t| t.trait_did() == cx.tcx().lang_items().deref_trait());
1196-
if let Some(impl_) = deref_impl {
1253+
if let Some(impl_) = deref_impl &&
1254+
(!is_alias || is_valid_impl_for(tcx, it, impl_.def_id()))
1255+
{
11971256
let has_deref_mut =
1198-
traits.iter().any(|t| t.trait_did() == cx.tcx().lang_items().deref_mut_trait());
1257+
traits.iter().any(|t| {
1258+
t.trait_did() == cx.tcx().lang_items().deref_mut_trait() &&
1259+
(!is_alias || is_valid_impl_for(tcx, it, t.def_id()))
1260+
});
11991261
render_deref_methods(&mut w, cx, impl_, containing_item, has_deref_mut, derefs);
12001262
}
12011263

@@ -1205,10 +1267,14 @@ fn render_assoc_items_inner(
12051267
return;
12061268
}
12071269

1208-
let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
1209-
traits.into_iter().partition(|t| t.inner_impl().kind.is_auto());
1210-
let (blanket_impl, concrete): (Vec<&Impl>, _) =
1211-
concrete.into_iter().partition(|t| t.inner_impl().kind.is_blanket());
1270+
let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) = traits
1271+
.into_iter()
1272+
.filter(|t| saw_impls.insert(t.def_id()))
1273+
.partition(|t| t.inner_impl().kind.is_auto());
1274+
let (blanket_impl, concrete): (Vec<&Impl>, _) = concrete
1275+
.into_iter()
1276+
.filter(|t| !is_alias || is_valid_impl_for(tcx, it, t.def_id()))
1277+
.partition(|t| t.inner_impl().kind.is_blanket());
12121278

12131279
render_all_impls(w, cx, containing_item, &concrete, &synthetic, &blanket_impl);
12141280
}
@@ -1247,10 +1313,10 @@ fn render_deref_methods(
12471313
return;
12481314
}
12491315
}
1250-
render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs);
1316+
render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs, None);
12511317
} else if let Some(prim) = target.primitive_type() {
12521318
if let Some(&did) = cache.primitive_locations.get(&prim) {
1253-
render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs);
1319+
render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs, None);
12541320
}
12551321
}
12561322
}

src/librustdoc/html/render/print_item.rs

+32-16
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ fn item_template_render_assoc_items<'a: 'b, 'b, 'cx: 'a>(
260260
display_fn(move |f| {
261261
let (item, mut cx) = templ.item_and_mut_cx();
262262
let def_id = item.item_id.expect_def_id();
263-
let v = render_assoc_items(*cx, item, def_id, AssocItemRender::All);
263+
let v = render_assoc_items(*cx, item, def_id, AssocItemRender::All, None);
264264
write!(f, "{v}")
265265
})
266266
}
@@ -893,7 +893,11 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
893893
}
894894

895895
// If there are methods directly on this trait object, render them here.
896-
write!(w, "{}", render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All));
896+
write!(
897+
w,
898+
"{}",
899+
render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All, None)
900+
);
897901

898902
let cloned_shared = Rc::clone(&cx.shared);
899903
let cache = &cloned_shared.cache;
@@ -1125,8 +1129,12 @@ fn item_trait_alias(
11251129
// won't be visible anywhere in the docs. It would be nice to also show
11261130
// associated items from the aliased type (see discussion in #32077), but
11271131
// we need #14072 to make sense of the generics.
1128-
write!(w, "{}", render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All))
1129-
.unwrap();
1132+
write!(
1133+
w,
1134+
"{}",
1135+
render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All, None)
1136+
)
1137+
.unwrap();
11301138
}
11311139

11321140
fn item_opaque_ty(
@@ -1154,8 +1162,12 @@ fn item_opaque_ty(
11541162
// won't be visible anywhere in the docs. It would be nice to also show
11551163
// associated items from the aliased type (see discussion in #32077), but
11561164
// we need #14072 to make sense of the generics.
1157-
write!(w, "{}", render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All))
1158-
.unwrap();
1165+
write!(
1166+
w,
1167+
"{}",
1168+
render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All, None)
1169+
)
1170+
.unwrap();
11591171
}
11601172

11611173
fn item_typedef(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::Typedef) {
@@ -1179,11 +1191,11 @@ fn item_typedef(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clea
11791191
write!(w, "{}", document(cx, it, None, HeadingOffset::H2));
11801192

11811193
let def_id = it.item_id.expect_def_id();
1182-
// Render any items associated directly to this alias, as otherwise they
1183-
// won't be visible anywhere in the docs. It would be nice to also show
1184-
// associated items from the aliased type (see discussion in #32077), but
1185-
// we need #14072 to make sense of the generics.
1186-
write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All));
1194+
write!(
1195+
w,
1196+
"{}",
1197+
render_assoc_items(cx, it, def_id, AssocItemRender::All, t.type_.def_id(&cx.cache()))
1198+
);
11871199
write!(w, "{}", document_type_layout(cx, def_id));
11881200
}
11891201

@@ -1423,7 +1435,7 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::
14231435
write!(w, "</div>");
14241436
}
14251437
let def_id = it.item_id.expect_def_id();
1426-
write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All));
1438+
write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All, None));
14271439
write!(w, "{}", document_type_layout(cx, def_id));
14281440
}
14291441

@@ -1466,7 +1478,7 @@ fn item_primitive(w: &mut impl fmt::Write, cx: &mut Context<'_>, it: &clean::Ite
14661478
let def_id = it.item_id.expect_def_id();
14671479
write!(w, "{}", document(cx, it, None, HeadingOffset::H2)).unwrap();
14681480
if it.name.map(|n| n.as_str() != "reference").unwrap_or(false) {
1469-
write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All)).unwrap();
1481+
write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All, None)).unwrap();
14701482
} else {
14711483
// We handle the "reference" primitive type on its own because we only want to list
14721484
// implementations on generic types.
@@ -1571,7 +1583,7 @@ fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean
15711583
}
15721584
}
15731585
let def_id = it.item_id.expect_def_id();
1574-
write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All));
1586+
write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All, None));
15751587
write!(w, "{}", document_type_layout(cx, def_id));
15761588
}
15771589

@@ -1606,8 +1618,12 @@ fn item_foreign_type(w: &mut impl fmt::Write, cx: &mut Context<'_>, it: &clean::
16061618
});
16071619

16081620
write!(w, "{}", document(cx, it, None, HeadingOffset::H2)).unwrap();
1609-
write!(w, "{}", render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All))
1610-
.unwrap();
1621+
write!(
1622+
w,
1623+
"{}",
1624+
render_assoc_items(cx, it, it.item_id.expect_def_id(), AssocItemRender::All, None)
1625+
)
1626+
.unwrap();
16111627
}
16121628

16131629
fn item_keyword(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) {

0 commit comments

Comments
 (0)