From 04b27efa00799f984b7ebc50d37a2d571db9235f Mon Sep 17 00:00:00 2001
From: Mark Rousskov <mark.simulacrum@gmail.com>
Date: Thu, 12 Sep 2019 19:59:14 -0400
Subject: [PATCH 01/18] Move to print functions on types instead of impl
 fmt::Display

This will eventually allow us to easily pass in more parameters to the
functions without TLS or other such hacks
---
 src/librustdoc/clean/mod.rs                   |  14 +-
 src/librustdoc/html/format.rs                 | 881 ++++++++++--------
 src/librustdoc/html/item_type.rs              |  39 +-
 src/librustdoc/html/render.rs                 | 127 +--
 .../passes/calculate_doc_coverage.rs          |   7 +-
 .../passes/collect_intra_doc_links.rs         |   2 +-
 src/librustdoc/passes/mod.rs                  |   4 +-
 src/librustdoc/passes/strip_hidden.rs         |   2 +-
 8 files changed, 553 insertions(+), 523 deletions(-)

diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index ae70fdc530be6..10c4231b82eeb 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -1849,7 +1849,7 @@ fn get_real_types(
     cx: &DocContext<'_>,
     recurse: i32,
 ) -> FxHashSet<Type> {
-    let arg_s = arg.to_string();
+    let arg_s = arg.print().to_string();
     let mut res = FxHashSet::default();
     if recurse >= 10 { // FIXME: remove this whole recurse thing when the recursion bug is fixed
         return res;
@@ -3573,16 +3573,6 @@ pub enum GenericArg {
     Const(Constant),
 }
 
-impl fmt::Display for GenericArg {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self {
-            GenericArg::Lifetime(lt) => lt.fmt(f),
-            GenericArg::Type(ty) => ty.fmt(f),
-            GenericArg::Const(ct) => ct.fmt(f),
-        }
-    }
-}
-
 #[derive(Clone, PartialEq, Eq, Debug, Hash)]
 pub enum GenericArgs {
     AngleBracketed {
@@ -4274,7 +4264,7 @@ fn resolve_type(cx: &DocContext<'_>,
             return Generic(kw::SelfUpper.to_string());
         }
         Res::Def(DefKind::TyParam, _) if path.segments.len() == 1 => {
-            return Generic(format!("{:#}", path));
+            return Generic(format!("{:#}", path.print()));
         }
         Res::SelfTy(..)
         | Res::Def(DefKind::TyParam, _)
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index dcd32192ff384..9baa69d981b52 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -99,10 +99,6 @@ impl Buffer {
         self.into_inner()
     }
 
-    crate fn with_formatter<T: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result>(&mut self, t: T) {
-        self.from_display(display_fn(move |f| (t)(f)));
-    }
-
     crate fn from_display<T: std::fmt::Display>(&mut self, t: T) {
         if self.for_html {
             write!(self, "{}", t);
@@ -131,8 +127,6 @@ pub struct AsyncSpace(pub hir::IsAsync);
 /// Similar to VisSpace, but used for mutability
 #[derive(Copy, Clone)]
 pub struct MutableSpace(pub clean::Mutability);
-/// Wrapper struct for emitting type parameter bounds.
-pub struct GenericBounds<'a>(pub &'a [clean::GenericBound]);
 pub struct AbiSpace(pub Abi);
 pub struct DefaultSpace(pub bool);
 
@@ -161,102 +155,89 @@ pub struct WhereClause<'a>{
     pub end_newline: bool,
 }
 
-impl<'a> VisSpace<'a> {
-    pub fn get(self) -> &'a Option<clean::Visibility> {
-        let VisSpace(v) = self; v
-    }
-}
-
-impl UnsafetySpace {
-    pub fn get(&self) -> hir::Unsafety {
-        let UnsafetySpace(v) = *self; v
-    }
-}
-
-impl ConstnessSpace {
-    pub fn get(&self) -> hir::Constness {
-        let ConstnessSpace(v) = *self; v
-    }
-}
-
-fn comma_sep<T: fmt::Display>(items: &[T]) -> impl fmt::Display + '_ {
+fn comma_sep<T: fmt::Display>(items: impl Iterator<Item=T>) -> impl fmt::Display {
     display_fn(move |f| {
-        for (i, item) in items.iter().enumerate() {
+        for (i, item) in items.enumerate() {
             if i != 0 { write!(f, ", ")?; }
-            fmt::Display::fmt(item, f)?;
+            fmt::Display::fmt(&item, f)?;
         }
         Ok(())
     })
 }
 
-impl<'a> fmt::Display for GenericBounds<'a> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+crate fn print_generic_bounds(bounds: &[clean::GenericBound]) -> impl fmt::Display + '_ {
+    display_fn(move |f| {
         let mut bounds_dup = FxHashSet::default();
-        let &GenericBounds(bounds) = self;
 
-        for (i, bound) in bounds.iter().filter(|b| bounds_dup.insert(b.to_string())).enumerate() {
+        for (i, bound) in bounds.iter().filter(|b| {
+            bounds_dup.insert(b.print().to_string())
+        }).enumerate() {
             if i > 0 {
                 f.write_str(" + ")?;
             }
-            fmt::Display::fmt(bound, f)?;
+            fmt::Display::fmt(&bound.print(), f)?;
         }
         Ok(())
-    }
+    })
 }
 
-impl fmt::Display for clean::GenericParamDef {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self.kind {
-            clean::GenericParamDefKind::Lifetime => write!(f, "{}", self.name),
-            clean::GenericParamDefKind::Type { ref bounds, ref default, .. } => {
-                f.write_str(&self.name)?;
+impl clean::GenericParamDef {
+    crate fn print(&self) -> impl fmt::Display + '_ {
+        display_fn(move |f| {
+            match self.kind {
+                clean::GenericParamDefKind::Lifetime => write!(f, "{}", self.name),
+                clean::GenericParamDefKind::Type { ref bounds, ref default, .. } => {
+                    f.write_str(&self.name)?;
 
-                if !bounds.is_empty() {
-                    if f.alternate() {
-                        write!(f, ": {:#}", GenericBounds(bounds))?;
-                    } else {
-                        write!(f, ":&nbsp;{}", GenericBounds(bounds))?;
+                    if !bounds.is_empty() {
+                        if f.alternate() {
+                            write!(f, ": {:#}", print_generic_bounds(bounds))?;
+                        } else {
+                            write!(f, ":&nbsp;{}", print_generic_bounds(bounds))?;
+                        }
+                    }
+
+                    if let Some(ref ty) = default {
+                        if f.alternate() {
+                            write!(f, " = {:#}", ty.print())?;
+                        } else {
+                            write!(f, "&nbsp;=&nbsp;{}", ty.print())?;
+                        }
                     }
+
+                    Ok(())
                 }
+                clean::GenericParamDefKind::Const { ref ty, .. } => {
+                    f.write_str("const ")?;
+                    f.write_str(&self.name)?;
 
-                if let Some(ref ty) = default {
                     if f.alternate() {
-                        write!(f, " = {:#}", ty)?;
+                        write!(f, ": {:#}", ty.print())
                     } else {
-                        write!(f, "&nbsp;=&nbsp;{}", ty)?;
+                        write!(f, ":&nbsp;{}", ty.print())
                     }
                 }
-
-                Ok(())
-            }
-            clean::GenericParamDefKind::Const { ref ty, .. } => {
-                f.write_str("const ")?;
-                f.write_str(&self.name)?;
-
-                if f.alternate() {
-                    write!(f, ": {:#}", ty)
-                } else {
-                    write!(f, ":&nbsp;{}", ty)
-                }
             }
-        }
+        })
     }
 }
 
-impl fmt::Display for clean::Generics {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let real_params = self.params
-            .iter()
-            .filter(|p| !p.is_synthetic_type_param())
-            .collect::<Vec<_>>();
-        if real_params.is_empty() {
-            return Ok(());
-        }
-        if f.alternate() {
-            write!(f, "<{:#}>", comma_sep(&real_params))
-        } else {
-            write!(f, "&lt;{}&gt;", comma_sep(&real_params))
-        }
+impl clean::Generics {
+    crate fn print(&self) -> impl fmt::Display + '_ {
+        display_fn(move |f| {
+            let real_params = self.params
+                .iter()
+                .filter(|p| !p.is_synthetic_type_param())
+                .collect::<Vec<_>>();
+            if real_params.is_empty() {
+                return Ok(());
+            }
+            if f.alternate() {
+                write!(f, "<{:#}>", comma_sep(real_params.iter().map(|g| g.print())))
+            } else {
+                write!(f, "&lt;{}&gt;", comma_sep(real_params.iter().map(|g| g.print())))
+            }
+        })
     }
 }
 
@@ -287,24 +268,26 @@ impl<'a> fmt::Display for WhereClause<'a> {
                 &clean::WherePredicate::BoundPredicate { ref ty, ref bounds } => {
                     let bounds = bounds;
                     if f.alternate() {
-                        clause.push_str(&format!("{:#}: {:#}", ty, GenericBounds(bounds)));
+                        clause.push_str(&format!("{:#}: {:#}",
+                                ty.print(), print_generic_bounds(bounds)));
                     } else {
-                        clause.push_str(&format!("{}: {}", ty, GenericBounds(bounds)));
+                        clause.push_str(&format!("{}: {}",
+                                ty.print(), print_generic_bounds(bounds)));
                     }
                 }
                 &clean::WherePredicate::RegionPredicate { ref lifetime, ref bounds } => {
                     clause.push_str(&format!("{}: {}",
-                                                lifetime,
+                                                lifetime.print(),
                                                 bounds.iter()
-                                                    .map(|b| b.to_string())
+                                                    .map(|b| b.print().to_string())
                                                     .collect::<Vec<_>>()
                                                     .join(" + ")));
                 }
                 &clean::WherePredicate::EqPredicate { ref lhs, ref rhs } => {
                     if f.alternate() {
-                        clause.push_str(&format!("{:#} == {:#}", lhs, rhs));
+                        clause.push_str(&format!("{:#} == {:#}", lhs.print(), rhs.print()));
                     } else {
-                        clause.push_str(&format!("{} == {}", lhs, rhs));
+                        clause.push_str(&format!("{} == {}", lhs.print(), rhs.print()));
                     }
                 }
             }
@@ -336,153 +319,164 @@ impl<'a> fmt::Display for WhereClause<'a> {
     }
 }
 
-impl fmt::Display for clean::Lifetime {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.write_str(self.get_ref())?;
-        Ok(())
+impl clean::Lifetime {
+    crate fn print(&self) -> &str {
+        self.get_ref()
     }
 }
 
-impl fmt::Display for clean::Constant {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        fmt::Display::fmt(&self.expr, f)
+impl clean::Constant {
+    crate fn print(&self) -> &str {
+        &self.expr
     }
 }
 
-impl fmt::Display for clean::PolyTrait {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        if !self.generic_params.is_empty() {
+impl clean::PolyTrait {
+    fn print(&self) -> impl fmt::Display + '_ {
+        display_fn(move |f| {
+            if !self.generic_params.is_empty() {
+                if f.alternate() {
+                    write!(f, "for<{:#}> ",
+                        comma_sep(self.generic_params.iter().map(|g| g.print())))?;
+                } else {
+                    write!(f, "for&lt;{}&gt; ",
+                        comma_sep(self.generic_params.iter().map(|g| g.print())))?;
+                }
+            }
             if f.alternate() {
-                write!(f, "for<{:#}> ", comma_sep(&self.generic_params))?;
+                write!(f, "{:#}", self.trait_.print())
             } else {
-                write!(f, "for&lt;{}&gt; ", comma_sep(&self.generic_params))?;
+                write!(f, "{}", self.trait_.print())
             }
-        }
-        if f.alternate() {
-            write!(f, "{:#}", self.trait_)
-        } else {
-            write!(f, "{}", self.trait_)
-        }
+        })
     }
 }
 
-impl fmt::Display for clean::GenericBound {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match *self {
-            clean::GenericBound::Outlives(ref lt) => {
-                write!(f, "{}", *lt)
-            }
-            clean::GenericBound::TraitBound(ref ty, modifier) => {
-                let modifier_str = match modifier {
-                    hir::TraitBoundModifier::None => "",
-                    hir::TraitBoundModifier::Maybe => "?",
-                };
-                if f.alternate() {
-                    write!(f, "{}{:#}", modifier_str, *ty)
-                } else {
-                    write!(f, "{}{}", modifier_str, *ty)
+impl clean::GenericBound {
+    crate fn print(&self) -> impl fmt::Display + '_ {
+        display_fn(move |f| {
+            match self {
+                clean::GenericBound::Outlives(lt) => {
+                    write!(f, "{}", lt.print())
+                }
+                clean::GenericBound::TraitBound(ty, modifier) => {
+                    let modifier_str = match modifier {
+                        hir::TraitBoundModifier::None => "",
+                        hir::TraitBoundModifier::Maybe => "?",
+                    };
+                    if f.alternate() {
+                        write!(f, "{}{:#}", modifier_str, ty.print())
+                    } else {
+                        write!(f, "{}{}", modifier_str, ty.print())
+                    }
                 }
             }
-        }
+        })
     }
 }
 
-impl fmt::Display for clean::GenericArgs {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match *self {
-            clean::GenericArgs::AngleBracketed { ref args, ref bindings } => {
-                if !args.is_empty() || !bindings.is_empty() {
-                    if f.alternate() {
-                        f.write_str("<")?;
-                    } else {
-                        f.write_str("&lt;")?;
-                    }
-                    let mut comma = false;
-                    for arg in args {
-                        if comma {
-                            f.write_str(", ")?;
+impl clean::GenericArgs {
+    fn print(&self) -> impl fmt::Display + '_ {
+        display_fn(move |f| {
+            match *self {
+                clean::GenericArgs::AngleBracketed { ref args, ref bindings } => {
+                    if !args.is_empty() || !bindings.is_empty() {
+                        if f.alternate() {
+                            f.write_str("<")?;
+                        } else {
+                            f.write_str("&lt;")?;
+                        }
+                        let mut comma = false;
+                        for arg in args {
+                            if comma {
+                                f.write_str(", ")?;
+                            }
+                            comma = true;
+                            if f.alternate() {
+                                write!(f, "{:#}", arg.print())?;
+                            } else {
+                                write!(f, "{}", arg.print())?;
+                            }
+                        }
+                        for binding in bindings {
+                            if comma {
+                                f.write_str(", ")?;
+                            }
+                            comma = true;
+                            if f.alternate() {
+                                write!(f, "{:#}", binding.print())?;
+                            } else {
+                                write!(f, "{}", binding.print())?;
+                            }
                         }
-                        comma = true;
                         if f.alternate() {
-                            write!(f, "{:#}", *arg)?;
+                            f.write_str(">")?;
                         } else {
-                            write!(f, "{}", *arg)?;
+                            f.write_str("&gt;")?;
                         }
                     }
-                    for binding in bindings {
+                }
+                clean::GenericArgs::Parenthesized { ref inputs, ref output } => {
+                    f.write_str("(")?;
+                    let mut comma = false;
+                    for ty in inputs {
                         if comma {
                             f.write_str(", ")?;
                         }
                         comma = true;
                         if f.alternate() {
-                            write!(f, "{:#}", *binding)?;
+                            write!(f, "{:#}", ty.print())?;
                         } else {
-                            write!(f, "{}", *binding)?;
+                            write!(f, "{}", ty.print())?;
                         }
                     }
-                    if f.alternate() {
-                        f.write_str(">")?;
-                    } else {
-                        f.write_str("&gt;")?;
-                    }
-                }
-            }
-            clean::GenericArgs::Parenthesized { ref inputs, ref output } => {
-                f.write_str("(")?;
-                let mut comma = false;
-                for ty in inputs {
-                    if comma {
-                        f.write_str(", ")?;
-                    }
-                    comma = true;
-                    if f.alternate() {
-                        write!(f, "{:#}", *ty)?;
-                    } else {
-                        write!(f, "{}", *ty)?;
-                    }
-                }
-                f.write_str(")")?;
-                if let Some(ref ty) = *output {
-                    if f.alternate() {
-                        write!(f, " -> {:#}", ty)?;
-                    } else {
-                        write!(f, " -&gt; {}", ty)?;
+                    f.write_str(")")?;
+                    if let Some(ref ty) = *output {
+                        if f.alternate() {
+                            write!(f, " -> {:#}", ty.print())?;
+                        } else {
+                            write!(f, " -&gt; {}", ty.print())?;
+                        }
                     }
                 }
             }
-        }
-        Ok(())
+            Ok(())
+        })
     }
 }
 
-impl fmt::Display for clean::PathSegment {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.write_str(&self.name)?;
-        if f.alternate() {
-            write!(f, "{:#}", self.args)
-        } else {
-            write!(f, "{}", self.args)
-        }
+impl clean::PathSegment {
+    crate fn print(&self) -> impl fmt::Display + '_ {
+        display_fn(move |f| {
+            f.write_str(&self.name)?;
+            if f.alternate() {
+                write!(f, "{:#}", self.args.print())
+            } else {
+                write!(f, "{}", self.args.print())
+            }
+        })
     }
 }
 
-impl fmt::Display for clean::Path {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        if self.global {
-            f.write_str("::")?
-        }
-
-        for (i, seg) in self.segments.iter().enumerate() {
-            if i > 0 {
+impl clean::Path {
+    crate fn print(&self) -> impl fmt::Display + '_ {
+        display_fn(move |f| {
+            if self.global {
                 f.write_str("::")?
             }
-            if f.alternate() {
-                write!(f, "{:#}", seg)?;
-            } else {
-                write!(f, "{}", seg)?;
+
+            for (i, seg) in self.segments.iter().enumerate() {
+                if i > 0 {
+                    f.write_str("::")?
+                }
+                if f.alternate() {
+                    write!(f, "{:#}", seg.print())?;
+                } else {
+                    write!(f, "{}", seg.print())?;
+                }
             }
-        }
-        Ok(())
+            Ok(())
+        })
     }
 }
 
@@ -516,7 +510,7 @@ pub fn href(did: DefId) -> Option<(String, ItemType, Vec<String>)> {
             url.push_str("/index.html");
         }
         _ => {
-            url.push_str(shortty.css_class());
+            url.push_str(shortty.as_str());
             url.push_str(".");
             url.push_str(fqp.last().unwrap());
             url.push_str(".html");
@@ -537,7 +531,7 @@ fn resolved_path(w: &mut fmt::Formatter<'_>, did: DefId, path: &clean::Path,
         }
     }
     if w.alternate() {
-        write!(w, "{}{:#}", &last.name, last.args)?;
+        write!(w, "{}{:#}", &last.name, last.args.print())?;
     } else {
         let path = if use_absolute {
             if let Some((_, _, fqp)) = href(did) {
@@ -550,7 +544,7 @@ fn resolved_path(w: &mut fmt::Formatter<'_>, did: DefId, path: &clean::Path,
         } else {
             anchor(did, &last.name).to_string()
         };
-        write!(w, "{}{}", path, last.args)?;
+        write!(w, "{}{}", path, last.args.print())?;
     }
     Ok(())
 }
@@ -606,7 +600,7 @@ fn tybounds(param_names: &Option<Vec<clean::GenericBound>>) -> impl fmt::Display
             Some(ref params) => {
                 for param in params {
                     write!(f, " + ")?;
-                    fmt::Display::fmt(param, f)?;
+                    fmt::Display::fmt(&param.print(), f)?;
                 }
                 Ok(())
             }
@@ -646,12 +640,12 @@ fn fmt_type(t: &clean::Type, f: &mut fmt::Formatter<'_>, use_absolute: bool) ->
                 write!(f, "{}{:#}fn{:#}{:#}",
                        UnsafetySpace(decl.unsafety),
                        AbiSpace(decl.abi),
-                       comma_sep(&decl.generic_params),
-                       decl.decl)
+                       decl.print_generic_params(),
+                       decl.decl.print())
             } else {
                 write!(f, "{}{}", UnsafetySpace(decl.unsafety), AbiSpace(decl.abi))?;
                 primitive_link(f, PrimitiveType::Fn, "fn")?;
-                write!(f, "{}{}", comma_sep(&decl.generic_params), decl.decl)
+                write!(f, "{}{}", decl.print_generic_params(), decl.decl.print())
             }
         }
         clean::Tuple(ref typs) => {
@@ -660,24 +654,27 @@ fn fmt_type(t: &clean::Type, f: &mut fmt::Formatter<'_>, use_absolute: bool) ->
                 &[ref one] => {
                     primitive_link(f, PrimitiveType::Tuple, "(")?;
                     // Carry `f.alternate()` into this display w/o branching manually.
-                    fmt::Display::fmt(one, f)?;
+                    fmt::Display::fmt(&one.print(), f)?;
                     primitive_link(f, PrimitiveType::Tuple, ",)")
                 }
                 many => {
                     primitive_link(f, PrimitiveType::Tuple, "(")?;
-                    fmt::Display::fmt(&comma_sep(many), f)?;
+                    for (i, item) in many.iter().enumerate() {
+                        if i != 0 { write!(f, ", ")?; }
+                        fmt::Display::fmt(&item.print(), f)?;
+                    }
                     primitive_link(f, PrimitiveType::Tuple, ")")
                 }
             }
         }
         clean::Slice(ref t) => {
             primitive_link(f, PrimitiveType::Slice, "[")?;
-            fmt::Display::fmt(t, f)?;
+            fmt::Display::fmt(&t.print(), f)?;
             primitive_link(f, PrimitiveType::Slice, "]")
         }
         clean::Array(ref t, ref n) => {
             primitive_link(f, PrimitiveType::Array, "[")?;
-            fmt::Display::fmt(t, f)?;
+            fmt::Display::fmt(&t.print(), f)?;
             primitive_link(f, PrimitiveType::Array, &format!("; {}]", n))
         }
         clean::Never => primitive_link(f, PrimitiveType::Never, "!"),
@@ -691,22 +688,22 @@ fn fmt_type(t: &clean::Type, f: &mut fmt::Formatter<'_>, use_absolute: bool) ->
                 clean::Generic(_) | clean::ResolvedPath {is_generic: true, ..} => {
                     if f.alternate() {
                         primitive_link(f, clean::PrimitiveType::RawPointer,
-                                       &format!("*{} {:#}", m, t))
+                                       &format!("*{} {:#}", m, t.print()))
                     } else {
                         primitive_link(f, clean::PrimitiveType::RawPointer,
-                                       &format!("*{} {}", m, t))
+                                       &format!("*{} {}", m, t.print()))
                     }
                 }
                 _ => {
                     primitive_link(f, clean::PrimitiveType::RawPointer, &format!("*{} ", m))?;
-                    fmt::Display::fmt(t, f)
+                    fmt::Display::fmt(&t.print(), f)
                 }
             }
         }
         clean::BorrowedRef{ lifetime: ref l, mutability, type_: ref ty} => {
-            let lt = match *l {
-                Some(ref l) => format!("{} ", *l),
-                _ => String::new(),
+            let lt = match l {
+                Some(l) => format!("{} ", l.print()),
+                _ => String::new()
             };
             let m = MutableSpace(mutability);
             let amp = if f.alternate() {
@@ -720,19 +717,19 @@ fn fmt_type(t: &clean::Type, f: &mut fmt::Formatter<'_>, use_absolute: bool) ->
                         clean::Generic(_) => {
                             if f.alternate() {
                                 primitive_link(f, PrimitiveType::Slice,
-                                    &format!("{}{}{}[{:#}]", amp, lt, m, **bt))
+                                    &format!("{}{}{}[{:#}]", amp, lt, m, bt.print()))
                             } else {
                                 primitive_link(f, PrimitiveType::Slice,
-                                    &format!("{}{}{}[{}]", amp, lt, m, **bt))
+                                    &format!("{}{}{}[{}]", amp, lt, m, bt.print()))
                             }
                         }
                         _ => {
                             primitive_link(f, PrimitiveType::Slice,
                                            &format!("{}{}{}[", amp, lt, m))?;
                             if f.alternate() {
-                                write!(f, "{:#}", **bt)?;
+                                write!(f, "{:#}", bt.print())?;
                             } else {
-                                write!(f, "{}", **bt)?;
+                                write!(f, "{}", bt.print())?;
                             }
                             primitive_link(f, PrimitiveType::Slice, "]")
                         }
@@ -756,9 +753,9 @@ fn fmt_type(t: &clean::Type, f: &mut fmt::Formatter<'_>, use_absolute: bool) ->
         }
         clean::ImplTrait(ref bounds) => {
             if f.alternate() {
-                write!(f, "impl {:#}", GenericBounds(bounds))
+                write!(f, "impl {:#}", print_generic_bounds(bounds))
             } else {
-                write!(f, "impl {}", GenericBounds(bounds))
+                write!(f, "impl {}", print_generic_bounds(bounds))
             }
         }
         clean::QPath { ref name, ref self_type, ref trait_ } => {
@@ -770,15 +767,15 @@ fn fmt_type(t: &clean::Type, f: &mut fmt::Formatter<'_>, use_absolute: bool) ->
             };
             if f.alternate() {
                 if should_show_cast {
-                    write!(f, "<{:#} as {:#}>::", self_type, trait_)?
+                    write!(f, "<{:#} as {:#}>::", self_type.print(), trait_.print())?
                 } else {
-                    write!(f, "{:#}::", self_type)?
+                    write!(f, "{:#}::", self_type.print())?
                 }
             } else {
                 if should_show_cast {
-                    write!(f, "&lt;{} as {}&gt;::", self_type, trait_)?
+                    write!(f, "&lt;{} as {}&gt;::", self_type.print(), trait_.print())?
                 } else {
-                    write!(f, "{}::", self_type)?
+                    write!(f, "{}::", self_type.print())?
                 }
             };
             match *trait_ {
@@ -818,55 +815,64 @@ fn fmt_type(t: &clean::Type, f: &mut fmt::Formatter<'_>, use_absolute: bool) ->
     }
 }
 
-impl fmt::Display for clean::Type {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        fmt_type(self, f, false)
+impl clean::Type {
+    crate fn print(&self) -> impl fmt::Display + '_ {
+        display_fn(move |f| {
+            fmt_type(self, f, false)
+        })
     }
 }
 
-fn fmt_impl(i: &clean::Impl,
-            f: &mut fmt::Formatter<'_>,
-            link_trait: bool,
-            use_absolute: bool) -> fmt::Result {
-    if f.alternate() {
-        write!(f, "impl{:#} ", i.generics)?;
-    } else {
-        write!(f, "impl{} ", i.generics)?;
+impl clean::Impl {
+    crate fn print(&self) -> impl fmt::Display + '_ {
+        self.print_inner(true, false)
     }
 
-    if let Some(ref ty) = i.trait_ {
-        if i.polarity == Some(clean::ImplPolarity::Negative) {
-            write!(f, "!")?;
-        }
+    fn print_inner(
+        &self,
+        link_trait: bool,
+        use_absolute: bool,
+    ) -> impl fmt::Display + '_ {
+        display_fn(move |f| {
+            if f.alternate() {
+                write!(f, "impl{:#} ", self.generics.print())?;
+            } else {
+                write!(f, "impl{} ", self.generics.print())?;
+            }
 
-        if link_trait {
-            fmt::Display::fmt(ty, f)?;
-        } else {
-            match *ty {
-                clean::ResolvedPath { param_names: None, ref path, is_generic: false, .. } => {
-                    let last = path.segments.last().unwrap();
-                    fmt::Display::fmt(&last.name, f)?;
-                    fmt::Display::fmt(&last.args, f)?;
+            if let Some(ref ty) = self.trait_ {
+                if self.polarity == Some(clean::ImplPolarity::Negative) {
+                    write!(f, "!")?;
                 }
-                _ => unreachable!(),
-            }
-        }
-        write!(f, " for ")?;
-    }
 
-    if let Some(ref ty) = i.blanket_impl {
-        fmt_type(ty, f, use_absolute)?;
-    } else {
-        fmt_type(&i.for_, f, use_absolute)?;
-    }
+                if link_trait {
+                    fmt::Display::fmt(&ty.print(), f)?;
+                } else {
+                    match ty {
+                        clean::ResolvedPath { param_names: None, path, is_generic: false, .. } => {
+                            let last = path.segments.last().unwrap();
+                            fmt::Display::fmt(&last.name, f)?;
+                            fmt::Display::fmt(&last.args.print(), f)?;
+                        }
+                        _ => unreachable!(),
+                    }
+                }
+                write!(f, " for ")?;
+            }
 
-    fmt::Display::fmt(&WhereClause { gens: &i.generics, indent: 0, end_newline: true }, f)?;
-    Ok(())
-}
+            if let Some(ref ty) = self.blanket_impl {
+                fmt_type(ty, f, use_absolute)?;
+            } else {
+                fmt_type(&self.for_, f, use_absolute)?;
+            }
 
-impl fmt::Display for clean::Impl {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        fmt_impl(self, f, true, false)
+            fmt::Display::fmt(&WhereClause {
+                gens: &self.generics,
+                indent: 0,
+                end_newline: true,
+            }, f)?;
+            Ok(())
+        })
     }
 }
 
@@ -874,162 +880,193 @@ impl fmt::Display for clean::Impl {
 pub fn fmt_impl_for_trait_page(i: &clean::Impl,
                                f: &mut Buffer,
                                use_absolute: bool) {
-    f.with_formatter(|f| fmt_impl(i, f, false, use_absolute))
+    f.from_display(i.print_inner(false, use_absolute))
 }
 
-impl fmt::Display for clean::Arguments {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        for (i, input) in self.values.iter().enumerate() {
-            if !input.name.is_empty() {
-                write!(f, "{}: ", input.name)?;
-            }
-            if f.alternate() {
-                write!(f, "{:#}", input.type_)?;
-            } else {
-                write!(f, "{}", input.type_)?;
+impl clean::Arguments {
+    crate fn print(&self) -> impl fmt::Display + '_ {
+        display_fn(move |f| {
+            for (i, input) in self.values.iter().enumerate() {
+                if !input.name.is_empty() {
+                    write!(f, "{}: ", input.name)?;
+                }
+                if f.alternate() {
+                    write!(f, "{:#}", input.type_.print())?;
+                } else {
+                    write!(f, "{}", input.type_.print())?;
+                }
+                if i + 1 < self.values.len() { write!(f, ", ")?; }
             }
-            if i + 1 < self.values.len() { write!(f, ", ")?; }
-        }
-        Ok(())
+            Ok(())
+        })
     }
 }
 
-impl fmt::Display for clean::FunctionRetTy {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match *self {
-            clean::Return(clean::Tuple(ref tys)) if tys.is_empty() => Ok(()),
-            clean::Return(ref ty) if f.alternate() => write!(f, " -> {:#}", ty),
-            clean::Return(ref ty) => write!(f, " -&gt; {}", ty),
-            clean::DefaultReturn => Ok(()),
-        }
+impl clean::FunctionRetTy {
+    crate fn print(&self) -> impl fmt::Display + '_ {
+        display_fn(move |f| {
+            match self {
+                clean::Return(clean::Tuple(tys)) if tys.is_empty() => Ok(()),
+                clean::Return(ty) if f.alternate() => write!(f, " -> {:#}", ty.print()),
+                clean::Return(ty) => write!(f, " -&gt; {}", ty.print()),
+                clean::DefaultReturn => Ok(()),
+            }
+        })
     }
 }
 
-impl fmt::Display for clean::FnDecl {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        if f.alternate() {
-            write!(f, "({args:#}){arrow:#}", args = self.inputs, arrow = self.output)
-        } else {
-            write!(f, "({args}){arrow}", args = self.inputs, arrow = self.output)
-        }
+impl clean::BareFunctionDecl {
+    fn print_generic_params(&self) -> impl fmt::Display + '_ {
+        comma_sep(self.generic_params.iter().map(|g| g.print()))
     }
 }
 
-impl<'a> fmt::Display for Function<'a> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let &Function { decl, header_len, indent, asyncness } = self;
-        let amp = if f.alternate() { "&" } else { "&amp;" };
-        let mut args = String::new();
-        let mut args_plain = String::new();
-        for (i, input) in decl.inputs.values.iter().enumerate() {
-            if i == 0 {
-                args.push_str("<br>");
+impl clean::FnDecl {
+    crate fn print(&self) -> impl fmt::Display + '_ {
+        display_fn(move |f| {
+            if f.alternate() {
+                write!(f,
+                    "({args:#}){arrow:#}", args = self.inputs.print(), arrow = self.output.print())
+            } else {
+                write!(f,
+                    "({args}){arrow}", args = self.inputs.print(), arrow = self.output.print())
             }
+        })
+    }
+}
+
+
+impl Function<'_> {
+    crate fn print(&self) -> impl fmt::Display + '_ {
+        display_fn(move |f| {
+            let &Function { decl, header_len, indent, asyncness } = self;
+            let amp = if f.alternate() { "&" } else { "&amp;" };
+            let mut args = String::new();
+            let mut args_plain = String::new();
+            for (i, input) in decl.inputs.values.iter().enumerate() {
+                if i == 0 {
+                    args.push_str("<br>");
+                }
 
-            if let Some(selfty) = input.to_self() {
-                match selfty {
-                    clean::SelfValue => {
-                        args.push_str("self");
-                        args_plain.push_str("self");
+                if let Some(selfty) = input.to_self() {
+                    match selfty {
+                        clean::SelfValue => {
+                            args.push_str("self");
+                            args_plain.push_str("self");
+                        }
+                        clean::SelfBorrowed(Some(ref lt), mtbl) => {
+                            args.push_str(
+                                &format!("{}{} {}self", amp, lt.print(), MutableSpace(mtbl)));
+                            args_plain.push_str(
+                                &format!("&{} {}self", lt.print(), MutableSpace(mtbl)));
+                        }
+                        clean::SelfBorrowed(None, mtbl) => {
+                            args.push_str(&format!("{}{}self", amp, MutableSpace(mtbl)));
+                            args_plain.push_str(&format!("&{}self", MutableSpace(mtbl)));
+                        }
+                        clean::SelfExplicit(ref typ) => {
+                            if f.alternate() {
+                                args.push_str(&format!("self: {:#}", typ.print()));
+                            } else {
+                                args.push_str(&format!("self: {}", typ.print()));
+                            }
+                            args_plain.push_str(&format!("self: {:#}", typ.print()));
+                        }
                     }
-                    clean::SelfBorrowed(Some(ref lt), mtbl) => {
-                        args.push_str(&format!("{}{} {}self", amp, *lt, MutableSpace(mtbl)));
-                        args_plain.push_str(&format!("&{} {}self", *lt, MutableSpace(mtbl)));
+                } else {
+                    if i > 0 {
+                        args.push_str(" <br>");
+                        args_plain.push_str(" ");
                     }
-                    clean::SelfBorrowed(None, mtbl) => {
-                        args.push_str(&format!("{}{}self", amp, MutableSpace(mtbl)));
-                        args_plain.push_str(&format!("&{}self", MutableSpace(mtbl)));
+                    if !input.name.is_empty() {
+                        args.push_str(&format!("{}: ", input.name));
+                        args_plain.push_str(&format!("{}: ", input.name));
                     }
-                    clean::SelfExplicit(ref typ) => {
-                        if f.alternate() {
-                            args.push_str(&format!("self: {:#}", *typ));
-                        } else {
-                            args.push_str(&format!("self: {}", *typ));
-                        }
-                        args_plain.push_str(&format!("self: {:#}", *typ));
+
+                    if f.alternate() {
+                        args.push_str(&format!("{:#}", input.type_.print()));
+                    } else {
+                        args.push_str(&input.type_.print().to_string());
                     }
+                    args_plain.push_str(&format!("{:#}", input.type_.print()));
                 }
-            } else {
-                if i > 0 {
-                    args.push_str(" <br>");
-                    args_plain.push_str(" ");
-                }
-                if !input.name.is_empty() {
-                    args.push_str(&format!("{}: ", input.name));
-                    args_plain.push_str(&format!("{}: ", input.name));
+                if i + 1 < decl.inputs.values.len() {
+                    args.push(',');
+                    args_plain.push(',');
                 }
-
-                if f.alternate() {
-                    args.push_str(&format!("{:#}", input.type_));
-                } else {
-                    args.push_str(&input.type_.to_string());
-                }
-                args_plain.push_str(&format!("{:#}", input.type_));
             }
-            if i + 1 < decl.inputs.values.len() {
-                args.push(',');
-                args_plain.push(',');
-            }
-        }
 
-        let args_plain = format!("({})", args_plain);
+            let args_plain = format!("({})", args_plain);
 
-        let output = if let hir::IsAsync::Async = asyncness {
-            Cow::Owned(decl.sugared_async_return_type())
-        } else {
-            Cow::Borrowed(&decl.output)
-        };
+            let output = if let hir::IsAsync::Async = asyncness {
+                Cow::Owned(decl.sugared_async_return_type())
+            } else {
+                Cow::Borrowed(&decl.output)
+            };
 
-        let arrow_plain = format!("{:#}", &output);
-        let arrow = if f.alternate() {
-            format!("{:#}", &output)
-        } else {
-            output.to_string()
-        };
+            let arrow_plain = format!("{:#}", &output.print());
+            let arrow = if f.alternate() {
+                format!("{:#}", &output.print())
+            } else {
+                output.print().to_string()
+            };
 
-        let declaration_len = header_len + args_plain.len() + arrow_plain.len();
-        let output = if declaration_len > 80 {
-            let full_pad = format!("<br>{}", "&nbsp;".repeat(indent + 4));
-            let close_pad = format!("<br>{}", "&nbsp;".repeat(indent));
-            format!("({args}{close}){arrow}",
-                    args = args.replace("<br>", &full_pad),
-                    close = close_pad,
-                    arrow = arrow)
-        } else {
-            format!("({args}){arrow}", args = args.replace("<br>", ""), arrow = arrow)
-        };
+            let declaration_len = header_len + args_plain.len() + arrow_plain.len();
+            let output = if declaration_len > 80 {
+                let full_pad = format!("<br>{}", "&nbsp;".repeat(indent + 4));
+                let close_pad = format!("<br>{}", "&nbsp;".repeat(indent));
+                format!("({args}{close}){arrow}",
+                        args = args.replace("<br>", &full_pad),
+                        close = close_pad,
+                        arrow = arrow)
+            } else {
+                format!("({args}){arrow}", args = args.replace("<br>", ""), arrow = arrow)
+            };
 
-        if f.alternate() {
-            write!(f, "{}", output.replace("<br>", "\n"))
-        } else {
-            write!(f, "{}", output)
-        }
+            if f.alternate() {
+                write!(f, "{}", output.replace("<br>", "\n"))
+            } else {
+                write!(f, "{}", output)
+            }
+        })
     }
 }
 
 impl<'a> fmt::Display for VisSpace<'a> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match *self.get() {
-            Some(clean::Public) => f.write_str("pub "),
-            Some(clean::Inherited) | None => Ok(()),
-            Some(clean::Visibility::Crate) => write!(f, "pub(crate) "),
-            Some(clean::Visibility::Restricted(did, ref path)) => {
-                f.write_str("pub(")?;
-                if path.segments.len() != 1
-                    || (path.segments[0].name != "self" && path.segments[0].name != "super")
-                {
-                    f.write_str("in ")?;
+        if let Some(v) = self.0 {
+            fmt::Display::fmt(&v.print_with_space(), f)
+        } else {
+            Ok(())
+        }
+    }
+}
+
+impl clean::Visibility {
+    fn print_with_space(&self) -> impl fmt::Display + '_ {
+        display_fn(move |f| {
+            match *self {
+                clean::Public => f.write_str("pub "),
+                clean::Inherited => Ok(()),
+                clean::Visibility::Crate => write!(f, "pub(crate) "),
+                clean::Visibility::Restricted(did, ref path) => {
+                    f.write_str("pub(")?;
+                    if path.segments.len() != 1
+                        || (path.segments[0].name != "self" && path.segments[0].name != "super")
+                    {
+                        f.write_str("in ")?;
+                    }
+                    resolved_path(f, did, path, true, false)?;
+                    f.write_str(") ")
                 }
-                resolved_path(f, did, path, true, false)?;
-                f.write_str(") ")
             }
-        }
+        })
     }
 }
 
 impl fmt::Display for UnsafetySpace {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self.get() {
+        match self.0 {
             hir::Unsafety::Unsafe => write!(f, "unsafe "),
             hir::Unsafety::Normal => Ok(())
         }
@@ -1038,7 +1075,7 @@ impl fmt::Display for UnsafetySpace {
 
 impl fmt::Display for ConstnessSpace {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self.get() {
+        match self.0 {
             hir::Constness::Const => write!(f, "const "),
             hir::Constness::NotConst => Ok(())
         }
@@ -1054,66 +1091,72 @@ impl fmt::Display for AsyncSpace {
     }
 }
 
-impl fmt::Display for clean::Import {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match *self {
-            clean::Import::Simple(ref name, ref src) => {
-                if *name == src.path.last_name() {
-                    write!(f, "use {};", *src)
-                } else {
-                    write!(f, "use {} as {};", *src, *name)
+impl clean::Import {
+    crate fn print(&self) -> impl fmt::Display + '_ {
+        display_fn(move |f| {
+            match *self {
+                clean::Import::Simple(ref name, ref src) => {
+                    if *name == src.path.last_name() {
+                        write!(f, "use {};", src.print())
+                    } else {
+                        write!(f, "use {} as {};", src.print(), *name)
+                    }
                 }
-            }
-            clean::Import::Glob(ref src) => {
-                if src.path.segments.is_empty() {
-                    write!(f, "use *;")
-                } else {
-                    write!(f, "use {}::*;", *src)
+                clean::Import::Glob(ref src) => {
+                    if src.path.segments.is_empty() {
+                        write!(f, "use *;")
+                    } else {
+                        write!(f, "use {}::*;", src.print())
+                    }
                 }
             }
-        }
+        })
     }
 }
 
-impl fmt::Display for clean::ImportSource {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self.did {
-            Some(did) => resolved_path(f, did, &self.path, true, false),
-            _ => {
-                for (i, seg) in self.path.segments.iter().enumerate() {
-                    if i > 0 {
-                        write!(f, "::")?
+impl clean::ImportSource {
+    crate fn print(&self) -> impl fmt::Display + '_ {
+        display_fn(move |f| {
+            match self.did {
+                Some(did) => resolved_path(f, did, &self.path, true, false),
+                _ => {
+                    for (i, seg) in self.path.segments.iter().enumerate() {
+                        if i > 0 {
+                            write!(f, "::")?
+                        }
+                        write!(f, "{}", seg.name)?;
                     }
-                    write!(f, "{}", seg.name)?;
+                    Ok(())
                 }
-                Ok(())
             }
-        }
+        })
     }
 }
 
-impl fmt::Display for clean::TypeBinding {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.write_str(&self.name)?;
-        match self.kind {
-            clean::TypeBindingKind::Equality { ref ty } => {
-                if f.alternate() {
-                    write!(f, " = {:#}", ty)?;
-                } else {
-                    write!(f, " = {}", ty)?;
-                }
-            }
-            clean::TypeBindingKind::Constraint { ref bounds } => {
-                if !bounds.is_empty() {
+impl clean::TypeBinding {
+    crate fn print(&self) -> impl fmt::Display + '_ {
+        display_fn(move |f| {
+            f.write_str(&self.name)?;
+            match self.kind {
+                clean::TypeBindingKind::Equality { ref ty } => {
                     if f.alternate() {
-                        write!(f, ": {:#}", GenericBounds(bounds))?;
+                        write!(f, " = {:#}", ty.print())?;
                     } else {
-                        write!(f, ":&nbsp;{}", GenericBounds(bounds))?;
+                        write!(f, " = {}", ty.print())?;
+                    }
+                }
+                clean::TypeBindingKind::Constraint { ref bounds } => {
+                    if !bounds.is_empty() {
+                        if f.alternate() {
+                            write!(f, ": {:#}", print_generic_bounds(bounds))?;
+                        } else {
+                            write!(f, ":&nbsp;{}", print_generic_bounds(bounds))?;
+                        }
                     }
                 }
             }
-        }
-        Ok(())
+            Ok(())
+        })
     }
 }
 
@@ -1146,6 +1189,18 @@ impl fmt::Display for DefaultSpace {
     }
 }
 
+impl clean::GenericArg {
+    crate fn print(&self) -> impl fmt::Display + '_ {
+        display_fn(move |f| {
+            match self {
+                clean::GenericArg::Lifetime(lt) => fmt::Display::fmt(&lt.print(), f),
+                clean::GenericArg::Type(ty) => fmt::Display::fmt(&ty.print(), f),
+                clean::GenericArg::Const(ct) => fmt::Display::fmt(&ct.print(), f),
+            }
+        })
+    }
+}
+
 crate fn display_fn(
     f: impl FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result,
 ) -> impl fmt::Display {
diff --git a/src/librustdoc/html/item_type.rs b/src/librustdoc/html/item_type.rs
index cf51a4eb5a5be..5fb9afd6c49a0 100644
--- a/src/librustdoc/html/item_type.rs
+++ b/src/librustdoc/html/item_type.rs
@@ -46,14 +46,6 @@ pub enum ItemType {
 }
 
 
-#[derive(Copy, Eq, PartialEq, Clone)]
-pub enum NameSpace {
-    Type,
-    Value,
-    Macro,
-    Keyword,
-}
-
 impl<'a> From<&'a clean::Item> for ItemType {
     fn from(item: &'a clean::Item) -> ItemType {
         let inner = match item.inner {
@@ -120,7 +112,7 @@ impl From<clean::TypeKind> for ItemType {
 }
 
 impl ItemType {
-    pub fn css_class(&self) -> &'static str {
+    pub fn as_str(&self) -> &'static str {
         match *self {
             ItemType::Module          => "mod",
             ItemType::ExternCrate     => "externcrate",
@@ -151,7 +143,7 @@ impl ItemType {
         }
     }
 
-    pub fn name_space(&self) -> NameSpace {
+    pub fn name_space(&self) -> &'static str {
         match *self {
             ItemType::Struct |
             ItemType::Union |
@@ -163,7 +155,7 @@ impl ItemType {
             ItemType::AssocType |
             ItemType::OpaqueTy |
             ItemType::TraitAlias |
-            ItemType::ForeignType => NameSpace::Type,
+            ItemType::ForeignType => NAMESPACE_TYPE,
 
             ItemType::ExternCrate |
             ItemType::Import |
@@ -175,20 +167,20 @@ impl ItemType {
             ItemType::StructField |
             ItemType::Variant |
             ItemType::Constant |
-            ItemType::AssocConst => NameSpace::Value,
+            ItemType::AssocConst => NAMESPACE_VALUE,
 
             ItemType::Macro |
             ItemType::ProcAttribute |
-            ItemType::ProcDerive => NameSpace::Macro,
+            ItemType::ProcDerive => NAMESPACE_MACRO,
 
-            ItemType::Keyword => NameSpace::Keyword,
+            ItemType::Keyword => NAMESPACE_KEYWORD,
         }
     }
 }
 
 impl fmt::Display for ItemType {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        self.css_class().fmt(f)
+        write!(f, "{}", self.as_str())
     }
 }
 
@@ -196,20 +188,3 @@ pub const NAMESPACE_TYPE: &'static str = "t";
 pub const NAMESPACE_VALUE: &'static str = "v";
 pub const NAMESPACE_MACRO: &'static str = "m";
 pub const NAMESPACE_KEYWORD: &'static str = "k";
-
-impl NameSpace {
-    pub fn to_static_str(&self) -> &'static str {
-        match *self {
-            NameSpace::Type => NAMESPACE_TYPE,
-            NameSpace::Value => NAMESPACE_VALUE,
-            NameSpace::Macro => NAMESPACE_MACRO,
-            NameSpace::Keyword => NAMESPACE_KEYWORD,
-        }
-    }
-}
-
-impl fmt::Display for NameSpace {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        self.to_static_str().fmt(f)
-    }
-}
diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs
index 9846073cad4bc..7aaf04f34d32f 100644
--- a/src/librustdoc/html/render.rs
+++ b/src/librustdoc/html/render.rs
@@ -66,7 +66,7 @@ use crate::doctree;
 use crate::fold::DocFolder;
 use crate::html::escape::Escape;
 use crate::html::format::{Buffer, AsyncSpace, ConstnessSpace};
-use crate::html::format::{GenericBounds, WhereClause, href, AbiSpace, DefaultSpace};
+use crate::html::format::{print_generic_bounds, WhereClause, href, AbiSpace, DefaultSpace};
 use crate::html::format::{VisSpace, Function, UnsafetySpace, MutableSpace};
 use crate::html::format::fmt_impl_for_trait_page;
 use crate::html::item_type::ItemType;
@@ -1203,7 +1203,7 @@ themePicker.onblur = handleThemeButtonsBlur;
             if !imp.impl_item.def_id.is_local() { continue }
             have_impls = true;
             write!(implementors, "{{text:{},synthetic:{},types:{}}},",
-                   as_json(&imp.inner_impl().to_string()),
+                   as_json(&imp.inner_impl().print().to_string()),
                    imp.inner_impl().synthetic,
                    as_json(&collect_paths_for_type(imp.inner_impl().for_.clone()))).unwrap();
         }
@@ -1222,7 +1222,7 @@ themePicker.onblur = handleThemeButtonsBlur;
         }
         cx.shared.ensure_dir(&mydst)?;
         mydst.push(&format!("{}.{}.js",
-                            remote_item_type.css_class(),
+                            remote_item_type,
                             remote_path[remote_path.len() - 1]));
 
         let (mut all_implementors, _, _) = try_err!(collect(&mydst, &krate.name, "implementors",
@@ -1665,9 +1665,11 @@ impl ItemEntry {
     }
 }
 
-impl fmt::Display for ItemEntry {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "<a href='{}'>{}</a>", self.url, Escape(&self.name))
+impl ItemEntry {
+    crate fn print(&self) -> impl fmt::Display + '_ {
+        crate::html::format::display_fn(move |f| {
+            write!(f, "<a href='{}'>{}</a>", self.url, Escape(&self.name))
+        })
     }
 }
 
@@ -1759,7 +1761,7 @@ fn print_entries(f: &mut Buffer, e: &FxHashSet<ItemEntry>, title: &str, class: &
                title,
                Escape(title),
                class,
-               e.iter().map(|s| format!("<li>{}</li>", s)).collect::<String>());
+               e.iter().map(|s| format!("<li>{}</li>", s.print())).collect::<String>());
     }
 }
 
@@ -1939,7 +1941,7 @@ impl Context {
             title.push_str(it.name.as_ref().unwrap());
         }
         title.push_str(" - Rust");
-        let tyname = it.type_().css_class();
+        let tyname = it.type_();
         let desc = if it.is_crate() {
             format!("API documentation for the Rust `{}` crate.",
                     self.shared.layout.krate)
@@ -1949,7 +1951,7 @@ impl Context {
         };
         let keywords = make_item_keywords(it);
         let page = layout::Page {
-            css_class: tyname,
+            css_class: tyname.as_str(),
             root_path: &self.root_path(),
             static_root_path: self.shared.static_root_path.as_deref(),
             title: &title,
@@ -2090,7 +2092,7 @@ impl Context {
         for item in &m.items {
             if item.is_stripped() { continue }
 
-            let short = item.type_().css_class();
+            let short = item.type_();
             let myname = match item.name {
                 None => continue,
                 Some(ref s) => s.to_string(),
@@ -2285,7 +2287,7 @@ fn print_item(cx: &Context, item: &clean::Item, buf: &mut Buffer) {
 fn item_path(ty: ItemType, name: &str) -> String {
     match ty {
         ItemType::Module => format!("{}index.html", SlashChecker(name)),
-        _ => format!("{}.{}.html", ty.css_class(), name),
+        _ => format!("{}.{}.html", ty, name),
     }
 }
 
@@ -2586,7 +2588,7 @@ fn item_module(w: &mut Buffer, cx: &Context, item: &clean::Item, items: &[clean:
 
             clean::ImportItem(ref import) => {
                 write!(w, "<tr><td><code>{}{}</code></td></tr>",
-                       VisSpace(&myitem.visibility), *import);
+                       VisSpace(&myitem.visibility), import.print());
             }
 
             _ => {
@@ -2794,7 +2796,7 @@ fn item_constant(w: &mut Buffer, cx: &Context, it: &clean::Item, c: &clean::Cons
                {name}: {typ}</pre>",
            vis = VisSpace(&it.visibility),
            name = it.name.as_ref().unwrap(),
-           typ = c.type_);
+           typ = c.type_.print());
     document(w, cx, it)
 }
 
@@ -2806,7 +2808,7 @@ fn item_static(w: &mut Buffer, cx: &Context, it: &clean::Item, s: &clean::Static
            vis = VisSpace(&it.visibility),
            mutability = MutableSpace(s.mutability),
            name = it.name.as_ref().unwrap(),
-           typ = s.type_);
+           typ = s.type_.print());
     document(w, cx, it)
 }
 
@@ -2819,7 +2821,7 @@ fn item_function(w: &mut Buffer, cx: &Context, it: &clean::Item, f: &clean::Func
         AsyncSpace(f.header.asyncness),
         AbiSpace(f.header.abi),
         it.name.as_ref().unwrap(),
-        f.generics
+        f.generics.print()
     ).len();
     write!(w, "{}<pre class='rust fn'>", render_spotlight_traits(it));
     render_attributes(w, it, false);
@@ -2832,14 +2834,14 @@ fn item_function(w: &mut Buffer, cx: &Context, it: &clean::Item, f: &clean::Func
            asyncness = AsyncSpace(f.header.asyncness),
            abi = AbiSpace(f.header.abi),
            name = it.name.as_ref().unwrap(),
-           generics = f.generics,
+           generics = f.generics.print(),
            where_clause = WhereClause { gens: &f.generics, indent: 0, end_newline: true },
            decl = Function {
               decl: &f.decl,
               header_len,
               indent: 0,
               asyncness: f.header.asyncness,
-           });
+           }.print());
     document(w, cx, it)
 }
 
@@ -2880,15 +2882,15 @@ fn bounds(t_bounds: &[clean::GenericBound], trait_alias: bool) -> String {
             if i > 0 {
                 bounds.push_str(" + ");
             }
-            bounds.push_str(&(*p).to_string());
+            bounds.push_str(&p.print().to_string());
         }
     }
     bounds
 }
 
 fn compare_impl<'a, 'b>(lhs: &'a &&Impl, rhs: &'b &&Impl) -> Ordering {
-    let lhs = format!("{}", lhs.inner_impl());
-    let rhs = format!("{}", rhs.inner_impl());
+    let lhs = format!("{}", lhs.inner_impl().print());
+    let rhs = format!("{}", rhs.inner_impl().print());
 
     // lhs and rhs are formatted as HTML, which may be unnecessary
     name_key(&lhs).cmp(&name_key(&rhs))
@@ -2915,7 +2917,7 @@ fn item_trait(
                UnsafetySpace(t.unsafety),
                if t.is_auto { "auto " } else { "" },
                it.name.as_ref().unwrap(),
-               t.generics,
+               t.generics.print(),
                bounds);
 
         if !t.generics.where_predicates.is_empty() {
@@ -3142,7 +3144,7 @@ fn item_trait(
                let (ref path, _) = cache.external_paths[&it.def_id];
                path[..path.len() - 1].join("/")
            },
-           ty = it.type_().css_class(),
+           ty = it.type_(),
            name = *it.name.as_ref().unwrap());
 }
 
@@ -3176,7 +3178,7 @@ fn assoc_const(w: &mut Buffer,
            VisSpace(&it.visibility),
            naive_assoc_href(it, link),
            it.name.as_ref().unwrap(),
-           ty);
+           ty.print());
 }
 
 fn assoc_type(w: &mut Buffer, it: &clean::Item,
@@ -3189,10 +3191,10 @@ fn assoc_type(w: &mut Buffer, it: &clean::Item,
            naive_assoc_href(it, link),
            it.name.as_ref().unwrap());
     if !bounds.is_empty() {
-        write!(w, ": {}", GenericBounds(bounds))
+        write!(w, ": {}", print_generic_bounds(bounds))
     }
     if let Some(default) = default {
-        write!(w, " = {}", default)
+        write!(w, " = {}", default.print())
     }
 }
 
@@ -3245,7 +3247,7 @@ fn render_assoc_item(w: &mut Buffer,
             DefaultSpace(meth.is_default()),
             AbiSpace(header.abi),
             name,
-            *g
+            g.print()
         ).len();
         let (indent, end_newline) = if parent == ItemType::Trait {
             header_len += 4;
@@ -3265,13 +3267,13 @@ fn render_assoc_item(w: &mut Buffer,
                AbiSpace(header.abi),
                href = href,
                name = name,
-               generics = *g,
+               generics = g.print(),
                decl = Function {
                    decl: d,
                    header_len,
                    indent,
                    asyncness: header.asyncness,
-               },
+               }.print(),
                where_clause = WhereClause {
                    gens: g,
                    indent,
@@ -3340,7 +3342,7 @@ fn item_struct(w: &mut Buffer, cx: &Context, it: &clean::Item, s: &clean::Struct
                        id = id,
                        ns_id = ns_id,
                        name = field.name.as_ref().unwrap(),
-                       ty = ty);
+                       ty = ty.print());
                 document(w, cx, field);
             }
         }
@@ -3381,7 +3383,7 @@ fn item_union(w: &mut Buffer, cx: &Context, it: &clean::Item, s: &clean::Union)
                    id = id,
                    name = name,
                    shortty = ItemType::StructField,
-                   ty = ty);
+                   ty = ty.print());
             if let Some(stability_class) = field.stability_class() {
                 write!(w, "<span class='stab {stab}'></span>",
                     stab = stability_class);
@@ -3399,7 +3401,7 @@ fn item_enum(w: &mut Buffer, cx: &Context, it: &clean::Item, e: &clean::Enum) {
         write!(w, "{}enum {}{}{}",
                VisSpace(&it.visibility),
                it.name.as_ref().unwrap(),
-               e.generics,
+               e.generics.print(),
                WhereClause { gens: &e.generics, indent: 0, end_newline: true });
         if e.variants.is_empty() && !e.variants_stripped {
             write!(w, " {{}}");
@@ -3418,7 +3420,7 @@ fn item_enum(w: &mut Buffer, cx: &Context, it: &clean::Item, e: &clean::Enum) {
                                     if i > 0 {
                                         write!(w, ",&nbsp;")
                                     }
-                                    write!(w, "{}", *ty);
+                                    write!(w, "{}", ty.print());
                                 }
                                 write!(w, ")");
                             }
@@ -3472,7 +3474,7 @@ fn item_enum(w: &mut Buffer, cx: &Context, it: &clean::Item, e: &clean::Enum) {
                         if i > 0 {
                             write!(w, ",&nbsp;");
                         }
-                        write!(w, "{}", *ty);
+                        write!(w, "{}", ty.print());
                     }
                     write!(w, ")");
                 }
@@ -3510,7 +3512,7 @@ fn item_enum(w: &mut Buffer, cx: &Context, it: &clean::Item, e: &clean::Enum) {
                                id = id,
                                ns_id = ns_id,
                                f = field.name.as_ref().unwrap(),
-                               t = *ty);
+                               t = ty.print());
                         document(w, cx, field);
                     }
                 }
@@ -3590,7 +3592,7 @@ fn render_struct(w: &mut Buffer, it: &clean::Item,
            if structhead {"struct "} else {""},
            it.name.as_ref().unwrap());
     if let Some(g) = g {
-        write!(w, "{}", g)
+        write!(w, "{}", g.print())
     }
     match ty {
         doctree::Plain => {
@@ -3605,7 +3607,7 @@ fn render_struct(w: &mut Buffer, it: &clean::Item,
                            tab,
                            VisSpace(&field.visibility),
                            field.name.as_ref().unwrap(),
-                           *ty);
+                           ty.print());
                     has_visible_fields = true;
                 }
             }
@@ -3633,7 +3635,7 @@ fn render_struct(w: &mut Buffer, it: &clean::Item,
                         write!(w, "_")
                     }
                     clean::StructFieldItem(ref ty) => {
-                        write!(w, "{}{}", VisSpace(&field.visibility), *ty)
+                        write!(w, "{}{}", VisSpace(&field.visibility), ty.print())
                     }
                     _ => unreachable!()
                 }
@@ -3664,7 +3666,7 @@ fn render_union(w: &mut Buffer, it: &clean::Item,
            if structhead {"union "} else {""},
            it.name.as_ref().unwrap());
     if let Some(g) = g {
-        write!(w, "{}", g);
+        write!(w, "{}", g.print());
         write!(w, "{}", WhereClause { gens: g, indent: 0, end_newline: true });
     }
 
@@ -3674,7 +3676,7 @@ fn render_union(w: &mut Buffer, it: &clean::Item,
             write!(w, "    {}{}: {},\n{}",
                    VisSpace(&field.visibility),
                    field.name.as_ref().unwrap(),
-                   *ty,
+                   ty.print(),
                    tab);
         }
     }
@@ -3740,7 +3742,7 @@ fn render_assoc_items(w: &mut Buffer,
                       Methods from {}&lt;Target = {}&gt;\
                       <a href='#deref-methods' class='anchor'></a>\
                     </h2>\
-                ", trait_, type_);
+                ", trait_.print(), type_.print());
                 RenderMode::ForDeref { mut_: deref_mut_ }
             }
         };
@@ -3885,12 +3887,13 @@ fn spotlight_decl(decl: &clean::FnDecl) -> String {
                         out.push_str(
                             &format!("<h3 class=\"important\">Important traits for {}</h3>\
                                       <code class=\"content\">",
-                                     impl_.for_));
-                        trait_.push_str(&impl_.for_.to_string());
+                                     impl_.for_.print()));
+                        trait_.push_str(&impl_.for_.print().to_string());
                     }
 
                     //use the "where" class here to make it small
-                    out.push_str(&format!("<span class=\"where fmt-newline\">{}</span>", impl_));
+                    out.push_str(
+                        &format!("<span class=\"where fmt-newline\">{}</span>", impl_.print()));
                     let t_did = impl_.trait_.def_id().unwrap();
                     for it in &impl_.items {
                         if let clean::TypedefItem(ref tydef, _) = it.inner {
@@ -3927,7 +3930,7 @@ fn render_impl(w: &mut Buffer, cx: &Context, i: &Impl, link: AssocItemLink<'_>,
             Some(ref t) => if is_on_foreign_type {
                 get_id_for_impl_on_foreign_type(&i.inner_impl().for_, t)
             } else {
-                format!("impl-{}", small_url_encode(&format!("{:#}", t)))
+                format!("impl-{}", small_url_encode(&format!("{:#}", t.print())))
             },
             None => "impl".to_string(),
         });
@@ -3948,7 +3951,7 @@ fn render_impl(w: &mut Buffer, cx: &Context, i: &Impl, link: AssocItemLink<'_>,
             write!(w, "</code>");
         } else {
             write!(w, "<h3 id='{}' class='impl'><code class='in-band'>{}</code>",
-                id, i.inner_impl()
+                id, i.inner_impl().print()
             );
         }
         write!(w, "<a href='#{}' class='anchor'></a>", id);
@@ -3993,8 +3996,10 @@ fn render_impl(w: &mut Buffer, cx: &Context, i: &Impl, link: AssocItemLink<'_>,
                 // Only render when the method is not static or we allow static methods
                 if render_method_item {
                     let id = cx.derive_id(format!("{}.{}", item_type, name));
-                    let ns_id = cx.derive_id(format!("{}.{}", name, item_type.name_space()));
-                    write!(w, "<h4 id='{}' class=\"{}{}\">", id, item_type, extra_class);
+                    let ns_id = cx.derive_id(format!("{}.{}",
+                            name, item_type.name_space()));
+                    write!(w, "<h4 id='{}' class=\"{}{}\">",
+                        id, item_type, extra_class);
                     write!(w, "{}", spotlight_decl(decl));
                     write!(w, "<code id='{}'>", ns_id);
                     render_assoc_item(w, item, link.anchor(&id), ItemType::Impl);
@@ -4125,7 +4130,7 @@ fn item_opaque_ty(
     render_attributes(w, it, false);
     write!(w, "type {}{}{where_clause} = impl {bounds};</pre>",
            it.name.as_ref().unwrap(),
-           t.generics,
+           t.generics.print(),
            where_clause = WhereClause { gens: &t.generics, indent: 0, end_newline: true },
            bounds = bounds(&t.bounds, false));
 
@@ -4144,7 +4149,7 @@ fn item_trait_alias(w: &mut Buffer, cx: &Context, it: &clean::Item,
     render_attributes(w, it, false);
     write!(w, "trait {}{}{} = {};</pre>",
            it.name.as_ref().unwrap(),
-           t.generics,
+           t.generics.print(),
            WhereClause { gens: &t.generics, indent: 0, end_newline: true },
            bounds(&t.bounds, true));
 
@@ -4162,9 +4167,9 @@ fn item_typedef(w: &mut Buffer, cx: &Context, it: &clean::Item, t: &clean::Typed
     render_attributes(w, it, false);
     write!(w, "type {}{}{where_clause} = {type_};</pre>",
            it.name.as_ref().unwrap(),
-           t.generics,
+           t.generics.print(),
            where_clause = WhereClause { gens: &t.generics, indent: 0, end_newline: true },
-           type_ = t.type_);
+           type_ = t.type_.print());
 
     document(w, cx, it);
 
@@ -4269,7 +4274,7 @@ fn print_sidebar(cx: &Context, it: &clean::Item, buffer: &mut Buffer) {
                 relpath: '{path}'\
             }};</script>",
             name = it.name.as_ref().map(|x| &x[..]).unwrap_or(""),
-            ty = it.type_().css_class(),
+            ty = it.type_(),
             path = relpath);
     if parentlen == 0 {
         // There is no sidebar-items.js beyond the crate root path
@@ -4370,9 +4375,10 @@ fn sidebar_assoc_items(it: &clean::Item) -> String {
                     if let Some(impls) = inner_impl {
                         out.push_str("<a class=\"sidebar-title\" href=\"#deref-methods\">");
                         out.push_str(&format!("Methods from {}&lt;Target={}&gt;",
-                                              Escape(&format!("{:#}",
-                                                     impl_.inner_impl().trait_.as_ref().unwrap())),
-                                              Escape(&format!("{:#}", target))));
+                            Escape(&format!(
+                                "{:#}", impl_.inner_impl().trait_.as_ref().unwrap().print()
+                            )),
+                            Escape(&format!("{:#}", target.print()))));
                         out.push_str("</a>");
                         let mut ret = impls.iter()
                                            .filter(|i| i.inner_impl().trait_.is_none())
@@ -4397,9 +4403,9 @@ fn sidebar_assoc_items(it: &clean::Item) -> String {
                     .filter_map(|i| {
                         let is_negative_impl = is_negative_impl(i.inner_impl());
                         if let Some(ref i) = i.inner_impl().trait_ {
-                            let i_display = format!("{:#}", i);
+                            let i_display = format!("{:#}", i.print());
                             let out = Escape(&i_display);
-                            let encoded = small_url_encode(&format!("{:#}", i));
+                            let encoded = small_url_encode(&format!("{:#}", i.print()));
                             let generated = format!("<a href=\"#impl-{}\">{}{}</a>",
                                                     encoded,
                                                     if is_negative_impl { "!" } else { "" },
@@ -4471,14 +4477,17 @@ fn sidebar_struct(buf: &mut Buffer, it: &clean::Item, s: &clean::Struct) {
 }
 
 fn get_id_for_impl_on_foreign_type(for_: &clean::Type, trait_: &clean::Type) -> String {
-    small_url_encode(&format!("impl-{:#}-for-{:#}", trait_, for_))
+    small_url_encode(&format!("impl-{:#}-for-{:#}", trait_.print(), for_.print()))
 }
 
 fn extract_for_impl_name(item: &clean::Item) -> Option<(String, String)> {
     match item.inner {
         clean::ItemEnum::ImplItem(ref i) => {
             if let Some(ref trait_) = i.trait_ {
-                Some((format!("{:#}", i.for_), get_id_for_impl_on_foreign_type(&i.for_, trait_)))
+                Some((
+                    format!("{:#}", i.for_.print()),
+                    get_id_for_impl_on_foreign_type(&i.for_, trait_),
+                ))
             } else {
                 None
             }
diff --git a/src/librustdoc/passes/calculate_doc_coverage.rs b/src/librustdoc/passes/calculate_doc_coverage.rs
index 4ee09f7096b61..dc1ca8d7668ae 100644
--- a/src/librustdoc/passes/calculate_doc_coverage.rs
+++ b/src/librustdoc/passes/calculate_doc_coverage.rs
@@ -142,7 +142,8 @@ impl fold::DocFolder for CoverageCalculator {
             }
             clean::ImplItem(ref impl_) => {
                 if let Some(ref tr) = impl_.trait_ {
-                    debug!("impl {:#} for {:#} in {}", tr, impl_.for_, i.source.filename);
+                    debug!("impl {:#} for {:#} in {}",
+                        tr.print(), impl_.for_.print(), i.source.filename);
 
                     // don't count trait impls, the missing-docs lint doesn't so we shouldn't
                     // either
@@ -151,11 +152,11 @@ impl fold::DocFolder for CoverageCalculator {
                     // inherent impls *can* be documented, and those docs show up, but in most
                     // cases it doesn't make sense, as all methods on a type are in one single
                     // impl block
-                    debug!("impl {:#} in {}", impl_.for_, i.source.filename);
+                    debug!("impl {:#} in {}", impl_.for_.print(), i.source.filename);
                 }
             }
             _ => {
-                debug!("counting {} {:?} in {}", i.type_(), i.name, i.source.filename);
+                debug!("counting {:?} {:?} in {}", i.type_(), i.name, i.source.filename);
                 self.items.entry(i.source.filename.clone())
                           .or_default()
                           .count_item(has_docs);
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index d6073cdc1e11d..b67f39d328015 100644
--- a/src/librustdoc/passes/collect_intra_doc_links.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links.rs
@@ -237,7 +237,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
         });
 
         if parent_node.is_some() {
-            debug!("got parent node for {} {:?}, id {:?}", item.type_(), item.name, item.def_id);
+            debug!("got parent node for {:?} {:?}, id {:?}", item.type_(), item.name, item.def_id);
         }
 
         let current_item = match item.inner {
diff --git a/src/librustdoc/passes/mod.rs b/src/librustdoc/passes/mod.rs
index 14f8b16dc3067..d0f2cdad8f3e4 100644
--- a/src/librustdoc/passes/mod.rs
+++ b/src/librustdoc/passes/mod.rs
@@ -153,7 +153,7 @@ impl<'a> DocFolder for Stripper<'a> {
                 // We need to recurse into stripped modules to strip things
                 // like impl methods but when doing so we must not add any
                 // items to the `retained` set.
-                debug!("Stripper: recursing into stripped {} {:?}", i.type_(), i.name);
+                debug!("Stripper: recursing into stripped {:?} {:?}", i.type_(), i.name);
                 let old = mem::replace(&mut self.update_retained, false);
                 let ret = self.fold_item_recur(i);
                 self.update_retained = old;
@@ -178,7 +178,7 @@ impl<'a> DocFolder for Stripper<'a> {
             | clean::ForeignTypeItem => {
                 if i.def_id.is_local() {
                     if !self.access_levels.is_exported(i.def_id) {
-                        debug!("Stripper: stripping {} {:?}", i.type_(), i.name);
+                        debug!("Stripper: stripping {:?} {:?}", i.type_(), i.name);
                         return None;
                     }
                 }
diff --git a/src/librustdoc/passes/strip_hidden.rs b/src/librustdoc/passes/strip_hidden.rs
index da8977544f64b..0159e03f6f299 100644
--- a/src/librustdoc/passes/strip_hidden.rs
+++ b/src/librustdoc/passes/strip_hidden.rs
@@ -39,7 +39,7 @@ struct Stripper<'a> {
 impl<'a> DocFolder for Stripper<'a> {
     fn fold_item(&mut self, i: Item) -> Option<Item> {
         if i.attrs.lists(sym::doc).has_word(sym::hidden) {
-            debug!("strip_hidden: stripping {} {:?}", i.type_(), i.name);
+            debug!("strip_hidden: stripping {:?} {:?}", i.type_(), i.name);
             // use a dedicated hidden item for given item type if any
             match i.inner {
                 clean::StructFieldItem(..) | clean::ModuleItem(..) => {

From ec349bef24dc6faf41970bc7de17d22bce6a7cfb Mon Sep 17 00:00:00 2001
From: Mark Rousskov <mark.simulacrum@gmail.com>
Date: Thu, 12 Sep 2019 22:32:12 -0400
Subject: [PATCH 02/18] Unwrap Visibility fields

There's not really any reason to not have the visibility default to
inherited, and this saves us the trouble of checking everywhere for
whether we have a visibility or not.
---
 src/librustdoc/clean/auto_trait.rs   |  2 +-
 src/librustdoc/clean/blanket_impl.rs |  2 +-
 src/librustdoc/clean/inline.rs       |  4 ++--
 src/librustdoc/clean/mod.rs          | 32 ++++++++++++++--------------
 src/librustdoc/html/format.rs        |  8 ++-----
 src/librustdoc/html/render.rs        |  5 ++---
 src/librustdoc/passes/mod.rs         |  6 +++---
 7 files changed, 27 insertions(+), 32 deletions(-)

diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs
index 516be99ed6aad..18a84cd0eeb76 100644
--- a/src/librustdoc/clean/auto_trait.rs
+++ b/src/librustdoc/clean/auto_trait.rs
@@ -119,7 +119,7 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
                 source: Span::empty(),
                 name: None,
                 attrs: Default::default(),
-                visibility: None,
+                visibility: Inherited,
                 def_id: self.cx.next_def_id(param_env_def_id.krate),
                 stability: None,
                 deprecation: None,
diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs
index 490d4107c51ab..4cd1cc1a1cf50 100644
--- a/src/librustdoc/clean/blanket_impl.rs
+++ b/src/librustdoc/clean/blanket_impl.rs
@@ -99,7 +99,7 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> {
                     source: self.cx.tcx.def_span(impl_def_id).clean(self.cx),
                     name: None,
                     attrs: Default::default(),
-                    visibility: None,
+                    visibility: Inherited,
                     def_id: self.cx.next_def_id(impl_def_id.krate),
                     stability: None,
                     deprecation: None,
diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs
index cb42ff1c8052f..031e77ff1dbe0 100644
--- a/src/librustdoc/clean/inline.rs
+++ b/src/librustdoc/clean/inline.rs
@@ -131,7 +131,7 @@ pub fn try_inline(
         name: Some(name.clean(cx)),
         attrs,
         inner,
-        visibility: Some(clean::Public),
+        visibility: clean::Public,
         stability: cx.tcx.lookup_stability(did).clean(cx),
         deprecation: cx.tcx.lookup_deprecation(did).clean(cx),
         def_id: did,
@@ -418,7 +418,7 @@ pub fn build_impl(cx: &DocContext<'_>, did: DefId, attrs: Option<Attrs<'_>>,
         source: tcx.def_span(did).clean(cx),
         name: None,
         attrs,
-        visibility: Some(clean::Inherited),
+        visibility: clean::Inherited,
         stability: tcx.lookup_stability(did).clean(cx),
         deprecation: tcx.lookup_deprecation(did).clean(cx),
         def_id: did,
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 10c4231b82eeb..197c09ba759e7 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -187,7 +187,7 @@ pub fn krate(mut cx: &mut DocContext<'_>) -> Crate {
                 source: Span::empty(),
                 name: Some(prim.to_url_str().to_string()),
                 attrs: attrs.clone(),
-                visibility: Some(Public),
+                visibility: Public,
                 stability: get_stability(cx, def_id),
                 deprecation: get_deprecation(cx, def_id),
                 def_id,
@@ -199,7 +199,7 @@ pub fn krate(mut cx: &mut DocContext<'_>) -> Crate {
                 source: Span::empty(),
                 name: Some(kw.clone()),
                 attrs: attrs,
-                visibility: Some(Public),
+                visibility: Public,
                 stability: get_stability(cx, def_id),
                 deprecation: get_deprecation(cx, def_id),
                 def_id,
@@ -361,7 +361,7 @@ pub struct Item {
     pub name: Option<String>,
     pub attrs: Attributes,
     pub inner: ItemEnum,
-    pub visibility: Option<Visibility>,
+    pub visibility: Visibility,
     pub def_id: DefId,
     pub stability: Option<Stability>,
     pub deprecation: Option<Deprecation>,
@@ -2311,7 +2311,7 @@ impl Clean<Item> for hir::TraitItem {
             attrs: self.attrs.clean(cx),
             source: self.span.clean(cx),
             def_id: local_did,
-            visibility: None,
+            visibility: Visibility::Inherited,
             stability: get_stability(cx, local_did),
             deprecation: get_deprecation(cx, local_did),
             inner,
@@ -2496,7 +2496,7 @@ impl Clean<Item> for ty::AssocItem {
 
         let visibility = match self.container {
             ty::ImplContainer(_) => self.vis.clean(cx),
-            ty::TraitContainer(_) => None,
+            ty::TraitContainer(_) => Inherited,
         };
 
         Item {
@@ -3293,9 +3293,9 @@ pub enum Visibility {
     Restricted(DefId, Path),
 }
 
-impl Clean<Option<Visibility>> for hir::Visibility {
-    fn clean(&self, cx: &DocContext<'_>) -> Option<Visibility> {
-        Some(match self.node {
+impl Clean<Visibility> for hir::Visibility {
+    fn clean(&self, cx: &DocContext<'_>) -> Visibility {
+        match self.node {
             hir::VisibilityKind::Public => Visibility::Public,
             hir::VisibilityKind::Inherited => Visibility::Inherited,
             hir::VisibilityKind::Crate(_) => Visibility::Crate,
@@ -3304,13 +3304,13 @@ impl Clean<Option<Visibility>> for hir::Visibility {
                 let did = register_res(cx, path.res);
                 Visibility::Restricted(did, path)
             }
-        })
+        }
     }
 }
 
-impl Clean<Option<Visibility>> for ty::Visibility {
-    fn clean(&self, _: &DocContext<'_>) -> Option<Visibility> {
-        Some(if *self == ty::Visibility::Public { Public } else { Inherited })
+impl Clean<Visibility> for ty::Visibility {
+    fn clean(&self, _: &DocContext<'_>) -> Visibility {
+        if *self == ty::Visibility::Public { Public } else { Inherited }
     }
 }
 
@@ -3427,7 +3427,7 @@ impl Clean<Item> for doctree::Variant<'_> {
             name: Some(self.name.clean(cx)),
             attrs: self.attrs.clean(cx),
             source: self.whence.clean(cx),
-            visibility: None,
+            visibility: Inherited,
             stability: cx.stability(self.id).clean(cx),
             deprecation: cx.deprecation(self.id).clean(cx),
             def_id: cx.tcx.hir().local_def_id(self.id),
@@ -3470,7 +3470,7 @@ impl Clean<Item> for ty::VariantDef {
             name: Some(self.ident.clean(cx)),
             attrs: inline::load_attrs(cx, self.def_id).clean(cx),
             source: cx.tcx.def_span(self.def_id).clean(cx),
-            visibility: Some(Inherited),
+            visibility: Inherited,
             def_id: self.def_id,
             inner: VariantItem(Variant { kind }),
             stability: get_stability(cx, self.def_id),
@@ -4333,7 +4333,7 @@ impl Clean<Item> for doctree::Macro<'_> {
             name: Some(name.clone()),
             attrs: self.attrs.clean(cx),
             source: self.whence.clean(cx),
-            visibility: Some(Public),
+            visibility: Public,
             stability: cx.stability(self.hid).clean(cx),
             deprecation: cx.deprecation(self.hid).clean(cx),
             def_id: self.def_id,
@@ -4361,7 +4361,7 @@ impl Clean<Item> for doctree::ProcMacro<'_> {
             name: Some(self.name.clean(cx)),
             attrs: self.attrs.clean(cx),
             source: self.whence.clean(cx),
-            visibility: Some(Public),
+            visibility: Public,
             stability: cx.stability(self.id).clean(cx),
             deprecation: cx.deprecation(self.id).clean(cx),
             def_id: cx.tcx.hir().local_def_id(self.id),
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index 9baa69d981b52..3c62977bd77d3 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -111,7 +111,7 @@ impl Buffer {
 /// Helper to render an optional visibility with a space after it (if the
 /// visibility is preset)
 #[derive(Copy, Clone)]
-pub struct VisSpace<'a>(pub &'a Option<clean::Visibility>);
+pub struct VisSpace<'a>(pub &'a clean::Visibility);
 /// Similarly to VisSpace, this structure is used to render a function style with a
 /// space after it.
 #[derive(Copy, Clone)]
@@ -1034,11 +1034,7 @@ impl Function<'_> {
 
 impl<'a> fmt::Display for VisSpace<'a> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        if let Some(v) = self.0 {
-            fmt::Display::fmt(&v.print_with_space(), f)
-        } else {
-            Ok(())
-        }
+        fmt::Display::fmt(&self.0.print_with_space(), f)
     }
 }
 
diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs
index 7aaf04f34d32f..32fa2daa026f7 100644
--- a/src/librustdoc/html/render.rs
+++ b/src/librustdoc/html/render.rs
@@ -1511,7 +1511,7 @@ impl DocFolder for Cache {
                 self.paths.insert(item.def_id, (stack, ItemType::Enum));
             }
 
-            clean::PrimitiveItem(..) if item.visibility.is_some() => {
+            clean::PrimitiveItem(..) => {
                 self.add_aliases(&item);
                 self.paths.insert(item.def_id, (self.stack.clone(),
                                                 item.type_()));
@@ -4306,8 +4306,7 @@ fn get_methods(
 ) -> Vec<String> {
     i.items.iter().filter_map(|item| {
         match item.name {
-            // Maybe check with clean::Visibility::Public as well?
-            Some(ref name) if !name.is_empty() && item.visibility.is_some() && item.is_method() => {
+            Some(ref name) if !name.is_empty() && item.is_method() => {
                 if !for_deref || should_render_item(item, deref_mut) {
                     Some(format!("<a href=\"#{}\">{}</a>",
                                  get_next_url(used_links, format!("method.{}", name)),
diff --git a/src/librustdoc/passes/mod.rs b/src/librustdoc/passes/mod.rs
index d0f2cdad8f3e4..f6560218a78c8 100644
--- a/src/librustdoc/passes/mod.rs
+++ b/src/librustdoc/passes/mod.rs
@@ -185,13 +185,13 @@ impl<'a> DocFolder for Stripper<'a> {
             }
 
             clean::StructFieldItem(..) => {
-                if i.visibility != Some(clean::Public) {
+                if i.visibility != clean::Public {
                     return StripItem(i).strip();
                 }
             }
 
             clean::ModuleItem(..) => {
-                if i.def_id.is_local() && i.visibility != Some(clean::Public) {
+                if i.def_id.is_local() && i.visibility != clean::Public {
                     debug!("Stripper: stripping module {:?}", i.name);
                     let old = mem::replace(&mut self.update_retained, false);
                     let ret = StripItem(self.fold_item_recur(i).unwrap()).strip();
@@ -299,7 +299,7 @@ impl DocFolder for ImportStripper {
     fn fold_item(&mut self, i: Item) -> Option<Item> {
         match i.inner {
             clean::ExternCrateItem(..) | clean::ImportItem(..)
-                if i.visibility != Some(clean::Public) =>
+                if i.visibility != clean::Public =>
             {
                 None
             }

From 8a9dab3a203ef26c6f8477bd388f2394747d598e Mon Sep 17 00:00:00 2001
From: Mark Rousskov <mark.simulacrum@gmail.com>
Date: Fri, 13 Sep 2019 08:36:00 -0400
Subject: [PATCH 03/18] Remove *Space wrappers in favor of direct impls or
 functions

---
 src/librustdoc/html/format.rs | 111 +++++++++++++---------------------
 src/librustdoc/html/render.rs |  82 ++++++++++++-------------
 2 files changed, 84 insertions(+), 109 deletions(-)

diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index 3c62977bd77d3..fafd43cb60b69 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -108,28 +108,6 @@ impl Buffer {
     }
 }
 
-/// Helper to render an optional visibility with a space after it (if the
-/// visibility is preset)
-#[derive(Copy, Clone)]
-pub struct VisSpace<'a>(pub &'a clean::Visibility);
-/// Similarly to VisSpace, this structure is used to render a function style with a
-/// space after it.
-#[derive(Copy, Clone)]
-pub struct UnsafetySpace(pub hir::Unsafety);
-/// Similarly to VisSpace, this structure is used to render a function constness
-/// with a space after it.
-#[derive(Copy, Clone)]
-pub struct ConstnessSpace(pub hir::Constness);
-/// Similarly to VisSpace, this structure is used to render a function asyncness
-/// with a space after it.
-#[derive(Copy, Clone)]
-pub struct AsyncSpace(pub hir::IsAsync);
-/// Similar to VisSpace, but used for mutability
-#[derive(Copy, Clone)]
-pub struct MutableSpace(pub clean::Mutability);
-pub struct AbiSpace(pub Abi);
-pub struct DefaultSpace(pub bool);
-
 /// Wrapper struct for properly emitting a function or method declaration.
 pub struct Function<'a> {
     /// The declaration to emit.
@@ -638,12 +616,13 @@ fn fmt_type(t: &clean::Type, f: &mut fmt::Formatter<'_>, use_absolute: bool) ->
         clean::BareFunction(ref decl) => {
             if f.alternate() {
                 write!(f, "{}{:#}fn{:#}{:#}",
-                       UnsafetySpace(decl.unsafety),
-                       AbiSpace(decl.abi),
+                       decl.unsafety.print_with_space(),
+                       print_abi_with_space(decl.abi),
                        decl.print_generic_params(),
                        decl.decl.print())
             } else {
-                write!(f, "{}{}", UnsafetySpace(decl.unsafety), AbiSpace(decl.abi))?;
+                write!(f, "{}{}",
+                    decl.unsafety.print_with_space(), print_abi_with_space(decl.abi))?;
                 primitive_link(f, PrimitiveType::Fn, "fn")?;
                 write!(f, "{}{}", decl.print_generic_params(), decl.decl.print())
             }
@@ -705,7 +684,7 @@ fn fmt_type(t: &clean::Type, f: &mut fmt::Formatter<'_>, use_absolute: bool) ->
                 Some(l) => format!("{} ", l.print()),
                 _ => String::new()
             };
-            let m = MutableSpace(mutability);
+            let m = mutability.print_with_space();
             let amp = if f.alternate() {
                 "&".to_string()
             } else {
@@ -956,13 +935,13 @@ impl Function<'_> {
                         }
                         clean::SelfBorrowed(Some(ref lt), mtbl) => {
                             args.push_str(
-                                &format!("{}{} {}self", amp, lt.print(), MutableSpace(mtbl)));
+                                &format!("{}{} {}self", amp, lt.print(), mtbl.print_with_space()));
                             args_plain.push_str(
-                                &format!("&{} {}self", lt.print(), MutableSpace(mtbl)));
+                                &format!("&{} {}self", lt.print(), mtbl.print_with_space()));
                         }
                         clean::SelfBorrowed(None, mtbl) => {
-                            args.push_str(&format!("{}{}self", amp, MutableSpace(mtbl)));
-                            args_plain.push_str(&format!("&{}self", MutableSpace(mtbl)));
+                            args.push_str(&format!("{}{}self", amp, mtbl.print_with_space()));
+                            args_plain.push_str(&format!("&{}self", mtbl.print_with_space()));
                         }
                         clean::SelfExplicit(ref typ) => {
                             if f.alternate() {
@@ -1032,14 +1011,8 @@ impl Function<'_> {
     }
 }
 
-impl<'a> fmt::Display for VisSpace<'a> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        fmt::Display::fmt(&self.0.print_with_space(), f)
-    }
-}
-
 impl clean::Visibility {
-    fn print_with_space(&self) -> impl fmt::Display + '_ {
+    crate fn print_with_space(&self) -> impl fmt::Display + '_ {
         display_fn(move |f| {
             match *self {
                 clean::Public => f.write_str("pub "),
@@ -1060,29 +1033,33 @@ impl clean::Visibility {
     }
 }
 
-impl fmt::Display for UnsafetySpace {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self.0 {
-            hir::Unsafety::Unsafe => write!(f, "unsafe "),
-            hir::Unsafety::Normal => Ok(())
+crate trait PrintWithSpace {
+    fn print_with_space(&self) -> &str;
+}
+
+impl PrintWithSpace for hir::Unsafety {
+    fn print_with_space(&self) -> &str {
+        match self {
+            hir::Unsafety::Unsafe => "unsafe ",
+            hir::Unsafety::Normal => ""
         }
     }
 }
 
-impl fmt::Display for ConstnessSpace {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self.0 {
-            hir::Constness::Const => write!(f, "const "),
-            hir::Constness::NotConst => Ok(())
+impl PrintWithSpace for hir::Constness {
+    fn print_with_space(&self) -> &str {
+        match self {
+            hir::Constness::Const => "const ",
+            hir::Constness::NotConst => ""
         }
     }
 }
 
-impl fmt::Display for AsyncSpace {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self.0 {
-            hir::IsAsync::Async => write!(f, "async "),
-            hir::IsAsync::NotAsync => Ok(()),
+impl PrintWithSpace for hir::IsAsync {
+    fn print_with_space(&self) -> &str {
+        match self {
+            hir::IsAsync::Async => "async ",
+            hir::IsAsync::NotAsync => "",
         }
     }
 }
@@ -1156,32 +1133,30 @@ impl clean::TypeBinding {
     }
 }
 
-impl fmt::Display for MutableSpace {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match *self {
-            MutableSpace(clean::Immutable) => Ok(()),
-            MutableSpace(clean::Mutable) => write!(f, "mut "),
+impl clean::Mutability {
+    crate fn print_with_space(&self) -> &str {
+        match self {
+            clean::Immutable => "",
+            clean::Mutable => "mut ",
         }
     }
 }
 
-impl fmt::Display for AbiSpace {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+crate fn print_abi_with_space(abi: Abi) -> impl fmt::Display {
+    display_fn(move |f| {
         let quot = if f.alternate() { "\"" } else { "&quot;" };
-        match self.0 {
+        match abi {
             Abi::Rust => Ok(()),
             abi => write!(f, "extern {0}{1}{0} ", quot, abi.name()),
         }
-    }
+    })
 }
 
-impl fmt::Display for DefaultSpace {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        if self.0 {
-            write!(f, "default ")
-        } else {
-            Ok(())
-        }
+crate fn print_default_space<'a>(v: bool) -> &'a str {
+    if v {
+        "default "
+    } else {
+        ""
     }
 }
 
diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs
index 32fa2daa026f7..0b3cf5ea1e226 100644
--- a/src/librustdoc/html/render.rs
+++ b/src/librustdoc/html/render.rs
@@ -65,9 +65,9 @@ use crate::docfs::{DocFS, ErrorStorage, PathError};
 use crate::doctree;
 use crate::fold::DocFolder;
 use crate::html::escape::Escape;
-use crate::html::format::{Buffer, AsyncSpace, ConstnessSpace};
-use crate::html::format::{print_generic_bounds, WhereClause, href, AbiSpace, DefaultSpace};
-use crate::html::format::{VisSpace, Function, UnsafetySpace, MutableSpace};
+use crate::html::format::{Buffer, PrintWithSpace, print_abi_with_space};
+use crate::html::format::{print_generic_bounds, WhereClause, href, print_default_space};
+use crate::html::format::{Function};
 use crate::html::format::fmt_impl_for_trait_page;
 use crate::html::item_type::ItemType;
 use crate::html::markdown::{self, Markdown, MarkdownHtml, MarkdownSummaryLine, ErrorCodes, IdMap};
@@ -2573,13 +2573,13 @@ fn item_module(w: &mut Buffer, cx: &Context, item: &clean::Item, items: &[clean:
                 match *src {
                     Some(ref src) => {
                         write!(w, "<tr><td><code>{}extern crate {} as {};",
-                               VisSpace(&myitem.visibility),
+                               myitem.visibility.print_with_space(),
                                anchor(myitem.def_id, src),
                                name)
                     }
                     None => {
                         write!(w, "<tr><td><code>{}extern crate {};",
-                               VisSpace(&myitem.visibility),
+                               myitem.visibility.print_with_space(),
                                anchor(myitem.def_id, name))
                     }
                 }
@@ -2588,7 +2588,7 @@ fn item_module(w: &mut Buffer, cx: &Context, item: &clean::Item, items: &[clean:
 
             clean::ImportItem(ref import) => {
                 write!(w, "<tr><td><code>{}{}</code></td></tr>",
-                       VisSpace(&myitem.visibility), import.print());
+                       myitem.visibility.print_with_space(), import.print());
             }
 
             _ => {
@@ -2794,7 +2794,7 @@ fn item_constant(w: &mut Buffer, cx: &Context, it: &clean::Item, c: &clean::Cons
     render_attributes(w, it, false);
     write!(w, "{vis}const \
                {name}: {typ}</pre>",
-           vis = VisSpace(&it.visibility),
+           vis = it.visibility.print_with_space(),
            name = it.name.as_ref().unwrap(),
            typ = c.type_.print());
     document(w, cx, it)
@@ -2805,8 +2805,8 @@ fn item_static(w: &mut Buffer, cx: &Context, it: &clean::Item, s: &clean::Static
     render_attributes(w, it, false);
     write!(w, "{vis}static {mutability}\
                {name}: {typ}</pre>",
-           vis = VisSpace(&it.visibility),
-           mutability = MutableSpace(s.mutability),
+           vis = it.visibility.print_with_space(),
+           mutability = s.mutability.print_with_space(),
            name = it.name.as_ref().unwrap(),
            typ = s.type_.print());
     document(w, cx, it)
@@ -2815,11 +2815,11 @@ fn item_static(w: &mut Buffer, cx: &Context, it: &clean::Item, s: &clean::Static
 fn item_function(w: &mut Buffer, cx: &Context, it: &clean::Item, f: &clean::Function) {
     let header_len = format!(
         "{}{}{}{}{:#}fn {}{:#}",
-        VisSpace(&it.visibility),
-        ConstnessSpace(f.header.constness),
-        UnsafetySpace(f.header.unsafety),
-        AsyncSpace(f.header.asyncness),
-        AbiSpace(f.header.abi),
+        it.visibility.print_with_space(),
+        f.header.constness.print_with_space(),
+        f.header.unsafety.print_with_space(),
+        f.header.asyncness.print_with_space(),
+        print_abi_with_space(f.header.abi),
         it.name.as_ref().unwrap(),
         f.generics.print()
     ).len();
@@ -2828,11 +2828,11 @@ fn item_function(w: &mut Buffer, cx: &Context, it: &clean::Item, f: &clean::Func
     write!(w,
            "{vis}{constness}{unsafety}{asyncness}{abi}fn \
            {name}{generics}{decl}{where_clause}</pre>",
-           vis = VisSpace(&it.visibility),
-           constness = ConstnessSpace(f.header.constness),
-           unsafety = UnsafetySpace(f.header.unsafety),
-           asyncness = AsyncSpace(f.header.asyncness),
-           abi = AbiSpace(f.header.abi),
+           vis = it.visibility.print_with_space(),
+           constness = f.header.constness.print_with_space(),
+           unsafety = f.header.unsafety.print_with_space(),
+           asyncness = f.header.asyncness.print_with_space(),
+           abi = print_abi_with_space(f.header.abi),
            name = it.name.as_ref().unwrap(),
            generics = f.generics.print(),
            where_clause = WhereClause { gens: &f.generics, indent: 0, end_newline: true },
@@ -2913,8 +2913,8 @@ fn item_trait(
         write!(w, "<pre class='rust trait'>");
         render_attributes(w, it, true);
         write!(w, "{}{}{}trait {}{}{}",
-               VisSpace(&it.visibility),
-               UnsafetySpace(t.unsafety),
+               it.visibility.print_with_space(),
+               t.unsafety.print_with_space(),
                if t.is_auto { "auto " } else { "" },
                it.name.as_ref().unwrap(),
                t.generics.print(),
@@ -3175,7 +3175,7 @@ fn assoc_const(w: &mut Buffer,
                extra: &str) {
     write!(w, "{}{}const <a href='{}' class=\"constant\"><b>{}</b></a>: {}",
            extra,
-           VisSpace(&it.visibility),
+           it.visibility.print_with_space(),
            naive_assoc_href(it, link),
            it.name.as_ref().unwrap(),
            ty.print());
@@ -3240,12 +3240,12 @@ fn render_assoc_item(w: &mut Buffer,
         };
         let mut header_len = format!(
             "{}{}{}{}{}{:#}fn {}{:#}",
-            VisSpace(&meth.visibility),
-            ConstnessSpace(header.constness),
-            UnsafetySpace(header.unsafety),
-            AsyncSpace(header.asyncness),
-            DefaultSpace(meth.is_default()),
-            AbiSpace(header.abi),
+            meth.visibility.print_with_space(),
+            header.constness.print_with_space(),
+            header.unsafety.print_with_space(),
+            header.asyncness.print_with_space(),
+            print_default_space(meth.is_default()),
+            print_abi_with_space(header.abi),
             name,
             g.print()
         ).len();
@@ -3259,12 +3259,12 @@ fn render_assoc_item(w: &mut Buffer,
         write!(w, "{}{}{}{}{}{}{}fn <a href='{href}' class='fnname'>{name}</a>\
                    {generics}{decl}{where_clause}",
                if parent == ItemType::Trait { "    " } else { "" },
-               VisSpace(&meth.visibility),
-               ConstnessSpace(header.constness),
-               UnsafetySpace(header.unsafety),
-               AsyncSpace(header.asyncness),
-               DefaultSpace(meth.is_default()),
-               AbiSpace(header.abi),
+               meth.visibility.print_with_space(),
+               header.constness.print_with_space(),
+               header.unsafety.print_with_space(),
+               header.asyncness.print_with_space(),
+               print_default_space(meth.is_default()),
+               print_abi_with_space(header.abi),
                href = href,
                name = name,
                generics = g.print(),
@@ -3399,7 +3399,7 @@ fn item_enum(w: &mut Buffer, cx: &Context, it: &clean::Item, e: &clean::Enum) {
         write!(w, "<pre class='rust enum'>");
         render_attributes(w, it, true);
         write!(w, "{}enum {}{}{}",
-               VisSpace(&it.visibility),
+               it.visibility.print_with_space(),
                it.name.as_ref().unwrap(),
                e.generics.print(),
                WhereClause { gens: &e.generics, indent: 0, end_newline: true });
@@ -3588,7 +3588,7 @@ fn render_struct(w: &mut Buffer, it: &clean::Item,
                  tab: &str,
                  structhead: bool) {
     write!(w, "{}{}{}",
-           VisSpace(&it.visibility),
+           it.visibility.print_with_space(),
            if structhead {"struct "} else {""},
            it.name.as_ref().unwrap());
     if let Some(g) = g {
@@ -3605,7 +3605,7 @@ fn render_struct(w: &mut Buffer, it: &clean::Item,
                 if let clean::StructFieldItem(ref ty) = field.inner {
                     write!(w, "\n{}    {}{}: {},",
                            tab,
-                           VisSpace(&field.visibility),
+                           field.visibility.print_with_space(),
                            field.name.as_ref().unwrap(),
                            ty.print());
                     has_visible_fields = true;
@@ -3635,7 +3635,7 @@ fn render_struct(w: &mut Buffer, it: &clean::Item,
                         write!(w, "_")
                     }
                     clean::StructFieldItem(ref ty) => {
-                        write!(w, "{}{}", VisSpace(&field.visibility), ty.print())
+                        write!(w, "{}{}", field.visibility.print_with_space(), ty.print())
                     }
                     _ => unreachable!()
                 }
@@ -3662,7 +3662,7 @@ fn render_union(w: &mut Buffer, it: &clean::Item,
                 tab: &str,
                 structhead: bool) {
     write!(w, "{}{}{}",
-           VisSpace(&it.visibility),
+           it.visibility.print_with_space(),
            if structhead {"union "} else {""},
            it.name.as_ref().unwrap());
     if let Some(g) = g {
@@ -3674,7 +3674,7 @@ fn render_union(w: &mut Buffer, it: &clean::Item,
     for field in fields {
         if let clean::StructFieldItem(ref ty) = field.inner {
             write!(w, "    {}{}: {},\n{}",
-                   VisSpace(&field.visibility),
+                   field.visibility.print_with_space(),
                    field.name.as_ref().unwrap(),
                    ty.print(),
                    tab);
@@ -4186,7 +4186,7 @@ fn item_foreign_type(w: &mut Buffer, cx: &Context, it: &clean::Item) {
     write!(
         w,
         "    {}type {};\n}}</pre>",
-        VisSpace(&it.visibility),
+        it.visibility.print_with_space(),
         it.name.as_ref().unwrap(),
     );
 

From 3f144e119ec958b2a6ac3db3568b1bd2b3ba488a Mon Sep 17 00:00:00 2001
From: Mark Rousskov <mark.simulacrum@gmail.com>
Date: Fri, 13 Sep 2019 08:41:27 -0400
Subject: [PATCH 04/18] Move Toc printing from fmt::Display

---
 src/librustdoc/html/markdown.rs |  2 +-
 src/librustdoc/html/toc.rs      | 37 ++++++++++++++-------------------
 2 files changed, 17 insertions(+), 22 deletions(-)

diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index 1a4fa38ff8db0..9ff1e1d31197d 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -752,7 +752,7 @@ impl MarkdownWithToc<'_> {
             html::push_html(&mut s, p);
         }
 
-        format!("<nav id=\"TOC\">{}</nav>{}", toc.into_toc(), s)
+        format!("<nav id=\"TOC\">{}</nav>{}", toc.into_toc().print(), s)
     }
 }
 
diff --git a/src/librustdoc/html/toc.rs b/src/librustdoc/html/toc.rs
index 2da7aceae8bf4..0fb2f8dd7962a 100644
--- a/src/librustdoc/html/toc.rs
+++ b/src/librustdoc/html/toc.rs
@@ -1,10 +1,7 @@
 //! Table-of-contents creation.
 
-use std::fmt;
-use std::string::String;
-
 /// A (recursive) table of contents
-#[derive(PartialEq)]
+#[derive(Debug, PartialEq)]
 pub struct Toc {
     /// The levels are strictly decreasing, i.e.
     ///
@@ -28,7 +25,7 @@ impl Toc {
     }
 }
 
-#[derive(PartialEq)]
+#[derive(Debug, PartialEq)]
 pub struct TocEntry {
     level: u32,
     sec_number: String,
@@ -165,25 +162,23 @@ impl TocBuilder {
     }
 }
 
-impl fmt::Debug for Toc {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        fmt::Display::fmt(self, f)
-    }
-}
-
-impl fmt::Display for Toc {
-    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(fmt, "<ul>")?;
+impl Toc {
+    fn print_inner(&self, v: &mut String) {
+        v.push_str("<ul>");
         for entry in &self.entries {
-            // recursively format this table of contents (the
-            // `{children}` is the key).
-            write!(fmt,
-                   "\n<li><a href=\"#{id}\">{num} {name}</a>{children}</li>",
+            // recursively format this table of contents
+            v.push_str(&format!("\n<li><a href=\"#{id}\">{num} {name}</a>",
                    id = entry.id,
-                   num = entry.sec_number, name = entry.name,
-                   children = entry.children)?
+                   num = entry.sec_number, name = entry.name));
+            entry.children.print_inner(&mut *v);
+            v.push_str("</li>");
         }
-        write!(fmt, "</ul>")
+        v.push_str("</ul>");
+    }
+    crate fn print(&self) -> String {
+        let mut v = String::new();
+        self.print_inner(&mut v);
+        v
     }
 }
 

From aa4055cd6c92095c1996dd6eb31b08cced604e88 Mon Sep 17 00:00:00 2001
From: Mark Rousskov <mark.simulacrum@gmail.com>
Date: Fri, 13 Sep 2019 08:42:58 -0400
Subject: [PATCH 05/18] Simplify render_spotlight_traits

---
 src/librustdoc/html/render.rs | 8 ++------
 1 file changed, 2 insertions(+), 6 deletions(-)

diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs
index 0b3cf5ea1e226..8d5bd0d2e16e1 100644
--- a/src/librustdoc/html/render.rs
+++ b/src/librustdoc/html/render.rs
@@ -3858,19 +3858,15 @@ fn should_render_item(item: &clean::Item, deref_mut_: bool) -> bool {
 }
 
 fn render_spotlight_traits(item: &clean::Item) -> String {
-    let mut out = String::new();
-
     match item.inner {
         clean::FunctionItem(clean::Function { ref decl, .. }) |
         clean::TyMethodItem(clean::TyMethod { ref decl, .. }) |
         clean::MethodItem(clean::Method { ref decl, .. }) |
         clean::ForeignFunctionItem(clean::Function { ref decl, .. }) => {
-            out = spotlight_decl(decl);
+            spotlight_decl(decl)
         }
-        _ => {}
+        _ => String::new()
     }
-
-    out
 }
 
 fn spotlight_decl(decl: &clean::FnDecl) -> String {

From e0e0c3787ca64d8c2540d703ca1e2f26607c5717 Mon Sep 17 00:00:00 2001
From: Mark Rousskov <mark.simulacrum@gmail.com>
Date: Fri, 13 Sep 2019 09:26:11 -0400
Subject: [PATCH 06/18] Replace SlashChecker with ensure_trailing_slash

---
 src/librustdoc/html/layout.rs |  4 ++--
 src/librustdoc/html/render.rs | 22 ++++++++++------------
 2 files changed, 12 insertions(+), 14 deletions(-)

diff --git a/src/librustdoc/html/layout.rs b/src/librustdoc/html/layout.rs
index 56074f4ab1192..6414241727a72 100644
--- a/src/librustdoc/html/layout.rs
+++ b/src/librustdoc/html/layout.rs
@@ -1,7 +1,7 @@
 use std::path::PathBuf;
 
 use crate::externalfiles::ExternalHtml;
-use crate::html::render::SlashChecker;
+use crate::html::render::ensure_trailing_slash;
 use crate::html::format::{Buffer, Print};
 
 #[derive(Clone)]
@@ -180,7 +180,7 @@ pub fn render<T: Print, S: Print>(
     css_class = page.css_class,
     logo      = {
         let p = format!("{}{}", page.root_path, layout.krate);
-        let p = SlashChecker(&p);
+        let p = ensure_trailing_slash(&p);
         if layout.logo.is_empty() {
             format!("<a href='{path}index.html'>\
                      <div class='logo-container'>\
diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs
index 8d5bd0d2e16e1..9064dc8b44db4 100644
--- a/src/librustdoc/html/render.rs
+++ b/src/librustdoc/html/render.rs
@@ -33,7 +33,7 @@ use std::cmp::Ordering;
 use std::collections::{BTreeMap, VecDeque};
 use std::default::Default;
 use std::error;
-use std::fmt::{self, Display, Formatter, Write as FmtWrite};
+use std::fmt::{self, Formatter, Write as FmtWrite};
 use std::ffi::OsStr;
 use std::fs::{self, File};
 use std::io::prelude::*;
@@ -82,16 +82,14 @@ mod tests;
 /// A pair of name and its optional document.
 pub type NameDoc = (String, Option<String>);
 
-pub struct SlashChecker<'a>(pub &'a str);
-
-impl<'a> Display for SlashChecker<'a> {
-    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
-        if !self.0.ends_with("/") && !self.0.is_empty() {
-            write!(f, "{}/", self.0)
+crate fn ensure_trailing_slash(v: &str) -> impl fmt::Display + '_ {
+    crate::html::format::display_fn(move |f| {
+        if !v.ends_with("/") && !v.is_empty() {
+            write!(f, "{}/", v)
         } else {
-            write!(f, "{}", self.0)
+            write!(f, "{}", v)
         }
-    }
+    })
 }
 
 #[derive(Debug)]
@@ -106,7 +104,7 @@ impl error::Error for Error {
     }
 }
 
-impl Display for Error {
+impl std::fmt::Display for Error {
     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
         let file = self.file.display().to_string();
         if file.is_empty() {
@@ -1162,7 +1160,7 @@ themePicker.onblur = handleThemeButtonsBlur;
                                     .iter()
                                     .map(|s| {
                                         format!("<li><a href=\"{}index.html\">{}</li>",
-                                                SlashChecker(s), s)
+                                                ensure_trailing_slash(s), s)
                                     })
                                     .collect::<String>());
             let v = layout::render(&cx.shared.layout,
@@ -2286,7 +2284,7 @@ fn print_item(cx: &Context, item: &clean::Item, buf: &mut Buffer) {
 
 fn item_path(ty: ItemType, name: &str) -> String {
     match ty {
-        ItemType::Module => format!("{}index.html", SlashChecker(name)),
+        ItemType::Module => format!("{}index.html", ensure_trailing_slash(name)),
         _ => format!("{}.{}.html", ty, name),
     }
 }

From 98c94f6f771c0e3894a65e82572c5dca1c43167d Mon Sep 17 00:00:00 2001
From: Mark Rousskov <mark.simulacrum@gmail.com>
Date: Fri, 13 Sep 2019 09:51:32 -0400
Subject: [PATCH 07/18] Move edition field out of Context

---
 src/librustdoc/html/render.rs | 17 +++++++++--------
 1 file changed, 9 insertions(+), 8 deletions(-)

diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs
index 9064dc8b44db4..1ecb2293fc22c 100644
--- a/src/librustdoc/html/render.rs
+++ b/src/librustdoc/html/render.rs
@@ -164,8 +164,6 @@ struct Context {
     /// publicly reused items to redirect to the right location.
     pub render_redirect_pages: bool,
     pub codes: ErrorCodes,
-    /// The default edition used to parse doctests.
-    pub edition: Edition,
     /// The map used to ensure all generated 'id=' attributes are unique.
     id_map: Rc<RefCell<IdMap>>,
     pub shared: Arc<SharedContext>,
@@ -208,6 +206,8 @@ crate struct SharedContext {
     pub generate_redirect_pages: bool,
     /// The fs handle we are working with.
     pub fs: DocFS,
+    /// The default edition used to parse doctests.
+    pub edition: Edition,
 }
 
 impl SharedContext {
@@ -539,6 +539,7 @@ pub fn run(mut krate: clean::Crate,
         static_root_path,
         generate_redirect_pages,
         fs: DocFS::new(&errors),
+        edition,
     };
 
     // If user passed in `--playground-url` arg, we fill in crate name here
@@ -585,7 +586,6 @@ pub fn run(mut krate: clean::Crate,
         dst,
         render_redirect_pages: false,
         codes: ErrorCodes::from(UnstableFeatures::from_environment().is_nightly_build()),
-        edition,
         id_map: Rc::new(RefCell::new(id_map)),
         shared: Arc::new(scx),
         playground,
@@ -1134,7 +1134,7 @@ themePicker.onblur = handleThemeButtonsBlur;
             md_opts.output = cx.dst.clone();
             md_opts.external_html = (*cx.shared).layout.external_html.clone();
 
-            crate::markdown::render(index_page, md_opts, diag, cx.edition);
+            crate::markdown::render(index_page, md_opts, diag, cx.shared.edition);
         } else {
             let dst = cx.dst.join("index.html");
             let page = layout::Page {
@@ -2353,7 +2353,7 @@ fn render_markdown(
            if is_hidden { " hidden" } else { "" },
            prefix,
            Markdown(md_text, &links, &mut ids,
-           cx.codes, cx.edition, &cx.playground).to_string())
+           cx.codes, cx.shared.edition, &cx.playground).to_string())
 }
 
 fn document_short(
@@ -2710,7 +2710,8 @@ fn short_stability(item: &clean::Item, cx: &Context) -> Vec<String> {
 
         if let Some(note) = note {
             let mut ids = cx.id_map.borrow_mut();
-            let html = MarkdownHtml(&note, &mut ids, error_codes, cx.edition, &cx.playground);
+            let html = MarkdownHtml(
+                &note, &mut ids, error_codes, cx.shared.edition, &cx.playground);
             message.push_str(&format!(": {}", html.to_string()));
         }
         stability.push(format!("<div class='stab deprecated'>{}</div>", message));
@@ -2763,7 +2764,7 @@ fn short_stability(item: &clean::Item, cx: &Context) -> Vec<String> {
                     &unstable_reason,
                     &mut ids,
                     error_codes,
-                    cx.edition,
+                    cx.shared.edition,
                     &cx.playground,
                 ).to_string()
             );
@@ -3960,7 +3961,7 @@ fn render_impl(w: &mut Buffer, cx: &Context, i: &Impl, link: AssocItemLink<'_>,
             let mut ids = cx.id_map.borrow_mut();
             write!(w, "<div class='docblock'>{}</div>",
                    Markdown(&*dox, &i.impl_item.links(), &mut ids,
-                            cx.codes, cx.edition, &cx.playground).to_string());
+                            cx.codes, cx.shared.edition, &cx.playground).to_string());
         }
     }
 

From c26518086eed253924f2f9b91f2dd812084e4b31 Mon Sep 17 00:00:00 2001
From: Mark Rousskov <mark.simulacrum@gmail.com>
Date: Fri, 13 Sep 2019 10:34:04 -0400
Subject: [PATCH 08/18] Move error codes to shared context

---
 src/librustdoc/html/render.rs | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs
index 1ecb2293fc22c..fb751041644ee 100644
--- a/src/librustdoc/html/render.rs
+++ b/src/librustdoc/html/render.rs
@@ -163,7 +163,6 @@ struct Context {
     /// real location of an item. This is used to allow external links to
     /// publicly reused items to redirect to the right location.
     pub render_redirect_pages: bool,
-    pub codes: ErrorCodes,
     /// The map used to ensure all generated 'id=' attributes are unique.
     id_map: Rc<RefCell<IdMap>>,
     pub shared: Arc<SharedContext>,
@@ -208,6 +207,7 @@ crate struct SharedContext {
     pub fs: DocFS,
     /// The default edition used to parse doctests.
     pub edition: Edition,
+    pub codes: ErrorCodes,
 }
 
 impl SharedContext {
@@ -540,6 +540,7 @@ pub fn run(mut krate: clean::Crate,
         generate_redirect_pages,
         fs: DocFS::new(&errors),
         edition,
+        codes: ErrorCodes::from(UnstableFeatures::from_environment().is_nightly_build()),
     };
 
     // If user passed in `--playground-url` arg, we fill in crate name here
@@ -585,7 +586,6 @@ pub fn run(mut krate: clean::Crate,
         current: Vec::new(),
         dst,
         render_redirect_pages: false,
-        codes: ErrorCodes::from(UnstableFeatures::from_environment().is_nightly_build()),
         id_map: Rc::new(RefCell::new(id_map)),
         shared: Arc::new(scx),
         playground,
@@ -2353,7 +2353,7 @@ fn render_markdown(
            if is_hidden { " hidden" } else { "" },
            prefix,
            Markdown(md_text, &links, &mut ids,
-           cx.codes, cx.shared.edition, &cx.playground).to_string())
+           cx.shared.codes, cx.shared.edition, &cx.playground).to_string())
 }
 
 fn document_short(
@@ -3961,7 +3961,7 @@ fn render_impl(w: &mut Buffer, cx: &Context, i: &Impl, link: AssocItemLink<'_>,
             let mut ids = cx.id_map.borrow_mut();
             write!(w, "<div class='docblock'>{}</div>",
                    Markdown(&*dox, &i.impl_item.links(), &mut ids,
-                            cx.codes, cx.shared.edition, &cx.playground).to_string());
+                            cx.shared.codes, cx.shared.edition, &cx.playground).to_string());
         }
     }
 

From f4bb5a7c1a3183f1af14695a48258331b0360860 Mon Sep 17 00:00:00 2001
From: Mark Rousskov <mark.simulacrum@gmail.com>
Date: Fri, 13 Sep 2019 10:40:22 -0400
Subject: [PATCH 09/18] Move playground to shared context

---
 src/librustdoc/html/render.rs | 73 ++++++++++++++++++-----------------
 1 file changed, 38 insertions(+), 35 deletions(-)

diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs
index fb751041644ee..9d5cddd6d47a7 100644
--- a/src/librustdoc/html/render.rs
+++ b/src/librustdoc/html/render.rs
@@ -166,7 +166,6 @@ struct Context {
     /// The map used to ensure all generated 'id=' attributes are unique.
     id_map: Rc<RefCell<IdMap>>,
     pub shared: Arc<SharedContext>,
-    playground: Option<markdown::Playground>,
 }
 
 crate struct SharedContext {
@@ -208,6 +207,7 @@ crate struct SharedContext {
     /// The default edition used to parse doctests.
     pub edition: Edition,
     pub codes: ErrorCodes,
+    playground: Option<markdown::Playground>,
 }
 
 impl SharedContext {
@@ -518,31 +518,6 @@ pub fn run(mut krate: clean::Crate,
         _ => PathBuf::new(),
     };
     let mut errors = Arc::new(ErrorStorage::new());
-    let mut scx = SharedContext {
-        collapsed: krate.collapsed,
-        src_root,
-        include_sources: true,
-        local_sources: Default::default(),
-        issue_tracker_base_url: None,
-        layout: layout::Layout {
-            logo: String::new(),
-            favicon: String::new(),
-            external_html,
-            krate: krate.name.clone(),
-            css_file_extension: extension_css,
-            generate_search_filter,
-        },
-        created_dirs: Default::default(),
-        sort_modules_alphabetically,
-        themes,
-        resource_suffix,
-        static_root_path,
-        generate_redirect_pages,
-        fs: DocFS::new(&errors),
-        edition,
-        codes: ErrorCodes::from(UnstableFeatures::from_environment().is_nightly_build()),
-    };
-
     // If user passed in `--playground-url` arg, we fill in crate name here
     let mut playground = None;
     if let Some(url) = playground_url {
@@ -551,6 +526,16 @@ pub fn run(mut krate: clean::Crate,
             url,
         });
     }
+    let mut layout = layout::Layout {
+        logo: String::new(),
+        favicon: String::new(),
+        external_html,
+        krate: krate.name.clone(),
+        css_file_extension: extension_css,
+        generate_search_filter,
+    };
+    let mut issue_tracker_base_url = None;
+    let mut include_sources = true;
 
     // Crawl the crate attributes looking for attributes which control how we're
     // going to emit HTML
@@ -558,10 +543,10 @@ pub fn run(mut krate: clean::Crate,
         for attr in attrs.lists(sym::doc) {
             match (attr.name_or_empty(), attr.value_str()) {
                 (sym::html_favicon_url, Some(s)) => {
-                    scx.layout.favicon = s.to_string();
+                    layout.favicon = s.to_string();
                 }
                 (sym::html_logo_url, Some(s)) => {
-                    scx.layout.logo = s.to_string();
+                    layout.logo = s.to_string();
                 }
                 (sym::html_playground_url, Some(s)) => {
                     playground = Some(markdown::Playground {
@@ -570,15 +555,34 @@ pub fn run(mut krate: clean::Crate,
                     });
                 }
                 (sym::issue_tracker_base_url, Some(s)) => {
-                    scx.issue_tracker_base_url = Some(s.to_string());
+                    issue_tracker_base_url = Some(s.to_string());
                 }
                 (sym::html_no_source, None) if attr.is_word() => {
-                    scx.include_sources = false;
+                    include_sources = false;
                 }
                 _ => {}
             }
         }
     }
+    let mut scx = SharedContext {
+        collapsed: krate.collapsed,
+        src_root,
+        include_sources,
+        local_sources: Default::default(),
+        issue_tracker_base_url,
+        layout,
+        created_dirs: Default::default(),
+        sort_modules_alphabetically,
+        themes,
+        resource_suffix,
+        static_root_path,
+        generate_redirect_pages,
+        fs: DocFS::new(&errors),
+        edition,
+        codes: ErrorCodes::from(UnstableFeatures::from_environment().is_nightly_build()),
+        playground,
+    };
+
     let dst = output;
     scx.ensure_dir(&dst)?;
     krate = sources::render(&dst, &mut scx, krate)?;
@@ -588,7 +592,6 @@ pub fn run(mut krate: clean::Crate,
         render_redirect_pages: false,
         id_map: Rc::new(RefCell::new(id_map)),
         shared: Arc::new(scx),
-        playground,
     };
 
     // Crawl the crate to build various caches used for the output
@@ -2353,7 +2356,7 @@ fn render_markdown(
            if is_hidden { " hidden" } else { "" },
            prefix,
            Markdown(md_text, &links, &mut ids,
-           cx.shared.codes, cx.shared.edition, &cx.playground).to_string())
+           cx.shared.codes, cx.shared.edition, &cx.shared.playground).to_string())
 }
 
 fn document_short(
@@ -2711,7 +2714,7 @@ fn short_stability(item: &clean::Item, cx: &Context) -> Vec<String> {
         if let Some(note) = note {
             let mut ids = cx.id_map.borrow_mut();
             let html = MarkdownHtml(
-                &note, &mut ids, error_codes, cx.shared.edition, &cx.playground);
+                &note, &mut ids, error_codes, cx.shared.edition, &cx.shared.playground);
             message.push_str(&format!(": {}", html.to_string()));
         }
         stability.push(format!("<div class='stab deprecated'>{}</div>", message));
@@ -2765,7 +2768,7 @@ fn short_stability(item: &clean::Item, cx: &Context) -> Vec<String> {
                     &mut ids,
                     error_codes,
                     cx.shared.edition,
-                    &cx.playground,
+                    &cx.shared.playground,
                 ).to_string()
             );
         }
@@ -3961,7 +3964,7 @@ fn render_impl(w: &mut Buffer, cx: &Context, i: &Impl, link: AssocItemLink<'_>,
             let mut ids = cx.id_map.borrow_mut();
             write!(w, "<div class='docblock'>{}</div>",
                    Markdown(&*dox, &i.impl_item.links(), &mut ids,
-                            cx.shared.codes, cx.shared.edition, &cx.playground).to_string());
+                            cx.shared.codes, cx.shared.edition, &cx.shared.playground).to_string());
         }
     }
 

From f5ed0fd1c0175679c7f72ee0e6f8f5532f94a69f Mon Sep 17 00:00:00 2001
From: Mark Rousskov <mark.simulacrum@gmail.com>
Date: Fri, 13 Sep 2019 11:22:12 -0400
Subject: [PATCH 10/18] Move `Cache` generation to separate module

---
 src/librustdoc/html/render.rs       | 668 +--------------------------
 src/librustdoc/html/render/cache.rs | 675 ++++++++++++++++++++++++++++
 2 files changed, 689 insertions(+), 654 deletions(-)
 create mode 100644 src/librustdoc/html/render/cache.rs

diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs
index 9d5cddd6d47a7..ff7134ab7c0d5 100644
--- a/src/librustdoc/html/render.rs
+++ b/src/librustdoc/html/render.rs
@@ -25,8 +25,6 @@
 //! These threads are not parallelized (they haven't been a bottleneck yet), and
 //! both occur before the crate is rendered.
 
-pub use self::ExternalLocation::*;
-
 use std::borrow::Cow;
 use std::cell::{Cell, RefCell};
 use std::cmp::Ordering;
@@ -38,7 +36,6 @@ use std::ffi::OsStr;
 use std::fs::{self, File};
 use std::io::prelude::*;
 use std::io::{self, BufReader};
-use std::mem;
 use std::path::{PathBuf, Path, Component};
 use std::str;
 use std::sync::Arc;
@@ -52,7 +49,7 @@ use syntax::ext::base::MacroKind;
 use syntax::source_map::FileName;
 use syntax::feature_gate::UnstableFeatures;
 use syntax::symbol::{Symbol, sym};
-use rustc::hir::def_id::{CrateNum, CRATE_DEF_INDEX, DefId};
+use rustc::hir::def_id::DefId;
 use rustc::middle::privacy::AccessLevels;
 use rustc::middle::stability;
 use rustc::hir;
@@ -63,7 +60,6 @@ use crate::clean::{self, AttributesExt, Deprecation, GetDefId, SelfTy, Mutabilit
 use crate::config::RenderOptions;
 use crate::docfs::{DocFS, ErrorStorage, PathError};
 use crate::doctree;
-use crate::fold::DocFolder;
 use crate::html::escape::Escape;
 use crate::html::format::{Buffer, PrintWithSpace, print_abi_with_space};
 use crate::html::format::{print_generic_bounds, WhereClause, href, print_default_space};
@@ -79,6 +75,11 @@ use minifier;
 #[cfg(test)]
 mod tests;
 
+mod cache;
+
+use cache::Cache;
+crate use cache::ExternalLocation::{self, *};
+
 /// A pair of name and its optional document.
 pub type NameDoc = (String, Option<String>);
 
@@ -234,16 +235,6 @@ impl SharedContext {
     }
 }
 
-/// Indicates where an external crate can be found.
-pub enum ExternalLocation {
-    /// Remote URL root of the external crate
-    Remote(String),
-    /// This external crate can be found in the local doc/ folder
-    Local,
-    /// The external crate could not be found.
-    Unknown,
-}
-
 /// Metadata about implementations for a type or trait.
 #[derive(Clone, Debug)]
 pub struct Impl {
@@ -263,106 +254,6 @@ impl Impl {
     }
 }
 
-/// This cache is used to store information about the `clean::Crate` being
-/// rendered in order to provide more useful documentation. This contains
-/// information like all implementors of a trait, all traits a type implements,
-/// documentation for all known traits, etc.
-///
-/// This structure purposefully does not implement `Clone` because it's intended
-/// to be a fairly large and expensive structure to clone. Instead this adheres
-/// to `Send` so it may be stored in a `Arc` instance and shared among the various
-/// rendering threads.
-#[derive(Default)]
-pub struct Cache {
-    /// Maps a type ID to all known implementations for that type. This is only
-    /// recognized for intra-crate `ResolvedPath` types, and is used to print
-    /// out extra documentation on the page of an enum/struct.
-    ///
-    /// The values of the map are a list of implementations and documentation
-    /// found on that implementation.
-    pub impls: FxHashMap<DefId, Vec<Impl>>,
-
-    /// Maintains a mapping of local crate `NodeId`s to the fully qualified name
-    /// and "short type description" of that node. This is used when generating
-    /// URLs when a type is being linked to. External paths are not located in
-    /// this map because the `External` type itself has all the information
-    /// necessary.
-    pub paths: FxHashMap<DefId, (Vec<String>, ItemType)>,
-
-    /// Similar to `paths`, but only holds external paths. This is only used for
-    /// generating explicit hyperlinks to other crates.
-    pub external_paths: FxHashMap<DefId, (Vec<String>, ItemType)>,
-
-    /// Maps local `DefId`s of exported types to fully qualified paths.
-    /// Unlike 'paths', this mapping ignores any renames that occur
-    /// due to 'use' statements.
-    ///
-    /// This map is used when writing out the special 'implementors'
-    /// javascript file. By using the exact path that the type
-    /// is declared with, we ensure that each path will be identical
-    /// to the path used if the corresponding type is inlined. By
-    /// doing this, we can detect duplicate impls on a trait page, and only display
-    /// the impl for the inlined type.
-    pub exact_paths: FxHashMap<DefId, Vec<String>>,
-
-    /// This map contains information about all known traits of this crate.
-    /// Implementations of a crate should inherit the documentation of the
-    /// parent trait if no extra documentation is specified, and default methods
-    /// should show up in documentation about trait implementations.
-    pub traits: FxHashMap<DefId, clean::Trait>,
-
-    /// When rendering traits, it's often useful to be able to list all
-    /// implementors of the trait, and this mapping is exactly, that: a mapping
-    /// of trait ids to the list of known implementors of the trait
-    pub implementors: FxHashMap<DefId, Vec<Impl>>,
-
-    /// Cache of where external crate documentation can be found.
-    pub extern_locations: FxHashMap<CrateNum, (String, PathBuf, ExternalLocation)>,
-
-    /// Cache of where documentation for primitives can be found.
-    pub primitive_locations: FxHashMap<clean::PrimitiveType, DefId>,
-
-    // Note that external items for which `doc(hidden)` applies to are shown as
-    // non-reachable while local items aren't. This is because we're reusing
-    // the access levels from the privacy check pass.
-    pub access_levels: AccessLevels<DefId>,
-
-    /// The version of the crate being documented, if given from the `--crate-version` flag.
-    pub crate_version: Option<String>,
-
-    // Private fields only used when initially crawling a crate to build a cache
-
-    stack: Vec<String>,
-    parent_stack: Vec<DefId>,
-    parent_is_trait_impl: bool,
-    search_index: Vec<IndexItem>,
-    stripped_mod: bool,
-    deref_trait_did: Option<DefId>,
-    deref_mut_trait_did: Option<DefId>,
-    owned_box_did: Option<DefId>,
-    masked_crates: FxHashSet<CrateNum>,
-
-    // In rare case where a structure is defined in one module but implemented
-    // in another, if the implementing module is parsed before defining module,
-    // then the fully qualified name of the structure isn't presented in `paths`
-    // yet when its implementation methods are being indexed. Caches such methods
-    // and their parent id here and indexes them at the end of crate parsing.
-    orphan_impl_items: Vec<(DefId, clean::Item)>,
-
-    // Similarly to `orphan_impl_items`, sometimes trait impls are picked up
-    // even though the trait itself is not exported. This can happen if a trait
-    // was defined in function/expression scope, since the impl will be picked
-    // up by `collect-trait-impls` but the trait won't be scraped out in the HIR
-    // crawl. In order to prevent crashes when looking for spotlight traits or
-    // when gathering trait documentation on a type, hold impls here while
-    // folding and add them to the cache later on if we find the trait.
-    orphan_trait_impls: Vec<(DefId, FxHashSet<DefId>, Impl)>,
-
-    /// Aliases added through `#[doc(alias = "...")]`. Since a few items can have the same alias,
-    /// we need the alias element to have an array of items.
-    aliases: FxHashMap<String, Vec<IndexItem>>,
-}
-
 /// Temporary storage for data obtained during `RustdocVisitor::clean()`.
 /// Later on moved into `CACHE_KEY`.
 #[derive(Default)]
@@ -594,89 +485,13 @@ pub fn run(mut krate: clean::Crate,
         shared: Arc::new(scx),
     };
 
-    // Crawl the crate to build various caches used for the output
-    let RenderInfo {
-        inlined: _,
-        external_paths,
-        exact_paths,
-        access_levels,
-        deref_trait_did,
-        deref_mut_trait_did,
-        owned_box_did,
-    } = renderinfo;
-
-    let external_paths = external_paths.into_iter()
-        .map(|(k, (v, t))| (k, (v, ItemType::from(t))))
-        .collect();
-
-    let mut cache = Cache {
-        impls: Default::default(),
-        external_paths,
-        exact_paths,
-        paths: Default::default(),
-        implementors: Default::default(),
-        stack: Vec::new(),
-        parent_stack: Vec::new(),
-        search_index: Vec::new(),
-        parent_is_trait_impl: false,
-        extern_locations: Default::default(),
-        primitive_locations: Default::default(),
-        stripped_mod: false,
-        access_levels,
-        crate_version: krate.version.take(),
-        orphan_impl_items: Vec::new(),
-        orphan_trait_impls: Vec::new(),
-        traits: krate.external_traits.replace(Default::default()),
-        deref_trait_did,
-        deref_mut_trait_did,
-        owned_box_did,
-        masked_crates: mem::take(&mut krate.masked_crates),
-        aliases: Default::default(),
-    };
-
-    // Cache where all our extern crates are located
-    for &(n, ref e) in &krate.externs {
-        let src_root = match e.src {
-            FileName::Real(ref p) => match p.parent() {
-                Some(p) => p.to_path_buf(),
-                None => PathBuf::new(),
-            },
-            _ => PathBuf::new(),
-        };
-        let extern_url = extern_html_root_urls.get(&e.name).map(|u| &**u);
-        cache.extern_locations.insert(n, (e.name.clone(), src_root,
-                                          extern_location(e, extern_url, &cx.dst)));
-
-        let did = DefId { krate: n, index: CRATE_DEF_INDEX };
-        cache.external_paths.insert(did, (vec![e.name.to_string()], ItemType::Module));
-    }
-
-    // Cache where all known primitives have their documentation located.
-    //
-    // Favor linking to as local extern as possible, so iterate all crates in
-    // reverse topological order.
-    for &(_, ref e) in krate.externs.iter().rev() {
-        for &(def_id, prim, _) in &e.primitives {
-            cache.primitive_locations.insert(prim, def_id);
-        }
-    }
-    for &(def_id, prim, _) in &krate.primitives {
-        cache.primitive_locations.insert(prim, def_id);
-    }
-
-    cache.stack.push(krate.name.clone());
-    krate = cache.fold_crate(krate);
-
-    for (trait_did, dids, impl_) in cache.orphan_trait_impls.drain(..) {
-        if cache.traits.contains_key(&trait_did) {
-            for did in dids {
-                cache.impls.entry(did).or_insert(vec![]).push(impl_.clone());
-            }
-        }
-    }
-
-    // Build our search index
-    let index = build_index(&krate, &mut cache);
+    let (new_crate, index, cache) = Cache::from_krate(
+        renderinfo,
+        &extern_html_root_urls,
+        &cx.dst,
+        krate,
+    );
+    krate = new_crate;
 
     // Freeze the cache now that the index has been built. Put an Arc into TLS
     // for future parallelization opportunities
@@ -701,76 +516,6 @@ pub fn run(mut krate: clean::Crate,
     }
 }
 
-/// Builds the search index from the collected metadata
-fn build_index(krate: &clean::Crate, cache: &mut Cache) -> String {
-    let mut nodeid_to_pathid = FxHashMap::default();
-    let mut crate_items = Vec::with_capacity(cache.search_index.len());
-    let mut crate_paths = Vec::<Json>::new();
-
-    let Cache { ref mut search_index,
-                ref orphan_impl_items,
-                ref mut paths, .. } = *cache;
-
-    // Attach all orphan items to the type's definition if the type
-    // has since been learned.
-    for &(did, ref item) in orphan_impl_items {
-        if let Some(&(ref fqp, _)) = paths.get(&did) {
-            search_index.push(IndexItem {
-                ty: item.type_(),
-                name: item.name.clone().unwrap(),
-                path: fqp[..fqp.len() - 1].join("::"),
-                desc: shorten(plain_summary_line(item.doc_value())),
-                parent: Some(did),
-                parent_idx: None,
-                search_type: get_index_search_type(&item),
-            });
-        }
-    }
-
-    // Reduce `NodeId` in paths into smaller sequential numbers,
-    // and prune the paths that do not appear in the index.
-    let mut lastpath = String::new();
-    let mut lastpathid = 0usize;
-
-    for item in search_index {
-        item.parent_idx = item.parent.map(|nodeid| {
-            if nodeid_to_pathid.contains_key(&nodeid) {
-                *nodeid_to_pathid.get(&nodeid).unwrap()
-            } else {
-                let pathid = lastpathid;
-                nodeid_to_pathid.insert(nodeid, pathid);
-                lastpathid += 1;
-
-                let &(ref fqp, short) = paths.get(&nodeid).unwrap();
-                crate_paths.push(((short as usize), fqp.last().unwrap().clone()).to_json());
-                pathid
-            }
-        });
-
-        // Omit the parent path if it is same to that of the prior item.
-        if lastpath == item.path {
-            item.path.clear();
-        } else {
-            lastpath = item.path.clone();
-        }
-        crate_items.push(item.to_json());
-    }
-
-    let crate_doc = krate.module.as_ref().map(|module| {
-        shorten(plain_summary_line(module.doc_value()))
-    }).unwrap_or(String::new());
-
-    let mut crate_data = BTreeMap::new();
-    crate_data.insert("doc".to_owned(), Json::String(crate_doc));
-    crate_data.insert("i".to_owned(), Json::Array(crate_items));
-    crate_data.insert("p".to_owned(), Json::Array(crate_paths));
-
-    // Collect the index into a string
-    format!("searchIndex[{}] = {};",
-            as_json(&krate.name),
-            Json::Object(crate_data))
-}
-
 fn write_shared(
     cx: &Context,
     krate: &clean::Crate,
@@ -1327,327 +1072,6 @@ fn minify_replacer(
     }
 }
 
-/// Attempts to find where an external crate is located, given that we're
-/// rendering in to the specified source destination.
-fn extern_location(e: &clean::ExternalCrate, extern_url: Option<&str>, dst: &Path)
-    -> ExternalLocation
-{
-    // See if there's documentation generated into the local directory
-    let local_location = dst.join(&e.name);
-    if local_location.is_dir() {
-        return Local;
-    }
-
-    if let Some(url) = extern_url {
-        let mut url = url.to_string();
-        if !url.ends_with("/") {
-            url.push('/');
-        }
-        return Remote(url);
-    }
-
-    // Failing that, see if there's an attribute specifying where to find this
-    // external crate
-    e.attrs.lists(sym::doc)
-     .filter(|a| a.check_name(sym::html_root_url))
-     .filter_map(|a| a.value_str())
-     .map(|url| {
-        let mut url = url.to_string();
-        if !url.ends_with("/") {
-            url.push('/')
-        }
-        Remote(url)
-    }).next().unwrap_or(Unknown) // Well, at least we tried.
-}
-
-impl DocFolder for Cache {
-    fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
-        if item.def_id.is_local() {
-            debug!("folding {} \"{:?}\", id {:?}", item.type_(), item.name, item.def_id);
-        }
-
-        // If this is a stripped module,
-        // we don't want it or its children in the search index.
-        let orig_stripped_mod = match item.inner {
-            clean::StrippedItem(box clean::ModuleItem(..)) => {
-                mem::replace(&mut self.stripped_mod, true)
-            }
-            _ => self.stripped_mod,
-        };
-
-        // If the impl is from a masked crate or references something from a
-        // masked crate then remove it completely.
-        if let clean::ImplItem(ref i) = item.inner {
-            if self.masked_crates.contains(&item.def_id.krate) ||
-               i.trait_.def_id().map_or(false, |d| self.masked_crates.contains(&d.krate)) ||
-               i.for_.def_id().map_or(false, |d| self.masked_crates.contains(&d.krate)) {
-                return None;
-            }
-        }
-
-        // Propagate a trait method's documentation to all implementors of the
-        // trait.
-        if let clean::TraitItem(ref t) = item.inner {
-            self.traits.entry(item.def_id).or_insert_with(|| t.clone());
-        }
-
-        // Collect all the implementors of traits.
-        if let clean::ImplItem(ref i) = item.inner {
-            if let Some(did) = i.trait_.def_id() {
-                if i.blanket_impl.is_none() {
-                    self.implementors.entry(did).or_default().push(Impl {
-                        impl_item: item.clone(),
-                    });
-                }
-            }
-        }
-
-        // Index this method for searching later on.
-        if let Some(ref s) = item.name {
-            let (parent, is_inherent_impl_item) = match item.inner {
-                clean::StrippedItem(..) => ((None, None), false),
-                clean::AssocConstItem(..) |
-                clean::TypedefItem(_, true) if self.parent_is_trait_impl => {
-                    // skip associated items in trait impls
-                    ((None, None), false)
-                }
-                clean::AssocTypeItem(..) |
-                clean::TyMethodItem(..) |
-                clean::StructFieldItem(..) |
-                clean::VariantItem(..) => {
-                    ((Some(*self.parent_stack.last().unwrap()),
-                      Some(&self.stack[..self.stack.len() - 1])),
-                     false)
-                }
-                clean::MethodItem(..) | clean::AssocConstItem(..) => {
-                    if self.parent_stack.is_empty() {
-                        ((None, None), false)
-                    } else {
-                        let last = self.parent_stack.last().unwrap();
-                        let did = *last;
-                        let path = match self.paths.get(&did) {
-                            // The current stack not necessarily has correlation
-                            // for where the type was defined. On the other
-                            // hand, `paths` always has the right
-                            // information if present.
-                            Some(&(ref fqp, ItemType::Trait)) |
-                            Some(&(ref fqp, ItemType::Struct)) |
-                            Some(&(ref fqp, ItemType::Union)) |
-                            Some(&(ref fqp, ItemType::Enum)) =>
-                                Some(&fqp[..fqp.len() - 1]),
-                            Some(..) => Some(&*self.stack),
-                            None => None
-                        };
-                        ((Some(*last), path), true)
-                    }
-                }
-                _ => ((None, Some(&*self.stack)), false)
-            };
-
-            match parent {
-                (parent, Some(path)) if is_inherent_impl_item || (!self.stripped_mod) => {
-                    debug_assert!(!item.is_stripped());
-
-                    // A crate has a module at its root, containing all items,
-                    // which should not be indexed. The crate-item itself is
-                    // inserted later on when serializing the search-index.
-                    if item.def_id.index != CRATE_DEF_INDEX {
-                        self.search_index.push(IndexItem {
-                            ty: item.type_(),
-                            name: s.to_string(),
-                            path: path.join("::"),
-                            desc: shorten(plain_summary_line(item.doc_value())),
-                            parent,
-                            parent_idx: None,
-                            search_type: get_index_search_type(&item),
-                        });
-                    }
-                }
-                (Some(parent), None) if is_inherent_impl_item => {
-                    // We have a parent, but we don't know where they're
-                    // defined yet. Wait for later to index this item.
-                    self.orphan_impl_items.push((parent, item.clone()));
-                }
-                _ => {}
-            }
-        }
-
-        // Keep track of the fully qualified path for this item.
-        let pushed = match item.name {
-            Some(ref n) if !n.is_empty() => {
-                self.stack.push(n.to_string());
-                true
-            }
-            _ => false,
-        };
-
-        match item.inner {
-            clean::StructItem(..) | clean::EnumItem(..) |
-            clean::TypedefItem(..) | clean::TraitItem(..) |
-            clean::FunctionItem(..) | clean::ModuleItem(..) |
-            clean::ForeignFunctionItem(..) | clean::ForeignStaticItem(..) |
-            clean::ConstantItem(..) | clean::StaticItem(..) |
-            clean::UnionItem(..) | clean::ForeignTypeItem |
-            clean::MacroItem(..) | clean::ProcMacroItem(..)
-            if !self.stripped_mod => {
-                // Re-exported items mean that the same id can show up twice
-                // in the rustdoc ast that we're looking at. We know,
-                // however, that a re-exported item doesn't show up in the
-                // `public_items` map, so we can skip inserting into the
-                // paths map if there was already an entry present and we're
-                // not a public item.
-                if !self.paths.contains_key(&item.def_id) ||
-                   self.access_levels.is_public(item.def_id)
-                {
-                    self.paths.insert(item.def_id,
-                                      (self.stack.clone(), item.type_()));
-                }
-                self.add_aliases(&item);
-            }
-            // Link variants to their parent enum because pages aren't emitted
-            // for each variant.
-            clean::VariantItem(..) if !self.stripped_mod => {
-                let mut stack = self.stack.clone();
-                stack.pop();
-                self.paths.insert(item.def_id, (stack, ItemType::Enum));
-            }
-
-            clean::PrimitiveItem(..) => {
-                self.add_aliases(&item);
-                self.paths.insert(item.def_id, (self.stack.clone(),
-                                                item.type_()));
-            }
-
-            _ => {}
-        }
-
-        // Maintain the parent stack
-        let orig_parent_is_trait_impl = self.parent_is_trait_impl;
-        let parent_pushed = match item.inner {
-            clean::TraitItem(..) | clean::EnumItem(..) | clean::ForeignTypeItem |
-            clean::StructItem(..) | clean::UnionItem(..) => {
-                self.parent_stack.push(item.def_id);
-                self.parent_is_trait_impl = false;
-                true
-            }
-            clean::ImplItem(ref i) => {
-                self.parent_is_trait_impl = i.trait_.is_some();
-                match i.for_ {
-                    clean::ResolvedPath{ did, .. } => {
-                        self.parent_stack.push(did);
-                        true
-                    }
-                    ref t => {
-                        let prim_did = t.primitive_type().and_then(|t| {
-                            self.primitive_locations.get(&t).cloned()
-                        });
-                        match prim_did {
-                            Some(did) => {
-                                self.parent_stack.push(did);
-                                true
-                            }
-                            None => false,
-                        }
-                    }
-                }
-            }
-            _ => false
-        };
-
-        // Once we've recursively found all the generics, hoard off all the
-        // implementations elsewhere.
-        let ret = self.fold_item_recur(item).and_then(|item| {
-            if let clean::Item { inner: clean::ImplItem(_), .. } = item {
-                // Figure out the id of this impl. This may map to a
-                // primitive rather than always to a struct/enum.
-                // Note: matching twice to restrict the lifetime of the `i` borrow.
-                let mut dids = FxHashSet::default();
-                if let clean::Item { inner: clean::ImplItem(ref i), .. } = item {
-                    match i.for_ {
-                        clean::ResolvedPath { did, .. } |
-                        clean::BorrowedRef {
-                            type_: box clean::ResolvedPath { did, .. }, ..
-                        } => {
-                            dids.insert(did);
-                        }
-                        ref t => {
-                            let did = t.primitive_type().and_then(|t| {
-                                self.primitive_locations.get(&t).cloned()
-                            });
-
-                            if let Some(did) = did {
-                                dids.insert(did);
-                            }
-                        }
-                    }
-
-                    if let Some(generics) = i.trait_.as_ref().and_then(|t| t.generics()) {
-                        for bound in generics {
-                            if let Some(did) = bound.def_id() {
-                                dids.insert(did);
-                            }
-                        }
-                    }
-                } else {
-                    unreachable!()
-                };
-                let impl_item = Impl {
-                    impl_item: item,
-                };
-                if impl_item.trait_did().map_or(true, |d| self.traits.contains_key(&d)) {
-                    for did in dids {
-                        self.impls.entry(did).or_insert(vec![]).push(impl_item.clone());
-                    }
-                } else {
-                    let trait_did = impl_item.trait_did().unwrap();
-                    self.orphan_trait_impls.push((trait_did, dids, impl_item));
-                }
-                None
-            } else {
-                Some(item)
-            }
-        });
-
-        if pushed { self.stack.pop().unwrap(); }
-        if parent_pushed { self.parent_stack.pop().unwrap(); }
-        self.stripped_mod = orig_stripped_mod;
-        self.parent_is_trait_impl = orig_parent_is_trait_impl;
-        ret
-    }
-}
-
-impl Cache {
-    fn add_aliases(&mut self, item: &clean::Item) {
-        if item.def_id.index == CRATE_DEF_INDEX {
-            return
-        }
-        if let Some(ref item_name) = item.name {
-            let path = self.paths.get(&item.def_id)
-                                 .map(|p| p.0[..p.0.len() - 1].join("::"))
-                                 .unwrap_or("std".to_owned());
-            for alias in item.attrs.lists(sym::doc)
-                                   .filter(|a| a.check_name(sym::alias))
-                                   .filter_map(|a| a.value_str()
-                                                    .map(|s| s.to_string().replace("\"", "")))
-                                   .filter(|v| !v.is_empty())
-                                   .collect::<FxHashSet<_>>()
-                                   .into_iter() {
-                self.aliases.entry(alias)
-                            .or_insert(Vec::with_capacity(1))
-                            .push(IndexItem {
-                                ty: item.type_(),
-                                name: item_name.to_string(),
-                                path: path.clone(),
-                                desc: shorten(plain_summary_line(item.doc_value())),
-                                parent: None,
-                                parent_idx: None,
-                                search_type: get_index_search_type(&item),
-                            });
-            }
-        }
-    }
-}
-
 #[derive(Debug, Eq, PartialEq, Hash)]
 struct ItemEntry {
     url: String,
@@ -4805,37 +4229,6 @@ fn make_item_keywords(it: &clean::Item) -> String {
     format!("{}, {}", BASIC_KEYWORDS, it.name.as_ref().unwrap())
 }
 
-fn get_index_search_type(item: &clean::Item) -> Option<IndexItemFunctionType> {
-    let (all_types, ret_types) = match item.inner {
-        clean::FunctionItem(ref f) => (&f.all_types, &f.ret_types),
-        clean::MethodItem(ref m) => (&m.all_types, &m.ret_types),
-        clean::TyMethodItem(ref m) => (&m.all_types, &m.ret_types),
-        _ => return None,
-    };
-
-    let inputs = all_types.iter().map(|arg| {
-        get_index_type(&arg)
-    }).filter(|a| a.name.is_some()).collect();
-    let output = ret_types.iter().map(|arg| {
-        get_index_type(&arg)
-    }).filter(|a| a.name.is_some()).collect::<Vec<_>>();
-    let output = if output.is_empty() {
-        None
-    } else {
-        Some(output)
-    };
-
-    Some(IndexItemFunctionType { inputs, output })
-}
-
-fn get_index_type(clean_type: &clean::Type) -> Type {
-    let t = Type {
-        name: get_index_type_name(clean_type, true).map(|s| s.to_ascii_lowercase()),
-        generics: get_generics(clean_type),
-    };
-    t
-}
-
 /// Returns a list of all paths used in the type.
 /// This is used to help deduplicate imported impls
 /// for reexported types. If any of the contained
@@ -4893,39 +4286,6 @@ fn collect_paths_for_type(first_ty: clean::Type) -> Vec<String> {
     out
 }
 
-fn get_index_type_name(clean_type: &clean::Type, accept_generic: bool) -> Option<String> {
-    match *clean_type {
-        clean::ResolvedPath { ref path, .. } => {
-            let segments = &path.segments;
-            let path_segment = segments.into_iter().last().unwrap_or_else(|| panic!(
-                "get_index_type_name(clean_type: {:?}, accept_generic: {:?}) had length zero path",
-                clean_type, accept_generic
-            ));
-            Some(path_segment.name.clone())
-        }
-        clean::Generic(ref s) if accept_generic => Some(s.clone()),
-        clean::Primitive(ref p) => Some(format!("{:?}", p)),
-        clean::BorrowedRef { ref type_, .. } => get_index_type_name(type_, accept_generic),
-        // FIXME: add all from clean::Type.
-        _ => None
-    }
-}
-
-fn get_generics(clean_type: &clean::Type) -> Option<Vec<String>> {
-    clean_type.generics()
-              .and_then(|types| {
-                  let r = types.iter()
-                               .filter_map(|t| get_index_type_name(t, false))
-                               .map(|s| s.to_ascii_lowercase())
-                               .collect::<Vec<_>>();
-                  if r.is_empty() {
-                      None
-                  } else {
-                      Some(r)
-                  }
-              })
-}
-
-pub fn cache() -> Arc<Cache> {
+crate fn cache() -> Arc<Cache> {
     CACHE_KEY.with(|c| c.borrow().clone())
 }
diff --git a/src/librustdoc/html/render/cache.rs b/src/librustdoc/html/render/cache.rs
new file mode 100644
index 0000000000000..e9f0955c54126
--- /dev/null
+++ b/src/librustdoc/html/render/cache.rs
@@ -0,0 +1,675 @@
+use crate::clean::{self, GetDefId, AttributesExt};
+use crate::fold::DocFolder;
+use rustc::hir::def_id::{CrateNum, CRATE_DEF_INDEX, DefId};
+use rustc::middle::privacy::AccessLevels;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use std::mem;
+use std::path::{Path, PathBuf};
+use std::collections::BTreeMap;
+use syntax::source_map::FileName;
+use syntax::symbol::sym;
+use serialize::json::{ToJson, Json, as_json};
+
+use super::{ItemType, IndexItem, IndexItemFunctionType, Impl, shorten, plain_summary_line};
+use super::{Type, RenderInfo};
+
+/// Indicates where an external crate can be found.
+pub enum ExternalLocation {
+    /// Remote URL root of the external crate
+    Remote(String),
+    /// This external crate can be found in the local doc/ folder
+    Local,
+    /// The external crate could not be found.
+    Unknown,
+}
+
+/// This cache is used to store information about the `clean::Crate` being
+/// rendered in order to provide more useful documentation. This contains
+/// information like all implementors of a trait, all traits a type implements,
+/// documentation for all known traits, etc.
+///
+/// This structure purposefully does not implement `Clone` because it's intended
+/// to be a fairly large and expensive structure to clone. Instead this adheres
+/// to `Send` so it may be stored in a `Arc` instance and shared among the various
+/// rendering threads.
+#[derive(Default)]
+crate struct Cache {
+    /// Maps a type ID to all known implementations for that type. This is only
+    /// recognized for intra-crate `ResolvedPath` types, and is used to print
+    /// out extra documentation on the page of an enum/struct.
+    ///
+    /// The values of the map are a list of implementations and documentation
+    /// found on that implementation.
+    pub impls: FxHashMap<DefId, Vec<Impl>>,
+
+    /// Maintains a mapping of local crate `NodeId`s to the fully qualified name
+    /// and "short type description" of that node. This is used when generating
+    /// URLs when a type is being linked to. External paths are not located in
+    /// this map because the `External` type itself has all the information
+    /// necessary.
+    pub paths: FxHashMap<DefId, (Vec<String>, ItemType)>,
+
+    /// Similar to `paths`, but only holds external paths. This is only used for
+    /// generating explicit hyperlinks to other crates.
+    pub external_paths: FxHashMap<DefId, (Vec<String>, ItemType)>,
+
+    /// Maps local `DefId`s of exported types to fully qualified paths.
+    /// Unlike 'paths', this mapping ignores any renames that occur
+    /// due to 'use' statements.
+    ///
+    /// This map is used when writing out the special 'implementors'
+    /// javascript file. By using the exact path that the type
+    /// is declared with, we ensure that each path will be identical
+    /// to the path used if the corresponding type is inlined. By
+    /// doing this, we can detect duplicate impls on a trait page, and only display
+    /// the impl for the inlined type.
+    pub exact_paths: FxHashMap<DefId, Vec<String>>,
+
+    /// This map contains information about all known traits of this crate.
+    /// Implementations of a crate should inherit the documentation of the
+    /// parent trait if no extra documentation is specified, and default methods
+    /// should show up in documentation about trait implementations.
+    pub traits: FxHashMap<DefId, clean::Trait>,
+
+    /// When rendering traits, it's often useful to be able to list all
+    /// implementors of the trait, and this mapping is exactly, that: a mapping
+    /// of trait ids to the list of known implementors of the trait
+    pub implementors: FxHashMap<DefId, Vec<Impl>>,
+
+    /// Cache of where external crate documentation can be found.
+    pub extern_locations: FxHashMap<CrateNum, (String, PathBuf, ExternalLocation)>,
+
+    /// Cache of where documentation for primitives can be found.
+    pub primitive_locations: FxHashMap<clean::PrimitiveType, DefId>,
+
+    // Note that external items for which `doc(hidden)` applies to are shown as
+    // non-reachable while local items aren't. This is because we're reusing
+    // the access levels from the privacy check pass.
+    pub access_levels: AccessLevels<DefId>,
+
+    /// The version of the crate being documented, if given from the `--crate-version` flag.
+    pub crate_version: Option<String>,
+
+    // Private fields only used when initially crawling a crate to build a cache
+
+    stack: Vec<String>,
+    parent_stack: Vec<DefId>,
+    parent_is_trait_impl: bool,
+    search_index: Vec<IndexItem>,
+    stripped_mod: bool,
+    pub deref_trait_did: Option<DefId>,
+    pub deref_mut_trait_did: Option<DefId>,
+    pub owned_box_did: Option<DefId>,
+    masked_crates: FxHashSet<CrateNum>,
+
+    // In rare case where a structure is defined in one module but implemented
+    // in another, if the implementing module is parsed before defining module,
+    // then the fully qualified name of the structure isn't presented in `paths`
+    // yet when its implementation methods are being indexed. Caches such methods
+    // and their parent id here and indexes them at the end of crate parsing.
+    orphan_impl_items: Vec<(DefId, clean::Item)>,
+
+    // Similarly to `orphan_impl_items`, sometimes trait impls are picked up
+    // even though the trait itself is not exported. This can happen if a trait
+    // was defined in function/expression scope, since the impl will be picked
+    // up by `collect-trait-impls` but the trait won't be scraped out in the HIR
+    // crawl. In order to prevent crashes when looking for spotlight traits or
+    // when gathering trait documentation on a type, hold impls here while
+    // folding and add them to the cache later on if we find the trait.
+    orphan_trait_impls: Vec<(DefId, FxHashSet<DefId>, Impl)>,
+
+    /// Aliases added through `#[doc(alias = "...")]`. Since a few items can have the same alias,
+    /// we need the alias element to have an array of items.
+    pub(super) aliases: FxHashMap<String, Vec<IndexItem>>,
+}
+
+impl Cache {
+    pub fn from_krate(
+        renderinfo: RenderInfo,
+        extern_html_root_urls: &BTreeMap<String, String>,
+        dst: &Path,
+        mut krate: clean::Crate,
+    ) -> (clean::Crate, String, Cache) {
+        // Crawl the crate to build various caches used for the output
+        let RenderInfo {
+            inlined: _,
+            external_paths,
+            exact_paths,
+            access_levels,
+            deref_trait_did,
+            deref_mut_trait_did,
+            owned_box_did,
+        } = renderinfo;
+
+        let external_paths = external_paths.into_iter()
+            .map(|(k, (v, t))| (k, (v, ItemType::from(t))))
+            .collect();
+
+        let mut cache = Cache {
+            impls: Default::default(),
+            external_paths,
+            exact_paths,
+            paths: Default::default(),
+            implementors: Default::default(),
+            stack: Vec::new(),
+            parent_stack: Vec::new(),
+            search_index: Vec::new(),
+            parent_is_trait_impl: false,
+            extern_locations: Default::default(),
+            primitive_locations: Default::default(),
+            stripped_mod: false,
+            access_levels,
+            crate_version: krate.version.take(),
+            orphan_impl_items: Vec::new(),
+            orphan_trait_impls: Vec::new(),
+            traits: krate.external_traits.replace(Default::default()),
+            deref_trait_did,
+            deref_mut_trait_did,
+            owned_box_did,
+            masked_crates: mem::take(&mut krate.masked_crates),
+            aliases: Default::default(),
+        };
+
+        // Cache where all our extern crates are located
+        for &(n, ref e) in &krate.externs {
+            let src_root = match e.src {
+                FileName::Real(ref p) => match p.parent() {
+                    Some(p) => p.to_path_buf(),
+                    None => PathBuf::new(),
+                },
+                _ => PathBuf::new(),
+            };
+            let extern_url = extern_html_root_urls.get(&e.name).map(|u| &**u);
+            cache.extern_locations.insert(n, (e.name.clone(), src_root,
+                                            extern_location(e, extern_url, &dst)));
+
+            let did = DefId { krate: n, index: CRATE_DEF_INDEX };
+            cache.external_paths.insert(did, (vec![e.name.to_string()], ItemType::Module));
+        }
+
+        // Cache where all known primitives have their documentation located.
+        //
+        // Favor linking to as local extern as possible, so iterate all crates in
+        // reverse topological order.
+        for &(_, ref e) in krate.externs.iter().rev() {
+            for &(def_id, prim, _) in &e.primitives {
+                cache.primitive_locations.insert(prim, def_id);
+            }
+        }
+        for &(def_id, prim, _) in &krate.primitives {
+            cache.primitive_locations.insert(prim, def_id);
+        }
+
+        cache.stack.push(krate.name.clone());
+        krate = cache.fold_crate(krate);
+
+        for (trait_did, dids, impl_) in cache.orphan_trait_impls.drain(..) {
+            if cache.traits.contains_key(&trait_did) {
+                for did in dids {
+                    cache.impls.entry(did).or_insert(vec![]).push(impl_.clone());
+                }
+            }
+        }
+
+        // Build our search index
+        let index = build_index(&krate, &mut cache);
+
+        (krate, index, cache)
+    }
+}
+
+impl DocFolder for Cache {
+    fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
+        if item.def_id.is_local() {
+            debug!("folding {} \"{:?}\", id {:?}", item.type_(), item.name, item.def_id);
+        }
+
+        // If this is a stripped module,
+        // we don't want it or its children in the search index.
+        let orig_stripped_mod = match item.inner {
+            clean::StrippedItem(box clean::ModuleItem(..)) => {
+                mem::replace(&mut self.stripped_mod, true)
+            }
+            _ => self.stripped_mod,
+        };
+
+        // If the impl is from a masked crate or references something from a
+        // masked crate then remove it completely.
+        if let clean::ImplItem(ref i) = item.inner {
+            if self.masked_crates.contains(&item.def_id.krate) ||
+               i.trait_.def_id().map_or(false, |d| self.masked_crates.contains(&d.krate)) ||
+               i.for_.def_id().map_or(false, |d| self.masked_crates.contains(&d.krate)) {
+                return None;
+            }
+        }
+
+        // Propagate a trait method's documentation to all implementors of the
+        // trait.
+        if let clean::TraitItem(ref t) = item.inner {
+            self.traits.entry(item.def_id).or_insert_with(|| t.clone());
+        }
+
+        // Collect all the implementors of traits.
+        if let clean::ImplItem(ref i) = item.inner {
+            if let Some(did) = i.trait_.def_id() {
+                if i.blanket_impl.is_none() {
+                    self.implementors.entry(did).or_default().push(Impl {
+                        impl_item: item.clone(),
+                    });
+                }
+            }
+        }
+
+        // Index this method for searching later on.
+        if let Some(ref s) = item.name {
+            let (parent, is_inherent_impl_item) = match item.inner {
+                clean::StrippedItem(..) => ((None, None), false),
+                clean::AssocConstItem(..) |
+                clean::TypedefItem(_, true) if self.parent_is_trait_impl => {
+                    // skip associated items in trait impls
+                    ((None, None), false)
+                }
+                clean::AssocTypeItem(..) |
+                clean::TyMethodItem(..) |
+                clean::StructFieldItem(..) |
+                clean::VariantItem(..) => {
+                    ((Some(*self.parent_stack.last().unwrap()),
+                      Some(&self.stack[..self.stack.len() - 1])),
+                     false)
+                }
+                clean::MethodItem(..) | clean::AssocConstItem(..) => {
+                    if self.parent_stack.is_empty() {
+                        ((None, None), false)
+                    } else {
+                        let last = self.parent_stack.last().unwrap();
+                        let did = *last;
+                        let path = match self.paths.get(&did) {
+                            // The current stack not necessarily has correlation
+                            // for where the type was defined. On the other
+                            // hand, `paths` always has the right
+                            // information if present.
+                            Some(&(ref fqp, ItemType::Trait)) |
+                            Some(&(ref fqp, ItemType::Struct)) |
+                            Some(&(ref fqp, ItemType::Union)) |
+                            Some(&(ref fqp, ItemType::Enum)) =>
+                                Some(&fqp[..fqp.len() - 1]),
+                            Some(..) => Some(&*self.stack),
+                            None => None
+                        };
+                        ((Some(*last), path), true)
+                    }
+                }
+                _ => ((None, Some(&*self.stack)), false)
+            };
+
+            match parent {
+                (parent, Some(path)) if is_inherent_impl_item || (!self.stripped_mod) => {
+                    debug_assert!(!item.is_stripped());
+
+                    // A crate has a module at its root, containing all items,
+                    // which should not be indexed. The crate-item itself is
+                    // inserted later on when serializing the search-index.
+                    if item.def_id.index != CRATE_DEF_INDEX {
+                        self.search_index.push(IndexItem {
+                            ty: item.type_(),
+                            name: s.to_string(),
+                            path: path.join("::"),
+                            desc: shorten(plain_summary_line(item.doc_value())),
+                            parent,
+                            parent_idx: None,
+                            search_type: get_index_search_type(&item),
+                        });
+                    }
+                }
+                (Some(parent), None) if is_inherent_impl_item => {
+                    // We have a parent, but we don't know where they're
+                    // defined yet. Wait for later to index this item.
+                    self.orphan_impl_items.push((parent, item.clone()));
+                }
+                _ => {}
+            }
+        }
+
+        // Keep track of the fully qualified path for this item.
+        let pushed = match item.name {
+            Some(ref n) if !n.is_empty() => {
+                self.stack.push(n.to_string());
+                true
+            }
+            _ => false,
+        };
+
+        match item.inner {
+            clean::StructItem(..) | clean::EnumItem(..) |
+            clean::TypedefItem(..) | clean::TraitItem(..) |
+            clean::FunctionItem(..) | clean::ModuleItem(..) |
+            clean::ForeignFunctionItem(..) | clean::ForeignStaticItem(..) |
+            clean::ConstantItem(..) | clean::StaticItem(..) |
+            clean::UnionItem(..) | clean::ForeignTypeItem |
+            clean::MacroItem(..) | clean::ProcMacroItem(..)
+            if !self.stripped_mod => {
+                // Re-exported items mean that the same id can show up twice
+                // in the rustdoc ast that we're looking at. We know,
+                // however, that a re-exported item doesn't show up in the
+                // `public_items` map, so we can skip inserting into the
+                // paths map if there was already an entry present and we're
+                // not a public item.
+                if !self.paths.contains_key(&item.def_id) ||
+                   self.access_levels.is_public(item.def_id)
+                {
+                    self.paths.insert(item.def_id,
+                                      (self.stack.clone(), item.type_()));
+                }
+                self.add_aliases(&item);
+            }
+            // Link variants to their parent enum because pages aren't emitted
+            // for each variant.
+            clean::VariantItem(..) if !self.stripped_mod => {
+                let mut stack = self.stack.clone();
+                stack.pop();
+                self.paths.insert(item.def_id, (stack, ItemType::Enum));
+            }
+
+            clean::PrimitiveItem(..) => {
+                self.add_aliases(&item);
+                self.paths.insert(item.def_id, (self.stack.clone(),
+                                                item.type_()));
+            }
+
+            _ => {}
+        }
+
+        // Maintain the parent stack
+        let orig_parent_is_trait_impl = self.parent_is_trait_impl;
+        let parent_pushed = match item.inner {
+            clean::TraitItem(..) | clean::EnumItem(..) | clean::ForeignTypeItem |
+            clean::StructItem(..) | clean::UnionItem(..) => {
+                self.parent_stack.push(item.def_id);
+                self.parent_is_trait_impl = false;
+                true
+            }
+            clean::ImplItem(ref i) => {
+                self.parent_is_trait_impl = i.trait_.is_some();
+                match i.for_ {
+                    clean::ResolvedPath{ did, .. } => {
+                        self.parent_stack.push(did);
+                        true
+                    }
+                    ref t => {
+                        let prim_did = t.primitive_type().and_then(|t| {
+                            self.primitive_locations.get(&t).cloned()
+                        });
+                        match prim_did {
+                            Some(did) => {
+                                self.parent_stack.push(did);
+                                true
+                            }
+                            None => false,
+                        }
+                    }
+                }
+            }
+            _ => false
+        };
+
+        // Once we've recursively found all the generics, hoard off all the
+        // implementations elsewhere.
+        let ret = self.fold_item_recur(item).and_then(|item| {
+            if let clean::Item { inner: clean::ImplItem(_), .. } = item {
+                // Figure out the id of this impl. This may map to a
+                // primitive rather than always to a struct/enum.
+                // Note: matching twice to restrict the lifetime of the `i` borrow.
+                let mut dids = FxHashSet::default();
+                if let clean::Item { inner: clean::ImplItem(ref i), .. } = item {
+                    match i.for_ {
+                        clean::ResolvedPath { did, .. } |
+                        clean::BorrowedRef {
+                            type_: box clean::ResolvedPath { did, .. }, ..
+                        } => {
+                            dids.insert(did);
+                        }
+                        ref t => {
+                            let did = t.primitive_type().and_then(|t| {
+                                self.primitive_locations.get(&t).cloned()
+                            });
+
+                            if let Some(did) = did {
+                                dids.insert(did);
+                            }
+                        }
+                    }
+
+                    if let Some(generics) = i.trait_.as_ref().and_then(|t| t.generics()) {
+                        for bound in generics {
+                            if let Some(did) = bound.def_id() {
+                                dids.insert(did);
+                            }
+                        }
+                    }
+                } else {
+                    unreachable!()
+                };
+                let impl_item = Impl {
+                    impl_item: item,
+                };
+                if impl_item.trait_did().map_or(true, |d| self.traits.contains_key(&d)) {
+                    for did in dids {
+                        self.impls.entry(did).or_insert(vec![]).push(impl_item.clone());
+                    }
+                } else {
+                    let trait_did = impl_item.trait_did().unwrap();
+                    self.orphan_trait_impls.push((trait_did, dids, impl_item));
+                }
+                None
+            } else {
+                Some(item)
+            }
+        });
+
+        if pushed { self.stack.pop().unwrap(); }
+        if parent_pushed { self.parent_stack.pop().unwrap(); }
+        self.stripped_mod = orig_stripped_mod;
+        self.parent_is_trait_impl = orig_parent_is_trait_impl;
+        ret
+    }
+}
+
+impl Cache {
+    fn add_aliases(&mut self, item: &clean::Item) {
+        if item.def_id.index == CRATE_DEF_INDEX {
+            return
+        }
+        if let Some(ref item_name) = item.name {
+            let path = self.paths.get(&item.def_id)
+                                 .map(|p| p.0[..p.0.len() - 1].join("::"))
+                                 .unwrap_or("std".to_owned());
+            for alias in item.attrs.lists(sym::doc)
+                                   .filter(|a| a.check_name(sym::alias))
+                                   .filter_map(|a| a.value_str()
+                                                    .map(|s| s.to_string().replace("\"", "")))
+                                   .filter(|v| !v.is_empty())
+                                   .collect::<FxHashSet<_>>()
+                                   .into_iter() {
+                self.aliases.entry(alias)
+                            .or_insert(Vec::with_capacity(1))
+                            .push(IndexItem {
+                                ty: item.type_(),
+                                name: item_name.to_string(),
+                                path: path.clone(),
+                                desc: shorten(plain_summary_line(item.doc_value())),
+                                parent: None,
+                                parent_idx: None,
+                                search_type: get_index_search_type(&item),
+                            });
+            }
+        }
+    }
+}
+
+/// Attempts to find where an external crate is located, given that we're
+/// rendering in to the specified source destination.
+fn extern_location(e: &clean::ExternalCrate, extern_url: Option<&str>, dst: &Path)
+    -> ExternalLocation
+{
+    use ExternalLocation::*;
+    // See if there's documentation generated into the local directory
+    let local_location = dst.join(&e.name);
+    if local_location.is_dir() {
+        return Local;
+    }
+
+    if let Some(url) = extern_url {
+        let mut url = url.to_string();
+        if !url.ends_with("/") {
+            url.push('/');
+        }
+        return Remote(url);
+    }
+
+    // Failing that, see if there's an attribute specifying where to find this
+    // external crate
+    e.attrs.lists(sym::doc)
+     .filter(|a| a.check_name(sym::html_root_url))
+     .filter_map(|a| a.value_str())
+     .map(|url| {
+        let mut url = url.to_string();
+        if !url.ends_with("/") {
+            url.push('/')
+        }
+        Remote(url)
+    }).next().unwrap_or(Unknown) // Well, at least we tried.
+}
+
+/// Builds the search index from the collected metadata
+fn build_index(krate: &clean::Crate, cache: &mut Cache) -> String {
+    let mut nodeid_to_pathid = FxHashMap::default();
+    let mut crate_items = Vec::with_capacity(cache.search_index.len());
+    let mut crate_paths = Vec::<Json>::new();
+
+    let Cache { ref mut search_index,
+                ref orphan_impl_items,
+                ref mut paths, .. } = *cache;
+
+    // Attach all orphan items to the type's definition if the type
+    // has since been learned.
+    for &(did, ref item) in orphan_impl_items {
+        if let Some(&(ref fqp, _)) = paths.get(&did) {
+            search_index.push(IndexItem {
+                ty: item.type_(),
+                name: item.name.clone().unwrap(),
+                path: fqp[..fqp.len() - 1].join("::"),
+                desc: shorten(plain_summary_line(item.doc_value())),
+                parent: Some(did),
+                parent_idx: None,
+                search_type: get_index_search_type(&item),
+            });
+        }
+    }
+
+    // Reduce `NodeId` in paths into smaller sequential numbers,
+    // and prune the paths that do not appear in the index.
+    let mut lastpath = String::new();
+    let mut lastpathid = 0usize;
+
+    for item in search_index {
+        item.parent_idx = item.parent.map(|nodeid| {
+            if nodeid_to_pathid.contains_key(&nodeid) {
+                *nodeid_to_pathid.get(&nodeid).unwrap()
+            } else {
+                let pathid = lastpathid;
+                nodeid_to_pathid.insert(nodeid, pathid);
+                lastpathid += 1;
+
+                let &(ref fqp, short) = paths.get(&nodeid).unwrap();
+                crate_paths.push(((short as usize), fqp.last().unwrap().clone()).to_json());
+                pathid
+            }
+        });
+
+        // Omit the parent path if it is same to that of the prior item.
+        if lastpath == item.path {
+            item.path.clear();
+        } else {
+            lastpath = item.path.clone();
+        }
+        crate_items.push(item.to_json());
+    }
+
+    let crate_doc = krate.module.as_ref().map(|module| {
+        shorten(plain_summary_line(module.doc_value()))
+    }).unwrap_or(String::new());
+
+    let mut crate_data = BTreeMap::new();
+    crate_data.insert("doc".to_owned(), Json::String(crate_doc));
+    crate_data.insert("i".to_owned(), Json::Array(crate_items));
+    crate_data.insert("p".to_owned(), Json::Array(crate_paths));
+
+    // Collect the index into a string
+    format!("searchIndex[{}] = {};",
+            as_json(&krate.name),
+            Json::Object(crate_data))
+}
+
+fn get_index_search_type(item: &clean::Item) -> Option<IndexItemFunctionType> {
+    let (all_types, ret_types) = match item.inner {
+        clean::FunctionItem(ref f) => (&f.all_types, &f.ret_types),
+        clean::MethodItem(ref m) => (&m.all_types, &m.ret_types),
+        clean::TyMethodItem(ref m) => (&m.all_types, &m.ret_types),
+        _ => return None,
+    };
+
+    let inputs = all_types.iter().map(|arg| {
+        get_index_type(&arg)
+    }).filter(|a| a.name.is_some()).collect();
+    let output = ret_types.iter().map(|arg| {
+        get_index_type(&arg)
+    }).filter(|a| a.name.is_some()).collect::<Vec<_>>();
+    let output = if output.is_empty() {
+        None
+    } else {
+        Some(output)
+    };
+
+    Some(IndexItemFunctionType { inputs, output })
+}
+
+fn get_index_type(clean_type: &clean::Type) -> Type {
+    let t = Type {
+        name: get_index_type_name(clean_type, true).map(|s| s.to_ascii_lowercase()),
+        generics: get_generics(clean_type),
+    };
+    t
+}
+
+fn get_index_type_name(clean_type: &clean::Type, accept_generic: bool) -> Option<String> {
+    match *clean_type {
+        clean::ResolvedPath { ref path, .. } => {
+            let segments = &path.segments;
+            let path_segment = segments.into_iter().last().unwrap_or_else(|| panic!(
+                "get_index_type_name(clean_type: {:?}, accept_generic: {:?}) had length zero path",
+                clean_type, accept_generic
+            ));
+            Some(path_segment.name.clone())
+        }
+        clean::Generic(ref s) if accept_generic => Some(s.clone()),
+        clean::Primitive(ref p) => Some(format!("{:?}", p)),
+        clean::BorrowedRef { ref type_, .. } => get_index_type_name(type_, accept_generic),
+        // FIXME: add all from clean::Type.
+        _ => None
+    }
+}
+
+fn get_generics(clean_type: &clean::Type) -> Option<Vec<String>> {
+    clean_type.generics()
+              .and_then(|types| {
+                  let r = types.iter()
+                               .filter_map(|t| get_index_type_name(t, false))
+                               .map(|s| s.to_ascii_lowercase())
+                               .collect::<Vec<_>>();
+                  if r.is_empty() {
+                      None
+                  } else {
+                      Some(r)
+                  }
+              })
+}

From 6e0b0d4d7094d8d63a3c11325ae8c53c9928aab7 Mon Sep 17 00:00:00 2001
From: Mark Rousskov <mark.simulacrum@gmail.com>
Date: Fri, 13 Sep 2019 11:40:51 -0400
Subject: [PATCH 11/18] Move cache into Context, avoid TLS

This doesn't move everything over as cache() is pretty annoying to
remove fully, but it gets the ball rolling.
---
 src/librustdoc/html/render.rs | 55 +++++++++++++++++------------------
 1 file changed, 26 insertions(+), 29 deletions(-)

diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs
index ff7134ab7c0d5..dc9e7131c89de 100644
--- a/src/librustdoc/html/render.rs
+++ b/src/librustdoc/html/render.rs
@@ -167,6 +167,7 @@ struct Context {
     /// The map used to ensure all generated 'id=' attributes are unique.
     id_map: Rc<RefCell<IdMap>>,
     pub shared: Arc<SharedContext>,
+    pub cache: Arc<Cache>,
 }
 
 crate struct SharedContext {
@@ -477,31 +478,31 @@ pub fn run(mut krate: clean::Crate,
     let dst = output;
     scx.ensure_dir(&dst)?;
     krate = sources::render(&dst, &mut scx, krate)?;
+    let (new_crate, index, cache) = Cache::from_krate(
+        renderinfo,
+        &extern_html_root_urls,
+        &dst,
+        krate,
+    );
+    krate = new_crate;
+    let cache = Arc::new(cache);
     let mut cx = Context {
         current: Vec::new(),
         dst,
         render_redirect_pages: false,
         id_map: Rc::new(RefCell::new(id_map)),
         shared: Arc::new(scx),
+        cache: cache.clone(),
     };
 
-    let (new_crate, index, cache) = Cache::from_krate(
-        renderinfo,
-        &extern_html_root_urls,
-        &cx.dst,
-        krate,
-    );
-    krate = new_crate;
-
     // Freeze the cache now that the index has been built. Put an Arc into TLS
     // for future parallelization opportunities
-    let cache = Arc::new(cache);
     CACHE_KEY.with(|v| *v.borrow_mut() = cache.clone());
     CURRENT_DEPTH.with(|s| s.set(0));
 
     // Write shared runs within a flock; disable thread dispatching of IO temporarily.
     Arc::get_mut(&mut cx.shared).unwrap().fs.set_sync_only(true);
-    write_shared(&cx, &krate, &*cache, index, &md_opts, diag)?;
+    write_shared(&cx, &krate, index, &md_opts, diag)?;
     Arc::get_mut(&mut cx.shared).unwrap().fs.set_sync_only(false);
 
     // And finally render the whole crate's documentation
@@ -519,7 +520,6 @@ pub fn run(mut krate: clean::Crate,
 fn write_shared(
     cx: &Context,
     krate: &clean::Crate,
-    cache: &Cache,
     search_index: String,
     options: &RenderOptions,
     diag: &errors::Handler,
@@ -750,7 +750,7 @@ themePicker.onblur = handleThemeButtonsBlur;
     {
         let (mut all_aliases, _, _) = try_err!(collect(&dst, &krate.name, "ALIASES", false), &dst);
         let mut output = String::with_capacity(100);
-        for (alias, items) in &cache.aliases {
+        for (alias, items) in &cx.cache.aliases {
             if items.is_empty() {
                 continue
             }
@@ -920,7 +920,7 @@ themePicker.onblur = handleThemeButtonsBlur;
 
     // Update the list of all implementors for traits
     let dst = cx.dst.join("implementors");
-    for (&did, imps) in &cache.implementors {
+    for (&did, imps) in &cx.cache.implementors {
         // Private modules can leak through to this phase of rustdoc, which
         // could contain implementations for otherwise private types. In some
         // rare cases we could find an implementation for an item which wasn't
@@ -928,9 +928,9 @@ themePicker.onblur = handleThemeButtonsBlur;
         //
         // FIXME: this is a vague explanation for why this can't be a `get`, in
         //        theory it should be...
-        let &(ref remote_path, remote_item_type) = match cache.paths.get(&did) {
+        let &(ref remote_path, remote_item_type) = match cx.cache.paths.get(&did) {
             Some(p) => p,
-            None => match cache.external_paths.get(&did) {
+            None => match cx.cache.external_paths.get(&did) {
                 Some(p) => p,
                 None => continue,
             }
@@ -958,7 +958,7 @@ themePicker.onblur = handleThemeButtonsBlur;
         // Only create a js file if we have impls to add to it. If the trait is
         // documented locally though we always create the file to avoid dead
         // links.
-        if !have_impls && !cache.paths.contains_key(&did) {
+        if !have_impls && !cx.cache.paths.contains_key(&did) {
             continue;
         }
 
@@ -1309,7 +1309,7 @@ impl Context {
             extra_scripts: &[],
             static_extra_scripts: &[],
         };
-        let sidebar = if let Some(ref version) = cache().crate_version {
+        let sidebar = if let Some(ref version) = self.cache.crate_version {
             format!("<p class='location'>Crate {}</p>\
                      <div class='block version'>\
                          <p>Version {}</p>\
@@ -1399,7 +1399,7 @@ impl Context {
                            &self.shared.themes)
         } else {
             let mut url = self.root_path();
-            if let Some(&(ref names, ty)) = cache().paths.get(&it.def_id) {
+            if let Some(&(ref names, ty)) = self.cache.paths.get(&it.def_id) {
                 for name in &names[..names.len() - 1] {
                     url.push_str(name);
                     url.push_str("/");
@@ -1549,7 +1549,6 @@ impl Context {
     fn src_href(&self, item: &clean::Item) -> Option<String> {
         let mut root = self.root_path();
 
-        let cache = cache();
         let mut path = String::new();
 
         // We can safely ignore macros from other libraries
@@ -1565,7 +1564,7 @@ impl Context {
                 return None;
             }
         } else {
-            let (krate, src_root) = match *cache.extern_locations.get(&item.def_id.krate)? {
+            let (krate, src_root) = match *self.cache.extern_locations.get(&item.def_id.krate)? {
                 (ref name, ref src, Local) => (name, src),
                 (ref name, ref src, Remote(ref s)) => {
                     root = s.to_string();
@@ -2475,11 +2474,9 @@ fn item_trait(
     // If there are methods directly on this trait object, render them here.
     render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All);
 
-    let cache = cache();
-
     let mut synthetic_types = Vec::new();
 
-    if let Some(implementors) = cache.implementors.get(&it.def_id) {
+    if let Some(implementors) = cx.cache.implementors.get(&it.def_id) {
         // The DefId is for the first Type found with that name. The bool is
         // if any Types with the same name but different DefId have been found.
         let mut implementor_dups: FxHashMap<&str, (DefId, bool)> = FxHashMap::default();
@@ -2502,7 +2499,7 @@ fn item_trait(
 
         let (local, foreign) = implementors.iter()
             .partition::<Vec<_>, _>(|i| i.inner_impl().for_.def_id()
-                                         .map_or(true, |d| cache.paths.contains_key(&d)));
+                                         .map_or(true, |d| cx.cache.paths.contains_key(&d)));
 
 
         let (mut synthetic, mut concrete): (Vec<&&Impl>, Vec<&&Impl>) = local.iter()
@@ -2567,7 +2564,7 @@ fn item_trait(
            path = if it.def_id.is_local() {
                cx.current.join("/")
            } else {
-               let (ref path, _) = cache.external_paths[&it.def_id];
+               let (ref path, _) = cx.cache.external_paths[&it.def_id];
                path[..path.len() - 1].join("/")
            },
            ty = it.type_(),
@@ -3144,7 +3141,7 @@ fn render_assoc_items(w: &mut Buffer,
                       containing_item: &clean::Item,
                       it: DefId,
                       what: AssocItemRender<'_>) {
-    let c = cache();
+    let c = &cx.cache;
     let v = match c.impls.get(&it) {
         Some(v) => v,
         None => return,
@@ -3250,7 +3247,7 @@ fn render_deref_methods(w: &mut Buffer, cx: &Context, impl_: &Impl,
         render_assoc_items(w, cx, container_item, did, what)
     } else {
         if let Some(prim) = target.primitive_type() {
-            if let Some(&did) = cache().primitive_locations.get(&prim) {
+            if let Some(&did) = cx.cache.primitive_locations.get(&prim) {
                 render_assoc_items(w, cx, container_item, did, what);
             }
         }
@@ -3500,7 +3497,7 @@ fn render_impl(w: &mut Buffer, cx: &Context, i: &Impl, link: AssocItemLink<'_>,
         }
     }
 
-    let traits = &cache().traits;
+    let traits = &cx.cache.traits;
     let trait_ = i.trait_did().map(|did| &traits[&did]);
 
     write!(w, "<div class='impl-items'>");
@@ -3642,7 +3639,7 @@ fn print_sidebar(cx: &Context, it: &clean::Item, buffer: &mut Buffer) {
     }
 
     if it.is_crate() {
-        if let Some(ref version) = cache().crate_version {
+        if let Some(ref version) = cx.cache.crate_version {
             write!(buffer,
                     "<div class='block version'>\
                     <p>Version {}</p>\

From 0ad789aa5b0950608b2d71b8388c3d167fcc22b1 Mon Sep 17 00:00:00 2001
From: Mark Rousskov <mark.simulacrum@gmail.com>
Date: Fri, 13 Sep 2019 14:11:10 -0400
Subject: [PATCH 12/18] Stylistic fix -- remove double impl

---
 src/librustdoc/html/render.rs | 2 --
 1 file changed, 2 deletions(-)

diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs
index dc9e7131c89de..2623f8d227e14 100644
--- a/src/librustdoc/html/render.rs
+++ b/src/librustdoc/html/render.rs
@@ -222,9 +222,7 @@ impl SharedContext {
 
         Ok(())
     }
-}
 
-impl SharedContext {
     /// Based on whether the `collapse-docs` pass was run, return either the `doc_value` or the
     /// `collapsed_doc_value` of the given item.
     pub fn maybe_collapsed_doc_value<'a>(&self, item: &'a clean::Item) -> Option<Cow<'a, str>> {

From 5f58834fb85b6a49da05c90ff592ccff2496cefe Mon Sep 17 00:00:00 2001
From: Mark Rousskov <mark.simulacrum@gmail.com>
Date: Fri, 13 Sep 2019 14:21:40 -0400
Subject: [PATCH 13/18] Provide helper for synthesizing paths with resource
 suffix

---
 src/librustdoc/html/render.rs | 69 ++++++++++++++++++++---------------
 1 file changed, 40 insertions(+), 29 deletions(-)

diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs
index 2623f8d227e14..4b281dc6a7b6d 100644
--- a/src/librustdoc/html/render.rs
+++ b/src/librustdoc/html/render.rs
@@ -212,6 +212,26 @@ crate struct SharedContext {
     playground: Option<markdown::Playground>,
 }
 
+impl Context {
+    fn path(&self, filename: &str) -> PathBuf {
+        // We use splitn vs Path::extension here because we might get a filename
+        // like `style.min.css` and we want to process that into
+        // `style-suffix.min.css`.  Path::extension would just return `css`
+        // which would result in `style.min-suffix.css` which isn't what we
+        // want.
+        let mut iter = filename.splitn(2, '.');
+        let base = iter.next().unwrap();
+        let ext = iter.next().unwrap();
+        let filename = format!(
+            "{}{}.{}",
+            base,
+            self.shared.resource_suffix,
+            ext,
+        );
+        self.dst.join(&filename)
+    }
+}
+
 impl SharedContext {
     crate fn ensure_dir(&self, dst: &Path) -> Result<(), Error> {
         let mut dirs = self.created_dirs.borrow_mut();
@@ -530,13 +550,13 @@ fn write_shared(
     // Add all the static files. These may already exist, but we just
     // overwrite them anyway to make sure that they're fresh and up-to-date.
 
-    write_minify(&cx.shared.fs, cx.dst.join(&format!("rustdoc{}.css", cx.shared.resource_suffix)),
+    write_minify(&cx.shared.fs, cx.path("rustdoc.css"),
                  static_files::RUSTDOC_CSS,
                  options.enable_minification)?;
-    write_minify(&cx.shared.fs, cx.dst.join(&format!("settings{}.css", cx.shared.resource_suffix)),
+    write_minify(&cx.shared.fs, cx.path("settings.css"),
                  static_files::SETTINGS_CSS,
                  options.enable_minification)?;
-    write_minify(&cx.shared.fs, cx.dst.join(&format!("noscript{}.css", cx.shared.resource_suffix)),
+    write_minify(&cx.shared.fs, cx.path("noscript.css"),
                  static_files::NOSCRIPT_CSS,
                  options.enable_minification)?;
 
@@ -548,34 +568,25 @@ fn write_shared(
         let content = try_err!(fs::read(&entry), &entry);
         let theme = try_none!(try_none!(entry.file_stem(), &entry).to_str(), &entry);
         let extension = try_none!(try_none!(entry.extension(), &entry).to_str(), &entry);
-        cx.shared.fs.write(
-            cx.dst.join(format!("{}{}.{}", theme, cx.shared.resource_suffix, extension)),
-            content.as_slice())?;
+        cx.shared.fs.write(cx.path(&format!("{}.{}", theme, extension)), content.as_slice())?;
         themes.insert(theme.to_owned());
     }
 
     let write = |p, c| { cx.shared.fs.write(p, c) };
     if (*cx.shared).layout.logo.is_empty() {
-        write(cx.dst.join(&format!("rust-logo{}.png", cx.shared.resource_suffix)),
-              static_files::RUST_LOGO)?;
+        write(cx.path("rust-log.png"), static_files::RUST_LOGO)?;
     }
     if (*cx.shared).layout.favicon.is_empty() {
-        write(cx.dst.join(&format!("favicon{}.ico", cx.shared.resource_suffix)),
-              static_files::RUST_FAVICON)?;
-    }
-    write(cx.dst.join(&format!("brush{}.svg", cx.shared.resource_suffix)),
-          static_files::BRUSH_SVG)?;
-    write(cx.dst.join(&format!("wheel{}.svg", cx.shared.resource_suffix)),
-          static_files::WHEEL_SVG)?;
-    write(cx.dst.join(&format!("down-arrow{}.svg", cx.shared.resource_suffix)),
-          static_files::DOWN_ARROW_SVG)?;
-    write_minify(&cx.shared.fs, cx.dst.join(&format!("light{}.css", cx.shared.resource_suffix)),
-                 static_files::themes::LIGHT,
-                 options.enable_minification)?;
+        write(cx.path("favicon.ico"), static_files::RUST_FAVICON)?;
+    }
+    write(cx.path("brush.svg"), static_files::BRUSH_SVG)?;
+    write(cx.path("wheel.svg"), static_files::WHEEL_SVG)?;
+    write(cx.path("down-arrow.svg"), static_files::DOWN_ARROW_SVG)?;
+    write_minify(&cx.shared.fs,
+        cx.path("light.css"), static_files::themes::LIGHT, options.enable_minification)?;
     themes.insert("light".to_owned());
-    write_minify(&cx.shared.fs, cx.dst.join(&format!("dark{}.css", cx.shared.resource_suffix)),
-                 static_files::themes::DARK,
-                 options.enable_minification)?;
+    write_minify(&cx.shared.fs,
+        cx.path("dark.css"), static_files::themes::DARK, options.enable_minification)?;
     themes.insert("dark".to_owned());
 
     let mut themes: Vec<&String> = themes.iter().collect();
@@ -638,16 +649,16 @@ themePicker.onblur = handleThemeButtonsBlur;
           theme_js.as_bytes()
     )?;
 
-    write_minify(&cx.shared.fs, cx.dst.join(&format!("main{}.js", cx.shared.resource_suffix)),
+    write_minify(&cx.shared.fs, cx.path("main.js"),
                  static_files::MAIN_JS,
                  options.enable_minification)?;
-    write_minify(&cx.shared.fs, cx.dst.join(&format!("settings{}.js", cx.shared.resource_suffix)),
+    write_minify(&cx.shared.fs, cx.path("settings.js"),
                  static_files::SETTINGS_JS,
                  options.enable_minification)?;
     if cx.shared.include_sources {
         write_minify(
             &cx.shared.fs,
-            cx.dst.join(&format!("source-script{}.js", cx.shared.resource_suffix)),
+            cx.path("source-script.js"),
             static_files::sidebar::SOURCE_SCRIPT,
             options.enable_minification)?;
     }
@@ -655,7 +666,7 @@ themePicker.onblur = handleThemeButtonsBlur;
     {
         write_minify(
             &cx.shared.fs,
-            cx.dst.join(&format!("storage{}.js", cx.shared.resource_suffix)),
+            cx.path("storage.js"),
             &format!("var resourcesSuffix = \"{}\";{}",
                      cx.shared.resource_suffix,
                      static_files::STORAGE_JS),
@@ -663,7 +674,7 @@ themePicker.onblur = handleThemeButtonsBlur;
     }
 
     if let Some(ref css) = cx.shared.layout.css_file_extension {
-        let out = cx.dst.join(&format!("theme{}.css", cx.shared.resource_suffix));
+        let out = cx.path("theme.css");
         let buffer = try_err!(fs::read_to_string(css), css);
         if !options.enable_minification {
             cx.shared.fs.write(&out, &buffer)?;
@@ -671,7 +682,7 @@ themePicker.onblur = handleThemeButtonsBlur;
             write_minify(&cx.shared.fs, out, &buffer, options.enable_minification)?;
         }
     }
-    write_minify(&cx.shared.fs, cx.dst.join(&format!("normalize{}.css", cx.shared.resource_suffix)),
+    write_minify(&cx.shared.fs, cx.path("normalize.css"),
                  static_files::NORMALIZE_CSS,
                  options.enable_minification)?;
     write(cx.dst.join("FiraSans-Regular.woff"),

From 61f16920b3783e91cc853c720d152be6dc94e2da Mon Sep 17 00:00:00 2001
From: Mark Rousskov <mark.simulacrum@gmail.com>
Date: Fri, 13 Sep 2019 14:25:56 -0400
Subject: [PATCH 14/18] Remove needless Rc<RefCell<...>>

---
 src/librustdoc/html/render.rs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs
index 4b281dc6a7b6d..e4ace7a0211b7 100644
--- a/src/librustdoc/html/render.rs
+++ b/src/librustdoc/html/render.rs
@@ -3771,12 +3771,12 @@ fn sidebar_assoc_items(it: &clean::Item) -> String {
         let mut used_links = FxHashSet::default();
 
         {
-            let used_links_bor = Rc::new(RefCell::new(&mut used_links));
+            let used_links_bor = &mut used_links;
             let mut ret = v.iter()
                            .filter(|i| i.inner_impl().trait_.is_none())
                            .flat_map(move |i| get_methods(i.inner_impl(),
                                                           false,
-                                                          &mut used_links_bor.borrow_mut(), false))
+                                                          used_links_bor, false))
                            .collect::<Vec<_>>();
             // We want links' order to be reproducible so we don't use unstable sort.
             ret.sort();

From 583a81dc5e19bcc21092c8f16fd7185c357bd077 Mon Sep 17 00:00:00 2001
From: Mark Rousskov <mark.simulacrum@gmail.com>
Date: Fri, 13 Sep 2019 14:32:59 -0400
Subject: [PATCH 15/18] Remove pointless or

---
 src/librustdoc/html/render.rs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs
index e4ace7a0211b7..e3f227e0cf4d6 100644
--- a/src/librustdoc/html/render.rs
+++ b/src/librustdoc/html/render.rs
@@ -3405,7 +3405,7 @@ fn render_impl(w: &mut Buffer, cx: &Context, i: &Impl, link: AssocItemLink<'_>,
         let item_type = item.type_();
         let name = item.name.as_ref().unwrap();
 
-        let render_method_item: bool = match render_mode {
+        let render_method_item = match render_mode {
             RenderMode::Normal => true,
             RenderMode::ForDeref { mut_: deref_mut_ } => should_render_item(&item, deref_mut_),
         };
@@ -3474,7 +3474,7 @@ fn render_impl(w: &mut Buffer, cx: &Context, i: &Impl, link: AssocItemLink<'_>,
             _ => panic!("can't make docs for trait item with name {:?}", item.name)
         }
 
-        if render_method_item || render_mode == RenderMode::Normal {
+        if render_method_item {
             if !is_default_item {
                 if let Some(t) = trait_ {
                     // The trait item may have been stripped so we might not

From 53acfc3f8a52f9d26ce3ea3cdaea00345b1e9973 Mon Sep 17 00:00:00 2001
From: Mark Rousskov <mark.simulacrum@gmail.com>
Date: Fri, 13 Sep 2019 14:59:08 -0400
Subject: [PATCH 16/18] Utilize shared error codes rather than re-querying env

---
 src/librustdoc/html/render.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs
index e3f227e0cf4d6..e66224c0b5e00 100644
--- a/src/librustdoc/html/render.rs
+++ b/src/librustdoc/html/render.rs
@@ -2123,7 +2123,7 @@ fn stability_tags(item: &clean::Item) -> String {
 /// documentation.
 fn short_stability(item: &clean::Item, cx: &Context) -> Vec<String> {
     let mut stability = vec![];
-    let error_codes = ErrorCodes::from(UnstableFeatures::from_environment().is_nightly_build());
+    let error_codes = cx.shared.codes;
 
     if let Some(Deprecation { note, since }) = &item.deprecation() {
         // We display deprecation messages for #[deprecated] and #[rustc_deprecated]

From cac7e5faedc0f7adb41c8cc81ee7be56d8217fd1 Mon Sep 17 00:00:00 2001
From: Mark Rousskov <mark.simulacrum@gmail.com>
Date: Fri, 13 Sep 2019 15:22:00 -0400
Subject: [PATCH 17/18] Remove unused arguments

---
 src/librustdoc/html/render.rs | 24 ++++++++++--------------
 1 file changed, 10 insertions(+), 14 deletions(-)

diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs
index e66224c0b5e00..301dddbbfb9b2 100644
--- a/src/librustdoc/html/render.rs
+++ b/src/librustdoc/html/render.rs
@@ -1702,12 +1702,12 @@ fn print_item(cx: &Context, item: &clean::Item, buf: &mut Buffer) {
         clean::TypedefItem(ref t, _) => item_typedef(buf, cx, item, t),
         clean::MacroItem(ref m) => item_macro(buf, cx, item, m),
         clean::ProcMacroItem(ref m) => item_proc_macro(buf, cx, item, m),
-        clean::PrimitiveItem(ref p) => item_primitive(buf, cx, item, p),
+        clean::PrimitiveItem(_) => item_primitive(buf, cx, item),
         clean::StaticItem(ref i) | clean::ForeignStaticItem(ref i) =>
             item_static(buf, cx, item, i),
         clean::ConstantItem(ref c) => item_constant(buf, cx, item, c),
         clean::ForeignTypeItem => item_foreign_type(buf, cx, item),
-        clean::KeywordItem(ref k) => item_keyword(buf, cx, item, k),
+        clean::KeywordItem(_) => item_keyword(buf, cx, item),
         clean::OpaqueTyItem(ref e, _) => item_opaque_ty(buf, cx, item, e),
         clean::TraitAliasItem(ref ta) => item_trait_alias(buf, cx, item, ta),
         _ => {
@@ -3665,11 +3665,11 @@ fn print_sidebar(cx: &Context, it: &clean::Item, buffer: &mut Buffer) {
     match it.inner {
         clean::StructItem(ref s) => sidebar_struct(buffer, it, s),
         clean::TraitItem(ref t) => sidebar_trait(buffer, it, t),
-        clean::PrimitiveItem(ref p) => sidebar_primitive(buffer, it, p),
+        clean::PrimitiveItem(_) => sidebar_primitive(buffer, it),
         clean::UnionItem(ref u) => sidebar_union(buffer, it, u),
         clean::EnumItem(ref e) => sidebar_enum(buffer, it, e),
-        clean::TypedefItem(ref t, _) => sidebar_typedef(buffer, it, t),
-        clean::ModuleItem(ref m) => sidebar_module(buffer, it, &m.items),
+        clean::TypedefItem(_, _) => sidebar_typedef(buffer, it),
+        clean::ModuleItem(ref m) => sidebar_module(buffer, &m.items),
         clean::ForeignTypeItem => sidebar_foreign_type(buffer, it),
         _ => (),
     }
@@ -4038,7 +4038,7 @@ fn sidebar_trait(buf: &mut Buffer, it: &clean::Item, t: &clean::Trait) {
     write!(buf, "<div class=\"block items\">{}</div>", sidebar)
 }
 
-fn sidebar_primitive(buf: &mut Buffer, it: &clean::Item, _p: &clean::PrimitiveType) {
+fn sidebar_primitive(buf: &mut Buffer, it: &clean::Item) {
     let sidebar = sidebar_assoc_items(it);
 
     if !sidebar.is_empty() {
@@ -4046,7 +4046,7 @@ fn sidebar_primitive(buf: &mut Buffer, it: &clean::Item, _p: &clean::PrimitiveTy
     }
 }
 
-fn sidebar_typedef(buf: &mut Buffer, it: &clean::Item, _t: &clean::Typedef) {
+fn sidebar_typedef(buf: &mut Buffer, it: &clean::Item) {
     let sidebar = sidebar_assoc_items(it);
 
     if !sidebar.is_empty() {
@@ -4138,7 +4138,7 @@ fn item_ty_to_strs(ty: &ItemType) -> (&'static str, &'static str) {
     }
 }
 
-fn sidebar_module(buf: &mut Buffer, _it: &clean::Item, items: &[clean::Item]) {
+fn sidebar_module(buf: &mut Buffer, items: &[clean::Item]) {
     let mut sidebar = String::new();
 
     if items.iter().any(|it| it.type_() == ItemType::ExternCrate ||
@@ -4216,16 +4216,12 @@ fn item_proc_macro(w: &mut Buffer, cx: &Context, it: &clean::Item, m: &clean::Pr
     document(w, cx, it)
 }
 
-fn item_primitive(w: &mut Buffer, cx: &Context,
-                  it: &clean::Item,
-                  _p: &clean::PrimitiveType) {
+fn item_primitive(w: &mut Buffer, cx: &Context, it: &clean::Item) {
     document(w, cx, it);
     render_assoc_items(w, cx, it, it.def_id, AssocItemRender::All)
 }
 
-fn item_keyword(w: &mut Buffer, cx: &Context,
-                it: &clean::Item,
-                _p: &str) {
+fn item_keyword(w: &mut Buffer, cx: &Context, it: &clean::Item) {
     document(w, cx, it)
 }
 

From 059163fad796e3ecf04c29f423b9cf985717f025 Mon Sep 17 00:00:00 2001
From: Mark Rousskov <mark.simulacrum@gmail.com>
Date: Fri, 13 Sep 2019 16:00:36 -0400
Subject: [PATCH 18/18] Remove needless `mut` in paths

---
 src/librustdoc/html/render/cache.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/librustdoc/html/render/cache.rs b/src/librustdoc/html/render/cache.rs
index e9f0955c54126..65dd119c27cb7 100644
--- a/src/librustdoc/html/render/cache.rs
+++ b/src/librustdoc/html/render/cache.rs
@@ -548,7 +548,7 @@ fn build_index(krate: &clean::Crate, cache: &mut Cache) -> String {
 
     let Cache { ref mut search_index,
                 ref orphan_impl_items,
-                ref mut paths, .. } = *cache;
+                ref paths, .. } = *cache;
 
     // Attach all orphan items to the type's definition if the type
     // has since been learned.