diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs index 8ad09a9edc01c..58f7f34c7d4cf 100644 --- a/src/librustdoc/clean/auto_trait.rs +++ b/src/librustdoc/clean/auto_trait.rs @@ -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` default.take(); diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index b566239423e04..3664687cbb79e 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -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 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 for hir::Lifetime { } } -impl Clean 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 for hir::ConstArg { fn clean(&self, cx: &mut DocContext<'_>) -> Constant { Constant { @@ -302,11 +280,30 @@ impl Clean> for ty::RegionKind { impl Clean 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 for ty::ProjectionTy<'tcx> { impl Clean 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 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 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 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); diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 4194c99c0ba70..d1820793d5882 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1231,7 +1231,9 @@ impl WherePredicate { #[derive(Clone, PartialEq, Eq, Debug, Hash)] crate enum GenericParamDefKind { - Lifetime, + Lifetime { + outlives: Vec, + }, Type { did: DefId, bounds: Vec, @@ -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(), } } diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 54be830bf42e7..ea0458034899c 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -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 { diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 9453e6d35ee67..6f35e2e310641 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -326,7 +326,9 @@ impl FromWithTcx 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)), diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs index 8bdf1a5981230..5089cc30a1e39 100644 --- a/src/librustdoc/json/mod.rs +++ b/src/librustdoc/json/mod.rs @@ -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()); diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index b81acd1a93fc2..2dbe4c42b888e 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -4,6 +4,7 @@ )] #![feature(rustc_private)] #![feature(array_methods)] +#![feature(assert_matches)] #![feature(box_patterns)] #![feature(control_flow_enum)] #![feature(in_band_lifetimes)] diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs index 38ba87322c238..37cdc94441df7 100644 --- a/src/rustdoc-json-types/lib.rs +++ b/src/rustdoc-json-types/lib.rs @@ -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 }, Type { bounds: Vec, default: Option }, Const { ty: Type, default: Option }, } diff --git a/src/test/rustdoc-json/structs/with_primitives.rs b/src/test/rustdoc-json/structs/with_primitives.rs index ea98676863b5e..9e64317ec203f 100644 --- a/src/test/rustdoc-json/structs/with_primitives.rs +++ b/src/test/rustdoc-json/structs/with_primitives.rs @@ -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> { diff --git a/src/test/rustdoc-ui/bounded-hr-lifetime.rs b/src/test/rustdoc-ui/bounded-hr-lifetime.rs new file mode 100644 index 0000000000000..b2e000b975740 --- /dev/null +++ b/src/test/rustdoc-ui/bounded-hr-lifetime.rs @@ -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 +{ +} diff --git a/src/test/rustdoc-ui/bounded-hr-lifetime.stderr b/src/test/rustdoc-ui/bounded-hr-lifetime.stderr new file mode 100644 index 0000000000000..d8fcd6cb4b13f --- /dev/null +++ b/src/test/rustdoc-ui/bounded-hr-lifetime.stderr @@ -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 +