Skip to content

[PoC] Rustdoc move hir cleaners #18

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

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
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
13 changes: 5 additions & 8 deletions src/librustdoc/clean/auto_trait.rs
Original file line number Diff line number Diff line change
@@ -11,8 +11,8 @@ use thin_vec::ThinVec;

use crate::clean::{self, simplify, Lifetime};
use crate::clean::{
clean_generic_param_def, clean_middle_ty, clean_predicate, clean_trait_ref_with_constraints,
clean_ty_generics,
clean_generic_param_def, clean_generics, clean_predicate, clean_trait_ref_with_constraints,
clean_ty,
};
use crate::core::DocContext;

@@ -102,11 +102,8 @@ fn synthesize_auto_trait_impl<'tcx>(
// Instead, we generate `impl !Send for Foo<T>`, which better
// expresses the fact that `Foo<T>` never implements `Send`,
// regardless of the choice of `T`.
let mut generics = clean_ty_generics(
cx,
tcx.generics_of(item_def_id),
ty::GenericPredicates::default(),
);
let mut generics =
clean_generics(cx, tcx.generics_of(item_def_id), ty::GenericPredicates::default());
generics.where_predicates.clear();

(generics, ty::ImplPolarity::Negative)
@@ -122,7 +119,7 @@ fn synthesize_auto_trait_impl<'tcx>(
safety: hir::Safety::Safe,
generics,
trait_: Some(clean_trait_ref_with_constraints(cx, trait_ref, ThinVec::new())),
for_: clean_middle_ty(ty::Binder::dummy(ty), cx, None, None),
for_: clean_ty(ty::Binder::dummy(ty), cx, None, None),
items: Vec::new(),
polarity,
kind: clean::ImplKind::Auto,
17 changes: 5 additions & 12 deletions src/librustdoc/clean/blanket_impl.rs
Original file line number Diff line number Diff line change
@@ -9,9 +9,7 @@ use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
use thin_vec::ThinVec;

use crate::clean;
use crate::clean::{
clean_middle_assoc_item, clean_middle_ty, clean_trait_ref_with_constraints, clean_ty_generics,
};
use crate::clean::{clean_assoc_item, clean_generics, clean_trait_ref_with_constraints, clean_ty};
use crate::core::DocContext;

