about summary refs log tree commit diff
path: root/src/librustdoc/clean
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2022-03-04 00:27:23 +0000
committerbors <bors@rust-lang.org>2022-03-04 00:27:23 +0000
commit6d7684101a51f1c375ec84aef5d2fbdeb214bbc2 (patch)
treed86722eb5dda8ae7ef9980dd4a19deebb9f37cf4 /src/librustdoc/clean
parent40d3040ae19b8c43c0027bc6d3e9805e5ee5e0ee (diff)
parent0e57a16c88cbb042bf4d19934e0ccdd3838645ec (diff)
downloadrust-6d7684101a51f1c375ec84aef5d2fbdeb214bbc2.tar.gz
rust-6d7684101a51f1c375ec84aef5d2fbdeb214bbc2.zip
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)
Diffstat (limited to 'src/librustdoc/clean')
-rw-r--r--src/librustdoc/clean/auto_trait.rs6
-rw-r--r--src/librustdoc/clean/inline.rs2
-rw-r--r--src/librustdoc/clean/mod.rs161
-rw-r--r--src/librustdoc/clean/simplify.rs4
-rw-r--r--src/librustdoc/clean/types.rs20
5 files changed, 133 insertions, 60 deletions
diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs
index a3154d8f03b..62f3527525e 100644
--- a/src/librustdoc/clean/auto_trait.rs
+++ b/src/librustdoc/clean/auto_trait.rs
@@ -546,11 +546,11 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
                 }
                 WherePredicate::EqPredicate { lhs, rhs } => {
                     match lhs {
-                        Type::QPath { name: left_name, ref self_type, ref trait_, .. } => {
+                        Type::QPath { ref assoc, ref self_type, ref trait_, .. } => {
                             let ty = &*self_type;
                             let mut new_trait = trait_.clone();
 
-                            if self.is_fn_trait(trait_) && left_name == sym::Output {
+                            if self.is_fn_trait(trait_) && assoc.name == sym::Output {
                                 ty_to_fn
                                     .entry(*ty.clone())
                                     .and_modify(|e| {
@@ -571,7 +571,7 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
                                 // to 'T: Iterator<Item=u8>'
                                 GenericArgs::AngleBracketed { ref mut bindings, .. } => {
                                     bindings.push(TypeBinding {
-                                        name: left_name,
+                                        assoc: *assoc.clone(),
                                         kind: TypeBindingKind::Equality { term: rhs },
                                     });
                                 }
diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs
index 3ae1df51695..2e66db425d6 100644
--- a/src/librustdoc/clean/inline.rs
+++ b/src/librustdoc/clean/inline.rs
@@ -636,7 +636,7 @@ fn filter_non_trait_generics(trait_did: DefId, mut g: clean::Generics) -> clean:
 
     g.where_predicates.retain(|pred| match pred {
         clean::WherePredicate::BoundPredicate {
-            ty: clean::QPath { self_type: box clean::Generic(ref s), trait_, name: _, .. },
+            ty: clean::QPath { self_type: box clean::Generic(ref s), trait_, .. },
             bounds,
             ..
         } => !(bounds.is_empty() || *s == kw::SelfUpper && trait_.def_id() == trait_did),
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 8fad625973f..0d1482ce470 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -388,7 +388,7 @@ impl<'tcx> Clean<Type> for ty::ProjectionTy<'tcx> {
         let trait_ = lifted.trait_ref(cx.tcx).clean(cx);
         let self_type = self.self_ty().clean(cx);
         Type::QPath {
-            name: cx.tcx.associated_item(self.item_def_id).name,
+            assoc: Box::new(projection_to_path_segment(*self, cx)),
             self_def_id: self_type.def_id(&cx.cache),
             self_type: box self_type,
             trait_,
@@ -396,6 +396,27 @@ impl<'tcx> Clean<Type> for ty::ProjectionTy<'tcx> {
     }
 }
 
+fn projection_to_path_segment(ty: ty::ProjectionTy<'_>, cx: &mut DocContext<'_>) -> PathSegment {
+    let item = cx.tcx.associated_item(ty.item_def_id);
+    let generics = cx.tcx.generics_of(ty.item_def_id);
+    PathSegment {
+        name: item.name,
+        args: GenericArgs::AngleBracketed {
+            args: ty.substs[generics.parent_count..]
+                .iter()
+                .map(|ty| match ty.unpack() {
+                    ty::subst::GenericArgKind::Lifetime(lt) => {
+                        GenericArg::Lifetime(lt.clean(cx).unwrap())
+                    }
+                    ty::subst::GenericArgKind::Type(ty) => GenericArg::Type(ty.clean(cx)),
+                    ty::subst::GenericArgKind::Const(c) => GenericArg::Const(Box::new(c.clean(cx))),
+                })
+                .collect(),
+            bindings: Default::default(),
+        },
+    }
+}
+
 impl Clean<GenericParamDef> for ty::GenericParamDef {
     fn clean(&self, cx: &mut DocContext<'_>) -> GenericParamDef {
         let (name, kind) = match self.kind {
@@ -601,8 +622,8 @@ fn clean_ty_generics(
         })
         .collect::<Vec<GenericParamDef>>();
 
-    // param index -> [(DefId of trait, associated type name, type)]
-    let mut impl_trait_proj = FxHashMap::<u32, Vec<(DefId, Symbol, Ty<'_>)>>::default();
+    // param index -> [(DefId of trait, associated type name and generics, type)]
+    let mut impl_trait_proj = FxHashMap::<u32, Vec<(DefId, PathSegment, Ty<'_>)>>::default();
 
     let where_predicates = preds
         .predicates
@@ -648,8 +669,9 @@ fn clean_ty_generics(
 
                     let proj = projection
                         .map(|p| (p.skip_binder().projection_ty.clean(cx), p.skip_binder().term));
-                    if let Some(((_, trait_did, name), rhs)) =
-                        proj.as_ref().and_then(|(lhs, rhs)| Some((lhs.projection()?, rhs)))
+                    if let Some(((_, trait_did, name), rhs)) = proj
+                        .as_ref()
+                        .and_then(|(lhs, rhs): &(Type, _)| Some((lhs.projection()?, rhs)))
                     {
                         // FIXME(...): Remove this unwrap()
                         impl_trait_proj.entry(param_idx).or_default().push((
@@ -992,9 +1014,10 @@ impl Clean<Item> for hir::TraitItem<'_> {
                     TyMethodItem(t)
                 }
                 hir::TraitItemKind::Type(bounds, ref default) => {
+                    let generics = enter_impl_trait(cx, |cx| self.generics.clean(cx));
                     let bounds = bounds.iter().filter_map(|x| x.clean(cx)).collect();
                     let default = default.map(|t| t.clean(cx));
-                    AssocTypeItem(bounds, default)
+                    AssocTypeItem(Box::new(generics), bounds, default)
                 }
             };
             let what_rustc_thinks =
@@ -1026,15 +1049,9 @@ impl Clean<Item> for hir::ImplItem<'_> {
                 }
                 hir::ImplItemKind::TyAlias(ref hir_ty) => {
                     let type_ = hir_ty.clean(cx);
+                    let generics = self.generics.clean(cx);
                     let item_type = hir_ty_to_ty(cx.tcx, hir_ty).clean(cx);
-                    TypedefItem(
-                        Typedef {
-                            type_,
-                            generics: Generics::default(),
-                            item_type: Some(item_type),
-                        },
-                        true,
-                    )
+                    TypedefItem(Typedef { type_, generics, item_type: Some(item_type) }, true)
                 }
             };
 
@@ -1140,35 +1157,79 @@ impl Clean<Item> for ty::AssocItem {
             ty::AssocKind::Type => {
                 let my_name = self.name;
 
+                fn param_eq_arg(param: &GenericParamDef, arg: &GenericArg) -> bool {
+                    match (&param.kind, arg) {
+                        (GenericParamDefKind::Type { .. }, GenericArg::Type(Type::Generic(ty)))
+                            if *ty == param.name =>
+                        {
+                            true
+                        }
+                        (
+                            GenericParamDefKind::Lifetime { .. },
+                            GenericArg::Lifetime(Lifetime(lt)),
+                        ) if *lt == param.name => true,
+                        (GenericParamDefKind::Const { .. }, GenericArg::Const(c)) => {
+                            match &c.kind {
+                                ConstantKind::TyConst { expr } => expr == param.name.as_str(),
+                                _ => false,
+                            }
+                        }
+                        _ => false,
+                    }
+                }
+
                 if let ty::TraitContainer(_) = self.container {
                     let bounds = tcx.explicit_item_bounds(self.def_id);
                     let predicates = ty::GenericPredicates { parent: None, predicates: bounds };
-                    let generics = clean_ty_generics(cx, tcx.generics_of(self.def_id), predicates);
+                    let mut generics =
+                        clean_ty_generics(cx, tcx.generics_of(self.def_id), predicates);
+                    // Filter out the bounds that are (likely?) directly attached to the associated type,
+                    // as opposed to being located in the where clause.
                     let mut bounds = generics
                         .where_predicates
-                        .iter()
-                        .filter_map(|pred| {
-                            let (name, self_type, trait_, bounds) = match *pred {
-                                WherePredicate::BoundPredicate {
-                                    ty: QPath { ref name, ref self_type, ref trait_, .. },
-                                    ref bounds,
-                                    ..
-                                } => (name, self_type, trait_, bounds),
-                                _ => return None,
-                            };
-                            if *name != my_name {
-                                return None;
-                            }
-                            if trait_.def_id() != self.container.id() {
-                                return None;
+                        .drain_filter(|pred| match *pred {
+                            WherePredicate::BoundPredicate {
+                                ty: QPath { ref assoc, ref self_type, ref trait_, .. },
+                                ..
+                            } => {
+                                if assoc.name != my_name {
+                                    return false;
+                                }
+                                if trait_.def_id() != self.container.id() {
+                                    return false;
+                                }
+                                match **self_type {
+                                    Generic(ref s) if *s == kw::SelfUpper => {}
+                                    _ => return false,
+                                }
+                                match &assoc.args {
+                                    GenericArgs::AngleBracketed { args, bindings } => {
+                                        if !bindings.is_empty()
+                                            || generics
+                                                .params
+                                                .iter()
+                                                .zip(args)
+                                                .any(|(param, arg)| !param_eq_arg(param, arg))
+                                        {
+                                            return false;
+                                        }
+                                    }
+                                    GenericArgs::Parenthesized { .. } => {
+                                        // The only time this happens is if we're inside the rustdoc for Fn(),
+                                        // which only has one associated type, which is not a GAT, so whatever.
+                                    }
+                                }
+                                true
                             }
-                            match **self_type {
-                                Generic(ref s) if *s == kw::SelfUpper => {}
-                                _ => return None,
+                            _ => false,
+                        })
+                        .flat_map(|pred| {
+                            if let WherePredicate::BoundPredicate { bounds, .. } = pred {
+                                bounds
+                            } else {
+                                unreachable!()
                             }
-                            Some(bounds)
                         })
-                        .flat_map(|i| i.iter().cloned())
                         .collect::<Vec<_>>();
                     // Our Sized/?Sized bound didn't get handled when creating the generics
                     // because we didn't actually get our whole set of bounds until just now
@@ -1188,7 +1249,7 @@ impl Clean<Item> for ty::AssocItem {
                         None
                     };
 
-                    AssocTypeItem(bounds, ty.map(|t| t.clean(cx)))
+                    AssocTypeItem(Box::new(generics), bounds, ty.map(|t| t.clean(cx)))
                 } else {
                     // FIXME: when could this happen? Associated items in inherent impls?
                     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 {
             };
             register_res(cx, trait_.res);
             Type::QPath {
-                name: p.segments.last().expect("segments were empty").ident.name,
+                assoc: Box::new(p.segments.last().expect("segments were empty").clean(cx)),
                 self_def_id: Some(DefId::local(qself.hir_id.owner.local_def_index)),
                 self_type: box qself.clean(cx),
                 trait_,
@@ -1276,7 +1337,7 @@ fn clean_qpath(hir_ty: &hir::Ty<'_>, cx: &mut DocContext<'_>) -> Type {
             let trait_ = hir::Path { span, res, segments: &[] }.clean(cx);
             register_res(cx, trait_.res);
             Type::QPath {
-                name: segment.ident.name,
+                assoc: Box::new(segment.clean(cx)),
                 self_def_id: res.opt_def_id(),
                 self_type: box qself.clean(cx),
                 trait_,
@@ -1548,7 +1609,16 @@ impl<'tcx> Clean<Type> for Ty<'tcx> {
                 let mut bindings = vec![];
                 for pb in obj.projection_bounds() {
                     bindings.push(TypeBinding {
-                        name: cx.tcx.associated_item(pb.item_def_id()).name,
+                        assoc: projection_to_path_segment(
+                            pb.skip_binder()
+                                .lift_to_tcx(cx.tcx)
+                                .unwrap()
+                                // HACK(compiler-errors): Doesn't actually matter what self
+                                // type we put here, because we're only using the GAT's substs.
+                                .with_self_ty(cx.tcx, cx.tcx.types.self_param)
+                                .projection_ty,
+                            cx,
+                        ),
                         kind: TypeBindingKind::Equality { term: pb.skip_binder().term.clean(cx) },
                     });
                 }
@@ -1614,10 +1684,10 @@ impl<'tcx> Clean<Type> for Ty<'tcx> {
                                         == trait_ref.skip_binder()
                                     {
                                         Some(TypeBinding {
-                                            name: cx
-                                                .tcx
-                                                .associated_item(proj.projection_ty.item_def_id)
-                                                .name,
+                                            assoc: projection_to_path_segment(
+                                                proj.projection_ty,
+                                                cx,
+                                            ),
                                             kind: TypeBindingKind::Equality {
                                                 term: proj.term.clean(cx),
                                             },
@@ -2160,7 +2230,10 @@ fn clean_maybe_renamed_foreign_item(
 
 impl Clean<TypeBinding> for hir::TypeBinding<'_> {
     fn clean(&self, cx: &mut DocContext<'_>) -> TypeBinding {
-        TypeBinding { name: self.ident.name, kind: self.kind.clean(cx) }
+        TypeBinding {
+            assoc: PathSegment { name: self.ident.name, args: self.gen_args.clean(cx) },
+            kind: self.kind.clean(cx),
+        }
     }
 }
 
diff --git a/src/librustdoc/clean/simplify.rs b/src/librustdoc/clean/simplify.rs
index ea18d915deb..dd8e1132572 100644
--- a/src/librustdoc/clean/simplify.rs
+++ b/src/librustdoc/clean/simplify.rs
@@ -89,7 +89,7 @@ crate fn merge_bounds(
     cx: &clean::DocContext<'_>,
     bounds: &mut Vec<clean::GenericBound>,
     trait_did: DefId,
-    name: Symbol,
+    assoc: clean::PathSegment,
     rhs: &clean::Term,
 ) -> bool {
     !bounds.iter_mut().any(|b| {
@@ -107,7 +107,7 @@ crate fn merge_bounds(
         match last.args {
             PP::AngleBracketed { ref mut bindings, .. } => {
                 bindings.push(clean::TypeBinding {
-                    name,
+                    assoc: assoc.clone(),
                     kind: clean::TypeBindingKind::Equality { term: rhs.clone() },
                 });
             }
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index 467a6940628..78928fb4059 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -683,7 +683,7 @@ crate enum ItemKind {
     ///
     /// The bounds may be non-empty if there is a `where` clause.
     /// The `Option<Type>` is the default concrete type (e.g. `trait Trait { type Target = usize; }`)
-    AssocTypeItem(Vec<GenericBound>, Option<Type>),
+    AssocTypeItem(Box<Generics>, Vec<GenericBound>, Option<Type>),
     /// An item that has been stripped by a rustdoc pass
     StrippedItem(Box<ItemKind>),
     KeywordItem(Symbol),
@@ -721,7 +721,7 @@ impl ItemKind {
             | ProcMacroItem(_)
             | PrimitiveItem(_)
             | AssocConstItem(_, _)
-            | AssocTypeItem(_, _)
+            | AssocTypeItem(..)
             | StrippedItem(_)
             | KeywordItem(_) => [].iter(),
         }
@@ -1397,7 +1397,7 @@ crate enum Type {
 
     /// A qualified path to an associated item: `<Type as Trait>::Name`
     QPath {
-        name: Symbol,
+        assoc: Box<PathSegment>,
         self_type: Box<Type>,
         /// FIXME: This is a hack that should be removed; see [this discussion][1].
         ///
@@ -1415,7 +1415,7 @@ crate enum Type {
 
 // `Type` is used a lot. Make sure it doesn't unintentionally get bigger.
 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
-rustc_data_structures::static_assert_size!(Type, 72);
+rustc_data_structures::static_assert_size!(Type, 80);
 
 impl Type {
     /// When comparing types for equality, it can help to ignore `&` wrapping.
@@ -1505,12 +1505,12 @@ impl Type {
         self.primitive_type().is_some()
     }
 
-    crate fn projection(&self) -> Option<(&Type, DefId, Symbol)> {
-        let (self_, trait_, name) = match self {
-            QPath { self_type, trait_, name, .. } => (self_type, trait_, name),
+    crate fn projection(&self) -> Option<(&Type, DefId, PathSegment)> {
+        let (self_, trait_, assoc) = match self {
+            QPath { self_type, trait_, assoc, .. } => (self_type, trait_, assoc),
             _ => return None,
         };
-        Some((&self_, trait_.def_id(), *name))
+        Some((&self_, trait_.def_id(), *assoc.clone()))
     }
 
     fn inner_def_id(&self, cache: Option<&Cache>) -> Option<DefId> {
@@ -2018,7 +2018,7 @@ crate enum GenericArg {
 // `GenericArg` can occur many times in a single `Path`, so make sure it
 // doesn't increase in size unexpectedly.
 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
-rustc_data_structures::static_assert_size!(GenericArg, 80);
+rustc_data_structures::static_assert_size!(GenericArg, 88);
 
 #[derive(Clone, PartialEq, Eq, Debug, Hash)]
 crate enum GenericArgs {
@@ -2256,7 +2256,7 @@ crate struct ProcMacro {
 /// `A: Send + Sync` in `Foo<A: Send + Sync>`).
 #[derive(Clone, PartialEq, Eq, Debug, Hash)]
 crate struct TypeBinding {
-    crate name: Symbol,
+    crate assoc: PathSegment,
     crate kind: TypeBindingKind,
 }