1
Fork 0

Render generic const items in rustdoc

This commit is contained in:
León Orell Valerian Liehr 2023-07-03 00:48:06 +02:00
parent 59bb77c7fe
commit a011dd9dac
No known key found for this signature in database
GPG key ID: D17A07215F68E713
8 changed files with 116 additions and 69 deletions

View file

@ -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 { 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 { clean::Constant {
type_: clean_middle_ty( type_: clean_middle_ty(
ty::Binder::dummy(cx.tcx.type_of(def_id).instantiate_identity()), 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), Some(def_id),
None, None,
), ),
generics: Box::new(generics),
kind: clean::ConstantKind::Extern { def_id }, kind: clean::ConstantKind::Extern { def_id },
} }
} }

View file

@ -273,6 +273,7 @@ pub(crate) fn clean_const<'tcx>(constant: &hir::ConstArg, cx: &mut DocContext<'t
Some(def_id), Some(def_id),
None, None,
), ),
generics: Box::new(Generics::default()),
kind: ConstantKind::Anonymous { body: constant.value.body }, 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. // FIXME: instead of storing the stringified expression, store `self` directly instead.
Constant { Constant {
type_: clean_middle_ty(constant.map_bound(|c| c.ty()), cx, None, None), 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() }, 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(); let local_did = trait_item.owner_id.to_def_id();
cx.with_param_env(local_did, |cx| { cx.with_param_env(local_did, |cx| {
let inner = match trait_item.kind { let inner = match trait_item.kind {
hir::TraitItemKind::Const(ty, Some(default)) => AssocConstItem( hir::TraitItemKind::Const(ty, Some(default)) => {
clean_ty(ty, cx), let generics = enter_impl_trait(cx, |cx| clean_generics(trait_item.generics, cx));
ConstantKind::Local { def_id: local_did, body: default }, AssocConstItem(
), Box::new(generics),
hir::TraitItemKind::Const(ty, None) => TyAssocConstItem(clean_ty(ty, cx)), 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)) => { hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => {
let m = clean_function(cx, sig, trait_item.generics, FunctionArgs::Body(body)); let m = clean_function(cx, sig, trait_item.generics, FunctionArgs::Body(body));
MethodItem(m, None) MethodItem(m, None)
@ -1237,8 +1246,9 @@ pub(crate) fn clean_impl_item<'tcx>(
cx.with_param_env(local_did, |cx| { cx.with_param_env(local_did, |cx| {
let inner = match impl_.kind { let inner = match impl_.kind {
hir::ImplItemKind::Const(ty, expr) => { hir::ImplItemKind::Const(ty, expr) => {
let generics = clean_generics(impl_.generics, cx);
let default = ConstantKind::Local { def_id: local_did, body: expr }; 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) => { hir::ImplItemKind::Fn(ref sig, body) => {
let m = clean_function(cx, sig, impl_.generics, FunctionArgs::Body(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, 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 { let provided = match assoc_item.container {
ty::ImplContainer => true, ty::ImplContainer => true,
ty::TraitContainer => tcx.defaultness(assoc_item.def_id).has_value(), ty::TraitContainer => tcx.defaultness(assoc_item.def_id).has_value(),
}; };
if provided { if provided {
AssocConstItem(ty, ConstantKind::Extern { def_id: assoc_item.def_id }) AssocConstItem(generics, ty, ConstantKind::Extern { def_id: assoc_item.def_id })
} else { } else {
TyAssocConstItem(ty) TyAssocConstItem(generics, ty)
} }
} }
ty::AssocKind::Fn => { ty::AssocKind::Fn => {
@ -1379,34 +1396,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
tcx.generics_of(assoc_item.def_id), tcx.generics_of(assoc_item.def_id),
ty::GenericPredicates { parent: None, predicates }, ty::GenericPredicates { parent: None, predicates },
); );
// Move bounds that are (likely) directly attached to the parameters of the simplify::move_bounds_to_generic_parameters(&mut generics);
// (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| &param.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| &param.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;
if let ty::TraitContainer = assoc_item.container { if let ty::TraitContainer = assoc_item.container {
// Move bounds that are (likely) directly attached to the associated type // 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) => { ItemKind::Static(ty, mutability, body_id) => {
StaticItem(Static { type_: clean_ty(ty, cx), mutability, expr: Some(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), type_: clean_ty(ty, cx),
generics: Box::new(clean_generics(generics, cx)),
kind: ConstantKind::Local { body: body_id, def_id }, kind: ConstantKind::Local { body: body_id, def_id },
}), }),
ItemKind::OpaqueTy(ref ty) => OpaqueTyItem(OpaqueTy { ItemKind::OpaqueTy(ref ty) => OpaqueTyItem(OpaqueTy {

View file

@ -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_)) .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| &param.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| &param.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;
}

View file

@ -824,9 +824,9 @@ pub(crate) enum ItemKind {
ProcMacroItem(ProcMacro), ProcMacroItem(ProcMacro),
PrimitiveItem(PrimitiveType), PrimitiveItem(PrimitiveType),
/// A required associated constant in a trait declaration. /// A required associated constant in a trait declaration.
TyAssocConstItem(Type), TyAssocConstItem(Box<Generics>, Type),
/// An associated constant in a trait impl or a provided one in a trait declaration. /// An associated constant in a trait impl or a provided one in a trait declaration.
AssocConstItem(Type, ConstantKind), AssocConstItem(Box<Generics>, Type, ConstantKind),
/// A required associated type in a trait declaration. /// A required associated type in a trait declaration.
/// ///
/// The bounds may be non-empty if there is a `where` clause. /// The bounds may be non-empty if there is a `where` clause.
@ -871,8 +871,8 @@ impl ItemKind {
| MacroItem(_) | MacroItem(_)
| ProcMacroItem(_) | ProcMacroItem(_)
| PrimitiveItem(_) | PrimitiveItem(_)
| TyAssocConstItem(_) | TyAssocConstItem(..)
| AssocConstItem(_, _) | AssocConstItem(..)
| TyAssocTypeItem(..) | TyAssocTypeItem(..)
| AssocTypeItem(..) | AssocTypeItem(..)
| StrippedItem(_) | StrippedItem(_)
@ -1278,7 +1278,7 @@ impl Lifetime {
} }
} }
#[derive(Clone, Debug)] #[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub(crate) enum WherePredicate { pub(crate) enum WherePredicate {
BoundPredicate { ty: Type, bounds: Vec<GenericBound>, bound_params: Vec<GenericParamDef> }, BoundPredicate { ty: Type, bounds: Vec<GenericBound>, bound_params: Vec<GenericParamDef> },
RegionPredicate { lifetime: Lifetime, bounds: Vec<GenericBound> }, RegionPredicate { lifetime: Lifetime, bounds: Vec<GenericBound> },
@ -1348,7 +1348,7 @@ impl GenericParamDef {
} }
// maybe use a Generic enum and use Vec<Generic>? // maybe use a Generic enum and use Vec<Generic>?
#[derive(Clone, Debug, Default)] #[derive(Clone, PartialEq, Eq, Hash, Debug, Default)]
pub(crate) struct Generics { pub(crate) struct Generics {
pub(crate) params: ThinVec<GenericParamDef>, pub(crate) params: ThinVec<GenericParamDef>,
pub(crate) where_predicates: ThinVec<WherePredicate>, pub(crate) where_predicates: ThinVec<WherePredicate>,
@ -2266,6 +2266,7 @@ pub(crate) struct Static {
#[derive(Clone, PartialEq, Eq, Hash, Debug)] #[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub(crate) struct Constant { pub(crate) struct Constant {
pub(crate) type_: Type, pub(crate) type_: Type,
pub(crate) generics: Box<Generics>,
pub(crate) kind: ConstantKind, pub(crate) kind: ConstantKind,
} }
@ -2515,7 +2516,8 @@ mod size_asserts {
static_assert_size!(GenericParamDef, 56); static_assert_size!(GenericParamDef, 56);
static_assert_size!(Generics, 16); static_assert_size!(Generics, 16);
static_assert_size!(Item, 56); 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!(PathSegment, 40);
static_assert_size!(Type, 32); static_assert_size!(Type, 32);
// tidy-alphabetical-end // tidy-alphabetical-end

View file

@ -748,20 +748,22 @@ fn assoc_href_attr(it: &clean::Item, link: AssocItemLink<'_>, cx: &Context<'_>)
fn assoc_const( fn assoc_const(
w: &mut Buffer, w: &mut Buffer,
it: &clean::Item, it: &clean::Item,
generics: &clean::Generics,
ty: &clean::Type, ty: &clean::Type,
default: Option<&clean::ConstantKind>, default: Option<&clean::ConstantKind>,
link: AssocItemLink<'_>, link: AssocItemLink<'_>,
extra: &str, indent: usize,
cx: &Context<'_>, cx: &Context<'_>,
) { ) {
let tcx = cx.tcx(); let tcx = cx.tcx();
write!( write!(
w, w,
"{extra}{vis}const <a{href} class=\"constant\">{name}</a>: {ty}", "{indent}{vis}const <a{href} class=\"constant\">{name}</a>{generics}: {ty}",
extra = extra, indent = " ".repeat(indent),
vis = visibility_print_with_space(it.visibility(tcx), it.item_id, cx), vis = visibility_print_with_space(it.visibility(tcx), it.item_id, cx),
href = assoc_href_attr(it, link, cx), href = assoc_href_attr(it, link, cx),
name = it.name.as_ref().unwrap(), name = it.name.as_ref().unwrap(),
generics = generics.print(cx),
ty = ty.print(cx), ty = ty.print(cx),
); );
if let Some(default) = default { if let Some(default) = default {
@ -774,6 +776,7 @@ fn assoc_const(
// Find a way to print constants here without all that jazz. // 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, "{}", Escape(&default.value(tcx).unwrap_or_else(|| default.expr(tcx))));
} }
write!(w, "{}", print_where_clause(generics, cx, indent, Ending::NoNewline));
} }
fn assoc_type( fn assoc_type(
@ -986,19 +989,22 @@ fn render_assoc_item(
clean::MethodItem(m, _) => { clean::MethodItem(m, _) => {
assoc_method(w, item, &m.generics, &m.decl, link, parent, cx, render_mode) assoc_method(w, item, &m.generics, &m.decl, link, parent, cx, render_mode)
} }
kind @ (clean::TyAssocConstItem(ty) | clean::AssocConstItem(ty, _)) => assoc_const( kind @ (clean::TyAssocConstItem(generics, ty) | clean::AssocConstItem(generics, ty, _)) => {
w, assoc_const(
item, w,
ty, item,
match kind { generics,
clean::TyAssocConstItem(_) => None, ty,
clean::AssocConstItem(_, default) => Some(default), match kind {
_ => unreachable!(), clean::TyAssocConstItem(..) => None,
}, clean::AssocConstItem(.., default) => Some(default),
link, _ => unreachable!(),
if parent == ItemType::Trait { " " } else { "" }, },
cx, link,
), if parent == ItemType::Trait { 4 } else { 0 },
cx,
)
}
clean::TyAssocTypeItem(ref generics, ref bounds) => assoc_type( clean::TyAssocTypeItem(ref generics, ref bounds) => assoc_type(
w, w,
item, item,
@ -1565,7 +1571,8 @@ fn render_impl(
w.write_str("</section>"); w.write_str("</section>");
} }
} }
kind @ (clean::TyAssocConstItem(ty) | clean::AssocConstItem(ty, _)) => { kind @ (clean::TyAssocConstItem(generics, ty)
| clean::AssocConstItem(generics, ty, _)) => {
let source_id = format!("{}.{}", item_type, name); let source_id = format!("{}.{}", item_type, name);
let id = cx.derive_id(source_id.clone()); let id = cx.derive_id(source_id.clone());
write!(w, "<section id=\"{}\" class=\"{}{}\">", id, item_type, in_trait_class); write!(w, "<section id=\"{}\" class=\"{}{}\">", id, item_type, in_trait_class);
@ -1578,14 +1585,15 @@ fn render_impl(
assoc_const( assoc_const(
w, w,
item, item,
generics,
ty, ty,
match kind { match kind {
clean::TyAssocConstItem(_) => None, clean::TyAssocConstItem(..) => None,
clean::AssocConstItem(_, default) => Some(default), clean::AssocConstItem(.., default) => Some(default),
_ => unreachable!(), _ => unreachable!(),
}, },
link.anchor(if trait_.is_some() { &source_id } else { &id }), link.anchor(if trait_.is_some() { &source_id } else { &id }),
"", 0,
cx, cx,
); );
w.write_str("</h4>"); w.write_str("</h4>");

View file

@ -1543,10 +1543,12 @@ fn item_constant(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, c: &cle
write!( write!(
w, w,
"{vis}const {name}: {typ}", "{vis}const {name}{generics}: {typ}{where_clause}",
vis = visibility_print_with_space(it.visibility(tcx), it.item_id, cx), vis = visibility_print_with_space(it.visibility(tcx), it.item_id, cx),
name = it.name.unwrap(), name = it.name.unwrap(),
generics = c.generics.print(cx),
typ = c.type_.print(cx), typ = c.type_.print(cx),
where_clause = print_where_clause(&c.generics, cx, 0, Ending::NoNewline),
); );
// FIXME: The code below now prints // FIXME: The code below now prints

View file

@ -219,7 +219,7 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> {
fn visit_item(&mut self, item: &'tcx Item<'tcx>) { fn visit_item(&mut self, item: &'tcx Item<'tcx>) {
match item.kind { match item.kind {
ItemKind::Static(_, _, _) ItemKind::Static(_, _, _)
| ItemKind::Const(_, _) | ItemKind::Const(_, _, _)
| ItemKind::Fn(_, _, _) | ItemKind::Fn(_, _, _)
| ItemKind::Macro(_, _) | ItemKind::Macro(_, _)
| ItemKind::TyAlias(_, _) | ItemKind::TyAlias(_, _)

View file

@ -171,6 +171,7 @@ impl FromWithTcx<clean::GenericArg> for GenericArg {
} }
impl FromWithTcx<clean::Constant> for Constant { impl FromWithTcx<clean::Constant> for Constant {
// FIXME(generic_const_items): Add support for generic const items.
fn from_tcx(constant: clean::Constant, tcx: TyCtxt<'_>) -> Self { fn from_tcx(constant: clean::Constant, tcx: TyCtxt<'_>) -> Self {
let expr = constant.expr(tcx); let expr = constant.expr(tcx);
let value = constant.value(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 impls: Vec::new(), // Added in JsonRenderer::item
}) })
} }
TyAssocConstItem(ty) => ItemEnum::AssocConst { type_: ty.into_tcx(tcx), default: None }, // FIXME(generic_const_items): Add support for generic associated consts.
AssocConstItem(ty, default) => { 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)) } ItemEnum::AssocConst { type_: ty.into_tcx(tcx), default: Some(default.expr(tcx)) }
} }
TyAssocTypeItem(g, b) => ItemEnum::AssocType { TyAssocTypeItem(g, b) => ItemEnum::AssocType {