Rollup merge of #121022 - fmease:rustdoc-x-crate-late-bound-lt-src-order, r=GuillaumeGomez

rustdoc: cross-crate re-exports: correctly render late-bound params in source order even if early-bound params are present

r? ghost
This commit is contained in:
Matthias Krüger 2024-02-15 09:20:18 +01:00 committed by GitHub
commit f9a0675c3c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 119 additions and 70 deletions

View File

@ -334,7 +334,7 @@ fn extract_for_generics(&self, pred: ty::Clause<'tcx>) -> FxHashSet<GenericParam
match br { match br {
// We only care about named late bound regions, as we need to add them // We only care about named late bound regions, as we need to add them
// to the 'for<>' section // to the 'for<>' section
ty::BrNamed(_, name) => Some(GenericParamDef::lifetime(name)), ty::BrNamed(def_id, name) => Some(GenericParamDef::lifetime(def_id, name)),
_ => None, _ => None,
} }
}) })

View File

@ -18,8 +18,8 @@
use rustc_span::symbol::{kw, sym, Symbol}; use rustc_span::symbol::{kw, sym, Symbol};
use crate::clean::{ use crate::clean::{
self, clean_bound_vars, clean_fn_decl_from_did_and_sig, clean_generics, clean_impl_item, self, clean_bound_vars, clean_generics, clean_impl_item, clean_middle_assoc_item,
clean_middle_assoc_item, clean_middle_field, clean_middle_ty, clean_trait_ref_with_bindings, clean_middle_field, clean_middle_ty, clean_poly_fn_sig, clean_trait_ref_with_bindings,
clean_ty, clean_ty_alias_inner_type, clean_ty_generics, clean_variant_def, utils, Attributes, clean_ty, clean_ty_alias_inner_type, clean_ty_generics, clean_variant_def, utils, Attributes,
AttributesExt, ImplKind, ItemId, Type, AttributesExt, ImplKind, ItemId, Type,
}; };
@ -72,7 +72,9 @@ pub(crate) fn try_inline(
} }
Res::Def(DefKind::Fn, did) => { Res::Def(DefKind::Fn, did) => {
record_extern_fqn(cx, did, ItemType::Function); record_extern_fqn(cx, did, ItemType::Function);
cx.with_param_env(did, |cx| clean::FunctionItem(build_external_function(cx, did))) cx.with_param_env(did, |cx| {
clean::enter_impl_trait(cx, |cx| clean::FunctionItem(build_function(cx, did)))
})
} }
Res::Def(DefKind::Struct, did) => { Res::Def(DefKind::Struct, did) => {
record_extern_fqn(cx, did, ItemType::Struct); record_extern_fqn(cx, did, ItemType::Struct);
@ -274,18 +276,38 @@ pub(crate) fn build_external_trait(cx: &mut DocContext<'_>, did: DefId) -> clean
clean::Trait { def_id: did, generics, items: trait_items, bounds: supertrait_bounds } clean::Trait { def_id: did, generics, items: trait_items, bounds: supertrait_bounds }
} }
fn build_external_function<'tcx>(cx: &mut DocContext<'tcx>, did: DefId) -> Box<clean::Function> { pub(crate) fn build_function<'tcx>(
let sig = cx.tcx.fn_sig(did).instantiate_identity(); cx: &mut DocContext<'tcx>,
let predicates = cx.tcx.explicit_predicates_of(did); def_id: DefId,
) -> Box<clean::Function> {
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));
let bound_vars = clean_bound_vars(sig.bound_vars());
// At the time of writing early & late-bound params are stored separately in rustc,
// namely in `generics.params` and `bound_vars` respectively.
//
// To reestablish the original source code order of the generic parameters, we
// need to manually sort them by their definition span after concatenation.
//
// See also:
// * https://rustc-dev-guide.rust-lang.org/bound-vars-and-params.html
// * https://rustc-dev-guide.rust-lang.org/what-does-early-late-bound-mean.html
let has_early_bound_params = !generics.params.is_empty();
let has_late_bound_params = !bound_vars.is_empty();
generics.params.extend(bound_vars);
if has_early_bound_params && has_late_bound_params {
// If this ever becomes a performances bottleneck either due to the sorting
// or due to the query calls, consider inserting the late-bound lifetime params
// right after the last early-bound lifetime param followed by only sorting
// the slice of lifetime params.
generics.params.sort_by_key(|param| cx.tcx.def_ident_span(param.def_id).unwrap());
}
let decl = clean_poly_fn_sig(cx, Some(def_id), sig);
let (generics, decl) = clean::enter_impl_trait(cx, |cx| {
// NOTE: generics need to be cleaned before the decl!
let mut generics = clean_ty_generics(cx, cx.tcx.generics_of(did), predicates);
// FIXME: This does not place parameters in source order (late-bound ones come last)
generics.params.extend(clean_bound_vars(sig.bound_vars()));
let decl = clean_fn_decl_from_did_and_sig(cx, Some(did), sig);
(generics, decl)
});
Box::new(clean::Function { decl, generics }) Box::new(clean::Function { decl, generics })
} }

