Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rustdoc: show inner enum and struct in type definition for concrete type #114855

Merged
merged 8 commits into from Sep 7, 2023
16 changes: 16 additions & 0 deletions src/doc/rustdoc/src/how-to-read-rustdoc.md
Expand Up @@ -38,6 +38,22 @@ followed by a list of fields or variants for Rust types.
Finally, the page lists associated functions and trait implementations,
including automatic and blanket implementations that `rustdoc` knows about.

### Sections

<!-- FIXME: Implementations -->
<!-- FIXME: Trait Implementations -->
<!-- FIXME: Implementors -->
<!-- FIXME: Auto Trait Implementations -->

#### Aliased Type

A type alias is expanded at compile time to its
[aliased type](https://doc.rust-lang.org/reference/items/type-aliases.html).
That may involve substituting some or all of the type parameters in the target
type with types provided by the type alias definition. The Aliased Type section
shows the result of this expansion, including the types of public fields or
variants, which may depend on those substitutions.

### Navigation

Subheadings, variants, fields, and many other things in this documentation
Expand Down
13 changes: 6 additions & 7 deletions src/librustdoc/clean/inline.rs
Expand Up @@ -20,7 +20,8 @@ use rustc_span::symbol::{kw, sym, Symbol};
use crate::clean::{
self, clean_fn_decl_from_did_and_sig, clean_generics, clean_impl_item, clean_middle_assoc_item,
clean_middle_field, clean_middle_ty, clean_trait_ref_with_bindings, clean_ty,
clean_ty_generics, clean_variant_def, utils, Attributes, AttributesExt, ImplKind, ItemId, Type,
clean_ty_alias_inner_type, clean_ty_generics, clean_variant_def, utils, Attributes,
AttributesExt, ImplKind, ItemId, Type,
};
use crate::core::DocContext;
use crate::formats::item_type::ItemType;
Expand Down Expand Up @@ -289,16 +290,14 @@ fn build_union(cx: &mut DocContext<'_>, did: DefId) -> clean::Union {

fn build_type_alias(cx: &mut DocContext<'_>, did: DefId) -> Box<clean::TypeAlias> {
let predicates = cx.tcx.explicit_predicates_of(did);
let type_ = clean_middle_ty(
ty::Binder::dummy(cx.tcx.type_of(did).instantiate_identity()),
cx,
Some(did),
None,
);
let ty = cx.tcx.type_of(did).instantiate_identity();
let type_ = clean_middle_ty(ty::Binder::dummy(ty), cx, Some(did), None);
let inner_type = clean_ty_alias_inner_type(ty, cx);

Box::new(clean::TypeAlias {
type_,
generics: clean_ty_generics(cx, cx.tcx.generics_of(did), predicates),
inner_type,
item_type: None,
})
}
Expand Down
136 changes: 132 additions & 4 deletions src/librustdoc/clean/mod.rs
Expand Up @@ -24,6 +24,7 @@ use rustc_infer::infer::region_constraints::{Constraint, RegionConstraintData};
use rustc_middle::metadata::Reexport;
use rustc_middle::middle::resolve_bound_vars as rbv;
use rustc_middle::ty::fold::TypeFolder;
use rustc_middle::ty::GenericArgsRef;
use rustc_middle::ty::TypeVisitableExt;
use rustc_middle::ty::{self, AdtKind, EarlyBinder, Ty, TyCtxt};
use rustc_middle::{bug, span_bug};
Expand Down Expand Up @@ -955,6 +956,43 @@ fn clean_ty_generics<'tcx>(
}
}

fn clean_ty_alias_inner_type<'tcx>(
ty: Ty<'tcx>,
cx: &mut DocContext<'tcx>,
) -> Option<TypeAliasInnerType> {
let ty::Adt(adt_def, args) = ty.kind() else {
return None;
};

Some(if adt_def.is_enum() {
let variants: rustc_index::IndexVec<_, _> = adt_def
.variants()
.iter()
.map(|variant| clean_variant_def_with_args(variant, args, cx))
.collect();

TypeAliasInnerType::Enum {
variants,
is_non_exhaustive: adt_def.is_variant_list_non_exhaustive(),
}
} else {
let variant = adt_def
.variants()
.iter()
.next()
.unwrap_or_else(|| bug!("a struct or union should always have one variant def"));

let fields: Vec<_> =
clean_variant_def_with_args(variant, args, cx).kind.inner_items().cloned().collect();

if adt_def.is_struct() {
TypeAliasInnerType::Struct { ctor_kind: variant.ctor_kind(), fields }
} else {
TypeAliasInnerType::Union { fields }
}
})
}

