about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorRalf Jung <post@ralfj.de>2023-01-15 16:46:17 -0500
committerRalf Jung <post@ralfj.de>2023-01-15 16:46:17 -0500
commitb23795229781a2125fade078fa610bc47cc75b16 (patch)
treec57813bd1e93312f265ead2df13e5a9f885b17e9 /src
parentec5f8253e28a660034a3936a171f93a3c063a0cc (diff)
parentfa2ff4d7e5a88bfda186f0f3f621402470745788 (diff)
Merge from rustc
Diffstat (limited to 'src')
-rw-r--r--src/README.md5
-rw-r--r--src/bootstrap/bin/rustc.rs35
-rw-r--r--src/bootstrap/builder.rs52
-rw-r--r--src/bootstrap/check.rs4
-rw-r--r--src/bootstrap/test.rs4
-rw-r--r--src/bootstrap/tool.rs25
-rwxr-xr-xsrc/ci/docker/host-x86_64/dist-various-2/build-wasi-toolchain.sh2
-rw-r--r--src/ci/docker/host-x86_64/mingw-check/Dockerfile1
-rw-r--r--src/doc/rustdoc/src/lints.md2
-rw-r--r--src/librustdoc/clean/blanket_impl.rs2
-rw-r--r--src/librustdoc/clean/inline.rs2
-rw-r--r--src/librustdoc/clean/mod.rs4
-rw-r--r--src/librustdoc/doctest.rs2
-rw-r--r--src/librustdoc/formats/cache.rs9
-rw-r--r--src/librustdoc/html/format.rs21
-rw-r--r--src/librustdoc/html/markdown.rs6
-rw-r--r--src/librustdoc/html/render/context.rs9
-rw-r--r--src/librustdoc/html/render/mod.rs20
-rw-r--r--src/librustdoc/html/render/print_item.rs10
-rw-r--r--src/librustdoc/html/render/search_index.rs10
-rw-r--r--src/librustdoc/html/render/write_shared.rs2
-rw-r--r--src/librustdoc/html/static/css/rustdoc.css82
-rw-r--r--src/librustdoc/html/static/css/settings.css14
-rw-r--r--src/librustdoc/html/static/js/main.js12
-rw-r--r--src/librustdoc/html/static/js/settings.js4
-rw-r--r--src/librustdoc/html/templates/print_item.html2
-rw-r--r--src/librustdoc/lib.rs3
-rw-r--r--src/librustdoc/passes/collect_intra_doc_links.rs2
-rw-r--r--src/librustdoc/passes/collect_intra_doc_links/early.rs22
-rw-r--r--src/librustdoc/passes/stripper.rs12
m---------src/tools/cargo0
-rw-r--r--src/tools/clippy/clippy_lints/src/attrs.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/derive.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/fallible_impl_from.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/from_over_into.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_inline.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/use_self.rs2
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-6252.stderr9
-rw-r--r--src/tools/clippy/tests/ui/def_id_nocore.rs2
-rw-r--r--src/tools/miri/tests/pass-dep/shims/libc-fs-with-isolation.rs5
-rw-r--r--src/tools/rustbook/Cargo.toml4
-rw-r--r--src/tools/rustbook/src/main.rs22
-rw-r--r--src/tools/tidy/Cargo.toml1
-rw-r--r--src/tools/tidy/src/deps.rs170
46 files changed, 331 insertions, 280 deletions
diff --git a/src/README.md b/src/README.md
index 90ab8026970..7b357da2ffc 100644
--- a/src/README.md
+++ b/src/README.md
@@ -1,8 +1,7 @@
-This directory contains the source code of the rust project, including:
+This directory contains some source code for the Rust project, including:
 
-- The test suite
 - The bootstrapping build system
-- Various submodules for tools, like cargo, etc.
+- Various submodules for tools, like cargo, tidy, etc.
 
 For more information on how various parts of the compiler work, see the [rustc dev guide].
 
diff --git a/src/bootstrap/bin/rustc.rs b/src/bootstrap/bin/rustc.rs
index 1d526022812..9611c866df5 100644
--- a/src/bootstrap/bin/rustc.rs
+++ b/src/bootstrap/bin/rustc.rs
@@ -154,6 +154,41 @@ fn main() {
         cmd.arg("-Z").arg("force-unstable-if-unmarked");
     }
 
+    // allow-features is handled from within this rustc wrapper because of
+    // issues with build scripts. Some packages use build scripts to
+    // dynamically detect if certain nightly features are available.
+    // There are different ways this causes problems:
+    //
+    // * rustix runs `rustc` on a small test program to see if the feature is
+    //   available (and sets a `cfg` if it is). It does not honor
+    //   CARGO_ENCODED_RUSTFLAGS.
+    // * proc-macro2 detects if `rustc -vV` says "nighty" or "dev" and enables
+    //   nightly features. It will scan CARGO_ENCODED_RUSTFLAGS for
+    //   -Zallow-features. Unfortunately CARGO_ENCODED_RUSTFLAGS is not set
+    //   for build-dependencies when --target is used.
+    //
+    // The issues above means we can't just use RUSTFLAGS, and we can't use
+    // `cargo -Zallow-features=…`. Passing it through here ensures that it
+    // always gets set. Unfortunately that also means we need to enable more
+    // features than we really want (like those for proc-macro2), but there
+    // isn't much of a way around it.
+    //
+    // I think it is unfortunate that build scripts are doing this at all,
+    // since changes to nightly features can cause crates to break even if the
+    // user didn't want or care about the use of the nightly features. I think
+    // nightly features should be opt-in only. Unfortunately the dynamic
+    // checks are now too wide spread that we just need to deal with it.
+    //
+    // If you want to try to remove this, I suggest working with the crate
+    // authors to remove the dynamic checking. Another option is to pursue
+    // https://github.com/rust-lang/cargo/issues/11244 and
+    // https://github.com/rust-lang/cargo/issues/4423, which will likely be
+    // very difficult, but could help expose -Zallow-features into build
+    // scripts so they could try to honor them.
+    if let Ok(allow_features) = env::var("RUSTC_ALLOW_FEATURES") {
+        cmd.arg(format!("-Zallow-features={allow_features}"));
+    }
+
     if let Ok(flags) = env::var("MAGIC_EXTRA_RUSTFLAGS") {
         for flag in flags.split(' ') {
             cmd.arg(flag);
diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs
index 52a83fa29c7..b4fc1d4f28d 100644
--- a/src/bootstrap/builder.rs
+++ b/src/bootstrap/builder.rs
@@ -1381,18 +1381,29 @@ impl<'a> Builder<'a> {
         // this), as well as #63012 which is the tracking issue for this
         // feature on the rustc side.
         cargo.arg("-Zbinary-dep-depinfo");
-        match mode {
-            Mode::ToolBootstrap => {
-                // Restrict the allowed features to those passed by rustbuild, so we don't depend on nightly accidentally.
-                rustflags.arg("-Zallow-features=binary-dep-depinfo");
-            }
-            Mode::ToolStd => {
-                // Right now this is just compiletest and a few other tools that build on stable.
-                // Allow them to use `feature(test)`, but nothing else.
-                rustflags.arg("-Zallow-features=binary-dep-depinfo,test,proc_macro_internals,proc_macro_diagnostic,proc_macro_span");
+        let allow_features = match mode {
+            Mode::ToolBootstrap | Mode::ToolStd => {
+                // Restrict the allowed features so we don't depend on nightly
+                // accidentally.
+                //
+                // binary-dep-depinfo is used by rustbuild itself for all
+                // compilations.
+                //
+                // Lots of tools depend on proc_macro2 and proc-macro-error.
+                // Those have build scripts which assume nightly features are
+                // available if the `rustc` version is "nighty" or "dev". See
+                // bin/rustc.rs for why that is a problem. Instead of labeling
+                // those features for each individual tool that needs them,
+                // just blanket allow them here.
+                //
+                // If this is ever removed, be sure to add something else in
+                // its place to keep the restrictions in place (or make a way
+                // to unset RUSTC_BOOTSTRAP).
+                "binary-dep-depinfo,proc_macro_span,proc_macro_span_shrink,proc_macro_diagnostic"
+                    .to_string()
             }
-            Mode::Std | Mode::Rustc | Mode::Codegen | Mode::ToolRustc => {}
-        }
+            Mode::Std | Mode::Rustc | Mode::Codegen | Mode::ToolRustc => String::new(),
+        };
 
         cargo.arg("-j").arg(self.jobs().to_string());
 
@@ -1915,7 +1926,7 @@ impl<'a> Builder<'a> {
             }
         }
 
-        Cargo { command: cargo, rustflags, rustdocflags }
+        Cargo { command: cargo, rustflags, rustdocflags, allow_features }
     }
 
     /// Ensure that a given step is built, returning its output. This will
@@ -2094,6 +2105,7 @@ pub struct Cargo {
     command: Command,
     rustflags: Rustflags,
     rustdocflags: Rustflags,
+    allow_features: String,
 }
 
 impl Cargo {
@@ -2138,6 +2150,18 @@ impl Cargo {
         self.command.current_dir(dir);
         self
     }
+
+    /// Adds nightly-only features that this invocation is allowed to use.
+    ///
+    /// By default, all nightly features are allowed. Once this is called, it
+    /// will be restricted to the given set.
+    pub fn allow_features(&mut self, features: &str) -> &mut Cargo {
+        if !self.allow_features.is_empty() {
+            self.allow_features.push(',');
+        }
+        self.allow_features.push_str(features);
+        self
+    }
 }
 
 impl From<Cargo> for Command {
@@ -2152,6 +2176,10 @@ impl From<Cargo> for Command {
             cargo.command.env("RUSTDOCFLAGS", rustdocflags);
         }
 
+        if !cargo.allow_features.is_empty() {
+            cargo.command.env("RUSTC_ALLOW_FEATURES", cargo.allow_features);
+        }
+
         cargo.command
     }
 }
diff --git a/src/bootstrap/check.rs b/src/bootstrap/check.rs
index 2771bd2264c..4b8a58e87b6 100644
--- a/src/bootstrap/check.rs
+++ b/src/bootstrap/check.rs
@@ -346,9 +346,7 @@ impl Step for RustAnalyzer {
             &["rust-analyzer/in-rust-tree".to_owned()],
         );
 
-        cargo.rustflag(
-            "-Zallow-features=proc_macro_internals,proc_macro_diagnostic,proc_macro_span",
-        );
+        cargo.allow_features(crate::tool::RustAnalyzer::ALLOW_FEATURES);
 
         // For ./x.py clippy, don't check those targets because
         // linting tests and benchmarks can produce very noisy results
diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs
index 159f2fba671..6078e39ac9d 100644
--- a/src/bootstrap/test.rs
+++ b/src/bootstrap/test.rs
@@ -378,6 +378,7 @@ impl Step for RustAnalyzer {
             SourceType::InTree,
             &["sysroot-abi".to_owned()],
         );
+        cargo.allow_features(tool::RustAnalyzer::ALLOW_FEATURES);
 
         let dir = builder.src.join(workspace_path);
         // needed by rust-analyzer to find its own text fixtures, cf.
@@ -690,7 +691,7 @@ impl Step for CompiletestTest {
         // We need `ToolStd` for the locally-built sysroot because
         // compiletest uses unstable features of the `test` crate.
         builder.ensure(compile::Std::new(compiler, host));
-        let cargo = tool::prepare_tool_cargo(
+        let mut cargo = tool::prepare_tool_cargo(
             builder,
             compiler,
             Mode::ToolStd,
@@ -700,6 +701,7 @@ impl Step for CompiletestTest {
             SourceType::InTree,
             &[],
         );
+        cargo.allow_features("test");
 
         try_run(builder, &mut cargo.into());
     }
diff --git a/src/bootstrap/tool.rs b/src/bootstrap/tool.rs
index 24b033cc0dc..9a2100c2fb7 100644
--- a/src/bootstrap/tool.rs
+++ b/src/bootstrap/tool.rs
@@ -29,6 +29,8 @@ struct ToolBuild {
     is_optional_tool: bool,
     source_type: SourceType,
     extra_features: Vec<String>,
+    /// Nightly-only features that are allowed (comma-separated list).
+    allow_features: &'static str,
 }
 
 impl Step for ToolBuild {
@@ -59,7 +61,7 @@ impl Step for ToolBuild {
             _ => panic!("unexpected Mode for tool build"),
         }
 
-        let cargo = prepare_tool_cargo(
+        let mut cargo = prepare_tool_cargo(
             builder,
             compiler,
             self.mode,
@@ -69,6 +71,9 @@ impl Step for ToolBuild {
             self.source_type,
             &self.extra_features,
         );
+        if !self.allow_features.is_empty() {
+            cargo.allow_features(self.allow_features);
+        }
 
         builder.info(&format!("Building stage{} tool {} ({})", compiler.stage, tool, target));
         let mut duplicates = Vec::new();
@@ -292,6 +297,7 @@ macro_rules! bootstrap_tool {
         $name:ident, $path:expr, $tool_name:expr
         $(,is_external_tool = $external:expr)*
         $(,is_unstable_tool = $unstable:expr)*
+        $(,allow_features = $allow_features:expr)?
         ;
     )+) => {
         #[derive(Copy, PartialEq, Eq, Clone)]
@@ -355,6 +361,7 @@ macro_rules! bootstrap_tool {
                         SourceType::InTree
                     },
                     extra_features: vec![],
+                    allow_features: concat!($($allow_features)*),
                 }).expect("expected to build -- essential tool")
             }
         }
@@ -368,7 +375,7 @@ bootstrap_tool!(
     Tidy, "src/tools/tidy", "tidy";
     Linkchecker, "src/tools/linkchecker", "linkchecker";
     CargoTest, "src/tools/cargotest", "cargotest";
-    Compiletest, "src/tools/compiletest", "compiletest", is_unstable_tool = true;
+    Compiletest, "src/tools/compiletest", "compiletest", is_unstable_tool = true, allow_features = "test";
     BuildManifest, "src/tools/build-manifest", "build-manifest";
     RemoteTestClient, "src/tools/remote-test-client", "remote-test-client";
     RustInstaller, "src/tools/rust-installer", "rust-installer", is_external_tool = true;
@@ -435,6 +442,7 @@ impl Step for ErrorIndex {
                 is_optional_tool: false,
                 source_type: SourceType::InTree,
                 extra_features: Vec::new(),
+                allow_features: "",
             })
             .expect("expected to build -- essential tool")
     }
@@ -471,6 +479,7 @@ impl Step for RemoteTestServer {
                 is_optional_tool: false,
                 source_type: SourceType::InTree,
                 extra_features: Vec::new(),
+                allow_features: "",
             })
             .expect("expected to build -- essential tool")
     }
