about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_hir/src/definitions.rs2
-rw-r--r--compiler/rustc_hir/src/hir.rs2
-rw-r--r--compiler/rustc_interface/src/passes.rs23
-rw-r--r--compiler/rustc_interface/src/queries.rs10
-rw-r--r--compiler/rustc_lint/src/context.rs10
-rw-r--r--compiler/rustc_metadata/src/creader.rs11
-rw-r--r--compiler/rustc_metadata/src/rmeta/decoder.rs12
-rw-r--r--compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs15
-rw-r--r--compiler/rustc_metadata/src/rmeta/encoder.rs16
-rw-r--r--compiler/rustc_middle/src/query/mod.rs5
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs1
-rw-r--r--compiler/rustc_parse/src/parser/diagnostics.rs58
-rw-r--r--compiler/rustc_resolve/src/build_reduced_graph.rs1
-rw-r--r--compiler/rustc_resolve/src/effective_visibilities.rs4
-rw-r--r--compiler/rustc_resolve/src/imports.rs12
-rw-r--r--compiler/rustc_resolve/src/late.rs9
-rw-r--r--compiler/rustc_resolve/src/late/diagnostics.rs4
-rw-r--r--compiler/rustc_resolve/src/lib.rs104
-rw-r--r--compiler/rustc_resolve/src/macros.rs4
-rw-r--r--compiler/rustc_trait_selection/src/solve/mod.rs73
-rw-r--r--compiler/rustc_trait_selection/src/solve/search_graph/mod.rs96
-rw-r--r--compiler/rustc_trait_selection/src/solve/search_graph/overflow.rs58
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs47
-rw-r--r--library/core/benches/array.rs10
-rw-r--r--library/core/src/ptr/const_ptr.rs20
-rw-r--r--library/core/src/ptr/mut_ptr.rs22
-rw-r--r--src/librustdoc/clean/types.rs12
-rw-r--r--src/librustdoc/core.rs24
-rw-r--r--src/librustdoc/html/format.rs16
-rw-r--r--src/librustdoc/html/markdown.rs16
-rw-r--r--src/librustdoc/lib.rs21
-rw-r--r--src/librustdoc/passes/collect_intra_doc_links.rs8
-rw-r--r--src/librustdoc/passes/collect_intra_doc_links/early.rs72
-rw-r--r--src/librustdoc/passes/collect_trait_impls.rs16
-rw-r--r--src/librustdoc/visit_lib.rs51
-rw-r--r--tests/rustdoc/intra-doc/basic.rs17
-rw-r--r--tests/ui/parser/suggest_misplaced_generics/enum.fixed9
-rw-r--r--tests/ui/parser/suggest_misplaced_generics/enum.rs9
-rw-r--r--tests/ui/parser/suggest_misplaced_generics/enum.stderr14
-rw-r--r--tests/ui/parser/suggest_misplaced_generics/existing_generics.rs9
-rw-r--r--tests/ui/parser/suggest_misplaced_generics/existing_generics.stderr10
-rw-r--r--tests/ui/parser/suggest_misplaced_generics/fn-complex-generics.fixed9
-rw-r--r--tests/ui/parser/suggest_misplaced_generics/fn-complex-generics.rs9
-rw-r--r--tests/ui/parser/suggest_misplaced_generics/fn-complex-generics.stderr14
-rw-r--r--tests/ui/parser/suggest_misplaced_generics/fn-invalid-generics.rs8
-rw-r--r--tests/ui/parser/suggest_misplaced_generics/fn-invalid-generics.stderr8
-rw-r--r--tests/ui/parser/suggest_misplaced_generics/fn-simple.fixed9
-rw-r--r--tests/ui/parser/suggest_misplaced_generics/fn-simple.rs9
-rw-r--r--tests/ui/parser/suggest_misplaced_generics/fn-simple.stderr14
-rw-r--r--tests/ui/parser/suggest_misplaced_generics/struct.fixed9
-rw-r--r--tests/ui/parser/suggest_misplaced_generics/struct.rs9
-rw-r--r--tests/ui/parser/suggest_misplaced_generics/struct.stderr14
-rw-r--r--tests/ui/parser/suggest_misplaced_generics/trait.fixed11
-rw-r--r--tests/ui/parser/suggest_misplaced_generics/trait.rs11
-rw-r--r--tests/ui/parser/suggest_misplaced_generics/trait.stderr14
-rw-r--r--tests/ui/parser/suggest_misplaced_generics/type.fixed9
-rw-r--r--tests/ui/parser/suggest_misplaced_generics/type.rs9
-rw-r--r--tests/ui/parser/suggest_misplaced_generics/type.stderr14
-rw-r--r--tests/ui/single-use-lifetime/issue-107998.rs9
-rw-r--r--tests/ui/single-use-lifetime/issue-107998.stderr30
-rw-r--r--tests/ui/traits/issue-103563.rs75
-rw-r--r--triagebot.toml3
62 files changed, 757 insertions, 473 deletions
diff --git a/compiler/rustc_hir/src/definitions.rs b/compiler/rustc_hir/src/definitions.rs
index 21cf214e47c..cd3c620cbb7 100644
--- a/compiler/rustc_hir/src/definitions.rs
+++ b/compiler/rustc_hir/src/definitions.rs
@@ -92,7 +92,7 @@ impl DefPathTable {
 /// The definition table containing node definitions.
 /// It holds the `DefPathTable` for `LocalDefId`s/`DefPath`s.
 /// It also stores mappings to convert `LocalDefId`s to/from `HirId`s.
-#[derive(Clone, Debug)]
+#[derive(Debug)]
 pub struct Definitions {
     table: DefPathTable,
     next_disambiguator: FxHashMap<(LocalDefId, DefPathData), u32>,
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index a063307af0c..7cb3b6e1525 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -3460,7 +3460,7 @@ pub struct Upvar {
 // The TraitCandidate's import_ids is empty if the trait is defined in the same module, and
 // has length > 0 if the trait is found through an chain of imports, starting with the
 // import/use statement in the scope where the trait is used.
-#[derive(Encodable, Decodable, Clone, Debug, HashStable_Generic)]
+#[derive(Encodable, Decodable, Debug, HashStable_Generic)]
 pub struct TraitCandidate {
     pub def_id: DefId,
     pub import_ids: SmallVec<[LocalDefId; 1]>,
diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs
index 33ebbb411ce..c8d8afae39e 100644
--- a/compiler/rustc_interface/src/passes.rs
+++ b/compiler/rustc_interface/src/passes.rs
@@ -35,13 +35,11 @@ use rustc_target::spec::PanicStrategy;
 use rustc_trait_selection::traits;
 
 use std::any::Any;
-use std::cell::RefCell;
 use std::ffi::OsString;
 use std::io::{self, BufWriter, Write};
 use std::marker::PhantomPinned;
 use std::path::{Path, PathBuf};
 use std::pin::Pin;
-use std::rc::Rc;
 use std::sync::{Arc, LazyLock};
 use std::{env, fs, iter};
 
@@ -131,21 +129,12 @@ mod boxed_resolver {
             f((&mut *resolver).as_mut().unwrap())
         }
 
-        pub fn to_resolver_outputs(resolver: Rc<RefCell<BoxedResolver>>) -> ty::ResolverOutputs {
-            match Rc::try_unwrap(resolver) {
-                Ok(resolver) => {
-                    let mut resolver = resolver.into_inner();
-                    // SAFETY: The resolver doesn't need to be pinned.
-                    let mut resolver = unsafe {
-                        resolver
-                            .0
-                            .as_mut()
-                            .map_unchecked_mut(|boxed_resolver| &mut boxed_resolver.resolver)
-                    };
-                    resolver.take().unwrap().into_outputs()
-                }
-                Err(resolver) => resolver.borrow_mut().access(|resolver| resolver.clone_outputs()),
-            }
+        pub fn into_outputs(mut self) -> ty::ResolverOutputs {
+            // SAFETY: The resolver doesn't need to be pinned.
+            let mut resolver = unsafe {
+                self.0.as_mut().map_unchecked_mut(|boxed_resolver| &mut boxed_resolver.resolver)
+            };
+            resolver.take().unwrap().into_outputs()
         }
     }
 }
diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs
index 6512695873e..67886b6b989 100644
--- a/compiler/rustc_interface/src/queries.rs
+++ b/compiler/rustc_interface/src/queries.rs
@@ -21,7 +21,6 @@ use rustc_span::symbol::sym;
 use rustc_span::Symbol;
 use std::any::Any;
 use std::cell::{RefCell, RefMut};
-use std::rc::Rc;
 use std::sync::Arc;
 
 /// Represent the result of a query.
@@ -88,7 +87,7 @@ pub struct Queries<'tcx> {
     parse: Query<ast::Crate>,
     crate_name: Query<Symbol>,
     register_plugins: Query<(ast::Crate, Lrc<LintStore>)>,
-    expansion: Query<(Lrc<ast::Crate>, Rc<RefCell<BoxedResolver>>, Lrc<LintStore>)>,
+    expansion: Query<(Lrc<ast::Crate>, BoxedResolver, Lrc<LintStore>)>,
     dep_graph: Query<DepGraph>,
     // This just points to what's in `gcx_cell`.
     gcx: Query<&'tcx GlobalCtxt<'tcx>>,
@@ -171,8 +170,7 @@ impl<'tcx> Queries<'tcx> {
 
     pub fn expansion(
         &self,
-    ) -> Result<QueryResult<'_, (Lrc<ast::Crate>, Rc<RefCell<BoxedResolver>>, Lrc<LintStore>)>>
-    {
+    ) -> Result<QueryResult<'_, (Lrc<ast::Crate>, BoxedResolver, Lrc<LintStore>)>> {
         trace!("expansion");
         self.expansion.compute(|| {
             let crate_name = *self.crate_name()?.borrow();
@@ -188,7 +186,7 @@ impl<'tcx> Queries<'tcx> {
             let krate = resolver.access(|resolver| {
                 passes::configure_and_expand(sess, &lint_store, krate, crate_name, resolver)
             })?;
-            Ok((Lrc::new(krate), Rc::new(RefCell::new(resolver)), lint_store))
+            Ok((Lrc::new(krate), resolver, lint_store))
         })
     }
 
@@ -217,7 +215,7 @@ impl<'tcx> Queries<'tcx> {
                 untracked,
                 global_ctxt: untracked_resolutions,
                 ast_lowering: untracked_resolver_for_lowering,
-            } = BoxedResolver::to_resolver_outputs(resolver);
+            } = resolver.into_outputs();
 
             let gcx = passes::create_global_ctxt(
                 self.compiler,
diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs
index d1d4bb37528..972240f42cf 100644
--- a/compiler/rustc_lint/src/context.rs
+++ b/compiler/rustc_lint/src/context.rs
@@ -837,9 +837,17 @@ pub trait LintContext: Sized {
                             (use_span, "'_".to_owned())
                         };
                         debug!(?deletion_span, ?use_span);
+
+                        // issue 107998 for the case such as a wrong function pointer type
+                        // `deletion_span` is empty and there is no need to report lifetime uses here
+                        let suggestions = if deletion_span.is_empty() {
+                            vec![(use_span, replace_lt)]
+                        } else {
+                            vec![(deletion_span, String::new()), (use_span, replace_lt)]
+                        };
                         db.multipart_suggestion(
                             msg,
-                            vec![(deletion_span, String::new()), (use_span, replace_lt)],
+                            suggestions,
                             Applicability::MachineApplicable,
                         );
                     }
diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs
index bf8b8aa2ce4..c357f294279 100644
--- a/compiler/rustc_metadata/src/creader.rs
+++ b/compiler/rustc_metadata/src/creader.rs
@@ -8,7 +8,7 @@ use rustc_ast::expand::allocator::AllocatorKind;
 use rustc_ast::{self as ast, *};
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::svh::Svh;
-use rustc_data_structures::sync::{Lrc, ReadGuard};
+use rustc_data_structures::sync::ReadGuard;
 use rustc_expand::base::SyntaxExtension;
 use rustc_hir::def_id::{CrateNum, LocalDefId, StableCrateId, LOCAL_CRATE};
 use rustc_hir::definitions::Definitions;
@@ -30,11 +30,10 @@ use proc_macro::bridge::client::ProcMacro;
 use std::ops::Fn;
 use std::path::Path;
 use std::time::Duration;
-use std::{cmp, env};
+use std::{cmp, env, iter};
 
-#[derive(Clone)]
 pub struct CStore {
-    metas: IndexVec<CrateNum, Option<Lrc<CrateMetadata>>>,
+    metas: IndexVec<CrateNum, Option<Box<CrateMetadata>>>,
     injected_panic_runtime: Option<CrateNum>,
     /// This crate needs an allocator and either provides it itself, or finds it in a dependency.
     /// If the above is true, then this field denotes the kind of the found allocator.
@@ -153,7 +152,7 @@ impl CStore {
 
     fn set_crate_data(&mut self, cnum: CrateNum, data: CrateMetadata) {
         assert!(self.metas[cnum].is_none(), "Overwriting crate metadata entry");
-        self.metas[cnum] = Some(Lrc::new(data));
+        self.metas[cnum] = Some(Box::new(data));
     }
 
     pub(crate) fn iter_crate_data(&self) -> impl Iterator<Item = (CrateNum, &CrateMetadata)> {
@@ -245,7 +244,7 @@ impl CStore {
             // order to make array indices in `metas` match with the
             // corresponding `CrateNum`. This first entry will always remain
             // `None`.
-            metas: IndexVec::from_elem_n(None, 1),
+            metas: IndexVec::from_iter(iter::once(None)),
             injected_panic_runtime: None,
             allocator_kind: None,
             alloc_error_handler_kind: None,
diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs
index 800f85063c4..3457e51f8e6 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder.rs
@@ -1169,15 +1169,9 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
     }
 
     /// Decodes all trait impls in the crate (for rustdoc).
-    fn get_trait_impls(self) -> impl Iterator<Item = (DefId, DefId, Option<SimplifiedType>)> + 'a {
-        self.cdata.trait_impls.iter().flat_map(move |(&(trait_cnum_raw, trait_index), impls)| {
-            let trait_def_id = DefId {
-                krate: self.cnum_map[CrateNum::from_u32(trait_cnum_raw)],
-                index: trait_index,
-            };
-            impls.decode(self).map(move |(impl_index, simplified_self_ty)| {
-                (trait_def_id, self.local_def_id(impl_index), simplified_self_ty)
-            })
+    fn get_trait_impls(self) -> impl Iterator<Item = DefId> + 'a {
+        self.cdata.trait_impls.values().flat_map(move |impls| {
+            impls.decode(self).map(move |(impl_index, _)| self.local_def_id(impl_index))
         })
     }
 
diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
index b12f9b5c917..8082a890320 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
@@ -304,6 +304,7 @@ provide! { tcx, def_id, other, cdata,
     extra_filename => { cdata.root.extra_filename.clone() }
 
     traits_in_crate => { tcx.arena.alloc_from_iter(cdata.get_traits()) }
+    trait_impls_in_crate => { tcx.arena.alloc_from_iter(cdata.get_trait_impls()) }
     implementations_of_trait => { cdata.get_implementations_of_trait(tcx, other) }
     crate_incoherent_impls => { cdata.get_incoherent_impls(tcx, other) }
 
@@ -608,20 +609,6 @@ impl CStore {
     ) -> Span {
         self.get_crate_data(cnum).get_proc_macro_quoted_span(id, sess)
     }
-
-    /// Decodes all trait impls in the crate (for rustdoc).
-    pub fn trait_impls_in_crate_untracked(
-        &self,
-        cnum: CrateNum,
-    ) -> impl Iterator<Item = (DefId, DefId, Option<SimplifiedType>)> + '_ {
-        self.get_crate_data(cnum).get_trait_impls()
-    }
-
-    pub fn is_doc_hidden_untracked(&self, def_id: DefId) -> bool {
-        self.get_crate_data(def_id.krate)
-            .get_attr_flags(def_id.index)
-            .contains(AttrFlags::IS_DOC_HIDDEN)
-    }
 }
 
 impl CrateStore for CStore {
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index 263c71ae702..060ade8a42f 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -2256,6 +2256,22 @@ pub fn provide(providers: &mut Providers) {
             traits.sort_by_cached_key(|&def_id| tcx.def_path_hash(def_id));
             tcx.arena.alloc_slice(&traits)
         },
+        trait_impls_in_crate: |tcx, cnum| {
+            assert_eq!(cnum, LOCAL_CRATE);
+
+            let mut trait_impls = Vec::new();
+            for id in tcx.hir().items() {
+                if matches!(tcx.def_kind(id.owner_id), DefKind::Impl)
+                    && tcx.impl_trait_ref(id.owner_id).is_some()
+                {
+                    trait_impls.push(id.owner_id.to_def_id())
+                }
+            }
+
+            // Bring everything into deterministic order.
+            trait_impls.sort_by_cached_key(|&def_id| tcx.def_path_hash(def_id));
+            tcx.arena.alloc_slice(&trait_impls)
+        },
 
         ..*providers
     }
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index d37d6b37a37..71c9058a696 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -1836,6 +1836,11 @@ rustc_queries! {
         separate_provide_extern
     }
 
+    query trait_impls_in_crate(_: CrateNum) -> &'tcx [DefId] {
+        desc { "fetching all trait impls in a crate" }
+        separate_provide_extern
+    }
+
     /// The list of symbols exported from the given crate.
     ///
     /// - All names contained in `exported_symbols(cnum)` are guaranteed to
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index 4ed0484438f..6d8c9d73763 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -187,6 +187,7 @@ pub struct ResolverGlobalCtxt {
     pub registered_tools: RegisteredTools,
     pub doc_link_resolutions: FxHashMap<LocalDefId, DocLinkResMap>,
     pub doc_link_traits_in_scope: FxHashMap<LocalDefId, Vec<DefId>>,
+    pub all_macro_rules: FxHashMap<Symbol, Res<ast::NodeId>>,
 }
 
 /// Resolutions that should only be used for lowering.
diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs
index cd9d85b1d91..49eff41329c 100644
--- a/compiler/rustc_parse/src/parser/diagnostics.rs
+++ b/compiler/rustc_parse/src/parser/diagnostics.rs
@@ -284,7 +284,7 @@ impl<'a> Parser<'a> {
         self.sess.source_map().span_to_snippet(span)
     }
 
-    pub(super) fn expected_ident_found(&self) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
+    pub(super) fn expected_ident_found(&mut self) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
         let valid_follow = &[
             TokenKind::Eq,
             TokenKind::Colon,
@@ -324,7 +324,61 @@ impl<'a> Parser<'a> {
             suggest_raw,
             suggest_remove_comma,
         };
-        err.into_diagnostic(&self.sess.span_diagnostic)
+        let mut err = err.into_diagnostic(&self.sess.span_diagnostic);
+
+        // if the token we have is a `<`
+        // it *might* be a misplaced generic
+        if self.token == token::Lt {
+            // all keywords that could have generic applied
+            let valid_prev_keywords =
+                [kw::Fn, kw::Type, kw::Struct, kw::Enum, kw::Union, kw::Trait];
+
+            // If we've expected an identifier,
+            // and the current token is a '<'
+            // if the previous token is a valid keyword
+            // that might use a generic, then suggest a correct
+            // generic placement (later on)
+            let maybe_keyword = self.prev_token.clone();
+            if valid_prev_keywords.into_iter().any(|x| maybe_keyword.is_keyword(x)) {
+                // if we have a valid keyword, attempt to parse generics
+                // also obtain the keywords symbol
+                match self.parse_generics() {
+                    Ok(generic) => {
+                        if let TokenKind::Ident(symbol, _) = maybe_keyword.kind {
+                            let ident_name = symbol;
+                            // at this point, we've found something like
+                            // `fn <T>id`
+                            // and current token should be Ident with the item name (i.e. the function name)
+                            // if there is a `<` after the fn name, then don't show a suggestion, show help
+
+                            if !self.look_ahead(1, |t| *t == token::Lt) &&
+                                let Ok(snippet) = self.sess.source_map().span_to_snippet(generic.span) {
+                                    err.multipart_suggestion_verbose(
+                                        format!("place the generic parameter name after the {ident_name} name"),
+                                        vec![
+                                            (self.token.span.shrink_to_hi(), snippet),
+                                            (generic.span, String::new())
+                                        ],
+                                        Applicability::MaybeIncorrect,
+                                    );
+                                } else {
+                                    err.help(format!(
+                                        "place the generic parameter name after the {ident_name} name"
+                                    ));
+                                }
+                        }
+                    }
+                    Err(err) => {
+                        // if there's an error parsing the generics,
+                        // then don't do a misplaced generics suggestion
+                        // and emit the expected ident error instead;
+                        err.cancel();
+                    }
+                }
+            }
+        }
+
+        err
     }
 
     pub(super) fn expected_one_of_not_found(
diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs
index e74bb0a9a4f..44f3bf1be05 100644
--- a/compiler/rustc_resolve/src/build_reduced_graph.rs
+++ b/compiler/rustc_resolve/src/build_reduced_graph.rs
@@ -1251,6 +1251,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
             };
             let binding = (res, vis, span, expansion).to_name_binding(self.r.arenas);
             self.r.set_binding_parent_module(binding, parent_scope.module);
+            self.r.all_macro_rules.insert(ident.name, res);
             if is_macro_export {
                 let import = self.r.arenas.alloc_import(Import {
                     kind: ImportKind::MacroExport,
diff --git a/compiler/rustc_resolve/src/effective_visibilities.rs b/compiler/rustc_resolve/src/effective_visibilities.rs
index b8efa3f8b27..ab68f25a886 100644
--- a/compiler/rustc_resolve/src/effective_visibilities.rs
+++ b/compiler/rustc_resolve/src/effective_visibilities.rs
@@ -29,7 +29,7 @@ impl ParentId<'_> {
     }
 }
 
-pub struct EffectiveVisibilitiesVisitor<'r, 'a> {
+pub(crate) struct EffectiveVisibilitiesVisitor<'r, 'a> {
     r: &'r mut Resolver<'a>,
     def_effective_visibilities: EffectiveVisibilities,
     /// While walking import chains we need to track effective visibilities per-binding, and def id
@@ -78,7 +78,7 @@ impl<'r, 'a> EffectiveVisibilitiesVisitor<'r, 'a> {
     /// Fills the `Resolver::effective_visibilities` table with public & exported items
     /// For now, this doesn't resolve macros (FIXME) and cannot resolve Impl, as we
     /// need access to a TyCtxt for that.
-    pub fn compute_effective_visibilities<'c>(r: &'r mut Resolver<'a>, krate: &'c Crate) {
+    pub(crate) fn compute_effective_visibilities<'c>(r: &'r mut Resolver<'a>, krate: &'c Crate) {
         let mut visitor = EffectiveVisibilitiesVisitor {
             r,
             def_effective_visibilities: Default::default(),
diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs
index 00f65ac37b6..8d1c789dea7 100644
--- a/compiler/rustc_resolve/src/imports.rs
+++ b/compiler/rustc_resolve/src/imports.rs
@@ -33,7 +33,7 @@ type Res = def::Res<NodeId>;
 
 /// Contains data for specific kinds of imports.
 #[derive(Clone)]
-pub enum ImportKind<'a> {
+pub(crate) enum ImportKind<'a> {
     Single {
         /// `source` in `use prefix::source as target`.
         source: Ident,
@@ -157,11 +157,11 @@ pub(crate) struct Import<'a> {
 }
 
 impl<'a> Import<'a> {
-    pub fn is_glob(&self) -> bool {
+    pub(crate) fn is_glob(&self) -> bool {
         matches!(self.kind, ImportKind::Glob { .. })
     }
 
-    pub fn is_nested(&self) -> bool {
+    pub(crate) fn is_nested(&self) -> bool {
         match self.kind {
             ImportKind::Single { nested, .. } => nested,
             _ => false,
@@ -405,7 +405,7 @@ struct UnresolvedImportError {
     candidates: Option<Vec<ImportSuggestion>>,
 }
 
-pub struct ImportResolver<'a, 'b> {
+pub(crate) struct ImportResolver<'a, 'b> {
     pub r: &'a mut Resolver<'b>,
 }
 
@@ -420,7 +420,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
 
     /// Resolves all imports for the crate. This method performs the fixed-
     /// point iteration.
-    pub fn resolve_imports(&mut self) {
+    pub(crate) fn resolve_imports(&mut self) {
         let mut prev_num_indeterminates = self.r.indeterminate_imports.len() + 1;
         while self.r.indeterminate_imports.len() < prev_num_indeterminates {
             prev_num_indeterminates = self.r.indeterminate_imports.len();
@@ -433,7 +433,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
         }
     }
 
-    pub fn finalize_imports(&mut self) {
+    pub(crate) fn finalize_imports(&mut self) {
         for module in self.r.arenas.local_modules().iter() {
             self.finalize_resolutions_in(module);
         }
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index bd74a010fa3..d4c056f12f6 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -56,7 +56,7 @@ struct BindingInfo {
 }
 
 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
-pub enum PatternSource {
+pub(crate) enum PatternSource {
     Match,
     Let,
     For,
@@ -70,7 +70,7 @@ enum IsRepeatExpr {
 }
 
 impl PatternSource {
-    pub fn descr(self) -> &'static str {
+    fn descr(self) -> &'static str {
         match self {
             PatternSource::Match => "match binding",
             PatternSource::Let => "let binding",
@@ -2374,9 +2374,8 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                 // Maintain macro_rules scopes in the same way as during early resolution
                 // for diagnostics and doc links.
                 if macro_def.macro_rules {
-                    let (macro_rules_scope, _) =
-                        self.r.macro_rules_scope(self.r.local_def_id(item.id));
-                    self.parent_scope.macro_rules = macro_rules_scope;
+                    let def_id = self.r.local_def_id(item.id);
+                    self.parent_scope.macro_rules = self.r.macro_rules_scopes[&def_id];
                 }
             }
 
diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs
index a9dbb3ca131..c6d27ec69c5 100644
--- a/compiler/rustc_resolve/src/late/diagnostics.rs
+++ b/compiler/rustc_resolve/src/late/diagnostics.rs
@@ -2626,7 +2626,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
 }
 
 /// Report lifetime/lifetime shadowing as an error.
-pub fn signal_lifetime_shadowing(sess: &Session, orig: Ident, shadower: Ident) {
+pub(super) fn signal_lifetime_shadowing(sess: &Session, orig: Ident, shadower: Ident) {
     let mut err = struct_span_err!(
         sess,
         shadower.span,
@@ -2641,7 +2641,7 @@ pub fn signal_lifetime_shadowing(sess: &Session, orig: Ident, shadower: Ident) {
 
 /// Shadowing involving a label is only a warning for historical reasons.
 //FIXME: make this a proper lint.
-pub fn signal_label_shadowing(sess: &Session, orig: Span, shadower: Ident) {
+pub(super) fn signal_label_shadowing(sess: &Session, orig: Span, shadower: Ident) {
     let name = shadower.name;
     let shadower = shadower.span;
     let mut err = sess.struct_span_warn(
diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs
index e61e83189c3..3c70e9c93e3 100644
--- a/compiler/rustc_resolve/src/lib.rs
+++ b/compiler/rustc_resolve/src/lib.rs
@@ -21,8 +21,6 @@
 #[macro_use]
 extern crate tracing;
 
-pub use rustc_hir::def::{Namespace, PerNS};
-
 use rustc_arena::{DroplessArena, TypedArena};
 use rustc_ast::node_id::NodeMap;
 use rustc_ast::{self as ast, NodeId, CRATE_NODE_ID};
@@ -32,8 +30,8 @@ use rustc_data_structures::intern::Interned;
 use rustc_data_structures::sync::{Lrc, RwLock};
 use rustc_errors::{Applicability, DiagnosticBuilder, ErrorGuaranteed};
 use rustc_expand::base::{DeriveResolutions, SyntaxExtension, SyntaxExtensionKind};
-use rustc_hir::def::Namespace::*;
-use rustc_hir::def::{self, CtorOf, DefKind, DocLinkResMap, LifetimeRes, PartialRes};
+use rustc_hir::def::Namespace::{self, *};
+use rustc_hir::def::{self, CtorOf, DefKind, DocLinkResMap, LifetimeRes, PartialRes, PerNS};
 use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId};
 use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE};
 use rustc_hir::definitions::{DefPathData, Definitions};
@@ -86,7 +84,7 @@ enum Weak {
 }
 
 #[derive(Copy, Clone, PartialEq, Debug)]
-pub enum Determinacy {
+enum Determinacy {
     Determined,
     Undetermined,
 }
@@ -257,7 +255,7 @@ enum VisResolutionError<'a> {
 /// A minimal representation of a path segment. We use this in resolve because we synthesize 'path
 /// segments' which don't have the rest of an AST or HIR `PathSegment`.
 #[derive(Clone, Copy, Debug)]
-pub struct Segment {
+struct Segment {
     ident: Ident,
     id: Option<NodeId>,
     /// Signals whether this `PathSegment` has generic arguments. Used to avoid providing
@@ -380,7 +378,7 @@ impl ModuleOrUniformRoot<'_> {
     }
 }
 
-#[derive(Clone, Debug)]
+#[derive(Debug)]
 enum PathResult<'a> {
     Module(ModuleOrUniformRoot<'a>),
     NonModule(PartialRes),
@@ -435,7 +433,7 @@ enum ModuleKind {
 
 impl ModuleKind {
     /// Get name of the module.
-    pub fn name(&self) -> Option<Symbol> {
+    fn name(&self) -> Option<Symbol> {
         match self {
             ModuleKind::Block => None,
             ModuleKind::Def(.., name) => Some(*name),
@@ -471,7 +469,7 @@ type Resolutions<'a> = RefCell<FxIndexMap<BindingKey, &'a RefCell<NameResolution
 /// * curly-braced block with statements
 ///
 /// You can use [`ModuleData::kind`] to determine the kind of module this is.
-pub struct ModuleData<'a> {
+struct ModuleData<'a> {
     /// The direct parent module (it may not be a `mod`, however).
     parent: Option<Module<'a>>,
     /// What kind of module this is, because this may not be a `mod`.
@@ -570,7 +568,7 @@ impl<'a> ModuleData<'a> {
     }
 
     // Public for rustdoc.
-    pub fn def_id(&self) -> DefId {
+    fn def_id(&self) -> DefId {
         self.opt_def_id().expect("`ModuleData::def_id` is called on a block module")
     }
 
@@ -628,7 +626,7 @@ impl<'a> fmt::Debug for ModuleData<'a> {
 
 /// Records a possibly-private value, type, or module definition.
 #[derive(Clone, Debug)]
-pub struct NameBinding<'a> {
+struct NameBinding<'a> {
     kind: NameBindingKind<'a>,
     ambiguity: Option<(&'a NameBinding<'a>, AmbiguityKind)>,
     expansion: LocalExpnId,
@@ -636,7 +634,7 @@ pub struct NameBinding<'a> {
     vis: ty::Visibility<DefId>,
 }
 
-pub trait ToNameBinding<'a> {
+trait ToNameBinding<'a> {
     fn to_name_binding(self, arenas: &'a ResolverArenas<'a>) -> &'a NameBinding<'a>;
 }
 
@@ -840,9 +838,9 @@ impl<'a> NameBinding<'a> {
 }
 
 #[derive(Default, Clone)]
-pub struct ExternPreludeEntry<'a> {
+struct ExternPreludeEntry<'a> {
     extern_crate_item: Option<&'a NameBinding<'a>>,
-    pub introduced_by_item: bool,
+    introduced_by_item: bool,
 }
 
 /// Used for better errors for E0773
@@ -1049,6 +1047,7 @@ pub struct Resolver<'a> {
     effective_visibilities: EffectiveVisibilities,
     doc_link_resolutions: FxHashMap<LocalDefId, DocLinkResMap>,
     doc_link_traits_in_scope: FxHashMap<LocalDefId, Vec<DefId>>,
+    all_macro_rules: FxHashMap<Symbol, Res>,
 }
 
 /// Nothing really interesting here; it just provides memory for the rest of the crate.
@@ -1147,7 +1146,7 @@ impl<'a> Resolver<'a> {
         self.node_id_to_def_id.get(&node).copied()
     }
 
-    pub fn local_def_id(&self, node: NodeId) -> LocalDefId {
+    fn local_def_id(&self, node: NodeId) -> LocalDefId {
         self.opt_local_def_id(node).unwrap_or_else(|| panic!("no entry for node id: `{:?}`", node))
     }
 
@@ -1199,10 +1198,6 @@ impl<'a> Resolver<'a> {
             self.cstore().item_generics_num_lifetimes(def_id, self.session)
         }
     }
-
-    pub fn sess(&self) -> &'a Session {
-        self.session
-    }
 }
 
 impl<'a> Resolver<'a> {
@@ -1379,6 +1374,7 @@ impl<'a> Resolver<'a> {
             effective_visibilities: Default::default(),
             doc_link_resolutions: Default::default(),
             doc_link_traits_in_scope: Default::default(),
+            all_macro_rules: Default::default(),
         };
 
         let root_parent_scope = ParentScope::module(graph_root, &resolver);
@@ -1399,14 +1395,14 @@ impl<'a> Resolver<'a> {
         self.arenas.new_module(parent, kind, expn_id, span, no_implicit_prelude, module_map)
     }
 
-    pub fn next_node_id(&mut self) -> NodeId {
+    fn next_node_id(&mut self) -> NodeId {
         let start = self.next_node_id;
         let next = start.as_u32().checked_add(1).expect("input too large; ran out of NodeIds");
         self.next_node_id = ast::NodeId::from_u32(next);
         start
     }
 
-    pub fn next_node_ids(&mut self, count: usize) -> std::ops::Range<NodeId> {
+    fn next_node_ids(&mut self, count: usize) -> std::ops::Range<NodeId> {
         let start = self.next_node_id;
         let end = start.as_usize().checked_add(count).expect("input too large; ran out of NodeIds");
         self.next_node_id = ast::NodeId::from_usize(end);
@@ -1457,6 +1453,7 @@ impl<'a> Resolver<'a> {
             registered_tools: self.registered_tools,
             doc_link_resolutions: self.doc_link_resolutions,
             doc_link_traits_in_scope: self.doc_link_traits_in_scope,
+            all_macro_rules: self.all_macro_rules,
         };
         let ast_lowering = ty::ResolverAstLowering {
             legacy_const_generic_args: self.legacy_const_generic_args,
@@ -1475,57 +1472,11 @@ impl<'a> Resolver<'a> {
         ResolverOutputs { global_ctxt, ast_lowering, untracked }
     }
 
-    pub fn clone_outputs(&self) -> ResolverOutputs {
-        let proc_macros = self.proc_macros.iter().map(|id| self.local_def_id(*id)).collect();
-        let definitions = self.untracked.definitions.clone();
-        let cstore = Box::new(self.cstore().clone());
-        let untracked =
-            Untracked { cstore, source_span: self.untracked.source_span.clone(), definitions };
-        let global_ctxt = ResolverGlobalCtxt {
-            expn_that_defined: self.expn_that_defined.clone(),
-            visibilities: self.visibilities.clone(),
-            has_pub_restricted: self.has_pub_restricted,
-            extern_crate_map: self.extern_crate_map.clone(),
-            reexport_map: self.reexport_map.clone(),
-            glob_map: self.glob_map.clone(),
-            maybe_unused_trait_imports: self.maybe_unused_trait_imports.clone(),
-            maybe_unused_extern_crates: self.maybe_unused_extern_crates.clone(),
-            extern_prelude: self
-                .extern_prelude
-                .iter()
-                .map(|(ident, entry)| (ident.name, entry.introduced_by_item))
-                .collect(),
-            main_def: self.main_def,
-            trait_impls: self.trait_impls.clone(),
-            proc_macros,
-            confused_type_with_std_module: self.confused_type_with_std_module.clone(),
-            registered_tools: self.registered_tools.clone(),
-            effective_visibilities: self.effective_visibilities.clone(),
-            doc_link_resolutions: self.doc_link_resolutions.clone(),
-            doc_link_traits_in_scope: self.doc_link_traits_in_scope.clone(),
-        };
-        let ast_lowering = ty::ResolverAstLowering {
-            legacy_const_generic_args: self.legacy_const_generic_args.clone(),
-            partial_res_map: self.partial_res_map.clone(),
-            import_res_map: self.import_res_map.clone(),
-            label_res_map: self.label_res_map.clone(),
-            lifetimes_res_map: self.lifetimes_res_map.clone(),
-            extra_lifetime_params_map: self.extra_lifetime_params_map.clone(),
-            next_node_id: self.next_node_id,
-            node_id_to_def_id: self.node_id_to_def_id.clone(),
-            def_id_to_node_id: self.def_id_to_node_id.clone(),
-            trait_map: self.trait_map.clone(),
-            builtin_macro_kinds: self.builtin_macro_kinds.clone(),
-            lifetime_elision_allowed: self.lifetime_elision_allowed.clone(),
-        };
-        ResolverOutputs { global_ctxt, ast_lowering, untracked }
-    }
-
     fn create_stable_hashing_context(&self) -> StableHashingContext<'_> {
         StableHashingContext::new(self.session, &self.untracked)
     }
 
-    pub fn crate_loader(&mut self) -> CrateLoader<'_> {
+    fn crate_loader(&mut self) -> CrateLoader<'_> {
         CrateLoader::new(
             &self.session,
             &*self.metadata_loader,
@@ -1536,7 +1487,7 @@ impl<'a> Resolver<'a> {
         )
     }
 
-    pub fn cstore(&self) -> &CStore {
+    fn cstore(&self) -> &CStore {
         self.untracked.cstore.as_any().downcast_ref().unwrap()
     }
 
@@ -1968,24 +1919,15 @@ impl<'a> Resolver<'a> {
         }
     }
 
-    /// For rustdoc.
-    pub fn macro_rules_scope(&self, def_id: LocalDefId) -> (MacroRulesScopeRef<'a>, Res) {
-        let scope = *self.macro_rules_scopes.get(&def_id).expect("not a `macro_rules` item");
-        match scope.get() {
-            MacroRulesScope::Binding(mb) => (scope, mb.binding.res()),
-            _ => unreachable!(),
-        }
-    }
-
     /// Retrieves the span of the given `DefId` if `DefId` is in the local crate.
     #[inline]
-    pub fn opt_span(&self, def_id: DefId) -> Option<Span> {
+    fn opt_span(&self, def_id: DefId) -> Option<Span> {
         def_id.as_local().map(|def_id| self.untracked.source_span[def_id])
     }
 
     /// Retrieves the name of the given `DefId`.
     #[inline]
-    pub fn opt_name(&self, def_id: DefId) -> Option<Symbol> {
+    fn opt_name(&self, def_id: DefId) -> Option<Symbol> {
         let def_key = match def_id.as_local() {
             Some(def_id) => self.untracked.definitions.read().def_key(def_id),
             None => self.cstore().def_key(def_id),
@@ -1996,7 +1938,7 @@ impl<'a> Resolver<'a> {
     /// Checks if an expression refers to a function marked with
     /// `#[rustc_legacy_const_generics]` and returns the argument index list
     /// from the attribute.
-    pub fn legacy_const_generic_args(&mut self, expr: &Expr) -> Option<Vec<usize>> {
+    fn legacy_const_generic_args(&mut self, expr: &Expr) -> Option<Vec<usize>> {
         if let ExprKind::Path(None, path) = &expr.kind {
             // Don't perform legacy const generics rewriting if the path already
             // has generic arguments.
diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs
index 0c2e8be0498..96ad6e96fac 100644
--- a/compiler/rustc_resolve/src/macros.rs
+++ b/compiler/rustc_resolve/src/macros.rs
@@ -39,7 +39,7 @@ type Res = def::Res<NodeId>;
 /// Binding produced by a `macro_rules` item.
 /// Not modularized, can shadow previous `macro_rules` bindings, etc.
 #[derive(Debug)]
-pub struct MacroRulesBinding<'a> {
+pub(crate) struct MacroRulesBinding<'a> {
     pub(crate) binding: &'a NameBinding<'a>,
     /// `macro_rules` scope into which the `macro_rules` item was planted.
     pub(crate) parent_macro_rules_scope: MacroRulesScopeRef<'a>,
@@ -52,7 +52,7 @@ pub struct MacroRulesBinding<'a> {
 /// Some macro invocations need to introduce `macro_rules` scopes too because they
 /// can potentially expand into macro definitions.
 #[derive(Copy, Clone, Debug)]
-pub enum MacroRulesScope<'a> {
+pub(crate) enum MacroRulesScope<'a> {
     /// Empty "root" scope at the crate start containing no names.
     Empty,
     /// The scope introduced by a `macro_rules!` macro definition.
diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs
index e56588c58bd..32fcd751b46 100644
--- a/compiler/rustc_trait_selection/src/solve/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/mod.rs
@@ -31,6 +31,7 @@ use rustc_middle::ty::{
 };
 use rustc_span::DUMMY_SP;
 
+use crate::solve::search_graph::OverflowHandler;
 use crate::traits::ObligationCause;
 
 mod assembly;
@@ -210,27 +211,16 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
         search_graph: &'a mut search_graph::SearchGraph<'tcx>,
         canonical_goal: CanonicalGoal<'tcx>,
     ) -> QueryResult<'tcx> {
-        match search_graph.try_push_stack(tcx, canonical_goal) {
-            Ok(()) => {}
-            // Our goal is already on the stack, eager return.
-            Err(response) => return response,
-        }
-
-        // We may have to repeatedly recompute the goal in case of coinductive cycles,
-        // check out the `cache` module for more information.
+        // Deal with overflow, caching, and coinduction.
         //
-        // FIXME: Similar to `evaluate_all`, this has to check for overflow.
-        loop {
+        // The actual solver logic happens in `ecx.compute_goal`.
+        search_graph.with_new_goal(tcx, canonical_goal, |search_graph| {
             let (ref infcx, goal, var_values) =
                 tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &canonical_goal);
             let mut ecx =
                 EvalCtxt { infcx, var_values, search_graph, in_projection_eq_hack: false };
-            let result = ecx.compute_goal(goal);
-
-            if search_graph.try_finalize_goal(tcx, canonical_goal, result) {
-                return result;
-            }
-        }
+            ecx.compute_goal(goal)
+        })
     }
 
     fn make_canonical_response(&self, certainty: Certainty) -> QueryResult<'tcx> {
@@ -485,35 +475,38 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         mut goals: Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
     ) -> Result<Certainty, NoSolution> {
         let mut new_goals = Vec::new();
-        self.repeat_while_none(|this| {
-            let mut has_changed = Err(Certainty::Yes);
-            for goal in goals.drain(..) {
-                let (changed, certainty) = match this.evaluate_goal(goal) {
-                    Ok(result) => result,
-                    Err(NoSolution) => return Some(Err(NoSolution)),
-                };
-
-                if changed {
-                    has_changed = Ok(());
-                }
+        self.repeat_while_none(
+            |_| Ok(Certainty::Maybe(MaybeCause::Overflow)),
+            |this| {
+                let mut has_changed = Err(Certainty::Yes);
+                for goal in goals.drain(..) {
+                    let (changed, certainty) = match this.evaluate_goal(goal) {
+                        Ok(result) => result,
+                        Err(NoSolution) => return Some(Err(NoSolution)),
+                    };
+
+                    if changed {
+                        has_changed = Ok(());
+                    }
 
-                match certainty {
-                    Certainty::Yes => {}
-                    Certainty::Maybe(_) => {
-                        new_goals.push(goal);
-                        has_changed = has_changed.map_err(|c| c.unify_and(certainty));
+                    match certainty {
+                        Certainty::Yes => {}
+                        Certainty::Maybe(_) => {
+                            new_goals.push(goal);
+                            has_changed = has_changed.map_err(|c| c.unify_and(certainty));
+                        }
                     }
                 }
-            }
 
-            match has_changed {
-                Ok(()) => {
-                    mem::swap(&mut new_goals, &mut goals);
-                    None
+                match has_changed {
+                    Ok(()) => {
+                        mem::swap(&mut new_goals, &mut goals);
+                        None
+                    }
+                    Err(certainty) => Some(Ok(certainty)),
                 }
-                Err(certainty) => Some(Ok(certainty)),
-            }
-        })
+            },
+        )
     }
 
     // Recursively evaluates a list of goals to completion, making a query response.
diff --git a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs
index a2ca4bc189c..e9945cde5df 100644
--- a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs
@@ -3,6 +3,7 @@ mod overflow;
 
 use self::cache::ProvisionalEntry;
 use super::{CanonicalGoal, Certainty, MaybeCause, QueryResult};
+pub(super) use crate::solve::search_graph::overflow::OverflowHandler;
 use cache::ProvisionalCache;
 use overflow::OverflowData;
 use rustc_index::vec::IndexVec;
@@ -46,7 +47,7 @@ impl<'tcx> SearchGraph<'tcx> {
     ///
     /// This correctly updates the provisional cache if there is a cycle.
     #[instrument(level = "debug", skip(self, tcx), ret)]
-    pub(super) fn try_push_stack(
+    fn try_push_stack(
         &mut self,
         tcx: TyCtxt<'tcx>,
         goal: CanonicalGoal<'tcx>,
@@ -121,19 +122,19 @@ impl<'tcx> SearchGraph<'tcx> {
     ///
     /// FIXME: Refer to the rustc-dev-guide entry once it exists.
     #[instrument(level = "debug", skip(self, tcx, actual_goal), ret)]
-    pub(super) fn try_finalize_goal(
+    fn try_finalize_goal(
         &mut self,
         tcx: TyCtxt<'tcx>,
         actual_goal: CanonicalGoal<'tcx>,
         response: QueryResult<'tcx>,
     ) -> bool {
-        let StackElem { goal, has_been_used } = self.stack.pop().unwrap();
+        let stack_elem = self.stack.pop().unwrap();
+        let StackElem { goal, has_been_used } = stack_elem;
         assert_eq!(goal, actual_goal);
 
         let cache = &mut self.provisional_cache;
         let provisional_entry_index = *cache.lookup_table.get(&goal).unwrap();
         let provisional_entry = &mut cache.entries[provisional_entry_index];
-        let depth = provisional_entry.depth;
         // We eagerly update the response in the cache here. If we have to reevaluate
         // this goal we use the new response when hitting a cycle, and we definitely
         // want to access the final response whenever we look at the cache.
@@ -157,29 +158,72 @@ impl<'tcx> SearchGraph<'tcx> {
             self.stack.push(StackElem { goal, has_been_used: false });
             false
         } else {
-            // If not, we're done with this goal.
-            //
-            // Check whether that this goal doesn't depend on a goal deeper on the stack
-            // and if so, move it and all nested goals to the global cache.
-            //
-            // Note that if any nested goal were to depend on something deeper on the stack,
-            // this would have also updated the depth of the current goal.
-            if depth == self.stack.next_index() {
-                for (i, entry) in cache.entries.drain_enumerated(provisional_entry_index.index()..)
-                {
-                    let actual_index = cache.lookup_table.remove(&entry.goal);
-                    debug_assert_eq!(Some(i), actual_index);
-                    debug_assert!(entry.depth == depth);
-                    cache::try_move_finished_goal_to_global_cache(
-                        tcx,
-                        &mut self.overflow_data,
-                        &self.stack,
-                        entry.goal,
-                        entry.response,
-                    );
-                }
-            }
+            self.try_move_finished_goal_to_global_cache(tcx, stack_elem);
             true
         }
     }
+
+    fn try_move_finished_goal_to_global_cache(
+        &mut self,
+        tcx: TyCtxt<'tcx>,
+        stack_elem: StackElem<'tcx>,
+    ) {
+        let StackElem { goal, .. } = stack_elem;
+        let cache = &mut self.provisional_cache;
+        let provisional_entry_index = *cache.lookup_table.get(&goal).unwrap();
+        let provisional_entry = &mut cache.entries[provisional_entry_index];
+        let depth = provisional_entry.depth;
+
+        // If not, we're done with this goal.
+        //
+        // Check whether that this goal doesn't depend on a goal deeper on the stack
+        // and if so, move it and all nested goals to the global cache.
+        //
+        // Note that if any nested goal were to depend on something deeper on the stack,
+        // this would have also updated the depth of the current goal.
+        if depth == self.stack.next_index() {
+            for (i, entry) in cache.entries.drain_enumerated(provisional_entry_index.index()..) {
+                let actual_index = cache.lookup_table.remove(&entry.goal);
+                debug_assert_eq!(Some(i), actual_index);
+                debug_assert!(entry.depth == depth);
+                cache::try_move_finished_goal_to_global_cache(
+                    tcx,
+                    &mut self.overflow_data,
+                    &self.stack,
+                    entry.goal,
+                    entry.response,
+                );
+            }
+        }
+    }
+
+    pub(super) fn with_new_goal(
+        &mut self,
+        tcx: TyCtxt<'tcx>,
+        canonical_goal: CanonicalGoal<'tcx>,
+        mut loop_body: impl FnMut(&mut Self) -> QueryResult<'tcx>,
+    ) -> QueryResult<'tcx> {
+        match self.try_push_stack(tcx, canonical_goal) {
+            Ok(()) => {}
+            // Our goal is already on the stack, eager return.
+            Err(response) => return response,
+        }
+
+        self.repeat_while_none(
+            |this| {
+                let result = this.deal_with_overflow(tcx, canonical_goal);
+                let stack_elem = this.stack.pop().unwrap();
+                this.try_move_finished_goal_to_global_cache(tcx, stack_elem);
+                result
+            },
+            |this| {
+                let result = loop_body(this);
+                if this.try_finalize_goal(tcx, canonical_goal, result) {
+                    Some(result)
+                } else {
+                    None
+                }
+            },
+        )
+    }
 }
diff --git a/compiler/rustc_trait_selection/src/solve/search_graph/overflow.rs b/compiler/rustc_trait_selection/src/solve/search_graph/overflow.rs
index 1dd3894c91a..56409b0602b 100644
--- a/compiler/rustc_trait_selection/src/solve/search_graph/overflow.rs
+++ b/compiler/rustc_trait_selection/src/solve/search_graph/overflow.rs
@@ -50,6 +50,42 @@ impl OverflowData {
     }
 }
 
+pub(in crate::solve) trait OverflowHandler<'tcx> {
+    fn search_graph(&mut self) -> &mut SearchGraph<'tcx>;
+
+    fn repeat_while_none<T>(
+        &mut self,
+        on_overflow: impl FnOnce(&mut Self) -> Result<T, NoSolution>,
+        mut loop_body: impl FnMut(&mut Self) -> Option<Result<T, NoSolution>>,
+    ) -> Result<T, NoSolution> {
+        let start_depth = self.search_graph().overflow_data.additional_depth;
+        let depth = self.search_graph().stack.len();
+        while !self.search_graph().overflow_data.has_overflow(depth) {
+            if let Some(result) = loop_body(self) {
+                self.search_graph().overflow_data.additional_depth = start_depth;
+                return result;
+            }
+
+            self.search_graph().overflow_data.additional_depth += 1;
+        }
+        self.search_graph().overflow_data.additional_depth = start_depth;
+        self.search_graph().overflow_data.deal_with_overflow();
+        on_overflow(self)
+    }
+}
+
+impl<'tcx> OverflowHandler<'tcx> for EvalCtxt<'_, 'tcx> {
+    fn search_graph(&mut self) -> &mut SearchGraph<'tcx> {
+        &mut self.search_graph
+    }
+}
+
+impl<'tcx> OverflowHandler<'tcx> for SearchGraph<'tcx> {
+    fn search_graph(&mut self) -> &mut SearchGraph<'tcx> {
+        self
+    }
+}
+
 impl<'tcx> SearchGraph<'tcx> {
     pub fn deal_with_overflow(
         &mut self,
@@ -60,25 +96,3 @@ impl<'tcx> SearchGraph<'tcx> {
         response_no_constraints(tcx, goal, Certainty::Maybe(MaybeCause::Overflow))
     }
 }
-
-impl<'tcx> EvalCtxt<'_, 'tcx> {
-    /// A `while`-loop which tracks overflow.
-    pub fn repeat_while_none(
-        &mut self,
-        mut loop_body: impl FnMut(&mut Self) -> Option<Result<Certainty, NoSolution>>,
-    ) -> Result<Certainty, NoSolution> {
-        let start_depth = self.search_graph.overflow_data.additional_depth;
-        let depth = self.search_graph.stack.len();
-        while !self.search_graph.overflow_data.has_overflow(depth) {
-            if let Some(result) = loop_body(self) {
-                self.search_graph.overflow_data.additional_depth = start_depth;
-                return result;
-            }
-
-            self.search_graph.overflow_data.additional_depth += 1;
-        }
-        self.search_graph.overflow_data.additional_depth = start_depth;
-        self.search_graph.overflow_data.deal_with_overflow();
-        Ok(Certainty::Maybe(MaybeCause::Overflow))
-    }
-}
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index 302adb79766..fc9678233c3 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -27,6 +27,7 @@ use super::{
 
 use crate::infer::{InferCtxt, InferOk, TypeFreshener};
 use crate::traits::error_reporting::TypeErrCtxtExt;
+use crate::traits::project::try_normalize_with_depth_to;
 use crate::traits::project::ProjectAndUnifyResult;
 use crate::traits::project::ProjectionCacheKeyExt;
 use crate::traits::ProjectionCacheKey;
@@ -1049,7 +1050,51 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             return Ok(cycle_result);
         }
 
-        let (result, dep_node) = self.in_task(|this| this.evaluate_stack(&stack));
+        let (result, dep_node) = self.in_task(|this| {
+            let mut result = this.evaluate_stack(&stack)?;
+
+            // fix issue #103563, we don't normalize
+            // nested obligations which produced by `TraitDef` candidate
+            // (i.e. using bounds on assoc items as assumptions).
+            // because we don't have enough information to
+            // normalize these obligations before evaluating.
+            // so we will try to normalize the obligation and evaluate again.
+            // we will replace it with new solver in the future.
+            if EvaluationResult::EvaluatedToErr == result
+                && fresh_trait_pred.has_projections()
+                && fresh_trait_pred.is_global()
+            {
+                let mut nested_obligations = Vec::new();
+                let predicate = try_normalize_with_depth_to(
+                    this,
+                    param_env,
+                    obligation.cause.clone(),
+                    obligation.recursion_depth + 1,
+                    obligation.predicate,
+                    &mut nested_obligations,
+                );
+                if predicate != obligation.predicate {
+                    let mut nested_result = EvaluationResult::EvaluatedToOk;
+                    for obligation in nested_obligations {
+                        nested_result = cmp::max(
+                            this.evaluate_predicate_recursively(stack.list(), obligation)?,
+                            nested_result,
+                        );
+                    }
+
+                    if nested_result.must_apply_modulo_regions() {
+                        let obligation = obligation.with(this.tcx(), predicate);
+                        result = cmp::max(
+                            nested_result,
+                            this.evaluate_trait_predicate_recursively(stack.list(), obligation)?,
+                        );
+                    }
+                }
+            }
+
+            Ok::<_, OverflowError>(result)
+        });
+
         let result = result?;
 
         if !result.must_apply_modulo_regions() {
diff --git a/library/core/benches/array.rs b/library/core/benches/array.rs
index 845c6076294..d8cc44d05c4 100644
--- a/library/core/benches/array.rs
+++ b/library/core/benches/array.rs
@@ -11,9 +11,9 @@ macro_rules! map_array {
     };
 }
 
-map_array!(map_8byte_8byte_8, 0u64, 1u64, 800);
-map_array!(map_8byte_8byte_64, 0u64, 1u64, 6400);
-map_array!(map_8byte_8byte_256, 0u64, 1u64, 25600);
+map_array!(map_8byte_8byte_8, 0u64, 1u64, 80);
+map_array!(map_8byte_8byte_64, 0u64, 1u64, 640);
+map_array!(map_8byte_8byte_256, 0u64, 1u64, 2560);
 
-map_array!(map_8byte_256byte_256, 0u64, [0u64; 4], 25600);
-map_array!(map_256byte_8byte_256, [0u64; 4], 0u64, 25600);
+map_array!(map_8byte_256byte_256, 0u64, [0u64; 4], 2560);
+map_array!(map_256byte_8byte_256, [0u64; 4], 0u64, 2560);
diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs
index 16eb726f6f6..57e2ffe5d20 100644
--- a/library/core/src/ptr/const_ptr.rs
+++ b/library/core/src/ptr/const_ptr.rs
@@ -23,8 +23,6 @@ impl<T: ?Sized> *const T {
     ///
     /// # Examples
     ///
-    /// Basic usage:
-    ///
     /// ```
     /// let s: &str = "Follow the rabbit";
     /// let ptr: *const u8 = s.as_ptr();
@@ -323,8 +321,6 @@ impl<T: ?Sized> *const T {
     ///
     /// # Examples
     ///
-    /// Basic usage:
-    ///
     /// ```
     /// let ptr: *const u8 = &10u8 as *const u8;
     ///
@@ -384,8 +380,6 @@ impl<T: ?Sized> *const T {
     ///
     /// # Examples
     ///
-    /// Basic usage:
-    ///
     /// ```
     /// #![feature(ptr_as_uninit)]
     ///
@@ -449,8 +443,6 @@ impl<T: ?Sized> *const T {
     ///
     /// # Examples
     ///
-    /// Basic usage:
-    ///
     /// ```
     /// let s: &str = "123";
     /// let ptr: *const u8 = s.as_ptr();
@@ -526,8 +518,6 @@ impl<T: ?Sized> *const T {
     ///
     /// # Examples
     ///
-    /// Basic usage:
-    ///
     /// ```
     /// // Iterate using a raw pointer in increments of two elements
     /// let data = [1u8, 2, 3, 4, 5];
@@ -908,8 +898,6 @@ impl<T: ?Sized> *const T {
     ///
     /// # Examples
     ///
-    /// Basic usage:
-    ///
     /// ```
     /// let s: &str = "123";
     /// let ptr: *const u8 = s.as_ptr();
@@ -993,8 +981,6 @@ impl<T: ?Sized> *const T {
     ///
     /// # Examples
     ///
-    /// Basic usage:
-    ///
     /// ```
     /// let s: &str = "123";
     ///
@@ -1072,8 +1058,6 @@ impl<T: ?Sized> *const T {
     ///
     /// # Examples
     ///
-    /// Basic usage:
-    ///
     /// ```
     /// // Iterate using a raw pointer in increments of two elements
     /// let data = [1u8, 2, 3, 4, 5];
@@ -1152,8 +1136,6 @@ impl<T: ?Sized> *const T {
     ///
     /// # Examples
     ///
-    /// Basic usage:
-    ///
     /// ```
     /// // Iterate using a raw pointer in increments of two elements (backwards)
     /// let data = [1u8, 2, 3, 4, 5];
@@ -1359,7 +1341,6 @@ impl<T: ?Sized> *const T {
     ///
     /// # Examples
     ///
-    /// Basic usage:
     /// ```
     /// #![feature(pointer_is_aligned)]
     /// #![feature(pointer_byte_offsets)]
@@ -1482,7 +1463,6 @@ impl<T: ?Sized> *const T {
     ///
     /// # Examples
     ///
-    /// Basic usage:
     /// ```
     /// #![feature(pointer_is_aligned)]
     /// #![feature(pointer_byte_offsets)]
diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs
index 0a2f63e3ec6..422d0f2b8f0 100644
--- a/library/core/src/ptr/mut_ptr.rs
+++ b/library/core/src/ptr/mut_ptr.rs
@@ -22,8 +22,6 @@ impl<T: ?Sized> *mut T {
     ///
     /// # Examples
     ///
-    /// Basic usage:
-    ///
     /// ```
     /// let mut s = [1, 2, 3];
     /// let ptr: *mut u32 = s.as_mut_ptr();
@@ -332,8 +330,6 @@ impl<T: ?Sized> *mut T {
     ///
     /// # Examples
     ///
-    /// Basic usage:
-    ///
     /// ```
     /// let ptr: *mut u8 = &mut 10u8 as *mut u8;
     ///
@@ -396,8 +392,6 @@ impl<T: ?Sized> *mut T {
     ///
     /// # Examples
     ///
-    /// Basic usage:
-    ///
     /// ```
     /// #![feature(ptr_as_uninit)]
     ///
@@ -461,8 +455,6 @@ impl<T: ?Sized> *mut T {
     ///
     /// # Examples
     ///
-    /// Basic usage:
-    ///
     /// ```
     /// let mut s = [1, 2, 3];
     /// let ptr: *mut u32 = s.as_mut_ptr();
@@ -539,8 +531,6 @@ impl<T: ?Sized> *mut T {
     ///
     /// # Examples
     ///
-    /// Basic usage:
-    ///
     /// ```
     /// // Iterate using a raw pointer in increments of two elements
     /// let mut data = [1u8, 2, 3, 4, 5];
@@ -660,8 +650,6 @@ impl<T: ?Sized> *mut T {
     ///
     /// # Examples
     ///
-    /// Basic usage:
-    ///
     /// ```
     /// let mut s = [1, 2, 3];
     /// let ptr: *mut u32 = s.as_mut_ptr();
@@ -1010,8 +998,6 @@ impl<T: ?Sized> *mut T {
     ///
     /// # Examples
     ///
-    /// Basic usage:
-    ///
     /// ```
     /// let s: &str = "123";
     /// let ptr: *const u8 = s.as_ptr();
@@ -1095,8 +1081,6 @@ impl<T: ?Sized> *mut T {
     ///
     /// # Examples
     ///
-    /// Basic usage:
-    ///
     /// ```
     /// let s: &str = "123";
     ///
@@ -1174,8 +1158,6 @@ impl<T: ?Sized> *mut T {
     ///
     /// # Examples
     ///
-    /// Basic usage:
-    ///
     /// ```
     /// // Iterate using a raw pointer in increments of two elements
     /// let data = [1u8, 2, 3, 4, 5];
@@ -1254,8 +1236,6 @@ impl<T: ?Sized> *mut T {
     ///
     /// # Examples
     ///
-    /// Basic usage:
-    ///
     /// ```
     /// // Iterate using a raw pointer in increments of two elements (backwards)
     /// let data = [1u8, 2, 3, 4, 5];
@@ -1627,7 +1607,6 @@ impl<T: ?Sized> *mut T {
     ///
     /// # Examples
     ///
-    /// Basic usage:
     /// ```
     /// #![feature(pointer_is_aligned)]
     /// #![feature(pointer_byte_offsets)]
@@ -1752,7 +1731,6 @@ impl<T: ?Sized> *mut T {
     ///
     /// # Examples
     ///
-    /// Basic usage:
     /// ```
     /// #![feature(pointer_is_aligned)]
     /// #![feature(pointer_byte_offsets)]
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index ffe6fea7ea4..b00cefdddb5 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -482,16 +482,16 @@ impl Item {
     }
 
     pub(crate) fn links(&self, cx: &Context<'_>) -> Vec<RenderedLink> {
-        use crate::html::format::href;
+        use crate::html::format::{href, link_tooltip};
 
         cx.cache()
             .intra_doc_links
             .get(&self.item_id)
             .map_or(&[][..], |v| v.as_slice())
             .iter()
-            .filter_map(|ItemLink { link: s, link_text, page_id: did, ref fragment }| {
-                debug!(?did);
-                if let Ok((mut href, ..)) = href(*did, cx) {
+            .filter_map(|ItemLink { link: s, link_text, page_id: id, ref fragment }| {
+                debug!(?id);
+                if let Ok((mut href, ..)) = href(*id, cx) {
                     debug!(?href);
                     if let Some(ref fragment) = *fragment {
                         fragment.render(&mut href, cx.tcx())
@@ -499,6 +499,7 @@ impl Item {
                     Some(RenderedLink {
                         original_text: s.clone(),
                         new_text: link_text.clone(),
+                        tooltip: link_tooltip(*id, fragment, cx),
                         href,
                     })
                 } else {
@@ -523,6 +524,7 @@ impl Item {
                 original_text: s.clone(),
                 new_text: link_text.clone(),
                 href: String::new(),
+                tooltip: String::new(),
             })
             .collect()
     }
@@ -1040,6 +1042,8 @@ pub struct RenderedLink {
     pub(crate) new_text: String,
     /// The URL to put in the `href`
     pub(crate) href: String,
+    /// The tooltip.
+    pub(crate) tooltip: String,
 }
 
 /// The attributes on an [`Item`], including attributes like `#[derive(...)]` and `#[inline]`,
diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs
index 7e16c4701be..5ab7056be44 100644
--- a/src/librustdoc/core.rs
+++ b/src/librustdoc/core.rs
@@ -1,4 +1,3 @@
-use rustc_ast::NodeId;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::sync::{self, Lrc};
 use rustc_data_structures::unord::UnordSet;
@@ -17,7 +16,7 @@ use rustc_session::config::{self, CrateType, ErrorOutputType, ResolveDocLinks};
 use rustc_session::lint;
 use rustc_session::Session;
 use rustc_span::symbol::sym;
-use rustc_span::{source_map, Span, Symbol};
+use rustc_span::{source_map, Span};
 
 use std::cell::RefCell;
 use std::mem;
@@ -32,15 +31,8 @@ use crate::passes::{self, Condition::*};
 
 pub(crate) use rustc_session::config::{Input, Options, UnstableOptions};
 
-pub(crate) struct ResolverCaches {
-    pub(crate) all_trait_impls: Option<Vec<DefId>>,
-    pub(crate) all_macro_rules: FxHashMap<Symbol, Res<NodeId>>,
-    pub(crate) extern_doc_reachable: DefIdSet,
-}
-
 pub(crate) struct DocContext<'tcx> {
     pub(crate) tcx: TyCtxt<'tcx>,
-    pub(crate) resolver_caches: ResolverCaches,
     /// Used for normalization.
     ///
     /// Most of this logic is copied from rustc_lint::late.
@@ -111,12 +103,6 @@ impl<'tcx> DocContext<'tcx> {
             _ => None,
         }
     }
-
-    pub(crate) fn with_all_trait_impls(&mut self, f: impl FnOnce(&mut Self, &[DefId])) {
-        let all_trait_impls = self.resolver_caches.all_trait_impls.take();
-        f(self, all_trait_impls.as_ref().expect("`all_trait_impls` are already borrowed"));
-        self.resolver_caches.all_trait_impls = all_trait_impls;
-    }
 }
 
 /// Creates a new diagnostic `Handler` that can be used to emit warnings and errors.
@@ -305,7 +291,6 @@ pub(crate) fn create_config(
 
 pub(crate) fn run_global_ctxt(
     tcx: TyCtxt<'_>,
-    resolver_caches: ResolverCaches,
     show_coverage: bool,
     render_options: RenderOptions,
     output_format: OutputFormat,
@@ -339,7 +324,6 @@ pub(crate) fn run_global_ctxt(
 
     let mut ctxt = DocContext {
         tcx,
-        resolver_caches,
         param_env: ParamEnv::empty(),
         external_traits: Default::default(),
         active_extern_traits: Default::default(),
@@ -354,9 +338,9 @@ pub(crate) fn run_global_ctxt(
         show_coverage,
     };
 
-    ctxt.cache
-        .effective_visibilities
-        .init(mem::take(&mut ctxt.resolver_caches.extern_doc_reachable));
+    for cnum in tcx.crates(()) {
+        crate::visit_lib::lib_embargo_visit_item(&mut ctxt, cnum.as_def_id());
+    }
 
     // Small hack to force the Sized trait to be present.
     //
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index 8a7a8ea5fd1..314f0612249 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -34,6 +34,7 @@ use crate::clean::{
 use crate::formats::item_type::ItemType;
 use crate::html::escape::Escape;
 use crate::html::render::Context;
+use crate::passes::collect_intra_doc_links::UrlFragment;
 
 use super::url_parts_builder::estimate_item_path_byte_length;
 use super::url_parts_builder::UrlPartsBuilder;
@@ -768,6 +769,21 @@ pub(crate) fn href_relative_parts<'fqp>(
     }
 }
 
+pub(crate) fn link_tooltip(did: DefId, fragment: &Option<UrlFragment>, cx: &Context<'_>) -> String {
+    let cache = cx.cache();
+    let Some((fqp, shortty)) = cache.paths.get(&did)
+        .or_else(|| cache.external_paths.get(&did))
+        else { return String::new() };
+    let fqp = fqp.iter().map(|sym| sym.as_str()).join("::");
+    if let &Some(UrlFragment::Item(id)) = fragment {
+        let name = cx.tcx().item_name(id);
+        let descr = cx.tcx().def_kind(id).descr(id);
+        format!("{descr} {fqp}::{name}")
+    } else {
+        format!("{shortty} {fqp}")
+    }
+}
+
 /// Used to render a [`clean::Path`].
 fn resolved_path<'cx>(
     w: &mut fmt::Formatter<'_>,
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index 33180439393..e4adee6ae4d 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -360,6 +360,9 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for LinkReplacer<'a, I> {
                     trace!("it matched");
                     assert!(self.shortcut_link.is_none(), "shortcut links cannot be nested");
                     self.shortcut_link = Some(link);
+                    if title.is_empty() && !link.tooltip.is_empty() {
+                        *title = CowStr::Borrowed(link.tooltip.as_ref());
+                    }
                 }
             }
             // Now that we're done with the shortcut link, don't replace any more text.
@@ -410,9 +413,12 @@ 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, _))) => {
+            Some(Event::Start(Tag::Link(_, dest, title))) => {
                 if let Some(link) = self.links.iter().find(|&link| *link.original_text == **dest) {
                     *dest = CowStr::Borrowed(link.href.as_ref());
+                    if title.is_empty() && !link.tooltip.is_empty() {
+                        *title = CowStr::Borrowed(link.tooltip.as_ref());
+                    }
                 }
             }
             // Anything else couldn't have been a valid Rust path, so no need to replace the text.
@@ -976,7 +982,7 @@ impl Markdown<'_> {
             links
                 .iter()
                 .find(|link| link.original_text.as_str() == &*broken_link.reference)
-                .map(|link| (link.href.as_str().into(), link.new_text.as_str().into()))
+                .map(|link| (link.href.as_str().into(), link.tooltip.as_str().into()))
         };
 
         let p = Parser::new_with_broken_link_callback(md, main_body_opts(), Some(&mut replacer));
@@ -1059,7 +1065,7 @@ impl MarkdownSummaryLine<'_> {
             links
                 .iter()
                 .find(|link| link.original_text.as_str() == &*broken_link.reference)
-                .map(|link| (link.href.as_str().into(), link.new_text.as_str().into()))
+                .map(|link| (link.href.as_str().into(), link.tooltip.as_str().into()))
         };
 
         let p = Parser::new_with_broken_link_callback(md, summary_opts(), Some(&mut replacer))
@@ -1106,7 +1112,7 @@ fn markdown_summary_with_limit(
         link_names
             .iter()
             .find(|link| link.original_text.as_str() == &*broken_link.reference)
-            .map(|link| (link.href.as_str().into(), link.new_text.as_str().into()))
+            .map(|link| (link.href.as_str().into(), link.tooltip.as_str().into()))
     };
 
     let p = Parser::new_with_broken_link_callback(md, summary_opts(), Some(&mut replacer));
@@ -1187,7 +1193,7 @@ pub(crate) fn plain_text_summary(md: &str, link_names: &[RenderedLink]) -> Strin
         link_names
             .iter()
             .find(|link| link.original_text.as_str() == &*broken_link.reference)
-            .map(|link| (link.href.as_str().into(), link.new_text.as_str().into()))
+            .map(|link| (link.href.as_str().into(), link.tooltip.as_str().into()))
     };
 
     let p = Parser::new_with_broken_link_callback(md, summary_opts(), Some(&mut replacer));
diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs
index feefb8b69d1..910a7190b58 100644
--- a/src/librustdoc/lib.rs
+++ b/src/librustdoc/lib.rs
@@ -82,7 +82,6 @@ use rustc_session::getopts;
 use rustc_session::{early_error, early_warn};
 
 use crate::clean::utils::DOC_RUST_LANG_ORG_CHANNEL;
-use crate::passes::collect_intra_doc_links;
 
 /// A macro to create a FxHashMap.
 ///
@@ -793,30 +792,14 @@ fn main_args(at_args: &[String]) -> MainResult {
         }
 
         compiler.enter(|queries| {
-            let resolver_caches = {
-                let expansion = abort_on_err(queries.expansion(), sess);
-                let (krate, resolver, _) = &*expansion.borrow();
-                let resolver_caches = resolver.borrow_mut().access(|resolver| {
-                    collect_intra_doc_links::early_resolve_intra_doc_links(resolver, krate)
-                });
-                resolver_caches
-            };
-
+            let mut gcx = abort_on_err(queries.global_ctxt(), sess);
             if sess.diagnostic().has_errors_or_lint_errors().is_some() {
                 sess.fatal("Compilation failed, aborting rustdoc");
             }
 
-            let mut gcx = abort_on_err(queries.global_ctxt(), sess);
-
             gcx.enter(|tcx| {
                 let (krate, render_opts, mut cache) = sess.time("run_global_ctxt", || {
-                    core::run_global_ctxt(
-                        tcx,
-                        resolver_caches,
-                        show_coverage,
-                        render_options,
-                        output_format,
-                    )
+                    core::run_global_ctxt(tcx, show_coverage, render_options, output_format)
                 });
                 info!("finished with rustc");
 
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index 0e2191185eb..7e3149a59e3 100644
--- a/src/librustdoc/passes/collect_intra_doc_links.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links.rs
@@ -24,6 +24,7 @@ use rustc_span::BytePos;
 use smallvec::{smallvec, SmallVec};
 
 use std::borrow::Cow;
+use std::mem;
 use std::ops::Range;
 
 use crate::clean::{self, utils::find_nearest_parent_module};
@@ -34,9 +35,6 @@ use crate::lint::{BROKEN_INTRA_DOC_LINKS, PRIVATE_INTRA_DOC_LINKS};
 use crate::passes::Pass;
 use crate::visit::DocVisitor;
 
-mod early;
-pub(crate) use early::early_resolve_intra_doc_links;
-
 pub(crate) const COLLECT_INTRA_DOC_LINKS: Pass = Pass {
     name: "collect-intra-doc-links",
     run: collect_intra_doc_links,
@@ -1616,7 +1614,7 @@ fn resolution_failure(
             // ignore duplicates
             let mut variants_seen = SmallVec::<[_; 3]>::new();
             for mut failure in kinds {
-                let variant = std::mem::discriminant(&failure);
+                let variant = mem::discriminant(&failure);
                 if variants_seen.contains(&variant) {
                     continue;
                 }
@@ -1686,7 +1684,7 @@ fn resolution_failure(
 
                         if !path_str.contains("::") {
                             if disambiguator.map_or(true, |d| d.ns() == MacroNS)
-                                && let Some(&res) = collector.cx.resolver_caches.all_macro_rules
+                                && let Some(&res) = collector.cx.tcx.resolutions(()).all_macro_rules
                                                              .get(&Symbol::intern(path_str))
                             {
                                 diag.note(format!(
diff --git a/src/librustdoc/passes/collect_intra_doc_links/early.rs b/src/librustdoc/passes/collect_intra_doc_links/early.rs
deleted file mode 100644
index ec449e94ce5..00000000000
--- a/src/librustdoc/passes/collect_intra_doc_links/early.rs
+++ /dev/null
@@ -1,72 +0,0 @@
-use crate::core::ResolverCaches;
-use crate::visit_lib::early_lib_embargo_visit_item;
-
-use rustc_ast::visit::{self, Visitor};
-use rustc_ast::{self as ast, ItemKind};
-use rustc_data_structures::fx::FxHashMap;
-use rustc_hir::def::Res;
-use rustc_hir::def_id::{DefId, DefIdSet};
-use rustc_resolve::Resolver;
-use rustc_span::Symbol;
-
-pub(crate) fn early_resolve_intra_doc_links(
-    resolver: &mut Resolver<'_>,
-    krate: &ast::Crate,
-) -> ResolverCaches {
-    let mut link_resolver = EarlyDocLinkResolver {
-        resolver,
-        all_trait_impls: Default::default(),
-        all_macro_rules: Default::default(),
-        extern_doc_reachable: Default::default(),
-    };
-
-    visit::walk_crate(&mut link_resolver, krate);
-    link_resolver.process_extern_impls();
-
-    ResolverCaches {
-        all_trait_impls: Some(link_resolver.all_trait_impls),
-        all_macro_rules: link_resolver.all_macro_rules,
-        extern_doc_reachable: link_resolver.extern_doc_reachable,
-    }
-}
-
-struct EarlyDocLinkResolver<'r, 'ra> {
-    resolver: &'r mut Resolver<'ra>,
-    all_trait_impls: Vec<DefId>,
-    all_macro_rules: FxHashMap<Symbol, Res<ast::NodeId>>,
-    /// This set is used as a seed for `effective_visibilities`, which are then extended by some
-    /// more items using `lib_embargo_visit_item` during doc inlining.
-    extern_doc_reachable: DefIdSet,
-}
-
-impl<'ra> EarlyDocLinkResolver<'_, 'ra> {
-    fn process_extern_impls(&mut self) {
-        for cnum in self.resolver.cstore().crates_untracked() {
-            early_lib_embargo_visit_item(
-                self.resolver,
-                &mut self.extern_doc_reachable,
-                cnum.as_def_id(),
-                true,
-            );
-            for (_, impl_def_id, _) in self.resolver.cstore().trait_impls_in_crate_untracked(cnum) {
-                self.all_trait_impls.push(impl_def_id);
-            }
-        }
-    }
-}
-
-impl Visitor<'_> for EarlyDocLinkResolver<'_, '_> {
-    fn visit_item(&mut self, item: &ast::Item) {
-        match &item.kind {
-            ItemKind::Impl(impl_) if impl_.of_trait.is_some() => {
-                self.all_trait_impls.push(self.resolver.local_def_id(item.id).to_def_id());
-            }
-            ItemKind::MacroDef(macro_def) if macro_def.macro_rules => {
-                let (_, res) = self.resolver.macro_rules_scope(self.resolver.local_def_id(item.id));
-                self.all_macro_rules.insert(item.ident.name, res);
-            }
-            _ => {}
-        }
-        visit::walk_item(self, item);
-    }
-}
diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs
index 7d15a207d06..878e738fe50 100644
--- a/src/librustdoc/passes/collect_trait_impls.rs
+++ b/src/librustdoc/passes/collect_trait_impls.rs
@@ -45,18 +45,20 @@ pub(crate) fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) ->
     let mut new_items_local = Vec::new();
 
     // External trait impls.
-    cx.with_all_trait_impls(|cx, all_trait_impls| {
+    {
         let _prof_timer = cx.tcx.sess.prof.generic_activity("build_extern_trait_impls");
-        for &impl_def_id in all_trait_impls.iter().skip_while(|def_id| def_id.is_local()) {
-            inline::build_impl(cx, None, impl_def_id, None, &mut new_items_external);
+        for &cnum in cx.tcx.crates(()) {
+            for &impl_def_id in cx.tcx.trait_impls_in_crate(cnum) {
+                inline::build_impl(cx, None, impl_def_id, None, &mut new_items_external);
+            }
         }
-    });
+    }
 
     // Local trait impls.
-    cx.with_all_trait_impls(|cx, all_trait_impls| {
+    {
         let _prof_timer = cx.tcx.sess.prof.generic_activity("build_local_trait_impls");
         let mut attr_buf = Vec::new();
-        for &impl_def_id in all_trait_impls.iter().take_while(|def_id| def_id.is_local()) {
+        for &impl_def_id in cx.tcx.trait_impls_in_crate(LOCAL_CRATE) {
             let mut parent = Some(cx.tcx.parent(impl_def_id));
             while let Some(did) = parent {
                 attr_buf.extend(
@@ -76,7 +78,7 @@ pub(crate) fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) ->
             inline::build_impl(cx, None, impl_def_id, Some(&attr_buf), &mut new_items_local);
             attr_buf.clear();
         }
-    });
+    }
 
     cx.tcx.sess.prof.generic_activity("build_primitive_trait_impls").run(|| {
         for def_id in PrimitiveType::all_impls(cx.tcx) {
diff --git a/src/librustdoc/visit_lib.rs b/src/librustdoc/visit_lib.rs
index 07d8b78d767..fd4f9254107 100644
--- a/src/librustdoc/visit_lib.rs
+++ b/src/librustdoc/visit_lib.rs
@@ -1,8 +1,7 @@
 use crate::core::DocContext;
-use rustc_hir::def::{DefKind, Res};
+use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{DefId, DefIdSet};
 use rustc_middle::ty::TyCtxt;
-use rustc_resolve::Resolver;
 
 // FIXME: this may not be exhaustive, but is sufficient for rustdocs current uses
 
@@ -26,10 +25,6 @@ impl RustdocEffectiveVisibilities {
     define_method!(is_directly_public);
     define_method!(is_exported);
     define_method!(is_reachable);
-
-    pub(crate) fn init(&mut self, extern_public: DefIdSet) {
-        self.extern_public = extern_public;
-    }
 }
 
 pub(crate) fn lib_embargo_visit_item(cx: &mut DocContext<'_>, def_id: DefId) {
@@ -42,17 +37,6 @@ pub(crate) fn lib_embargo_visit_item(cx: &mut DocContext<'_>, def_id: DefId) {
     .visit_item(def_id)
 }
 
-pub(crate) fn early_lib_embargo_visit_item(
-    resolver: &Resolver<'_>,
-    extern_public: &mut DefIdSet,
-    def_id: DefId,
-    is_mod: bool,
-) {
-    assert!(!def_id.is_local());
-    EarlyLibEmbargoVisitor { resolver, extern_public, visited_mods: Default::default() }
-        .visit_item(def_id, is_mod)
-}
-
 /// Similar to `librustc_privacy::EmbargoVisitor`, but also takes
 /// specific rustdoc annotations into account (i.e., `doc(hidden)`)
 struct LibEmbargoVisitor<'a, 'tcx> {
@@ -63,14 +47,6 @@ struct LibEmbargoVisitor<'a, 'tcx> {
     visited_mods: DefIdSet,
 }
 
-struct EarlyLibEmbargoVisitor<'r, 'ra> {
-    resolver: &'r Resolver<'ra>,
-    // Effective visibilities for reachable nodes
-    extern_public: &'r mut DefIdSet,
-    // Keeps track of already visited modules, in case a module re-exports its parent
-    visited_mods: DefIdSet,
-}
-
 impl LibEmbargoVisitor<'_, '_> {
     fn visit_mod(&mut self, def_id: DefId) {
         if !self.visited_mods.insert(def_id) {
@@ -95,28 +71,3 @@ impl LibEmbargoVisitor<'_, '_> {
         }
     }
 }
-
-impl EarlyLibEmbargoVisitor<'_, '_> {
-    fn visit_mod(&mut self, def_id: DefId) {
-        if !self.visited_mods.insert(def_id) {
-            return;
-        }
-
-        for item in self.resolver.cstore().module_children_untracked(def_id, self.resolver.sess()) {
-            if let Some(def_id) = item.res.opt_def_id() {
-                if item.vis.is_public() {
-                    self.visit_item(def_id, matches!(item.res, Res::Def(DefKind::Mod, _)));
-                }
-            }
-        }
-    }
-
-    fn visit_item(&mut self, def_id: DefId, is_mod: bool) {
-        if !self.resolver.cstore().is_doc_hidden_untracked(def_id) {
-            self.extern_public.insert(def_id);
-            if is_mod {
-                self.visit_mod(def_id);
-            }
-        }
-    }
-}
diff --git a/tests/rustdoc/intra-doc/basic.rs b/tests/rustdoc/intra-doc/basic.rs
index 39f5c298bc4..e2d3ef425cb 100644
--- a/tests/rustdoc/intra-doc/basic.rs
+++ b/tests/rustdoc/intra-doc/basic.rs
@@ -1,21 +1,38 @@
 // @has basic/index.html
 // @has - '//a/@href' 'struct.ThisType.html'
+// @has - '//a/@title' 'struct basic::ThisType'
 // @has - '//a/@href' 'struct.ThisType.html#method.this_method'
+// @has - '//a/@title' 'associated function basic::ThisType::this_method'
 // @has - '//a/@href' 'enum.ThisEnum.html'
+// @has - '//a/@title' 'enum basic::ThisEnum'
 // @has - '//a/@href' 'enum.ThisEnum.html#variant.ThisVariant'
+// @has - '//a/@title' 'variant basic::ThisEnum::ThisVariant'
 // @has - '//a/@href' 'trait.ThisTrait.html'
+// @has - '//a/@title' 'trait basic::ThisTrait'
 // @has - '//a/@href' 'trait.ThisTrait.html#tymethod.this_associated_method'
+// @has - '//a/@title' 'associated function basic::ThisTrait::this_associated_method'
 // @has - '//a/@href' 'trait.ThisTrait.html#associatedtype.ThisAssociatedType'
+// @has - '//a/@title' 'associated type basic::ThisTrait::ThisAssociatedType'
 // @has - '//a/@href' 'trait.ThisTrait.html#associatedconstant.THIS_ASSOCIATED_CONST'
+// @has - '//a/@title' 'associated constant basic::ThisTrait::THIS_ASSOCIATED_CONST'
 // @has - '//a/@href' 'trait.ThisTrait.html'
+// @has - '//a/@title' 'trait basic::ThisTrait'
 // @has - '//a/@href' 'type.ThisAlias.html'
+// @has - '//a/@title' 'type basic::ThisAlias'
 // @has - '//a/@href' 'union.ThisUnion.html'
+// @has - '//a/@title' 'union basic::ThisUnion'
 // @has - '//a/@href' 'fn.this_function.html'
+// @has - '//a/@title' 'fn basic::this_function'
 // @has - '//a/@href' 'constant.THIS_CONST.html'
+// @has - '//a/@title' 'constant basic::THIS_CONST'
 // @has - '//a/@href' 'static.THIS_STATIC.html'
+// @has - '//a/@title' 'static basic::THIS_STATIC'
 // @has - '//a/@href' 'macro.this_macro.html'
+// @has - '//a/@title' 'macro basic::this_macro'
 // @has - '//a/@href' 'trait.SoAmbiguous.html'
+// @has - '//a/@title' 'trait basic::SoAmbiguous'
 // @has - '//a/@href' 'fn.SoAmbiguous.html'
+// @has - '//a/@title' 'fn basic::SoAmbiguous'
 //! In this crate we would like to link to:
 //!
 //! * [`ThisType`](ThisType)
diff --git a/tests/ui/parser/suggest_misplaced_generics/enum.fixed b/tests/ui/parser/suggest_misplaced_generics/enum.fixed
new file mode 100644
index 00000000000..3332118a1e7
--- /dev/null
+++ b/tests/ui/parser/suggest_misplaced_generics/enum.fixed
@@ -0,0 +1,9 @@
+// Issue: 103366 , Suggest fix for misplaced generic params
+// run-rustfix
+
+#[allow(unused)]
+enum Foo<T> { Variant(T) }
+//~^ ERROR expected identifier, found `<`
+//~| HELP place the generic parameter name after the enum name
+
+fn main() {}
diff --git a/tests/ui/parser/suggest_misplaced_generics/enum.rs b/tests/ui/parser/suggest_misplaced_generics/enum.rs
new file mode 100644
index 00000000000..5a2289c5c5a
--- /dev/null
+++ b/tests/ui/parser/suggest_misplaced_generics/enum.rs
@@ -0,0 +1,9 @@
+// Issue: 103366 , Suggest fix for misplaced generic params
+// run-rustfix
+
+#[allow(unused)]
+enum<T> Foo { Variant(T) }
+//~^ ERROR expected identifier, found `<`
+//~| HELP place the generic parameter name after the enum name
+
+fn main() {}
diff --git a/tests/ui/parser/suggest_misplaced_generics/enum.stderr b/tests/ui/parser/suggest_misplaced_generics/enum.stderr
new file mode 100644
index 00000000000..5f5947627ee
--- /dev/null
+++ b/tests/ui/parser/suggest_misplaced_generics/enum.stderr
@@ -0,0 +1,14 @@
+error: expected identifier, found `<`
+  --> $DIR/enum.rs:5:5
+   |
+LL | enum<T> Foo { Variant(T) }
+   |     ^ expected identifier
+   |
+help: place the generic parameter name after the enum name
+   |
+LL - enum<T> Foo { Variant(T) }
+LL + enum Foo<T> { Variant(T) }
+   |
+
+error: aborting due to previous error
+
diff --git a/tests/ui/parser/suggest_misplaced_generics/existing_generics.rs b/tests/ui/parser/suggest_misplaced_generics/existing_generics.rs
new file mode 100644
index 00000000000..1dc182398d8
--- /dev/null
+++ b/tests/ui/parser/suggest_misplaced_generics/existing_generics.rs
@@ -0,0 +1,9 @@
+// Issue: 103366
+// there is already an existing generic on f, so don't show a suggestion
+
+#[allow(unused)]
+fn<'a, B: 'a + std::ops::Add<Output = u32>> f<T>(_x: B) { }
+//~^ ERROR expected identifier, found `<`
+//~| HELP place the generic parameter name after the fn name
+
+fn main() {}
diff --git a/tests/ui/parser/suggest_misplaced_generics/existing_generics.stderr b/tests/ui/parser/suggest_misplaced_generics/existing_generics.stderr
new file mode 100644
index 00000000000..89716e6f1ed
--- /dev/null
+++ b/tests/ui/parser/suggest_misplaced_generics/existing_generics.stderr
@@ -0,0 +1,10 @@
+error: expected identifier, found `<`
+  --> $DIR/existing_generics.rs:5:3
+   |
+LL | fn<'a, B: 'a + std::ops::Add<Output = u32>> f<T>(_x: B) { }
+   |   ^ expected identifier
+   |
+   = help: place the generic parameter name after the fn name
+
+error: aborting due to previous error
+
diff --git a/tests/ui/parser/suggest_misplaced_generics/fn-complex-generics.fixed b/tests/ui/parser/suggest_misplaced_generics/fn-complex-generics.fixed
new file mode 100644
index 00000000000..84bf64bd63c
--- /dev/null
+++ b/tests/ui/parser/suggest_misplaced_generics/fn-complex-generics.fixed
@@ -0,0 +1,9 @@
+// Issue: 103366 , Suggest fix for misplaced generic params
+// run-rustfix
+
+#[allow(unused)]
+fn f<'a, B: 'a + std::ops::Add<Output = u32>>(_x: B) { }
+//~^ ERROR expected identifier, found `<`
+//~| HELP place the generic parameter name after the fn name
+
+fn main() {}
diff --git a/tests/ui/parser/suggest_misplaced_generics/fn-complex-generics.rs b/tests/ui/parser/suggest_misplaced_generics/fn-complex-generics.rs
new file mode 100644
index 00000000000..d0684397e74
--- /dev/null
+++ b/tests/ui/parser/suggest_misplaced_generics/fn-complex-generics.rs
@@ -0,0 +1,9 @@
+// Issue: 103366 , Suggest fix for misplaced generic params
+// run-rustfix
+
+#[allow(unused)]
+fn<'a, B: 'a + std::ops::Add<Output = u32>> f(_x: B) { }
+//~^ ERROR expected identifier, found `<`
+//~| HELP place the generic parameter name after the fn name
+
+fn main() {}
diff --git a/tests/ui/parser/suggest_misplaced_generics/fn-complex-generics.stderr b/tests/ui/parser/suggest_misplaced_generics/fn-complex-generics.stderr
new file mode 100644
index 00000000000..061d0910a74
--- /dev/null
+++ b/tests/ui/parser/suggest_misplaced_generics/fn-complex-generics.stderr
@@ -0,0 +1,14 @@
+error: expected identifier, found `<`
+  --> $DIR/fn-complex-generics.rs:5:3
+   |
+LL | fn<'a, B: 'a + std::ops::Add<Output = u32>> f(_x: B) { }
+   |   ^ expected identifier
+   |
+help: place the generic parameter name after the fn name
+   |
+LL - fn<'a, B: 'a + std::ops::Add<Output = u32>> f(_x: B) { }
+LL + fn f<'a, B: 'a + std::ops::Add<Output = u32>>(_x: B) { }
+   |
+
+error: aborting due to previous error
+
diff --git a/tests/ui/parser/suggest_misplaced_generics/fn-invalid-generics.rs b/tests/ui/parser/suggest_misplaced_generics/fn-invalid-generics.rs
new file mode 100644
index 00000000000..7fcb6a82ce4
--- /dev/null
+++ b/tests/ui/parser/suggest_misplaced_generics/fn-invalid-generics.rs
@@ -0,0 +1,8 @@
+// Issue: 103366 , Suggest fix for misplaced generic params
+// The generics fail to parse here, so don't make any suggestions/help
+
+#[allow(unused)]
+fn<~>()> id(x: T) -> T { x }
+//~^ ERROR expected identifier, found `<`
+
+fn main() {}
diff --git a/tests/ui/parser/suggest_misplaced_generics/fn-invalid-generics.stderr b/tests/ui/parser/suggest_misplaced_generics/fn-invalid-generics.stderr
new file mode 100644
index 00000000000..47e12016938
--- /dev/null
+++ b/tests/ui/parser/suggest_misplaced_generics/fn-invalid-generics.stderr
@@ -0,0 +1,8 @@
+error: expected identifier, found `<`
+  --> $DIR/fn-invalid-generics.rs:5:3
+   |
+LL | fn<~>()> id(x: T) -> T { x }
+   |   ^ expected identifier
+
+error: aborting due to previous error
+
diff --git a/tests/ui/parser/suggest_misplaced_generics/fn-simple.fixed b/tests/ui/parser/suggest_misplaced_generics/fn-simple.fixed
new file mode 100644
index 00000000000..cbfd5f2d39c
--- /dev/null
+++ b/tests/ui/parser/suggest_misplaced_generics/fn-simple.fixed
@@ -0,0 +1,9 @@
+// Issue: 103366 , Suggest fix for misplaced generic params
+// run-rustfix
+
+#[allow(unused)]
+fn id<T>(x: T) -> T { x }
+//~^ ERROR expected identifier, found `<`
+//~| HELP place the generic parameter name after the fn name
+
+fn main() {}
diff --git a/tests/ui/parser/suggest_misplaced_generics/fn-simple.rs b/tests/ui/parser/suggest_misplaced_generics/fn-simple.rs
new file mode 100644
index 00000000000..b207cf70d85
--- /dev/null
+++ b/tests/ui/parser/suggest_misplaced_generics/fn-simple.rs
@@ -0,0 +1,9 @@
+// Issue: 103366 , Suggest fix for misplaced generic params
+// run-rustfix
+
+#[allow(unused)]
+fn<T> id(x: T) -> T { x }
+//~^ ERROR expected identifier, found `<`
+//~| HELP place the generic parameter name after the fn name
+
+fn main() {}
diff --git a/tests/ui/parser/suggest_misplaced_generics/fn-simple.stderr b/tests/ui/parser/suggest_misplaced_generics/fn-simple.stderr
new file mode 100644
index 00000000000..e749f1a0d00
--- /dev/null
+++ b/tests/ui/parser/suggest_misplaced_generics/fn-simple.stderr
@@ -0,0 +1,14 @@
+error: expected identifier, found `<`
+  --> $DIR/fn-simple.rs:5:3
+   |
+LL | fn<T> id(x: T) -> T { x }
+   |   ^ expected identifier
+   |
+help: place the generic parameter name after the fn name
+   |
+LL - fn<T> id(x: T) -> T { x }
+LL + fn id<T>(x: T) -> T { x }
+   |
+
+error: aborting due to previous error
+
diff --git a/tests/ui/parser/suggest_misplaced_generics/struct.fixed b/tests/ui/parser/suggest_misplaced_generics/struct.fixed
new file mode 100644
index 00000000000..fec05bdeca1
--- /dev/null
+++ b/tests/ui/parser/suggest_misplaced_generics/struct.fixed
@@ -0,0 +1,9 @@
+// Issue: 103366 , Suggest fix for misplaced generic params
+// run-rustfix
+
+#[allow(unused)]
+struct Foo<T> { x: T }
+//~^ ERROR expected identifier, found `<`
+//~| HELP place the generic parameter name after the struct name
+
+fn main() {}
diff --git a/tests/ui/parser/suggest_misplaced_generics/struct.rs b/tests/ui/parser/suggest_misplaced_generics/struct.rs
new file mode 100644
index 00000000000..6b80150d546
--- /dev/null
+++ b/tests/ui/parser/suggest_misplaced_generics/struct.rs
@@ -0,0 +1,9 @@
+// Issue: 103366 , Suggest fix for misplaced generic params
+// run-rustfix
+
+#[allow(unused)]
+struct<T> Foo { x: T }
+//~^ ERROR expected identifier, found `<`
+//~| HELP place the generic parameter name after the struct name
+
+fn main() {}
diff --git a/tests/ui/parser/suggest_misplaced_generics/struct.stderr b/tests/ui/parser/suggest_misplaced_generics/struct.stderr
new file mode 100644
index 00000000000..2b650907092
--- /dev/null
+++ b/tests/ui/parser/suggest_misplaced_generics/struct.stderr
@@ -0,0 +1,14 @@
+error: expected identifier, found `<`
+  --> $DIR/struct.rs:5:7
+   |
+LL | struct<T> Foo { x: T }
+   |       ^ expected identifier
+   |
+help: place the generic parameter name after the struct name
+   |
+LL - struct<T> Foo { x: T }
+LL + struct Foo<T> { x: T }
+   |
+
+error: aborting due to previous error
+
diff --git a/tests/ui/parser/suggest_misplaced_generics/trait.fixed b/tests/ui/parser/suggest_misplaced_generics/trait.fixed
new file mode 100644
index 00000000000..a471a078af1
--- /dev/null
+++ b/tests/ui/parser/suggest_misplaced_generics/trait.fixed
@@ -0,0 +1,11 @@
+// Issue: 103366 , Suggest fix for misplaced generic params
+// run-rustfix
+
+#[allow(unused)]
+trait Foo<T> {
+    //~^ ERROR expected identifier, found `<`
+    //~| HELP place the generic parameter name after the trait name
+}
+
+
+fn main() {}
diff --git a/tests/ui/parser/suggest_misplaced_generics/trait.rs b/tests/ui/parser/suggest_misplaced_generics/trait.rs
new file mode 100644
index 00000000000..55355f451f9
--- /dev/null
+++ b/tests/ui/parser/suggest_misplaced_generics/trait.rs
@@ -0,0 +1,11 @@
+// Issue: 103366 , Suggest fix for misplaced generic params
+// run-rustfix
+
+#[allow(unused)]
+trait<T> Foo {
+    //~^ ERROR expected identifier, found `<`
+    //~| HELP place the generic parameter name after the trait name
+}
+
+
+fn main() {}
diff --git a/tests/ui/parser/suggest_misplaced_generics/trait.stderr b/tests/ui/parser/suggest_misplaced_generics/trait.stderr
new file mode 100644
index 00000000000..ac86cfa4697
--- /dev/null
+++ b/tests/ui/parser/suggest_misplaced_generics/trait.stderr
@@ -0,0 +1,14 @@
+error: expected identifier, found `<`
+  --> $DIR/trait.rs:5:6
+   |
+LL | trait<T> Foo {
+   |      ^ expected identifier
+   |
+help: place the generic parameter name after the trait name
+   |
+LL - trait<T> Foo {
+LL + trait Foo<T> {
+   |
+
+error: aborting due to previous error
+
diff --git a/tests/ui/parser/suggest_misplaced_generics/type.fixed b/tests/ui/parser/suggest_misplaced_generics/type.fixed
new file mode 100644
index 00000000000..a97b9e66d0b
--- /dev/null
+++ b/tests/ui/parser/suggest_misplaced_generics/type.fixed
@@ -0,0 +1,9 @@
+// Issue: 103366 , Suggest fix for misplaced generic params
+// run-rustfix
+
+#[allow(unused)]
+type Foo<T> = T;
+//~^ ERROR expected identifier, found `<`
+//~| HELP place the generic parameter name after the type name
+
+fn main() {}
diff --git a/tests/ui/parser/suggest_misplaced_generics/type.rs b/tests/ui/parser/suggest_misplaced_generics/type.rs
new file mode 100644
index 00000000000..17e200536fa
--- /dev/null
+++ b/tests/ui/parser/suggest_misplaced_generics/type.rs
@@ -0,0 +1,9 @@
+// Issue: 103366 , Suggest fix for misplaced generic params
+// run-rustfix
+
+#[allow(unused)]
+type<T> Foo = T;
+//~^ ERROR expected identifier, found `<`
+//~| HELP place the generic parameter name after the type name
+
+fn main() {}
diff --git a/tests/ui/parser/suggest_misplaced_generics/type.stderr b/tests/ui/parser/suggest_misplaced_generics/type.stderr
new file mode 100644
index 00000000000..22744f6cf37
--- /dev/null
+++ b/tests/ui/parser/suggest_misplaced_generics/type.stderr
@@ -0,0 +1,14 @@
+error: expected identifier, found `<`
+  --> $DIR/type.rs:5:5
+   |
+LL | type<T> Foo = T;
+   |     ^ expected identifier
+   |
+help: place the generic parameter name after the type name
+   |
+LL - type<T> Foo = T;
+LL + type Foo<T> = T;
+   |
+
+error: aborting due to previous error
+
diff --git a/tests/ui/single-use-lifetime/issue-107998.rs b/tests/ui/single-use-lifetime/issue-107998.rs
new file mode 100644
index 00000000000..f32688d2058
--- /dev/null
+++ b/tests/ui/single-use-lifetime/issue-107998.rs
@@ -0,0 +1,9 @@
+#![deny(single_use_lifetimes)]
+
+fn with<R>(f: &fn<'a>(x: &'a i32) -> R) -> R {
+    //~^ ERROR function pointer types may not have generic parameters
+    //~| ERROR lifetime parameter `'a` only used once
+    f(&3)
+}
+
+fn main() {}
diff --git a/tests/ui/single-use-lifetime/issue-107998.stderr b/tests/ui/single-use-lifetime/issue-107998.stderr
new file mode 100644
index 00000000000..e870351de9e
--- /dev/null
+++ b/tests/ui/single-use-lifetime/issue-107998.stderr
@@ -0,0 +1,30 @@
+error: function pointer types may not have generic parameters
+  --> $DIR/issue-107998.rs:3:18
+   |
+LL | fn with<R>(f: &fn<'a>(x: &'a i32) -> R) -> R {
+   |                  ^^^^
+   |
+help: consider moving the lifetime parameter to a `for` parameter list
+   |
+LL - fn with<R>(f: &fn<'a>(x: &'a i32) -> R) -> R {
+LL + fn with<R>(f: &for<'a> fn(x: &'a i32) -> R) -> R {
+   |
+
+error: lifetime parameter `'a` only used once
+  --> $DIR/issue-107998.rs:3:19
+   |
+LL | fn with<R>(f: &fn<'a>(x: &'a i32) -> R) -> R {
+   |                   ^^      ---
+   |                   |       |
+   |                   |       ...is used only here
+   |                   |       help: elide the single-use lifetime
+   |                   this lifetime...
+   |
+note: the lint level is defined here
+  --> $DIR/issue-107998.rs:1:9
+   |
+LL | #![deny(single_use_lifetimes)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/traits/issue-103563.rs b/tests/ui/traits/issue-103563.rs
new file mode 100644
index 00000000000..cd3eea09b99
--- /dev/null
+++ b/tests/ui/traits/issue-103563.rs
@@ -0,0 +1,75 @@
+// build-pass
+
+fn main() {
+    let mut log_service = LogService { inner: Inner };
+    log_service.call(());
+}
+
+pub trait Service<Request> {
+    type Response;
+
+    fn call(&mut self, req: Request) -> Self::Response;
+}
+
+pub struct LogService<S> {
+    inner: S,
+}
+
+impl<T, U, S> Service<T> for LogService<S>
+where
+    S: Service<T, Response = U>,
+    U: Extension + 'static,
+    for<'a> U::Item<'a>: std::fmt::Debug,
+{
+    type Response = S::Response;
+
+    fn call(&mut self, req: T) -> Self::Response {
+        self.inner.call(req)
+    }
+}
+
+pub struct Inner;
+
+impl Service<()> for Inner {
+    type Response = Resp;
+
+    fn call(&mut self, req: ()) -> Self::Response {
+        Resp::A(req)
+    }
+}
+
+pub trait Extension {
+    type Item<'a>;
+
+    fn touch<F>(self, f: F) -> Self
+    where
+        for<'a> F: Fn(Self::Item<'a>);
+}
+
+pub enum Resp {
+    A(()),
+}
+
+impl Extension for Resp {
+    type Item<'a> = RespItem<'a>;
+    fn touch<F>(self, _f: F) -> Self
+    where
+        for<'a> F: Fn(Self::Item<'a>),
+    {
+        match self {
+            Self::A(a) => Self::A(a),
+        }
+    }
+}
+
+pub enum RespItem<'a> {
+    A(&'a ()),
+}
+
+impl<'a> std::fmt::Debug for RespItem<'a> {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            Self::A(arg0) => f.debug_tuple("A").field(arg0).finish(),
+        }
+    }
+}
diff --git a/triagebot.toml b/triagebot.toml
index 62a99b70438..883bc8720e2 100644
--- a/triagebot.toml
+++ b/triagebot.toml
@@ -468,6 +468,9 @@ This was probably unintentional and should be reverted before this PR is merged.
 If this was intentional then you can ignore this comment.
 """
 
+[mentions."src/tools/x"]
+message = "`src/tools/x` was changed. Bump version of Cargo.toml in `src/tools/x` so tidy will suggest installing the new version."
+
 [assign]
 warn_non_default_branch = true
 contributing_url = "https://rustc-dev-guide.rust-lang.org/contributing.html"