From b39e99cfc72a21ae6e697e9db2cef252dfb1aecb Mon Sep 17 00:00:00 2001 From: Tom Jakubowski Date: Tue, 16 Dec 2014 06:32:52 -0800 Subject: [PATCH 1/4] rustdoc: render `for<>` on old closure lifetimes --- src/librustdoc/html/format.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index a2d5530692c11..4ec974b0cf777 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -407,7 +407,7 @@ impl fmt::Show for clean::Type { lifetimes = if decl.lifetimes.len() == 0 { "".to_string() } else { - format!("<{:#}>", decl.lifetimes) + format!("for <{:#}>", decl.lifetimes) }, args = decl.decl.inputs, arrow = decl.decl.output, @@ -436,7 +436,7 @@ impl fmt::Show for clean::Type { lifetimes = if decl.lifetimes.len() == 0 { "".to_string() } else { - format!("<{:#}>", decl.lifetimes) + format!("for <{:#}>", decl.lifetimes) }, args = decl.decl.inputs, bounds = if decl.bounds.len() == 0 { From 37225288be1f29ce15ad24855cdbbf06ba5890c8 Mon Sep 17 00:00:00 2001 From: Tom Jakubowski Date: Tue, 16 Dec 2014 08:50:52 -0800 Subject: [PATCH 2/4] rustdoc: render higher-rank trait bounds Fix #19915 --- src/librustdoc/clean/inline.rs | 2 +- src/librustdoc/clean/mod.rs | 68 ++++++++++++++++++++++++++-------- src/librustdoc/html/format.rs | 34 ++++++++++++----- 3 files changed, 78 insertions(+), 26 deletions(-) diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 2bc93ade7774e..cdc51bb801c58 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -328,7 +328,7 @@ fn build_impl(cx: &DocContext, tcx: &ty::ctxt, derived: clean::detect_derived(attrs.as_slice()), trait_: associated_trait.clean(cx).map(|bound| { match bound { - clean::TraitBound(ty) => ty, + clean::TraitBound(polyt) => polyt.trait_, clean::RegionBound(..) => unreachable!(), } }), diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 0dd6c2a7ce730..749686fa28351 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -43,8 +43,7 @@ use rustc::metadata::cstore; use rustc::metadata::csearch; use rustc::metadata::decoder; use rustc::middle::def; -use rustc::middle::subst; -use rustc::middle::subst::VecPerParamSpace; +use rustc::middle::subst::{mod, ParamSpace, VecPerParamSpace}; use rustc::middle::ty; use rustc::middle::stability; use rustc::session::config; @@ -493,7 +492,7 @@ impl<'tcx> Clean for ty::TypeParameterDef<'tcx> { #[deriving(Clone, RustcEncodable, RustcDecodable, PartialEq)] pub enum TyParamBound { RegionBound(Lifetime), - TraitBound(Type) + TraitBound(PolyTrait) } impl Clean for ast::TyParamBound { @@ -558,10 +557,13 @@ impl Clean for ty::BuiltinBound { let fqn = fqn.into_iter().map(|i| i.to_string()).collect(); cx.external_paths.borrow_mut().as_mut().unwrap().insert(did, (fqn, TypeTrait)); - TraitBound(ResolvedPath { - path: path, - typarams: None, - did: did, + TraitBound(PolyTrait { + trait_: ResolvedPath { + path: path, + typarams: None, + did: did, + }, + lifetimes: vec![] }) } } @@ -585,10 +587,31 @@ impl<'tcx> Clean for ty::TraitRef<'tcx> { &self.substs); cx.external_paths.borrow_mut().as_mut().unwrap().insert(self.def_id, (fqn, TypeTrait)); - TraitBound(ResolvedPath { - path: path, - typarams: None, - did: self.def_id, + + debug!("ty::TraitRef\n substs.types(TypeSpace): {}\n", + self.substs.types.get_slice(ParamSpace::TypeSpace)); + + // collect any late bound regions + let mut late_bounds = vec![]; + for &ty_s in self.substs.types.get_slice(ParamSpace::TypeSpace).iter() { + use rustc::middle::ty::{Region, sty}; + if let sty::ty_tup(ref ts) = ty_s.sty { + for &ty_s in ts.iter() { + if let sty::ty_rptr(ref reg, _) = ty_s.sty { + if let &Region::ReLateBound(_, _) = reg { + debug!(" hit an ReLateBound {}", reg); + if let Some(lt) = reg.clean(cx) { + late_bounds.push(lt) + } + } + } + } + } + } + + TraitBound(PolyTrait { + trait_: ResolvedPath { path: path, typarams: None, did: self.def_id, }, + lifetimes: late_bounds }) } } @@ -615,7 +638,7 @@ impl<'tcx> Clean<(Vec, Option)> for ty::ParamBounds<'tcx> { (v, None) } else { let ty = match ty::BoundSized.clean(cx) { - TraitBound(ty) => ty, + TraitBound(polyt) => polyt.trait_, _ => unreachable!() }; (v, Some(ty)) @@ -627,7 +650,10 @@ impl<'tcx> Clean>> for subst::Substs<'tcx> { fn clean(&self, cx: &DocContext) -> Option> { let mut v = Vec::new(); v.extend(self.regions().iter().filter_map(|r| r.clean(cx)).map(RegionBound)); - v.extend(self.types.iter().map(|t| TraitBound(t.clean(cx)))); + v.extend(self.types.iter().map(|t| TraitBound(PolyTrait { + trait_: t.clean(cx), + lifetimes: vec![] + }))); if v.len() > 0 {Some(v)} else {None} } } @@ -1006,9 +1032,12 @@ impl Clean for ast::TraitRef { } } -impl Clean for ast::PolyTraitRef { - fn clean(&self, cx: &DocContext) -> Type { - self.trait_ref.clean(cx) +impl Clean for ast::PolyTraitRef { + fn clean(&self, cx: &DocContext) -> PolyTrait { + PolyTrait { + trait_: self.trait_ref.clean(cx), + lifetimes: self.bound_lifetimes.clean(cx) + } } } @@ -1129,6 +1158,13 @@ impl<'tcx> Clean for ty::ImplOrTraitItem<'tcx> { } } +/// A trait reference, which may have higher ranked lifetimes. +#[deriving(Clone, RustcEncodable, RustcDecodable, PartialEq)] +pub struct PolyTrait { + pub trait_: Type, + pub lifetimes: Vec +} + /// A representation of a Type suitable for hyperlinking purposes. Ideally one can get the original /// type out of the AST/ty::ctxt given one of these, if more information is needed. Most importantly /// it does not preserve mutability or boxes. diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 4ec974b0cf777..83f760f15f63a 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -142,6 +142,22 @@ impl fmt::Show for clean::Lifetime { } } +impl fmt::Show for clean::PolyTrait { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + if self.lifetimes.len() > 0 { + try!(f.write("for<".as_bytes())); + for (i, lt) in self.lifetimes.iter().enumerate() { + if i > 0 { + try!(f.write(", ".as_bytes())); + } + try!(write!(f, "{}", lt)); + } + try!(f.write("> ".as_bytes())); + } + write!(f, "{}", self.trait_) + } +} + impl fmt::Show for clean::TyParamBound { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { @@ -389,15 +405,6 @@ impl fmt::Show for clean::Type { try!(resolved_path(f, did, path, false)); tybounds(f, typarams) } - clean::PolyTraitRef(ref bounds) => { - for (i, bound) in bounds.iter().enumerate() { - if i != 0 { - try!(write!(f, " + ")); - } - try!(write!(f, "{}", *bound)); - } - Ok(()) - } clean::Infer => write!(f, "_"), clean::Self(..) => f.write("Self".as_bytes()), clean::Primitive(prim) => primitive_link(f, prim, prim.to_string()), @@ -505,6 +512,15 @@ impl fmt::Show for clean::Type { } } } + clean::PolyTraitRef(ref bounds) => { + for (i, bound) in bounds.iter().enumerate() { + if i != 0 { + try!(write!(f, " + ")); + } + try!(write!(f, "{}", *bound)); + } + Ok(()) + } clean::QPath { ref name, ref self_type, ref trait_ } => { write!(f, "<{} as {}>::{}", self_type, trait_, name) } From c639cf6ee9ae78d01ca0a34c1a9c05d6c0406fa7 Mon Sep 17 00:00:00 2001 From: Tom Jakubowski Date: Tue, 16 Dec 2014 12:40:43 -0800 Subject: [PATCH 3/4] rustdoc: Use unboxed closure sugar This unfortunately leaves sugaring Fn/FnMut/FnOnce on cross-crate re-exports for future work. cc #19909 --- src/librustdoc/clean/mod.rs | 59 ++++++++++++++------- src/librustdoc/html/format.rs | 97 +++++++++++++++++++++-------------- 2 files changed, 98 insertions(+), 58 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 749686fa28351..8dae353c64f61 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -526,8 +526,10 @@ fn external_path(cx: &DocContext, name: &str, substs: &subst::Substs) -> Path { global: false, segments: vec![PathSegment { name: name.to_string(), - lifetimes: lifetimes, - types: types, + params: PathParameters::AngleBracketed { + lifetimes: lifetimes, + types: types, + } }], } } @@ -1744,31 +1746,48 @@ impl Clean for ast::Path { } #[deriving(Clone, RustcEncodable, RustcDecodable, PartialEq)] -pub struct PathSegment { - pub name: String, - pub lifetimes: Vec, - pub types: Vec, +pub enum PathParameters { + AngleBracketed { + lifetimes: Vec, + types: Vec, + }, + Parenthesized { + inputs: Vec, + output: Option + } } -impl Clean for ast::PathSegment { - fn clean(&self, cx: &DocContext) -> PathSegment { - let (lifetimes, types) = match self.parameters { +impl Clean for ast::PathParameters { + fn clean(&self, cx: &DocContext) -> PathParameters { + match *self { ast::AngleBracketedParameters(ref data) => { - (data.lifetimes.clean(cx), data.types.clean(cx)) + PathParameters::AngleBracketed { + lifetimes: data.lifetimes.clean(cx), + types: data.types.clean(cx) + } } ast::ParenthesizedParameters(ref data) => { - // FIXME -- rustdoc should be taught about Foo() notation - let inputs = Tuple(data.inputs.clean(cx)); - let output = data.output.as_ref().map(|t| t.clean(cx)).unwrap_or(Tuple(Vec::new())); - (Vec::new(), vec![inputs, output]) + PathParameters::Parenthesized { + inputs: data.inputs.clean(cx), + output: data.output.clean(cx) + } } - }; + } + } +} +#[deriving(Clone, RustcEncodable, RustcDecodable, PartialEq)] +pub struct PathSegment { + pub name: String, + pub params: PathParameters +} + +impl Clean for ast::PathSegment { + fn clean(&self, cx: &DocContext) -> PathSegment { PathSegment { name: self.identifier.clean(cx), - lifetimes: lifetimes, - types: types, + params: self.parameters.clean(cx) } } } @@ -2399,8 +2418,10 @@ fn lang_struct(cx: &DocContext, did: Option, global: false, segments: vec![PathSegment { name: name.to_string(), - lifetimes: vec![], - types: vec![t.clean(cx)], + params: PathParameters::AngleBracketed { + lifetimes: vec![], + types: vec![t.clean(cx)], + } }], }, } diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 83f760f15f63a..841588e401261 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -171,37 +171,69 @@ impl fmt::Show for clean::TyParamBound { } } -impl fmt::Show for clean::Path { +impl fmt::Show for clean::PathParameters { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - if self.global { - try!(f.write("::".as_bytes())) - } - - for (i, seg) in self.segments.iter().enumerate() { - if i > 0 { - try!(f.write("::".as_bytes())) - } - try!(f.write(seg.name.as_bytes())); - - if seg.lifetimes.len() > 0 || seg.types.len() > 0 { - try!(f.write("<".as_bytes())); - let mut comma = false; - for lifetime in seg.lifetimes.iter() { - if comma { - try!(f.write(", ".as_bytes())); + match *self { + clean::PathParameters::AngleBracketed { ref lifetimes, ref types } => { + if lifetimes.len() > 0 || types.len() > 0 { + try!(f.write("<".as_bytes())); + let mut comma = false; + for lifetime in lifetimes.iter() { + if comma { + try!(f.write(", ".as_bytes())); + } + comma = true; + try!(write!(f, "{}", *lifetime)); } - comma = true; - try!(write!(f, "{}", *lifetime)); + for ty in types.iter() { + if comma { + try!(f.write(", ".as_bytes())); + } + comma = true; + try!(write!(f, "{}", *ty)); + } + try!(f.write(">".as_bytes())); } - for ty in seg.types.iter() { + } + clean::PathParameters::Parenthesized { ref inputs, ref output } => { + try!(f.write("(".as_bytes())); + let mut comma = false; + for ty in inputs.iter() { if comma { try!(f.write(", ".as_bytes())); } comma = true; try!(write!(f, "{}", *ty)); } - try!(f.write(">".as_bytes())); + try!(f.write(")".as_bytes())); + if let Some(ref ty) = *output { + try!(f.write(" -> ".as_bytes())); + try!(write!(f, "{}", ty)); + } + } + } + Ok(()) + } +} + +impl fmt::Show for clean::PathSegment { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + try!(f.write(self.name.as_bytes())); + write!(f, "{}", self.params) + } +} + +impl fmt::Show for clean::Path { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + if self.global { + try!(f.write("::".as_bytes())) + } + + for (i, seg) in self.segments.iter().enumerate() { + if i > 0 { + try!(f.write("::".as_bytes())) } + try!(write!(f, "{}", seg)); } Ok(()) } @@ -243,23 +275,8 @@ fn path(w: &mut fmt::Formatter, G: FnOnce(&render::Cache) -> Option<(Vec, ItemType)>, { // The generics will get written to both the title and link - let mut generics = String::new(); let last = path.segments.last().unwrap(); - if last.lifetimes.len() > 0 || last.types.len() > 0 { - let mut counter = 0u; - generics.push_str("<"); - for lifetime in last.lifetimes.iter() { - if counter > 0 { generics.push_str(", "); } - counter += 1; - generics.push_str(format!("{}", *lifetime).as_slice()); - } - for ty in last.types.iter() { - if counter > 0 { generics.push_str(", "); } - counter += 1; - generics.push_str(format!("{}", *ty).as_slice()); - } - generics.push_str(">"); - } + let generics = format!("{}", last.params); let loc = CURRENT_LOCATION_KEY.with(|l| l.borrow().clone()); let cache = cache(); @@ -660,8 +677,10 @@ impl fmt::Show for clean::ViewListIdent { global: false, segments: vec!(clean::PathSegment { name: self.name.clone(), - lifetimes: Vec::new(), - types: Vec::new(), + params: clean::PathParameters::AngleBracketed { + lifetimes: Vec::new(), + types: Vec::new(), + } }) }; resolved_path(f, did, &path, false) From 64b5464f303ec2e0561e8d879d6d43691743f392 Mon Sep 17 00:00:00 2001 From: Tom Jakubowski Date: Wed, 17 Dec 2014 02:39:10 -0800 Subject: [PATCH 4/4] rustdoc: Use Fn trait sugar for external paths Fix #19909 --- src/librustdoc/clean/mod.rs | 58 +++++++++++++++++++++++++++++-------- 1 file changed, 46 insertions(+), 12 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 8dae353c64f61..e28e125caa4d4 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -515,21 +515,55 @@ impl Clean> for ty::ExistentialBounds { } } -fn external_path(cx: &DocContext, name: &str, substs: &subst::Substs) -> Path { +fn external_path_params(cx: &DocContext, trait_did: Option, + substs: &subst::Substs) -> PathParameters { + use rustc::middle::ty::sty; let lifetimes = substs.regions().get_slice(subst::TypeSpace) .iter() .filter_map(|v| v.clean(cx)) .collect(); let types = substs.types.get_slice(subst::TypeSpace).to_vec(); - let types = types.clean(cx); + + match (trait_did, cx.tcx_opt()) { + // Attempt to sugar an external path like Fn<(A, B,), C> to Fn(A, B) -> C + (Some(did), Some(ref tcx)) if tcx.lang_items.fn_trait_kind(did).is_some() => { + assert_eq!(types.len(), 2); + let inputs = match types[0].sty { + sty::ty_tup(ref tys) => tys.iter().map(|t| t.clean(cx)).collect(), + _ => { + return PathParameters::AngleBracketed { + lifetimes: lifetimes, + types: types.clean(cx) + } + } + }; + let output = match types[1].sty { + sty::ty_tup(ref v) if v.is_empty() => None, // -> () + _ => Some(types[1].clean(cx)) + }; + PathParameters::Parenthesized { + inputs: inputs, + output: output + } + }, + (_, _) => { + PathParameters::AngleBracketed { + lifetimes: lifetimes, + types: types.clean(cx), + } + } + } +} + +// 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 { Path { global: false, segments: vec![PathSegment { name: name.to_string(), - params: PathParameters::AngleBracketed { - lifetimes: lifetimes, - types: types, - } + params: external_path_params(cx, trait_did, substs) }], } } @@ -544,16 +578,16 @@ impl Clean for ty::BuiltinBound { let (did, path) = match *self { ty::BoundSend => (tcx.lang_items.send_trait().unwrap(), - external_path(cx, "Send", &empty)), + external_path(cx, "Send", None, &empty)), ty::BoundSized => (tcx.lang_items.sized_trait().unwrap(), - external_path(cx, "Sized", &empty)), + external_path(cx, "Sized", None, &empty)), ty::BoundCopy => (tcx.lang_items.copy_trait().unwrap(), - external_path(cx, "Copy", &empty)), + external_path(cx, "Copy", None, &empty)), ty::BoundSync => (tcx.lang_items.sync_trait().unwrap(), - external_path(cx, "Sync", &empty)), + external_path(cx, "Sync", None, &empty)), }; let fqn = csearch::get_item_path(tcx, did); let fqn = fqn.into_iter().map(|i| i.to_string()).collect(); @@ -586,7 +620,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(), - &self.substs); + Some(self.def_id), &self.substs); cx.external_paths.borrow_mut().as_mut().unwrap().insert(self.def_id, (fqn, TypeTrait)); @@ -1437,7 +1471,7 @@ impl<'tcx> Clean for ty::Ty<'tcx> { _ => TypeEnum, }; let path = external_path(cx, fqn.last().unwrap().to_string().as_slice(), - substs); + None, substs); cx.external_paths.borrow_mut().as_mut().unwrap().insert(did, (fqn, kind)); ResolvedPath { path: path,