Skip to content

Commit aefc0a2

Browse files
make generic projection types print correctly
1 parent ca6e06e commit aefc0a2

File tree

8 files changed

+107
-41
lines changed

8 files changed

+107
-41
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

+71-16
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((
@@ -985,7 +1007,7 @@ impl Clean<Item> for hir::TraitItem<'_> {
9851007
TyMethodItem(t)
9861008
}
9871009
hir::TraitItemKind::Type(bounds, ref default) => {
988-
let generics = self.generics.clean(cx);
1010+
let generics = enter_impl_trait(cx, |cx| self.generics.clean(cx));
9891011
let bounds = bounds.iter().filter_map(|x| x.clean(cx)).collect();
9901012
let default = default.map(|t| t.clean(cx));
9911013
AssocTypeItem(Box::new(generics), bounds, default)
@@ -1136,6 +1158,27 @@ impl Clean<Item> for ty::AssocItem {
11361158
ty::AssocKind::Type => {
11371159
let my_name = self.name;
11381160

1161+
fn param_eq_arg(param: &GenericParamDef, arg: &GenericArg) -> bool {
1162+
match (&param.kind, arg) {
1163+
(GenericParamDefKind::Type { .. }, GenericArg::Type(Type::Generic(ty)))
1164+
if *ty == param.name =>
1165+
{
1166+
true
1167+
}
1168+
(
1169+
GenericParamDefKind::Lifetime { .. },
1170+
GenericArg::Lifetime(Lifetime(lt)),
1171+
) if *lt == param.name => true,
1172+
(GenericParamDefKind::Const { .. }, GenericArg::Const(c)) => {
1173+
match &c.kind {
1174+
ConstantKind::TyConst { expr } => expr == param.name.as_str(),
1175+
_ => false,
1176+
}
1177+
}
1178+
_ => false,
1179+
}
1180+
}
1181+
11391182
if let ty::TraitContainer(_) = self.container {
11401183
let bounds = tcx.explicit_item_bounds(self.def_id);
11411184
let predicates = ty::GenericPredicates { parent: None, predicates: bounds };
@@ -1147,10 +1190,10 @@ impl Clean<Item> for ty::AssocItem {
11471190
.where_predicates
11481191
.drain_filter(|pred| match *pred {
11491192
WherePredicate::BoundPredicate {
1150-
ty: QPath { name, ref self_type, ref trait_, .. },
1193+
ty: QPath { ref assoc, ref self_type, ref trait_, .. },
11511194
..
11521195
} => {
1153-
if name != my_name {
1196+
if assoc.name != my_name {
11541197
return false;
11551198
}
11561199
if trait_.def_id() != self.container.id() {
@@ -1267,7 +1310,7 @@ fn clean_qpath(hir_ty: &hir::Ty<'_>, cx: &mut DocContext<'_>) -> Type {
12671310
};
12681311
register_res(cx, trait_.res);
12691312
Type::QPath {
1270-
name: p.segments.last().expect("segments were empty").ident.name,
1313+
assoc: Box::new(p.segments.last().expect("segments were empty").clean(cx)),
12711314
self_def_id: Some(DefId::local(qself.hir_id.owner.local_def_index)),
12721315
self_type: box qself.clean(cx),
12731316
trait_,
@@ -1284,7 +1327,7 @@ fn clean_qpath(hir_ty: &hir::Ty<'_>, cx: &mut DocContext<'_>) -> Type {
12841327
let trait_ = hir::Path { span, res, segments: &[] }.clean(cx);
12851328
register_res(cx, trait_.res);
12861329
Type::QPath {
1287-
name: segment.ident.name,
1330+
assoc: Box::new(segment.clean(cx)),
12881331
self_def_id: res.opt_def_id(),
12891332
self_type: box qself.clean(cx),
12901333
trait_,
@@ -1557,7 +1600,16 @@ impl<'tcx> Clean<Type> for Ty<'tcx> {
15571600
let mut bindings = vec![];
15581601
for pb in obj.projection_bounds() {
15591602
bindings.push(TypeBinding {
1560-
name: cx.tcx.associated_item(pb.item_def_id()).name,
1603+
assoc: projection_to_path_segment(
1604+
pb.skip_binder()
1605+
.lift_to_tcx(cx.tcx)
1606+
.unwrap()
1607+
// HACK(compiler-errors): Doesn't actually matter what self
1608+
// type we put here, because we're only using the GAT's substs.
1609+
.with_self_ty(cx.tcx, cx.tcx.types.self_param)
1610+
.projection_ty,
1611+
cx,
1612+
),
15611613
kind: TypeBindingKind::Equality { term: pb.skip_binder().term.clean(cx) },
15621614
});
15631615
}
@@ -1623,10 +1675,10 @@ impl<'tcx> Clean<Type> for Ty<'tcx> {
16231675
== trait_ref.skip_binder()
16241676
{
16251677
Some(TypeBinding {
1626-
name: cx
1627-
.tcx
1628-
.associated_item(proj.projection_ty.item_def_id)
1629-
.name,
1678+
assoc: projection_to_path_segment(
1679+
proj.projection_ty,
1680+
cx,
1681+
),
16301682
kind: TypeBindingKind::Equality {
16311683
term: proj.term.clean(cx),
16321684
},
@@ -2169,7 +2221,10 @@ fn clean_maybe_renamed_foreign_item(
21692221

21702222
impl Clean<TypeBinding> for hir::TypeBinding<'_> {
21712223
fn clean(&self, cx: &mut DocContext<'_>) -> TypeBinding {
2172-
TypeBinding { name: self.ident.name, kind: self.kind.clean(cx) }
2224+
TypeBinding {
2225+
assoc: PathSegment { name: self.ident.name, args: self.gen_args.clean(cx) },
2226+
kind: self.kind.clean(cx),
2227+
}
21732228
}
21742229
}
21752230

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

+8-8
Original file line numberDiff line numberDiff line change
@@ -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/html/format.rs

+11-5
Original file line numberDiff line numberDiff line change
@@ -961,7 +961,7 @@ fn fmt_type<'cx>(
961961
write!(f, "impl {}", print_generic_bounds(bounds, cx))
962962
}
963963
}
964-
clean::QPath { ref name, ref self_type, ref trait_, ref self_def_id } => {
964+
clean::QPath { ref assoc, ref self_type, ref trait_, ref self_def_id } => {
965965
let should_show_cast = !trait_.segments.is_empty()
966966
&& self_def_id
967967
.zip(Some(trait_.def_id()))
@@ -994,14 +994,15 @@ fn fmt_type<'cx>(
994994
write!(
995995
f,
996996
"<a class=\"associatedtype\" href=\"{url}#{shortty}.{name}\" \
997-
title=\"type {path}::{name}\">{name}</a>",
997+
title=\"type {path}::{name}\">{name}</a>{args}",
998998
url = url,
999999
shortty = ItemType::AssocType,
1000-
name = name,
1000+
name = assoc.name,
10011001
path = join_with_double_colon(path),
1002+
args = assoc.args.print(cx),
10021003
)?;
10031004
}
1004-
_ => write!(f, "{}", name)?,
1005+
_ => write!(f, "{}{:#}", assoc.name, assoc.args.print(cx))?,
10051006
}
10061007
Ok(())
10071008
}
@@ -1457,7 +1458,12 @@ impl clean::TypeBinding {
14571458
cx: &'a Context<'tcx>,
14581459
) -> impl fmt::Display + 'a + Captures<'tcx> {
14591460
display_fn(move |f| {
1460-
f.write_str(self.name.as_str())?;
1461+
f.write_str(self.assoc.name.as_str())?;
1462+
if f.alternate() {
1463+
write!(f, "{:#}", self.assoc.args.print(cx))?;
1464+
} else {
1465+
write!(f, "{}", self.assoc.args.print(cx))?;
1466+
}
14611467
match self.kind {
14621468
clean::TypeBindingKind::Equality { ref term } => {
14631469
if f.alternate() {

src/librustdoc/json/conversions.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,11 @@ impl FromWithTcx<clean::Constant> for Constant {
154154

155155
impl FromWithTcx<clean::TypeBinding> for TypeBinding {
156156
fn from_tcx(binding: clean::TypeBinding, tcx: TyCtxt<'_>) -> Self {
157-
TypeBinding { name: binding.name.to_string(), binding: binding.kind.into_tcx(tcx) }
157+
TypeBinding {
158+
name: binding.assoc.name.to_string(),
159+
args: binding.assoc.args.into_tcx(tcx),
160+
binding: binding.kind.into_tcx(tcx),
161+
}
158162
}
159163
}
160164

@@ -445,11 +449,12 @@ impl FromWithTcx<clean::Type> for Type {
445449
mutable: mutability == ast::Mutability::Mut,
446450
type_: Box::new((*type_).into_tcx(tcx)),
447451
},
448-
QPath { name, self_type, trait_, .. } => {
452+
QPath { assoc, self_type, trait_, .. } => {
449453
// FIXME: should `trait_` be a clean::Path equivalent in JSON?
450454
let trait_ = clean::Type::Path { path: trait_ }.into_tcx(tcx);
451455
Type::QualifiedPath {
452-
name: name.to_string(),
456+
name: assoc.name.to_string(),
457+
args: Box::new(assoc.args.clone().into_tcx(tcx)),
453458
self_type: Box::new((*self_type).into_tcx(tcx)),
454459
trait_: Box::new(trait_),
455460
}

src/rustdoc-json-types/lib.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use std::path::PathBuf;
99
use serde::{Deserialize, Serialize};
1010

1111
/// rustdoc format-version.
12-
pub const FORMAT_VERSION: u32 = 11;
12+
pub const FORMAT_VERSION: u32 = 12;
1313

1414
/// A `Crate` is the root of the emitted JSON blob. It contains all type/documentation information
1515
/// about the language items in the local crate, as well as info about external items to allow
@@ -145,6 +145,7 @@ pub struct Constant {
145145
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
146146
pub struct TypeBinding {
147147
pub name: String,
148+
pub args: GenericArgs,
148149
pub binding: TypeBindingKind,
149150
}
150151

@@ -233,9 +234,7 @@ pub enum ItemEnum {
233234
default: Option<String>,
234235
},
235236
AssocType {
236-
/// generics and `where` clause
237237
generics: Generics,
238-
/// e.g. `: Sized`
239238
bounds: Vec<GenericBound>,
240239
/// e.g. `type X = usize;`
241240
default: Option<Type>,
@@ -435,6 +434,7 @@ pub enum Type {
435434
/// `<Type as Trait>::Name` or associated types like `T::Item` where `T: Iterator`
436435
QualifiedPath {
437436
name: String,
437+
args: Box<GenericArgs>,
438438
self_type: Box<Type>,
439439
#[serde(rename = "trait")]
440440
trait_: Box<Type>,

0 commit comments

Comments
 (0)