about summary refs log tree commit diff
path: root/src/librustdoc
diff options
context:
space:
mode:
authorLaurențiu Nicola <lnicola@users.noreply.github.com>2024-12-23 10:12:23 +0000
committerGitHub <noreply@github.com>2024-12-23 10:12:23 +0000
commit8dbdcc03ebfd5d3c21671d8a021669dc79d93038 (patch)
treee61d058d30fdd35c4a306fde5be669b08092a4fd /src/librustdoc
parent63a3c394617b114a8fa6e54401700b3adee65a7d (diff)
parent0180d2d16f5f5d60384a594568a98a6e6f8eea59 (diff)
Merge pull request #18746 from lnicola/sync-from-rust
minor: Sync from downstream
Diffstat (limited to 'src/librustdoc')
-rw-r--r--src/librustdoc/clean/inline.rs22
-rw-r--r--src/librustdoc/clean/mod.rs97
-rw-r--r--src/librustdoc/clean/render_macro_matchers.rs2
-rw-r--r--src/librustdoc/clean/types.rs96
-rw-r--r--src/librustdoc/clean/utils.rs5
-rw-r--r--src/librustdoc/config.rs5
-rw-r--r--src/librustdoc/doctest.rs88
-rw-r--r--src/librustdoc/doctest/make.rs60
-rw-r--r--src/librustdoc/doctest/rust.rs2
-rw-r--r--src/librustdoc/doctest/tests.rs22
-rw-r--r--src/librustdoc/fold.rs9
-rw-r--r--src/librustdoc/formats/cache.rs24
-rw-r--r--src/librustdoc/formats/item_type.rs8
-rw-r--r--src/librustdoc/html/format.rs7
-rw-r--r--src/librustdoc/html/markdown.rs66
-rw-r--r--src/librustdoc/html/render/mod.rs79
-rw-r--r--src/librustdoc/html/render/print_item.rs12
-rw-r--r--src/librustdoc/html/render/search_index.rs17
-rw-r--r--src/librustdoc/html/render/sidebar.rs4
-rw-r--r--src/librustdoc/html/static/js/search.js7
-rw-r--r--src/librustdoc/json/conversions.rs24
-rw-r--r--src/librustdoc/lib.rs91
-rw-r--r--src/librustdoc/passes/check_doc_test_visibility.rs7
-rw-r--r--src/librustdoc/passes/propagate_stability.rs11
-rw-r--r--src/librustdoc/passes/stripper.rs9
-rw-r--r--src/librustdoc/visit.rs9
26 files changed, 497 insertions, 286 deletions
diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs
index d81c47886d2..019a888bd2f 100644
--- a/src/librustdoc/clean/inline.rs
+++ b/src/librustdoc/clean/inline.rs
@@ -4,6 +4,7 @@ use std::iter::once;
 use std::sync::Arc;
 
 use rustc_data_structures::fx::FxHashSet;
+use rustc_hir as hir;
 use rustc_hir::Mutability;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{DefId, DefIdSet, LocalDefId, LocalModDefId};
@@ -15,7 +16,6 @@ use rustc_span::hygiene::MacroKind;
 use rustc_span::symbol::{Symbol, sym};
 use thin_vec::{ThinVec, thin_vec};
 use tracing::{debug, trace};
-use {rustc_ast as ast, rustc_hir as hir};
 
 use super::Item;
 use crate::clean::{
@@ -43,7 +43,7 @@ pub(crate) fn try_inline(
     cx: &mut DocContext<'_>,
     res: Res,
     name: Symbol,
-    attrs: Option<(&[ast::Attribute], Option<LocalDefId>)>,
+    attrs: Option<(&[hir::Attribute], Option<LocalDefId>)>,
     visited: &mut DefIdSet,
 ) -> Option<Vec<clean::Item>> {
     let did = res.opt_def_id()?;
@@ -206,7 +206,7 @@ pub(crate) fn try_inline_glob(
     }
 }
 
-pub(crate) fn load_attrs<'hir>(cx: &DocContext<'hir>, did: DefId) -> &'hir [ast::Attribute] {
+pub(crate) fn load_attrs<'hir>(cx: &DocContext<'hir>, did: DefId) -> &'hir [hir::Attribute] {
     cx.tcx.get_attrs_unchecked(did)
 }
 
