about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMark Rousskov <mark.simulacrum@gmail.com>2019-08-11 15:18:47 -0400
committerGitHub <noreply@github.com>2019-08-11 15:18:47 -0400
commit86ceab47b1caff6928ba04dbcffd058f6cd37a29 (patch)
tree140989dc20dead69cf71d0701459298d658f704d
parente16b12fd49748a5aa7fbdabd6cea13fffbc3bb3d (diff)
parent3b8a24d193a3b2d9e7a888338d0c2bb478892ec2 (diff)
downloadrust-86ceab47b1caff6928ba04dbcffd058f6cd37a29.tar.gz
rust-86ceab47b1caff6928ba04dbcffd058f6cd37a29.zip
Rollup merge of #63453 - Mark-Simulacrum:rustdoc-clean-2, r=GuillaumeGomez
rustdoc: general cleanup

Almost all commits stand alone; but all commits can be reviewed individually.
-rw-r--r--Cargo.lock1
-rw-r--r--src/librustdoc/Cargo.toml1
-rw-r--r--src/librustdoc/clean/blanket_impl.rs3
-rw-r--r--src/librustdoc/clean/inline.rs17
-rw-r--r--src/librustdoc/clean/mod.rs26
-rw-r--r--src/librustdoc/config.rs18
-rw-r--r--src/librustdoc/core.rs47
-rw-r--r--src/librustdoc/doctree.rs42
-rw-r--r--src/librustdoc/externalfiles.rs48
-rw-r--r--src/librustdoc/fold.rs8
-rw-r--r--src/librustdoc/html/markdown.rs259
-rw-r--r--src/librustdoc/html/markdown/tests.rs12
-rw-r--r--src/librustdoc/html/render.rs52
-rw-r--r--src/librustdoc/lib.rs16
-rw-r--r--src/librustdoc/markdown.rs12
-rw-r--r--src/librustdoc/passes/collapse_docs.rs4
-rw-r--r--src/librustdoc/passes/collect_trait_impls.rs4
-rw-r--r--src/librustdoc/passes/mod.rs59
-rw-r--r--src/librustdoc/visit_ast.rs28
-rw-r--r--src/tools/error_index_generator/main.rs13
20 files changed, 322 insertions, 348 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 52cfa2cb1f8..ab6731e4d43 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3252,7 +3252,6 @@ name = "rustdoc"
 version = "0.0.0"
 dependencies = [
  "minifier 0.0.33 (registry+https://github.com/rust-lang/crates.io-index)",
- "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "pulldown-cmark 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc-rayon 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "tempfile 3.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml
index 334dc74c6c8..0eb8b73016d 100644
--- a/src/librustdoc/Cargo.toml
+++ b/src/librustdoc/Cargo.toml
@@ -13,4 +13,3 @@ pulldown-cmark = { version = "0.5.3", default-features = false }
 minifier = "0.0.33"
 rayon = { version = "0.2.0", package = "rustc-rayon" }
 tempfile = "3"
-parking_lot = "0.7"
diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs
index 64cffaec2ea..490d4107c51 100644
--- a/src/librustdoc/clean/blanket_impl.rs
+++ b/src/librustdoc/clean/blanket_impl.rs
@@ -3,6 +3,7 @@ use rustc::traits;
 use rustc::ty::ToPredicate;
 use rustc::ty::subst::Subst;
 use rustc::infer::InferOk;
+use rustc::hir::def_id::LOCAL_CRATE;
 use syntax_pos::DUMMY_SP;
 
 use super::*;
@@ -27,7 +28,7 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> {
 
         debug!("get_blanket_impls({:?})", ty);
         let mut impls = Vec::new();
-        for &trait_def_id in self.cx.all_traits.iter() {
+        for &trait_def_id in self.cx.tcx.all_traits(LOCAL_CRATE).iter() {
             if !self.cx.renderinfo.borrow().access_levels.is_public(trait_def_id) ||
                self.cx.generated_synthetics
                       .borrow_mut()
diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs
index d2a6dcf19bc..6f93c95edef 100644
--- a/src/librustdoc/clean/inline.rs
+++ b/src/librustdoc/clean/inline.rs
@@ -163,10 +163,7 @@ pub fn load_attrs<'hir>(cx: &DocContext<'hir>, did: DefId) -> Attrs<'hir> {
 /// These names are used later on by HTML rendering to generate things like
 /// source links back to the original item.
 pub fn record_extern_fqn(cx: &DocContext<'_>, did: DefId, kind: clean::TypeKind) {
-    let mut crate_name = cx.tcx.crate_name(did.krate).to_string();
-    if did.is_local() {
-        crate_name = cx.crate_name.clone().unwrap_or(crate_name);
-    }
+    let crate_name = cx.tcx.crate_name(did.krate).to_string();
 
     let relative = cx.tcx.def_path(did).data.into_iter().filter_map(|elem| {
         // extern blocks have an empty name
@@ -577,22 +574,18 @@ pub fn record_extern_trait(cx: &DocContext<'_>, did: DefId) {
     }
 
     {
-        let external_traits = cx.external_traits.lock();
-        if external_traits.borrow().contains_key(&did) ||
+        if cx.external_traits.borrow().contains_key(&did) ||
             cx.active_extern_traits.borrow().contains(&did)
         {
             return;
         }
     }
 
-    cx.active_extern_traits.borrow_mut().push(did);
+    cx.active_extern_traits.borrow_mut().insert(did);
 
     debug!("record_extern_trait: {:?}", did);
     let trait_ = build_external_trait(cx, did);
 
-    {
-        let external_traits = cx.external_traits.lock();
-        external_traits.borrow_mut().insert(did, trait_);
-    }
-    cx.active_extern_traits.borrow_mut().remove_item(&did);
+    cx.external_traits.borrow_mut().insert(did, trait_);
+    cx.active_extern_traits.borrow_mut().remove(&did);
 }
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 6f33bdd7f4d..b281505956d 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -39,14 +39,12 @@ use std::fmt;
 use std::hash::{Hash, Hasher};
 use std::default::Default;
 use std::{mem, slice, vec};
-use std::iter::{FromIterator, once};
+use std::iter::FromIterator;
 use std::rc::Rc;
 use std::cell::RefCell;
 use std::sync::Arc;
 use std::u32;
 
-use parking_lot::ReentrantMutex;
-
 use crate::core::{self, DocContext};
 use crate::doctree;
 use crate::html::render::{cache, ExternalLocation};
@@ -133,8 +131,9 @@ pub struct Crate {
     pub primitives: Vec<(DefId, PrimitiveType, Attributes)>,
     // These are later on moved into `CACHEKEY`, leaving the map empty.
     // Only here so that they can be filtered through the rustdoc passes.
-    pub external_traits: Arc<ReentrantMutex<RefCell<FxHashMap<DefId, Trait>>>>,
+    pub external_traits: Rc<RefCell<FxHashMap<DefId, Trait>>>,
     pub masked_crates: FxHashSet<CrateNum>,
+    pub collapsed: bool,
 }
 
 impl Clean<Crate> for hir::Crate {
@@ -223,6 +222,7 @@ impl Clean<Crate> for hir::Crate {
             primitives,
             external_traits: cx.external_traits.clone(),
             masked_crates,
+            collapsed: false,
         }
     }
 }
@@ -4398,24 +4398,6 @@ impl Clean<TypeBindingKind> for hir::TypeBindingKind {
     }
 }
 
-pub fn def_id_to_path(
-    cx: &DocContext<'_>,
-    did: DefId,
-    name: Option<String>
-) -> Vec<String> {
-    let crate_name = name.unwrap_or_else(|| cx.tcx.crate_name(did.krate).to_string());
-    let relative = cx.tcx.def_path(did).data.into_iter().filter_map(|elem| {
-        // extern blocks have an empty name
-        let s = elem.data.to_string();
-        if !s.is_empty() {
-            Some(s)
-        } else {
-            None
-        }
-    });
-    once(crate_name).chain(relative).collect()
-}
-
 pub fn enter_impl_trait<F, R>(cx: &DocContext<'_>, f: F) -> R
 where
     F: FnOnce() -> R,
diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs
index db90bb4524d..98ab957ecbb 100644
--- a/src/librustdoc/config.rs
+++ b/src/librustdoc/config.rs
@@ -220,22 +220,22 @@ impl Options {
                 println!("{:>20} - {}", pass.name, pass.description);
             }
             println!("\nDefault passes for rustdoc:");
-            for &name in passes::DEFAULT_PASSES {
-                println!("{:>20}", name);
+            for pass in passes::DEFAULT_PASSES {
+                println!("{:>20}", pass.name);
             }
             println!("\nPasses run with `--document-private-items`:");
-            for &name in passes::DEFAULT_PRIVATE_PASSES {
-                println!("{:>20}", name);
+            for pass in passes::DEFAULT_PRIVATE_PASSES {
+                println!("{:>20}", pass.name);
             }
 
             if nightly_options::is_nightly_build() {
                 println!("\nPasses run with `--show-coverage`:");
-                for &name in passes::DEFAULT_COVERAGE_PASSES {
-                    println!("{:>20}", name);
+                for pass in passes::DEFAULT_COVERAGE_PASSES {
+                    println!("{:>20}", pass.name);
                 }
                 println!("\nPasses run with `--show-coverage --document-private-items`:");
-                for &name in passes::PRIVATE_COVERAGE_PASSES {
-                    println!("{:>20}", name);
+                for pass in passes::PRIVATE_COVERAGE_PASSES {
+                    println!("{:>20}", pass.name);
                 }
             }
 
@@ -378,7 +378,7 @@ impl Options {
                 &matches.opt_strs("html-after-content"),
                 &matches.opt_strs("markdown-before-content"),
                 &matches.opt_strs("markdown-after-content"),
-                &diag, &mut id_map, edition) {
+                &diag, &mut id_map, edition, &None) {
             Some(eh) => eh,
             None => return Err(3),
         };
diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs
index 5138e4a23a4..87381f224d0 100644
--- a/src/librustdoc/core.rs
+++ b/src/librustdoc/core.rs
@@ -22,12 +22,10 @@ use syntax::json::JsonEmitter;
 use syntax::symbol::sym;
 use errors;
 use errors::emitter::{Emitter, EmitterWriter};
-use parking_lot::ReentrantMutex;
 
 use std::cell::RefCell;
 use std::mem;
 use rustc_data_structures::sync::{self, Lrc};
-use std::sync::Arc;
 use std::rc::Rc;
 
 use crate::config::{Options as RustdocOptions, RenderOptions};
@@ -46,16 +44,14 @@ pub struct DocContext<'tcx> {
 
     pub tcx: TyCtxt<'tcx>,
     pub resolver: Rc<RefCell<interface::BoxedResolver>>,
-    /// The stack of module NodeIds up till this point
-    pub crate_name: Option<String>,
     pub cstore: Lrc<CStore>,
     /// Later on moved into `html::render::CACHE_KEY`
     pub renderinfo: RefCell<RenderInfo>,
     /// Later on moved through `clean::Crate` into `html::render::CACHE_KEY`
-    pub external_traits: Arc<ReentrantMutex<RefCell<FxHashMap<DefId, clean::Trait>>>>,
+    pub external_traits: Rc<RefCell<FxHashMap<DefId, clean::Trait>>>,
     /// Used while populating `external_traits` to ensure we don't process the same trait twice at
     /// the same time.
-    pub active_extern_traits: RefCell<Vec<DefId>>,
+    pub active_extern_traits: RefCell<FxHashSet<DefId>>,
     // The current set of type and lifetime substitutions,
     // for expanding type aliases at the HIR level:
 
@@ -72,7 +68,6 @@ pub struct DocContext<'tcx> {
     /// Auto-trait or blanket impls processed so far, as `(self_ty, trait_def_id)`.
     // FIXME(eddyb) make this a `ty::TraitRef<'tcx>` set.
     pub generated_synthetics: RefCell<FxHashSet<(Ty<'tcx>, DefId)>>,
-    pub all_traits: Vec<DefId>,
     pub auto_traits: Vec<DefId>,
 }
 
@@ -227,7 +222,7 @@ pub fn new_handler(error_format: ErrorOutputType,
     )
 }
 
-pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOptions, Vec<String>) {
+pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOptions) {
     // Parse, resolve, and typecheck the given crate.
 
     let RustdocOptions {
@@ -332,7 +327,7 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt
         file_loader: None,
         diagnostic_output: DiagnosticOutput::Default,
         stderr: None,
-        crate_name: crate_name.clone(),
+        crate_name,
         lint_caps,
     };
 
@@ -368,11 +363,9 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt
             let mut renderinfo = RenderInfo::default();
             renderinfo.access_levels = access_levels;
 
-            let all_traits = tcx.all_traits(LOCAL_CRATE).to_vec();
             let ctxt = DocContext {
                 tcx,
                 resolver,
-                crate_name,
                 cstore: compiler.cstore().clone(),
                 external_traits: Default::default(),
                 active_extern_traits: Default::default(),
@@ -384,10 +377,9 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt
                 fake_def_ids: Default::default(),
                 all_fake_def_ids: Default::default(),
                 generated_synthetics: Default::default(),
-                auto_traits: all_traits.iter().cloned().filter(|trait_def_id| {
+                auto_traits: tcx.all_traits(LOCAL_CRATE).iter().cloned().filter(|trait_def_id| {
                     tcx.trait_is_auto(*trait_def_id)
                 }).collect(),
-                all_traits,
             };
             debug!("crate: {:?}", tcx.hir().krate());
 
@@ -432,8 +424,8 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt
                         },
                         _ => continue,
                     };
-                    for p in value.as_str().split_whitespace() {
-                        sink.push(p.to_string());
+                    for name in value.as_str().split_whitespace() {
+                        sink.push(name.to_string());
                     }
                 }
 
@@ -444,25 +436,26 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt
                 }
             }
 
-            let mut passes: Vec<String> =
-                passes::defaults(default_passes).iter().map(|p| p.to_string()).collect();
-            passes.extend(manual_passes);
+            let passes = passes::defaults(default_passes).iter().chain(manual_passes.into_iter()
+                .flat_map(|name| {
+                    if let Some(pass) = passes::find_pass(&name) {
+                        Some(pass)
+                    } else {
+                        error!("unknown pass {}, skipping", name);
+                        None
+                    }
+                }));
 
             info!("Executing passes");
 
-            for pass_name in &passes {
-                match passes::find_pass(pass_name).map(|p| p.pass) {
-                    Some(pass) => {
-                        debug!("running pass {}", pass_name);
-                        krate = pass(krate, &ctxt);
-                    }
-                    None => error!("unknown pass {}, skipping", *pass_name),
-                }
+            for pass in passes {
+                debug!("running pass {}", pass.name);
+                krate = (pass.pass)(krate, &ctxt);
             }
 
             ctxt.sess().abort_if_errors();
 
-            (krate, ctxt.renderinfo.into_inner(), render_options, passes)
+            (krate, ctxt.renderinfo.into_inner(), render_options)
         })
     })
 }
diff --git a/src/librustdoc/doctree.rs b/src/librustdoc/doctree.rs
index 90dcf1be76c..6e453561f6d 100644
--- a/src/librustdoc/doctree.rs
+++ b/src/librustdoc/doctree.rs
@@ -13,7 +13,7 @@ use rustc::hir::ptr::P;
 
 pub struct Module<'hir> {
     pub name: Option<Name>,
-    pub attrs: &'hir hir::HirVec<ast::Attribute>,
+    pub attrs: &'hir [ast::Attribute],
     pub where_outer: Span,
     pub where_inner: Span,
     pub extern_crates: Vec<ExternCrate<'hir>>,
@@ -41,7 +41,7 @@ pub struct Module<'hir> {
 impl Module<'hir> {
     pub fn new(
         name: Option<Name>,
-        attrs: &'hir hir::HirVec<ast::Attribute>,
+        attrs: &'hir [ast::Attribute],
         vis: &'hir hir::Visibility,
     ) -> Module<'hir> {
         Module {
@@ -89,7 +89,7 @@ pub struct Struct<'hir> {
     pub struct_type: StructType,
     pub name: Name,
     pub generics: &'hir hir::Generics,
-    pub attrs: &'hir hir::HirVec<ast::Attribute>,
+    pub attrs: &'hir [ast::Attribute],
     pub fields: &'hir [hir::StructField],
     pub whence: Span,
 }
@@ -100,7 +100,7 @@ pub struct Union<'hir> {
     pub struct_type: StructType,
     pub name: Name,
     pub generics: &'hir hir::Generics,
-    pub attrs: &'hir hir::HirVec<ast::Attribute>,
+    pub attrs: &'hir [ast::Attribute],
     pub fields: &'hir [hir::StructField],
     pub whence: Span,
 }
