Skip to content

Commit 6d76841

Browse files
committed
Auto merge of #94009 - compiler-errors:gat-rustdoc, r=GuillaumeGomez
Support GATs in Rustdoc Implements: 1. Rendering GATs in trait definitions and impl blocks 2. Rendering GATs in types (e.g. in the return type of a function) Fixes #92341 This is my first rustdoc PR, so I have absolutely no idea how to produce tests for this. Advice from the rustdoc team would be wonderful! I tested locally and things looked correct: ![image](https://user-images.githubusercontent.com/3674314/153988325-9732cbf3-0645-4e1a-9e64-ddfd93877b55.png)
2 parents 40d3040 + 0e57a16 commit 6d76841

File tree

15 files changed

+363
-176
lines changed

15 files changed

+363
-176
lines changed

src/librustdoc/clean/auto_trait.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -546,11 +546,11 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
546546
}
547547
WherePredicate::EqPredicate { lhs, rhs } => {
548548
match lhs {
549-
Type::QPath { name: left_name, ref self_type, ref trait_, .. } => {
549+
Type::QPath { ref assoc, ref self_type, ref trait_, .. } => {
550550
let ty = &*self_type;
551551
let mut new_trait = trait_.clone();
552552

553-
if self.is_fn_trait(trait_) && left_name == sym::Output {
553+
if self.is_fn_trait(trait_) && assoc.name == sym::Output {
554554
ty_to_fn
555555
.entry(*ty.clone())
556556
.and_modify(|e| {
@@ -571,7 +571,7 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
571571
// to 'T: Iterator<Item=u8>'
572572
GenericArgs::AngleBracketed { ref mut bindings, .. } => {
573573
bindings.push(TypeBinding {
574-
name: left_name,
574+
assoc: *assoc.clone(),
575575
kind: TypeBindingKind::Equality { term: rhs },
576576
});
577577
}

src/librustdoc/clean/inline.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -636,7 +636,7 @@ fn filter_non_trait_generics(trait_did: DefId, mut g: clean::Generics) -> clean:
636636

637637
g.where_predicates.retain(|pred| match pred {
638638
clean::WherePredicate::BoundPredicate {
639-
ty: clean::QPath { self_type: box clean::Generic(ref s), trait_, name: _, .. },
639+
ty: clean::QPath { self_type: box clean::Generic(ref s), trait_, .. },
640640
bounds,
641641
..
642642
} => !(bounds.is_empty() || *s == kw::SelfUpper && trait_.def_id() == trait_did),

src/librustdoc/clean/mod.rs

+117-44
Original file line numberDiff line numberDiff line change
@@ -388,14 +388,35 @@ impl<'tcx> Clean<Type> for ty::ProjectionTy<'tcx> {
388388
let trait_ = lifted.trait_ref(cx.tcx).clean(cx);
389389
let self_type = self.self_ty().clean(cx);
390390
Type::QPath {
391-
name: cx.tcx.associated_item(self.item_def_id).name,
391+
assoc: Box::new(projection_to_path_segment(*self, cx)),
392392
self_def_id: self_type.def_id(&cx.cache),
393393
self_type: box self_type,
394394
trait_,
395395
}
396396
}
397397
}
398398

399+
fn projection_to_path_segment(ty: ty::ProjectionTy<'_>, cx: &mut DocContext<'_>) -> PathSegment {
400+
let item = cx.tcx.associated_item(ty.item_def_id);
401+
let generics = cx.tcx.generics_of(ty.item_def_id);
402+
PathSegment {
403+
name: item.name,
404+
args: GenericArgs::AngleBracketed {
405+
args: ty.substs[generics.parent_count..]
406+
.iter()
407+
.map(|ty| match ty.unpack() {
408+
ty::subst::GenericArgKind::Lifetime(lt) => {
409+
GenericArg::Lifetime(lt.clean(cx).unwrap())
410+
}
411+
ty::subst::GenericArgKind::Type(ty) => GenericArg::Type(ty.clean(cx)),
412+
ty::subst::GenericArgKind::Const(c) => GenericArg::Const(Box::new(c.clean(cx))),
413+
})
414+
.collect(),
415+
bindings: Default::default(),
416+
},
417+
}
418+
}
419+
399420
impl Clean<GenericParamDef> for ty::GenericParamDef {
400421
fn clean(&self, cx: &mut DocContext<'_>) -> GenericParamDef {
401422
let (name, kind) = match self.kind {
@@ -601,8 +622,8 @@ fn clean_ty_generics(
601622
})
602623
.collect::<Vec<GenericParamDef>>();
603624

604-
// param index -> [(DefId of trait, associated type name, type)]
605-
let mut impl_trait_proj = FxHashMap::<u32, Vec<(DefId, Symbol, Ty<'_>)>>::default();
625+
// param index -> [(DefId of trait, associated type name and generics, type)]
626+
let mut impl_trait_proj = FxHashMap::<u32, Vec<(DefId, PathSegment, Ty<'_>)>>::default();
606627

607628
let where_predicates = preds
608629
.predicates
@@ -648,8 +669,9 @@ fn clean_ty_generics(
648669

649670
let proj = projection
650671
.map(|p| (p.skip_binder().projection_ty.clean(cx), p.skip_binder().term));
651-
if let Some(((_, trait_did, name), rhs)) =
652-
proj.as_ref().and_then(|(lhs, rhs)| Some((lhs.projection()?, rhs)))
672+
if let Some(((_, trait_did, name), rhs)) = proj
673+
.as_ref()
674+
.and_then(|(lhs, rhs): &(Type, _)| Some((lhs.projection()?, rhs)))
653675
{
654676
// FIXME(...): Remove this unwrap()
655677
impl_trait_proj.entry(param_idx).or_default().push((
@@ -992,9 +1014,10 @@ impl Clean<Item> for hir::TraitItem<'_> {
9921014
TyMethodItem(t)
9931015
}
9941016
hir::TraitItemKind::Type(bounds, ref default) => {
1017+
let generics = enter_impl_trait(cx, |cx| self.generics.clean(cx));
9951018
let bounds = bounds.iter().filter_map(|x| x.clean(cx)).collect();
9961019
let default = default.map(|t| t.clean(cx));
997-
AssocTypeItem(bounds, default)
1020+
AssocTypeItem(Box::new(generics), bounds, default)
9981021
}
9991022
};
10001023
let what_rustc_thinks =
@@ -1026,15 +1049,9 @@ impl Clean<Item> for hir::ImplItem<'_> {
10261049
}
10271050
hir::ImplItemKind::TyAlias(ref hir_ty) => {
10281051
let type_ = hir_ty.clean(cx);
1052+
let generics = self.generics.clean(cx);
10291053
let item_type = hir_ty_to_ty(cx.tcx, hir_ty).clean(cx);
1030-
TypedefItem(
1031-
Typedef {
1032-
type_,
1033-
generics: Generics::default(),
1034-
item_type: Some(item_type),
1035-
},
1036-
true,
1037-
)
1054+
TypedefItem(Typedef { type_, generics, item_type: Some(item_type) }, true)
10381055
}
10391056
};
10401057

@@ -1140,35 +1157,79 @@ impl Clean<Item> for ty::AssocItem {
11401157
ty::AssocKind::Type => {
11411158
let my_name = self.name;
11421159

1160+
fn param_eq_arg(param: &GenericParamDef, arg: &GenericArg) -> bool {
1161+
match (&param.kind, arg) {
1162+
(GenericParamDefKind::Type { .. }, GenericArg::Type(Type::Generic(ty)))
1163+
if *ty == param.name =>
1164+
{
1165+
true
1166+
}
1167+
(
1168+
GenericParamDefKind::Lifetime { .. },
1169+
GenericArg::Lifetime(Lifetime(lt)),
1170+
) if *lt == param.name => true,
1171+
(GenericParamDefKind::Const { .. }, GenericArg::Const(c)) => {
1172+
match &c.kind {
1173+
ConstantKind::TyConst { expr } => expr == param.name.as_str(),
1174+
_ => false,
1175+
}
1176+
}
1177+
_ => false,
1178+
}
1179+
}
1180+
11431181
if let ty::TraitContainer(_) = self.container {
11441182
let bounds = tcx.explicit_item_bounds(self.def_id);
11451183
let predicates = ty::GenericPredicates { parent: None, predicates: bounds };
1146-
let generics = clean_ty_generics(cx, tcx.generics_of(self.def_id), predicates);
1184+
let mut generics =
1185+
clean_ty_generics(cx, tcx.generics_of(self.def_id), predicates);
1186+
// Filter out the bounds that are (likely?) directly attached to the associated type,
1187+
// as opposed to being located in the where clause.
11471188
let mut bounds = generics
11481189
.where_predicates
1149-
.iter()
1150-
.filter_map(|pred| {
1151-
let (name, self_type, trait_, bounds) = match *pred {
1152-
WherePredicate::BoundPredicate {
1153-
ty: QPath { ref name, ref self_type, ref trait_, .. },
1154-
ref bounds,
1155-
..
1156-
} => (name, self_type, trait_, bounds),
1157-
_ => return None,
1158-
};
1159-
if *name != my_name {
1160-
return None;
1161-
}
1162-
if trait_.def_id() != self.container.id() {
1163-
return None;
1190+
.drain_filter(|pred| match *pred {
1191+
WherePredicate::BoundPredicate {
1192+
ty: QPath { ref assoc, ref self_type, ref trait_, .. },
1193+
..
1194+
} => {
1195+
if assoc.name != my_name {
1196+
return false;
1197+
}
1198+
if trait_.def_id() != self.container.id() {
1199+
return false;
1200+
}
1201+
match **self_type {
1202+
Generic(ref s) if *s == kw::SelfUpper => {}
1203+
_ => return false,
1204+
}
1205+
match &assoc.args {
1206+
GenericArgs::AngleBracketed { args, bindings } => {
1207+
if !bindings.is_empty()
1208+
|| generics
1209+
.params
1210+
.iter()
1211+
.zip(args)
1212+
.any(|(param, arg)| !param_eq_arg(param, arg))
1213+
{
1214+
return false;
1215+
}
1216+
}
1217+
GenericArgs::Parenthesized { .. } => {
1218+
// The only time this happens is if we're inside the rustdoc for Fn(),
1219+
// which only has one associated type, which is not a GAT, so whatever.
1220+
}
1221+
}
1222+
true
11641223
}
1165-
match **self_type {
1166-
Generic(ref s) if *s == kw::SelfUpper => {}
1167-
_ => return None,
1224+
_ => false,
1225+
})
1226+
.flat_map(|pred| {
1227+
if let WherePredicate::BoundPredicate { bounds, .. } = pred {
1228+
bounds
1229+
} else {
1230+
unreachable!()
11681231
}
1169-
Some(bounds)
11701232
})
1171-
.flat_map(|i| i.iter().cloned())
11721233
.collect::<Vec<_>>();
11731234
// Our Sized/?Sized bound didn't get handled when creating the generics
11741235
// because we didn't actually get our whole set of bounds until just now
@@ -1188,7 +1249,7 @@ impl Clean<Item> for ty::AssocItem {
11881249
None
11891250
};
11901251

1191-
AssocTypeItem(bounds, ty.map(|t| t.clean(cx)))
1252+
AssocTypeItem(Box::new(generics), bounds, ty.map(|t| t.clean(cx)))
11921253
} else {
11931254
// FIXME: when could this happen? Associated items in inherent impls?
11941255
let type_ = tcx.type_of(self.def_id).clean(cx);
@@ -1259,7 +1320,7 @@ fn clean_qpath(hir_ty: &hir::Ty<'_>, cx: &mut DocContext<'_>) -> Type {
12591320
};
12601321
register_res(cx, trait_.res);
12611322
Type::QPath {
1262-
name: p.segments.last().expect("segments were empty").ident.name,
1323+
assoc: Box::new(p.segments.last().expect("segments were empty").clean(cx)),
12631324
self_def_id: Some(DefId::local(qself.hir_id.owner.local_def_index)),
12641325
self_type: box qself.clean(cx),
12651326
trait_,
@@ -1276,7 +1337,7 @@ fn clean_qpath(hir_ty: &hir::Ty<'_>, cx: &mut DocContext<'_>) -> Type {
12761337
let trait_ = hir::Path { span, res, segments: &[] }.clean(cx);
12771338
register_res(cx, trait_.res);
12781339
Type::QPath {
1279-
name: segment.ident.name,
1340+
assoc: Box::new(segment.clean(cx)),
12801341
self_def_id: res.opt_def_id(),
12811342
self_type: box qself.clean(cx),
12821343
trait_,
@@ -1548,7 +1609,16 @@ impl<'tcx> Clean<Type> for Ty<'tcx> {
15481609
let mut bindings = vec![];
15491610
for pb in obj.projection_bounds() {
15501611
bindings.push(TypeBinding {
1551-
name: cx.tcx.associated_item(pb.item_def_id()).name,
1612+
assoc: projection_to_path_segment(
1613+
pb.skip_binder()
1614+
.lift_to_tcx(cx.tcx)
1615+
.unwrap()
1616+
// HACK(compiler-errors): Doesn't actually matter what self
1617+
// type we put here, because we're only using the GAT's substs.
1618+
.with_self_ty(cx.tcx, cx.tcx.types.self_param)
1619+
.projection_ty,
1620+
cx,
1621+
),
15521622
kind: TypeBindingKind::Equality { term: pb.skip_binder().term.clean(cx) },
15531623
});
15541624
}
@@ -1614,10 +1684,10 @@ impl<'tcx> Clean<Type> for Ty<'tcx> {
16141684
== trait_ref.skip_binder()
16151685
{
16161686
Some(TypeBinding {
1617-
name: cx
1618-
.tcx
1619-
.associated_item(proj.projection_ty.item_def_id)
1620-
.name,
1687+
assoc: projection_to_path_segment(
1688+
proj.projection_ty,
1689+
cx,
1690+
),
16211691
kind: TypeBindingKind::Equality {
16221692
term: proj.term.clean(cx),
16231693
},
@@ -2160,7 +2230,10 @@ fn clean_maybe_renamed_foreign_item(
21602230

21612231
impl Clean<TypeBinding> for hir::TypeBinding<'_> {
21622232
fn clean(&self, cx: &mut DocContext<'_>) -> TypeBinding {
2163-
TypeBinding { name: self.ident.name, kind: self.kind.clean(cx) }
2233+
TypeBinding {
2234+
assoc: PathSegment { name: self.ident.name, args: self.gen_args.clean(cx) },
2235+
kind: self.kind.clean(cx),
2236+
}
21642237
}
21652238
}
21662239

src/librustdoc/clean/simplify.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ crate fn merge_bounds(
8989
cx: &clean::DocContext<'_>,
9090
bounds: &mut Vec<clean::GenericBound>,
9191
trait_did: DefId,
92-
name: Symbol,
92+
assoc: clean::PathSegment,
9393
rhs: &clean::Term,
9494
) -> bool {
9595
!bounds.iter_mut().any(|b| {
@@ -107,7 +107,7 @@ crate fn merge_bounds(
107107
match last.args {
108108
PP::AngleBracketed { ref mut bindings, .. } => {
109109
bindings.push(clean::TypeBinding {
110-
name,
110+
assoc: assoc.clone(),
111111
kind: clean::TypeBindingKind::Equality { term: rhs.clone() },
112112
});
113113
}

src/librustdoc/clean/types.rs

+10-10
Original file line numberDiff line numberDiff line change
@@ -683,7 +683,7 @@ crate enum ItemKind {
683683
///
684684
/// The bounds may be non-empty if there is a `where` clause.
685685
/// The `Option<Type>` is the default concrete type (e.g. `trait Trait { type Target = usize; }`)
686-
AssocTypeItem(Vec<GenericBound>, Option<Type>),
686+
AssocTypeItem(Box<Generics>, Vec<GenericBound>, Option<Type>),
687687
/// An item that has been stripped by a rustdoc pass
688688
StrippedItem(Box<ItemKind>),
689689
KeywordItem(Symbol),
@@ -721,7 +721,7 @@ impl ItemKind {
721721
| ProcMacroItem(_)
722722
| PrimitiveItem(_)
723723
| AssocConstItem(_, _)
724-
| AssocTypeItem(_, _)
724+
| AssocTypeItem(..)
725725
| StrippedItem(_)
726726
| KeywordItem(_) => [].iter(),
727727
}
@@ -1397,7 +1397,7 @@ crate enum Type {
13971397

13981398
/// A qualified path to an associated item: `<Type as Trait>::Name`
13991399
QPath {
1400-
name: Symbol,
1400+
assoc: Box<PathSegment>,
14011401
self_type: Box<Type>,
14021402
/// FIXME: This is a hack that should be removed; see [this discussion][1].
14031403
///
@@ -1415,7 +1415,7 @@ crate enum Type {
14151415

14161416
// `Type` is used a lot. Make sure it doesn't unintentionally get bigger.
14171417
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
1418-
rustc_data_structures::static_assert_size!(Type, 72);
1418+
rustc_data_structures::static_assert_size!(Type, 80);
14191419

14201420
impl Type {
14211421
/// When comparing types for equality, it can help to ignore `&` wrapping.
@@ -1505,12 +1505,12 @@ impl Type {
15051505
self.primitive_type().is_some()
15061506
}
15071507

1508-
crate fn projection(&self) -> Option<(&Type, DefId, Symbol)> {
1509-
let (self_, trait_, name) = match self {
1510-
QPath { self_type, trait_, name, .. } => (self_type, trait_, name),
1508+
crate fn projection(&self) -> Option<(&Type, DefId, PathSegment)> {
1509+
let (self_, trait_, assoc) = match self {
1510+
QPath { self_type, trait_, assoc, .. } => (self_type, trait_, assoc),
15111511
_ => return None,
15121512
};
1513-
Some((&self_, trait_.def_id(), *name))
1513+
Some((&self_, trait_.def_id(), *assoc.clone()))
15141514
}
15151515

15161516
fn inner_def_id(&self, cache: Option<&Cache>) -> Option<DefId> {
@@ -2018,7 +2018,7 @@ crate enum GenericArg {
20182018
// `GenericArg` can occur many times in a single `Path`, so make sure it
20192019
// doesn't increase in size unexpectedly.
20202020
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
2021-
rustc_data_structures::static_assert_size!(GenericArg, 80);
2021+
rustc_data_structures::static_assert_size!(GenericArg, 88);
20222022

20232023
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
20242024
crate enum GenericArgs {
@@ -2256,7 +2256,7 @@ crate struct ProcMacro {
22562256
/// `A: Send + Sync` in `Foo<A: Send + Sync>`).
22572257
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
22582258
crate struct TypeBinding {
2259-
crate name: Symbol,
2259+
crate assoc: PathSegment,
22602260
crate kind: TypeBindingKind,
22612261
}
22622262

src/librustdoc/fold.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ crate trait DocFolder: Sized {
8686
| ProcMacroItem(_)
8787
| PrimitiveItem(_)
8888
| AssocConstItem(_, _)
89-
| AssocTypeItem(_, _)
89+
| AssocTypeItem(..)
9090
| KeywordItem(_) => kind,
9191
}
9292
}

0 commit comments

Comments
 (0)