Skip to content

Commit a8d869e

Browse files
committed
rustdoc: cross-crate re-exports: correctly render late-bound params in source order even if early-bound params are present
1 parent 0a5b998 commit a8d869e

File tree

7 files changed

+119
-70
lines changed

7 files changed

+119
-70
lines changed

src/librustdoc/clean/auto_trait.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,7 @@ where
334334
match br {
335335
// We only care about named late bound regions, as we need to add them
336336
// to the 'for<>' section
337-
ty::BrNamed(_, name) => Some(GenericParamDef::lifetime(name)),
337+
ty::BrNamed(def_id, name) => Some(GenericParamDef::lifetime(def_id, name)),
338338
_ => None,
339339
}
340340
})

src/librustdoc/clean/inline.rs

+36-14
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ use rustc_span::hygiene::MacroKind;
1818
use rustc_span::symbol::{kw, sym, Symbol};
1919

2020
use crate::clean::{
21-
self, clean_bound_vars, clean_fn_decl_from_did_and_sig, clean_generics, clean_impl_item,
22-
clean_middle_assoc_item, clean_middle_field, clean_middle_ty, clean_trait_ref_with_bindings,
21+
self, clean_bound_vars, clean_generics, clean_impl_item, clean_middle_assoc_item,
22+
clean_middle_field, clean_middle_ty, clean_poly_fn_sig, clean_trait_ref_with_bindings,
2323
clean_ty, clean_ty_alias_inner_type, clean_ty_generics, clean_variant_def, utils, Attributes,
2424
AttributesExt, ImplKind, ItemId, Type,
2525
};
@@ -72,7 +72,9 @@ pub(crate) fn try_inline(
7272
}
7373
Res::Def(DefKind::Fn, did) => {
7474
record_extern_fqn(cx, did, ItemType::Function);
75-
cx.with_param_env(did, |cx| clean::FunctionItem(build_external_function(cx, did)))
75+
cx.with_param_env(did, |cx| {
76+
clean::enter_impl_trait(cx, |cx| clean::FunctionItem(build_function(cx, did)))
77+
})
7678
}
7779
Res::Def(DefKind::Struct, did) => {
7880
record_extern_fqn(cx, did, ItemType::Struct);
@@ -274,18 +276,38 @@ pub(crate) fn build_external_trait(cx: &mut DocContext<'_>, did: DefId) -> clean
274276
clean::Trait { def_id: did, generics, items: trait_items, bounds: supertrait_bounds }
275277
}
276278

277-
fn build_external_function<'tcx>(cx: &mut DocContext<'tcx>, did: DefId) -> Box<clean::Function> {
278-
let sig = cx.tcx.fn_sig(did).instantiate_identity();
279-
let predicates = cx.tcx.explicit_predicates_of(did);
279+
pub(crate) fn build_function<'tcx>(
280+
cx: &mut DocContext<'tcx>,
281+
def_id: DefId,
282+
) -> Box<clean::Function> {
283+
let sig = cx.tcx.fn_sig(def_id).instantiate_identity();
284+
// The generics need to be cleaned before the signature.
285+
let mut generics =
286+
clean_ty_generics(cx, cx.tcx.generics_of(def_id), cx.tcx.explicit_predicates_of(def_id));
287+
let bound_vars = clean_bound_vars(sig.bound_vars());
288+
289+
// At the time of writing early & late-bound params are stored separately in rustc,
290+
// namely in `generics.params` and `bound_vars` respectively.
291+
//
292+
// To reestablish the original source code order of the generic parameters, we
293+
// need to manually sort them by their definition span after concatenation.
294+
//
295+
// See also:
296+
// * https://rustc-dev-guide.rust-lang.org/bound-vars-and-params.html
297+
// * https://rustc-dev-guide.rust-lang.org/what-does-early-late-bound-mean.html
298+
let has_early_bound_params = !generics.params.is_empty();
299+
let has_late_bound_params = !bound_vars.is_empty();
300+
generics.params.extend(bound_vars);
301+
if has_early_bound_params && has_late_bound_params {
302+
// If this ever becomes a performances bottleneck either due to the sorting
303+
// or due to the query calls, consider inserting the late-bound lifetime params
304+
// right after the last early-bound lifetime param followed by only sorting
305+
// the slice of lifetime params.
306+
generics.params.sort_by_key(|param| cx.tcx.def_ident_span(param.def_id).unwrap());
307+
}
308+
309+
let decl = clean_poly_fn_sig(cx, Some(def_id), sig);
280310

281-
let (generics, decl) = clean::enter_impl_trait(cx, |cx| {
282-
// NOTE: generics need to be cleaned before the decl!
283-
let mut generics = clean_ty_generics(cx, cx.tcx.generics_of(did), predicates);
284-
// FIXME: This does not place parameters in source order (late-bound ones come last)
285-
generics.params.extend(clean_bound_vars(sig.bound_vars()));
286-
let decl = clean_fn_decl_from_did_and_sig(cx, Some(did), sig);
287-
(generics, decl)
288-
});
289311
Box::new(clean::Function { decl, generics })
290312
}
291313

