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: use JS to inline target type impl docs into alias #116471

Merged
merged 12 commits into from
Oct 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4688,6 +4688,7 @@ dependencies = [
"arrayvec",
"askama",
"expect-test",
"indexmap 2.0.0",
"itertools",
"minifier",
"once_cell",
Expand Down
1 change: 1 addition & 0 deletions src/librustdoc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ path = "lib.rs"
arrayvec = { version = "0.7", default-features = false }
askama = { version = "0.12", default-features = false, features = ["config"] }
itertools = "0.10.1"
indexmap = "2"
minifier = "0.2.3"
once_cell = "1.10.0"
regex = "1"
Expand Down
12 changes: 9 additions & 3 deletions src/librustdoc/clean/inline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ use crate::clean::{
use crate::core::DocContext;
use crate::formats::item_type::ItemType;

use super::Item;

/// Attempt to inline a definition into this AST.
///
/// This function will fetch the definition specified, and if it is
Expand Down Expand Up @@ -83,7 +85,7 @@ pub(crate) fn try_inline(
Res::Def(DefKind::TyAlias, did) => {
record_extern_fqn(cx, did, ItemType::TypeAlias);
build_impls(cx, did, attrs_without_docs, &mut ret);
clean::TypeAliasItem(build_type_alias(cx, did))
clean::TypeAliasItem(build_type_alias(cx, did, &mut ret))
}
Res::Def(DefKind::Enum, did) => {
record_extern_fqn(cx, did, ItemType::Enum);
Expand Down Expand Up @@ -281,11 +283,15 @@ fn build_union(cx: &mut DocContext<'_>, did: DefId) -> clean::Union {
clean::Union { generics, fields }
}

fn build_type_alias(cx: &mut DocContext<'_>, did: DefId) -> Box<clean::TypeAlias> {
fn build_type_alias(
cx: &mut DocContext<'_>,
did: DefId,
ret: &mut Vec<Item>,
) -> Box<clean::TypeAlias> {
let predicates = cx.tcx.explicit_predicates_of(did);
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);
let inner_type = clean_ty_alias_inner_type(ty, cx, ret);

Box::new(clean::TypeAlias {
type_,
Expand Down
39 changes: 32 additions & 7 deletions src/librustdoc/clean/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -934,18 +934,27 @@ fn clean_ty_generics<'tcx>(
fn clean_ty_alias_inner_type<'tcx>(
ty: Ty<'tcx>,
cx: &mut DocContext<'tcx>,
ret: &mut Vec<Item>,
) -> Option<TypeAliasInnerType> {
let ty::Adt(adt_def, args) = ty.kind() else {
return None;
};

if !adt_def.did().is_local() {
inline::build_impls(cx, adt_def.did(), None, ret);
}

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();

if !adt_def.did().is_local() {
inline::record_extern_fqn(cx, adt_def.did(), ItemType::Enum);
}

TypeAliasInnerType::Enum {
variants,
is_non_exhaustive: adt_def.is_variant_list_non_exhaustive(),
Expand All @@ -961,8 +970,14 @@ fn clean_ty_alias_inner_type<'tcx>(
clean_variant_def_with_args(variant, args, cx).kind.inner_items().cloned().collect();

if adt_def.is_struct() {
if !adt_def.did().is_local() {
inline::record_extern_fqn(cx, adt_def.did(), ItemType::Struct);
}
TypeAliasInnerType::Struct { ctor_kind: variant.ctor_kind(), fields }
} else {
if !adt_def.did().is_local() {
inline::record_extern_fqn(cx, adt_def.did(), ItemType::Union);
}
TypeAliasInnerType::Union { fields }
}
})
Expand Down Expand Up @@ -2744,14 +2759,24 @@ fn clean_maybe_renamed_item<'tcx>(
}

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

TypeAliasItem(Box::new(TypeAlias {
generics,
inner_type,
type_: rustdoc_ty,
item_type: Some(type_),
}))
let mut ret = Vec::new();
let inner_type = clean_ty_alias_inner_type(ty, cx, &mut ret);

ret.push(generate_item_with_correct_attrs(
cx,
TypeAliasItem(Box::new(TypeAlias {
generics,
inner_type,
type_: rustdoc_ty,
item_type: Some(type_),
})),
item.owner_id.def_id.to_def_id(),
name,
import_id,
renamed,
));
return ret;
}
ItemKind::Enum(ref def, generics) => EnumItem(Enum {
variants: def.variants.iter().map(|v| clean_variant(v, cx)).collect(),
Expand Down
4 changes: 2 additions & 2 deletions src/librustdoc/formats/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ pub(crate) struct Cache {
/// Unlike 'paths', this mapping ignores any renames that occur
/// due to 'use' statements.
///
/// This map is used when writing out the special 'implementors'
/// javascript file. By using the exact path that the type
/// This map is used when writing out the `impl.trait` and `impl.type`
/// javascript files. By using the exact path that the type
/// is declared with, we ensure that each path will be identical
/// to the path used if the corresponding type is inlined. By
/// doing this, we can detect duplicate impls on a trait page, and only display
Expand Down
3 changes: 3 additions & 0 deletions src/librustdoc/formats/item_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,9 @@ impl ItemType {
pub(crate) fn is_method(&self) -> bool {
matches!(*self, ItemType::Method | ItemType::TyMethod)
}
pub(crate) fn is_adt(&self) -> bool {
matches!(*self, ItemType::Struct | ItemType::Union | ItemType::Enum)
}
}

impl fmt::Display for ItemType {
Expand Down
53 changes: 2 additions & 51 deletions src/librustdoc/html/render/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,8 @@ use std::sync::mpsc::{channel, Receiver};

use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir::def_id::{DefIdMap, LOCAL_CRATE};
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
use rustc_middle::ty::TyCtxt;
use rustc_session::Session;
use rustc_span::def_id::DefId;
use rustc_span::edition::Edition;
use rustc_span::source_map::FileName;
use rustc_span::{sym, Symbol};
Expand All @@ -25,13 +23,13 @@ use super::{
AllTypes, LinkFromSrc, StylePath,
};
use crate::clean::utils::has_doc_flag;
use crate::clean::{self, types::ExternalLocation, ExternalCrate, TypeAliasItem};
use crate::clean::{self, types::ExternalLocation, ExternalCrate};
use crate::config::{ModuleSorting, RenderOptions};
use crate::docfs::{DocFS, PathError};
use crate::error::Error;
use crate::formats::cache::Cache;
use crate::formats::item_type::ItemType;
use crate::formats::{self, FormatRenderer};
use crate::formats::FormatRenderer;
use crate::html::escape::Escape;
use crate::html::format::{join_with_double_colon, Buffer};
use crate::html::markdown::{self, plain_text_summary, ErrorCodes, IdMap};
Expand Down Expand Up @@ -150,53 +148,6 @@ impl SharedContext<'_> {
pub(crate) fn edition(&self) -> Edition {
self.tcx.sess.edition()
}

/// Returns a list of impls on the given type, and, if it's a type alias,
/// other types that it aliases.
pub(crate) fn all_impls_for_item<'a>(
&'a self,
it: &clean::Item,
did: DefId,
) -> Vec<&'a formats::Impl> {
let tcx = self.tcx;
let cache = &self.cache;
let mut saw_impls = FxHashSet::default();
let mut v: Vec<&formats::Impl> = cache
.impls
.get(&did)
.map(Vec::as_slice)
.unwrap_or(&[])
.iter()
.filter(|i| saw_impls.insert(i.def_id()))
.collect();
if let TypeAliasItem(ait) = &*it.kind &&
let aliased_clean_type = ait.item_type.as_ref().unwrap_or(&ait.type_) &&
let Some(aliased_type_defid) = aliased_clean_type.def_id(cache) &&
let Some(av) = cache.impls.get(&aliased_type_defid) &&
let Some(alias_def_id) = it.item_id.as_def_id()
{
// This branch of the compiler compares types structually, but does
// not check trait bounds. That's probably fine, since type aliases
// don't normally constrain on them anyway.
// https://github.com/rust-lang/rust/issues/21903
//
// FIXME(lazy_type_alias): Once the feature is complete or stable, rewrite this to use type unification.
// Be aware of `tests/rustdoc/issue-112515-impl-ty-alias.rs` which might regress.
let aliased_ty = tcx.type_of(alias_def_id).skip_binder();
let reject_cx = DeepRejectCtxt {
treat_obligation_params: TreatParams::AsCandidateKey,
};
v.extend(av.iter().filter(|impl_| {
if let Some(impl_def_id) = impl_.impl_item.item_id.as_def_id() {
reject_cx.types_may_unify(aliased_ty, tcx.type_of(impl_def_id).skip_binder())
&& saw_impls.insert(impl_def_id)
} else {
false
}
}));
}
v
}
}

impl<'tcx> Context<'tcx> {
Expand Down
25 changes: 9 additions & 16 deletions src/librustdoc/html/render/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1132,13 +1132,13 @@ pub(crate) fn render_all_impls(
fn render_assoc_items<'a, 'cx: 'a>(
cx: &'a mut Context<'cx>,
containing_item: &'a clean::Item,
did: DefId,
it: DefId,
what: AssocItemRender<'a>,
) -> impl fmt::Display + 'a + Captures<'cx> {
let mut derefs = DefIdSet::default();
derefs.insert(did);
derefs.insert(it);
display_fn(move |f| {
render_assoc_items_inner(f, cx, containing_item, did, what, &mut derefs);
render_assoc_items_inner(f, cx, containing_item, it, what, &mut derefs);
Ok(())
})
}
Expand All @@ -1147,17 +1147,15 @@ fn render_assoc_items_inner(
mut w: &mut dyn fmt::Write,
cx: &mut Context<'_>,
containing_item: &clean::Item,
did: DefId,
it: DefId,
what: AssocItemRender<'_>,
derefs: &mut DefIdSet,
) {
info!("Documenting associated items of {:?}", containing_item.name);
let shared = Rc::clone(&cx.shared);
let v = shared.all_impls_for_item(containing_item, did);
let v = v.as_slice();
let (non_trait, traits): (Vec<&Impl>, _) =
v.iter().partition(|i| i.inner_impl().trait_.is_none());
let mut saw_impls = FxHashSet::default();
let cache = &shared.cache;
let Some(v) = cache.impls.get(&it) else { return };
let (non_trait, traits): (Vec<_>, _) = v.iter().partition(|i| i.inner_impl().trait_.is_none());
if !non_trait.is_empty() {
let mut tmp_buf = Buffer::html();
let (render_mode, id, class_html) = match what {
Expand Down Expand Up @@ -1186,9 +1184,6 @@ fn render_assoc_items_inner(
};
let mut impls_buf = Buffer::html();
for i in &non_trait {
if !saw_impls.insert(i.def_id()) {
continue;
}
render_impl(
&mut impls_buf,
cx,
Expand Down Expand Up @@ -1234,10 +1229,8 @@ fn render_assoc_items_inner(

let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
traits.into_iter().partition(|t| t.inner_impl().kind.is_auto());
let (blanket_impl, concrete): (Vec<&Impl>, _) = concrete
.into_iter()
.filter(|t| saw_impls.insert(t.def_id()))
.partition(|t| t.inner_impl().kind.is_blanket());
let (blanket_impl, concrete): (Vec<&Impl>, _) =
concrete.into_iter().partition(|t| t.inner_impl().kind.is_blanket());

render_all_impls(w, cx, containing_item, &concrete, &synthetic, &blanket_impl);
}
Expand Down
Loading
Loading