@@ -622,6 +631,7 @@ impl Step for Cargo {
                 is_optional_tool: false,
                 source_type: SourceType::Submodule,
                 extra_features: Vec::new(),
+                allow_features: "",
             })
             .expect("expected to build -- essential tool");
 
@@ -637,6 +647,7 @@ impl Step for Cargo {
                 is_optional_tool: true,
                 source_type: SourceType::Submodule,
                 extra_features: Vec::new(),
+                allow_features: "",
             });
         };
 
@@ -684,6 +695,7 @@ impl Step for LldWrapper {
                 is_optional_tool: false,
                 source_type: SourceType::InTree,
                 extra_features: Vec::new(),
+                allow_features: "",
             })
             .expect("expected to build -- essential tool");
 
@@ -697,6 +709,11 @@ pub struct RustAnalyzer {
     pub target: TargetSelection,
 }
 
+impl RustAnalyzer {
+    pub const ALLOW_FEATURES: &str =
+        "proc_macro_internals,proc_macro_diagnostic,proc_macro_span,proc_macro_span_shrink";
+}
+
 impl Step for RustAnalyzer {
     type Output = Option<PathBuf>;
     const DEFAULT: bool = true;
@@ -731,6 +748,7 @@ impl Step for RustAnalyzer {
             extra_features: vec!["rust-analyzer/in-rust-tree".to_owned()],
             is_optional_tool: false,
             source_type: SourceType::InTree,
+            allow_features: RustAnalyzer::ALLOW_FEATURES,
         })
     }
 }
@@ -769,6 +787,7 @@ impl Step for RustAnalyzerProcMacroSrv {
             extra_features: vec!["proc-macro-srv/sysroot-abi".to_owned()],
             is_optional_tool: false,
             source_type: SourceType::InTree,
+            allow_features: RustAnalyzer::ALLOW_FEATURES,
         })?;
 
         // Copy `rust-analyzer-proc-macro-srv` to `<sysroot>/libexec/`
@@ -788,6 +807,7 @@ macro_rules! tool_extended {
        $tool_name:expr,
        stable = $stable:expr
        $(,tool_std = $tool_std:literal)?
+       $(,allow_features = $allow_features:expr)?
        ;)+) => {
         $(
             #[derive(Debug, Clone, Hash, PartialEq, Eq)]
@@ -839,6 +859,7 @@ macro_rules! tool_extended {
                     extra_features: $sel.extra_features,
                     is_optional_tool: true,
                     source_type: SourceType::InTree,
+                    allow_features: concat!($($allow_features)*),
                 })
             }
         }
diff --git a/src/ci/docker/host-x86_64/dist-various-2/build-wasi-toolchain.sh b/src/ci/docker/host-x86_64/dist-various-2/build-wasi-toolchain.sh
index 67cee0148b2..5fbce36c39d 100755
--- a/src/ci/docker/host-x86_64/dist-various-2/build-wasi-toolchain.sh
+++ b/src/ci/docker/host-x86_64/dist-various-2/build-wasi-toolchain.sh
@@ -10,7 +10,7 @@ bin="$PWD/clang+llvm-15.0.6-x86_64-linux-gnu-ubuntu-18.04/bin"
 git clone https://github.com/WebAssembly/wasi-libc
 
 cd wasi-libc
-git reset --hard 8b7148f69ae241a2749b3defe4606da8143b72e0
+git reset --hard 4362b1885fd369e042a7c0ecd8df3b6cd47fb4e8
 make -j$(nproc) \
     CC="$bin/clang" \
     NM="$bin/llvm-nm" \
diff --git a/src/ci/docker/host-x86_64/mingw-check/Dockerfile b/src/ci/docker/host-x86_64/mingw-check/Dockerfile
index adf6bb4b377..d5bc76eeb23 100644
--- a/src/ci/docker/host-x86_64/mingw-check/Dockerfile
+++ b/src/ci/docker/host-x86_64/mingw-check/Dockerfile
@@ -45,6 +45,7 @@ ENV SCRIPT python3 ../x.py --stage 2 test src/tools/expand-yaml-anchors && \
            python3 ../x.py test --stage 0 src/tools/compiletest && \
            python3 ../x.py test --stage 0 core alloc std test proc_macro && \
            # Build both public and internal documentation.
+           RUSTDOCFLAGS=\"--document-private-items --document-hidden-items\" python3 ../x.py doc --stage 0 compiler && \
            RUSTDOCFLAGS=\"--document-private-items --document-hidden-items\" python3 ../x.py doc --stage 0 library/test && \
            /scripts/validate-toolstate.sh && \
            /scripts/validate-error-codes.sh && \
diff --git a/src/doc/rustdoc/src/lints.md b/src/doc/rustdoc/src/lints.md
index bff01d7cb7c..45db3bb9b00 100644
--- a/src/doc/rustdoc/src/lints.md
+++ b/src/doc/rustdoc/src/lints.md
@@ -261,7 +261,7 @@ typo mistakes for some common attributes.
 
 ## `invalid_html_tags`
 
