From a53872388e03b4712a6c3d8a865e8cb214dab532 Mon Sep 17 00:00:00 2001
From: Yann Simon <yann.simon@commercetools.com>
Date: Fri, 16 Dec 2022 16:39:40 +0100
Subject: [PATCH 01/11] update stdarch

This will allow using miri on simd instructions
https://github.com/rust-lang/stdarch/issues/1347#issuecomment-1353664361
---
 library/stdarch | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/library/stdarch b/library/stdarch
index 790411f93c4b5..a0c30f3e3c75a 160000
--- a/library/stdarch
+++ b/library/stdarch
@@ -1 +1 @@
-Subproject commit 790411f93c4b5eada3c23abb4c9a063fb0b24d99
+Subproject commit a0c30f3e3c75adcd6ee7efc94014ebcead61c507

From 57ca36861d35dd1f75d8b51dd975c884bb2c6872 Mon Sep 17 00:00:00 2001
From: Michael Howell <michael@notriddle.com>
Date: Mon, 23 Jan 2023 14:31:35 -0700
Subject: [PATCH 02/11] rustdoc: make item links consistently use
 `title="{shortty} {path}"`

The ordering in item tables was flipped in 3030cbea957adbd560bf2eaa34c1b8a56daee16a, making it
inconsistent with the ordering in method signatures.

Compare these:

https://github.com/rust-lang/rust/blob/c8e6a9e8b6251bbc8276cb78cabe1998deecbed7/src/librustdoc/html/render/print_item.rs#L455-L459

https://github.com/rust-lang/rust/blob/c8e6a9e8b6251bbc8276cb78cabe1998deecbed7/src/librustdoc/html/format.rs#L903-L908
---
 src/librustdoc/html/render/print_item.rs                      | 2 +-
 tests/rustdoc-gui/unsafe-fn.goml                              | 4 ++--
 ...ue-83375-multiple-mods-w-same-name-doc-inline-last-item.rs | 2 +-
 .../issue-83375-multiple-mods-w-same-name-doc-inline.rs       | 2 +-
 tests/rustdoc/issue-99221-multiple-structs-w-same-name.rs     | 2 +-
 tests/rustdoc/issue-99734-multiple-foreigns-w-same-name.rs    | 2 +-
 tests/rustdoc/issue-99734-multiple-mods-w-same-name.rs        | 2 +-
 7 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index f824c9e3ad2bd..a09f309502a28 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -452,7 +452,7 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items:
                     stab = stab.unwrap_or_default(),
                     unsafety_flag = unsafety_flag,
                     href = item_path(myitem.type_(), myitem.name.unwrap().as_str()),
-                    title = [full_path(cx, myitem), myitem.type_().to_string()]
+                    title = [myitem.type_().to_string(), full_path(cx, myitem)]
                         .iter()
                         .filter_map(|s| if !s.is_empty() { Some(s.as_str()) } else { None })
                         .collect::<Vec<_>>()
diff --git a/tests/rustdoc-gui/unsafe-fn.goml b/tests/rustdoc-gui/unsafe-fn.goml
index d3a672ddde6e4..3ecb25c82a44e 100644
--- a/tests/rustdoc-gui/unsafe-fn.goml
+++ b/tests/rustdoc-gui/unsafe-fn.goml
@@ -4,8 +4,8 @@ goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
 show-text: true
 
 compare-elements-property: (
-    "//a[@title='test_docs::safe_fn fn']/..",
-    "//a[@title='test_docs::unsafe_fn fn']/..",
+    "//a[@title='fn test_docs::safe_fn']/..",
+    "//a[@title='fn test_docs::unsafe_fn']/..",
     ["clientHeight"]
 )
 
diff --git a/tests/rustdoc/issue-83375-multiple-mods-w-same-name-doc-inline-last-item.rs b/tests/rustdoc/issue-83375-multiple-mods-w-same-name-doc-inline-last-item.rs
index d3a7a870b580a..9bce25846d858 100644
--- a/tests/rustdoc/issue-83375-multiple-mods-w-same-name-doc-inline-last-item.rs
+++ b/tests/rustdoc/issue-83375-multiple-mods-w-same-name-doc-inline-last-item.rs
@@ -11,6 +11,6 @@ pub mod sub {
 #[doc(inline)]
 pub use sub::*;
 
-// @count foo/index.html '//a[@class="mod"][@title="foo::prelude mod"]' 1
+// @count foo/index.html '//a[@class="mod"][@title="mod foo::prelude"]' 1
 // @count foo/prelude/index.html '//div[@class="item-row"]' 0
 pub mod prelude {}
diff --git a/tests/rustdoc/issue-83375-multiple-mods-w-same-name-doc-inline.rs b/tests/rustdoc/issue-83375-multiple-mods-w-same-name-doc-inline.rs
index b836925099364..d0960dfef4362 100644
--- a/tests/rustdoc/issue-83375-multiple-mods-w-same-name-doc-inline.rs
+++ b/tests/rustdoc/issue-83375-multiple-mods-w-same-name-doc-inline.rs
@@ -8,7 +8,7 @@ pub mod sub {
     }
 }
 
-// @count foo/index.html '//a[@class="mod"][@title="foo::prelude mod"]' 1
+// @count foo/index.html '//a[@class="mod"][@title="mod foo::prelude"]' 1
 // @count foo/prelude/index.html '//div[@class="item-row"]' 0
 pub mod prelude {}
 
diff --git a/tests/rustdoc/issue-99221-multiple-structs-w-same-name.rs b/tests/rustdoc/issue-99221-multiple-structs-w-same-name.rs
index 41e64726a3246..ba29a77ebdff8 100644
--- a/tests/rustdoc/issue-99221-multiple-structs-w-same-name.rs
+++ b/tests/rustdoc/issue-99221-multiple-structs-w-same-name.rs
@@ -9,6 +9,6 @@ extern crate issue_99221_aux;
 
 pub use issue_99221_aux::*;
 
-// @count foo/index.html '//a[@class="struct"][@title="foo::Print struct"]' 1
+// @count foo/index.html '//a[@class="struct"][@title="struct foo::Print"]' 1
 
 pub struct Print;
diff --git a/tests/rustdoc/issue-99734-multiple-foreigns-w-same-name.rs b/tests/rustdoc/issue-99734-multiple-foreigns-w-same-name.rs
index 3208fea05b376..b56ec6e11eafc 100644
--- a/tests/rustdoc/issue-99734-multiple-foreigns-w-same-name.rs
+++ b/tests/rustdoc/issue-99734-multiple-foreigns-w-same-name.rs
@@ -9,7 +9,7 @@ extern crate issue_99734_aux;
 
 pub use issue_99734_aux::*;
 