src/librustdoc/clean/mod.rs

+33-38
Original file line numberDiff line numberDiff line change
@@ -525,7 +525,6 @@ fn clean_generic_param_def<'tcx>(
525525
(
526526
def.name,
527527
GenericParamDefKind::Type {
528-
did: def.def_id,
529528
bounds: ThinVec::new(), // These are filled in from the where-clauses.
530529
default: default.map(Box::new),
531530
synthetic,
@@ -557,7 +556,7 @@ fn clean_generic_param_def<'tcx>(
557556
),
558557
};
559558

560-
GenericParamDef { name, kind }
559+
GenericParamDef { name, def_id: def.def_id, kind }
561560
}
562561

563562
fn clean_generic_param<'tcx>(
@@ -596,7 +595,6 @@ fn clean_generic_param<'tcx>(
596595
(
597596
param.name.ident().name,
598597
GenericParamDefKind::Type {
599-
did: param.def_id.to_def_id(),
600598
bounds,
601599
default: default.map(|t| clean_ty(t, cx)).map(Box::new),
602600
synthetic,
@@ -614,7 +612,7 @@ fn clean_generic_param<'tcx>(
614612
),
615613
};
616614

617-
GenericParamDef { name, kind }
615+
GenericParamDef { name, def_id: param.def_id.to_def_id(), kind }
618616
}
619617