@@ -360,7 +360,7 @@ fn build_type_alias(
 pub(crate) fn build_impls(
     cx: &mut DocContext<'_>,
     did: DefId,
-    attrs: Option<(&[ast::Attribute], Option<LocalDefId>)>,
+    attrs: Option<(&[hir::Attribute], Option<LocalDefId>)>,
     ret: &mut Vec<clean::Item>,
 ) {
     let _prof_timer = cx.tcx.sess.prof.generic_activity("build_inherent_impls");
@@ -392,8 +392,8 @@ pub(crate) fn build_impls(
 
 pub(crate) fn merge_attrs(
     cx: &mut DocContext<'_>,
-    old_attrs: &[ast::Attribute],
-    new_attrs: Option<(&[ast::Attribute], Option<LocalDefId>)>,
+    old_attrs: &[hir::Attribute],
+    new_attrs: Option<(&[hir::Attribute], Option<LocalDefId>)>,
 ) -> (clean::Attributes, Option<Arc<clean::cfg::Cfg>>) {
     // NOTE: If we have additional attributes (from a re-export),
     // always insert them first. This ensure that re-export
@@ -404,14 +404,14 @@ pub(crate) fn merge_attrs(
         both.extend_from_slice(old_attrs);
         (
             if let Some(item_id) = item_id {
-                Attributes::from_ast_with_additional(old_attrs, (inner, item_id.to_def_id()))
+                Attributes::from_hir_with_additional(old_attrs, (inner, item_id.to_def_id()))
             } else {
-                Attributes::from_ast(&both)
+                Attributes::from_hir(&both)
             },
             both.cfg(cx.tcx, &cx.cache.hidden_cfg),
         )
     } else {
-        (Attributes::from_ast(old_attrs), old_attrs.cfg(cx.tcx, &cx.cache.hidden_cfg))
+        (Attributes::from_hir(old_attrs), old_attrs.cfg(cx.tcx, &cx.cache.hidden_cfg))
     }
 }
 
@@ -419,7 +419,7 @@ pub(crate) fn merge_attrs(
 pub(crate) fn build_impl(
     cx: &mut DocContext<'_>,
     did: DefId,
-    attrs: Option<(&[ast::Attribute], Option<LocalDefId>)>,
+    attrs: Option<(&[hir::Attribute], Option<LocalDefId>)>,
     ret: &mut Vec<clean::Item>,
 ) {
     if !cx.inlined.insert(did.into()) {
@@ -629,7 +629,7 @@ fn build_module_items(
     visited: &mut DefIdSet,
     inlined_names: &mut FxHashSet<(ItemType, Symbol)>,
     allowed_def_ids: Option<&DefIdSet>,
-    attrs: Option<(&[ast::Attribute], Option<LocalDefId>)>,
+    attrs: Option<(&[hir::Attribute], Option<LocalDefId>)>,
 ) -> Vec<clean::Item> {
     let mut items = Vec::new();
 
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 34141c47cf3..9d0c0f687c7 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -55,7 +55,7 @@ use rustc_trait_selection::traits::wf::object_region_bounds;
 use thin_vec::ThinVec;
 use tracing::{debug, instrument};
 use utils::*;
-use {rustc_ast as ast, rustc_attr as attr, rustc_hir as hir};
+use {rustc_ast as ast, rustc_hir as hir};
 
 pub(crate) use self::types::*;
 pub(crate) use self::utils::{krate, register_res, synthesize_auto_trait_and_blanket_impls};
@@ -201,7 +201,7 @@ fn generate_item_with_correct_attrs(
     };
 
     let cfg = attrs.cfg(cx.tcx, &cx.cache.hidden_cfg);
-    let attrs = Attributes::from_ast_iter(attrs.iter().map(|(attr, did)| (&**attr, *did)), false);
+    let attrs = Attributes::from_hir_iter(attrs.iter().map(|(attr, did)| (&**attr, *did)), false);
 
     let name = renamed.or(Some(name));
     let mut item = Item::from_def_id_and_attrs_and_parts(def_id, name, kind, attrs, cfg);
@@ -1036,7 +1036,7 @@ fn clean_fn_or_proc_macro<'tcx>(
 /// This is needed to make it more "readable" when documenting functions using
 /// `rustc_legacy_const_generics`. More information in
 /// <https://github.com/rust-lang/rust/issues/83167>.
-fn clean_fn_decl_legacy_const_generics(func: &mut Function, attrs: &[ast::Attribute]) {
+fn clean_fn_decl_legacy_const_generics(func: &mut Function, attrs: &[hir::Attribute]) {
     for meta_item_list in attrs
         .iter()
         .filter(|a| a.has_name(sym::rustc_legacy_const_generics))
@@ -1222,14 +1222,16 @@ fn clean_trait_item<'tcx>(trait_item: &hir::TraitItem<'tcx>, cx: &mut DocContext
     let local_did = trait_item.owner_id.to_def_id();
     cx.with_param_env(local_did, |cx| {
         let inner = match trait_item.kind {
-            hir::TraitItemKind::Const(ty, Some(default)) => AssocConstItem(Box::new(Constant {
-                generics: enter_impl_trait(cx, |cx| clean_generics(trait_item.generics, cx)),
-                kind: ConstantKind::Local { def_id: local_did, body: default },
-                type_: clean_ty(ty, cx),
-            })),
+            hir::TraitItemKind::Const(ty, Some(default)) => {
+                ProvidedAssocConstItem(Box::new(Constant {
+                    generics: enter_impl_trait(cx, |cx| clean_generics(trait_item.generics, cx)),
+                    kind: ConstantKind::Local { def_id: local_did, body: default },
+                    type_: clean_ty(ty, cx),
+                }))
+            }
             hir::TraitItemKind::Const(ty, None) => {
                 let generics = enter_impl_trait(cx, |cx| clean_generics(trait_item.generics, cx));
-                TyAssocConstItem(generics, Box::new(clean_ty(ty, cx)))
+                RequiredAssocConstItem(generics, Box::new(clean_ty(ty, cx)))
             }
             hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => {
                 let m = clean_function(cx, sig, trait_item.generics, FunctionArgs::Body(body));
@@ -1237,7 +1239,7 @@ fn clean_trait_item<'tcx>(trait_item: &hir::TraitItem<'tcx>, cx: &mut DocContext
             }
             hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Required(names)) => {
                 let m = clean_function(cx, sig, trait_item.generics, FunctionArgs::Names(names));
-                TyMethodItem(m)
+                RequiredMethodItem(m)
             }
             hir::TraitItemKind::Type(bounds, Some(default)) => {
                 let generics = enter_impl_trait(cx, |cx| clean_generics(trait_item.generics, cx));
@@ -1257,7 +1259,7 @@ fn clean_trait_item<'tcx>(trait_item: &hir::TraitItem<'tcx>, cx: &mut DocContext
             hir::TraitItemKind::Type(bounds, None) => {
                 let generics = enter_impl_trait(cx, |cx| clean_generics(trait_item.generics, cx));
                 let bounds = bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect();
-                TyAssocTypeItem(generics, bounds)
+                RequiredAssocTypeItem(generics, bounds)
             }
         };
         Item::from_def_id_and_parts(local_did, Some(trait_item.ident.name), inner, cx)
@@ -1271,7 +1273,7 @@ pub(crate) fn clean_impl_item<'tcx>(
     let local_did = impl_.owner_id.to_def_id();
     cx.with_param_env(local_did, |cx| {
         let inner = match impl_.kind {
-            hir::ImplItemKind::Const(ty, expr) => AssocConstItem(Box::new(Constant {
+            hir::ImplItemKind::Const(ty, expr) => ImplAssocConstItem(Box::new(Constant {
                 generics: clean_generics(impl_.generics, cx),
                 kind: ConstantKind::Local { def_id: local_did, body: expr },
                 type_: clean_ty(ty, cx),
@@ -1320,18 +1322,23 @@ pub(crate) fn clean_middle_assoc_item(assoc_item: &ty::AssocItem, cx: &mut DocCo
             );
             simplify::move_bounds_to_generic_parameters(&mut generics);
 
-            let provided = match assoc_item.container {
-                ty::AssocItemContainer::Impl => true,
-                ty::AssocItemContainer::Trait => tcx.defaultness(assoc_item.def_id).has_value(),
-            };
-            if provided {
-                AssocConstItem(Box::new(Constant {
+            match assoc_item.container {
+                ty::AssocItemContainer::Impl => ImplAssocConstItem(Box::new(Constant {
                     generics,
                     kind: ConstantKind::Extern { def_id: assoc_item.def_id },
                     type_: ty,
-                }))
-            } else {
-                TyAssocConstItem(generics, Box::new(ty))
+                })),
+                ty::AssocItemContainer::Trait => {
+                    if tcx.defaultness(assoc_item.def_id).has_value() {
+                        ProvidedAssocConstItem(Box::new(Constant {
+                            generics,
+                            kind: ConstantKind::Extern { def_id: assoc_item.def_id },
+                            type_: ty,
+                        }))
+                    } else {
+                        RequiredAssocConstItem(generics, Box::new(ty))
+                    }
+                }
             }
         }
         ty::AssocKind::Fn => {
@@ -1369,7 +1376,7 @@ pub(crate) fn clean_middle_assoc_item(assoc_item: &ty::AssocItem, cx: &mut DocCo
                 };
                 MethodItem(item, defaultness)
             } else {
-                TyMethodItem(item)
+                RequiredMethodItem(item)
             }
         }
         ty::AssocKind::Type => {
@@ -1486,7 +1493,7 @@ pub(crate) fn clean_middle_assoc_item(assoc_item: &ty::AssocItem, cx: &mut DocCo
                         bounds,
                     )
                 } else {
-                    TyAssocTypeItem(generics, bounds)
+                    RequiredAssocTypeItem(generics, bounds)
                 }
             } else {
                 AssocTypeItem(
@@ -1839,11 +1846,15 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T
             DynTrait(bounds, lifetime)
         }
         TyKind::BareFn(barefn) => BareFunction(Box::new(clean_bare_fn_ty(barefn, cx))),
-        // Rustdoc handles `TyKind::Err`s by turning them into `Type::Infer`s.
-        TyKind::Infer | TyKind::Err(_) | TyKind::Typeof(..) | TyKind::InferDelegation(..) => Infer,
-        TyKind::AnonAdt(..) => {
-            unimplemented!("Anonymous structs or unions are not supported yet")
+        TyKind::UnsafeBinder(..) => {
+            unimplemented!("unsafe binders are not supported yet")
         }
+        // Rustdoc handles `TyKind::Err`s by turning them into `Type::Infer`s.
+        TyKind::Infer
+        | TyKind::Err(_)
+        | TyKind::Typeof(..)
+        | TyKind::InferDelegation(..)
+        | TyKind::TraitAscription(_) => Infer,
     }
 }
 
@@ -2574,7 +2585,7 @@ fn get_all_import_attributes<'hir>(
     import_def_id: LocalDefId,
     target_def_id: DefId,
     is_inline: bool,
-) -> Vec<(Cow<'hir, ast::Attribute>, Option<DefId>)> {
+) -> Vec<(Cow<'hir, hir::Attribute>, Option<DefId>)> {
     let mut attrs = Vec::new();
     let mut first = true;
     for def_id in reexport_chain(cx.tcx, import_def_id, target_def_id)
@@ -2600,7 +2611,7 @@ fn filter_tokens_from_list(
 ) -> Vec<TokenTree> {
     let mut tokens = Vec::with_capacity(args_tokens.len());
     let mut skip_next_comma = false;
-    for token in args_tokens.trees() {
+    for token in args_tokens.iter() {
         match token {
             TokenTree::Token(Token { kind: TokenKind::Comma, .. }, _) if skip_next_comma => {
                 skip_next_comma = false;
@@ -2627,9 +2638,9 @@ fn filter_doc_attr_ident(ident: Symbol, is_inline: bool) -> bool {
 
 /// Remove attributes from `normal` that should not be inherited by `use` re-export.
 /// Before calling this function, make sure `normal` is a `#[doc]` attribute.
-fn filter_doc_attr(normal: &mut ast::NormalAttr, is_inline: bool) {
-    match normal.item.args {
-        ast::AttrArgs::Delimited(ref mut args) => {
+fn filter_doc_attr(args: &mut hir::AttrArgs, is_inline: bool) {
+    match args {
+        hir::AttrArgs::Delimited(ref mut args) => {
             let tokens = filter_tokens_from_list(&args.tokens, |token| {
                 !matches!(
                     token,
@@ -2647,7 +2658,7 @@ fn filter_doc_attr(normal: &mut ast::NormalAttr, is_inline: bool) {
             });
             args.tokens = TokenStream::new(tokens);
         }
-        ast::AttrArgs::Empty | ast::AttrArgs::Eq { .. } => {}
+        hir::AttrArgs::Empty | hir::AttrArgs::Eq { .. } => {}
     }
 }
 
@@ -2672,23 +2683,23 @@ fn filter_doc_attr(normal: &mut ast::NormalAttr, is_inline: bool) {
 /// * `doc(no_inline)`
 /// * `doc(hidden)`
 fn add_without_unwanted_attributes<'hir>(
-    attrs: &mut Vec<(Cow<'hir, ast::Attribute>, Option<DefId>)>,
-    new_attrs: &'hir [ast::Attribute],
+    attrs: &mut Vec<(Cow<'hir, hir::Attribute>, Option<DefId>)>,
+    new_attrs: &'hir [hir::Attribute],
     is_inline: bool,
     import_parent: Option<DefId>,
 ) {
     for attr in new_attrs {
-        if matches!(attr.kind, ast::AttrKind::DocComment(..)) {
+        if matches!(attr.kind, hir::AttrKind::DocComment(..)) {
             attrs.push((Cow::Borrowed(attr), import_parent));
             continue;
         }
         let mut attr = attr.clone();
         match attr.kind {
-            ast::AttrKind::Normal(ref mut normal) => {
-                if let [ident] = &*normal.item.path.segments {
-                    let ident = ident.ident.name;
+            hir::AttrKind::Normal(ref mut normal) => {
+                if let [ident] = &*normal.path.segments {
+                    let ident = ident.name;
                     if ident == sym::doc {
-                        filter_doc_attr(normal, is_inline);
+                        filter_doc_attr(&mut normal.args, is_inline);
                         attrs.push((Cow::Owned(attr), import_parent));
                     } else if is_inline || ident != sym::cfg {
                         // If it's not a `cfg()` attribute, we keep it.
@@ -2891,7 +2902,7 @@ fn clean_extern_crate<'tcx>(
         && attrs.iter().any(|a| {
             a.has_name(sym::doc)
                 && match a.meta_item_list() {
-                    Some(l) => attr::list_contains_name(&l, sym::inline),
+                    Some(l) => ast::attr::list_contains_name(&l, sym::inline),
                     None => false,
                 }
         })
@@ -2996,8 +3007,8 @@ fn clean_use_statement_inner<'tcx>(
             a.has_name(sym::doc)
                 && match a.meta_item_list() {
                     Some(l) => {
-                        attr::list_contains_name(&l, sym::no_inline)
-                            || attr::list_contains_name(&l, sym::hidden)
+                        ast::attr::list_contains_name(&l, sym::no_inline)
+                            || ast::attr::list_contains_name(&l, sym::hidden)
                     }
                     None => false,
                 }
diff --git a/src/librustdoc/clean/render_macro_matchers.rs b/src/librustdoc/clean/render_macro_matchers.rs
index d39ecf83ac0..3cc5f8d615a 100644
--- a/src/librustdoc/clean/render_macro_matchers.rs
+++ b/src/librustdoc/clean/render_macro_matchers.rs
@@ -131,7 +131,7 @@ fn print_tts(printer: &mut Printer<'_>, tts: &TokenStream) {
     use State::*;
 
     let mut state = Start;
-    for tt in tts.trees() {
+    for tt in tts.iter() {
         let (needs_space, next_state) = match &tt {
             TokenTree::Token(tt, _) => match (state, &tt.kind) {
                 (Dollar, token::Ident(..)) => (false, DollarIdent),
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index cba947eb833..8c7ab925577 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -6,9 +6,7 @@ use std::{fmt, iter};
 
 use arrayvec::ArrayVec;
 use rustc_abi::{ExternAbi, VariantIdx};
-use rustc_ast::MetaItemInner;
-use rustc_ast_pretty::pprust;
-use rustc_attr::{ConstStability, Deprecation, Stability, StableSince};
+use rustc_attr_parsing::{ConstStability, Deprecation, Stability, StableSince};
 use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
 use rustc_hir::def::{CtorKind, DefKind, Res};
 use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId};
@@ -454,14 +452,14 @@ impl Item {
         kind: ItemKind,
         cx: &mut DocContext<'_>,
     ) -> Item {
-        let ast_attrs = cx.tcx.get_attrs_unchecked(def_id);
+        let hir_attrs = cx.tcx.get_attrs_unchecked(def_id);
 
         Self::from_def_id_and_attrs_and_parts(
             def_id,
             name,
             kind,
-            Attributes::from_ast(ast_attrs),
-            ast_attrs.cfg(cx.tcx, &cx.cache.hidden_cfg),
+            Attributes::from_hir(hir_attrs),
+            hir_attrs.cfg(cx.tcx, &cx.cache.hidden_cfg),
         )
     }
 
@@ -547,14 +545,14 @@ impl Item {
     pub(crate) fn is_associated_type(&self) -> bool {
         matches!(self.kind, AssocTypeItem(..) | StrippedItem(box AssocTypeItem(..)))
     }
-    pub(crate) fn is_ty_associated_type(&self) -> bool {
-        matches!(self.kind, TyAssocTypeItem(..) | StrippedItem(box TyAssocTypeItem(..)))
+    pub(crate) fn is_required_associated_type(&self) -> bool {
+        matches!(self.kind, RequiredAssocTypeItem(..) | StrippedItem(box RequiredAssocTypeItem(..)))
     }
     pub(crate) fn is_associated_const(&self) -> bool {
-        matches!(self.kind, AssocConstItem(..) | StrippedItem(box AssocConstItem(..)))
+        matches!(self.kind, ProvidedAssocConstItem(..) | ImplAssocConstItem(..) | StrippedItem(box (ProvidedAssocConstItem(..) | ImplAssocConstItem(..))))
     }
-    pub(crate) fn is_ty_associated_const(&self) -> bool {
-        matches!(self.kind, TyAssocConstItem(..) | StrippedItem(box TyAssocConstItem(..)))
+    pub(crate) fn is_required_associated_const(&self) -> bool {
+        matches!(self.kind, RequiredAssocConstItem(..) | StrippedItem(box RequiredAssocConstItem(..)))
     }
     pub(crate) fn is_method(&self) -> bool {
         self.type_() == ItemType::Method
@@ -671,7 +669,9 @@ impl Item {
                     asyncness: hir::IsAsync::NotAsync,
                 }
             }
-            ItemKind::FunctionItem(_) | ItemKind::MethodItem(_, _) | ItemKind::TyMethodItem(_) => {
+            ItemKind::FunctionItem(_)
+            | ItemKind::MethodItem(_, _)
+            | ItemKind::RequiredMethodItem(_) => {
                 let def_id = self.def_id().unwrap();
                 build_fn_header(def_id, tcx, tcx.asyncness(def_id))
             }
@@ -701,8 +701,13 @@ impl Item {
             // Variants always inherit visibility
             VariantItem(..) | ImplItem(..) => return None,
             // Trait items inherit the trait's visibility
-            AssocConstItem(..) | TyAssocConstItem(..) | AssocTypeItem(..) | TyAssocTypeItem(..)
-            | TyMethodItem(..) | MethodItem(..) => {
+            RequiredAssocConstItem(..)
+            | ProvidedAssocConstItem(..)
+            | ImplAssocConstItem(..)
+            | AssocTypeItem(..)
+            | RequiredAssocTypeItem(..)
+            | RequiredMethodItem(..)
+            | MethodItem(..) => {
                 let assoc_item = tcx.associated_item(def_id);
                 let is_trait_item = match assoc_item.container {
                     ty::AssocItemContainer::Trait => true,
@@ -742,10 +747,10 @@ impl Item {
             .iter()
             .filter_map(|attr| {
                 if keep_as_is {
-                    Some(pprust::attribute_to_string(attr))
+                    Some(rustc_hir_pretty::attribute_to_string(&tcx, attr))
                 } else if ALLOWED_ATTRIBUTES.contains(&attr.name_or_empty()) {
                     Some(
-                        pprust::attribute_to_string(attr)
+                        rustc_hir_pretty::attribute_to_string(&tcx, attr)
                             .replace("\\\n", "")
                             .replace('\n', "")
                             .replace("  ", " "),
@@ -847,10 +852,10 @@ pub(crate) enum ItemKind {
     TraitAliasItem(TraitAlias),
     ImplItem(Box<Impl>),
     /// A required method in a trait declaration meaning it's only a function signature.
-    TyMethodItem(Box<Function>),
+    RequiredMethodItem(Box<Function>),
     /// A method in a trait impl or a provided method in a trait declaration.
     ///
-    /// Compared to [TyMethodItem], it also contains a method body.
+    /// Compared to [RequiredMethodItem], it also contains a method body.
     MethodItem(Box<Function>, Option<hir::Defaultness>),
     StructFieldItem(Type),
     VariantItem(Variant),
@@ -864,14 +869,16 @@ pub(crate) enum ItemKind {
     ProcMacroItem(ProcMacro),
     PrimitiveItem(PrimitiveType),
     /// A required associated constant in a trait declaration.
-    TyAssocConstItem(Generics, Box<Type>),
+    RequiredAssocConstItem(Generics, Box<Type>),
     ConstantItem(Box<Constant>),
-    /// An associated constant in a trait impl or a provided one in a trait declaration.
-    AssocConstItem(Box<Constant>),
+    /// An associated constant in a trait declaration with provided default value.
+    ProvidedAssocConstItem(Box<Constant>),
+    /// An associated constant in an inherent impl or trait impl.
+    ImplAssocConstItem(Box<Constant>),
     /// A required associated type in a trait declaration.
     ///
     /// The bounds may be non-empty if there is a `where` clause.
-    TyAssocTypeItem(Generics, Vec<GenericBound>),
+    RequiredAssocTypeItem(Generics, Vec<GenericBound>),
     /// An associated type in a trait impl or a provided one in a trait declaration.
     AssocTypeItem(Box<TypeAlias>, Vec<GenericBound>),
     /// An item that has been stripped by a rustdoc pass
@@ -902,7 +909,7 @@ impl ItemKind {
             | StaticItem(_)
             | ConstantItem(_)
             | TraitAliasItem(_)
-            | TyMethodItem(_)
+            | RequiredMethodItem(_)
             | MethodItem(_, _)
             | StructFieldItem(_)
             | ForeignFunctionItem(_, _)
@@ -911,9 +918,10 @@ impl ItemKind {
             | MacroItem(_)
             | ProcMacroItem(_)
             | PrimitiveItem(_)
-            | TyAssocConstItem(..)
-            | AssocConstItem(..)
-            | TyAssocTypeItem(..)
+            | RequiredAssocConstItem(..)
+            | ProvidedAssocConstItem(..)
+            | ImplAssocConstItem(..)
+            | RequiredAssocTypeItem(..)
             | AssocTypeItem(..)
             | StrippedItem(_)
             | KeywordItem => [].iter(),
@@ -955,7 +963,7 @@ pub(crate) trait AttributesExt {
     type AttributeIterator<'a>: Iterator<Item = ast::MetaItemInner>
     where
         Self: 'a;
-    type Attributes<'a>: Iterator<Item = &'a ast::Attribute>
+    type Attributes<'a>: Iterator<Item = &'a hir::Attribute>
     where
         Self: 'a;
 
@@ -1009,7 +1017,7 @@ pub(crate) trait AttributesExt {
             // #[doc]
             if attr.doc_str().is_none() && attr.has_name(sym::doc) {
                 // #[doc(...)]
-                if let Some(list) = attr.meta().as_ref().and_then(|mi| mi.meta_item_list()) {
+                if let Some(list) = attr.meta_item_list() {
                     for item in list {
                         // #[doc(hidden)]
                         if !item.has_name(sym::cfg) {
@@ -1042,7 +1050,7 @@ pub(crate) trait AttributesExt {
                     let mut meta = attr.meta_item().unwrap().clone();
                     meta.path = ast::Path::from_ident(Ident::with_dummy_span(sym::target_feature));
 
-                    if let Ok(feat_cfg) = Cfg::parse(&MetaItemInner::MetaItem(meta)) {
+                    if let Ok(feat_cfg) = Cfg::parse(&ast::MetaItemInner::MetaItem(meta)) {
                         cfg &= feat_cfg;
                     }
                 }
@@ -1053,14 +1061,14 @@ pub(crate) trait AttributesExt {
     }
 }
 
-impl AttributesExt for [ast::Attribute] {
+impl AttributesExt for [hir::Attribute] {
     type AttributeIterator<'a> = impl Iterator<Item = ast::MetaItemInner> + 'a;
-    type Attributes<'a> = impl Iterator<Item = &'a ast::Attribute> + 'a;
+    type Attributes<'a> = impl Iterator<Item = &'a hir::Attribute> + 'a;
 
     fn lists(&self, name: Symbol) -> Self::AttributeIterator<'_> {
         self.iter()
             .filter(move |attr| attr.has_name(name))
-            .filter_map(ast::Attribute::meta_item_list)
+            .filter_map(ast::attr::AttributeExt::meta_item_list)
             .flatten()
     }
 
@@ -1069,20 +1077,20 @@ impl AttributesExt for [ast::Attribute] {
     }
 }
 
-impl AttributesExt for [(Cow<'_, ast::Attribute>, Option<DefId>)] {
+impl AttributesExt for [(Cow<'_, hir::Attribute>, Option<DefId>)] {
     type AttributeIterator<'a>
         = impl Iterator<Item = ast::MetaItemInner> + 'a
     where
         Self: 'a;
     type Attributes<'a>
-        = impl Iterator<Item = &'a ast::Attribute> + 'a
+        = impl Iterator<Item = &'a hir::Attribute> + 'a
     where
         Self: 'a;
 
     fn lists(&self, name: Symbol) -> Self::AttributeIterator<'_> {
         AttributesExt::iter(self)
             .filter(move |attr| attr.has_name(name))
-            .filter_map(ast::Attribute::meta_item_list)
+            .filter_map(hir::Attribute::meta_item_list)
             .flatten()
     }
 
@@ -1152,7 +1160,7 @@ pub struct RenderedLink {
 #[derive(Clone, Debug, Default)]
 pub(crate) struct Attributes {
     pub(crate) doc_strings: Vec<DocFragment>,
-    pub(crate) other_attrs: ast::AttrVec,
+    pub(crate) other_attrs: ThinVec<hir::Attribute>,
 }
 
 impl Attributes {
@@ -1180,22 +1188,22 @@ impl Attributes {
         self.has_doc_flag(sym::hidden)
     }
 
-    pub(crate) fn from_ast(attrs: &[ast::Attribute]) -> Attributes {
-        Attributes::from_ast_iter(attrs.iter().map(|attr| (attr, None)), false)
+    pub(crate) fn from_hir(attrs: &[hir::Attribute]) -> Attributes {
+        Attributes::from_hir_iter(attrs.iter().map(|attr| (attr, None)), false)
     }
 
-    pub(crate) fn from_ast_with_additional(
-        attrs: &[ast::Attribute],
-        (additional_attrs, def_id): (&[ast::Attribute], DefId),
+    pub(crate) fn from_hir_with_additional(
+        attrs: &[hir::Attribute],
+        (additional_attrs, def_id): (&[hir::Attribute], DefId),
     ) -> Attributes {
         // Additional documentation should be shown before the original documentation.
         let attrs1 = additional_attrs.iter().map(|attr| (attr, Some(def_id)));
         let attrs2 = attrs.iter().map(|attr| (attr, None));
-        Attributes::from_ast_iter(attrs1.chain(attrs2), false)
+        Attributes::from_hir_iter(attrs1.chain(attrs2), false)
     }
 
-    pub(crate) fn from_ast_iter<'a>(
-        attrs: impl Iterator<Item = (&'a ast::Attribute, Option<DefId>)>,
+    pub(crate) fn from_hir_iter<'a>(
+        attrs: impl Iterator<Item = (&'a hir::Attribute, Option<DefId>)>,
         doc_only: bool,
     ) -> Attributes {
         let (doc_strings, other_attrs) = attrs_to_doc_fragments(attrs, doc_only);
diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs
index d59b4e4081c..8aeebdde7bb 100644
--- a/src/librustdoc/clean/utils.rs
+++ b/src/librustdoc/clean/utils.rs
@@ -578,11 +578,10 @@ pub(crate) fn has_doc_flag(tcx: TyCtxt<'_>, did: DefId, flag: Symbol) -> bool {
 }
 
 pub(crate) fn attrs_have_doc_flag<'a>(
-    mut attrs: impl Iterator<Item = &'a ast::Attribute>,
+    mut attrs: impl Iterator<Item = &'a hir::Attribute>,
     flag: Symbol,
 ) -> bool {
-    attrs
-        .any(|attr| attr.meta_item_list().is_some_and(|l| rustc_attr::list_contains_name(&l, flag)))
+    attrs.any(|attr| attr.meta_item_list().is_some_and(|l| ast::attr::list_contains_name(&l, flag)))
 }
 
 /// A link to `doc.rust-lang.org` that includes the channel name. Use this instead of manual links
diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs
index 34c91e33db7..af3c7cc7be3 100644
--- a/src/librustdoc/config.rs
+++ b/src/librustdoc/config.rs
@@ -172,6 +172,9 @@ pub(crate) struct Options {
     /// This is mainly useful for other tools that reads that debuginfo to figure out
     /// how to call the compiler with the same arguments.
     pub(crate) expanded_args: Vec<String>,
+
+    /// Arguments to be used when compiling doctests.
+    pub(crate) doctest_compilation_args: Vec<String>,
 }
 
 impl fmt::Debug for Options {
@@ -774,6 +777,7 @@ impl Options {
         let scrape_examples_options = ScrapeExamplesOptions::new(matches, dcx);
         let with_examples = matches.opt_strs("with-examples");
         let call_locations = crate::scrape_examples::load_call_locations(with_examples, dcx);
+        let doctest_compilation_args = matches.opt_strs("doctest-compilation-args");
 
         let unstable_features =
             rustc_feature::UnstableFeatures::from_environment(crate_name.as_deref());
@@ -819,6 +823,7 @@ impl Options {
             scrape_examples_options,
             unstable_features,
             expanded_args: args,
+            doctest_compilation_args,
         };
         let render_options = RenderOptions {
             output,
diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs
index 70d9269ae5c..e6e5123d0bb 100644
--- a/src/librustdoc/doctest.rs
+++ b/src/librustdoc/doctest.rs
@@ -13,10 +13,10 @@ use std::{panic, str};
 
 pub(crate) use make::DocTestBuilder;
 pub(crate) use markdown::test as test_markdown;
-use rustc_ast as ast;
 use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet};
 use rustc_errors::emitter::HumanReadableErrorType;
 use rustc_errors::{ColorConfig, DiagCtxtHandle};
+use rustc_hir as hir;
 use rustc_hir::CRATE_HIR_ID;
 use rustc_hir::def_id::LOCAL_CRATE;
 use rustc_interface::interface;
@@ -50,6 +50,46 @@ pub(crate) struct GlobalTestOptions {
     pub(crate) args_file: PathBuf,
 }
 
+/// Function used to split command line arguments just like a shell would.
+fn split_args(args: &str) -> Vec<String> {
+    let mut out = Vec::new();
+    let mut iter = args.chars();
+    let mut current = String::new();
+
+    while let Some(c) = iter.next() {
+        if c == '\\' {
+            if let Some(c) = iter.next() {
+                // If it's escaped, even a quote or a whitespace will be ignored.
+                current.push(c);
+            }
+        } else if c == '"' || c == '\'' {
+            while let Some(new_c) = iter.next() {
+                if new_c == c {
+                    break;
+                } else if new_c == '\\' {
+                    if let Some(c) = iter.next() {
+                        // If it's escaped, even a quote will be ignored.
+                        current.push(c);
+                    }
+                } else {
+                    current.push(new_c);
+                }
+            }
+        } else if " \n\t\r".contains(c) {
+            if !current.is_empty() {
+                out.push(current.clone());
+                current.clear();
+            }
+        } else {
+            current.push(c);
+        }
+    }
+    if !current.is_empty() {
+        out.push(current);
+    }
+    out
+}
+
 pub(crate) fn generate_args_file(file_path: &Path, options: &RustdocOptions) -> Result<(), String> {
     let mut file = File::create(file_path)
         .map_err(|error| format!("failed to create args file: {error:?}"))?;
@@ -78,6 +118,10 @@ pub(crate) fn generate_args_file(file_path: &Path, options: &RustdocOptions) ->
         content.push(format!("-Z{unstable_option_str}"));
     }
 
+    for compilation_args in &options.doctest_compilation_args {
+        content.extend(split_args(compilation_args));
+    }
+
     let content = content.join("\n");
 
     file.write_all(content.as_bytes())
@@ -174,28 +218,28 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, input: Input, options: RustdocOptions
         compiling_test_count,
         ..
     } = interface::run_compiler(config, |compiler| {
-        compiler.enter(|queries| {
-            let collector = queries.global_ctxt().enter(|tcx| {
-                let crate_name = tcx.crate_name(LOCAL_CRATE).to_string();
-                let crate_attrs = tcx.hir().attrs(CRATE_HIR_ID);
-                let opts = scrape_test_config(crate_name, crate_attrs, args_path);
-                let enable_per_target_ignores = options.enable_per_target_ignores;
-
-                let mut collector = CreateRunnableDocTests::new(options, opts);
-                let hir_collector = HirCollector::new(
-                    ErrorCodes::from(compiler.sess.opts.unstable_features.is_nightly_build()),
-                    enable_per_target_ignores,
-                    tcx,
-                );
-                let tests = hir_collector.collect_crate();
-                tests.into_iter().for_each(|t| collector.add_test(t));
-
-                collector
-            });
-            compiler.sess.dcx().abort_if_errors();
+        let krate = rustc_interface::passes::parse(&compiler.sess);
+
+        let collector = rustc_interface::create_and_enter_global_ctxt(&compiler, krate, |tcx| {
+            let crate_name = tcx.crate_name(LOCAL_CRATE).to_string();
+            let crate_attrs = tcx.hir().attrs(CRATE_HIR_ID);
+            let opts = scrape_test_config(crate_name, crate_attrs, args_path);
+            let enable_per_target_ignores = options.enable_per_target_ignores;
+
+            let mut collector = CreateRunnableDocTests::new(options, opts);
+            let hir_collector = HirCollector::new(
+                ErrorCodes::from(compiler.sess.opts.unstable_features.is_nightly_build()),
+                enable_per_target_ignores,
+                tcx,
+            );
+            let tests = hir_collector.collect_crate();
+            tests.into_iter().for_each(|t| collector.add_test(t));
 
             collector
-        })
+        });
+        compiler.sess.dcx().abort_if_errors();
+
+        collector
     });
 
     run_tests(opts, &rustdoc_options, &unused_extern_reports, standalone_tests, mergeable_tests);
@@ -325,7 +369,7 @@ pub(crate) fn run_tests(
 // Look for `#![doc(test(no_crate_inject))]`, used by crates in the std facade.
 fn scrape_test_config(
     crate_name: String,
-    attrs: &[ast::Attribute],
+    attrs: &[hir::Attribute],
     args_file: PathBuf,
 ) -> GlobalTestOptions {
     use rustc_ast_pretty::pprust;
diff --git a/src/librustdoc/doctest/make.rs b/src/librustdoc/doctest/make.rs
index 3ae60938749..a188bc8ebd9 100644
--- a/src/librustdoc/doctest/make.rs
+++ b/src/librustdoc/doctest/make.rs
@@ -51,8 +51,17 @@ impl DocTestBuilder {
                 !lang_str.compile_fail && !lang_str.test_harness && !lang_str.standalone_crate
             });
 
-        let SourceInfo { crate_attrs, maybe_crate_attrs, crates, everything_else } =
-            partition_source(source, edition);
+        let Some(SourceInfo { crate_attrs, maybe_crate_attrs, crates, everything_else }) =
+            partition_source(source, edition)
+        else {
+            return Self::invalid(
+                String::new(),
+                String::new(),
+                String::new(),
+                source.to_string(),
+                test_id,
+            );
+        };
 
         // Uses librustc_ast to parse the doctest and find if there's a main fn and the extern
         // crate already is included.
@@ -77,18 +86,7 @@ impl DocTestBuilder {
         else {
             // If the parser panicked due to a fatal error, pass the test code through unchanged.
             // The error will be reported during compilation.
-            return Self {
-                supports_color: false,
-                has_main_fn: false,
-                crate_attrs,
-                maybe_crate_attrs,
-                crates,
-                everything_else,
-                already_has_extern_crate: false,
-                test_id,
-                failed_ast: true,
-                can_be_merged: false,
-            };
+            return Self::invalid(crate_attrs, maybe_crate_attrs, crates, everything_else, test_id);
         };
         // If the AST returned an error, we don't want this doctest to be merged with the
         // others. Same if it contains `#[feature]` or `#[no_std]`.
@@ -113,6 +111,27 @@ impl DocTestBuilder {
         }
     }
 
+    fn invalid(
+        crate_attrs: String,
+        maybe_crate_attrs: String,
+        crates: String,
+        everything_else: String,
+        test_id: Option<String>,
+    ) -> Self {
+        Self {
+            supports_color: false,
+            has_main_fn: false,
+            crate_attrs,
+            maybe_crate_attrs,
+            crates,
+            everything_else,
+            already_has_extern_crate: false,
+            test_id,
+            failed_ast: true,
+            can_be_merged: false,
+        }
+    }
+
     /// Transforms a test into code that can be compiled into a Rust binary, and returns the number of
     /// lines before the test code begins.
     pub(crate) fn generate_unique_doctest(
@@ -518,8 +537,8 @@ fn handle_attr(mod_attr_pending: &mut String, source_info: &mut SourceInfo, edit
         push_to.push('\n');
         // If it's complete, then we can clear the pending content.
         mod_attr_pending.clear();
-    } else if mod_attr_pending.ends_with('\\') {
-        mod_attr_pending.push('n');
+    } else {
+        mod_attr_pending.push_str("\n");
     }
 }
 
@@ -531,7 +550,7 @@ struct SourceInfo {
     everything_else: String,
 }
 
-fn partition_source(s: &str, edition: Edition) -> SourceInfo {
+fn partition_source(s: &str, edition: Edition) -> Option<SourceInfo> {
     #[derive(Copy, Clone, PartialEq)]
     enum PartitionState {
         Attrs,
@@ -606,11 +625,16 @@ fn partition_source(s: &str, edition: Edition) -> SourceInfo {
         }
     }
 
+    if !mod_attr_pending.is_empty() {
+        debug!("invalid doctest code: {s:?}");
+        return None;
+    }
+
     source_info.everything_else = source_info.everything_else.trim().to_string();
 
     debug!("crate_attrs:\n{}{}", source_info.crate_attrs, source_info.maybe_crate_attrs);
     debug!("crates:\n{}", source_info.crates);
     debug!("after:\n{}", source_info.everything_else);
 
-    source_info
+    Some(source_info)
 }
diff --git a/src/librustdoc/doctest/rust.rs b/src/librustdoc/doctest/rust.rs
index dc68f48f635..0903baddabe 100644
--- a/src/librustdoc/doctest/rust.rs
+++ b/src/librustdoc/doctest/rust.rs
@@ -110,7 +110,7 @@ impl HirCollector<'_> {
 
         // The collapse-docs pass won't combine sugared/raw doc attributes, or included files with
         // anything else, this will combine them for us.
-        let attrs = Attributes::from_ast(ast_attrs);
+        let attrs = Attributes::from_hir(ast_attrs);
         if let Some(doc) = attrs.opt_doc_value() {
             let span = span_of_fragments(&attrs.doc_strings).unwrap_or(sp);
             self.collector.position = if span.edition().at_least_rust_2024() {
diff --git a/src/librustdoc/doctest/tests.rs b/src/librustdoc/doctest/tests.rs
index 160d0f222b4..fa6cca3681b 100644
--- a/src/librustdoc/doctest/tests.rs
+++ b/src/librustdoc/doctest/tests.rs
@@ -379,3 +379,25 @@ fn main() {
     let (output, len) = make_test(input, None, false, &opts, None);
     assert_eq!((output, len), (expected, 1));
 }
+
+#[test]
+fn check_split_args() {
+    fn compare(input: &str, expected: &[&str]) {
+        let output = super::split_args(input);
+        let expected = expected.iter().map(|s| s.to_string()).collect::<Vec<_>>();
+        assert_eq!(expected, output, "test failed for {input:?}");
+    }
+
+    compare("'a' \"b\"c", &["a", "bc"]);
+    compare("'a' \"b \"c d", &["a", "b c", "d"]);
+    compare("'a' \"b\\\"c\"", &["a", "b\"c"]);
+    compare("'a\"'", &["a\""]);
+    compare("\"a'\"", &["a'"]);
+    compare("\\ a", &[" a"]);
+    compare("\\\\", &["\\"]);
+    compare("a'", &["a"]);
+    compare("a          ", &["a"]);
+    compare("a          b", &["a", "b"]);
+    compare("a\n\t \rb", &["a", "b"]);
+    compare("a\n\t1 \rb", &["a", "1", "b"]);
+}
diff --git a/src/librustdoc/fold.rs b/src/librustdoc/fold.rs
index 95e495205ae..c03d16ad081 100644
--- a/src/librustdoc/fold.rs
+++ b/src/librustdoc/fold.rs
@@ -82,7 +82,7 @@ pub(crate) trait DocFolder: Sized {
             | StaticItem(_)
             | ConstantItem(..)
             | TraitAliasItem(_)
-            | TyMethodItem(_)
+            | RequiredMethodItem(_)
             | MethodItem(_, _)
             | StructFieldItem(_)
             | ForeignFunctionItem(..)
@@ -91,9 +91,10 @@ pub(crate) trait DocFolder: Sized {
             | MacroItem(_)
             | ProcMacroItem(_)
             | PrimitiveItem(_)
-            | TyAssocConstItem(..)
-            | AssocConstItem(..)
-            | TyAssocTypeItem(..)
+            | RequiredAssocConstItem(..)
+            | ProvidedAssocConstItem(..)
+            | ImplAssocConstItem(..)
+            | RequiredAssocTypeItem(..)
             | AssocTypeItem(..)
             | KeywordItem => kind,
         }
diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs
index e387eb010be..b63122565c4 100644
--- a/src/librustdoc/formats/cache.rs
+++ b/src/librustdoc/formats/cache.rs
@@ -334,12 +334,13 @@ impl DocFolder for CacheBuilder<'_, '_> {
             clean::ExternCrateItem { .. }
             | clean::ImportItem(..)
             | clean::ImplItem(..)
-            | clean::TyMethodItem(..)
+            | clean::RequiredMethodItem(..)
             | clean::MethodItem(..)
             | clean::StructFieldItem(..)
-            | clean::TyAssocConstItem(..)
-            | clean::AssocConstItem(..)
-            | clean::TyAssocTypeItem(..)
+            | clean::RequiredAssocConstItem(..)
+            | clean::ProvidedAssocConstItem(..)
+            | clean::ImplAssocConstItem(..)
+            | clean::RequiredAssocTypeItem(..)
             | clean::AssocTypeItem(..)
             | clean::StrippedItem(..)
             | clean::KeywordItem => {
@@ -443,15 +444,17 @@ fn add_item_to_search_index(tcx: TyCtxt<'_>, cache: &mut Cache, item: &clean::It
     let item_def_id = item.item_id.as_def_id().unwrap();
     let (parent_did, parent_path) = match item.kind {
         clean::StrippedItem(..) => return,
-        clean::AssocConstItem(..) | clean::AssocTypeItem(..)
+        clean::ProvidedAssocConstItem(..)
+        | clean::ImplAssocConstItem(..)
+        | clean::AssocTypeItem(..)
             if cache.parent_stack.last().is_some_and(|parent| parent.is_trait_impl()) =>
         {
             // skip associated items in trait impls
             return;
         }
-        clean::TyMethodItem(..)
-        | clean::TyAssocConstItem(..)
-        | clean::TyAssocTypeItem(..)
+        clean::RequiredMethodItem(..)
+        | clean::RequiredAssocConstItem(..)
+        | clean::RequiredAssocTypeItem(..)
         | clean::StructFieldItem(..)
         | clean::VariantItem(..) => {
             // Don't index if containing module is stripped (i.e., private),
@@ -467,7 +470,10 @@ fn add_item_to_search_index(tcx: TyCtxt<'_>, cache: &mut Cache, item: &clean::It
             let parent_path = &cache.stack[..cache.stack.len() - 1];
             (Some(parent_did), parent_path)
         }
-        clean::MethodItem(..) | clean::AssocConstItem(..) | clean::AssocTypeItem(..) => {
+        clean::MethodItem(..)
+        | clean::ProvidedAssocConstItem(..)
+        | clean::ImplAssocConstItem(..)
+        | clean::AssocTypeItem(..) => {
             let last = cache.parent_stack.last().expect("parent_stack is empty 2");
             let parent_did = match last {
                 // impl Trait for &T { fn method(self); }
diff --git a/src/librustdoc/formats/item_type.rs b/src/librustdoc/formats/item_type.rs
index 383e3135faa..de6537e992f 100644
--- a/src/librustdoc/formats/item_type.rs
+++ b/src/librustdoc/formats/item_type.rs
@@ -88,7 +88,7 @@ impl<'a> From<&'a clean::Item> for ItemType {
             clean::ConstantItem(..) => ItemType::Constant,
             clean::TraitItem(..) => ItemType::Trait,
             clean::ImplItem(..) => ItemType::Impl,
-            clean::TyMethodItem(..) => ItemType::TyMethod,
+            clean::RequiredMethodItem(..) => ItemType::TyMethod,
             clean::MethodItem(..) => ItemType::Method,
             clean::StructFieldItem(..) => ItemType::StructField,
             clean::VariantItem(..) => ItemType::Variant,
@@ -96,8 +96,10 @@ impl<'a> From<&'a clean::Item> for ItemType {
             clean::ForeignStaticItem(..) => ItemType::Static,     // no ForeignStatic
             clean::MacroItem(..) => ItemType::Macro,
             clean::PrimitiveItem(..) => ItemType::Primitive,
-            clean::TyAssocConstItem(..) | clean::AssocConstItem(..) => ItemType::AssocConst,
-            clean::TyAssocTypeItem(..) | clean::AssocTypeItem(..) => ItemType::AssocType,
+            clean::RequiredAssocConstItem(..)
+            | clean::ProvidedAssocConstItem(..)
+            | clean::ImplAssocConstItem(..) => ItemType::AssocConst,
+            clean::RequiredAssocTypeItem(..) | clean::AssocTypeItem(..) => ItemType::AssocType,
             clean::ForeignTypeItem => ItemType::ForeignType,
             clean::KeywordItem => ItemType::Keyword,
             clean::TraitAliasItem(..) => ItemType::TraitAlias,
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index 0a563b1df26..136002b8e15 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -15,7 +15,7 @@ use std::iter::{self, once};
 
 use itertools::Itertools;
 use rustc_abi::ExternAbi;
-use rustc_attr::{ConstStability, StabilityLevel, StableSince};
+use rustc_attr_parsing::{ConstStability, StabilityLevel, StableSince};
 use rustc_data_structures::captures::Captures;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_hir as hir;
@@ -1626,10 +1626,7 @@ pub(crate) trait PrintWithSpace {
 
 impl PrintWithSpace for hir::Safety {
     fn print_with_space(&self) -> &str {
-        match self {
-            hir::Safety::Unsafe => "unsafe ",
-            hir::Safety::Safe => "",
-        }
+        self.prefix_str()
     }
 }
 
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index 90c49270566..aa8fdaaee4c 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -344,35 +344,48 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> {
 }
 
 /// Make headings links with anchor IDs and build up TOC.
-struct LinkReplacer<'a, I: Iterator<Item = Event<'a>>> {
-    inner: I,
+struct LinkReplacerInner<'a> {
     links: &'a [RenderedLink],
     shortcut_link: Option<&'a RenderedLink>,
 }
 
+struct LinkReplacer<'a, I: Iterator<Item = Event<'a>>> {
+    iter: I,
+    inner: LinkReplacerInner<'a>,
+}
+
 impl<'a, I: Iterator<Item = Event<'a>>> LinkReplacer<'a, I> {
     fn new(iter: I, links: &'a [RenderedLink]) -> Self {
-        LinkReplacer { inner: iter, links, shortcut_link: None }
+        LinkReplacer { iter, inner: { LinkReplacerInner { links, shortcut_link: None } } }
     }
 }
 
-impl<'a, I: Iterator<Item = Event<'a>>> Iterator for LinkReplacer<'a, I> {
-    type Item = Event<'a>;
+// FIXME: Once we have specialized trait impl (for `Iterator` impl on `LinkReplacer`),
+// we can remove this type and move back `LinkReplacerInner` fields into `LinkReplacer`.
+struct SpannedLinkReplacer<'a, I: Iterator<Item = SpannedEvent<'a>>> {
+    iter: I,
+    inner: LinkReplacerInner<'a>,
+}
 
-    fn next(&mut self) -> Option<Self::Item> {
-        let mut event = self.inner.next();
+impl<'a, I: Iterator<Item = SpannedEvent<'a>>> SpannedLinkReplacer<'a, I> {
+    fn new(iter: I, links: &'a [RenderedLink]) -> Self {
+        SpannedLinkReplacer { iter, inner: { LinkReplacerInner { links, shortcut_link: None } } }
+    }
+}
 
+impl<'a> LinkReplacerInner<'a> {
+    fn handle_event(&mut self, event: &mut Event<'a>) {
         // Replace intra-doc links and remove disambiguators from shortcut links (`[fn@f]`).
-        match &mut event {
+        match event {
             // This is a shortcut link that was resolved by the broken_link_callback: `[fn@f]`
             // Remove any disambiguator.
-            Some(Event::Start(Tag::Link {
+            Event::Start(Tag::Link {
                 // [fn@f] or [fn@f][]
                 link_type: LinkType::ShortcutUnknown | LinkType::CollapsedUnknown,
                 dest_url,
                 title,
                 ..
-            })) => {
+            }) => {
                 debug!("saw start of shortcut link to {dest_url} with title {title}");
                 // If this is a shortcut link, it was resolved by the broken_link_callback.
                 // So the URL will already be updated properly.
@@ -389,13 +402,13 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for LinkReplacer<'a, I> {
                 }
             }
             // Now that we're done with the shortcut link, don't replace any more text.
-            Some(Event::End(TagEnd::Link)) if self.shortcut_link.is_some() => {
+            Event::End(TagEnd::Link) if self.shortcut_link.is_some() => {
                 debug!("saw end of shortcut link");
                 self.shortcut_link = None;
             }
             // Handle backticks in inline code blocks, but only if we're in the middle of a shortcut link.
             // [`fn@f`]
-            Some(Event::Code(text)) => {
+            Event::Code(text) => {
                 trace!("saw code {text}");
                 if let Some(link) = self.shortcut_link {
                     // NOTE: this only replaces if the code block is the *entire* text.
@@ -418,7 +431,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for LinkReplacer<'a, I> {
             }
             // Replace plain text in links, but only in the middle of a shortcut link.
             // [fn@f]
-            Some(Event::Text(text)) => {
+            Event::Text(text) => {
                 trace!("saw text {text}");
                 if let Some(link) = self.shortcut_link {
                     // NOTE: same limitations as `Event::Code`
@@ -434,7 +447,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for LinkReplacer<'a, I> {
             }
             // If this is a link, but not a shortcut link,
             // replace the URL, since the broken_link_callback was not called.
-            Some(Event::Start(Tag::Link { dest_url, title, .. })) => {
+            Event::Start(Tag::Link { dest_url, title, .. }) => {
                 if let Some(link) =
                     self.links.iter().find(|&link| *link.original_text == **dest_url)
                 {
@@ -447,12 +460,33 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for LinkReplacer<'a, I> {
             // Anything else couldn't have been a valid Rust path, so no need to replace the text.
             _ => {}
         }
+    }
+}
+
+impl<'a, I: Iterator<Item = Event<'a>>> Iterator for LinkReplacer<'a, I> {
+    type Item = Event<'a>;
 
+    fn next(&mut self) -> Option<Self::Item> {
+        let mut event = self.iter.next();
+        if let Some(ref mut event) = event {
+            self.inner.handle_event(event);
+        }
         // Yield the modified event
         event
     }
 }
 
+impl<'a, I: Iterator<Item = SpannedEvent<'a>>> Iterator for SpannedLinkReplacer<'a, I> {
+    type Item = SpannedEvent<'a>;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        let Some((mut event, range)) = self.iter.next() else { return None };
+        self.inner.handle_event(&mut event);
+        // Yield the modified event
+        Some((event, range))
+    }
+}
+
 /// Wrap HTML tables into `<div>` to prevent having the doc blocks width being too big.
 struct TableWrapper<'a, I: Iterator<Item = Event<'a>>> {
     inner: I,
@@ -1339,9 +1373,9 @@ impl<'a> Markdown<'a> {
 
         ids.handle_footnotes(|ids, existing_footnotes| {
             let p = HeadingLinks::new(p, None, ids, heading_offset);
+            let p = SpannedLinkReplacer::new(p, links);
             let p = footnotes::Footnotes::new(p, existing_footnotes);
-            let p = LinkReplacer::new(p.map(|(ev, _)| ev), links);
-            let p = TableWrapper::new(p);
+            let p = TableWrapper::new(p.map(|(ev, _)| ev));
             CodeBlocks::new(p, codes, edition, playground)
         })
     }
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index e013829e5e0..dfdf2cd6ec3 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -44,14 +44,15 @@ use std::path::PathBuf;
 use std::{fs, str};
 
 use rinja::Template;
-use rustc_attr::{ConstStability, DeprecatedSince, Deprecation, StabilityLevel, StableSince};
+use rustc_attr_parsing::{
+    ConstStability, DeprecatedSince, Deprecation, RustcVersion, StabilityLevel, StableSince,
+};
 use rustc_data_structures::captures::Captures;
 use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
 use rustc_hir::Mutability;
 use rustc_hir::def_id::{DefId, DefIdSet};
 use rustc_middle::ty::print::PrintTraitRefExt;
 use rustc_middle::ty::{self, TyCtxt};
-use rustc_session::RustcVersion;
 use rustc_span::symbol::{Symbol, sym};
 use rustc_span::{BytePos, DUMMY_SP, FileName, RealFileName};
 use serde::ser::SerializeMap;
@@ -835,12 +836,23 @@ fn assoc_href_attr(it: &clean::Item, link: AssocItemLink<'_>, cx: &Context<'_>)
     href.map(|href| format!(" href=\"{href}\"")).unwrap_or_default()
 }
 
+#[derive(Debug)]
+enum AssocConstValue<'a> {
+    // In trait definitions, it is relevant for the public API whether an
+    // associated constant comes with a default value, so even if we cannot
+    // render its value, the presence of a value must be shown using `= _`.
+    TraitDefault(&'a clean::ConstantKind),
+    // In impls, there is no need to show `= _`.
+    Impl(&'a clean::ConstantKind),
+    None,
+}
+
 fn assoc_const(
     w: &mut Buffer,
     it: &clean::Item,
     generics: &clean::Generics,
     ty: &clean::Type,
-    default: Option<&clean::ConstantKind>,
+    value: AssocConstValue<'_>,
     link: AssocItemLink<'_>,
     indent: usize,
     cx: &Context<'_>,
@@ -856,15 +868,20 @@ fn assoc_const(
         generics = generics.print(cx),
         ty = ty.print(cx),
     );
-    if let Some(default) = default {
-        w.write_str(" = ");
-
+    if let AssocConstValue::TraitDefault(konst) | AssocConstValue::Impl(konst) = value {
         // FIXME: `.value()` uses `clean::utils::format_integer_with_underscore_sep` under the
         //        hood which adds noisy underscores and a type suffix to number literals.
         //        This hurts readability in this context especially when more complex expressions
         //        are involved and it doesn't add much of value.
         //        Find a way to print constants here without all that jazz.
-        write!(w, "{}", Escape(&default.value(tcx).unwrap_or_else(|| default.expr(tcx))));
+        let repr = konst.value(tcx).unwrap_or_else(|| konst.expr(tcx));
+        if match value {
+            AssocConstValue::TraitDefault(_) => true, // always show
+            AssocConstValue::Impl(_) => repr != "_",  // show if there is a meaningful value to show
+            AssocConstValue::None => unreachable!(),
+        } {
+            write!(w, " = {}", Escape(&repr));
+        }
     }
     write!(w, "{}", print_where_clause(generics, cx, indent, Ending::NoNewline));
 }
@@ -1075,33 +1092,43 @@ fn render_assoc_item(
 ) {
     match &item.kind {
         clean::StrippedItem(..) => {}
-        clean::TyMethodItem(m) => {
+        clean::RequiredMethodItem(m) => {
             assoc_method(w, item, &m.generics, &m.decl, link, parent, cx, render_mode)
         }
         clean::MethodItem(m, _) => {
             assoc_method(w, item, &m.generics, &m.decl, link, parent, cx, render_mode)
         }
-        clean::TyAssocConstItem(generics, ty) => assoc_const(
+        clean::RequiredAssocConstItem(generics, ty) => assoc_const(
             w,
             item,
             generics,
             ty,
-            None,
+            AssocConstValue::None,
             link,
             if parent == ItemType::Trait { 4 } else { 0 },
             cx,
         ),
-        clean::AssocConstItem(ci) => assoc_const(
+        clean::ProvidedAssocConstItem(ci) => assoc_const(
             w,
             item,
             &ci.generics,
             &ci.type_,
-            Some(&ci.kind),
+            AssocConstValue::TraitDefault(&ci.kind),
             link,
             if parent == ItemType::Trait { 4 } else { 0 },
             cx,
         ),
-        clean::TyAssocTypeItem(ref generics, ref bounds) => assoc_type(
+        clean::ImplAssocConstItem(ci) => assoc_const(
+            w,
+            item,
+            &ci.generics,
+            &ci.type_,
+            AssocConstValue::Impl(&ci.kind),
+            link,
+            if parent == ItemType::Trait { 4 } else { 0 },
+            cx,
+        ),
+        clean::RequiredAssocTypeItem(ref generics, ref bounds) => assoc_type(
             w,
             item,
             generics,
@@ -1383,7 +1410,7 @@ fn render_deref_methods(
 fn should_render_item(item: &clean::Item, deref_mut_: bool, tcx: TyCtxt<'_>) -> bool {
     let self_type_opt = match item.kind {
         clean::MethodItem(ref method, _) => method.decl.receiver_type(),
-        clean::TyMethodItem(ref method) => method.decl.receiver_type(),
+        clean::RequiredMethodItem(ref method) => method.decl.receiver_type(),
         _ => None,
     };
 
@@ -1659,7 +1686,7 @@ fn render_impl(
             write!(w, "<details class=\"toggle{method_toggle_class}\" open><summary>");
         }
         match &item.kind {
-            clean::MethodItem(..) | clean::TyMethodItem(_) => {
+            clean::MethodItem(..) | clean::RequiredMethodItem(_) => {
                 // Only render when the method is not static or we allow static methods
                 if render_method_item {
                     let id = cx.derive_id(format!("{item_type}.{name}"));
@@ -1689,7 +1716,7 @@ fn render_impl(
                     w.write_str("</h4></section>");
                 }
             }
-            clean::TyAssocConstItem(ref generics, ref ty) => {
+            clean::RequiredAssocConstItem(ref generics, ref ty) => {
                 let source_id = format!("{item_type}.{name}");
                 let id = cx.derive_id(&source_id);
                 write!(w, "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">");
@@ -1704,14 +1731,14 @@ fn render_impl(
                     item,
                     generics,
                     ty,
-                    None,
+                    AssocConstValue::None,
                     link.anchor(if trait_.is_some() { &source_id } else { &id }),
                     0,
                     cx,
                 );
                 w.write_str("</h4></section>");
             }
-            clean::AssocConstItem(ci) => {
+            clean::ProvidedAssocConstItem(ci) | clean::ImplAssocConstItem(ci) => {
                 let source_id = format!("{item_type}.{name}");
                 let id = cx.derive_id(&source_id);
                 write!(w, "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">");
@@ -1726,14 +1753,18 @@ fn render_impl(
                     item,
                     &ci.generics,
                     &ci.type_,
-                    Some(&ci.kind),
+                    match item.kind {
+                        clean::ProvidedAssocConstItem(_) => AssocConstValue::TraitDefault(&ci.kind),
+                        clean::ImplAssocConstItem(_) => AssocConstValue::Impl(&ci.kind),
+                        _ => unreachable!(),
+                    },
                     link.anchor(if trait_.is_some() { &source_id } else { &id }),
                     0,
                     cx,
                 );
                 w.write_str("</h4></section>");
             }
-            clean::TyAssocTypeItem(ref generics, ref bounds) => {
+            clean::RequiredAssocTypeItem(ref generics, ref bounds) => {
                 let source_id = format!("{item_type}.{name}");
                 let id = cx.derive_id(&source_id);
                 write!(w, "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">");
@@ -1808,11 +1839,13 @@ fn render_impl(
     if !impl_.is_negative_trait_impl() {
         for trait_item in &impl_.items {
             match trait_item.kind {
-                clean::MethodItem(..) | clean::TyMethodItem(_) => methods.push(trait_item),
-                clean::TyAssocTypeItem(..) | clean::AssocTypeItem(..) => {
+                clean::MethodItem(..) | clean::RequiredMethodItem(_) => methods.push(trait_item),
+                clean::RequiredAssocTypeItem(..) | clean::AssocTypeItem(..) => {
                     assoc_types.push(trait_item)
                 }
-                clean::TyAssocConstItem(..) | clean::AssocConstItem(_) => {
+                clean::RequiredAssocConstItem(..)
+                | clean::ProvidedAssocConstItem(_)
+                | clean::ImplAssocConstItem(_) => {
                     // We render it directly since they're supposed to come first.
                     doc_impl_item(
                         &mut default_impl_items,
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index 4c8d704e65b..0fb77d38f16 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -469,7 +469,7 @@ fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[cl
 
                 let unsafety_flag = match myitem.kind {
                     clean::FunctionItem(_) | clean::ForeignFunctionItem(..)
-                        if myitem.fn_header(tcx).unwrap().safety == hir::Safety::Unsafe =>
+                        if myitem.fn_header(tcx).unwrap().safety.is_unsafe() =>
                     {
                         "<sup title=\"unsafe function\">⚠</sup>"
                     }
@@ -651,9 +651,11 @@ fn item_function(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, f: &clean::
 fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Trait) {
     let tcx = cx.tcx();
     let bounds = bounds(&t.bounds, false, cx);
-    let required_types = t.items.iter().filter(|m| m.is_ty_associated_type()).collect::<Vec<_>>();
+    let required_types =
+        t.items.iter().filter(|m| m.is_required_associated_type()).collect::<Vec<_>>();
     let provided_types = t.items.iter().filter(|m| m.is_associated_type()).collect::<Vec<_>>();
-    let required_consts = t.items.iter().filter(|m| m.is_ty_associated_const()).collect::<Vec<_>>();
+    let required_consts =
+        t.items.iter().filter(|m| m.is_required_associated_const()).collect::<Vec<_>>();
     let provided_consts = t.items.iter().filter(|m| m.is_associated_const()).collect::<Vec<_>>();
     let required_methods = t.items.iter().filter(|m| m.is_ty_method()).collect::<Vec<_>>();
     let provided_methods = t.items.iter().filter(|m| m.is_method()).collect::<Vec<_>>();
@@ -1926,9 +1928,7 @@ fn item_static(
             buffer,
             "{vis}{safe}static {mutability}{name}: {typ}",
             vis = visibility_print_with_space(it, cx),
-            safe = safety
-                .map(|safe| if safe == hir::Safety::Unsafe { "unsafe " } else { "" })
-                .unwrap_or(""),
+            safe = safety.map(|safe| safe.prefix_str()).unwrap_or(""),
             mutability = s.mutability.print_with_space(),
             name = it.name.unwrap(),
             typ = s.type_.print(cx)
diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs
index cfb62c3ca16..fe2e155c9ba 100644
--- a/src/librustdoc/html/render/search_index.rs
+++ b/src/librustdoc/html/render/search_index.rs
@@ -201,7 +201,7 @@ pub(crate) fn build_index(
                             // exported from this same module). It's also likely to Do
                             // What I Mean, since if a re-export changes the name, it might
                             // also be a change in semantic meaning.
-                            .filter(|fqp| fqp.last() == fqp.last());
+                            .filter(|this_fqp| this_fqp.last() == fqp.last());
                         Some(insert_into_map(
                             itemid_to_pathid,
                             ItemId::DefId(defid),
@@ -423,6 +423,14 @@ pub(crate) fn build_index(
                     }
                     Some(path)
                 });
+            } else if let Some(parent_idx) = item.parent_idx {
+                let i = <isize as TryInto<usize>>::try_into(parent_idx).unwrap();
+                item.path = {
+                    let p = &crate_paths[i].1;
+                    join_with_double_colon(&p[..p.len() - 1])
+                };
+                item.exact_path =
+                    crate_paths[i].2.as_ref().map(|xp| join_with_double_colon(&xp[..xp.len() - 1]));
             }
 
             // Omit the parent path if it is same to that of the prior item.
@@ -829,7 +837,7 @@ pub(crate) fn get_function_type_for_search(
         clean::ForeignFunctionItem(ref f, _)
         | clean::FunctionItem(ref f)
         | clean::MethodItem(ref f, _)
-        | clean::TyMethodItem(ref f) => {
+        | clean::RequiredMethodItem(ref f) => {
             get_fn_inputs_and_outputs(f, tcx, impl_or_trait_generics, cache)
         }
         _ => return None,
@@ -1199,10 +1207,11 @@ fn simplify_fn_type<'a, 'tcx>(
                 && let Type::Path { path } = arg
                 && let def_id = path.def_id()
                 && let Some(trait_) = cache.traits.get(&def_id)
-                && trait_.items.iter().any(|at| at.is_ty_associated_type())
+                && trait_.items.iter().any(|at| at.is_required_associated_type())
             {
                 for assoc_ty in &trait_.items {
-                    if let clean::ItemKind::TyAssocTypeItem(_generics, bounds) = &assoc_ty.kind
+                    if let clean::ItemKind::RequiredAssocTypeItem(_generics, bounds) =
+                        &assoc_ty.kind
                         && let Some(name) = assoc_ty.name
                     {
                         let idx = -isize::try_from(rgen.len() + 1).unwrap();
diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs
index e99e2f04b2c..af39d15f671 100644
--- a/src/librustdoc/html/render/sidebar.rs
+++ b/src/librustdoc/html/render/sidebar.rs
@@ -282,10 +282,10 @@ fn sidebar_trait<'a>(
         res
     }
 
-    let req_assoc = filter_items(&t.items, |m| m.is_ty_associated_type(), "associatedtype");
+    let req_assoc = filter_items(&t.items, |m| m.is_required_associated_type(), "associatedtype");
     let prov_assoc = filter_items(&t.items, |m| m.is_associated_type(), "associatedtype");
     let req_assoc_const =
-        filter_items(&t.items, |m| m.is_ty_associated_const(), "associatedconstant");
+        filter_items(&t.items, |m| m.is_required_associated_const(), "associatedconstant");
     let prov_assoc_const =
         filter_items(&t.items, |m| m.is_associated_const(), "associatedconstant");
     let req_method = filter_items(&t.items, |m| m.is_ty_method(), "tymethod");
diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index 9e5cf497211..04eeee37fe8 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -2592,7 +2592,12 @@ class DocSearch {
             const writeFn = (fnType, result) => {
                 if (fnType.id < 0) {
                     if (fnParamNames[-1 - fnType.id] === "") {
-                        for (const nested of fnType.generics) {
+                        // Normally, there's no need to shown an unhighlighted
+                        // where clause, but if it's impl Trait, then we do.
+                        const generics = fnType.generics.length > 0 ?
+                            fnType.generics :
+                            obj.type.where_clause[-1 - fnType.id];
+                        for (const nested of generics) {
                             writeFn(nested, result);
                         }
                         return;
diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs
index bb967b7f163..583d0214a46 100644
--- a/src/librustdoc/json/conversions.rs
+++ b/src/librustdoc/json/conversions.rs
@@ -6,7 +6,7 @@
 
 use rustc_abi::ExternAbi;
 use rustc_ast::ast;
-use rustc_attr::DeprecatedSince;
+use rustc_attr_parsing::DeprecatedSince;
 use rustc_hir::def::{CtorKind, DefKind};
 use rustc_hir::def_id::DefId;
 use rustc_metadata::rendered_const;
@@ -215,8 +215,8 @@ where
     }
 }
 
-pub(crate) fn from_deprecation(deprecation: rustc_attr::Deprecation) -> Deprecation {
-    let rustc_attr::Deprecation { since, note, suggestion: _ } = deprecation;
+pub(crate) fn from_deprecation(deprecation: rustc_attr_parsing::Deprecation) -> Deprecation {
+    let rustc_attr_parsing::Deprecation { since, note, suggestion: _ } = deprecation;
     let since = match since {
         DeprecatedSince::RustcVersion(version) => Some(version.to_string()),
         DeprecatedSince::Future => Some("TBD".to_owned()),
@@ -319,7 +319,9 @@ fn from_clean_item(item: clean::Item, renderer: &JsonRenderer<'_>) -> ItemEnum {
         TraitItem(t) => ItemEnum::Trait((*t).into_json(renderer)),
         TraitAliasItem(t) => ItemEnum::TraitAlias(t.into_json(renderer)),
         MethodItem(m, _) => ItemEnum::Function(from_function(m, true, header.unwrap(), renderer)),
-        TyMethodItem(m) => ItemEnum::Function(from_function(m, false, header.unwrap(), renderer)),
+        RequiredMethodItem(m) => {
+            ItemEnum::Function(from_function(m, false, header.unwrap(), renderer))
+        }
         ImplItem(i) => ItemEnum::Impl((*i).into_json(renderer)),
         StaticItem(s) => ItemEnum::Static(convert_static(s, rustc_hir::Safety::Safe, renderer)),
         ForeignStaticItem(s, safety) => ItemEnum::Static(convert_static(s, safety, renderer)),
@@ -339,15 +341,15 @@ fn from_clean_item(item: clean::Item, renderer: &JsonRenderer<'_>) -> ItemEnum {
             })
         }
         // FIXME(generic_const_items): Add support for generic associated consts.
-        TyAssocConstItem(_generics, ty) => {
+        RequiredAssocConstItem(_generics, ty) => {
             ItemEnum::AssocConst { type_: (*ty).into_json(renderer), value: None }
         }
         // FIXME(generic_const_items): Add support for generic associated consts.
-        AssocConstItem(ci) => ItemEnum::AssocConst {
+        ProvidedAssocConstItem(ci) | ImplAssocConstItem(ci) => ItemEnum::AssocConst {
             type_: ci.type_.into_json(renderer),
             value: Some(ci.kind.expr(renderer.tcx)),
         },
-        TyAssocTypeItem(g, b) => ItemEnum::AssocType {
+        RequiredAssocTypeItem(g, b) => ItemEnum::AssocType {
             generics: g.into_json(renderer),
             bounds: b.into_json(renderer),
             type_: None,
@@ -639,7 +641,7 @@ impl FromClean<clean::BareFunctionDecl> for FunctionPointer {
         let clean::BareFunctionDecl { safety, generic_params, decl, abi } = bare_decl;
         FunctionPointer {
             header: FunctionHeader {
-                is_unsafe: matches!(safety, rustc_hir::Safety::Unsafe),
+                is_unsafe: safety.is_unsafe(),
                 is_const: false,
                 is_async: false,
                 abi: convert_abi(abi),
@@ -669,7 +671,7 @@ impl FromClean<clean::Trait> for Trait {
     fn from_clean(trait_: clean::Trait, renderer: &JsonRenderer<'_>) -> Self {
         let tcx = renderer.tcx;
         let is_auto = trait_.is_auto(tcx);
-        let is_unsafe = trait_.safety(tcx) == rustc_hir::Safety::Unsafe;
+        let is_unsafe = trait_.safety(tcx).is_unsafe();
         let is_dyn_compatible = trait_.is_dyn_compatible(tcx);
         let clean::Trait { items, generics, bounds, .. } = trait_;
         Trait {
@@ -711,7 +713,7 @@ impl FromClean<clean::Impl> for Impl {
             ty::ImplPolarity::Negative => true,
         };
         Impl {
-            is_unsafe: safety == rustc_hir::Safety::Unsafe,
+            is_unsafe: safety.is_unsafe(),
             generics: generics.into_json(renderer),
             provided_trait_methods: provided_trait_methods
                 .into_iter()
@@ -840,7 +842,7 @@ fn convert_static(
     Static {
         type_: (*stat.type_).into_json(renderer),
         is_mutable: stat.mutability == ast::Mutability::Mut,
-        is_unsafe: safety == rustc_hir::Safety::Unsafe,
+        is_unsafe: safety.is_unsafe(),
         expr: stat
             .expr
             .map(|e| rendered_const(tcx, tcx.hir().body(e), tcx.hir().body_owner_def_id(e)))
diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs
index a384c286039..ff1c9c61720 100644
--- a/src/librustdoc/lib.rs
+++ b/src/librustdoc/lib.rs
@@ -36,7 +36,7 @@ extern crate pulldown_cmark;
 extern crate rustc_abi;
 extern crate rustc_ast;
 extern crate rustc_ast_pretty;
-extern crate rustc_attr;
+extern crate rustc_attr_parsing;
 extern crate rustc_data_structures;
 extern crate rustc_driver;
 extern crate rustc_errors;
@@ -642,6 +642,15 @@ fn opts() -> Vec<RustcOptGroup> {
             "Includes trait implementations and other crate info from provided path. Only use with --merge=finalize",
             "path/to/doc.parts/<crate-name>",
         ),
+        opt(Unstable, Flag, "", "html-no-source", "Disable HTML source code pages generation", ""),
+        opt(
+            Unstable,
+            Multi,
+            "",
+            "doctest-compilation-args",
+            "",
+            "add arguments to be used when compiling doctests",
+        ),
         // deprecated / removed options
         opt(Unstable, FlagMulti, "", "disable-minification", "removed", ""),
         opt(
@@ -684,7 +693,6 @@ fn opts() -> Vec<RustcOptGroup> {
             "removed, see issue #44136 <https://github.com/rust-lang/rust/issues/44136> for more information",
             "[rust]",
         ),
-        opt(Unstable, Flag, "", "html-no-source", "Disable HTML source code pages generation", ""),
     ]
 }
 
@@ -846,58 +854,51 @@ fn main_args(
 
     let config = core::create_config(input, options, &render_options, using_internal_features);
 
+    let registered_lints = config.register_lints.is_some();
+
     interface::run_compiler(config, |compiler| {
         let sess = &compiler.sess;
 
         if sess.opts.describe_lints {
-            rustc_driver::describe_lints(sess);
+            rustc_driver::describe_lints(sess, registered_lints);
             return;
         }
 
-        compiler.enter(|queries| {
-            let mut gcx = queries.global_ctxt();
-            if sess.dcx().has_errors().is_some() {
-                sess.dcx().fatal("Compilation failed, aborting rustdoc");
+        let krate = rustc_interface::passes::parse(sess);
+        if sess.dcx().has_errors().is_some() {
+            sess.dcx().fatal("Compilation failed, aborting rustdoc");
+        }
+
+        rustc_interface::create_and_enter_global_ctxt(&compiler, krate, |tcx| {
+            let (krate, render_opts, mut cache) = sess.time("run_global_ctxt", || {
+                core::run_global_ctxt(tcx, show_coverage, render_options, output_format)
+            });
+            info!("finished with rustc");
+
+            if let Some(options) = scrape_examples_options {
+                return scrape_examples::run(krate, render_opts, cache, tcx, options, bin_crate);
             }
 
-            gcx.enter(|tcx| {
-                let (krate, render_opts, mut cache) = sess.time("run_global_ctxt", || {
-                    core::run_global_ctxt(tcx, show_coverage, render_options, output_format)
-                });
-                info!("finished with rustc");
-
-                if let Some(options) = scrape_examples_options {
-                    return scrape_examples::run(
-                        krate,
-                        render_opts,
-                        cache,
-                        tcx,
-                        options,
-                        bin_crate,
-                    );
-                }
-
-                cache.crate_version = crate_version;
-
-                if show_coverage {
-                    // if we ran coverage, bail early, we don't need to also generate docs at this point
-                    // (also we didn't load in any of the useful passes)
-                    return;
-                } else if run_check {
-                    // Since we're in "check" mode, no need to generate anything beyond this point.
-                    return;
-                }
-
-                info!("going to format");
-                match output_format {
-                    config::OutputFormat::Html => sess.time("render_html", || {
-                        run_renderer::<html::render::Context<'_>>(krate, render_opts, cache, tcx)
-                    }),
-                    config::OutputFormat::Json => sess.time("render_json", || {
-                        run_renderer::<json::JsonRenderer<'_>>(krate, render_opts, cache, tcx)
-                    }),
-                }
-            })
+            cache.crate_version = crate_version;
+
+            if show_coverage {
+                // if we ran coverage, bail early, we don't need to also generate docs at this point
+                // (also we didn't load in any of the useful passes)
+                return;
+            } else if run_check {
+                // Since we're in "check" mode, no need to generate anything beyond this point.
+                return;
+            }
+
+            info!("going to format");
+            match output_format {
+                config::OutputFormat::Html => sess.time("render_html", || {
+                    run_renderer::<html::render::Context<'_>>(krate, render_opts, cache, tcx)
+                }),
+                config::OutputFormat::Json => sess.time("render_json", || {
+                    run_renderer::<json::JsonRenderer<'_>>(krate, render_opts, cache, tcx)
+                }),
+            }
         })
     })
 }
diff --git a/src/librustdoc/passes/check_doc_test_visibility.rs b/src/librustdoc/passes/check_doc_test_visibility.rs
index c288a3cf2a4..0fefd13f763 100644
--- a/src/librustdoc/passes/check_doc_test_visibility.rs
+++ b/src/librustdoc/passes/check_doc_test_visibility.rs
@@ -72,10 +72,11 @@ pub(crate) fn should_have_doc_example(cx: &DocContext<'_>, item: &clean::Item) -
                 | clean::ForeignFunctionItem(..)
                 | clean::ForeignStaticItem(..)
                 | clean::ForeignTypeItem
-                | clean::AssocConstItem(..)
                 | clean::AssocTypeItem(..)
-                | clean::TyAssocConstItem(..)
-                | clean::TyAssocTypeItem(..)
+                | clean::RequiredAssocConstItem(..)
+                | clean::ProvidedAssocConstItem(..)
+                | clean::ImplAssocConstItem(..)
+                | clean::RequiredAssocTypeItem(..)
                 // check for trait impl
                 | clean::ImplItem(box clean::Impl { trait_: Some(_), .. })
         )
diff --git a/src/librustdoc/passes/propagate_stability.rs b/src/librustdoc/passes/propagate_stability.rs
index a81b130a218..4c682c3d4ca 100644
--- a/src/librustdoc/passes/propagate_stability.rs
+++ b/src/librustdoc/passes/propagate_stability.rs
@@ -6,7 +6,7 @@
 //! [`core::error`] module is marked as stable since 1.81.0, so we want to show
 //! [`core::error::Error`] as stable since 1.81.0 as well.
 
-use rustc_attr::{Stability, StabilityLevel};
+use rustc_attr_parsing::{Stability, StabilityLevel};
 use rustc_hir::def_id::CRATE_DEF_ID;
 
 use crate::clean::{Crate, Item, ItemId, ItemKind};
@@ -67,11 +67,12 @@ impl DocFolder for StabilityPropagator<'_, '_> {
                     // Don't inherit the parent's stability for these items, because they
                     // are potentially accessible even if the parent is more unstable.
                     ItemKind::ImplItem(..)
-                    | ItemKind::TyMethodItem(..)
+                    | ItemKind::RequiredMethodItem(..)
                     | ItemKind::MethodItem(..)
-                    | ItemKind::TyAssocConstItem(..)
-                    | ItemKind::AssocConstItem(..)
-                    | ItemKind::TyAssocTypeItem(..)
+                    | ItemKind::RequiredAssocConstItem(..)
+                    | ItemKind::ProvidedAssocConstItem(..)
+                    | ItemKind::ImplAssocConstItem(..)
+                    | ItemKind::RequiredAssocTypeItem(..)
                     | ItemKind::AssocTypeItem(..)
                     | ItemKind::PrimitiveItem(..)
                     | ItemKind::KeywordItem => own_stability,
diff --git a/src/librustdoc/passes/stripper.rs b/src/librustdoc/passes/stripper.rs
index 60909754b33..eedbbca0f8d 100644
--- a/src/librustdoc/passes/stripper.rs
+++ b/src/librustdoc/passes/stripper.rs
@@ -79,7 +79,10 @@ impl DocFolder for Stripper<'_, '_> {
                 }
             }
 
-            clean::MethodItem(..) | clean::AssocConstItem(..) | clean::AssocTypeItem(..) => {
+            clean::MethodItem(..)
+            | clean::ProvidedAssocConstItem(..)
+            | clean::ImplAssocConstItem(..)
+            | clean::AssocTypeItem(..) => {
                 let item_id = i.item_id;
                 if item_id.is_local()
                     && !self.effective_visibilities.is_reachable(self.tcx, item_id.expect_def_id())
@@ -118,7 +121,9 @@ impl DocFolder for Stripper<'_, '_> {
             clean::ImplItem(..) => {}
 
             // tymethods etc. have no control over privacy
-            clean::TyMethodItem(..) | clean::TyAssocConstItem(..) | clean::TyAssocTypeItem(..) => {}
+            clean::RequiredMethodItem(..)
+            | clean::RequiredAssocConstItem(..)
+            | clean::RequiredAssocTypeItem(..) => {}
 
             // Proc-macros are always public
             clean::ProcMacroItem(..) => {}
diff --git a/src/librustdoc/visit.rs b/src/librustdoc/visit.rs
index c2e8ffd7665..b8b619514aa 100644
--- a/src/librustdoc/visit.rs
+++ b/src/librustdoc/visit.rs
@@ -35,7 +35,7 @@ pub(crate) trait DocVisitor<'a>: Sized {
             | StaticItem(_)
             | ConstantItem(..)
             | TraitAliasItem(_)
-            | TyMethodItem(_)
+            | RequiredMethodItem(_)
             | MethodItem(_, _)
             | StructFieldItem(_)
             | ForeignFunctionItem(..)
@@ -44,9 +44,10 @@ pub(crate) trait DocVisitor<'a>: Sized {
             | MacroItem(_)
             | ProcMacroItem(_)
             | PrimitiveItem(_)
-            | TyAssocConstItem(..)
-            | AssocConstItem(..)
-            | TyAssocTypeItem(..)
+            | RequiredAssocConstItem(..)
+            | ProvidedAssocConstItem(..)
+            | ImplAssocConstItem(..)
+            | RequiredAssocTypeItem(..)
             | AssocTypeItem(..)
             | KeywordItem => {}
         }