fn clean_proc_macro<'tcx>(
item: &hir::Item<'tcx>,
name: &mut Symbol,
Expand Down Expand Up @@ -1222,6 +1260,7 @@ fn clean_trait_item<'tcx>(trait_item: &hir::TraitItem<'tcx>, cx: &mut DocContext
Box::new(TypeAlias {
type_: clean_ty(default, cx),
generics,
inner_type: None,
item_type: Some(item_type),
}),
bounds,
Expand Down Expand Up @@ -1264,7 +1303,12 @@ pub(crate) fn clean_impl_item<'tcx>(
None,
);
AssocTypeItem(
Box::new(TypeAlias { type_, generics, item_type: Some(item_type) }),
Box::new(TypeAlias {
type_,
generics,
inner_type: None,
item_type: Some(item_type),
}),
Vec::new(),
)
}
Expand Down Expand Up @@ -1471,6 +1515,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
None,
),
generics,
inner_type: None,
item_type: None,
}),
bounds,
Expand All @@ -1490,6 +1535,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
None,
),
generics,
inner_type: None,
item_type: None,
}),
// Associated types inside trait or inherent impls are not allowed to have
Expand Down Expand Up @@ -2350,6 +2396,83 @@ pub(crate) fn clean_variant_def<'tcx>(variant: &ty::VariantDef, cx: &mut DocCont
)
}

pub(crate) fn clean_variant_def_with_args<'tcx>(
variant: &ty::VariantDef,
args: &GenericArgsRef<'tcx>,
cx: &mut DocContext<'tcx>,
) -> Item {
let discriminant = match variant.discr {
ty::VariantDiscr::Explicit(def_id) => Some(Discriminant { expr: None, value: def_id }),
ty::VariantDiscr::Relative(_) => None,
};

use rustc_middle::traits::ObligationCause;
use rustc_trait_selection::infer::TyCtxtInferExt;
use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt;

let infcx = cx.tcx.infer_ctxt().build();
let kind = match variant.ctor_kind() {
Some(CtorKind::Const) => VariantKind::CLike,
Some(CtorKind::Fn) => VariantKind::Tuple(
variant
.fields
.iter()
.map(|field| {
let ty = cx.tcx.type_of(field.did).instantiate(cx.tcx, args);

// normalize the type to only show concrete types
// note: we do not use try_normalize_erasing_regions since we
// do care about showing the regions
let ty = infcx
.at(&ObligationCause::dummy(), cx.param_env)
.query_normalize(ty)
.map(|normalized| normalized.value)
.unwrap_or(ty);
Urgau marked this conversation as resolved.
Show resolved Hide resolved

clean_field_with_def_id(
field.did,
field.name,
clean_middle_ty(ty::Binder::dummy(ty), cx, Some(field.did), None),
cx,
)
})
.collect(),
),
None => VariantKind::Struct(VariantStruct {
fields: variant
.fields
.iter()
.map(|field| {
let ty = cx.tcx.type_of(field.did).instantiate(cx.tcx, args);

// normalize the type to only show concrete types
// note: we do not use try_normalize_erasing_regions since we
// do care about showing the regions
let ty = infcx
.at(&ObligationCause::dummy(), cx.param_env)
.query_normalize(ty)
.map(|normalized| normalized.value)
.unwrap_or(ty);

clean_field_with_def_id(
field.did,
field.name,
clean_middle_ty(ty::Binder::dummy(ty), cx, Some(field.did), None),
cx,
)
})
.collect(),
}),
};

Item::from_def_id_and_parts(
variant.def_id,
Some(variant.name),
VariantItem(Variant { kind, discriminant }),
cx,
)
}