-// @count foo/index.html '//a[@class="fn"][@title="foo::main fn"]' 1
+// @count foo/index.html '//a[@class="fn"][@title="fn foo::main"]' 1
 
 extern "C" {
     pub fn main() -> std::ffi::c_int;
diff --git a/tests/rustdoc/issue-99734-multiple-mods-w-same-name.rs b/tests/rustdoc/issue-99734-multiple-mods-w-same-name.rs
index b2f9b8b46578b..8f5d6fa3d56d3 100644
--- a/tests/rustdoc/issue-99734-multiple-mods-w-same-name.rs
+++ b/tests/rustdoc/issue-99734-multiple-mods-w-same-name.rs
@@ -9,6 +9,6 @@ extern crate issue_99734_aux;
 
 pub use issue_99734_aux::*;
 
-// @count foo/index.html '//a[@class="mod"][@title="foo::task mod"]' 1
+// @count foo/index.html '//a[@class="mod"][@title="mod foo::task"]' 1
 
 pub mod task {}

From 1cd7dbfbf85599d764c403cb5fee555da16c003a Mon Sep 17 00:00:00 2001
From: Michal Rostecki <vadorovsky@gmail.com>
Date: Mon, 16 Jan 2023 19:13:52 +0800
Subject: [PATCH 03/11] Add `target_has_atomic*` symbols if any atomic width is
 supported

Atomic operations for different widths (8-bit, 16-bit, 32-bit etc.) are
guarded by `target_has_atomic = "value"` symbol (i.e. `target_has_atomic
= "8"`) (and the other derivatives), but before this change, there was
no width-agnostic symbol indicating a general availability of atomic
operations.

This change introduces:

* `target_has_atomic_load_store` symbol when atomics for any integer
  width are supported by the target.
* `target_has_atomic` symbol when also CAS is supported.

Fixes #106845

Signed-off-by: Michal Rostecki <vadorovsky@gmail.com>
---
 compiler/rustc_session/src/config.rs | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index 586454f76574c..09bd474688bf2 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -957,6 +957,7 @@ fn default_configuration(sess: &Session) -> CrateConfig {
     if sess.target.has_thread_local {
         ret.insert((sym::target_thread_local, None));
     }
+    let mut has_atomic = false;
     for (i, align) in [
         (8, layout.i8_align.abi),
         (16, layout.i16_align.abi),
@@ -965,6 +966,7 @@ fn default_configuration(sess: &Session) -> CrateConfig {
         (128, layout.i128_align.abi),
     ] {
         if i >= min_atomic_width && i <= max_atomic_width {
+            has_atomic = true;
             let mut insert_atomic = |s, align: Align| {
                 ret.insert((sym::target_has_atomic_load_store, Some(Symbol::intern(s))));
                 if atomic_cas {
@@ -981,6 +983,12 @@ fn default_configuration(sess: &Session) -> CrateConfig {
             }
         }
     }
+    if sess.is_nightly_build() && has_atomic {
+        ret.insert((sym::target_has_atomic_load_store, None));
+        if atomic_cas {
+            ret.insert((sym::target_has_atomic, None));
+        }
+    }
 
     let panic_strategy = sess.panic_strategy();
     ret.insert((sym::panic, Some(panic_strategy.desc_symbol())));

From 474ea87943c3077feb7d7f2a6f295bd06654ef82 Mon Sep 17 00:00:00 2001
From: Michal Rostecki <vadorovsky@gmail.com>
Date: Mon, 16 Jan 2023 19:40:38 +0800
Subject: [PATCH 04/11] core: Support variety of atomic widths in
 width-agnostic functions

Before this change, the following functions and macros were annotated
with `#[cfg(target_has_atomic = "8")]` or
`#[cfg(target_has_atomic_load_store = "8")]`:

* `atomic_int`
* `strongest_failure_ordering`
* `atomic_swap`
* `atomic_add`
* `atomic_sub`
* `atomic_compare_exchange`
* `atomic_compare_exchange_weak`
* `atomic_and`
* `atomic_nand`
* `atomic_or`
* `atomic_xor`
* `atomic_max`
* `atomic_min`
* `atomic_umax`
* `atomic_umin`

However, none of those functions and macros actually depend on 8-bit
width and they are needed for all atomic widths (16-bit, 32-bit, 64-bit
etc.). Some targets might not support 8-bit atomics (i.e. BPF, if we
would enable atomic CAS for it).

This change fixes that by removing the `"8"` argument from annotations,
which results in accepting the whole variety of widths.

Fixes #106845
Fixes #106795

Signed-off-by: Michal Rostecki <vadorovsky@gmail.com>
---
 library/core/src/sync/atomic.rs | 45 ++++++++++++++++++++++-----------
 1 file changed, 30 insertions(+), 15 deletions(-)

diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs
index 14367eb09bc75..818721062d7f7 100644
--- a/library/core/src/sync/atomic.rs
+++ b/library/core/src/sync/atomic.rs
@@ -1861,7 +1861,8 @@ macro_rules! if_not_8_bit {
     ($_:ident, $($tt:tt)*) => { $($tt)* };
 }
 
-#[cfg(target_has_atomic_load_store = "8")]
+#[cfg_attr(not(bootstrap), cfg(target_has_atomic_load_store))]
+#[cfg_attr(bootstrap, cfg(target_has_atomic_load_store = "8"))]
 macro_rules! atomic_int {
     ($cfg_cas:meta,
      $cfg_align:meta,
@@ -2988,7 +2989,8 @@ atomic_int_ptr_sized! {
 }
 
 #[inline]
-#[cfg(target_has_atomic = "8")]
+#[cfg_attr(not(bootstrap), cfg(target_has_atomic))]
+#[cfg_attr(bootstrap, cfg(target_has_atomic = "8"))]
 fn strongest_failure_ordering(order: Ordering) -> Ordering {
     match order {
         Release => Relaxed,
@@ -3030,7 +3032,8 @@ unsafe fn atomic_load<T: Copy>(dst: *const T, order: Ordering) -> T {
 }
 
 #[inline]
-#[cfg(target_has_atomic = "8")]
+#[cfg_attr(not(bootstrap), cfg(target_has_atomic))]
+#[cfg_attr(bootstrap, cfg(target_has_atomic = "8"))]
 #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
 unsafe fn atomic_swap<T: Copy>(dst: *mut T, val: T, order: Ordering) -> T {
     // SAFETY: the caller must uphold the safety contract for `atomic_swap`.
@@ -3047,7 +3050,8 @@ unsafe fn atomic_swap<T: Copy>(dst: *mut T, val: T, order: Ordering) -> T {
 
 /// Returns the previous value (like __sync_fetch_and_add).
 #[inline]
-#[cfg(target_has_atomic = "8")]
+#[cfg_attr(not(bootstrap), cfg(target_has_atomic))]
+#[cfg_attr(bootstrap, cfg(target_has_atomic = "8"))]
 #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
 unsafe fn atomic_add<T: Copy>(dst: *mut T, val: T, order: Ordering) -> T {
     // SAFETY: the caller must uphold the safety contract for `atomic_add`.
@@ -3064,7 +3068,8 @@ unsafe fn atomic_add<T: Copy>(dst: *mut T, val: T, order: Ordering) -> T {
 
 /// Returns the previous value (like __sync_fetch_and_sub).
 #[inline]
-#[cfg(target_has_atomic = "8")]
+#[cfg_attr(not(bootstrap), cfg(target_has_atomic))]
+#[cfg_attr(bootstrap, cfg(target_has_atomic = "8"))]
 #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
 unsafe fn atomic_sub<T: Copy>(dst: *mut T, val: T, order: Ordering) -> T {
     // SAFETY: the caller must uphold the safety contract for `atomic_sub`.
@@ -3080,7 +3085,8 @@ unsafe fn atomic_sub<T: Copy>(dst: *mut T, val: T, order: Ordering) -> T {
 }
 
 #[inline]
-#[cfg(target_has_atomic = "8")]
+#[cfg_attr(not(bootstrap), cfg(target_has_atomic))]
+#[cfg_attr(bootstrap, cfg(target_has_atomic = "8"))]
 #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
 unsafe fn atomic_compare_exchange<T: Copy>(
     dst: *mut T,
@@ -3115,7 +3121,8 @@ unsafe fn atomic_compare_exchange<T: Copy>(
 }
 
 #[inline]
-#[cfg(target_has_atomic = "8")]
+#[cfg_attr(not(bootstrap), cfg(target_has_atomic))]
+#[cfg_attr(bootstrap, cfg(target_has_atomic = "8"))]
 #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
 unsafe fn atomic_compare_exchange_weak<T: Copy>(
     dst: *mut T,
@@ -3150,7 +3157,8 @@ unsafe fn atomic_compare_exchange_weak<T: Copy>(
 }
 
 #[inline]
-#[cfg(target_has_atomic = "8")]
+#[cfg_attr(not(bootstrap), cfg(target_has_atomic))]
+#[cfg_attr(bootstrap, cfg(target_has_atomic = "8"))]
 #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
 unsafe fn atomic_and<T: Copy>(dst: *mut T, val: T, order: Ordering) -> T {
     // SAFETY: the caller must uphold the safety contract for `atomic_and`
@@ -3166,7 +3174,8 @@ unsafe fn atomic_and<T: Copy>(dst: *mut T, val: T, order: Ordering) -> T {
 }
 
 #[inline]
-#[cfg(target_has_atomic = "8")]
+#[cfg_attr(not(bootstrap), cfg(target_has_atomic))]
+#[cfg_attr(bootstrap, cfg(target_has_atomic = "8"))]
 #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
 unsafe fn atomic_nand<T: Copy>(dst: *mut T, val: T, order: Ordering) -> T {
     // SAFETY: the caller must uphold the safety contract for `atomic_nand`
@@ -3182,7 +3191,8 @@ unsafe fn atomic_nand<T: Copy>(dst: *mut T, val: T, order: Ordering) -> T {
 }
 
 #[inline]
-#[cfg(target_has_atomic = "8")]
+#[cfg_attr(not(bootstrap), cfg(target_has_atomic))]
+#[cfg_attr(bootstrap, cfg(target_has_atomic = "8"))]
 #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
 unsafe fn atomic_or<T: Copy>(dst: *mut T, val: T, order: Ordering) -> T {
     // SAFETY: the caller must uphold the safety contract for `atomic_or`
@@ -3198,7 +3208,8 @@ unsafe fn atomic_or<T: Copy>(dst: *mut T, val: T, order: Ordering) -> T {
 }
 
 #[inline]
-#[cfg(target_has_atomic = "8")]
+#[cfg_attr(not(bootstrap), cfg(target_has_atomic))]
+#[cfg_attr(bootstrap, cfg(target_has_atomic = "8"))]
 #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
 unsafe fn atomic_xor<T: Copy>(dst: *mut T, val: T, order: Ordering) -> T {
     // SAFETY: the caller must uphold the safety contract for `atomic_xor`
@@ -3215,7 +3226,8 @@ unsafe fn atomic_xor<T: Copy>(dst: *mut T, val: T, order: Ordering) -> T {
 
 /// returns the max value (signed comparison)
 #[inline]
-#[cfg(target_has_atomic = "8")]
+#[cfg_attr(not(bootstrap), cfg(target_has_atomic))]
+#[cfg_attr(bootstrap, cfg(target_has_atomic = "8"))]
 #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
 unsafe fn atomic_max<T: Copy>(dst: *mut T, val: T, order: Ordering) -> T {
     // SAFETY: the caller must uphold the safety contract for `atomic_max`
@@ -3232,7 +3244,8 @@ unsafe fn atomic_max<T: Copy>(dst: *mut T, val: T, order: Ordering) -> T {
 
 /// returns the min value (signed comparison)
 #[inline]
-#[cfg(target_has_atomic = "8")]
+#[cfg_attr(not(bootstrap), cfg(target_has_atomic))]
+#[cfg_attr(bootstrap, cfg(target_has_atomic = "8"))]
 #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
 unsafe fn atomic_min<T: Copy>(dst: *mut T, val: T, order: Ordering) -> T {
     // SAFETY: the caller must uphold the safety contract for `atomic_min`
@@ -3249,7 +3262,8 @@ unsafe fn atomic_min<T: Copy>(dst: *mut T, val: T, order: Ordering) -> T {
 
 /// returns the max value (unsigned comparison)
 #[inline]
-#[cfg(target_has_atomic = "8")]
+#[cfg_attr(not(bootstrap), cfg(target_has_atomic))]
+#[cfg_attr(bootstrap, cfg(target_has_atomic = "8"))]
 #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
 unsafe fn atomic_umax<T: Copy>(dst: *mut T, val: T, order: Ordering) -> T {
     // SAFETY: the caller must uphold the safety contract for `atomic_umax`
@@ -3266,7 +3280,8 @@ unsafe fn atomic_umax<T: Copy>(dst: *mut T, val: T, order: Ordering) -> T {
 
 /// returns the min value (unsigned comparison)
 #[inline]
-#[cfg(target_has_atomic = "8")]
+#[cfg_attr(not(bootstrap), cfg(target_has_atomic))]
+#[cfg_attr(bootstrap, cfg(target_has_atomic = "8"))]
 #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
 unsafe fn atomic_umin<T: Copy>(dst: *mut T, val: T, order: Ordering) -> T {
     // SAFETY: the caller must uphold the safety contract for `atomic_umin`

From c70b7aafae283b69762ee809d6b2b5adaf0f3c43 Mon Sep 17 00:00:00 2001
From: Vadim Petrochenkov <vadim.petrochenkov@gmail.com>
Date: Sat, 21 Jan 2023 23:49:23 +0400
Subject: [PATCH 05/11] rustc_metadata: Fix `encode_attrs`

This function didn't do what the authors intended it to do.

- Due to `move` in the closure `is_public` wasn't captured by mutalbe reference and wasn't used as a cache.
- Due to iterator cloning all the `should_encode_attr` logic run for the second time to calculate `may_have_doc_links`

This PR fixes these issues, and calculates all the needed attribute flags in one go.
---
 compiler/rustc_metadata/src/rmeta/encoder.rs | 77 ++++++++++++--------
 1 file changed, 48 insertions(+), 29 deletions(-)

diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index 2ecaa33d4d315..e6430d327879f 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -3,6 +3,7 @@ use crate::rmeta::def_path_hash_map::DefPathHashMapRef;
 use crate::rmeta::table::TableBuilder;
 use crate::rmeta::*;
 
+use rustc_ast::util::comments;
 use rustc_ast::Attribute;
 use rustc_data_structures::fingerprint::Fingerprint;
 use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
@@ -760,36 +761,54 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
     }
 }
 
+struct AnalyzeAttrState {
+    is_exported: bool,
+    may_have_doc_links: bool,
+    is_doc_hidden: bool,
+}
+
 /// Returns whether an attribute needs to be recorded in metadata, that is, if it's usable and
 /// useful in downstream crates. Local-only attributes are an obvious example, but some
 /// rustdoc-specific attributes can equally be of use while documenting the current crate only.
 ///
 /// Removing these superfluous attributes speeds up compilation by making the metadata smaller.
 ///
-/// Note: the `is_def_id_public` parameter is used to cache whether the given `DefId` has a public
+/// Note: the `is_exported` parameter is used to cache whether the given `DefId` has a public
 /// visibility: this is a piece of data that can be computed once per defid, and not once per
 /// attribute. Some attributes would only be usable downstream if they are public.
 #[inline]
-fn should_encode_attr(
-    tcx: TyCtxt<'_>,
-    attr: &Attribute,
-    def_id: LocalDefId,
-    is_def_id_public: &mut Option<bool>,
-) -> bool {
+fn analyze_attr(attr: &Attribute, state: &mut AnalyzeAttrState) -> bool {
+    let mut should_encode = false;
     if rustc_feature::is_builtin_only_local(attr.name_or_empty()) {
         // Attributes marked local-only don't need to be encoded for downstream crates.
-        false
-    } else if attr.doc_str().is_some() {
-        // We keep all public doc comments because they might be "imported" into downstream crates
-        // if they use `#[doc(inline)]` to copy an item's documentation into their own.
-        *is_def_id_public.get_or_insert_with(|| tcx.effective_visibilities(()).is_exported(def_id))
+    } else if let Some(s) = attr.doc_str() {
+        // We keep all doc comments reachable to rustdoc because they might be "imported" into
+        // downstream crates if they use `#[doc(inline)]` to copy an item's documentation into
+        // their own.
+        if state.is_exported {
+            should_encode = true;
+            if comments::may_have_doc_links(s.as_str()) {
+                state.may_have_doc_links = true;
+            }
+        }
     } else if attr.has_name(sym::doc) {
-        // If this is a `doc` attribute, and it's marked `inline` (as in `#[doc(inline)]`), we can
-        // remove it. It won't be inlinable in downstream crates.
-        attr.meta_item_list().map(|l| l.iter().any(|l| !l.has_name(sym::inline))).unwrap_or(false)
+        // If this is a `doc` attribute that doesn't have anything except maybe `inline` (as in
+        // `#[doc(inline)]`), then we can remove it. It won't be inlinable in downstream crates.
+        if let Some(item_list) = attr.meta_item_list() {
+            for item in item_list {
+                if !item.has_name(sym::inline) {
+                    should_encode = true;
+                    if item.has_name(sym::hidden) {
+                        state.is_doc_hidden = true;
+                        break;
+                    }
+                }
+            }
+        }
     } else {
-        true
+        should_encode = true;
     }
+    should_encode
 }
 
 fn should_encode_visibility(def_kind: DefKind) -> bool {
@@ -1109,24 +1128,24 @@ fn should_encode_trait_impl_trait_tys(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
 impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
     fn encode_attrs(&mut self, def_id: LocalDefId) {
         let tcx = self.tcx;
-        let mut is_public: Option<bool> = None;
-
-        let hir_attrs = tcx.hir().attrs(tcx.hir().local_def_id_to_hir_id(def_id));
-        let mut attrs = hir_attrs
+        let mut state = AnalyzeAttrState {
+            is_exported: tcx.effective_visibilities(()).is_exported(def_id),
+            may_have_doc_links: false,
+            is_doc_hidden: false,
+        };
+        let attr_iter = tcx
+            .hir()
+            .attrs(tcx.hir().local_def_id_to_hir_id(def_id))
             .iter()
-            .filter(move |attr| should_encode_attr(tcx, attr, def_id, &mut is_public));
+            .filter(|attr| analyze_attr(attr, &mut state));
+
+        record_array!(self.tables.attributes[def_id.to_def_id()] <- attr_iter);
 
-        record_array!(self.tables.attributes[def_id.to_def_id()] <- attrs.clone());
         let mut attr_flags = AttrFlags::empty();
-        if attrs.any(|attr| attr.may_have_doc_links()) {
+        if state.may_have_doc_links {
             attr_flags |= AttrFlags::MAY_HAVE_DOC_LINKS;
         }
-        if hir_attrs
-            .iter()
-            .filter(|attr| attr.has_name(sym::doc))
-            .filter_map(|attr| attr.meta_item_list())
-            .any(|items| items.iter().any(|item| item.has_name(sym::hidden)))
-        {
+        if state.is_doc_hidden {
             attr_flags |= AttrFlags::IS_DOC_HIDDEN;
         }
         if !attr_flags.is_empty() {

From a499862948371bbbcc534ca8d7a20cb107ce9952 Mon Sep 17 00:00:00 2001
From: Yann Simon <yann.simon@commercetools.com>
Date: Thu, 26 Jan 2023 11:01:36 +0100
Subject: [PATCH 06/11] remove avx512 prefix for gfni, vaes and vpclmulqdq

---
 library/std/tests/run-time-detect.rs | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/library/std/tests/run-time-detect.rs b/library/std/tests/run-time-detect.rs
index 7fbfe3daaa826..c25c3f84d2b6c 100644
--- a/library/std/tests/run-time-detect.rs
+++ b/library/std/tests/run-time-detect.rs
@@ -120,16 +120,16 @@ fn x86_all() {
     println!("avx512dq: {:?}", is_x86_feature_detected!("avx512dq"));
     println!("avx512er: {:?}", is_x86_feature_detected!("avx512er"));
     println!("avx512f: {:?}", is_x86_feature_detected!("avx512f"));
-    println!("avx512gfni: {:?}", is_x86_feature_detected!("avx512gfni"));
+    println!("gfni: {:?}", is_x86_feature_detected!("gfni"));
     println!("avx512ifma: {:?}", is_x86_feature_detected!("avx512ifma"));
     println!("avx512pf: {:?}", is_x86_feature_detected!("avx512pf"));
-    println!("avx512vaes: {:?}", is_x86_feature_detected!("avx512vaes"));
+    println!("vaes: {:?}", is_x86_feature_detected!("vaes"));
     println!("avx512vbmi2: {:?}", is_x86_feature_detected!("avx512vbmi2"));
     println!("avx512vbmi: {:?}", is_x86_feature_detected!("avx512vbmi"));
     println!("avx512vl: {:?}", is_x86_feature_detected!("avx512vl"));
     println!("avx512vnni: {:?}", is_x86_feature_detected!("avx512vnni"));
     println!("avx512vp2intersect: {:?}", is_x86_feature_detected!("avx512vp2intersect"));
-    println!("avx512vpclmulqdq: {:?}", is_x86_feature_detected!("avx512vpclmulqdq"));
+    println!("vpclmulqdq: {:?}", is_x86_feature_detected!("vpclmulqdq"));
     println!("avx512vpopcntdq: {:?}", is_x86_feature_detected!("avx512vpopcntdq"));
     println!("avx: {:?}", is_x86_feature_detected!("avx"));
     println!("bmi1: {:?}", is_x86_feature_detected!("bmi1"));

From 2e8162a0b00c3b1cc06b244c3b6ddbbb31caa26d Mon Sep 17 00:00:00 2001
From: Yann Simon <yann.simon@commercetools.com>
Date: Thu, 26 Jan 2023 11:09:32 +0100
Subject: [PATCH 07/11] fix alphabetical sort

---
 library/std/tests/run-time-detect.rs | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/library/std/tests/run-time-detect.rs b/library/std/tests/run-time-detect.rs
index c25c3f84d2b6c..ffcca9897c135 100644
--- a/library/std/tests/run-time-detect.rs
+++ b/library/std/tests/run-time-detect.rs
@@ -120,16 +120,13 @@ fn x86_all() {
     println!("avx512dq: {:?}", is_x86_feature_detected!("avx512dq"));
     println!("avx512er: {:?}", is_x86_feature_detected!("avx512er"));
     println!("avx512f: {:?}", is_x86_feature_detected!("avx512f"));
-    println!("gfni: {:?}", is_x86_feature_detected!("gfni"));
     println!("avx512ifma: {:?}", is_x86_feature_detected!("avx512ifma"));
     println!("avx512pf: {:?}", is_x86_feature_detected!("avx512pf"));
-    println!("vaes: {:?}", is_x86_feature_detected!("vaes"));
     println!("avx512vbmi2: {:?}", is_x86_feature_detected!("avx512vbmi2"));
     println!("avx512vbmi: {:?}", is_x86_feature_detected!("avx512vbmi"));
     println!("avx512vl: {:?}", is_x86_feature_detected!("avx512vl"));
     println!("avx512vnni: {:?}", is_x86_feature_detected!("avx512vnni"));
     println!("avx512vp2intersect: {:?}", is_x86_feature_detected!("avx512vp2intersect"));
-    println!("vpclmulqdq: {:?}", is_x86_feature_detected!("vpclmulqdq"));
     println!("avx512vpopcntdq: {:?}", is_x86_feature_detected!("avx512vpopcntdq"));
     println!("avx: {:?}", is_x86_feature_detected!("avx"));
     println!("bmi1: {:?}", is_x86_feature_detected!("bmi1"));
@@ -138,6 +135,7 @@ fn x86_all() {
     println!("f16c: {:?}", is_x86_feature_detected!("f16c"));
     println!("fma: {:?}", is_x86_feature_detected!("fma"));
     println!("fxsr: {:?}", is_x86_feature_detected!("fxsr"));
+    println!("gfni: {:?}", is_x86_feature_detected!("gfni"));
     println!("lzcnt: {:?}", is_x86_feature_detected!("lzcnt"));
     //println!("movbe: {:?}", is_x86_feature_detected!("movbe")); // movbe is unsupported as a target feature
     println!("pclmulqdq: {:?}", is_x86_feature_detected!("pclmulqdq"));
@@ -154,6 +152,8 @@ fn x86_all() {
     println!("sse: {:?}", is_x86_feature_detected!("sse"));
     println!("ssse3: {:?}", is_x86_feature_detected!("ssse3"));
     println!("tbm: {:?}", is_x86_feature_detected!("tbm"));
+    println!("vaes: {:?}", is_x86_feature_detected!("vaes"));
+    println!("vpclmulqdq: {:?}", is_x86_feature_detected!("vpclmulqdq"));
     println!("xsave: {:?}", is_x86_feature_detected!("xsave"));
     println!("xsavec: {:?}", is_x86_feature_detected!("xsavec"));
     println!("xsaveopt: {:?}", is_x86_feature_detected!("xsaveopt"));

From 347fa7a26f3eb6085418a26cef18f133635f7a9f Mon Sep 17 00:00:00 2001
From: Vadim Petrochenkov <vadim.petrochenkov@gmail.com>
Date: Thu, 26 Jan 2023 14:30:28 +0400
Subject: [PATCH 08/11] rustdoc: Stop using `HirId`s

Use `LocalDefId`s instead
---
 src/librustdoc/clean/mod.rs                   | 36 ++++----
 src/librustdoc/clean/types.rs                 | 16 +---
 src/librustdoc/doctest.rs                     | 26 +++---
 src/librustdoc/html/markdown.rs               | 44 +++-------
 .../passes/calculate_doc_coverage.rs          |  8 +-
 .../passes/check_doc_test_visibility.rs       | 19 +++--
 .../passes/collect_intra_doc_links.rs         | 11 +--
 .../passes/lint/check_code_block_syntax.rs    |  5 +-
 src/librustdoc/passes/propagate_doc_cfg.rs    | 17 ++--
 src/librustdoc/visit_ast.rs                   | 82 ++++++++++---------
 10 files changed, 113 insertions(+), 151 deletions(-)

diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 34a7068e5da53..3cb6ad10e72b8 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -15,7 +15,7 @@ use rustc_attr as attr;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet, IndexEntry};
 use rustc_hir as hir;
 use rustc_hir::def::{CtorKind, DefKind, Res};
-use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LOCAL_CRATE};
+use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LocalDefId, LOCAL_CRATE};
 use rustc_hir::PredicateOrigin;
 use rustc_hir_analysis::hir_ty_to_ty;
 use rustc_infer::infer::region_constraints::{Constraint, RegionConstraintData};
@@ -116,7 +116,8 @@ pub(crate) fn clean_doc_module<'tcx>(doc: &DocModule<'tcx>, cx: &mut DocContext<
         }
     });
 
-    Item::from_hir_id_and_parts(doc.id, Some(doc.name), ModuleItem(Module { items, span }), cx)
+    let kind = ModuleItem(Module { items, span });
+    Item::from_def_id_and_parts(doc.def_id.to_def_id(), Some(doc.name), kind, cx)
 }
 
 fn clean_generic_bound<'tcx>(
@@ -2067,12 +2068,12 @@ struct OneLevelVisitor<'hir> {
     map: rustc_middle::hir::map::Map<'hir>,
     item: Option<&'hir hir::Item<'hir>>,
     looking_for: Ident,
-    target_hir_id: hir::HirId,
+    target_def_id: LocalDefId,
 }
 
 impl<'hir> OneLevelVisitor<'hir> {
-    fn new(map: rustc_middle::hir::map::Map<'hir>, target_hir_id: hir::HirId) -> Self {
-        Self { map, item: None, looking_for: Ident::empty(), target_hir_id }
+    fn new(map: rustc_middle::hir::map::Map<'hir>, target_def_id: LocalDefId) -> Self {
+        Self { map, item: None, looking_for: Ident::empty(), target_def_id }
     }
 
     fn reset(&mut self, looking_for: Ident) {
@@ -2092,7 +2093,7 @@ impl<'hir> hir::intravisit::Visitor<'hir> for OneLevelVisitor<'hir> {
         if self.item.is_none()
             && item.ident == self.looking_for
             && matches!(item.kind, hir::ItemKind::Use(_, _))
-            || item.hir_id() == self.target_hir_id
+            || item.owner_id.def_id == self.target_def_id
         {
             self.item = Some(item);
         }
@@ -2106,11 +2107,11 @@ impl<'hir> hir::intravisit::Visitor<'hir> for OneLevelVisitor<'hir> {
 fn get_all_import_attributes<'hir>(
     mut item: &hir::Item<'hir>,
     tcx: TyCtxt<'hir>,
-    target_hir_id: hir::HirId,
+    target_def_id: LocalDefId,
     attributes: &mut Vec<ast::Attribute>,
 ) {
     let hir_map = tcx.hir();
-    let mut visitor = OneLevelVisitor::new(hir_map, target_hir_id);
+    let mut visitor = OneLevelVisitor::new(hir_map, target_def_id);
     // If the item is an import and has at least a path with two parts, we go into it.
     while let hir::ItemKind::Use(path, _) = item.kind &&
         path.segments.len() > 1 &&
@@ -2138,7 +2139,7 @@ fn clean_maybe_renamed_item<'tcx>(
     cx: &mut DocContext<'tcx>,
     item: &hir::Item<'tcx>,
     renamed: Option<Symbol>,
-    import_id: Option<hir::HirId>,
+    import_id: Option<LocalDefId>,
 ) -> Vec<Item> {
     use hir::ItemKind;
 
@@ -2183,7 +2184,7 @@ fn clean_maybe_renamed_item<'tcx>(
                 generics: clean_generics(generics, cx),
                 fields: variant_data.fields().iter().map(|x| clean_field(x, cx)).collect(),
             }),
-            ItemKind::Impl(impl_) => return clean_impl(impl_, item.hir_id(), cx),
+            ItemKind::Impl(impl_) => return clean_impl(impl_, item.owner_id.def_id, cx),
             // proc macros can have a name set by attributes
             ItemKind::Fn(ref sig, generics, body_id) => {
                 clean_fn_or_proc_macro(item, sig, generics, body_id, &mut name, cx)
@@ -2218,10 +2219,10 @@ fn clean_maybe_renamed_item<'tcx>(
 
         let mut extra_attrs = Vec::new();
         if let Some(hir::Node::Item(use_node)) =
-            import_id.and_then(|hir_id| cx.tcx.hir().find(hir_id))
+            import_id.and_then(|def_id| cx.tcx.hir().find_by_def_id(def_id))
         {
             // We get all the various imports' attributes.
-            get_all_import_attributes(use_node, cx.tcx, item.hir_id(), &mut extra_attrs);
+            get_all_import_attributes(use_node, cx.tcx, item.owner_id.def_id, &mut extra_attrs);
         }
 
         if !extra_attrs.is_empty() {
@@ -2244,12 +2245,12 @@ fn clean_maybe_renamed_item<'tcx>(
 
 fn clean_variant<'tcx>(variant: &hir::Variant<'tcx>, cx: &mut DocContext<'tcx>) -> Item {
     let kind = VariantItem(clean_variant_data(&variant.data, &variant.disr_expr, cx));
-    Item::from_hir_id_and_parts(variant.hir_id, Some(variant.ident.name), kind, cx)
+    Item::from_def_id_and_parts(variant.def_id.to_def_id(), Some(variant.ident.name), kind, cx)
 }
 
 fn clean_impl<'tcx>(
     impl_: &hir::Impl<'tcx>,
-    hir_id: hir::HirId,
+    def_id: LocalDefId,
     cx: &mut DocContext<'tcx>,
 ) -> Vec<Item> {
     let tcx = cx.tcx;
@@ -2260,7 +2261,6 @@ fn clean_impl<'tcx>(
         .iter()
         .map(|ii| clean_impl_item(tcx.hir().impl_item(ii.id), cx))
         .collect::<Vec<_>>();
-    let def_id = tcx.hir().local_def_id(hir_id);
 
     // If this impl block is an implementation of the Deref trait, then we
     // need to try inlining the target's inherent impl blocks as well.
@@ -2289,7 +2289,7 @@ fn clean_impl<'tcx>(
                 ImplKind::Normal
             },
         }));
-        Item::from_hir_id_and_parts(hir_id, None, kind, cx)
+        Item::from_def_id_and_parts(def_id.to_def_id(), None, kind, cx)
     };
     if let Some(type_alias) = type_alias {
         ret.push(make_item(trait_.clone(), type_alias, items.clone()));
@@ -2510,8 +2510,8 @@ fn clean_maybe_renamed_foreign_item<'tcx>(
             hir::ForeignItemKind::Type => ForeignTypeItem,
         };
 
-        Item::from_hir_id_and_parts(
-            item.hir_id(),
+        Item::from_def_id_and_parts(
+            item.owner_id.def_id.to_def_id(),
             Some(renamed.unwrap_or(item.ident.name)),
             kind,
             cx,
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index a020ccd53b842..3b258c4d919f8 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -439,17 +439,6 @@ impl Item {
         self.attrs.doc_value()
     }
 
-    /// Convenience wrapper around [`Self::from_def_id_and_parts`] which converts
-    /// `hir_id` to a [`DefId`]
-    pub(crate) fn from_hir_id_and_parts(
-        hir_id: hir::HirId,
-        name: Option<Symbol>,
-        kind: ItemKind,
-        cx: &mut DocContext<'_>,
-    ) -> Item {
-        Item::from_def_id_and_parts(cx.tcx.hir().local_def_id(hir_id).to_def_id(), name, kind, cx)
-    }
-
     pub(crate) fn from_def_id_and_parts(
         def_id: DefId,
         name: Option<Symbol>,
@@ -2416,10 +2405,7 @@ impl ConstantKind {
 
     pub(crate) fn is_literal(&self, tcx: TyCtxt<'_>) -> bool {
         match *self {
-            ConstantKind::TyConst { .. } => false,
-            ConstantKind::Extern { def_id } => def_id.as_local().map_or(false, |def_id| {
-                is_literal_expr(tcx, tcx.hir().local_def_id_to_hir_id(def_id))
-            }),
+            ConstantKind::TyConst { .. } | ConstantKind::Extern { .. } => false,
             ConstantKind::Local { body, .. } | ConstantKind::Anonymous { body } => {
                 is_literal_expr(tcx, body.hir_id)
             }
diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs
index c1a652c75f4a1..37a1005cba1fc 100644
--- a/src/librustdoc/doctest.rs
+++ b/src/librustdoc/doctest.rs
@@ -2,10 +2,8 @@ use rustc_ast as ast;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::sync::Lrc;
 use rustc_errors::{ColorConfig, ErrorGuaranteed, FatalError};
-use rustc_hir as hir;
-use rustc_hir::def_id::LOCAL_CRATE;
-use rustc_hir::intravisit;
-use rustc_hir::{HirId, CRATE_HIR_ID};
+use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID, LOCAL_CRATE};
+use rustc_hir::{self as hir, intravisit, CRATE_HIR_ID};
 use rustc_interface::interface;
 use rustc_middle::hir::map::Map;
 use rustc_middle::hir::nested_filter;
@@ -140,7 +138,7 @@ pub(crate) fn run(options: RustdocOptions) -> Result<(), ErrorGuaranteed> {
                     };
                     hir_collector.visit_testable(
                         "".to_string(),
-                        CRATE_HIR_ID,
+                        CRATE_DEF_ID,
                         tcx.hir().span(CRATE_HIR_ID),
                         |this| tcx.hir().walk_toplevel_module(this),
                     );
@@ -1214,11 +1212,11 @@ impl<'a, 'hir, 'tcx> HirCollector<'a, 'hir, 'tcx> {
     fn visit_testable<F: FnOnce(&mut Self)>(
         &mut self,
         name: String,
-        hir_id: HirId,
+        def_id: LocalDefId,
         sp: Span,
         nested: F,
     ) {
-        let ast_attrs = self.tcx.hir().attrs(hir_id);
+        let ast_attrs = self.tcx.hir().attrs(self.tcx.hir().local_def_id_to_hir_id(def_id));
         if let Some(ref cfg) = ast_attrs.cfg(self.tcx, &FxHashSet::default()) {
             if !cfg.matches(&self.sess.parse_sess, Some(self.tcx.features())) {
                 return;
@@ -1247,7 +1245,7 @@ impl<'a, 'hir, 'tcx> HirCollector<'a, 'hir, 'tcx> {
                 self.collector.enable_per_target_ignores,
                 Some(&crate::html::markdown::ExtraInfo::new(
                     self.tcx,
-                    hir_id,
+                    def_id.to_def_id(),
                     span_of_attrs(&attrs).unwrap_or(sp),
                 )),
             );
@@ -1276,37 +1274,37 @@ impl<'a, 'hir, 'tcx> intravisit::Visitor<'hir> for HirCollector<'a, 'hir, 'tcx>
             _ => item.ident.to_string(),
         };
 
-        self.visit_testable(name, item.hir_id(), item.span, |this| {
+        self.visit_testable(name, item.owner_id.def_id, item.span, |this| {
             intravisit::walk_item(this, item);
         });
     }
 
     fn visit_trait_item(&mut self, item: &'hir hir::TraitItem<'_>) {
-        self.visit_testable(item.ident.to_string(), item.hir_id(), item.span, |this| {
+        self.visit_testable(item.ident.to_string(), item.owner_id.def_id, item.span, |this| {
             intravisit::walk_trait_item(this, item);
         });
     }
 
     fn visit_impl_item(&mut self, item: &'hir hir::ImplItem<'_>) {
-        self.visit_testable(item.ident.to_string(), item.hir_id(), item.span, |this| {
+        self.visit_testable(item.ident.to_string(), item.owner_id.def_id, item.span, |this| {
             intravisit::walk_impl_item(this, item);
         });
     }
 
     fn visit_foreign_item(&mut self, item: &'hir hir::ForeignItem<'_>) {
-        self.visit_testable(item.ident.to_string(), item.hir_id(), item.span, |this| {
+        self.visit_testable(item.ident.to_string(), item.owner_id.def_id, item.span, |this| {
             intravisit::walk_foreign_item(this, item);
         });
     }
 
     fn visit_variant(&mut self, v: &'hir hir::Variant<'_>) {
-        self.visit_testable(v.ident.to_string(), v.hir_id, v.span, |this| {
+        self.visit_testable(v.ident.to_string(), v.def_id, v.span, |this| {
             intravisit::walk_variant(this, v);
         });
     }
 
     fn visit_field_def(&mut self, f: &'hir hir::FieldDef<'_>) {
-        self.visit_testable(f.ident.to_string(), f.hir_id, f.span, |this| {
+        self.visit_testable(f.ident.to_string(), f.def_id, f.span, |this| {
             intravisit::walk_field_def(this, f);
         });
     }
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index 4ff67fe1551dd..33bff01b64fed 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -27,7 +27,6 @@
 
 use rustc_data_structures::fx::FxHashMap;
 use rustc_hir::def_id::DefId;
-use rustc_hir::HirId;
 use rustc_middle::ty::TyCtxt;
 use rustc_span::edition::Edition;
 use rustc_span::{Span, Symbol};
@@ -784,45 +783,26 @@ pub(crate) fn find_testable_code<T: doctest::Tester>(
 }
 
 pub(crate) struct ExtraInfo<'tcx> {
-    id: ExtraInfoId,
+    def_id: DefId,
     sp: Span,
     tcx: TyCtxt<'tcx>,
 }
 
-enum ExtraInfoId {
-    Hir(HirId),
-    Def(DefId),
-}
-
 impl<'tcx> ExtraInfo<'tcx> {
-    pub(crate) fn new(tcx: TyCtxt<'tcx>, hir_id: HirId, sp: Span) -> ExtraInfo<'tcx> {
-        ExtraInfo { id: ExtraInfoId::Hir(hir_id), sp, tcx }
-    }
-
-    pub(crate) fn new_did(tcx: TyCtxt<'tcx>, did: DefId, sp: Span) -> ExtraInfo<'tcx> {
-        ExtraInfo { id: ExtraInfoId::Def(did), sp, tcx }
+    pub(crate) fn new(tcx: TyCtxt<'tcx>, def_id: DefId, sp: Span) -> ExtraInfo<'tcx> {
+        ExtraInfo { def_id, sp, tcx }
     }
 
     fn error_invalid_codeblock_attr(&self, msg: &str, help: &str) {
-        let hir_id = match self.id {
-            ExtraInfoId::Hir(hir_id) => hir_id,
-            ExtraInfoId::Def(item_did) => {
-                match item_did.as_local() {
-                    Some(item_did) => self.tcx.hir().local_def_id_to_hir_id(item_did),
-                    None => {
-                        // If non-local, no need to check anything.
-                        return;
-                    }
-                }
-            }
-        };
-        self.tcx.struct_span_lint_hir(
-            crate::lint::INVALID_CODEBLOCK_ATTRIBUTES,
-            hir_id,
-            self.sp,
-            msg,
-            |lint| lint.help(help),
-        );
+        if let Some(def_id) = self.def_id.as_local() {
+            self.tcx.struct_span_lint_hir(
+                crate::lint::INVALID_CODEBLOCK_ATTRIBUTES,
+                self.tcx.hir().local_def_id_to_hir_id(def_id),
+                self.sp,
+                msg,
+                |lint| lint.help(help),
+            );
+        }
     }
 }
 
diff --git a/src/librustdoc/passes/calculate_doc_coverage.rs b/src/librustdoc/passes/calculate_doc_coverage.rs
index 02b2278960869..0b22f943dab99 100644
--- a/src/librustdoc/passes/calculate_doc_coverage.rs
+++ b/src/librustdoc/passes/calculate_doc_coverage.rs
@@ -216,13 +216,7 @@ impl<'a, 'b> DocVisitor for CoverageCalculator<'a, 'b> {
                 );
 
                 let has_doc_example = tests.found_tests != 0;
-                // The `expect_def_id()` should be okay because `local_def_id_to_hir_id`
-                // would presumably panic if a fake `DefIndex` were passed.
-                let hir_id = self
-                    .ctx
-                    .tcx
-                    .hir()
-                    .local_def_id_to_hir_id(i.item_id.expect_def_id().expect_local());
+                let hir_id = DocContext::as_local_hir_id(self.ctx.tcx, i.item_id).unwrap();
                 let (level, source) = self.ctx.tcx.lint_level_at_node(MISSING_DOCS, hir_id);
 
                 // In case we have:
diff --git a/src/librustdoc/passes/check_doc_test_visibility.rs b/src/librustdoc/passes/check_doc_test_visibility.rs
index 6aa2dda980cf3..f3961d5017ef4 100644
--- a/src/librustdoc/passes/check_doc_test_visibility.rs
+++ b/src/librustdoc/passes/check_doc_test_visibility.rs
@@ -14,8 +14,8 @@ use crate::visit::DocVisitor;
 use crate::visit_ast::inherits_doc_hidden;
 use rustc_hir as hir;
 use rustc_middle::lint::LintLevelSource;
+use rustc_middle::ty::DefIdTree;
 use rustc_session::lint;
-use rustc_span::symbol::sym;
 
 pub(crate) const CHECK_DOC_TEST_VISIBILITY: Pass = Pass {
     name: "check_doc_test_visibility",
@@ -79,11 +79,11 @@ pub(crate) fn should_have_doc_example(cx: &DocContext<'_>, item: &clean::Item) -
 
     // The `expect_def_id()` should be okay because `local_def_id_to_hir_id`
     // would presumably panic if a fake `DefIndex` were passed.
-    let hir_id = cx.tcx.hir().local_def_id_to_hir_id(item.item_id.expect_def_id().expect_local());
+    let def_id = item.item_id.expect_def_id().expect_local();
 
     // check if parent is trait impl
-    if let Some(parent_hir_id) = cx.tcx.hir().opt_parent_id(hir_id) {
-        if let Some(parent_node) = cx.tcx.hir().find(parent_hir_id) {
+    if let Some(parent_def_id) = cx.tcx.opt_local_parent(def_id) {
+        if let Some(parent_node) = cx.tcx.hir().find_by_def_id(parent_def_id) {
             if matches!(
                 parent_node,
                 hir::Node::Item(hir::Item {
@@ -96,13 +96,16 @@ pub(crate) fn should_have_doc_example(cx: &DocContext<'_>, item: &clean::Item) -
         }
     }
 
-    if cx.tcx.hir().attrs(hir_id).lists(sym::doc).has_word(sym::hidden)
-        || inherits_doc_hidden(cx.tcx, hir_id)
-        || cx.tcx.hir().span(hir_id).in_derive_expansion()
+    if cx.tcx.is_doc_hidden(def_id.to_def_id())
+        || inherits_doc_hidden(cx.tcx, def_id)
+        || cx.tcx.def_span(def_id.to_def_id()).in_derive_expansion()
     {
         return false;
     }
-    let (level, source) = cx.tcx.lint_level_at_node(crate::lint::MISSING_DOC_CODE_EXAMPLES, hir_id);
+    let (level, source) = cx.tcx.lint_level_at_node(
+        crate::lint::MISSING_DOC_CODE_EXAMPLES,
+        cx.tcx.hir().local_def_id_to_hir_id(def_id),
+    );
     level != lint::Level::Allow || matches!(source, LintLevelSource::Default)
 }
 
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index 075951312a639..e42921c080945 100644
--- a/src/librustdoc/passes/collect_intra_doc_links.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links.rs
@@ -1194,14 +1194,9 @@ impl LinkCollector<'_, '_> {
             }
 
         // item can be non-local e.g. when using #[doc(primitive = "pointer")]
-        if let Some((src_id, dst_id)) = id
-            .as_local()
-            // The `expect_def_id()` should be okay because `local_def_id_to_hir_id`
-            // would presumably panic if a fake `DefIndex` were passed.
-            .and_then(|dst_id| {
-                item.item_id.expect_def_id().as_local().map(|src_id| (src_id, dst_id))
-            })
-        {
+        if let Some((src_id, dst_id)) = id.as_local().and_then(|dst_id| {
+            item.item_id.expect_def_id().as_local().map(|src_id| (src_id, dst_id))
+        }) {
             if self.cx.tcx.effective_visibilities(()).is_exported(src_id)
                 && !self.cx.tcx.effective_visibilities(()).is_exported(dst_id)
             {
diff --git a/src/librustdoc/passes/lint/check_code_block_syntax.rs b/src/librustdoc/passes/lint/check_code_block_syntax.rs
index 7158355ffdacc..03be5e7997167 100644
--- a/src/librustdoc/passes/lint/check_code_block_syntax.rs
+++ b/src/librustdoc/passes/lint/check_code_block_syntax.rs
@@ -19,8 +19,7 @@ use crate::passes::source_span_for_markdown_range;
 pub(crate) fn visit_item(cx: &DocContext<'_>, item: &clean::Item) {
     if let Some(dox) = &item.attrs.collapsed_doc_value() {
         let sp = item.attr_span(cx.tcx);
-        let extra =
-            crate::html::markdown::ExtraInfo::new_did(cx.tcx, item.item_id.expect_def_id(), sp);
+        let extra = crate::html::markdown::ExtraInfo::new(cx.tcx, item.item_id.expect_def_id(), sp);
         for code_block in markdown::rust_code_blocks(dox, &extra) {
             check_rust_syntax(cx, item, dox, code_block);
         }
@@ -73,7 +72,6 @@ fn check_rust_syntax(
             return;
         };
 
-    let hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_id);
     let empty_block = code_block.lang_string == Default::default() && code_block.is_fenced;
     let is_ignore = code_block.lang_string.ignore != markdown::Ignore::None;
 
@@ -93,6 +91,7 @@ fn check_rust_syntax(
     // Finally build and emit the completed diagnostic.
     // All points of divergence have been handled earlier so this can be
     // done the same way whether the span is precise or not.
+    let hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_id);
     cx.tcx.struct_span_lint_hir(crate::lint::INVALID_RUST_CODEBLOCKS, hir_id, sp, msg, |lint| {
         let explanation = if is_ignore {
             "`ignore` code blocks require valid Rust code for syntax highlighting; \
diff --git a/src/librustdoc/passes/propagate_doc_cfg.rs b/src/librustdoc/passes/propagate_doc_cfg.rs
index de3a4b3390595..a4bc486900b3e 100644
--- a/src/librustdoc/passes/propagate_doc_cfg.rs
+++ b/src/librustdoc/passes/propagate_doc_cfg.rs
@@ -9,6 +9,7 @@ use crate::fold::DocFolder;
 use crate::passes::Pass;
 
 use rustc_hir::def_id::LocalDefId;
+use rustc_middle::ty::DefIdTree;
 
 pub(crate) const PROPAGATE_DOC_CFG: Pass = Pass {
     name: "propagate-doc-cfg",
@@ -41,24 +42,22 @@ impl<'a, 'tcx> CfgPropagator<'a, 'tcx> {
         let Some(def_id) = item.item_id.as_def_id().and_then(|def_id| def_id.as_local())
             else { return };
 
-        let hir = self.cx.tcx.hir();
-        let hir_id = hir.local_def_id_to_hir_id(def_id);
-
         if check_parent {
-            let expected_parent = hir.get_parent_item(hir_id);
+            let expected_parent = self.cx.tcx.opt_local_parent(def_id);
             // If parents are different, it means that `item` is a reexport and we need
             // to compute the actual `cfg` by iterating through its "real" parents.
-            if self.parent == Some(expected_parent.def_id) {
+            if self.parent.is_some() && self.parent == expected_parent {
                 return;
             }
         }
 
         let mut attrs = Vec::new();
-        for (parent_hir_id, _) in hir.parent_iter(hir_id) {
-            if let Some(def_id) = hir.opt_local_def_id(parent_hir_id) {
-                attrs.extend_from_slice(load_attrs(self.cx, def_id.to_def_id()));
-            }
+        let mut next_def_id = def_id;
+        while let Some(parent_def_id) = self.cx.tcx.opt_local_parent(next_def_id) {
+            attrs.extend_from_slice(load_attrs(self.cx, parent_def_id.to_def_id()));
+            next_def_id = parent_def_id;
         }
+
         let (_, cfg) = merge_attrs(self.cx, None, item.attrs.other_attrs.as_slice(), Some(&attrs));
         item.cfg = cfg;
     }
diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs
index 00ea6ca4152c8..a89d6fa83983d 100644
--- a/src/librustdoc/visit_ast.rs
+++ b/src/librustdoc/visit_ast.rs
@@ -4,9 +4,9 @@
 use rustc_data_structures::fx::FxHashSet;
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
-use rustc_hir::def_id::{DefId, DefIdMap};
-use rustc_hir::{HirIdSet, Node, CRATE_HIR_ID};
-use rustc_middle::ty::TyCtxt;
+use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId, LocalDefIdSet};
+use rustc_hir::{Node, CRATE_HIR_ID};
+use rustc_middle::ty::{DefIdTree, TyCtxt};
 use rustc_span::def_id::{CRATE_DEF_ID, LOCAL_CRATE};
 use rustc_span::symbol::{kw, sym, Symbol};
 use rustc_span::Span;
@@ -23,19 +23,26 @@ pub(crate) struct Module<'hir> {
     pub(crate) name: Symbol,
     pub(crate) where_inner: Span,
     pub(crate) mods: Vec<Module<'hir>>,
-    pub(crate) id: hir::HirId,
+    pub(crate) def_id: LocalDefId,
     // (item, renamed, import_id)
-    pub(crate) items: Vec<(&'hir hir::Item<'hir>, Option<Symbol>, Option<hir::HirId>)>,
+    pub(crate) items: Vec<(&'hir hir::Item<'hir>, Option<Symbol>, Option<LocalDefId>)>,
     pub(crate) foreigns: Vec<(&'hir hir::ForeignItem<'hir>, Option<Symbol>)>,
 }
 
 impl Module<'_> {
-    pub(crate) fn new(name: Symbol, id: hir::HirId, where_inner: Span) -> Self {
-        Module { name, id, where_inner, mods: Vec::new(), items: Vec::new(), foreigns: Vec::new() }
+    pub(crate) fn new(name: Symbol, def_id: LocalDefId, where_inner: Span) -> Self {
+        Module {
+            name,
+            def_id,
+            where_inner,
+            mods: Vec::new(),
+            items: Vec::new(),
+            foreigns: Vec::new(),
+        }
     }
 
     pub(crate) fn where_outer(&self, tcx: TyCtxt<'_>) -> Span {
-        tcx.hir().span(self.id)
+        tcx.def_span(self.def_id)
     }
 }
 
@@ -46,10 +53,10 @@ fn def_id_to_path(tcx: TyCtxt<'_>, did: DefId) -> Vec<Symbol> {
     std::iter::once(crate_name).chain(relative).collect()
 }
 
-pub(crate) fn inherits_doc_hidden(tcx: TyCtxt<'_>, mut node: hir::HirId) -> bool {
-    while let Some(id) = tcx.hir().get_enclosing_scope(node) {
+pub(crate) fn inherits_doc_hidden(tcx: TyCtxt<'_>, mut node: LocalDefId) -> bool {
+    while let Some(id) = tcx.opt_local_parent(node) {
         node = id;
-        if tcx.hir().attrs(node).lists(sym::doc).has_word(sym::hidden) {
+        if tcx.is_doc_hidden(node.to_def_id()) {
             return true;
         }
     }
@@ -61,7 +68,7 @@ pub(crate) fn inherits_doc_hidden(tcx: TyCtxt<'_>, mut node: hir::HirId) -> bool
 
 pub(crate) struct RustdocVisitor<'a, 'tcx> {
     cx: &'a mut core::DocContext<'tcx>,
-    view_item_stack: HirIdSet,
+    view_item_stack: LocalDefIdSet,
     inlining: bool,
     /// Are the current module and all of its parents public?
     inside_public_path: bool,
@@ -71,8 +78,8 @@ pub(crate) struct RustdocVisitor<'a, 'tcx> {
 impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
     pub(crate) fn new(cx: &'a mut core::DocContext<'tcx>) -> RustdocVisitor<'a, 'tcx> {
         // If the root is re-exported, terminate all recursion.
-        let mut stack = HirIdSet::default();
-        stack.insert(hir::CRATE_HIR_ID);
+        let mut stack = LocalDefIdSet::default();
+        stack.insert(CRATE_DEF_ID);
         RustdocVisitor {
             cx,
             view_item_stack: stack,
@@ -89,7 +96,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
 
     pub(crate) fn visit(mut self) -> Module<'tcx> {
         let mut top_level_module = self.visit_mod_contents(
-            hir::CRATE_HIR_ID,
+            CRATE_DEF_ID,
             self.cx.tcx.hir().root_module(),
             self.cx.tcx.crate_name(LOCAL_CRATE),
             None,
@@ -152,16 +159,15 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
 
     fn visit_mod_contents(
         &mut self,
-        id: hir::HirId,
+        def_id: LocalDefId,
         m: &'tcx hir::Mod<'tcx>,
         name: Symbol,
-        parent_id: Option<hir::HirId>,
+        parent_id: Option<LocalDefId>,
     ) -> Module<'tcx> {
-        let mut om = Module::new(name, id, m.spans.inner_span);
-        let def_id = self.cx.tcx.hir().local_def_id(id).to_def_id();
+        let mut om = Module::new(name, def_id, m.spans.inner_span);
         // Keep track of if there were any private modules in the path.
         let orig_inside_public_path = self.inside_public_path;
-        self.inside_public_path &= self.cx.tcx.visibility(def_id).is_public();
+        self.inside_public_path &= self.cx.tcx.local_visibility(def_id).is_public();
         for &i in m.item_ids {
             let item = self.cx.tcx.hir().item(i);
             if matches!(item.kind, hir::ItemKind::Use(_, hir::UseKind::Glob)) {
@@ -193,7 +199,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
     /// Returns `true` if the target has been inlined.
     fn maybe_inline_local(
         &mut self,
-        id: hir::HirId,
+        def_id: LocalDefId,
         res: Res,
         renamed: Option<Symbol>,
         glob: bool,
@@ -211,10 +217,10 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
             return false;
         };
 
-        let use_attrs = tcx.hir().attrs(id);
+        let use_attrs = tcx.hir().attrs(tcx.hir().local_def_id_to_hir_id(def_id));
         // Don't inline `doc(hidden)` imports so they can be stripped at a later stage.
         let is_no_inline = use_attrs.lists(sym::doc).has_word(sym::no_inline)
-            || use_attrs.lists(sym::doc).has_word(sym::hidden);
+            || tcx.is_doc_hidden(def_id.to_def_id());
 
         // For cross-crate impl inlining we need to know whether items are
         // reachable in documentation -- a previously unreachable item can be
@@ -225,37 +231,39 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
             return false;
         }
 
-        let res_hir_id = match res_did.as_local() {
-            Some(n) => tcx.hir().local_def_id_to_hir_id(n),
-            None => return false,
+        let Some(res_did) = res_did.as_local() else {
+            return false;
         };
 
-        let is_private =
-            !self.cx.cache.effective_visibilities.is_directly_public(self.cx.tcx, res_did);
-        let is_hidden = inherits_doc_hidden(self.cx.tcx, res_hir_id);
+        let is_private = !self
+            .cx
+            .cache
+            .effective_visibilities
+            .is_directly_public(self.cx.tcx, res_did.to_def_id());
+        let is_hidden = inherits_doc_hidden(self.cx.tcx, res_did);
 
         // Only inline if requested or if the item would otherwise be stripped.
         if (!please_inline && !is_private && !is_hidden) || is_no_inline {
             return false;
         }
 
-        if !self.view_item_stack.insert(res_hir_id) {
+        if !self.view_item_stack.insert(res_did) {
             return false;
         }
 
-        let ret = match tcx.hir().get(res_hir_id) {
+        let ret = match tcx.hir().get_by_def_id(res_did) {
             Node::Item(&hir::Item { kind: hir::ItemKind::Mod(ref m), .. }) if glob => {
                 let prev = mem::replace(&mut self.inlining, true);
                 for &i in m.item_ids {
                     let i = self.cx.tcx.hir().item(i);
-                    self.visit_item(i, None, om, Some(id));
+                    self.visit_item(i, None, om, Some(def_id));
                 }
                 self.inlining = prev;
                 true
             }
             Node::Item(it) if !glob => {
                 let prev = mem::replace(&mut self.inlining, true);
-                self.visit_item(it, renamed, om, Some(id));
+                self.visit_item(it, renamed, om, Some(def_id));
                 self.inlining = prev;
                 true
             }
@@ -267,7 +275,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
             }
             _ => false,
         };
-        self.view_item_stack.remove(&res_hir_id);
+        self.view_item_stack.remove(&res_did);
         ret
     }
 
@@ -276,7 +284,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
         item: &'tcx hir::Item<'_>,
         renamed: Option<Symbol>,
         om: &mut Module<'tcx>,
-        parent_id: Option<hir::HirId>,
+        parent_id: Option<LocalDefId>,
     ) {
         debug!("visiting item {:?}", item);
         let name = renamed.unwrap_or(item.ident.name);
@@ -321,7 +329,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
                         let is_glob = kind == hir::UseKind::Glob;
                         let ident = if is_glob { None } else { Some(name) };
                         if self.maybe_inline_local(
-                            item.hir_id(),
+                            item.owner_id.def_id,
                             res,
                             ident,
                             is_glob,
@@ -356,7 +364,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
                 }
             }
             hir::ItemKind::Mod(ref m) => {
-                om.mods.push(self.visit_mod_contents(item.hir_id(), m, name, parent_id));
+                om.mods.push(self.visit_mod_contents(item.owner_id.def_id, m, name, parent_id));
             }
             hir::ItemKind::Fn(..)
             | hir::ItemKind::ExternCrate(..)

From 51df99f3c2bac3ee7158b115ff6d54b687d018e1 Mon Sep 17 00:00:00 2001
From: Michael Howell <michael@notriddle.com>
Date: Tue, 24 Jan 2023 15:39:59 -0700
Subject: [PATCH 09/11] rustdoc: use smarter encoding for playground URL

The old way would compress okay with DEFLATE, but this version makes
uncompressed docs smaller, which matters for memory usage and stuff
like `cargo doc`.

Try it out: <https://play.rust-lang.org/?code=fn+main()+{%0Alet+mut+v+=+Vec::new();%0Av.push(1+/+1);%0Aprintln!(%22{}%22,+v[0]);%0A}>

In local testing, this change shrinks sample pages by anywhere between
4.0% and 0.031%

    $ du -b after.dir/std/vec/struct.Vec.html before.dir/std/vec/struct.Vec.html
    759235  after.dir/std/vec/struct.Vec.html
    781842  before.dir/std/vec/struct.Vec.html

100*((759235-781842)/781842)=-2.8

    $ du -b after.dir/std/num/struct.Wrapping.html before.dir/std/num/struct.Wrapping.html
    3194173 after.dir/std/num/struct.Wrapping.html
    3204351 before.dir/std/num/struct.Wrapping.html

100*((3194173-3204351)/3204351)=-0.031

    $ du -b after.dir/std/keyword.match.html before.dir/std/keyword.match.html
    8151    after.dir/std/keyword.match.html
    8495    before.dir/std/keyword.match.html

100*((8151-8495)/8495)=-4.0

Gzipped tarball sizes seem shrunk, but not by much.

    du -s before.tar.gz after.tar.gz
    69600   before.tar.gz
    69480   after.tar.gz

100*((69480-69600)/69600)=-0.17
---
 src/librustdoc/html/markdown.rs | 19 ++++++++++++++++++-
 tests/rustdoc/playground-arg.rs |  2 +-
 tests/rustdoc/playground.rs     |  6 +++---
 3 files changed, 22 insertions(+), 5 deletions(-)

diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index 4ff67fe1551dd..b1efbf4bdcaf7 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -296,7 +296,9 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> {
             let channel = if test.contains("#![feature(") { "&amp;version=nightly" } else { "" };
 
             // These characters don't need to be escaped in a URI.
-            // FIXME: use a library function for percent encoding.
+            // See https://url.spec.whatwg.org/#query-percent-encode-set
+            // and https://url.spec.whatwg.org/#urlencoded-parsing
+            // and https://url.spec.whatwg.org/#url-code-points
             fn dont_escape(c: u8) -> bool {
                 (b'a' <= c && c <= b'z')
                     || (b'A' <= c && c <= b'Z')
@@ -304,17 +306,32 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> {
                     || c == b'-'
                     || c == b'_'
                     || c == b'.'
+                    || c == b','
                     || c == b'~'
                     || c == b'!'
                     || c == b'\''
                     || c == b'('
                     || c == b')'
                     || c == b'*'
+                    || c == b'/'
+                    || c == b';'
+                    || c == b':'
+                    || c == b'?'
+                    // As described in urlencoded-parsing, the
+                    // first `=` is the one that separates key from
+                    // value. Following `=`s are part of the value.
+                    || c == b'='
             }
             let mut test_escaped = String::new();
             for b in test.bytes() {
                 if dont_escape(b) {
                     test_escaped.push(char::from(b));
+                } else if b == b' ' {
+                    // URL queries are decoded with + replaced with SP
+                    test_escaped.push('+');
+                } else if b == b'%' {
+                    test_escaped.push('%');
+                    test_escaped.push('%');
                 } else {
                     write!(test_escaped, "%{:02X}", b).unwrap();
                 }
diff --git a/tests/rustdoc/playground-arg.rs b/tests/rustdoc/playground-arg.rs
index 69c8962653931..f3811fe0b0ad1 100644
--- a/tests/rustdoc/playground-arg.rs
+++ b/tests/rustdoc/playground-arg.rs
@@ -10,4 +10,4 @@
 pub fn dummy() {}
 
 // ensure that `extern crate foo;` was inserted into code snips automatically:
-// @matches foo/index.html '//a[@class="test-arrow"][@href="https://example.com/?code=%23!%5Ballow(unused)%5D%0Aextern%20crate%20r%23foo%3B%0Afn%20main()%20%7B%0Ause%20foo%3A%3Adummy%3B%0Adummy()%3B%0A%7D&edition=2015"]' "Run"
+// @matches foo/index.html '//a[@class="test-arrow"][@href="https://example.com/?code=%23!%5Ballow(unused)%5D%0Aextern+crate+r%23foo;%0Afn+main()+%7B%0Ause+foo::dummy;%0Adummy();%0A%7D&edition=2015"]' "Run"
diff --git a/tests/rustdoc/playground.rs b/tests/rustdoc/playground.rs
index 877ea1cfba15a..5c7fa33efc5e5 100644
--- a/tests/rustdoc/playground.rs
+++ b/tests/rustdoc/playground.rs
@@ -22,6 +22,6 @@
 //! }
 //! ```
 
-// @matches foo/index.html '//a[@class="test-arrow"][@href="https://www.example.com/?code=%23!%5Ballow(unused)%5D%0Afn%20main()%20%7B%0A%20%20%20%20println!(%22Hello%2C%20world!%22)%3B%0A%7D&edition=2015"]' "Run"
-// @matches foo/index.html '//a[@class="test-arrow"][@href="https://www.example.com/?code=%23!%5Ballow(unused)%5D%0Afn%20main()%20%7B%0Aprintln!(%22Hello%2C%20world!%22)%3B%0A%7D&edition=2015"]' "Run"
-// @matches foo/index.html '//a[@class="test-arrow"][@href="https://www.example.com/?code=%23!%5Ballow(unused)%5D%0A%23!%5Bfeature(something)%5D%0A%0Afn%20main()%20%7B%0A%20%20%20%20println!(%22Hello%2C%20world!%22)%3B%0A%7D&version=nightly&edition=2015"]' "Run"
+// @matches foo/index.html '//a[@class="test-arrow"][@href="https://www.example.com/?code=%23!%5Ballow(unused)%5D%0Afn+main()+%7B%0Aprintln!(%22Hello,+world!%22);%0A%7D&edition=2015"]' "Run"
+// @matches foo/index.html '//a[@class="test-arrow"][@href="https://www.example.com/?code=%23!%5Ballow(unused)%5D%0Afn+main()+%7B%0A++++println!(%22Hello,+world!%22);%0A%7D&edition=2015"]' "Run"
+// @matches foo/index.html '//a[@class="test-arrow"][@href="https://www.example.com/?code=%23!%5Ballow(unused)%5D%0A%23!%5Bfeature(something)%5D%0A%0Afn+main()+%7B%0A++++println!(%22Hello,+world!%22);%0A%7D&version=nightly&edition=2015"]' "Run"

From 97f8189614a5a6c52382e735dda55400c97cd8af Mon Sep 17 00:00:00 2001
From: Michael Howell <michael@notriddle.com>
Date: Thu, 26 Jan 2023 12:50:14 -0700
Subject: [PATCH 10/11] rustdoc: remove mostly-unused CSS classes
 import/module-item

---
 src/librustdoc/html/render/print_item.rs    | 4 ++--
 src/librustdoc/html/static/css/rustdoc.css  | 3 +--
 tests/rustdoc-gui/label-next-to-symbol.goml | 8 ++++----
 tests/rustdoc-gui/module-items-font.goml    | 2 +-
 tests/rustdoc/cfg_doc_reexport.rs           | 4 ++--
 tests/rustdoc/deprecated.rs                 | 2 +-
 tests/rustdoc/doc-cfg.rs                    | 6 +++---
 tests/rustdoc/duplicate-cfg.rs              | 4 ++--
 tests/rustdoc/glob-shadowing.rs             | 2 +-
 tests/rustdoc/inline_cross/macros.rs        | 4 ++--
 tests/rustdoc/issue-32374.rs                | 4 ++--
 tests/rustdoc/issue-55364.rs                | 4 ++--
 tests/rustdoc/issue-95873.rs                | 2 +-
 tests/rustdoc/reexport-check.rs             | 4 ++--
 14 files changed, 26 insertions(+), 27 deletions(-)

diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index f824c9e3ad2bd..61fba4ecddddf 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -391,7 +391,7 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items:
                 };
                 write!(
                     w,
-                    "<div class=\"item-left {stab}{add}import-item\"{id}>\
+                    "<div class=\"item-left{add}{stab}\"{id}>\
                          <code>{vis}{imp}</code>\
                      </div>\
                      {stab_tags_before}{stab_tags}{stab_tags_after}",
@@ -437,7 +437,7 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items:
                 };
                 write!(
                     w,
-                    "<div class=\"item-left {stab}{add}module-item\">\
+                    "<div class=\"item-left{add}{stab}\">\
                         <a class=\"{class}\" href=\"{href}\" title=\"{title}\">{name}</a>\
                         {visibility_emoji}\
                         {unsafety_flag}\
diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css
index bf83ff2044e69..8699508e43916 100644
--- a/src/librustdoc/html/static/css/rustdoc.css
+++ b/src/librustdoc/html/static/css/rustdoc.css
@@ -977,8 +977,7 @@ so that we can apply CSS-filters to change the arrow color in themes */
 		0 -1px 0 black;
 }
 
-.module-item.unstable,
-.import-item.unstable {
+.item-left.unstable {
 	opacity: 0.65;
 }
 
diff --git a/tests/rustdoc-gui/label-next-to-symbol.goml b/tests/rustdoc-gui/label-next-to-symbol.goml
index 05f8ddc716e87..3f4f65890b422 100644
--- a/tests/rustdoc-gui/label-next-to-symbol.goml
+++ b/tests/rustdoc-gui/label-next-to-symbol.goml
@@ -20,7 +20,7 @@ assert-css: (
 // table like view
 assert-css: (".item-right.docblock-short", { "padding-left": "0px" })
 compare-elements-position-near: (
-    "//*[@class='item-left module-item']//a[text()='replaced_function']",
+    "//*[@class='item-left']//a[text()='replaced_function']",
     ".item-left .stab.deprecated",
     {"y": 2},
 )
@@ -32,7 +32,7 @@ compare-elements-position: (
 
 // Ensure no wrap
 compare-elements-position: (
-    "//*[@class='item-left module-item']//a[text()='replaced_function']/..",
+    "//*[@class='item-left']//a[text()='replaced_function']/..",
     "//*[@class='item-right docblock-short'][text()='a thing with a label']",
     ("y"),
 )
@@ -43,7 +43,7 @@ size: (600, 600)
 // staggered layout with 2em spacing
 assert-css: (".item-right.docblock-short", { "padding-left": "32px" })
 compare-elements-position-near: (
-    "//*[@class='item-left module-item']//a[text()='replaced_function']",
+    "//*[@class='item-left']//a[text()='replaced_function']",
     ".item-left .stab.deprecated",
     {"y": 2},
 )
@@ -55,7 +55,7 @@ compare-elements-position: (
 
 // Ensure wrap
 compare-elements-position-false: (
-    "//*[@class='item-left module-item']//a[text()='replaced_function']/..",
+    "//*[@class='item-left']//a[text()='replaced_function']/..",
     "//*[@class='item-right docblock-short'][text()='a thing with a label']",
     ("y"),
 )
diff --git a/tests/rustdoc-gui/module-items-font.goml b/tests/rustdoc-gui/module-items-font.goml
index cd3676a987138..5940962a8ddba 100644
--- a/tests/rustdoc-gui/module-items-font.goml
+++ b/tests/rustdoc-gui/module-items-font.goml
@@ -1,7 +1,7 @@
 // This test checks that the correct font is used on module items (in index.html pages).
 goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
 assert-css: (
-    ".item-table .module-item a",
+    ".item-table .item-left > a",
     {"font-family": '"Fira Sans", Arial, NanumBarunGothic, sans-serif'},
     ALL,
 )
diff --git a/tests/rustdoc/cfg_doc_reexport.rs b/tests/rustdoc/cfg_doc_reexport.rs
index addb6709db1da..89c7f0a6f342c 100644
--- a/tests/rustdoc/cfg_doc_reexport.rs
+++ b/tests/rustdoc/cfg_doc_reexport.rs
@@ -5,8 +5,8 @@
 #![no_core]
 
 // @has 'foo/index.html'
-// @has - '//*[@class="item-left module-item"]/*[@class="stab portability"]' 'foobar'
-// @has - '//*[@class="item-left module-item"]/*[@class="stab portability"]' 'bar'
+// @has - '//*[@class="item-left"]/*[@class="stab portability"]' 'foobar'
+// @has - '//*[@class="item-left"]/*[@class="stab portability"]' 'bar'
 
 #[doc(cfg(feature = "foobar"))]
 mod imp_priv {
diff --git a/tests/rustdoc/deprecated.rs b/tests/rustdoc/deprecated.rs
index b3178da98eeb2..5cbe4d59108a4 100644
--- a/tests/rustdoc/deprecated.rs
+++ b/tests/rustdoc/deprecated.rs
@@ -1,4 +1,4 @@
-// @has deprecated/index.html '//*[@class="item-left module-item"]/span[@class="stab deprecated"]' \
+// @has deprecated/index.html '//*[@class="item-left"]/span[@class="stab deprecated"]' \
 //      'Deprecated'
 // @has - '//*[@class="item-right docblock-short"]' 'Deprecated docs'
 
diff --git a/tests/rustdoc/doc-cfg.rs b/tests/rustdoc/doc-cfg.rs
index 4cddb0b76d410..1cfbfec6fcd30 100644
--- a/tests/rustdoc/doc-cfg.rs
+++ b/tests/rustdoc/doc-cfg.rs
@@ -12,7 +12,7 @@ pub struct Portable;
 // @has doc_cfg/unix_only/index.html \
 //  '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \
 //  'Available on Unix only.'
-// @matches - '//*[@class="item-left module-item"]//*[@class="stab portability"]' '\AARM\Z'
+// @matches - '//*[@class="item-left"]//*[@class="stab portability"]' '\AARM\Z'
 // @count - '//*[@class="stab portability"]' 2
 #[doc(cfg(unix))]
 pub mod unix_only {
@@ -42,7 +42,7 @@ pub mod unix_only {
 // @has doc_cfg/wasi_only/index.html \
 //  '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \
 //  'Available on WASI only.'
-// @matches - '//*[@class="item-left module-item"]//*[@class="stab portability"]' '\AWebAssembly\Z'
+// @matches - '//*[@class="item-left"]//*[@class="stab portability"]' '\AWebAssembly\Z'
 // @count - '//*[@class="stab portability"]' 2
 #[doc(cfg(target_os = "wasi"))]
 pub mod wasi_only {
@@ -74,7 +74,7 @@ pub mod wasi_only {
 
 // the portability header is different on the module view versus the full view
 // @has doc_cfg/index.html
-// @matches - '//*[@class="item-left module-item"]//*[@class="stab portability"]' '\Aavx\Z'
+// @matches - '//*[@class="item-left"]//*[@class="stab portability"]' '\Aavx\Z'
 
 // @has doc_cfg/fn.uses_target_feature.html
 // @has - '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \
diff --git a/tests/rustdoc/duplicate-cfg.rs b/tests/rustdoc/duplicate-cfg.rs
index 18f3900b263b0..1ac2e52324964 100644
--- a/tests/rustdoc/duplicate-cfg.rs
+++ b/tests/rustdoc/duplicate-cfg.rs
@@ -2,8 +2,8 @@
 #![feature(doc_cfg)]
 
 // @has 'foo/index.html'
-// @matches '-' '//*[@class="item-left module-item"]//*[@class="stab portability"]' '^sync$'
-// @has '-' '//*[@class="item-left module-item"]//*[@class="stab portability"]/@title' 'Available on crate feature `sync` only'
+// @matches '-' '//*[@class="item-left"]//*[@class="stab portability"]' '^sync$'
+// @has '-' '//*[@class="item-left"]//*[@class="stab portability"]/@title' 'Available on crate feature `sync` only'
 
 // @has 'foo/struct.Foo.html'
 // @has '-' '//*[@class="stab portability"]' 'sync'
diff --git a/tests/rustdoc/glob-shadowing.rs b/tests/rustdoc/glob-shadowing.rs
index 66a31c42bcfc7..2668b33349790 100644
--- a/tests/rustdoc/glob-shadowing.rs
+++ b/tests/rustdoc/glob-shadowing.rs
@@ -1,5 +1,5 @@
 // @has 'glob_shadowing/index.html'
-// @count - '//div[@class="item-left module-item"]' 6
+// @count - '//div[@class="item-left"]' 6
 // @!has - '//div[@class="item-right docblock-short"]' 'sub1::describe'
 // @has - '//div[@class="item-right docblock-short"]' 'sub2::describe'
 
diff --git a/tests/rustdoc/inline_cross/macros.rs b/tests/rustdoc/inline_cross/macros.rs
index 5daa0d4baad63..d5b0de5725bce 100644
--- a/tests/rustdoc/inline_cross/macros.rs
+++ b/tests/rustdoc/inline_cross/macros.rs
@@ -6,9 +6,9 @@
 
 extern crate macros;
 
-// @has foo/index.html '//*[@class="item-left unstable deprecated module-item"]/span[@class="stab deprecated"]' \
+// @has foo/index.html '//*[@class="item-left unstable deprecated"]/span[@class="stab deprecated"]' \
 //         Deprecated
-// @has - '//*[@class="item-left unstable deprecated module-item"]/span[@class="stab unstable"]' \
+// @has - '//*[@class="item-left unstable deprecated"]/span[@class="stab unstable"]' \
 //         Experimental
 
 // @has foo/macro.my_macro.html
diff --git a/tests/rustdoc/issue-32374.rs b/tests/rustdoc/issue-32374.rs
index 8d2c27cf3d77d..8296d7a81f2b1 100644
--- a/tests/rustdoc/issue-32374.rs
+++ b/tests/rustdoc/issue-32374.rs
@@ -2,9 +2,9 @@
 #![doc(issue_tracker_base_url = "https://issue_url/")]
 #![unstable(feature = "test", issue = "32374")]
 
-// @matches issue_32374/index.html '//*[@class="item-left unstable deprecated module-item"]/span[@class="stab deprecated"]' \
+// @matches issue_32374/index.html '//*[@class="item-left unstable deprecated"]/span[@class="stab deprecated"]' \
 //      'Deprecated'
-// @matches issue_32374/index.html '//*[@class="item-left unstable deprecated module-item"]/span[@class="stab unstable"]' \
+// @matches issue_32374/index.html '//*[@class="item-left unstable deprecated"]/span[@class="stab unstable"]' \
 //      'Experimental'
 // @matches issue_32374/index.html '//*[@class="item-right docblock-short"]/text()' 'Docs'
 
diff --git a/tests/rustdoc/issue-55364.rs b/tests/rustdoc/issue-55364.rs
index 14a6f5041f208..b987da30ed290 100644
--- a/tests/rustdoc/issue-55364.rs
+++ b/tests/rustdoc/issue-55364.rs
@@ -29,8 +29,8 @@ pub mod subone {
 // @has - '//section[@id="main-content"]/details/div[@class="docblock"]//a[@href="../fn.foo.html"]' 'foo'
 // @has - '//section[@id="main-content"]/details/div[@class="docblock"]//a[@href="../fn.bar.html"]' 'bar'
 // Though there should be such links later
-// @has - '//section[@id="main-content"]/div[@class="item-table"]//div[@class="item-left module-item"]/a[@class="fn"][@href="fn.foo.html"]' 'foo'
-// @has - '//section[@id="main-content"]/div[@class="item-table"]//div[@class="item-left module-item"]/a[@class="fn"][@href="fn.bar.html"]' 'bar'
+// @has - '//section[@id="main-content"]/div[@class="item-table"]//div[@class="item-left"]/a[@class="fn"][@href="fn.foo.html"]' 'foo'
+// @has - '//section[@id="main-content"]/div[@class="item-table"]//div[@class="item-left"]/a[@class="fn"][@href="fn.bar.html"]' 'bar'
 /// See either [foo] or [bar].
 pub mod subtwo {
 
diff --git a/tests/rustdoc/issue-95873.rs b/tests/rustdoc/issue-95873.rs
index ff33fb63a0bab..3df93eb7cf16f 100644
--- a/tests/rustdoc/issue-95873.rs
+++ b/tests/rustdoc/issue-95873.rs
@@ -1,2 +1,2 @@
-// @has issue_95873/index.html "//*[@class='item-left import-item']" "pub use ::std as x;"
+// @has issue_95873/index.html "//*[@class='item-left']" "pub use ::std as x;"
 pub use ::std as x;
diff --git a/tests/rustdoc/reexport-check.rs b/tests/rustdoc/reexport-check.rs
index db1f90c699978..acac0c9919716 100644
--- a/tests/rustdoc/reexport-check.rs
+++ b/tests/rustdoc/reexport-check.rs
@@ -4,12 +4,12 @@
 extern crate reexport_check;
 
 // @!has 'foo/index.html' '//code' 'pub use self::i32;'
-// @has 'foo/index.html' '//div[@class="item-left deprecated module-item"]' 'i32'
+// @has 'foo/index.html' '//div[@class="item-left deprecated"]' 'i32'
 // @has 'foo/i32/index.html'
 #[allow(deprecated, deprecated_in_future)]
 pub use std::i32;
 // @!has 'foo/index.html' '//code' 'pub use self::string::String;'
-// @has 'foo/index.html' '//div[@class="item-left module-item"]' 'String'
+// @has 'foo/index.html' '//div[@class="item-left"]' 'String'
 pub use std::string::String;
 
 // @has 'foo/index.html' '//div[@class="item-right docblock-short"]' 'Docs in original'

From 9b5a2a4a4861500d64b9f1ebbe6c1140eda0a493 Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Tue, 24 Jan 2023 21:43:21 +0000
Subject: [PATCH 11/11] Use new solver during selection

---
 .../src/traits/select/mod.rs                  | 57 ++++++++++++++-----
 1 file changed, 43 insertions(+), 14 deletions(-)

diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index f90da95d51668..1d23634b6aacf 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -38,6 +38,8 @@ use rustc_errors::Diagnostic;
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_infer::infer::LateBoundRegionConversionTime;
+use rustc_infer::traits::TraitEngine;
+use rustc_infer::traits::TraitEngineExt;
 use rustc_middle::dep_graph::{DepKind, DepNodeIndex};
 use rustc_middle::mir::interpret::ErrorHandled;
 use rustc_middle::ty::abstract_const::NotConstEvaluatable;
@@ -47,6 +49,7 @@ use rustc_middle::ty::relate::TypeRelation;
 use rustc_middle::ty::SubstsRef;
 use rustc_middle::ty::{self, EarlyBinder, PolyProjectionPredicate, ToPolyTraitRef, ToPredicate};
 use rustc_middle::ty::{Ty, TyCtxt, TypeFoldable, TypeVisitable};
+use rustc_session::config::TraitSolver;
 use rustc_span::symbol::sym;
 
 use std::cell::{Cell, RefCell};
@@ -544,10 +547,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         obligation: &PredicateObligation<'tcx>,
     ) -> Result<EvaluationResult, OverflowError> {
         self.evaluation_probe(|this| {
-            this.evaluate_predicate_recursively(
-                TraitObligationStackList::empty(&ProvisionalEvaluationCache::default()),
-                obligation.clone(),
-            )
+            if this.tcx().sess.opts.unstable_opts.trait_solver != TraitSolver::Next {
+                this.evaluate_predicate_recursively(
+                    TraitObligationStackList::empty(&ProvisionalEvaluationCache::default()),
+                    obligation.clone(),
+                )
+            } else {
+                this.evaluate_predicates_recursively_in_new_solver([obligation.clone()])
+            }
         })
     }
 
@@ -586,18 +593,40 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
     where
         I: IntoIterator<Item = PredicateObligation<'tcx>> + std::fmt::Debug,
     {
-        let mut result = EvaluatedToOk;
-        for obligation in predicates {
-            let eval = self.evaluate_predicate_recursively(stack, obligation.clone())?;
-            if let EvaluatedToErr = eval {
-                // fast-path - EvaluatedToErr is the top of the lattice,
-                // so we don't need to look on the other predicates.
-                return Ok(EvaluatedToErr);
-            } else {
-                result = cmp::max(result, eval);
+        if self.tcx().sess.opts.unstable_opts.trait_solver != TraitSolver::Next {
+            let mut result = EvaluatedToOk;
+            for obligation in predicates {
+                let eval = self.evaluate_predicate_recursively(stack, obligation.clone())?;
+                if let EvaluatedToErr = eval {
+                    // fast-path - EvaluatedToErr is the top of the lattice,
+                    // so we don't need to look on the other predicates.
+                    return Ok(EvaluatedToErr);
+                } else {
+                    result = cmp::max(result, eval);
+                }
             }
+            Ok(result)
+        } else {
+            self.evaluate_predicates_recursively_in_new_solver(predicates)
         }
-        Ok(result)
+    }
+
+    /// Evaluates the predicates using the new solver when `-Ztrait-solver=next` is enabled
+    fn evaluate_predicates_recursively_in_new_solver(
+        &mut self,
+        predicates: impl IntoIterator<Item = PredicateObligation<'tcx>>,
+    ) -> Result<EvaluationResult, OverflowError> {
+        let mut fulfill_cx = crate::solve::FulfillmentCtxt::new();
+        fulfill_cx.register_predicate_obligations(self.infcx, predicates);
+        // True errors
+        if !fulfill_cx.select_where_possible(self.infcx).is_empty() {
+            return Ok(EvaluatedToErr);
+        }
+        if !fulfill_cx.select_all_or_error(self.infcx).is_empty() {
+            return Ok(EvaluatedToAmbig);
+        }
+        // Regions and opaques are handled in the `evaluation_probe` by looking at the snapshot
+        Ok(EvaluatedToOk)
     }
 
     #[instrument(