]> git.lizzy.rs Git - rust.git/commitdiff
rustdoc: Fix where clauses on re-exports
authorTom Jakubowski <tom@crystae.net>
Sun, 11 Jan 2015 07:50:46 +0000 (23:50 -0800)
committerTom Jakubowski <tom@crystae.net>
Mon, 12 Jan 2015 17:25:07 +0000 (09:25 -0800)
Projection predicates on re-exports, for the time being, are rendered as
equality predicates because that's easier. It would be nice to fix this
in the future.

Some gymnastics were needed to remove redundant bounds from the `types`
and `lifetimes` fields, remove implicit `Sized` bounds, and re-create
`?Sized` bounds.

Fix #20203, fix #20924, fix #20911, fix #20534

src/librustdoc/clean/mod.rs
src/librustdoc/html/format.rs

index a44c73e8c4120f91d9b986673e6a68273003faf8..80494e3811090d9a3111072572ad094baf946c62 100644 (file)
@@ -104,6 +104,12 @@ fn clean(&self, cx: &DocContext) -> Option<U> {
     }
 }
 
+impl<T, U> Clean<U> for ty::Binder<T> where T: Clean<U> {
+    fn clean(&self, cx: &DocContext) -> U {
+        self.0.clean(cx)
+    }
+}
+
 impl<T: Clean<U>, U> Clean<Vec<U>> for syntax::owned_slice::OwnedSlice<T> {
     fn clean(&self, cx: &DocContext) -> Vec<U> {
         self.iter().map(|x| x.clean(cx)).collect()
@@ -603,12 +609,6 @@ fn clean(&self, cx: &DocContext) -> TyParamBound {
     }
 }
 
-impl<'tcx> Clean<TyParamBound> for ty::PolyTraitRef<'tcx> {
-    fn clean(&self, cx: &DocContext) -> TyParamBound {
-        self.0.clean(cx)
-    }
-}
-
 impl<'tcx> Clean<TyParamBound> for ty::TraitRef<'tcx> {
     fn clean(&self, cx: &DocContext) -> TyParamBound {
         let tcx = match cx.tcx_opt() {
@@ -730,8 +730,7 @@ fn clean(&self, cx: &DocContext) -> Option<Lifetime> {
 pub enum WherePredicate {
     BoundPredicate { ty: Type, bounds: Vec<TyParamBound> },
     RegionPredicate { lifetime: Lifetime, bounds: Vec<Lifetime>},
-    // FIXME (#20041)
-    EqPredicate
+    EqPredicate { lhs: Type, rhs: Type }
 }
 
 impl Clean<WherePredicate> for ast::WherePredicate {
@@ -752,12 +751,89 @@ fn clean(&self, cx: &DocContext) -> WherePredicate {
             }
 
             ast::WherePredicate::EqPredicate(_) => {
-                WherePredicate::EqPredicate
+                unimplemented!() // FIXME(#20041)
             }
         }
     }
 }
 
+impl<'a> Clean<WherePredicate> for ty::Predicate<'a> {
+    fn clean(&self, cx: &DocContext) -> WherePredicate {
+        use rustc::middle::ty::Predicate;
+
+        match *self {
+            Predicate::Trait(ref pred) => pred.clean(cx),
+            Predicate::Equate(ref pred) => pred.clean(cx),
+            Predicate::RegionOutlives(ref pred) => pred.clean(cx),
+            Predicate::TypeOutlives(ref pred) => pred.clean(cx),
+            Predicate::Projection(ref pred) => pred.clean(cx)
+        }
+    }
+}
+
+impl<'a> Clean<WherePredicate> for ty::TraitPredicate<'a> {
+    fn clean(&self, cx: &DocContext) -> WherePredicate {
+        WherePredicate::BoundPredicate {
+            ty: self.trait_ref.substs.self_ty().clean(cx).unwrap(),
+            bounds: vec![self.trait_ref.clean(cx)]
+        }
+    }
+}
+
+impl<'tcx> Clean<WherePredicate> for ty::EquatePredicate<'tcx> {
+    fn clean(&self, cx: &DocContext) -> WherePredicate {
+        let ty::EquatePredicate(ref lhs, ref rhs) = *self;
+        WherePredicate::EqPredicate {
+            lhs: lhs.clean(cx),
+            rhs: rhs.clean(cx)
+        }
+    }
+}
+
+impl Clean<WherePredicate> for ty::OutlivesPredicate<ty::Region, ty::Region> {
+    fn clean(&self, cx: &DocContext) -> WherePredicate {
+        let ty::OutlivesPredicate(ref a, ref b) = *self;
+        WherePredicate::RegionPredicate {
+            lifetime: a.clean(cx).unwrap(),
+            bounds: vec![b.clean(cx).unwrap()]
+        }
+    }
+}
+
+impl<'tcx> Clean<WherePredicate> for ty::OutlivesPredicate<ty::Ty<'tcx>, ty::Region> {
+    fn clean(&self, cx: &DocContext) -> WherePredicate {
+        let ty::OutlivesPredicate(ref ty, ref lt) = *self;
+
+        WherePredicate::BoundPredicate {
+            ty: ty.clean(cx),
+            bounds: vec![TyParamBound::RegionBound(lt.clean(cx).unwrap())]
+        }
+    }
+}
+
+impl<'tcx> Clean<WherePredicate> for ty::ProjectionPredicate<'tcx> {
+    fn clean(&self, cx: &DocContext) -> WherePredicate {
+        WherePredicate::EqPredicate {
+            lhs: self.projection_ty.clean(cx),
+            rhs: self.ty.clean(cx)
+        }
+    }
+}
+
+impl<'tcx> Clean<Type> for ty::ProjectionTy<'tcx> {
+    fn clean(&self, cx: &DocContext) -> Type {
+        let trait_ = match self.trait_ref.clean(cx) {
+            TyParamBound::TraitBound(t, _) => t.trait_,
+            TyParamBound::RegionBound(_) => panic!("cleaning a trait got a region??"),
+        };
+        Type::QPath {
+            name: self.item_name.clean(cx),
+            self_type: box self.trait_ref.self_ty().clean(cx),
+            trait_: box trait_
+        }
+    }
+}
+
 // maybe use a Generic enum and use ~[Generic]?
 #[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Show)]
 pub struct Generics {
@@ -778,11 +854,80 @@ fn clean(&self, cx: &DocContext) -> Generics {
 
 impl<'a, 'tcx> Clean<Generics> for (&'a ty::Generics<'tcx>, subst::ParamSpace) {
     fn clean(&self, cx: &DocContext) -> Generics {
-        let (me, space) = *self;
+        use std::collections::HashSet;
+        use syntax::ast::TraitBoundModifier as TBM;
+        use self::WherePredicate as WP;
+
+        fn has_sized_bound(bounds: &[TyParamBound], cx: &DocContext) -> bool {
+            if let Some(tcx) = cx.tcx_opt() {
+                let sized_did = match tcx.lang_items.sized_trait() {
+                    Some(did) => did,
+                    None => return false
+                };
+                for bound in bounds.iter() {
+                    if let TyParamBound::TraitBound(PolyTrait {
+                        trait_: Type::ResolvedPath { did, .. }, ..
+                    }, TBM::None) = *bound {
+                        if did == sized_did {
+                            return true
+                        }
+                    }
+                }
+            }
+            false
+        }
+
+        let (gens, space) = *self;
+        // Bounds in the type_params and lifetimes fields are repeated in the predicates
+        // field (see rustc_typeck::collect::ty_generics), so remove them.
+        let stripped_typarams = gens.types.get_slice(space).iter().map(|tp| {
+            let mut stp = tp.clone();
+            stp.bounds = ty::ParamBounds::empty();
+            stp.clean(cx)
+        }).collect::<Vec<_>>();
+        let stripped_lifetimes = gens.regions.get_slice(space).iter().map(|rp| {
+            let mut srp = rp.clone();
+            srp.bounds = Vec::new();
+            srp.clean(cx)
+        }).collect::<Vec<_>>();
+
+        let where_predicates = gens.predicates.get_slice(space).to_vec().clean(cx);
+        // Type parameters have a Sized bound by default unless removed with ?Sized.
+        // Scan through the predicates and mark any type parameter with a Sized
+        // bound, removing the bounds as we find them.
+        let mut sized_params = HashSet::new();
+        let mut where_predicates = where_predicates.into_iter().filter_map(|pred| {
+            if let WP::BoundPredicate { ty: Type::Generic(ref g), ref bounds } = pred {
+                if has_sized_bound(&**bounds, cx) {
+                    sized_params.insert(g.clone());
+                    return None
+                }
+            }
+            Some(pred)
+        }).collect::<Vec<_>>();
+        // Finally, run through the type parameters again and insert a ?Sized unbound for
+        // any we didn't find to be Sized.
+        for tp in stripped_typarams.iter() {
+            if !sized_params.contains(&tp.name) {
+                let mut sized_bound = ty::BuiltinBound::BoundSized.clean(cx);
+                if let TyParamBound::TraitBound(_, ref mut tbm) = sized_bound {
+                    *tbm = TBM::Maybe
+                };
+                where_predicates.push(WP::BoundPredicate {
+                    ty: Type::Generic(tp.name.clone()),
+                    bounds: vec![sized_bound]
+                })
+            }
+        }
+
+        // It would be nice to collect all of the bounds on a type and recombine
+        // them if possible, to avoid e.g. `where T: Foo, T: Bar, T: Sized, T: 'a`
+        // and instead see `where T: Foo + Bar + Sized + 'a`
+
         Generics {
-            type_params: me.types.get_slice(space).to_vec().clean(cx),
-            lifetimes: me.regions.get_slice(space).to_vec().clean(cx),
-            where_predicates: vec![]
+            type_params: stripped_typarams,
+            lifetimes: stripped_lifetimes,
+            where_predicates: where_predicates
         }
     }
 }
index 00f7c570b5d09f5b96c3d61eef0b213060ad561b..4bfde9e5d7d973af08fa06ae94ec18bbc80b320e 100644 (file)
@@ -153,8 +153,8 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                         try!(write!(f, "{}", lifetime));
                     }
                 }
-                &clean::WherePredicate::EqPredicate => {
-                    unimplemented!()
+                &clean::WherePredicate::EqPredicate { ref lhs, ref rhs } => {
+                    try!(write!(f, "{} == {}", lhs, rhs));
                 }
             }
         }