about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustdoc/clean/mod.rs241
-rw-r--r--src/librustdoc/html/format.rs59
2 files changed, 187 insertions, 113 deletions
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index a44c73e8c41..b65e1d1d664 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -104,6 +104,12 @@ impl<T: Clean<U>, U> Clean<Option<U>> for Option<T> {
     }
 }
 
+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()
@@ -498,22 +504,28 @@ impl Clean<TyParamBound> for ast::TyParamBound {
     }
 }
 
-impl<'tcx> Clean<Vec<TyParamBound>> for ty::ExistentialBounds<'tcx> {
-    fn clean(&self, cx: &DocContext) -> Vec<TyParamBound> {
-        let mut vec = vec![];
-        self.region_bound.clean(cx).map(|b| vec.push(RegionBound(b)));
+impl<'tcx> Clean<(Vec<TyParamBound>, Vec<TypeBinding>)> for ty::ExistentialBounds<'tcx> {
+    fn clean(&self, cx: &DocContext) -> (Vec<TyParamBound>, Vec<TypeBinding>) {
+        let mut tp_bounds = vec![];
+        self.region_bound.clean(cx).map(|b| tp_bounds.push(RegionBound(b)));
         for bb in self.builtin_bounds.iter() {
-            vec.push(bb.clean(cx));
+            tp_bounds.push(bb.clean(cx));
         }
 
-        // FIXME(#20299) -- should do something with projection bounds
+        let mut bindings = vec![];
+        for &ty::Binder(ref pb) in self.projection_bounds.iter() {
+            bindings.push(TypeBinding {
+                name: pb.projection_ty.item_name.clean(cx),
+                ty: pb.ty.clean(cx)
+            });
+        }
 
-        vec
+        (tp_bounds, bindings)
     }
 }
 
 fn external_path_params(cx: &DocContext, trait_did: Option<ast::DefId>,
-                        substs: &subst::Substs) -> PathParameters {
+                        bindings: Vec<TypeBinding>, substs: &subst::Substs) -> PathParameters {
     use rustc::middle::ty::sty;
     let lifetimes = substs.regions().get_slice(subst::TypeSpace)
                     .iter()
@@ -531,7 +543,7 @@ fn external_path_params(cx: &DocContext, trait_did: Option<ast::DefId>,
                     return PathParameters::AngleBracketed {
                         lifetimes: lifetimes,
                         types: types.clean(cx),
-                        bindings: vec![]
+                        bindings: bindings
                     }
                 }
             };
@@ -548,7 +560,7 @@ fn external_path_params(cx: &DocContext, trait_did: Option<ast::DefId>,
             PathParameters::AngleBracketed {
                 lifetimes: lifetimes,
                 types: types.clean(cx),
-                bindings: vec![] // FIXME(#20646)
+                bindings: bindings
             }
         }
     }
@@ -557,12 +569,12 @@ fn external_path_params(cx: &DocContext, trait_did: Option<ast::DefId>,
 // trait_did should be set to a trait's DefId if called on a TraitRef, in order to sugar
 // from Fn<(A, B,), C> to Fn(A, B) -> C
 fn external_path(cx: &DocContext, name: &str, trait_did: Option<ast::DefId>,
-                 substs: &subst::Substs) -> Path {
+                 bindings: Vec<TypeBinding>, substs: &subst::Substs) -> Path {
     Path {
         global: false,
         segments: vec![PathSegment {
             name: name.to_string(),
-            params: external_path_params(cx, trait_did, substs)
+            params: external_path_params(cx, trait_did, bindings, substs)
         }],
     }
 }
@@ -577,16 +589,16 @@ impl Clean<TyParamBound> for ty::BuiltinBound {
         let (did, path) = match *self {
             ty::BoundSend =>
                 (tcx.lang_items.send_trait().unwrap(),
-                 external_path(cx, "Send", None, &empty)),
+                 external_path(cx, "Send", None, vec![], &empty)),
             ty::BoundSized =>
                 (tcx.lang_items.sized_trait().unwrap(),
-                 external_path(cx, "Sized", None, &empty)),
+                 external_path(cx, "Sized", None, vec![], &empty)),
             ty::BoundCopy =>
                 (tcx.lang_items.copy_trait().unwrap(),
-                 external_path(cx, "Copy", None, &empty)),
+                 external_path(cx, "Copy", None, vec![], &empty)),
             ty::BoundSync =>
                 (tcx.lang_items.sync_trait().unwrap(),
-                 external_path(cx, "Sync", None, &empty)),
+                 external_path(cx, "Sync", None, vec![], &empty)),
         };
         let fqn = csearch::get_item_path(tcx, did);
         let fqn = fqn.into_iter().map(|i| i.to_string()).collect();
@@ -603,12 +615,6 @@ impl Clean<TyParamBound> for ty::BuiltinBound {
     }
 }
 
-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() {
@@ -619,7 +625,7 @@ impl<'tcx> Clean<TyParamBound> for ty::TraitRef<'tcx> {
         let fqn = fqn.into_iter().map(|i| i.to_string())
                      .collect::<Vec<String>>();
         let path = external_path(cx, fqn.last().unwrap().as_slice(),
-                                 Some(self.def_id), self.substs);
+                                 Some(self.def_id), vec![], self.substs);
         cx.external_paths.borrow_mut().as_mut().unwrap().insert(self.def_id,
                                                             (fqn, TypeTrait));
 
@@ -730,8 +736,7 @@ impl Clean<Option<Lifetime>> for ty::Region {
 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 +757,89 @@ impl Clean<WherePredicate> for ast::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 +860,80 @@ impl Clean<Generics> for ast::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
         }
     }
 }