@@ -109,7 +109,7 @@ pub struct Enum<'hir> {
     pub vis: &'hir hir::Visibility,
     pub variants: Vec<Variant<'hir>>,
     pub generics: &'hir hir::Generics,
-    pub attrs: &'hir hir::HirVec<ast::Attribute>,
+    pub attrs: &'hir [ast::Attribute],
     pub id: hir::HirId,
     pub whence: Span,
     pub name: Name,
@@ -118,14 +118,14 @@ pub struct Enum<'hir> {
 pub struct Variant<'hir> {
     pub name: Name,
     pub id: hir::HirId,
-    pub attrs: &'hir hir::HirVec<ast::Attribute>,
+    pub attrs: &'hir [ast::Attribute],
     pub def: &'hir hir::VariantData,
     pub whence: Span,
 }
 
 pub struct Function<'hir> {
     pub decl: &'hir hir::FnDecl,
-    pub attrs: &'hir hir::HirVec<ast::Attribute>,
+    pub attrs: &'hir [ast::Attribute],
     pub id: hir::HirId,
     pub name: Name,
     pub vis: &'hir hir::Visibility,
@@ -140,7 +140,7 @@ pub struct Typedef<'hir> {
     pub gen: &'hir hir::Generics,
     pub name: Name,
     pub id: hir::HirId,
