diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs index a3f63ea1046..0828f0b3e51 100644 --- a/src/librustdoc/clean/auto_trait.rs +++ b/src/librustdoc/clean/auto_trait.rs @@ -414,7 +414,11 @@ fn make_final_bounds( let mut bounds_vec = bounds.into_iter().collect(); self.sort_where_bounds(&mut bounds_vec); - Some(WherePredicate::BoundPredicate { ty, bounds: bounds_vec }) + Some(WherePredicate::BoundPredicate { + ty, + bounds: bounds_vec, + bound_params: Vec::new(), + }) }) .chain( lifetime_to_bounds.into_iter().filter(|&(_, ref bounds)| !bounds.is_empty()).map( @@ -492,7 +496,7 @@ fn param_env_to_generics( } let p = p.unwrap(); match p { - WherePredicate::BoundPredicate { ty, mut bounds } => { + WherePredicate::BoundPredicate { ty, mut bounds, .. } => { // Writing a projection trait bound of the form // ::Name : ?Sized // is illegal, because ?Sized bounds can only diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 111827aacdf..e8f0960da79 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -566,9 +566,11 @@ fn build_macro(cx: &mut DocContext<'_>, did: DefId, name: Symbol) -> clean::Item fn filter_non_trait_generics(trait_did: DefId, mut g: clean::Generics) -> clean::Generics { for pred in &mut g.where_predicates { match *pred { - clean::WherePredicate::BoundPredicate { ty: clean::Generic(ref s), ref mut bounds } - if *s == kw::SelfUpper => - { + clean::WherePredicate::BoundPredicate { + ty: clean::Generic(ref s), + ref mut bounds, + .. + } if *s == kw::SelfUpper => { bounds.retain(|bound| match *bound { clean::GenericBound::TraitBound( clean::PolyTrait { trait_: clean::ResolvedPath { did, .. }, .. }, @@ -591,6 +593,7 @@ fn filter_non_trait_generics(trait_did: DefId, mut g: clean::Generics) -> clean: .. }, ref bounds, + .. } => !(bounds.is_empty() || *s == kw::SelfUpper && did == trait_did), _ => true, }); @@ -605,7 +608,7 @@ fn separate_supertrait_bounds( ) -> (clean::Generics, Vec) { let mut ty_bounds = Vec::new(); g.where_predicates.retain(|pred| match *pred { - clean::WherePredicate::BoundPredicate { ty: clean::Generic(ref s), ref bounds } + clean::WherePredicate::BoundPredicate { ty: clean::Generic(ref s), ref bounds, .. } if *s == kw::SelfUpper => { ty_bounds.extend(bounds.iter().cloned()); diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index bc04480ab7c..634a8dae763 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -330,6 +330,7 @@ fn clean(&self, cx: &mut DocContext<'_>) -> WherePredicate { 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::RegionPredicate(ref wrp) => WherePredicate::RegionPredicate { @@ -370,6 +371,7 @@ fn clean(&self, cx: &mut DocContext<'_>) -> WherePredicate { WherePredicate::BoundPredicate { ty: poly_trait_ref.skip_binder().self_ty().clean(cx), bounds: vec![poly_trait_ref.clean(cx)], + bound_params: Vec::new(), } } } @@ -402,6 +404,7 @@ fn clean(&self, cx: &mut DocContext<'_>) -> Option { Some(WherePredicate::BoundPredicate { ty: ty.clean(cx), bounds: vec![GenericBound::Outlives(lt.clean(cx).expect("failed to clean lifetimes"))], + bound_params: Vec::new(), }) } } @@ -567,7 +570,9 @@ fn is_elided_lifetime(param: &hir::GenericParam<'_>) -> bool { // to where predicates when such cases occur. for where_pred in &mut generics.where_predicates { match *where_pred { - WherePredicate::BoundPredicate { ty: Generic(ref name), ref mut bounds } => { + WherePredicate::BoundPredicate { + ty: Generic(ref name), ref mut bounds, .. + } => { if bounds.is_empty() { for param in &mut generics.params { match param.kind { @@ -721,7 +726,7 @@ fn clean(&self, cx: &mut DocContext<'_>) -> Generics { // handled in cleaning associated types let mut sized_params = FxHashSet::default(); where_predicates.retain(|pred| match *pred { - WP::BoundPredicate { ty: Generic(ref g), ref bounds } => { + WP::BoundPredicate { ty: Generic(ref g), ref bounds, .. } => { if bounds.iter().any(|b| b.is_sized_bound(cx)) { sized_params.insert(*g); false @@ -741,6 +746,7 @@ fn clean(&self, cx: &mut DocContext<'_>) -> Generics { where_predicates.push(WP::BoundPredicate { ty: Type::Generic(tp.name), bounds: vec![GenericBound::maybe_sized(cx)], + bound_params: Vec::new(), }) } } @@ -1117,6 +1123,7 @@ fn clean(&self, cx: &mut DocContext<'_>) -> Item { WherePredicate::BoundPredicate { ty: QPath { ref name, ref self_type, ref trait_, .. }, ref bounds, + .. } => (name, self_type, trait_, bounds), _ => return None, }; diff --git a/src/librustdoc/clean/simplify.rs b/src/librustdoc/clean/simplify.rs index d4d0a8ce24c..3ec0a22a2c0 100644 --- a/src/librustdoc/clean/simplify.rs +++ b/src/librustdoc/clean/simplify.rs @@ -24,16 +24,20 @@ crate fn where_clauses(cx: &DocContext<'_>, clauses: Vec) -> Vec { // First, partition the where clause into its separate components - let mut params: BTreeMap<_, Vec<_>> = BTreeMap::new(); + let mut params: BTreeMap<_, (Vec<_>, Vec<_>)> = BTreeMap::new(); let mut lifetimes = Vec::new(); let mut equalities = Vec::new(); let mut tybounds = Vec::new(); for clause in clauses { match clause { - WP::BoundPredicate { ty, bounds } => match ty { - clean::Generic(s) => params.entry(s).or_default().extend(bounds), - t => tybounds.push((t, bounds)), + WP::BoundPredicate { ty, bounds, bound_params } => match ty { + clean::Generic(s) => { + let (b, p) = params.entry(s).or_default(); + b.extend(bounds); + p.extend(bound_params); + } + t => tybounds.push((t, (bounds, bound_params))), }, WP::RegionPredicate { lifetime, bounds } => { lifetimes.push((lifetime, bounds)); @@ -54,7 +58,7 @@ clean::Generic(s) => s, _ => return true, }; - let bounds = match params.get_mut(generic) { + let (bounds, _) = match params.get_mut(generic) { Some(bound) => bound, None => return true, }; @@ -67,10 +71,16 @@ clauses.extend( lifetimes.into_iter().map(|(lt, bounds)| WP::RegionPredicate { lifetime: lt, bounds }), ); - clauses.extend( - params.into_iter().map(|(k, v)| WP::BoundPredicate { ty: clean::Generic(k), bounds: v }), - ); - clauses.extend(tybounds.into_iter().map(|(ty, bounds)| WP::BoundPredicate { ty, bounds })); + clauses.extend(params.into_iter().map(|(k, (bounds, params))| WP::BoundPredicate { + ty: clean::Generic(k), + bounds, + bound_params: params, + })); + clauses.extend(tybounds.into_iter().map(|(ty, (bounds, bound_params))| WP::BoundPredicate { + ty, + bounds, + bound_params, + })); clauses.extend(equalities.into_iter().map(|(lhs, rhs)| WP::EqPredicate { lhs, rhs })); clauses } diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 6a7c3f8caa4..5d036d4d35b 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1193,7 +1193,7 @@ impl Lifetime { #[derive(Clone, Debug)] crate enum WherePredicate { - BoundPredicate { ty: Type, bounds: Vec }, + BoundPredicate { ty: Type, bounds: Vec, bound_params: Vec }, RegionPredicate { lifetime: Lifetime, bounds: Vec }, EqPredicate { lhs: Type, rhs: Type }, } diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 918a5cb5094..a424932d83f 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -249,17 +249,33 @@ impl clean::Generics { } match pred { - clean::WherePredicate::BoundPredicate { ty, bounds } => { + clean::WherePredicate::BoundPredicate { ty, bounds, bound_params } => { let bounds = bounds; + let for_prefix = match bound_params.len() { + 0 => String::new(), + _ if f.alternate() => { + format!( + "for<{:#}> ", + comma_sep(bound_params.iter().map(|lt| lt.print())) + ) + } + _ => format!( + "for<{}> ", + comma_sep(bound_params.iter().map(|lt| lt.print())) + ), + }; + if f.alternate() { clause.push_str(&format!( - "{:#}: {:#}", + "{}{:#}: {:#}", + for_prefix, ty.print(cx), print_generic_bounds(bounds, cx) )); } else { clause.push_str(&format!( - "{}: {}", + "{}{}: {}", + for_prefix, ty.print(cx), print_generic_bounds(bounds, cx) )); diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 7086dd8c4d2..c844d91096a 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -328,9 +328,10 @@ impl FromWithTcx for WherePredicate { fn from_tcx(predicate: clean::WherePredicate, tcx: TyCtxt<'_>) -> Self { use clean::WherePredicate::*; match predicate { - BoundPredicate { ty, bounds } => WherePredicate::BoundPredicate { + BoundPredicate { ty, bounds, .. } => WherePredicate::BoundPredicate { ty: ty.into_tcx(tcx), bounds: bounds.into_iter().map(|x| x.into_tcx(tcx)).collect(), + // FIXME: add `bound_params` to rustdoc-json-params? }, RegionPredicate { lifetime, bounds } => WherePredicate::RegionPredicate { lifetime: lifetime.0.to_string(), diff --git a/src/test/rustdoc/higher-ranked-trait-bounds.rs b/src/test/rustdoc/higher-ranked-trait-bounds.rs new file mode 100644 index 00000000000..b5c55df287b --- /dev/null +++ b/src/test/rustdoc/higher-ranked-trait-bounds.rs @@ -0,0 +1,41 @@ +#![crate_name = "foo"] + +trait A<'x> {} + +// @has foo/fn.test1.html +// @has - '//pre' "pub fn test1() where for<'a> &'a T: Iterator," +pub fn test1() +where + for<'a> &'a T: Iterator, +{ +} + +// @has foo/fn.test2.html +// @has - '//pre' "pub fn test2() where for<'a, 'b> &'a T: A<'b>," +pub fn test2() +where + for<'a, 'b> &'a T: A<'b>, +{ +} + +// @has foo/fn.test3.html +// @has - '//pre' "pub fn test3() where F: for<'a, 'b> Fn(&'a u8, &'b u8)," +pub fn test3() +where + F: for<'a, 'b> Fn(&'a u8, &'b u8), +{ +} + +// @has foo/struct.Foo.html +pub struct Foo<'a> { + _x: &'a u8, +} + +impl<'a> Foo<'a> { + // @has - '//code' "pub fn bar() where T: A<'a>," + pub fn bar() + where + T: A<'a>, + { + } +}