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:
bors 2021-09-05 12:52:34 +00:00
commit f7c00dc409
11 changed files with 96 additions and 64 deletions

View file

@ -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();

View file

@ -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 &param.name == name {
mem::swap(bounds, ty_bounds);

View file

@ -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(),
}
}

View file

@ -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 {

View file

@ -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)),

View file

@ -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());

View file

@ -4,6 +4,7 @@
)]
#![feature(rustc_private)]
#![feature(array_methods)]
#![feature(assert_matches)]
#![feature(box_patterns)]
#![feature(control_flow_enum)]
#![feature(in_band_lifetimes)]

View file

@ -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> },
}

View file

@ -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> {

View 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
{
}

View 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