Skip to content
Merged
Show file tree
Hide file tree
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
49 changes: 37 additions & 12 deletions src/librustdoc/clean/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1089,7 +1089,13 @@ fn clean_fn_or_proc_macro<'tcx>(
match macro_kind {
Some(kind) => clean_proc_macro(item, name, kind, cx.tcx),
None => {
let mut func = clean_function(cx, sig, generics, ParamsSrc::Body(body_id));
let mut func = clean_function(
cx,
sig,
generics,
ParamsSrc::Body(body_id),
item.owner_id.to_def_id(),
);
clean_fn_decl_legacy_const_generics(&mut func, attrs);
FunctionItem(func)
}
Expand Down Expand Up @@ -1127,18 +1133,30 @@ fn clean_function<'tcx>(
sig: &hir::FnSig<'tcx>,
generics: &hir::Generics<'tcx>,
params: ParamsSrc<'tcx>,
def_id: DefId,
) -> Box<Function> {
let (generics, decl) = enter_impl_trait(cx, |cx| {
// NOTE: Generics must be cleaned before params.
let generics = clean_generics(generics, cx);
let params = match params {
ParamsSrc::Body(body_id) => clean_params_via_body(cx, sig.decl.inputs, body_id),
// Let's not perpetuate anon params from Rust 2015; use `_` for them.
ParamsSrc::Idents(idents) => clean_params(cx, sig.decl.inputs, idents, |ident| {
Some(ident.map_or(kw::Underscore, |ident| ident.name))
}),
let decl = if sig.decl.opt_delegation_sig_id().is_some() {
// A delegation item (`reuse path::method`) has no resolved signature in the
// HIR: its inputs and return type are `InferDelegation` nodes that clean to
// `_`, and an `async` header over that inferred return type would panic in
// `sugared_async_return_type`. The resolved signature only exists on the ty
// side, so clean that instead, exactly like an inlined item. This both fixes
// the rendered `-> _` / `self: _` and makes the async sugaring well-defined.
let sig = cx.tcx.fn_sig(def_id).instantiate_identity().skip_norm_wip();
clean_poly_fn_sig(cx, Some(def_id), sig)
} else {
let params = match params {
ParamsSrc::Body(body_id) => clean_params_via_body(cx, sig.decl.inputs, body_id),
// Let's not perpetuate anon params from Rust 2015; use `_` for them.
ParamsSrc::Idents(idents) => clean_params(cx, sig.decl.inputs, idents, |ident| {
Some(ident.map_or(kw::Underscore, |ident| ident.name))
}),
};
clean_fn_decl_with_params(cx, sig.decl, Some(&sig.header), params)
};
let decl = clean_fn_decl_with_params(cx, sig.decl, Some(&sig.header), params);
(generics, decl)
});
Box::new(Function { decl, generics })
Expand Down Expand Up @@ -1270,11 +1288,18 @@ fn clean_trait_item<'tcx>(trait_item: &hir::TraitItem<'tcx>, cx: &mut DocContext
RequiredAssocConstItem(generics, Box::new(clean_ty(ty, cx)))
}
hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => {
let m = clean_function(cx, sig, trait_item.generics, ParamsSrc::Body(body));
let m =
clean_function(cx, sig, trait_item.generics, ParamsSrc::Body(body), local_did);
MethodItem(m, Defaultness::from_trait_item(trait_item.defaultness))
}
hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Required(idents)) => {
let m = clean_function(cx, sig, trait_item.generics, ParamsSrc::Idents(idents));
let m = clean_function(
cx,
sig,
trait_item.generics,
ParamsSrc::Idents(idents),
local_did,
);
RequiredMethodItem(m, Defaultness::from_trait_item(trait_item.defaultness))
}
hir::TraitItemKind::Type(bounds, Some(default)) => {
Expand Down Expand Up @@ -1315,7 +1340,7 @@ pub(crate) fn clean_impl_item<'tcx>(
type_: clean_ty(ty, cx),
})),
hir::ImplItemKind::Fn(ref sig, body) => {
let m = clean_function(cx, sig, impl_.generics, ParamsSrc::Body(body));
let m = clean_function(cx, sig, impl_.generics, ParamsSrc::Body(body), local_did);
let defaultness = match impl_.impl_kind {
hir::ImplItemImplKind::Inherent { .. } => hir::Defaultness::Final,
hir::ImplItemImplKind::Trait { defaultness, .. } => defaultness,
Expand Down Expand Up @@ -3254,7 +3279,7 @@ fn clean_maybe_renamed_foreign_item<'tcx>(
cx.with_param_env(def_id, |cx| {
let kind = match item.kind {
hir::ForeignItemKind::Fn(sig, idents, generics) => ForeignFunctionItem(
clean_function(cx, &sig, generics, ParamsSrc::Idents(idents)),
clean_function(cx, &sig, generics, ParamsSrc::Idents(idents), def_id),
sig.header.safety(),
),
hir::ForeignItemKind::Static(ty, mutability, safety) => ForeignStaticItem(
Expand Down
42 changes: 42 additions & 0 deletions tests/rustdoc-html/async/async-fn-delegation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//@ edition: 2021

// Regression test for <https://github.com/rust-lang/rust/issues/157040>.
//
// rustdoc used to ICE with "unexpected async fn return type" when cleaning a
// delegated (`reuse`) async fn: the delegation's HIR signature is unresolved
// (`InferDelegation`), so its return type cleaned to `_` even though the header
// is `async`, and unconditionally sugaring that inferred type panicked.
//
// We now clean the resolved (ty-side) signature for delegation items, like we
// already do for inlined items. That both avoids the ICE and renders the real
// return type and `self` parameter instead of `-> _` / `self: _`.
//
// Note: the `<Self>` generic on the free-function variants is a pre-existing
// quirk of how delegation generics are rendered (plain sync delegation prints it
// too); it is tracked separately and is not what this test is about.

#![feature(fn_delegation)]
#![allow(incomplete_features)]
#![crate_name = "async_delegation"]

pub trait Trait {
async fn unit(&self) {}
async fn nonunit(&self) -> i32 {
0
}
}

//@ has async_delegation/fn.unit.html '//pre[@class="rust item-decl"]' 'pub async fn unit<Self>(&self)'
pub reuse Trait::unit;
//@ has async_delegation/fn.nonunit.html '//pre[@class="rust item-decl"]' 'pub async fn nonunit<Self>(&self) -> i32'
pub reuse Trait::nonunit;

pub struct S;
impl Trait for S {}

//@ has async_delegation/struct.S.html '//*[@class="code-header"]' 'pub async fn unit(self: &S)'
//@ has async_delegation/struct.S.html '//*[@class="code-header"]' 'pub async fn nonunit(self: &S) -> i32'
impl S {
pub reuse Trait::unit { self }
pub reuse Trait::nonunit { self }
}
Loading