about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLeón Orell Valerian Liehr <liehr.exchange@gmx.net>2022-10-04 14:08:25 +0200
committerLeón Orell Valerian Liehr <liehr.exchange@gmx.net>2022-10-05 23:14:15 +0200
commit73c239e5eb0b3197ee6d0faeaf85a8eaccb8a491 (patch)
treeb00473157aaff5ba71ff469c49fbeee73b88ef92
parentf1112099eba41abadb6f921df7edba70affe92c5 (diff)
downloadrust-73c239e5eb0b3197ee6d0faeaf85a8eaccb8a491.tar.gz
rust-73c239e5eb0b3197ee6d0faeaf85a8eaccb8a491.zip
rustdoc: render more cross-crate hrtbs properly
-rw-r--r--src/librustdoc/clean/auto_trait.rs13
-rw-r--r--src/librustdoc/clean/mod.rs62
-rw-r--r--src/librustdoc/clean/simplify.rs37
-rw-r--r--src/librustdoc/clean/types.rs11
-rw-r--r--src/librustdoc/html/format.rs3
-rw-r--r--src/librustdoc/json/conversions.rs5
-rw-r--r--src/test/rustdoc/inline_cross/assoc_item_trait_bounds_with_bindings.rs9
-rw-r--r--src/test/rustdoc/inline_cross/auxiliary/assoc_item_trait_bounds_with_bindings.rs5
-rw-r--r--src/test/rustdoc/inline_cross/auxiliary/impl_trait_aux.rs13
-rw-r--r--src/test/rustdoc/inline_cross/impl_trait.rs7
10 files changed, 124 insertions, 41 deletions
diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs
index 175472797cb..efa9242a467 100644
--- a/src/librustdoc/clean/auto_trait.rs
+++ b/src/librustdoc/clean/auto_trait.rs
@@ -475,6 +475,12 @@ where
 
         let mut ty_to_fn: FxHashMap<Type, (PolyTrait, Option<Type>)> = Default::default();
 
+        // FIXME: This code shares much of the logic found in `clean_ty_generics` and
+        //        `simplify::where_clause`. Consider deduplicating it to avoid diverging
+        //        implementations.
+        //        Further, the code below does not merge (partially re-sugared) bounds like
+        //        `Tr<A = T>` & `Tr<B = U>` and it does not render higher-ranked parameters
+        //        originating from equality predicates.
         for p in clean_where_predicates {
             let (orig_p, p) = (p, clean_predicate(p, self.cx));
             if p.is_none() {
@@ -549,8 +555,8 @@ where
                 WherePredicate::RegionPredicate { lifetime, bounds } => {
                     lifetime_to_bounds.entry(lifetime).or_default().extend(bounds);
                 }
-                WherePredicate::EqPredicate { lhs, rhs } => {
-                    match lhs {
+                WherePredicate::EqPredicate { lhs, rhs, bound_params } => {
+                    match *lhs {
                         Type::QPath(box QPathData {
                             ref assoc, ref self_type, ref trait_, ..
                         }) => {
@@ -585,13 +591,14 @@ where
                                 GenericArgs::AngleBracketed { ref mut bindings, .. } => {
                                     bindings.push(TypeBinding {
                                         assoc: assoc.clone(),
-                                        kind: TypeBindingKind::Equality { term: rhs },
+                                        kind: TypeBindingKind::Equality { term: *rhs },
                                     });
                                 }
                                 GenericArgs::Parenthesized { .. } => {
                                     existing_predicates.push(WherePredicate::EqPredicate {
                                         lhs: lhs.clone(),
                                         rhs,
+                                        bound_params,
                                     });
                                     continue; // If something other than a Fn ends up
                                     // with parentheses, leave it alone
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index c61175ecebf..11834a53afb 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -292,8 +292,9 @@ fn clean_where_predicate<'tcx>(
         },
 
         hir::WherePredicate::EqPredicate(ref wrp) => WherePredicate::EqPredicate {
-            lhs: clean_ty(wrp.lhs_ty, cx),
-            rhs: clean_ty(wrp.rhs_ty, cx).into(),
+            lhs: Box::new(clean_ty(wrp.lhs_ty, cx)),
+            rhs: Box::new(clean_ty(wrp.rhs_ty, cx).into()),
+            bound_params: Vec::new(),
         },
     })
 }
@@ -309,7 +310,9 @@ pub(crate) fn clean_predicate<'tcx>(
         }
         ty::PredicateKind::RegionOutlives(pred) => clean_region_outlives_predicate(pred),
         ty::PredicateKind::TypeOutlives(pred) => clean_type_outlives_predicate(pred, cx),
-        ty::PredicateKind::Projection(pred) => Some(clean_projection_predicate(pred, cx)),
+        ty::PredicateKind::Projection(pred) => {
+            Some(clean_projection_predicate(bound_predicate.rebind(pred), cx))
+        }
         ty::PredicateKind::ConstEvaluatable(..) => None,
         ty::PredicateKind::WellFormed(..) => None,
 
@@ -387,13 +390,25 @@ fn clean_hir_term<'tcx>(term: &hir::Term<'tcx>, cx: &mut DocContext<'tcx>) -> Te
 }
 
 fn clean_projection_predicate<'tcx>(
-    pred: ty::ProjectionPredicate<'tcx>,
+    pred: ty::Binder<'tcx, ty::ProjectionPredicate<'tcx>>,
     cx: &mut DocContext<'tcx>,
 ) -> WherePredicate {
-    let ty::ProjectionPredicate { projection_ty, term } = pred;
+    let late_bound_regions = cx
+        .tcx
+        .collect_referenced_late_bound_regions(&pred)
+        .into_iter()
+        .filter_map(|br| match br {
+            ty::BrNamed(_, name) if name != kw::UnderscoreLifetime => Some(Lifetime(name)),
+            _ => None,
+        })
+        .collect();
+
+    let ty::ProjectionPredicate { projection_ty, term } = pred.skip_binder();
+
     WherePredicate::EqPredicate {
-        lhs: clean_projection(projection_ty, cx, None),
-        rhs: clean_middle_term(term, cx),
+        lhs: Box::new(clean_projection(projection_ty, cx, None)),
+        rhs: Box::new(clean_middle_term(term, cx)),
+        bound_params: late_bound_regions,
     }
 }
 
@@ -655,8 +670,9 @@ fn clean_ty_generics<'tcx>(
         })
         .collect::<Vec<GenericParamDef>>();
 
