Add RTN support to rustdoc

This commit is contained in:
Michael Goulet 2025-03-03 18:44:02 +00:00
parent 4d30011f6c
commit e3ac1fa81a
10 changed files with 98 additions and 32 deletions

View file

@ -252,7 +252,9 @@ pub fn lower_ty<'tcx>(tcx: TyCtxt<'tcx>, hir_ty: &hir::Ty<'tcx>) -> Ty<'tcx> {
// def-ID that will be used to determine the traits/predicates in
// scope. This is derived from the enclosing item-like thing.
let env_def_id = tcx.hir_get_parent_item(hir_ty.hir_id);
collect::ItemCtxt::new(tcx, env_def_id.def_id).lower_ty(hir_ty)
collect::ItemCtxt::new(tcx, env_def_id.def_id)
.lowerer()
.lower_ty_maybe_return_type_notation(hir_ty)
}
/// This is for rustdoc.

View file

@ -1488,6 +1488,9 @@ pub(crate) fn clean_middle_assoc_item(assoc_item: &ty::AssocItem, cx: &mut DocCo
// The only time this happens is if we're inside the rustdoc for Fn(),
// which only has one associated type, which is not a GAT, so whatever.
}
GenericArgs::ReturnTypeNotation => {
// Never move these.
}
}
bounds.extend(mem::take(pred_bounds));
false
@ -2553,36 +2556,42 @@ fn clean_generic_args<'tcx>(
generic_args: &hir::GenericArgs<'tcx>,
cx: &mut DocContext<'tcx>,
) -> GenericArgs {
// FIXME(return_type_notation): Fix RTN parens rendering
if let Some((inputs, output)) = generic_args.paren_sugar_inputs_output() {
let inputs = inputs.iter().map(|x| clean_ty(x, cx)).collect();
let output = match output.kind {
hir::TyKind::Tup(&[]) => None,
_ => Some(Box::new(clean_ty(output, cx))),
};
GenericArgs::Parenthesized { inputs, output }
} else {
let args = generic_args
.args
.iter()
.map(|arg| match arg {
hir::GenericArg::Lifetime(lt) if !lt.is_anonymous() => {
GenericArg::Lifetime(clean_lifetime(lt, cx))
}
hir::GenericArg::Lifetime(_) => GenericArg::Lifetime(Lifetime::elided()),
hir::GenericArg::Type(ty) => GenericArg::Type(clean_ty(ty.as_unambig_ty(), cx)),
hir::GenericArg::Const(ct) => {
GenericArg::Const(Box::new(clean_const(ct.as_unambig_ct(), cx)))
}
hir::GenericArg::Infer(_inf) => GenericArg::Infer,
})
.collect();
let constraints = generic_args
.constraints
.iter()
.map(|c| clean_assoc_item_constraint(c, cx))
.collect::<ThinVec<_>>();
GenericArgs::AngleBracketed { args, constraints }
match generic_args.parenthesized {
hir::GenericArgsParentheses::No => {
let args = generic_args
.args
.iter()
.map(|arg| match arg {
hir::GenericArg::Lifetime(lt) if !lt.is_anonymous() => {
GenericArg::Lifetime(clean_lifetime(lt, cx))
}
hir::GenericArg::Lifetime(_) => GenericArg::Lifetime(Lifetime::elided()),
hir::GenericArg::Type(ty) => GenericArg::Type(clean_ty(ty.as_unambig_ty(), cx)),
hir::GenericArg::Const(ct) => {
GenericArg::Const(Box::new(clean_const(ct.as_unambig_ct(), cx)))
}
hir::GenericArg::Infer(_inf) => GenericArg::Infer,
})
.collect();
let constraints = generic_args
.constraints
.iter()
.map(|c| clean_assoc_item_constraint(c, cx))
.collect::<ThinVec<_>>();
GenericArgs::AngleBracketed { args, constraints }
}
hir::GenericArgsParentheses::ParenSugar => {
let Some((inputs, output)) = generic_args.paren_sugar_inputs_output() else {
bug!();
};
let inputs = inputs.iter().map(|x| clean_ty(x, cx)).collect();
let output = match output.kind {
hir::TyKind::Tup(&[]) => None,
_ => Some(Box::new(clean_ty(output, cx))),
};
GenericArgs::Parenthesized { inputs, output }
}
hir::GenericArgsParentheses::ReturnTypeNotation => GenericArgs::ReturnTypeNotation,
}
}

View file

@ -102,6 +102,10 @@ pub(crate) fn merge_bounds(
}
}
},
PP::ReturnTypeNotation => {
// Cannot merge bounds with RTN.
return false;
}
};
true
})

