Skip to content

Commit

Permalink
Auto merge of #88604 - camelid:rustdoc-lifetime-bounds, r=GuillaumeGomez
Browse files Browse the repository at this point in the history
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`
  • Loading branch information
bors committed Sep 5, 2021
2 parents 7e1e3eb + 3a3f99a commit f7c00dc
Show file tree
Hide file tree
Showing 11 changed files with 96 additions and 64 deletions.
9 changes: 5 additions & 4 deletions src/librustdoc/clean/auto_trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
})
Expand Down Expand Up @@ -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();
Expand Down
91 changes: 42 additions & 49 deletions src/librustdoc/clean/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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());
}
Expand Down Expand Up @@ -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);
Expand Down
8 changes: 5 additions & 3 deletions src/librustdoc/clean/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>,
Expand All @@ -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,
}
}
}
Expand All @@ -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(),
}
}
Expand Down
22 changes: 18 additions & 4 deletions src/librustdoc/html/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -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 {
Expand Down
4 changes: 3 additions & 1 deletion src/librustdoc/json/conversions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)),
Expand Down
2 changes: 1 addition & 1 deletion src/librustdoc/json/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down
1 change: 1 addition & 0 deletions src/librustdoc/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
)]
#![feature(rustc_private)]
#![feature(array_methods)]
#![feature(assert_matches)]
#![feature(box_patterns)]
#![feature(control_flow_enum)]
#![feature(in_band_lifetimes)]
Expand Down
2 changes: 1 addition & 1 deletion src/rustdoc-json-types/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> },
}
Expand Down
2 changes: 1 addition & 1 deletion src/test/rustdoc-json/structs/with_primitives.rs
Original file line number Diff line number Diff line change
@@ -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> {
Expand Down
9 changes: 9 additions & 0 deletions src/test/rustdoc-ui/bounded-hr-lifetime.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 10 additions & 0 deletions src/test/rustdoc-ui/bounded-hr-lifetime.stderr
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit f7c00dc

Please sign in to comment.