-This lint is **allowed by default** and is **nightly-only**. It detects unclosed
+This lint **warns by default**. It detects unclosed
 or invalid HTML tags. For example:
 
 ```rust
diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs
index 4ef5747596b..e6b2b234945 100644
--- a/src/librustdoc/clean/blanket_impl.rs
+++ b/src/librustdoc/clean/blanket_impl.rs
@@ -33,7 +33,7 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> {
                     trait_def_id,
                     impl_def_id
                 );
-                let trait_ref = cx.tcx.bound_impl_trait_ref(impl_def_id).unwrap();
+                let trait_ref = cx.tcx.impl_trait_ref(impl_def_id).unwrap();
                 if !matches!(trait_ref.0.self_ty().kind(), ty::Param(_)) {
                     continue;
                 }
diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs
index aad24b4a074..c6939326144 100644
--- a/src/librustdoc/clean/inline.rs
+++ b/src/librustdoc/clean/inline.rs
@@ -376,7 +376,7 @@ pub(crate) fn build_impl(
     let _prof_timer = cx.tcx.sess.prof.generic_activity("build_impl");
 
     let tcx = cx.tcx;
-    let associated_trait = tcx.impl_trait_ref(did);
+    let associated_trait = tcx.impl_trait_ref(did).map(ty::EarlyBinder::skip_binder);
 
     // Only inline impl if the implemented trait is
     // reachable in rustdoc generated documentation
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 025a4379f45..415e7d5a360 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -506,7 +506,9 @@ fn clean_generic_param_def<'tcx>(
                     Some(def.def_id),
                 )),
                 default: match has_default {
-                    true => Some(Box::new(cx.tcx.const_param_default(def.def_id).to_string())),
+                    true => Some(Box::new(
+                        cx.tcx.const_param_default(def.def_id).subst_identity().to_string(),
+                    )),
                     false => None,
                 },
             },
diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs
index 5f8c777f32a..d1b6d470e86 100644
--- a/src/librustdoc/doctest.rs
+++ b/src/librustdoc/doctest.rs
@@ -1221,7 +1221,7 @@ impl<'a, 'hir, 'tcx> HirCollector<'a, 'hir, 'tcx> {
     ) {
         let ast_attrs = self.tcx.hir().attrs(hir_id);
         if let Some(ref cfg) = ast_attrs.cfg(self.tcx, &FxHashSet::default()) {
-            if !cfg.matches(&self.sess.parse_sess, Some(self.sess.features_untracked())) {
+            if !cfg.matches(&self.sess.parse_sess, Some(self.tcx.features())) {
                 return;
             }
         }
diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs
index d027fb6e876..3149c22b8e5 100644
--- a/src/librustdoc/formats/cache.rs
+++ b/src/librustdoc/formats/cache.rs
@@ -242,7 +242,7 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
         }
 
         // Index this method for searching later on.
-        if let Some(ref s) = item.name.or_else(|| {
+        if let Some(s) = item.name.or_else(|| {
             if item.is_stripped() {
                 None
             } else if let clean::ImportItem(ref i) = *item.kind &&
@@ -317,14 +317,15 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
                             short_markdown_summary(x.as_str(), &item.link_names(self.cache))
                         });
                         let ty = item.type_();
-                        let name = s.to_string();
-                        if ty != ItemType::StructField || u16::from_str_radix(&name, 10).is_err() {
+                        if ty != ItemType::StructField
+                            || u16::from_str_radix(s.as_str(), 10).is_err()
+                        {
                             // In case this is a field from a tuple struct, we don't add it into
                             // the search index because its name is something like "0", which is
                             // not useful for rustdoc search.
                             self.cache.search_index.push(IndexItem {
                                 ty,
-                                name,
+                                name: s,
                                 path: join_with_double_colon(path),
                                 desc,
                                 parent,
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index 5ad24bf2681..006076baf72 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -569,7 +569,7 @@ fn generate_macro_def_id_path(
     root_path: Option<&str>,
 ) -> Result<(String, ItemType, Vec<Symbol>), HrefError> {
     let tcx = cx.shared.tcx;
-    let crate_name = tcx.crate_name(def_id.krate).to_string();
+    let crate_name = tcx.crate_name(def_id.krate);
     let cache = cx.cache();
 
     let fqp: Vec<Symbol> = tcx
@@ -584,7 +584,7 @@ fn generate_macro_def_id_path(
             }
         })
         .collect();
-    let mut relative = fqp.iter().map(|elem| elem.to_string());
+    let mut relative = fqp.iter().copied();
     let cstore = CStore::from_tcx(tcx);
     // We need this to prevent a `panic` when this function is used from intra doc links...
     if !cstore.has_crate_data(def_id.krate) {
@@ -602,9 +602,9 @@ fn generate_macro_def_id_path(
     };
 
     let mut path = if is_macro_2 {
-        once(crate_name.clone()).chain(relative).collect()
+        once(crate_name).chain(relative).collect()
     } else {
-        vec![crate_name.clone(), relative.next_back().unwrap()]
+        vec![crate_name, relative.next_back().unwrap()]
     };
     if path.len() < 2 {
         // The minimum we can have is the crate name followed by the macro name. If shorter, then
@@ -614,17 +614,22 @@ fn generate_macro_def_id_path(
     }
 
     if let Some(last) = path.last_mut() {
-        *last = format!("macro.{}.html", last);
+        *last = Symbol::intern(&format!("macro.{}.html", last.as_str()));
     }
 
     let url = match cache.extern_locations[&def_id.krate] {
         ExternalLocation::Remote(ref s) => {
             // `ExternalLocation::Remote` always end with a `/`.
-            format!("{}{}", s, path.join("/"))
+            format!("{}{}", s, path.iter().map(|p| p.as_str()).join("/"))
         }
         ExternalLocation::Local => {
             // `root_path` always end with a `/`.
-            format!("{}{}/{}", root_path.unwrap_or(""), crate_name, path.join("/"))
+            format!(
+                "{}{}/{}",
+                root_path.unwrap_or(""),
+                crate_name,
+                path.iter().map(|p| p.as_str()).join("/")
+            )
         }
         ExternalLocation::Unknown => {
             debug!("crate {} not in cache when linkifying macros", crate_name);
@@ -1050,7 +1055,7 @@ fn fmt_type<'cx>(
                 _ => String::new(),
             };
             let m = mutability.print_with_space();
-            let amp = if f.alternate() { "&".to_string() } else { "&amp;".to_string() };
+            let amp = if f.alternate() { "&" } else { "&amp;" };
             match **ty {
                 clean::DynTrait(ref bounds, ref trait_lt)
                     if bounds.len() > 1 || trait_lt.is_some() =>
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index aeaee524fd4..4ff67fe1551 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -30,7 +30,7 @@ use rustc_hir::def_id::DefId;
 use rustc_hir::HirId;
 use rustc_middle::ty::TyCtxt;
 use rustc_span::edition::Edition;
-use rustc_span::Span;
+use rustc_span::{Span, Symbol};
 
 use once_cell::sync::Lazy;
 use std::borrow::Cow;
@@ -198,7 +198,7 @@ fn slugify(c: char) -> Option<char> {
 
 #[derive(Clone, Debug)]
 pub struct Playground {
-    pub crate_name: Option<String>,
+    pub crate_name: Option<Symbol>,
     pub url: String,
 }
 
@@ -290,7 +290,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> {
                 .map(|l| map_line(l).for_code())
                 .intersperse("\n".into())
                 .collect::<String>();
-            let krate = krate.as_ref().map(|s| &**s);
+            let krate = krate.as_ref().map(|s| s.as_str());
             let (test, _, _) =
                 doctest::make_test(&test, krate, false, &Default::default(), edition, None);
             let channel = if test.contains("#![feature(") { "&amp;version=nightly" } else { "" };
diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs
index c8899ee62b5..5cefe9475e7 100644
--- a/src/librustdoc/html/render/context.rs
+++ b/src/librustdoc/html/render/context.rs
@@ -464,8 +464,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
         // If user passed in `--playground-url` arg, we fill in crate name here
         let mut playground = None;
         if let Some(url) = playground_url {
-            playground =
-                Some(markdown::Playground { crate_name: Some(krate.name(tcx).to_string()), url });
+            playground = Some(markdown::Playground { crate_name: Some(krate.name(tcx)), url });
         }
         let mut layout = layout::Layout {
             logo: String::new(),
@@ -491,7 +490,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
                 }
                 (sym::html_playground_url, Some(s)) => {
                     playground = Some(markdown::Playground {
-                        crate_name: Some(krate.name(tcx).to_string()),
+                        crate_name: Some(krate.name(tcx)),
                         url: s.to_string(),
                     });
                 }
@@ -639,7 +638,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
                 write!(
                     buf,
                     "<div class=\"main-heading\">\
-                     <h1 class=\"fqn\">Rustdoc settings</h1>\
+                     <h1>Rustdoc settings</h1>\
                      <span class=\"out-of-band\">\
                          <a id=\"back\" href=\"javascript:void(0)\" onclick=\"history.back();\">\
                             Back\
@@ -677,7 +676,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
                 write!(
                     buf,
                     "<div class=\"main-heading\">\
-                     <h1 class=\"fqn\">Rustdoc help</h1>\
+                     <h1>Rustdoc help</h1>\
                      <span class=\"out-of-band\">\
                          <a id=\"back\" href=\"javascript:void(0)\" onclick=\"history.back();\">\
                             Back\
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 8bccf68029a..f95d8e43035 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -100,7 +100,7 @@ pub(crate) fn ensure_trailing_slash(v: &str) -> impl fmt::Display + '_ {
 #[derive(Debug)]
 pub(crate) struct IndexItem {
     pub(crate) ty: ItemType,
-    pub(crate) name: String,
+    pub(crate) name: Symbol,
     pub(crate) path: String,
     pub(crate) desc: String,
     pub(crate) parent: Option<DefId>,
@@ -364,7 +364,7 @@ impl AllTypes {
             }
         }
 
-        f.write_str("<h1 class=\"fqn\">List of all items</h1>");
+        f.write_str("<h1>List of all items</h1>");
         // Note: print_entries does not escape the title, because we know the current set of titles
         // doesn't require escaping.
         print_entries(f, &self.structs, ItemSection::Structs);
@@ -394,7 +394,7 @@ fn scrape_examples_help(shared: &SharedContext<'_>) -> String {
     let mut ids = IdMap::default();
     format!(
         "<div class=\"main-heading\">\
-            <h1 class=\"fqn\">About scraped examples</h1>\
+            <h1>About scraped examples</h1>\
         </div>\
         <div>{}</div>",
         Markdown {
@@ -513,7 +513,7 @@ fn document_full_inner(
         debug!("Doc block: =====\n{}\n=====", s);
         if is_collapsible {
             w.write_str(
-                "<details class=\"rustdoc-toggle top-doc\" open>\
+                "<details class=\"toggle top-doc\" open>\
                 <summary class=\"hideme\">\
                      <span>Expand description</span>\
                 </summary>",
@@ -1343,7 +1343,7 @@ fn notable_traits_decl(ty: &clean::Type, cx: &Context<'_>) -> (String, String) {
                     write!(
                         &mut out,
                         "<h3>Notable traits for <code>{}</code></h3>\
-                     <pre class=\"content\"><code>",
+                     <pre><code>",
                         impl_.for_.print(cx)
                     );
                 }
@@ -1514,7 +1514,7 @@ fn render_impl(
         let toggled = !doc_buffer.is_empty();
         if toggled {
             let method_toggle_class = if item_type.is_method() { " method-toggle" } else { "" };
-            write!(w, "<details class=\"rustdoc-toggle{}\" open><summary>", method_toggle_class);
+            write!(w, "<details class=\"toggle{}\" open><summary>", method_toggle_class);
         }
         match &*item.kind {
             clean::MethodItem(..) | clean::TyMethodItem(_) => {
@@ -1730,7 +1730,7 @@ fn render_impl(
             close_tags.insert_str(0, "</details>");
             write!(
                 w,
-                "<details class=\"rustdoc-toggle implementors-toggle\"{}>",
+                "<details class=\"toggle implementors-toggle\"{}>",
                 if rendering_params.toggle_open_by_default { " open" } else { "" }
             );
             write!(w, "<summary>")
@@ -2769,8 +2769,8 @@ fn collect_paths_for_type(first_ty: clean::Type, cache: &Cache) -> Vec<String> {
     let mut work = VecDeque::new();
 
     let mut process_path = |did: DefId| {
-        let get_extern = || cache.external_paths.get(&did).map(|s| s.0.clone());
-        let fqp = cache.exact_paths.get(&did).cloned().or_else(get_extern);
+        let get_extern = || cache.external_paths.get(&did).map(|s| &s.0);
+        let fqp = cache.exact_paths.get(&did).or_else(get_extern);
 
         if let Some(path) = fqp {
             out.push(join_with_double_colon(&path));
@@ -2999,7 +2999,7 @@ fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Ite
     if it.peek().is_some() {
         write!(
             w,
-            "<details class=\"rustdoc-toggle more-examples-toggle\">\
+            "<details class=\"toggle more-examples-toggle\">\
                   <summary class=\"hideme\">\
                      <span>More examples</span>\
                   </summary>\
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index c16d6477fc3..b93db7e28b2 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -204,7 +204,7 @@ fn should_hide_fields(n_fields: usize) -> bool {
 fn toggle_open(w: &mut Buffer, text: impl fmt::Display) {
     write!(
         w,
-        "<details class=\"rustdoc-toggle type-contents-toggle\">\
+        "<details class=\"toggle type-contents-toggle\">\
             <summary class=\"hideme\">\
                 <span>Show {}</span>\
             </summary>",
@@ -733,7 +733,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
         let toggled = !content.is_empty();
         if toggled {
             let method_toggle_class = if item_type.is_method() { " method-toggle" } else { "" };
-            write!(w, "<details class=\"rustdoc-toggle{method_toggle_class}\" open><summary>");
+            write!(w, "<details class=\"toggle{method_toggle_class}\" open><summary>");
         }
         write!(w, "<section id=\"{}\" class=\"method has-srclink\">", id);
         render_rightside(w, cx, m, t, RenderMode::Normal);
@@ -1027,8 +1027,8 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
         .chain(std::iter::once("implementors"))
         .collect();
     if let Some(did) = it.item_id.as_def_id() &&
-        let get_extern = { || cache.external_paths.get(&did).map(|s| s.0.clone()) } &&
-        let Some(fqp) = cache.exact_paths.get(&did).cloned().or_else(get_extern) {
+        let get_extern = { || cache.external_paths.get(&did).map(|s| &s.0) } &&
+        let Some(fqp) = cache.exact_paths.get(&did).or_else(get_extern) {
         js_src_path.extend(fqp[..fqp.len() - 1].iter().copied());
         js_src_path.push_fmt(format_args!("{}.{}.js", it.type_(), fqp.last().unwrap()));
     } else {
@@ -1840,7 +1840,7 @@ fn document_non_exhaustive(w: &mut Buffer, item: &clean::Item) {
     if item.is_non_exhaustive() {
         write!(
             w,
-            "<details class=\"rustdoc-toggle non-exhaustive\">\
+            "<details class=\"toggle non-exhaustive\">\
                  <summary class=\"hideme\"><span>{}</span></summary>\
                  <div class=\"docblock\">",
             {
diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs
index bc74d9cf969..c64349f413c 100644
--- a/src/librustdoc/html/render/search_index.rs
+++ b/src/librustdoc/html/render/search_index.rs
@@ -35,7 +35,7 @@ pub(crate) fn build_index<'tcx>(
                 .map_or_else(String::new, |s| short_markdown_summary(&s, &item.link_names(cache)));
             cache.search_index.push(IndexItem {
                 ty: item.type_(),
-                name: item.name.unwrap().to_string(),
+                name: item.name.unwrap(),
                 path: join_with_double_colon(&fqp[..fqp.len() - 1]),
                 desc,
                 parent: Some(parent),
@@ -58,8 +58,8 @@ pub(crate) fn build_index<'tcx>(
     // Sort search index items. This improves the compressibility of the search index.
     cache.search_index.sort_unstable_by(|k1, k2| {
         // `sort_unstable_by_key` produces lifetime errors
-        let k1 = (&k1.path, &k1.name, &k1.ty, &k1.parent);
-        let k2 = (&k2.path, &k2.name, &k2.ty, &k2.parent);
+        let k1 = (&k1.path, k1.name.as_str(), &k1.ty, &k1.parent);
+        let k2 = (&k2.path, k2.name.as_str(), &k2.ty, &k2.parent);
         std::cmp::Ord::cmp(&k1, &k2)
     });
 
@@ -240,7 +240,7 @@ pub(crate) fn build_index<'tcx>(
             )?;
             crate_data.serialize_field(
                 "n",
-                &self.items.iter().map(|item| &item.name).collect::<Vec<_>>(),
+                &self.items.iter().map(|item| item.name.as_str()).collect::<Vec<_>>(),
             )?;
             crate_data.serialize_field(
                 "q",
@@ -299,7 +299,7 @@ pub(crate) fn build_index<'tcx>(
             )?;
             crate_data.serialize_field(
                 "p",
-                &self.paths.iter().map(|(it, s)| (it, s.to_string())).collect::<Vec<_>>(),
+                &self.paths.iter().map(|(it, s)| (it, s.as_str())).collect::<Vec<_>>(),
             )?;
             if has_aliases {
                 crate_data.serialize_field("a", &self.aliases)?;
diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs
index 3ea4c4bea88..ca3e9916487 100644
--- a/src/librustdoc/html/render/write_shared.rs
+++ b/src/librustdoc/html/render/write_shared.rs
@@ -345,7 +345,7 @@ if (typeof exports !== 'undefined') {exports.searchIndex = searchIndex};
             };
 
             let content = format!(
-                "<h1 class=\"fqn\">List of all crates</h1><ul class=\"all-items\">{}</ul>",
+                "<h1>List of all crates</h1><ul class=\"all-items\">{}</ul>",
                 krates
                     .iter()
                     .map(|s| {
diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css
index b2fa6e82acc..a08b8d89db6 100644
--- a/src/librustdoc/html/static/css/rustdoc.css
+++ b/src/librustdoc/html/static/css/rustdoc.css
@@ -139,7 +139,7 @@ h1, h2, h3, h4 {
 .docblock > h6:first-child {
 	margin-top: 0;
 }
-h1.fqn {
+.main-heading h1 {
 	margin: 0;
 	padding: 0;
 	flex-grow: 1;
@@ -317,7 +317,7 @@ main {
 	margin-right: auto;
 }
 
-details:not(.rustdoc-toggle) summary {
+details:not(.toggle) summary {
 	margin-bottom: .6em;
 }
 
@@ -1214,11 +1214,11 @@ a.test-arrow:hover {
 	content: "\00a0";
 }
 
-.notable .docblock {
+.notable .content {
 	margin: 0.25em 0.5em;
 }
 
-.notable .docblock pre, .notable .docblock code {
+.notable .content pre, .notable .content code {
 	background: transparent;
 	margin: 0;
 	padding: 0;
@@ -1226,6 +1226,10 @@ a.test-arrow:hover {
 	white-space: pre-wrap;
 }
 
+.notable .content > h3:first-child {
+	margin: 0 0 5px 0;
+}
+
 .search-failed {
 	text-align: center;
 	margin-top: 20px;
@@ -1401,7 +1405,7 @@ details.dir-entry a {
 	Unfortunately we can't yet specify contain: content or contain: strict
 	because the [-]/[+] toggles extend past the boundaries of the <details>
 	https://developer.mozilla.org/en-US/docs/Web/CSS/contain */
-details.rustdoc-toggle {
+details.toggle {
 	contain: layout;
 	position: relative;
 }
@@ -1409,26 +1413,26 @@ details.rustdoc-toggle {
 /* The hideme class is used on summary tags that contain a span with
 	placeholder text shown only when the toggle is closed. For instance,
 	"Expand description" or "Show methods". */
-details.rustdoc-toggle > summary.hideme {
+details.toggle > summary.hideme {
 	cursor: pointer;
 	font-size: 1rem;
 }
 
-details.rustdoc-toggle > summary {
+details.toggle > summary {
 	list-style: none;
 	/* focus outline is shown on `::before` instead of this */
 	outline: none;
 }
-details.rustdoc-toggle > summary::-webkit-details-marker,
-details.rustdoc-toggle > summary::marker {
+details.toggle > summary::-webkit-details-marker,
+details.toggle > summary::marker {
 	display: none;
 }
 
-details.rustdoc-toggle > summary.hideme > span {
+details.toggle > summary.hideme > span {
 	margin-left: 9px;
 }
 
-details.rustdoc-toggle > summary::before {
+details.toggle > summary::before {
 	background: url("toggle-plus-1092eb4930d581b0.svg") no-repeat top left;
 	content: "";
 	cursor: pointer;
@@ -1440,14 +1444,14 @@ details.rustdoc-toggle > summary::before {
 	filter: var(--toggle-filter);
 }
 
-details.rustdoc-toggle > summary.hideme > span,
+details.toggle > summary.hideme > span,
 .more-examples-toggle summary, .more-examples-toggle .hide-more {
 	color: var(--toggles-color);
 }
 
 /* Screen readers see the text version at the end the line.
 	Visual readers see the icon at the start of the line, but small and transparent. */
-details.rustdoc-toggle > summary::after {
+details.toggle > summary::after {
 	content: "Expand";
 	overflow: hidden;
 	width: 0;
@@ -1455,17 +1459,17 @@ details.rustdoc-toggle > summary::after {
 	position: absolute;
 }
 
-details.rustdoc-toggle > summary.hideme::after {
+details.toggle > summary.hideme::after {
 	/* "hideme" toggles already have a description when they're contracted */
 	content: "";
 }
 
-details.rustdoc-toggle > summary:focus::before,
-details.rustdoc-toggle > summary:hover::before {
+details.toggle > summary:focus::before,
+details.toggle > summary:hover::before {
 	opacity: 1;
 }
 
-details.rustdoc-toggle > summary:focus-visible::before {
+details.toggle > summary:focus-visible::before {
 	/* The SVG is black, and gets turned white using a filter in the dark themes.
 	   Do the same with the outline.
 	   The dotted 1px style is copied from Firefox's focus ring style.
@@ -1478,17 +1482,17 @@ details.non-exhaustive {
 	margin-bottom: 8px;
 }
 
-details.rustdoc-toggle > summary.hideme::before {
+details.toggle > summary.hideme::before {
 	position: relative;
 }
 
-details.rustdoc-toggle > summary:not(.hideme)::before {
+details.toggle > summary:not(.hideme)::before {
 	position: absolute;
 	left: -24px;
 	top: 4px;
 }
 
-.impl-items > details.rustdoc-toggle > summary:not(.hideme)::before {
+.impl-items > details.toggle > summary:not(.hideme)::before {
 	position: absolute;
 	left: -24px;
 }
@@ -1498,19 +1502,19 @@ details.rustdoc-toggle > summary:not(.hideme)::before {
 	affect the layout of the items to its right. To do that, we use
 	absolute positioning. Note that we also set position: relative
 	on the parent <details> to make this work properly. */
-details.rustdoc-toggle[open] > summary.hideme {
+details.toggle[open] > summary.hideme {
 	position: absolute;
 }
 
-details.rustdoc-toggle[open] > summary.hideme > span {
+details.toggle[open] > summary.hideme > span {
 	display: none;
 }
 
-details.rustdoc-toggle[open] > summary::before {
+details.toggle[open] > summary::before {
 	background: url("toggle-minus-31bbd6e4c77f5c96.svg") no-repeat top left;
 }
 
-details.rustdoc-toggle[open] > summary::after {
+details.toggle[open] > summary::after {
 	content: "Collapse";
 }
 
@@ -1660,8 +1664,8 @@ in storage.js
 		display: block;
 	}
 
-	#main-content > details.rustdoc-toggle > summary::before,
-	#main-content > div > details.rustdoc-toggle > summary::before {
+	#main-content > details.toggle > summary::before,
+	#main-content > div > details.toggle > summary::before {
 		left: -11px;
 	}
 
@@ -1715,12 +1719,12 @@ in storage.js
 	}
 
 	/* Position of the "[-]" element. */
-	details.rustdoc-toggle:not(.top-doc) > summary {
+	details.toggle:not(.top-doc) > summary {
 		margin-left: 10px;
 	}
-	.impl-items > details.rustdoc-toggle > summary:not(.hideme)::before,
-	#main-content > details.rustdoc-toggle:not(.top-doc) > summary::before,
-	#main-content > div > details.rustdoc-toggle > summary::before {
+	.impl-items > details.toggle > summary:not(.hideme)::before,
+	#main-content > details.toggle:not(.top-doc) > summary::before,
+	#main-content > div > details.toggle > summary::before {
 		left: -11px;
 	}
 
@@ -1753,8 +1757,8 @@ in storage.js
 
 @media print {
 	nav.sidebar, nav.sub, .out-of-band, a.srclink, #copy-path,
-	details.rustdoc-toggle[open] > summary::before, details.rustdoc-toggle > summary::before,
-	details.rustdoc-toggle.top-doc > summary {
+	details.toggle[open] > summary::before, details.toggle > summary::before,
+	details.toggle.top-doc > summary {
 		display: none;
 	}
 
@@ -1796,24 +1800,24 @@ in storage.js
 .impl,
 #implementors-list > .docblock,
 .impl-items > section,
-.impl-items > .rustdoc-toggle > summary,
+.impl-items > .toggle > summary,
 .methods > section,
-.methods > .rustdoc-toggle > summary
+.methods > .toggle > summary
 {
 	margin-bottom: 0.75em;
 }
 
 .variants > .docblock,
 .implementors-toggle > .docblock,
-.impl-items > .rustdoc-toggle[open]:not(:last-child),
-.methods > .rustdoc-toggle[open]:not(:last-child),
+.impl-items > .toggle[open]:not(:last-child),
+.methods > .toggle[open]:not(:last-child),
 .implementors-toggle[open]:not(:last-child) {
 	margin-bottom: 2em;
 }
 
-#trait-implementations-list .impl-items > .rustdoc-toggle:not(:last-child),
-#synthetic-implementations-list .impl-items > .rustdoc-toggle:not(:last-child),
-#blanket-implementations-list .impl-items > .rustdoc-toggle:not(:last-child) {
+#trait-implementations-list .impl-items > .toggle:not(:last-child),
+#synthetic-implementations-list .impl-items > .toggle:not(:last-child),
+#blanket-implementations-list .impl-items > .toggle:not(:last-child) {
 	margin-bottom: 1em;
 }
 
diff --git a/src/librustdoc/html/static/css/settings.css b/src/librustdoc/html/static/css/settings.css
index 875a260c811..91419093147 100644
--- a/src/librustdoc/html/static/css/settings.css
+++ b/src/librustdoc/html/static/css/settings.css
@@ -9,7 +9,7 @@
 }
 
 .setting-line .radio-line input,
-.setting-line .toggle input {
+.setting-line .settings-toggle input {
 	margin-right: 0.3em;
 	height: 1.2rem;
 	width: 1.2rem;
@@ -22,14 +22,14 @@
 .setting-line .radio-line input {
 	border-radius: 50%;
 }
-.setting-line .toggle input:checked {
+.setting-line .settings-toggle input:checked {
 	content: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40">\
 		<path d="M7,25L17,32L33,12" fill="none" stroke="black" stroke-width="5"/>\
 		<path d="M7,23L17,30L33,10" fill="none" stroke="white" stroke-width="5"/></svg>');
 }
 
 .setting-line .radio-line input + span,
-.setting-line .toggle span {
+.setting-line .settings-toggle span {
 	padding-bottom: 1px;
 }
 
@@ -50,7 +50,7 @@
 	margin-left: 0.5em;
 }
 
-.toggle {
+.settings-toggle {
 	position: relative;
 	width: 100%;
 	margin-right: 20px;
@@ -67,11 +67,11 @@
 	box-shadow: inset 0 0 0 3px var(--main-background-color);
 	background-color: var(--settings-input-color);
 }
-.setting-line .toggle input:checked {
+.setting-line .settings-toggle input:checked {
 	background-color: var(--settings-input-color);
 }
 .setting-line .radio-line input:focus,
-.setting-line .toggle input:focus {
+.setting-line .settings-toggle input:focus {
 	box-shadow: 0 0 1px 1px var(--settings-input-color);
 }
 /* In here we combine both `:focus` and `:checked` properties. */
@@ -80,6 +80,6 @@
 		0 0 2px 2px var(--settings-input-color);
 }
 .setting-line .radio-line input:hover,
-.setting-line .toggle input:hover {
+.setting-line .settings-toggle input:hover {
 	border-color: var(--settings-input-color) !important;
 }
diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js
index 51aee8e7c89..f52229d8095 100644
--- a/src/librustdoc/html/static/js/main.js
+++ b/src/librustdoc/html/static/js/main.js
@@ -526,7 +526,7 @@ function loadCss(cssUrl) {
         }
 
         let currentNbImpls = implementors.getElementsByClassName("impl").length;
-        const traitName = document.querySelector("h1.fqn > .trait").textContent;
+        const traitName = document.querySelector(".main-heading h1 > .trait").textContent;
         const baseIdName = "impl-" + traitName + "-";
         const libs = Object.getOwnPropertyNames(imp);
         // We don't want to include impls from this JS file, when the HTML already has them.
@@ -620,7 +620,7 @@ function loadCss(cssUrl) {
     function expandAllDocs() {
         const innerToggle = document.getElementById(toggleAllDocsId);
         removeClass(innerToggle, "will-expand");
-        onEachLazy(document.getElementsByClassName("rustdoc-toggle"), e => {
+        onEachLazy(document.getElementsByClassName("toggle"), e => {
             if (!hasClass(e, "type-contents-toggle") && !hasClass(e, "more-examples-toggle")) {
                 e.open = true;
             }
@@ -632,7 +632,7 @@ function loadCss(cssUrl) {
     function collapseAllDocs() {
         const innerToggle = document.getElementById(toggleAllDocsId);
         addClass(innerToggle, "will-expand");
-        onEachLazy(document.getElementsByClassName("rustdoc-toggle"), e => {
+        onEachLazy(document.getElementsByClassName("toggle"), e => {
             if (e.parentNode.id !== "implementations-list" ||
                 (!hasClass(e, "implementors-toggle") &&
                  !hasClass(e, "type-contents-toggle"))
@@ -680,7 +680,7 @@ function loadCss(cssUrl) {
             setImplementorsTogglesOpen("blanket-implementations-list", false);
         }
 
-        onEachLazy(document.getElementsByClassName("rustdoc-toggle"), e => {
+        onEachLazy(document.getElementsByClassName("toggle"), e => {
             if (!hideLargeItemContents && hasClass(e, "type-contents-toggle")) {
                 e.open = true;
             }
@@ -823,7 +823,7 @@ function loadCss(cssUrl) {
         });
     });
 
-    onEachLazy(document.querySelectorAll(".rustdoc-toggle > summary:not(.hideme)"), el => {
+    onEachLazy(document.querySelectorAll(".toggle > summary:not(.hideme)"), el => {
         el.addEventListener("click", e => {
             if (e.target.tagName !== "SUMMARY" && e.target.tagName !== "A") {
                 e.preventDefault();
@@ -847,7 +847,7 @@ function loadCss(cssUrl) {
         window.hideAllModals(false);
         const ty = e.getAttribute("data-ty");
         const wrapper = document.createElement("div");
-        wrapper.innerHTML = "<div class=\"docblock\">" + window.NOTABLE_TRAITS[ty] + "</div>";
+        wrapper.innerHTML = "<div class=\"content\">" + window.NOTABLE_TRAITS[ty] + "</div>";
         wrapper.className = "notable popover";
         const focusCatcher = document.createElement("div");
         focusCatcher.setAttribute("tabindex", "0");
diff --git a/src/librustdoc/html/static/js/settings.js b/src/librustdoc/html/static/js/settings.js
index 589bfc79360..9ed8f63610f 100644
--- a/src/librustdoc/html/static/js/settings.js
+++ b/src/librustdoc/html/static/js/settings.js
@@ -150,10 +150,10 @@
                 });
                 output += "</div></div>";
             } else {
-                // This is a toggle.
+                // This is a checkbox toggle.
                 const checked = setting["default"] === true ? " checked" : "";
                 output += `\