-    pub attrs: &'hir hir::HirVec<ast::Attribute>,
+    pub attrs: &'hir [ast::Attribute],
     pub whence: Span,
     pub vis: &'hir hir::Visibility,
 }
@@ -149,7 +149,7 @@ pub struct OpaqueTy<'hir> {
     pub opaque_ty: &'hir hir::OpaqueTy,
     pub name: Name,
     pub id: hir::HirId,
-    pub attrs: &'hir hir::HirVec<ast::Attribute>,
+    pub attrs: &'hir [ast::Attribute],
     pub whence: Span,
     pub vis: &'hir hir::Visibility,
 }
@@ -160,7 +160,7 @@ pub struct Static<'hir> {
     pub mutability: hir::Mutability,
     pub expr: hir::BodyId,
     pub name: Name,
-    pub attrs: &'hir hir::HirVec<ast::Attribute>,
+    pub attrs: &'hir [ast::Attribute],
     pub vis: &'hir hir::Visibility,
     pub id: hir::HirId,
     pub whence: Span,
@@ -170,7 +170,7 @@ pub struct Constant<'hir> {
     pub type_: &'hir P<hir::Ty>,
     pub expr: hir::BodyId,
     pub name: Name,
-    pub attrs: &'hir hir::HirVec<ast::Attribute>,
+    pub attrs: &'hir [ast::Attribute],
     pub vis: &'hir hir::Visibility,
     pub id: hir::HirId,
     pub whence: Span,
@@ -182,8 +182,8 @@ pub struct Trait<'hir> {
     pub name: Name,
     pub items: Vec<&'hir hir::TraitItem>,
     pub generics: &'hir hir::Generics,
-    pub bounds: &'hir hir::HirVec<hir::GenericBound>,
-    pub attrs: &'hir hir::HirVec<ast::Attribute>,
+    pub bounds: &'hir [hir::GenericBound],
+    pub attrs: &'hir [ast::Attribute],
     pub id: hir::HirId,
     pub whence: Span,
     pub vis: &'hir hir::Visibility,
@@ -192,8 +192,8 @@ pub struct Trait<'hir> {
 pub struct TraitAlias<'hir> {
     pub name: Name,
     pub generics: &'hir hir::Generics,
-    pub bounds: &'hir hir::HirVec<hir::GenericBound>,
-    pub attrs: &'hir hir::HirVec<ast::Attribute>,
+    pub bounds: &'hir [hir::GenericBound],
+    pub attrs: &'hir [ast::Attribute],
     pub id: hir::HirId,
     pub whence: Span,
     pub vis: &'hir hir::Visibility,
@@ -208,7 +208,7 @@ pub struct Impl<'hir> {
     pub trait_: &'hir Option<hir::TraitRef>,
     pub for_: &'hir P<hir::Ty>,
     pub items: Vec<&'hir hir::ImplItem>,
-    pub attrs: &'hir hir::HirVec<ast::Attribute>,
+    pub attrs: &'hir [ast::Attribute],
     pub whence: Span,
     pub vis: &'hir hir::Visibility,
     pub id: hir::HirId,
@@ -219,7 +219,7 @@ pub struct ForeignItem<'hir> {
     pub id: hir::HirId,
     pub name: Name,
     pub kind: &'hir hir::ForeignItemKind,
-    pub attrs: &'hir hir::HirVec<ast::Attribute>,
+    pub attrs: &'hir [ast::Attribute],
     pub whence: Span,
 }
 
@@ -229,7 +229,7 @@ pub struct Macro<'hir> {
     pub name: Name,
     pub hid: hir::HirId,
     pub def_id: hir::def_id::DefId,
-    pub attrs: &'hir hir::HirVec<ast::Attribute>,
+    pub attrs: &'hir [ast::Attribute],
     pub whence: Span,
     pub matchers: hir::HirVec<Span>,
     pub imported_from: Option<Name>,
@@ -240,7 +240,7 @@ pub struct ExternCrate<'hir> {
     pub cnum: CrateNum,
     pub path: Option<String>,
     pub vis: &'hir hir::Visibility,
-    pub attrs: &'hir hir::HirVec<ast::Attribute>,
+    pub attrs: &'hir [ast::Attribute],
     pub whence: Span,
 }
 
@@ -248,7 +248,7 @@ pub struct Import<'hir> {
     pub name: Name,
     pub id: hir::HirId,
     pub vis: &'hir hir::Visibility,
-    pub attrs: &'hir hir::HirVec<ast::Attribute>,
+    pub attrs: &'hir [ast::Attribute],
     pub path: &'hir hir::Path,
     pub glob: bool,
     pub whence: Span,
@@ -259,7 +259,7 @@ pub struct ProcMacro<'hir> {
     pub id: hir::HirId,
     pub kind: MacroKind,
     pub helpers: Vec<Name>,
-    pub attrs: &'hir hir::HirVec<ast::Attribute>,
+    pub attrs: &'hir [ast::Attribute],
     pub whence: Span,
 }
 
diff --git a/src/librustdoc/externalfiles.rs b/src/librustdoc/externalfiles.rs
index d604ba11d41..56f1191feed 100644
--- a/src/librustdoc/externalfiles.rs
+++ b/src/librustdoc/externalfiles.rs
@@ -4,9 +4,7 @@ use std::str;
 use errors;
 use crate::syntax::feature_gate::UnstableFeatures;
 use crate::syntax::edition::Edition;
