diff --git a/Cargo.lock b/Cargo.lock
index 9d726b240da93..8a50e84340d40 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4390,12 +4390,20 @@ dependencies = [
  "pulldown-cmark 0.8.0",
  "regex",
  "rustc-rayon",
+ "rustdoc-json-types",
  "serde",
  "serde_json",
  "smallvec 1.4.2",
  "tempfile",
 ]
 
+[[package]]
+name = "rustdoc-json-types"
+version = "0.1.0"
+dependencies = [
+ "serde",
+]
+
 [[package]]
 name = "rustdoc-themes"
 version = "0.1.0"
diff --git a/Cargo.toml b/Cargo.toml
index 5bd1147cad554..f3b2e0f740d61 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -4,6 +4,7 @@ members = [
   "compiler/rustc",
   "library/std",
   "library/test",
+  "src/rustdoc-json-types",
   "src/tools/cargotest",
   "src/tools/clippy",
   "src/tools/compiletest",
diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs
index 29415973ed073..5effe68752808 100644
--- a/compiler/rustc_codegen_llvm/src/back/lto.rs
+++ b/compiler/rustc_codegen_llvm/src/back/lto.rs
@@ -732,10 +732,7 @@ pub unsafe fn optimize_thin_module(
     let diag_handler = cgcx.create_diag_handler();
 
     let module_name = &thin_module.shared.module_names[thin_module.idx];
-    let split_dwarf_file = cgcx
-        .output_filenames
-        .split_dwarf_filename(cgcx.split_dwarf_kind, Some(module_name.to_str().unwrap()));
-    let tm_factory_config = TargetMachineFactoryConfig { split_dwarf_file };
+    let tm_factory_config = TargetMachineFactoryConfig::new(cgcx, module_name.to_str().unwrap());
     let tm =
         (cgcx.tm_factory)(tm_factory_config).map_err(|e| write::llvm_err(&diag_handler, &e))?;
 
diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs
index e225730dce061..326ae354ccf48 100644
--- a/compiler/rustc_codegen_llvm/src/back/write.rs
+++ b/compiler/rustc_codegen_llvm/src/back/write.rs
@@ -23,13 +23,11 @@ use rustc_fs_util::{link_or_copy, path_to_c_string};
 use rustc_hir::def_id::LOCAL_CRATE;
 use rustc_middle::bug;
 use rustc_middle::ty::TyCtxt;
-use rustc_session::config::{
-    self, Lto, OutputType, Passes, SanitizerSet, SplitDwarfKind, SwitchWithOptPath,
-};
+use rustc_session::config::{self, Lto, OutputType, Passes, SanitizerSet, SwitchWithOptPath};
 use rustc_session::Session;
 use rustc_span::symbol::sym;
 use rustc_span::InnerSpan;
-use rustc_target::spec::{CodeModel, RelocModel};
+use rustc_target::spec::{CodeModel, RelocModel, SplitDebuginfo};
 use tracing::debug;
 
 use libc::{c_char, c_int, c_uint, c_void, size_t};
@@ -93,9 +91,12 @@ pub fn create_informational_target_machine(sess: &Session) -> &'static mut llvm:
 }
 
 pub fn create_target_machine(tcx: TyCtxt<'_>, mod_name: &str) -> &'static mut llvm::TargetMachine {
-    let split_dwarf_file = tcx
-        .output_filenames(LOCAL_CRATE)
-        .split_dwarf_filename(tcx.sess.opts.debugging_opts.split_dwarf, Some(mod_name));
+    let split_dwarf_file = if tcx.sess.target_can_use_split_dwarf() {
+        tcx.output_filenames(LOCAL_CRATE)
+            .split_dwarf_filename(tcx.sess.split_debuginfo(), Some(mod_name))
+    } else {
+        None
+    };
     let config = TargetMachineFactoryConfig { split_dwarf_file };
     target_machine_factory(&tcx.sess, tcx.backend_optimization_level(LOCAL_CRATE))(config)
         .unwrap_or_else(|err| llvm_err(tcx.sess.diagnostic(), &err).raise())
@@ -838,11 +839,17 @@ pub(crate) unsafe fn codegen(
                     .generic_activity_with_arg("LLVM_module_codegen_emit_obj", &module.name[..]);
 
                 let dwo_out = cgcx.output_filenames.temp_path_dwo(module_name);
-                let dwo_out = match cgcx.split_dwarf_kind {
+                let dwo_out = match cgcx.split_debuginfo {
                     // Don't change how DWARF is emitted in single mode (or when disabled).
-                    SplitDwarfKind::None | SplitDwarfKind::Single => None,
+                    SplitDebuginfo::Off | SplitDebuginfo::Packed => None,
                     // Emit (a subset of the) DWARF into a separate file in split mode.
-                    SplitDwarfKind::Split => Some(dwo_out.as_path()),
+                    SplitDebuginfo::Unpacked => {
+                        if cgcx.target_can_use_split_dwarf {
+                            Some(dwo_out.as_path())
+                        } else {
+                            None
+                        }
+                    }
                 };
 
                 with_codegen(tm, llmod, config.no_builtins, |cpm| {
@@ -880,7 +887,7 @@ pub(crate) unsafe fn codegen(
 
     Ok(module.into_compiled_module(
         config.emit_obj != EmitObj::None,
-        cgcx.split_dwarf_kind == SplitDwarfKind::Split,
+        cgcx.target_can_use_split_dwarf && cgcx.split_debuginfo == SplitDebuginfo::Unpacked,
         config.emit_bc,
         &cgcx.output_filenames,
     ))
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
index b9ae796325023..ae629a3778e0f 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
@@ -996,10 +996,13 @@ pub fn compile_unit_metadata(
     let flags = "\0";
 
     let out_dir = &tcx.output_filenames(LOCAL_CRATE).out_directory;
-    let split_name = tcx
-        .output_filenames(LOCAL_CRATE)
-        .split_dwarf_filename(tcx.sess.opts.debugging_opts.split_dwarf, Some(codegen_unit_name))
-        .unwrap_or_default();
+    let split_name = if tcx.sess.target_can_use_split_dwarf() {
+        tcx.output_filenames(LOCAL_CRATE)
+            .split_dwarf_filename(tcx.sess.split_debuginfo(), Some(codegen_unit_name))
+    } else {
+        None
+    }
+    .unwrap_or_default();
     let out_dir = out_dir.to_str().unwrap();
     let split_name = split_name.to_str().unwrap();
 
diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs
index 92ac770aca554..d11c1592f99d1 100644
--- a/compiler/rustc_codegen_llvm/src/lib.rs
+++ b/compiler/rustc_codegen_llvm/src/lib.rs
@@ -351,12 +351,7 @@ impl ModuleLlvm {
         unsafe {
             let llcx = llvm::LLVMRustContextCreate(cgcx.fewer_names);
             let llmod_raw = back::lto::parse_module(llcx, name, buffer, handler)?;
-
-            let split_dwarf_file = cgcx
-                .output_filenames
-                .split_dwarf_filename(cgcx.split_dwarf_kind, Some(name.to_str().unwrap()));
-            let tm_factory_config = TargetMachineFactoryConfig { split_dwarf_file };
-
+            let tm_factory_config = TargetMachineFactoryConfig::new(&cgcx, name.to_str().unwrap());
             let tm = match (cgcx.tm_factory)(tm_factory_config) {
                 Ok(m) => m,
                 Err(e) => {
diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs
index 72e049b6d7469..0738b2df71e0c 100644
--- a/compiler/rustc_codegen_ssa/src/back/link.rs
+++ b/compiler/rustc_codegen_ssa/src/back/link.rs
@@ -14,7 +14,7 @@ use rustc_session::utils::NativeLibKind;
 use rustc_session::{filesearch, Session};
 use rustc_span::symbol::Symbol;
 use rustc_target::spec::crt_objects::{CrtObjects, CrtObjectsFallback};
-use rustc_target::spec::{LinkOutputKind, LinkerFlavor, LldFlavor};
+use rustc_target::spec::{LinkOutputKind, LinkerFlavor, LldFlavor, SplitDebuginfo};
 use rustc_target::spec::{PanicStrategy, RelocModel, RelroLevel, Target};
 
 use super::archive::ArchiveBuilder;
@@ -99,9 +99,6 @@ pub fn link_binary<'a, B: ArchiveBuilder<'a>>(
                         path.as_ref(),
                         target_cpu,
                     );
-                    if sess.opts.debugging_opts.split_dwarf == config::SplitDwarfKind::Split {
-                        link_dwarf_object(sess, &out_filename);
-                    }
                 }
             }
             if sess.opts.json_artifact_notifications {
@@ -828,29 +825,43 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>(
         }
     }
 
-    // On macOS, debuggers need this utility to get run to do some munging of
-    // the symbols. Note, though, that if the object files are being preserved
-    // for their debug information there's no need for us to run dsymutil.
-    if sess.target.is_like_osx
-        && sess.opts.debuginfo != DebugInfo::None
-        && !preserve_objects_for_their_debuginfo(sess)
-    {
-        let prog = Command::new("dsymutil").arg(out_filename).output();
-        match prog {
-            Ok(prog) => {
-                if !prog.status.success() {
-                    let mut output = prog.stderr.clone();
-                    output.extend_from_slice(&prog.stdout);
-                    sess.struct_warn(&format!(
-                        "processing debug info with `dsymutil` failed: {}",
-                        prog.status
-                    ))
-                    .note(&escape_string(&output))
-                    .emit();
+    match sess.split_debuginfo() {
+        // If split debug information is disabled or located in individual files
+        // there's nothing to do here.
+        SplitDebuginfo::Off | SplitDebuginfo::Unpacked => {}
+
+        // If packed split-debuginfo is requested, but the final compilation
+        // doesn't actually have any debug information, then we skip this step.
+        SplitDebuginfo::Packed if sess.opts.debuginfo == DebugInfo::None => {}
+
+        // On macOS the external `dsymutil` tool is used to create the packed
+        // debug information. Note that this will read debug information from
+        // the objects on the filesystem which we'll clean up later.
+        SplitDebuginfo::Packed if sess.target.is_like_osx => {
+            let prog = Command::new("dsymutil").arg(out_filename).output();
+            match prog {
+                Ok(prog) => {
+                    if !prog.status.success() {
+                        let mut output = prog.stderr.clone();
+                        output.extend_from_slice(&prog.stdout);
+                        sess.struct_warn(&format!(
+                            "processing debug info with `dsymutil` failed: {}",
+                            prog.status
+                        ))
+                        .note(&escape_string(&output))
+                        .emit();
+                    }
                 }
+                Err(e) => sess.fatal(&format!("unable to run `dsymutil`: {}", e)),
             }
-            Err(e) => sess.fatal(&format!("unable to run `dsymutil`: {}", e)),
         }
+
+        // On MSVC packed debug information is produced by the linker itself so
+        // there's no need to do anything else here.
+        SplitDebuginfo::Packed if sess.target.is_like_msvc => {}
+
+        // ... and otherwise we're processing a `*.dwp` packed dwarf file.
+        SplitDebuginfo::Packed => link_dwarf_object(sess, &out_filename),
     }
 }
 
@@ -1050,28 +1061,9 @@ fn preserve_objects_for_their_debuginfo(sess: &Session) -> bool {
         return false;
     }
 
-    // Single mode keeps debuginfo in the same object file, but in such a way that it it skipped
-    // by the linker - so it's expected that when codegen units are linked together that this
-    // debuginfo would be lost without keeping around the temps.
-    if sess.opts.debugging_opts.split_dwarf == config::SplitDwarfKind::Single {
-        return true;
-    }
-
-    // If we're on OSX then the equivalent of split dwarf is turned on by
-    // default. The final executable won't actually have any debug information
-    // except it'll have pointers to elsewhere. Historically we've always run
-    // `dsymutil` to "link all the dwarf together" but this is actually sort of
-    // a bummer for incremental compilation! (the whole point of split dwarf is
-    // that you don't do this sort of dwarf link).
-    //
-    // Basically as a result this just means that if we're on OSX and we're
-    // *not* running dsymutil then the object files are the only source of truth
-    // for debug information, so we must preserve them.
-    if sess.target.is_like_osx {
-        return !sess.opts.debugging_opts.run_dsymutil;
-    }
-
-    false
+    // "unpacked" split debuginfo means that we leave object files as the
+    // debuginfo is found in the original object files themselves
+    sess.split_debuginfo() == SplitDebuginfo::Unpacked
 }
 
 pub fn archive_search_paths(sess: &Session) -> Vec<PathBuf> {
diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs
index c84b87964b845..6aef5cb535a1f 100644
--- a/compiler/rustc_codegen_ssa/src/back/write.rs
+++ b/compiler/rustc_codegen_ssa/src/back/write.rs
@@ -282,6 +282,20 @@ pub struct TargetMachineFactoryConfig {
     pub split_dwarf_file: Option<PathBuf>,
 }
 
+impl TargetMachineFactoryConfig {
+    pub fn new(
+        cgcx: &CodegenContext<impl WriteBackendMethods>,
+        module_name: &str,
+    ) -> TargetMachineFactoryConfig {
+        let split_dwarf_file = if cgcx.target_can_use_split_dwarf {
+            cgcx.output_filenames.split_dwarf_filename(cgcx.split_debuginfo, Some(module_name))
+        } else {
+            None
+        };
+        TargetMachineFactoryConfig { split_dwarf_file }
+    }
+}
+
 pub type TargetMachineFactoryFn<B> = Arc<
     dyn Fn(TargetMachineFactoryConfig) -> Result<<B as WriteBackendMethods>::TargetMachine, String>
         + Send
@@ -311,10 +325,11 @@ pub struct CodegenContext<B: WriteBackendMethods> {
     pub tm_factory: TargetMachineFactoryFn<B>,
     pub msvc_imps_needed: bool,
     pub is_pe_coff: bool,
+    pub target_can_use_split_dwarf: bool,
     pub target_pointer_width: u32,
     pub target_arch: String,
     pub debuginfo: config::DebugInfo,
-    pub split_dwarf_kind: config::SplitDwarfKind,
+    pub split_debuginfo: rustc_target::spec::SplitDebuginfo,
 
     // Number of cgus excluding the allocator/metadata modules
     pub total_cgus: usize,
@@ -1035,10 +1050,11 @@ fn start_executing_work<B: ExtraBackendMethods>(
         total_cgus,
         msvc_imps_needed: msvc_imps_needed(tcx),
         is_pe_coff: tcx.sess.target.is_like_windows,
+        target_can_use_split_dwarf: tcx.sess.target_can_use_split_dwarf(),
         target_pointer_width: tcx.sess.target.pointer_width,
         target_arch: tcx.sess.target.arch.clone(),
         debuginfo: tcx.sess.opts.debuginfo,
-        split_dwarf_kind: tcx.sess.opts.debugging_opts.split_dwarf,
+        split_debuginfo: tcx.sess.split_debuginfo(),
     };
 
     // This is the "main loop" of parallel work happening for parallel codegen.
diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs
index 2f43940a9dcbb..08543d1622a7d 100644
--- a/compiler/rustc_expand/src/base.rs
+++ b/compiler/rustc_expand/src/base.rs
@@ -929,7 +929,9 @@ pub struct ExtCtxt<'a> {
     pub force_mode: bool,
     pub expansions: FxHashMap<Span, Vec<String>>,
     /// Called directly after having parsed an external `mod foo;` in expansion.
-    pub(super) extern_mod_loaded: Option<&'a dyn Fn(&ast::Crate)>,
+    ///
+    /// `Ident` is the module name.
+    pub(super) extern_mod_loaded: Option<&'a dyn Fn(&ast::Crate, Ident)>,
 }
 
 impl<'a> ExtCtxt<'a> {
@@ -937,7 +939,7 @@ impl<'a> ExtCtxt<'a> {
         sess: &'a Session,
         ecfg: expand::ExpansionConfig<'a>,
         resolver: &'a mut dyn ResolverExpand,
-        extern_mod_loaded: Option<&'a dyn Fn(&ast::Crate)>,
+        extern_mod_loaded: Option<&'a dyn Fn(&ast::Crate, Ident)>,
     ) -> ExtCtxt<'a> {
         ExtCtxt {
             sess,
diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs
index 5d398935ce815..50832d5edbfc5 100644
--- a/compiler/rustc_expand/src/expand.rs
+++ b/compiler/rustc_expand/src/expand.rs
@@ -1407,7 +1407,7 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
                         proc_macros: vec![],
                     };
                     if let Some(extern_mod_loaded) = self.cx.extern_mod_loaded {
-                        extern_mod_loaded(&krate);
+                        extern_mod_loaded(&krate, ident);
                     }
 
                     *old_mod = krate.module;
diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs
index 7031234e1089d..56aa3939b22dc 100644
--- a/compiler/rustc_interface/src/passes.rs
+++ b/compiler/rustc_interface/src/passes.rs
@@ -33,7 +33,7 @@ use rustc_session::lint;
 use rustc_session::output::{filename_for_input, filename_for_metadata};
 use rustc_session::search_paths::PathKind;
 use rustc_session::Session;
-use rustc_span::symbol::Symbol;
+use rustc_span::symbol::{Ident, Symbol};
 use rustc_span::{FileName, RealFileName};
 use rustc_trait_selection::traits;
 use rustc_typeck as typeck;
@@ -211,8 +211,13 @@ pub fn register_plugins<'a>(
     Ok((krate, lint_store))
 }
 
-fn pre_expansion_lint(sess: &Session, lint_store: &LintStore, krate: &ast::Crate) {
-    sess.time("pre_AST_expansion_lint_checks", || {
+fn pre_expansion_lint(
+    sess: &Session,
+    lint_store: &LintStore,
+    krate: &ast::Crate,
+    crate_name: &str,
+) {
+    sess.prof.generic_activity_with_arg("pre_AST_expansion_lint_checks", crate_name).run(|| {
         rustc_lint::check_ast_crate(
             sess,
             lint_store,
@@ -233,7 +238,7 @@ fn configure_and_expand_inner<'a>(
     metadata_loader: &'a MetadataLoaderDyn,
 ) -> Result<(ast::Crate, Resolver<'a>)> {
     tracing::trace!("configure_and_expand_inner");
-    pre_expansion_lint(sess, lint_store, &krate);
+    pre_expansion_lint(sess, lint_store, &krate, crate_name);
 
     let mut resolver = Resolver::new(sess, &krate, crate_name, metadata_loader, &resolver_arenas);
     rustc_builtin_macros::register_builtin_macros(&mut resolver);
@@ -295,7 +300,9 @@ fn configure_and_expand_inner<'a>(
             ..rustc_expand::expand::ExpansionConfig::default(crate_name.to_string())
         };
 
-        let extern_mod_loaded = |k: &ast::Crate| pre_expansion_lint(sess, lint_store, k);
+        let extern_mod_loaded = |k: &ast::Crate, ident: Ident| {
+            pre_expansion_lint(sess, lint_store, k, &*ident.name.as_str())
+        };
         let mut ecx = ExtCtxt::new(&sess, cfg, &mut resolver, Some(&extern_mod_loaded));
 
         // Expand macros now!
diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs
index 55d521a9b5ff5..305ae23669bbf 100644
--- a/compiler/rustc_interface/src/tests.rs
+++ b/compiler/rustc_interface/src/tests.rs
@@ -17,7 +17,7 @@ use rustc_span::edition::{Edition, DEFAULT_EDITION};
 use rustc_span::symbol::sym;
 use rustc_span::SourceFileHashAlgorithm;
 use rustc_target::spec::{CodeModel, LinkerFlavor, MergeFunctions, PanicStrategy};
-use rustc_target::spec::{RelocModel, RelroLevel, TlsModel};
+use rustc_target::spec::{RelocModel, RelroLevel, SplitDebuginfo, TlsModel};
 use std::collections::{BTreeMap, BTreeSet};
 use std::iter::FromIterator;
 use std::path::PathBuf;
@@ -446,6 +446,7 @@ fn test_codegen_options_tracking_hash() {
     tracked!(profile_use, Some(PathBuf::from("abc")));
     tracked!(relocation_model, Some(RelocModel::Pic));
     tracked!(soft_float, true);
+    tracked!(split_debuginfo, Some(SplitDebuginfo::Packed));
     tracked!(target_cpu, Some(String::from("abc")));
     tracked!(target_feature, String::from("all the features, all of them"));
 }
@@ -579,7 +580,6 @@ fn test_debugging_options_tracking_hash() {
     tracked!(relax_elf_relocations, Some(true));
     tracked!(relro_level, Some(RelroLevel::Full));
     tracked!(report_delayed_bugs, true);
-    tracked!(run_dsymutil, false);
     tracked!(sanitizer, SanitizerSet::ADDRESS);
     tracked!(sanitizer_memory_track_origins, 2);
     tracked!(sanitizer_recover, SanitizerSet::ADDRESS);
diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs
index b17765998807f..8bd9dad785c4b 100644
--- a/compiler/rustc_lint/src/context.rs
+++ b/compiler/rustc_lint/src/context.rs
@@ -746,6 +746,14 @@ impl<'tcx> LateContext<'tcx> {
             hir::QPath::Resolved(_, ref path) => path.res,
             hir::QPath::TypeRelative(..) | hir::QPath::LangItem(..) => self
                 .maybe_typeck_results()
+                .filter(|typeck_results| typeck_results.hir_owner == id.owner)
+                .or_else(|| {
+                    if self.tcx.has_typeck_results(id.owner.to_def_id()) {
+                        Some(self.tcx.typeck(id.owner))
+                    } else {
+                        None
+                    }
+                })
                 .and_then(|typeck_results| typeck_results.type_dependent_def(id))
                 .map_or(Res::Err, |(kind, def_id)| Res::Def(kind, def_id)),
         }
diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs
index 015e109871182..3821a393efb8b 100644
--- a/compiler/rustc_lint/src/late.rs
+++ b/compiler/rustc_lint/src/late.rs
@@ -140,6 +140,8 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas
     fn visit_item(&mut self, it: &'tcx hir::Item<'tcx>) {
         let generics = self.context.generics.take();
         self.context.generics = it.kind.generics();
+        let old_cached_typeck_results = self.context.cached_typeck_results.take();
+        let old_enclosing_body = self.context.enclosing_body.take();
         self.with_lint_attrs(it.hir_id, &it.attrs, |cx| {
             cx.with_param_env(it.hir_id, |cx| {
                 lint_callback!(cx, check_item, it);
@@ -147,6 +149,8 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas
                 lint_callback!(cx, check_item_post, it);
             });
         });
+        self.context.enclosing_body = old_enclosing_body;
+        self.context.cached_typeck_results.set(old_cached_typeck_results);
         self.context.generics = generics;
     }
 
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index c4d867161386d..2778882adcf74 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -780,8 +780,20 @@ pub fn place_to_string_for_capture(tcx: TyCtxt<'tcx>, place: &HirPlace<'tcx>) ->
 pub struct CaptureInfo<'tcx> {
     /// Expr Id pointing to use that resulted in selecting the current capture kind
     ///
+    /// Eg:
+    /// ```rust,no_run
+    /// let mut t = (0,1);
+    ///
+    /// let c = || {
+    ///     println!("{}",t); // L1
+    ///     t.1 = 4; // L2
+    /// };
+    /// ```
+    /// `capture_kind_expr_id` will point to the use on L2 and `path_expr_id` will point to the
+    /// use on L1.
+    ///
     /// If the user doesn't enable feature `capture_disjoint_fields` (RFC 2229) then, it is
-    /// possible that we don't see the use of a particular place resulting in expr_id being
+    /// possible that we don't see the use of a particular place resulting in capture_kind_expr_id being
     /// None. In such case we fallback on uvpars_mentioned for span.
     ///
     /// Eg:
@@ -795,7 +807,12 @@ pub struct CaptureInfo<'tcx> {
     ///
     /// In this example, if `capture_disjoint_fields` is **not** set, then x will be captured,
     /// but we won't see it being used during capture analysis, since it's essentially a discard.
-    pub expr_id: Option<hir::HirId>,
+    pub capture_kind_expr_id: Option<hir::HirId>,
+    /// Expr Id pointing to use that resulted the corresponding place being captured
+    ///
+    /// See `capture_kind_expr_id` for example.
+    ///
+    pub path_expr_id: Option<hir::HirId>,
 
     /// Capture mode that was selected
     pub capture_kind: UpvarCapture<'tcx>,
diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs
index e1af6fc07cf8f..73196c732f5bb 100644
--- a/compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs
+++ b/compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs
@@ -1,9 +1,12 @@
 use rustc_hir as hir;
 use rustc_hir::Node;
 use rustc_index::vec::Idx;
-use rustc_middle::mir::{self, ClearCrossCrate, Local, LocalDecl, LocalInfo, Location};
 use rustc_middle::mir::{Mutability, Place, PlaceRef, ProjectionElem};
 use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_middle::{
+    hir::place::PlaceBase,
+    mir::{self, ClearCrossCrate, Local, LocalDecl, LocalInfo, Location},
+};
 use rustc_span::source_map::DesugaringKind;
 use rustc_span::symbol::{kw, Symbol};
 use rustc_span::Span;
@@ -241,6 +244,10 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                     format!("mut {}", self.local_names[local].unwrap()),
                     Applicability::MachineApplicable,
                 );
+                let tcx = self.infcx.tcx;
+                if let ty::Closure(id, _) = the_place_err.ty(self.body, tcx).ty.kind() {
+                    self.show_mutating_upvar(tcx, id, the_place_err, &mut err);
+                }
             }
 
             // Also suggest adding mut for upvars
@@ -271,6 +278,14 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                         );
                     }
                 }
+
+                let tcx = self.infcx.tcx;
+                if let ty::Ref(_, ty, Mutability::Mut) = the_place_err.ty(self.body, tcx).ty.kind()
+                {
+                    if let ty::Closure(id, _) = ty.kind() {
+                        self.show_mutating_upvar(tcx, id, the_place_err, &mut err);
+                    }
+                }
             }
 
             // complete hack to approximate old AST-borrowck
@@ -463,6 +478,45 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
         err.buffer(&mut self.errors_buffer);
     }
 
+    // point to span of upvar making closure call require mutable borrow
+    fn show_mutating_upvar(
+        &self,
+        tcx: TyCtxt<'_>,
+        id: &hir::def_id::DefId,
+        the_place_err: PlaceRef<'tcx>,
+        err: &mut DiagnosticBuilder<'_>,
+    ) {
+        let id = id.expect_local();
+        let tables = tcx.typeck(id);
+        let hir_id = tcx.hir().local_def_id_to_hir_id(id);
+        let (span, place) = &tables.closure_kind_origins()[hir_id];
+        let reason = if let PlaceBase::Upvar(upvar_id) = place.base {
+            let upvar = ty::place_to_string_for_capture(tcx, place);
+            match tables.upvar_capture(upvar_id) {
+                ty::UpvarCapture::ByRef(ty::UpvarBorrow {
+                    kind: ty::BorrowKind::MutBorrow,
+                    ..
+                }) => {
+                    format!("mutable borrow of `{}`", upvar)
+                }
+                ty::UpvarCapture::ByValue(_) => {
+                    format!("possible mutation of `{}`", upvar)
+                }
+                _ => bug!("upvar `{}` borrowed, but not mutably", upvar),
+            }
+        } else {
+            bug!("not an upvar")
+        };
+        err.span_label(
+            *span,
+            format!(
+                "calling `{}` requires mutable binding due to {}",
+                self.describe_place(the_place_err).unwrap(),
+                reason
+            ),
+        );
+    }
+
     /// Targeted error when encountering an `FnMut` closure where an `Fn` closure was expected.
     fn expected_fn_found_fn_mut_call(&self, err: &mut DiagnosticBuilder<'_>, sp: Span, act: &str) {
         err.span_label(sp, format!("cannot {}", act));
diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs
index 4234740b2b15f..6b7059eecf4e2 100644
--- a/compiler/rustc_parse/src/parser/path.rs
+++ b/compiler/rustc_parse/src/parser/path.rs
@@ -133,7 +133,15 @@ impl<'a> Parser<'a> {
         maybe_whole!(self, NtPath, |path| {
             if style == PathStyle::Mod && path.segments.iter().any(|segment| segment.args.is_some())
             {
-                self.struct_span_err(path.span, "unexpected generic arguments in path").emit();
+                self.struct_span_err(
+                    path.segments
+                        .iter()
+                        .filter_map(|segment| segment.args.as_ref())
+                        .map(|arg| arg.span())
+                        .collect::<Vec<_>>(),
+                    "unexpected generic arguments in path",
+                )
+                .emit();
             }
             path
         });
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index 6e269e9e1264c..f9e40919149d9 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -13,7 +13,7 @@ use rustc_data_structures::impl_stable_hash_via_hash;
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 
 use rustc_target::abi::{Align, TargetDataLayout};
-use rustc_target::spec::{Target, TargetTriple};
+use rustc_target::spec::{SplitDebuginfo, Target, TargetTriple};
 
 use crate::parse::CrateConfig;
 use rustc_feature::UnstableFeatures;
@@ -221,23 +221,6 @@ pub enum DebugInfo {
     Full,
 }
 
-/// Some debuginfo requires link-time relocation and some does not. LLVM can partition the debuginfo
-/// into sections depending on whether or not it requires link-time relocation. Split DWARF
-/// provides a mechanism which allows the linker to skip the sections which don't require link-time
-/// relocation - either by putting those sections into DWARF object files, or keeping them in the
-/// object file in such a way that the linker will skip them.
-#[derive(Clone, Copy, Debug, PartialEq, Hash)]
-pub enum SplitDwarfKind {
-    /// Disabled.
-    None,
-    /// Sections which do not require relocation are written into the object file but ignored
-    /// by the linker.
-    Single,
-    /// Sections which do not require relocation are written into a DWARF object (`.dwo`) file,
-    /// which is skipped by the linker by virtue of being a different file.
-    Split,
-}
-
 #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)]
 #[derive(Encodable, Decodable)]
 pub enum OutputType {
@@ -635,10 +618,10 @@ impl OutputFilenames {
     /// mode is being used, which is the logic that this function is intended to encapsulate.
     pub fn split_dwarf_filename(
         &self,
-        split_dwarf_kind: SplitDwarfKind,
+        split_debuginfo_kind: SplitDebuginfo,
         cgu_name: Option<&str>,
     ) -> Option<PathBuf> {
-        self.split_dwarf_path(split_dwarf_kind, cgu_name)
+        self.split_dwarf_path(split_debuginfo_kind, cgu_name)
             .map(|path| path.strip_prefix(&self.out_directory).unwrap_or(&path).to_path_buf())
     }
 
@@ -646,19 +629,19 @@ impl OutputFilenames {
     /// mode is being used, which is the logic that this function is intended to encapsulate.
     pub fn split_dwarf_path(
         &self,
-        split_dwarf_kind: SplitDwarfKind,
+        split_debuginfo_kind: SplitDebuginfo,
         cgu_name: Option<&str>,
     ) -> Option<PathBuf> {
         let obj_out = self.temp_path(OutputType::Object, cgu_name);
         let dwo_out = self.temp_path_dwo(cgu_name);
-        match split_dwarf_kind {
-            SplitDwarfKind::None => None,
+        match split_debuginfo_kind {
+            SplitDebuginfo::Off => None,
             // Single mode doesn't change how DWARF is emitted, but does add Split DWARF attributes
             // (pointing at the path which is being determined here). Use the path to the current
             // object file.
-            SplitDwarfKind::Single => Some(obj_out),
+            SplitDebuginfo::Packed => Some(obj_out),
             // Split mode emits the DWARF into a different file, use that path.
-            SplitDwarfKind::Split => Some(dwo_out),
+            SplitDebuginfo::Unpacked => Some(dwo_out),
         }
     }
 }
@@ -1910,6 +1893,15 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
 
     let pretty = parse_pretty(matches, &debugging_opts, error_format);
 
+    if !debugging_opts.unstable_options
+        && !target_triple.triple().contains("apple")
+        && cg.split_debuginfo.is_some()
+    {
+        {
+            early_error(error_format, "`-Csplit-debuginfo` is unstable on this platform");
+        }
+    }
+
     Options {
         crate_types,
         optimize: opt_level,
@@ -2191,7 +2183,7 @@ crate mod dep_tracking {
     use rustc_feature::UnstableFeatures;
     use rustc_span::edition::Edition;
     use rustc_target::spec::{CodeModel, MergeFunctions, PanicStrategy, RelocModel};
-    use rustc_target::spec::{RelroLevel, TargetTriple, TlsModel};
+    use rustc_target::spec::{RelroLevel, SplitDebuginfo, TargetTriple, TlsModel};
     use std::collections::hash_map::DefaultHasher;
     use std::collections::BTreeMap;
     use std::hash::Hash;
@@ -2263,6 +2255,7 @@ crate mod dep_tracking {
     impl_dep_tracking_hash_via_hash!(TargetTriple);
     impl_dep_tracking_hash_via_hash!(Edition);
     impl_dep_tracking_hash_via_hash!(LinkerPluginLto);
+    impl_dep_tracking_hash_via_hash!(Option<SplitDebuginfo>);
     impl_dep_tracking_hash_via_hash!(SwitchWithOptPath);
     impl_dep_tracking_hash_via_hash!(Option<SymbolManglingVersion>);
     impl_dep_tracking_hash_via_hash!(Option<SourceFileHashAlgorithm>);
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index 30af65e49a075..2aaab84585d07 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -6,7 +6,7 @@ use crate::search_paths::SearchPath;
 use crate::utils::NativeLibKind;
 
 use rustc_target::spec::{CodeModel, LinkerFlavor, MergeFunctions, PanicStrategy};
-use rustc_target::spec::{RelocModel, RelroLevel, TargetTriple, TlsModel};
+use rustc_target::spec::{RelocModel, RelroLevel, SplitDebuginfo, TargetTriple, TlsModel};
 
 use rustc_feature::UnstableFeatures;
 use rustc_span::edition::Edition;
@@ -269,7 +269,6 @@ macro_rules! options {
         pub const parse_switch_with_opt_path: &str =
             "an optional path to the profiling data output directory";
         pub const parse_merge_functions: &str = "one of: `disabled`, `trampolines`, or `aliases`";
-        pub const parse_split_dwarf_kind: &str = "one of: `none`, `single` or `split`";
         pub const parse_symbol_mangling_version: &str = "either `legacy` or `v0` (RFC 2603)";
         pub const parse_src_file_hash: &str = "either `md5` or `sha1`";
         pub const parse_relocation_model: &str =
@@ -280,6 +279,8 @@ macro_rules! options {
             "one of supported TLS models (`rustc --print tls-models`)";
         pub const parse_target_feature: &str = parse_string;
         pub const parse_wasi_exec_model: &str = "either `command` or `reactor`";
+        pub const parse_split_debuginfo: &str =
+            "one of supported split-debuginfo modes (`off` or `dsymutil`)";
     }
 
     #[allow(dead_code)]
@@ -678,19 +679,6 @@ macro_rules! options {
             true
         }
 
-        fn parse_split_dwarf_kind(
-            slot: &mut SplitDwarfKind,
-            v: Option<&str>,
-        ) -> bool {
-            *slot = match v {
-                Some("none") => SplitDwarfKind::None,
-                Some("split") => SplitDwarfKind::Split,
-                Some("single") => SplitDwarfKind::Single,
-                _ => return false,
-            };
-            true
-        }
-
         fn parse_symbol_mangling_version(
             slot: &mut Option<SymbolManglingVersion>,
             v: Option<&str>,
@@ -732,6 +720,14 @@ macro_rules! options {
             }
             true
         }
+
+        fn parse_split_debuginfo(slot: &mut Option<SplitDebuginfo>, v: Option<&str>) -> bool {
+            match v.and_then(|s| SplitDebuginfo::from_str(s).ok()) {
+                Some(e) => *slot = Some(e),
+                _ => return false,
+            }
+            true
+        }
     }
 ) }
 
@@ -830,6 +826,8 @@ options! {CodegenOptions, CodegenSetter, basic_codegen_options,
         "save all temporary output files during compilation (default: no)"),
     soft_float: bool = (false, parse_bool, [TRACKED],
         "use soft float ABI (*eabihf targets only) (default: no)"),
+    split_debuginfo: Option<SplitDebuginfo> = (None, parse_split_debuginfo, [TRACKED],
+        "how to handle split-debuginfo, a platform-specific option"),
     target_cpu: Option<String> = (None, parse_opt_string, [TRACKED],
         "select target processor (`rustc --print target-cpus` for details)"),
     target_feature: String = (String::new(), parse_target_feature, [TRACKED],
@@ -1073,11 +1071,6 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
         "choose which RELRO level to use"),
     report_delayed_bugs: bool = (false, parse_bool, [TRACKED],
         "immediately print bugs registered with `delay_span_bug` (default: no)"),
-    // The default historical behavior was to always run dsymutil, so we're
-    // preserving that temporarily, but we're likely to switch the default
-    // soon.
-    run_dsymutil: bool = (true, parse_bool, [TRACKED],
-        "if on Mac, run `dsymutil` and delete intermediate object files (default: yes)"),
     sanitizer: SanitizerSet = (SanitizerSet::empty(), parse_sanitizers, [TRACKED],
         "use a sanitizer"),
     sanitizer_memory_track_origins: usize = (0, parse_sanitizer_memory_track_origins, [TRACKED],
@@ -1112,8 +1105,6 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
         "hash algorithm of source files in debug info (`md5`, `sha1`, or `sha256`)"),
     strip: Strip = (Strip::None, parse_strip, [UNTRACKED],
         "tell the linker which information to strip (`none` (default), `debuginfo` or `symbols`)"),
-    split_dwarf: SplitDwarfKind = (SplitDwarfKind::None, parse_split_dwarf_kind, [UNTRACKED],
-        "enable generation of split dwarf"),
     split_dwarf_inlining: bool = (true, parse_bool, [UNTRACKED],
         "provide minimal debug info in the object/executable to facilitate online \
          symbolication/stack traces in the absence of .dwo/.dwp files when using Split DWARF"),
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index 6d01854228662..dad21e59502de 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -28,7 +28,7 @@ use rustc_span::source_map::{FileLoader, MultiSpan, RealFileLoader, SourceMap, S
 use rustc_span::{sym, SourceFileHashAlgorithm, Symbol};
 use rustc_target::asm::InlineAsmArch;
 use rustc_target::spec::{CodeModel, PanicStrategy, RelocModel, RelroLevel};
-use rustc_target::spec::{Target, TargetTriple, TlsModel};
+use rustc_target::spec::{SplitDebuginfo, Target, TargetTriple, TlsModel};
 
 use std::cell::{self, RefCell};
 use std::env;
@@ -804,6 +804,14 @@ impl Session {
             )
     }
 
+    pub fn split_debuginfo(&self) -> SplitDebuginfo {
+        self.opts.cg.split_debuginfo.unwrap_or(self.target.split_debuginfo)
+    }
+
+    pub fn target_can_use_split_dwarf(&self) -> bool {
+        !self.target.is_like_windows && !self.target.is_like_osx
+    }
+
     pub fn must_not_eliminate_frame_pointers(&self) -> bool {
         // "mcount" function relies on stack pointer.
         // See <https://sourceware.org/binutils/docs/gprof/Implementation.html>.
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 7b90e5b611cd1..2e7c9701c0c67 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -622,6 +622,7 @@ symbols! {
         intel,
         into_iter,
         into_result,
+        into_trait,
         intra_doc_pointers,
         intrinsics,
         irrefutable_let_patterns,
@@ -1159,6 +1160,8 @@ symbols! {
         truncf32,
         truncf64,
         try_blocks,
+        try_from_trait,
+        try_into_trait,
         try_trait,
         tt,
         tuple,
diff --git a/compiler/rustc_target/src/spec/apple_base.rs b/compiler/rustc_target/src/spec/apple_base.rs
index 8842239521643..3b458962b3d07 100644
--- a/compiler/rustc_target/src/spec/apple_base.rs
+++ b/compiler/rustc_target/src/spec/apple_base.rs
@@ -1,6 +1,6 @@
 use std::env;
 
-use crate::spec::{LinkArgs, TargetOptions};
+use crate::spec::{LinkArgs, SplitDebuginfo, TargetOptions};
 
 pub fn opts(os: &str) -> TargetOptions {
     // ELF TLS is only available in macOS 10.7+. If you try to compile for 10.6
@@ -36,6 +36,10 @@ pub fn opts(os: &str) -> TargetOptions {
         emit_debug_gdb_scripts: false,
         eh_frame_header: false,
 
+        // The historical default for macOS targets is to run `dsymutil` which
+        // generates a packed version of debuginfo split from the main file.
+        split_debuginfo: SplitDebuginfo::Packed,
+
         // This environment variable is pretty magical but is intended for
         // producing deterministic builds. This was first discovered to be used
         // by the `ar` tool as a way to control whether or not mtime entries in
diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs
index 90d35efaa25bd..0227febd294a0 100644
--- a/compiler/rustc_target/src/spec/mod.rs
+++ b/compiler/rustc_target/src/spec/mod.rs
@@ -448,6 +448,69 @@ impl fmt::Display for LinkOutputKind {
 
 pub type LinkArgs = BTreeMap<LinkerFlavor, Vec<String>>;
 
+#[derive(Clone, Copy, Hash, Debug, PartialEq, Eq)]
+pub enum SplitDebuginfo {
+    /// Split debug-information is disabled, meaning that on supported platforms
+    /// you can find all debug information in the executable itself. This is
+    /// only supported for ELF effectively.
+    ///
+    /// * Windows - not supported
+    /// * macOS - don't run `dsymutil`
+    /// * ELF - `.dwarf_*` sections
+    Off,
+
+    /// Split debug-information can be found in a "packed" location separate
+    /// from the final artifact. This is supported on all platforms.
+    ///
+    /// * Windows - `*.pdb`
+    /// * macOS - `*.dSYM` (run `dsymutil`)
+    /// * ELF - `*.dwp` (run `rust-llvm-dwp`)
+    Packed,
+
+    /// Split debug-information can be found in individual object files on the
+    /// filesystem. The main executable may point to the object files.
+    ///
+    /// * Windows - not supported
+    /// * macOS - supported, scattered object files
+    /// * ELF - supported, scattered `*.dwo` files
+    Unpacked,
+}
+
+impl SplitDebuginfo {
+    fn as_str(&self) -> &'static str {
+        match self {
+            SplitDebuginfo::Off => "off",
+            SplitDebuginfo::Packed => "packed",
+            SplitDebuginfo::Unpacked => "unpacked",
+        }
+    }
+}
+
+impl FromStr for SplitDebuginfo {
+    type Err = ();
+
+    fn from_str(s: &str) -> Result<SplitDebuginfo, ()> {
+        Ok(match s {
+            "off" => SplitDebuginfo::Off,
+            "unpacked" => SplitDebuginfo::Unpacked,
+            "packed" => SplitDebuginfo::Packed,
+            _ => return Err(()),
+        })
+    }
+}
+
+impl ToJson for SplitDebuginfo {
+    fn to_json(&self) -> Json {
+        self.as_str().to_json()
+    }
+}
+
+impl fmt::Display for SplitDebuginfo {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str(self.as_str())
+    }
+}
+
 macro_rules! supported_targets {
     ( $(($( $triple:literal, )+ $module:ident ),)+ ) => {
         $(mod $module;)+
@@ -1085,6 +1148,10 @@ pub struct TargetOptions {
     /// Is true if the target is an ARM architecture using thumb v1 which allows for
     /// thumb and arm interworking.
     pub has_thumb_interworking: bool,
+
+    /// How to handle split debug information, if at all. Specifying `None` has
+    /// target-specific meaning.
+    pub split_debuginfo: SplitDebuginfo,
 }
 
 impl Default for TargetOptions {
@@ -1184,6 +1251,7 @@ impl Default for TargetOptions {
             use_ctors_section: false,
             eh_frame_header: true,
             has_thumb_interworking: false,
+            split_debuginfo: SplitDebuginfo::Off,
         }
     }
 }
@@ -1382,6 +1450,18 @@ impl Target {
                     Some(Ok(()))
                 })).unwrap_or(Ok(()))
             } );
+            ($key_name:ident, SplitDebuginfo) => ( {
+                let name = (stringify!($key_name)).replace("_", "-");
+                obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| {
+                    match s.parse::<SplitDebuginfo>() {
+                        Ok(level) => base.$key_name = level,
+                        _ => return Some(Err(format!("'{}' is not a valid value for \
+                                                      split-debuginfo. Use 'off' or 'dsymutil'.",
+                                                      s))),
+                    }
+                    Some(Ok(()))
+                })).unwrap_or(Ok(()))
+            } );
             ($key_name:ident, list) => ( {
                 let name = (stringify!($key_name)).replace("_", "-");
                 if let Some(v) = obj.find(&name).and_then(Json::as_array) {
@@ -1627,6 +1707,7 @@ impl Target {
         key!(use_ctors_section, bool);
         key!(eh_frame_header, bool);
         key!(has_thumb_interworking, bool);
+        key!(split_debuginfo, SplitDebuginfo)?;
 
         // NB: The old name is deprecated, but support for it is retained for
         // compatibility.
@@ -1862,6 +1943,7 @@ impl ToJson for Target {
         target_option_val!(use_ctors_section);
         target_option_val!(eh_frame_header);
         target_option_val!(has_thumb_interworking);
+        target_option_val!(split_debuginfo);
 
         if default.unsupported_abis != self.unsupported_abis {
             d.insert(
diff --git a/compiler/rustc_target/src/spec/msvc_base.rs b/compiler/rustc_target/src/spec/msvc_base.rs
index 8cd6735a8c1ec..39c0d5f0bb4ff 100644
--- a/compiler/rustc_target/src/spec/msvc_base.rs
+++ b/compiler/rustc_target/src/spec/msvc_base.rs
@@ -1,4 +1,4 @@
-use crate::spec::{LinkArgs, LinkerFlavor, LldFlavor, TargetOptions};
+use crate::spec::{LinkArgs, LinkerFlavor, LldFlavor, SplitDebuginfo, TargetOptions};
 
 pub fn opts() -> TargetOptions {
     let pre_link_args_msvc = vec![
@@ -27,6 +27,10 @@ pub fn opts() -> TargetOptions {
         abi_return_struct_as_int: true,
         emit_debug_gdb_scripts: false,
 
+        // Currently this is the only supported method of debuginfo on MSVC
+        // where `*.pdb` files show up next to the final artifact.
+        split_debuginfo: SplitDebuginfo::Packed,
+
         ..Default::default()
     }
 }
diff --git a/compiler/rustc_target/src/spec/wasm32_base.rs b/compiler/rustc_target/src/spec/wasm32_base.rs
index a7957d84cbeef..bfef3d37228f0 100644
--- a/compiler/rustc_target/src/spec/wasm32_base.rs
+++ b/compiler/rustc_target/src/spec/wasm32_base.rs
@@ -55,15 +55,6 @@ pub fn options() -> TargetOptions {
     // to do so.
     arg("--no-demangle");
 
-    // The symbol visibility story is a bit in flux right now with LLD.
-    // It's... not entirely clear to me what's going on, but this looks to
-    // make everything work when `export_symbols` isn't otherwise called for
-    // things like executables.
-    //
-    // This is really only here to get things working. If it can be removed and
-    // basic tests still work, then sounds like it should be removed!
-    arg("--export-dynamic");
-
     let mut pre_link_args = BTreeMap::new();
     pre_link_args.insert(LinkerFlavor::Lld(LldFlavor::Wasm), lld_args);
     pre_link_args.insert(LinkerFlavor::Gcc, clang_args);
diff --git a/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs b/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs
index c12757b8f9812..9f69ce16c215d 100644
--- a/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs
+++ b/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs
@@ -2,6 +2,17 @@ use super::wasm32_base;
 use super::{LinkArgs, LinkerFlavor, PanicStrategy, Target, TargetOptions};
 
 pub fn target() -> Target {
+    let mut options = wasm32_base::options();
+
+    let clang_args = options.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap();
+
+    // Rust really needs a way for users to specify exports and imports in
+    // the source code. --export-dynamic isn't the right tool for this job,
+    // however it does have the side effect of automatically exporting a lot
+    // of symbols, which approximates what people want when compiling for
+    // wasm32-unknown-unknown expect, so use it for now.
+    clang_args.push("--export-dynamic".to_string());
+
     let mut post_link_args = LinkArgs::new();
     post_link_args.insert(
         LinkerFlavor::Em,
@@ -28,7 +39,7 @@ pub fn target() -> Target {
         panic_strategy: PanicStrategy::Unwind,
         post_link_args,
         os_family: Some("unix".to_string()),
-        ..wasm32_base::options()
+        ..options
     };
     Target {
         llvm_target: "wasm32-unknown-emscripten".to_string(),
diff --git a/compiler/rustc_target/src/spec/wasm32_unknown_unknown.rs b/compiler/rustc_target/src/spec/wasm32_unknown_unknown.rs
index 6037aa5b4306e..5e89ba2520bdd 100644
--- a/compiler/rustc_target/src/spec/wasm32_unknown_unknown.rs
+++ b/compiler/rustc_target/src/spec/wasm32_unknown_unknown.rs
@@ -26,11 +26,18 @@ pub fn target() -> Target {
     // For now this target just never has an entry symbol no matter the output
     // type, so unconditionally pass this.
     clang_args.push("-Wl,--no-entry".to_string());
-    options
-        .pre_link_args
-        .get_mut(&LinkerFlavor::Lld(LldFlavor::Wasm))
-        .unwrap()
-        .push("--no-entry".to_string());
+
+    // Rust really needs a way for users to specify exports and imports in
+    // the source code. --export-dynamic isn't the right tool for this job,
+    // however it does have the side effect of automatically exporting a lot
+    // of symbols, which approximates what people want when compiling for
+    // wasm32-unknown-unknown expect, so use it for now.
+    clang_args.push("-Wl,--export-dynamic".to_string());
+
+    // Add the flags to wasm-ld's args too.
+    let lld_args = options.pre_link_args.get_mut(&LinkerFlavor::Lld(LldFlavor::Wasm)).unwrap();
+    lld_args.push("--no-entry".to_string());
+    lld_args.push("--export-dynamic".to_string());
 
     Target {
         llvm_target: "wasm32-unknown-unknown".to_string(),
diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs
index feda35daa617d..6b2cba62fa6b7 100644
--- a/compiler/rustc_typeck/src/check/upvar.rs
+++ b/compiler/rustc_typeck/src/check/upvar.rs
@@ -42,7 +42,7 @@ use rustc_infer::infer::UpvarRegion;
 use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId, ProjectionKind};
 use rustc_middle::ty::{self, Ty, TyCtxt, UpvarSubsts};
 use rustc_span::sym;
-use rustc_span::{Span, Symbol};
+use rustc_span::{MultiSpan, Span, Symbol};
 
 /// Describe the relationship between the paths of two places
 /// eg:
@@ -135,7 +135,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
                     let upvar_id = ty::UpvarId::new(var_hir_id, local_def_id);
                     let capture_kind = self.init_capture_kind(capture_clause, upvar_id, span);
-                    let info = ty::CaptureInfo { expr_id: None, capture_kind };
+                    let info = ty::CaptureInfo {
+                        capture_kind_expr_id: None,
+                        path_expr_id: None,
+                        capture_kind,
+                    };
 
                     capture_information.insert(place, info);
                 }
@@ -308,8 +312,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         if let Some(capture_kind) = upvar_capture_map.get(&upvar_id) {
                             // upvar_capture_map only stores the UpvarCapture (CaptureKind),
                             // so we create a fake capture info with no expression.
-                            let fake_capture_info =
-                                ty::CaptureInfo { expr_id: None, capture_kind: *capture_kind };
+                            let fake_capture_info = ty::CaptureInfo {
+                                capture_kind_expr_id: None,
+                                path_expr_id: None,
+                                capture_kind: *capture_kind,
+                            };
                             determine_capture_info(fake_capture_info, capture_info).capture_kind
                         } else {
                             capture_info.capture_kind
@@ -359,20 +366,44 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     ///
     /// ```
     /// {
-    ///       Place(base: hir_id_s, projections: [], ....) -> (hir_id_L5, ByValue),
-    ///       Place(base: hir_id_p, projections: [Field(0, 0)], ...) -> (hir_id_L2, ByRef(MutBorrow))
-    ///       Place(base: hir_id_p, projections: [Field(1, 0)], ...) -> (hir_id_L3, ByRef(ImmutBorrow))
-    ///       Place(base: hir_id_p, projections: [], ...) -> (hir_id_L4, ByRef(ImmutBorrow))
+    ///       Place(base: hir_id_s, projections: [], ....) -> {
+    ///                                                            capture_kind_expr: hir_id_L5,
+    ///                                                            path_expr_id: hir_id_L5,
+    ///                                                            capture_kind: ByValue
+    ///                                                       },
+    ///       Place(base: hir_id_p, projections: [Field(0, 0)], ...) -> {
+    ///                                                                     capture_kind_expr: hir_id_L2,
+    ///                                                                     path_expr_id: hir_id_L2,
+    ///                                                                     capture_kind: ByValue
+    ///                                                                 },
+    ///       Place(base: hir_id_p, projections: [Field(1, 0)], ...) -> {
+    ///                                                                     capture_kind_expr: hir_id_L3,
+    ///                                                                     path_expr_id: hir_id_L3,
+    ///                                                                     capture_kind: ByValue
+    ///                                                                 },
+    ///       Place(base: hir_id_p, projections: [], ...) -> {
+    ///                                                          capture_kind_expr: hir_id_L4,
+    ///                                                          path_expr_id: hir_id_L4,
+    ///                                                          capture_kind: ByValue
+    ///                                                      },
     /// ```
     ///
     /// After the min capture analysis, we get:
     /// ```
     /// {
     ///       hir_id_s -> [
-    ///            Place(base: hir_id_s, projections: [], ....) -> (hir_id_L4, ByValue)
+    ///            Place(base: hir_id_s, projections: [], ....) -> {
+    ///                                                                capture_kind_expr: hir_id_L5,
+    ///                                                                path_expr_id: hir_id_L5,
+    ///                                                                capture_kind: ByValue
+    ///                                                            },
     ///       ],
     ///       hir_id_p -> [
-    ///            Place(base: hir_id_p, projections: [], ...) -> (hir_id_L2, ByRef(MutBorrow)),
+    ///            Place(base: hir_id_p, projections: [], ...) -> {
+    ///                                                               capture_kind_expr: hir_id_L2,
+    ///                                                               path_expr_id: hir_id_L4,
+    ///                                                               capture_kind: ByValue
+    ///                                                           },
     ///       ],
     /// ```
     fn compute_min_captures(
@@ -425,8 +456,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     // current place is ancestor of possible_descendant
                     PlaceAncestryRelation::Ancestor => {
                         descendant_found = true;
+                        let backup_path_expr_id = updated_capture_info.path_expr_id;
+
                         updated_capture_info =
                             determine_capture_info(updated_capture_info, possible_descendant.info);
+
+                        // we need to keep the ancestor's `path_expr_id`
+                        updated_capture_info.path_expr_id = backup_path_expr_id;
                         false
                     }
 
@@ -441,9 +477,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         // current place is descendant of possible_ancestor
                         PlaceAncestryRelation::Descendant => {
                             ancestor_found = true;
+                            let backup_path_expr_id = possible_ancestor.info.path_expr_id;
                             possible_ancestor.info =
                                 determine_capture_info(possible_ancestor.info, capture_info);
 
+                            // we need to keep the ancestor's `path_expr_id`
+                            possible_ancestor.info.path_expr_id = backup_path_expr_id;
+
                             // Only one ancestor of the current place will be in the list.
                             break;
                         }
@@ -518,7 +558,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 let capture_str = construct_capture_info_string(self.tcx, place, capture_info);
                 let output_str = format!("Capturing {}", capture_str);
 
-                let span = capture_info.expr_id.map_or(closure_span, |e| self.tcx.hir().span(e));
+                let span =
+                    capture_info.path_expr_id.map_or(closure_span, |e| self.tcx.hir().span(e));
                 diag.span_note(span, &output_str);
             }
             diag.emit();
@@ -542,9 +583,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             construct_capture_info_string(self.tcx, place, capture_info);
                         let output_str = format!("Min Capture {}", capture_str);
 
-                        let span =
-                            capture_info.expr_id.map_or(closure_span, |e| self.tcx.hir().span(e));
-                        diag.span_note(span, &output_str);
+                        if capture.info.path_expr_id != capture.info.capture_kind_expr_id {
+                            let path_span = capture_info
+                                .path_expr_id
+                                .map_or(closure_span, |e| self.tcx.hir().span(e));
+                            let capture_kind_span = capture_info
+                                .capture_kind_expr_id
+                                .map_or(closure_span, |e| self.tcx.hir().span(e));
+
+                            let mut multi_span: MultiSpan =
+                                MultiSpan::from_spans(vec![path_span, capture_kind_span]);
+
+                            let capture_kind_label =
+                                construct_capture_kind_reason_string(self.tcx, place, capture_info);
+                            let path_label = construct_path_string(self.tcx, place);
+
+                            multi_span.push_span_label(path_span, path_label);
+                            multi_span.push_span_label(capture_kind_span, capture_kind_label);
+
+                            diag.span_note(multi_span, &output_str);
+                        } else {
+                            let span = capture_info
+                                .path_expr_id
+                                .map_or(closure_span, |e| self.tcx.hir().span(e));
+
+                            diag.span_note(span, &output_str);
+                        };
                     }
                 }
                 diag.emit();
@@ -642,7 +706,8 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
         );
 
         let capture_info = ty::CaptureInfo {
-            expr_id: Some(diag_expr_id),
+            capture_kind_expr_id: Some(diag_expr_id),
+            path_expr_id: Some(diag_expr_id),
             capture_kind: ty::UpvarCapture::ByValue(Some(usage_span)),
         };
 
@@ -762,7 +827,8 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
             let new_upvar_borrow = ty::UpvarBorrow { kind, region: curr_upvar_borrow.region };
 
             let capture_info = ty::CaptureInfo {
-                expr_id: Some(diag_expr_id),
+                capture_kind_expr_id: Some(diag_expr_id),
+                path_expr_id: Some(diag_expr_id),
                 capture_kind: ty::UpvarCapture::ByRef(new_upvar_borrow),
             };
             let updated_info = determine_capture_info(curr_capture_info, capture_info);
@@ -824,7 +890,11 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
                 self.fcx.init_capture_kind(self.capture_clause, upvar_id, self.closure_span);
 
             let expr_id = Some(diag_expr_id);
-            let capture_info = ty::CaptureInfo { expr_id, capture_kind };
+            let capture_info = ty::CaptureInfo {
+                capture_kind_expr_id: expr_id,
+                path_expr_id: expr_id,
+                capture_kind,
+            };
 
             debug!("Capturing new place {:?}, capture_info={:?}", place_with_id, capture_info);
 
@@ -890,11 +960,7 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
     }
 }
 
-fn construct_capture_info_string(
-    tcx: TyCtxt<'_>,
-    place: &Place<'tcx>,
-    capture_info: &ty::CaptureInfo<'tcx>,
-) -> String {
+fn construct_place_string(tcx: TyCtxt<'_>, place: &Place<'tcx>) -> String {
     let variable_name = match place.base {
         PlaceBase::Upvar(upvar_id) => var_name(tcx, upvar_id.var_path.hir_id).to_string(),
         _ => bug!("Capture_information should only contain upvars"),
@@ -914,11 +980,42 @@ fn construct_capture_info_string(
         projections_str.push_str(proj.as_str());
     }
 
+    format!("{}[{}]", variable_name, projections_str)
+}
+
+fn construct_capture_kind_reason_string(
+    tcx: TyCtxt<'_>,
+    place: &Place<'tcx>,
+    capture_info: &ty::CaptureInfo<'tcx>,
+) -> String {
+    let place_str = construct_place_string(tcx, &place);
+
     let capture_kind_str = match capture_info.capture_kind {
         ty::UpvarCapture::ByValue(_) => "ByValue".into(),
         ty::UpvarCapture::ByRef(borrow) => format!("{:?}", borrow.kind),
     };
-    format!("{}[{}] -> {}", variable_name, projections_str, capture_kind_str)
+
+    format!("{} captured as {} here", place_str, capture_kind_str)
+}
+
+fn construct_path_string(tcx: TyCtxt<'_>, place: &Place<'tcx>) -> String {
+    let place_str = construct_place_string(tcx, &place);
+
+    format!("{} used here", place_str)
+}
+
+fn construct_capture_info_string(
+    tcx: TyCtxt<'_>,
+    place: &Place<'tcx>,
+    capture_info: &ty::CaptureInfo<'tcx>,
+) -> String {
+    let place_str = construct_place_string(tcx, &place);
+
+    let capture_kind_str = match capture_info.capture_kind {
+        ty::UpvarCapture::ByValue(_) => "ByValue".into(),
+        ty::UpvarCapture::ByRef(borrow) => format!("{:?}", borrow.kind),
+    };
+    format!("{} -> {}", place_str, capture_kind_str)
 }
 
 fn var_name(tcx: TyCtxt<'_>, var_hir_id: hir::HirId) -> Symbol {
@@ -930,7 +1027,9 @@ fn var_name(tcx: TyCtxt<'_>, var_hir_id: hir::HirId) -> Symbol {
 /// (Note: CaptureInfo contains CaptureKind and an expression that led to capture it in that way)
 ///
 /// If both `CaptureKind`s are considered equivalent, then the CaptureInfo is selected based
-/// on the `CaptureInfo` containing an associated expression id.
+/// on the `CaptureInfo` containing an associated `capture_kind_expr_id`.
+///
+/// It is the caller's duty to figure out which path_expr_id to use.
 ///
 /// If both the CaptureKind and Expression are considered to be equivalent,
 /// then `CaptureInfo` A is preferred. This can be useful in cases where we want to priortize
@@ -981,7 +1080,7 @@ fn determine_capture_info(
     };
 
     if eq_capture_kind {
-        match (capture_info_a.expr_id, capture_info_b.expr_id) {
+        match (capture_info_a.capture_kind_expr_id, capture_info_b.capture_kind_expr_id) {
             (Some(_), _) | (None, None) => capture_info_a,
             (None, Some(_)) => capture_info_b,
         }
diff --git a/compiler/rustc_typeck/src/check/writeback.rs b/compiler/rustc_typeck/src/check/writeback.rs
index 9e6bf5ce52d8a..b6d740a4fdb57 100644
--- a/compiler/rustc_typeck/src/check/writeback.rs
+++ b/compiler/rustc_typeck/src/check/writeback.rs
@@ -348,7 +348,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
                 let min_list_wb = min_list
                     .iter()
                     .map(|captured_place| {
-                        let locatable = captured_place.info.expr_id.unwrap_or(
+                        let locatable = captured_place.info.path_expr_id.unwrap_or(
                             self.tcx().hir().local_def_id_to_hir_id(closure_def_id.expect_local()),
                         );
 
diff --git a/compiler/rustc_typeck/src/expr_use_visitor.rs b/compiler/rustc_typeck/src/expr_use_visitor.rs
index 24364c6954e49..bd2c266d93dca 100644
--- a/compiler/rustc_typeck/src/expr_use_visitor.rs
+++ b/compiler/rustc_typeck/src/expr_use_visitor.rs
@@ -630,7 +630,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
                         PlaceBase::Local(*var_hir_id)
                     };
                     let place_with_id = PlaceWithHirId::new(
-                        capture_info.expr_id.unwrap_or(closure_expr.hir_id),
+                        capture_info.path_expr_id.unwrap_or(closure_expr.hir_id),
                         place.base_ty,
                         place_base,
                         place.projections.clone(),
diff --git a/library/core/src/convert/mod.rs b/library/core/src/convert/mod.rs
index 139863bbe7f81..a6d3b5ef813f1 100644
--- a/library/core/src/convert/mod.rs
+++ b/library/core/src/convert/mod.rs
@@ -267,6 +267,7 @@ pub trait AsMut<T: ?Sized> {
 ///
 /// [`String`]: ../../std/string/struct.String.html
 /// [`Vec`]: ../../std/vec/struct.Vec.html
+#[rustc_diagnostic_item = "into_trait"]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub trait Into<T>: Sized {
     /// Performs the conversion.
@@ -382,6 +383,7 @@ pub trait From<T>: Sized {
 ///
 /// This suffers the same restrictions and reasoning as implementing
 /// [`Into`], see there for details.
+#[rustc_diagnostic_item = "try_into_trait"]
 #[stable(feature = "try_from", since = "1.34.0")]
 pub trait TryInto<T>: Sized {
     /// The type returned in the event of a conversion error.
@@ -462,6 +464,7 @@ pub trait TryInto<T>: Sized {
 ///
 /// [`try_from`]: TryFrom::try_from
 /// [`!`]: ../../std/primitive.never.html
+#[rustc_diagnostic_item = "try_from_trait"]
 #[stable(feature = "try_from", since = "1.34.0")]
 pub trait TryFrom<T>: Sized {
     /// The type returned in the event of a conversion error.
diff --git a/library/core/src/iter/adapters/flatten.rs b/library/core/src/iter/adapters/flatten.rs
index 29e191db0f62f..081f282edcf89 100644
--- a/library/core/src/iter/adapters/flatten.rs
+++ b/library/core/src/iter/adapters/flatten.rs
@@ -265,7 +265,13 @@ where
                 }
             }
             match self.iter.next() {
-                None => return self.backiter.as_mut()?.next(),
+                None => match self.backiter.as_mut()?.next() {
+                    None => {
+                        self.backiter = None;
+                        return None;
+                    }
+                    elt @ Some(_) => return elt,
+                },
                 Some(inner) => self.frontiter = Some(inner.into_iter()),
             }
         }
@@ -353,7 +359,13 @@ where
                 }
             }
             match self.iter.next_back() {
-                None => return self.frontiter.as_mut()?.next_back(),
+                None => match self.frontiter.as_mut()?.next_back() {
+                    None => {
+                        self.frontiter = None;
+                        return None;
+                    }
+                    elt @ Some(_) => return elt,
+                },
                 next => self.backiter = next.map(IntoIterator::into_iter),
             }
         }
diff --git a/library/core/tests/iter/adapters/flatten.rs b/library/core/tests/iter/adapters/flatten.rs
index bd2c6fd9252df..4bbae6947bf66 100644
--- a/library/core/tests/iter/adapters/flatten.rs
+++ b/library/core/tests/iter/adapters/flatten.rs
@@ -64,6 +64,14 @@ fn test_flatten_non_fused_outer() {
     assert_eq!(iter.next_back(), Some(1));
     assert_eq!(iter.next(), Some(0));
     assert_eq!(iter.next(), None);
+    assert_eq!(iter.next(), None);
+
+    let mut iter = NonFused::new(once(0..2)).flatten();
+
+    assert_eq!(iter.next(), Some(0));
+    assert_eq!(iter.next_back(), Some(1));
+    assert_eq!(iter.next_back(), None);
+    assert_eq!(iter.next_back(), None);
 }
 
 #[test]
@@ -74,6 +82,15 @@ fn test_flatten_non_fused_inner() {
     assert_eq!(iter.next(), Some(0));
     assert_eq!(iter.next(), Some(1));
     assert_eq!(iter.next(), None);
+    assert_eq!(iter.next(), None);
+
+    let mut iter = once(0..1).chain(once(1..3)).flat_map(NonFused::new);
+
+    assert_eq!(iter.next(), Some(0));
+    assert_eq!(iter.next_back(), Some(2));
+    assert_eq!(iter.next_back(), Some(1));
+    assert_eq!(iter.next_back(), None);
+    assert_eq!(iter.next_back(), None);
 }
 
 #[test]
diff --git a/library/test/src/test_result.rs b/library/test/src/test_result.rs
index 465f3f8f99427..598fb670bb4bf 100644
--- a/library/test/src/test_result.rs
+++ b/library/test/src/test_result.rs
@@ -63,7 +63,7 @@ pub fn calc_result<'a>(
                 ))
             }
         }
-        (&ShouldPanic::Yes, Ok(())) => {
+        (&ShouldPanic::Yes, Ok(())) | (&ShouldPanic::YesWithMessage(_), Ok(())) => {
             TestResult::TrFailedMsg("test did not panic as expected".to_string())
         }
         _ if desc.allow_fail => TestResult::TrAllowedFail,
diff --git a/library/test/src/tests.rs b/library/test/src/tests.rs
index 74313cc4438ed..a629829b88514 100644
--- a/library/test/src/tests.rs
+++ b/library/test/src/tests.rs
@@ -228,21 +228,30 @@ fn test_should_panic_non_string_message_type() {
 #[test]
 #[cfg(not(target_os = "emscripten"))]
 fn test_should_panic_but_succeeds() {
-    fn f() {}
-    let desc = TestDescAndFn {
-        desc: TestDesc {
-            name: StaticTestName("whatever"),
-            ignore: false,
-            should_panic: ShouldPanic::Yes,
-            allow_fail: false,
-            test_type: TestType::Unknown,
-        },
-        testfn: DynTestFn(Box::new(f)),
-    };
-    let (tx, rx) = channel();
-    run_test(&TestOpts::new(), false, desc, RunStrategy::InProcess, tx, Concurrent::No);
-    let result = rx.recv().unwrap().result;
-    assert_eq!(result, TrFailedMsg("test did not panic as expected".to_string()));
+    let should_panic_variants = [ShouldPanic::Yes, ShouldPanic::YesWithMessage("error message")];
+
+    for &should_panic in should_panic_variants.iter() {
+        fn f() {}
+        let desc = TestDescAndFn {
+            desc: TestDesc {
+                name: StaticTestName("whatever"),
+                ignore: false,
+                should_panic,
+                allow_fail: false,
+                test_type: TestType::Unknown,
+            },
+            testfn: DynTestFn(Box::new(f)),
+        };
+        let (tx, rx) = channel();
+        run_test(&TestOpts::new(), false, desc, RunStrategy::InProcess, tx, Concurrent::No);
+        let result = rx.recv().unwrap().result;
+        assert_eq!(
+            result,
+            TrFailedMsg("test did not panic as expected".to_string()),
+            "should_panic == {:?}",
+            should_panic
+        );
+    }
 }
 
 fn report_time_test_template(report_time: bool) -> Option<TestExecTime> {
diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs
index 62065e27dd966..2f655e3b396f1 100644
--- a/src/bootstrap/builder.rs
+++ b/src/bootstrap/builder.rs
@@ -1139,10 +1139,18 @@ impl<'a> Builder<'a> {
         // itself, we skip it by default since we know it's safe to do so in that case.
         // See https://github.com/rust-lang/rust/issues/79361 for more info on this flag.
         if target.contains("apple") {
-            if self.config.rust_run_dsymutil {
-                rustflags.arg("-Zrun-dsymutil=yes");
+            if stage == 0 {
+                if self.config.rust_run_dsymutil {
+                    rustflags.arg("-Zrun-dsymutil=yes");
+                } else {
+                    rustflags.arg("-Zrun-dsymutil=no");
+                }
             } else {
-                rustflags.arg("-Zrun-dsymutil=no");
+                if self.config.rust_run_dsymutil {
+                    rustflags.arg("-Csplit-debuginfo=packed");
+                } else {
+                    rustflags.arg("-Csplit-debuginfo=unpacked");
+                }
             }
         }
 
diff --git a/src/doc/rustc/src/codegen-options/index.md b/src/doc/rustc/src/codegen-options/index.md
index f6493e49c3c3e..51e7d987d9d82 100644
--- a/src/doc/rustc/src/codegen-options/index.md
+++ b/src/doc/rustc/src/codegen-options/index.md
@@ -492,6 +492,34 @@ point instructions in software. It takes one of the following values:
 * `y`, `yes`, `on`, or no value: use soft floats.
 * `n`, `no`, or `off`: use hardware floats (the default).
 
+## split-debuginfo
+
+This option controls the emission of "split debuginfo" for debug information
+that `rustc` generates. The default behavior of this option is
+platform-specific, and not all possible values for this option work on all
+platform. Possible values are:
+
+* `off` - This is the default for platforms with ELF binaries and windows-gnu
+  (not Windows MSVC and not macOS). This typically means that dwarf debug
+  information can be found in the final artifact in sections of the executable.
+  This option is not supported on Windows MSVC. On macOS this options prevents
+  the final execution of `dsymutil` to generate debuginfo.
+
+* `packed` - This is the default for Windows MSVC and macOS platforms. The term
+  "packed" here means that all the debug information is packed into a separate
+  file from the main executable. On Windows MSVC this is a `*.pdb` file, on
+  macOS this is a `*.dSYM` folder, and on other platforms this is a `*.dwp`
+  files.
+
+* `unpacked` - This means that debug information will be found in separate
+  files for each compilation unit (object file). This is not supported on
+  Windows MSVC. On macOS this means the original object files will contain
+  debug information. On other Unix platforms this means that `*.dwo` files will
+  contain debug information.
+
+Note that `packed` and `unpacked` gated behind `-Zunstable-options` on
+non-macOS platforms at this time.
+
 ## target-cpu
 
 This instructs `rustc` to generate code specifically for a particular processor.
@@ -499,7 +527,7 @@ This instructs `rustc` to generate code specifically for a particular processor.
 You can run `rustc --print target-cpus` to see the valid options to pass
 here. Each target has a default base CPU. Special values include:
 
-* `native` can be passed to use the processor of the host machine. 
+* `native` can be passed to use the processor of the host machine.
 * `generic` refers to an LLVM target with minimal features but modern tuning.
 
 ## target-feature
diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml
index b0f5bac6abd0f..db64b31f31cfc 100644
--- a/src/librustdoc/Cargo.toml
+++ b/src/librustdoc/Cargo.toml
@@ -17,6 +17,7 @@ smallvec = "1.0"
 tempfile = "3"
 itertools = "0.9"
 regex = "1"
+rustdoc-json-types = { path = "../rustdoc-json-types" }
 
 [dev-dependencies]
 expect-test = "1.0"
diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs
index 0f5495c831058..60bd3c984b728 100644
--- a/src/librustdoc/clean/utils.rs
+++ b/src/librustdoc/clean/utils.rs
@@ -487,12 +487,16 @@ crate fn get_auto_trait_and_blanket_impls(
     ty: Ty<'tcx>,
     param_env_def_id: DefId,
 ) -> impl Iterator<Item = Item> {
-    let auto_impls = cx.sess().time("get_auto_trait_impls", || {
-        AutoTraitFinder::new(cx).get_auto_trait_impls(ty, param_env_def_id)
-    });
-    let blanket_impls = cx.sess().time("get_blanket_impls", || {
-        BlanketImplFinder::new(cx).get_blanket_impls(ty, param_env_def_id)
-    });
+    let auto_impls = cx
+        .sess()
+        .prof
+        .generic_activity("get_auto_trait_impls")
+        .run(|| AutoTraitFinder::new(cx).get_auto_trait_impls(ty, param_env_def_id));
+    let blanket_impls = cx
+        .sess()
+        .prof
+        .generic_activity("get_blanket_impls")
+        .run(|| BlanketImplFinder::new(cx).get_blanket_impls(ty, param_env_def_id));
     auto_impls.into_iter().chain(blanket_impls)
 }
 
diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs
index bfd2141d9a174..b2e5c8834b8ff 100644
--- a/src/librustdoc/json/conversions.rs
+++ b/src/librustdoc/json/conversions.rs
@@ -9,9 +9,10 @@ use rustc_hir::def::CtorKind;
 use rustc_span::def_id::{DefId, CRATE_DEF_INDEX};
 use rustc_span::Pos;
 
+use rustdoc_json_types::*;
+
 use crate::clean;
 use crate::formats::item_type::ItemType;
-use crate::json::types::*;
 use crate::json::JsonRenderer;
 
 impl JsonRenderer<'_> {
@@ -22,7 +23,7 @@ impl JsonRenderer<'_> {
         match *kind {
             clean::StrippedItem(_) => None,
             kind => Some(Item {
-                id: def_id.into(),
+                id: from_def_id(def_id),
                 crate_id: def_id.krate.as_u32(),
                 name: name.map(|sym| sym.to_string()),
                 source: self.convert_span(source),
@@ -32,7 +33,7 @@ impl JsonRenderer<'_> {
                     .links
                     .into_iter()
                     .filter_map(|clean::ItemLink { link, did, .. }| {
-                        did.map(|did| (link, did.into()))
+                        did.map(|did| (link, from_def_id(did)))
                     })
                     .collect(),
                 attrs: attrs
@@ -40,7 +41,7 @@ impl JsonRenderer<'_> {
                     .iter()
                     .map(rustc_ast_pretty::pprust::attribute_to_string)
                     .collect(),
-                deprecation: deprecation.map(Into::into),
+                deprecation: deprecation.map(from_deprecation),
                 kind: item_type.into(),
                 inner: kind.into(),
             }),
@@ -74,19 +75,17 @@ impl JsonRenderer<'_> {
             Inherited => Visibility::Default,
             Restricted(did) if did.index == CRATE_DEF_INDEX => Visibility::Crate,
             Restricted(did) => Visibility::Restricted {
-                parent: did.into(),
+                parent: from_def_id(did),
                 path: self.tcx.def_path(did).to_string_no_crate_verbose(),
             },
         }
     }
 }
 
-impl From<rustc_attr::Deprecation> for Deprecation {
-    fn from(deprecation: rustc_attr::Deprecation) -> Self {
-        #[rustfmt::skip]
-        let rustc_attr::Deprecation { since, note, is_since_rustc_version: _, suggestion: _ } = deprecation;
-        Deprecation { since: since.map(|s| s.to_string()), note: note.map(|s| s.to_string()) }
-    }
+crate fn from_deprecation(deprecation: rustc_attr::Deprecation) -> Deprecation {
+    #[rustfmt::skip]
+    let rustc_attr::Deprecation { since, note, is_since_rustc_version: _, suggestion: _ } = deprecation;
+    Deprecation { since: since.map(|s| s.to_string()), note: note.map(|s| s.to_string()) }
 }
 
 impl From<clean::GenericArgs> for GenericArgs {
@@ -141,10 +140,8 @@ impl From<clean::TypeBindingKind> for TypeBindingKind {
     }
 }
 
-impl From<DefId> for Id {
-    fn from(did: DefId) -> Self {
-        Id(format!("{}:{}", did.krate.as_u32(), u32::from(did.index)))
-    }
+crate fn from_def_id(did: DefId) -> Id {
+    Id(format!("{}:{}", did.krate.as_u32(), u32::from(did.index)))
 }
 
 impl From<clean::ItemKind> for ItemEnum {
@@ -199,7 +196,7 @@ impl From<clean::Struct> for Struct {
     fn from(struct_: clean::Struct) -> Self {
         let clean::Struct { struct_type, generics, fields, fields_stripped } = struct_;
         Struct {
-            struct_type: struct_type.into(),
+            struct_type: from_ctor_kind(struct_type),
             generics: generics.into(),
             fields_stripped,
             fields: ids(fields),
@@ -221,13 +218,11 @@ impl From<clean::Union> for Struct {
     }
 }
 
-impl From<CtorKind> for StructType {
-    fn from(struct_type: CtorKind) -> Self {
-        match struct_type {
-            CtorKind::Fictive => StructType::Plain,
-            CtorKind::Fn => StructType::Tuple,
-            CtorKind::Const => StructType::Unit,
-        }
+crate fn from_ctor_kind(struct_type: CtorKind) -> StructType {
+    match struct_type {
+        CtorKind::Fictive => StructType::Plain,
+        CtorKind::Fn => StructType::Tuple,
+        CtorKind::Const => StructType::Unit,
     }
 }
 
@@ -310,7 +305,7 @@ impl From<clean::GenericBound> for GenericBound {
                 GenericBound::TraitBound {
                     trait_: trait_.into(),
                     generic_params: generic_params.into_iter().map(Into::into).collect(),
-                    modifier: modifier.into(),
+                    modifier: from_trait_bound_modifier(modifier),
                 }
             }
             Outlives(lifetime) => GenericBound::Outlives(lifetime.0.to_string()),
@@ -318,14 +313,12 @@ impl From<clean::GenericBound> for GenericBound {
     }
 }
 
-impl From<rustc_hir::TraitBoundModifier> for TraitBoundModifier {
-    fn from(modifier: rustc_hir::TraitBoundModifier) -> Self {
-        use rustc_hir::TraitBoundModifier::*;
-        match modifier {
-            None => TraitBoundModifier::None,
-            Maybe => TraitBoundModifier::Maybe,
-            MaybeConst => TraitBoundModifier::MaybeConst,
-        }
+crate fn from_trait_bound_modifier(modifier: rustc_hir::TraitBoundModifier) -> TraitBoundModifier {
+    use rustc_hir::TraitBoundModifier::*;
+    match modifier {
+        None => TraitBoundModifier::None,
+        Maybe => TraitBoundModifier::Maybe,
+        MaybeConst => TraitBoundModifier::MaybeConst,
     }
 }
 
@@ -335,7 +328,7 @@ impl From<clean::Type> for Type {
         match ty {
             ResolvedPath { path, param_names, did, is_generic: _ } => Type::ResolvedPath {
                 name: path.whole_name(),
-                id: did.into(),
+                id: from_def_id(did),
                 args: path.segments.last().map(|args| Box::new(args.clone().args.into())),
                 param_names: param_names
                     .map(|v| v.into_iter().map(Into::into).collect())
@@ -470,7 +463,7 @@ impl From<clean::VariantStruct> for Struct {
     fn from(struct_: clean::VariantStruct) -> Self {
         let clean::VariantStruct { struct_type, fields, fields_stripped } = struct_;
         Struct {
-            struct_type: struct_type.into(),
+            struct_type: from_ctor_kind(struct_type),
             generics: Default::default(),
             fields_stripped,
             fields: ids(fields),
@@ -497,13 +490,13 @@ impl From<clean::Import> for Import {
             Simple(s) => Import {
                 span: import.source.path.whole_name(),
                 name: s.to_string(),
-                id: import.source.did.map(Into::into),
+                id: import.source.did.map(from_def_id),
                 glob: false,
             },
             Glob => Import {
                 span: import.source.path.whole_name(),
                 name: import.source.path.last_name().to_string(),
-                id: import.source.did.map(Into::into),
+                id: import.source.did.map(from_def_id),
                 glob: true,
             },
         }
@@ -513,20 +506,18 @@ impl From<clean::Import> for Import {
 impl From<clean::ProcMacro> for ProcMacro {
     fn from(mac: clean::ProcMacro) -> Self {
         ProcMacro {
-            kind: mac.kind.into(),
+            kind: from_macro_kind(mac.kind),
             helpers: mac.helpers.iter().map(|x| x.to_string()).collect(),
         }
     }
 }
 
-impl From<rustc_span::hygiene::MacroKind> for MacroKind {
-    fn from(kind: rustc_span::hygiene::MacroKind) -> Self {
-        use rustc_span::hygiene::MacroKind::*;
-        match kind {
-            Bang => MacroKind::Bang,
-            Attr => MacroKind::Attr,
-            Derive => MacroKind::Derive,
-        }
+crate fn from_macro_kind(kind: rustc_span::hygiene::MacroKind) -> MacroKind {
+    use rustc_span::hygiene::MacroKind::*;
+    match kind {
+        Bang => MacroKind::Bang,
+        Attr => MacroKind::Attr,
+        Derive => MacroKind::Derive,
     }
 }
 
@@ -599,5 +590,5 @@ impl From<ItemType> for ItemKind {
 }
 
 fn ids(items: impl IntoIterator<Item = clean::Item>) -> Vec<Id> {
-    items.into_iter().filter(|x| !x.is_stripped()).map(|i| i.def_id.into()).collect()
+    items.into_iter().filter(|x| !x.is_stripped()).map(|i| from_def_id(i.def_id)).collect()
 }
diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs
index 512c9124727ef..8ff650c323f55 100644
--- a/src/librustdoc/json/mod.rs
+++ b/src/librustdoc/json/mod.rs
@@ -5,7 +5,6 @@
 //! docs for usage and details.
 
 mod conversions;
-pub mod types;
 
 use std::cell::RefCell;
 use std::fs::File;
@@ -17,12 +16,15 @@ use rustc_middle::ty::TyCtxt;
 use rustc_session::Session;
 use rustc_span::edition::Edition;
 
+use rustdoc_json_types as types;
+
 use crate::clean;
 use crate::config::{RenderInfo, RenderOptions};
 use crate::error::Error;
 use crate::formats::cache::Cache;
 use crate::formats::FormatRenderer;
 use crate::html::render::cache::ExternalLocation;
+use crate::json::conversions::from_def_id;
 
 #[derive(Clone)]
 crate struct JsonRenderer<'tcx> {
@@ -53,7 +55,7 @@ impl JsonRenderer<'_> {
                     .map(|i| {
                         let item = &i.impl_item;
                         self.item(item.clone(), cache).unwrap();
-                        item.def_id.into()
+                        from_def_id(item.def_id)
                     })
                     .collect()
             })
@@ -71,7 +73,7 @@ impl JsonRenderer<'_> {
                         let item = &i.impl_item;
                         if item.def_id.is_local() {
                             self.item(item.clone(), cache).unwrap();
-                            Some(item.def_id.into())
+                            Some(from_def_id(item.def_id))
                         } else {
                             None
                         }
@@ -90,9 +92,9 @@ impl JsonRenderer<'_> {
                 if !id.is_local() {
                     trait_item.items.clone().into_iter().for_each(|i| self.item(i, cache).unwrap());
                     Some((
-                        id.into(),
+                        from_def_id(id),
                         types::Item {
-                            id: id.into(),
+                            id: from_def_id(id),
                             crate_id: id.krate.as_u32(),
                             name: cache
                                 .paths
@@ -164,7 +166,7 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
             } else if let types::ItemEnum::EnumItem(ref mut e) = new_item.inner {
                 e.impls = self.get_impls(id, cache)
             }
-            let removed = self.index.borrow_mut().insert(id.into(), new_item.clone());
+            let removed = self.index.borrow_mut().insert(from_def_id(id), new_item.clone());
             // FIXME(adotinthevoid): Currently, the index is duplicated. This is a sanity check
             // to make sure the items are unique.
             if let Some(old_item) = removed {
@@ -216,7 +218,7 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
             root: types::Id(String::from("0:0")),
             crate_version: krate.version.clone(),
             includes_private: cache.document_private,
-            index,
+            index: index.into_iter().collect(),
             paths: cache
                 .paths
                 .clone()
@@ -224,7 +226,7 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
                 .chain(cache.external_paths.clone().into_iter())
                 .map(|(k, (path, kind))| {
                     (
-                        k.into(),
+                        from_def_id(k),
                         types::ItemSummary { crate_id: k.krate.as_u32(), path, kind: kind.into() },
                     )
                 })
diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs
index 7b5e9e5905f33..6ec6620681bf5 100644
--- a/src/librustdoc/passes/collect_trait_impls.rs
+++ b/src/librustdoc/passes/collect_trait_impls.rs
@@ -30,7 +30,7 @@ crate fn collect_trait_impls(krate: Crate, cx: &DocContext<'_>) -> Crate {
 
     for &cnum in cx.tcx.crates().iter() {
         for &(did, _) in cx.tcx.all_trait_implementations(cnum).iter() {
-            cx.tcx.sess.time("build_extern_trait_impl", || {
+            cx.tcx.sess.prof.generic_activity("build_extern_trait_impl").run(|| {
                 inline::build_impl(cx, None, did, None, &mut new_items);
             });
         }
@@ -39,7 +39,7 @@ crate fn collect_trait_impls(krate: Crate, cx: &DocContext<'_>) -> Crate {
     // Also try to inline primitive impls from other crates.
     for &def_id in PrimitiveType::all_impls(cx.tcx).values().flatten() {
         if !def_id.is_local() {
-            cx.sess().time("build_primitive_trait_impl", || {
+            cx.tcx.sess.prof.generic_activity("build_primitive_trait_impls").run(|| {
                 inline::build_impl(cx, None, def_id, None, &mut new_items);
 
                 // FIXME(eddyb) is this `doc(hidden)` check needed?
@@ -59,7 +59,7 @@ crate fn collect_trait_impls(krate: Crate, cx: &DocContext<'_>) -> Crate {
     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);
-            cx.tcx.sess.time("build_local_trait_impl", || {
+            cx.tcx.sess.prof.generic_activity("build_local_trait_impl").run(|| {
                 let mut extra_attrs = Vec::new();
                 let mut parent = cx.tcx.parent(impl_did.to_def_id());
                 while let Some(did) = parent {
@@ -177,13 +177,11 @@ impl<'a, 'tcx> DocFolder for SyntheticImplCollector<'a, 'tcx> {
         if i.is_struct() || i.is_enum() || i.is_union() {
             // FIXME(eddyb) is this `doc(hidden)` check needed?
             if !self.cx.tcx.get_attrs(i.def_id).lists(sym::doc).has_word(sym::hidden) {
-                self.cx.sess().time("get_auto_trait_and_blanket_synthetic_impls", || {
-                    self.impls.extend(get_auto_trait_and_blanket_impls(
-                        self.cx,
-                        self.cx.tcx.type_of(i.def_id),
-                        i.def_id,
-                    ));
-                });
+                self.impls.extend(get_auto_trait_and_blanket_impls(
+                    self.cx,
+                    self.cx.tcx.type_of(i.def_id),
+                    i.def_id,
+                ));
             }
         }
 
diff --git a/src/rustdoc-json-types/Cargo.toml b/src/rustdoc-json-types/Cargo.toml
new file mode 100644
index 0000000000000..7bba16a68b96c
--- /dev/null
+++ b/src/rustdoc-json-types/Cargo.toml
@@ -0,0 +1,11 @@
+[package]
+name = "rustdoc-json-types"
+version = "0.1.0"
+authors = ["The Rust Project Developers"]
+edition = "2018"
+
+[lib]
+path = "lib.rs"
+
+[dependencies]
+serde = { version = "1.0", features = ["derive"] }
diff --git a/src/rustdoc-json-types/README.md b/src/rustdoc-json-types/README.md
new file mode 100644
index 0000000000000..17894c3c61d03
--- /dev/null
+++ b/src/rustdoc-json-types/README.md
@@ -0,0 +1,12 @@
+# Rustdoc JSON Types
+
+This crate exposes the Rustdoc JSON API as a set of types with serde implementations.
+These types are part of the public interface of the rustdoc JSON output, and making them
+their own crate allows them to be versioned and distributed without having to depend on
+any rustc/rustdoc internals. This way, consumers can rely on this crate for both documentation
+of the output, and as a way to read the output easily, and its versioning is intended to
+follow semver guarantees about the version of the format. JSON format X will always be
+compatible with rustdoc-json-types version N.
+
+Currently, this crate is only used by rustdoc itself. Upon the stabilization of
+rustdoc-json, it may be start to be distributed separately for consumers of the API.
diff --git a/src/librustdoc/json/types.rs b/src/rustdoc-json-types/lib.rs
similarity index 98%
rename from src/librustdoc/json/types.rs
rename to src/rustdoc-json-types/lib.rs
index 66cf12954dd0b..3fb2a32d5a0a3 100644
--- a/src/librustdoc/json/types.rs
+++ b/src/rustdoc-json-types/lib.rs
@@ -3,9 +3,9 @@
 //! These types are the public API exposed through the `--output-format json` flag. The [`Crate`]
 //! struct is the root of the JSON blob and all other items are contained within.
 
+use std::collections::HashMap;
 use std::path::PathBuf;
 
-use rustc_data_structures::fx::FxHashMap;
 use serde::{Deserialize, Serialize};
 
 /// A `Crate` is the root of the emitted JSON blob. It contains all type/documentation information
@@ -21,11 +21,11 @@ pub struct Crate {
     pub includes_private: bool,
     /// A collection of all items in the local crate as well as some external traits and their
     /// items that are referenced locally.
-    pub index: FxHashMap<Id, Item>,
+    pub index: HashMap<Id, Item>,
     /// Maps IDs to fully qualified paths and other info helpful for generating links.
-    pub paths: FxHashMap<Id, ItemSummary>,
+    pub paths: HashMap<Id, ItemSummary>,
     /// Maps `crate_id` of items to a crate name and html_root_url if it exists.
-    pub external_crates: FxHashMap<u32, ExternalCrate>,
+    pub external_crates: HashMap<u32, ExternalCrate>,
     /// A single version number to be used in the future when making backwards incompatible changes
     /// to the JSON output.
     pub format_version: u32,
@@ -72,7 +72,7 @@ pub struct Item {
     /// Some("") if there is some documentation but it is empty (EG `#[doc = ""]`).
     pub docs: Option<String>,
     /// This mapping resolves [intra-doc links](https://github.com/rust-lang/rfcs/blob/master/text/1946-intra-rustdoc-links.md) from the docstring to their IDs
-    pub links: FxHashMap<String, Id>,
+    pub links: HashMap<String, Id>,
     /// Stringified versions of the attributes on this item (e.g. `"#[inline]"`)
     pub attrs: Vec<String>,
     pub deprecation: Option<Deprecation>,
diff --git a/src/test/run-make-fulldeps/split-debuginfo/Makefile b/src/test/run-make-fulldeps/split-debuginfo/Makefile
new file mode 100644
index 0000000000000..e8e62efe01c14
--- /dev/null
+++ b/src/test/run-make-fulldeps/split-debuginfo/Makefile
@@ -0,0 +1,59 @@
+-include ../tools.mk
+
+all: off packed unpacked
+
+ifeq ($(UNAME),Darwin)
+# If disabled, don't run dsymutil
+off:
+	rm -rf $(TMPDIR)/*.dSYM
+	$(RUSTC) foo.rs -g -C split-debuginfo=off
+	[ ! -d $(TMPDIR)/foo.dSYM ]
+
+# Packed by default, but only if debuginfo is requested
+packed:
+	rm -rf $(TMPDIR)/*.dSYM
+	$(RUSTC) foo.rs
+	[ ! -d $(TMPDIR)/foo.dSYM ]
+	rm -rf $(TMPDIR)/*.dSYM
+	$(RUSTC) foo.rs -g
+	[ -d $(TMPDIR)/foo.dSYM ]
+	rm -rf $(TMPDIR)/*.dSYM
+	$(RUSTC) foo.rs -g -C split-debuginfo=packed
+	[ -d $(TMPDIR)/foo.dSYM ]
+	rm -rf $(TMPDIR)/*.dSYM
+
+# Object files are preserved with unpacked and `dsymutil` isn't run
+unpacked:
+	$(RUSTC) foo.rs -g -C split-debuginfo=unpacked
+	ls $(TMPDIR)/*.o
+	[ ! -d $(TMPDIR)/foo.dSYM ]
+else
+ifdef IS_WINDOWS
+# Windows only supports =off
+off:
+packed:
+unpacked:
+else
+# If disabled, don't run dsymutil
+off:
+	$(RUSTC) foo.rs -g -C split-debuginfo=off -Z unstable-options
+	[ ! -f $(TMPDIR)/*.dwp ]
+	[ ! -f $(TMPDIR)/*.dwo ]
+
+	$(RUSTC) foo.rs -g
+	[ ! -f $(TMPDIR)/*.dwp ]
+	[ ! -f $(TMPDIR)/*.dwo ]
+
+packed:
+	$(RUSTC) foo.rs -g -C split-debuginfo=packed -Z unstable-options
+	ls $(TMPDIR)/*.dwp
+	ls $(TMPDIR)/*.dwo && exit 1 || exit 0
+	rm -rf $(TMPDIR)/*.dwp
+
+unpacked:
+	$(RUSTC) foo.rs -g -C split-debuginfo=unpacked -Z unstable-options
+	ls $(TMPDIR)/*.dwp && exit 1 || exit 0
+	ls $(TMPDIR)/*.dwo
+	rm -rf $(TMPDIR)/*.dwo
+endif
+endif
diff --git a/src/test/run-make-fulldeps/split-debuginfo/foo.rs b/src/test/run-make-fulldeps/split-debuginfo/foo.rs
new file mode 100644
index 0000000000000..f328e4d9d04c3
--- /dev/null
+++ b/src/test/run-make-fulldeps/split-debuginfo/foo.rs
@@ -0,0 +1 @@
+fn main() {}
diff --git a/src/test/run-make-fulldeps/split-dwarf/Makefile b/src/test/run-make-fulldeps/split-dwarf/Makefile
index e1a78e2edfc44..93dfc8e76a961 100644
--- a/src/test/run-make-fulldeps/split-dwarf/Makefile
+++ b/src/test/run-make-fulldeps/split-dwarf/Makefile
@@ -3,6 +3,6 @@
 # only-linux
 
 all:
-	$(RUSTC) -Z split-dwarf=split foo.rs
+	$(RUSTC) -Z unstable-options -C split-debuginfo=packed foo.rs -g
 	rm $(TMPDIR)/foo.dwp
 	rm $(TMPDIR)/$(call BIN,foo)
diff --git a/src/test/ui/backtrace-apple-no-dsymutil.rs b/src/test/ui/backtrace-apple-no-dsymutil.rs
new file mode 100644
index 0000000000000..72a21faa4e7ea
--- /dev/null
+++ b/src/test/ui/backtrace-apple-no-dsymutil.rs
@@ -0,0 +1,30 @@
+// run-pass
+
+// compile-flags:-g -Csplit-debuginfo=off
+// only-macos
+
+#![feature(backtrace)]
+
+use std::process::Command;
+use std::str;
+
+#[inline(never)]
+fn main() {
+    let args: Vec<String> = std::env::args().collect();
+    if args.len() >= 2 {
+        println!("{}", std::backtrace::Backtrace::force_capture());
+        return;
+    }
+    let out = Command::new(&args[0]).env("RUST_BACKTRACE", "1").arg("foo").output().unwrap();
+    let output = format!(
+        "{}\n{}",
+        str::from_utf8(&out.stdout).unwrap(),
+        str::from_utf8(&out.stderr).unwrap(),
+    );
+    if out.status.success() && output.contains(file!()) {
+        return;
+    }
+    println!("status: {}", out.status);
+    println!("child output:\n\t{}", output.replace("\n", "\n\t"));
+    panic!("failed to find {:?} in output", file!());
+}
diff --git a/src/test/ui/borrowck/borrow-raw-address-of-mutability.stderr b/src/test/ui/borrowck/borrow-raw-address-of-mutability.stderr
index 44dde0fd80b0d..ea74fb966846f 100644
--- a/src/test/ui/borrowck/borrow-raw-address-of-mutability.stderr
+++ b/src/test/ui/borrowck/borrow-raw-address-of-mutability.stderr
@@ -20,7 +20,9 @@ error[E0596]: cannot borrow `f` as mutable, as it is not declared as mutable
    |
 LL |     let f = || {
    |         - help: consider changing this to be mutable: `mut f`
-...
+LL |         let y = &raw mut x;
+   |                          - calling `f` requires mutable binding due to mutable borrow of `x`
+LL |     };
 LL |     f();
    |     ^ cannot borrow as mutable
 
diff --git a/src/test/ui/closures/2229_closure_analysis/capture-analysis-1.rs b/src/test/ui/closures/2229_closure_analysis/capture-analysis-1.rs
new file mode 100644
index 0000000000000..4368c830e1c61
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/capture-analysis-1.rs
@@ -0,0 +1,35 @@
+#![feature(capture_disjoint_fields)]
+//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
+//~| NOTE: `#[warn(incomplete_features)]` on by default
+//~| NOTE: see issue #53488 <https://github.com/rust-lang/rust/issues/53488>
+#![feature(rustc_attrs)]
+
+#[derive(Debug)]
+struct Point {
+    x: i32,
+    y: i32,
+}
+
+fn main() {
+    let p = Point { x: 10, y: 10 };
+    let q = Point { x: 10, y: 10 };
+
+    let c = #[rustc_capture_analysis]
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    || {
+    //~^ First Pass analysis includes:
+    //~| Min Capture analysis includes:
+        println!("{:?}", p);
+        //~^ NOTE: Capturing p[] -> ImmBorrow
+        //~| NOTE: Min Capture p[] -> ImmBorrow
+        println!("{:?}", p.x);
+        //~^ NOTE: Capturing p[(0, 0)] -> ImmBorrow
+
+        println!("{:?}", q.x);
+        //~^ NOTE: Capturing q[(0, 0)] -> ImmBorrow
+        println!("{:?}", q);
+        //~^ NOTE: Capturing q[] -> ImmBorrow
+        //~| NOTE: Min Capture q[] -> ImmBorrow
+    };
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/capture-analysis-1.stderr b/src/test/ui/closures/2229_closure_analysis/capture-analysis-1.stderr
new file mode 100644
index 0000000000000..09255343af0e8
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/capture-analysis-1.stderr
@@ -0,0 +1,77 @@
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/capture-analysis-1.rs:17:13
+   |
+LL |     let c = #[rustc_capture_analysis]
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/capture-analysis-1.rs:1:12
+   |
+LL | #![feature(capture_disjoint_fields)]
+   |            ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `#[warn(incomplete_features)]` on by default
+   = note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information
+
+error: First Pass analysis includes:
+  --> $DIR/capture-analysis-1.rs:20:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         println!("{:?}", p);
+...  |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Capturing p[] -> ImmBorrow
+  --> $DIR/capture-analysis-1.rs:23:26
+   |
+LL |         println!("{:?}", p);
+   |                          ^
+note: Capturing p[(0, 0)] -> ImmBorrow
+  --> $DIR/capture-analysis-1.rs:26:26
+   |
+LL |         println!("{:?}", p.x);
+   |                          ^^^
+note: Capturing q[(0, 0)] -> ImmBorrow
+  --> $DIR/capture-analysis-1.rs:29:26
+   |
+LL |         println!("{:?}", q.x);
+   |                          ^^^
+note: Capturing q[] -> ImmBorrow
+  --> $DIR/capture-analysis-1.rs:31:26
+   |
+LL |         println!("{:?}", q);
+   |                          ^
+
+error: Min Capture analysis includes:
+  --> $DIR/capture-analysis-1.rs:20:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         println!("{:?}", p);
+...  |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Min Capture p[] -> ImmBorrow
+  --> $DIR/capture-analysis-1.rs:23:26
+   |
+LL |         println!("{:?}", p);
+   |                          ^
+note: Min Capture q[] -> ImmBorrow
+  --> $DIR/capture-analysis-1.rs:31:26
+   |
+LL |         println!("{:?}", q);
+   |                          ^
+
+error: aborting due to 3 previous errors; 1 warning emitted
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/closures/2229_closure_analysis/capture-analysis-2.rs b/src/test/ui/closures/2229_closure_analysis/capture-analysis-2.rs
new file mode 100644
index 0000000000000..ab7fce6a43099
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/capture-analysis-2.rs
@@ -0,0 +1,30 @@
+#![feature(capture_disjoint_fields)]
+//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
+//~| NOTE: `#[warn(incomplete_features)]` on by default
+//~| NOTE: see issue #53488 <https://github.com/rust-lang/rust/issues/53488>
+#![feature(rustc_attrs)]
+
+#[derive(Debug)]
+struct Point {
+    x: String,
+    y: i32,
+}
+
+fn main() {
+    let mut p = Point { x: String::new(), y: 10 };
+
+    let c = #[rustc_capture_analysis]
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    || {
+    //~^ First Pass analysis includes:
+    //~| Min Capture analysis includes:
+        let _x = p.x;
+        //~^ NOTE: Capturing p[(0, 0)] -> ByValue
+        //~| NOTE: p[] captured as ByValue here
+        println!("{:?}", p);
+        //~^ NOTE: Capturing p[] -> ImmBorrow
+        //~| NOTE: Min Capture p[] -> ByValue
+        //~| NOTE: p[] used here
+    };
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/capture-analysis-2.stderr b/src/test/ui/closures/2229_closure_analysis/capture-analysis-2.stderr
new file mode 100644
index 0000000000000..0e48d6b300b72
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/capture-analysis-2.stderr
@@ -0,0 +1,65 @@
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/capture-analysis-2.rs:16:13
+   |
+LL |     let c = #[rustc_capture_analysis]
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/capture-analysis-2.rs:1:12
+   |
+LL | #![feature(capture_disjoint_fields)]
+   |            ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `#[warn(incomplete_features)]` on by default
+   = note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information
+
+error: First Pass analysis includes:
+  --> $DIR/capture-analysis-2.rs:19:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         let _x = p.x;
+...  |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Capturing p[(0, 0)] -> ByValue
+  --> $DIR/capture-analysis-2.rs:22:18
+   |
+LL |         let _x = p.x;
+   |                  ^^^
+note: Capturing p[] -> ImmBorrow
+  --> $DIR/capture-analysis-2.rs:25:26
+   |
+LL |         println!("{:?}", p);
+   |                          ^
+
+error: Min Capture analysis includes:
+  --> $DIR/capture-analysis-2.rs:19:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         let _x = p.x;
+...  |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Min Capture p[] -> ByValue
+  --> $DIR/capture-analysis-2.rs:22:18
+   |
+LL |         let _x = p.x;
+   |                  ^^^ p[] captured as ByValue here
+...
+LL |         println!("{:?}", p);
+   |                          ^ p[] used here
+
+error: aborting due to 3 previous errors; 1 warning emitted
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/closures/2229_closure_analysis/capture-analysis-3.rs b/src/test/ui/closures/2229_closure_analysis/capture-analysis-3.rs
new file mode 100644
index 0000000000000..817ade899e2a0
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/capture-analysis-3.rs
@@ -0,0 +1,35 @@
+#![feature(capture_disjoint_fields)]
+//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
+//~| NOTE: `#[warn(incomplete_features)]` on by default
+//~| NOTE: see issue #53488 <https://github.com/rust-lang/rust/issues/53488>
+#![feature(rustc_attrs)]
+
+#[derive(Debug)]
+struct Child {
+    c: String,
+    d: String,
+}
+
+#[derive(Debug)]
+struct Parent {
+    b: Child,
+}
+
+fn main() {
+    let mut a = Parent { b: Child {c: String::new(), d: String::new()} };
+
+    let c = #[rustc_capture_analysis]
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    || {
+    //~^ First Pass analysis includes:
+    //~| Min Capture analysis includes:
+        let _x = a.b.c;
+        //~^ NOTE: Capturing a[(0, 0),(0, 0)] -> ByValue
+        //~| NOTE: a[(0, 0)] captured as ByValue here
+        println!("{:?}", a.b);
+        //~^ NOTE: Capturing a[(0, 0)] -> ImmBorrow
+        //~| NOTE: Min Capture a[(0, 0)] -> ByValue
+        //~| NOTE: a[(0, 0)] used here
+    };
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/capture-analysis-3.stderr b/src/test/ui/closures/2229_closure_analysis/capture-analysis-3.stderr
new file mode 100644
index 0000000000000..263e9ca56ebf6
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/capture-analysis-3.stderr
@@ -0,0 +1,65 @@
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/capture-analysis-3.rs:21:13
+   |
+LL |     let c = #[rustc_capture_analysis]
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/capture-analysis-3.rs:1:12
+   |
+LL | #![feature(capture_disjoint_fields)]
+   |            ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `#[warn(incomplete_features)]` on by default
+   = note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information
+
+error: First Pass analysis includes:
+  --> $DIR/capture-analysis-3.rs:24:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         let _x = a.b.c;
+...  |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Capturing a[(0, 0),(0, 0)] -> ByValue
+  --> $DIR/capture-analysis-3.rs:27:18
+   |
+LL |         let _x = a.b.c;
+   |                  ^^^^^
+note: Capturing a[(0, 0)] -> ImmBorrow
+  --> $DIR/capture-analysis-3.rs:30:26
+   |
+LL |         println!("{:?}", a.b);
+   |                          ^^^
+
+error: Min Capture analysis includes:
+  --> $DIR/capture-analysis-3.rs:24:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         let _x = a.b.c;
+...  |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Min Capture a[(0, 0)] -> ByValue
+  --> $DIR/capture-analysis-3.rs:27:18
+   |
+LL |         let _x = a.b.c;
+   |                  ^^^^^ a[(0, 0)] captured as ByValue here
+...
+LL |         println!("{:?}", a.b);
+   |                          ^^^ a[(0, 0)] used here
+
+error: aborting due to 3 previous errors; 1 warning emitted
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/closures/2229_closure_analysis/capture-analysis-4.rs b/src/test/ui/closures/2229_closure_analysis/capture-analysis-4.rs
new file mode 100644
index 0000000000000..e8401299b30ad
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/capture-analysis-4.rs
@@ -0,0 +1,33 @@
+#![feature(capture_disjoint_fields)]
+//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
+//~| NOTE: `#[warn(incomplete_features)]` on by default
+//~| NOTE: see issue #53488 <https://github.com/rust-lang/rust/issues/53488>
+#![feature(rustc_attrs)]
+
+#[derive(Debug)]
+struct Child {
+    c: String,
+    d: String,
+}
+
+#[derive(Debug)]
+struct Parent {
+    b: Child,
+}
+
+fn main() {
+    let mut a = Parent { b: Child {c: String::new(), d: String::new()} };
+
+    let c = #[rustc_capture_analysis]
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    || {
+    //~^ First Pass analysis includes:
+    //~| Min Capture analysis includes:
+        let _x = a.b;
+        //~^ NOTE: Capturing a[(0, 0)] -> ByValue
+        //~| NOTE: Min Capture a[(0, 0)] -> ByValue
+        println!("{:?}", a.b.c);
+        //~^ NOTE: Capturing a[(0, 0),(0, 0)] -> ImmBorrow
+    };
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/capture-analysis-4.stderr b/src/test/ui/closures/2229_closure_analysis/capture-analysis-4.stderr
new file mode 100644
index 0000000000000..f4605c1d51b76
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/capture-analysis-4.stderr
@@ -0,0 +1,62 @@
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/capture-analysis-4.rs:21:13
+   |
+LL |     let c = #[rustc_capture_analysis]
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/capture-analysis-4.rs:1:12
+   |
+LL | #![feature(capture_disjoint_fields)]
+   |            ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `#[warn(incomplete_features)]` on by default
+   = note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information
+
+error: First Pass analysis includes:
+  --> $DIR/capture-analysis-4.rs:24:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         let _x = a.b;
+...  |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Capturing a[(0, 0)] -> ByValue
+  --> $DIR/capture-analysis-4.rs:27:18
+   |
+LL |         let _x = a.b;
+   |                  ^^^
+note: Capturing a[(0, 0),(0, 0)] -> ImmBorrow
+  --> $DIR/capture-analysis-4.rs:30:26
+   |
+LL |         println!("{:?}", a.b.c);
+   |                          ^^^^^
+
+error: Min Capture analysis includes:
+  --> $DIR/capture-analysis-4.rs:24:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         let _x = a.b;
+...  |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Min Capture a[(0, 0)] -> ByValue
+  --> $DIR/capture-analysis-4.rs:27:18
+   |
+LL |         let _x = a.b;
+   |                  ^^^
+
+error: aborting due to 3 previous errors; 1 warning emitted
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/closures/2229_closure_analysis/deep-multilevel-struct.rs b/src/test/ui/closures/2229_closure_analysis/deep-multilevel-struct.rs
new file mode 100644
index 0000000000000..f81866bb7e096
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/deep-multilevel-struct.rs
@@ -0,0 +1,52 @@
+#![feature(capture_disjoint_fields)]
+//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
+//~| NOTE: `#[warn(incomplete_features)]` on by default
+//~| NOTE: see issue #53488 <https://github.com/rust-lang/rust/issues/53488>
+#![feature(rustc_attrs)]
+#![allow(unused)]
+
+#[derive(Debug)]
+struct Point {
+    x: i32,
+    y: i32,
+}
+#[derive(Debug)]
+struct Line {
+    p: Point,
+    q: Point
+}
+#[derive(Debug)]
+struct Plane {
+    a: Line,
+    b: Line,
+}
+
+fn main() {
+    let mut p = Plane {
+        a: Line {
+            p: Point { x: 1,y: 2 },
+            q: Point { x: 3,y: 4 },
+        },
+        b: Line {
+            p: Point { x: 1,y: 2 },
+            q: Point { x: 3,y: 4 },
+        }
+    };
+
+    let c = #[rustc_capture_analysis]
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    || {
+    //~^ ERROR: First Pass analysis includes:
+    //~| ERROR: Min Capture analysis includes:
+        let x = &p.a.p.x;
+        //~^ NOTE: Capturing p[(0, 0),(0, 0),(0, 0)] -> ImmBorrow
+        p.b.q.y = 9;
+        //~^ NOTE: Capturing p[(1, 0),(1, 0),(1, 0)] -> MutBorrow
+        //~| NOTE: p[] captured as MutBorrow here
+        println!("{:?}", p);
+        //~^ NOTE: Capturing p[] -> ImmBorrow
+        //~| NOTE: Min Capture p[] -> MutBorrow
+        //~| NOTE: p[] used here
+    };
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/deep-multilevel-struct.stderr b/src/test/ui/closures/2229_closure_analysis/deep-multilevel-struct.stderr
new file mode 100644
index 0000000000000..863f1009131a1
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/deep-multilevel-struct.stderr
@@ -0,0 +1,70 @@
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/deep-multilevel-struct.rs:36:13
+   |
+LL |     let c = #[rustc_capture_analysis]
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/deep-multilevel-struct.rs:1:12
+   |
+LL | #![feature(capture_disjoint_fields)]
+   |            ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `#[warn(incomplete_features)]` on by default
+   = note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information
+
+error: First Pass analysis includes:
+  --> $DIR/deep-multilevel-struct.rs:39:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         let x = &p.a.p.x;
+...  |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Capturing p[(0, 0),(0, 0),(0, 0)] -> ImmBorrow
+  --> $DIR/deep-multilevel-struct.rs:42:18
+   |
+LL |         let x = &p.a.p.x;
+   |                  ^^^^^^^
+note: Capturing p[(1, 0),(1, 0),(1, 0)] -> MutBorrow
+  --> $DIR/deep-multilevel-struct.rs:44:9
+   |
+LL |         p.b.q.y = 9;
+   |         ^^^^^^^
+note: Capturing p[] -> ImmBorrow
+  --> $DIR/deep-multilevel-struct.rs:47:26
+   |
+LL |         println!("{:?}", p);
+   |                          ^
+
+error: Min Capture analysis includes:
+  --> $DIR/deep-multilevel-struct.rs:39:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         let x = &p.a.p.x;
+...  |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Min Capture p[] -> MutBorrow
+  --> $DIR/deep-multilevel-struct.rs:44:9
+   |
+LL |         p.b.q.y = 9;
+   |         ^^^^^^^ p[] captured as MutBorrow here
+...
+LL |         println!("{:?}", p);
+   |                          ^ p[] used here
+
+error: aborting due to 3 previous errors; 1 warning emitted
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/closures/2229_closure_analysis/deep-multilevel-tuple.rs b/src/test/ui/closures/2229_closure_analysis/deep-multilevel-tuple.rs
new file mode 100644
index 0000000000000..fb03a02efa09e
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/deep-multilevel-tuple.rs
@@ -0,0 +1,27 @@
+#![feature(capture_disjoint_fields)]
+//~^ WARNING: the feature `capture_disjoint_fields` is incomplete
+//~| NOTE: `#[warn(incomplete_features)]` on by default
+//~| NOTE: see issue #53488 <https://github.com/rust-lang/rust/issues/53488>
+#![feature(rustc_attrs)]
+#![allow(unused)]
+
+fn main() {
+    let mut t = (((1,2),(3,4)),((5,6),(7,8)));
+
+    let c = #[rustc_capture_analysis]
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    || {
+    //~^ ERROR: First Pass analysis includes:
+    //~| ERROR: Min Capture analysis includes:
+        let x = &t.0.0.0;
+        //~^ NOTE: Capturing t[(0, 0),(0, 0),(0, 0)] -> ImmBorrow
+        t.1.1.1 = 9;
+        //~^ NOTE: Capturing t[(1, 0),(1, 0),(1, 0)] -> MutBorrow
+        //~| NOTE: t[] captured as MutBorrow here
+        println!("{:?}", t);
+        //~^ NOTE: Min Capture t[] -> MutBorrow
+        //~| NOTE: Capturing t[] -> ImmBorrow
+        //~| NOTE: t[] used here
+    };
+}
diff --git a/src/test/ui/closures/2229_closure_analysis/deep-multilevel-tuple.stderr b/src/test/ui/closures/2229_closure_analysis/deep-multilevel-tuple.stderr
new file mode 100644
index 0000000000000..252db44473222
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/deep-multilevel-tuple.stderr
@@ -0,0 +1,70 @@
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/deep-multilevel-tuple.rs:11:13
+   |
+LL |     let c = #[rustc_capture_analysis]
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+   = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/deep-multilevel-tuple.rs:1:12
+   |
+LL | #![feature(capture_disjoint_fields)]
+   |            ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `#[warn(incomplete_features)]` on by default
+   = note: see issue #53488 <https://github.com/rust-lang/rust/issues/53488> for more information
+
+error: First Pass analysis includes:
+  --> $DIR/deep-multilevel-tuple.rs:14:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         let x = &t.0.0.0;
+...  |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Capturing t[(0, 0),(0, 0),(0, 0)] -> ImmBorrow
+  --> $DIR/deep-multilevel-tuple.rs:17:18
+   |
+LL |         let x = &t.0.0.0;
+   |                  ^^^^^^^
+note: Capturing t[(1, 0),(1, 0),(1, 0)] -> MutBorrow
+  --> $DIR/deep-multilevel-tuple.rs:19:9
+   |
+LL |         t.1.1.1 = 9;
+   |         ^^^^^^^
+note: Capturing t[] -> ImmBorrow
+  --> $DIR/deep-multilevel-tuple.rs:22:26
+   |
+LL |         println!("{:?}", t);
+   |                          ^
+
+error: Min Capture analysis includes:
+  --> $DIR/deep-multilevel-tuple.rs:14:5
+   |
+LL | /     || {
+LL | |
+LL | |
+LL | |         let x = &t.0.0.0;
+...  |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Min Capture t[] -> MutBorrow
+  --> $DIR/deep-multilevel-tuple.rs:19:9
+   |
+LL |         t.1.1.1 = 9;
+   |         ^^^^^^^ t[] captured as MutBorrow here
+...
+LL |         println!("{:?}", t);
+   |                          ^ t[] used here
+
+error: aborting due to 3 previous errors; 1 warning emitted
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.rs b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.rs
index aaff3531e5850..a6b5e12d2ed78 100644
--- a/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.rs
+++ b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.rs
@@ -32,9 +32,11 @@ fn main() {
     //~| ERROR: Min Capture analysis includes:
         p.x += 10;
         //~^ NOTE: Capturing p[(0, 0)] -> MutBorrow
-        //~| NOTE: Min Capture p[] -> MutBorrow
+        //~| NOTE: p[] captured as MutBorrow here
         println!("{:?}", p);
         //~^ NOTE: Capturing p[] -> ImmBorrow
+        //~| NOTE: Min Capture p[] -> MutBorrow
+        //~| NOTE: p[] used here
     };
 
     c();
diff --git a/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stderr b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stderr
index 30d3d5f504eb9..cbbc879219915 100644
--- a/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stderr
+++ b/src/test/ui/closures/2229_closure_analysis/simple-struct-min-capture.stderr
@@ -55,7 +55,10 @@ note: Min Capture p[] -> MutBorrow
   --> $DIR/simple-struct-min-capture.rs:33:9
    |
 LL |         p.x += 10;
-   |         ^^^
+   |         ^^^ p[] captured as MutBorrow here
+...
+LL |         println!("{:?}", p);
+   |                          ^ p[] used here
 
 error: aborting due to 3 previous errors; 1 warning emitted
 
diff --git a/src/test/ui/closures/issue-80313-mutable-borrow-in-closure.rs b/src/test/ui/closures/issue-80313-mutable-borrow-in-closure.rs
new file mode 100644
index 0000000000000..ff210ae06a3bd
--- /dev/null
+++ b/src/test/ui/closures/issue-80313-mutable-borrow-in-closure.rs
@@ -0,0 +1,7 @@
+fn main() {
+    let mut my_var = false;
+    let callback = || {
+        &mut my_var;
+    };
+    callback(); //~ ERROR E0596
+}
diff --git a/src/test/ui/closures/issue-80313-mutable-borrow-in-closure.stderr b/src/test/ui/closures/issue-80313-mutable-borrow-in-closure.stderr
new file mode 100644
index 0000000000000..bf9e1febdbba4
--- /dev/null
+++ b/src/test/ui/closures/issue-80313-mutable-borrow-in-closure.stderr
@@ -0,0 +1,14 @@
+error[E0596]: cannot borrow `callback` as mutable, as it is not declared as mutable
+  --> $DIR/issue-80313-mutable-borrow-in-closure.rs:6:5
+   |
+LL |     let callback = || {
+   |         -------- help: consider changing this to be mutable: `mut callback`
+LL |         &mut my_var;
+   |              ------ calling `callback` requires mutable binding due to mutable borrow of `my_var`
+LL |     };
+LL |     callback();
+   |     ^^^^^^^^ cannot borrow as mutable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0596`.
diff --git a/src/test/ui/closures/issue-80313-mutable-borrow-in-move-closure.rs b/src/test/ui/closures/issue-80313-mutable-borrow-in-move-closure.rs
new file mode 100644
index 0000000000000..8f2d8a676302c
--- /dev/null
+++ b/src/test/ui/closures/issue-80313-mutable-borrow-in-move-closure.rs
@@ -0,0 +1,7 @@
+fn main() {
+    let mut my_var = false;
+    let callback = move || {
+        &mut my_var;
+    };
+    callback(); //~ ERROR E0596
+}
diff --git a/src/test/ui/closures/issue-80313-mutable-borrow-in-move-closure.stderr b/src/test/ui/closures/issue-80313-mutable-borrow-in-move-closure.stderr
new file mode 100644
index 0000000000000..b67cec6a609f0
--- /dev/null
+++ b/src/test/ui/closures/issue-80313-mutable-borrow-in-move-closure.stderr
@@ -0,0 +1,14 @@
+error[E0596]: cannot borrow `callback` as mutable, as it is not declared as mutable
+  --> $DIR/issue-80313-mutable-borrow-in-move-closure.rs:6:5
+   |
+LL |     let callback = move || {
+   |         -------- help: consider changing this to be mutable: `mut callback`
+LL |         &mut my_var;
+   |              ------ calling `callback` requires mutable binding due to possible mutation of `my_var`
+LL |     };
+LL |     callback();
+   |     ^^^^^^^^ cannot borrow as mutable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0596`.
diff --git a/src/test/ui/closures/issue-80313-mutation-in-closure.rs b/src/test/ui/closures/issue-80313-mutation-in-closure.rs
new file mode 100644
index 0000000000000..e082ea562ef22
--- /dev/null
+++ b/src/test/ui/closures/issue-80313-mutation-in-closure.rs
@@ -0,0 +1,7 @@
+fn main() {
+    let mut my_var = false;
+    let callback = || {
+        my_var = true;
+    };
+    callback(); //~ ERROR E0596
+}
diff --git a/src/test/ui/closures/issue-80313-mutation-in-closure.stderr b/src/test/ui/closures/issue-80313-mutation-in-closure.stderr
new file mode 100644
index 0000000000000..6e98549f6b84f
--- /dev/null
+++ b/src/test/ui/closures/issue-80313-mutation-in-closure.stderr
@@ -0,0 +1,14 @@
+error[E0596]: cannot borrow `callback` as mutable, as it is not declared as mutable
+  --> $DIR/issue-80313-mutation-in-closure.rs:6:5
+   |
+LL |     let callback = || {
+   |         -------- help: consider changing this to be mutable: `mut callback`
+LL |         my_var = true;
+   |         ------ calling `callback` requires mutable binding due to mutable borrow of `my_var`
+LL |     };
+LL |     callback();
+   |     ^^^^^^^^ cannot borrow as mutable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0596`.
diff --git a/src/test/ui/closures/issue-80313-mutation-in-move-closure.rs b/src/test/ui/closures/issue-80313-mutation-in-move-closure.rs
new file mode 100644
index 0000000000000..f66bf4e062831
--- /dev/null
+++ b/src/test/ui/closures/issue-80313-mutation-in-move-closure.rs
@@ -0,0 +1,7 @@
+fn main() {
+    let mut my_var = false;
+    let callback = move || {
+        my_var = true;
+    };
+    callback(); //~ ERROR E0596
+}
diff --git a/src/test/ui/closures/issue-80313-mutation-in-move-closure.stderr b/src/test/ui/closures/issue-80313-mutation-in-move-closure.stderr
new file mode 100644
index 0000000000000..edd55422a0bd4
--- /dev/null
+++ b/src/test/ui/closures/issue-80313-mutation-in-move-closure.stderr
@@ -0,0 +1,14 @@
+error[E0596]: cannot borrow `callback` as mutable, as it is not declared as mutable
+  --> $DIR/issue-80313-mutation-in-move-closure.rs:6:5
+   |
+LL |     let callback = move || {
+   |         -------- help: consider changing this to be mutable: `mut callback`
+LL |         my_var = true;
+   |         ------ calling `callback` requires mutable binding due to possible mutation of `my_var`
+LL |     };
+LL |     callback();
+   |     ^^^^^^^^ cannot borrow as mutable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0596`.
diff --git a/src/test/ui/issues/issue-43424.stderr b/src/test/ui/issues/issue-43424.stderr
index 6274a7928ba04..8f59d7cc3aa77 100644
--- a/src/test/ui/issues/issue-43424.stderr
+++ b/src/test/ui/issues/issue-43424.stderr
@@ -1,8 +1,8 @@
 error: unexpected generic arguments in path
-  --> $DIR/issue-43424.rs:10:4
+  --> $DIR/issue-43424.rs:10:10
    |
 LL | m!(inline<u8>);
-   |    ^^^^^^^^^^
+   |          ^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/span/import-ty-params.rs b/src/test/ui/span/import-ty-params.rs
index 850bbd857ffec..313cd227b7c27 100644
--- a/src/test/ui/span/import-ty-params.rs
+++ b/src/test/ui/span/import-ty-params.rs
@@ -16,5 +16,8 @@ fn f1() {
 fn f2() {
     import! { a::b::c::S<> } //~ ERROR unexpected generic arguments in path
 }
+fn f3() {
+    import! { a::b<>::c<u8>::S<> } //~ ERROR unexpected generic arguments in path
+}
 
 fn main() {}
diff --git a/src/test/ui/span/import-ty-params.stderr b/src/test/ui/span/import-ty-params.stderr
index a02a1edc13499..701cd0c04bd1f 100644
--- a/src/test/ui/span/import-ty-params.stderr
+++ b/src/test/ui/span/import-ty-params.stderr
@@ -1,14 +1,20 @@
 error: unexpected generic arguments in path
-  --> $DIR/import-ty-params.rs:14:15
+  --> $DIR/import-ty-params.rs:14:25
    |
 LL |     import! { a::b::c::S<u8> }
-   |               ^^^^^^^^^^^^^^
+   |                         ^^^^
 
 error: unexpected generic arguments in path
-  --> $DIR/import-ty-params.rs:17:15
+  --> $DIR/import-ty-params.rs:17:25
    |
 LL |     import! { a::b::c::S<> }
-   |               ^^^^^^^^^^^^
+   |                         ^^
 
-error: aborting due to 2 previous errors
+error: unexpected generic arguments in path
+  --> $DIR/import-ty-params.rs:20:19
+   |
+LL |     import! { a::b<>::c<u8>::S<> }
+   |                   ^^   ^^^^   ^^
+
+error: aborting due to 3 previous errors
 
diff --git a/src/test/ui/span/visibility-ty-params.stderr b/src/test/ui/span/visibility-ty-params.stderr
index d3fa1d7732e72..067893fd22d34 100644
--- a/src/test/ui/span/visibility-ty-params.stderr
+++ b/src/test/ui/span/visibility-ty-params.stderr
@@ -1,8 +1,8 @@
 error: unexpected generic arguments in path
-  --> $DIR/visibility-ty-params.rs:6:5
+  --> $DIR/visibility-ty-params.rs:6:6
    |
 LL | m!{ S<u8> }
-   |     ^^^^^
+   |      ^^^^
 
 error[E0577]: expected module, found struct `S`
   --> $DIR/visibility-ty-params.rs:6:5
@@ -11,10 +11,10 @@ LL | m!{ S<u8> }
    |     ^^^^^ not a module
 
 error: unexpected generic arguments in path
-  --> $DIR/visibility-ty-params.rs:10:9
+  --> $DIR/visibility-ty-params.rs:10:10
    |
 LL |     m!{ m<> }
-   |         ^^^
+   |          ^^
 
 error: aborting due to 3 previous errors
 
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut-calling-fnmut-no-mut.stderr b/src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut-calling-fnmut-no-mut.stderr
index 5dea424596e9c..a0ed56d4bcf7b 100644
--- a/src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut-calling-fnmut-no-mut.stderr
+++ b/src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut-calling-fnmut-no-mut.stderr
@@ -3,6 +3,8 @@ error[E0596]: cannot borrow `tick1` as mutable, as it is not declared as mutable
    |
 LL |     let tick1 = || {
    |         ----- help: consider changing this to be mutable: `mut tick1`
+LL |         counter += 1;
+   |         ------- calling `tick1` requires mutable binding due to mutable borrow of `counter`
 ...
 LL |         tick1();
    |         ^^^^^ cannot borrow as mutable
@@ -12,6 +14,8 @@ error[E0596]: cannot borrow `tick2` as mutable, as it is not declared as mutable
    |
 LL |     let tick2 = || {
    |         ----- help: consider changing this to be mutable: `mut tick2`
+LL |         tick1();
+   |         ----- calling `tick2` requires mutable binding due to mutable borrow of `tick1`
 ...
 LL |     tick2();
    |     ^^^^^ cannot borrow as mutable
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut-missing-mut.stderr b/src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut-missing-mut.stderr
index eb398628846dd..27d23e3fa044b 100644
--- a/src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut-missing-mut.stderr
+++ b/src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut-missing-mut.stderr
@@ -2,7 +2,9 @@ error[E0596]: cannot borrow `tick` as mutable, as it is not declared as mutable
   --> $DIR/unboxed-closures-infer-fnmut-missing-mut.rs:7:5
    |
 LL |     let tick = || counter += 1;
-   |         ---- help: consider changing this to be mutable: `mut tick`
+   |         ----      ------- calling `tick` requires mutable binding due to mutable borrow of `counter`
+   |         |
+   |         help: consider changing this to be mutable: `mut tick`
 LL |     tick();
    |     ^^^^ cannot borrow as mutable
 
diff --git a/src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut-move-missing-mut.stderr b/src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut-move-missing-mut.stderr
index b9d76d9a752ce..c00f986c397a7 100644
--- a/src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut-move-missing-mut.stderr
+++ b/src/test/ui/unboxed-closures/unboxed-closures-infer-fnmut-move-missing-mut.stderr
@@ -2,7 +2,9 @@ error[E0596]: cannot borrow `tick` as mutable, as it is not declared as mutable
   --> $DIR/unboxed-closures-infer-fnmut-move-missing-mut.rs:7:5
    |
 LL |     let tick = move || counter += 1;
-   |         ---- help: consider changing this to be mutable: `mut tick`
+   |         ----           ------- calling `tick` requires mutable binding due to possible mutation of `counter`
+   |         |
+   |         help: consider changing this to be mutable: `mut tick`
 LL |     tick();
    |     ^^^^ cannot borrow as mutable
 
diff --git a/src/tools/clippy/clippy_lints/src/default.rs b/src/tools/clippy/clippy_lints/src/default.rs
index f7224811e6e79..6fa1378b8c73d 100644
--- a/src/tools/clippy/clippy_lints/src/default.rs
+++ b/src/tools/clippy/clippy_lints/src/default.rs
@@ -1,5 +1,5 @@
 use crate::utils::{
-    any_parent_is_automatically_derived, contains_name, match_def_path, paths, qpath_res, snippet_with_macro_callsite,
+    any_parent_is_automatically_derived, contains_name, match_def_path, paths, snippet_with_macro_callsite,
 };
 use crate::utils::{span_lint_and_note, span_lint_and_sugg};
 use if_chain::if_chain;
@@ -231,7 +231,7 @@ fn is_expr_default<'tcx>(expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> bool
     if_chain! {
         if let ExprKind::Call(ref fn_expr, _) = &expr.kind;
         if let ExprKind::Path(qpath) = &fn_expr.kind;
-        if let Res::Def(_, def_id) = qpath_res(cx, qpath, fn_expr.hir_id);
+        if let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id);
         then {
             // right hand side of assignment is `Default::default`
             match_def_path(cx, def_id, &paths::DEFAULT_TRAIT_METHOD)
diff --git a/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs b/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs
index cf528d189b4b1..a84f9c4628716 100644
--- a/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs
+++ b/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs
@@ -1,4 +1,4 @@
-use crate::utils::{is_copy, match_def_path, paths, qpath_res, span_lint_and_note};
+use crate::utils::{is_copy, match_def_path, paths, span_lint_and_note};
 use if_chain::if_chain;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
@@ -114,7 +114,7 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef {
             if let ExprKind::Call(ref path, ref args) = expr.kind;
             if let ExprKind::Path(ref qpath) = path.kind;
             if args.len() == 1;
-            if let Some(def_id) = qpath_res(cx, qpath, path.hir_id).opt_def_id();
+            if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id();
             then {
                 let lint;
                 let msg;
diff --git a/src/tools/clippy/clippy_lints/src/exit.rs b/src/tools/clippy/clippy_lints/src/exit.rs
index 7337d98c8be37..915859270009b 100644
--- a/src/tools/clippy/clippy_lints/src/exit.rs
+++ b/src/tools/clippy/clippy_lints/src/exit.rs
@@ -1,4 +1,4 @@
-use crate::utils::{is_entrypoint_fn, match_def_path, paths, qpath_res, span_lint};
+use crate::utils::{is_entrypoint_fn, match_def_path, paths, span_lint};
 use if_chain::if_chain;
 use rustc_hir::{Expr, ExprKind, Item, ItemKind, Node};
 use rustc_lint::{LateContext, LateLintPass};
@@ -29,7 +29,7 @@ impl<'tcx> LateLintPass<'tcx> for Exit {
         if_chain! {
             if let ExprKind::Call(ref path_expr, ref _args) = e.kind;
             if let ExprKind::Path(ref path) = path_expr.kind;
-            if let Some(def_id) = qpath_res(cx, path, path_expr.hir_id).opt_def_id();
+            if let Some(def_id) = cx.qpath_res(path, path_expr.hir_id).opt_def_id();
             if match_def_path(cx, def_id, &paths::EXIT);
             then {
                 let parent = cx.tcx.hir().get_parent_item(e.hir_id);
diff --git a/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs b/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs
index 9f389c8d2f9e7..527905e375d28 100644
--- a/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs
+++ b/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs
@@ -1,7 +1,4 @@
-use crate::utils::paths::FROM_TRAIT;
-use crate::utils::{
-    is_expn_of, is_type_diagnostic_item, match_def_path, match_panic_def_id, method_chain_args, span_lint_and_then,
-};
+use crate::utils::{is_expn_of, is_type_diagnostic_item, match_panic_def_id, method_chain_args, span_lint_and_then};
 use if_chain::if_chain;
 use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass};
@@ -59,7 +56,7 @@ impl<'tcx> LateLintPass<'tcx> for FallibleImplFrom {
         if_chain! {
             if let hir::ItemKind::Impl(impl_) = &item.kind;
             if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(impl_def_id);
-            if match_def_path(cx, impl_trait_ref.def_id, &FROM_TRAIT);
+            if cx.tcx.is_diagnostic_item(sym::from_trait, impl_trait_ref.def_id);
             then {
                 lint_impl_body(cx, item.span, impl_.items);
             }
diff --git a/src/tools/clippy/clippy_lints/src/functions.rs b/src/tools/clippy/clippy_lints/src/functions.rs
index fd93548b55c6d..8795425461033 100644
--- a/src/tools/clippy/clippy_lints/src/functions.rs
+++ b/src/tools/clippy/clippy_lints/src/functions.rs
@@ -1,7 +1,7 @@
 use crate::utils::{
     attr_by_name, attrs::is_proc_macro, is_must_use_ty, is_trait_impl_item, is_type_diagnostic_item, iter_input_pats,
-    last_path_segment, match_def_path, must_use_attr, qpath_res, return_ty, snippet, snippet_opt, span_lint,
-    span_lint_and_help, span_lint_and_then, trait_ref_of_method, type_is_unsafe_function,
+    last_path_segment, match_def_path, must_use_attr, return_ty, snippet, snippet_opt, span_lint, span_lint_and_help,
+    span_lint_and_then, trait_ref_of_method, type_is_unsafe_function,
 };
 use if_chain::if_chain;
 use rustc_ast::ast::Attribute;
@@ -659,7 +659,7 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for DerefVisitor<'a, 'tcx> {
 impl<'a, 'tcx> DerefVisitor<'a, 'tcx> {
     fn check_arg(&self, ptr: &hir::Expr<'_>) {
         if let hir::ExprKind::Path(ref qpath) = ptr.kind {
-            if let Res::Local(id) = qpath_res(self.cx, qpath, ptr.hir_id) {
+            if let Res::Local(id) = self.cx.qpath_res(qpath, ptr.hir_id) {
                 if self.ptrs.contains(&id) {
                     span_lint(
                         self.cx,
@@ -722,7 +722,7 @@ fn is_mutated_static(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> bool {
     use hir::ExprKind::{Field, Index, Path};
 
     match e.kind {
-        Path(ref qpath) => !matches!(qpath_res(cx, qpath, e.hir_id), Res::Local(_)),
+        Path(ref qpath) => !matches!(cx.qpath_res(qpath, e.hir_id), Res::Local(_)),
         Field(ref inner, _) | Index(ref inner, _) => is_mutated_static(cx, inner),
         _ => false,
     }
diff --git a/src/tools/clippy/clippy_lints/src/let_if_seq.rs b/src/tools/clippy/clippy_lints/src/let_if_seq.rs
index db717cd1240a4..5886c2360e362 100644
--- a/src/tools/clippy/clippy_lints/src/let_if_seq.rs
+++ b/src/tools/clippy/clippy_lints/src/let_if_seq.rs
@@ -1,4 +1,4 @@
-use crate::utils::{qpath_res, snippet, span_lint_and_then, visitors::LocalUsedVisitor};
+use crate::utils::{snippet, span_lint_and_then, visitors::LocalUsedVisitor};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
@@ -145,7 +145,7 @@ fn check_assign<'tcx>(
         if let hir::StmtKind::Semi(ref expr) = expr.kind;
         if let hir::ExprKind::Assign(ref var, ref value, _) = expr.kind;
         if let hir::ExprKind::Path(ref qpath) = var.kind;
-        if let Res::Local(local_id) = qpath_res(cx, qpath, var.hir_id);
+        if let Res::Local(local_id) = cx.qpath_res(qpath, var.hir_id);
         if decl == local_id;
         then {
             let mut v = LocalUsedVisitor::new(decl);
diff --git a/src/tools/clippy/clippy_lints/src/loops.rs b/src/tools/clippy/clippy_lints/src/loops.rs
index bbcea387de2cb..f89075005bc7a 100644
--- a/src/tools/clippy/clippy_lints/src/loops.rs
+++ b/src/tools/clippy/clippy_lints/src/loops.rs
@@ -6,9 +6,9 @@ use crate::utils::visitors::LocalUsedVisitor;
 use crate::utils::{
     contains_name, get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait,
     indent_of, is_in_panic_handler, is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item,
-    last_path_segment, match_trait_method, match_type, match_var, multispan_sugg, qpath_res, single_segment_path,
-    snippet, snippet_with_applicability, snippet_with_macro_callsite, span_lint, span_lint_and_help,
-    span_lint_and_sugg, span_lint_and_then, sugg, SpanlessEq,
+    last_path_segment, match_trait_method, match_type, match_var, multispan_sugg, single_segment_path, snippet,
+    snippet_with_applicability, snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg,
+    span_lint_and_then, sugg, SpanlessEq,
 };
 use if_chain::if_chain;
 use rustc_ast::ast;
@@ -848,7 +848,7 @@ fn same_var<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, var: HirId) -> bool {
         if let ExprKind::Path(qpath) = &expr.kind;
         if let QPath::Resolved(None, path) = qpath;
         if path.segments.len() == 1;
-        if let Res::Local(local_id) = qpath_res(cx, qpath, expr.hir_id);
+        if let Res::Local(local_id) = cx.qpath_res(qpath, expr.hir_id);
         then {
             // our variable!
             local_id == var
@@ -1420,7 +1420,7 @@ fn detect_same_item_push<'tcx>(
                 // Make sure that the push does not involve possibly mutating values
                 match pushed_item.kind {
                     ExprKind::Path(ref qpath) => {
-                        match qpath_res(cx, qpath, pushed_item.hir_id) {
+                        match cx.qpath_res(qpath, pushed_item.hir_id) {
                             // immutable bindings that are initialized with literal or constant
                             Res::Local(hir_id) => {
                                 if_chain! {
@@ -1437,7 +1437,7 @@ fn detect_same_item_push<'tcx>(
                                             ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item),
                                             // immutable bindings that are initialized with constant
                                             ExprKind::Path(ref path) => {
-                                                if let Res::Def(DefKind::Const, ..) = qpath_res(cx, path, init.hir_id) {
+                                                if let Res::Def(DefKind::Const, ..) = cx.qpath_res(path, init.hir_id) {
                                                     emit_lint(cx, vec, pushed_item);
                                                 }
                                             }
@@ -2028,7 +2028,7 @@ fn check_for_mutability(cx: &LateContext<'_>, bound: &Expr<'_>) -> Option<HirId>
         if let ExprKind::Path(ref qpath) = bound.kind;
         if let QPath::Resolved(None, _) = *qpath;
         then {
-            let res = qpath_res(cx, qpath, bound.hir_id);
+            let res = cx.qpath_res(qpath, bound.hir_id);
             if let Res::Local(hir_id) = res {
                 let node_str = cx.tcx.hir().get(hir_id);
                 if_chain! {
@@ -2120,7 +2120,7 @@ impl<'a, 'tcx> VarVisitor<'a, 'tcx> {
                     if self.prefer_mutable {
                         self.indexed_mut.insert(seqvar.segments[0].ident.name);
                     }
-                    let res = qpath_res(self.cx, seqpath, seqexpr.hir_id);
+                    let res = self.cx.qpath_res(seqpath, seqexpr.hir_id);
                     match res {
                         Res::Local(hir_id) => {
                             let parent_id = self.cx.tcx.hir().get_parent_item(expr.hir_id);
@@ -2184,7 +2184,7 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> {
             if let QPath::Resolved(None, ref path) = *qpath;
             if path.segments.len() == 1;
             then {
-                if let Res::Local(local_id) = qpath_res(self.cx, qpath, expr.hir_id) {
+                if let Res::Local(local_id) = self.cx.qpath_res(qpath, expr.hir_id) {
                     if local_id == self.var {
                         self.nonindex = true;
                     } else {
@@ -2589,7 +2589,7 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> {
 
 fn var_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<HirId> {
     if let ExprKind::Path(ref qpath) = expr.kind {
-        let path_res = qpath_res(cx, qpath, expr.hir_id);
+        let path_res = cx.qpath_res(qpath, expr.hir_id);
         if let Res::Local(hir_id) = path_res {
             return Some(hir_id);
         }
@@ -2819,7 +2819,7 @@ impl<'a, 'tcx> VarCollectorVisitor<'a, 'tcx> {
         if_chain! {
             if let ExprKind::Path(ref qpath) = ex.kind;
             if let QPath::Resolved(None, _) = *qpath;
-            let res = qpath_res(self.cx, qpath, ex.hir_id);
+            let res = self.cx.qpath_res(qpath, ex.hir_id);
             then {
                 match res {
                     Res::Local(hir_id) => {
diff --git a/src/tools/clippy/clippy_lints/src/manual_strip.rs b/src/tools/clippy/clippy_lints/src/manual_strip.rs
index a0cfe145a301c..42a92104a4919 100644
--- a/src/tools/clippy/clippy_lints/src/manual_strip.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_strip.rs
@@ -1,7 +1,7 @@
 use crate::consts::{constant, Constant};
 use crate::utils::usage::mutated_variables;
 use crate::utils::{
-    eq_expr_value, higher, match_def_path, meets_msrv, multispan_sugg, paths, qpath_res, snippet, span_lint_and_then,
+    eq_expr_value, higher, match_def_path, meets_msrv, multispan_sugg, paths, snippet, span_lint_and_then,
 };
 
 use if_chain::if_chain;
@@ -92,7 +92,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip {
                 } else {
                     return;
                 };
-                let target_res = qpath_res(cx, &target_path, target_arg.hir_id);
+                let target_res = cx.qpath_res(&target_path, target_arg.hir_id);
                 if target_res == Res::Err {
                     return;
                 };
@@ -221,7 +221,7 @@ fn find_stripping<'tcx>(
                 if let ExprKind::Index(indexed, index) = &unref.kind;
                 if let Some(higher::Range { start, end, .. }) = higher::range(index);
                 if let ExprKind::Path(path) = &indexed.kind;
-                if qpath_res(self.cx, path, ex.hir_id) == self.target;
+                if self.cx.qpath_res(path, ex.hir_id) == self.target;
                 then {
                     match (self.strip_kind, start, end) {
                         (StripKind::Prefix, Some(start), None) => {
@@ -235,7 +235,7 @@ fn find_stripping<'tcx>(
                                 if let ExprKind::Binary(Spanned { node: BinOpKind::Sub, .. }, left, right) = end.kind;
                                 if let Some(left_arg) = len_arg(self.cx, left);
                                 if let ExprKind::Path(left_path) = &left_arg.kind;
-                                if qpath_res(self.cx, left_path, left_arg.hir_id) == self.target;
+                                if self.cx.qpath_res(left_path, left_arg.hir_id) == self.target;
                                 if eq_pattern_length(self.cx, self.pattern, right);
                                 then {
                                     self.results.push(ex.span);
diff --git a/src/tools/clippy/clippy_lints/src/mem_forget.rs b/src/tools/clippy/clippy_lints/src/mem_forget.rs
index 8c6fd10f98a1e..d34f9761e26f9 100644
--- a/src/tools/clippy/clippy_lints/src/mem_forget.rs
+++ b/src/tools/clippy/clippy_lints/src/mem_forget.rs
@@ -1,4 +1,4 @@
-use crate::utils::{match_def_path, paths, qpath_res, span_lint};
+use crate::utils::{match_def_path, paths, span_lint};
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -29,7 +29,7 @@ impl<'tcx> LateLintPass<'tcx> for MemForget {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
         if let ExprKind::Call(ref path_expr, ref args) = e.kind {
             if let ExprKind::Path(ref qpath) = path_expr.kind {
-                if let Some(def_id) = qpath_res(cx, qpath, path_expr.hir_id).opt_def_id() {
+                if let Some(def_id) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id() {
                     if match_def_path(cx, def_id, &paths::MEM_FORGET) {
                         let forgot_ty = cx.typeck_results().expr_ty(&args[0]);
 
diff --git a/src/tools/clippy/clippy_lints/src/no_effect.rs b/src/tools/clippy/clippy_lints/src/no_effect.rs
index b1b5b3439a0e3..69302d695ce0a 100644
--- a/src/tools/clippy/clippy_lints/src/no_effect.rs
+++ b/src/tools/clippy/clippy_lints/src/no_effect.rs
@@ -1,4 +1,4 @@
-use crate::utils::{has_drop, qpath_res, snippet_opt, span_lint, span_lint_and_sugg};
+use crate::utils::{has_drop, snippet_opt, span_lint, span_lint_and_sugg};
 use rustc_errors::Applicability;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::{BinOpKind, BlockCheckMode, Expr, ExprKind, Stmt, StmtKind, UnsafeSource};
@@ -67,7 +67,7 @@ fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
         },
         ExprKind::Call(ref callee, ref args) => {
             if let ExprKind::Path(ref qpath) = callee.kind {
-                let res = qpath_res(cx, qpath, callee.hir_id);
+                let res = cx.qpath_res(qpath, callee.hir_id);
                 match res {
                     Res::Def(DefKind::Struct | DefKind::Variant | DefKind::Ctor(..), ..) => {
                         !has_drop(cx, cx.typeck_results().expr_ty(expr))
@@ -146,7 +146,7 @@ fn reduce_expression<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<Vec
         },
         ExprKind::Call(ref callee, ref args) => {
             if let ExprKind::Path(ref qpath) = callee.kind {
-                let res = qpath_res(cx, qpath, callee.hir_id);
+                let res = cx.qpath_res(qpath, callee.hir_id);
                 match res {
                     Res::Def(DefKind::Struct | DefKind::Variant | DefKind::Ctor(..), ..)
                         if !has_drop(cx, cx.typeck_results().expr_ty(expr)) =>
diff --git a/src/tools/clippy/clippy_lints/src/non_copy_const.rs b/src/tools/clippy/clippy_lints/src/non_copy_const.rs
index 3a9aa6ced03ba..f57d753631755 100644
--- a/src/tools/clippy/clippy_lints/src/non_copy_const.rs
+++ b/src/tools/clippy/clippy_lints/src/non_copy_const.rs
@@ -18,7 +18,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::{InnerSpan, Span, DUMMY_SP};
 use rustc_typeck::hir_ty_to_ty;
 
-use crate::utils::{in_constant, qpath_res, span_lint_and_then};
+use crate::utils::{in_constant, span_lint_and_then};
 use if_chain::if_chain;
 
 // FIXME: this is a correctness problem but there's no suitable
@@ -339,7 +339,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst {
             }
 
             // Make sure it is a const item.
-            let item_def_id = match qpath_res(cx, qpath, expr.hir_id) {
+            let item_def_id = match cx.qpath_res(qpath, expr.hir_id) {
                 Res::Def(DefKind::Const | DefKind::AssocConst, did) => did,
                 _ => return,
             };
diff --git a/src/tools/clippy/clippy_lints/src/to_string_in_display.rs b/src/tools/clippy/clippy_lints/src/to_string_in_display.rs
index c53727ba16004..fa508df865e48 100644
--- a/src/tools/clippy/clippy_lints/src/to_string_in_display.rs
+++ b/src/tools/clippy/clippy_lints/src/to_string_in_display.rs
@@ -1,4 +1,4 @@
-use crate::utils::{match_def_path, match_trait_method, paths, qpath_res, span_lint};
+use crate::utils::{match_def_path, match_trait_method, paths, span_lint};
 use if_chain::if_chain;
 use rustc_hir::def::Res;
 use rustc_hir::{Expr, ExprKind, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind};
@@ -94,7 +94,7 @@ impl LateLintPass<'_> for ToStringInDisplay {
             if match_trait_method(cx, expr, &paths::TO_STRING);
             if self.in_display_impl;
             if let ExprKind::Path(ref qpath) = args[0].kind;
-            if let Res::Local(hir_id) = qpath_res(cx, qpath, args[0].hir_id);
+            if let Res::Local(hir_id) = cx.qpath_res(qpath, args[0].hir_id);
             if let Some(self_hir_id) = self.self_hir_id;
             if hir_id == self_hir_id;
             then {
diff --git a/src/tools/clippy/clippy_lints/src/types.rs b/src/tools/clippy/clippy_lints/src/types.rs
index 3b5a83d2a0bec..624ea16f585d2 100644
--- a/src/tools/clippy/clippy_lints/src/types.rs
+++ b/src/tools/clippy/clippy_lints/src/types.rs
@@ -34,7 +34,7 @@ use crate::utils::sugg::Sugg;
 use crate::utils::{
     clip, comparisons, differing_macro_contexts, higher, in_constant, indent_of, int_bits, is_hir_ty_cfg_dependant,
     is_type_diagnostic_item, last_path_segment, match_def_path, match_path, meets_msrv, method_chain_args,
-    multispan_sugg, numeric_literal::NumericLiteral, qpath_res, reindent_multiline, sext, snippet, snippet_opt,
+    multispan_sugg, numeric_literal::NumericLiteral, reindent_multiline, sext, snippet, snippet_opt,
     snippet_with_applicability, snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg,
     span_lint_and_then, unsext,
 };
@@ -298,7 +298,7 @@ fn match_type_parameter(cx: &LateContext<'_>, qpath: &QPath<'_>, path: &[&str])
             _ => None,
         });
         if let TyKind::Path(ref qpath) = ty.kind;
-        if let Some(did) = qpath_res(cx, qpath, ty.hir_id).opt_def_id();
+        if let Some(did) = cx.qpath_res(qpath, ty.hir_id).opt_def_id();
         if match_def_path(cx, did, path);
         then {
             return Some(ty.span);
@@ -365,7 +365,7 @@ impl Types {
         match hir_ty.kind {
             TyKind::Path(ref qpath) if !is_local => {
                 let hir_id = hir_ty.hir_id;
-                let res = qpath_res(cx, qpath, hir_id);
+                let res = cx.qpath_res(qpath, hir_id);
                 if let Some(def_id) = res.opt_def_id() {
                     if Some(def_id) == cx.tcx.lang_items().owned_box() {
                         if let Some(span) = match_borrows_parameter(cx, qpath) {
@@ -535,7 +535,7 @@ impl Types {
                             });
                             // ty is now _ at this point
                             if let TyKind::Path(ref ty_qpath) = ty.kind;
-                            let res = qpath_res(cx, ty_qpath, ty.hir_id);
+                            let res = cx.qpath_res(ty_qpath, ty.hir_id);
                             if let Some(def_id) = res.opt_def_id();
                             if Some(def_id) == cx.tcx.lang_items().owned_box();
                             // At this point, we know ty is Box<T>, now get T
@@ -652,7 +652,7 @@ impl Types {
         match mut_ty.ty.kind {
             TyKind::Path(ref qpath) => {
                 let hir_id = mut_ty.ty.hir_id;
-                let def = qpath_res(cx, qpath, hir_id);
+                let def = cx.qpath_res(qpath, hir_id);
                 if_chain! {
                     if let Some(def_id) = def.opt_def_id();
                     if Some(def_id) == cx.tcx.lang_items().owned_box();
@@ -739,7 +739,7 @@ fn is_any_trait(t: &hir::Ty<'_>) -> bool {
 
 fn get_bounds_if_impl_trait<'tcx>(cx: &LateContext<'tcx>, qpath: &QPath<'_>, id: HirId) -> Option<GenericBounds<'tcx>> {
     if_chain! {
-        if let Some(did) = qpath_res(cx, qpath, id).opt_def_id();
+        if let Some(did) = cx.qpath_res(qpath, id).opt_def_id();
         if let Some(Node::GenericParam(generic_param)) = cx.tcx.hir().get_if_local(did);
         if let GenericParamKind::Type { synthetic, .. } = generic_param.kind;
         if synthetic == Some(SyntheticTyParamKind::ImplTrait);
diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
index 7aa17520ba79f..822863ca3e279 100644
--- a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
@@ -1,7 +1,7 @@
 use crate::consts::{constant_simple, Constant};
 use crate::utils::{
-    is_expn_of, match_def_path, match_qpath, match_type, method_calls, path_to_res, paths, qpath_res, run_lints,
-    snippet, span_lint, span_lint_and_help, span_lint_and_sugg, SpanlessEq,
+    is_expn_of, match_def_path, match_qpath, match_type, method_calls, path_to_res, paths, run_lints, snippet,
+    span_lint, span_lint_and_help, span_lint_and_sugg, SpanlessEq,
 };
 use if_chain::if_chain;
 use rustc_ast::ast::{Crate as AstCrate, ItemKind, LitKind, NodeId};
@@ -787,7 +787,7 @@ fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Ve
 
     match &expr.kind {
         ExprKind::AddrOf(.., expr) => return path_to_matched_type(cx, expr),
-        ExprKind::Path(qpath) => match qpath_res(cx, qpath, expr.hir_id) {
+        ExprKind::Path(qpath) => match cx.qpath_res(qpath, expr.hir_id) {
             Res::Local(hir_id) => {
                 let parent_id = cx.tcx.hir().get_parent_node(hir_id);
                 if let Some(Node::Local(local)) = cx.tcx.hir().find(parent_id) {
diff --git a/src/tools/clippy/clippy_lints/src/utils/mod.rs b/src/tools/clippy/clippy_lints/src/utils/mod.rs
index 9b262517a9834..023fb0a7112c1 100644
--- a/src/tools/clippy/clippy_lints/src/utils/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/mod.rs
@@ -370,19 +370,6 @@ pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Option<def::Res> {
     }
 }
 
-pub fn qpath_res(cx: &LateContext<'_>, qpath: &hir::QPath<'_>, id: hir::HirId) -> Res {
-    match qpath {
-        hir::QPath::Resolved(_, path) => path.res,
-        hir::QPath::TypeRelative(..) | hir::QPath::LangItem(..) => {
-            if cx.tcx.has_typeck_results(id.owner.to_def_id()) {
-                cx.tcx.typeck(id.owner).qpath_res(qpath, id)
-            } else {
-                Res::Err
-            }
-        },
-    }
-}
-
 /// Convenience function to get the `DefId` of a trait by path.
 /// It could be a trait or trait alias.
 pub fn get_trait_def_id(cx: &LateContext<'_>, path: &[&str]) -> Option<DefId> {
diff --git a/src/tools/clippy/clippy_lints/src/utils/paths.rs b/src/tools/clippy/clippy_lints/src/utils/paths.rs
index c0b203b5388dc..432cc5b59f684 100644
--- a/src/tools/clippy/clippy_lints/src/utils/paths.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/paths.rs
@@ -48,7 +48,6 @@ pub const FN_MUT: [&str; 3] = ["core", "ops", "FnMut"];
 pub const FN_ONCE: [&str; 3] = ["core", "ops", "FnOnce"];
 pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"];
 pub const FROM_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "FromIterator"];
-pub const FROM_TRAIT: [&str; 3] = ["core", "convert", "From"];
 pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"];
 pub const HASH: [&str; 3] = ["core", "hash", "Hash"];
 pub const HASHMAP: [&str; 5] = ["std", "collections", "hash", "map", "HashMap"];
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index 5608ff98417cd..52aed57fc76af 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -2015,10 +2015,10 @@ impl<'test> TestCx<'test> {
                 rustc.args(&["-Zchalk"]);
             }
             Some(CompareMode::SplitDwarf) => {
-                rustc.args(&["-Zsplit-dwarf=split"]);
+                rustc.args(&["-Csplit-debuginfo=unpacked", "-Zunstable-options"]);
             }
             Some(CompareMode::SplitDwarfSingle) => {
-                rustc.args(&["-Zsplit-dwarf=single"]);
+                rustc.args(&["-Csplit-debuginfo=packed", "-Zunstable-options"]);
             }
             None => {}
         }
diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs
index e687901f212b6..21d05226fb42c 100644
--- a/src/tools/tidy/src/ui_tests.rs
+++ b/src/tools/tidy/src/ui_tests.rs
@@ -7,7 +7,7 @@ use std::path::Path;
 
 const ENTRY_LIMIT: usize = 1000;
 // FIXME: The following limits should be reduced eventually.
-const ROOT_ENTRY_LIMIT: usize = 1458;
+const ROOT_ENTRY_LIMIT: usize = 1459;
 const ISSUES_ENTRY_LIMIT: usize = 2669;
 
 fn check_entries(path: &Path, bad: &mut bool) {