-<label class="toggle">\
+<label class="settings-toggle">\
     <input type="checkbox" id="${js_data_name}"${checked}>\
     <span class="label">${setting_name}</span>\
 </label>`;
diff --git a/src/librustdoc/html/templates/print_item.html b/src/librustdoc/html/templates/print_item.html
index 611d124d4b9..ee2880bf6d1 100644
--- a/src/librustdoc/html/templates/print_item.html
+++ b/src/librustdoc/html/templates/print_item.html
@@ -1,5 +1,5 @@
 <div class="main-heading"> {#- -#}
-    <h1 class="fqn"> {#- -#}
+    <h1> {#- -#}
         {{-typ-}}
         {#- The breadcrumbs of the item path, like std::string -#}
         {%- for component in path_components -%}
diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs
index ed77de200a9..86454e1f2eb 100644
--- a/src/librustdoc/lib.rs
+++ b/src/librustdoc/lib.rs
@@ -772,7 +772,6 @@ fn main_args(at_args: &[String]) -> MainResult {
     let crate_version = options.crate_version.clone();
 
     let output_format = options.output_format;
-    let externs = options.externs.clone();
     let scrape_examples_options = options.scrape_examples_options.clone();
     let bin_crate = options.bin_crate;
 
@@ -805,9 +804,7 @@ fn main_args(at_args: &[String]) -> MainResult {
                 let resolver_caches = resolver.borrow_mut().access(|resolver| {
                     collect_intra_doc_links::early_resolve_intra_doc_links(
                         resolver,
-                        sess,
                         krate,
-                        externs,
                         render_options.document_private,
                     )
                 });
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index 4f0eb8b8076..075951312a6 100644
--- a/src/librustdoc/passes/collect_intra_doc_links.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links.rs
@@ -786,7 +786,7 @@ fn trait_impls_for<'a>(
         tcx.find_map_relevant_impl(trait_, ty, |impl_| {
             let trait_ref = tcx.impl_trait_ref(impl_).expect("this is not an inherent impl");
             // Check if these are the same type.
-            let impl_type = trait_ref.self_ty();
+            let impl_type = trait_ref.skip_binder().self_ty();
             trace!(
                 "comparing type {} with kind {:?} against type {:?}",
                 impl_type,
diff --git a/src/librustdoc/passes/collect_intra_doc_links/early.rs b/src/librustdoc/passes/collect_intra_doc_links/early.rs
index 1b373cfe5bb..42677bd8497 100644
--- a/src/librustdoc/passes/collect_intra_doc_links/early.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links/early.rs
@@ -12,8 +12,6 @@ use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, CRATE_DEF_ID};
 use rustc_hir::TraitCandidate;
 use rustc_middle::ty::{DefIdTree, Visibility};
 use rustc_resolve::{ParentScope, Resolver};
-use rustc_session::config::Externs;
-use rustc_session::Session;
 use rustc_span::symbol::sym;
 use rustc_span::{Symbol, SyntaxContext};
 
@@ -22,16 +20,13 @@ use std::mem;
 
 pub(crate) fn early_resolve_intra_doc_links(
     resolver: &mut Resolver<'_>,
-    sess: &Session,
     krate: &ast::Crate,
-    externs: Externs,
     document_private_items: bool,
 ) -> ResolverCaches {
     let parent_scope =
         ParentScope::module(resolver.expect_module(CRATE_DEF_ID.to_def_id()), resolver);
     let mut link_resolver = EarlyDocLinkResolver {
         resolver,
-        sess,
         parent_scope,
         visited_mods: Default::default(),
         markdown_links: Default::default(),
@@ -52,7 +47,9 @@ pub(crate) fn early_resolve_intra_doc_links(
     // the known necessary crates. Load them all unconditionally until we find a way to fix this.
     // DO NOT REMOVE THIS without first testing on the reproducer in
     // https://github.com/jyn514/objr/commit/edcee7b8124abf0e4c63873e8422ff81beb11ebb
-    for (extern_name, _) in externs.iter().filter(|(_, entry)| entry.add_prelude) {
+    for (extern_name, _) in
+        link_resolver.resolver.sess().opts.externs.iter().filter(|(_, entry)| entry.add_prelude)
+    {
         link_resolver.resolver.resolve_rustdoc_path(extern_name, TypeNS, parent_scope);
     }
 
@@ -73,7 +70,6 @@ fn doc_attrs<'a>(attrs: impl Iterator<Item = &'a ast::Attribute>) -> Attributes
 
 struct EarlyDocLinkResolver<'r, 'ra> {
     resolver: &'r mut Resolver<'ra>,
-    sess: &'r Session,
     parent_scope: ParentScope<'ra>,
     visited_mods: DefIdSet,
     markdown_links: FxHashMap<String, Vec<PreprocessedMarkdownLink>>,
@@ -166,7 +162,7 @@ impl<'ra> EarlyDocLinkResolver<'_, 'ra> {
     fn resolve_doc_links_extern_impl(&mut self, def_id: DefId, is_inherent: bool) {
         self.resolve_doc_links_extern_outer_fixme(def_id, def_id);
         let assoc_item_def_ids = Vec::from_iter(
-            self.resolver.cstore().associated_item_def_ids_untracked(def_id, self.sess),
+            self.resolver.cstore().associated_item_def_ids_untracked(def_id, self.resolver.sess()),
         );
         for assoc_def_id in assoc_item_def_ids {
             if !is_inherent || self.resolver.cstore().visibility_untracked(assoc_def_id).is_public()
@@ -191,7 +187,9 @@ impl<'ra> EarlyDocLinkResolver<'_, 'ra> {
         if !self.resolver.cstore().may_have_doc_links_untracked(def_id) {
             return;
         }
-        let attrs = Vec::from_iter(self.resolver.cstore().item_attrs_untracked(def_id, self.sess));
+        let attrs = Vec::from_iter(
+            self.resolver.cstore().item_attrs_untracked(def_id, self.resolver.sess()),
+        );
         let parent_scope = ParentScope::module(
             self.resolver.get_nearest_non_block_module(
                 self.resolver.opt_parent(scope_id).unwrap_or(scope_id),
@@ -205,7 +203,9 @@ impl<'ra> EarlyDocLinkResolver<'_, 'ra> {
         if !self.resolver.cstore().may_have_doc_links_untracked(def_id) {
             return;
         }
-        let attrs = Vec::from_iter(self.resolver.cstore().item_attrs_untracked(def_id, self.sess));
+        let attrs = Vec::from_iter(
+            self.resolver.cstore().item_attrs_untracked(def_id, self.resolver.sess()),
+        );
         let parent_scope = ParentScope::module(self.resolver.expect_module(def_id), self.resolver);
         self.resolve_doc_links(doc_attrs(attrs.iter()), parent_scope);
     }
@@ -321,7 +321,7 @@ impl<'ra> EarlyDocLinkResolver<'_, 'ra> {
                         let field_def_ids = Vec::from_iter(
                             self.resolver
                                 .cstore()
-                                .associated_item_def_ids_untracked(def_id, self.sess),
+                                .associated_item_def_ids_untracked(def_id, self.resolver.sess()),
                         );
                         for field_def_id in field_def_ids {
                             self.resolve_doc_links_extern_outer(field_def_id, scope_id);
diff --git a/src/librustdoc/passes/stripper.rs b/src/librustdoc/passes/stripper.rs
index f5501b3d523..048ed264623 100644
--- a/src/librustdoc/passes/stripper.rs
+++ b/src/librustdoc/passes/stripper.rs
@@ -97,17 +97,7 @@ impl<'a, 'tcx> DocFolder for Stripper<'a, 'tcx> {
             }
 
             // handled in the `strip-priv-imports` pass
-            clean::ExternCrateItem { .. } => {}
-            clean::ImportItem(ref imp) => {
-                // Because json doesn't inline imports from private modules, we need to mark
-                // the imported item as retained so it's impls won't be stripped.
-                //
-                // FIXME: Is it necessary to check for json output here: See
-                // https://github.com/rust-lang/rust/pull/100325#discussion_r941495215
-                if let Some(did) = imp.source.did && self.is_json_output {
-                    self.retained.insert(did.into());
-                }
-            }
+            clean::ExternCrateItem { .. } | clean::ImportItem(_) => {}
 
             clean::ImplItem(..) => {}
 
diff --git a/src/tools/cargo b/src/tools/cargo
-Subproject d992ab4e9034930e7809749f04077045af8f4d7
+Subproject 1cd6d3803dfb0b342272862a8590f5dfc9f7257
diff --git a/src/tools/clippy/clippy_lints/src/attrs.rs b/src/tools/clippy/clippy_lints/src/attrs.rs
index 0710ac0bb0a..751c262673b 100644
--- a/src/tools/clippy/clippy_lints/src/attrs.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs.rs
@@ -472,7 +472,7 @@ fn check_clippy_lint_names(cx: &LateContext<'_>, name: Symbol, items: &[NestedMe
 
 fn check_lint_reason(cx: &LateContext<'_>, name: Symbol, items: &[NestedMetaItem], attr: &'_ Attribute) {
     // Check for the feature
-    if !cx.tcx.sess.features_untracked().lint_reasons {
+    if !cx.tcx.features().lint_reasons {
         return;
     }
 
diff --git a/src/tools/clippy/clippy_lints/src/derive.rs b/src/tools/clippy/clippy_lints/src/derive.rs
index f4b15e0916d..248d7388410 100644
--- a/src/tools/clippy/clippy_lints/src/derive.rs
+++ b/src/tools/clippy/clippy_lints/src/derive.rs
@@ -251,7 +251,7 @@ fn check_hash_peq<'tcx>(
 
                 // Only care about `impl PartialEq<Foo> for Foo`
                 // For `impl PartialEq<B> for A, input_types is [A, B]