fn clean_variant_data<'tcx>(
variant: &hir::VariantData<'tcx>,
disr_expr: &Option<hir::AnonConst>,
Expand Down Expand Up @@ -2604,7 +2727,7 @@ fn clean_maybe_renamed_item<'tcx>(
ItemKind::TyAlias(hir_ty, generics) => {
*cx.current_type_aliases.entry(def_id).or_insert(0) += 1;
let rustdoc_ty = clean_ty(hir_ty, cx);
let ty = clean_middle_ty(
let type_ = clean_middle_ty(
ty::Binder::dummy(hir_ty_to_ty(cx.tcx, hir_ty)),
cx,
None,
Expand All @@ -2617,10 +2740,15 @@ fn clean_maybe_renamed_item<'tcx>(
cx.current_type_aliases.remove(&def_id);
}
}

let ty = cx.tcx.type_of(def_id).instantiate_identity();
let inner_type = clean_ty_alias_inner_type(ty, cx);

TypeAliasItem(Box::new(TypeAlias {
type_: rustdoc_ty,
generics,
item_type: Some(ty),
inner_type,
type_: rustdoc_ty,
item_type: Some(type_),
}))
}
ItemKind::Enum(ref def, generics) => EnumItem(Enum {
Expand Down
10 changes: 10 additions & 0 deletions src/librustdoc/clean/types.rs
Expand Up @@ -2229,10 +2229,20 @@ pub(crate) struct PathSegment {
pub(crate) args: GenericArgs,
}

#[derive(Clone, Debug)]
pub(crate) enum TypeAliasInnerType {
Enum { variants: IndexVec<VariantIdx, Item>, is_non_exhaustive: bool },
Union { fields: Vec<Item> },
Struct { ctor_kind: Option<CtorKind>, fields: Vec<Item> },
}

#[derive(Clone, Debug)]
pub(crate) struct TypeAlias {
pub(crate) type_: Type,
pub(crate) generics: Generics,
/// Inner `AdtDef` type, ie `type TyKind = IrTyKind<Adt, Ty>`,
/// to be shown directly on the typedef page.
pub(crate) inner_type: Option<TypeAliasInnerType>,
/// `type_` can come from either the HIR or from metadata. If it comes from HIR, it may be a type
/// alias instead of the final type. This will always have the final type, regardless of whether
/// `type_` came from HIR or from metadata.
Expand Down
23 changes: 22 additions & 1 deletion src/librustdoc/fold.rs
Expand Up @@ -52,10 +52,31 @@ pub(crate) trait DocFolder: Sized {

VariantItem(Variant { kind, discriminant })
}
TypeAliasItem(mut typealias) => {
typealias.inner_type = typealias.inner_type.map(|inner_type| match inner_type {
TypeAliasInnerType::Enum { variants, is_non_exhaustive } => {
let variants = variants
.into_iter_enumerated()
.filter_map(|(_, x)| self.fold_item(x))
.collect();

TypeAliasInnerType::Enum { variants, is_non_exhaustive }
}
TypeAliasInnerType::Union { fields } => {
let fields = fields.into_iter().filter_map(|x| self.fold_item(x)).collect();
TypeAliasInnerType::Union { fields }
}
TypeAliasInnerType::Struct { ctor_kind, fields } => {
let fields = fields.into_iter().filter_map(|x| self.fold_item(x)).collect();
TypeAliasInnerType::Struct { ctor_kind, fields }
}
});

TypeAliasItem(typealias)
}
ExternCrateItem { src: _ }
| ImportItem(_)
| FunctionItem(_)
| TypeAliasItem(_)
| OpaqueTyItem(_)
| StaticItem(_)
| ConstantItem(_)
Expand Down
1 change: 1 addition & 0 deletions src/librustdoc/formats/cache.rs
Expand Up @@ -457,6 +457,7 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
| clean::StructItem(..)
| clean::UnionItem(..)
| clean::VariantItem(..)
| clean::TypeAliasItem(..)
| clean::ImplItem(..) => {
self.cache.parent_stack.push(ParentStackItem::new(&item));
(self.fold_item_recur(item), true)
Expand Down