about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2014-12-25 01:01:42 +0000
committerbors <bors@rust-lang.org>2014-12-25 01:01:42 +0000
commit65248c5e05493d2f90acf2944ea657a03bce380a (patch)
treec8872fd121ad29d6639f67aed27f9740c238e873
parent7e11b22713aebd28ceaaa2ecef937c9b9d247c2f (diff)
parent64b5464f303ec2e0561e8d879d6d43691743f392 (diff)
auto merge of #19934 : tomjakubowski/rust/rustdoc-unboxed-closures-redux, r=alexcrichton
We render HRTB and the unboxed closure trait sugar (the so-called 
"parenthesized" notation) where appropriate. Also address the new
`for` syntax on the old closures.
-rw-r--r--src/librustdoc/clean/inline.rs2
-rw-r--r--src/librustdoc/clean/mod.rs177
-rw-r--r--src/librustdoc/html/format.rs135
3 files changed, 220 insertions, 94 deletions
diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs
index 2bc93ade777..cdc51bb801c 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 0dd6c2a7ce7..e28e125caa4 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<TyParam> for ty::TypeParameterDef<'tcx> {
 #[deriving(Clone, RustcEncodable, RustcDecodable, PartialEq)]
 pub enum TyParamBound {
     RegionBound(Lifetime),
-    TraitBound(Type)
+    TraitBound(PolyTrait)
 }
 
 impl Clean<TyParamBound> for ast::TyParamBound {
@@ -516,19 +515,55 @@ impl Clean<Vec<TyParamBound>> for ty::ExistentialBounds {
     }
 }
 
-fn external_path(cx: &DocContext, name: &str, substs: &subst::Substs) -> Path {
+fn external_path_params(cx: &DocContext, trait_did: Option<ast::DefId>,
+                        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<ast::DefId>,
+                 substs: &subst::Substs) -> Path {
     Path {
         global: false,
         segments: vec![PathSegment {
             name: name.to_string(),
-            lifetimes: lifetimes,
-            types: types,
+            params: external_path_params(cx, trait_did, substs)
         }],
     }
 }
@@ -543,25 +578,28 @@ impl Clean<TyParamBound> 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();
         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![]
         })
     }
 }
@@ -582,13 +620,34 @@ 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(),
-                                 &self.substs);
+                                 Some(self.def_id), &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 +674,7 @@ impl<'tcx> Clean<(Vec<TyParamBound>, Option<Type>)> 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 +686,10 @@ impl<'tcx> Clean<Option<Vec<TyParamBound>>> for subst::Substs<'tcx> {
     fn clean(&self, cx: &DocContext) -> Option<Vec<TyParamBound>> {
         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 +1068,12 @@ impl Clean<Type> for ast::TraitRef {
     }
 }
 
-impl Clean<Type> for ast::PolyTraitRef {
-    fn clean(&self, cx: &DocContext) -> Type {
-        self.trait_ref.clean(cx)
+impl Clean<PolyTrait> for ast::PolyTraitRef {
+    fn clean(&self, cx: &DocContext) -> PolyTrait {
+        PolyTrait {
+            trait_: self.trait_ref.clean(cx),
+            lifetimes: self.bound_lifetimes.clean(cx)
+        }
     }
 }
 
@@ -1129,6 +1194,13 @@ impl<'tcx> Clean<Item> 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<Lifetime>
+}
+
 /// 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.
@@ -1399,7 +1471,7 @@ impl<'tcx> Clean<Type> 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,
@@ -1708,31 +1780,48 @@ impl Clean<Path> for ast::Path {
 }
 
 #[deriving(Clone, RustcEncodable, RustcDecodable, PartialEq)]
-pub struct PathSegment {
-    pub name: String,
-    pub lifetimes: Vec<Lifetime>,
-    pub types: Vec<Type>,
+pub enum PathParameters {
+    AngleBracketed {
+        lifetimes: Vec<Lifetime>,
+        types: Vec<Type>,
+    },
+    Parenthesized {
+        inputs: Vec<Type>,
+        output: Option<Type>
+    }
 }
 
-impl Clean<PathSegment> for ast::PathSegment {
-    fn clean(&self, cx: &DocContext) -> PathSegment {
-        let (lifetimes, types) = match self.parameters {
+impl Clean<PathParameters> 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<PathSegment> for ast::PathSegment {
+    fn clean(&self, cx: &DocContext) -> PathSegment {
         PathSegment {
             name: self.identifier.clean(cx),
-            lifetimes: lifetimes,
-            types: types,
+            params: self.parameters.clean(cx)
         }
     }
 }
@@ -2363,8 +2452,10 @@ fn lang_struct(cx: &DocContext, did: Option<ast::DefId>,
             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 a2d5530692c..841588e4012 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&lt;".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("&gt; ".as_bytes()));
+        }
+        write!(f, "{}", self.trait_)
+    }
+}
+
 impl fmt::Show for clean::TyParamBound {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match *self {
@@ -155,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("&lt;".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("&lt;".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("&gt;".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("&gt;".as_bytes()));
+                try!(f.write(")".as_bytes()));
+                if let Some(ref ty) = *output {
+                    try!(f.write(" -&gt; ".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(())
     }
@@ -227,23 +275,8 @@ fn path<F, G>(w: &mut fmt::Formatter,
     G: FnOnce(&render::Cache) -> Option<(Vec<String>, 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("&lt;");
-        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("&gt;");
-    }
+    let generics = format!("{}", last.params);
 
     let loc = CURRENT_LOCATION_KEY.with(|l| l.borrow().clone());
     let cache = cache();
@@ -389,15 +422,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()),
@@ -407,7 +431,7 @@ impl fmt::Show for clean::Type {
                        lifetimes = if decl.lifetimes.len() == 0 {
                            "".to_string()
                        } else {
-                           format!("&lt;{:#}&gt;", decl.lifetimes)
+                           format!("for &lt;{:#}&gt;", decl.lifetimes)
                        },
                        args = decl.decl.inputs,
                        arrow = decl.decl.output,
@@ -436,7 +460,7 @@ impl fmt::Show for clean::Type {
                        lifetimes = if decl.lifetimes.len() == 0 {
                            "".to_string()
                        } else {
-                           format!("&lt;{:#}&gt;", decl.lifetimes)
+                           format!("for &lt;{:#}&gt;", decl.lifetimes)
                        },
                        args = decl.decl.inputs,
                        bounds = if decl.bounds.len() == 0 {
@@ -505,6 +529,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, "&lt;{} as {}&gt;::{}", self_type, trait_, name)
             }
@@ -644,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)