Skip to content
16 changes: 16 additions & 0 deletions src/doc/rustdoc/src/how-to-read-rustdoc.md
Original file line number Diff line number Diff line change
@@ -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
13 changes: 6 additions & 7 deletions src/librustdoc/clean/inline.rs
Original file line number Diff line number Diff line change
@@ -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;
@@ -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,
})
}
136 changes: 132 additions & 4 deletions src/librustdoc/clean/mod.rs
Original file line number Diff line number Diff line change
@@ -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};
@@ -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,
@@ -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,
@@ -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(),
)
}
@@ -1471,6 +1515,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
None,
),
generics,
inner_type: None,
item_type: None,
}),
bounds,
@@ -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
@@ -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);

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>,
@@ -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,
@@ -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 {
10 changes: 10 additions & 0 deletions src/librustdoc/clean/types.rs
Original file line number Diff line number Diff line change
@@ -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.
23 changes: 22 additions & 1 deletion src/librustdoc/fold.rs
Original file line number Diff line number Diff line change
@@ -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(_)
1 change: 1 addition & 0 deletions src/librustdoc/formats/cache.rs
Original file line number Diff line number Diff line change
@@ -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)
Loading