-    // param index -> [(DefId of trait, associated type name and generics, type)]
-    let mut impl_trait_proj = FxHashMap::<u32, Vec<(DefId, PathSegment, Ty<'_>)>>::default();
+    // param index -> [(trait DefId, associated type name & generics, type, higher-ranked params)]
+    let mut impl_trait_proj =
+        FxHashMap::<u32, Vec<(DefId, PathSegment, Ty<'_>, Vec<GenericParamDef>)>>::default();
 
     let where_predicates = preds
         .predicates
@@ -715,6 +731,14 @@ fn clean_ty_generics<'tcx>(
                             trait_did,
                             name,
                             rhs.ty().unwrap(),
+                            p.get_bound_params()
+                                .into_iter()
+                                .flatten()
+                                .map(|param| GenericParamDef {
+                                    name: param.0,
+                                    kind: GenericParamDefKind::Lifetime { outlives: Vec::new() },
+                                })
+                                .collect(),
                         ));
                     }
 
@@ -730,15 +754,19 @@ fn clean_ty_generics<'tcx>(
         // Move trait bounds to the front.
         bounds.sort_by_key(|b| !matches!(b, GenericBound::TraitBound(..)));
 
-        if let crate::core::ImplTraitParam::ParamIndex(idx) = param {
-            if let Some(proj) = impl_trait_proj.remove(&idx) {
-                for (trait_did, name, rhs) in proj {
-                    let rhs = clean_middle_ty(rhs, cx, None);
-                    simplify::merge_bounds(cx, &mut bounds, trait_did, name, &Term::Type(rhs));
-                }
+        let crate::core::ImplTraitParam::ParamIndex(idx) = param else { unreachable!() };
+        if let Some(proj) = impl_trait_proj.remove(&idx) {
+            for (trait_did, name, rhs, bound_params) in proj {
+                let rhs = clean_middle_ty(rhs, cx, None);
+                simplify::merge_bounds(
+                    cx,
+                    &mut bounds,
+                    bound_params,
+                    trait_did,
+                    name,
+                    &Term::Type(rhs),
+                );
             }
-        } else {
-            unreachable!();
         }
 
         cx.impl_trait_bounds.insert(param, bounds);
diff --git a/src/librustdoc/clean/simplify.rs b/src/librustdoc/clean/simplify.rs
index f82ea8969ab..1bcb9fcd5a4 100644
--- a/src/librustdoc/clean/simplify.rs
+++ b/src/librustdoc/clean/simplify.rs
@@ -39,23 +39,23 @@ pub(crate) fn where_clauses(cx: &DocContext<'_>, clauses: Vec<WP>) -> Vec<WP> {
             WP::RegionPredicate { lifetime, bounds } => {
                 lifetimes.push((lifetime, bounds));
             }
-            WP::EqPredicate { lhs, rhs } => equalities.push((lhs, rhs)),
+            WP::EqPredicate { lhs, rhs, bound_params } => equalities.push((lhs, rhs, bound_params)),
         }
     }
 
     // Look for equality predicates on associated types that can be merged into
     // general bound predicates.
-    equalities.retain(|&(ref lhs, ref rhs)| {
+    equalities.retain(|&(ref lhs, ref rhs, ref bound_params)| {
         let Some((ty, trait_did, name)) = lhs.projection() else { return true; };
-        // FIXME(fmease): We don't handle HRTBs correctly here.
-        //                Pass `_bound_params` (higher-rank lifetimes) to a modified version of
-        //                `merge_bounds`. That vector is currently always empty though since we
-        //                don't keep track of late-bound lifetimes when cleaning projection
-        //                predicates to cleaned equality predicates while we should first query
-        //                them with `collect_referenced_late_bound_regions` and then store them
-        //                (or something similar). For prior art, see `clean::auto_trait`.
-        let Some((bounds, _bound_params)) = tybounds.get_mut(ty) else { return true };
-        merge_bounds(cx, bounds, trait_did, name, rhs)
+        let Some((bounds, _)) = tybounds.get_mut(ty) else { return true };
+        let bound_params = bound_params
+            .into_iter()
+            .map(|param| clean::GenericParamDef {
+                name: param.0,
+                kind: clean::GenericParamDefKind::Lifetime { outlives: Vec::new() },
+            })
+            .collect();
+        merge_bounds(cx, bounds, bound_params, trait_did, name, rhs)
     });
 
     // And finally, let's reassemble everything
@@ -68,13 +68,18 @@ pub(crate) fn where_clauses(cx: &DocContext<'_>, clauses: Vec<WP>) -> Vec<WP> {
         bounds,
         bound_params,
     }));
-    clauses.extend(equalities.into_iter().map(|(lhs, rhs)| WP::EqPredicate { lhs, rhs }));
+    clauses.extend(equalities.into_iter().map(|(lhs, rhs, bound_params)| WP::EqPredicate {
+        lhs,
+        rhs,
+        bound_params,
+    }));
     clauses
 }
 
 pub(crate) fn merge_bounds(
     cx: &clean::DocContext<'_>,
     bounds: &mut Vec<clean::GenericBound>,
+    mut bound_params: Vec<clean::GenericParamDef>,
     trait_did: DefId,
     assoc: clean::PathSegment,
     rhs: &clean::Term,
@@ -91,6 +96,14 @@ pub(crate) fn merge_bounds(
             return false;
         }
         let last = trait_ref.trait_.segments.last_mut().expect("segments were empty");
+
+        trait_ref.generic_params.append(&mut bound_params);
+        // Since the parameters (probably) originate from `tcx.collect_*_late_bound_regions` which
+        // returns a hash set, sort them alphabetically to guarantee a stable and deterministic
+        // output (and to fully deduplicate them).
+        trait_ref.generic_params.sort_unstable_by(|p, q| p.name.as_str().cmp(q.name.as_str()));
+        trait_ref.generic_params.dedup_by_key(|p| p.name);
+
         match last.args {
             PP::AngleBracketed { ref mut bindings, .. } => {
                 bindings.push(clean::TypeBinding {
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index 5a4fa05e261..7e053380069 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -1350,7 +1350,7 @@ impl Lifetime {
 pub(crate) enum WherePredicate {
     BoundPredicate { ty: Type, bounds: Vec<GenericBound>, bound_params: Vec<Lifetime> },
     RegionPredicate { lifetime: Lifetime, bounds: Vec<GenericBound> },
-    EqPredicate { lhs: Type, rhs: Term },
+    EqPredicate { lhs: Box<Type>, rhs: Box<Term>, bound_params: Vec<Lifetime> },
 }
 
 impl WherePredicate {
@@ -1361,6 +1361,15 @@ impl WherePredicate {
             _ => None,
         }
     }
+
+    pub(crate) fn get_bound_params(&self) -> Option<&[Lifetime]> {
+        match self {
+            Self::BoundPredicate { bound_params, .. } | Self::EqPredicate { bound_params, .. } => {
+                Some(bound_params)
+            }
+            _ => None,
+        }
+    }
 }
 
 #[derive(Clone, PartialEq, Eq, Debug, Hash)]
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index 6d46267931b..943a074366e 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -331,7 +331,8 @@ pub(crate) fn print_where_clause<'a, 'tcx: 'a>(
                         bounds_display.truncate(bounds_display.len() - " + ".len());
                         write!(f, "{}: {bounds_display}", lifetime.print())
                     }
-                    clean::WherePredicate::EqPredicate { lhs, rhs } => {
+                    // FIXME(fmease): Render bound params.
+                    clean::WherePredicate::EqPredicate { lhs, rhs, bound_params: _ } => {
                         if f.alternate() {
                             write!(f, "{:#} == {:#}", lhs.print(cx), rhs.print(cx))
                         } else {
diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs
index 4170412caef..cdf59cdd328 100644
--- a/src/librustdoc/json/conversions.rs
+++ b/src/librustdoc/json/conversions.rs
@@ -432,8 +432,9 @@ impl FromWithTcx<clean::WherePredicate> for WherePredicate {
                 lifetime: convert_lifetime(lifetime),
                 bounds: bounds.into_tcx(tcx),
             },
-            EqPredicate { lhs, rhs } => {
-                WherePredicate::EqPredicate { lhs: lhs.into_tcx(tcx), rhs: rhs.into_tcx(tcx) }
+            // FIXME(fmease): Convert bound parameters as well.
+            EqPredicate { lhs, rhs, bound_params: _ } => {
+                WherePredicate::EqPredicate { lhs: (*lhs).into_tcx(tcx), rhs: (*rhs).into_tcx(tcx) }
             }
         }
     }
diff --git a/src/test/rustdoc/inline_cross/assoc_item_trait_bounds_with_bindings.rs b/src/test/rustdoc/inline_cross/assoc_item_trait_bounds_with_bindings.rs
index b026f399a56..00976aa7442 100644
--- a/src/test/rustdoc/inline_cross/assoc_item_trait_bounds_with_bindings.rs
+++ b/src/test/rustdoc/inline_cross/assoc_item_trait_bounds_with_bindings.rs
@@ -8,8 +8,6 @@ extern crate assoc_item_trait_bounds_with_bindings as aux;
 
 // FIXME(fmease): Don't render an incorrect `T: ?Sized` where-clause for parameters
 //                of GATs like `Main::Out{2,4}`. Add a snapshot test once it's fixed.
-// FIXME(fmease): Print the `for<>` parameter list in the bounds of
-//                `Main::Out{6,11,12}`.
 
 // @has main/trait.Main.html
 // @has - '//*[@id="associatedtype.Out0"]' 'type Out0: Support<Item = ()>'
@@ -18,13 +16,14 @@ extern crate assoc_item_trait_bounds_with_bindings as aux;
 // @has - '//*[@id="associatedtype.Out3"]' 'type Out3: Support<Produce<()> = bool>'
 // @has - '//*[@id="associatedtype.Out4"]' 'type Out4<T>: Support<Produce<T> = T>'
 // @has - '//*[@id="associatedtype.Out5"]' "type Out5: Support<Output<'static> = &'static ()>"
-// @has - '//*[@id="associatedtype.Out6"]' "type Out6: Support<Output<'a> = &'a ()>"
+// @has - '//*[@id="associatedtype.Out6"]' "type Out6: for<'a> Support<Output<'a> = &'a ()>"
 // @has - '//*[@id="associatedtype.Out7"]' "type Out7: Support<Item = String, Produce<i32> = u32> + Unrelated"
 // @has - '//*[@id="associatedtype.Out8"]' "type Out8: Unrelated + Protocol<i16, Q1 = u128, Q0 = ()>"
 // @has - '//*[@id="associatedtype.Out9"]' "type Out9: FnMut(i32) -> bool + Clone"
 // @has - '//*[@id="associatedtype.Out10"]' "type Out10<'q>: Support<Output<'q> = ()>"
-// @has - '//*[@id="associatedtype.Out11"]' "type Out11: Helper<A<'s> = &'s (), B<'r> = ()>"
-// @has - '//*[@id="associatedtype.Out12"]' "type Out12: Helper<B<'w> = Cow<'w, str>, A<'w> = bool>"
+// @has - '//*[@id="associatedtype.Out11"]' "type Out11: for<'r, 's> Helper<A<'s> = &'s (), B<'r> = ()>"
+// @has - '//*[@id="associatedtype.Out12"]' "type Out12: for<'w> Helper<B<'w> = Cow<'w, str>, A<'w> = bool>"
+// @has - '//*[@id="associatedtype.Out13"]' "type Out13: for<'fst, 'snd> Aid<'snd, Result<'fst> = &'fst mut str>"
 //
 // Snapshots: Check that we do not render any where-clauses for those associated types since all of
 // the trait bounds contained within were moved to the bounds of the respective item.
diff --git a/src/test/rustdoc/inline_cross/auxiliary/assoc_item_trait_bounds_with_bindings.rs b/src/test/rustdoc/inline_cross/auxiliary/assoc_item_trait_bounds_with_bindings.rs
index 7225f2dca10..f451b1a0e99 100644
--- a/src/test/rustdoc/inline_cross/auxiliary/assoc_item_trait_bounds_with_bindings.rs
+++ b/src/test/rustdoc/inline_cross/auxiliary/assoc_item_trait_bounds_with_bindings.rs
@@ -14,6 +14,7 @@ pub trait Main {
     type Out10<'q>: Support<Output<'q> = ()>;
     type Out11: for<'r, 's> Helper<A<'s> = &'s (), B<'r> = ()>;
     type Out12: for<'w> Helper<B<'w> = std::borrow::Cow<'w, str>, A<'w> = bool>;
+    type Out13: for<'fst, 'snd> Aid<'snd, Result<'fst> = &'fst mut str>;
 
     fn make<F>(_: F, _: impl FnMut(&str) -> bool)
     where
@@ -38,3 +39,7 @@ pub trait Helper {
     type A<'q>;
     type B<'q>;
 }
+
+pub trait Aid<'src> {
+    type Result<'inter>;
+}
diff --git a/src/test/rustdoc/inline_cross/auxiliary/impl_trait_aux.rs b/src/test/rustdoc/inline_cross/auxiliary/impl_trait_aux.rs
index 913ba8f2a16..19433c9682b 100644
--- a/src/test/rustdoc/inline_cross/auxiliary/impl_trait_aux.rs
+++ b/src/test/rustdoc/inline_cross/auxiliary/impl_trait_aux.rs
@@ -13,6 +13,19 @@ pub fn func3(_x: impl Iterator<Item = impl Iterator<Item = u8>> + Clone) {}
 
 pub fn func4<T: Iterator<Item = impl Clone>>(_x: T) {}
 
+pub fn func5(
+    _f: impl for<'any> Fn(&'any str, &'any str) -> bool + for<'r> Other<T<'r> = ()>,
+    _a: impl for<'alpha, 'beta> Auxiliary<'alpha, Item<'beta> = fn(&'beta ())>,
+) {}
+
+pub trait Other {
+    type T<'dependency>;
+}
+
+pub trait Auxiliary<'arena> {
+    type Item<'input>;
+}
+
 pub async fn async_fn() {}
 
 pub struct Foo;
diff --git a/src/test/rustdoc/inline_cross/impl_trait.rs b/src/test/rustdoc/inline_cross/impl_trait.rs
index ef615472b0e..6c1cf8252a9 100644
--- a/src/test/rustdoc/inline_cross/impl_trait.rs
+++ b/src/test/rustdoc/inline_cross/impl_trait.rs
@@ -26,6 +26,13 @@ pub use impl_trait_aux::func3;
 // @has - '//pre[@class="rust fn"]' "T: Iterator<Item = impl Clone>,"
 pub use impl_trait_aux::func4;
 
+// @has impl_trait/fn.func5.html
+// @has - '//pre[@class="rust fn"]' "func5("
+// @has - '//pre[@class="rust fn"]' "_f: impl for<'any> Fn(&'any str, &'any str) -> bool + for<'r> Other<T<'r> = ()>,"
+// @has - '//pre[@class="rust fn"]' "_a: impl for<'alpha, 'beta> Auxiliary<'alpha, Item<'beta> = fn(&'beta ())>"
+// @!has - '//pre[@class="rust fn"]' 'where'
+pub use impl_trait_aux::func5;
+
 // @has impl_trait/fn.async_fn.html
 // @has - '//pre[@class="rust fn"]' "pub async fn async_fn()"
 pub use impl_trait_aux::async_fn;