620618
/// Synthetic type-parameters are inserted after normal ones.
@@ -646,8 +644,8 @@ pub(crate) fn clean_generics<'tcx>(
646644
let param = clean_generic_param(cx, Some(gens), param);
647645
match param.kind {
648646
GenericParamDefKind::Lifetime { .. } => unreachable!(),
649-
GenericParamDefKind::Type { did, ref bounds, .. } => {
650-
cx.impl_trait_bounds.insert(did.into(), bounds.to_vec());
647+
GenericParamDefKind::Type { ref bounds, .. } => {
648+
cx.impl_trait_bounds.insert(param.def_id.into(), bounds.to_vec());
651649
}
652650
GenericParamDefKind::Const { .. } => unreachable!(),
653651
}
@@ -1064,8 +1062,11 @@ fn clean_fn_decl_legacy_const_generics(func: &mut Function, attrs: &[ast::Attrib
10641062
match literal.kind {
10651063
ast::LitKind::Int(a, _) => {
10661064
let gen = func.generics.params.remove(0);
1067-
if let GenericParamDef { name, kind: GenericParamDefKind::Const { ty, .. } } =
1068-
gen
1065+
if let GenericParamDef {
1066+
name,
1067+
kind: GenericParamDefKind::Const { ty, .. },
1068+
..
1069+
} = gen
10691070
{
10701071
func.decl
10711072
.inputs
@@ -1169,7 +1170,7 @@ fn clean_fn_decl_with_args<'tcx>(
11691170
FnDecl { inputs: args, output, c_variadic: decl.c_variadic }
11701171
}
11711172

1172-
fn clean_fn_decl_from_did_and_sig<'tcx>(
1173+
fn clean_poly_fn_sig<'tcx>(
11731174
cx: &mut DocContext<'tcx>,
11741175
did: Option<DefId>,
11751176
sig: ty::PolyFnSig<'tcx>,
@@ -1359,16 +1360,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
13591360
}
13601361
}
13611362
ty::AssocKind::Fn => {
1362-
let sig = tcx.fn_sig(assoc_item.def_id).instantiate_identity();
1363-
let mut generics = clean_ty_generics(
1364-
cx,
1365-
tcx.generics_of(assoc_item.def_id),
1366-
tcx.explicit_predicates_of(assoc_item.def_id),
1367-
);
1368-
// FIXME: This does not place parameters in source order (late-bound ones come last)
1369-
generics.params.extend(clean_bound_vars(sig.bound_vars()));
1370-
1371-
let mut decl = clean_fn_decl_from_did_and_sig(cx, Some(assoc_item.def_id), sig);
1363+
let mut item = inline::build_function(cx, assoc_item.def_id);
13721364

13731365
if assoc_item.fn_has_self_parameter {
13741366
let self_ty = match assoc_item.container {
@@ -1377,12 +1369,13 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
13771369
}
13781370
ty::TraitContainer => tcx.types.self_param,
13791371
};
1380-
let self_arg_ty = sig.input(0).skip_binder();
1372+
let self_arg_ty =
1373+
tcx.fn_sig(assoc_item.def_id).instantiate_identity().input(0).skip_binder();
13811374
if self_arg_ty == self_ty {
1382-
decl.inputs.values[0].type_ = Generic(kw::SelfUpper);
1375+
item.decl.inputs.values[0].type_ = Generic(kw::SelfUpper);
13831376
} else if let ty::Ref(_, ty, _) = *self_arg_ty.kind() {
13841377
if ty == self_ty {
1385-
match decl.inputs.values[0].type_ {
1378+
match item.decl.inputs.values[0].type_ {
13861379
BorrowedRef { ref mut type_, .. } => **type_ = Generic(kw::SelfUpper),
13871380
_ => unreachable!(),
13881381
}
@@ -1399,9 +1392,9 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
13991392
ty::ImplContainer => Some(assoc_item.defaultness(tcx)),
14001393
ty::TraitContainer => None,
14011394
};
1402-
MethodItem(Box::new(Function { generics, decl }), defaultness)
1395+
MethodItem(item, defaultness)
14031396
} else {
1404-
TyMethodItem(Box::new(Function { generics, decl }))
1397+
TyMethodItem(item)
14051398
}
14061399
}
14071400
ty::AssocKind::Type => {
@@ -2109,7 +2102,7 @@ pub(crate) fn clean_middle_ty<'tcx>(
21092102
ty::FnDef(..) | ty::FnPtr(_) => {
21102103
// FIXME: should we merge the outer and inner binders somehow?
21112104
let sig = bound_ty.skip_binder().fn_sig(cx.tcx);
2112-
let decl = clean_fn_decl_from_did_and_sig(cx, None, sig);
2105+
let decl = clean_poly_fn_sig(cx, None, sig);
21132106
let generic_params = clean_bound_vars(sig.bound_vars());
21142107

21152108
BareFunction(Box::new(BareFunctionDecl {
@@ -2192,10 +2185,10 @@ pub(crate) fn clean_middle_ty<'tcx>(
21922185
.iter()
21932186
.flat_map(|pred| pred.bound_vars())
21942187
.filter_map(|var| match var {
2195-
ty::BoundVariableKind::Region(ty::BrNamed(_, name))
2188+
ty::BoundVariableKind::Region(ty::BrNamed(def_id, name))
21962189
if name != kw::UnderscoreLifetime =>
21972190
{
2198-
Some(GenericParamDef::lifetime(name))
2191+
Some(GenericParamDef::lifetime(def_id, name))
21992192
}
22002193
_ => None,
22012194
})
@@ -3167,20 +3160,22 @@ fn clean_bound_vars<'tcx>(
31673160
bound_vars
31683161
.into_iter()
31693162
.filter_map(|var| match var {
3170-
ty::BoundVariableKind::Region(ty::BrNamed(_, name))
3163+
ty::BoundVariableKind::Region(ty::BrNamed(def_id, name))
31713164
if name != kw::UnderscoreLifetime =>
31723165
{
3173-
Some(GenericParamDef::lifetime(name))
3166+
Some(GenericParamDef::lifetime(def_id, name))
3167+
}
3168+
ty::BoundVariableKind::Ty(ty::BoundTyKind::Param(def_id, name)) => {
3169+
Some(GenericParamDef {
3170+
name,
3171+
def_id,
3172+
kind: GenericParamDefKind::Type {
3173+
bounds: ThinVec::new(),
3174+
default: None,
3175+
synthetic: false,
3176+
},
3177+
})
31743178
}
3175-
ty::BoundVariableKind::Ty(ty::BoundTyKind::Param(did, name)) => Some(GenericParamDef {
3176-
name,
3177-
kind: GenericParamDefKind::Type {
3178-
did,
3179-
bounds: ThinVec::new(),
3180-
default: None,
3181-
synthetic: false,
3182-
},
3183-
}),
31843179
// FIXME(non_lifetime_binders): Support higher-ranked const parameters.
31853180
ty::BoundVariableKind::Const => None,
31863181
_ => None,

src/librustdoc/clean/types.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -1326,7 +1326,7 @@ impl WherePredicate {
13261326
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
13271327
pub(crate) enum GenericParamDefKind {
13281328
Lifetime { outlives: ThinVec<Lifetime> },
1329-
Type { did: DefId, bounds: ThinVec<GenericBound>, default: Option<Box<Type>>, synthetic: bool },
1329+
Type { bounds: ThinVec<GenericBound>, default: Option<Box<Type>>, synthetic: bool },
13301330
// Option<Box<String>> makes this type smaller than `Option<String>` would.
13311331
Const { ty: Box<Type>, default: Option<Box<String>>, is_host_effect: bool },
13321332
}
@@ -1340,12 +1340,13 @@ impl GenericParamDefKind {
13401340
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
13411341
pub(crate) struct GenericParamDef {
13421342
pub(crate) name: Symbol,
1343+
pub(crate) def_id: DefId,
13431344
pub(crate) kind: GenericParamDefKind,
13441345
}
13451346

13461347
impl GenericParamDef {
1347-
pub(crate) fn lifetime(name: Symbol) -> Self {
1348-
Self { name, kind: GenericParamDefKind::Lifetime { outlives: ThinVec::new() } }
1348+
pub(crate) fn lifetime(def_id: DefId, name: Symbol) -> Self {
1349+
Self { name, def_id, kind: GenericParamDefKind::Lifetime { outlives: ThinVec::new() } }
13491350
}
13501351

13511352
pub(crate) fn is_synthetic_param(&self) -> bool {

src/librustdoc/json/conversions.rs

+11-14
Original file line numberDiff line numberDiff line change
@@ -456,7 +456,7 @@ impl FromWithTcx<clean::GenericParamDefKind> for GenericParamDefKind {
456456
Lifetime { outlives } => GenericParamDefKind::Lifetime {
457457
outlives: outlives.into_iter().map(convert_lifetime).collect(),
458458
},
459-
Type { did: _, bounds, default, synthetic } => GenericParamDefKind::Type {
459+
Type { bounds, default, synthetic } => GenericParamDefKind::Type {
460460
bounds: bounds.into_tcx(tcx),
461461
default: default.map(|x| (*x).into_tcx(tcx)),
462462
synthetic,
@@ -486,19 +486,16 @@ impl FromWithTcx<clean::WherePredicate> for WherePredicate {
486486
outlives: outlives.iter().map(|lt| lt.0.to_string()).collect(),
487487
}
488488
}
489-
clean::GenericParamDefKind::Type {
490-
did: _,
491-
bounds,
492-
default,
493-
synthetic,
494-
} => GenericParamDefKind::Type {
495-
bounds: bounds
496-
.into_iter()
497-
.map(|bound| bound.into_tcx(tcx))
498-
.collect(),
499-
default: default.map(|ty| (*ty).into_tcx(tcx)),
500-
synthetic,
501-
},
489+
clean::GenericParamDefKind::Type { bounds, default, synthetic } => {
490+
GenericParamDefKind::Type {
491+
bounds: bounds
492+
.into_iter()
493+
.map(|bound| bound.into_tcx(tcx))
494+
.collect(),
495+
default: default.map(|ty| (*ty).into_tcx(tcx)),
496+
synthetic,
497+
}
498+
}
502499
clean::GenericParamDefKind::Const {
503500
ty,
504501
default,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Here, `'a` and `'c` are late-bound and `'b`, `'d`, `T` and `N` are early-bound.
2+
3+
pub fn f<'a, 'b, 'c, 'd, T, const N: usize>(_: impl Copy)
4+
where
5+
'b:,
6+
'd:,
7+
{}
8+
9+
pub struct Ty;
10+
11+
impl Ty {
12+
pub fn f<'a, 'b, 'c, 'd, T, const N: usize>(_: impl Copy)
13+
where
14+
'b:,
15+
'd:,
16+
{}
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Check that we correctly render late-bound lifetime params in source order
2+
// even if early-bound generic params are present.
3+
//
4+
// For context, at the time of writing early- and late-bound params are stored
5+
// separately in rustc and therefore rustdoc needs to manually merge them.
6+
7+
#![crate_name = "usr"]
8+
// aux-crate:dep=early-late-bound-lifetime-params.rs
9+
// edition:2021
10+
11+
// @has usr/fn.f.html
12+
// @has - '//pre[@class="rust item-decl"]' "fn f<'a, 'b, 'c, 'd, T, const N: usize>(_: impl Copy)"
13+
pub use dep::f;
14+
15+
// @has usr/struct.Ty.html
16+
// @has - '//*[@id="method.f"]' "fn f<'a, 'b, 'c, 'd, T, const N: usize>(_: impl Copy)"
17+
pub use dep::Ty;

0 commit comments

Comments
 (0)