From a011dd9dacef28a53c868fa537c45fe81415c733 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Mon, 3 Jul 2023 00:48:06 +0200 Subject: [PATCH] Render generic const items in rustdoc --- src/librustdoc/clean/inline.rs | 5 ++ src/librustdoc/clean/mod.rs | 66 ++++++++++-------------- src/librustdoc/clean/simplify.rs | 35 +++++++++++++ src/librustdoc/clean/types.rs | 16 +++--- src/librustdoc/html/render/mod.rs | 48 ++++++++++------- src/librustdoc/html/render/print_item.rs | 4 +- src/librustdoc/html/render/span_map.rs | 2 +- src/librustdoc/json/conversions.rs | 9 +++- 8 files changed, 116 insertions(+), 69 deletions(-) diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 1c4149fd545..c31d104f8cb 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -644,6 +644,10 @@ pub(crate) fn print_inlined_const(tcx: TyCtxt<'_>, did: DefId) -> String { } fn build_const(cx: &mut DocContext<'_>, def_id: DefId) -> clean::Constant { + let mut generics = + clean_ty_generics(cx, cx.tcx.generics_of(def_id), cx.tcx.explicit_predicates_of(def_id)); + clean::simplify::move_bounds_to_generic_parameters(&mut generics); + clean::Constant { type_: clean_middle_ty( ty::Binder::dummy(cx.tcx.type_of(def_id).instantiate_identity()), @@ -651,6 +655,7 @@ fn build_const(cx: &mut DocContext<'_>, def_id: DefId) -> clean::Constant { Some(def_id), None, ), + generics: Box::new(generics), kind: clean::ConstantKind::Extern { def_id }, } } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index a26eb222c18..9839b82d7d7 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -273,6 +273,7 @@ pub(crate) fn clean_const<'tcx>(constant: &hir::ConstArg, cx: &mut DocContext<'t Some(def_id), None, ), + generics: Box::new(Generics::default()), kind: ConstantKind::Anonymous { body: constant.value.body }, } } @@ -284,6 +285,7 @@ pub(crate) fn clean_middle_const<'tcx>( // FIXME: instead of storing the stringified expression, store `self` directly instead. Constant { type_: clean_middle_ty(constant.map_bound(|c| c.ty()), cx, None, None), + generics: Box::new(Generics::default()), kind: ConstantKind::TyConst { expr: constant.skip_binder().to_string().into() }, } } @@ -1188,11 +1190,18 @@ fn clean_trait_item<'tcx>(trait_item: &hir::TraitItem<'tcx>, cx: &mut DocContext let local_did = trait_item.owner_id.to_def_id(); cx.with_param_env(local_did, |cx| { let inner = match trait_item.kind { - hir::TraitItemKind::Const(ty, Some(default)) => AssocConstItem( - clean_ty(ty, cx), - ConstantKind::Local { def_id: local_did, body: default }, - ), - hir::TraitItemKind::Const(ty, None) => TyAssocConstItem(clean_ty(ty, cx)), + hir::TraitItemKind::Const(ty, Some(default)) => { + let generics = enter_impl_trait(cx, |cx| clean_generics(trait_item.generics, cx)); + AssocConstItem( + Box::new(generics), + clean_ty(ty, cx), + ConstantKind::Local { def_id: local_did, body: default }, + ) + } + hir::TraitItemKind::Const(ty, None) => { + let generics = enter_impl_trait(cx, |cx| clean_generics(trait_item.generics, cx)); + TyAssocConstItem(Box::new(generics), clean_ty(ty, cx)) + } hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => { let m = clean_function(cx, sig, trait_item.generics, FunctionArgs::Body(body)); MethodItem(m, None) @@ -1237,8 +1246,9 @@ pub(crate) fn clean_impl_item<'tcx>( cx.with_param_env(local_did, |cx| { let inner = match impl_.kind { hir::ImplItemKind::Const(ty, expr) => { + let generics = clean_generics(impl_.generics, cx); let default = ConstantKind::Local { def_id: local_did, body: expr }; - AssocConstItem(clean_ty(ty, cx), default) + AssocConstItem(Box::new(generics), clean_ty(ty, cx), default) } hir::ImplItemKind::Fn(ref sig, body) => { let m = clean_function(cx, sig, impl_.generics, FunctionArgs::Body(body)); @@ -1279,14 +1289,21 @@ pub(crate) fn clean_middle_assoc_item<'tcx>( None, ); + let mut generics = Box::new(clean_ty_generics( + cx, + tcx.generics_of(assoc_item.def_id), + tcx.explicit_predicates_of(assoc_item.def_id), + )); + simplify::move_bounds_to_generic_parameters(&mut generics); + let provided = match assoc_item.container { ty::ImplContainer => true, ty::TraitContainer => tcx.defaultness(assoc_item.def_id).has_value(), }; if provided { - AssocConstItem(ty, ConstantKind::Extern { def_id: assoc_item.def_id }) + AssocConstItem(generics, ty, ConstantKind::Extern { def_id: assoc_item.def_id }) } else { - TyAssocConstItem(ty) + TyAssocConstItem(generics, ty) } } ty::AssocKind::Fn => { @@ -1379,34 +1396,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>( tcx.generics_of(assoc_item.def_id), ty::GenericPredicates { parent: None, predicates }, ); - // Move bounds that are (likely) directly attached to the parameters of the - // (generic) associated type from the where clause to the respective parameter. - // There is no guarantee that this is what the user actually wrote but we have - // no way of knowing. - let mut where_predicates = ThinVec::new(); - for mut pred in generics.where_predicates { - if let WherePredicate::BoundPredicate { ty: Generic(arg), bounds, .. } = &mut pred - && let Some(GenericParamDef { - kind: GenericParamDefKind::Type { bounds: param_bounds, .. }, - .. - }) = generics.params.iter_mut().find(|param| ¶m.name == arg) - { - param_bounds.append(bounds); - } else if let WherePredicate::RegionPredicate { lifetime: Lifetime(arg), bounds } = &mut pred - && let Some(GenericParamDef { - kind: GenericParamDefKind::Lifetime { outlives: param_bounds }, - .. - }) = generics.params.iter_mut().find(|param| ¶m.name == arg) - { - param_bounds.extend(bounds.drain(..).map(|bound| match bound { - GenericBound::Outlives(lifetime) => lifetime, - _ => unreachable!(), - })); - } else { - where_predicates.push(pred); - } - } - generics.where_predicates = where_predicates; + simplify::move_bounds_to_generic_parameters(&mut generics); if let ty::TraitContainer = assoc_item.container { // Move bounds that are (likely) directly attached to the associated type @@ -2603,9 +2593,9 @@ fn clean_maybe_renamed_item<'tcx>( ItemKind::Static(ty, mutability, body_id) => { StaticItem(Static { type_: clean_ty(ty, cx), mutability, expr: Some(body_id) }) } - // FIXME(fmease): rustdoc integration - ItemKind::Const(ty, _generics, body_id) => ConstantItem(Constant { + ItemKind::Const(ty, generics, body_id) => ConstantItem(Constant { type_: clean_ty(ty, cx), + generics: Box::new(clean_generics(generics, cx)), kind: ConstantKind::Local { body: body_id, def_id }, }), ItemKind::OpaqueTy(ref ty) => OpaqueTyItem(OpaqueTy { diff --git a/src/librustdoc/clean/simplify.rs b/src/librustdoc/clean/simplify.rs index d53b038019b..7b8f20326ed 100644 --- a/src/librustdoc/clean/simplify.rs +++ b/src/librustdoc/clean/simplify.rs @@ -138,3 +138,38 @@ fn trait_is_same_or_supertrait(cx: &DocContext<'_>, child: DefId, trait_: DefId) }) .any(|did| trait_is_same_or_supertrait(cx, did, trait_)) } + +/// Move bounds that are (likely) directly attached to generic parameters from the where-clause to +/// the respective parameter. +/// +/// There is no guarantee that this is what the user actually wrote but we have no way of knowing. +// FIXME(fmease): It'd make a lot of sense to just incorporate this logic into `clean_ty_generics` +// making every of its users benefit from it. +pub(crate) fn move_bounds_to_generic_parameters(generics: &mut clean::Generics) { + use clean::types::*; + + let mut where_predicates = ThinVec::new(); + for mut pred in generics.where_predicates.drain(..) { + if let WherePredicate::BoundPredicate { ty: Generic(arg), bounds, .. } = &mut pred + && let Some(GenericParamDef { + kind: GenericParamDefKind::Type { bounds: param_bounds, .. }, + .. + }) = generics.params.iter_mut().find(|param| ¶m.name == arg) + { + param_bounds.append(bounds); + } else if let WherePredicate::RegionPredicate { lifetime: Lifetime(arg), bounds } = &mut pred + && let Some(GenericParamDef { + kind: GenericParamDefKind::Lifetime { outlives: param_bounds }, + .. + }) = generics.params.iter_mut().find(|param| ¶m.name == arg) + { + param_bounds.extend(bounds.drain(..).map(|bound| match bound { + GenericBound::Outlives(lifetime) => lifetime, + _ => unreachable!(), + })); + } else { + where_predicates.push(pred); + } + } + generics.where_predicates = where_predicates; +} diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 6eec01771f3..cb089ddd09a 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -824,9 +824,9 @@ pub(crate) enum ItemKind { ProcMacroItem(ProcMacro), PrimitiveItem(PrimitiveType), /// A required associated constant in a trait declaration. - TyAssocConstItem(Type), + TyAssocConstItem(Box, Type), /// An associated constant in a trait impl or a provided one in a trait declaration. - AssocConstItem(Type, ConstantKind), + AssocConstItem(Box, Type, ConstantKind), /// A required associated type in a trait declaration. /// /// The bounds may be non-empty if there is a `where` clause. @@ -871,8 +871,8 @@ impl ItemKind { | MacroItem(_) | ProcMacroItem(_) | PrimitiveItem(_) - | TyAssocConstItem(_) - | AssocConstItem(_, _) + | TyAssocConstItem(..) + | AssocConstItem(..) | TyAssocTypeItem(..) | AssocTypeItem(..) | StrippedItem(_) @@ -1278,7 +1278,7 @@ impl Lifetime { } } -#[derive(Clone, Debug)] +#[derive(Clone, PartialEq, Eq, Hash, Debug)] pub(crate) enum WherePredicate { BoundPredicate { ty: Type, bounds: Vec, bound_params: Vec }, RegionPredicate { lifetime: Lifetime, bounds: Vec }, @@ -1348,7 +1348,7 @@ impl GenericParamDef { } // maybe use a Generic enum and use Vec? -#[derive(Clone, Debug, Default)] +#[derive(Clone, PartialEq, Eq, Hash, Debug, Default)] pub(crate) struct Generics { pub(crate) params: ThinVec, pub(crate) where_predicates: ThinVec, @@ -2266,6 +2266,7 @@ pub(crate) struct Static { #[derive(Clone, PartialEq, Eq, Hash, Debug)] pub(crate) struct Constant { pub(crate) type_: Type, + pub(crate) generics: Box, pub(crate) kind: ConstantKind, } @@ -2515,7 +2516,8 @@ mod size_asserts { static_assert_size!(GenericParamDef, 56); static_assert_size!(Generics, 16); static_assert_size!(Item, 56); - static_assert_size!(ItemKind, 64); + // FIXME(generic_const_items): Further reduce the size. + static_assert_size!(ItemKind, 72); static_assert_size!(PathSegment, 40); static_assert_size!(Type, 32); // tidy-alphabetical-end diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 2fcf61d0049..a6200654ffa 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -748,20 +748,22 @@ fn assoc_href_attr(it: &clean::Item, link: AssocItemLink<'_>, cx: &Context<'_>) fn assoc_const( w: &mut Buffer, it: &clean::Item, + generics: &clean::Generics, ty: &clean::Type, default: Option<&clean::ConstantKind>, link: AssocItemLink<'_>, - extra: &str, + indent: usize, cx: &Context<'_>, ) { let tcx = cx.tcx(); write!( w, - "{extra}{vis}const {name}: {ty}", - extra = extra, + "{indent}{vis}const {name}{generics}: {ty}", + indent = " ".repeat(indent), vis = visibility_print_with_space(it.visibility(tcx), it.item_id, cx), href = assoc_href_attr(it, link, cx), name = it.name.as_ref().unwrap(), + generics = generics.print(cx), ty = ty.print(cx), ); if let Some(default) = default { @@ -774,6 +776,7 @@ fn assoc_const( // Find a way to print constants here without all that jazz. write!(w, "{}", Escape(&default.value(tcx).unwrap_or_else(|| default.expr(tcx)))); } + write!(w, "{}", print_where_clause(generics, cx, indent, Ending::NoNewline)); } fn assoc_type( @@ -986,19 +989,22 @@ fn render_assoc_item( clean::MethodItem(m, _) => { assoc_method(w, item, &m.generics, &m.decl, link, parent, cx, render_mode) } - kind @ (clean::TyAssocConstItem(ty) | clean::AssocConstItem(ty, _)) => assoc_const( - w, - item, - ty, - match kind { - clean::TyAssocConstItem(_) => None, - clean::AssocConstItem(_, default) => Some(default), - _ => unreachable!(), - }, - link, - if parent == ItemType::Trait { " " } else { "" }, - cx, - ), + kind @ (clean::TyAssocConstItem(generics, ty) | clean::AssocConstItem(generics, ty, _)) => { + assoc_const( + w, + item, + generics, + ty, + match kind { + clean::TyAssocConstItem(..) => None, + clean::AssocConstItem(.., default) => Some(default), + _ => unreachable!(), + }, + link, + if parent == ItemType::Trait { 4 } else { 0 }, + cx, + ) + } clean::TyAssocTypeItem(ref generics, ref bounds) => assoc_type( w, item, @@ -1565,7 +1571,8 @@ fn render_impl( w.write_str(""); } } - kind @ (clean::TyAssocConstItem(ty) | clean::AssocConstItem(ty, _)) => { + kind @ (clean::TyAssocConstItem(generics, ty) + | clean::AssocConstItem(generics, ty, _)) => { let source_id = format!("{}.{}", item_type, name); let id = cx.derive_id(source_id.clone()); write!(w, "
", id, item_type, in_trait_class); @@ -1578,14 +1585,15 @@ fn render_impl( assoc_const( w, item, + generics, ty, match kind { - clean::TyAssocConstItem(_) => None, - clean::AssocConstItem(_, default) => Some(default), + clean::TyAssocConstItem(..) => None, + clean::AssocConstItem(.., default) => Some(default), _ => unreachable!(), }, link.anchor(if trait_.is_some() { &source_id } else { &id }), - "", + 0, cx, ); w.write_str(""); diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 383e3c17088..e363e75f565 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -1543,10 +1543,12 @@ fn item_constant(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, c: &cle write!( w, - "{vis}const {name}: {typ}", + "{vis}const {name}{generics}: {typ}{where_clause}", vis = visibility_print_with_space(it.visibility(tcx), it.item_id, cx), name = it.name.unwrap(), + generics = c.generics.print(cx), typ = c.type_.print(cx), + where_clause = print_where_clause(&c.generics, cx, 0, Ending::NoNewline), ); // FIXME: The code below now prints diff --git a/src/librustdoc/html/render/span_map.rs b/src/librustdoc/html/render/span_map.rs index ac587bf6008..5f130f1875a 100644 --- a/src/librustdoc/html/render/span_map.rs +++ b/src/librustdoc/html/render/span_map.rs @@ -219,7 +219,7 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> { fn visit_item(&mut self, item: &'tcx Item<'tcx>) { match item.kind { ItemKind::Static(_, _, _) - | ItemKind::Const(_, _) + | ItemKind::Const(_, _, _) | ItemKind::Fn(_, _, _) | ItemKind::Macro(_, _) | ItemKind::TyAlias(_, _) diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 91cd55b1113..8673138f649 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -171,6 +171,7 @@ impl FromWithTcx for GenericArg { } impl FromWithTcx for Constant { + // FIXME(generic_const_items): Add support for generic const items. fn from_tcx(constant: clean::Constant, tcx: TyCtxt<'_>) -> Self { let expr = constant.expr(tcx); let value = constant.value(tcx); @@ -321,8 +322,12 @@ fn from_clean_item(item: clean::Item, tcx: TyCtxt<'_>) -> ItemEnum { impls: Vec::new(), // Added in JsonRenderer::item }) } - TyAssocConstItem(ty) => ItemEnum::AssocConst { type_: ty.into_tcx(tcx), default: None }, - AssocConstItem(ty, default) => { + // FIXME(generic_const_items): Add support for generic associated consts. + TyAssocConstItem(_generics, ty) => { + ItemEnum::AssocConst { type_: ty.into_tcx(tcx), default: None } + } + // FIXME(generic_const_items): Add support for generic associated consts. + AssocConstItem(_generics, ty, default) => { ItemEnum::AssocConst { type_: ty.into_tcx(tcx), default: Some(default.expr(tcx)) } } TyAssocTypeItem(g, b) => ItemEnum::AssocType {