#[instrument(level = "debug", skip(cx))]
@@ -88,7 +86,7 @@ pub(crate) fn synthesize_blanket_impls(
item_id: clean::ItemId::Blanket { impl_id: impl_def_id, for_: item_def_id },
kind: Box::new(clean::ImplItem(Box::new(clean::Impl {
safety: hir::Safety::Safe,
generics: clean_ty_generics(
generics: clean_generics(
cx,
tcx.generics_of(impl_def_id),
tcx.explicit_predicates_of(impl_def_id),
@@ -100,20 +98,15 @@ pub(crate) fn synthesize_blanket_impls(
ty::Binder::dummy(trait_ref.instantiate_identity()),
ThinVec::new(),
)),
for_: clean_middle_ty(
ty::Binder::dummy(ty.instantiate_identity()),
cx,
None,
None,
),
for_: clean_ty(ty::Binder::dummy(ty.instantiate_identity()), cx, None, None),
items: tcx
.associated_items(impl_def_id)
.in_definition_order()
.filter(|item| !item.is_impl_trait_in_trait())
.map(|item| clean_middle_assoc_item(item, cx))
.map(|item| clean_assoc_item(item, cx))
.collect(),
polarity: ty::ImplPolarity::Positive,
kind: clean::ImplKind::Blanket(Box::new(clean_middle_ty(
kind: clean::ImplKind::Blanket(Box::new(clean_ty(
ty::Binder::dummy(trait_ref.instantiate_identity().self_ty()),
cx,
None,
53 changes: 23 additions & 30 deletions src/librustdoc/clean/inline.rs
Original file line number Diff line number Diff line change
@@ -19,10 +19,9 @@ use rustc_span::hygiene::MacroKind;
use rustc_span::symbol::{kw, sym, Symbol};

use crate::clean::{
self, clean_bound_vars, clean_generics, clean_impl_item, clean_middle_assoc_item,
clean_middle_field, clean_middle_ty, clean_poly_fn_sig, clean_trait_ref_with_constraints,
clean_ty, clean_ty_alias_inner_type, clean_ty_generics, clean_variant_def, utils, Attributes,
AttributesExt, ImplKind, ItemId, Type,
self, clean_assoc_item, clean_bound_vars, clean_field, clean_generics, clean_poly_fn_sig,
clean_trait_ref_with_constraints, clean_ty, clean_ty_alias_inner_type, clean_variant_def,
local, utils, Attributes, AttributesExt, ImplKind, ItemId, Type,
};
use crate::core::DocContext;
use crate::formats::item_type::ItemType;
@@ -281,11 +280,11 @@ pub(crate) fn build_external_trait(cx: &mut DocContext<'_>, did: DefId) -> clean
.associated_items(did)
.in_definition_order()
.filter(|item| !item.is_impl_trait_in_trait())
.map(|item| clean_middle_assoc_item(item, cx))
.map(|item| clean_assoc_item(item, cx))
.collect();

let predicates = cx.tcx.predicates_of(did);
let generics = clean_ty_generics(cx, cx.tcx.generics_of(did), predicates);
let generics = clean_generics(cx, cx.tcx.generics_of(did), predicates);
let generics = filter_non_trait_generics(did, generics);
let (generics, supertrait_bounds) = separate_supertrait_bounds(generics);
clean::Trait { def_id: did, generics, items: trait_items, bounds: supertrait_bounds }
@@ -298,7 +297,7 @@ pub(crate) fn build_function<'tcx>(
let sig = cx.tcx.fn_sig(def_id).instantiate_identity();
// The generics need to be cleaned before the signature.
let mut generics =
clean_ty_generics(cx, cx.tcx.generics_of(def_id), cx.tcx.explicit_predicates_of(def_id));
clean_generics(cx, cx.tcx.generics_of(def_id), cx.tcx.explicit_predicates_of(def_id));
let bound_vars = clean_bound_vars(sig.bound_vars());

// At the time of writing early & late-bound params are stored separately in rustc,
@@ -330,7 +329,7 @@ fn build_enum(cx: &mut DocContext<'_>, did: DefId) -> clean::Enum {
let predicates = cx.tcx.explicit_predicates_of(did);

clean::Enum {
generics: clean_ty_generics(cx, cx.tcx.generics_of(did), predicates),
generics: clean_generics(cx, cx.tcx.generics_of(did), predicates),
variants: cx.tcx.adt_def(did).variants().iter().map(|v| clean_variant_def(v, cx)).collect(),
}
}
@@ -341,17 +340,17 @@ fn build_struct(cx: &mut DocContext<'_>, did: DefId) -> clean::Struct {

clean::Struct {
ctor_kind: variant.ctor_kind(),
generics: clean_ty_generics(cx, cx.tcx.generics_of(did), predicates),
fields: variant.fields.iter().map(|x| clean_middle_field(x, cx)).collect(),
generics: clean_generics(cx, cx.tcx.generics_of(did), predicates),
fields: variant.fields.iter().map(|x| clean_field(x, cx)).collect(),
}
}

fn build_union(cx: &mut DocContext<'_>, did: DefId) -> clean::Union {
let predicates = cx.tcx.explicit_predicates_of(did);
let variant = cx.tcx.adt_def(did).non_enum_variant();

let generics = clean_ty_generics(cx, cx.tcx.generics_of(did), predicates);
let fields = variant.fields.iter().map(|x| clean_middle_field(x, cx)).collect();
let generics = clean_generics(cx, cx.tcx.generics_of(did), predicates);
let fields = variant.fields.iter().map(|x| clean_field(x, cx)).collect();
clean::Union { generics, fields }
}

@@ -362,12 +361,12 @@ fn build_type_alias(
) -> 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 type_ = clean_ty(ty::Binder::dummy(ty), cx, Some(did), None);
let inner_type = clean_ty_alias_inner_type(ty, cx, ret);

Box::new(clean::TypeAlias {
type_,
generics: clean_ty_generics(cx, cx.tcx.generics_of(did), predicates),
generics: clean_generics(cx, cx.tcx.generics_of(did), predicates),
inner_type,
item_type: None,
})
@@ -477,8 +476,8 @@ pub(crate) fn build_impl(
};

let for_ = match &impl_item {
Some(impl_) => clean_ty(impl_.self_ty, cx),
None => clean_middle_ty(
Some(impl_) => local::clean_ty(impl_.self_ty, cx),
None => clean_ty(
ty::Binder::dummy(tcx.type_of(did).instantiate_identity()),
cx,
Some(did),
@@ -533,9 +532,9 @@ pub(crate) fn build_impl(
true
}
})
.map(|item| clean_impl_item(item, cx))
.map(|item| local::clean_impl_item(item, cx))
.collect::<Vec<_>>(),
clean_generics(impl_.generics, cx),
local::clean_generics(impl_.generics, cx),
),
None => (
tcx.associated_items(did)
@@ -560,11 +559,9 @@ pub(crate) fn build_impl(
item.visibility(tcx).is_public()
}
})
.map(|item| clean_middle_assoc_item(item, cx))
.map(|item| clean_assoc_item(item, cx))
.collect::<Vec<_>>(),
clean::enter_impl_trait(cx, |cx| {
clean_ty_generics(cx, tcx.generics_of(did), predicates)
}),
clean::enter_impl_trait(cx, |cx| clean_generics(cx, tcx.generics_of(did), predicates)),
),
};
let polarity = tcx.impl_polarity(did);
@@ -725,20 +722,16 @@ fn build_const_item(
def_id: DefId,
) -> (clean::Generics, clean::Type, clean::Constant) {
let mut generics =
clean_ty_generics(cx, cx.tcx.generics_of(def_id), cx.tcx.explicit_predicates_of(def_id));
clean_generics(cx, cx.tcx.generics_of(def_id), cx.tcx.explicit_predicates_of(def_id));
clean::simplify::move_bounds_to_generic_parameters(&mut generics);
let ty = clean_middle_ty(
ty::Binder::dummy(cx.tcx.type_of(def_id).instantiate_identity()),
cx,
None,
None,
);
let ty =
clean_ty(ty::Binder::dummy(cx.tcx.type_of(def_id).instantiate_identity()), cx, None, None);
(generics, ty, clean::Constant { kind: clean::ConstantKind::Extern { def_id } })
}

fn build_static(cx: &mut DocContext<'_>, did: DefId, mutable: bool) -> clean::Static {
clean::Static {
type_: clean_middle_ty(
type_: clean_ty(
ty::Binder::dummy(cx.tcx.type_of(did).instantiate_identity()),
cx,
Some(did),
1,451 changes: 1,451 additions & 0 deletions src/librustdoc/clean/local.rs

Large diffs are not rendered by default.

1,725 changes: 232 additions & 1,493 deletions src/librustdoc/clean/mod.rs

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions src/librustdoc/clean/types.rs
Original file line number Diff line number Diff line change
@@ -37,7 +37,7 @@ use rustc_target::abi::VariantIdx;
use rustc_target::spec::abi::Abi;

use crate::clean::cfg::Cfg;
use crate::clean::clean_middle_path;
use crate::clean::clean_path;
use crate::clean::inline::{self, print_inlined_const};
use crate::clean::utils::{is_literal_expr, print_evaluated_const};
use crate::core::DocContext;
@@ -1258,7 +1258,7 @@ impl GenericBound {
fn sized_with(cx: &mut DocContext<'_>, modifier: hir::TraitBoundModifier) -> GenericBound {
let did = cx.tcx.require_lang_item(LangItem::Sized, None);
let empty = ty::Binder::dummy(ty::GenericArgs::empty());
let path = clean_middle_path(cx, did, false, ThinVec::new(), empty);
let path = clean_path(cx, did, false, ThinVec::new(), empty);
inline::record_extern_fqn(cx, did, ItemType::Trait);
GenericBound::TraitBound(PolyTrait { trait_: path, generic_params: Vec::new() }, modifier)
}
188 changes: 3 additions & 185 deletions src/librustdoc/clean/utils.rs
Original file line number Diff line number Diff line change
@@ -2,10 +2,8 @@ use crate::clean::auto_trait::synthesize_auto_trait_impls;
use crate::clean::blanket_impl::synthesize_blanket_impls;
use crate::clean::render_macro_matchers::render_macro_matcher;
use crate::clean::{
clean_doc_module, clean_middle_const, clean_middle_region, clean_middle_ty, inline,
AssocItemConstraint, AssocItemConstraintKind, Crate, ExternalCrate, Generic, GenericArg,
GenericArgs, ImportSource, Item, ItemKind, Lifetime, Path, PathSegment, Primitive,
PrimitiveType, Term, Type,
clean_doc_module, inline, Crate, ExternalCrate, Generic, ImportSource, Item, ItemKind, Path,
Primitive, PrimitiveType, Type,
};
use crate::core::DocContext;
use crate::html::format::visibility_to_src_with_space;
@@ -17,14 +15,11 @@ use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE};
use rustc_metadata::rendered_const;
use rustc_middle::mir;
use rustc_middle::ty::TypeVisitableExt;
use rustc_middle::ty::{self, GenericArgKind, GenericArgsRef, TyCtxt};
use rustc_middle::ty::{self, TyCtxt};
use rustc_span::symbol::{kw, sym, Symbol};
use std::assert_matches::debug_assert_matches;
use std::fmt::Write as _;
use std::mem;
use std::sync::LazyLock as Lazy;
use thin_vec::{thin_vec, ThinVec};

#[cfg(test)]
mod tests;
@@ -77,183 +72,6 @@ pub(crate) fn krate(cx: &mut DocContext<'_>) -> Crate {
Crate { module, external_traits: cx.external_traits.clone() }
}

pub(crate) fn clean_middle_generic_args<'tcx>(
cx: &mut DocContext<'tcx>,
args: ty::Binder<'tcx, &'tcx [ty::GenericArg<'tcx>]>,
mut has_self: bool,
owner: DefId,
) -> Vec<GenericArg> {
let (args, bound_vars) = (args.skip_binder(), args.bound_vars());
if args.is_empty() {
// Fast path which avoids executing the query `generics_of`.
return Vec::new();
}

// If the container is a trait object type, the arguments won't contain the self type but the
// generics of the corresponding trait will. In such a case, prepend a dummy self type in order
// to align the arguments and parameters for the iteration below and to enable us to correctly
// instantiate the generic parameter default later.
let generics = cx.tcx.generics_of(owner);
let args = if !has_self && generics.parent.is_none() && generics.has_self {
has_self = true;
[cx.tcx.types.trait_object_dummy_self.into()]
.into_iter()
.chain(args.iter().copied())
.collect::<Vec<_>>()
.into()
} else {
std::borrow::Cow::from(args)
};

let mut elision_has_failed_once_before = false;
let clean_arg = |(index, &arg): (usize, &ty::GenericArg<'tcx>)| {
// Elide the self type.
if has_self && index == 0 {
return None;
}

// Elide internal host effect args.
let param = generics.param_at(index, cx.tcx);
if param.is_host_effect() {
return None;
}

let arg = ty::Binder::bind_with_vars(arg, bound_vars);

// Elide arguments that coincide with their default.
if !elision_has_failed_once_before && let Some(default) = param.default_value(cx.tcx) {
let default = default.instantiate(cx.tcx, args.as_ref());
if can_elide_generic_arg(arg, arg.rebind(default)) {
return None;
}
elision_has_failed_once_before = true;
}

match arg.skip_binder().unpack() {
GenericArgKind::Lifetime(lt) => {
Some(GenericArg::Lifetime(clean_middle_region(lt).unwrap_or(Lifetime::elided())))
}
GenericArgKind::Type(ty) => Some(GenericArg::Type(clean_middle_ty(
arg.rebind(ty),
cx,
None,
Some(crate::clean::ContainerTy::Regular {
ty: owner,
args: arg.rebind(args.as_ref()),
arg: index,
}),
))),
GenericArgKind::Const(ct) => {
Some(GenericArg::Const(Box::new(clean_middle_const(arg.rebind(ct), cx))))
}
}
};

let offset = if has_self { 1 } else { 0 };
let mut clean_args = Vec::with_capacity(args.len().saturating_sub(offset));
clean_args.extend(args.iter().enumerate().rev().filter_map(clean_arg));
clean_args.reverse();
clean_args
}

/// Check if the generic argument `actual` coincides with the `default` and can therefore be elided.
///
/// This uses a very conservative approach for performance and correctness reasons, meaning for
/// several classes of terms it claims that they cannot be elided even if they theoretically could.
/// This is absolutely fine since it mostly concerns edge cases.
fn can_elide_generic_arg<'tcx>(
actual: ty::Binder<'tcx, ty::GenericArg<'tcx>>,
default: ty::Binder<'tcx, ty::GenericArg<'tcx>>,
) -> bool {
debug_assert_matches!(
(actual.skip_binder().unpack(), default.skip_binder().unpack()),
(ty::GenericArgKind::Lifetime(_), ty::GenericArgKind::Lifetime(_))
| (ty::GenericArgKind::Type(_), ty::GenericArgKind::Type(_))
| (ty::GenericArgKind::Const(_), ty::GenericArgKind::Const(_))
);

// In practice, we shouldn't have any inference variables at this point.
// However to be safe, we bail out if we do happen to stumble upon them.
if actual.has_infer() || default.has_infer() {
return false;
}

// Since we don't properly keep track of bound variables in rustdoc (yet), we don't attempt to
// make any sense out of escaping bound variables. We simply don't have enough context and it
// would be incorrect to try to do so anyway.
if actual.has_escaping_bound_vars() || default.has_escaping_bound_vars() {
return false;
}

// Theoretically we could now check if either term contains (non-escaping) late-bound regions or
// projections, relate the two using an `InferCtxt` and check if the resulting obligations hold.
// Having projections means that the terms can potentially be further normalized thereby possibly
// revealing that they are equal after all. Regarding late-bound regions, they could to be
// liberated allowing us to consider more types to be equal by ignoring the names of binders
// (e.g., `for<'a> TYPE<'a>` and `for<'b> TYPE<'b>`).
//
// However, we are mostly interested in “reeliding” generic args, i.e., eliding generic args that
// were originally elided by the user and later filled in by the compiler contrary to eliding
// arbitrary generic arguments if they happen to semantically coincide with the default (of course,
// we cannot possibly distinguish these two cases). Therefore and for performance reasons, it
// suffices to only perform a syntactic / structural check by comparing the memory addresses of
// the interned arguments.
actual.skip_binder() == default.skip_binder()
}

fn clean_middle_generic_args_with_constraints<'tcx>(
cx: &mut DocContext<'tcx>,
did: DefId,
has_self: bool,
constraints: ThinVec<AssocItemConstraint>,
ty_args: ty::Binder<'tcx, GenericArgsRef<'tcx>>,
) -> GenericArgs {
let args = clean_middle_generic_args(cx, ty_args.map_bound(|args| &args[..]), has_self, did);

if cx.tcx.fn_trait_kind_from_def_id(did).is_some() {
let ty = ty_args
.iter()
.nth(if has_self { 1 } else { 0 })
.unwrap()
.map_bound(|arg| arg.expect_ty());
let inputs =
// The trait's first substitution is the one after self, if there is one.
match ty.skip_binder().kind() {
ty::Tuple(tys) => tys.iter().map(|t| clean_middle_ty(ty.rebind(t), cx, None, None)).collect::<Vec<_>>().into(),
_ => return GenericArgs::AngleBracketed { args: args.into(), constraints },
};
let output = constraints.into_iter().next().and_then(|binding| match binding.kind {
AssocItemConstraintKind::Equality { term: Term::Type(ty) }
if ty != Type::Tuple(Vec::new()) =>
{
Some(Box::new(ty))
}
_ => None,
});
GenericArgs::Parenthesized { inputs, output }
} else {
GenericArgs::AngleBracketed { args: args.into(), constraints }
}
}

pub(super) fn clean_middle_path<'tcx>(
cx: &mut DocContext<'tcx>,
did: DefId,
has_self: bool,
constraints: ThinVec<AssocItemConstraint>,
args: ty::Binder<'tcx, GenericArgsRef<'tcx>>,
) -> Path {
let def_kind = cx.tcx.def_kind(did);
let name = cx.tcx.opt_item_name(did).unwrap_or(kw::Empty);
Path {
res: Res::Def(def_kind, did),
segments: thin_vec![PathSegment {
name,
args: clean_middle_generic_args_with_constraints(cx, did, has_self, constraints, args),
}],
}
}

pub(crate) fn qpath_to_string(p: &hir::QPath<'_>) -> String {
let segments = match *p {
hir::QPath::Resolved(_, path) => &path.segments,