Auto merge of #88604 - camelid:rustdoc-lifetime-bounds, r=GuillaumeGomez
rustdoc: Clean up handling of lifetime bounds Previously, rustdoc recorded lifetime bounds by rendering them into the name of the lifetime parameter. Now, it leaves the name as the actual name and instead records lifetime bounds in an `outlives` list, similar to how type parameter bounds are recorded. Also, higher-ranked lifetimes cannot currently have bounds, so I simplified the code to reflect that. r? `@GuillaumeGomez`
This commit is contained in:
commit
f7c00dc409
@ -331,9 +331,10 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
|
||||
match br {
|
||||
// We only care about named late bound regions, as we need to add them
|
||||
// to the 'for<>' section
|
||||
ty::BrNamed(_, name) => {
|
||||
Some(GenericParamDef { name, kind: GenericParamDefKind::Lifetime })
|
||||
}
|
||||
ty::BrNamed(_, name) => Some(GenericParamDef {
|
||||
name,
|
||||
kind: GenericParamDefKind::Lifetime { outlives: vec![] },
|
||||
}),
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
@ -659,7 +660,7 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
|
||||
bounds.insert(0, GenericBound::maybe_sized(self.cx));
|
||||
}
|
||||
}
|
||||
GenericParamDefKind::Lifetime => {}
|
||||
GenericParamDefKind::Lifetime { .. } => {}
|
||||
GenericParamDefKind::Const { ref mut default, .. } => {
|
||||
// We never want something like `impl<const N: usize = 10>`
|
||||
default.take();
|
||||
|
@ -30,6 +30,7 @@ use rustc_target::spec::abi::Abi;
|
||||
use rustc_typeck::check::intrinsic::intrinsic_operation_unsafety;
|
||||
use rustc_typeck::hir_ty_to_ty;
|
||||
|
||||
use std::assert_matches::assert_matches;
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::default::Default;
|
||||
use std::hash::Hash;
|
||||
@ -199,9 +200,10 @@ impl Clean<GenericBound> for (ty::PolyTraitRef<'_>, &[TypeBinding]) {
|
||||
.collect_referenced_late_bound_regions(&poly_trait_ref)
|
||||
.into_iter()
|
||||
.filter_map(|br| match br {
|
||||
ty::BrNamed(_, name) => {
|
||||
Some(GenericParamDef { name, kind: GenericParamDefKind::Lifetime })
|
||||
}
|
||||
ty::BrNamed(_, name) => Some(GenericParamDef {
|
||||
name,
|
||||
kind: GenericParamDefKind::Lifetime { outlives: vec![] },
|
||||
}),
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
@ -241,30 +243,6 @@ impl Clean<Lifetime> for hir::Lifetime {
|
||||
}
|
||||
}
|
||||
|
||||
impl Clean<Lifetime> for hir::GenericParam<'_> {
|
||||
fn clean(&self, _: &mut DocContext<'_>) -> Lifetime {
|
||||
match self.kind {
|
||||
hir::GenericParamKind::Lifetime { .. } => {
|
||||
if !self.bounds.is_empty() {
|
||||
let mut bounds = self.bounds.iter().map(|bound| match bound {
|
||||
hir::GenericBound::Outlives(lt) => lt,
|
||||
_ => panic!(),
|
||||
});
|
||||
let name = bounds.next().expect("no more bounds").name.ident();
|
||||
let mut s = format!("{}: {}", self.name.ident(), name);
|
||||
for bound in bounds {
|
||||
s.push_str(&format!(" + {}", bound.name.ident()));
|
||||
}
|
||||
Lifetime(Symbol::intern(&s))
|
||||
} else {
|
||||
Lifetime(self.name.ident().name)
|
||||
}
|
||||
}
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Clean<Constant> for hir::ConstArg {
|
||||
fn clean(&self, cx: &mut DocContext<'_>) -> Constant {
|
||||
Constant {
|
||||
@ -302,11 +280,30 @@ impl Clean<Option<Lifetime>> for ty::RegionKind {
|
||||
impl Clean<WherePredicate> for hir::WherePredicate<'_> {
|
||||
fn clean(&self, cx: &mut DocContext<'_>) -> WherePredicate {
|
||||
match *self {
|
||||
hir::WherePredicate::BoundPredicate(ref wbp) => WherePredicate::BoundPredicate {
|
||||
ty: wbp.bounded_ty.clean(cx),
|
||||
bounds: wbp.bounds.clean(cx),
|
||||
bound_params: wbp.bound_generic_params.into_iter().map(|x| x.clean(cx)).collect(),
|
||||
},
|
||||
hir::WherePredicate::BoundPredicate(ref wbp) => {
|
||||
let bound_params = wbp
|
||||
.bound_generic_params
|
||||
.into_iter()
|
||||
.map(|param| {
|
||||
// Higher-ranked params must be lifetimes.
|
||||
// Higher-ranked lifetimes can't have bounds.
|
||||
assert_matches!(
|
||||
param,
|
||||
hir::GenericParam {
|
||||
kind: hir::GenericParamKind::Lifetime { .. },
|
||||
bounds: [],
|
||||
..
|
||||
}
|
||||
);
|
||||
Lifetime(param.name.ident().name)
|
||||
})
|
||||
.collect();
|
||||
WherePredicate::BoundPredicate {
|
||||
ty: wbp.bounded_ty.clean(cx),
|
||||
bounds: wbp.bounds.clean(cx),
|
||||
bound_params,
|
||||
}
|
||||
}
|
||||
|
||||
hir::WherePredicate::RegionPredicate(ref wrp) => WherePredicate::RegionPredicate {
|
||||
lifetime: wrp.lifetime.clean(cx),
|
||||
@ -412,7 +409,9 @@ impl<'tcx> Clean<Type> for ty::ProjectionTy<'tcx> {
|
||||
impl Clean<GenericParamDef> for ty::GenericParamDef {
|
||||
fn clean(&self, cx: &mut DocContext<'_>) -> GenericParamDef {
|
||||
let (name, kind) = match self.kind {
|
||||
ty::GenericParamDefKind::Lifetime => (self.name, GenericParamDefKind::Lifetime),
|
||||
ty::GenericParamDefKind::Lifetime => {
|
||||
(self.name, GenericParamDefKind::Lifetime { outlives: vec![] })
|
||||
}
|
||||
ty::GenericParamDefKind::Type { has_default, synthetic, .. } => {
|
||||
let default = if has_default {
|
||||
let mut default = cx.tcx.type_of(self.def_id).clean(cx);
|
||||
@ -462,21 +461,15 @@ impl Clean<GenericParamDef> for hir::GenericParam<'_> {
|
||||
fn clean(&self, cx: &mut DocContext<'_>) -> GenericParamDef {
|
||||
let (name, kind) = match self.kind {
|
||||
hir::GenericParamKind::Lifetime { .. } => {
|
||||
let name = if !self.bounds.is_empty() {
|
||||
let mut bounds = self.bounds.iter().map(|bound| match bound {
|
||||
hir::GenericBound::Outlives(lt) => lt,
|
||||
let outlives = self
|
||||
.bounds
|
||||
.iter()
|
||||
.map(|bound| match bound {
|
||||
hir::GenericBound::Outlives(lt) => lt.clean(cx),
|
||||
_ => panic!(),
|
||||
});
|
||||
let name = bounds.next().expect("no more bounds").name.ident();
|
||||
let mut s = format!("{}: {}", self.name.ident(), name);
|
||||
for bound in bounds {
|
||||
s.push_str(&format!(" + {}", bound.name.ident()));
|
||||
}
|
||||
Symbol::intern(&s)
|
||||
} else {
|
||||
self.name.ident().name
|
||||
};
|
||||
(name, GenericParamDefKind::Lifetime)
|
||||
})
|
||||
.collect();
|
||||
(self.name.ident().name, GenericParamDefKind::Lifetime { outlives })
|
||||
}
|
||||
hir::GenericParamKind::Type { ref default, synthetic } => (
|
||||
self.name.ident().name,
|
||||
@ -536,7 +529,7 @@ impl Clean<Generics> for hir::Generics<'_> {
|
||||
.map(|param| {
|
||||
let param: GenericParamDef = param.clean(cx);
|
||||
match param.kind {
|
||||
GenericParamDefKind::Lifetime => unreachable!(),
|
||||
GenericParamDefKind::Lifetime { .. } => unreachable!(),
|
||||
GenericParamDefKind::Type { did, ref bounds, .. } => {
|
||||
cx.impl_trait_bounds.insert(did.into(), bounds.clone());
|
||||
}
|
||||
@ -569,7 +562,7 @@ impl Clean<Generics> for hir::Generics<'_> {
|
||||
{
|
||||
for param in &mut generics.params {
|
||||
match param.kind {
|
||||
GenericParamDefKind::Lifetime => {}
|
||||
GenericParamDefKind::Lifetime { .. } => {}
|
||||
GenericParamDefKind::Type { bounds: ref mut ty_bounds, .. } => {
|
||||
if ¶m.name == name {
|
||||
mem::swap(bounds, ty_bounds);
|
||||
|
@ -1231,7 +1231,9 @@ impl WherePredicate {
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
||||
crate enum GenericParamDefKind {
|
||||
Lifetime,
|
||||
Lifetime {
|
||||
outlives: Vec<Lifetime>,
|
||||
},
|
||||
Type {
|
||||
did: DefId,
|
||||
bounds: Vec<GenericBound>,
|
||||
@ -1257,7 +1259,7 @@ impl GenericParamDefKind {
|
||||
match self {
|
||||
GenericParamDefKind::Type { default, .. } => default.clone(),
|
||||
GenericParamDefKind::Const { ty, .. } => Some(ty.clone()),
|
||||
GenericParamDefKind::Lifetime => None,
|
||||
GenericParamDefKind::Lifetime { .. } => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1271,7 +1273,7 @@ crate struct GenericParamDef {
|
||||
impl GenericParamDef {
|
||||
crate fn is_synthetic_type_param(&self) -> bool {
|
||||
match self.kind {
|
||||
GenericParamDefKind::Lifetime | GenericParamDefKind::Const { .. } => false,
|
||||
GenericParamDefKind::Lifetime { .. } | GenericParamDefKind::Const { .. } => false,
|
||||
GenericParamDefKind::Type { ref synthetic, .. } => synthetic.is_some(),
|
||||
}
|
||||
}
|
||||
|
@ -155,9 +155,23 @@ impl clean::GenericParamDef {
|
||||
&'a self,
|
||||
cx: &'a Context<'tcx>,
|
||||
) -> impl fmt::Display + 'a + Captures<'tcx> {
|
||||
display_fn(move |f| match self.kind {
|
||||
clean::GenericParamDefKind::Lifetime => write!(f, "{}", self.name),
|
||||
clean::GenericParamDefKind::Type { ref bounds, ref default, .. } => {
|
||||
display_fn(move |f| match &self.kind {
|
||||
clean::GenericParamDefKind::Lifetime { outlives } => {
|
||||
write!(f, "{}", self.name)?;
|
||||
|
||||
if !outlives.is_empty() {
|
||||
f.write_str(": ")?;
|
||||
for (i, lt) in outlives.iter().enumerate() {
|
||||
if i != 0 {
|
||||
f.write_str(" + ")?;
|
||||
}
|
||||
write!(f, "{}", lt.print())?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
clean::GenericParamDefKind::Type { bounds, default, .. } => {
|
||||
f.write_str(&*self.name.as_str())?;
|
||||
|
||||
if !bounds.is_empty() {
|
||||
@ -178,7 +192,7 @@ impl clean::GenericParamDef {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
clean::GenericParamDefKind::Const { ref ty, ref default, .. } => {
|
||||
clean::GenericParamDefKind::Const { ty, default, .. } => {
|
||||
if f.alternate() {
|
||||
write!(f, "const {}: {:#}", self.name, ty.print(cx))?;
|
||||
} else {
|
||||
|
@ -326,7 +326,9 @@ impl FromWithTcx<clean::GenericParamDefKind> for GenericParamDefKind {
|
||||
fn from_tcx(kind: clean::GenericParamDefKind, tcx: TyCtxt<'_>) -> Self {
|
||||
use clean::GenericParamDefKind::*;
|
||||
match kind {
|
||||
Lifetime => GenericParamDefKind::Lifetime,
|
||||
Lifetime { outlives } => GenericParamDefKind::Lifetime {
|
||||
outlives: outlives.into_iter().map(|lt| lt.0.to_string()).collect(),
|
||||
},
|
||||
Type { did: _, bounds, default, synthetic: _ } => GenericParamDefKind::Type {
|
||||
bounds: bounds.into_iter().map(|x| x.into_tcx(tcx)).collect(),
|
||||
default: default.map(|x| x.into_tcx(tcx)),
|
||||
|
@ -234,7 +234,7 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
format_version: 6,
|
||||
format_version: 7,
|
||||
};
|
||||
let mut p = self.out_path.clone();
|
||||
p.push(output.index.get(&output.root).unwrap().name.clone().unwrap());
|
||||
|
@ -4,6 +4,7 @@
|
||||
)]
|
||||
#![feature(rustc_private)]
|
||||
#![feature(array_methods)]
|
||||
#![feature(assert_matches)]
|
||||
#![feature(box_patterns)]
|
||||
#![feature(control_flow_enum)]
|
||||
#![feature(in_band_lifetimes)]
|
||||
|
@ -323,7 +323,7 @@ pub struct GenericParamDef {
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum GenericParamDefKind {
|
||||
Lifetime,
|
||||
Lifetime { outlives: Vec<String> },
|
||||
Type { bounds: Vec<GenericBound>, default: Option<Type> },
|
||||
Const { ty: Type, default: Option<String> },
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
// @has with_primitives.json "$.index[*][?(@.name=='WithPrimitives')].visibility" \"public\"
|
||||
// @has - "$.index[*][?(@.name=='WithPrimitives')].kind" \"struct\"
|
||||
// @has - "$.index[*][?(@.name=='WithPrimitives')].inner.generics.params[0].name" \"\'a\"
|
||||
// @has - "$.index[*][?(@.name=='WithPrimitives')].inner.generics.params[0].kind" \"lifetime\"
|
||||
// @has - "$.index[*][?(@.name=='WithPrimitives')].inner.generics.params[0].kind.lifetime.outlives" []
|
||||
// @has - "$.index[*][?(@.name=='WithPrimitives')].inner.struct_type" \"plain\"
|
||||
// @has - "$.index[*][?(@.name=='WithPrimitives')].inner.fields_stripped" true
|
||||
pub struct WithPrimitives<'a> {
|
||||
|
9
src/test/rustdoc-ui/bounded-hr-lifetime.rs
Normal file
9
src/test/rustdoc-ui/bounded-hr-lifetime.rs
Normal file
@ -0,0 +1,9 @@
|
||||
// This test ensures that rustdoc doesn't panic on higher-ranked lifetimes
|
||||
// with bounds, because an error should have already been emitted by rustc.
|
||||
|
||||
pub fn hrlt<'b, 'c>()
|
||||
where
|
||||
for<'a: 'b + 'c> &'a (): std::fmt::Debug,
|
||||
//~^ ERROR lifetime bounds cannot be used in this context
|
||||
{
|
||||
}
|
10
src/test/rustdoc-ui/bounded-hr-lifetime.stderr
Normal file
10
src/test/rustdoc-ui/bounded-hr-lifetime.stderr
Normal file
@ -0,0 +1,10 @@
|
||||
error: lifetime bounds cannot be used in this context
|
||||
--> $DIR/bounded-hr-lifetime.rs:6:13
|
||||
|
|
||||
LL | for<'a: 'b + 'c> &'a (): std::fmt::Debug,
|
||||
| ^^ ^^
|
||||
|
||||
error: Compilation failed, aborting rustdoc
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
Loading…
x
Reference in New Issue
Block a user