Skip to content

Commit af8418d

Browse files
committed
rustdoc: fix delegated impl signatures
1 parent 8954863 commit af8418d

3 files changed

Lines changed: 139 additions & 24 deletions

File tree

src/librustdoc/clean/mod.rs

Lines changed: 84 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -858,6 +858,67 @@ pub(crate) fn clean_generics<'tcx>(
858858
}
859859
}
860860

861+
/// Cleans delegation generics with HIR ordering and ty-side fallback.
862+
fn clean_delegation_generics<'tcx>(
863+
gens: &hir::Generics<'tcx>,
864+
cx: &mut DocContext<'tcx>,
865+
ty_generics: Generics,
866+
) -> Generics {
867+
let mut hir_generics = clean_generics(gens, cx);
868+
let impl_trait_param_def_ids = hir_generics
869+
.params
870+
.iter()
871+
.filter(|param| param.is_synthetic_param())
872+
.map(|param| param.def_id)
873+
.collect::<Vec<_>>();
874+
875+
for def_id in impl_trait_param_def_ids {
876+
cx.impl_trait_bounds.remove(&def_id.into());
877+
}
878+
hir_generics.params.retain(|param| !param.is_synthetic_param());
879+
hir_generics.where_predicates =
880+
hir_generics
881+
.where_predicates
882+
.into_iter()
883+
.filter_map(|mut pred| {
884+
if retain_delegation_predicate(cx.tcx, &mut pred) { Some(pred) } else { None }
885+
})
886+
.collect();
887+
888+
let Generics { params: ty_params, where_predicates } = ty_generics;
889+
let where_predicates =
890+
where_predicates
891+
.into_iter()
892+
.filter_map(|mut pred| {
893+
if retain_delegation_predicate(cx.tcx, &mut pred) { Some(pred) } else { None }
894+
})
895+
.collect();
896+
let mut generics = Generics { params: hir_generics.params, where_predicates };
897+
898+
for param in ty_params {
899+
if !generics.params.iter().any(|existing| existing.def_id == param.def_id) {
900+
generics.params.push(param);
901+
}
902+
}
903+
for pred in hir_generics.where_predicates {
904+
if !generics.where_predicates.contains(&pred) {
905+
generics.where_predicates.push(pred);
906+
}
907+
}
908+
909+
generics
910+
}
911+
912+
fn retain_delegation_predicate(tcx: TyCtxt<'_>, pred: &mut WherePredicate) -> bool {
913+
match pred {
914+
WherePredicate::BoundPredicate { bounds, .. } => {
915+
bounds.retain(|bound| !bound.is_meta_sized_bound(tcx));
916+
!bounds.is_empty()
917+
}
918+
WherePredicate::RegionPredicate { .. } | WherePredicate::EqPredicate { .. } => true,
919+
}
920+
}
921+
861922
fn clean_ty_generics<'tcx>(cx: &mut DocContext<'tcx>, def_id: DefId) -> Generics {
862923
clean_ty_generics_inner(cx, cx.tcx.generics_of(def_id), cx.tcx.explicit_predicates_of(def_id))
863924
}
@@ -1091,10 +1152,10 @@ fn clean_fn_or_proc_macro<'tcx>(
10911152
None => {
10921153
let mut func = clean_function(
10931154
cx,
1155+
item.owner_id.to_def_id(),
10941156
sig,
10951157
generics,
10961158
ParamsSrc::Body(body_id),
1097-
item.owner_id.to_def_id(),
10981159
);
10991160
clean_fn_decl_legacy_const_generics(&mut func, attrs);
11001161
FunctionItem(func)
@@ -1130,33 +1191,32 @@ enum ParamsSrc<'tcx> {
11301191

11311192
fn clean_function<'tcx>(
11321193
cx: &mut DocContext<'tcx>,
1194+
def_id: DefId,
11331195
sig: &hir::FnSig<'tcx>,
11341196
generics: &hir::Generics<'tcx>,
11351197
params: ParamsSrc<'tcx>,
1136-
def_id: DefId,
11371198
) -> Box<Function> {
1199+
if sig.decl.opt_delegation_sig_id().is_some() {
1200+
return enter_impl_trait(cx, |cx| {
1201+
// Delegation HIR signatures are unresolved, so clean the resolved ty-side function.
1202+
let mut function = inline::build_function(cx, def_id);
1203+
let ty_generics = mem::take(&mut function.generics);
1204+
function.generics = clean_delegation_generics(generics, cx, ty_generics);
1205+
function
1206+
});
1207+
}
1208+
11381209
let (generics, decl) = enter_impl_trait(cx, |cx| {
11391210
// NOTE: Generics must be cleaned before params.
11401211
let generics = clean_generics(generics, cx);
1141-
let decl = if sig.decl.opt_delegation_sig_id().is_some() {
1142-
// A delegation item (`reuse path::method`) has no resolved signature in the
1143-
// HIR: its inputs and return type are `InferDelegation` nodes that clean to
1144-
// `_`, and an `async` header over that inferred return type would panic in
1145-
// `sugared_async_return_type`. The resolved signature only exists on the ty
1146-
// side, so clean that instead, exactly like an inlined item. This both fixes
1147-
// the rendered `-> _` / `self: _` and makes the async sugaring well-defined.
1148-
let sig = cx.tcx.fn_sig(def_id).instantiate_identity().skip_norm_wip();
1149-
clean_poly_fn_sig(cx, Some(def_id), sig)
1150-
} else {
1151-
let params = match params {
1152-
ParamsSrc::Body(body_id) => clean_params_via_body(cx, sig.decl.inputs, body_id),
1153-
// Let's not perpetuate anon params from Rust 2015; use `_` for them.
1154-
ParamsSrc::Idents(idents) => clean_params(cx, sig.decl.inputs, idents, |ident| {
1155-
Some(ident.map_or(kw::Underscore, |ident| ident.name))
1156-
}),
1157-
};
1158-
clean_fn_decl_with_params(cx, sig.decl, Some(&sig.header), params)
1212+
let params = match params {
1213+
ParamsSrc::Body(body_id) => clean_params_via_body(cx, sig.decl.inputs, body_id),
1214+
// Let's not perpetuate anon params from Rust 2015; use `_` for them.
1215+
ParamsSrc::Idents(idents) => clean_params(cx, sig.decl.inputs, idents, |ident| {
1216+
Some(ident.map_or(kw::Underscore, |ident| ident.name))
1217+
}),
11591218
};
1219+
let decl = clean_fn_decl_with_params(cx, sig.decl, Some(&sig.header), params);
11601220
(generics, decl)
11611221
});
11621222
Box::new(Function { decl, generics })
@@ -1289,16 +1349,16 @@ fn clean_trait_item<'tcx>(trait_item: &hir::TraitItem<'tcx>, cx: &mut DocContext
12891349
}
12901350
hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => {
12911351
let m =
1292-
clean_function(cx, sig, trait_item.generics, ParamsSrc::Body(body), local_did);
1352+
clean_function(cx, local_did, sig, trait_item.generics, ParamsSrc::Body(body));
12931353
MethodItem(m, Defaultness::from_trait_item(trait_item.defaultness))
12941354
}
12951355
hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Required(idents)) => {
12961356
let m = clean_function(
12971357
cx,
1358+
local_did,
12981359
sig,
12991360
trait_item.generics,
13001361
ParamsSrc::Idents(idents),
1301-
local_did,
13021362
);
13031363
RequiredMethodItem(m, Defaultness::from_trait_item(trait_item.defaultness))
13041364
}
@@ -1340,7 +1400,7 @@ pub(crate) fn clean_impl_item<'tcx>(
13401400
type_: clean_ty(ty, cx),
13411401
})),
13421402
hir::ImplItemKind::Fn(ref sig, body) => {
1343-
let m = clean_function(cx, sig, impl_.generics, ParamsSrc::Body(body), local_did);
1403+
let m = clean_function(cx, local_did, sig, impl_.generics, ParamsSrc::Body(body));
13441404
let defaultness = match impl_.impl_kind {
13451405
hir::ImplItemImplKind::Inherent { .. } => hir::Defaultness::Final,
13461406
hir::ImplItemImplKind::Trait { defaultness, .. } => defaultness,
@@ -3279,7 +3339,7 @@ fn clean_maybe_renamed_foreign_item<'tcx>(
32793339
cx.with_param_env(def_id, |cx| {
32803340
let kind = match item.kind {
32813341
hir::ForeignItemKind::Fn(sig, idents, generics) => ForeignFunctionItem(
3282-
clean_function(cx, &sig, generics, ParamsSrc::Idents(idents), def_id),
3342+
clean_function(cx, def_id, &sig, generics, ParamsSrc::Idents(idents)),
32833343
sig.header.safety(),
32843344
),
32853345
hir::ForeignItemKind::Static(ty, mutability, safety) => ForeignStaticItem(
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#![feature(fn_delegation)]
2+
#![allow(incomplete_features)]
3+
4+
pub fn external(_: impl FnOnce()) {}
5+
6+
fn delegated_to(_: impl FnOnce()) {}
7+
8+
pub reuse delegated_to as delegated;
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Regression test for <https://github.com/rust-lang/rust/issues/155728>
2+
// Make sure delegating functions with `impl Trait` in argument position works correctly.
3+
//@ aux-crate:aux=fn-delegation-impl-trait-aux.rs
4+
//@ edition: 2021
5+
#![feature(fn_delegation)]
6+
#![allow(incomplete_features)]
7+
8+
fn foo(_: impl FnOnce()) {}
9+
10+
//@ has fn_delegation_impl_trait/fn.top_level.html '//pre[@class="rust item-decl"]' 'pub fn top_level(arg0: impl FnOnce())'
11+
pub reuse foo as top_level;
12+
13+
pub struct S;
14+
15+
//@ has fn_delegation_impl_trait/struct.S.html '//*[@id="method.method"]' 'pub fn method(arg0: impl FnOnce())'
16+
impl S {
17+
pub reuse foo as method;
18+
}
19+
20+
pub trait A {
21+
fn f(&self, _: impl FnOnce()) {}
22+
}
23+
24+
impl A for S {}
25+
26+
//@ has fn_delegation_impl_trait/struct.S.html '//*[@id="method.f"]' 'pub fn f(self: &S, arg1: impl FnOnce())'
27+
//@ !has fn_delegation_impl_trait/struct.S.html '//*[@id="method.f"]' 'MetaSized'
28+
impl S {
29+
pub reuse <S as A>::f;
30+
}
31+
32+
//@ has fn_delegation_impl_trait/trait.T.html '//*[@id="method.provided"]' 'fn provided(arg0: impl FnOnce())'
33+
pub trait T {
34+
reuse foo as provided;
35+
}
36+
37+
//@ has fn_delegation_impl_trait/fn.cross_crate.html '//pre[@class="rust item-decl"]' 'pub fn cross_crate(arg0: impl FnOnce())'
38+
pub reuse aux::external as cross_crate;
39+
40+
//@ has fn_delegation_impl_trait/fn.inlined_cross_crate_delegated.html '//pre[@class="rust item-decl"]' 'pub fn inlined_cross_crate_delegated(arg0: impl FnOnce())'
41+
#[doc(inline)]
42+
pub use aux::delegated as inlined_cross_crate_delegated;
43+
44+
//@ has fn_delegation_impl_trait/fn.redelegated_cross_crate.html '//pre[@class="rust item-decl"]' 'pub fn redelegated_cross_crate(arg0: impl FnOnce())'
45+
pub reuse aux::delegated as redelegated_cross_crate;
46+
47+
pub fn main() {}

0 commit comments

Comments
 (0)