diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index a44c73e8c4120..b65e1d1d664b4 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -104,6 +104,12 @@ impl, U> Clean> for Option { } } +impl Clean for ty::Binder where T: Clean { + fn clean(&self, cx: &DocContext) -> U { + self.0.clean(cx) + } +} + impl, U> Clean> for syntax::owned_slice::OwnedSlice { fn clean(&self, cx: &DocContext) -> Vec { self.iter().map(|x| x.clean(cx)).collect() @@ -498,22 +504,28 @@ impl Clean for ast::TyParamBound { } } -impl<'tcx> Clean> for ty::ExistentialBounds<'tcx> { - fn clean(&self, cx: &DocContext) -> Vec { - let mut vec = vec![]; - self.region_bound.clean(cx).map(|b| vec.push(RegionBound(b))); +impl<'tcx> Clean<(Vec, Vec)> for ty::ExistentialBounds<'tcx> { + fn clean(&self, cx: &DocContext) -> (Vec, Vec) { + 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, - substs: &subst::Substs) -> PathParameters { + bindings: Vec, 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, 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, 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, // 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, - substs: &subst::Substs) -> Path { + bindings: Vec, 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 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 for ty::BuiltinBound { } } -impl<'tcx> Clean for ty::PolyTraitRef<'tcx> { - fn clean(&self, cx: &DocContext) -> TyParamBound { - self.0.clean(cx) - } -} - impl<'tcx> Clean for ty::TraitRef<'tcx> { fn clean(&self, cx: &DocContext) -> TyParamBound { let tcx = match cx.tcx_opt() { @@ -619,7 +625,7 @@ impl<'tcx> Clean for ty::TraitRef<'tcx> { let fqn = fqn.into_iter().map(|i| i.to_string()) .collect::>(); 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> for ty::Region { pub enum WherePredicate { BoundPredicate { ty: Type, bounds: Vec }, RegionPredicate { lifetime: Lifetime, bounds: Vec}, - // FIXME (#20041) - EqPredicate + EqPredicate { lhs: Type, rhs: Type } } impl Clean for ast::WherePredicate { @@ -752,12 +757,89 @@ impl Clean for ast::WherePredicate { } ast::WherePredicate::EqPredicate(_) => { - WherePredicate::EqPredicate + unimplemented!() // FIXME(#20041) } } } } +impl<'a> Clean 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 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 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 for ty::OutlivesPredicate { + 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 for ty::OutlivesPredicate, 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 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 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 for ast::Generics { impl<'a, 'tcx> Clean 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::>(); + let stripped_lifetimes = gens.regions.get_slice(space).iter().map(|rp| { + let mut srp = rp.clone(); + srp.bounds = Vec::new(); + srp.clean(cx) + }).collect::>(); + + 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::>(); + // 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 } } } @@ -910,27 +1061,6 @@ impl Clean for doctree::Function { } } -#[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Show)] -pub struct ClosureDecl { - pub lifetimes: Vec, - pub decl: FnDecl, - pub onceness: ast::Onceness, - pub unsafety: ast::Unsafety, - pub bounds: Vec, -} - -impl Clean 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, @@ -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), - Proc(Box), /// extern "ABI" fn BareFunction(Box), Tuple(Vec), @@ -1436,7 +1564,7 @@ impl<'tcx> Clean 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 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 00f7c570b5d09..61d2a9ee07107 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 <{}>", - 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 <{}>", - 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::>().connect(" + ")) - }, - arrow = decl.decl.output) - } clean::BareFunction(ref decl) => { write!(f, "{}{}fn{}{}", UnsafetySpace(decl.unsafety),