View file

@ -2259,8 +2259,12 @@ impl GenericArg {
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
pub(crate) enum GenericArgs {
/// `<args, constraints = ..>`
AngleBracketed { args: ThinVec<GenericArg>, constraints: ThinVec<AssocItemConstraint> },
/// `(inputs) -> output`
Parenthesized { inputs: ThinVec<Type>, output: Option<Box<Type>> },
/// `(..)`
ReturnTypeNotation,
}
impl GenericArgs {
@ -2270,6 +2274,7 @@ impl GenericArgs {
args.is_empty() && constraints.is_empty()
}
GenericArgs::Parenthesized { inputs, output } => inputs.is_empty() && output.is_none(),
GenericArgs::ReturnTypeNotation => false,
}
}
pub(crate) fn constraints(&self) -> Box<dyn Iterator<Item = AssocItemConstraint> + '_> {
@ -2294,6 +2299,7 @@ impl GenericArgs {
})
.into_iter(),
),
GenericArgs::ReturnTypeNotation => Box::new([].into_iter()),
}
}
}
@ -2305,8 +2311,10 @@ impl<'a> IntoIterator for &'a GenericArgs {
match self {
GenericArgs::AngleBracketed { args, .. } => Box::new(args.iter().cloned()),
GenericArgs::Parenthesized { inputs, .. } => {
// FIXME: This isn't really right, since `Fn(A, B)` is `Fn<(A, B)>`
Box::new(inputs.iter().cloned().map(GenericArg::Type))
}
GenericArgs::ReturnTypeNotation => Box::new([].into_iter()),
}
}
}

View file

@ -332,6 +332,9 @@ impl clean::GenericArgs {
}
}
}
clean::GenericArgs::ReturnTypeNotation => {
f.write_str("(..)")?;
}
}
Ok(())
})

View file

@ -176,6 +176,7 @@ impl FromClean<clean::GenericArgs> for GenericArgs {
inputs: inputs.into_json(renderer),
output: output.map(|a| (*a).into_json(renderer)),
},
ReturnTypeNotation => GenericArgs::ReturnTypeNotation,
}
}
}

View file

@ -30,7 +30,7 @@ pub type FxHashMap<K, V> = HashMap<K, V>; // re-export for use in src/librustdoc
/// This integer is incremented with every breaking change to the API,
/// and is returned along with the JSON blob as [`Crate::format_version`].
/// Consuming code should assert that this value matches the format version(s) that it supports.
pub const FORMAT_VERSION: u32 = 41;
pub const FORMAT_VERSION: u32 = 42;
/// The root of the emitted JSON blob.
///
@ -229,6 +229,8 @@ pub enum GenericArgs {
/// The output type provided after the `->`, if present.
output: Option<Type>,
},
/// `T::method(..)`
ReturnTypeNotation,
}
/// One argument in a list of generic arguments to a path segment.

View file

@ -326,6 +326,7 @@ impl<'a> Validator<'a> {
self.check_type(o);
}
}
GenericArgs::ReturnTypeNotation => {}
}
}

View file

@ -0,0 +1,18 @@
//@ edition: 2021
// ignore-tidy-linelength
#![crate_type = "lib"]
#![feature(return_type_notation)]
pub trait Foo {
async fn bar();
}
//@ is "$.index[*][?(@.name=='foo')].inner.function.generics.params[0].kind.type.bounds[0].trait_bound.trait.args.angle_bracketed.constraints[0].args" '"return_type_notation"'
//@ ismany "$.index[*][?(@.name=='foo')].inner.function.generics.where_predicates[*].bound_predicate.type.qualified_path.args" '"return_type_notation"' '"return_type_notation"'
pub fn foo<T: Foo<bar(..): Send>>()
where
<T as Foo>::bar(..): 'static,
T::bar(..): Sync,
{
}

View file

@ -0,0 +1,18 @@
//@ edition: 2021
#![crate_type = "lib"]
#![feature(return_type_notation)]
pub trait Foo {
async fn bar();
}
//@ has "return_type_notation/fn.foo.html"
//@ has - '//pre[@class="rust item-decl"]' "pub fn foo<T: Foo<bar(..): Send>>()"
//@ has - '//pre[@class="rust item-decl"]' "where <T as Foo>::bar(..): 'static, T::bar(..): Sync"
pub fn foo<T: Foo<bar(..): Send>>()
where
<T as Foo>::bar(..): 'static,
T::bar(..): Sync,
{
}