Skip to content

Commit 58be455

Browse files
committed
rustdoc: fix delegated impl signatures
1 parent 4f84d9f commit 58be455

5 files changed

Lines changed: 132 additions & 32 deletions

File tree

src/librustdoc/clean/mod.rs

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

861+
fn clean_generics_without_impl_trait_params<'tcx>(
862+
gens: &hir::Generics<'tcx>,
863+
cx: &mut DocContext<'tcx>,
864+
) -> Generics {
865+
let mut generics = clean_generics(gens, cx);
866+
let impl_trait_param_def_ids = generics
867+
.params
868+
.iter()
869+
.filter(|param| param.is_synthetic_param())
870+
.map(|param| param.def_id)
871+
.collect::<Vec<_>>();
872+
873+
for def_id in impl_trait_param_def_ids {
874+
cx.impl_trait_bounds.remove(&def_id.into());
875+
}
876+
generics.params.retain(|param| !param.is_synthetic_param());
877+
generics
878+
}
879+
861880
fn clean_ty_generics<'tcx>(cx: &mut DocContext<'tcx>, def_id: DefId) -> Generics {
862881
clean_ty_generics_inner(cx, cx.tcx.generics_of(def_id), cx.tcx.explicit_predicates_of(def_id))
863882
}
@@ -1091,10 +1110,10 @@ fn clean_fn_or_proc_macro<'tcx>(
10911110
None => {
10921111
let mut func = clean_function(
10931112
cx,
1113+
item.owner_id.to_def_id(),
10941114
sig,
10951115
generics,
10961116
ParamsSrc::Body(body_id),
1097-
item.owner_id.to_def_id(),
10981117
);
10991118
clean_fn_decl_legacy_const_generics(&mut func, attrs);
11001119
FunctionItem(func)
@@ -1130,33 +1149,48 @@ enum ParamsSrc<'tcx> {
11301149

11311150
fn clean_function<'tcx>(
11321151
cx: &mut DocContext<'tcx>,
1152+
def_id: DefId,
11331153
sig: &hir::FnSig<'tcx>,
11341154
generics: &hir::Generics<'tcx>,
11351155
params: ParamsSrc<'tcx>,
1136-
def_id: DefId,
11371156
) -> Box<Function> {
1157+
if sig.decl.opt_delegation_sig_id().is_some() {
1158+
return enter_impl_trait(cx, |cx| {
1159+
// A delegation item (`reuse path::method`) has no resolved signature in the
1160+
// HIR: its inputs and return type are `InferDelegation` nodes. The resolved
1161+
// signature and `impl Trait` bounds only exist on the ty side, but some
1162+
// delegation generics, like the generated `Self` in `reuse Trait::method`,
1163+
// are still only represented in HIR.
1164+
let mut function = inline::build_function(cx, def_id);
1165+
let hir_generics = clean_generics_without_impl_trait_params(generics, cx);
1166+
1167+
for param in hir_generics.params {
1168+
if !function.generics.params.iter().any(|existing| existing.def_id == param.def_id)
1169+
{
1170+
function.generics.params.push(param);
1171+
}
1172+
}
1173+
for pred in hir_generics.where_predicates {
1174+
if !function.generics.where_predicates.contains(&pred) {
1175+
function.generics.where_predicates.push(pred);
1176+
}
1177+
}
1178+
1179+
function
1180+
});
1181+
}
1182+
11381183
let (generics, decl) = enter_impl_trait(cx, |cx| {
11391184
// NOTE: Generics must be cleaned before params.
11401185
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)
1186+
let params = match params {
1187+
ParamsSrc::Body(body_id) => clean_params_via_body(cx, sig.decl.inputs, body_id),
1188+
// Let's not perpetuate anon params from Rust 2015; use `_` for them.
1189+
ParamsSrc::Idents(idents) => clean_params(cx, sig.decl.inputs, idents, |ident| {
1190+
Some(ident.map_or(kw::Underscore, |ident| ident.name))
1191+
}),
11591192
};
1193+
let decl = clean_fn_decl_with_params(cx, sig.decl, Some(&sig.header), params);
11601194
(generics, decl)
11611195
});
11621196
Box::new(Function { decl, generics })
@@ -1289,16 +1323,16 @@ fn clean_trait_item<'tcx>(trait_item: &hir::TraitItem<'tcx>, cx: &mut DocContext
12891323
}
12901324
hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => {
12911325
let m =
1292-
clean_function(cx, sig, trait_item.generics, ParamsSrc::Body(body), local_did);
1326+
clean_function(cx, local_did, sig, trait_item.generics, ParamsSrc::Body(body));
12931327
MethodItem(m, Defaultness::from_trait_item(trait_item.defaultness))
12941328
}
12951329
hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Required(idents)) => {
12961330
let m = clean_function(
12971331
cx,
1332+
local_did,
12981333
sig,
12991334
trait_item.generics,
13001335
ParamsSrc::Idents(idents),
1301-
local_did,
13021336
);
13031337
RequiredMethodItem(m, Defaultness::from_trait_item(trait_item.defaultness))
13041338
}
@@ -1340,7 +1374,7 @@ pub(crate) fn clean_impl_item<'tcx>(
13401374
type_: clean_ty(ty, cx),
13411375
})),
13421376
hir::ImplItemKind::Fn(ref sig, body) => {
1343-
let m = clean_function(cx, sig, impl_.generics, ParamsSrc::Body(body), local_did);
1377+
let m = clean_function(cx, local_did, sig, impl_.generics, ParamsSrc::Body(body));
13441378
let defaultness = match impl_.impl_kind {
13451379
hir::ImplItemImplKind::Inherent { .. } => hir::Defaultness::Final,
13461380
hir::ImplItemImplKind::Trait { defaultness, .. } => defaultness,
@@ -3279,7 +3313,7 @@ fn clean_maybe_renamed_foreign_item<'tcx>(
32793313
cx.with_param_env(def_id, |cx| {
32803314
let kind = match item.kind {
32813315
hir::ForeignItemKind::Fn(sig, idents, generics) => ForeignFunctionItem(
3282-
clean_function(cx, &sig, generics, ParamsSrc::Idents(idents), def_id),
3316+
clean_function(cx, def_id, &sig, generics, ParamsSrc::Idents(idents)),
32833317
sig.header.safety(),
32843318
),
32853319
hir::ForeignItemKind::Static(ty, mutability, safety) => ForeignStaticItem(

src/librustdoc/clean/simplify.rs

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -136,20 +136,25 @@ pub(crate) fn sized_bounds(cx: &mut DocContext<'_>, generics: &mut clean::Generi
136136
// Note that associated types also have an implicit `Sized` bound but we
137137
// don't actually know the set of associated types right here so that
138138
// should be handled when cleaning associated types.
139-
generics.where_predicates.retain(|pred| {
140-
let WP::BoundPredicate { ty: clean::Generic(param), bounds, .. } = pred else {
139+
generics.where_predicates.retain_mut(|pred| {
140+
let WP::BoundPredicate { ty, bounds, .. } = pred else {
141141
return true;
142142
};
143143

144+
// FIXME(sized-hierarchy): Always skip `MetaSized` bounds so that only `?Sized`
145+
// is shown and none of the new sizedness traits leak into documentation.
146+
bounds.retain(|b| !b.is_meta_sized_bound(cx.tcx));
147+
148+
let clean::Generic(param) = ty else {
149+
return !bounds.is_empty();
150+
};
151+
144152
if bounds.iter().any(|b| b.is_sized_bound(cx.tcx)) {
145153
sized_params.insert(*param);
146-
false
147-
} else if bounds.iter().any(|b| b.is_meta_sized_bound(cx.tcx)) {
148-
// FIXME(sized-hierarchy): Always skip `MetaSized` bounds so that only `?Sized`
149-
// is shown and none of the new sizedness traits leak into documentation.
150-
false
154+
bounds.retain(|b| !b.is_sized_bound(cx.tcx));
155+
!bounds.is_empty()
151156
} else {
152-
true
157+
!bounds.is_empty()
153158
}
154159
});
155160

tests/rustdoc-gui/go-to-collapsed-elem.goml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
// This test ensures that when clicking on a link which leads to an item inside a collapsed element,
22
// the collapsed element will be expanded.
3+
4+
// We need to disable this check because `trait.impl/lib2/scroll_traits/trait.Iterator.js`
5+
// doesn't exist.
6+
fail-on-request-error: false
7+
38
go-to: "file://" + |DOC_PATH| + "/test_docs/struct.Foo.html"
49
// We check that the implementors block is expanded.
510
assert-property: ("#implementations-list .implementors-toggle", {"open": "true"})
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: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
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:fn_delegation_impl_trait_aux=fn-delegation-impl-trait-aux.rs
4+
#![feature(fn_delegation)]
5+
#![allow(incomplete_features)]
6+
7+
extern crate fn_delegation_impl_trait_aux as aux;
8+
9+
fn foo(_: impl FnOnce()) {}
10+
11+
//@ has fn_delegation_impl_trait/fn.top_level.html '//pre[@class="rust item-decl"]' 'pub fn top_level(arg0: impl FnOnce())'
12+
pub reuse foo as top_level;
13+
14+
pub struct S;
15+
16+
//@ has fn_delegation_impl_trait/struct.S.html '//*[@id="method.method"]' 'pub fn method(arg0: impl FnOnce())'
17+
impl S {
18+
pub reuse foo as method;
19+
}
20+
21+
pub trait A {
22+
fn f(&self, _: impl FnOnce()) {}
23+
}
24+
25+
impl A for S {}
26+
27+
//@ has fn_delegation_impl_trait/struct.S.html '//*[@id="method.f"]' 'pub fn f(self: &S, arg1: impl FnOnce())'
28+
//@ !has fn_delegation_impl_trait/struct.S.html '//*[@id="method.f"]' 'MetaSized'
29+
impl S {
30+
pub reuse <S as A>::f;
31+
}
32+
33+
//@ has fn_delegation_impl_trait/trait.T.html '//*[@id="method.provided"]' 'fn provided(arg0: impl FnOnce())'
34+
pub trait T {
35+
reuse foo as provided;
36+
}
37+
38+
//@ has fn_delegation_impl_trait/fn.cross_crate.html '//pre[@class="rust item-decl"]' 'pub fn cross_crate(arg0: impl FnOnce())'
39+
pub reuse aux::external as cross_crate;
40+
41+
//@ 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())'
42+
#[doc(inline)]
43+
pub use aux::delegated as inlined_cross_crate_delegated;
44+
45+
//@ has fn_delegation_impl_trait/fn.redelegated_cross_crate.html '//pre[@class="rust item-decl"]' 'pub fn redelegated_cross_crate(arg0: impl FnOnce())'
46+
pub reuse aux::delegated as redelegated_cross_crate;
47+
48+
pub fn main() {}

0 commit comments

Comments
 (0)