-                if trait_ref.substs.type_at(1) == ty {
+                if trait_ref.subst_identity().substs.type_at(1) == ty {
                     span_lint_and_then(
                         cx,
                         DERIVED_HASH_WITH_MANUAL_EQ,
@@ -299,7 +299,7 @@ fn check_ord_partial_ord<'tcx>(
 
                 // Only care about `impl PartialOrd<Foo> for Foo`
                 // For `impl PartialOrd<B> for A, input_types is [A, B]
-                if trait_ref.substs.type_at(1) == ty {
+                if trait_ref.subst_identity().substs.type_at(1) == ty {
                     let mess = if partial_ord_is_automatically_derived {
                         "you are implementing `Ord` explicitly but have derived `PartialOrd`"
                     } else {
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 9a1058470e1..2ef547526d4 100644
--- a/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs
+++ b/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs
@@ -56,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(item.owner_id);
-            if cx.tcx.is_diagnostic_item(sym::From, impl_trait_ref.def_id);
+            if cx.tcx.is_diagnostic_item(sym::From, impl_trait_ref.skip_binder().def_id);
             then {
                 lint_impl_body(cx, item.span, impl_.items);
             }
diff --git a/src/tools/clippy/clippy_lints/src/from_over_into.rs b/src/tools/clippy/clippy_lints/src/from_over_into.rs
index a92f7548ff2..bd66ace4500 100644
--- a/src/tools/clippy/clippy_lints/src/from_over_into.rs
+++ b/src/tools/clippy/clippy_lints/src/from_over_into.rs
@@ -76,7 +76,7 @@ impl<'tcx> LateLintPass<'tcx> for FromOverInto {
             && let Some(into_trait_seg) = hir_trait_ref.path.segments.last()
             // `impl Into<target_ty> for self_ty`
             && let Some(GenericArgs { args: [GenericArg::Type(target_ty)], .. }) = into_trait_seg.args
-            && let Some(middle_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id)
+            && let Some(middle_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id).map(ty::EarlyBinder::subst_identity)
             && cx.tcx.is_diagnostic_item(sym::Into, middle_trait_ref.def_id)
             && !matches!(middle_trait_ref.substs.type_at(1).kind(), ty::Alias(ty::Opaque, _))
         {
diff --git a/src/tools/clippy/clippy_lints/src/missing_inline.rs b/src/tools/clippy/clippy_lints/src/missing_inline.rs
index 758ce47cf11..5a459548153 100644
--- a/src/tools/clippy/clippy_lints/src/missing_inline.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_inline.rs
@@ -155,7 +155,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline {
         let container_id = assoc_item.container_id(cx.tcx);
         let trait_def_id = match assoc_item.container {
             TraitContainer => Some(container_id),
-            ImplContainer => cx.tcx.impl_trait_ref(container_id).map(|t| t.def_id),
+            ImplContainer => cx.tcx.impl_trait_ref(container_id).map(|t| t.skip_binder().def_id),
         };
 
         if let Some(trait_def_id) = trait_def_id {
diff --git a/src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs b/src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs
index 714c0ff227b..839c3a3815c 100644
--- a/src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs
+++ b/src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs
@@ -90,7 +90,7 @@ impl<'tcx> LateLintPass<'tcx> for NonSendFieldInSendTy {
             if send_trait == trait_id;
             if hir_impl.polarity == ImplPolarity::Positive;
             if let Some(ty_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id);
-            if let self_ty = ty_trait_ref.self_ty();
+            if let self_ty = ty_trait_ref.subst_identity().self_ty();
             if let ty::Adt(adt_def, impl_trait_substs) = self_ty.kind();
             then {
                 let mut non_send_fields = Vec::new();
diff --git a/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs b/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs
index 7722a476d7b..7b1d974f2f8 100644
--- a/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs
+++ b/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs
@@ -244,7 +244,7 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
             })) => {
                 #[allow(trivial_casts)]
                 if let Some(Node::Item(item)) = get_parent_node(cx.tcx, owner_id.into())
-                    && let Some(trait_ref) = cx.tcx.impl_trait_ref(item.owner_id)
+                    && let Some(trait_ref) = cx.tcx.impl_trait_ref(item.owner_id).map(|t| t.subst_identity())
                     && let Some(trait_item_id) = cx.tcx.associated_item(owner_id).trait_item_def_id
                 {
                     (
diff --git a/src/tools/clippy/clippy_lints/src/use_self.rs b/src/tools/clippy/clippy_lints/src/use_self.rs
index 4c755d812a0..6ae9d9d6353 100644
--- a/src/tools/clippy/clippy_lints/src/use_self.rs
+++ b/src/tools/clippy/clippy_lints/src/use_self.rs
@@ -137,7 +137,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
             then {
                 // `self_ty` is the semantic self type of `impl <trait> for <type>`. This cannot be
                 // `Self`.
-                let self_ty = impl_trait_ref.self_ty();
+                let self_ty = impl_trait_ref.subst_identity().self_ty();
 
                 // `trait_method_sig` is the signature of the function, how it is declared in the
                 // trait, not in the impl of the trait.
diff --git a/src/tools/clippy/tests/ui/crashes/ice-6252.stderr b/src/tools/clippy/tests/ui/crashes/ice-6252.stderr
index 638e4a54849..efdd56dd47d 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-6252.stderr
+++ b/src/tools/clippy/tests/ui/crashes/ice-6252.stderr
@@ -17,9 +17,12 @@ error[E0412]: cannot find type `VAL` in this scope
   --> $DIR/ice-6252.rs:10:63
    |
 LL | impl<N, M> TypeVal<usize> for Multiply<N, M> where N: TypeVal<VAL> {}
-   |          -                                                    ^^^ not found in this scope
-   |          |
-   |          help: you might be missing a type parameter: `, VAL`
+   |                                                               ^^^ not found in this scope
+   |
+help: you might be missing a type parameter
+   |
+LL | impl<N, M, VAL> TypeVal<usize> for Multiply<N, M> where N: TypeVal<VAL> {}
+   |          +++++
 
 error[E0046]: not all trait items implemented, missing: `VAL`
   --> $DIR/ice-6252.rs:10:1
diff --git a/src/tools/clippy/tests/ui/def_id_nocore.rs b/src/tools/clippy/tests/ui/def_id_nocore.rs
index a7da8f89aa3..1af77d1a25b 100644
--- a/src/tools/clippy/tests/ui/def_id_nocore.rs
+++ b/src/tools/clippy/tests/ui/def_id_nocore.rs
@@ -15,7 +15,7 @@ pub trait Copy {}
 pub unsafe trait Freeze {}
 
 #[lang = "start"]
-fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize {
+fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize {
     0
 }
 
diff --git a/src/tools/miri/tests/pass-dep/shims/libc-fs-with-isolation.rs b/src/tools/miri/tests/pass-dep/shims/libc-fs-with-isolation.rs
index f1838cf64f7..d6d19a3fe81 100644
--- a/src/tools/miri/tests/pass-dep/shims/libc-fs-with-isolation.rs
+++ b/src/tools/miri/tests/pass-dep/shims/libc-fs-with-isolation.rs
@@ -22,7 +22,8 @@ fn main() {
     }
 
     // test `stat`
-    assert_eq!(fs::metadata("foo.txt").unwrap_err().kind(), ErrorKind::PermissionDenied);
+    let err = fs::metadata("foo.txt").unwrap_err();
+    assert_eq!(err.kind(), ErrorKind::PermissionDenied);
     // check that it is the right kind of `PermissionDenied`
-    assert_eq!(Error::last_os_error().raw_os_error(), Some(libc::EACCES));
+    assert_eq!(err.raw_os_error(), Some(libc::EACCES));
 }
diff --git a/src/tools/rustbook/Cargo.toml b/src/tools/rustbook/Cargo.toml
index 33c05180408..b296aa2f4e6 100644
--- a/src/tools/rustbook/Cargo.toml
+++ b/src/tools/rustbook/Cargo.toml
@@ -5,10 +5,10 @@ license = "MIT OR Apache-2.0"
 edition = "2021"
 
 [dependencies]
-clap = "3.1.1"
+clap = "4.0.32"
 env_logger = "0.7.1"
 
 [dependencies.mdbook]
-version = "0.4.21"
+version = "0.4.25"
 default-features = false
 features = ["search"]
diff --git a/src/tools/rustbook/src/main.rs b/src/tools/rustbook/src/main.rs
index 3c7dc0183d7..1368ec653de 100644
--- a/src/tools/rustbook/src/main.rs
+++ b/src/tools/rustbook/src/main.rs
@@ -9,18 +9,21 @@ use mdbook::errors::Result as Result3;
 use mdbook::MDBook;
 
 fn main() {
-    let crate_version = format!("v{}", crate_version!());
+    let crate_version = concat!("v", crate_version!());
     env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("warn")).init();
     let d_arg = arg!(-d --"dest-dir" <DEST_DIR>
 "The output directory for your book\n(Defaults to ./book when omitted)")
-    .required(false);
-    let dir_arg = arg!([dir]
-"A directory for your book\n(Defaults to Current Directory when omitted)");
+    .required(false)
+    .value_parser(clap::value_parser!(PathBuf));
+
+    let dir_arg = arg!([dir] "Root directory for the book\n\
+                              (Defaults to the current directory when omitted)")
+    .value_parser(clap::value_parser!(PathBuf));
 
     let matches = Command::new("rustbook")
         .about("Build a book with mdBook")
         .author("Steve Klabnik <steve@steveklabnik.com>")
-        .version(&*crate_version)
+        .version(crate_version)
         .subcommand_required(true)
         .arg_required_else_help(true)
         .subcommand(
@@ -60,8 +63,8 @@ pub fn build(args: &ArgMatches) -> Result3<()> {
     // Set this to allow us to catch bugs in advance.
     book.config.build.create_missing = false;
 
-    if let Some(dest_dir) = args.value_of("dest-dir") {
-        book.config.build.build_dir = PathBuf::from(dest_dir);
+    if let Some(dest_dir) = args.get_one::<PathBuf>("dest-dir") {
+        book.config.build.build_dir = dest_dir.into();
     }
 
     book.build()?;
@@ -76,10 +79,9 @@ fn test(args: &ArgMatches) -> Result3<()> {
 }
 
 fn get_book_dir(args: &ArgMatches) -> PathBuf {
-    if let Some(dir) = args.value_of("dir") {
+    if let Some(p) = args.get_one::<PathBuf>("dir") {
         // Check if path is relative from current dir, or absolute...
-        let p = Path::new(dir);
-        if p.is_relative() { env::current_dir().unwrap().join(dir) } else { p.to_path_buf() }
+        if p.is_relative() { env::current_dir().unwrap().join(p) } else { p.to_path_buf() }
     } else {
         env::current_dir().unwrap()
     }
diff --git a/src/tools/tidy/Cargo.toml b/src/tools/tidy/Cargo.toml
index fff83a1d097..19812fc6f55 100644
--- a/src/tools/tidy/Cargo.toml
+++ b/src/tools/tidy/Cargo.toml
@@ -6,6 +6,7 @@ autobins = false
 
 [dependencies]
 cargo_metadata = "0.14"
+cargo-platform = "0.1.2"
 regex = "1"
 miropt-test-tools = { path = "../miropt-test-tools" }
 lazy_static = "1"
diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs
index 29501d2d3b6..bc2edf634de 100644
--- a/src/tools/tidy/src/deps.rs
+++ b/src/tools/tidy/src/deps.rs
@@ -1,7 +1,7 @@
 //! Checks the licenses of third-party dependencies.
 
-use cargo_metadata::{Metadata, Package, PackageId, Resolve};
-use std::collections::{BTreeSet, HashSet};
+use cargo_metadata::{DepKindInfo, Metadata, Package, PackageId};
+use std::collections::HashSet;
 use std::path::Path;
 
 /// These are licenses that are allowed for all crates, including the runtime,
@@ -98,14 +98,12 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
     "autocfg",
     "bitflags",
     "block-buffer",
-    "bumpalo", // Included in Cargo's dep graph but only activated on wasm32-*-unknown.
     "cc",
     "cfg-if",
     "chalk-derive",
     "chalk-engine",
     "chalk-ir",
     "chalk-solve",
-    "chrono",
     "convert_case", // dependency of derive_more
     "compiler_builtins",
     "cpufeatures",
@@ -124,11 +122,9 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
     "dlmalloc",
     "either",
     "ena",
-    "env_logger",
     "expect-test",
     "fallible-iterator", // dependency of `thorin`
     "fastrand",
-    "filetime",
     "fixedbitset",
     "flate2",
     "fluent-bundle",
@@ -142,13 +138,11 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
     "gsgdt",
     "hashbrown",
     "hermit-abi",
-    "humantime",
     "icu_list",
     "icu_locid",
     "icu_provider",
     "icu_provider_adapters",
     "icu_provider_macros",
-    "if_chain",
     "indexmap",
     "instant",
     "intl-memoizer",
@@ -156,7 +150,6 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
     "itertools",
     "itoa",
     "jobserver",
-    "js-sys", // Included in Cargo's dep graph but only activated on wasm32-*-unknown.
     "lazy_static",
     "libc",
     "libloading",
@@ -171,8 +164,6 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
     "memmap2",
     "memoffset",
     "miniz_oxide",
-    "num-integer",
-    "num-traits",
     "num_cpus",
     "object",
     "odht",
@@ -190,7 +181,6 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
     "proc-macro2",
     "psm",
     "punycode",
-    "quick-error",
     "quote",
     "rand",
     "rand_chacha",
@@ -217,7 +207,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
     "serde",
     "serde_derive",
     "serde_json",
-    "sha-1",
+    "sha1",
     "sha2",
     "sharded-slab",
     "smallvec",
@@ -235,7 +225,6 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
     "thiserror-impl",
     "thorin-dwp",
     "thread_local",
-    "time",
     "tinystr",
     "tinyvec",
     "tinyvec_macros",
@@ -268,13 +257,6 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
     "valuable",
     "version_check",
     "wasi",
-    // vvv Included in Cargo's dep graph but only activated on wasm32-*-unknown.
-    "wasm-bindgen",
-    "wasm-bindgen-backend",
-    "wasm-bindgen-macro",
-    "wasm-bindgen-macro-support",
-    "wasm-bindgen-shared",
-    // ^^^ Included in Cargo's dep graph but only activated on wasm32-*-unknown.
     "winapi",
     "winapi-i686-pc-windows-gnu",
     "winapi-util",
@@ -485,73 +467,55 @@ fn check_permitted_dependencies(
     restricted_dependency_crates: &[&'static str],
     bad: &mut bool,
 ) {
+    let mut deps = HashSet::new();
+    for to_check in restricted_dependency_crates {
+        let to_check = pkg_from_name(metadata, to_check);
+        use cargo_platform::Cfg;
+        use std::str::FromStr;
+        // We don't expect the compiler to ever run on wasm32, so strip
+        // out those dependencies to avoid polluting the permitted list.
+        deps_of_filtered(metadata, &to_check.id, &mut deps, &|dep_kinds| {
+            dep_kinds.iter().any(|dep_kind| {
+                dep_kind
+                    .target
+                    .as_ref()
+                    .map(|target| {
+                        !target.matches(
+                            "wasm32-unknown-unknown",
+                            &[
+                                Cfg::from_str("target_arch=\"wasm32\"").unwrap(),
+                                Cfg::from_str("target_os=\"unknown\"").unwrap(),
+                            ],
+                        )
+                    })
+                    .unwrap_or(true)
+            })
+        });
+    }
+
     // Check that the PERMITTED_DEPENDENCIES does not have unused entries.
-    for name in permitted_dependencies {
-        if !metadata.packages.iter().any(|p| p.name == *name) {
+    for permitted in permitted_dependencies {
+        if !deps.iter().any(|dep_id| &pkg_from_id(metadata, dep_id).name == permitted) {
             tidy_error!(
                 bad,
-                "could not find allowed package `{}`\n\
+                "could not find allowed package `{permitted}`\n\
                 Remove from PERMITTED_DEPENDENCIES list if it is no longer used.",
-                name
             );
         }
     }
-    // Get the list in a convenient form.
-    let permitted_dependencies: HashSet<_> = permitted_dependencies.iter().cloned().collect();
-
-    // Check dependencies.
-    let mut visited = BTreeSet::new();
-    let mut unapproved = BTreeSet::new();
-    for &krate in restricted_dependency_crates.iter() {
-        let pkg = pkg_from_name(metadata, krate);
-        let mut bad =
-            check_crate_dependencies(&permitted_dependencies, metadata, &mut visited, pkg);
-        unapproved.append(&mut bad);
-    }
-
-    if !unapproved.is_empty() {
-        tidy_error!(bad, "Dependencies for {} not explicitly permitted:", descr);
-        for dep in unapproved {
-            println!("* {dep}");
-        }
-    }
-}
-
-/// Checks the dependencies of the given crate from the given cargo metadata to see if they are on
-/// the list of permitted dependencies. Returns a list of disallowed dependencies.
-fn check_crate_dependencies<'a>(
-    permitted_dependencies: &'a HashSet<&'static str>,
-    metadata: &'a Metadata,
-    visited: &mut BTreeSet<&'a PackageId>,
-    krate: &'a Package,
-) -> BTreeSet<&'a PackageId> {
-    // This will contain bad deps.
-    let mut unapproved = BTreeSet::new();
-
-    // Check if we have already visited this crate.
-    if visited.contains(&krate.id) {
-        return unapproved;
-    }
 
-    visited.insert(&krate.id);
+    // Get in a convenient form.
+    let permitted_dependencies: HashSet<_> = permitted_dependencies.iter().cloned().collect();
 
-    // If this path is in-tree, we don't require it to be explicitly permitted.
-    if krate.source.is_some() {
-        // If this dependency is not on `PERMITTED_DEPENDENCIES`, add to bad set.
-        if !permitted_dependencies.contains(krate.name.as_str()) {
-            unapproved.insert(&krate.id);
+    for dep in deps {
+        let dep = pkg_from_id(metadata, dep);
+        // If this path is in-tree, we don't require it to be explicitly permitted.
+        if dep.source.is_some() {
+            if !permitted_dependencies.contains(dep.name.as_str()) {
+                tidy_error!(bad, "Dependency for {descr} not explicitly permitted: {}", dep.id);
+            }
         }
     }
-
-    // Do a DFS in the crate graph.
-    let to_check = deps_of(metadata, &krate.id);
-
-    for dep in to_check {
-        let mut bad = check_crate_dependencies(permitted_dependencies, metadata, visited, dep);
-        unapproved.append(&mut bad);
-    }
-
-    unapproved
 }
 
 /// Prevents multiple versions of some expensive crates.
@@ -588,24 +552,6 @@ fn check_crate_duplicate(
     }
 }
 
-/// Returns a list of dependencies for the given package.
-fn deps_of<'a>(metadata: &'a Metadata, pkg_id: &'a PackageId) -> Vec<&'a Package> {
-    let resolve = metadata.resolve.as_ref().unwrap();
-    let node = resolve
-        .nodes
-        .iter()
-        .find(|n| &n.id == pkg_id)
-        .unwrap_or_else(|| panic!("could not find `{pkg_id}` in resolve"));
-    node.deps
-        .iter()
-        .map(|dep| {
-            metadata.packages.iter().find(|pkg| pkg.id == dep.pkg).unwrap_or_else(|| {
-                panic!("could not find dep `{}` for pkg `{}` in resolve", dep.pkg, pkg_id)
-            })
-        })
-        .collect()
-}
-
 /// Finds a package with the given name.
 fn pkg_from_name<'a>(metadata: &'a Metadata, name: &'static str) -> &'a Package {
     let mut i = metadata.packages.iter().filter(|p| p.name == name);
@@ -615,41 +561,57 @@ fn pkg_from_name<'a>(metadata: &'a Metadata, name: &'static str) -> &'a Package
     result
 }
 
+fn pkg_from_id<'a>(metadata: &'a Metadata, id: &PackageId) -> &'a Package {
+    metadata.packages.iter().find(|p| &p.id == id).unwrap()
+}
+
 /// Finds all the packages that are in the rust runtime.
 fn compute_runtime_crates<'a>(metadata: &'a Metadata) -> HashSet<&'a PackageId> {
-    let resolve = metadata.resolve.as_ref().unwrap();
     let mut result = HashSet::new();
     for name in RUNTIME_CRATES {
         let id = &pkg_from_name(metadata, name).id;
-        normal_deps_of_r(resolve, id, &mut result);
+        deps_of_filtered(metadata, id, &mut result, &|_| true);
     }
     result
 }
 
-/// Recursively find all normal dependencies.
-fn normal_deps_of_r<'a>(
-    resolve: &'a Resolve,
+/// Recursively find all dependencies.
+fn deps_of_filtered<'a>(
+    metadata: &'a Metadata,
     pkg_id: &'a PackageId,
     result: &mut HashSet<&'a PackageId>,
+    filter: &dyn Fn(&[DepKindInfo]) -> bool,
 ) {
     if !result.insert(pkg_id) {
         return;
     }
-    let node = resolve
+    let node = metadata
+        .resolve
+        .as_ref()
+        .unwrap()
         .nodes
         .iter()
         .find(|n| &n.id == pkg_id)
         .unwrap_or_else(|| panic!("could not find `{pkg_id}` in resolve"));
     for dep in &node.deps {
-        normal_deps_of_r(resolve, &dep.pkg, result);
+        if !filter(&dep.dep_kinds) {
+            continue;
+        }
+        deps_of_filtered(metadata, &dep.pkg, result, filter);
     }
 }
 
+fn direct_deps_of<'a>(metadata: &'a Metadata, pkg_id: &'a PackageId) -> Vec<&'a Package> {
+    let resolve = metadata.resolve.as_ref().unwrap();
+    let node = resolve.nodes.iter().find(|n| &n.id == pkg_id).unwrap();
+    node.deps.iter().map(|dep| pkg_from_id(metadata, &dep.pkg)).collect()
+}
+
 fn check_rustfix(metadata: &Metadata, bad: &mut bool) {
     let cargo = pkg_from_name(metadata, "cargo");
     let compiletest = pkg_from_name(metadata, "compiletest");
-    let cargo_deps = deps_of(metadata, &cargo.id);
-    let compiletest_deps = deps_of(metadata, &compiletest.id);
+    let cargo_deps = direct_deps_of(metadata, &cargo.id);
+    let compiletest_deps = direct_deps_of(metadata, &compiletest.id);
     let cargo_rustfix = cargo_deps.iter().find(|p| p.name == "rustfix").unwrap();
     let compiletest_rustfix = compiletest_deps.iter().find(|p| p.name == "rustfix").unwrap();
     if cargo_rustfix.version != compiletest_rustfix.version {