-use crate::html::markdown::{IdMap, ErrorCodes, Markdown};
-
-use std::cell::RefCell;
+use crate::html::markdown::{IdMap, ErrorCodes, Markdown, Playground};
 
 #[derive(Clone, Debug)]
 pub struct ExternalHtml {
@@ -24,37 +22,23 @@ pub struct ExternalHtml {
 impl ExternalHtml {
     pub fn load(in_header: &[String], before_content: &[String], after_content: &[String],
                 md_before_content: &[String], md_after_content: &[String], diag: &errors::Handler,
-                id_map: &mut IdMap, edition: Edition)
+                id_map: &mut IdMap, edition: Edition, playground: &Option<Playground>)
             -> Option<ExternalHtml> {
         let codes = ErrorCodes::from(UnstableFeatures::from_environment().is_nightly_build());
-        load_external_files(in_header, diag)
-            .and_then(|ih|
-                load_external_files(before_content, diag)
-                    .map(|bc| (ih, bc))
-            )
-            .and_then(|(ih, bc)|
-                load_external_files(md_before_content, diag)
-                    .map(|m_bc| (ih,
-                            format!("{}{}", bc, Markdown(&m_bc, &[], RefCell::new(id_map),
-                                    codes, edition))))
-            )
-            .and_then(|(ih, bc)|
-                load_external_files(after_content, diag)
-                    .map(|ac| (ih, bc, ac))
-            )
-            .and_then(|(ih, bc, ac)|
-                load_external_files(md_after_content, diag)
-                    .map(|m_ac| (ih, bc,
-                            format!("{}{}", ac, Markdown(&m_ac, &[], RefCell::new(id_map),
-                                    codes, edition))))
-            )
-            .map(|(ih, bc, ac)|
-                ExternalHtml {
-                    in_header: ih,
-                    before_content: bc,
-                    after_content: ac,
-                }
-            )
+        let ih = load_external_files(in_header, diag)?;
+        let bc = load_external_files(before_content, diag)?;
+        let m_bc = load_external_files(md_before_content, diag)?;
+        let bc = format!("{}{}", bc, Markdown(&m_bc, &[], id_map,
+                                    codes, edition, playground).to_string());
+        let ac = load_external_files(after_content, diag)?;
+        let m_ac = load_external_files(md_after_content, diag)?;
+        let ac = format!("{}{}", ac, Markdown(&m_ac, &[], id_map,
+                                    codes, edition, playground).to_string());
+        Some(ExternalHtml {
+            in_header: ih,
+            before_content: bc,
+            after_content: ac,
+        })
     }
 }
 
diff --git a/src/librustdoc/fold.rs b/src/librustdoc/fold.rs
index cfa22bc27b7..5482239c7ce 100644
--- a/src/librustdoc/fold.rs
+++ b/src/librustdoc/fold.rs
@@ -105,12 +105,12 @@ pub trait DocFolder : Sized {
         c.module = c.module.take().and_then(|module| self.fold_item(module));
 
         {
-            let guard = c.external_traits.lock();
-            let traits = guard.replace(Default::default());
-            guard.borrow_mut().extend(traits.into_iter().map(|(k, mut v)| {
+            let mut guard = c.external_traits.borrow_mut();
+            let external_traits = std::mem::replace(&mut *guard, Default::default());
+            *guard = external_traits.into_iter().map(|(k, mut v)| {
                 v.items = v.items.into_iter().filter_map(|i| self.fold_item(i)).collect();
                 (k, v)
-            }));
+            }).collect();
         }
         c
     }
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index ef52ce62875..5a7deb651b0 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -1,9 +1,6 @@
 //! Markdown formatting for rustdoc.
 //!
-//! This module implements markdown formatting through the pulldown-cmark
-//! rust-library. This module exposes all of the
-//! functionality through a unit struct, `Markdown`, which has an implementation
-//! of `fmt::Display`. Example usage:
+//! This module implements markdown formatting through the pulldown-cmark library.
 //!
 //! ```
 //! #![feature(rustc_private)]
@@ -12,12 +9,11 @@
 //!
 //! use syntax::edition::Edition;
 //! use rustdoc::html::markdown::{IdMap, Markdown, ErrorCodes};
-//! use std::cell::RefCell;
 //!
 //! let s = "My *markdown* _text_";
 //! let mut id_map = IdMap::new();
-//! let html = format!("{}", Markdown(s, &[], RefCell::new(&mut id_map),
-//!                                   ErrorCodes::Yes, Edition::Edition2015));
+//! let md = Markdown(s, &[], &mut id_map, ErrorCodes::Yes, Edition::Edition2015, &None);
+//! let html = md.to_string();
 //! // ... something using html
 //! ```
 
@@ -27,7 +23,7 @@ use rustc_data_structures::fx::FxHashMap;
 use std::cell::RefCell;
 use std::collections::VecDeque;
 use std::default::Default;
-use std::fmt::{self, Write};
+use std::fmt::Write;
 use std::borrow::Cow;
 use std::ops::Range;
 use std::str;
@@ -46,29 +42,36 @@ fn opts() -> Options {
     Options::ENABLE_TABLES | Options::ENABLE_FOOTNOTES
 }
 
-/// A tuple struct that has the `fmt::Display` trait implemented.
-/// When formatted, this struct will emit the HTML corresponding to the rendered
-/// version of the contained markdown string.
+/// When `to_string` is called, this struct will emit the HTML corresponding to
+/// the rendered version of the contained markdown string.
 pub struct Markdown<'a>(
     pub &'a str,
     /// A list of link replacements.
     pub &'a [(String, String)],
     /// The current list of used header IDs.
-    pub RefCell<&'a mut IdMap>,
+    pub &'a mut IdMap,
     /// Whether to allow the use of explicit error codes in doctest lang strings.
     pub ErrorCodes,
     /// Default edition to use when parsing doctests (to add a `fn main`).
     pub Edition,
+    pub &'a Option<Playground>,
 );
 /// A tuple struct like `Markdown` that renders the markdown with a table of contents.
 pub struct MarkdownWithToc<'a>(
     pub &'a str,
-    pub RefCell<&'a mut IdMap>,
+    pub &'a mut IdMap,
     pub ErrorCodes,
     pub Edition,
+    pub &'a Option<Playground>,
 );
 /// A tuple struct like `Markdown` that renders the markdown escaping HTML tags.
-pub struct MarkdownHtml<'a>(pub &'a str, pub RefCell<&'a mut IdMap>, pub ErrorCodes, pub Edition);
+pub struct MarkdownHtml<'a>(
+    pub &'a str,
+    pub &'a mut IdMap,
+    pub ErrorCodes,
+    pub Edition,
+    pub &'a Option<Playground>,
+);
 /// A tuple struct like `Markdown` that renders only the first paragraph.
 pub struct MarkdownSummaryLine<'a>(pub &'a str, pub &'a [(String, String)]);
 
@@ -155,30 +158,39 @@ fn slugify(c: char) -> Option<char> {
     }
 }
 
-// Information about the playground if a URL has been specified, containing an
-// optional crate name and the URL.
-thread_local!(pub static PLAYGROUND: RefCell<Option<(Option<String>, String)>> = {
-    RefCell::new(None)
-});
+#[derive(Clone, Debug)]
+pub struct Playground {
+    pub crate_name: Option<String>,
+    pub url: String,
+}
 
 /// Adds syntax highlighting and playground Run buttons to Rust code blocks.
-struct CodeBlocks<'a, I: Iterator<Item = Event<'a>>> {
+struct CodeBlocks<'p, 'a, I: Iterator<Item = Event<'a>>> {
     inner: I,
     check_error_codes: ErrorCodes,
     edition: Edition,
+    // Information about the playground if a URL has been specified, containing an
+    // optional crate name and the URL.
+    playground: &'p Option<Playground>,
 }
 
-impl<'a, I: Iterator<Item = Event<'a>>> CodeBlocks<'a, I> {
-    fn new(iter: I, error_codes: ErrorCodes, edition: Edition) -> Self {
+impl<'p, 'a, I: Iterator<Item = Event<'a>>> CodeBlocks<'p, 'a, I> {
+    fn new(
+        iter: I,
+        error_codes: ErrorCodes,
+        edition: Edition,
+        playground: &'p Option<Playground>,
+    ) -> Self {
         CodeBlocks {
             inner: iter,
             check_error_codes: error_codes,
             edition,
+            playground,
         }
     }
 }
 
-impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'a, I> {
+impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> {
     type Item = Event<'a>;
 
     fn next(&mut self) -> Option<Self::Item> {
@@ -213,86 +225,86 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'a, I> {
         }
         let lines = origtext.lines().filter_map(|l| map_line(l).for_html());
         let text = lines.collect::<Vec<Cow<'_, str>>>().join("\n");
-        PLAYGROUND.with(|play| {
-            // insert newline to clearly separate it from the
-            // previous block so we can shorten the html output
-            let mut s = String::from("\n");
-            let playground_button = play.borrow().as_ref().and_then(|&(ref krate, ref url)| {
-                if url.is_empty() {
-                    return None;
-                }
-                let test = origtext.lines()
-                    .map(|l| map_line(l).for_code())
-                    .collect::<Vec<Cow<'_, str>>>().join("\n");
-                let krate = krate.as_ref().map(|s| &**s);
-                let (test, _) = test::make_test(&test, krate, false,
-                                           &Default::default(), edition);
-                let channel = if test.contains("#![feature(") {
-                    "&amp;version=nightly"
-                } else {
-                    ""
-                };
-
-                let edition_string = format!("&amp;edition={}", edition);
-
-                // These characters don't need to be escaped in a URI.
-                // FIXME: use a library function for percent encoding.
-                fn dont_escape(c: u8) -> bool {
-                    (b'a' <= c && c <= b'z') ||
-                    (b'A' <= c && c <= b'Z') ||
-                    (b'0' <= c && c <= b'9') ||
-                    c == b'-' || c == b'_' || c == b'.' ||
-                    c == b'~' || c == b'!' || c == b'\'' ||
-                    c == b'(' || c == b')' || c == b'*'
-                }
-                let mut test_escaped = String::new();
-                for b in test.bytes() {
-                    if dont_escape(b) {
-                        test_escaped.push(char::from(b));
-                    } else {
-                        write!(test_escaped, "%{:02X}", b).unwrap();
-                    }
-                }
-                Some(format!(
-                    r#"<a class="test-arrow" target="_blank" href="{}?code={}{}{}">Run</a>"#,
-                    url, test_escaped, channel, edition_string
-                ))
-            });
-
-            let tooltip = if ignore {
-                Some(("This example is not tested".to_owned(), "ignore"))
-            } else if compile_fail {
-                Some(("This example deliberately fails to compile".to_owned(), "compile_fail"))
-            } else if explicit_edition {
-                Some((format!("This code runs with edition {}", edition), "edition"))
+        // insert newline to clearly separate it from the
+        // previous block so we can shorten the html output
+        let mut s = String::from("\n");
+        let playground_button = self.playground.as_ref().and_then(|playground| {
+            let krate = &playground.crate_name;
+            let url = &playground.url;
+            if url.is_empty() {
+                return None;
+            }
+            let test = origtext.lines()
+                .map(|l| map_line(l).for_code())
+                .collect::<Vec<Cow<'_, str>>>().join("\n");
+            let krate = krate.as_ref().map(|s| &**s);
+            let (test, _) = test::make_test(&test, krate, false,
+                                        &Default::default(), edition);
+            let channel = if test.contains("#![feature(") {
+                "&amp;version=nightly"
             } else {
-                None
+                ""
             };
 
-            if let Some((s1, s2)) = tooltip {
-                s.push_str(&highlight::render_with_highlighting(
-                    &text,
-                    Some(&format!("rust-example-rendered{}",
-                                  if ignore { " ignore" }
-                                  else if compile_fail { " compile_fail" }
-                                  else if explicit_edition { " edition " }
-                                  else { "" })),
-                    playground_button.as_ref().map(String::as_str),
-                    Some((s1.as_str(), s2))));
-                Some(Event::Html(s.into()))
-            } else {
-                s.push_str(&highlight::render_with_highlighting(
-                    &text,
-                    Some(&format!("rust-example-rendered{}",
-                                  if ignore { " ignore" }
-                                  else if compile_fail { " compile_fail" }
-                                  else if explicit_edition { " edition " }
-                                  else { "" })),
-                    playground_button.as_ref().map(String::as_str),
-                    None));
-                Some(Event::Html(s.into()))
+            let edition_string = format!("&amp;edition={}", edition);
+
+            // These characters don't need to be escaped in a URI.
+            // FIXME: use a library function for percent encoding.
+            fn dont_escape(c: u8) -> bool {
+                (b'a' <= c && c <= b'z') ||
+                (b'A' <= c && c <= b'Z') ||
+                (b'0' <= c && c <= b'9') ||
+                c == b'-' || c == b'_' || c == b'.' ||
+                c == b'~' || c == b'!' || c == b'\'' ||
+                c == b'(' || c == b')' || c == b'*'
+            }
+            let mut test_escaped = String::new();
+            for b in test.bytes() {
+                if dont_escape(b) {
+                    test_escaped.push(char::from(b));
+                } else {
+                    write!(test_escaped, "%{:02X}", b).unwrap();
+                }
             }
-        })
+            Some(format!(
+                r#"<a class="test-arrow" target="_blank" href="{}?code={}{}{}">Run</a>"#,
+                url, test_escaped, channel, edition_string
+            ))
+        });
+
+        let tooltip = if ignore {
+            Some(("This example is not tested".to_owned(), "ignore"))
+        } else if compile_fail {
+            Some(("This example deliberately fails to compile".to_owned(), "compile_fail"))
+        } else if explicit_edition {
+            Some((format!("This code runs with edition {}", edition), "edition"))
+        } else {
+            None
+        };
+
+        if let Some((s1, s2)) = tooltip {
+            s.push_str(&highlight::render_with_highlighting(
+                &text,
+                Some(&format!("rust-example-rendered{}",
+                                if ignore { " ignore" }
+                                else if compile_fail { " compile_fail" }
+                                else if explicit_edition { " edition " }
+                                else { "" })),
+                playground_button.as_ref().map(String::as_str),
+                Some((s1.as_str(), s2))));
+            Some(Event::Html(s.into()))
+        } else {
+            s.push_str(&highlight::render_with_highlighting(
+                &text,
+                Some(&format!("rust-example-rendered{}",
+                                if ignore { " ignore" }
+                                else if compile_fail { " compile_fail" }
+                                else if explicit_edition { " edition " }
+                                else { "" })),
+                playground_button.as_ref().map(String::as_str),
+                None));
+            Some(Event::Html(s.into()))
+        }
     }
 }
 
@@ -674,13 +686,12 @@ impl LangString {
     }
 }
 