@@ -911,27 +1062,6 @@ impl Clean<Item> for doctree::Function {
 }
 
 #[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Show)]
-pub struct ClosureDecl {
-    pub lifetimes: Vec<Lifetime>,
-    pub decl: FnDecl,
-    pub onceness: ast::Onceness,
-    pub unsafety: ast::Unsafety,
-    pub bounds: Vec<TyParamBound>,
-}
-
-impl Clean<ClosureDecl> for ast::ClosureTy {
-    fn clean(&self, cx: &DocContext) -> ClosureDecl {
-        ClosureDecl {
-            lifetimes: self.lifetimes.clean(cx),
-            decl: self.decl.clean(cx),
-            onceness: self.onceness,
-            unsafety: self.unsafety,
-            bounds: self.bounds.clean(cx)
-        }
-    }
-}
-
-#[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Show)]
 pub struct FnDecl {
     pub inputs: Arguments,
     pub output: FunctionRetTy,
@@ -1207,8 +1337,6 @@ pub enum Type {
     Generic(String),
     /// Primitives are just the fixed-size numeric types (plus int/uint/float), and char.
     Primitive(PrimitiveType),
-    Closure(Box<ClosureDecl>),
-    Proc(Box<ClosureDecl>),
     /// extern "ABI" fn
     BareFunction(Box<BareFunctionDecl>),
     Tuple(Vec<Type>),
@@ -1436,7 +1564,7 @@ impl<'tcx> Clean<Type> for ty::Ty<'tcx> {
                     _ => TypeEnum,
                 };
                 let path = external_path(cx, fqn.last().unwrap().to_string().as_slice(),
-                                         None, substs);
+                                         None, vec![], substs);
                 cx.external_paths.borrow_mut().as_mut().unwrap().insert(did, (fqn, kind));
                 ResolvedPath {
                     path: path,
@@ -1448,12 +1576,13 @@ impl<'tcx> Clean<Type> for ty::Ty<'tcx> {
                 let did = principal.def_id();
                 let fqn = csearch::get_item_path(cx.tcx(), did);
                 let fqn: Vec<_> = fqn.into_iter().map(|i| i.to_string()).collect();
+                let (typarams, bindings) = bounds.clean(cx);
                 let path = external_path(cx, fqn.last().unwrap().to_string().as_slice(),
-                                         Some(did), principal.substs());
+                                         Some(did), bindings, principal.substs());
                 cx.external_paths.borrow_mut().as_mut().unwrap().insert(did, (fqn, TypeTrait));
                 ResolvedPath {
                     path: path,
-                    typarams: Some(bounds.clean(cx)),
+                    typarams: Some(typarams),
                     did: did,
                 }
             }
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index 00f7c570b5d..61d2a9ee071 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -153,8 +153,8 @@ impl<'a> fmt::String for WhereClause<'a> {
                         try!(write!(f, "{}", lifetime));
                     }
                 }
-                &clean::WherePredicate::EqPredicate => {
-                    unimplemented!()
+                &clean::WherePredicate::EqPredicate { ref lhs, ref rhs } => {
+                    try!(write!(f, "{} == {}", lhs, rhs));
                 }
             }
         }
@@ -465,61 +465,6 @@ impl fmt::String for clean::Type {
             }
             clean::Infer => write!(f, "_"),
             clean::Primitive(prim) => primitive_link(f, prim, prim.to_string()),
-            clean::Closure(ref decl) => {
-                write!(f, "{style}{lifetimes}|{args}|{bounds}{arrow}",
-                       style = UnsafetySpace(decl.unsafety),
-                       lifetimes = if decl.lifetimes.len() == 0 {
-                           "".to_string()
-                       } else {
-                           format!("for &lt;{}&gt;",
-                                   CommaSep(decl.lifetimes.as_slice()))
-                       },
-                       args = decl.decl.inputs,
-                       arrow = decl.decl.output,
-                       bounds = {
-                           let mut ret = String::new();
-                           for bound in decl.bounds.iter() {
-                                match *bound {
-                                    clean::RegionBound(..) => {}
-                                    clean::TraitBound(ref t, modifier) => {
-                                        if ret.len() == 0 {
-                                            ret.push_str(": ");
-                                        } else {
-                                            ret.push_str(" + ");
-                                        }
-                                        if modifier == ast::TraitBoundModifier::Maybe {
-                                            ret.push_str("?");
-                                        }
-                                        ret.push_str(format!("{}",
-                                                             *t).as_slice());
-                                    }
-                                }
-                           }
-                           ret
-                       })
-            }
-            clean::Proc(ref decl) => {
-                write!(f, "{style}{lifetimes}proc({args}){bounds}{arrow}",
-                       style = UnsafetySpace(decl.unsafety),
-                       lifetimes = if decl.lifetimes.len() == 0 {
-                           "".to_string()
-                       } else {
-                           format!("for &lt;{}&gt;",
-                                   CommaSep(decl.lifetimes.as_slice()))
-                       },
-                       args = decl.decl.inputs,
-                       bounds = if decl.bounds.len() == 0 {
-                           "".to_string()
-                       } else {
-                           let m = decl.bounds
-                                           .iter()
-                                           .map(|s| s.to_string());
-                           format!(
-                               ": {}",
-                               m.collect::<Vec<String>>().connect(" + "))
-                       },
-                       arrow = decl.decl.output)
-            }
             clean::BareFunction(ref decl) => {
                 write!(f, "{}{}fn{}{}",
                        UnsafetySpace(decl.unsafety),