View File

@ -523,7 +523,6 @@ fn clean_generic_param_def<'tcx>(
( (
def.name, def.name,
GenericParamDefKind::Type { GenericParamDefKind::Type {
did: def.def_id,
bounds: ThinVec::new(), // These are filled in from the where-clauses. bounds: ThinVec::new(), // These are filled in from the where-clauses.
default: default.map(Box::new), default: default.map(Box::new),
synthetic, synthetic,
@ -555,7 +554,7 @@ fn clean_generic_param_def<'tcx>(
), ),
}; };
GenericParamDef { name, kind } GenericParamDef { name, def_id: def.def_id, kind }
} }
fn clean_generic_param<'tcx>( fn clean_generic_param<'tcx>(
@ -594,7 +593,6 @@ fn clean_generic_param<'tcx>(
( (
param.name.ident().name, param.name.ident().name,
GenericParamDefKind::Type { GenericParamDefKind::Type {
did: param.def_id.to_def_id(),
bounds, bounds,
default: default.map(|t| clean_ty(t, cx)).map(Box::new), default: default.map(|t| clean_ty(t, cx)).map(Box::new),
synthetic, synthetic,
@ -612,7 +610,7 @@ fn clean_generic_param<'tcx>(
), ),
}; };
GenericParamDef { name, kind } GenericParamDef { name, def_id: param.def_id.to_def_id(), kind }
} }
/// Synthetic type-parameters are inserted after normal ones. /// Synthetic type-parameters are inserted after normal ones.
@ -644,8 +642,8 @@ pub(crate) fn clean_generics<'tcx>(
let param = clean_generic_param(cx, Some(gens), param); let param = clean_generic_param(cx, Some(gens), param);
match param.kind { match param.kind {
GenericParamDefKind::Lifetime { .. } => unreachable!(), GenericParamDefKind::Lifetime { .. } => unreachable!(),
GenericParamDefKind::Type { did, ref bounds, .. } => { GenericParamDefKind::Type { ref bounds, .. } => {
cx.impl_trait_bounds.insert(did.into(), bounds.to_vec()); cx.impl_trait_bounds.insert(param.def_id.into(), bounds.to_vec());
} }
GenericParamDefKind::Const { .. } => unreachable!(), GenericParamDefKind::Const { .. } => unreachable!(),
} }
@ -1062,8 +1060,11 @@ fn clean_fn_decl_legacy_const_generics(func: &mut Function, attrs: &[ast::Attrib
match literal.kind { match literal.kind {
ast::LitKind::Int(a, _) => { ast::LitKind::Int(a, _) => {
let gen = func.generics.params.remove(0); let gen = func.generics.params.remove(0);
if let GenericParamDef { name, kind: GenericParamDefKind::Const { ty, .. } } = if let GenericParamDef {
gen name,
kind: GenericParamDefKind::Const { ty, .. },
..
} = gen
{ {
func.decl func.decl
.inputs .inputs
@ -1167,7 +1168,7 @@ fn clean_fn_decl_with_args<'tcx>(
FnDecl { inputs: args, output, c_variadic: decl.c_variadic } FnDecl { inputs: args, output, c_variadic: decl.c_variadic }
} }
fn clean_fn_decl_from_did_and_sig<'tcx>( fn clean_poly_fn_sig<'tcx>(
cx: &mut DocContext<'tcx>, cx: &mut DocContext<'tcx>,
did: Option<DefId>, did: Option<DefId>,
sig: ty::PolyFnSig<'tcx>, sig: ty::PolyFnSig<'tcx>,
@ -1357,16 +1358,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
} }
} }
ty::AssocKind::Fn => { ty::AssocKind::Fn => {
let sig = tcx.fn_sig(assoc_item.def_id).instantiate_identity(); let mut item = inline::build_function(cx, assoc_item.def_id);
let mut generics = clean_ty_generics(
cx,
tcx.generics_of(assoc_item.def_id),
tcx.explicit_predicates_of(assoc_item.def_id),
);
// FIXME: This does not place parameters in source order (late-bound ones come last)
generics.params.extend(clean_bound_vars(sig.bound_vars()));
let mut decl = clean_fn_decl_from_did_and_sig(cx, Some(assoc_item.def_id), sig);
if assoc_item.fn_has_self_parameter { if assoc_item.fn_has_self_parameter {
let self_ty = match assoc_item.container { let self_ty = match assoc_item.container {
@ -1375,12 +1367,13 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
} }
ty::TraitContainer => tcx.types.self_param, ty::TraitContainer => tcx.types.self_param,
}; };
let self_arg_ty = sig.input(0).skip_binder(); let self_arg_ty =
tcx.fn_sig(assoc_item.def_id).instantiate_identity().input(0).skip_binder();
if self_arg_ty == self_ty { if self_arg_ty == self_ty {
decl.inputs.values[0].type_ = Generic(kw::SelfUpper); item.decl.inputs.values[0].type_ = Generic(kw::SelfUpper);
} else if let ty::Ref(_, ty, _) = *self_arg_ty.kind() { } else if let ty::Ref(_, ty, _) = *self_arg_ty.kind() {
if ty == self_ty { if ty == self_ty {
match decl.inputs.values[0].type_ { match item.decl.inputs.values[0].type_ {
BorrowedRef { ref mut type_, .. } => **type_ = Generic(kw::SelfUpper), BorrowedRef { ref mut type_, .. } => **type_ = Generic(kw::SelfUpper),
_ => unreachable!(), _ => unreachable!(),
} }
@ -1397,9 +1390,9 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
ty::ImplContainer => Some(assoc_item.defaultness(tcx)), ty::ImplContainer => Some(assoc_item.defaultness(tcx)),
ty::TraitContainer => None, ty::TraitContainer => None,
}; };
MethodItem(Box::new(Function { generics, decl }), defaultness) MethodItem(item, defaultness)
} else { } else {
TyMethodItem(Box::new(Function { generics, decl })) TyMethodItem(item)
} }
} }
ty::AssocKind::Type => { ty::AssocKind::Type => {
@ -2083,7 +2076,7 @@ pub(crate) fn clean_middle_ty<'tcx>(
ty::FnDef(..) | ty::FnPtr(_) => { ty::FnDef(..) | ty::FnPtr(_) => {
// FIXME: should we merge the outer and inner binders somehow? // FIXME: should we merge the outer and inner binders somehow?
let sig = bound_ty.skip_binder().fn_sig(cx.tcx); let sig = bound_ty.skip_binder().fn_sig(cx.tcx);
let decl = clean_fn_decl_from_did_and_sig(cx, None, sig); let decl = clean_poly_fn_sig(cx, None, sig);
let generic_params = clean_bound_vars(sig.bound_vars()); let generic_params = clean_bound_vars(sig.bound_vars());
BareFunction(Box::new(BareFunctionDecl { BareFunction(Box::new(BareFunctionDecl {
@ -2166,10 +2159,10 @@ pub(crate) fn clean_middle_ty<'tcx>(
.iter() .iter()
.flat_map(|pred| pred.bound_vars()) .flat_map(|pred| pred.bound_vars())
.filter_map(|var| match var { .filter_map(|var| match var {
ty::BoundVariableKind::Region(ty::BrNamed(_, name)) ty::BoundVariableKind::Region(ty::BrNamed(def_id, name))
if name != kw::UnderscoreLifetime => if name != kw::UnderscoreLifetime =>
{ {
Some(GenericParamDef::lifetime(name)) Some(GenericParamDef::lifetime(def_id, name))
} }
_ => None, _ => None,
}) })
@ -3141,20 +3134,22 @@ fn clean_bound_vars<'tcx>(
bound_vars bound_vars
.into_iter() .into_iter()
.filter_map(|var| match var { .filter_map(|var| match var {
ty::BoundVariableKind::Region(ty::BrNamed(_, name)) ty::BoundVariableKind::Region(ty::BrNamed(def_id, name))
if name != kw::UnderscoreLifetime => if name != kw::UnderscoreLifetime =>
{ {
Some(GenericParamDef::lifetime(name)) Some(GenericParamDef::lifetime(def_id, name))
}
ty::BoundVariableKind::Ty(ty::BoundTyKind::Param(def_id, name)) => {
Some(GenericParamDef {
name,
def_id,
kind: GenericParamDefKind::Type {
bounds: ThinVec::new(),
default: None,
synthetic: false,
},
})
} }
ty::BoundVariableKind::Ty(ty::BoundTyKind::Param(did, name)) => Some(GenericParamDef {
name,
kind: GenericParamDefKind::Type {
did,
bounds: ThinVec::new(),
default: None,
synthetic: false,
},
}),
// FIXME(non_lifetime_binders): Support higher-ranked const parameters. // FIXME(non_lifetime_binders): Support higher-ranked const parameters.
ty::BoundVariableKind::Const => None, ty::BoundVariableKind::Const => None,
_ => None, _ => None,

View File

@ -1326,7 +1326,7 @@ pub(crate) fn get_bounds(&self) -> Option<&[GenericBound]> {
#[derive(Clone, PartialEq, Eq, Debug, Hash)] #[derive(Clone, PartialEq, Eq, Debug, Hash)]
pub(crate) enum GenericParamDefKind { pub(crate) enum GenericParamDefKind {
Lifetime { outlives: ThinVec<Lifetime> }, Lifetime { outlives: ThinVec<Lifetime> },
Type { did: DefId, bounds: ThinVec<GenericBound>, default: Option<Box<Type>>, synthetic: bool }, Type { bounds: ThinVec<GenericBound>, default: Option<Box<Type>>, synthetic: bool },
// Option<Box<String>> makes this type smaller than `Option<String>` would. // Option<Box<String>> makes this type smaller than `Option<String>` would.
Const { ty: Box<Type>, default: Option<Box<String>>, is_host_effect: bool }, Const { ty: Box<Type>, default: Option<Box<String>>, is_host_effect: bool },
} }
@ -1340,12 +1340,13 @@ pub(crate) fn is_type(&self) -> bool {
#[derive(Clone, PartialEq, Eq, Debug, Hash)] #[derive(Clone, PartialEq, Eq, Debug, Hash)]
pub(crate) struct GenericParamDef { pub(crate) struct GenericParamDef {
pub(crate) name: Symbol, pub(crate) name: Symbol,
pub(crate) def_id: DefId,
pub(crate) kind: GenericParamDefKind, pub(crate) kind: GenericParamDefKind,
} }
impl GenericParamDef { impl GenericParamDef {
pub(crate) fn lifetime(name: Symbol) -> Self { pub(crate) fn lifetime(def_id: DefId, name: Symbol) -> Self {
Self { name, kind: GenericParamDefKind::Lifetime { outlives: ThinVec::new() } } Self { name, def_id, kind: GenericParamDefKind::Lifetime { outlives: ThinVec::new() } }
} }
pub(crate) fn is_synthetic_param(&self) -> bool { pub(crate) fn is_synthetic_param(&self) -> bool {

View File

@ -456,7 +456,7 @@ fn from_tcx(kind: clean::GenericParamDefKind, tcx: TyCtxt<'_>) -> Self {
Lifetime { outlives } => GenericParamDefKind::Lifetime { Lifetime { outlives } => GenericParamDefKind::Lifetime {
outlives: outlives.into_iter().map(convert_lifetime).collect(), outlives: outlives.into_iter().map(convert_lifetime).collect(),
}, },
Type { did: _, bounds, default, synthetic } => GenericParamDefKind::Type { Type { bounds, default, synthetic } => GenericParamDefKind::Type {
bounds: bounds.into_tcx(tcx), bounds: bounds.into_tcx(tcx),
default: default.map(|x| (*x).into_tcx(tcx)), default: default.map(|x| (*x).into_tcx(tcx)),
synthetic, synthetic,
@ -486,19 +486,16 @@ fn from_tcx(predicate: clean::WherePredicate, tcx: TyCtxt<'_>) -> Self {
outlives: outlives.iter().map(|lt| lt.0.to_string()).collect(), outlives: outlives.iter().map(|lt| lt.0.to_string()).collect(),
} }
} }
clean::GenericParamDefKind::Type { clean::GenericParamDefKind::Type { bounds, default, synthetic } => {
did: _, GenericParamDefKind::Type {
bounds, bounds: bounds
default, .into_iter()
synthetic, .map(|bound| bound.into_tcx(tcx))
} => GenericParamDefKind::Type { .collect(),
bounds: bounds default: default.map(|ty| (*ty).into_tcx(tcx)),
.into_iter() synthetic,
.map(|bound| bound.into_tcx(tcx)) }
.collect(), }
default: default.map(|ty| (*ty).into_tcx(tcx)),
synthetic,
},
clean::GenericParamDefKind::Const { clean::GenericParamDefKind::Const {
ty, ty,
default, default,

View File

@ -0,0 +1,17 @@
// Here, `'a` and `'c` are late-bound and `'b`, `'d`, `T` and `N` are early-bound.
pub fn f<'a, 'b, 'c, 'd, T, const N: usize>(_: impl Copy)
where
'b:,
'd:,
{}
pub struct Ty;
impl Ty {
pub fn f<'a, 'b, 'c, 'd, T, const N: usize>(_: impl Copy)
where
'b:,
'd:,
{}
}

View File

@ -0,0 +1,17 @@
// Check that we correctly render late-bound lifetime params in source order
// even if early-bound generic params are present.
//
// For context, at the time of writing early- and late-bound params are stored
// separately in rustc and therefore rustdoc needs to manually merge them.
#![crate_name = "usr"]
// aux-crate:dep=early-late-bound-lifetime-params.rs
// edition:2021
// @has usr/fn.f.html
// @has - '//pre[@class="rust item-decl"]' "fn f<'a, 'b, 'c, 'd, T, const N: usize>(_: impl Copy)"
pub use dep::f;
// @has usr/struct.Ty.html
// @has - '//*[@id="method.f"]' "fn f<'a, 'b, 'c, 'd, T, const N: usize>(_: impl Copy)"
pub use dep::Ty;