-impl<'a> fmt::Display for Markdown<'a> {
-    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let Markdown(md, links, ref ids, codes, edition) = *self;
-        let mut ids = ids.borrow_mut();
+impl Markdown<'_> {
+    pub fn to_string(self) -> String {
+        let Markdown(md, links, mut ids, codes, edition, playground) = self;
 
         // This is actually common enough to special-case
-        if md.is_empty() { return Ok(()) }
+        if md.is_empty() { return String::new(); }
         let replacer = |_: &str, s: &str| {
             if let Some(&(_, ref replace)) = links.into_iter().find(|link| &*link.0 == s) {
                 Some((replace.clone(), s.to_owned()))
@@ -695,18 +706,17 @@ impl<'a> fmt::Display for Markdown<'a> {
 
         let p = HeadingLinks::new(p, None, &mut ids);
         let p = LinkReplacer::new(p, links);
-        let p = CodeBlocks::new(p, codes, edition);
+        let p = CodeBlocks::new(p, codes, edition, playground);
         let p = Footnotes::new(p);
         html::push_html(&mut s, p);
 
-        fmt.write_str(&s)
+        s
     }
 }
 
-impl<'a> fmt::Display for MarkdownWithToc<'a> {
-    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let MarkdownWithToc(md, ref ids, codes, edition) = *self;
-        let mut ids = ids.borrow_mut();
+impl MarkdownWithToc<'_> {
+    pub fn to_string(self) -> String {
+        let MarkdownWithToc(md, mut ids, codes, edition, playground) = self;
 
         let p = Parser::new_ext(md, opts());
 
@@ -716,24 +726,21 @@ impl<'a> fmt::Display for MarkdownWithToc<'a> {
 
         {
             let p = HeadingLinks::new(p, Some(&mut toc), &mut ids);
-            let p = CodeBlocks::new(p, codes, edition);
+            let p = CodeBlocks::new(p, codes, edition, playground);
             let p = Footnotes::new(p);
             html::push_html(&mut s, p);
         }
 
-        write!(fmt, "<nav id=\"TOC\">{}</nav>", toc.into_toc())?;
-
-        fmt.write_str(&s)
+        format!("<nav id=\"TOC\">{}</nav>{}", toc.into_toc(), s)
     }
 }
 
-impl<'a> fmt::Display for MarkdownHtml<'a> {
-    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let MarkdownHtml(md, ref ids, codes, edition) = *self;
-        let mut ids = ids.borrow_mut();
+impl MarkdownHtml<'_> {
+    pub fn to_string(self) -> String {
+        let MarkdownHtml(md, mut ids, codes, edition, playground) = self;
 
         // This is actually common enough to special-case
-        if md.is_empty() { return Ok(()) }
+        if md.is_empty() { return String::new(); }
         let p = Parser::new_ext(md, opts());
 
         // Treat inline HTML as plain text.
@@ -745,19 +752,19 @@ impl<'a> fmt::Display for MarkdownHtml<'a> {
         let mut s = String::with_capacity(md.len() * 3 / 2);
 
         let p = HeadingLinks::new(p, None, &mut ids);
-        let p = CodeBlocks::new(p, codes, edition);
+        let p = CodeBlocks::new(p, codes, edition, playground);
         let p = Footnotes::new(p);
         html::push_html(&mut s, p);
 
-        fmt.write_str(&s)
+        s
     }
 }
 
-impl<'a> fmt::Display for MarkdownSummaryLine<'a> {
-    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let MarkdownSummaryLine(md, links) = *self;
+impl MarkdownSummaryLine<'_> {
+    pub fn to_string(self) -> String {
+        let MarkdownSummaryLine(md, links) = self;
         // This is actually common enough to special-case
-        if md.is_empty() { return Ok(()) }
+        if md.is_empty() { return String::new(); }
 
         let replacer = |_: &str, s: &str| {
             if let Some(&(_, ref replace)) = links.into_iter().find(|link| &*link.0 == s) {
@@ -773,7 +780,7 @@ impl<'a> fmt::Display for MarkdownSummaryLine<'a> {
 
         html::push_html(&mut s, LinkReplacer::new(SummaryLine::new(p), links));
 
-        fmt.write_str(&s)
+        s
     }
 }
 
diff --git a/src/librustdoc/html/markdown/tests.rs b/src/librustdoc/html/markdown/tests.rs
index 681f363544a..a95c29038d4 100644
--- a/src/librustdoc/html/markdown/tests.rs
+++ b/src/librustdoc/html/markdown/tests.rs
@@ -73,8 +73,8 @@ fn test_lang_string_parse() {
 fn test_header() {
     fn t(input: &str, expect: &str) {
         let mut map = IdMap::new();
-        let output = Markdown(input, &[], RefCell::new(&mut map),
-                              ErrorCodes::Yes, DEFAULT_EDITION).to_string();
+        let output = Markdown(
+            input, &[], &mut map, ErrorCodes::Yes, DEFAULT_EDITION, &None).to_string();
         assert_eq!(output, expect, "original: {}", input);
     }
 
@@ -96,8 +96,8 @@ fn test_header() {
 fn test_header_ids_multiple_blocks() {
     let mut map = IdMap::new();
     fn t(map: &mut IdMap, input: &str, expect: &str) {
-        let output = Markdown(input, &[], RefCell::new(map),
-                              ErrorCodes::Yes, DEFAULT_EDITION).to_string();
+        let output = Markdown(input, &[], map,
+                              ErrorCodes::Yes, DEFAULT_EDITION, &None).to_string();
         assert_eq!(output, expect, "original: {}", input);
     }
 
@@ -134,8 +134,8 @@ fn test_plain_summary_line() {
 fn test_markdown_html_escape() {
     fn t(input: &str, expect: &str) {
         let mut idmap = IdMap::new();
-        let output = MarkdownHtml(input, RefCell::new(&mut idmap),
-                                  ErrorCodes::Yes, DEFAULT_EDITION).to_string();
+        let output = MarkdownHtml(input, &mut idmap,
+                                  ErrorCodes::Yes, DEFAULT_EDITION, &None).to_string();
         assert_eq!(output, expect, "original: {}", input);
     }
 
diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs
index b1cee018205..ea97cea9428 100644
--- a/src/librustdoc/html/render.rs
+++ b/src/librustdoc/html/render.rs
@@ -170,6 +170,7 @@ 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>,
 }
 
 struct SharedContext {
@@ -185,8 +186,8 @@ struct SharedContext {
     pub include_sources: bool,
     /// The local file sources we've emitted and their respective url-paths.
     pub local_sources: FxHashMap<PathBuf, String>,
-    /// All the passes that were run on this crate.
-    pub passes: FxHashSet<String>,
+    /// Whether the collapsed pass ran
+    pub collapsed: bool,
     /// The base-URL of the issue tracker for when an item has been tagged with
     /// an issue number.
     pub issue_tracker_base_url: Option<String>,
@@ -229,15 +230,10 @@ impl SharedContext {
 }
 
 impl SharedContext {
-    /// Returns `true` if the `collapse-docs` pass was run on this crate.
-    pub fn was_collapsed(&self) -> bool {
-        self.passes.contains("collapse-docs")
-    }
-
     /// 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>> {
-        if self.was_collapsed() {
+        if self.collapsed {
             item.collapsed_doc_value().map(|s| s.into())
         } else {
             item.doc_value().map(|s| s.into())
@@ -526,7 +522,6 @@ pub fn initial_ids() -> Vec<String> {
 /// Generates the documentation for `crate` into the directory `dst`
 pub fn run(mut krate: clean::Crate,
            options: RenderOptions,
-           passes: FxHashSet<String>,
            renderinfo: RenderInfo,
            diag: &errors::Handler,
            edition: Edition) -> Result<(), Error> {
@@ -557,8 +552,8 @@ pub fn run(mut krate: clean::Crate,
     };
     let mut errors = Arc::new(ErrorStorage::new());
     let mut scx = SharedContext {
+        collapsed: krate.collapsed,
         src_root,
-        passes,
         include_sources: true,
         local_sources: Default::default(),
         issue_tracker_base_url: None,
@@ -580,9 +575,11 @@ pub fn run(mut krate: clean::Crate,
     };
 
     // If user passed in `--playground-url` arg, we fill in crate name here
+    let mut playground = None;
     if let Some(url) = playground_url {
-        markdown::PLAYGROUND.with(|slot| {
-            *slot.borrow_mut() = Some((Some(krate.name.clone()), url));
+        playground = Some(markdown::Playground {
+            crate_name: Some(krate.name.clone()),
+            url,
         });
     }
 
@@ -598,9 +595,9 @@ pub fn run(mut krate: clean::Crate,
                     scx.layout.logo = s.to_string();
                 }
                 (sym::html_playground_url, Some(s)) => {
-                    markdown::PLAYGROUND.with(|slot| {
-                        let name = krate.name.clone();
-                        *slot.borrow_mut() = Some((Some(name), s.to_string()));
+                    playground = Some(markdown::Playground {
+                        crate_name: Some(krate.name.clone()),
+                        url: s.to_string(),
                     });
                 }
                 (sym::issue_tracker_base_url, Some(s)) => {
@@ -624,6 +621,7 @@ pub fn run(mut krate: clean::Crate,
         edition,
         id_map: Rc::new(RefCell::new(id_map)),
         shared: Arc::new(scx),
+        playground,
     };
 
     // Crawl the crate to build various caches used for the output
@@ -659,7 +657,7 @@ pub fn run(mut krate: clean::Crate,
         crate_version: krate.version.take(),
         orphan_impl_items: Vec::new(),
         orphan_trait_impls: Vec::new(),
-        traits: krate.external_traits.lock().replace(Default::default()),
+        traits: krate.external_traits.replace(Default::default()),
         deref_trait_did,
         deref_mut_trait_did,
         owned_box_did,
@@ -2597,8 +2595,8 @@ fn render_markdown(w: &mut fmt::Formatter<'_>,
     write!(w, "<div class='docblock{}'>{}{}</div>",
            if is_hidden { " hidden" } else { "" },
            prefix,
-           Markdown(md_text, &links, RefCell::new(&mut ids),
-           cx.codes, cx.edition))
+           Markdown(md_text, &links, &mut ids,
+           cx.codes, cx.edition, &cx.playground).to_string())
 }
 
 fn document_short(
@@ -2868,7 +2866,7 @@ fn item_module(w: &mut fmt::Formatter<'_>, cx: &Context,
                        </tr>",
                        name = *myitem.name.as_ref().unwrap(),
                        stab_tags = stability_tags(myitem),
-                       docs = MarkdownSummaryLine(doc_value, &myitem.links()),
+                       docs = MarkdownSummaryLine(doc_value, &myitem.links()).to_string(),
                        class = myitem.type_(),
                        add = add,
                        stab = stab.unwrap_or_else(|| String::new()),
@@ -2963,8 +2961,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, RefCell::new(&mut ids), error_codes, cx.edition);
-            message.push_str(&format!(": {}", html));
+            let html = MarkdownHtml(&note, &mut ids, error_codes, cx.edition, &cx.playground);
+            message.push_str(&format!(": {}", html.to_string()));
         }
         stability.push(format!("<div class='stab deprecated'>{}</div>", message));
     }
@@ -3012,7 +3010,13 @@ fn short_stability(item: &clean::Item, cx: &Context) -> Vec<String> {
             message = format!(
                 "<details><summary>{}</summary>{}</details>",
                 message,
-                MarkdownHtml(&unstable_reason, RefCell::new(&mut ids), error_codes, cx.edition)
+                MarkdownHtml(
+                    &unstable_reason,
+                    &mut ids,
+                    error_codes,
+                    cx.edition,
+                    &cx.playground,
+                ).to_string()
             );
         }
 
@@ -4242,8 +4246,8 @@ fn render_impl(w: &mut fmt::Formatter<'_>, cx: &Context, i: &Impl, link: AssocIt
         if let Some(ref dox) = cx.shared.maybe_collapsed_doc_value(&i.impl_item) {
             let mut ids = cx.id_map.borrow_mut();
             write!(w, "<div class='docblock'>{}</div>",
-                   Markdown(&*dox, &i.impl_item.links(), RefCell::new(&mut ids),
-                            cx.codes, cx.edition))?;
+                   Markdown(&*dox, &i.impl_item.links(), &mut ids,
+                            cx.codes, cx.edition, &cx.playground).to_string())?;
         }
     }
 
diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs
index 799b5d923ae..e30b35937db 100644
--- a/src/librustdoc/lib.rs
+++ b/src/librustdoc/lib.rs
@@ -44,7 +44,6 @@ use std::default::Default;
 use std::env;
 use std::panic;
 use std::process;
-use std::sync::mpsc::channel;
 
 use rustc::session::{early_warn, early_error};
 use rustc::session::config::{ErrorOutputType, RustcOptGroup};
@@ -80,7 +79,6 @@ struct Output {
     krate: clean::Crate,
     renderinfo: html::render::RenderInfo,
     renderopts: config::RenderOptions,
-    passes: Vec<String>,
 }
 
 pub fn main() {
@@ -419,14 +417,13 @@ fn main_options(options: config::Options) -> i32 {
             return rustc_driver::EXIT_SUCCESS;
         }
 
-        let Output { krate, passes, renderinfo, renderopts } = out;
+        let Output { krate, renderinfo, renderopts } = out;
         info!("going to format");
         let (error_format, treat_err_as_bug, ui_testing, edition) = diag_opts;
         let diag = core::new_handler(error_format, None, treat_err_as_bug, ui_testing);
         match html::render::run(
             krate,
             renderopts,
-            passes.into_iter().collect(),
             renderinfo,
             &diag,
             edition,
@@ -454,12 +451,10 @@ where R: 'static + Send,
     // First, parse the crate and extract all relevant information.
     info!("starting to run rustc");
 
-    let (tx, rx) = channel();
-
     let result = rustc_driver::report_ices_to_stderr_if_any(move || {
         let crate_name = options.crate_name.clone();
         let crate_version = options.crate_version.clone();
-        let (mut krate, renderinfo, renderopts, passes) = core::run_core(options);
+        let (mut krate, renderinfo, renderopts) = core::run_core(options);
 
         info!("finished with rustc");
 
@@ -469,16 +464,15 @@ where R: 'static + Send,
 
         krate.version = crate_version;
 
-        tx.send(f(Output {
+        f(Output {
             krate: krate,
             renderinfo: renderinfo,
             renderopts,
-            passes: passes
-        })).unwrap();
+        })
     });
 
     match result {
-        Ok(()) => rx.recv().unwrap(),
+        Ok(output) => output,
         Err(_) => panic::resume_unwind(Box::new(errors::FatalErrorMarker)),
     }
 }
diff --git a/src/librustdoc/markdown.rs b/src/librustdoc/markdown.rs
index 50a647f244d..eaaae3261c7 100644
--- a/src/librustdoc/markdown.rs
+++ b/src/librustdoc/markdown.rs
@@ -1,7 +1,6 @@
 use std::fs::File;
 use std::io::prelude::*;
 use std::path::PathBuf;
-use std::cell::RefCell;
 
 use errors;
 use testing;
@@ -60,9 +59,10 @@ pub fn render(
     };
     let playground_url = options.markdown_playground_url
                             .or(options.playground_url);
-    if let Some(playground) = playground_url {
-        markdown::PLAYGROUND.with(|s| { *s.borrow_mut() = Some((None, playground)); });
-    }
+    let playground = playground_url.map(|url| markdown::Playground {
+        crate_name: None,
+        url,
+    });
 
     let mut out = match File::create(&output) {
         Err(e) => {
@@ -82,9 +82,9 @@ pub fn render(
     let mut ids = IdMap::new();
     let error_codes = ErrorCodes::from(UnstableFeatures::from_environment().is_nightly_build());
     let text = if !options.markdown_no_toc {
-        MarkdownWithToc(text, RefCell::new(&mut ids), error_codes, edition).to_string()
+        MarkdownWithToc(text, &mut ids, error_codes, edition, &playground).to_string()
     } else {
-        Markdown(text, &[], RefCell::new(&mut ids), error_codes, edition).to_string()
+        Markdown(text, &[], &mut ids, error_codes, edition, &playground).to_string()
     };
 
     let err = write!(
diff --git a/src/librustdoc/passes/collapse_docs.rs b/src/librustdoc/passes/collapse_docs.rs
index 144ff226c42..31288345ce5 100644
--- a/src/librustdoc/passes/collapse_docs.rs
+++ b/src/librustdoc/passes/collapse_docs.rs
@@ -30,7 +30,9 @@ impl DocFragment {
 }
 
 pub fn collapse_docs(krate: clean::Crate, _: &DocContext<'_>) -> clean::Crate {
-    Collapser.fold_crate(krate)
+    let mut krate = Collapser.fold_crate(krate);
+    krate.collapsed = true;
+    krate
 }
 
 struct Collapser;
diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs
index cd488b9df78..86e4e9fd956 100644
--- a/src/librustdoc/passes/collect_trait_impls.rs
+++ b/src/librustdoc/passes/collect_trait_impls.rs
@@ -4,7 +4,7 @@ use crate::fold::DocFolder;
 use super::Pass;
 
 use rustc::util::nodemap::FxHashSet;
-use rustc::hir::def_id::DefId;
+use rustc::hir::def_id::{LOCAL_CRATE, DefId};
 use syntax::symbol::sym;
 
 pub const COLLECT_TRAIT_IMPLS: Pass = Pass {
@@ -116,7 +116,7 @@ pub fn collect_trait_impls(krate: Crate, cx: &DocContext<'_>) -> Crate {
 
     // `tcx.crates()` doesn't include the local crate, and `tcx.all_trait_implementations`
     // doesn't work with it anyway, so pull them from the HIR map instead
-    for &trait_did in cx.all_traits.iter() {
+    for &trait_did in cx.tcx.all_traits(LOCAL_CRATE).iter() {
         for &impl_node in cx.tcx.hir().trait_impls(trait_did) {
             let impl_did = cx.tcx.hir().local_def_id(impl_node);
             inline::build_impl(cx, impl_did, None, &mut new_items);
diff --git a/src/librustdoc/passes/mod.rs b/src/librustdoc/passes/mod.rs
index 4e10b46bc8a..641a6df2214 100644
--- a/src/librustdoc/passes/mod.rs
+++ b/src/librustdoc/passes/mod.rs
@@ -57,8 +57,9 @@ pub struct Pass {
     pub description: &'static str,
 }
 
+
 /// The full list of passes.
-pub const PASSES: &'static [Pass] = &[
+pub const PASSES: &[Pass] = &[
     CHECK_PRIVATE_ITEMS_DOC_TESTS,
     STRIP_HIDDEN,
     UNINDENT_COMMENTS,
@@ -73,43 +74,43 @@ pub const PASSES: &'static [Pass] = &[
 ];
 
 /// The list of passes run by default.
-pub const DEFAULT_PASSES: &[&str] = &[
-    "collect-trait-impls",
-    "collapse-docs",
-    "unindent-comments",
-    "check-private-items-doc-tests",
-    "strip-hidden",
-    "strip-private",
-    "collect-intra-doc-links",
-    "check-code-block-syntax",
-    "propagate-doc-cfg",
+pub const DEFAULT_PASSES: &[Pass] = &[
+    COLLECT_TRAIT_IMPLS,
+    COLLAPSE_DOCS,
+    UNINDENT_COMMENTS,
+    CHECK_PRIVATE_ITEMS_DOC_TESTS,
+    STRIP_HIDDEN,
+    STRIP_PRIVATE,
+    COLLECT_INTRA_DOC_LINKS,
+    CHECK_CODE_BLOCK_SYNTAX,
+    PROPAGATE_DOC_CFG,
 ];
 
 /// The list of default passes run with `--document-private-items` is passed to rustdoc.
-pub const DEFAULT_PRIVATE_PASSES: &[&str] = &[
-    "collect-trait-impls",
-    "collapse-docs",
-    "unindent-comments",
-    "check-private-items-doc-tests",
-    "strip-priv-imports",
-    "collect-intra-doc-links",
-    "check-code-block-syntax",
-    "propagate-doc-cfg",
+pub const DEFAULT_PRIVATE_PASSES: &[Pass] = &[
+    COLLECT_TRAIT_IMPLS,
+    COLLAPSE_DOCS,
+    UNINDENT_COMMENTS,
+    CHECK_PRIVATE_ITEMS_DOC_TESTS,
+    STRIP_PRIV_IMPORTS,
+    COLLECT_INTRA_DOC_LINKS,
+    CHECK_CODE_BLOCK_SYNTAX,
+    PROPAGATE_DOC_CFG,
 ];
 
 /// The list of default passes run when `--doc-coverage` is passed to rustdoc.
-pub const DEFAULT_COVERAGE_PASSES: &'static [&'static str] = &[
-    "collect-trait-impls",
-    "strip-hidden",
-    "strip-private",
-    "calculate-doc-coverage",
+pub const DEFAULT_COVERAGE_PASSES: &[Pass] = &[
+    COLLECT_TRAIT_IMPLS,
+    STRIP_HIDDEN,
+    STRIP_PRIVATE,
+    CALCULATE_DOC_COVERAGE,
 ];
 
 /// The list of default passes run when `--doc-coverage --document-private-items` is passed to
 /// rustdoc.
-pub const PRIVATE_COVERAGE_PASSES: &'static [&'static str] = &[
-    "collect-trait-impls",
-    "calculate-doc-coverage",
+pub const PRIVATE_COVERAGE_PASSES: &[Pass] = &[
+    COLLECT_TRAIT_IMPLS,
+    CALCULATE_DOC_COVERAGE,
 ];
 
 /// A shorthand way to refer to which set of passes to use, based on the presence of
@@ -124,7 +125,7 @@ pub enum DefaultPassOption {
 }
 
 /// Returns the given default set of passes.
-pub fn defaults(default_set: DefaultPassOption) -> &'static [&'static str] {
+pub fn defaults(default_set: DefaultPassOption) -> &'static [Pass] {
     match default_set {
         DefaultPassOption::Default => DEFAULT_PASSES,
         DefaultPassOption::Private => DEFAULT_PRIVATE_PASSES,
diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs
index f01b9eeb30f..35b6d9972da 100644
--- a/src/librustdoc/visit_ast.rs
+++ b/src/librustdoc/visit_ast.rs
@@ -6,6 +6,7 @@ use rustc::hir::def::{Res, DefKind};
 use rustc::hir::def_id::{DefId, LOCAL_CRATE};
 use rustc::middle::privacy::AccessLevel;
 use rustc::util::nodemap::{FxHashSet, FxHashMap};
+use rustc::ty::TyCtxt;
 use syntax::ast;
 use syntax::ext::base::MacroKind;
 use syntax::source_map::Spanned;
@@ -15,9 +16,26 @@ use syntax_pos::{self, Span};
 use std::mem;
 
 use crate::core;
-use crate::clean::{self, AttributesExt, NestedAttributesExt, def_id_to_path};
+use crate::clean::{self, AttributesExt, NestedAttributesExt};
 use crate::doctree::*;
 
+// FIXME: Should this be replaced with tcx.def_path_str?
+fn def_id_to_path(
+    tcx: TyCtxt<'_>,
+    did: DefId,
+) -> Vec<String> {
+    let crate_name = tcx.crate_name(did.krate).to_string();
+    let relative = tcx.def_path(did).data.into_iter().filter_map(|elem| {
+        // extern blocks have an empty name
+        let s = elem.data.to_string();
+        if !s.is_empty() {
+            Some(s)
+        } else {
+            None
+        }
+    });
+    std::iter::once(crate_name).chain(relative).collect()
+}
 
 // Also, is there some reason that this doesn't use the 'visit'
 // framework from syntax?.
@@ -48,12 +66,8 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
     }
 
     fn store_path(&mut self, did: DefId) {
-        // We can't use the entry API, as that keeps the mutable borrow of `self` active
-        // when we try to use `cx`.
-        if self.exact_paths.get(&did).is_none() {
-            let path = def_id_to_path(self.cx, did, self.cx.crate_name.clone());
-            self.exact_paths.insert(did, path);
-        }
+        let tcx = self.cx.tcx;
+        self.exact_paths.entry(did).or_insert_with(|| def_id_to_path(tcx, did));
     }
 
     pub fn visit(mut self, krate: &'tcx hir::Crate) -> Module<'tcx> {
diff --git a/src/tools/error_index_generator/main.rs b/src/tools/error_index_generator/main.rs
index c31a5069e46..a9d1d9997f6 100644
--- a/src/tools/error_index_generator/main.rs
+++ b/src/tools/error_index_generator/main.rs
@@ -16,7 +16,7 @@ use std::cell::RefCell;
 use syntax::edition::DEFAULT_EDITION;
 use syntax::diagnostics::metadata::{get_metadata_dir, ErrorMetadataMap, ErrorMetadata};
 
-use rustdoc::html::markdown::{Markdown, IdMap, ErrorCodes, PLAYGROUND};
+use rustdoc::html::markdown::{Markdown, IdMap, ErrorCodes, Playground};
 use rustc_serialize::json;
 
 enum OutputFormat {
@@ -95,9 +95,13 @@ impl Formatter for HTMLFormatter {
         match info.description {
             Some(ref desc) => {
                 let mut id_map = self.0.borrow_mut();
+                let playground = Playground {
+                    crate_name: None,
+                    url: String::from("https://play.rust-lang.org/"),
+                };
                 write!(output, "{}",
-                    Markdown(desc, &[], RefCell::new(&mut id_map),
-                             ErrorCodes::Yes, DEFAULT_EDITION))?
+                    Markdown(desc, &[], &mut id_map,
+                             ErrorCodes::Yes, DEFAULT_EDITION, &Some(playground)).to_string())?
             },
             None => write!(output, "<p>No description.</p>\n")?,
         }
@@ -260,9 +264,6 @@ fn parse_args() -> (OutputFormat, PathBuf) {
 
 fn main() {
     env_logger::init();
-    PLAYGROUND.with(|slot| {
-        *slot.borrow_mut() = Some((None, String::from("https://play.rust-lang.org/")));
-    });
     let (format, dst) = parse_args();
     let result = syntax::with_default_globals(move || {
         main_with_result(format, &dst)