about summary refs log tree commit diff
path: root/compiler/rustc_resolve/src
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_resolve/src')
-rw-r--r--compiler/rustc_resolve/src/build_reduced_graph.rs1464
-rw-r--r--compiler/rustc_resolve/src/check_unused.rs486
-rw-r--r--compiler/rustc_resolve/src/def_collector.rs375
-rw-r--r--compiler/rustc_resolve/src/diagnostics.rs2976
-rw-r--r--compiler/rustc_resolve/src/diagnostics/tests.rs40
-rw-r--r--compiler/rustc_resolve/src/effective_visibilities.rs284
-rw-r--r--compiler/rustc_resolve/src/errors.rs780
-rw-r--r--compiler/rustc_resolve/src/ident.rs1564
-rw-r--r--compiler/rustc_resolve/src/imports.rs1488
-rw-r--r--compiler/rustc_resolve/src/late.rs4689
-rw-r--r--compiler/rustc_resolve/src/late/diagnostics.rs3338
-rw-r--r--compiler/rustc_resolve/src/lib.rs2175
-rw-r--r--compiler/rustc_resolve/src/macros.rs964
-rw-r--r--compiler/rustc_resolve/src/rustdoc.rs552
14 files changed, 21175 insertions, 0 deletions
diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs
new file mode 100644
index 00000000000..a4e2f9e3ff8
--- /dev/null
+++ b/compiler/rustc_resolve/src/build_reduced_graph.rs
@@ -0,0 +1,1464 @@
+//! After we obtain a fresh AST fragment from a macro, code in this module helps to integrate
+//! that fragment into the module structures that are already partially built.
+//!
+//! Items from the fragment are placed into modules,
+//! unexpanded macros in the fragment are visited and registered.
+//! Imports are also considered items and placed into modules here, but not resolved yet.
+
+use crate::def_collector::collect_definitions;
+use crate::imports::{ImportData, ImportKind};
+use crate::macros::{MacroRulesBinding, MacroRulesScope, MacroRulesScopeRef};
+use crate::Namespace::{self, MacroNS, TypeNS, ValueNS};
+use crate::{errors, BindingKey, MacroData, NameBindingData};
+use crate::{Determinacy, ExternPreludeEntry, Finalize, Module, ModuleKind, ModuleOrUniformRoot};
+use crate::{NameBinding, NameBindingKind, ParentScope, PathResult, PerNS, ResolutionError};
+use crate::{Resolver, ResolverArenas, Segment, ToNameBinding, VisResolutionError};
+
+use rustc_ast::visit::{self, AssocCtxt, Visitor};
+use rustc_ast::{self as ast, AssocItem, AssocItemKind, MetaItemKind, StmtKind};
+use rustc_ast::{Block, ForeignItem, ForeignItemKind, Impl, Item, ItemKind, NodeId};
+use rustc_attr as attr;
+use rustc_data_structures::sync::Lrc;
+use rustc_errors::{struct_span_code_err, Applicability};
+use rustc_expand::expand::AstFragment;
+use rustc_hir::def::{self, *};
+use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID};
+use rustc_metadata::creader::LoadedMacro;
+use rustc_middle::metadata::ModChild;
+use rustc_middle::{bug, ty};
+use rustc_span::hygiene::{ExpnId, LocalExpnId, MacroKind};
+use rustc_span::symbol::{kw, sym, Ident, Symbol};
+use rustc_span::Span;
+
+use std::cell::Cell;
+
+type Res = def::Res<NodeId>;
+
+impl<'a, Id: Into<DefId>> ToNameBinding<'a>
+    for (Module<'a>, ty::Visibility<Id>, Span, LocalExpnId)
+{
+    fn to_name_binding(self, arenas: &'a ResolverArenas<'a>) -> NameBinding<'a> {
+        arenas.alloc_name_binding(NameBindingData {
+            kind: NameBindingKind::Module(self.0),
+            ambiguity: None,
+            warn_ambiguity: false,
+            vis: self.1.to_def_id(),
+            span: self.2,
+            expansion: self.3,
+        })
+    }
+}
+
+impl<'a, Id: Into<DefId>> ToNameBinding<'a> for (Res, ty::Visibility<Id>, Span, LocalExpnId) {
+    fn to_name_binding(self, arenas: &'a ResolverArenas<'a>) -> NameBinding<'a> {
+        arenas.alloc_name_binding(NameBindingData {
+            kind: NameBindingKind::Res(self.0),
+            ambiguity: None,
+            warn_ambiguity: false,
+            vis: self.1.to_def_id(),
+            span: self.2,
+            expansion: self.3,
+        })
+    }
+}
+
+impl<'a, 'tcx> Resolver<'a, 'tcx> {
+    /// Defines `name` in namespace `ns` of module `parent` to be `def` if it is not yet defined;
+    /// otherwise, reports an error.
+    pub(crate) fn define<T>(&mut self, parent: Module<'a>, ident: Ident, ns: Namespace, def: T)
+    where
+        T: ToNameBinding<'a>,
+    {
+        let binding = def.to_name_binding(self.arenas);
+        let key = self.new_disambiguated_key(ident, ns);
+        if let Err(old_binding) = self.try_define(parent, key, binding, false) {
+            self.report_conflict(parent, ident, ns, old_binding, binding);
+        }
+    }
+
+    /// Walks up the tree of definitions starting at `def_id`,
+    /// stopping at the first encountered module.
+    /// Parent block modules for arbitrary def-ids are not recorded for the local crate,
+    /// and are not preserved in metadata for foreign crates, so block modules are never
+    /// returned by this function.
+    ///
+    /// For the local crate ignoring block modules may be incorrect, so use this method with care.
+    ///
+    /// For foreign crates block modules can be ignored without introducing observable differences,
+    /// moreover they has to be ignored right now because they are not kept in metadata.
+    /// Foreign parent modules are used for resolving names used by foreign macros with def-site
+    /// hygiene, therefore block module ignorability relies on macros with def-site hygiene and
+    /// block module parents being unreachable from other crates.
+    /// Reachable macros with block module parents exist due to `#[macro_export] macro_rules!`,
+    /// but they cannot use def-site hygiene, so the assumption holds
+    /// (<https://github.com/rust-lang/rust/pull/77984#issuecomment-712445508>).
+    pub(crate) fn get_nearest_non_block_module(&mut self, mut def_id: DefId) -> Module<'a> {
+        loop {
+            match self.get_module(def_id) {
+                Some(module) => return module,
+                None => def_id = self.tcx.parent(def_id),
+            }
+        }
+    }
+
+    pub(crate) fn expect_module(&mut self, def_id: DefId) -> Module<'a> {
+        self.get_module(def_id).expect("argument `DefId` is not a module")
+    }
+
+    /// If `def_id` refers to a module (in resolver's sense, i.e. a module item, crate root, enum,
+    /// or trait), then this function returns that module's resolver representation, otherwise it
+    /// returns `None`.
+    pub(crate) fn get_module(&mut self, def_id: DefId) -> Option<Module<'a>> {
+        if let module @ Some(..) = self.module_map.get(&def_id) {
+            return module.copied();
+        }
+
+        if !def_id.is_local() {
+            // Query `def_kind` is not used because query system overhead is too expensive here.
+            let def_kind = self.cstore().def_kind_untracked(def_id);
+            if let DefKind::Mod | DefKind::Enum | DefKind::Trait = def_kind {
+                let parent = self
+                    .tcx
+                    .opt_parent(def_id)
+                    .map(|parent_id| self.get_nearest_non_block_module(parent_id));
+                // Query `expn_that_defined` is not used because
+                // hashing spans in its result is expensive.
+                let expn_id = self.cstore().expn_that_defined_untracked(def_id, self.tcx.sess);
+                return Some(self.new_module(
+                    parent,
+                    ModuleKind::Def(def_kind, def_id, self.tcx.item_name(def_id)),
+                    expn_id,
+                    self.def_span(def_id),
+                    // FIXME: Account for `#[no_implicit_prelude]` attributes.
+                    parent.is_some_and(|module| module.no_implicit_prelude),
+                ));
+            }
+        }
+
+        None
+    }
+
+    pub(crate) fn expn_def_scope(&mut self, expn_id: ExpnId) -> Module<'a> {
+        match expn_id.expn_data().macro_def_id {
+            Some(def_id) => self.macro_def_scope(def_id),
+            None => expn_id
+                .as_local()
+                .and_then(|expn_id| self.ast_transform_scopes.get(&expn_id).copied())
+                .unwrap_or(self.graph_root),
+        }
+    }
+
+    pub(crate) fn macro_def_scope(&mut self, def_id: DefId) -> Module<'a> {
+        if let Some(id) = def_id.as_local() {
+            self.local_macro_def_scopes[&id]
+        } else {
+            self.get_nearest_non_block_module(def_id)
+        }
+    }
+
+    pub(crate) fn get_macro(&mut self, res: Res) -> Option<&MacroData> {
+        match res {
+            Res::Def(DefKind::Macro(..), def_id) => Some(self.get_macro_by_def_id(def_id)),
+            Res::NonMacroAttr(_) => Some(&self.non_macro_attr),
+            _ => None,
+        }
+    }
+
+    pub(crate) fn get_macro_by_def_id(&mut self, def_id: DefId) -> &MacroData {
+        if self.macro_map.contains_key(&def_id) {
+            return &self.macro_map[&def_id];
+        }
+
+        let loaded_macro = self.cstore().load_macro_untracked(def_id, self.tcx);
+        let macro_data = match loaded_macro {
+            LoadedMacro::MacroDef(item, edition) => self.compile_macro(&item, edition),
+            LoadedMacro::ProcMacro(ext) => MacroData::new(Lrc::new(ext)),
+        };
+
+        self.macro_map.entry(def_id).or_insert(macro_data)
+    }
+
+    pub(crate) fn build_reduced_graph(
+        &mut self,
+        fragment: &AstFragment,
+        parent_scope: ParentScope<'a>,
+    ) -> MacroRulesScopeRef<'a> {
+        collect_definitions(self, fragment, parent_scope.expansion);
+        let mut visitor = BuildReducedGraphVisitor { r: self, parent_scope };
+        fragment.visit_with(&mut visitor);
+        visitor.parent_scope.macro_rules
+    }
+
+    pub(crate) fn build_reduced_graph_external(&mut self, module: Module<'a>) {
+        for child in self.tcx.module_children(module.def_id()) {
+            let parent_scope = ParentScope::module(module, self);
+            BuildReducedGraphVisitor { r: self, parent_scope }
+                .build_reduced_graph_for_external_crate_res(child);
+        }
+    }
+}
+
+struct BuildReducedGraphVisitor<'a, 'b, 'tcx> {
+    r: &'b mut Resolver<'a, 'tcx>,
+    parent_scope: ParentScope<'a>,
+}
+
+impl<'a, 'tcx> AsMut<Resolver<'a, 'tcx>> for BuildReducedGraphVisitor<'a, '_, 'tcx> {
+    fn as_mut(&mut self) -> &mut Resolver<'a, 'tcx> {
+        self.r
+    }
+}
+
+impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
+    fn res(&self, def_id: impl Into<DefId>) -> Res {
+        let def_id = def_id.into();
+        Res::Def(self.r.tcx.def_kind(def_id), def_id)
+    }
+
+    fn resolve_visibility(&mut self, vis: &ast::Visibility) -> ty::Visibility {
+        self.try_resolve_visibility(vis, true).unwrap_or_else(|err| {
+            self.r.report_vis_error(err);
+            ty::Visibility::Public
+        })
+    }
+
+    fn try_resolve_visibility<'ast>(
+        &mut self,
+        vis: &'ast ast::Visibility,
+        finalize: bool,
+    ) -> Result<ty::Visibility, VisResolutionError<'ast>> {
+        let parent_scope = &self.parent_scope;
+        match vis.kind {
+            ast::VisibilityKind::Public => Ok(ty::Visibility::Public),
+            ast::VisibilityKind::Inherited => {
+                Ok(match self.parent_scope.module.kind {
+                    // Any inherited visibility resolved directly inside an enum or trait
+                    // (i.e. variants, fields, and trait items) inherits from the visibility
+                    // of the enum or trait.
+                    ModuleKind::Def(DefKind::Enum | DefKind::Trait, def_id, _) => {
+                        self.r.tcx.visibility(def_id).expect_local()
+                    }
+                    // Otherwise, the visibility is restricted to the nearest parent `mod` item.
+                    _ => ty::Visibility::Restricted(
+                        self.parent_scope.module.nearest_parent_mod().expect_local(),
+                    ),
+                })
+            }
+            ast::VisibilityKind::Restricted { ref path, id, .. } => {
+                // For visibilities we are not ready to provide correct implementation of "uniform
+                // paths" right now, so on 2018 edition we only allow module-relative paths for now.
+                // On 2015 edition visibilities are resolved as crate-relative by default,
+                // so we are prepending a root segment if necessary.
+                let ident = path.segments.get(0).expect("empty path in visibility").ident;
+                let crate_root = if ident.is_path_segment_keyword() {
+                    None
+                } else if ident.span.is_rust_2015() {
+                    Some(Segment::from_ident(Ident::new(
+                        kw::PathRoot,
+                        path.span.shrink_to_lo().with_ctxt(ident.span.ctxt()),
+                    )))
+                } else {
+                    return Err(VisResolutionError::Relative2018(ident.span, path));
+                };
+
+                let segments = crate_root
+                    .into_iter()
+                    .chain(path.segments.iter().map(|seg| seg.into()))
+                    .collect::<Vec<_>>();
+                let expected_found_error = |res| {
+                    Err(VisResolutionError::ExpectedFound(
+                        path.span,
+                        Segment::names_to_string(&segments),
+                        res,
+                    ))
+                };
+                match self.r.resolve_path(
+                    &segments,
+                    None,
+                    parent_scope,
+                    finalize.then(|| Finalize::new(id, path.span)),
+                    None,
+                ) {
+                    PathResult::Module(ModuleOrUniformRoot::Module(module)) => {
+                        let res = module.res().expect("visibility resolved to unnamed block");
+                        if finalize {
+                            self.r.record_partial_res(id, PartialRes::new(res));
+                        }
+                        if module.is_normal() {
+                            match res {
+                                Res::Err => Ok(ty::Visibility::Public),
+                                _ => {
+                                    let vis = ty::Visibility::Restricted(res.def_id());
+                                    if self.r.is_accessible_from(vis, parent_scope.module) {
+                                        Ok(vis.expect_local())
+                                    } else {
+                                        Err(VisResolutionError::AncestorOnly(path.span))
+                                    }
+                                }
+                            }
+                        } else {
+                            expected_found_error(res)
+                        }
+                    }
+                    PathResult::Module(..) => Err(VisResolutionError::ModuleOnly(path.span)),
+                    PathResult::NonModule(partial_res) => {
+                        expected_found_error(partial_res.expect_full_res())
+                    }
+                    PathResult::Failed { span, label, suggestion, .. } => {
+                        Err(VisResolutionError::FailedToResolve(span, label, suggestion))
+                    }
+                    PathResult::Indeterminate => Err(VisResolutionError::Indeterminate(path.span)),
+                }
+            }
+        }
+    }
+
+    fn insert_field_def_ids(&mut self, def_id: LocalDefId, vdata: &ast::VariantData) {
+        if vdata.fields().iter().any(|field| field.is_placeholder) {
+            // The fields are not expanded yet.
+            return;
+        }
+        let def_ids = vdata.fields().iter().map(|field| self.r.local_def_id(field.id).to_def_id());
+        self.r.field_def_ids.insert(def_id, self.r.tcx.arena.alloc_from_iter(def_ids));
+    }
+
+    fn insert_field_visibilities_local(&mut self, def_id: DefId, vdata: &ast::VariantData) {
+        let field_vis = vdata
+            .fields()
+            .iter()
+            .map(|field| field.vis.span.until(field.ident.map_or(field.ty.span, |i| i.span)))
+            .collect();
+        self.r.field_visibility_spans.insert(def_id, field_vis);
+    }
+
+    fn block_needs_anonymous_module(&mut self, block: &Block) -> bool {
+        // If any statements are items, we need to create an anonymous module
+        block
+            .stmts
+            .iter()
+            .any(|statement| matches!(statement.kind, StmtKind::Item(_) | StmtKind::MacCall(_)))
+    }
+
+    // Add an import to the current module.
+    fn add_import(
+        &mut self,
+        module_path: Vec<Segment>,
+        kind: ImportKind<'a>,
+        span: Span,
+        item: &ast::Item,
+        root_span: Span,
+        root_id: NodeId,
+        vis: ty::Visibility,
+    ) {
+        let current_module = self.parent_scope.module;
+        let import = self.r.arenas.alloc_import(ImportData {
+            kind,
+            parent_scope: self.parent_scope,
+            module_path,
+            imported_module: Cell::new(None),
+            span,
+            use_span: item.span,
+            use_span_with_attributes: item.span_with_attributes(),
+            has_attributes: !item.attrs.is_empty(),
+            root_span,
+            root_id,
+            vis: Cell::new(Some(vis)),
+            used: Cell::new(false),
+        });
+
+        self.r.indeterminate_imports.push(import);
+        match import.kind {
+            // Don't add unresolved underscore imports to modules
+            ImportKind::Single { target: Ident { name: kw::Underscore, .. }, .. } => {}
+            ImportKind::Single { target, type_ns_only, .. } => {
+                self.r.per_ns(|this, ns| {
+                    if !type_ns_only || ns == TypeNS {
+                        let key = BindingKey::new(target, ns);
+                        let mut resolution = this.resolution(current_module, key).borrow_mut();
+                        resolution.single_imports.insert(import);
+                    }
+                });
+            }
+            // We don't add prelude imports to the globs since they only affect lexical scopes,
+            // which are not relevant to import resolution.
+            ImportKind::Glob { is_prelude: true, .. } => {}
+            ImportKind::Glob { .. } => current_module.globs.borrow_mut().push(import),
+            _ => unreachable!(),
+        }
+    }
+
+    fn build_reduced_graph_for_use_tree(
+        &mut self,
+        // This particular use tree
+        use_tree: &ast::UseTree,
+        id: NodeId,
+        parent_prefix: &[Segment],
+        nested: bool,
+        list_stem: bool,
+        // The whole `use` item
+        item: &Item,
+        vis: ty::Visibility,
+        root_span: Span,
+    ) {
+        debug!(
+            "build_reduced_graph_for_use_tree(parent_prefix={:?}, use_tree={:?}, nested={})",
+            parent_prefix, use_tree, nested
+        );
+
+        // Top level use tree reuses the item's id and list stems reuse their parent
+        // use tree's ids, so in both cases their visibilities are already filled.
+        if nested && !list_stem {
+            self.r.feed_visibility(self.r.local_def_id(id), vis);
+        }
+
+        let mut prefix_iter = parent_prefix
+            .iter()
+            .cloned()
+            .chain(use_tree.prefix.segments.iter().map(|seg| seg.into()))
+            .peekable();
+
+        // On 2015 edition imports are resolved as crate-relative by default,
+        // so prefixes are prepended with crate root segment if necessary.
+        // The root is prepended lazily, when the first non-empty prefix or terminating glob
+        // appears, so imports in braced groups can have roots prepended independently.
+        let is_glob = matches!(use_tree.kind, ast::UseTreeKind::Glob);
+        let crate_root = match prefix_iter.peek() {
+            Some(seg) if !seg.ident.is_path_segment_keyword() && seg.ident.span.is_rust_2015() => {
+                Some(seg.ident.span.ctxt())
+            }
+            None if is_glob && use_tree.span.is_rust_2015() => Some(use_tree.span.ctxt()),
+            _ => None,
+        }
+        .map(|ctxt| {
+            Segment::from_ident(Ident::new(
+                kw::PathRoot,
+                use_tree.prefix.span.shrink_to_lo().with_ctxt(ctxt),
+            ))
+        });
+
+        let prefix = crate_root.into_iter().chain(prefix_iter).collect::<Vec<_>>();
+        debug!("build_reduced_graph_for_use_tree: prefix={:?}", prefix);
+
+        let empty_for_self = |prefix: &[Segment]| {
+            prefix.is_empty() || prefix.len() == 1 && prefix[0].ident.name == kw::PathRoot
+        };
+        match use_tree.kind {
+            ast::UseTreeKind::Simple(rename) => {
+                let mut ident = use_tree.ident();
+                let mut module_path = prefix;
+                let mut source = module_path.pop().unwrap();
+                let mut type_ns_only = false;
+
+                if nested {
+                    // Correctly handle `self`
+                    if source.ident.name == kw::SelfLower {
+                        type_ns_only = true;
+
+                        if empty_for_self(&module_path) {
+                            self.r.report_error(
+                                use_tree.span,
+                                ResolutionError::SelfImportOnlyInImportListWithNonEmptyPrefix,
+                            );
+                            return;
+                        }
+
+                        // Replace `use foo::{ self };` with `use foo;`
+                        let self_span = source.ident.span;
+                        source = module_path.pop().unwrap();
+                        if rename.is_none() {
+                            // Keep the span of `self`, but the name of `foo`
+                            ident = Ident { name: source.ident.name, span: self_span };
+                        }
+                    }
+                } else {
+                    // Disallow `self`
+                    if source.ident.name == kw::SelfLower {
+                        let parent = module_path.last();
+
+                        let span = match parent {
+                            // only `::self` from `use foo::self as bar`
+                            Some(seg) => seg.ident.span.shrink_to_hi().to(source.ident.span),
+                            None => source.ident.span,
+                        };
+                        let span_with_rename = match rename {
+                            // only `self as bar` from `use foo::self as bar`
+                            Some(rename) => source.ident.span.to(rename.span),
+                            None => source.ident.span,
+                        };
+                        self.r.report_error(
+                            span,
+                            ResolutionError::SelfImportsOnlyAllowedWithin {
+                                root: parent.is_none(),
+                                span_with_rename,
+                            },
+                        );
+
+                        // Error recovery: replace `use foo::self;` with `use foo;`
+                        if let Some(parent) = module_path.pop() {
+                            source = parent;
+                            if rename.is_none() {
+                                ident = source.ident;
+                            }
+                        }
+                    }
+
+                    // Disallow `use $crate;`
+                    if source.ident.name == kw::DollarCrate && module_path.is_empty() {
+                        let crate_root = self.r.resolve_crate_root(source.ident);
+                        let crate_name = match crate_root.kind {
+                            ModuleKind::Def(.., name) => name,
+                            ModuleKind::Block => unreachable!(),
+                        };
+                        // HACK(eddyb) unclear how good this is, but keeping `$crate`
+                        // in `source` breaks `tests/ui/imports/import-crate-var.rs`,
+                        // while the current crate doesn't have a valid `crate_name`.
+                        if crate_name != kw::Empty {
+                            // `crate_name` should not be interpreted as relative.
+                            module_path.push(Segment::from_ident_and_id(
+                                Ident { name: kw::PathRoot, span: source.ident.span },
+                                self.r.next_node_id(),
+                            ));
+                            source.ident.name = crate_name;
+                        }
+                        if rename.is_none() {
+                            ident.name = crate_name;
+                        }
+
+                        self.r.dcx().emit_err(errors::CrateImported { span: item.span });
+                    }
+                }
+
+                if ident.name == kw::Crate {
+                    self.r.dcx().span_err(
+                        ident.span,
+                        "crate root imports need to be explicitly named: \
+                         `use crate as name;`",
+                    );
+                }
+
+                let kind = ImportKind::Single {
+                    source: source.ident,
+                    target: ident,
+                    source_bindings: PerNS {
+                        type_ns: Cell::new(Err(Determinacy::Undetermined)),
+                        value_ns: Cell::new(Err(Determinacy::Undetermined)),
+                        macro_ns: Cell::new(Err(Determinacy::Undetermined)),
+                    },
+                    target_bindings: PerNS {
+                        type_ns: Cell::new(None),
+                        value_ns: Cell::new(None),
+                        macro_ns: Cell::new(None),
+                    },
+                    type_ns_only,
+                    nested,
+                    id,
+                };
+
+                self.add_import(module_path, kind, use_tree.span, item, root_span, item.id, vis);
+            }
+            ast::UseTreeKind::Glob => {
+                let kind = ImportKind::Glob {
+                    is_prelude: attr::contains_name(&item.attrs, sym::prelude_import),
+                    max_vis: Cell::new(None),
+                    id,
+                };
+
+                self.add_import(prefix, kind, use_tree.span, item, root_span, item.id, vis);
+            }
+            ast::UseTreeKind::Nested(ref items) => {
+                // Ensure there is at most one `self` in the list
+                let self_spans = items
+                    .iter()
+                    .filter_map(|(use_tree, _)| {
+                        if let ast::UseTreeKind::Simple(..) = use_tree.kind {
+                            if use_tree.ident().name == kw::SelfLower {
+                                return Some(use_tree.span);
+                            }
+                        }
+
+                        None
+                    })
+                    .collect::<Vec<_>>();
+                if self_spans.len() > 1 {
+                    let mut e = self.r.into_struct_error(
+                        self_spans[0],
+                        ResolutionError::SelfImportCanOnlyAppearOnceInTheList,
+                    );
+
+                    for other_span in self_spans.iter().skip(1) {
+                        e.span_label(*other_span, "another `self` import appears here");
+                    }
+
+                    e.emit();
+                }
+
+                for &(ref tree, id) in items {
+                    self.build_reduced_graph_for_use_tree(
+                        // This particular use tree
+                        tree, id, &prefix, true, false, // The whole `use` item
+                        item, vis, root_span,
+                    );
+                }
+
+                // Empty groups `a::b::{}` are turned into synthetic `self` imports
+                // `a::b::c::{self as _}`, so that their prefixes are correctly
+                // resolved and checked for privacy/stability/etc.
+                if items.is_empty() && !empty_for_self(&prefix) {
+                    let new_span = prefix[prefix.len() - 1].ident.span;
+                    let tree = ast::UseTree {
+                        prefix: ast::Path::from_ident(Ident::new(kw::SelfLower, new_span)),
+                        kind: ast::UseTreeKind::Simple(Some(Ident::new(kw::Underscore, new_span))),
+                        span: use_tree.span,
+                    };
+                    self.build_reduced_graph_for_use_tree(
+                        // This particular use tree
+                        &tree,
+                        id,
+                        &prefix,
+                        true,
+                        true,
+                        // The whole `use` item
+                        item,
+                        ty::Visibility::Restricted(
+                            self.parent_scope.module.nearest_parent_mod().expect_local(),
+                        ),
+                        root_span,
+                    );
+                }
+            }
+        }
+    }
+
+    /// Constructs the reduced graph for one item.
+    fn build_reduced_graph_for_item(&mut self, item: &'b Item) {
+        let parent_scope = &self.parent_scope;
+        let parent = parent_scope.module;
+        let expansion = parent_scope.expansion;
+        let ident = item.ident;
+        let sp = item.span;
+        let vis = self.resolve_visibility(&item.vis);
+        let local_def_id = self.r.local_def_id(item.id);
+        let def_id = local_def_id.to_def_id();
+        let def_kind = self.r.tcx.def_kind(def_id);
+        let res = Res::Def(def_kind, def_id);
+
+        self.r.feed_visibility(local_def_id, vis);
+
+        match item.kind {
+            ItemKind::Use(ref use_tree) => {
+                self.build_reduced_graph_for_use_tree(
+                    // This particular use tree
+                    use_tree,
+                    item.id,
+                    &[],
+                    false,
+                    false,
+                    // The whole `use` item
+                    item,
+                    vis,
+                    use_tree.span,
+                );
+            }
+
+            ItemKind::ExternCrate(orig_name) => {
+                self.build_reduced_graph_for_extern_crate(
+                    orig_name,
+                    item,
+                    local_def_id,
+                    vis,
+                    parent,
+                );
+            }
+
+            ItemKind::Mod(..) => {
+                let module = self.r.new_module(
+                    Some(parent),
+                    ModuleKind::Def(def_kind, def_id, ident.name),
+                    expansion.to_expn_id(),
+                    item.span,
+                    parent.no_implicit_prelude
+                        || attr::contains_name(&item.attrs, sym::no_implicit_prelude),
+                );
+                self.r.define(parent, ident, TypeNS, (module, vis, sp, expansion));
+
+                // Descend into the module.
+                self.parent_scope.module = module;
+            }
+
+            // These items live in the value namespace.
+            ItemKind::Const(..) | ItemKind::Delegation(..) | ItemKind::Static(..) => {
+                self.r.define(parent, ident, ValueNS, (res, vis, sp, expansion));
+            }
+            ItemKind::Fn(..) => {
+                self.r.define(parent, ident, ValueNS, (res, vis, sp, expansion));
+
+                // Functions introducing procedural macros reserve a slot
+                // in the macro namespace as well (see #52225).
+                self.define_macro(item);
+            }
+
+            // These items live in the type namespace.
+            ItemKind::TyAlias(..) | ItemKind::TraitAlias(..) => {
+                self.r.define(parent, ident, TypeNS, (res, vis, sp, expansion));
+            }
+
+            ItemKind::Enum(_, _) | ItemKind::Trait(..) => {
+                let module = self.r.new_module(
+                    Some(parent),
+                    ModuleKind::Def(def_kind, def_id, ident.name),
+                    expansion.to_expn_id(),
+                    item.span,
+                    parent.no_implicit_prelude,
+                );
+                self.r.define(parent, ident, TypeNS, (module, vis, sp, expansion));
+                self.parent_scope.module = module;
+            }
+
+            // These items live in both the type and value namespaces.
+            ItemKind::Struct(ref vdata, _) => {
+                // Define a name in the type namespace.
+                self.r.define(parent, ident, TypeNS, (res, vis, sp, expansion));
+
+                // Record field names for error reporting.
+                self.insert_field_def_ids(local_def_id, vdata);
+                self.insert_field_visibilities_local(def_id, vdata);
+
+                // If this is a tuple or unit struct, define a name
+                // in the value namespace as well.
+                if let Some(ctor_node_id) = vdata.ctor_node_id() {
+                    // If the structure is marked as non_exhaustive then lower the visibility
+                    // to within the crate.
+                    let mut ctor_vis = if vis.is_public()
+                        && attr::contains_name(&item.attrs, sym::non_exhaustive)
+                    {
+                        ty::Visibility::Restricted(CRATE_DEF_ID)
+                    } else {
+                        vis
+                    };
+
+                    let mut ret_fields = Vec::with_capacity(vdata.fields().len());
+
+                    for field in vdata.fields() {
+                        // NOTE: The field may be an expansion placeholder, but expansion sets
+                        // correct visibilities for unnamed field placeholders specifically, so the
+                        // constructor visibility should still be determined correctly.
+                        let field_vis = self
+                            .try_resolve_visibility(&field.vis, false)
+                            .unwrap_or(ty::Visibility::Public);
+                        if ctor_vis.is_at_least(field_vis, self.r.tcx) {
+                            ctor_vis = field_vis;
+                        }
+                        ret_fields.push(field_vis.to_def_id());
+                    }
+                    let ctor_def_id = self.r.local_def_id(ctor_node_id);
+                    let ctor_res = self.res(ctor_def_id);
+                    self.r.define(parent, ident, ValueNS, (ctor_res, ctor_vis, sp, expansion));
+                    self.r.feed_visibility(ctor_def_id, ctor_vis);
+                    // We need the field visibility spans also for the constructor for E0603.
+                    self.insert_field_visibilities_local(ctor_def_id.to_def_id(), vdata);
+
+                    self.r
+                        .struct_constructors
+                        .insert(local_def_id, (ctor_res, ctor_vis.to_def_id(), ret_fields));
+                }
+            }
+
+            ItemKind::Union(ref vdata, _) => {
+                self.r.define(parent, ident, TypeNS, (res, vis, sp, expansion));
+
+                // Record field names for error reporting.
+                self.insert_field_def_ids(local_def_id, vdata);
+                self.insert_field_visibilities_local(def_id, vdata);
+            }
+
+            // These items do not add names to modules.
+            ItemKind::Impl(box Impl { of_trait: Some(..), .. }) => {
+                self.r.trait_impl_items.insert(local_def_id);
+            }
+            ItemKind::Impl { .. } | ItemKind::ForeignMod(..) | ItemKind::GlobalAsm(..) => {}
+
+            ItemKind::MacroDef(..) | ItemKind::MacCall(_) => unreachable!(),
+        }
+    }
+
+    fn build_reduced_graph_for_extern_crate(
+        &mut self,
+        orig_name: Option<Symbol>,
+        item: &Item,
+        local_def_id: LocalDefId,
+        vis: ty::Visibility,
+        parent: Module<'a>,
+    ) {
+        let ident = item.ident;
+        let sp = item.span;
+        let parent_scope = self.parent_scope;
+        let expansion = parent_scope.expansion;
+
+        let (used, module, binding) = if orig_name.is_none() && ident.name == kw::SelfLower {
+            self.r
+                .dcx()
+                .struct_span_err(item.span, "`extern crate self;` requires renaming")
+                .with_span_suggestion(
+                    item.span,
+                    "rename the `self` crate to be able to import it",
+                    "extern crate self as name;",
+                    Applicability::HasPlaceholders,
+                )
+                .emit();
+            return;
+        } else if orig_name == Some(kw::SelfLower) {
+            Some(self.r.graph_root)
+        } else {
+            let tcx = self.r.tcx;
+            let crate_id = self.r.crate_loader(|c| {
+                c.process_extern_crate(item, local_def_id, &tcx.definitions_untracked())
+            });
+            crate_id.map(|crate_id| {
+                self.r.extern_crate_map.insert(local_def_id, crate_id);
+                self.r.expect_module(crate_id.as_def_id())
+            })
+        }
+        .map(|module| {
+            let used = self.process_macro_use_imports(item, module);
+            let vis = ty::Visibility::<LocalDefId>::Public;
+            let binding = (module, vis, sp, expansion).to_name_binding(self.r.arenas);
+            (used, Some(ModuleOrUniformRoot::Module(module)), binding)
+        })
+        .unwrap_or((true, None, self.r.dummy_binding));
+        let import = self.r.arenas.alloc_import(ImportData {
+            kind: ImportKind::ExternCrate { source: orig_name, target: ident, id: item.id },
+            root_id: item.id,
+            parent_scope: self.parent_scope,
+            imported_module: Cell::new(module),
+            has_attributes: !item.attrs.is_empty(),
+            use_span_with_attributes: item.span_with_attributes(),
+            use_span: item.span,
+            root_span: item.span,
+            span: item.span,
+            module_path: Vec::new(),
+            vis: Cell::new(Some(vis)),
+            used: Cell::new(used),
+        });
+        self.r.potentially_unused_imports.push(import);
+        let imported_binding = self.r.import(binding, import);
+        if parent == self.r.graph_root {
+            if let Some(entry) = self.r.extern_prelude.get(&ident.normalize_to_macros_2_0()) {
+                if expansion != LocalExpnId::ROOT && orig_name.is_some() && !entry.is_import() {
+                    let msg = "macro-expanded `extern crate` items cannot \
+                                       shadow names passed with `--extern`";
+                    self.r.dcx().span_err(item.span, msg);
+                    // `return` is intended to discard this binding because it's an
+                    // unregistered ambiguity error which would result in a panic
+                    // caused by inconsistency `path_res`
+                    // more details: https://github.com/rust-lang/rust/pull/111761
+                    return;
+                }
+            }
+            let entry = self
+                .r
+                .extern_prelude
+                .entry(ident.normalize_to_macros_2_0())
+                .or_insert(ExternPreludeEntry { binding: None, introduced_by_item: true });
+            // Binding from `extern crate` item in source code can replace
+            // a binding from `--extern` on command line here.
+            entry.binding = Some(imported_binding);
+            if orig_name.is_some() {
+                entry.introduced_by_item = true;
+            }
+        }
+        self.r.define(parent, ident, TypeNS, imported_binding);
+    }
+
+    /// Constructs the reduced graph for one foreign item.
+    fn build_reduced_graph_for_foreign_item(&mut self, item: &ForeignItem) {
+        let local_def_id = self.r.local_def_id(item.id);
+        let def_id = local_def_id.to_def_id();
+        let ns = match item.kind {
+            ForeignItemKind::Fn(..) => ValueNS,
+            ForeignItemKind::Static(..) => ValueNS,
+            ForeignItemKind::TyAlias(..) => TypeNS,
+            ForeignItemKind::MacCall(..) => unreachable!(),
+        };
+        let parent = self.parent_scope.module;
+        let expansion = self.parent_scope.expansion;
+        let vis = self.resolve_visibility(&item.vis);
+        self.r.define(parent, item.ident, ns, (self.res(def_id), vis, item.span, expansion));
+        self.r.feed_visibility(local_def_id, vis);
+    }
+
+    fn build_reduced_graph_for_block(&mut self, block: &Block) {
+        let parent = self.parent_scope.module;
+        let expansion = self.parent_scope.expansion;
+        if self.block_needs_anonymous_module(block) {
+            let module = self.r.new_module(
+                Some(parent),
+                ModuleKind::Block,
+                expansion.to_expn_id(),
+                block.span,
+                parent.no_implicit_prelude,
+            );
+            self.r.block_map.insert(block.id, module);
+            self.parent_scope.module = module; // Descend into the block.
+        }
+    }
+
+    /// Builds the reduced graph for a single item in an external crate.
+    fn build_reduced_graph_for_external_crate_res(&mut self, child: &ModChild) {
+        let parent = self.parent_scope.module;
+        let ModChild { ident, res, vis, ref reexport_chain } = *child;
+        let span = self.r.def_span(
+            reexport_chain
+                .first()
+                .and_then(|reexport| reexport.id())
+                .unwrap_or_else(|| res.def_id()),
+        );
+        let res = res.expect_non_local();
+        let expansion = self.parent_scope.expansion;
+        // Record primary definitions.
+        match res {
+            Res::Def(DefKind::Mod | DefKind::Enum | DefKind::Trait, def_id) => {
+                let module = self.r.expect_module(def_id);
+                self.r.define(parent, ident, TypeNS, (module, vis, span, expansion));
+            }
+            Res::Def(
+                DefKind::Struct
+                | DefKind::Union
+                | DefKind::Variant
+                | DefKind::TyAlias
+                | DefKind::ForeignTy
+                | DefKind::OpaqueTy
+                | DefKind::TraitAlias
+                | DefKind::AssocTy,
+                _,
+            )
+            | Res::PrimTy(..)
+            | Res::ToolMod => self.r.define(parent, ident, TypeNS, (res, vis, span, expansion)),
+            Res::Def(
+                DefKind::Fn
+                | DefKind::AssocFn
+                | DefKind::Static(_)
+                | DefKind::Const
+                | DefKind::AssocConst
+                | DefKind::Ctor(..),
+                _,
+            ) => self.r.define(parent, ident, ValueNS, (res, vis, span, expansion)),
+            Res::Def(DefKind::Macro(..), _) | Res::NonMacroAttr(..) => {
+                self.r.define(parent, ident, MacroNS, (res, vis, span, expansion))
+            }
+            Res::Def(
+                DefKind::TyParam
+                | DefKind::ConstParam
+                | DefKind::ExternCrate
+                | DefKind::Use
+                | DefKind::ForeignMod
+                | DefKind::AnonConst
+                | DefKind::InlineConst
+                | DefKind::Field
+                | DefKind::LifetimeParam
+                | DefKind::GlobalAsm
+                | DefKind::Closure
+                | DefKind::Impl { .. },
+                _,
+            )
+            | Res::Local(..)
+            | Res::SelfTyParam { .. }
+            | Res::SelfTyAlias { .. }
+            | Res::SelfCtor(..)
+            | Res::Err => bug!("unexpected resolution: {:?}", res),
+        }
+    }
+
+    fn add_macro_use_binding(
+        &mut self,
+        name: Symbol,
+        binding: NameBinding<'a>,
+        span: Span,
+        allow_shadowing: bool,
+    ) {
+        if self.r.macro_use_prelude.insert(name, binding).is_some() && !allow_shadowing {
+            let msg = format!("`{name}` is already in scope");
+            let note =
+                "macro-expanded `#[macro_use]`s may not shadow existing macros (see RFC 1560)";
+            self.r.dcx().struct_span_err(span, msg).with_note(note).emit();
+        }
+    }
+
+    /// Returns `true` if we should consider the underlying `extern crate` to be used.
+    fn process_macro_use_imports(&mut self, item: &Item, module: Module<'a>) -> bool {
+        let mut import_all = None;
+        let mut single_imports = Vec::new();
+        for attr in &item.attrs {
+            if attr.has_name(sym::macro_use) {
+                if self.parent_scope.module.parent.is_some() {
+                    struct_span_code_err!(
+                        self.r.dcx(),
+                        item.span,
+                        E0468,
+                        "an `extern crate` loading macros must be at the crate root"
+                    )
+                    .emit();
+                }
+                if let ItemKind::ExternCrate(Some(orig_name)) = item.kind {
+                    if orig_name == kw::SelfLower {
+                        self.r.dcx().emit_err(errors::MacroUseExternCrateSelf { span: attr.span });
+                    }
+                }
+                let ill_formed = |span| {
+                    struct_span_code_err!(self.r.dcx(), span, E0466, "bad macro import").emit();
+                };
+                match attr.meta() {
+                    Some(meta) => match meta.kind {
+                        MetaItemKind::Word => {
+                            import_all = Some(meta.span);
+                            break;
+                        }
+                        MetaItemKind::List(nested_metas) => {
+                            for nested_meta in nested_metas {
+                                match nested_meta.ident() {
+                                    Some(ident) if nested_meta.is_word() => {
+                                        single_imports.push(ident)
+                                    }
+                                    _ => ill_formed(nested_meta.span()),
+                                }
+                            }
+                        }
+                        MetaItemKind::NameValue(..) => ill_formed(meta.span),
+                    },
+                    None => ill_formed(attr.span),
+                }
+            }
+        }
+
+        let macro_use_import = |this: &Self, span| {
+            this.r.arenas.alloc_import(ImportData {
+                kind: ImportKind::MacroUse,
+                root_id: item.id,
+                parent_scope: this.parent_scope,
+                imported_module: Cell::new(Some(ModuleOrUniformRoot::Module(module))),
+                use_span_with_attributes: item.span_with_attributes(),
+                has_attributes: !item.attrs.is_empty(),
+                use_span: item.span,
+                root_span: span,
+                span,
+                module_path: Vec::new(),
+                vis: Cell::new(Some(ty::Visibility::Restricted(CRATE_DEF_ID))),
+                used: Cell::new(false),
+            })
+        };
+
+        let allow_shadowing = self.parent_scope.expansion == LocalExpnId::ROOT;
+        if let Some(span) = import_all {
+            let import = macro_use_import(self, span);
+            self.r.potentially_unused_imports.push(import);
+            module.for_each_child(self, |this, ident, ns, binding| {
+                if ns == MacroNS {
+                    let imported_binding = this.r.import(binding, import);
+                    this.add_macro_use_binding(ident.name, imported_binding, span, allow_shadowing);
+                }
+            });
+        } else {
+            for ident in single_imports.iter().cloned() {
+                let result = self.r.maybe_resolve_ident_in_module(
+                    ModuleOrUniformRoot::Module(module),
+                    ident,
+                    MacroNS,
+                    &self.parent_scope,
+                );
+                if let Ok(binding) = result {
+                    let import = macro_use_import(self, ident.span);
+                    self.r.potentially_unused_imports.push(import);
+                    let imported_binding = self.r.import(binding, import);
+                    self.add_macro_use_binding(
+                        ident.name,
+                        imported_binding,
+                        ident.span,
+                        allow_shadowing,
+                    );
+                } else {
+                    struct_span_code_err!(
+                        self.r.dcx(),
+                        ident.span,
+                        E0469,
+                        "imported macro not found"
+                    )
+                    .emit();
+                }
+            }
+        }
+        import_all.is_some() || !single_imports.is_empty()
+    }
+
+    /// Returns `true` if this attribute list contains `macro_use`.
+    fn contains_macro_use(&mut self, attrs: &[ast::Attribute]) -> bool {
+        for attr in attrs {
+            if attr.has_name(sym::macro_escape) {
+                let msg = "`#[macro_escape]` is a deprecated synonym for `#[macro_use]`";
+                let mut err = self.r.dcx().struct_span_warn(attr.span, msg);
+                if let ast::AttrStyle::Inner = attr.style {
+                    err.help("try an outer attribute: `#[macro_use]`");
+                }
+                err.emit();
+            } else if !attr.has_name(sym::macro_use) {
+                continue;
+            }
+
+            if !attr.is_word() {
+                self.r.dcx().span_err(attr.span, "arguments to `macro_use` are not allowed here");
+            }
+            return true;
+        }
+
+        false
+    }
+
+    fn visit_invoc(&mut self, id: NodeId) -> LocalExpnId {
+        let invoc_id = id.placeholder_to_expn_id();
+        let old_parent_scope = self.r.invocation_parent_scopes.insert(invoc_id, self.parent_scope);
+        assert!(old_parent_scope.is_none(), "invocation data is reset for an invocation");
+        invoc_id
+    }
+
+    /// Visit invocation in context in which it can emit a named item (possibly `macro_rules`)
+    /// directly into its parent scope's module.
+    fn visit_invoc_in_module(&mut self, id: NodeId) -> MacroRulesScopeRef<'a> {
+        let invoc_id = self.visit_invoc(id);
+        self.parent_scope.module.unexpanded_invocations.borrow_mut().insert(invoc_id);
+        self.r.arenas.alloc_macro_rules_scope(MacroRulesScope::Invocation(invoc_id))
+    }
+
+    fn proc_macro_stub(&self, item: &ast::Item) -> Option<(MacroKind, Ident, Span)> {
+        if attr::contains_name(&item.attrs, sym::proc_macro) {
+            return Some((MacroKind::Bang, item.ident, item.span));
+        } else if attr::contains_name(&item.attrs, sym::proc_macro_attribute) {
+            return Some((MacroKind::Attr, item.ident, item.span));
+        } else if let Some(attr) = attr::find_by_name(&item.attrs, sym::proc_macro_derive) {
+            if let Some(nested_meta) = attr.meta_item_list().and_then(|list| list.get(0).cloned()) {
+                if let Some(ident) = nested_meta.ident() {
+                    return Some((MacroKind::Derive, ident, ident.span));
+                }
+            }
+        }
+        None
+    }
+
+    // Mark the given macro as unused unless its name starts with `_`.
+    // Macro uses will remove items from this set, and the remaining
+    // items will be reported as `unused_macros`.
+    fn insert_unused_macro(&mut self, ident: Ident, def_id: LocalDefId, node_id: NodeId) {
+        if !ident.as_str().starts_with('_') {
+            self.r.unused_macros.insert(def_id, (node_id, ident));
+            for (rule_i, rule_span) in &self.r.macro_map[&def_id.to_def_id()].rule_spans {
+                self.r.unused_macro_rules.insert((def_id, *rule_i), (ident, *rule_span));
+            }
+        }
+    }
+
+    fn define_macro(&mut self, item: &ast::Item) -> MacroRulesScopeRef<'a> {
+        let parent_scope = self.parent_scope;
+        let expansion = parent_scope.expansion;
+        let def_id = self.r.local_def_id(item.id);
+        let (res, ident, span, macro_rules) = match &item.kind {
+            ItemKind::MacroDef(def) => (self.res(def_id), item.ident, item.span, def.macro_rules),
+            ItemKind::Fn(..) => match self.proc_macro_stub(item) {
+                Some((macro_kind, ident, span)) => {
+                    let res = Res::Def(DefKind::Macro(macro_kind), def_id.to_def_id());
+                    let macro_data = MacroData::new(self.r.dummy_ext(macro_kind));
+                    self.r.macro_map.insert(def_id.to_def_id(), macro_data);
+                    self.r.proc_macro_stubs.insert(def_id);
+                    (res, ident, span, false)
+                }
+                None => return parent_scope.macro_rules,
+            },
+            _ => unreachable!(),
+        };
+
+        self.r.local_macro_def_scopes.insert(def_id, parent_scope.module);
+
+        if macro_rules {
+            let ident = ident.normalize_to_macros_2_0();
+            self.r.macro_names.insert(ident);
+            let is_macro_export = attr::contains_name(&item.attrs, sym::macro_export);
+            let vis = if is_macro_export {
+                ty::Visibility::Public
+            } else {
+                ty::Visibility::Restricted(CRATE_DEF_ID)
+            };
+            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(ImportData {
+                    kind: ImportKind::MacroExport,
+                    root_id: item.id,
+                    parent_scope: self.parent_scope,
+                    imported_module: Cell::new(None),
+                    has_attributes: false,
+                    use_span_with_attributes: span,
+                    use_span: span,
+                    root_span: span,
+                    span,
+                    module_path: Vec::new(),
+                    vis: Cell::new(Some(vis)),
+                    used: Cell::new(true),
+                });
+                let import_binding = self.r.import(binding, import);
+                self.r.define(self.r.graph_root, ident, MacroNS, import_binding);
+            } else {
+                self.r.check_reserved_macro_name(ident, res);
+                self.insert_unused_macro(ident, def_id, item.id);
+            }
+            self.r.feed_visibility(def_id, vis);
+            let scope = self.r.arenas.alloc_macro_rules_scope(MacroRulesScope::Binding(
+                self.r.arenas.alloc_macro_rules_binding(MacroRulesBinding {
+                    parent_macro_rules_scope: parent_scope.macro_rules,
+                    binding,
+                    ident,
+                }),
+            ));
+            self.r.macro_rules_scopes.insert(def_id, scope);
+            scope
+        } else {
+            let module = parent_scope.module;
+            let vis = match item.kind {
+                // Visibilities must not be resolved non-speculatively twice
+                // and we already resolved this one as a `fn` item visibility.
+                ItemKind::Fn(..) => {
+                    self.try_resolve_visibility(&item.vis, false).unwrap_or(ty::Visibility::Public)
+                }
+                _ => self.resolve_visibility(&item.vis),
+            };
+            if !vis.is_public() {
+                self.insert_unused_macro(ident, def_id, item.id);
+            }
+            self.r.define(module, ident, MacroNS, (res, vis, span, expansion));
+            self.r.feed_visibility(def_id, vis);
+            self.parent_scope.macro_rules
+        }
+    }
+}
+
+macro_rules! method {
+    ($visit:ident: $ty:ty, $invoc:path, $walk:ident) => {
+        fn $visit(&mut self, node: &'b $ty) {
+            if let $invoc(..) = node.kind {
+                self.visit_invoc(node.id);
+            } else {
+                visit::$walk(self, node);
+            }
+        }
+    };
+}
+
+impl<'a, 'b, 'tcx> Visitor<'b> for BuildReducedGraphVisitor<'a, 'b, 'tcx> {
+    method!(visit_expr: ast::Expr, ast::ExprKind::MacCall, walk_expr);
+    method!(visit_pat: ast::Pat, ast::PatKind::MacCall, walk_pat);
+    method!(visit_ty: ast::Ty, ast::TyKind::MacCall, walk_ty);
+
+    fn visit_item(&mut self, item: &'b Item) {
+        let orig_module_scope = self.parent_scope.module;
+        self.parent_scope.macro_rules = match item.kind {
+            ItemKind::MacroDef(..) => {
+                let macro_rules_scope = self.define_macro(item);
+                visit::walk_item(self, item);
+                macro_rules_scope
+            }
+            ItemKind::MacCall(..) => {
+                let macro_rules_scope = self.visit_invoc_in_module(item.id);
+                visit::walk_item(self, item);
+                macro_rules_scope
+            }
+            _ => {
+                let orig_macro_rules_scope = self.parent_scope.macro_rules;
+                self.build_reduced_graph_for_item(item);
+                visit::walk_item(self, item);
+                match item.kind {
+                    ItemKind::Mod(..) if self.contains_macro_use(&item.attrs) => {
+                        self.parent_scope.macro_rules
+                    }
+                    _ => orig_macro_rules_scope,
+                }
+            }
+        };
+        self.parent_scope.module = orig_module_scope;
+    }
+
+    fn visit_stmt(&mut self, stmt: &'b ast::Stmt) {
+        if let ast::StmtKind::MacCall(..) = stmt.kind {
+            self.parent_scope.macro_rules = self.visit_invoc_in_module(stmt.id);
+        } else {
+            visit::walk_stmt(self, stmt);
+        }
+    }
+
+    fn visit_foreign_item(&mut self, foreign_item: &'b ForeignItem) {
+        if let ForeignItemKind::MacCall(_) = foreign_item.kind {
+            self.visit_invoc_in_module(foreign_item.id);
+            return;
+        }
+
+        self.build_reduced_graph_for_foreign_item(foreign_item);
+        visit::walk_foreign_item(self, foreign_item);
+    }
+
+    fn visit_block(&mut self, block: &'b Block) {
+        let orig_current_module = self.parent_scope.module;
+        let orig_current_macro_rules_scope = self.parent_scope.macro_rules;
+        self.build_reduced_graph_for_block(block);
+        visit::walk_block(self, block);
+        self.parent_scope.module = orig_current_module;
+        self.parent_scope.macro_rules = orig_current_macro_rules_scope;
+    }
+
+    fn visit_assoc_item(&mut self, item: &'b AssocItem, ctxt: AssocCtxt) {
+        if let AssocItemKind::MacCall(_) = item.kind {
+            match ctxt {
+                AssocCtxt::Trait => {
+                    self.visit_invoc_in_module(item.id);
+                }
+                AssocCtxt::Impl => {
+                    self.visit_invoc(item.id);
+                }
+            }
+            return;
+        }
+
+        let vis = self.resolve_visibility(&item.vis);
+        let local_def_id = self.r.local_def_id(item.id);
+        let def_id = local_def_id.to_def_id();
+
+        if !(ctxt == AssocCtxt::Impl
+            && matches!(item.vis.kind, ast::VisibilityKind::Inherited)
+            && self.r.trait_impl_items.contains(&self.r.tcx.local_parent(local_def_id)))
+        {
+            // Trait impl item visibility is inherited from its trait when not specified
+            // explicitly. In that case we cannot determine it here in early resolve,
+            // so we leave a hole in the visibility table to be filled later.
+            self.r.feed_visibility(local_def_id, vis);
+        }
+
+        if ctxt == AssocCtxt::Trait {
+            let ns = match item.kind {
+                AssocItemKind::Const(..)
+                | AssocItemKind::Delegation(..)
+                | AssocItemKind::Fn(..) => ValueNS,
+                AssocItemKind::Type(..) => TypeNS,
+                AssocItemKind::MacCall(_) => bug!(), // handled above
+            };
+
+            let parent = self.parent_scope.module;
+            let expansion = self.parent_scope.expansion;
+            self.r.define(parent, item.ident, ns, (self.res(def_id), vis, item.span, expansion));
+        }
+
+        visit::walk_assoc_item(self, item, ctxt);
+    }
+
+    fn visit_attribute(&mut self, attr: &'b ast::Attribute) {
+        if !attr.is_doc_comment() && attr::is_builtin_attr(attr) {
+            self.r
+                .builtin_attrs
+                .push((attr.get_normal_item().path.segments[0].ident, self.parent_scope));
+        }
+        visit::walk_attribute(self, attr);
+    }
+
+    fn visit_arm(&mut self, arm: &'b ast::Arm) {
+        if arm.is_placeholder {
+            self.visit_invoc(arm.id);
+        } else {
+            visit::walk_arm(self, arm);
+        }
+    }
+
+    fn visit_expr_field(&mut self, f: &'b ast::ExprField) {
+        if f.is_placeholder {
+            self.visit_invoc(f.id);
+        } else {
+            visit::walk_expr_field(self, f);
+        }
+    }
+
+    fn visit_pat_field(&mut self, fp: &'b ast::PatField) {
+        if fp.is_placeholder {
+            self.visit_invoc(fp.id);
+        } else {
+            visit::walk_pat_field(self, fp);
+        }
+    }
+
+    fn visit_generic_param(&mut self, param: &'b ast::GenericParam) {
+        if param.is_placeholder {
+            self.visit_invoc(param.id);
+        } else {
+            visit::walk_generic_param(self, param);
+        }
+    }
+
+    fn visit_param(&mut self, p: &'b ast::Param) {
+        if p.is_placeholder {
+            self.visit_invoc(p.id);
+        } else {
+            visit::walk_param(self, p);
+        }
+    }
+
+    fn visit_field_def(&mut self, sf: &'b ast::FieldDef) {
+        if sf.is_placeholder {
+            self.visit_invoc(sf.id);
+        } else {
+            let vis = self.resolve_visibility(&sf.vis);
+            self.r.feed_visibility(self.r.local_def_id(sf.id), vis);
+            visit::walk_field_def(self, sf);
+        }
+    }
+
+    // Constructs the reduced graph for one variant. Variants exist in the
+    // type and value namespaces.
+    fn visit_variant(&mut self, variant: &'b ast::Variant) {
+        if variant.is_placeholder {
+            self.visit_invoc_in_module(variant.id);
+            return;
+        }
+
+        let parent = self.parent_scope.module;
+        let expn_id = self.parent_scope.expansion;
+        let ident = variant.ident;
+
+        // Define a name in the type namespace.
+        let def_id = self.r.local_def_id(variant.id);
+        let vis = self.resolve_visibility(&variant.vis);
+        self.r.define(parent, ident, TypeNS, (self.res(def_id), vis, variant.span, expn_id));
+        self.r.feed_visibility(def_id, vis);
+
+        // If the variant is marked as non_exhaustive then lower the visibility to within the crate.
+        let ctor_vis =
+            if vis.is_public() && attr::contains_name(&variant.attrs, sym::non_exhaustive) {
+                ty::Visibility::Restricted(CRATE_DEF_ID)
+            } else {
+                vis
+            };
+
+        // Define a constructor name in the value namespace.
+        if let Some(ctor_node_id) = variant.data.ctor_node_id() {
+            let ctor_def_id = self.r.local_def_id(ctor_node_id);
+            let ctor_res = self.res(ctor_def_id);
+            self.r.define(parent, ident, ValueNS, (ctor_res, ctor_vis, variant.span, expn_id));
+            self.r.feed_visibility(ctor_def_id, ctor_vis);
+        }
+
+        // Record field names for error reporting.
+        self.insert_field_def_ids(def_id, &variant.data);
+        self.insert_field_visibilities_local(def_id.to_def_id(), &variant.data);
+
+        visit::walk_variant(self, variant);
+    }
+
+    fn visit_crate(&mut self, krate: &'b ast::Crate) {
+        if krate.is_placeholder {
+            self.visit_invoc_in_module(krate.id);
+        } else {
+            visit::walk_crate(self, krate);
+            self.contains_macro_use(&krate.attrs);
+        }
+    }
+}
diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs
new file mode 100644
index 00000000000..0e43a35ce73
--- /dev/null
+++ b/compiler/rustc_resolve/src/check_unused.rs
@@ -0,0 +1,486 @@
+//
+// Unused import checking
+//
+// Although this is mostly a lint pass, it lives in here because it depends on
+// resolve data structures and because it finalises the privacy information for
+// `use` items.
+//
+// Unused trait imports can't be checked until the method resolution. We save
+// candidates here, and do the actual check in rustc_hir_analysis/check_unused.rs.
+//
+// Checking for unused imports is split into three steps:
+//
+//  - `UnusedImportCheckVisitor` walks the AST to find all the unused imports
+//    inside of `UseTree`s, recording their `NodeId`s and grouping them by
+//    the parent `use` item
+//
+//  - `calc_unused_spans` then walks over all the `use` items marked in the
+//    previous step to collect the spans associated with the `NodeId`s and to
+//    calculate the spans that can be removed by rustfix; This is done in a
+//    separate step to be able to collapse the adjacent spans that rustfix
+//    will remove
+//
+//  - `check_unused` finally emits the diagnostics based on the data generated
+//    in the last step
+
+use crate::imports::ImportKind;
+use crate::module_to_string;
+use crate::Resolver;
+
+use rustc_ast as ast;
+use rustc_ast::visit::{self, Visitor};
+use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
+use rustc_data_structures::unord::UnordSet;
+use rustc_errors::{pluralize, MultiSpan};
+use rustc_hir::def::{DefKind, Res};
+use rustc_session::lint::builtin::{MACRO_USE_EXTERN_CRATE, UNUSED_EXTERN_CRATES, UNUSED_IMPORTS};
+use rustc_session::lint::BuiltinLintDiagnostics;
+use rustc_span::symbol::{kw, Ident};
+use rustc_span::{Span, DUMMY_SP};
+
+struct UnusedImport<'a> {
+    use_tree: &'a ast::UseTree,
+    use_tree_id: ast::NodeId,
+    item_span: Span,
+    unused: UnordSet<ast::NodeId>,
+}
+
+impl<'a> UnusedImport<'a> {
+    fn add(&mut self, id: ast::NodeId) {
+        self.unused.insert(id);
+    }
+}
+
+struct UnusedImportCheckVisitor<'a, 'b, 'tcx> {
+    r: &'a mut Resolver<'b, 'tcx>,
+    /// All the (so far) unused imports, grouped path list
+    unused_imports: FxIndexMap<ast::NodeId, UnusedImport<'a>>,
+    extern_crate_items: Vec<ExternCrateToLint>,
+    base_use_tree: Option<&'a ast::UseTree>,
+    base_id: ast::NodeId,
+    item_span: Span,
+}
+
+struct ExternCrateToLint {
+    id: ast::NodeId,
+    /// Span from the item
+    span: Span,
+    /// Span to use to suggest complete removal.
+    span_with_attributes: Span,
+    /// Span of the visibility, if any.
+    vis_span: Span,
+    /// Whether the item has attrs.
+    has_attrs: bool,
+    /// Name used to refer to the crate.
+    ident: Ident,
+    /// Whether the statement renames the crate `extern crate orig_name as new_name;`.
+    renames: bool,
+}
+
+impl<'a, 'b, 'tcx> UnusedImportCheckVisitor<'a, 'b, 'tcx> {
+    // We have information about whether `use` (import) items are actually
+    // used now. If an import is not used at all, we signal a lint error.
+    fn check_import(&mut self, id: ast::NodeId) {
+        let used = self.r.used_imports.contains(&id);
+        let def_id = self.r.local_def_id(id);
+        if !used {
+            if self.r.maybe_unused_trait_imports.contains(&def_id) {
+                // Check later.
+                return;
+            }
+            self.unused_import(self.base_id).add(id);
+        } else {
+            // This trait import is definitely used, in a way other than
+            // method resolution.
+            self.r.maybe_unused_trait_imports.remove(&def_id);
+            if let Some(i) = self.unused_imports.get_mut(&self.base_id) {
+                i.unused.remove(&id);
+            }
+        }
+    }
+
+    fn unused_import(&mut self, id: ast::NodeId) -> &mut UnusedImport<'a> {
+        let use_tree_id = self.base_id;
+        let use_tree = self.base_use_tree.unwrap();
+        let item_span = self.item_span;
+
+        self.unused_imports.entry(id).or_insert_with(|| UnusedImport {
+            use_tree,
+            use_tree_id,
+            item_span,
+            unused: Default::default(),
+        })
+    }
+
+    fn check_import_as_underscore(&mut self, item: &ast::UseTree, id: ast::NodeId) {
+        match item.kind {
+            ast::UseTreeKind::Simple(Some(ident)) => {
+                if ident.name == kw::Underscore
+                    && !self.r.import_res_map.get(&id).is_some_and(|per_ns| {
+                        per_ns.iter().filter_map(|res| res.as_ref()).any(|res| {
+                            matches!(res, Res::Def(DefKind::Trait | DefKind::TraitAlias, _))
+                        })
+                    })
+                {
+                    self.unused_import(self.base_id).add(id);
+                }
+            }
+            ast::UseTreeKind::Nested(ref items) => self.check_imports_as_underscore(items),
+            _ => {}
+        }
+    }
+
+    fn check_imports_as_underscore(&mut self, items: &[(ast::UseTree, ast::NodeId)]) {
+        for (item, id) in items {
+            self.check_import_as_underscore(item, *id);
+        }
+    }
+}
+
+impl<'a, 'b, 'tcx> Visitor<'a> for UnusedImportCheckVisitor<'a, 'b, 'tcx> {
+    fn visit_item(&mut self, item: &'a ast::Item) {
+        match item.kind {
+            // Ignore is_public import statements because there's no way to be sure
+            // whether they're used or not. Also ignore imports with a dummy span
+            // because this means that they were generated in some fashion by the
+            // compiler and we don't need to consider them.
+            ast::ItemKind::Use(..) if item.span.is_dummy() => return,
+            ast::ItemKind::ExternCrate(orig_name) => {
+                self.extern_crate_items.push(ExternCrateToLint {
+                    id: item.id,
+                    span: item.span,
+                    vis_span: item.vis.span,
+                    span_with_attributes: item.span_with_attributes(),
+                    has_attrs: !item.attrs.is_empty(),
+                    ident: item.ident,
+                    renames: orig_name.is_some(),
+                });
+            }
+            _ => {}
+        }
+
+        self.item_span = item.span_with_attributes();
+        visit::walk_item(self, item);
+    }
+
+    fn visit_use_tree(&mut self, use_tree: &'a ast::UseTree, id: ast::NodeId, nested: bool) {
+        // Use the base UseTree's NodeId as the item id
+        // This allows the grouping of all the lints in the same item
+        if !nested {
+            self.base_id = id;
+            self.base_use_tree = Some(use_tree);
+        }
+
+        if self.r.effective_visibilities.is_exported(self.r.local_def_id(id)) {
+            self.check_import_as_underscore(use_tree, id);
+            return;
+        }
+
+        if let ast::UseTreeKind::Nested(ref items) = use_tree.kind {
+            if items.is_empty() {
+                self.unused_import(self.base_id).add(id);
+            }
+        } else {
+            self.check_import(id);
+        }
+
+        visit::walk_use_tree(self, use_tree, id);
+    }
+}
+
+enum UnusedSpanResult {
+    Used,
+    FlatUnused(Span, Span),
+    NestedFullUnused(Vec<Span>, Span),
+    NestedPartialUnused(Vec<Span>, Vec<Span>),
+}
+
+fn calc_unused_spans(
+    unused_import: &UnusedImport<'_>,
+    use_tree: &ast::UseTree,
+    use_tree_id: ast::NodeId,
+) -> UnusedSpanResult {
+    // The full span is the whole item's span if this current tree is not nested inside another
+    // This tells rustfix to remove the whole item if all the imports are unused
+    let full_span = if unused_import.use_tree.span == use_tree.span {
+        unused_import.item_span
+    } else {
+        use_tree.span
+    };
+    match use_tree.kind {
+        ast::UseTreeKind::Simple(..) | ast::UseTreeKind::Glob => {
+            if unused_import.unused.contains(&use_tree_id) {
+                UnusedSpanResult::FlatUnused(use_tree.span, full_span)
+            } else {
+                UnusedSpanResult::Used
+            }
+        }
+        ast::UseTreeKind::Nested(ref nested) => {
+            if nested.is_empty() {
+                return UnusedSpanResult::FlatUnused(use_tree.span, full_span);
+            }
+
+            let mut unused_spans = Vec::new();
+            let mut to_remove = Vec::new();
+            let mut all_nested_unused = true;
+            let mut previous_unused = false;
+            for (pos, (use_tree, use_tree_id)) in nested.iter().enumerate() {
+                let remove = match calc_unused_spans(unused_import, use_tree, *use_tree_id) {
+                    UnusedSpanResult::Used => {
+                        all_nested_unused = false;
+                        None
+                    }
+                    UnusedSpanResult::FlatUnused(span, remove) => {
+                        unused_spans.push(span);
+                        Some(remove)
+                    }
+                    UnusedSpanResult::NestedFullUnused(mut spans, remove) => {
+                        unused_spans.append(&mut spans);
+                        Some(remove)
+                    }
+                    UnusedSpanResult::NestedPartialUnused(mut spans, mut to_remove_extra) => {
+                        all_nested_unused = false;
+                        unused_spans.append(&mut spans);
+                        to_remove.append(&mut to_remove_extra);
+                        None
+                    }
+                };
+                if let Some(remove) = remove {
+                    let remove_span = if nested.len() == 1 {
+                        remove
+                    } else if pos == nested.len() - 1 || !all_nested_unused {
+                        // Delete everything from the end of the last import, to delete the
+                        // previous comma
+                        nested[pos - 1].0.span.shrink_to_hi().to(use_tree.span)
+                    } else {
+                        // Delete everything until the next import, to delete the trailing commas
+                        use_tree.span.to(nested[pos + 1].0.span.shrink_to_lo())
+                    };
+
+                    // Try to collapse adjacent spans into a single one. This prevents all cases of
+                    // overlapping removals, which are not supported by rustfix
+                    if previous_unused && !to_remove.is_empty() {
+                        let previous = to_remove.pop().unwrap();
+                        to_remove.push(previous.to(remove_span));
+                    } else {
+                        to_remove.push(remove_span);
+                    }
+                }
+                previous_unused = remove.is_some();
+            }
+            if unused_spans.is_empty() {
+                UnusedSpanResult::Used
+            } else if all_nested_unused {
+                UnusedSpanResult::NestedFullUnused(unused_spans, full_span)
+            } else {
+                UnusedSpanResult::NestedPartialUnused(unused_spans, to_remove)
+            }
+        }
+    }
+}
+
+impl Resolver<'_, '_> {
+    pub(crate) fn check_unused(&mut self, krate: &ast::Crate) {
+        let tcx = self.tcx;
+        let mut maybe_unused_extern_crates = FxHashMap::default();
+
+        for import in self.potentially_unused_imports.iter() {
+            match import.kind {
+                _ if import.used.get()
+                    || import.expect_vis().is_public()
+                    || import.span.is_dummy() =>
+                {
+                    if let ImportKind::MacroUse = import.kind {
+                        if !import.span.is_dummy() {
+                            self.lint_buffer.buffer_lint(
+                                MACRO_USE_EXTERN_CRATE,
+                                import.root_id,
+                                import.span,
+                                "deprecated `#[macro_use]` attribute used to \
+                                import macros should be replaced at use sites \
+                                with a `use` item to import the macro \
+                                instead",
+                            );
+                        }
+                    }
+                }
+                ImportKind::ExternCrate { id, .. } => {
+                    let def_id = self.local_def_id(id);
+                    if self.extern_crate_map.get(&def_id).map_or(true, |&cnum| {
+                        !tcx.is_compiler_builtins(cnum)
+                            && !tcx.is_panic_runtime(cnum)
+                            && !tcx.has_global_allocator(cnum)
+                            && !tcx.has_panic_handler(cnum)
+                    }) {
+                        maybe_unused_extern_crates.insert(id, import.span);
+                    }
+                }
+                ImportKind::MacroUse => {
+                    let msg = "unused `#[macro_use]` import";
+                    self.lint_buffer.buffer_lint(UNUSED_IMPORTS, import.root_id, import.span, msg);
+                }
+                _ => {}
+            }
+        }
+
+        let mut visitor = UnusedImportCheckVisitor {
+            r: self,
+            unused_imports: Default::default(),
+            extern_crate_items: Default::default(),
+            base_use_tree: None,
+            base_id: ast::DUMMY_NODE_ID,
+            item_span: DUMMY_SP,
+        };
+        visit::walk_crate(&mut visitor, krate);
+
+        for unused in visitor.unused_imports.values() {
+            let mut fixes = Vec::new();
+            let spans = match calc_unused_spans(unused, unused.use_tree, unused.use_tree_id) {
+                UnusedSpanResult::Used => continue,
+                UnusedSpanResult::FlatUnused(span, remove) => {
+                    fixes.push((remove, String::new()));
+                    vec![span]
+                }
+                UnusedSpanResult::NestedFullUnused(spans, remove) => {
+                    fixes.push((remove, String::new()));
+                    spans
+                }
+                UnusedSpanResult::NestedPartialUnused(spans, remove) => {
+                    for fix in &remove {
+                        fixes.push((*fix, String::new()));
+                    }
+                    spans
+                }
+            };
+
+            let ms = MultiSpan::from_spans(spans);
+
+            let mut span_snippets = ms
+                .primary_spans()
+                .iter()
+                .filter_map(|span| tcx.sess.source_map().span_to_snippet(*span).ok())
+                .map(|s| format!("`{s}`"))
+                .collect::<Vec<String>>();
+            span_snippets.sort();
+
+            let msg = format!(
+                "unused import{}{}",
+                pluralize!(ms.primary_spans().len()),
+                if !span_snippets.is_empty() {
+                    format!(": {}", span_snippets.join(", "))
+                } else {
+                    String::new()
+                }
+            );
+
+            let fix_msg = if fixes.len() == 1 && fixes[0].0 == unused.item_span {
+                "remove the whole `use` item"
+            } else if ms.primary_spans().len() > 1 {
+                "remove the unused imports"
+            } else {
+                "remove the unused import"
+            };
+
+            // If we are in the `--test` mode, suppress a help that adds the `#[cfg(test)]`
+            // attribute; however, if not, suggest adding the attribute. There is no way to
+            // retrieve attributes here because we do not have a `TyCtxt` yet.
+            let test_module_span = if tcx.sess.is_test_crate() {
+                None
+            } else {
+                let parent_module = visitor.r.get_nearest_non_block_module(
+                    visitor.r.local_def_id(unused.use_tree_id).to_def_id(),
+                );
+                match module_to_string(parent_module) {
+                    Some(module)
+                        if module == "test"
+                            || module == "tests"
+                            || module.starts_with("test_")
+                            || module.starts_with("tests_")
+                            || module.ends_with("_test")
+                            || module.ends_with("_tests") =>
+                    {
+                        Some(parent_module.span)
+                    }
+                    _ => None,
+                }
+            };
+
+            visitor.r.lint_buffer.buffer_lint_with_diagnostic(
+                UNUSED_IMPORTS,
+                unused.use_tree_id,
+                ms,
+                msg,
+                BuiltinLintDiagnostics::UnusedImports(fix_msg.into(), fixes, test_module_span),
+            );
+        }
+
+        for extern_crate in visitor.extern_crate_items {
+            let warn_if_unused = !extern_crate.ident.name.as_str().starts_with('_');
+
+            // If the crate is fully unused, we suggest removing it altogether.
+            // We do this in any edition.
+            if warn_if_unused {
+                if let Some(&span) = maybe_unused_extern_crates.get(&extern_crate.id) {
+                    visitor.r.lint_buffer.buffer_lint_with_diagnostic(
+                        UNUSED_EXTERN_CRATES,
+                        extern_crate.id,
+                        span,
+                        "unused extern crate",
+                        BuiltinLintDiagnostics::UnusedExternCrate {
+                            removal_span: extern_crate.span_with_attributes,
+                        },
+                    );
+                    continue;
+                }
+            }
+
+            // If we are not in Rust 2018 edition, then we don't make any further
+            // suggestions.
+            if !tcx.sess.at_least_rust_2018() {
+                continue;
+            }
+
+            // If the extern crate has any attributes, they may have funky
+            // semantics we can't faithfully represent using `use` (most
+            // notably `#[macro_use]`). Ignore it.
+            if extern_crate.has_attrs {
+                continue;
+            }
+
+            // If the extern crate is renamed, then we cannot suggest replacing it with a use as this
+            // would not insert the new name into the prelude, where other imports in the crate may be
+            // expecting it.
+            if extern_crate.renames {
+                continue;
+            }
+
+            // If the extern crate isn't in the extern prelude,
+            // there is no way it can be written as a `use`.
+            if !visitor
+                .r
+                .extern_prelude
+                .get(&extern_crate.ident)
+                .is_some_and(|entry| !entry.introduced_by_item)
+            {
+                continue;
+            }
+
+            let vis_span = extern_crate
+                .vis_span
+                .find_ancestor_inside(extern_crate.span)
+                .unwrap_or(extern_crate.vis_span);
+            let ident_span = extern_crate
+                .ident
+                .span
+                .find_ancestor_inside(extern_crate.span)
+                .unwrap_or(extern_crate.ident.span);
+            visitor.r.lint_buffer.buffer_lint_with_diagnostic(
+                UNUSED_EXTERN_CRATES,
+                extern_crate.id,
+                extern_crate.span,
+                "`extern crate` is not idiomatic in the new edition",
+                BuiltinLintDiagnostics::ExternCrateNotIdiomatic { vis_span, ident_span },
+            );
+        }
+    }
+}
diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs
new file mode 100644
index 00000000000..b77102c085c
--- /dev/null
+++ b/compiler/rustc_resolve/src/def_collector.rs
@@ -0,0 +1,375 @@
+use crate::{ImplTraitContext, Resolver};
+use rustc_ast::visit::{self, FnKind};
+use rustc_ast::*;
+use rustc_expand::expand::AstFragment;
+use rustc_hir::def::{CtorKind, CtorOf, DefKind};
+use rustc_hir::def_id::LocalDefId;
+use rustc_span::hygiene::LocalExpnId;
+use rustc_span::symbol::{kw, sym, Symbol};
+use rustc_span::Span;
+
+pub(crate) fn collect_definitions(
+    resolver: &mut Resolver<'_, '_>,
+    fragment: &AstFragment,
+    expansion: LocalExpnId,
+) {
+    let (parent_def, impl_trait_context) = resolver.invocation_parents[&expansion];
+    fragment.visit_with(&mut DefCollector { resolver, parent_def, expansion, impl_trait_context });
+}
+
+/// Creates `DefId`s for nodes in the AST.
+struct DefCollector<'a, 'b, 'tcx> {
+    resolver: &'a mut Resolver<'b, 'tcx>,
+    parent_def: LocalDefId,
+    impl_trait_context: ImplTraitContext,
+    expansion: LocalExpnId,
+}
+
+impl<'a, 'b, 'tcx> DefCollector<'a, 'b, 'tcx> {
+    fn create_def(
+        &mut self,
+        node_id: NodeId,
+        name: Symbol,
+        def_kind: DefKind,
+        span: Span,
+    ) -> LocalDefId {
+        let parent_def = self.parent_def;
+        debug!(
+            "create_def(node_id={:?}, def_kind={:?}, parent_def={:?})",
+            node_id, def_kind, parent_def
+        );
+        self.resolver.create_def(
+            parent_def,
+            node_id,
+            name,
+            def_kind,
+            self.expansion.to_expn_id(),
+            span.with_parent(None),
+        )
+    }
+
+    fn with_parent<F: FnOnce(&mut Self)>(&mut self, parent_def: LocalDefId, f: F) {
+        let orig_parent_def = std::mem::replace(&mut self.parent_def, parent_def);
+        f(self);
+        self.parent_def = orig_parent_def;
+    }
+
+    fn with_impl_trait<F: FnOnce(&mut Self)>(
+        &mut self,
+        impl_trait_context: ImplTraitContext,
+        f: F,
+    ) {
+        let orig_itc = std::mem::replace(&mut self.impl_trait_context, impl_trait_context);
+        f(self);
+        self.impl_trait_context = orig_itc;
+    }
+
+    fn collect_field(&mut self, field: &'a FieldDef, index: Option<usize>) {
+        let index = |this: &Self| {
+            index.unwrap_or_else(|| {
+                let node_id = NodeId::placeholder_from_expn_id(this.expansion);
+                this.resolver.placeholder_field_indices[&node_id]
+            })
+        };
+
+        if field.is_placeholder {
+            let old_index = self.resolver.placeholder_field_indices.insert(field.id, index(self));
+            assert!(old_index.is_none(), "placeholder field index is reset for a node ID");
+            self.visit_macro_invoc(field.id);
+        } else {
+            let name = field.ident.map_or_else(|| sym::integer(index(self)), |ident| ident.name);
+            let def = self.create_def(field.id, name, DefKind::Field, field.span);
+            self.with_parent(def, |this| visit::walk_field_def(this, field));
+        }
+    }
+
+    fn visit_macro_invoc(&mut self, id: NodeId) {
+        let id = id.placeholder_to_expn_id();
+        let old_parent =
+            self.resolver.invocation_parents.insert(id, (self.parent_def, self.impl_trait_context));
+        assert!(old_parent.is_none(), "parent `LocalDefId` is reset for an invocation");
+    }
+}
+
+impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> {
+    fn visit_item(&mut self, i: &'a Item) {
+        debug!("visit_item: {:?}", i);
+
+        // Pick the def data. This need not be unique, but the more
+        // information we encapsulate into, the better
+        let mut opt_macro_data = None;
+        let def_kind = match &i.kind {
+            ItemKind::Impl(i) => DefKind::Impl { of_trait: i.of_trait.is_some() },
+            ItemKind::ForeignMod(..) => DefKind::ForeignMod,
+            ItemKind::Mod(..) => DefKind::Mod,
+            ItemKind::Trait(..) => DefKind::Trait,
+            ItemKind::TraitAlias(..) => DefKind::TraitAlias,
+            ItemKind::Enum(..) => DefKind::Enum,
+            ItemKind::Struct(..) => DefKind::Struct,
+            ItemKind::Union(..) => DefKind::Union,
+            ItemKind::ExternCrate(..) => DefKind::ExternCrate,
+            ItemKind::TyAlias(..) => DefKind::TyAlias,
+            ItemKind::Static(s) => DefKind::Static(s.mutability),
+            ItemKind::Const(..) => DefKind::Const,
+            ItemKind::Fn(..) | ItemKind::Delegation(..) => DefKind::Fn,
+            ItemKind::MacroDef(..) => {
+                let macro_data = self.resolver.compile_macro(i, self.resolver.tcx.sess.edition());
+                let macro_kind = macro_data.ext.macro_kind();
+                opt_macro_data = Some(macro_data);
+                DefKind::Macro(macro_kind)
+            }
+            ItemKind::MacCall(..) => {
+                visit::walk_item(self, i);
+                return self.visit_macro_invoc(i.id);
+            }
+            ItemKind::GlobalAsm(..) => DefKind::GlobalAsm,
+            ItemKind::Use(..) => {
+                return visit::walk_item(self, i);
+            }
+        };
+        let def_id = self.create_def(i.id, i.ident.name, def_kind, i.span);
+
+        if let Some(macro_data) = opt_macro_data {
+            self.resolver.macro_map.insert(def_id.to_def_id(), macro_data);
+        }
+
+        self.with_parent(def_id, |this| {
+            this.with_impl_trait(ImplTraitContext::Existential, |this| {
+                match i.kind {
+                    ItemKind::Struct(ref struct_def, _) | ItemKind::Union(ref struct_def, _) => {
+                        // If this is a unit or tuple-like struct, register the constructor.
+                        if let Some((ctor_kind, ctor_node_id)) = CtorKind::from_ast(struct_def) {
+                            this.create_def(
+                                ctor_node_id,
+                                kw::Empty,
+                                DefKind::Ctor(CtorOf::Struct, ctor_kind),
+                                i.span,
+                            );
+                        }
+                    }
+                    _ => {}
+                }
+                visit::walk_item(this, i);
+            })
+        });
+    }
+
+    fn visit_fn(&mut self, fn_kind: FnKind<'a>, span: Span, _: NodeId) {
+        if let FnKind::Fn(_, _, sig, _, generics, body) = fn_kind {
+            match sig.header.coroutine_kind {
+                Some(coroutine_kind) => {
+                    self.visit_generics(generics);
+
+                    // For async functions, we need to create their inner defs inside of a
+                    // closure to match their desugared representation. Besides that,
+                    // we must mirror everything that `visit::walk_fn` below does.
+                    self.visit_fn_header(&sig.header);
+                    for param in &sig.decl.inputs {
+                        self.visit_param(param);
+                    }
+                    self.visit_fn_ret_ty(&sig.decl.output);
+                    // If this async fn has no body (i.e. it's an async fn signature in a trait)
+                    // then the closure_def will never be used, and we should avoid generating a
+                    // def-id for it.
+                    if let Some(body) = body {
+                        let closure_def = self.create_def(
+                            coroutine_kind.closure_id(),
+                            kw::Empty,
+                            DefKind::Closure,
+                            span,
+                        );
+                        self.with_parent(closure_def, |this| this.visit_block(body));
+                    }
+                    return;
+                }
+                None => {}
+            }
+        }
+
+        visit::walk_fn(self, fn_kind);
+    }
+
+    fn visit_use_tree(&mut self, use_tree: &'a UseTree, id: NodeId, _nested: bool) {
+        self.create_def(id, kw::Empty, DefKind::Use, use_tree.span);
+        visit::walk_use_tree(self, use_tree, id);
+    }
+
+    fn visit_foreign_item(&mut self, fi: &'a ForeignItem) {
+        let def_kind = match fi.kind {
+            ForeignItemKind::Static(_, mt, _) => DefKind::Static(mt),
+            ForeignItemKind::Fn(_) => DefKind::Fn,
+            ForeignItemKind::TyAlias(_) => DefKind::ForeignTy,
+            ForeignItemKind::MacCall(_) => return self.visit_macro_invoc(fi.id),
+        };
+
+        let def = self.create_def(fi.id, fi.ident.name, def_kind, fi.span);
+
+        self.with_parent(def, |this| visit::walk_foreign_item(this, fi));
+    }
+
+    fn visit_variant(&mut self, v: &'a Variant) {
+        if v.is_placeholder {
+            return self.visit_macro_invoc(v.id);
+        }
+        let def = self.create_def(v.id, v.ident.name, DefKind::Variant, v.span);
+        self.with_parent(def, |this| {
+            if let Some((ctor_kind, ctor_node_id)) = CtorKind::from_ast(&v.data) {
+                this.create_def(
+                    ctor_node_id,
+                    kw::Empty,
+                    DefKind::Ctor(CtorOf::Variant, ctor_kind),
+                    v.span,
+                );
+            }
+            visit::walk_variant(this, v)
+        });
+    }
+
+    fn visit_variant_data(&mut self, data: &'a VariantData) {
+        // The assumption here is that non-`cfg` macro expansion cannot change field indices.
+        // It currently holds because only inert attributes are accepted on fields,
+        // and every such attribute expands into a single field after it's resolved.
+        for (index, field) in data.fields().iter().enumerate() {
+            self.collect_field(field, Some(index));
+        }
+    }
+
+    fn visit_generic_param(&mut self, param: &'a GenericParam) {
+        if param.is_placeholder {
+            self.visit_macro_invoc(param.id);
+            return;
+        }
+        let def_kind = match param.kind {
+            GenericParamKind::Lifetime { .. } => DefKind::LifetimeParam,
+            GenericParamKind::Type { .. } => DefKind::TyParam,
+            GenericParamKind::Const { .. } => DefKind::ConstParam,
+        };
+        self.create_def(param.id, param.ident.name, def_kind, param.ident.span);
+
+        // impl-Trait can happen inside generic parameters, like
+        // ```
+        // fn foo<U: Iterator<Item = impl Clone>>() {}
+        // ```
+        //
+        // In that case, the impl-trait is lowered as an additional generic parameter.
+        self.with_impl_trait(ImplTraitContext::Universal, |this| {
+            visit::walk_generic_param(this, param)
+        });
+    }
+
+    fn visit_assoc_item(&mut self, i: &'a AssocItem, ctxt: visit::AssocCtxt) {
+        let def_kind = match &i.kind {
+            AssocItemKind::Fn(..) | AssocItemKind::Delegation(..) => DefKind::AssocFn,
+            AssocItemKind::Const(..) => DefKind::AssocConst,
+            AssocItemKind::Type(..) => DefKind::AssocTy,
+            AssocItemKind::MacCall(..) => return self.visit_macro_invoc(i.id),
+        };
+
+        let def = self.create_def(i.id, i.ident.name, def_kind, i.span);
+        self.with_parent(def, |this| visit::walk_assoc_item(this, i, ctxt));
+    }
+
+    fn visit_pat(&mut self, pat: &'a Pat) {
+        match pat.kind {
+            PatKind::MacCall(..) => self.visit_macro_invoc(pat.id),
+            _ => visit::walk_pat(self, pat),
+        }
+    }
+
+    fn visit_anon_const(&mut self, constant: &'a AnonConst) {
+        let def = self.create_def(constant.id, kw::Empty, DefKind::AnonConst, constant.value.span);
+        self.with_parent(def, |this| visit::walk_anon_const(this, constant));
+    }
+
+    fn visit_expr(&mut self, expr: &'a Expr) {
+        let parent_def = match expr.kind {
+            ExprKind::MacCall(..) => return self.visit_macro_invoc(expr.id),
+            ExprKind::Closure(ref closure) => {
+                // Async closures desugar to closures inside of closures, so
+                // we must create two defs.
+                let closure_def = self.create_def(expr.id, kw::Empty, DefKind::Closure, expr.span);
+                match closure.coroutine_kind {
+                    Some(coroutine_kind) => self.create_def(
+                        coroutine_kind.closure_id(),
+                        kw::Empty,
+                        DefKind::Closure,
+                        expr.span,
+                    ),
+                    None => closure_def,
+                }
+            }
+            ExprKind::Gen(_, _, _) => {
+                self.create_def(expr.id, kw::Empty, DefKind::Closure, expr.span)
+            }
+            ExprKind::ConstBlock(ref constant) => {
+                let def = self.create_def(
+                    constant.id,
+                    kw::Empty,
+                    DefKind::InlineConst,
+                    constant.value.span,
+                );
+                self.with_parent(def, |this| visit::walk_anon_const(this, constant));
+                return;
+            }
+            _ => self.parent_def,
+        };
+
+        self.with_parent(parent_def, |this| visit::walk_expr(this, expr));
+    }
+
+    fn visit_ty(&mut self, ty: &'a Ty) {
+        match ty.kind {
+            TyKind::MacCall(..) => self.visit_macro_invoc(ty.id),
+            _ => visit::walk_ty(self, ty),
+        }
+    }
+
+    fn visit_stmt(&mut self, stmt: &'a Stmt) {
+        match stmt.kind {
+            StmtKind::MacCall(..) => self.visit_macro_invoc(stmt.id),
+            _ => visit::walk_stmt(self, stmt),
+        }
+    }
+
+    fn visit_arm(&mut self, arm: &'a Arm) {
+        if arm.is_placeholder { self.visit_macro_invoc(arm.id) } else { visit::walk_arm(self, arm) }
+    }
+
+    fn visit_expr_field(&mut self, f: &'a ExprField) {
+        if f.is_placeholder {
+            self.visit_macro_invoc(f.id)
+        } else {
+            visit::walk_expr_field(self, f)
+        }
+    }
+
+    fn visit_pat_field(&mut self, fp: &'a PatField) {
+        if fp.is_placeholder {
+            self.visit_macro_invoc(fp.id)
+        } else {
+            visit::walk_pat_field(self, fp)
+        }
+    }
+
+    fn visit_param(&mut self, p: &'a Param) {
+        if p.is_placeholder {
+            self.visit_macro_invoc(p.id)
+        } else {
+            self.with_impl_trait(ImplTraitContext::Universal, |this| visit::walk_param(this, p))
+        }
+    }
+
+    // This method is called only when we are visiting an individual field
+    // after expanding an attribute on it.
+    fn visit_field_def(&mut self, field: &'a FieldDef) {
+        self.collect_field(field, None);
+    }
+
+    fn visit_crate(&mut self, krate: &'a Crate) {
+        if krate.is_placeholder {
+            self.visit_macro_invoc(krate.id)
+        } else {
+            visit::walk_crate(self, krate)
+        }
+    }
+}
diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs
new file mode 100644
index 00000000000..b5700661385
--- /dev/null
+++ b/compiler/rustc_resolve/src/diagnostics.rs
@@ -0,0 +1,2976 @@
+use rustc_ast::expand::StrippedCfgItem;
+use rustc_ast::ptr::P;
+use rustc_ast::visit::{self, Visitor};
+use rustc_ast::{self as ast, Crate, ItemKind, ModKind, NodeId, Path, CRATE_NODE_ID};
+use rustc_ast::{MetaItemKind, NestedMetaItem};
+use rustc_ast_pretty::pprust;
+use rustc_data_structures::fx::FxHashSet;
+use rustc_errors::{
+    pluralize, report_ambiguity_error, struct_span_code_err, Applicability, DiagCtxt, Diagnostic,
+    DiagnosticBuilder, ErrorGuaranteed, MultiSpan, SuggestionStyle,
+};
+use rustc_feature::BUILTIN_ATTRIBUTES;
+use rustc_hir::def::Namespace::{self, *};
+use rustc_hir::def::{self, CtorKind, CtorOf, DefKind, NonMacroAttrKind, PerNS};
+use rustc_hir::def_id::{DefId, CRATE_DEF_ID};
+use rustc_hir::PrimTy;
+use rustc_middle::bug;
+use rustc_middle::ty::TyCtxt;
+use rustc_session::lint::builtin::ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE;
+use rustc_session::lint::builtin::AMBIGUOUS_GLOB_IMPORTS;
+use rustc_session::lint::builtin::MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS;
+use rustc_session::lint::{AmbiguityErrorDiag, BuiltinLintDiagnostics};
+use rustc_session::Session;
+use rustc_span::edit_distance::find_best_match_for_name;
+use rustc_span::edition::Edition;
+use rustc_span::hygiene::MacroKind;
+use rustc_span::source_map::SourceMap;
+use rustc_span::symbol::{kw, sym, Ident, Symbol};
+use rustc_span::{BytePos, Span, SyntaxContext};
+use thin_vec::{thin_vec, ThinVec};
+
+use crate::errors::{AddedMacroUse, ChangeImportBinding, ChangeImportBindingSuggestion};
+use crate::errors::{ConsiderAddingADerive, ExplicitUnsafeTraits, MaybeMissingMacroRulesName};
+use crate::imports::{Import, ImportKind};
+use crate::late::{PatternSource, Rib};
+use crate::path_names_to_string;
+use crate::{errors as errs, BindingKey};
+use crate::{AmbiguityError, AmbiguityErrorMisc, AmbiguityKind, BindingError, Finalize};
+use crate::{HasGenericParams, MacroRulesScope, Module, ModuleKind, ModuleOrUniformRoot};
+use crate::{LexicalScopeBinding, NameBinding, NameBindingKind, PrivacyError, VisResolutionError};
+use crate::{ParentScope, PathResult, ResolutionError, Resolver, Scope, ScopeSet};
+use crate::{Segment, UseError};
+
+#[cfg(test)]
+mod tests;
+
+type Res = def::Res<ast::NodeId>;
+
+/// A vector of spans and replacements, a message and applicability.
+pub(crate) type Suggestion = (Vec<(Span, String)>, String, Applicability);
+
+/// Potential candidate for an undeclared or out-of-scope label - contains the ident of a
+/// similarly named label and whether or not it is reachable.
+pub(crate) type LabelSuggestion = (Ident, bool);
+
+#[derive(Debug)]
+pub(crate) enum SuggestionTarget {
+    /// The target has a similar name as the name used by the programmer (probably a typo)
+    SimilarlyNamed,
+    /// The target is the only valid item that can be used in the corresponding context
+    SingleItem,
+}
+
+#[derive(Debug)]
+pub(crate) struct TypoSuggestion {
+    pub candidate: Symbol,
+    /// The source location where the name is defined; None if the name is not defined
+    /// in source e.g. primitives
+    pub span: Option<Span>,
+    pub res: Res,
+    pub target: SuggestionTarget,
+}
+
+impl TypoSuggestion {
+    pub(crate) fn typo_from_ident(ident: Ident, res: Res) -> TypoSuggestion {
+        Self {
+            candidate: ident.name,
+            span: Some(ident.span),
+            res,
+            target: SuggestionTarget::SimilarlyNamed,
+        }
+    }
+    pub(crate) fn typo_from_name(candidate: Symbol, res: Res) -> TypoSuggestion {
+        Self { candidate, span: None, res, target: SuggestionTarget::SimilarlyNamed }
+    }
+    pub(crate) fn single_item_from_ident(ident: Ident, res: Res) -> TypoSuggestion {
+        Self {
+            candidate: ident.name,
+            span: Some(ident.span),
+            res,
+            target: SuggestionTarget::SingleItem,
+        }
+    }
+}
+
+/// A free importable items suggested in case of resolution failure.
+#[derive(Debug, Clone)]
+pub(crate) struct ImportSuggestion {
+    pub did: Option<DefId>,
+    pub descr: &'static str,
+    pub path: Path,
+    pub accessible: bool,
+    // false if the path traverses a foreign `#[doc(hidden)]` item.
+    pub doc_visible: bool,
+    pub via_import: bool,
+    /// An extra note that should be issued if this item is suggested
+    pub note: Option<String>,
+}
+
+/// Adjust the impl span so that just the `impl` keyword is taken by removing
+/// everything after `<` (`"impl<T> Iterator for A<T> {}" -> "impl"`) and
+/// everything after the first whitespace (`"impl Iterator for A" -> "impl"`).
+///
+/// *Attention*: the method used is very fragile since it essentially duplicates the work of the
+/// parser. If you need to use this function or something similar, please consider updating the
+/// `source_map` functions and this function to something more robust.
+fn reduce_impl_span_to_impl_keyword(sm: &SourceMap, impl_span: Span) -> Span {
+    let impl_span = sm.span_until_char(impl_span, '<');
+    sm.span_until_whitespace(impl_span)
+}
+
+impl<'a, 'tcx> Resolver<'a, 'tcx> {
+    pub(crate) fn dcx(&self) -> &'tcx DiagCtxt {
+        self.tcx.dcx()
+    }
+
+    pub(crate) fn report_errors(&mut self, krate: &Crate) {
+        self.report_with_use_injections(krate);
+
+        for &(span_use, span_def) in &self.macro_expanded_macro_export_errors {
+            let msg = "macro-expanded `macro_export` macros from the current crate \
+                       cannot be referred to by absolute paths";
+            self.lint_buffer.buffer_lint_with_diagnostic(
+                MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS,
+                CRATE_NODE_ID,
+                span_use,
+                msg,
+                BuiltinLintDiagnostics::MacroExpandedMacroExportsAccessedByAbsolutePaths(span_def),
+            );
+        }
+
+        for ambiguity_error in &self.ambiguity_errors {
+            let diag = self.ambiguity_diagnostics(ambiguity_error);
+            if ambiguity_error.warning {
+                let NameBindingKind::Import { import, .. } = ambiguity_error.b1.0.kind else {
+                    unreachable!()
+                };
+                self.lint_buffer.buffer_lint_with_diagnostic(
+                    AMBIGUOUS_GLOB_IMPORTS,
+                    import.root_id,
+                    ambiguity_error.ident.span,
+                    diag.msg.to_string(),
+                    BuiltinLintDiagnostics::AmbiguousGlobImports { diag },
+                );
+            } else {
+                let mut err = struct_span_code_err!(self.dcx(), diag.span, E0659, "{}", &diag.msg);
+                report_ambiguity_error(&mut err, diag);
+                err.emit();
+            }
+        }
+
+        let mut reported_spans = FxHashSet::default();
+        for error in std::mem::take(&mut self.privacy_errors) {
+            if reported_spans.insert(error.dedup_span) {
+                self.report_privacy_error(&error);
+            }
+        }
+    }
+
+    fn report_with_use_injections(&mut self, krate: &Crate) {
+        for UseError { mut err, candidates, def_id, instead, suggestion, path, is_call } in
+            self.use_injections.drain(..)
+        {
+            let (span, found_use) = if let Some(def_id) = def_id.as_local() {
+                UsePlacementFinder::check(krate, self.def_id_to_node_id[def_id])
+            } else {
+                (None, FoundUse::No)
+            };
+
+            if !candidates.is_empty() {
+                show_candidates(
+                    self.tcx,
+                    &mut err,
+                    span,
+                    &candidates,
+                    if instead { Instead::Yes } else { Instead::No },
+                    found_use,
+                    DiagnosticMode::Normal,
+                    path,
+                    "",
+                );
+                err.emit();
+            } else if let Some((span, msg, sugg, appl)) = suggestion {
+                err.span_suggestion_verbose(span, msg, sugg, appl);
+                err.emit();
+            } else if let [segment] = path.as_slice()
+                && is_call
+            {
+                err.stash(segment.ident.span, rustc_errors::StashKey::CallIntoMethod);
+            } else {
+                err.emit();
+            }
+        }
+    }
+
+    pub(crate) fn report_conflict(
+        &mut self,
+        parent: Module<'_>,
+        ident: Ident,
+        ns: Namespace,
+        new_binding: NameBinding<'a>,
+        old_binding: NameBinding<'a>,
+    ) {
+        // Error on the second of two conflicting names
+        if old_binding.span.lo() > new_binding.span.lo() {
+            return self.report_conflict(parent, ident, ns, old_binding, new_binding);
+        }
+
+        let container = match parent.kind {
+            // Avoid using TyCtxt::def_kind_descr in the resolver, because it
+            // indirectly *calls* the resolver, and would cause a query cycle.
+            ModuleKind::Def(kind, _, _) => kind.descr(parent.def_id()),
+            ModuleKind::Block => "block",
+        };
+
+        let old_noun = match old_binding.is_import_user_facing() {
+            true => "import",
+            false => "definition",
+        };
+
+        let new_participle = match new_binding.is_import_user_facing() {
+            true => "imported",
+            false => "defined",
+        };
+
+        let (name, span) =
+            (ident.name, self.tcx.sess.source_map().guess_head_span(new_binding.span));
+
+        if let Some(s) = self.name_already_seen.get(&name) {
+            if s == &span {
+                return;
+            }
+        }
+
+        let old_kind = match (ns, old_binding.module()) {
+            (ValueNS, _) => "value",
+            (MacroNS, _) => "macro",
+            (TypeNS, _) if old_binding.is_extern_crate() => "extern crate",
+            (TypeNS, Some(module)) if module.is_normal() => "module",
+            (TypeNS, Some(module)) if module.is_trait() => "trait",
+            (TypeNS, _) => "type",
+        };
+
+        let msg = format!("the name `{name}` is defined multiple times");
+
+        let mut err = match (old_binding.is_extern_crate(), new_binding.is_extern_crate()) {
+            (true, true) => struct_span_code_err!(self.dcx(), span, E0259, "{}", msg),
+            (true, _) | (_, true) => match new_binding.is_import() && old_binding.is_import() {
+                true => struct_span_code_err!(self.dcx(), span, E0254, "{}", msg),
+                false => struct_span_code_err!(self.dcx(), span, E0260, "{}", msg),
+            },
+            _ => match (old_binding.is_import_user_facing(), new_binding.is_import_user_facing()) {
+                (false, false) => struct_span_code_err!(self.dcx(), span, E0428, "{}", msg),
+                (true, true) => struct_span_code_err!(self.dcx(), span, E0252, "{}", msg),
+                _ => struct_span_code_err!(self.dcx(), span, E0255, "{}", msg),
+            },
+        };
+
+        err.note(format!(
+            "`{}` must be defined only once in the {} namespace of this {}",
+            name,
+            ns.descr(),
+            container
+        ));
+
+        err.span_label(span, format!("`{name}` re{new_participle} here"));
+        if !old_binding.span.is_dummy() && old_binding.span != span {
+            err.span_label(
+                self.tcx.sess.source_map().guess_head_span(old_binding.span),
+                format!("previous {old_noun} of the {old_kind} `{name}` here"),
+            );
+        }
+
+        // See https://github.com/rust-lang/rust/issues/32354
+        use NameBindingKind::Import;
+        let can_suggest = |binding: NameBinding<'_>, import: self::Import<'_>| {
+            !binding.span.is_dummy()
+                && !matches!(import.kind, ImportKind::MacroUse | ImportKind::MacroExport)
+        };
+        let import = match (&new_binding.kind, &old_binding.kind) {
+            // If there are two imports where one or both have attributes then prefer removing the
+            // import without attributes.
+            (Import { import: new, .. }, Import { import: old, .. })
+                if {
+                    (new.has_attributes || old.has_attributes)
+                        && can_suggest(old_binding, *old)
+                        && can_suggest(new_binding, *new)
+                } =>
+            {
+                if old.has_attributes {
+                    Some((*new, new_binding.span, true))
+                } else {
+                    Some((*old, old_binding.span, true))
+                }
+            }
+            // Otherwise prioritize the new binding.
+            (Import { import, .. }, other) if can_suggest(new_binding, *import) => {
+                Some((*import, new_binding.span, other.is_import()))
+            }
+            (other, Import { import, .. }) if can_suggest(old_binding, *import) => {
+                Some((*import, old_binding.span, other.is_import()))
+            }
+            _ => None,
+        };
+
+        // Check if the target of the use for both bindings is the same.
+        let duplicate = new_binding.res().opt_def_id() == old_binding.res().opt_def_id();
+        let has_dummy_span = new_binding.span.is_dummy() || old_binding.span.is_dummy();
+        let from_item =
+            self.extern_prelude.get(&ident).map_or(true, |entry| entry.introduced_by_item);
+        // Only suggest removing an import if both bindings are to the same def, if both spans
+        // aren't dummy spans. Further, if both bindings are imports, then the ident must have
+        // been introduced by an item.
+        let should_remove_import = duplicate
+            && !has_dummy_span
+            && ((new_binding.is_extern_crate() || old_binding.is_extern_crate()) || from_item);
+
+        match import {
+            Some((import, span, true)) if should_remove_import && import.is_nested() => {
+                self.add_suggestion_for_duplicate_nested_use(&mut err, import, span)
+            }
+            Some((import, _, true)) if should_remove_import && !import.is_glob() => {
+                // Simple case - remove the entire import. Due to the above match arm, this can
+                // only be a single use so just remove it entirely.
+                err.tool_only_span_suggestion(
+                    import.use_span_with_attributes,
+                    "remove unnecessary import",
+                    "",
+                    Applicability::MaybeIncorrect,
+                );
+            }
+            Some((import, span, _)) => {
+                self.add_suggestion_for_rename_of_use(&mut err, name, import, span)
+            }
+            _ => {}
+        }
+
+        err.emit();
+        self.name_already_seen.insert(name, span);
+    }
+
+    /// This function adds a suggestion to change the binding name of a new import that conflicts
+    /// with an existing import.
+    ///
+    /// ```text,ignore (diagnostic)
+    /// help: you can use `as` to change the binding name of the import
+    ///    |
+    /// LL | use foo::bar as other_bar;
+    ///    |     ^^^^^^^^^^^^^^^^^^^^^
+    /// ```
+    fn add_suggestion_for_rename_of_use(
+        &self,
+        err: &mut Diagnostic,
+        name: Symbol,
+        import: Import<'_>,
+        binding_span: Span,
+    ) {
+        let suggested_name = if name.as_str().chars().next().unwrap().is_uppercase() {
+            format!("Other{name}")
+        } else {
+            format!("other_{name}")
+        };
+
+        let mut suggestion = None;
+        match import.kind {
+            ImportKind::Single { type_ns_only: true, .. } => {
+                suggestion = Some(format!("self as {suggested_name}"))
+            }
+            ImportKind::Single { source, .. } => {
+                if let Some(pos) =
+                    source.span.hi().0.checked_sub(binding_span.lo().0).map(|pos| pos as usize)
+                {
+                    if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(binding_span) {
+                        if pos <= snippet.len() {
+                            suggestion = Some(format!(
+                                "{} as {}{}",
+                                &snippet[..pos],
+                                suggested_name,
+                                if snippet.ends_with(';') { ";" } else { "" }
+                            ))
+                        }
+                    }
+                }
+            }
+            ImportKind::ExternCrate { source, target, .. } => {
+                suggestion = Some(format!(
+                    "extern crate {} as {};",
+                    source.unwrap_or(target.name),
+                    suggested_name,
+                ))
+            }
+            _ => unreachable!(),
+        }
+
+        if let Some(suggestion) = suggestion {
+            err.subdiagnostic(ChangeImportBindingSuggestion { span: binding_span, suggestion });
+        } else {
+            err.subdiagnostic(ChangeImportBinding { span: binding_span });
+        }
+    }
+
+    /// This function adds a suggestion to remove an unnecessary binding from an import that is
+    /// nested. In the following example, this function will be invoked to remove the `a` binding
+    /// in the second use statement:
+    ///
+    /// ```ignore (diagnostic)
+    /// use issue_52891::a;
+    /// use issue_52891::{d, a, e};
+    /// ```
+    ///
+    /// The following suggestion will be added:
+    ///
+    /// ```ignore (diagnostic)
+    /// use issue_52891::{d, a, e};
+    ///                      ^-- help: remove unnecessary import
+    /// ```
+    ///
+    /// If the nested use contains only one import then the suggestion will remove the entire
+    /// line.
+    ///
+    /// It is expected that the provided import is nested - this isn't checked by the
+    /// function. If this invariant is not upheld, this function's behaviour will be unexpected
+    /// as characters expected by span manipulations won't be present.
+    fn add_suggestion_for_duplicate_nested_use(
+        &self,
+        err: &mut Diagnostic,
+        import: Import<'_>,
+        binding_span: Span,
+    ) {
+        assert!(import.is_nested());
+        let message = "remove unnecessary import";
+
+        // Two examples will be used to illustrate the span manipulations we're doing:
+        //
+        // - Given `use issue_52891::{d, a, e};` where `a` is a duplicate then `binding_span` is
+        //   `a` and `import.use_span` is `issue_52891::{d, a, e};`.
+        // - Given `use issue_52891::{d, e, a};` where `a` is a duplicate then `binding_span` is
+        //   `a` and `import.use_span` is `issue_52891::{d, e, a};`.
+
+        let (found_closing_brace, span) =
+            find_span_of_binding_until_next_binding(self.tcx.sess, binding_span, import.use_span);
+
+        // If there was a closing brace then identify the span to remove any trailing commas from
+        // previous imports.
+        if found_closing_brace {
+            if let Some(span) = extend_span_to_previous_binding(self.tcx.sess, span) {
+                err.tool_only_span_suggestion(span, message, "", Applicability::MaybeIncorrect);
+            } else {
+                // Remove the entire line if we cannot extend the span back, this indicates an
+                // `issue_52891::{self}` case.
+                err.span_suggestion(
+                    import.use_span_with_attributes,
+                    message,
+                    "",
+                    Applicability::MaybeIncorrect,
+                );
+            }
+
+            return;
+        }
+
+        err.span_suggestion(span, message, "", Applicability::MachineApplicable);
+    }
+
+    pub(crate) fn lint_if_path_starts_with_module(
+        &mut self,
+        finalize: Option<Finalize>,
+        path: &[Segment],
+        second_binding: Option<NameBinding<'_>>,
+    ) {
+        let Some(Finalize { node_id, root_span, .. }) = finalize else {
+            return;
+        };
+
+        let first_name = match path.get(0) {
+            // In the 2018 edition this lint is a hard error, so nothing to do
+            Some(seg) if seg.ident.span.is_rust_2015() && self.tcx.sess.is_rust_2015() => {
+                seg.ident.name
+            }
+            _ => return,
+        };
+
+        // We're only interested in `use` paths which should start with
+        // `{{root}}` currently.
+        if first_name != kw::PathRoot {
+            return;
+        }
+
+        match path.get(1) {
+            // If this import looks like `crate::...` it's already good
+            Some(Segment { ident, .. }) if ident.name == kw::Crate => return,
+            // Otherwise go below to see if it's an extern crate
+            Some(_) => {}
+            // If the path has length one (and it's `PathRoot` most likely)
+            // then we don't know whether we're gonna be importing a crate or an
+            // item in our crate. Defer this lint to elsewhere
+            None => return,
+        }
+
+        // If the first element of our path was actually resolved to an
+        // `ExternCrate` (also used for `crate::...`) then no need to issue a
+        // warning, this looks all good!
+        if let Some(binding) = second_binding {
+            if let NameBindingKind::Import { import, .. } = binding.kind {
+                // Careful: we still want to rewrite paths from renamed extern crates.
+                if let ImportKind::ExternCrate { source: None, .. } = import.kind {
+                    return;
+                }
+            }
+        }
+
+        let diag = BuiltinLintDiagnostics::AbsPathWithModule(root_span);
+        self.lint_buffer.buffer_lint_with_diagnostic(
+            ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE,
+            node_id,
+            root_span,
+            "absolute paths must start with `self`, `super`, \
+             `crate`, or an external crate name in the 2018 edition",
+            diag,
+        );
+    }
+
+    pub(crate) fn add_module_candidates(
+        &mut self,
+        module: Module<'a>,
+        names: &mut Vec<TypoSuggestion>,
+        filter_fn: &impl Fn(Res) -> bool,
+        ctxt: Option<SyntaxContext>,
+    ) {
+        for (key, resolution) in self.resolutions(module).borrow().iter() {
+            if let Some(binding) = resolution.borrow().binding {
+                let res = binding.res();
+                if filter_fn(res) && ctxt.map_or(true, |ctxt| ctxt == key.ident.span.ctxt()) {
+                    names.push(TypoSuggestion::typo_from_ident(key.ident, res));
+                }
+            }
+        }
+    }
+
+    /// Combines an error with provided span and emits it.
+    ///
+    /// This takes the error provided, combines it with the span and any additional spans inside the
+    /// error and emits it.
+    pub(crate) fn report_error(&mut self, span: Span, resolution_error: ResolutionError<'a>) {
+        self.into_struct_error(span, resolution_error).emit();
+    }
+
+    pub(crate) fn into_struct_error(
+        &mut self,
+        span: Span,
+        resolution_error: ResolutionError<'a>,
+    ) -> DiagnosticBuilder<'_> {
+        match resolution_error {
+            ResolutionError::GenericParamsFromOuterItem(outer_res, has_generic_params) => {
+                use errs::GenericParamsFromOuterItemLabel as Label;
+                let mut err = errs::GenericParamsFromOuterItem {
+                    span,
+                    label: None,
+                    refer_to_type_directly: None,
+                    sugg: None,
+                };
+
+                let sm = self.tcx.sess.source_map();
+                let def_id = match outer_res {
+                    Res::SelfTyParam { .. } => {
+                        err.label = Some(Label::SelfTyParam(span));
+                        return self.dcx().create_err(err);
+                    }
+                    Res::SelfTyAlias { alias_to: def_id, .. } => {
+                        err.label = Some(Label::SelfTyAlias(reduce_impl_span_to_impl_keyword(
+                            sm,
+                            self.def_span(def_id),
+                        )));
+                        err.refer_to_type_directly = Some(span);
+                        return self.dcx().create_err(err);
+                    }
+                    Res::Def(DefKind::TyParam, def_id) => {
+                        err.label = Some(Label::TyParam(self.def_span(def_id)));
+                        def_id
+                    }
+                    Res::Def(DefKind::ConstParam, def_id) => {
+                        err.label = Some(Label::ConstParam(self.def_span(def_id)));
+                        def_id
+                    }
+                    _ => {
+                        bug!(
+                            "GenericParamsFromOuterItem should only be used with \
+                            Res::SelfTyParam, Res::SelfTyAlias, DefKind::TyParam or \
+                            DefKind::ConstParam"
+                        );
+                    }
+                };
+
+                if let HasGenericParams::Yes(span) = has_generic_params {
+                    let name = self.tcx.item_name(def_id);
+                    let (span, snippet) = if span.is_empty() {
+                        let snippet = format!("<{name}>");
+                        (span, snippet)
+                    } else {
+                        let span = sm.span_through_char(span, '<').shrink_to_hi();
+                        let snippet = format!("{name}, ");
+                        (span, snippet)
+                    };
+                    err.sugg = Some(errs::GenericParamsFromOuterItemSugg { span, snippet });
+                }
+
+                self.dcx().create_err(err)
+            }
+            ResolutionError::NameAlreadyUsedInParameterList(name, first_use_span) => self
+                .dcx()
+                .create_err(errs::NameAlreadyUsedInParameterList { span, first_use_span, name }),
+            ResolutionError::MethodNotMemberOfTrait(method, trait_, candidate) => {
+                self.dcx().create_err(errs::MethodNotMemberOfTrait {
+                    span,
+                    method,
+                    trait_,
+                    sub: candidate.map(|c| errs::AssociatedFnWithSimilarNameExists {
+                        span: method.span,
+                        candidate: c,
+                    }),
+                })
+            }
+            ResolutionError::TypeNotMemberOfTrait(type_, trait_, candidate) => {
+                self.dcx().create_err(errs::TypeNotMemberOfTrait {
+                    span,
+                    type_,
+                    trait_,
+                    sub: candidate.map(|c| errs::AssociatedTypeWithSimilarNameExists {
+                        span: type_.span,
+                        candidate: c,
+                    }),
+                })
+            }
+            ResolutionError::ConstNotMemberOfTrait(const_, trait_, candidate) => {
+                self.dcx().create_err(errs::ConstNotMemberOfTrait {
+                    span,
+                    const_,
+                    trait_,
+                    sub: candidate.map(|c| errs::AssociatedConstWithSimilarNameExists {
+                        span: const_.span,
+                        candidate: c,
+                    }),
+                })
+            }
+            ResolutionError::VariableNotBoundInPattern(binding_error, parent_scope) => {
+                let BindingError { name, target, origin, could_be_path } = binding_error;
+
+                let target_sp = target.iter().copied().collect::<Vec<_>>();
+                let origin_sp = origin.iter().copied().collect::<Vec<_>>();
+
+                let msp = MultiSpan::from_spans(target_sp.clone());
+                let mut err = struct_span_code_err!(
+                    self.dcx(),
+                    msp,
+                    E0408,
+                    "variable `{}` is not bound in all patterns",
+                    name,
+                );
+                for sp in target_sp {
+                    err.span_label(sp, format!("pattern doesn't bind `{name}`"));
+                }
+                for sp in origin_sp {
+                    err.span_label(sp, "variable not in all patterns");
+                }
+                if could_be_path {
+                    let import_suggestions = self.lookup_import_candidates(
+                        Ident::with_dummy_span(name),
+                        Namespace::ValueNS,
+                        &parent_scope,
+                        &|res: Res| {
+                            matches!(
+                                res,
+                                Res::Def(
+                                    DefKind::Ctor(CtorOf::Variant, CtorKind::Const)
+                                        | DefKind::Ctor(CtorOf::Struct, CtorKind::Const)
+                                        | DefKind::Const
+                                        | DefKind::AssocConst,
+                                    _,
+                                )
+                            )
+                        },
+                    );
+
+                    if import_suggestions.is_empty() {
+                        let help_msg = format!(
+                            "if you meant to match on a variant or a `const` item, consider \
+                             making the path in the pattern qualified: `path::to::ModOrType::{name}`",
+                        );
+                        err.span_help(span, help_msg);
+                    }
+                    show_candidates(
+                        self.tcx,
+                        &mut err,
+                        Some(span),
+                        &import_suggestions,
+                        Instead::No,
+                        FoundUse::Yes,
+                        DiagnosticMode::Pattern,
+                        vec![],
+                        "",
+                    );
+                }
+                err
+            }
+            ResolutionError::VariableBoundWithDifferentMode(variable_name, first_binding_span) => {
+                self.dcx().create_err(errs::VariableBoundWithDifferentMode {
+                    span,
+                    first_binding_span,
+                    variable_name,
+                })
+            }
+            ResolutionError::IdentifierBoundMoreThanOnceInParameterList(identifier) => self
+                .dcx()
+                .create_err(errs::IdentifierBoundMoreThanOnceInParameterList { span, identifier }),
+            ResolutionError::IdentifierBoundMoreThanOnceInSamePattern(identifier) => self
+                .dcx()
+                .create_err(errs::IdentifierBoundMoreThanOnceInSamePattern { span, identifier }),
+            ResolutionError::UndeclaredLabel { name, suggestion } => {
+                let ((sub_reachable, sub_reachable_suggestion), sub_unreachable) = match suggestion
+                {
+                    // A reachable label with a similar name exists.
+                    Some((ident, true)) => (
+                        (
+                            Some(errs::LabelWithSimilarNameReachable(ident.span)),
+                            Some(errs::TryUsingSimilarlyNamedLabel {
+                                span,
+                                ident_name: ident.name,
+                            }),
+                        ),
+                        None,
+                    ),
+                    // An unreachable label with a similar name exists.
+                    Some((ident, false)) => (
+                        (None, None),
+                        Some(errs::UnreachableLabelWithSimilarNameExists {
+                            ident_span: ident.span,
+                        }),
+                    ),
+                    // No similarly-named labels exist.
+                    None => ((None, None), None),
+                };
+                self.dcx().create_err(errs::UndeclaredLabel {
+                    span,
+                    name,
+                    sub_reachable,
+                    sub_reachable_suggestion,
+                    sub_unreachable,
+                })
+            }
+            ResolutionError::SelfImportsOnlyAllowedWithin { root, span_with_rename } => {
+                // None of the suggestions below would help with a case like `use self`.
+                let (suggestion, mpart_suggestion) = if root {
+                    (None, None)
+                } else {
+                    // use foo::bar::self        -> foo::bar
+                    // use foo::bar::self as abc -> foo::bar as abc
+                    let suggestion = errs::SelfImportsOnlyAllowedWithinSuggestion { span };
+
+                    // use foo::bar::self        -> foo::bar::{self}
+                    // use foo::bar::self as abc -> foo::bar::{self as abc}
+                    let mpart_suggestion = errs::SelfImportsOnlyAllowedWithinMultipartSuggestion {
+                        multipart_start: span_with_rename.shrink_to_lo(),
+                        multipart_end: span_with_rename.shrink_to_hi(),
+                    };
+                    (Some(suggestion), Some(mpart_suggestion))
+                };
+                self.dcx().create_err(errs::SelfImportsOnlyAllowedWithin {
+                    span,
+                    suggestion,
+                    mpart_suggestion,
+                })
+            }
+            ResolutionError::SelfImportCanOnlyAppearOnceInTheList => {
+                self.dcx().create_err(errs::SelfImportCanOnlyAppearOnceInTheList { span })
+            }
+            ResolutionError::SelfImportOnlyInImportListWithNonEmptyPrefix => {
+                self.dcx().create_err(errs::SelfImportOnlyInImportListWithNonEmptyPrefix { span })
+            }
+            ResolutionError::FailedToResolve { segment, label, suggestion, module } => {
+                let mut err =
+                    struct_span_code_err!(self.dcx(), span, E0433, "failed to resolve: {}", &label);
+                err.span_label(span, label);
+
+                if let Some((suggestions, msg, applicability)) = suggestion {
+                    if suggestions.is_empty() {
+                        err.help(msg);
+                        return err;
+                    }
+                    err.multipart_suggestion(msg, suggestions, applicability);
+                }
+
+                if let Some(ModuleOrUniformRoot::Module(module)) = module
+                    && let Some(module) = module.opt_def_id()
+                    && let Some(segment) = segment
+                {
+                    self.find_cfg_stripped(&mut err, &segment, module);
+                }
+
+                err
+            }
+            ResolutionError::CannotCaptureDynamicEnvironmentInFnItem => {
+                self.dcx().create_err(errs::CannotCaptureDynamicEnvironmentInFnItem { span })
+            }
+            ResolutionError::AttemptToUseNonConstantValueInConstant(ident, suggestion, current) => {
+                // let foo =...
+                //     ^^^ given this Span
+                // ------- get this Span to have an applicable suggestion
+
+                // edit:
+                // only do this if the const and usage of the non-constant value are on the same line
+                // the further the two are apart, the higher the chance of the suggestion being wrong
+
+                let sp = self
+                    .tcx
+                    .sess
+                    .source_map()
+                    .span_extend_to_prev_str(ident.span, current, true, false);
+
+                let ((with, with_label), without) = match sp {
+                    Some(sp) if !self.tcx.sess.source_map().is_multiline(sp) => {
+                        let sp = sp.with_lo(BytePos(sp.lo().0 - (current.len() as u32)));
+                        (
+                        (Some(errs::AttemptToUseNonConstantValueInConstantWithSuggestion {
+                                span: sp,
+                                ident,
+                                suggestion,
+                                current,
+                            }), Some(errs::AttemptToUseNonConstantValueInConstantLabelWithSuggestion {span})),
+                            None,
+                        )
+                    }
+                    _ => (
+                        (None, None),
+                        Some(errs::AttemptToUseNonConstantValueInConstantWithoutSuggestion {
+                            ident_span: ident.span,
+                            suggestion,
+                        }),
+                    ),
+                };
+
+                self.dcx().create_err(errs::AttemptToUseNonConstantValueInConstant {
+                    span,
+                    with,
+                    with_label,
+                    without,
+                })
+            }
+            ResolutionError::BindingShadowsSomethingUnacceptable {
+                shadowing_binding,
+                name,
+                participle,
+                article,
+                shadowed_binding,
+                shadowed_binding_span,
+            } => self.dcx().create_err(errs::BindingShadowsSomethingUnacceptable {
+                span,
+                shadowing_binding,
+                shadowed_binding,
+                article,
+                sub_suggestion: match (shadowing_binding, shadowed_binding) {
+                    (
+                        PatternSource::Match,
+                        Res::Def(DefKind::Ctor(CtorOf::Variant | CtorOf::Struct, CtorKind::Fn), _),
+                    ) => Some(errs::BindingShadowsSomethingUnacceptableSuggestion { span, name }),
+                    _ => None,
+                },
+                shadowed_binding_span,
+                participle,
+                name,
+            }),
+            ResolutionError::ForwardDeclaredGenericParam => {
+                self.dcx().create_err(errs::ForwardDeclaredGenericParam { span })
+            }
+            ResolutionError::ParamInTyOfConstParam { name, param_kind: is_type } => self
+                .dcx()
+                .create_err(errs::ParamInTyOfConstParam { span, name, param_kind: is_type }),
+            ResolutionError::ParamInNonTrivialAnonConst { name, param_kind: is_type } => {
+                self.dcx().create_err(errs::ParamInNonTrivialAnonConst {
+                    span,
+                    name,
+                    param_kind: is_type,
+                    help: self
+                        .tcx
+                        .sess
+                        .is_nightly_build()
+                        .then_some(errs::ParamInNonTrivialAnonConstHelp),
+                })
+            }
+            ResolutionError::ParamInEnumDiscriminant { name, param_kind: is_type } => self
+                .dcx()
+                .create_err(errs::ParamInEnumDiscriminant { span, name, param_kind: is_type }),
+            ResolutionError::SelfInGenericParamDefault => {
+                self.dcx().create_err(errs::SelfInGenericParamDefault { span })
+            }
+            ResolutionError::UnreachableLabel { name, definition_span, suggestion } => {
+                let ((sub_suggestion_label, sub_suggestion), sub_unreachable_label) =
+                    match suggestion {
+                        // A reachable label with a similar name exists.
+                        Some((ident, true)) => (
+                            (
+                                Some(errs::UnreachableLabelSubLabel { ident_span: ident.span }),
+                                Some(errs::UnreachableLabelSubSuggestion {
+                                    span,
+                                    // intentionally taking 'ident.name' instead of 'ident' itself, as this
+                                    // could be used in suggestion context
+                                    ident_name: ident.name,
+                                }),
+                            ),
+                            None,
+                        ),
+                        // An unreachable label with a similar name exists.
+                        Some((ident, false)) => (
+                            (None, None),
+                            Some(errs::UnreachableLabelSubLabelUnreachable {
+                                ident_span: ident.span,
+                            }),
+                        ),
+                        // No similarly-named labels exist.
+                        None => ((None, None), None),
+                    };
+                self.dcx().create_err(errs::UnreachableLabel {
+                    span,
+                    name,
+                    definition_span,
+                    sub_suggestion,
+                    sub_suggestion_label,
+                    sub_unreachable_label,
+                })
+            }
+            ResolutionError::TraitImplMismatch {
+                name,
+                kind,
+                code,
+                trait_item_span,
+                trait_path,
+            } => {
+                self.dcx().struct_span_err(
+                    span,
+                    format!(
+                        "item `{name}` is an associated {kind}, which doesn't match its trait `{trait_path}`",
+                    ),
+                )
+                .with_code(code)
+                .with_span_label(span, "does not match trait")
+                .with_span_label(trait_item_span, "item in trait")
+            }
+            ResolutionError::TraitImplDuplicate { name, trait_item_span, old_span } => self
+                .dcx()
+                .create_err(errs::TraitImplDuplicate { span, name, trait_item_span, old_span }),
+            ResolutionError::InvalidAsmSym => self.dcx().create_err(errs::InvalidAsmSym { span }),
+            ResolutionError::LowercaseSelf => self.dcx().create_err(errs::LowercaseSelf { span }),
+        }
+    }
+
+    pub(crate) fn report_vis_error(
+        &mut self,
+        vis_resolution_error: VisResolutionError<'_>,
+    ) -> ErrorGuaranteed {
+        match vis_resolution_error {
+            VisResolutionError::Relative2018(span, path) => {
+                self.dcx().create_err(errs::Relative2018 {
+                    span,
+                    path_span: path.span,
+                    // intentionally converting to String, as the text would also be used as
+                    // in suggestion context
+                    path_str: pprust::path_to_string(path),
+                })
+            }
+            VisResolutionError::AncestorOnly(span) => {
+                self.dcx().create_err(errs::AncestorOnly(span))
+            }
+            VisResolutionError::FailedToResolve(span, label, suggestion) => self.into_struct_error(
+                span,
+                ResolutionError::FailedToResolve { segment: None, label, suggestion, module: None },
+            ),
+            VisResolutionError::ExpectedFound(span, path_str, res) => {
+                self.dcx().create_err(errs::ExpectedFound { span, res, path_str })
+            }
+            VisResolutionError::Indeterminate(span) => {
+                self.dcx().create_err(errs::Indeterminate(span))
+            }
+            VisResolutionError::ModuleOnly(span) => self.dcx().create_err(errs::ModuleOnly(span)),
+        }
+        .emit()
+    }
+
+    /// Lookup typo candidate in scope for a macro or import.
+    fn early_lookup_typo_candidate(
+        &mut self,
+        scope_set: ScopeSet<'a>,
+        parent_scope: &ParentScope<'a>,
+        ident: Ident,
+        filter_fn: &impl Fn(Res) -> bool,
+    ) -> Option<TypoSuggestion> {
+        let mut suggestions = Vec::new();
+        let ctxt = ident.span.ctxt();
+        self.visit_scopes(scope_set, parent_scope, ctxt, |this, scope, use_prelude, _| {
+            match scope {
+                Scope::DeriveHelpers(expn_id) => {
+                    let res = Res::NonMacroAttr(NonMacroAttrKind::DeriveHelper);
+                    if filter_fn(res) {
+                        suggestions.extend(
+                            this.helper_attrs
+                                .get(&expn_id)
+                                .into_iter()
+                                .flatten()
+                                .map(|(ident, _)| TypoSuggestion::typo_from_ident(*ident, res)),
+                        );
+                    }
+                }
+                Scope::DeriveHelpersCompat => {
+                    let res = Res::NonMacroAttr(NonMacroAttrKind::DeriveHelperCompat);
+                    if filter_fn(res) {
+                        for derive in parent_scope.derives {
+                            let parent_scope = &ParentScope { derives: &[], ..*parent_scope };
+                            if let Ok((Some(ext), _)) = this.resolve_macro_path(
+                                derive,
+                                Some(MacroKind::Derive),
+                                parent_scope,
+                                false,
+                                false,
+                            ) {
+                                suggestions.extend(
+                                    ext.helper_attrs
+                                        .iter()
+                                        .map(|name| TypoSuggestion::typo_from_name(*name, res)),
+                                );
+                            }
+                        }
+                    }
+                }
+                Scope::MacroRules(macro_rules_scope) => {
+                    if let MacroRulesScope::Binding(macro_rules_binding) = macro_rules_scope.get() {
+                        let res = macro_rules_binding.binding.res();
+                        if filter_fn(res) {
+                            suggestions.push(TypoSuggestion::typo_from_ident(
+                                macro_rules_binding.ident,
+                                res,
+                            ))
+                        }
+                    }
+                }
+                Scope::CrateRoot => {
+                    let root_ident = Ident::new(kw::PathRoot, ident.span);
+                    let root_module = this.resolve_crate_root(root_ident);
+                    this.add_module_candidates(root_module, &mut suggestions, filter_fn, None);
+                }
+                Scope::Module(module, _) => {
+                    this.add_module_candidates(module, &mut suggestions, filter_fn, None);
+                }
+                Scope::MacroUsePrelude => {
+                    suggestions.extend(this.macro_use_prelude.iter().filter_map(
+                        |(name, binding)| {
+                            let res = binding.res();
+                            filter_fn(res).then_some(TypoSuggestion::typo_from_name(*name, res))
+                        },
+                    ));
+                }
+                Scope::BuiltinAttrs => {
+                    let res = Res::NonMacroAttr(NonMacroAttrKind::Builtin(kw::Empty));
+                    if filter_fn(res) {
+                        suggestions.extend(
+                            BUILTIN_ATTRIBUTES
+                                .iter()
+                                .map(|attr| TypoSuggestion::typo_from_name(attr.name, res)),
+                        );
+                    }
+                }
+                Scope::ExternPrelude => {
+                    suggestions.extend(this.extern_prelude.iter().filter_map(|(ident, _)| {
+                        let res = Res::Def(DefKind::Mod, CRATE_DEF_ID.to_def_id());
+                        filter_fn(res).then_some(TypoSuggestion::typo_from_ident(*ident, res))
+                    }));
+                }
+                Scope::ToolPrelude => {
+                    let res = Res::NonMacroAttr(NonMacroAttrKind::Tool);
+                    suggestions.extend(
+                        this.registered_tools
+                            .iter()
+                            .map(|ident| TypoSuggestion::typo_from_ident(*ident, res)),
+                    );
+                }
+                Scope::StdLibPrelude => {
+                    if let Some(prelude) = this.prelude {
+                        let mut tmp_suggestions = Vec::new();
+                        this.add_module_candidates(prelude, &mut tmp_suggestions, filter_fn, None);
+                        suggestions.extend(
+                            tmp_suggestions
+                                .into_iter()
+                                .filter(|s| use_prelude || this.is_builtin_macro(s.res)),
+                        );
+                    }
+                }
+                Scope::BuiltinTypes => {
+                    suggestions.extend(PrimTy::ALL.iter().filter_map(|prim_ty| {
+                        let res = Res::PrimTy(*prim_ty);
+                        filter_fn(res)
+                            .then_some(TypoSuggestion::typo_from_name(prim_ty.name(), res))
+                    }))
+                }
+            }
+
+            None::<()>
+        });
+
+        // Make sure error reporting is deterministic.
+        suggestions.sort_by(|a, b| a.candidate.as_str().partial_cmp(b.candidate.as_str()).unwrap());
+
+        match find_best_match_for_name(
+            &suggestions.iter().map(|suggestion| suggestion.candidate).collect::<Vec<Symbol>>(),
+            ident.name,
+            None,
+        ) {
+            Some(found) if found != ident.name => {
+                suggestions.into_iter().find(|suggestion| suggestion.candidate == found)
+            }
+            _ => None,
+        }
+    }
+
+    fn lookup_import_candidates_from_module<FilterFn>(
+        &mut self,
+        lookup_ident: Ident,
+        namespace: Namespace,
+        parent_scope: &ParentScope<'a>,
+        start_module: Module<'a>,
+        crate_path: ThinVec<ast::PathSegment>,
+        filter_fn: FilterFn,
+    ) -> Vec<ImportSuggestion>
+    where
+        FilterFn: Fn(Res) -> bool,
+    {
+        let mut candidates = Vec::new();
+        let mut seen_modules = FxHashSet::default();
+        let start_did = start_module.def_id();
+        let mut worklist = vec![(
+            start_module,
+            ThinVec::<ast::PathSegment>::new(),
+            true,
+            start_did.is_local() || !self.tcx.is_doc_hidden(start_did),
+        )];
+        let mut worklist_via_import = vec![];
+
+        while let Some((in_module, path_segments, accessible, doc_visible)) = match worklist.pop() {
+            None => worklist_via_import.pop(),
+            Some(x) => Some(x),
+        } {
+            let in_module_is_extern = !in_module.def_id().is_local();
+            in_module.for_each_child(self, |this, ident, ns, name_binding| {
+                // avoid non-importable candidates
+                if !name_binding.is_importable() {
+                    return;
+                }
+
+                if ident.name == kw::Underscore {
+                    return;
+                }
+
+                let child_accessible =
+                    accessible && this.is_accessible_from(name_binding.vis, parent_scope.module);
+
+                // do not venture inside inaccessible items of other crates
+                if in_module_is_extern && !child_accessible {
+                    return;
+                }
+
+                let via_import = name_binding.is_import() && !name_binding.is_extern_crate();
+
+                // There is an assumption elsewhere that paths of variants are in the enum's
+                // declaration and not imported. With this assumption, the variant component is
+                // chopped and the rest of the path is assumed to be the enum's own path. For
+                // errors where a variant is used as the type instead of the enum, this causes
+                // funny looking invalid suggestions, i.e `foo` instead of `foo::MyEnum`.
+                if via_import && name_binding.is_possibly_imported_variant() {
+                    return;
+                }
+
+                // #90113: Do not count an inaccessible reexported item as a candidate.
+                if let NameBindingKind::Import { binding, .. } = name_binding.kind {
+                    if this.is_accessible_from(binding.vis, parent_scope.module)
+                        && !this.is_accessible_from(name_binding.vis, parent_scope.module)
+                    {
+                        return;
+                    }
+                }
+
+                let res = name_binding.res();
+                let did = match res {
+                    Res::Def(DefKind::Ctor(..), did) => this.tcx.opt_parent(did),
+                    _ => res.opt_def_id(),
+                };
+                let child_doc_visible = doc_visible
+                    && (did.map_or(true, |did| did.is_local() || !this.tcx.is_doc_hidden(did)));
+
+                // collect results based on the filter function
+                // avoid suggesting anything from the same module in which we are resolving
+                // avoid suggesting anything with a hygienic name
+                if ident.name == lookup_ident.name
+                    && ns == namespace
+                    && in_module != parent_scope.module
+                    && !ident.span.normalize_to_macros_2_0().from_expansion()
+                {
+                    if filter_fn(res) {
+                        // create the path
+                        let mut segms = if lookup_ident.span.at_least_rust_2018() {
+                            // crate-local absolute paths start with `crate::` in edition 2018
+                            // FIXME: may also be stabilized for Rust 2015 (Issues #45477, #44660)
+                            crate_path.clone()
+                        } else {
+                            ThinVec::new()
+                        };
+                        segms.append(&mut path_segments.clone());
+
+                        segms.push(ast::PathSegment::from_ident(ident));
+                        let path = Path { span: name_binding.span, segments: segms, tokens: None };
+
+                        if child_accessible {
+                            // Remove invisible match if exists
+                            if let Some(idx) = candidates
+                                .iter()
+                                .position(|v: &ImportSuggestion| v.did == did && !v.accessible)
+                            {
+                                candidates.remove(idx);
+                            }
+                        }
+
+                        if candidates.iter().all(|v: &ImportSuggestion| v.did != did) {
+                            // See if we're recommending TryFrom, TryInto, or FromIterator and add
+                            // a note about editions
+                            let note = if let Some(did) = did {
+                                let requires_note = !did.is_local()
+                                    && this.tcx.get_attrs(did, sym::rustc_diagnostic_item).any(
+                                        |attr| {
+                                            [sym::TryInto, sym::TryFrom, sym::FromIterator]
+                                                .map(|x| Some(x))
+                                                .contains(&attr.value_str())
+                                        },
+                                    );
+
+                                requires_note.then(|| {
+                                    format!(
+                                        "'{}' is included in the prelude starting in Edition 2021",
+                                        path_names_to_string(&path)
+                                    )
+                                })
+                            } else {
+                                None
+                            };
+
+                            candidates.push(ImportSuggestion {
+                                did,
+                                descr: res.descr(),
+                                path,
+                                accessible: child_accessible,
+                                doc_visible: child_doc_visible,
+                                note,
+                                via_import,
+                            });
+                        }
+                    }
+                }
+
+                // collect submodules to explore
+                if let Some(module) = name_binding.module() {
+                    // form the path
+                    let mut path_segments = path_segments.clone();
+                    path_segments.push(ast::PathSegment::from_ident(ident));
+
+                    let is_extern_crate_that_also_appears_in_prelude =
+                        name_binding.is_extern_crate() && lookup_ident.span.at_least_rust_2018();
+
+                    if !is_extern_crate_that_also_appears_in_prelude {
+                        // add the module to the lookup
+                        if seen_modules.insert(module.def_id()) {
+                            if via_import { &mut worklist_via_import } else { &mut worklist }
+                                .push((module, path_segments, child_accessible, child_doc_visible));
+                        }
+                    }
+                }
+            })
+        }
+
+        // If only some candidates are accessible, take just them
+        if !candidates.iter().all(|v: &ImportSuggestion| !v.accessible) {
+            candidates.retain(|x| x.accessible)
+        }
+
+        candidates
+    }
+
+    /// When name resolution fails, this method can be used to look up candidate
+    /// entities with the expected name. It allows filtering them using the
+    /// supplied predicate (which should be used to only accept the types of
+    /// definitions expected, e.g., traits). The lookup spans across all crates.
+    ///
+    /// N.B., the method does not look into imports, but this is not a problem,
+    /// since we report the definitions (thus, the de-aliased imports).
+    pub(crate) fn lookup_import_candidates<FilterFn>(
+        &mut self,
+        lookup_ident: Ident,
+        namespace: Namespace,
+        parent_scope: &ParentScope<'a>,
+        filter_fn: FilterFn,
+    ) -> Vec<ImportSuggestion>
+    where
+        FilterFn: Fn(Res) -> bool,
+    {
+        let crate_path = thin_vec![ast::PathSegment::from_ident(Ident::with_dummy_span(kw::Crate))];
+        let mut suggestions = self.lookup_import_candidates_from_module(
+            lookup_ident,
+            namespace,
+            parent_scope,
+            self.graph_root,
+            crate_path,
+            &filter_fn,
+        );
+
+        if lookup_ident.span.at_least_rust_2018() {
+            for ident in self.extern_prelude.clone().into_keys() {
+                if ident.span.from_expansion() {
+                    // Idents are adjusted to the root context before being
+                    // resolved in the extern prelude, so reporting this to the
+                    // user is no help. This skips the injected
+                    // `extern crate std` in the 2018 edition, which would
+                    // otherwise cause duplicate suggestions.
+                    continue;
+                }
+                let crate_id = self.crate_loader(|c| c.maybe_process_path_extern(ident.name));
+                if let Some(crate_id) = crate_id {
+                    let crate_def_id = crate_id.as_def_id();
+                    let crate_root = self.expect_module(crate_def_id);
+
+                    // Check if there's already an item in scope with the same name as the crate.
+                    // If so, we have to disambiguate the potential import suggestions by making
+                    // the paths *global* (i.e., by prefixing them with `::`).
+                    let needs_disambiguation =
+                        self.resolutions(parent_scope.module).borrow().iter().any(
+                            |(key, name_resolution)| {
+                                if key.ns == TypeNS
+                                    && key.ident == ident
+                                    && let Some(binding) = name_resolution.borrow().binding
+                                {
+                                    match binding.res() {
+                                        // No disambiguation needed if the identically named item we
+                                        // found in scope actually refers to the crate in question.
+                                        Res::Def(_, def_id) => def_id != crate_def_id,
+                                        Res::PrimTy(_) => true,
+                                        _ => false,
+                                    }
+                                } else {
+                                    false
+                                }
+                            },
+                        );
+                    let mut crate_path = ThinVec::new();
+                    if needs_disambiguation {
+                        crate_path.push(ast::PathSegment::path_root(rustc_span::DUMMY_SP));
+                    }
+                    crate_path.push(ast::PathSegment::from_ident(ident));
+
+                    suggestions.extend(self.lookup_import_candidates_from_module(
+                        lookup_ident,
+                        namespace,
+                        parent_scope,
+                        crate_root,
+                        crate_path,
+                        &filter_fn,
+                    ));
+                }
+            }
+        }
+
+        suggestions
+    }
+
+    pub(crate) fn unresolved_macro_suggestions(
+        &mut self,
+        err: &mut Diagnostic,
+        macro_kind: MacroKind,
+        parent_scope: &ParentScope<'a>,
+        ident: Ident,
+        krate: &Crate,
+    ) {
+        let is_expected = &|res: Res| res.macro_kind() == Some(macro_kind);
+        let suggestion = self.early_lookup_typo_candidate(
+            ScopeSet::Macro(macro_kind),
+            parent_scope,
+            ident,
+            is_expected,
+        );
+        self.add_typo_suggestion(err, suggestion, ident.span);
+
+        let import_suggestions =
+            self.lookup_import_candidates(ident, Namespace::MacroNS, parent_scope, is_expected);
+        let (span, found_use) = match parent_scope.module.nearest_parent_mod().as_local() {
+            Some(def_id) => UsePlacementFinder::check(krate, self.def_id_to_node_id[def_id]),
+            None => (None, FoundUse::No),
+        };
+        show_candidates(
+            self.tcx,
+            err,
+            span,
+            &import_suggestions,
+            Instead::No,
+            found_use,
+            DiagnosticMode::Normal,
+            vec![],
+            "",
+        );
+
+        if macro_kind == MacroKind::Bang && ident.name == sym::macro_rules {
+            err.subdiagnostic(MaybeMissingMacroRulesName { span: ident.span });
+            return;
+        }
+
+        if macro_kind == MacroKind::Derive && (ident.name == sym::Send || ident.name == sym::Sync) {
+            err.subdiagnostic(ExplicitUnsafeTraits { span: ident.span, ident });
+            return;
+        }
+
+        if self.macro_names.contains(&ident.normalize_to_macros_2_0()) {
+            err.subdiagnostic(AddedMacroUse);
+            return;
+        }
+
+        if ident.name == kw::Default
+            && let ModuleKind::Def(DefKind::Enum, def_id, _) = parent_scope.module.kind
+        {
+            let span = self.def_span(def_id);
+            let source_map = self.tcx.sess.source_map();
+            let head_span = source_map.guess_head_span(span);
+            err.subdiagnostic(ConsiderAddingADerive {
+                span: head_span.shrink_to_lo(),
+                suggestion: "#[derive(Default)]\n".to_string(),
+            });
+        }
+        for ns in [Namespace::MacroNS, Namespace::TypeNS, Namespace::ValueNS] {
+            if let Ok(binding) = self.early_resolve_ident_in_lexical_scope(
+                ident,
+                ScopeSet::All(ns),
+                parent_scope,
+                None,
+                false,
+                None,
+            ) {
+                let desc = match binding.res() {
+                    Res::Def(DefKind::Macro(MacroKind::Bang), _) => {
+                        "a function-like macro".to_string()
+                    }
+                    Res::Def(DefKind::Macro(MacroKind::Attr), _) | Res::NonMacroAttr(..) => {
+                        format!("an attribute: `#[{ident}]`")
+                    }
+                    Res::Def(DefKind::Macro(MacroKind::Derive), _) => {
+                        format!("a derive macro: `#[derive({ident})]`")
+                    }
+                    Res::ToolMod => {
+                        // Don't confuse the user with tool modules.
+                        continue;
+                    }
+                    Res::Def(DefKind::Trait, _) if macro_kind == MacroKind::Derive => {
+                        "only a trait, without a derive macro".to_string()
+                    }
+                    res => format!(
+                        "{} {}, not {} {}",
+                        res.article(),
+                        res.descr(),
+                        macro_kind.article(),
+                        macro_kind.descr_expected(),
+                    ),
+                };
+                if let crate::NameBindingKind::Import { import, .. } = binding.kind {
+                    if !import.span.is_dummy() {
+                        err.span_note(
+                            import.span,
+                            format!("`{ident}` is imported here, but it is {desc}"),
+                        );
+                        // Silence the 'unused import' warning we might get,
+                        // since this diagnostic already covers that import.
+                        self.record_use(ident, binding, false);
+                        return;
+                    }
+                }
+                err.note(format!("`{ident}` is in scope, but it is {desc}"));
+                return;
+            }
+        }
+    }
+
+    pub(crate) fn add_typo_suggestion(
+        &self,
+        err: &mut Diagnostic,
+        suggestion: Option<TypoSuggestion>,
+        span: Span,
+    ) -> bool {
+        let suggestion = match suggestion {
+            None => return false,
+            // We shouldn't suggest underscore.
+            Some(suggestion) if suggestion.candidate == kw::Underscore => return false,
+            Some(suggestion) => suggestion,
+        };
+        if let Some(def_span) = suggestion.res.opt_def_id().map(|def_id| self.def_span(def_id)) {
+            if span.overlaps(def_span) {
+                // Don't suggest typo suggestion for itself like in the following:
+                // error[E0423]: expected function, tuple struct or tuple variant, found struct `X`
+                //   --> $DIR/issue-64792-bad-unicode-ctor.rs:3:14
+                //    |
+                // LL | struct X {}
+                //    | ----------- `X` defined here
+                // LL |
+                // LL | const Y: X = X("ö");
+                //    | -------------^^^^^^- similarly named constant `Y` defined here
+                //    |
+                // help: use struct literal syntax instead
+                //    |
+                // LL | const Y: X = X {};
+                //    |              ^^^^
+                // help: a constant with a similar name exists
+                //    |
+                // LL | const Y: X = Y("ö");
+                //    |              ^
+                return false;
+            }
+            let prefix = match suggestion.target {
+                SuggestionTarget::SimilarlyNamed => "similarly named ",
+                SuggestionTarget::SingleItem => "",
+            };
+
+            err.span_label(
+                self.tcx.sess.source_map().guess_head_span(def_span),
+                format!(
+                    "{}{} `{}` defined here",
+                    prefix,
+                    suggestion.res.descr(),
+                    suggestion.candidate,
+                ),
+            );
+        }
+
+        let (span, sugg, post) = if let SuggestionTarget::SimilarlyNamed = suggestion.target
+            && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
+            && let Some(span) = suggestion.span
+            && let Some(candidate) = suggestion.candidate.as_str().strip_prefix('_')
+            && snippet == candidate
+        {
+            // When the suggested binding change would be from `x` to `_x`, suggest changing the
+            // original binding definition instead. (#60164)
+            (span, snippet, ", consider changing it")
+        } else {
+            (span, suggestion.candidate.to_string(), "")
+        };
+        let msg = match suggestion.target {
+            SuggestionTarget::SimilarlyNamed => format!(
+                "{} {} with a similar name exists{post}",
+                suggestion.res.article(),
+                suggestion.res.descr()
+            ),
+            SuggestionTarget::SingleItem => {
+                format!("maybe you meant this {}", suggestion.res.descr())
+            }
+        };
+        err.span_suggestion(span, msg, sugg, Applicability::MaybeIncorrect);
+        true
+    }
+
+    fn binding_description(&self, b: NameBinding<'_>, ident: Ident, from_prelude: bool) -> String {
+        let res = b.res();
+        if b.span.is_dummy() || !self.tcx.sess.source_map().is_span_accessible(b.span) {
+            // These already contain the "built-in" prefix or look bad with it.
+            let add_built_in =
+                !matches!(b.res(), Res::NonMacroAttr(..) | Res::PrimTy(..) | Res::ToolMod);
+            let (built_in, from) = if from_prelude {
+                ("", " from prelude")
+            } else if b.is_extern_crate()
+                && !b.is_import()
+                && self.tcx.sess.opts.externs.get(ident.as_str()).is_some()
+            {
+                ("", " passed with `--extern`")
+            } else if add_built_in {
+                (" built-in", "")
+            } else {
+                ("", "")
+            };
+
+            let a = if built_in.is_empty() { res.article() } else { "a" };
+            format!("{a}{built_in} {thing}{from}", thing = res.descr())
+        } else {
+            let introduced = if b.is_import_user_facing() { "imported" } else { "defined" };
+            format!("the {thing} {introduced} here", thing = res.descr())
+        }
+    }
+
+    fn ambiguity_diagnostics(&self, ambiguity_error: &AmbiguityError<'_>) -> AmbiguityErrorDiag {
+        let AmbiguityError { kind, ident, b1, b2, misc1, misc2, .. } = *ambiguity_error;
+        let (b1, b2, misc1, misc2, swapped) = if b2.span.is_dummy() && !b1.span.is_dummy() {
+            // We have to print the span-less alternative first, otherwise formatting looks bad.
+            (b2, b1, misc2, misc1, true)
+        } else {
+            (b1, b2, misc1, misc2, false)
+        };
+        let could_refer_to = |b: NameBinding<'_>, misc: AmbiguityErrorMisc, also: &str| {
+            let what = self.binding_description(b, ident, misc == AmbiguityErrorMisc::FromPrelude);
+            let note_msg = format!("`{ident}` could{also} refer to {what}");
+
+            let thing = b.res().descr();
+            let mut help_msgs = Vec::new();
+            if b.is_glob_import()
+                && (kind == AmbiguityKind::GlobVsGlob
+                    || kind == AmbiguityKind::GlobVsExpanded
+                    || kind == AmbiguityKind::GlobVsOuter && swapped != also.is_empty())
+            {
+                help_msgs.push(format!(
+                    "consider adding an explicit import of `{ident}` to disambiguate"
+                ))
+            }
+            if b.is_extern_crate() && ident.span.at_least_rust_2018() {
+                help_msgs.push(format!("use `::{ident}` to refer to this {thing} unambiguously"))
+            }
+            match misc {
+                AmbiguityErrorMisc::SuggestCrate => help_msgs
+                    .push(format!("use `crate::{ident}` to refer to this {thing} unambiguously")),
+                AmbiguityErrorMisc::SuggestSelf => help_msgs
+                    .push(format!("use `self::{ident}` to refer to this {thing} unambiguously")),
+                AmbiguityErrorMisc::FromPrelude | AmbiguityErrorMisc::None => {}
+            }
+
+            (
+                b.span,
+                note_msg,
+                help_msgs
+                    .iter()
+                    .enumerate()
+                    .map(|(i, help_msg)| {
+                        let or = if i == 0 { "" } else { "or " };
+                        format!("{or}{help_msg}")
+                    })
+                    .collect::<Vec<_>>(),
+            )
+        };
+        let (b1_span, b1_note_msg, b1_help_msgs) = could_refer_to(b1, misc1, "");
+        let (b2_span, b2_note_msg, b2_help_msgs) = could_refer_to(b2, misc2, " also");
+
+        AmbiguityErrorDiag {
+            msg: format!("`{ident}` is ambiguous"),
+            span: ident.span,
+            label_span: ident.span,
+            label_msg: "ambiguous name".to_string(),
+            note_msg: format!("ambiguous because of {}", kind.descr()),
+            b1_span,
+            b1_note_msg,
+            b1_help_msgs,
+            b2_span,
+            b2_note_msg,
+            b2_help_msgs,
+        }
+    }
+
+    /// If the binding refers to a tuple struct constructor with fields,
+    /// returns the span of its fields.
+    fn ctor_fields_span(&self, binding: NameBinding<'_>) -> Option<Span> {
+        if let NameBindingKind::Res(Res::Def(
+            DefKind::Ctor(CtorOf::Struct, CtorKind::Fn),
+            ctor_def_id,
+        )) = binding.kind
+        {
+            let def_id = self.tcx.parent(ctor_def_id);
+            return self
+                .field_def_ids(def_id)?
+                .iter()
+                .map(|&field_id| self.def_span(field_id))
+                .reduce(Span::to); // None for `struct Foo()`
+        }
+        None
+    }
+
+    fn report_privacy_error(&mut self, privacy_error: &PrivacyError<'a>) {
+        let PrivacyError { ident, binding, outermost_res, parent_scope, dedup_span } =
+            *privacy_error;
+
+        let res = binding.res();
+        let ctor_fields_span = self.ctor_fields_span(binding);
+        let plain_descr = res.descr().to_string();
+        let nonimport_descr =
+            if ctor_fields_span.is_some() { plain_descr + " constructor" } else { plain_descr };
+        let import_descr = nonimport_descr.clone() + " import";
+        let get_descr =
+            |b: NameBinding<'_>| if b.is_import() { &import_descr } else { &nonimport_descr };
+
+        // Print the primary message.
+        let descr = get_descr(binding);
+        let mut err = struct_span_code_err!(
+            self.dcx(),
+            ident.span,
+            E0603,
+            "{} `{}` is private",
+            descr,
+            ident
+        );
+        err.span_label(ident.span, format!("private {descr}"));
+
+        let mut not_publicly_reexported = false;
+        if let Some((this_res, outer_ident)) = outermost_res {
+            let import_suggestions = self.lookup_import_candidates(
+                outer_ident,
+                this_res.ns().unwrap_or(Namespace::TypeNS),
+                &parent_scope,
+                &|res: Res| res == this_res,
+            );
+            let point_to_def = !show_candidates(
+                self.tcx,
+                &mut err,
+                Some(dedup_span.until(outer_ident.span.shrink_to_hi())),
+                &import_suggestions,
+                Instead::Yes,
+                FoundUse::Yes,
+                DiagnosticMode::Import,
+                vec![],
+                "",
+            );
+            // If we suggest importing a public re-export, don't point at the definition.
+            if point_to_def && ident.span != outer_ident.span {
+                not_publicly_reexported = true;
+                err.span_label(
+                    outer_ident.span,
+                    format!("{} `{outer_ident}` is not publicly re-exported", this_res.descr()),
+                );
+            }
+        }
+
+        let mut non_exhaustive = None;
+        // If an ADT is foreign and marked as `non_exhaustive`, then that's
+        // probably why we have the privacy error.
+        // Otherwise, point out if the struct has any private fields.
+        if let Some(def_id) = res.opt_def_id()
+            && !def_id.is_local()
+            && let Some(attr) = self.tcx.get_attr(def_id, sym::non_exhaustive)
+        {
+            non_exhaustive = Some(attr.span);
+        } else if let Some(span) = ctor_fields_span {
+            err.span_label(span, "a constructor is private if any of the fields is private");
+            if let Res::Def(_, d) = res
+                && let Some(fields) = self.field_visibility_spans.get(&d)
+            {
+                err.multipart_suggestion_verbose(
+                    format!(
+                        "consider making the field{} publicly accessible",
+                        pluralize!(fields.len())
+                    ),
+                    fields.iter().map(|span| (*span, "pub ".to_string())).collect(),
+                    Applicability::MaybeIncorrect,
+                );
+            }
+        }
+
+        let mut sugg_paths = vec![];
+        if let Some(mut def_id) = res.opt_def_id() {
+            // We can't use `def_path_str` in resolve.
+            let mut path = vec![def_id];
+            while let Some(parent) = self.tcx.opt_parent(def_id) {
+                def_id = parent;
+                if !def_id.is_top_level_module() {
+                    path.push(def_id);
+                } else {
+                    break;
+                }
+            }
+            // We will only suggest importing directly if it is accessible through that path.
+            let path_names: Option<Vec<String>> = path
+                .iter()
+                .rev()
+                .map(|def_id| {
+                    self.tcx.opt_item_name(*def_id).map(|n| {
+                        if def_id.is_top_level_module() {
+                            "crate".to_string()
+                        } else {
+                            n.to_string()
+                        }
+                    })
+                })
+                .collect();
+            if let Some(def_id) = path.get(0)
+                && let Some(path) = path_names
+            {
+                if let Some(def_id) = def_id.as_local() {
+                    if self.effective_visibilities.is_directly_public(def_id) {
+                        sugg_paths.push((path, false));
+                    }
+                } else if self.is_accessible_from(self.tcx.visibility(def_id), parent_scope.module)
+                {
+                    sugg_paths.push((path, false));
+                }
+            }
+        }
+
+        // Print the whole import chain to make it easier to see what happens.
+        let first_binding = binding;
+        let mut next_binding = Some(binding);
+        let mut next_ident = ident;
+        let mut path = vec![];
+        while let Some(binding) = next_binding {
+            let name = next_ident;
+            next_binding = match binding.kind {
+                _ if res == Res::Err => None,
+                NameBindingKind::Import { binding, import, .. } => match import.kind {
+                    _ if binding.span.is_dummy() => None,
+                    ImportKind::Single { source, .. } => {
+                        next_ident = source;
+                        Some(binding)
+                    }
+                    ImportKind::Glob { .. } | ImportKind::MacroUse | ImportKind::MacroExport => {
+                        Some(binding)
+                    }
+                    ImportKind::ExternCrate { .. } => None,
+                },
+                _ => None,
+            };
+
+            match binding.kind {
+                NameBindingKind::Import { import, .. } => {
+                    for segment in import.module_path.iter().skip(1) {
+                        path.push(segment.ident.to_string());
+                    }
+                    sugg_paths.push((
+                        path.iter()
+                            .cloned()
+                            .chain(vec![ident.to_string()].into_iter())
+                            .collect::<Vec<_>>(),
+                        true, // re-export
+                    ));
+                }
+                NameBindingKind::Res(_) | NameBindingKind::Module(_) => {}
+            }
+            let first = binding == first_binding;
+            let msg = format!(
+                "{and_refers_to}the {item} `{name}`{which} is defined here{dots}",
+                and_refers_to = if first { "" } else { "...and refers to " },
+                item = get_descr(binding),
+                which = if first { "" } else { " which" },
+                dots = if next_binding.is_some() { "..." } else { "" },
+            );
+            let def_span = self.tcx.sess.source_map().guess_head_span(binding.span);
+            let mut note_span = MultiSpan::from_span(def_span);
+            if !first && binding.vis.is_public() {
+                let desc = match binding.kind {
+                    NameBindingKind::Import { .. } => "re-export",
+                    _ => "directly",
+                };
+                note_span.push_span_label(def_span, format!("you could import this {desc}"));
+            }
+            // Final step in the import chain, point out if the ADT is `non_exhaustive`
+            // which is probably why this privacy violation occurred.
+            if next_binding.is_none()
+                && let Some(span) = non_exhaustive
+            {
+                note_span.push_span_label(
+                    span,
+                    "cannot be constructed because it is `#[non_exhaustive]`",
+                );
+            }
+            err.span_note(note_span, msg);
+        }
+        // We prioritize shorter paths, non-core imports and direct imports over the alternatives.
+        sugg_paths.sort_by_key(|(p, reexport)| (p.len(), p[0] == "core", *reexport));
+        for (sugg, reexport) in sugg_paths {
+            if not_publicly_reexported {
+                break;
+            }
+            if sugg.len() <= 1 {
+                // A single path segment suggestion is wrong. This happens on circular imports.
+                // `tests/ui/imports/issue-55884-2.rs`
+                continue;
+            }
+            let path = sugg.join("::");
+            err.span_suggestion_verbose(
+                dedup_span,
+                format!(
+                    "import `{ident}` {}",
+                    if reexport { "through the re-export" } else { "directly" }
+                ),
+                path,
+                Applicability::MachineApplicable,
+            );
+            break;
+        }
+
+        err.emit();
+    }
+
+    pub(crate) fn find_similarly_named_module_or_crate(
+        &mut self,
+        ident: Symbol,
+        current_module: Module<'a>,
+    ) -> Option<Symbol> {
+        let mut candidates = self
+            .extern_prelude
+            .keys()
+            .map(|ident| ident.name)
+            .chain(
+                self.module_map
+                    .iter()
+                    .filter(|(_, module)| {
+                        current_module.is_ancestor_of(**module) && current_module != **module
+                    })
+                    .flat_map(|(_, module)| module.kind.name()),
+            )
+            .filter(|c| !c.to_string().is_empty())
+            .collect::<Vec<_>>();
+        candidates.sort();
+        candidates.dedup();
+        match find_best_match_for_name(&candidates, ident, None) {
+            Some(sugg) if sugg == ident => None,
+            sugg => sugg,
+        }
+    }
+
+    pub(crate) fn report_path_resolution_error(
+        &mut self,
+        path: &[Segment],
+        opt_ns: Option<Namespace>, // `None` indicates a module path in import
+        parent_scope: &ParentScope<'a>,
+        ribs: Option<&PerNS<Vec<Rib<'a>>>>,
+        ignore_binding: Option<NameBinding<'a>>,
+        module: Option<ModuleOrUniformRoot<'a>>,
+        failed_segment_idx: usize,
+        ident: Ident,
+    ) -> (String, Option<Suggestion>) {
+        let is_last = failed_segment_idx == path.len() - 1;
+        let ns = if is_last { opt_ns.unwrap_or(TypeNS) } else { TypeNS };
+        let module_res = match module {
+            Some(ModuleOrUniformRoot::Module(module)) => module.res(),
+            _ => None,
+        };
+        if module_res == self.graph_root.res() {
+            let is_mod = |res| matches!(res, Res::Def(DefKind::Mod, _));
+            let mut candidates = self.lookup_import_candidates(ident, TypeNS, parent_scope, is_mod);
+            candidates
+                .sort_by_cached_key(|c| (c.path.segments.len(), pprust::path_to_string(&c.path)));
+            if let Some(candidate) = candidates.get(0) {
+                (
+                    String::from("unresolved import"),
+                    Some((
+                        vec![(ident.span, pprust::path_to_string(&candidate.path))],
+                        String::from("a similar path exists"),
+                        Applicability::MaybeIncorrect,
+                    )),
+                )
+            } else if ident.name == sym::core {
+                (
+                    format!("maybe a missing crate `{ident}`?"),
+                    Some((
+                        vec![(ident.span, "std".to_string())],
+                        "try using `std` instead of `core`".to_string(),
+                        Applicability::MaybeIncorrect,
+                    )),
+                )
+            } else if self.tcx.sess.is_rust_2015() {
+                (
+                    format!("maybe a missing crate `{ident}`?"),
+                    Some((
+                        vec![],
+                        format!(
+                            "consider adding `extern crate {ident}` to use the `{ident}` crate"
+                        ),
+                        Applicability::MaybeIncorrect,
+                    )),
+                )
+            } else {
+                (format!("could not find `{ident}` in the crate root"), None)
+            }
+        } else if failed_segment_idx > 0 {
+            let parent = path[failed_segment_idx - 1].ident.name;
+            let parent = match parent {
+                // ::foo is mounted at the crate root for 2015, and is the extern
+                // prelude for 2018+
+                kw::PathRoot if self.tcx.sess.edition() > Edition::Edition2015 => {
+                    "the list of imported crates".to_owned()
+                }
+                kw::PathRoot | kw::Crate => "the crate root".to_owned(),
+                _ => format!("`{parent}`"),
+            };
+
+            let mut msg = format!("could not find `{ident}` in {parent}");
+            if ns == TypeNS || ns == ValueNS {
+                let ns_to_try = if ns == TypeNS { ValueNS } else { TypeNS };
+                let binding = if let Some(module) = module {
+                    self.resolve_ident_in_module(
+                        module,
+                        ident,
+                        ns_to_try,
+                        parent_scope,
+                        None,
+                        ignore_binding,
+                    )
+                    .ok()
+                } else if let Some(ribs) = ribs
+                    && let Some(TypeNS | ValueNS) = opt_ns
+                {
+                    match self.resolve_ident_in_lexical_scope(
+                        ident,
+                        ns_to_try,
+                        parent_scope,
+                        None,
+                        &ribs[ns_to_try],
+                        ignore_binding,
+                    ) {
+                        // we found a locally-imported or available item/module
+                        Some(LexicalScopeBinding::Item(binding)) => Some(binding),
+                        _ => None,
+                    }
+                } else {
+                    self.early_resolve_ident_in_lexical_scope(
+                        ident,
+                        ScopeSet::All(ns_to_try),
+                        parent_scope,
+                        None,
+                        false,
+                        ignore_binding,
+                    )
+                    .ok()
+                };
+                if let Some(binding) = binding {
+                    let mut found = |what| {
+                        msg = format!(
+                            "expected {}, found {} `{}` in {}",
+                            ns.descr(),
+                            what,
+                            ident,
+                            parent
+                        )
+                    };
+                    if binding.module().is_some() {
+                        found("module")
+                    } else {
+                        match binding.res() {
+                            // Avoid using TyCtxt::def_kind_descr in the resolver, because it
+                            // indirectly *calls* the resolver, and would cause a query cycle.
+                            Res::Def(kind, id) => found(kind.descr(id)),
+                            _ => found(ns_to_try.descr()),
+                        }
+                    }
+                };
+            }
+            (msg, None)
+        } else if ident.name == kw::SelfUpper {
+            // As mentioned above, `opt_ns` being `None` indicates a module path in import.
+            // We can use this to improve a confusing error for, e.g. `use Self::Variant` in an
+            // impl
+            if opt_ns.is_none() {
+                ("`Self` cannot be used in imports".to_string(), None)
+            } else {
+                (
+                    "`Self` is only available in impls, traits, and type definitions".to_string(),
+                    None,
+                )
+            }
+        } else if ident.name.as_str().chars().next().is_some_and(|c| c.is_ascii_uppercase()) {
+            // Check whether the name refers to an item in the value namespace.
+            let binding = if let Some(ribs) = ribs {
+                self.resolve_ident_in_lexical_scope(
+                    ident,
+                    ValueNS,
+                    parent_scope,
+                    None,
+                    &ribs[ValueNS],
+                    ignore_binding,
+                )
+            } else {
+                None
+            };
+            let match_span = match binding {
+                // Name matches a local variable. For example:
+                // ```
+                // fn f() {
+                //     let Foo: &str = "";
+                //     println!("{}", Foo::Bar); // Name refers to local
+                //                               // variable `Foo`.
+                // }
+                // ```
+                Some(LexicalScopeBinding::Res(Res::Local(id))) => {
+                    Some(*self.pat_span_map.get(&id).unwrap())
+                }
+                // Name matches item from a local name binding
+                // created by `use` declaration. For example:
+                // ```
+                // pub Foo: &str = "";
+                //
+                // mod submod {
+                //     use super::Foo;
+                //     println!("{}", Foo::Bar); // Name refers to local
+                //                               // binding `Foo`.
+                // }
+                // ```
+                Some(LexicalScopeBinding::Item(name_binding)) => Some(name_binding.span),
+                _ => None,
+            };
+            let suggestion = match_span.map(|span| {
+                (
+                    vec![(span, String::from(""))],
+                    format!("`{ident}` is defined here, but is not a type"),
+                    Applicability::MaybeIncorrect,
+                )
+            });
+
+            (format!("use of undeclared type `{ident}`"), suggestion)
+        } else {
+            let mut suggestion = None;
+            if ident.name == sym::alloc {
+                suggestion = Some((
+                    vec![],
+                    String::from("add `extern crate alloc` to use the `alloc` crate"),
+                    Applicability::MaybeIncorrect,
+                ))
+            }
+
+            suggestion = suggestion.or_else(|| {
+                self.find_similarly_named_module_or_crate(ident.name, parent_scope.module).map(
+                    |sugg| {
+                        (
+                            vec![(ident.span, sugg.to_string())],
+                            String::from("there is a crate or module with a similar name"),
+                            Applicability::MaybeIncorrect,
+                        )
+                    },
+                )
+            });
+            if let Ok(binding) = self.early_resolve_ident_in_lexical_scope(
+                ident,
+                ScopeSet::All(ValueNS),
+                parent_scope,
+                None,
+                false,
+                ignore_binding,
+            ) {
+                let descr = binding.res().descr();
+                (format!("{descr} `{ident}` is not a crate or module"), suggestion)
+            } else {
+                (format!("use of undeclared crate or module `{ident}`"), suggestion)
+            }
+        }
+    }
+
+    /// Adds suggestions for a path that cannot be resolved.
+    pub(crate) fn make_path_suggestion(
+        &mut self,
+        span: Span,
+        mut path: Vec<Segment>,
+        parent_scope: &ParentScope<'a>,
+    ) -> Option<(Vec<Segment>, Option<String>)> {
+        debug!("make_path_suggestion: span={:?} path={:?}", span, path);
+
+        match (path.get(0), path.get(1)) {
+            // `{{root}}::ident::...` on both editions.
+            // On 2015 `{{root}}` is usually added implicitly.
+            (Some(fst), Some(snd))
+                if fst.ident.name == kw::PathRoot && !snd.ident.is_path_segment_keyword() => {}
+            // `ident::...` on 2018.
+            (Some(fst), _)
+                if fst.ident.span.at_least_rust_2018() && !fst.ident.is_path_segment_keyword() =>
+            {
+                // Insert a placeholder that's later replaced by `self`/`super`/etc.
+                path.insert(0, Segment::from_ident(Ident::empty()));
+            }
+            _ => return None,
+        }
+
+        self.make_missing_self_suggestion(path.clone(), parent_scope)
+            .or_else(|| self.make_missing_crate_suggestion(path.clone(), parent_scope))
+            .or_else(|| self.make_missing_super_suggestion(path.clone(), parent_scope))
+            .or_else(|| self.make_external_crate_suggestion(path, parent_scope))
+    }
+
+    /// Suggest a missing `self::` if that resolves to an correct module.
+    ///
+    /// ```text
+    ///    |
+    /// LL | use foo::Bar;
+    ///    |     ^^^ did you mean `self::foo`?
+    /// ```
+    fn make_missing_self_suggestion(
+        &mut self,
+        mut path: Vec<Segment>,
+        parent_scope: &ParentScope<'a>,
+    ) -> Option<(Vec<Segment>, Option<String>)> {
+        // Replace first ident with `self` and check if that is valid.
+        path[0].ident.name = kw::SelfLower;
+        let result = self.maybe_resolve_path(&path, None, parent_scope);
+        debug!("make_missing_self_suggestion: path={:?} result={:?}", path, result);
+        if let PathResult::Module(..) = result { Some((path, None)) } else { None }
+    }
+
+    /// Suggests a missing `crate::` if that resolves to an correct module.
+    ///
+    /// ```text
+    ///    |
+    /// LL | use foo::Bar;
+    ///    |     ^^^ did you mean `crate::foo`?
+    /// ```
+    fn make_missing_crate_suggestion(
+        &mut self,
+        mut path: Vec<Segment>,
+        parent_scope: &ParentScope<'a>,
+    ) -> Option<(Vec<Segment>, Option<String>)> {
+        // Replace first ident with `crate` and check if that is valid.
+        path[0].ident.name = kw::Crate;
+        let result = self.maybe_resolve_path(&path, None, parent_scope);
+        debug!("make_missing_crate_suggestion:  path={:?} result={:?}", path, result);
+        if let PathResult::Module(..) = result {
+            Some((
+                path,
+                Some(
+                    "`use` statements changed in Rust 2018; read more at \
+                     <https://doc.rust-lang.org/edition-guide/rust-2018/module-system/path-\
+                     clarity.html>"
+                        .to_string(),
+                ),
+            ))
+        } else {
+            None
+        }
+    }
+
+    /// Suggests a missing `super::` if that resolves to an correct module.
+    ///
+    /// ```text
+    ///    |
+    /// LL | use foo::Bar;
+    ///    |     ^^^ did you mean `super::foo`?
+    /// ```
+    fn make_missing_super_suggestion(
+        &mut self,
+        mut path: Vec<Segment>,
+        parent_scope: &ParentScope<'a>,
+    ) -> Option<(Vec<Segment>, Option<String>)> {
+        // Replace first ident with `crate` and check if that is valid.
+        path[0].ident.name = kw::Super;
+        let result = self.maybe_resolve_path(&path, None, parent_scope);
+        debug!("make_missing_super_suggestion:  path={:?} result={:?}", path, result);
+        if let PathResult::Module(..) = result { Some((path, None)) } else { None }
+    }
+
+    /// Suggests a missing external crate name if that resolves to an correct module.
+    ///
+    /// ```text
+    ///    |
+    /// LL | use foobar::Baz;
+    ///    |     ^^^^^^ did you mean `baz::foobar`?
+    /// ```
+    ///
+    /// Used when importing a submodule of an external crate but missing that crate's
+    /// name as the first part of path.
+    fn make_external_crate_suggestion(
+        &mut self,
+        mut path: Vec<Segment>,
+        parent_scope: &ParentScope<'a>,
+    ) -> Option<(Vec<Segment>, Option<String>)> {
+        if path[1].ident.span.is_rust_2015() {
+            return None;
+        }
+
+        // Sort extern crate names in *reverse* order to get
+        // 1) some consistent ordering for emitted diagnostics, and
+        // 2) `std` suggestions before `core` suggestions.
+        let mut extern_crate_names =
+            self.extern_prelude.keys().map(|ident| ident.name).collect::<Vec<_>>();
+        extern_crate_names.sort_by(|a, b| b.as_str().partial_cmp(a.as_str()).unwrap());
+
+        for name in extern_crate_names.into_iter() {
+            // Replace first ident with a crate name and check if that is valid.
+            path[0].ident.name = name;
+            let result = self.maybe_resolve_path(&path, None, parent_scope);
+            debug!(
+                "make_external_crate_suggestion: name={:?} path={:?} result={:?}",
+                name, path, result
+            );
+            if let PathResult::Module(..) = result {
+                return Some((path, None));
+            }
+        }
+
+        None
+    }
+
+    /// Suggests importing a macro from the root of the crate rather than a module within
+    /// the crate.
+    ///
+    /// ```text
+    /// help: a macro with this name exists at the root of the crate
+    ///    |
+    /// LL | use issue_59764::makro;
+    ///    |     ^^^^^^^^^^^^^^^^^^
+    ///    |
+    ///    = note: this could be because a macro annotated with `#[macro_export]` will be exported
+    ///            at the root of the crate instead of the module where it is defined
+    /// ```
+    pub(crate) fn check_for_module_export_macro(
+        &mut self,
+        import: Import<'a>,
+        module: ModuleOrUniformRoot<'a>,
+        ident: Ident,
+    ) -> Option<(Option<Suggestion>, Option<String>)> {
+        let ModuleOrUniformRoot::Module(mut crate_module) = module else {
+            return None;
+        };
+
+        while let Some(parent) = crate_module.parent {
+            crate_module = parent;
+        }
+
+        if module == ModuleOrUniformRoot::Module(crate_module) {
+            // Don't make a suggestion if the import was already from the root of the crate.
+            return None;
+        }
+
+        let resolutions = self.resolutions(crate_module).borrow();
+        let binding_key = BindingKey::new(ident, MacroNS);
+        let resolution = resolutions.get(&binding_key)?;
+        let binding = resolution.borrow().binding()?;
+        if let Res::Def(DefKind::Macro(MacroKind::Bang), _) = binding.res() {
+            let module_name = crate_module.kind.name().unwrap();
+            let import_snippet = match import.kind {
+                ImportKind::Single { source, target, .. } if source != target => {
+                    format!("{source} as {target}")
+                }
+                _ => format!("{ident}"),
+            };
+
+            let mut corrections: Vec<(Span, String)> = Vec::new();
+            if !import.is_nested() {
+                // Assume this is the easy case of `use issue_59764::foo::makro;` and just remove
+                // intermediate segments.
+                corrections.push((import.span, format!("{module_name}::{import_snippet}")));
+            } else {
+                // Find the binding span (and any trailing commas and spaces).
+                //   ie. `use a::b::{c, d, e};`
+                //                      ^^^
+                let (found_closing_brace, binding_span) = find_span_of_binding_until_next_binding(
+                    self.tcx.sess,
+                    import.span,
+                    import.use_span,
+                );
+                debug!(
+                    "check_for_module_export_macro: found_closing_brace={:?} binding_span={:?}",
+                    found_closing_brace, binding_span
+                );
+
+                let mut removal_span = binding_span;
+                if found_closing_brace {
+                    // If the binding span ended with a closing brace, as in the below example:
+                    //   ie. `use a::b::{c, d};`
+                    //                      ^
+                    // Then expand the span of characters to remove to include the previous
+                    // binding's trailing comma.
+                    //   ie. `use a::b::{c, d};`
+                    //                    ^^^
+                    if let Some(previous_span) =
+                        extend_span_to_previous_binding(self.tcx.sess, binding_span)
+                    {
+                        debug!("check_for_module_export_macro: previous_span={:?}", previous_span);
+                        removal_span = removal_span.with_lo(previous_span.lo());
+                    }
+                }
+                debug!("check_for_module_export_macro: removal_span={:?}", removal_span);
+
+                // Remove the `removal_span`.
+                corrections.push((removal_span, "".to_string()));
+
+                // Find the span after the crate name and if it has nested imports immediately
+                // after the crate name already.
+                //   ie. `use a::b::{c, d};`
+                //               ^^^^^^^^^
+                //   or  `use a::{b, c, d}};`
+                //               ^^^^^^^^^^^
+                let (has_nested, after_crate_name) = find_span_immediately_after_crate_name(
+                    self.tcx.sess,
+                    module_name,
+                    import.use_span,
+                );
+                debug!(
+                    "check_for_module_export_macro: has_nested={:?} after_crate_name={:?}",
+                    has_nested, after_crate_name
+                );
+
+                let source_map = self.tcx.sess.source_map();
+
+                // Make sure this is actually crate-relative.
+                let is_definitely_crate = import
+                    .module_path
+                    .first()
+                    .is_some_and(|f| f.ident.name != kw::SelfLower && f.ident.name != kw::Super);
+
+                // Add the import to the start, with a `{` if required.
+                let start_point = source_map.start_point(after_crate_name);
+                if is_definitely_crate
+                    && let Ok(start_snippet) = source_map.span_to_snippet(start_point)
+                {
+                    corrections.push((
+                        start_point,
+                        if has_nested {
+                            // In this case, `start_snippet` must equal '{'.
+                            format!("{start_snippet}{import_snippet}, ")
+                        } else {
+                            // In this case, add a `{`, then the moved import, then whatever
+                            // was there before.
+                            format!("{{{import_snippet}, {start_snippet}")
+                        },
+                    ));
+
+                    // Add a `};` to the end if nested, matching the `{` added at the start.
+                    if !has_nested {
+                        corrections
+                            .push((source_map.end_point(after_crate_name), "};".to_string()));
+                    }
+                } else {
+                    // If the root import is module-relative, add the import separately
+                    corrections.push((
+                        import.use_span.shrink_to_lo(),
+                        format!("use {module_name}::{import_snippet};\n"),
+                    ));
+                }
+            }
+
+            let suggestion = Some((
+                corrections,
+                String::from("a macro with this name exists at the root of the crate"),
+                Applicability::MaybeIncorrect,
+            ));
+            Some((suggestion, Some("this could be because a macro annotated with `#[macro_export]` will be exported \
+            at the root of the crate instead of the module where it is defined"
+               .to_string())))
+        } else {
+            None
+        }
+    }
+
+    /// Finds a cfg-ed out item inside `module` with the matching name.
+    pub(crate) fn find_cfg_stripped(
+        &mut self,
+        err: &mut Diagnostic,
+        segment: &Symbol,
+        module: DefId,
+    ) {
+        let local_items;
+        let symbols = if module.is_local() {
+            local_items = self
+                .stripped_cfg_items
+                .iter()
+                .filter_map(|item| {
+                    let parent_module = self.opt_local_def_id(item.parent_module)?.to_def_id();
+                    Some(StrippedCfgItem { parent_module, name: item.name, cfg: item.cfg.clone() })
+                })
+                .collect::<Vec<_>>();
+            local_items.as_slice()
+        } else {
+            self.tcx.stripped_cfg_items(module.krate)
+        };
+
+        for &StrippedCfgItem { parent_module, name, ref cfg } in symbols {
+            if parent_module != module || name.name != *segment {
+                continue;
+            }
+
+            err.span_note(name.span, "found an item that was configured out");
+
+            if let MetaItemKind::List(nested) = &cfg.kind
+                && let NestedMetaItem::MetaItem(meta_item) = &nested[0]
+                && let MetaItemKind::NameValue(feature_name) = &meta_item.kind
+            {
+                err.note(format!("the item is gated behind the `{}` feature", feature_name.symbol));
+            }
+        }
+    }
+}
+
+/// Given a `binding_span` of a binding within a use statement:
+///
+/// ```ignore (illustrative)
+/// use foo::{a, b, c};
+/// //           ^
+/// ```
+///
+/// then return the span until the next binding or the end of the statement:
+///
+/// ```ignore (illustrative)
+/// use foo::{a, b, c};
+/// //           ^^^
+/// ```
+fn find_span_of_binding_until_next_binding(
+    sess: &Session,
+    binding_span: Span,
+    use_span: Span,
+) -> (bool, Span) {
+    let source_map = sess.source_map();
+
+    // Find the span of everything after the binding.
+    //   ie. `a, e};` or `a};`
+    let binding_until_end = binding_span.with_hi(use_span.hi());
+
+    // Find everything after the binding but not including the binding.
+    //   ie. `, e};` or `};`
+    let after_binding_until_end = binding_until_end.with_lo(binding_span.hi());
+
+    // Keep characters in the span until we encounter something that isn't a comma or
+    // whitespace.
+    //   ie. `, ` or ``.
+    //
+    // Also note whether a closing brace character was encountered. If there
+    // was, then later go backwards to remove any trailing commas that are left.
+    let mut found_closing_brace = false;
+    let after_binding_until_next_binding =
+        source_map.span_take_while(after_binding_until_end, |&ch| {
+            if ch == '}' {
+                found_closing_brace = true;
+            }
+            ch == ' ' || ch == ','
+        });
+
+    // Combine the two spans.
+    //   ie. `a, ` or `a`.
+    //
+    // Removing these would leave `issue_52891::{d, e};` or `issue_52891::{d, e, };`
+    let span = binding_span.with_hi(after_binding_until_next_binding.hi());
+
+    (found_closing_brace, span)
+}
+
+/// Given a `binding_span`, return the span through to the comma or opening brace of the previous
+/// binding.
+///
+/// ```ignore (illustrative)
+/// use foo::a::{a, b, c};
+/// //            ^^--- binding span
+/// //            |
+/// //            returned span
+///
+/// use foo::{a, b, c};
+/// //        --- binding span
+/// ```
+fn extend_span_to_previous_binding(sess: &Session, binding_span: Span) -> Option<Span> {
+    let source_map = sess.source_map();
+
+    // `prev_source` will contain all of the source that came before the span.
+    // Then split based on a command and take the first (ie. closest to our span)
+    // snippet. In the example, this is a space.
+    let prev_source = source_map.span_to_prev_source(binding_span).ok()?;
+
+    let prev_comma = prev_source.rsplit(',').collect::<Vec<_>>();
+    let prev_starting_brace = prev_source.rsplit('{').collect::<Vec<_>>();
+    if prev_comma.len() <= 1 || prev_starting_brace.len() <= 1 {
+        return None;
+    }
+
+    let prev_comma = prev_comma.first().unwrap();
+    let prev_starting_brace = prev_starting_brace.first().unwrap();
+
+    // If the amount of source code before the comma is greater than
+    // the amount of source code before the starting brace then we've only
+    // got one item in the nested item (eg. `issue_52891::{self}`).
+    if prev_comma.len() > prev_starting_brace.len() {
+        return None;
+    }
+
+    Some(binding_span.with_lo(BytePos(
+        // Take away the number of bytes for the characters we've found and an
+        // extra for the comma.
+        binding_span.lo().0 - (prev_comma.as_bytes().len() as u32) - 1,
+    )))
+}
+
+/// Given a `use_span` of a binding within a use statement, returns the highlighted span and if
+/// it is a nested use tree.
+///
+/// ```ignore (illustrative)
+/// use foo::a::{b, c};
+/// //       ^^^^^^^^^^ -- false
+///
+/// use foo::{a, b, c};
+/// //       ^^^^^^^^^^ -- true
+///
+/// use foo::{a, b::{c, d}};
+/// //       ^^^^^^^^^^^^^^^ -- true
+/// ```
+fn find_span_immediately_after_crate_name(
+    sess: &Session,
+    module_name: Symbol,
+    use_span: Span,
+) -> (bool, Span) {
+    debug!(
+        "find_span_immediately_after_crate_name: module_name={:?} use_span={:?}",
+        module_name, use_span
+    );
+    let source_map = sess.source_map();
+
+    // Using `use issue_59764::foo::{baz, makro};` as an example throughout..
+    let mut num_colons = 0;
+    // Find second colon.. `use issue_59764:`
+    let until_second_colon = source_map.span_take_while(use_span, |c| {
+        if *c == ':' {
+            num_colons += 1;
+        }
+        !matches!(c, ':' if num_colons == 2)
+    });
+    // Find everything after the second colon.. `foo::{baz, makro};`
+    let from_second_colon = use_span.with_lo(until_second_colon.hi() + BytePos(1));
+
+    let mut found_a_non_whitespace_character = false;
+    // Find the first non-whitespace character in `from_second_colon`.. `f`
+    let after_second_colon = source_map.span_take_while(from_second_colon, |c| {
+        if found_a_non_whitespace_character {
+            return false;
+        }
+        if !c.is_whitespace() {
+            found_a_non_whitespace_character = true;
+        }
+        true
+    });
+
+    // Find the first `{` in from_second_colon.. `foo::{`
+    let next_left_bracket = source_map.span_through_char(from_second_colon, '{');
+
+    (next_left_bracket == after_second_colon, from_second_colon)
+}
+
+/// A suggestion has already been emitted, change the wording slightly to clarify that both are
+/// independent options.
+enum Instead {
+    Yes,
+    No,
+}
+
+/// Whether an existing place with an `use` item was found.
+enum FoundUse {
+    Yes,
+    No,
+}
+
+/// Whether a binding is part of a pattern or a use statement. Used for diagnostics.
+pub(crate) enum DiagnosticMode {
+    Normal,
+    /// The binding is part of a pattern
+    Pattern,
+    /// The binding is part of a use statement
+    Import,
+}
+
+pub(crate) fn import_candidates(
+    tcx: TyCtxt<'_>,
+    err: &mut Diagnostic,
+    // This is `None` if all placement locations are inside expansions
+    use_placement_span: Option<Span>,
+    candidates: &[ImportSuggestion],
+    mode: DiagnosticMode,
+    append: &str,
+) {
+    show_candidates(
+        tcx,
+        err,
+        use_placement_span,
+        candidates,
+        Instead::Yes,
+        FoundUse::Yes,
+        mode,
+        vec![],
+        append,
+    );
+}
+
+/// When an entity with a given name is not available in scope, we search for
+/// entities with that name in all crates. This method allows outputting the
+/// results of this search in a programmer-friendly way. If any entities are
+/// found and suggested, returns `true`, otherwise returns `false`.
+fn show_candidates(
+    tcx: TyCtxt<'_>,
+    err: &mut Diagnostic,
+    // This is `None` if all placement locations are inside expansions
+    use_placement_span: Option<Span>,
+    candidates: &[ImportSuggestion],
+    instead: Instead,
+    found_use: FoundUse,
+    mode: DiagnosticMode,
+    path: Vec<Segment>,
+    append: &str,
+) -> bool {
+    if candidates.is_empty() {
+        return false;
+    }
+
+    let mut accessible_path_strings: Vec<(String, &str, Option<DefId>, &Option<String>, bool)> =
+        Vec::new();
+    let mut inaccessible_path_strings: Vec<(String, &str, Option<DefId>, &Option<String>, bool)> =
+        Vec::new();
+
+    candidates.iter().for_each(|c| {
+        if c.accessible {
+            // Don't suggest `#[doc(hidden)]` items from other crates
+            if c.doc_visible {
+                accessible_path_strings.push((
+                    pprust::path_to_string(&c.path),
+                    c.descr,
+                    c.did,
+                    &c.note,
+                    c.via_import,
+                ))
+            }
+        } else {
+            inaccessible_path_strings.push((
+                pprust::path_to_string(&c.path),
+                c.descr,
+                c.did,
+                &c.note,
+                c.via_import,
+            ))
+        }
+    });
+
+    // we want consistent results across executions, but candidates are produced
+    // by iterating through a hash map, so make sure they are ordered:
+    for path_strings in [&mut accessible_path_strings, &mut inaccessible_path_strings] {
+        path_strings.sort_by(|a, b| a.0.cmp(&b.0));
+        let core_path_strings =
+            path_strings.extract_if(|p| p.0.starts_with("core::")).collect::<Vec<_>>();
+        path_strings.extend(core_path_strings);
+        path_strings.dedup_by(|a, b| a.0 == b.0);
+    }
+    accessible_path_strings.sort();
+
+    if !accessible_path_strings.is_empty() {
+        let (determiner, kind, name, through) =
+            if let [(name, descr, _, _, via_import)] = &accessible_path_strings[..] {
+                (
+                    "this",
+                    *descr,
+                    format!(" `{name}`"),
+                    if *via_import { " through its public re-export" } else { "" },
+                )
+            } else {
+                ("one of these", "items", String::new(), "")
+            };
+
+        let instead = if let Instead::Yes = instead { " instead" } else { "" };
+        let mut msg = if let DiagnosticMode::Pattern = mode {
+            format!(
+                "if you meant to match on {kind}{instead}{name}, use the full path in the pattern",
+            )
+        } else {
+            format!("consider importing {determiner} {kind}{through}{instead}")
+        };
+
+        for note in accessible_path_strings.iter().flat_map(|cand| cand.3.as_ref()) {
+            err.note(note.clone());
+        }
+
+        if let Some(span) = use_placement_span {
+            let (add_use, trailing) = match mode {
+                DiagnosticMode::Pattern => {
+                    err.span_suggestions(
+                        span,
+                        msg,
+                        accessible_path_strings.into_iter().map(|a| a.0),
+                        Applicability::MaybeIncorrect,
+                    );
+                    return true;
+                }
+                DiagnosticMode::Import => ("", ""),
+                DiagnosticMode::Normal => ("use ", ";\n"),
+            };
+            for candidate in &mut accessible_path_strings {
+                // produce an additional newline to separate the new use statement
+                // from the directly following item.
+                let additional_newline = if let FoundUse::No = found_use
+                    && let DiagnosticMode::Normal = mode
+                {
+                    "\n"
+                } else {
+                    ""
+                };
+                candidate.0 =
+                    format!("{add_use}{}{append}{trailing}{additional_newline}", &candidate.0);
+            }
+
+            err.span_suggestions_with_style(
+                span,
+                msg,
+                accessible_path_strings.into_iter().map(|a| a.0),
+                Applicability::MaybeIncorrect,
+                SuggestionStyle::ShowAlways,
+            );
+            if let [first, .., last] = &path[..] {
+                let sp = first.ident.span.until(last.ident.span);
+                // Our suggestion is empty, so make sure the span is not empty (or we'd ICE).
+                // Can happen for derive-generated spans.
+                if sp.can_be_used_for_suggestions() && !sp.is_empty() {
+                    err.span_suggestion_verbose(
+                        sp,
+                        format!("if you import `{}`, refer to it directly", last.ident),
+                        "",
+                        Applicability::Unspecified,
+                    );
+                }
+            }
+        } else {
+            msg.push(':');
+
+            for candidate in accessible_path_strings {
+                msg.push('\n');
+                msg.push_str(&candidate.0);
+            }
+
+            err.help(msg);
+        }
+        true
+    } else if !(inaccessible_path_strings.is_empty() || matches!(mode, DiagnosticMode::Import)) {
+        let prefix = if let DiagnosticMode::Pattern = mode {
+            "you might have meant to match on "
+        } else {
+            ""
+        };
+        if let [(name, descr, def_id, note, _)] = &inaccessible_path_strings[..] {
+            let msg = format!(
+                "{prefix}{descr} `{name}`{} exists but is inaccessible",
+                if let DiagnosticMode::Pattern = mode { ", which" } else { "" }
+            );
+
+            if let Some(local_def_id) = def_id.and_then(|did| did.as_local()) {
+                let span = tcx.source_span(local_def_id);
+                let span = tcx.sess.source_map().guess_head_span(span);
+                let mut multi_span = MultiSpan::from_span(span);
+                multi_span.push_span_label(span, "not accessible");
+                err.span_note(multi_span, msg);
+            } else {
+                err.note(msg);
+            }
+            if let Some(note) = (*note).as_deref() {
+                err.note(note.to_string());
+            }
+        } else {
+            let (_, descr_first, _, _, _) = &inaccessible_path_strings[0];
+            let descr = if inaccessible_path_strings
+                .iter()
+                .skip(1)
+                .all(|(_, descr, _, _, _)| descr == descr_first)
+            {
+                descr_first
+            } else {
+                "item"
+            };
+            let plural_descr =
+                if descr.ends_with('s') { format!("{descr}es") } else { format!("{descr}s") };
+
+            let mut msg = format!("{prefix}these {plural_descr} exist but are inaccessible");
+            let mut has_colon = false;
+
+            let mut spans = Vec::new();
+            for (name, _, def_id, _, _) in &inaccessible_path_strings {
+                if let Some(local_def_id) = def_id.and_then(|did| did.as_local()) {
+                    let span = tcx.source_span(local_def_id);
+                    let span = tcx.sess.source_map().guess_head_span(span);
+                    spans.push((name, span));
+                } else {
+                    if !has_colon {
+                        msg.push(':');
+                        has_colon = true;
+                    }
+                    msg.push('\n');
+                    msg.push_str(name);
+                }
+            }
+
+            let mut multi_span = MultiSpan::from_spans(spans.iter().map(|(_, sp)| *sp).collect());
+            for (name, span) in spans {
+                multi_span.push_span_label(span, format!("`{name}`: not accessible"));
+            }
+
+            for note in inaccessible_path_strings.iter().flat_map(|cand| cand.3.as_ref()) {
+                err.note(note.clone());
+            }
+
+            err.span_note(multi_span, msg);
+        }
+        true
+    } else {
+        false
+    }
+}
+
+#[derive(Debug)]
+struct UsePlacementFinder {
+    target_module: NodeId,
+    first_legal_span: Option<Span>,
+    first_use_span: Option<Span>,
+}
+
+impl UsePlacementFinder {
+    fn check(krate: &Crate, target_module: NodeId) -> (Option<Span>, FoundUse) {
+        let mut finder =
+            UsePlacementFinder { target_module, first_legal_span: None, first_use_span: None };
+        finder.visit_crate(krate);
+        if let Some(use_span) = finder.first_use_span {
+            (Some(use_span), FoundUse::Yes)
+        } else {
+            (finder.first_legal_span, FoundUse::No)
+        }
+    }
+}
+
+impl<'tcx> visit::Visitor<'tcx> for UsePlacementFinder {
+    fn visit_crate(&mut self, c: &Crate) {
+        if self.target_module == CRATE_NODE_ID {
+            let inject = c.spans.inject_use_span;
+            if is_span_suitable_for_use_injection(inject) {
+                self.first_legal_span = Some(inject);
+            }
+            self.first_use_span = search_for_any_use_in_items(&c.items);
+            return;
+        } else {
+            visit::walk_crate(self, c);
+        }
+    }
+
+    fn visit_item(&mut self, item: &'tcx ast::Item) {
+        if self.target_module == item.id {
+            if let ItemKind::Mod(_, ModKind::Loaded(items, _inline, mod_spans)) = &item.kind {
+                let inject = mod_spans.inject_use_span;
+                if is_span_suitable_for_use_injection(inject) {
+                    self.first_legal_span = Some(inject);
+                }
+                self.first_use_span = search_for_any_use_in_items(items);
+                return;
+            }
+        } else {
+            visit::walk_item(self, item);
+        }
+    }
+}
+
+fn search_for_any_use_in_items(items: &[P<ast::Item>]) -> Option<Span> {
+    for item in items {
+        if let ItemKind::Use(..) = item.kind {
+            if is_span_suitable_for_use_injection(item.span) {
+                let mut lo = item.span.lo();
+                for attr in &item.attrs {
+                    if attr.span.eq_ctxt(item.span) {
+                        lo = std::cmp::min(lo, attr.span.lo());
+                    }
+                }
+                return Some(Span::new(lo, lo, item.span.ctxt(), item.span.parent()));
+            }
+        }
+    }
+    return None;
+}
+
+fn is_span_suitable_for_use_injection(s: Span) -> bool {
+    // don't suggest placing a use before the prelude
+    // import or other generated ones
+    !s.from_expansion()
+}
+
+/// Convert the given number into the corresponding ordinal
+pub(crate) fn ordinalize(v: usize) -> String {
+    let suffix = match ((11..=13).contains(&(v % 100)), v % 10) {
+        (false, 1) => "st",
+        (false, 2) => "nd",
+        (false, 3) => "rd",
+        _ => "th",
+    };
+    format!("{v}{suffix}")
+}
diff --git a/compiler/rustc_resolve/src/diagnostics/tests.rs b/compiler/rustc_resolve/src/diagnostics/tests.rs
new file mode 100644
index 00000000000..2aa6cc61e46
--- /dev/null
+++ b/compiler/rustc_resolve/src/diagnostics/tests.rs
@@ -0,0 +1,40 @@
+use super::ordinalize;
+
+#[test]
+fn test_ordinalize() {
+    assert_eq!(ordinalize(1), "1st");
+    assert_eq!(ordinalize(2), "2nd");
+    assert_eq!(ordinalize(3), "3rd");
+    assert_eq!(ordinalize(4), "4th");
+    assert_eq!(ordinalize(5), "5th");
+    // ...
+    assert_eq!(ordinalize(10), "10th");
+    assert_eq!(ordinalize(11), "11th");
+    assert_eq!(ordinalize(12), "12th");
+    assert_eq!(ordinalize(13), "13th");
+    assert_eq!(ordinalize(14), "14th");
+    // ...
+    assert_eq!(ordinalize(20), "20th");
+    assert_eq!(ordinalize(21), "21st");
+    assert_eq!(ordinalize(22), "22nd");
+    assert_eq!(ordinalize(23), "23rd");
+    assert_eq!(ordinalize(24), "24th");
+    // ...
+    assert_eq!(ordinalize(30), "30th");
+    assert_eq!(ordinalize(31), "31st");
+    assert_eq!(ordinalize(32), "32nd");
+    assert_eq!(ordinalize(33), "33rd");
+    assert_eq!(ordinalize(34), "34th");
+    // ...
+    assert_eq!(ordinalize(7010), "7010th");
+    assert_eq!(ordinalize(7011), "7011th");
+    assert_eq!(ordinalize(7012), "7012th");
+    assert_eq!(ordinalize(7013), "7013th");
+    assert_eq!(ordinalize(7014), "7014th");
+    // ...
+    assert_eq!(ordinalize(7020), "7020th");
+    assert_eq!(ordinalize(7021), "7021st");
+    assert_eq!(ordinalize(7022), "7022nd");
+    assert_eq!(ordinalize(7023), "7023rd");
+    assert_eq!(ordinalize(7024), "7024th");
+}
diff --git a/compiler/rustc_resolve/src/effective_visibilities.rs b/compiler/rustc_resolve/src/effective_visibilities.rs
new file mode 100644
index 00000000000..3443bbe6e11
--- /dev/null
+++ b/compiler/rustc_resolve/src/effective_visibilities.rs
@@ -0,0 +1,284 @@
+use crate::{NameBinding, NameBindingKind, Resolver};
+use rustc_ast::ast;
+use rustc_ast::visit;
+use rustc_ast::visit::Visitor;
+use rustc_ast::Crate;
+use rustc_ast::EnumDef;
+use rustc_data_structures::fx::FxHashSet;
+use rustc_hir::def_id::LocalDefId;
+use rustc_hir::def_id::CRATE_DEF_ID;
+use rustc_middle::middle::privacy::Level;
+use rustc_middle::middle::privacy::{EffectiveVisibilities, EffectiveVisibility};
+use rustc_middle::ty::Visibility;
+use std::mem;
+
+#[derive(Clone, Copy)]
+enum ParentId<'a> {
+    Def(LocalDefId),
+    Import(NameBinding<'a>),
+}
+
+impl ParentId<'_> {
+    fn level(self) -> Level {
+        match self {
+            ParentId::Def(_) => Level::Direct,
+            ParentId::Import(_) => Level::Reexported,
+        }
+    }
+}
+
+pub(crate) struct EffectiveVisibilitiesVisitor<'r, 'a, 'tcx> {
+    r: &'r mut Resolver<'a, 'tcx>,
+    def_effective_visibilities: EffectiveVisibilities,
+    /// While walking import chains we need to track effective visibilities per-binding, and def id
+    /// keys in `Resolver::effective_visibilities` are not enough for that, because multiple
+    /// bindings can correspond to a single def id in imports. So we keep a separate table.
+    import_effective_visibilities: EffectiveVisibilities<NameBinding<'a>>,
+    // It's possible to recalculate this at any point, but it's relatively expensive.
+    current_private_vis: Visibility,
+    changed: bool,
+}
+
+impl Resolver<'_, '_> {
+    fn nearest_normal_mod(&mut self, def_id: LocalDefId) -> LocalDefId {
+        self.get_nearest_non_block_module(def_id.to_def_id()).nearest_parent_mod().expect_local()
+    }
+
+    fn private_vis_import(&mut self, binding: NameBinding<'_>) -> Visibility {
+        let NameBindingKind::Import { import, .. } = binding.kind else { unreachable!() };
+        Visibility::Restricted(
+            import
+                .id()
+                .map(|id| self.nearest_normal_mod(self.local_def_id(id)))
+                .unwrap_or(CRATE_DEF_ID),
+        )
+    }
+
+    fn private_vis_def(&mut self, def_id: LocalDefId) -> Visibility {
+        // For mod items `nearest_normal_mod` returns its argument, but we actually need its parent.
+        let normal_mod_id = self.nearest_normal_mod(def_id);
+        if normal_mod_id == def_id {
+            Visibility::Restricted(self.tcx.local_parent(def_id))
+        } else {
+            Visibility::Restricted(normal_mod_id)
+        }
+    }
+}
+
+impl<'r, 'a, 'tcx> EffectiveVisibilitiesVisitor<'r, 'a, 'tcx> {
+    /// 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. Returns the set of ambiguous re-exports.
+    pub(crate) fn compute_effective_visibilities<'c>(
+        r: &'r mut Resolver<'a, 'tcx>,
+        krate: &'c Crate,
+    ) -> FxHashSet<NameBinding<'a>> {
+        let mut visitor = EffectiveVisibilitiesVisitor {
+            r,
+            def_effective_visibilities: Default::default(),
+            import_effective_visibilities: Default::default(),
+            current_private_vis: Visibility::Restricted(CRATE_DEF_ID),
+            changed: true,
+        };
+
+        visitor.def_effective_visibilities.update_root();
+        visitor.set_bindings_effective_visibilities(CRATE_DEF_ID);
+
+        while visitor.changed {
+            visitor.changed = false;
+            visit::walk_crate(&mut visitor, krate);
+        }
+        visitor.r.effective_visibilities = visitor.def_effective_visibilities;
+
+        let mut exported_ambiguities = FxHashSet::default();
+
+        // Update visibilities for import def ids. These are not used during the
+        // `EffectiveVisibilitiesVisitor` pass, because we have more detailed binding-based
+        // information, but are used by later passes. Effective visibility of an import def id
+        // is the maximum value among visibilities of bindings corresponding to that def id.
+        for (binding, eff_vis) in visitor.import_effective_visibilities.iter() {
+            let NameBindingKind::Import { import, .. } = binding.kind else { unreachable!() };
+            if !binding.is_ambiguity() {
+                if let Some(node_id) = import.id() {
+                    r.effective_visibilities.update_eff_vis(r.local_def_id(node_id), eff_vis, r.tcx)
+                }
+            } else if binding.ambiguity.is_some() && eff_vis.is_public_at_level(Level::Reexported) {
+                exported_ambiguities.insert(*binding);
+            }
+        }
+
+        info!("resolve::effective_visibilities: {:#?}", r.effective_visibilities);
+
+        exported_ambiguities
+    }
+
+    /// Update effective visibilities of bindings in the given module,
+    /// including their whole reexport chains.
+    fn set_bindings_effective_visibilities(&mut self, module_id: LocalDefId) {
+        assert!(self.r.module_map.contains_key(&module_id.to_def_id()));
+        let module = self.r.get_module(module_id.to_def_id()).unwrap();
+        let resolutions = self.r.resolutions(module);
+
+        for (_, name_resolution) in resolutions.borrow().iter() {
+            if let Some(mut binding) = name_resolution.borrow().binding() {
+                // Set the given effective visibility level to `Level::Direct` and
+                // sets the rest of the `use` chain to `Level::Reexported` until
+                // we hit the actual exported item.
+                //
+                // If the binding is ambiguous, put the root ambiguity binding and all reexports
+                // leading to it into the table. They are used by the `ambiguous_glob_reexports`
+                // lint. For all bindings added to the table this way `is_ambiguity` returns true.
+                let is_ambiguity =
+                    |binding: NameBinding<'a>, warn: bool| binding.ambiguity.is_some() && !warn;
+                let mut parent_id = ParentId::Def(module_id);
+                let mut warn_ambiguity = binding.warn_ambiguity;
+                while let NameBindingKind::Import { binding: nested_binding, .. } = binding.kind {
+                    self.update_import(binding, parent_id);
+
+                    if is_ambiguity(binding, warn_ambiguity) {
+                        // Stop at the root ambiguity, further bindings in the chain should not
+                        // be reexported because the root ambiguity blocks any access to them.
+                        // (Those further bindings are most likely not ambiguities themselves.)
+                        break;
+                    }
+
+                    parent_id = ParentId::Import(binding);
+                    binding = nested_binding;
+                    warn_ambiguity |= nested_binding.warn_ambiguity;
+                }
+                if !is_ambiguity(binding, warn_ambiguity)
+                    && let Some(def_id) = binding.res().opt_def_id().and_then(|id| id.as_local())
+                {
+                    self.update_def(def_id, binding.vis.expect_local(), parent_id);
+                }
+            }
+        }
+    }
+
+    fn effective_vis_or_private(&mut self, parent_id: ParentId<'a>) -> EffectiveVisibility {
+        // Private nodes are only added to the table for caching, they could be added or removed at
+        // any moment without consequences, so we don't set `changed` to true when adding them.
+        *match parent_id {
+            ParentId::Def(def_id) => self
+                .def_effective_visibilities
+                .effective_vis_or_private(def_id, || self.r.private_vis_def(def_id)),
+            ParentId::Import(binding) => self
+                .import_effective_visibilities
+                .effective_vis_or_private(binding, || self.r.private_vis_import(binding)),
+        }
+    }
+
+    /// All effective visibilities for a node are larger or equal than private visibility
+    /// for that node (see `check_invariants` in middle/privacy.rs).
+    /// So if either parent or nominal visibility is the same as private visibility, then
+    /// `min(parent_vis, nominal_vis) <= private_vis`, and the update logic is guaranteed
+    /// to not update anything and we can skip it.
+    ///
+    /// We are checking this condition only if the correct value of private visibility is
+    /// cheaply available, otherwise it doesn't make sense performance-wise.
+    ///
+    /// `None` is returned if the update can be skipped,
+    /// and cheap private visibility is returned otherwise.
+    fn may_update(
+        &self,
+        nominal_vis: Visibility,
+        parent_id: ParentId<'_>,
+    ) -> Option<Option<Visibility>> {
+        match parent_id {
+            ParentId::Def(def_id) => (nominal_vis != self.current_private_vis
+                && self.r.tcx.local_visibility(def_id) != self.current_private_vis)
+                .then_some(Some(self.current_private_vis)),
+            ParentId::Import(_) => Some(None),
+        }
+    }
+
+    fn update_import(&mut self, binding: NameBinding<'a>, parent_id: ParentId<'a>) {
+        let nominal_vis = binding.vis.expect_local();
+        let Some(cheap_private_vis) = self.may_update(nominal_vis, parent_id) else { return };
+        let inherited_eff_vis = self.effective_vis_or_private(parent_id);
+        let tcx = self.r.tcx;
+        self.changed |= self.import_effective_visibilities.update(
+            binding,
+            Some(nominal_vis),
+            || cheap_private_vis.unwrap_or_else(|| self.r.private_vis_import(binding)),
+            inherited_eff_vis,
+            parent_id.level(),
+            tcx,
+        );
+    }
+
+    fn update_def(&mut self, def_id: LocalDefId, nominal_vis: Visibility, parent_id: ParentId<'a>) {
+        let Some(cheap_private_vis) = self.may_update(nominal_vis, parent_id) else { return };
+        let inherited_eff_vis = self.effective_vis_or_private(parent_id);
+        let tcx = self.r.tcx;
+        self.changed |= self.def_effective_visibilities.update(
+            def_id,
+            Some(nominal_vis),
+            || cheap_private_vis.unwrap_or_else(|| self.r.private_vis_def(def_id)),
+            inherited_eff_vis,
+            parent_id.level(),
+            tcx,
+        );
+    }
+
+    fn update_field(&mut self, def_id: LocalDefId, parent_id: LocalDefId) {
+        self.update_def(def_id, self.r.tcx.local_visibility(def_id), ParentId::Def(parent_id));
+    }
+}
+
+impl<'r, 'ast, 'tcx> Visitor<'ast> for EffectiveVisibilitiesVisitor<'ast, 'r, 'tcx> {
+    fn visit_item(&mut self, item: &'ast ast::Item) {
+        let def_id = self.r.local_def_id(item.id);
+        // Update effective visibilities of nested items.
+        // If it's a mod, also make the visitor walk all of its items
+        match item.kind {
+            // Resolved in rustc_privacy when types are available
+            ast::ItemKind::Impl(..) => return,
+
+            // Should be unreachable at this stage
+            ast::ItemKind::MacCall(..) => panic!(
+                "ast::ItemKind::MacCall encountered, this should not anymore appear at this stage"
+            ),
+
+            ast::ItemKind::Mod(..) => {
+                let prev_private_vis =
+                    mem::replace(&mut self.current_private_vis, Visibility::Restricted(def_id));
+                self.set_bindings_effective_visibilities(def_id);
+                visit::walk_item(self, item);
+                self.current_private_vis = prev_private_vis;
+            }
+
+            ast::ItemKind::Enum(EnumDef { ref variants }, _) => {
+                self.set_bindings_effective_visibilities(def_id);
+                for variant in variants {
+                    let variant_def_id = self.r.local_def_id(variant.id);
+                    for field in variant.data.fields() {
+                        self.update_field(self.r.local_def_id(field.id), variant_def_id);
+                    }
+                }
+            }
+
+            ast::ItemKind::Struct(ref def, _) | ast::ItemKind::Union(ref def, _) => {
+                for field in def.fields() {
+                    self.update_field(self.r.local_def_id(field.id), def_id);
+                }
+            }
+
+            ast::ItemKind::Trait(..) => {
+                self.set_bindings_effective_visibilities(def_id);
+            }
+
+            ast::ItemKind::ExternCrate(..)
+            | ast::ItemKind::Use(..)
+            | ast::ItemKind::Static(..)
+            | ast::ItemKind::Const(..)
+            | ast::ItemKind::GlobalAsm(..)
+            | ast::ItemKind::TyAlias(..)
+            | ast::ItemKind::TraitAlias(..)
+            | ast::ItemKind::MacroDef(..)
+            | ast::ItemKind::ForeignMod(..)
+            | ast::ItemKind::Fn(..)
+            | ast::ItemKind::Delegation(..) => return,
+        }
+    }
+}
diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs
new file mode 100644
index 00000000000..1fdb193e571
--- /dev/null
+++ b/compiler/rustc_resolve/src/errors.rs
@@ -0,0 +1,780 @@
+use rustc_macros::{Diagnostic, Subdiagnostic};
+use rustc_span::{
+    symbol::{Ident, Symbol},
+    Span,
+};
+
+use crate::{late::PatternSource, Res};
+
+#[derive(Diagnostic)]
+#[diag(resolve_parent_module_reset_for_binding, code = "E0637")]
+pub(crate) struct ParentModuleResetForBinding;
+
+#[derive(Diagnostic)]
+#[diag(resolve_ampersand_used_without_explicit_lifetime_name, code = "E0637")]
+#[note]
+pub(crate) struct AmpersandUsedWithoutExplicitLifetimeName(#[primary_span] pub(crate) Span);
+
+#[derive(Diagnostic)]
+#[diag(resolve_underscore_lifetime_name_cannot_be_used_here, code = "E0637")]
+#[note]
+pub(crate) struct UnderscoreLifetimeNameCannotBeUsedHere(#[primary_span] pub(crate) Span);
+
+#[derive(Diagnostic)]
+#[diag(resolve_crate_may_not_be_imported)]
+pub(crate) struct CrateMayNotBeImported(#[primary_span] pub(crate) Span);
+
+#[derive(Diagnostic)]
+#[diag(resolve_crate_root_imports_must_be_named_explicitly)]
+pub(crate) struct CrateRootNamesMustBeNamedExplicitly(#[primary_span] pub(crate) Span);
+
+#[derive(Diagnostic)]
+#[diag(resolve_crate_root_imports_must_be_named_explicitly)]
+pub(crate) struct ResolutionError(#[primary_span] pub(crate) Span);
+
+#[derive(Diagnostic)]
+#[diag(resolve_generic_params_from_outer_item, code = "E0401")]
+pub(crate) struct GenericParamsFromOuterItem {
+    #[primary_span]
+    #[label]
+    pub(crate) span: Span,
+    #[subdiagnostic]
+    pub(crate) label: Option<GenericParamsFromOuterItemLabel>,
+    #[label(resolve_refer_to_type_directly)]
+    pub(crate) refer_to_type_directly: Option<Span>,
+    #[subdiagnostic]
+    pub(crate) sugg: Option<GenericParamsFromOuterItemSugg>,
+}
+
+#[derive(Subdiagnostic)]
+pub(crate) enum GenericParamsFromOuterItemLabel {
+    #[label(resolve_generic_params_from_outer_item_self_ty_param)]
+    SelfTyParam(#[primary_span] Span),
+    #[label(resolve_generic_params_from_outer_item_self_ty_alias)]
+    SelfTyAlias(#[primary_span] Span),
+    #[label(resolve_generic_params_from_outer_item_ty_param)]
+    TyParam(#[primary_span] Span),
+    #[label(resolve_generic_params_from_outer_item_const_param)]
+    ConstParam(#[primary_span] Span),
+}
+
+#[derive(Subdiagnostic)]
+#[suggestion(resolve_suggestion, code = "{snippet}", applicability = "maybe-incorrect")]
+pub(crate) struct GenericParamsFromOuterItemSugg {
+    #[primary_span]
+    pub(crate) span: Span,
+    pub(crate) snippet: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(resolve_name_is_already_used_as_generic_parameter, code = "E0403")]
+pub(crate) struct NameAlreadyUsedInParameterList {
+    #[primary_span]
+    #[label]
+    pub(crate) span: Span,
+    #[label(resolve_first_use_of_name)]
+    pub(crate) first_use_span: Span,
+    pub(crate) name: Symbol,
+}
+
+#[derive(Diagnostic)]
+#[diag(resolve_method_not_member_of_trait, code = "E0407")]
+pub(crate) struct MethodNotMemberOfTrait {
+    #[primary_span]
+    #[label]
+    pub(crate) span: Span,
+    pub(crate) method: Ident,
+    pub(crate) trait_: String,
+    #[subdiagnostic]
+    pub(crate) sub: Option<AssociatedFnWithSimilarNameExists>,
+}
+
+#[derive(Subdiagnostic)]
+#[suggestion(
+    resolve_associated_fn_with_similar_name_exists,
+    code = "{candidate}",
+    applicability = "maybe-incorrect"
+)]
+pub(crate) struct AssociatedFnWithSimilarNameExists {
+    #[primary_span]
+    pub(crate) span: Span,
+    pub(crate) candidate: Symbol,
+}
+
+#[derive(Diagnostic)]
+#[diag(resolve_type_not_member_of_trait, code = "E0437")]
+pub(crate) struct TypeNotMemberOfTrait {
+    #[primary_span]
+    #[label]
+    pub(crate) span: Span,
+    pub(crate) type_: Ident,
+    pub(crate) trait_: String,
+    #[subdiagnostic]
+    pub(crate) sub: Option<AssociatedTypeWithSimilarNameExists>,
+}
+
+#[derive(Subdiagnostic)]
+#[suggestion(
+    resolve_associated_type_with_similar_name_exists,
+    code = "{candidate}",
+    applicability = "maybe-incorrect"
+)]
+pub(crate) struct AssociatedTypeWithSimilarNameExists {
+    #[primary_span]
+    pub(crate) span: Span,
+    pub(crate) candidate: Symbol,
+}
+
+#[derive(Diagnostic)]
+#[diag(resolve_const_not_member_of_trait, code = "E0438")]
+pub(crate) struct ConstNotMemberOfTrait {
+    #[primary_span]
+    #[label]
+    pub(crate) span: Span,
+    pub(crate) const_: Ident,
+    pub(crate) trait_: String,
+    #[subdiagnostic]
+    pub(crate) sub: Option<AssociatedConstWithSimilarNameExists>,
+}
+
+#[derive(Subdiagnostic)]
+#[suggestion(
+    resolve_associated_const_with_similar_name_exists,
+    code = "{candidate}",
+    applicability = "maybe-incorrect"
+)]
+pub(crate) struct AssociatedConstWithSimilarNameExists {
+    #[primary_span]
+    pub(crate) span: Span,
+    pub(crate) candidate: Symbol,
+}
+
+#[derive(Diagnostic)]
+#[diag(resolve_variable_bound_with_different_mode, code = "E0409")]
+pub(crate) struct VariableBoundWithDifferentMode {
+    #[primary_span]
+    #[label]
+    pub(crate) span: Span,
+    #[label(resolve_first_binding_span)]
+    pub(crate) first_binding_span: Span,
+    pub(crate) variable_name: Symbol,
+}
+
+#[derive(Diagnostic)]
+#[diag(resolve_ident_bound_more_than_once_in_parameter_list, code = "E0415")]
+pub(crate) struct IdentifierBoundMoreThanOnceInParameterList {
+    #[primary_span]
+    #[label]
+    pub(crate) span: Span,
+    pub(crate) identifier: Symbol,
+}
+
+#[derive(Diagnostic)]
+#[diag(resolve_ident_bound_more_than_once_in_same_pattern, code = "E0416")]
+pub(crate) struct IdentifierBoundMoreThanOnceInSamePattern {
+    #[primary_span]
+    #[label]
+    pub(crate) span: Span,
+    pub(crate) identifier: Symbol,
+}
+
+#[derive(Diagnostic)]
+#[diag(resolve_undeclared_label, code = "E0426")]
+pub(crate) struct UndeclaredLabel {
+    #[primary_span]
+    #[label]
+    pub(crate) span: Span,
+    pub(crate) name: Symbol,
+    #[subdiagnostic]
+    pub(crate) sub_reachable: Option<LabelWithSimilarNameReachable>,
+    #[subdiagnostic]
+    pub(crate) sub_reachable_suggestion: Option<TryUsingSimilarlyNamedLabel>,
+    #[subdiagnostic]
+    pub(crate) sub_unreachable: Option<UnreachableLabelWithSimilarNameExists>,
+}
+
+#[derive(Subdiagnostic)]
+#[label(resolve_label_with_similar_name_reachable)]
+pub(crate) struct LabelWithSimilarNameReachable(#[primary_span] pub(crate) Span);
+
+#[derive(Subdiagnostic)]
+#[suggestion(
+    resolve_try_using_similarly_named_label,
+    code = "{ident_name}",
+    applicability = "maybe-incorrect"
+)]
+pub(crate) struct TryUsingSimilarlyNamedLabel {
+    #[primary_span]
+    pub(crate) span: Span,
+    pub(crate) ident_name: Symbol,
+}
+
+#[derive(Subdiagnostic)]
+#[label(resolve_unreachable_label_with_similar_name_exists)]
+pub(crate) struct UnreachableLabelWithSimilarNameExists {
+    #[primary_span]
+    pub(crate) ident_span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(resolve_self_import_can_only_appear_once_in_the_list, code = "E0430")]
+pub(crate) struct SelfImportCanOnlyAppearOnceInTheList {
+    #[primary_span]
+    #[label]
+    pub(crate) span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(resolve_self_import_only_in_import_list_with_non_empty_prefix, code = "E0431")]
+pub(crate) struct SelfImportOnlyInImportListWithNonEmptyPrefix {
+    #[primary_span]
+    #[label]
+    pub(crate) span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(resolve_cannot_capture_dynamic_environment_in_fn_item, code = "E0434")]
+#[help]
+pub(crate) struct CannotCaptureDynamicEnvironmentInFnItem {
+    #[primary_span]
+    pub(crate) span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(resolve_attempt_to_use_non_constant_value_in_constant, code = "E0435")]
+pub(crate) struct AttemptToUseNonConstantValueInConstant<'a> {
+    #[primary_span]
+    pub(crate) span: Span,
+    #[subdiagnostic]
+    pub(crate) with: Option<AttemptToUseNonConstantValueInConstantWithSuggestion<'a>>,
+    #[subdiagnostic]
+    pub(crate) with_label: Option<AttemptToUseNonConstantValueInConstantLabelWithSuggestion>,
+    #[subdiagnostic]
+    pub(crate) without: Option<AttemptToUseNonConstantValueInConstantWithoutSuggestion<'a>>,
+}
+
+#[derive(Subdiagnostic)]
+#[suggestion(
+    resolve_attempt_to_use_non_constant_value_in_constant_with_suggestion,
+    code = "{suggestion} {ident}",
+    applicability = "maybe-incorrect"
+)]
+pub(crate) struct AttemptToUseNonConstantValueInConstantWithSuggestion<'a> {
+    #[primary_span]
+    pub(crate) span: Span,
+    pub(crate) ident: Ident,
+    pub(crate) suggestion: &'a str,
+    pub(crate) current: &'a str,
+}
+
+#[derive(Subdiagnostic)]
+#[label(resolve_attempt_to_use_non_constant_value_in_constant_label_with_suggestion)]
+pub(crate) struct AttemptToUseNonConstantValueInConstantLabelWithSuggestion {
+    #[primary_span]
+    pub(crate) span: Span,
+}
+
+#[derive(Subdiagnostic)]
+#[label(resolve_attempt_to_use_non_constant_value_in_constant_without_suggestion)]
+pub(crate) struct AttemptToUseNonConstantValueInConstantWithoutSuggestion<'a> {
+    #[primary_span]
+    pub(crate) ident_span: Span,
+    pub(crate) suggestion: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(resolve_self_imports_only_allowed_within, code = "E0429")]
+pub(crate) struct SelfImportsOnlyAllowedWithin {
+    #[primary_span]
+    pub(crate) span: Span,
+    #[subdiagnostic]
+    pub(crate) suggestion: Option<SelfImportsOnlyAllowedWithinSuggestion>,
+    #[subdiagnostic]
+    pub(crate) mpart_suggestion: Option<SelfImportsOnlyAllowedWithinMultipartSuggestion>,
+}
+
+#[derive(Subdiagnostic)]
+#[suggestion(
+    resolve_self_imports_only_allowed_within_suggestion,
+    code = "",
+    applicability = "machine-applicable"
+)]
+pub(crate) struct SelfImportsOnlyAllowedWithinSuggestion {
+    #[primary_span]
+    pub(crate) span: Span,
+}
+
+#[derive(Subdiagnostic)]
+#[multipart_suggestion(
+    resolve_self_imports_only_allowed_within_multipart_suggestion,
+    applicability = "machine-applicable"
+)]
+pub(crate) struct SelfImportsOnlyAllowedWithinMultipartSuggestion {
+    #[suggestion_part(code = "{{")]
+    pub(crate) multipart_start: Span,
+    #[suggestion_part(code = "}}")]
+    pub(crate) multipart_end: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(resolve_binding_shadows_something_unacceptable, code = "E0530")]
+pub(crate) struct BindingShadowsSomethingUnacceptable<'a> {
+    #[primary_span]
+    #[label]
+    pub(crate) span: Span,
+    pub(crate) shadowing_binding: PatternSource,
+    pub(crate) shadowed_binding: Res,
+    pub(crate) article: &'a str,
+    #[subdiagnostic]
+    pub(crate) sub_suggestion: Option<BindingShadowsSomethingUnacceptableSuggestion>,
+    #[label(resolve_label_shadowed_binding)]
+    pub(crate) shadowed_binding_span: Span,
+    pub(crate) participle: &'a str,
+    pub(crate) name: Symbol,
+}
+
+#[derive(Subdiagnostic)]
+#[suggestion(
+    resolve_binding_shadows_something_unacceptable_suggestion,
+    code = "{name}(..)",
+    applicability = "unspecified"
+)]
+pub(crate) struct BindingShadowsSomethingUnacceptableSuggestion {
+    #[primary_span]
+    pub(crate) span: Span,
+    pub(crate) name: Symbol,
+}
+
+#[derive(Diagnostic)]
+#[diag(resolve_forward_declared_generic_param, code = "E0128")]
+pub(crate) struct ForwardDeclaredGenericParam {
+    #[primary_span]
+    #[label]
+    pub(crate) span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(resolve_param_in_ty_of_const_param, code = "E0770")]
+pub(crate) struct ParamInTyOfConstParam {
+    #[primary_span]
+    #[label]
+    pub(crate) span: Span,
+    pub(crate) name: Symbol,
+    #[subdiagnostic]
+    pub(crate) param_kind: Option<ParamKindInTyOfConstParam>,
+}
+
+#[derive(Debug)]
+#[derive(Subdiagnostic)]
+pub(crate) enum ParamKindInTyOfConstParam {
+    #[note(resolve_type_param_in_ty_of_const_param)]
+    Type,
+    #[note(resolve_const_param_in_ty_of_const_param)]
+    Const,
+    #[note(resolve_lifetime_param_in_ty_of_const_param)]
+    Lifetime,
+}
+
+#[derive(Diagnostic)]
+#[diag(resolve_self_in_generic_param_default, code = "E0735")]
+pub(crate) struct SelfInGenericParamDefault {
+    #[primary_span]
+    #[label]
+    pub(crate) span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(resolve_param_in_non_trivial_anon_const)]
+pub(crate) struct ParamInNonTrivialAnonConst {
+    #[primary_span]
+    #[label]
+    pub(crate) span: Span,
+    pub(crate) name: Symbol,
+    #[subdiagnostic]
+    pub(crate) param_kind: ParamKindInNonTrivialAnonConst,
+    #[subdiagnostic]
+    pub(crate) help: Option<ParamInNonTrivialAnonConstHelp>,
+}
+
+#[derive(Subdiagnostic)]
+#[help(resolve_param_in_non_trivial_anon_const_help)]
+pub(crate) struct ParamInNonTrivialAnonConstHelp;
+
+#[derive(Debug)]
+#[derive(Subdiagnostic)]
+pub(crate) enum ParamKindInNonTrivialAnonConst {
+    #[note(resolve_type_param_in_non_trivial_anon_const)]
+    Type,
+    #[help(resolve_const_param_in_non_trivial_anon_const)]
+    Const { name: Symbol },
+    #[note(resolve_lifetime_param_in_non_trivial_anon_const)]
+    Lifetime,
+}
+
+#[derive(Diagnostic)]
+#[diag(resolve_unreachable_label, code = "E0767")]
+#[note]
+pub(crate) struct UnreachableLabel {
+    #[primary_span]
+    #[label]
+    pub(crate) span: Span,
+    pub(crate) name: Symbol,
+    #[label(resolve_label_definition_span)]
+    pub(crate) definition_span: Span,
+    #[subdiagnostic]
+    pub(crate) sub_suggestion: Option<UnreachableLabelSubSuggestion>,
+    #[subdiagnostic]
+    pub(crate) sub_suggestion_label: Option<UnreachableLabelSubLabel>,
+    #[subdiagnostic]
+    pub(crate) sub_unreachable_label: Option<UnreachableLabelSubLabelUnreachable>,
+}
+
+#[derive(Subdiagnostic)]
+#[suggestion(
+    resolve_unreachable_label_suggestion_use_similarly_named,
+    code = "{ident_name}",
+    applicability = "maybe-incorrect"
+)]
+pub(crate) struct UnreachableLabelSubSuggestion {
+    #[primary_span]
+    pub(crate) span: Span,
+    pub(crate) ident_name: Symbol,
+}
+
+#[derive(Subdiagnostic)]
+#[label(resolve_unreachable_label_similar_name_reachable)]
+pub(crate) struct UnreachableLabelSubLabel {
+    #[primary_span]
+    pub(crate) ident_span: Span,
+}
+
+#[derive(Subdiagnostic)]
+#[label(resolve_unreachable_label_similar_name_unreachable)]
+pub(crate) struct UnreachableLabelSubLabelUnreachable {
+    #[primary_span]
+    pub(crate) ident_span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(resolve_trait_impl_mismatch, code = "{code}")]
+pub(crate) struct TraitImplMismatch {
+    #[primary_span]
+    #[label]
+    pub(crate) span: Span,
+    pub(crate) name: Symbol,
+    pub(crate) kind: String,
+    #[label(resolve_label_trait_item)]
+    pub(crate) trait_item_span: Span,
+    pub(crate) trait_path: String,
+    pub(crate) code: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(resolve_invalid_asm_sym)]
+#[help]
+pub(crate) struct InvalidAsmSym {
+    #[primary_span]
+    #[label]
+    pub(crate) span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(resolve_lowercase_self)]
+pub(crate) struct LowercaseSelf {
+    #[primary_span]
+    #[suggestion(code = "Self", applicability = "maybe-incorrect", style = "short")]
+    pub(crate) span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(resolve_trait_impl_duplicate, code = "E0201")]
+pub(crate) struct TraitImplDuplicate {
+    #[primary_span]
+    #[label]
+    pub(crate) span: Span,
+    #[label(resolve_old_span_label)]
+    pub(crate) old_span: Span,
+    #[label(resolve_trait_item_span)]
+    pub(crate) trait_item_span: Span,
+    pub(crate) name: Symbol,
+}
+
+#[derive(Diagnostic)]
+#[diag(resolve_relative_2018)]
+pub(crate) struct Relative2018 {
+    #[primary_span]
+    pub(crate) span: Span,
+    #[suggestion(code = "crate::{path_str}", applicability = "maybe-incorrect")]
+    pub(crate) path_span: Span,
+    pub(crate) path_str: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(resolve_ancestor_only, code = "E0742")]
+pub(crate) struct AncestorOnly(#[primary_span] pub(crate) Span);
+
+#[derive(Diagnostic)]
+#[diag(resolve_expected_found, code = "E0577")]
+pub(crate) struct ExpectedFound {
+    #[primary_span]
+    #[label]
+    pub(crate) span: Span,
+    pub(crate) res: Res,
+    pub(crate) path_str: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(resolve_indeterminate, code = "E0578")]
+pub(crate) struct Indeterminate(#[primary_span] pub(crate) Span);
+
+#[derive(Diagnostic)]
+#[diag(resolve_tool_module_imported)]
+pub(crate) struct ToolModuleImported {
+    #[primary_span]
+    pub(crate) span: Span,
+    #[note]
+    pub(crate) import: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(resolve_module_only)]
+pub(crate) struct ModuleOnly(#[primary_span] pub(crate) Span);
+
+#[derive(Diagnostic, Default)]
+#[diag(resolve_macro_expected_found)]
+pub(crate) struct MacroExpectedFound<'a> {
+    #[primary_span]
+    pub(crate) span: Span,
+    pub(crate) found: &'a str,
+    pub(crate) expected: &'a str,
+    pub(crate) macro_path: &'a str,
+    #[subdiagnostic]
+    pub(crate) remove_surrounding_derive: Option<RemoveSurroundingDerive>,
+    #[subdiagnostic]
+    pub(crate) add_as_non_derive: Option<AddAsNonDerive<'a>>,
+}
+
+#[derive(Subdiagnostic)]
+#[help(resolve_remove_surrounding_derive)]
+pub(crate) struct RemoveSurroundingDerive {
+    #[primary_span]
+    pub(crate) span: Span,
+}
+
+#[derive(Subdiagnostic)]
+#[help(resolve_add_as_non_derive)]
+pub(crate) struct AddAsNonDerive<'a> {
+    pub(crate) macro_path: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(resolve_proc_macro_same_crate)]
+pub(crate) struct ProcMacroSameCrate {
+    #[primary_span]
+    pub(crate) span: Span,
+    #[help]
+    pub(crate) is_test: bool,
+}
+
+#[derive(Diagnostic)]
+#[diag(resolve_imported_crate)]
+pub(crate) struct CrateImported {
+    #[primary_span]
+    pub(crate) span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(resolve_macro_use_extern_crate_self)]
+pub(crate) struct MacroUseExternCrateSelf {
+    #[primary_span]
+    pub(crate) span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(resolve_accessible_unsure)]
+#[note]
+pub(crate) struct CfgAccessibleUnsure {
+    #[primary_span]
+    pub(crate) span: Span,
+}
+
+#[derive(Debug)]
+#[derive(Diagnostic)]
+#[diag(resolve_param_in_enum_discriminant)]
+pub(crate) struct ParamInEnumDiscriminant {
+    #[primary_span]
+    #[label]
+    pub(crate) span: Span,
+    pub(crate) name: Symbol,
+    #[subdiagnostic]
+    pub(crate) param_kind: ParamKindInEnumDiscriminant,
+}
+
+#[derive(Debug)]
+#[derive(Subdiagnostic)]
+pub(crate) enum ParamKindInEnumDiscriminant {
+    #[note(resolve_type_param_in_enum_discriminant)]
+    Type,
+    #[note(resolve_const_param_in_enum_discriminant)]
+    Const,
+    #[note(resolve_lifetime_param_in_enum_discriminant)]
+    Lifetime,
+}
+
+#[derive(Subdiagnostic)]
+#[label(resolve_change_import_binding)]
+pub(crate) struct ChangeImportBinding {
+    #[primary_span]
+    pub(crate) span: Span,
+}
+
+#[derive(Subdiagnostic)]
+#[suggestion(
+    resolve_change_import_binding,
+    code = "{suggestion}",
+    applicability = "maybe-incorrect"
+)]
+pub(crate) struct ChangeImportBindingSuggestion {
+    #[primary_span]
+    pub(crate) span: Span,
+    pub(crate) suggestion: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(resolve_imports_cannot_refer_to)]
+pub(crate) struct ImportsCannotReferTo<'a> {
+    #[primary_span]
+    pub(crate) span: Span,
+    pub(crate) what: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(resolve_cannot_find_ident_in_this_scope)]
+pub(crate) struct CannotFindIdentInThisScope<'a> {
+    #[primary_span]
+    pub(crate) span: Span,
+    pub(crate) expected: &'a str,
+    pub(crate) ident: Ident,
+}
+
+#[derive(Subdiagnostic)]
+#[note(resolve_explicit_unsafe_traits)]
+pub(crate) struct ExplicitUnsafeTraits {
+    #[primary_span]
+    pub(crate) span: Span,
+    pub(crate) ident: Ident,
+}
+
+#[derive(Subdiagnostic)]
+#[note(resolve_missing_macro_rules_name)]
+pub(crate) struct MaybeMissingMacroRulesName {
+    #[primary_span]
+    pub(crate) span: Span,
+}
+
+#[derive(Subdiagnostic)]
+#[help(resolve_added_macro_use)]
+pub(crate) struct AddedMacroUse;
+
+#[derive(Subdiagnostic)]
+#[suggestion(
+    resolve_consider_adding_a_derive,
+    code = "{suggestion}",
+    applicability = "maybe-incorrect"
+)]
+pub(crate) struct ConsiderAddingADerive {
+    #[primary_span]
+    pub(crate) span: Span,
+    pub(crate) suggestion: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(resolve_cannot_determine_import_resolution)]
+pub(crate) struct CannotDetermineImportResolution {
+    #[primary_span]
+    pub(crate) span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(resolve_cannot_determine_macro_resolution)]
+#[note]
+pub(crate) struct CannotDetermineMacroResolution {
+    #[primary_span]
+    pub(crate) span: Span,
+    pub(crate) kind: &'static str,
+    pub(crate) path: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(resolve_cannot_be_reexported_private, code = "E0364")]
+pub(crate) struct CannotBeReexportedPrivate {
+    #[primary_span]
+    pub(crate) span: Span,
+    pub(crate) ident: Ident,
+}
+
+#[derive(Diagnostic)]
+#[diag(resolve_cannot_be_reexported_crate_public, code = "E0364")]
+pub(crate) struct CannotBeReexportedCratePublic {
+    #[primary_span]
+    pub(crate) span: Span,
+    pub(crate) ident: Ident,
+}
+
+#[derive(Diagnostic)]
+#[diag(resolve_cannot_be_reexported_private, code = "E0365")]
+#[note(resolve_consider_declaring_with_pub)]
+pub(crate) struct CannotBeReexportedPrivateNS {
+    #[primary_span]
+    #[label(resolve_reexport_of_private)]
+    pub(crate) span: Span,
+    pub(crate) ident: Ident,
+}
+
+#[derive(Diagnostic)]
+#[diag(resolve_cannot_be_reexported_crate_public, code = "E0365")]
+#[note(resolve_consider_declaring_with_pub)]
+pub(crate) struct CannotBeReexportedCratePublicNS {
+    #[primary_span]
+    #[label(resolve_reexport_of_crate_public)]
+    pub(crate) span: Span,
+    pub(crate) ident: Ident,
+}
+
+#[derive(Subdiagnostic)]
+#[help(resolve_consider_adding_macro_export)]
+pub(crate) struct ConsiderAddingMacroExport {
+    #[primary_span]
+    pub(crate) span: Span,
+}
+
+#[derive(Subdiagnostic)]
+#[note(resolve_consider_marking_as_pub)]
+pub(crate) struct ConsiderMarkingAsPub {
+    #[primary_span]
+    pub(crate) span: Span,
+    pub(crate) ident: Ident,
+}
+
+#[derive(Diagnostic)]
+#[diag(resolve_cannot_glob_import_possible_crates)]
+pub(crate) struct CannotGlobImportAllCrates {
+    #[primary_span]
+    pub(crate) span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(resolve_items_in_traits_are_not_importable)]
+pub(crate) struct ItemsInTraitsAreNotImportable {
+    #[primary_span]
+    pub(crate) span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(resolve_is_not_directly_importable, code = "E0253")]
+pub(crate) struct IsNotDirectlyImportable {
+    #[primary_span]
+    #[label]
+    pub(crate) span: Span,
+    pub(crate) target: Ident,
+}
diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs
new file mode 100644
index 00000000000..7fb9db16e9c
--- /dev/null
+++ b/compiler/rustc_resolve/src/ident.rs
@@ -0,0 +1,1564 @@
+use rustc_ast::{self as ast, NodeId};
+use rustc_hir::def::{DefKind, Namespace, NonMacroAttrKind, PartialRes, PerNS};
+use rustc_middle::bug;
+use rustc_middle::ty;
+use rustc_session::lint::builtin::PROC_MACRO_DERIVE_RESOLUTION_FALLBACK;
+use rustc_session::lint::BuiltinLintDiagnostics;
+use rustc_span::def_id::LocalDefId;
+use rustc_span::hygiene::{ExpnId, ExpnKind, LocalExpnId, MacroKind, SyntaxContext};
+use rustc_span::symbol::{kw, Ident};
+use rustc_span::Span;
+
+use crate::errors::{ParamKindInEnumDiscriminant, ParamKindInNonTrivialAnonConst};
+use crate::late::{
+    ConstantHasGenerics, HasGenericParams, NoConstantGenericsReason, PathSource, Rib, RibKind,
+};
+use crate::macros::{sub_namespace_match, MacroRulesScope};
+use crate::BindingKey;
+use crate::{errors, AmbiguityError, AmbiguityErrorMisc, AmbiguityKind, Determinacy, Finalize};
+use crate::{ImportKind, LexicalScopeBinding, Module, ModuleKind, ModuleOrUniformRoot};
+use crate::{NameBinding, NameBindingKind, ParentScope, PathResult, PrivacyError, Res};
+use crate::{ResolutionError, Resolver, Scope, ScopeSet, Segment, ToNameBinding, Weak};
+
+use Determinacy::*;
+use Namespace::*;
+
+type Visibility = ty::Visibility<LocalDefId>;
+
+impl<'a, 'tcx> Resolver<'a, 'tcx> {
+    /// A generic scope visitor.
+    /// Visits scopes in order to resolve some identifier in them or perform other actions.
+    /// If the callback returns `Some` result, we stop visiting scopes and return it.
+    pub(crate) fn visit_scopes<T>(
+        &mut self,
+        scope_set: ScopeSet<'a>,
+        parent_scope: &ParentScope<'a>,
+        ctxt: SyntaxContext,
+        mut visitor: impl FnMut(
+            &mut Self,
+            Scope<'a>,
+            /*use_prelude*/ bool,
+            SyntaxContext,
+        ) -> Option<T>,
+    ) -> Option<T> {
+        // General principles:
+        // 1. Not controlled (user-defined) names should have higher priority than controlled names
+        //    built into the language or standard library. This way we can add new names into the
+        //    language or standard library without breaking user code.
+        // 2. "Closed set" below means new names cannot appear after the current resolution attempt.
+        // Places to search (in order of decreasing priority):
+        // (Type NS)
+        // 1. FIXME: Ribs (type parameters), there's no necessary infrastructure yet
+        //    (open set, not controlled).
+        // 2. Names in modules (both normal `mod`ules and blocks), loop through hygienic parents
+        //    (open, not controlled).
+        // 3. Extern prelude (open, the open part is from macro expansions, not controlled).
+        // 4. Tool modules (closed, controlled right now, but not in the future).
+        // 5. Standard library prelude (de-facto closed, controlled).
+        // 6. Language prelude (closed, controlled).
+        // (Value NS)
+        // 1. FIXME: Ribs (local variables), there's no necessary infrastructure yet
+        //    (open set, not controlled).
+        // 2. Names in modules (both normal `mod`ules and blocks), loop through hygienic parents
+        //    (open, not controlled).
+        // 3. Standard library prelude (de-facto closed, controlled).
+        // (Macro NS)
+        // 1-3. Derive helpers (open, not controlled). All ambiguities with other names
+        //    are currently reported as errors. They should be higher in priority than preludes
+        //    and probably even names in modules according to the "general principles" above. They
+        //    also should be subject to restricted shadowing because are effectively produced by
+        //    derives (you need to resolve the derive first to add helpers into scope), but they
+        //    should be available before the derive is expanded for compatibility.
+        //    It's mess in general, so we are being conservative for now.
+        // 1-3. `macro_rules` (open, not controlled), loop through `macro_rules` scopes. Have higher
+        //    priority than prelude macros, but create ambiguities with macros in modules.
+        // 1-3. Names in modules (both normal `mod`ules and blocks), loop through hygienic parents
+        //    (open, not controlled). Have higher priority than prelude macros, but create
+        //    ambiguities with `macro_rules`.
+        // 4. `macro_use` prelude (open, the open part is from macro expansions, not controlled).
+        // 4a. User-defined prelude from macro-use
+        //    (open, the open part is from macro expansions, not controlled).
+        // 4b. "Standard library prelude" part implemented through `macro-use` (closed, controlled).
+        // 4c. Standard library prelude (de-facto closed, controlled).
+        // 6. Language prelude: builtin attributes (closed, controlled).
+
+        let rust_2015 = ctxt.edition().is_rust_2015();
+        let (ns, macro_kind, is_absolute_path) = match scope_set {
+            ScopeSet::All(ns) => (ns, None, false),
+            ScopeSet::AbsolutePath(ns) => (ns, None, true),
+            ScopeSet::Macro(macro_kind) => (MacroNS, Some(macro_kind), false),
+            ScopeSet::Late(ns, ..) => (ns, None, false),
+        };
+        let module = match scope_set {
+            // Start with the specified module.
+            ScopeSet::Late(_, module, _) => module,
+            // Jump out of trait or enum modules, they do not act as scopes.
+            _ => parent_scope.module.nearest_item_scope(),
+        };
+        let mut scope = match ns {
+            _ if is_absolute_path => Scope::CrateRoot,
+            TypeNS | ValueNS => Scope::Module(module, None),
+            MacroNS => Scope::DeriveHelpers(parent_scope.expansion),
+        };
+        let mut ctxt = ctxt.normalize_to_macros_2_0();
+        let mut use_prelude = !module.no_implicit_prelude;
+
+        loop {
+            let visit = match scope {
+                // Derive helpers are not in scope when resolving derives in the same container.
+                Scope::DeriveHelpers(expn_id) => {
+                    !(expn_id == parent_scope.expansion && macro_kind == Some(MacroKind::Derive))
+                }
+                Scope::DeriveHelpersCompat => true,
+                Scope::MacroRules(macro_rules_scope) => {
+                    // Use "path compression" on `macro_rules` scope chains. This is an optimization
+                    // used to avoid long scope chains, see the comments on `MacroRulesScopeRef`.
+                    // As another consequence of this optimization visitors never observe invocation
+                    // scopes for macros that were already expanded.
+                    while let MacroRulesScope::Invocation(invoc_id) = macro_rules_scope.get() {
+                        if let Some(next_scope) = self.output_macro_rules_scopes.get(&invoc_id) {
+                            macro_rules_scope.set(next_scope.get());
+                        } else {
+                            break;
+                        }
+                    }
+                    true
+                }
+                Scope::CrateRoot => true,
+                Scope::Module(..) => true,
+                Scope::MacroUsePrelude => use_prelude || rust_2015,
+                Scope::BuiltinAttrs => true,
+                Scope::ExternPrelude => use_prelude || is_absolute_path,
+                Scope::ToolPrelude => use_prelude,
+                Scope::StdLibPrelude => use_prelude || ns == MacroNS,
+                Scope::BuiltinTypes => true,
+            };
+
+            if visit {
+                if let break_result @ Some(..) = visitor(self, scope, use_prelude, ctxt) {
+                    return break_result;
+                }
+            }
+
+            scope = match scope {
+                Scope::DeriveHelpers(LocalExpnId::ROOT) => Scope::DeriveHelpersCompat,
+                Scope::DeriveHelpers(expn_id) => {
+                    // Derive helpers are not visible to code generated by bang or derive macros.
+                    let expn_data = expn_id.expn_data();
+                    match expn_data.kind {
+                        ExpnKind::Root
+                        | ExpnKind::Macro(MacroKind::Bang | MacroKind::Derive, _) => {
+                            Scope::DeriveHelpersCompat
+                        }
+                        _ => Scope::DeriveHelpers(expn_data.parent.expect_local()),
+                    }
+                }
+                Scope::DeriveHelpersCompat => Scope::MacroRules(parent_scope.macro_rules),
+                Scope::MacroRules(macro_rules_scope) => match macro_rules_scope.get() {
+                    MacroRulesScope::Binding(binding) => {
+                        Scope::MacroRules(binding.parent_macro_rules_scope)
+                    }
+                    MacroRulesScope::Invocation(invoc_id) => {
+                        Scope::MacroRules(self.invocation_parent_scopes[&invoc_id].macro_rules)
+                    }
+                    MacroRulesScope::Empty => Scope::Module(module, None),
+                },
+                Scope::CrateRoot => match ns {
+                    TypeNS => {
+                        ctxt.adjust(ExpnId::root());
+                        Scope::ExternPrelude
+                    }
+                    ValueNS | MacroNS => break,
+                },
+                Scope::Module(module, prev_lint_id) => {
+                    use_prelude = !module.no_implicit_prelude;
+                    let derive_fallback_lint_id = match scope_set {
+                        ScopeSet::Late(.., lint_id) => lint_id,
+                        _ => None,
+                    };
+                    match self.hygienic_lexical_parent(module, &mut ctxt, derive_fallback_lint_id) {
+                        Some((parent_module, lint_id)) => {
+                            Scope::Module(parent_module, lint_id.or(prev_lint_id))
+                        }
+                        None => {
+                            ctxt.adjust(ExpnId::root());
+                            match ns {
+                                TypeNS => Scope::ExternPrelude,
+                                ValueNS => Scope::StdLibPrelude,
+                                MacroNS => Scope::MacroUsePrelude,
+                            }
+                        }
+                    }
+                }
+                Scope::MacroUsePrelude => Scope::StdLibPrelude,
+                Scope::BuiltinAttrs => break, // nowhere else to search
+                Scope::ExternPrelude if is_absolute_path => break,
+                Scope::ExternPrelude => Scope::ToolPrelude,
+                Scope::ToolPrelude => Scope::StdLibPrelude,
+                Scope::StdLibPrelude => match ns {
+                    TypeNS => Scope::BuiltinTypes,
+                    ValueNS => break, // nowhere else to search
+                    MacroNS => Scope::BuiltinAttrs,
+                },
+                Scope::BuiltinTypes => break, // nowhere else to search
+            };
+        }
+
+        None
+    }
+
+    fn hygienic_lexical_parent(
+        &mut self,
+        module: Module<'a>,
+        ctxt: &mut SyntaxContext,
+        derive_fallback_lint_id: Option<NodeId>,
+    ) -> Option<(Module<'a>, Option<NodeId>)> {
+        if !module.expansion.outer_expn_is_descendant_of(*ctxt) {
+            return Some((self.expn_def_scope(ctxt.remove_mark()), None));
+        }
+
+        if let ModuleKind::Block = module.kind {
+            return Some((module.parent.unwrap().nearest_item_scope(), None));
+        }
+
+        // We need to support the next case under a deprecation warning
+        // ```
+        // struct MyStruct;
+        // ---- begin: this comes from a proc macro derive
+        // mod implementation_details {
+        //     // Note that `MyStruct` is not in scope here.
+        //     impl SomeTrait for MyStruct { ... }
+        // }
+        // ---- end
+        // ```
+        // So we have to fall back to the module's parent during lexical resolution in this case.
+        if derive_fallback_lint_id.is_some() {
+            if let Some(parent) = module.parent {
+                // Inner module is inside the macro, parent module is outside of the macro.
+                if module.expansion != parent.expansion
+                    && module.expansion.is_descendant_of(parent.expansion)
+                {
+                    // The macro is a proc macro derive
+                    if let Some(def_id) = module.expansion.expn_data().macro_def_id {
+                        let ext = &self.get_macro_by_def_id(def_id).ext;
+                        if ext.builtin_name.is_none()
+                            && ext.macro_kind() == MacroKind::Derive
+                            && parent.expansion.outer_expn_is_descendant_of(*ctxt)
+                        {
+                            return Some((parent, derive_fallback_lint_id));
+                        }
+                    }
+                }
+            }
+        }
+
+        None
+    }
+
+    /// This resolves the identifier `ident` in the namespace `ns` in the current lexical scope.
+    /// More specifically, we proceed up the hierarchy of scopes and return the binding for
+    /// `ident` in the first scope that defines it (or None if no scopes define it).
+    ///
+    /// A block's items are above its local variables in the scope hierarchy, regardless of where
+    /// the items are defined in the block. For example,
+    /// ```rust
+    /// fn f() {
+    ///    g(); // Since there are no local variables in scope yet, this resolves to the item.
+    ///    let g = || {};
+    ///    fn g() {}
+    ///    g(); // This resolves to the local variable `g` since it shadows the item.
+    /// }
+    /// ```
+    ///
+    /// Invariant: This must only be called during main resolution, not during
+    /// import resolution.
+    #[instrument(level = "debug", skip(self, ribs))]
+    pub(crate) fn resolve_ident_in_lexical_scope(
+        &mut self,
+        mut ident: Ident,
+        ns: Namespace,
+        parent_scope: &ParentScope<'a>,
+        finalize: Option<Finalize>,
+        ribs: &[Rib<'a>],
+        ignore_binding: Option<NameBinding<'a>>,
+    ) -> Option<LexicalScopeBinding<'a>> {
+        assert!(ns == TypeNS || ns == ValueNS);
+        let orig_ident = ident;
+        if ident.name == kw::Empty {
+            return Some(LexicalScopeBinding::Res(Res::Err));
+        }
+        let (general_span, normalized_span) = if ident.name == kw::SelfUpper {
+            // FIXME(jseyfried) improve `Self` hygiene
+            let empty_span = ident.span.with_ctxt(SyntaxContext::root());
+            (empty_span, empty_span)
+        } else if ns == TypeNS {
+            let normalized_span = ident.span.normalize_to_macros_2_0();
+            (normalized_span, normalized_span)
+        } else {
+            (ident.span.normalize_to_macro_rules(), ident.span.normalize_to_macros_2_0())
+        };
+        ident.span = general_span;
+        let normalized_ident = Ident { span: normalized_span, ..ident };
+
+        // Walk backwards up the ribs in scope.
+        let mut module = self.graph_root;
+        for i in (0..ribs.len()).rev() {
+            debug!("walk rib\n{:?}", ribs[i].bindings);
+            // Use the rib kind to determine whether we are resolving parameters
+            // (macro 2.0 hygiene) or local variables (`macro_rules` hygiene).
+            let rib_ident = if ribs[i].kind.contains_params() { normalized_ident } else { ident };
+            if let Some((original_rib_ident_def, res)) = ribs[i].bindings.get_key_value(&rib_ident)
+            {
+                // The ident resolves to a type parameter or local variable.
+                return Some(LexicalScopeBinding::Res(self.validate_res_from_ribs(
+                    i,
+                    rib_ident,
+                    *res,
+                    finalize.map(|finalize| finalize.path_span),
+                    *original_rib_ident_def,
+                    ribs,
+                )));
+            }
+
+            module = match ribs[i].kind {
+                RibKind::Module(module) => module,
+                RibKind::MacroDefinition(def) if def == self.macro_def(ident.span.ctxt()) => {
+                    // If an invocation of this macro created `ident`, give up on `ident`
+                    // and switch to `ident`'s source from the macro definition.
+                    ident.span.remove_mark();
+                    continue;
+                }
+                _ => continue,
+            };
+
+            match module.kind {
+                ModuleKind::Block => {} // We can see through blocks
+                _ => break,
+            }
+
+            let item = self.resolve_ident_in_module_unadjusted(
+                ModuleOrUniformRoot::Module(module),
+                ident,
+                ns,
+                parent_scope,
+                finalize,
+                ignore_binding,
+            );
+            if let Ok(binding) = item {
+                // The ident resolves to an item.
+                return Some(LexicalScopeBinding::Item(binding));
+            }
+        }
+        self.early_resolve_ident_in_lexical_scope(
+            orig_ident,
+            ScopeSet::Late(ns, module, finalize.map(|finalize| finalize.node_id)),
+            parent_scope,
+            finalize,
+            finalize.is_some(),
+            ignore_binding,
+        )
+        .ok()
+        .map(LexicalScopeBinding::Item)
+    }
+
+    /// Resolve an identifier in lexical scope.
+    /// This is a variation of `fn resolve_ident_in_lexical_scope` that can be run during
+    /// expansion and import resolution (perhaps they can be merged in the future).
+    /// The function is used for resolving initial segments of macro paths (e.g., `foo` in
+    /// `foo::bar!();` or `foo!();`) and also for import paths on 2018 edition.
+    #[instrument(level = "debug", skip(self))]
+    pub(crate) fn early_resolve_ident_in_lexical_scope(
+        &mut self,
+        orig_ident: Ident,
+        scope_set: ScopeSet<'a>,
+        parent_scope: &ParentScope<'a>,
+        finalize: Option<Finalize>,
+        force: bool,
+        ignore_binding: Option<NameBinding<'a>>,
+    ) -> Result<NameBinding<'a>, Determinacy> {
+        bitflags::bitflags! {
+            #[derive(Clone, Copy)]
+            struct Flags: u8 {
+                const MACRO_RULES          = 1 << 0;
+                const MODULE               = 1 << 1;
+                const MISC_SUGGEST_CRATE   = 1 << 2;
+                const MISC_SUGGEST_SELF    = 1 << 3;
+                const MISC_FROM_PRELUDE    = 1 << 4;
+            }
+        }
+
+        assert!(force || finalize.is_none()); // `finalize` implies `force`
+
+        // Make sure `self`, `super` etc produce an error when passed to here.
+        if orig_ident.is_path_segment_keyword() {
+            return Err(Determinacy::Determined);
+        }
+
+        let (ns, macro_kind) = match scope_set {
+            ScopeSet::All(ns) => (ns, None),
+            ScopeSet::AbsolutePath(ns) => (ns, None),
+            ScopeSet::Macro(macro_kind) => (MacroNS, Some(macro_kind)),
+            ScopeSet::Late(ns, ..) => (ns, None),
+        };
+
+        // This is *the* result, resolution from the scope closest to the resolved identifier.
+        // However, sometimes this result is "weak" because it comes from a glob import or
+        // a macro expansion, and in this case it cannot shadow names from outer scopes, e.g.
+        // mod m { ... } // solution in outer scope
+        // {
+        //     use prefix::*; // imports another `m` - innermost solution
+        //                    // weak, cannot shadow the outer `m`, need to report ambiguity error
+        //     m::mac!();
+        // }
+        // So we have to save the innermost solution and continue searching in outer scopes
+        // to detect potential ambiguities.
+        let mut innermost_result: Option<(NameBinding<'_>, Flags)> = None;
+        let mut determinacy = Determinacy::Determined;
+
+        // Go through all the scopes and try to resolve the name.
+        let break_result = self.visit_scopes(
+            scope_set,
+            parent_scope,
+            orig_ident.span.ctxt(),
+            |this, scope, use_prelude, ctxt| {
+                let ident = Ident::new(orig_ident.name, orig_ident.span.with_ctxt(ctxt));
+                let result = match scope {
+                    Scope::DeriveHelpers(expn_id) => {
+                        if let Some(binding) = this.helper_attrs.get(&expn_id).and_then(|attrs| {
+                            attrs.iter().rfind(|(i, _)| ident == *i).map(|(_, binding)| *binding)
+                        }) {
+                            Ok((binding, Flags::empty()))
+                        } else {
+                            Err(Determinacy::Determined)
+                        }
+                    }
+                    Scope::DeriveHelpersCompat => {
+                        // FIXME: Try running this logic eariler, to allocate name bindings for
+                        // legacy derive helpers when creating an attribute invocation with
+                        // following derives. Legacy derive helpers are not common, so it shouldn't
+                        // affect performance. It should also allow to remove the `derives`
+                        // component from `ParentScope`.
+                        let mut result = Err(Determinacy::Determined);
+                        for derive in parent_scope.derives {
+                            let parent_scope = &ParentScope { derives: &[], ..*parent_scope };
+                            match this.resolve_macro_path(
+                                derive,
+                                Some(MacroKind::Derive),
+                                parent_scope,
+                                true,
+                                force,
+                            ) {
+                                Ok((Some(ext), _)) => {
+                                    if ext.helper_attrs.contains(&ident.name) {
+                                        let binding = (
+                                            Res::NonMacroAttr(NonMacroAttrKind::DeriveHelperCompat),
+                                            Visibility::Public,
+                                            derive.span,
+                                            LocalExpnId::ROOT,
+                                        )
+                                            .to_name_binding(this.arenas);
+                                        result = Ok((binding, Flags::empty()));
+                                        break;
+                                    }
+                                }
+                                Ok(_) | Err(Determinacy::Determined) => {}
+                                Err(Determinacy::Undetermined) => {
+                                    result = Err(Determinacy::Undetermined)
+                                }
+                            }
+                        }
+                        result
+                    }
+                    Scope::MacroRules(macro_rules_scope) => match macro_rules_scope.get() {
+                        MacroRulesScope::Binding(macro_rules_binding)
+                            if ident == macro_rules_binding.ident =>
+                        {
+                            Ok((macro_rules_binding.binding, Flags::MACRO_RULES))
+                        }
+                        MacroRulesScope::Invocation(_) => Err(Determinacy::Undetermined),
+                        _ => Err(Determinacy::Determined),
+                    },
+                    Scope::CrateRoot => {
+                        let root_ident = Ident::new(kw::PathRoot, ident.span);
+                        let root_module = this.resolve_crate_root(root_ident);
+                        let binding = this.resolve_ident_in_module_ext(
+                            ModuleOrUniformRoot::Module(root_module),
+                            ident,
+                            ns,
+                            parent_scope,
+                            finalize,
+                            ignore_binding,
+                        );
+                        match binding {
+                            Ok(binding) => Ok((binding, Flags::MODULE | Flags::MISC_SUGGEST_CRATE)),
+                            Err((Determinacy::Undetermined, Weak::No)) => {
+                                return Some(Err(Determinacy::determined(force)));
+                            }
+                            Err((Determinacy::Undetermined, Weak::Yes)) => {
+                                Err(Determinacy::Undetermined)
+                            }
+                            Err((Determinacy::Determined, _)) => Err(Determinacy::Determined),
+                        }
+                    }
+                    Scope::Module(module, derive_fallback_lint_id) => {
+                        let adjusted_parent_scope = &ParentScope { module, ..*parent_scope };
+                        let binding = this.resolve_ident_in_module_unadjusted_ext(
+                            ModuleOrUniformRoot::Module(module),
+                            ident,
+                            ns,
+                            adjusted_parent_scope,
+                            !matches!(scope_set, ScopeSet::Late(..)),
+                            finalize,
+                            ignore_binding,
+                        );
+                        match binding {
+                            Ok(binding) => {
+                                if let Some(lint_id) = derive_fallback_lint_id {
+                                    this.lint_buffer.buffer_lint_with_diagnostic(
+                                        PROC_MACRO_DERIVE_RESOLUTION_FALLBACK,
+                                        lint_id,
+                                        orig_ident.span,
+                                        format!(
+                                            "cannot find {} `{}` in this scope",
+                                            ns.descr(),
+                                            ident
+                                        ),
+                                        BuiltinLintDiagnostics::ProcMacroDeriveResolutionFallback(
+                                            orig_ident.span,
+                                        ),
+                                    );
+                                }
+                                let misc_flags = if module == this.graph_root {
+                                    Flags::MISC_SUGGEST_CRATE
+                                } else if module.is_normal() {
+                                    Flags::MISC_SUGGEST_SELF
+                                } else {
+                                    Flags::empty()
+                                };
+                                Ok((binding, Flags::MODULE | misc_flags))
+                            }
+                            Err((Determinacy::Undetermined, Weak::No)) => {
+                                return Some(Err(Determinacy::determined(force)));
+                            }
+                            Err((Determinacy::Undetermined, Weak::Yes)) => {
+                                Err(Determinacy::Undetermined)
+                            }
+                            Err((Determinacy::Determined, _)) => Err(Determinacy::Determined),
+                        }
+                    }
+                    Scope::MacroUsePrelude => {
+                        match this.macro_use_prelude.get(&ident.name).cloned() {
+                            Some(binding) => Ok((binding, Flags::MISC_FROM_PRELUDE)),
+                            None => Err(Determinacy::determined(
+                                this.graph_root.unexpanded_invocations.borrow().is_empty(),
+                            )),
+                        }
+                    }
+                    Scope::BuiltinAttrs => match this.builtin_attrs_bindings.get(&ident.name) {
+                        Some(binding) => Ok((*binding, Flags::empty())),
+                        None => Err(Determinacy::Determined),
+                    },
+                    Scope::ExternPrelude => {
+                        match this.extern_prelude_get(ident, finalize.is_some()) {
+                            Some(binding) => Ok((binding, Flags::empty())),
+                            None => Err(Determinacy::determined(
+                                this.graph_root.unexpanded_invocations.borrow().is_empty(),
+                            )),
+                        }
+                    }
+                    Scope::ToolPrelude => match this.registered_tool_bindings.get(&ident) {
+                        Some(binding) => Ok((*binding, Flags::empty())),
+                        None => Err(Determinacy::Determined),
+                    },
+                    Scope::StdLibPrelude => {
+                        let mut result = Err(Determinacy::Determined);
+                        if let Some(prelude) = this.prelude {
+                            if let Ok(binding) = this.resolve_ident_in_module_unadjusted(
+                                ModuleOrUniformRoot::Module(prelude),
+                                ident,
+                                ns,
+                                parent_scope,
+                                None,
+                                ignore_binding,
+                            ) {
+                                if use_prelude || this.is_builtin_macro(binding.res()) {
+                                    result = Ok((binding, Flags::MISC_FROM_PRELUDE));
+                                }
+                            }
+                        }
+                        result
+                    }
+                    Scope::BuiltinTypes => match this.builtin_types_bindings.get(&ident.name) {
+                        Some(binding) => Ok((*binding, Flags::empty())),
+                        None => Err(Determinacy::Determined),
+                    },
+                };
+
+                match result {
+                    Ok((binding, flags))
+                        if sub_namespace_match(binding.macro_kind(), macro_kind) =>
+                    {
+                        if finalize.is_none() || matches!(scope_set, ScopeSet::Late(..)) {
+                            return Some(Ok(binding));
+                        }
+
+                        if let Some((innermost_binding, innermost_flags)) = innermost_result {
+                            // Found another solution, if the first one was "weak", report an error.
+                            let (res, innermost_res) = (binding.res(), innermost_binding.res());
+                            if res != innermost_res {
+                                let is_builtin = |res| {
+                                    matches!(res, Res::NonMacroAttr(NonMacroAttrKind::Builtin(..)))
+                                };
+                                let derive_helper =
+                                    Res::NonMacroAttr(NonMacroAttrKind::DeriveHelper);
+                                let derive_helper_compat =
+                                    Res::NonMacroAttr(NonMacroAttrKind::DeriveHelperCompat);
+
+                                let ambiguity_error_kind = if is_builtin(innermost_res)
+                                    || is_builtin(res)
+                                {
+                                    Some(AmbiguityKind::BuiltinAttr)
+                                } else if innermost_res == derive_helper_compat
+                                    || res == derive_helper_compat && innermost_res != derive_helper
+                                {
+                                    Some(AmbiguityKind::DeriveHelper)
+                                } else if innermost_flags.contains(Flags::MACRO_RULES)
+                                    && flags.contains(Flags::MODULE)
+                                    && !this.disambiguate_macro_rules_vs_modularized(
+                                        innermost_binding,
+                                        binding,
+                                    )
+                                    || flags.contains(Flags::MACRO_RULES)
+                                        && innermost_flags.contains(Flags::MODULE)
+                                        && !this.disambiguate_macro_rules_vs_modularized(
+                                            binding,
+                                            innermost_binding,
+                                        )
+                                {
+                                    Some(AmbiguityKind::MacroRulesVsModularized)
+                                } else if innermost_binding.is_glob_import() {
+                                    Some(AmbiguityKind::GlobVsOuter)
+                                } else if innermost_binding
+                                    .may_appear_after(parent_scope.expansion, binding)
+                                {
+                                    Some(AmbiguityKind::MoreExpandedVsOuter)
+                                } else {
+                                    None
+                                };
+                                if let Some(kind) = ambiguity_error_kind {
+                                    let misc = |f: Flags| {
+                                        if f.contains(Flags::MISC_SUGGEST_CRATE) {
+                                            AmbiguityErrorMisc::SuggestCrate
+                                        } else if f.contains(Flags::MISC_SUGGEST_SELF) {
+                                            AmbiguityErrorMisc::SuggestSelf
+                                        } else if f.contains(Flags::MISC_FROM_PRELUDE) {
+                                            AmbiguityErrorMisc::FromPrelude
+                                        } else {
+                                            AmbiguityErrorMisc::None
+                                        }
+                                    };
+                                    this.ambiguity_errors.push(AmbiguityError {
+                                        kind,
+                                        ident: orig_ident,
+                                        b1: innermost_binding,
+                                        b2: binding,
+                                        warning: false,
+                                        misc1: misc(innermost_flags),
+                                        misc2: misc(flags),
+                                    });
+                                    return Some(Ok(innermost_binding));
+                                }
+                            }
+                        } else {
+                            // Found the first solution.
+                            innermost_result = Some((binding, flags));
+                        }
+                    }
+                    Ok(..) | Err(Determinacy::Determined) => {}
+                    Err(Determinacy::Undetermined) => determinacy = Determinacy::Undetermined,
+                }
+
+                None
+            },
+        );
+
+        if let Some(break_result) = break_result {
+            return break_result;
+        }
+
+        // The first found solution was the only one, return it.
+        if let Some((binding, _)) = innermost_result {
+            return Ok(binding);
+        }
+
+        Err(Determinacy::determined(determinacy == Determinacy::Determined || force))
+    }
+
+    #[instrument(level = "debug", skip(self))]
+    pub(crate) fn maybe_resolve_ident_in_module(
+        &mut self,
+        module: ModuleOrUniformRoot<'a>,
+        ident: Ident,
+        ns: Namespace,
+        parent_scope: &ParentScope<'a>,
+    ) -> Result<NameBinding<'a>, Determinacy> {
+        self.resolve_ident_in_module_ext(module, ident, ns, parent_scope, None, None)
+            .map_err(|(determinacy, _)| determinacy)
+    }
+
+    #[instrument(level = "debug", skip(self))]
+    pub(crate) fn resolve_ident_in_module(
+        &mut self,
+        module: ModuleOrUniformRoot<'a>,
+        ident: Ident,
+        ns: Namespace,
+        parent_scope: &ParentScope<'a>,
+        finalize: Option<Finalize>,
+        ignore_binding: Option<NameBinding<'a>>,
+    ) -> Result<NameBinding<'a>, Determinacy> {
+        self.resolve_ident_in_module_ext(module, ident, ns, parent_scope, finalize, ignore_binding)
+            .map_err(|(determinacy, _)| determinacy)
+    }
+
+    #[instrument(level = "debug", skip(self))]
+    fn resolve_ident_in_module_ext(
+        &mut self,
+        module: ModuleOrUniformRoot<'a>,
+        mut ident: Ident,
+        ns: Namespace,
+        parent_scope: &ParentScope<'a>,
+        finalize: Option<Finalize>,
+        ignore_binding: Option<NameBinding<'a>>,
+    ) -> Result<NameBinding<'a>, (Determinacy, Weak)> {
+        let tmp_parent_scope;
+        let mut adjusted_parent_scope = parent_scope;
+        match module {
+            ModuleOrUniformRoot::Module(m) => {
+                if let Some(def) = ident.span.normalize_to_macros_2_0_and_adjust(m.expansion) {
+                    tmp_parent_scope =
+                        ParentScope { module: self.expn_def_scope(def), ..*parent_scope };
+                    adjusted_parent_scope = &tmp_parent_scope;
+                }
+            }
+            ModuleOrUniformRoot::ExternPrelude => {
+                ident.span.normalize_to_macros_2_0_and_adjust(ExpnId::root());
+            }
+            ModuleOrUniformRoot::CrateRootAndExternPrelude | ModuleOrUniformRoot::CurrentScope => {
+                // No adjustments
+            }
+        }
+        self.resolve_ident_in_module_unadjusted_ext(
+            module,
+            ident,
+            ns,
+            adjusted_parent_scope,
+            false,
+            finalize,
+            ignore_binding,
+        )
+    }
+
+    #[instrument(level = "debug", skip(self))]
+    fn resolve_ident_in_module_unadjusted(
+        &mut self,
+        module: ModuleOrUniformRoot<'a>,
+        ident: Ident,
+        ns: Namespace,
+        parent_scope: &ParentScope<'a>,
+        finalize: Option<Finalize>,
+        ignore_binding: Option<NameBinding<'a>>,
+    ) -> Result<NameBinding<'a>, Determinacy> {
+        self.resolve_ident_in_module_unadjusted_ext(
+            module,
+            ident,
+            ns,
+            parent_scope,
+            false,
+            finalize,
+            ignore_binding,
+        )
+        .map_err(|(determinacy, _)| determinacy)
+    }
+
+    /// Attempts to resolve `ident` in namespaces `ns` of `module`.
+    /// Invariant: if `finalize` is `Some`, expansion and import resolution must be complete.
+    #[instrument(level = "debug", skip(self))]
+    fn resolve_ident_in_module_unadjusted_ext(
+        &mut self,
+        module: ModuleOrUniformRoot<'a>,
+        ident: Ident,
+        ns: Namespace,
+        parent_scope: &ParentScope<'a>,
+        restricted_shadowing: bool,
+        finalize: Option<Finalize>,
+        // This binding should be ignored during in-module resolution, so that we don't get
+        // "self-confirming" import resolutions during import validation and checking.
+        ignore_binding: Option<NameBinding<'a>>,
+    ) -> Result<NameBinding<'a>, (Determinacy, Weak)> {
+        let module = match module {
+            ModuleOrUniformRoot::Module(module) => module,
+            ModuleOrUniformRoot::CrateRootAndExternPrelude => {
+                assert!(!restricted_shadowing);
+                let binding = self.early_resolve_ident_in_lexical_scope(
+                    ident,
+                    ScopeSet::AbsolutePath(ns),
+                    parent_scope,
+                    finalize,
+                    finalize.is_some(),
+                    ignore_binding,
+                );
+                return binding.map_err(|determinacy| (determinacy, Weak::No));
+            }
+            ModuleOrUniformRoot::ExternPrelude => {
+                assert!(!restricted_shadowing);
+                return if ns != TypeNS {
+                    Err((Determined, Weak::No))
+                } else if let Some(binding) = self.extern_prelude_get(ident, finalize.is_some()) {
+                    Ok(binding)
+                } else if !self.graph_root.unexpanded_invocations.borrow().is_empty() {
+                    // Macro-expanded `extern crate` items can add names to extern prelude.
+                    Err((Undetermined, Weak::No))
+                } else {
+                    Err((Determined, Weak::No))
+                };
+            }
+            ModuleOrUniformRoot::CurrentScope => {
+                assert!(!restricted_shadowing);
+                if ns == TypeNS {
+                    if ident.name == kw::Crate || ident.name == kw::DollarCrate {
+                        let module = self.resolve_crate_root(ident);
+                        return Ok(self.module_self_bindings[&module]);
+                    } else if ident.name == kw::Super || ident.name == kw::SelfLower {
+                        // FIXME: Implement these with renaming requirements so that e.g.
+                        // `use super;` doesn't work, but `use super as name;` does.
+                        // Fall through here to get an error from `early_resolve_...`.
+                    }
+                }
+
+                let binding = self.early_resolve_ident_in_lexical_scope(
+                    ident,
+                    ScopeSet::All(ns),
+                    parent_scope,
+                    finalize,
+                    finalize.is_some(),
+                    ignore_binding,
+                );
+                return binding.map_err(|determinacy| (determinacy, Weak::No));
+            }
+        };
+
+        let key = BindingKey::new(ident, ns);
+        let resolution =
+            self.resolution(module, key).try_borrow_mut().map_err(|_| (Determined, Weak::No))?; // This happens when there is a cycle of imports.
+
+        // If the primary binding is unusable, search further and return the shadowed glob
+        // binding if it exists. What we really want here is having two separate scopes in
+        // a module - one for non-globs and one for globs, but until that's done use this
+        // hack to avoid inconsistent resolution ICEs during import validation.
+        let binding = [resolution.binding, resolution.shadowed_glob]
+            .into_iter()
+            .find_map(|binding| if binding == ignore_binding { None } else { binding });
+
+        if let Some(Finalize { path_span, report_private, .. }) = finalize {
+            let Some(binding) = binding else {
+                return Err((Determined, Weak::No));
+            };
+
+            if !self.is_accessible_from(binding.vis, parent_scope.module) {
+                if report_private {
+                    self.privacy_errors.push(PrivacyError {
+                        ident,
+                        binding,
+                        dedup_span: path_span,
+                        outermost_res: None,
+                        parent_scope: *parent_scope,
+                    });
+                } else {
+                    return Err((Determined, Weak::No));
+                }
+            }
+
+            // Forbid expanded shadowing to avoid time travel.
+            if let Some(shadowed_glob) = resolution.shadowed_glob
+                && restricted_shadowing
+                && binding.expansion != LocalExpnId::ROOT
+                && binding.res() != shadowed_glob.res()
+            {
+                self.ambiguity_errors.push(AmbiguityError {
+                    kind: AmbiguityKind::GlobVsExpanded,
+                    ident,
+                    b1: binding,
+                    b2: shadowed_glob,
+                    warning: false,
+                    misc1: AmbiguityErrorMisc::None,
+                    misc2: AmbiguityErrorMisc::None,
+                });
+            }
+
+            if !restricted_shadowing && binding.expansion != LocalExpnId::ROOT {
+                if let NameBindingKind::Import { import, .. } = binding.kind
+                    && matches!(import.kind, ImportKind::MacroExport)
+                {
+                    self.macro_expanded_macro_export_errors.insert((path_span, binding.span));
+                }
+            }
+
+            self.record_use(ident, binding, restricted_shadowing);
+            return Ok(binding);
+        }
+
+        let check_usable = |this: &mut Self, binding: NameBinding<'a>| {
+            let usable = this.is_accessible_from(binding.vis, parent_scope.module);
+            if usable { Ok(binding) } else { Err((Determined, Weak::No)) }
+        };
+
+        // Items and single imports are not shadowable, if we have one, then it's determined.
+        if let Some(binding) = binding {
+            if !binding.is_glob_import() {
+                return check_usable(self, binding);
+            }
+        }
+
+        // --- From now on we either have a glob resolution or no resolution. ---
+
+        // Check if one of single imports can still define the name,
+        // if it can then our result is not determined and can be invalidated.
+        for single_import in &resolution.single_imports {
+            let Some(import_vis) = single_import.vis.get() else {
+                continue;
+            };
+            if !self.is_accessible_from(import_vis, parent_scope.module) {
+                continue;
+            }
+            if let Some(ignored) = ignore_binding
+                && let NameBindingKind::Import { import, .. } = ignored.kind
+                && import == *single_import
+            {
+                // Ignore not just the binding itself, but if it has a shadowed_glob,
+                // ignore that, too, because this loop is supposed to only process
+                // named imports.
+                continue;
+            }
+            let Some(module) = single_import.imported_module.get() else {
+                return Err((Undetermined, Weak::No));
+            };
+            let ImportKind::Single { source: ident, .. } = single_import.kind else {
+                unreachable!();
+            };
+            match self.resolve_ident_in_module(
+                module,
+                ident,
+                ns,
+                &single_import.parent_scope,
+                None,
+                ignore_binding,
+            ) {
+                Err(Determined) => continue,
+                Ok(binding)
+                    if !self.is_accessible_from(binding.vis, single_import.parent_scope.module) =>
+                {
+                    continue;
+                }
+                Ok(_) | Err(Undetermined) => return Err((Undetermined, Weak::No)),
+            }
+        }
+
+        // So we have a resolution that's from a glob import. This resolution is determined
+        // if it cannot be shadowed by some new item/import expanded from a macro.
+        // This happens either if there are no unexpanded macros, or expanded names cannot
+        // shadow globs (that happens in macro namespace or with restricted shadowing).
+        //
+        // Additionally, any macro in any module can plant names in the root module if it creates
+        // `macro_export` macros, so the root module effectively has unresolved invocations if any
+        // module has unresolved invocations.
+        // However, it causes resolution/expansion to stuck too often (#53144), so, to make
+        // progress, we have to ignore those potential unresolved invocations from other modules
+        // and prohibit access to macro-expanded `macro_export` macros instead (unless restricted
+        // shadowing is enabled, see `macro_expanded_macro_export_errors`).
+        if let Some(binding) = binding {
+            if binding.determined() || ns == MacroNS || restricted_shadowing {
+                return check_usable(self, binding);
+            } else {
+                return Err((Undetermined, Weak::No));
+            }
+        }
+
+        // --- From now on we have no resolution. ---
+
+        // Now we are in situation when new item/import can appear only from a glob or a macro
+        // expansion. With restricted shadowing names from globs and macro expansions cannot
+        // shadow names from outer scopes, so we can freely fallback from module search to search
+        // in outer scopes. For `early_resolve_ident_in_lexical_scope` to continue search in outer
+        // scopes we return `Undetermined` with `Weak::Yes`.
+
+        // Check if one of unexpanded macros can still define the name,
+        // if it can then our "no resolution" result is not determined and can be invalidated.
+        if !module.unexpanded_invocations.borrow().is_empty() {
+            return Err((Undetermined, Weak::Yes));
+        }
+
+        // Check if one of glob imports can still define the name,
+        // if it can then our "no resolution" result is not determined and can be invalidated.
+        for glob_import in module.globs.borrow().iter() {
+            let Some(import_vis) = glob_import.vis.get() else {
+                continue;
+            };
+            if !self.is_accessible_from(import_vis, parent_scope.module) {
+                continue;
+            }
+            let module = match glob_import.imported_module.get() {
+                Some(ModuleOrUniformRoot::Module(module)) => module,
+                Some(_) => continue,
+                None => return Err((Undetermined, Weak::Yes)),
+            };
+            let tmp_parent_scope;
+            let (mut adjusted_parent_scope, mut ident) =
+                (parent_scope, ident.normalize_to_macros_2_0());
+            match ident.span.glob_adjust(module.expansion, glob_import.span) {
+                Some(Some(def)) => {
+                    tmp_parent_scope =
+                        ParentScope { module: self.expn_def_scope(def), ..*parent_scope };
+                    adjusted_parent_scope = &tmp_parent_scope;
+                }
+                Some(None) => {}
+                None => continue,
+            };
+            let result = self.resolve_ident_in_module_unadjusted(
+                ModuleOrUniformRoot::Module(module),
+                ident,
+                ns,
+                adjusted_parent_scope,
+                None,
+                ignore_binding,
+            );
+
+            match result {
+                Err(Determined) => continue,
+                Ok(binding)
+                    if !self.is_accessible_from(binding.vis, glob_import.parent_scope.module) =>
+                {
+                    continue;
+                }
+                Ok(_) | Err(Undetermined) => return Err((Undetermined, Weak::Yes)),
+            }
+        }
+
+        // No resolution and no one else can define the name - determinate error.
+        Err((Determined, Weak::No))
+    }
+
+    /// Validate a local resolution (from ribs).
+    #[instrument(level = "debug", skip(self, all_ribs))]
+    fn validate_res_from_ribs(
+        &mut self,
+        rib_index: usize,
+        rib_ident: Ident,
+        mut res: Res,
+        finalize: Option<Span>,
+        original_rib_ident_def: Ident,
+        all_ribs: &[Rib<'a>],
+    ) -> Res {
+        const CG_BUG_STR: &str = "min_const_generics resolve check didn't stop compilation";
+        debug!("validate_res_from_ribs({:?})", res);
+        let ribs = &all_ribs[rib_index + 1..];
+
+        // An invalid forward use of a generic parameter from a previous default.
+        if let RibKind::ForwardGenericParamBan = all_ribs[rib_index].kind {
+            if let Some(span) = finalize {
+                let res_error = if rib_ident.name == kw::SelfUpper {
+                    ResolutionError::SelfInGenericParamDefault
+                } else {
+                    ResolutionError::ForwardDeclaredGenericParam
+                };
+                self.report_error(span, res_error);
+            }
+            assert_eq!(res, Res::Err);
+            return Res::Err;
+        }
+
+        match res {
+            Res::Local(_) => {
+                use ResolutionError::*;
+                let mut res_err = None;
+
+                for rib in ribs {
+                    match rib.kind {
+                        RibKind::Normal
+                        | RibKind::FnOrCoroutine
+                        | RibKind::Module(..)
+                        | RibKind::MacroDefinition(..)
+                        | RibKind::ForwardGenericParamBan => {
+                            // Nothing to do. Continue.
+                        }
+                        RibKind::Item(_) | RibKind::AssocItem => {
+                            // This was an attempt to access an upvar inside a
+                            // named function item. This is not allowed, so we
+                            // report an error.
+                            if let Some(span) = finalize {
+                                // We don't immediately trigger a resolve error, because
+                                // we want certain other resolution errors (namely those
+                                // emitted for `ConstantItemRibKind` below) to take
+                                // precedence.
+                                res_err = Some((span, CannotCaptureDynamicEnvironmentInFnItem));
+                            }
+                        }
+                        RibKind::ConstantItem(_, item) => {
+                            // Still doesn't deal with upvars
+                            if let Some(span) = finalize {
+                                let (span, resolution_error) = match item {
+                                    None if rib_ident.as_str() == "self" => (span, LowercaseSelf),
+                                    None => (
+                                        rib_ident.span,
+                                        AttemptToUseNonConstantValueInConstant(
+                                            original_rib_ident_def,
+                                            "const",
+                                            "let",
+                                        ),
+                                    ),
+                                    Some((ident, kind)) => (
+                                        span,
+                                        AttemptToUseNonConstantValueInConstant(
+                                            ident,
+                                            "let",
+                                            kind.as_str(),
+                                        ),
+                                    ),
+                                };
+                                self.report_error(span, resolution_error);
+                            }
+                            return Res::Err;
+                        }
+                        RibKind::ConstParamTy => {
+                            if let Some(span) = finalize {
+                                self.report_error(
+                                    span,
+                                    ParamInTyOfConstParam {
+                                        name: rib_ident.name,
+                                        param_kind: None,
+                                    },
+                                );
+                            }
+                            return Res::Err;
+                        }
+                        RibKind::InlineAsmSym => {
+                            if let Some(span) = finalize {
+                                self.report_error(span, InvalidAsmSym);
+                            }
+                            return Res::Err;
+                        }
+                    }
+                }
+                if let Some((span, res_err)) = res_err {
+                    self.report_error(span, res_err);
+                    return Res::Err;
+                }
+            }
+            Res::Def(DefKind::TyParam, _) | Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } => {
+                for rib in ribs {
+                    let has_generic_params: HasGenericParams = match rib.kind {
+                        RibKind::Normal
+                        | RibKind::FnOrCoroutine
+                        | RibKind::Module(..)
+                        | RibKind::MacroDefinition(..)
+                        | RibKind::InlineAsmSym
+                        | RibKind::AssocItem
+                        | RibKind::ForwardGenericParamBan => {
+                            // Nothing to do. Continue.
+                            continue;
+                        }
+
+                        RibKind::ConstantItem(trivial, _) => {
+                            if let ConstantHasGenerics::No(cause) = trivial {
+                                // HACK(min_const_generics): If we encounter `Self` in an anonymous
+                                // constant we can't easily tell if it's generic at this stage, so
+                                // we instead remember this and then enforce the self type to be
+                                // concrete later on.
+                                if let Res::SelfTyAlias {
+                                    alias_to: def,
+                                    forbid_generic: _,
+                                    is_trait_impl,
+                                } = res
+                                {
+                                    res = Res::SelfTyAlias {
+                                        alias_to: def,
+                                        forbid_generic: true,
+                                        is_trait_impl,
+                                    }
+                                } else {
+                                    if let Some(span) = finalize {
+                                        let error = match cause {
+                                            NoConstantGenericsReason::IsEnumDiscriminant => {
+                                                ResolutionError::ParamInEnumDiscriminant {
+                                                    name: rib_ident.name,
+                                                    param_kind: ParamKindInEnumDiscriminant::Type,
+                                                }
+                                            }
+                                            NoConstantGenericsReason::NonTrivialConstArg => {
+                                                ResolutionError::ParamInNonTrivialAnonConst {
+                                                    name: rib_ident.name,
+                                                    param_kind:
+                                                        ParamKindInNonTrivialAnonConst::Type,
+                                                }
+                                            }
+                                        };
+                                        self.report_error(span, error);
+                                        self.dcx().span_delayed_bug(span, CG_BUG_STR);
+                                    }
+
+                                    return Res::Err;
+                                }
+                            }
+
+                            continue;
+                        }
+
+                        // This was an attempt to use a type parameter outside its scope.
+                        RibKind::Item(has_generic_params) => has_generic_params,
+                        RibKind::ConstParamTy => {
+                            if let Some(span) = finalize {
+                                self.report_error(
+                                    span,
+                                    ResolutionError::ParamInTyOfConstParam {
+                                        name: rib_ident.name,
+                                        param_kind: Some(errors::ParamKindInTyOfConstParam::Type),
+                                    },
+                                );
+                            }
+                            return Res::Err;
+                        }
+                    };
+
+                    if let Some(span) = finalize {
+                        self.report_error(
+                            span,
+                            ResolutionError::GenericParamsFromOuterItem(res, has_generic_params),
+                        );
+                    }
+                    return Res::Err;
+                }
+            }
+            Res::Def(DefKind::ConstParam, _) => {
+                for rib in ribs {
+                    let has_generic_params = match rib.kind {
+                        RibKind::Normal
+                        | RibKind::FnOrCoroutine
+                        | RibKind::Module(..)
+                        | RibKind::MacroDefinition(..)
+                        | RibKind::InlineAsmSym
+                        | RibKind::AssocItem
+                        | RibKind::ForwardGenericParamBan => continue,
+
+                        RibKind::ConstantItem(trivial, _) => {
+                            if let ConstantHasGenerics::No(cause) = trivial {
+                                if let Some(span) = finalize {
+                                    let error = match cause {
+                                        NoConstantGenericsReason::IsEnumDiscriminant => {
+                                            ResolutionError::ParamInEnumDiscriminant {
+                                                name: rib_ident.name,
+                                                param_kind: ParamKindInEnumDiscriminant::Const,
+                                            }
+                                        }
+                                        NoConstantGenericsReason::NonTrivialConstArg => {
+                                            ResolutionError::ParamInNonTrivialAnonConst {
+                                                name: rib_ident.name,
+                                                param_kind: ParamKindInNonTrivialAnonConst::Const {
+                                                    name: rib_ident.name,
+                                                },
+                                            }
+                                        }
+                                    };
+                                    self.report_error(span, error);
+                                }
+
+                                return Res::Err;
+                            }
+
+                            continue;
+                        }
+
+                        RibKind::Item(has_generic_params) => has_generic_params,
+                        RibKind::ConstParamTy => {
+                            if let Some(span) = finalize {
+                                self.report_error(
+                                    span,
+                                    ResolutionError::ParamInTyOfConstParam {
+                                        name: rib_ident.name,
+                                        param_kind: Some(errors::ParamKindInTyOfConstParam::Const),
+                                    },
+                                );
+                            }
+                            return Res::Err;
+                        }
+                    };
+
+                    // This was an attempt to use a const parameter outside its scope.
+                    if let Some(span) = finalize {
+                        self.report_error(
+                            span,
+                            ResolutionError::GenericParamsFromOuterItem(res, has_generic_params),
+                        );
+                    }
+                    return Res::Err;
+                }
+            }
+            _ => {}
+        }
+        res
+    }
+
+    #[instrument(level = "debug", skip(self))]
+    pub(crate) fn maybe_resolve_path(
+        &mut self,
+        path: &[Segment],
+        opt_ns: Option<Namespace>, // `None` indicates a module path in import
+        parent_scope: &ParentScope<'a>,
+    ) -> PathResult<'a> {
+        self.resolve_path_with_ribs(path, opt_ns, parent_scope, None, None, None)
+    }
+
+    #[instrument(level = "debug", skip(self))]
+    pub(crate) fn resolve_path(
+        &mut self,
+        path: &[Segment],
+        opt_ns: Option<Namespace>, // `None` indicates a module path in import
+        parent_scope: &ParentScope<'a>,
+        finalize: Option<Finalize>,
+        ignore_binding: Option<NameBinding<'a>>,
+    ) -> PathResult<'a> {
+        self.resolve_path_with_ribs(path, opt_ns, parent_scope, finalize, None, ignore_binding)
+    }
+
+    pub(crate) fn resolve_path_with_ribs(
+        &mut self,
+        path: &[Segment],
+        opt_ns: Option<Namespace>, // `None` indicates a module path in import
+        parent_scope: &ParentScope<'a>,
+        finalize: Option<Finalize>,
+        ribs: Option<&PerNS<Vec<Rib<'a>>>>,
+        ignore_binding: Option<NameBinding<'a>>,
+    ) -> PathResult<'a> {
+        let mut module = None;
+        let mut allow_super = true;
+        let mut second_binding = None;
+
+        // We'll provide more context to the privacy errors later, up to `len`.
+        let privacy_errors_len = self.privacy_errors.len();
+
+        for (segment_idx, &Segment { ident, id, .. }) in path.iter().enumerate() {
+            debug!("resolve_path ident {} {:?} {:?}", segment_idx, ident, id);
+            let record_segment_res = |this: &mut Self, res| {
+                if finalize.is_some() {
+                    if let Some(id) = id {
+                        if !this.partial_res_map.contains_key(&id) {
+                            assert!(id != ast::DUMMY_NODE_ID, "Trying to resolve dummy id");
+                            this.record_partial_res(id, PartialRes::new(res));
+                        }
+                    }
+                }
+            };
+
+            let is_last = segment_idx + 1 == path.len();
+            let ns = if is_last { opt_ns.unwrap_or(TypeNS) } else { TypeNS };
+            let name = ident.name;
+
+            allow_super &= ns == TypeNS && (name == kw::SelfLower || name == kw::Super);
+
+            if ns == TypeNS {
+                if allow_super && name == kw::Super {
+                    let mut ctxt = ident.span.ctxt().normalize_to_macros_2_0();
+                    let self_module = match segment_idx {
+                        0 => Some(self.resolve_self(&mut ctxt, parent_scope.module)),
+                        _ => match module {
+                            Some(ModuleOrUniformRoot::Module(module)) => Some(module),
+                            _ => None,
+                        },
+                    };
+                    if let Some(self_module) = self_module {
+                        if let Some(parent) = self_module.parent {
+                            module = Some(ModuleOrUniformRoot::Module(
+                                self.resolve_self(&mut ctxt, parent),
+                            ));
+                            continue;
+                        }
+                    }
+                    return PathResult::failed(ident, false, finalize.is_some(), module, || {
+                        ("there are too many leading `super` keywords".to_string(), None)
+                    });
+                }
+                if segment_idx == 0 {
+                    if name == kw::SelfLower {
+                        let mut ctxt = ident.span.ctxt().normalize_to_macros_2_0();
+                        module = Some(ModuleOrUniformRoot::Module(
+                            self.resolve_self(&mut ctxt, parent_scope.module),
+                        ));
+                        continue;
+                    }
+                    if name == kw::PathRoot && ident.span.at_least_rust_2018() {
+                        module = Some(ModuleOrUniformRoot::ExternPrelude);
+                        continue;
+                    }
+                    if name == kw::PathRoot
+                        && ident.span.is_rust_2015()
+                        && self.tcx.sess.at_least_rust_2018()
+                    {
+                        // `::a::b` from 2015 macro on 2018 global edition
+                        module = Some(ModuleOrUniformRoot::CrateRootAndExternPrelude);
+                        continue;
+                    }
+                    if name == kw::PathRoot || name == kw::Crate || name == kw::DollarCrate {
+                        // `::a::b`, `crate::a::b` or `$crate::a::b`
+                        module = Some(ModuleOrUniformRoot::Module(self.resolve_crate_root(ident)));
+                        continue;
+                    }
+                }
+            }
+
+            // Report special messages for path segment keywords in wrong positions.
+            if ident.is_path_segment_keyword() && segment_idx != 0 {
+                return PathResult::failed(ident, false, finalize.is_some(), module, || {
+                    let name_str = if name == kw::PathRoot {
+                        "crate root".to_string()
+                    } else {
+                        format!("`{name}`")
+                    };
+                    let label = if segment_idx == 1 && path[0].ident.name == kw::PathRoot {
+                        format!("global paths cannot start with {name_str}")
+                    } else {
+                        format!("{name_str} in paths can only be used in start position")
+                    };
+                    (label, None)
+                });
+            }
+
+            let binding = if let Some(module) = module {
+                self.resolve_ident_in_module(
+                    module,
+                    ident,
+                    ns,
+                    parent_scope,
+                    finalize,
+                    ignore_binding,
+                )
+            } else if let Some(ribs) = ribs
+                && let Some(TypeNS | ValueNS) = opt_ns
+            {
+                match self.resolve_ident_in_lexical_scope(
+                    ident,
+                    ns,
+                    parent_scope,
+                    finalize,
+                    &ribs[ns],
+                    ignore_binding,
+                ) {
+                    // we found a locally-imported or available item/module
+                    Some(LexicalScopeBinding::Item(binding)) => Ok(binding),
+                    // we found a local variable or type param
+                    Some(LexicalScopeBinding::Res(res)) => {
+                        record_segment_res(self, res);
+                        return PathResult::NonModule(PartialRes::with_unresolved_segments(
+                            res,
+                            path.len() - 1,
+                        ));
+                    }
+                    _ => Err(Determinacy::determined(finalize.is_some())),
+                }
+            } else {
+                self.early_resolve_ident_in_lexical_scope(
+                    ident,
+                    ScopeSet::All(ns),
+                    parent_scope,
+                    finalize,
+                    finalize.is_some(),
+                    ignore_binding,
+                )
+            };
+
+            match binding {
+                Ok(binding) => {
+                    if segment_idx == 1 {
+                        second_binding = Some(binding);
+                    }
+                    let res = binding.res();
+
+                    // Mark every privacy error in this path with the res to the last element. This allows us
+                    // to detect the item the user cares about and either find an alternative import, or tell
+                    // the user it is not accessible.
+                    for error in &mut self.privacy_errors[privacy_errors_len..] {
+                        error.outermost_res = Some((res, ident));
+                    }
+
+                    let maybe_assoc = opt_ns != Some(MacroNS) && PathSource::Type.is_expected(res);
+                    if let Some(next_module) = binding.module() {
+                        module = Some(ModuleOrUniformRoot::Module(next_module));
+                        record_segment_res(self, res);
+                    } else if res == Res::ToolMod && !is_last && opt_ns.is_some() {
+                        if binding.is_import() {
+                            self.dcx().emit_err(errors::ToolModuleImported {
+                                span: ident.span,
+                                import: binding.span,
+                            });
+                        }
+                        let res = Res::NonMacroAttr(NonMacroAttrKind::Tool);
+                        return PathResult::NonModule(PartialRes::new(res));
+                    } else if res == Res::Err {
+                        return PathResult::NonModule(PartialRes::new(Res::Err));
+                    } else if opt_ns.is_some() && (is_last || maybe_assoc) {
+                        self.lint_if_path_starts_with_module(finalize, path, second_binding);
+                        record_segment_res(self, res);
+                        return PathResult::NonModule(PartialRes::with_unresolved_segments(
+                            res,
+                            path.len() - segment_idx - 1,
+                        ));
+                    } else {
+                        return PathResult::failed(
+                            ident,
+                            is_last,
+                            finalize.is_some(),
+                            module,
+                            || {
+                                let label = format!(
+                                    "`{ident}` is {} {}, not a module",
+                                    res.article(),
+                                    res.descr()
+                                );
+                                (label, None)
+                            },
+                        );
+                    }
+                }
+                Err(Undetermined) => return PathResult::Indeterminate,
+                Err(Determined) => {
+                    if let Some(ModuleOrUniformRoot::Module(module)) = module {
+                        if opt_ns.is_some() && !module.is_normal() {
+                            return PathResult::NonModule(PartialRes::with_unresolved_segments(
+                                module.res().unwrap(),
+                                path.len() - segment_idx,
+                            ));
+                        }
+                    }
+
+                    return PathResult::failed(ident, is_last, finalize.is_some(), module, || {
+                        self.report_path_resolution_error(
+                            path,
+                            opt_ns,
+                            parent_scope,
+                            ribs,
+                            ignore_binding,
+                            module,
+                            segment_idx,
+                            ident,
+                        )
+                    });
+                }
+            }
+        }
+
+        self.lint_if_path_starts_with_module(finalize, path, second_binding);
+
+        PathResult::Module(match module {
+            Some(module) => module,
+            None if path.is_empty() => ModuleOrUniformRoot::CurrentScope,
+            _ => bug!("resolve_path: non-empty path `{:?}` has no module", path),
+        })
+    }
+}
diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs
new file mode 100644
index 00000000000..f846dbec2c6
--- /dev/null
+++ b/compiler/rustc_resolve/src/imports.rs
@@ -0,0 +1,1488 @@
+//! A bunch of methods and structures more or less related to resolving imports.
+
+use crate::diagnostics::{import_candidates, DiagnosticMode, Suggestion};
+use crate::errors::{
+    CannotBeReexportedCratePublic, CannotBeReexportedCratePublicNS, CannotBeReexportedPrivate,
+    CannotBeReexportedPrivateNS, CannotDetermineImportResolution, CannotGlobImportAllCrates,
+    ConsiderAddingMacroExport, ConsiderMarkingAsPub, IsNotDirectlyImportable,
+    ItemsInTraitsAreNotImportable,
+};
+use crate::Determinacy::{self, *};
+use crate::Namespace::*;
+use crate::{module_to_string, names_to_string, ImportSuggestion};
+use crate::{AmbiguityKind, BindingKey, ModuleKind, ResolutionError, Resolver, Segment};
+use crate::{Finalize, Module, ModuleOrUniformRoot, ParentScope, PerNS, ScopeSet};
+use crate::{NameBinding, NameBindingData, NameBindingKind, PathResult};
+
+use rustc_ast::NodeId;
+use rustc_data_structures::fx::FxHashSet;
+use rustc_data_structures::intern::Interned;
+use rustc_errors::{pluralize, struct_span_code_err, Applicability, MultiSpan};
+use rustc_hir::def::{self, DefKind, PartialRes};
+use rustc_middle::metadata::ModChild;
+use rustc_middle::metadata::Reexport;
+use rustc_middle::span_bug;
+use rustc_middle::ty;
+use rustc_session::lint::builtin::{
+    AMBIGUOUS_GLOB_REEXPORTS, HIDDEN_GLOB_REEXPORTS, PUB_USE_OF_PRIVATE_EXTERN_CRATE,
+    UNUSED_IMPORTS,
+};
+use rustc_session::lint::BuiltinLintDiagnostics;
+use rustc_span::edit_distance::find_best_match_for_name;
+use rustc_span::hygiene::LocalExpnId;
+use rustc_span::symbol::{kw, Ident, Symbol};
+use rustc_span::Span;
+use smallvec::SmallVec;
+
+use std::cell::Cell;
+use std::mem;
+
+type Res = def::Res<NodeId>;
+
+/// Contains data for specific kinds of imports.
+#[derive(Clone)]
+pub(crate) enum ImportKind<'a> {
+    Single {
+        /// `source` in `use prefix::source as target`.
+        source: Ident,
+        /// `target` in `use prefix::source as target`.
+        target: Ident,
+        /// Bindings to which `source` refers to.
+        source_bindings: PerNS<Cell<Result<NameBinding<'a>, Determinacy>>>,
+        /// Bindings introduced by `target`.
+        target_bindings: PerNS<Cell<Option<NameBinding<'a>>>>,
+        /// `true` for `...::{self [as target]}` imports, `false` otherwise.
+        type_ns_only: bool,
+        /// Did this import result from a nested import? ie. `use foo::{bar, baz};`
+        nested: bool,
+        /// The ID of the `UseTree` that imported this `Import`.
+        ///
+        /// In the case where the `Import` was expanded from a "nested" use tree,
+        /// this id is the ID of the leaf tree. For example:
+        ///
+        /// ```ignore (pacify the merciless tidy)
+        /// use foo::bar::{a, b}
+        /// ```
+        ///
+        /// If this is the import for `foo::bar::a`, we would have the ID of the `UseTree`
+        /// for `a` in this field.
+        id: NodeId,
+    },
+    Glob {
+        is_prelude: bool,
+        // The visibility of the greatest re-export.
+        // n.b. `max_vis` is only used in `finalize_import` to check for re-export errors.
+        max_vis: Cell<Option<ty::Visibility>>,
+        id: NodeId,
+    },
+    ExternCrate {
+        source: Option<Symbol>,
+        target: Ident,
+        id: NodeId,
+    },
+    MacroUse,
+    MacroExport,
+}
+
+/// Manually implement `Debug` for `ImportKind` because the `source/target_bindings`
+/// contain `Cell`s which can introduce infinite loops while printing.
+impl<'a> std::fmt::Debug for ImportKind<'a> {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        use ImportKind::*;
+        match self {
+            Single {
+                ref source,
+                ref target,
+                ref source_bindings,
+                ref target_bindings,
+                ref type_ns_only,
+                ref nested,
+                ref id,
+            } => f
+                .debug_struct("Single")
+                .field("source", source)
+                .field("target", target)
+                // Ignore the nested bindings to avoid an infinite loop while printing.
+                .field(
+                    "source_bindings",
+                    &source_bindings.clone().map(|b| b.into_inner().map(|_| format_args!(".."))),
+                )
+                .field(
+                    "target_bindings",
+                    &target_bindings.clone().map(|b| b.into_inner().map(|_| format_args!(".."))),
+                )
+                .field("type_ns_only", type_ns_only)
+                .field("nested", nested)
+                .field("id", id)
+                .finish(),
+            Glob { ref is_prelude, ref max_vis, ref id } => f
+                .debug_struct("Glob")
+                .field("is_prelude", is_prelude)
+                .field("max_vis", max_vis)
+                .field("id", id)
+                .finish(),
+            ExternCrate { ref source, ref target, ref id } => f
+                .debug_struct("ExternCrate")
+                .field("source", source)
+                .field("target", target)
+                .field("id", id)
+                .finish(),
+            MacroUse => f.debug_struct("MacroUse").finish(),
+            MacroExport => f.debug_struct("MacroExport").finish(),
+        }
+    }
+}
+
+/// One import.
+#[derive(Debug, Clone)]
+pub(crate) struct ImportData<'a> {
+    pub kind: ImportKind<'a>,
+
+    /// Node ID of the "root" use item -- this is always the same as `ImportKind`'s `id`
+    /// (if it exists) except in the case of "nested" use trees, in which case
+    /// it will be the ID of the root use tree. e.g., in the example
+    /// ```ignore (incomplete code)
+    /// use foo::bar::{a, b}
+    /// ```
+    /// this would be the ID of the `use foo::bar` `UseTree` node.
+    /// In case of imports without their own node ID it's the closest node that can be used,
+    /// for example, for reporting lints.
+    pub root_id: NodeId,
+
+    /// Span of the entire use statement.
+    pub use_span: Span,
+
+    /// Span of the entire use statement with attributes.
+    pub use_span_with_attributes: Span,
+
+    /// Did the use statement have any attributes?
+    pub has_attributes: bool,
+
+    /// Span of this use tree.
+    pub span: Span,
+
+    /// Span of the *root* use tree (see `root_id`).
+    pub root_span: Span,
+
+    pub parent_scope: ParentScope<'a>,
+    pub module_path: Vec<Segment>,
+    /// The resolution of `module_path`.
+    pub imported_module: Cell<Option<ModuleOrUniformRoot<'a>>>,
+    pub vis: Cell<Option<ty::Visibility>>,
+    pub used: Cell<bool>,
+}
+
+/// All imports are unique and allocated on a same arena,
+/// so we can use referential equality to compare them.
+pub(crate) type Import<'a> = Interned<'a, ImportData<'a>>;
+
+impl<'a> ImportData<'a> {
+    pub(crate) fn is_glob(&self) -> bool {
+        matches!(self.kind, ImportKind::Glob { .. })
+    }
+
+    pub(crate) fn is_nested(&self) -> bool {
+        match self.kind {
+            ImportKind::Single { nested, .. } => nested,
+            _ => false,
+        }
+    }
+
+    pub(crate) fn expect_vis(&self) -> ty::Visibility {
+        self.vis.get().expect("encountered cleared import visibility")
+    }
+
+    pub(crate) fn id(&self) -> Option<NodeId> {
+        match self.kind {
+            ImportKind::Single { id, .. }
+            | ImportKind::Glob { id, .. }
+            | ImportKind::ExternCrate { id, .. } => Some(id),
+            ImportKind::MacroUse | ImportKind::MacroExport => None,
+        }
+    }
+
+    fn simplify(&self, r: &Resolver<'_, '_>) -> Reexport {
+        let to_def_id = |id| r.local_def_id(id).to_def_id();
+        match self.kind {
+            ImportKind::Single { id, .. } => Reexport::Single(to_def_id(id)),
+            ImportKind::Glob { id, .. } => Reexport::Glob(to_def_id(id)),
+            ImportKind::ExternCrate { id, .. } => Reexport::ExternCrate(to_def_id(id)),
+            ImportKind::MacroUse => Reexport::MacroUse,
+            ImportKind::MacroExport => Reexport::MacroExport,
+        }
+    }
+}
+
+/// Records information about the resolution of a name in a namespace of a module.
+#[derive(Clone, Default, Debug)]
+pub(crate) struct NameResolution<'a> {
+    /// Single imports that may define the name in the namespace.
+    /// Imports are arena-allocated, so it's ok to use pointers as keys.
+    pub single_imports: FxHashSet<Import<'a>>,
+    /// The least shadowable known binding for this name, or None if there are no known bindings.
+    pub binding: Option<NameBinding<'a>>,
+    pub shadowed_glob: Option<NameBinding<'a>>,
+}
+
+impl<'a> NameResolution<'a> {
+    /// Returns the binding for the name if it is known or None if it not known.
+    pub(crate) fn binding(&self) -> Option<NameBinding<'a>> {
+        self.binding.and_then(|binding| {
+            if !binding.is_glob_import() || self.single_imports.is_empty() {
+                Some(binding)
+            } else {
+                None
+            }
+        })
+    }
+}
+
+/// An error that may be transformed into a diagnostic later. Used to combine multiple unresolved
+/// import errors within the same use tree into a single diagnostic.
+#[derive(Debug, Clone)]
+struct UnresolvedImportError {
+    span: Span,
+    label: Option<String>,
+    note: Option<String>,
+    suggestion: Option<Suggestion>,
+    candidates: Option<Vec<ImportSuggestion>>,
+}
+
+// Reexports of the form `pub use foo as bar;` where `foo` is `extern crate foo;`
+// are permitted for backward-compatibility under a deprecation lint.
+fn pub_use_of_private_extern_crate_hack(import: Import<'_>, binding: NameBinding<'_>) -> bool {
+    match (&import.kind, &binding.kind) {
+        (ImportKind::Single { .. }, NameBindingKind::Import { import: binding_import, .. }) => {
+            matches!(binding_import.kind, ImportKind::ExternCrate { .. })
+                && import.expect_vis().is_public()
+        }
+        _ => false,
+    }
+}
+
+impl<'a, 'tcx> Resolver<'a, 'tcx> {
+    /// Given a binding and an import that resolves to it,
+    /// return the corresponding binding defined by the import.
+    pub(crate) fn import(&self, binding: NameBinding<'a>, import: Import<'a>) -> NameBinding<'a> {
+        let import_vis = import.expect_vis().to_def_id();
+        let vis = if binding.vis.is_at_least(import_vis, self.tcx)
+            || pub_use_of_private_extern_crate_hack(import, binding)
+        {
+            import_vis
+        } else {
+            binding.vis
+        };
+
+        if let ImportKind::Glob { ref max_vis, .. } = import.kind {
+            if vis == import_vis
+                || max_vis.get().map_or(true, |max_vis| vis.is_at_least(max_vis, self.tcx))
+            {
+                max_vis.set(Some(vis.expect_local()))
+            }
+        }
+
+        self.arenas.alloc_name_binding(NameBindingData {
+            kind: NameBindingKind::Import { binding, import, used: Cell::new(false) },
+            ambiguity: None,
+            warn_ambiguity: false,
+            span: import.span,
+            vis,
+            expansion: import.parent_scope.expansion,
+        })
+    }
+
+    /// Define the name or return the existing binding if there is a collision.
+    /// `update` indicates if the definition is a redefinition of an existing binding.
+    pub(crate) fn try_define(
+        &mut self,
+        module: Module<'a>,
+        key: BindingKey,
+        binding: NameBinding<'a>,
+        warn_ambiguity: bool,
+    ) -> Result<(), NameBinding<'a>> {
+        let res = binding.res();
+        self.check_reserved_macro_name(key.ident, res);
+        self.set_binding_parent_module(binding, module);
+        self.update_resolution(module, key, warn_ambiguity, |this, resolution| {
+            if let Some(old_binding) = resolution.binding {
+                if res == Res::Err && old_binding.res() != Res::Err {
+                    // Do not override real bindings with `Res::Err`s from error recovery.
+                    return Ok(());
+                }
+                match (old_binding.is_glob_import(), binding.is_glob_import()) {
+                    (true, true) => {
+                        // FIXME: remove `!binding.is_ambiguity()` after delete the warning ambiguity.
+                        if !binding.is_ambiguity()
+                            && let NameBindingKind::Import { import: old_import, .. } =
+                                old_binding.kind
+                            && let NameBindingKind::Import { import, .. } = binding.kind
+                            && old_import == import
+                        {
+                            // We should replace the `old_binding` with `binding` regardless
+                            // of whether they has same resolution or not when they are
+                            // imported from the same glob-import statement.
+                            resolution.binding = Some(binding);
+                        } else if res != old_binding.res() {
+                            let binding = if warn_ambiguity {
+                                this.warn_ambiguity(AmbiguityKind::GlobVsGlob, old_binding, binding)
+                            } else {
+                                this.ambiguity(AmbiguityKind::GlobVsGlob, old_binding, binding)
+                            };
+                            resolution.binding = Some(binding);
+                        } else if !old_binding.vis.is_at_least(binding.vis, this.tcx) {
+                            // We are glob-importing the same item but with greater visibility.
+                            resolution.binding = Some(binding);
+                        } else if binding.is_ambiguity() {
+                            resolution.binding =
+                                Some(self.arenas.alloc_name_binding(NameBindingData {
+                                    warn_ambiguity: true,
+                                    ..(*binding).clone()
+                                }));
+                        }
+                    }
+                    (old_glob @ true, false) | (old_glob @ false, true) => {
+                        let (glob_binding, nonglob_binding) =
+                            if old_glob { (old_binding, binding) } else { (binding, old_binding) };
+                        if glob_binding.res() != nonglob_binding.res()
+                            && key.ns == MacroNS
+                            && nonglob_binding.expansion != LocalExpnId::ROOT
+                        {
+                            resolution.binding = Some(this.ambiguity(
+                                AmbiguityKind::GlobVsExpanded,
+                                nonglob_binding,
+                                glob_binding,
+                            ));
+                        } else {
+                            resolution.binding = Some(nonglob_binding);
+                        }
+
+                        if let Some(old_binding) = resolution.shadowed_glob {
+                            assert!(old_binding.is_glob_import());
+                            if glob_binding.res() != old_binding.res() {
+                                resolution.shadowed_glob = Some(this.ambiguity(
+                                    AmbiguityKind::GlobVsGlob,
+                                    old_binding,
+                                    glob_binding,
+                                ));
+                            } else if !old_binding.vis.is_at_least(binding.vis, this.tcx) {
+                                resolution.shadowed_glob = Some(glob_binding);
+                            }
+                        } else {
+                            resolution.shadowed_glob = Some(glob_binding);
+                        }
+                    }
+                    (false, false) => {
+                        return Err(old_binding);
+                    }
+                }
+            } else {
+                resolution.binding = Some(binding);
+            }
+
+            Ok(())
+        })
+    }
+
+    fn ambiguity(
+        &self,
+        kind: AmbiguityKind,
+        primary_binding: NameBinding<'a>,
+        secondary_binding: NameBinding<'a>,
+    ) -> NameBinding<'a> {
+        self.arenas.alloc_name_binding(NameBindingData {
+            ambiguity: Some((secondary_binding, kind)),
+            ..(*primary_binding).clone()
+        })
+    }
+
+    fn warn_ambiguity(
+        &self,
+        kind: AmbiguityKind,
+        primary_binding: NameBinding<'a>,
+        secondary_binding: NameBinding<'a>,
+    ) -> NameBinding<'a> {
+        self.arenas.alloc_name_binding(NameBindingData {
+            ambiguity: Some((secondary_binding, kind)),
+            warn_ambiguity: true,
+            ..(*primary_binding).clone()
+        })
+    }
+
+    // Use `f` to mutate the resolution of the name in the module.
+    // If the resolution becomes a success, define it in the module's glob importers.
+    fn update_resolution<T, F>(
+        &mut self,
+        module: Module<'a>,
+        key: BindingKey,
+        warn_ambiguity: bool,
+        f: F,
+    ) -> T
+    where
+        F: FnOnce(&mut Resolver<'a, 'tcx>, &mut NameResolution<'a>) -> T,
+    {
+        // Ensure that `resolution` isn't borrowed when defining in the module's glob importers,
+        // during which the resolution might end up getting re-defined via a glob cycle.
+        let (binding, t, warn_ambiguity) = {
+            let resolution = &mut *self.resolution(module, key).borrow_mut();
+            let old_binding = resolution.binding();
+
+            let t = f(self, resolution);
+
+            if let Some(binding) = resolution.binding()
+                && old_binding != Some(binding)
+            {
+                (binding, t, warn_ambiguity || old_binding.is_some())
+            } else {
+                return t;
+            }
+        };
+
+        let Ok(glob_importers) = module.glob_importers.try_borrow_mut() else {
+            return t;
+        };
+
+        // Define or update `binding` in `module`s glob importers.
+        for import in glob_importers.iter() {
+            let mut ident = key.ident;
+            let scope = match ident.span.reverse_glob_adjust(module.expansion, import.span) {
+                Some(Some(def)) => self.expn_def_scope(def),
+                Some(None) => import.parent_scope.module,
+                None => continue,
+            };
+            if self.is_accessible_from(binding.vis, scope) {
+                let imported_binding = self.import(binding, *import);
+                let key = BindingKey { ident, ..key };
+                let _ = self.try_define(
+                    import.parent_scope.module,
+                    key,
+                    imported_binding,
+                    warn_ambiguity,
+                );
+            }
+        }
+
+        t
+    }
+
+    // Define a dummy resolution containing a `Res::Err` as a placeholder for a failed
+    // or indeterminate resolution, also mark such failed imports as used to avoid duplicate diagnostics.
+    fn import_dummy_binding(&mut self, import: Import<'a>, is_indeterminate: bool) {
+        if let ImportKind::Single { target, ref target_bindings, .. } = import.kind {
+            if !(is_indeterminate || target_bindings.iter().all(|binding| binding.get().is_none()))
+            {
+                return; // Has resolution, do not create the dummy binding
+            }
+            let dummy_binding = self.dummy_binding;
+            let dummy_binding = self.import(dummy_binding, import);
+            self.per_ns(|this, ns| {
+                let key = BindingKey::new(target, ns);
+                let _ = this.try_define(import.parent_scope.module, key, dummy_binding, false);
+                this.update_resolution(import.parent_scope.module, key, false, |_, resolution| {
+                    resolution.single_imports.remove(&import);
+                })
+            });
+            self.record_use(target, dummy_binding, false);
+        } else if import.imported_module.get().is_none() {
+            import.used.set(true);
+            if let Some(id) = import.id() {
+                self.used_imports.insert(id);
+            }
+        }
+    }
+
+    // Import resolution
+    //
+    // This is a fixed-point algorithm. We resolve imports until our efforts
+    // are stymied by an unresolved import; then we bail out of the current
+    // module and continue. We terminate successfully once no more imports
+    // remain or unsuccessfully when no forward progress in resolving imports
+    // is made.
+
+    /// Resolves all imports for the crate. This method performs the fixed-
+    /// point iteration.
+    pub(crate) fn resolve_imports(&mut self) {
+        let mut prev_indeterminate_count = usize::MAX;
+        let mut indeterminate_count = self.indeterminate_imports.len() * 3;
+        while indeterminate_count < prev_indeterminate_count {
+            prev_indeterminate_count = indeterminate_count;
+            indeterminate_count = 0;
+            for import in mem::take(&mut self.indeterminate_imports) {
+                let import_indeterminate_count = self.resolve_import(import);
+                indeterminate_count += import_indeterminate_count;
+                match import_indeterminate_count {
+                    0 => self.determined_imports.push(import),
+                    _ => self.indeterminate_imports.push(import),
+                }
+            }
+        }
+    }
+
+    pub(crate) fn finalize_imports(&mut self) {
+        for module in self.arenas.local_modules().iter() {
+            self.finalize_resolutions_in(*module);
+        }
+
+        let mut seen_spans = FxHashSet::default();
+        let mut errors = vec![];
+        let mut prev_root_id: NodeId = NodeId::from_u32(0);
+        let determined_imports = mem::take(&mut self.determined_imports);
+        let indeterminate_imports = mem::take(&mut self.indeterminate_imports);
+
+        for (is_indeterminate, import) in determined_imports
+            .iter()
+            .map(|i| (false, i))
+            .chain(indeterminate_imports.iter().map(|i| (true, i)))
+        {
+            let unresolved_import_error = self.finalize_import(*import);
+
+            // If this import is unresolved then create a dummy import
+            // resolution for it so that later resolve stages won't complain.
+            self.import_dummy_binding(*import, is_indeterminate);
+
+            if let Some(err) = unresolved_import_error {
+                if let ImportKind::Single { source, ref source_bindings, .. } = import.kind {
+                    if source.name == kw::SelfLower {
+                        // Silence `unresolved import` error if E0429 is already emitted
+                        if let Err(Determined) = source_bindings.value_ns.get() {
+                            continue;
+                        }
+                    }
+                }
+
+                if prev_root_id.as_u32() != 0
+                    && prev_root_id.as_u32() != import.root_id.as_u32()
+                    && !errors.is_empty()
+                {
+                    // In the case of a new import line, throw a diagnostic message
+                    // for the previous line.
+                    self.throw_unresolved_import_error(errors);
+                    errors = vec![];
+                }
+                if seen_spans.insert(err.span) {
+                    errors.push((*import, err));
+                    prev_root_id = import.root_id;
+                }
+            }
+        }
+
+        if !errors.is_empty() {
+            self.throw_unresolved_import_error(errors);
+            return;
+        }
+
+        for import in &indeterminate_imports {
+            let path = import_path_to_string(
+                &import.module_path.iter().map(|seg| seg.ident).collect::<Vec<_>>(),
+                &import.kind,
+                import.span,
+            );
+            let err = UnresolvedImportError {
+                span: import.span,
+                label: None,
+                note: None,
+                suggestion: None,
+                candidates: None,
+            };
+            // FIXME: there should be a better way of doing this than
+            // formatting this as a string then checking for `::`
+            if path.contains("::") {
+                errors.push((*import, err))
+            }
+        }
+
+        if !errors.is_empty() {
+            self.throw_unresolved_import_error(errors);
+        }
+    }
+
+    pub(crate) fn check_hidden_glob_reexports(
+        &mut self,
+        exported_ambiguities: FxHashSet<NameBinding<'a>>,
+    ) {
+        for module in self.arenas.local_modules().iter() {
+            for (key, resolution) in self.resolutions(*module).borrow().iter() {
+                let resolution = resolution.borrow();
+
+                if let Some(binding) = resolution.binding {
+                    if let NameBindingKind::Import { import, .. } = binding.kind
+                        && let Some((amb_binding, _)) = binding.ambiguity
+                        && binding.res() != Res::Err
+                        && exported_ambiguities.contains(&binding)
+                    {
+                        self.lint_buffer.buffer_lint_with_diagnostic(
+                            AMBIGUOUS_GLOB_REEXPORTS,
+                            import.root_id,
+                            import.root_span,
+                            "ambiguous glob re-exports",
+                            BuiltinLintDiagnostics::AmbiguousGlobReexports {
+                                name: key.ident.to_string(),
+                                namespace: key.ns.descr().to_string(),
+                                first_reexport_span: import.root_span,
+                                duplicate_reexport_span: amb_binding.span,
+                            },
+                        );
+                    }
+
+                    if let Some(glob_binding) = resolution.shadowed_glob {
+                        let binding_id = match binding.kind {
+                            NameBindingKind::Res(res) => {
+                                Some(self.def_id_to_node_id[res.def_id().expect_local()])
+                            }
+                            NameBindingKind::Module(module) => {
+                                Some(self.def_id_to_node_id[module.def_id().expect_local()])
+                            }
+                            NameBindingKind::Import { import, .. } => import.id(),
+                        };
+
+                        if binding.res() != Res::Err
+                            && glob_binding.res() != Res::Err
+                            && let NameBindingKind::Import { import: glob_import, .. } =
+                                glob_binding.kind
+                            && let Some(binding_id) = binding_id
+                            && let Some(glob_import_id) = glob_import.id()
+                            && let glob_import_def_id = self.local_def_id(glob_import_id)
+                            && self.effective_visibilities.is_exported(glob_import_def_id)
+                            && glob_binding.vis.is_public()
+                            && !binding.vis.is_public()
+                        {
+                            self.lint_buffer.buffer_lint_with_diagnostic(
+                                HIDDEN_GLOB_REEXPORTS,
+                                binding_id,
+                                binding.span,
+                                "private item shadows public glob re-export",
+                                BuiltinLintDiagnostics::HiddenGlobReexports {
+                                    name: key.ident.name.to_string(),
+                                    namespace: key.ns.descr().to_owned(),
+                                    glob_reexport_span: glob_binding.span,
+                                    private_item_span: binding.span,
+                                },
+                            );
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    fn throw_unresolved_import_error(&mut self, errors: Vec<(Import<'_>, UnresolvedImportError)>) {
+        if errors.is_empty() {
+            return;
+        }
+
+        /// Upper limit on the number of `span_label` messages.
+        const MAX_LABEL_COUNT: usize = 10;
+
+        let span = MultiSpan::from_spans(errors.iter().map(|(_, err)| err.span).collect());
+        let paths = errors
+            .iter()
+            .map(|(import, err)| {
+                let path = import_path_to_string(
+                    &import.module_path.iter().map(|seg| seg.ident).collect::<Vec<_>>(),
+                    &import.kind,
+                    err.span,
+                );
+                format!("`{path}`")
+            })
+            .collect::<Vec<_>>();
+        let msg = format!("unresolved import{} {}", pluralize!(paths.len()), paths.join(", "),);
+
+        let mut diag = struct_span_code_err!(self.dcx(), span, E0432, "{}", &msg);
+
+        if let Some((_, UnresolvedImportError { note: Some(note), .. })) = errors.iter().last() {
+            diag.note(note.clone());
+        }
+
+        for (import, err) in errors.into_iter().take(MAX_LABEL_COUNT) {
+            if let Some(label) = err.label {
+                diag.span_label(err.span, label);
+            }
+
+            if let Some((suggestions, msg, applicability)) = err.suggestion {
+                if suggestions.is_empty() {
+                    diag.help(msg);
+                    continue;
+                }
+                diag.multipart_suggestion(msg, suggestions, applicability);
+            }
+
+            if let Some(candidates) = &err.candidates {
+                match &import.kind {
+                    ImportKind::Single { nested: false, source, target, .. } => import_candidates(
+                        self.tcx,
+                        &mut diag,
+                        Some(err.span),
+                        candidates,
+                        DiagnosticMode::Import,
+                        (source != target)
+                            .then(|| format!(" as {target}"))
+                            .as_deref()
+                            .unwrap_or(""),
+                    ),
+                    ImportKind::Single { nested: true, source, target, .. } => {
+                        import_candidates(
+                            self.tcx,
+                            &mut diag,
+                            None,
+                            candidates,
+                            DiagnosticMode::Normal,
+                            (source != target)
+                                .then(|| format!(" as {target}"))
+                                .as_deref()
+                                .unwrap_or(""),
+                        );
+                    }
+                    _ => {}
+                }
+            }
+
+            match &import.kind {
+                ImportKind::Single { source, .. } => {
+                    if let Some(ModuleOrUniformRoot::Module(module)) = import.imported_module.get()
+                        && let Some(module) = module.opt_def_id()
+                    {
+                        self.find_cfg_stripped(&mut diag, &source.name, module)
+                    }
+                }
+                _ => {}
+            }
+        }
+
+        diag.emit();
+    }
+
+    /// Attempts to resolve the given import, returning:
+    /// - `0` means its resolution is determined.
+    /// - Other values mean that indeterminate exists under certain namespaces.
+    ///
+    /// Meanwhile, if resolve successful, the resolved bindings are written
+    /// into the module.
+    fn resolve_import(&mut self, import: Import<'a>) -> usize {
+        debug!(
+            "(resolving import for module) resolving import `{}::...` in `{}`",
+            Segment::names_to_string(&import.module_path),
+            module_to_string(import.parent_scope.module).unwrap_or_else(|| "???".to_string()),
+        );
+        let module = if let Some(module) = import.imported_module.get() {
+            module
+        } else {
+            // For better failure detection, pretend that the import will
+            // not define any names while resolving its module path.
+            let orig_vis = import.vis.take();
+            let path_res = self.maybe_resolve_path(&import.module_path, None, &import.parent_scope);
+            import.vis.set(orig_vis);
+
+            match path_res {
+                PathResult::Module(module) => module,
+                PathResult::Indeterminate => return 3,
+                PathResult::NonModule(..) | PathResult::Failed { .. } => return 0,
+            }
+        };
+
+        import.imported_module.set(Some(module));
+        let (source, target, source_bindings, target_bindings, type_ns_only) = match import.kind {
+            ImportKind::Single {
+                source,
+                target,
+                ref source_bindings,
+                ref target_bindings,
+                type_ns_only,
+                ..
+            } => (source, target, source_bindings, target_bindings, type_ns_only),
+            ImportKind::Glob { .. } => {
+                self.resolve_glob_import(import);
+                return 0;
+            }
+            _ => unreachable!(),
+        };
+
+        let mut indeterminate_count = 0;
+        self.per_ns(|this, ns| {
+            if !type_ns_only || ns == TypeNS {
+                if let Err(Undetermined) = source_bindings[ns].get() {
+                    // For better failure detection, pretend that the import will
+                    // not define any names while resolving its module path.
+                    let orig_vis = import.vis.take();
+                    let binding = this.maybe_resolve_ident_in_module(
+                        module,
+                        source,
+                        ns,
+                        &import.parent_scope,
+                    );
+                    import.vis.set(orig_vis);
+                    source_bindings[ns].set(binding);
+                } else {
+                    return;
+                };
+
+                let parent = import.parent_scope.module;
+                match source_bindings[ns].get() {
+                    Err(Undetermined) => indeterminate_count += 1,
+                    // Don't update the resolution, because it was never added.
+                    Err(Determined) if target.name == kw::Underscore => {}
+                    Ok(binding) if binding.is_importable() => {
+                        let imported_binding = this.import(binding, import);
+                        target_bindings[ns].set(Some(imported_binding));
+                        this.define(parent, target, ns, imported_binding);
+                    }
+                    source_binding @ (Ok(..) | Err(Determined)) => {
+                        if source_binding.is_ok() {
+                            this.dcx()
+                                .create_err(IsNotDirectlyImportable { span: import.span, target })
+                                .emit();
+                        }
+                        let key = BindingKey::new(target, ns);
+                        this.update_resolution(parent, key, false, |_, resolution| {
+                            resolution.single_imports.remove(&import);
+                        });
+                    }
+                }
+            }
+        });
+
+        indeterminate_count
+    }
+
+    /// Performs final import resolution, consistency checks and error reporting.
+    ///
+    /// Optionally returns an unresolved import error. This error is buffered and used to
+    /// consolidate multiple unresolved import errors into a single diagnostic.
+    fn finalize_import(&mut self, import: Import<'a>) -> Option<UnresolvedImportError> {
+        let orig_vis = import.vis.take();
+        let ignore_binding = match &import.kind {
+            ImportKind::Single { target_bindings, .. } => target_bindings[TypeNS].get(),
+            _ => None,
+        };
+        let prev_ambiguity_errors_len = self.ambiguity_errors.len();
+        let finalize = Finalize::with_root_span(import.root_id, import.span, import.root_span);
+
+        // We'll provide more context to the privacy errors later, up to `len`.
+        let privacy_errors_len = self.privacy_errors.len();
+
+        let path_res = self.resolve_path(
+            &import.module_path,
+            None,
+            &import.parent_scope,
+            Some(finalize),
+            ignore_binding,
+        );
+
+        let no_ambiguity = self.ambiguity_errors.len() == prev_ambiguity_errors_len;
+        import.vis.set(orig_vis);
+        let module = match path_res {
+            PathResult::Module(module) => {
+                // Consistency checks, analogous to `finalize_macro_resolutions`.
+                if let Some(initial_module) = import.imported_module.get() {
+                    if module != initial_module && no_ambiguity {
+                        span_bug!(import.span, "inconsistent resolution for an import");
+                    }
+                } else if self.privacy_errors.is_empty() {
+                    self.dcx()
+                        .create_err(CannotDetermineImportResolution { span: import.span })
+                        .emit();
+                }
+
+                module
+            }
+            PathResult::Failed {
+                is_error_from_last_segment: false,
+                span,
+                segment_name,
+                label,
+                suggestion,
+                module,
+            } => {
+                if no_ambiguity {
+                    assert!(import.imported_module.get().is_none());
+                    self.report_error(
+                        span,
+                        ResolutionError::FailedToResolve {
+                            segment: Some(segment_name),
+                            label,
+                            suggestion,
+                            module,
+                        },
+                    );
+                }
+                return None;
+            }
+            PathResult::Failed {
+                is_error_from_last_segment: true,
+                span,
+                label,
+                suggestion,
+                ..
+            } => {
+                if no_ambiguity {
+                    assert!(import.imported_module.get().is_none());
+                    let err = match self.make_path_suggestion(
+                        span,
+                        import.module_path.clone(),
+                        &import.parent_scope,
+                    ) {
+                        Some((suggestion, note)) => UnresolvedImportError {
+                            span,
+                            label: None,
+                            note,
+                            suggestion: Some((
+                                vec![(span, Segment::names_to_string(&suggestion))],
+                                String::from("a similar path exists"),
+                                Applicability::MaybeIncorrect,
+                            )),
+                            candidates: None,
+                        },
+                        None => UnresolvedImportError {
+                            span,
+                            label: Some(label),
+                            note: None,
+                            suggestion,
+                            candidates: None,
+                        },
+                    };
+                    return Some(err);
+                }
+                return None;
+            }
+            PathResult::NonModule(partial_res) => {
+                if no_ambiguity && partial_res.full_res() != Some(Res::Err) {
+                    // Check if there are no ambiguities and the result is not dummy.
+                    assert!(import.imported_module.get().is_none());
+                }
+                // The error was already reported earlier.
+                return None;
+            }
+            PathResult::Indeterminate => unreachable!(),
+        };
+
+        let (ident, target, source_bindings, target_bindings, type_ns_only, import_id) =
+            match import.kind {
+                ImportKind::Single {
+                    source,
+                    target,
+                    ref source_bindings,
+                    ref target_bindings,
+                    type_ns_only,
+                    id,
+                    ..
+                } => (source, target, source_bindings, target_bindings, type_ns_only, id),
+                ImportKind::Glob { is_prelude, ref max_vis, id } => {
+                    if import.module_path.len() <= 1 {
+                        // HACK(eddyb) `lint_if_path_starts_with_module` needs at least
+                        // 2 segments, so the `resolve_path` above won't trigger it.
+                        let mut full_path = import.module_path.clone();
+                        full_path.push(Segment::from_ident(Ident::empty()));
+                        self.lint_if_path_starts_with_module(Some(finalize), &full_path, None);
+                    }
+
+                    if let ModuleOrUniformRoot::Module(module) = module {
+                        if module == import.parent_scope.module {
+                            // Importing a module into itself is not allowed.
+                            return Some(UnresolvedImportError {
+                                span: import.span,
+                                label: Some(String::from(
+                                    "cannot glob-import a module into itself",
+                                )),
+                                note: None,
+                                suggestion: None,
+                                candidates: None,
+                            });
+                        }
+                    }
+                    if !is_prelude
+                        && let Some(max_vis) = max_vis.get()
+                        && let import_vis = import.expect_vis()
+                        && !max_vis.is_at_least(import_vis, self.tcx)
+                    {
+                        let def_id = self.local_def_id(id);
+                        let msg = format!(
+                            "glob import doesn't reexport anything with visibility `{}` because no imported item is public enough",
+                            import_vis.to_string(def_id, self.tcx)
+                        );
+                        self.lint_buffer.buffer_lint_with_diagnostic(
+                            UNUSED_IMPORTS,
+                            id,
+                            import.span,
+                            msg,
+                            BuiltinLintDiagnostics::RedundantImportVisibility {
+                                max_vis: max_vis.to_string(def_id, self.tcx),
+                                span: import.span,
+                            },
+                        );
+                    }
+                    return None;
+                }
+                _ => unreachable!(),
+            };
+
+        if self.privacy_errors.len() != privacy_errors_len {
+            // Get the Res for the last element, so that we can point to alternative ways of
+            // importing it if available.
+            let mut path = import.module_path.clone();
+            path.push(Segment::from_ident(ident));
+            if let PathResult::Module(ModuleOrUniformRoot::Module(module)) =
+                self.resolve_path(&path, None, &import.parent_scope, Some(finalize), ignore_binding)
+            {
+                let res = module.res().map(|r| (r, ident));
+                for error in &mut self.privacy_errors[privacy_errors_len..] {
+                    error.outermost_res = res;
+                }
+            }
+        }
+
+        let mut all_ns_err = true;
+        self.per_ns(|this, ns| {
+            if !type_ns_only || ns == TypeNS {
+                let orig_vis = import.vis.take();
+                let binding = this.resolve_ident_in_module(
+                    module,
+                    ident,
+                    ns,
+                    &import.parent_scope,
+                    Some(Finalize { report_private: false, ..finalize }),
+                    target_bindings[ns].get(),
+                );
+                import.vis.set(orig_vis);
+
+                match binding {
+                    Ok(binding) => {
+                        // Consistency checks, analogous to `finalize_macro_resolutions`.
+                        let initial_res = source_bindings[ns].get().map(|initial_binding| {
+                            all_ns_err = false;
+                            if let Some(target_binding) = target_bindings[ns].get() {
+                                if target.name == kw::Underscore
+                                    && initial_binding.is_extern_crate()
+                                    && !initial_binding.is_import()
+                                {
+                                    this.record_use(
+                                        ident,
+                                        target_binding,
+                                        import.module_path.is_empty(),
+                                    );
+                                }
+                            }
+                            initial_binding.res()
+                        });
+                        let res = binding.res();
+                        let has_ambiguity_error =
+                            this.ambiguity_errors.iter().any(|error| !error.warning);
+                        if res == Res::Err || has_ambiguity_error {
+                            this.dcx()
+                                .span_delayed_bug(import.span, "some error happened for an import");
+                            return;
+                        }
+                        if let Ok(initial_res) = initial_res {
+                            if res != initial_res {
+                                span_bug!(import.span, "inconsistent resolution for an import");
+                            }
+                        } else if this.privacy_errors.is_empty() {
+                            this.dcx()
+                                .create_err(CannotDetermineImportResolution { span: import.span })
+                                .emit();
+                        }
+                    }
+                    Err(..) => {
+                        // FIXME: This assert may fire if public glob is later shadowed by a private
+                        // single import (see test `issue-55884-2.rs`). In theory single imports should
+                        // always block globs, even if they are not yet resolved, so that this kind of
+                        // self-inconsistent resolution never happens.
+                        // Re-enable the assert when the issue is fixed.
+                        // assert!(result[ns].get().is_err());
+                    }
+                }
+            }
+        });
+
+        if all_ns_err {
+            let mut all_ns_failed = true;
+            self.per_ns(|this, ns| {
+                if !type_ns_only || ns == TypeNS {
+                    let binding = this.resolve_ident_in_module(
+                        module,
+                        ident,
+                        ns,
+                        &import.parent_scope,
+                        Some(finalize),
+                        None,
+                    );
+                    if binding.is_ok() {
+                        all_ns_failed = false;
+                    }
+                }
+            });
+
+            return if all_ns_failed {
+                let resolutions = match module {
+                    ModuleOrUniformRoot::Module(module) => Some(self.resolutions(module).borrow()),
+                    _ => None,
+                };
+                let resolutions = resolutions.as_ref().into_iter().flat_map(|r| r.iter());
+                let names = resolutions
+                    .filter_map(|(BindingKey { ident: i, .. }, resolution)| {
+                        if i.name == ident.name {
+                            return None;
+                        } // Never suggest the same name
+                        match *resolution.borrow() {
+                            NameResolution { binding: Some(name_binding), .. } => {
+                                match name_binding.kind {
+                                    NameBindingKind::Import { binding, .. } => {
+                                        match binding.kind {
+                                            // Never suggest the name that has binding error
+                                            // i.e., the name that cannot be previously resolved
+                                            NameBindingKind::Res(Res::Err) => None,
+                                            _ => Some(i.name),
+                                        }
+                                    }
+                                    _ => Some(i.name),
+                                }
+                            }
+                            NameResolution { ref single_imports, .. }
+                                if single_imports.is_empty() =>
+                            {
+                                None
+                            }
+                            _ => Some(i.name),
+                        }
+                    })
+                    .collect::<Vec<Symbol>>();
+
+                let lev_suggestion =
+                    find_best_match_for_name(&names, ident.name, None).map(|suggestion| {
+                        (
+                            vec![(ident.span, suggestion.to_string())],
+                            String::from("a similar name exists in the module"),
+                            Applicability::MaybeIncorrect,
+                        )
+                    });
+
+                let (suggestion, note) =
+                    match self.check_for_module_export_macro(import, module, ident) {
+                        Some((suggestion, note)) => (suggestion.or(lev_suggestion), note),
+                        _ => (lev_suggestion, None),
+                    };
+
+                let label = match module {
+                    ModuleOrUniformRoot::Module(module) => {
+                        let module_str = module_to_string(module);
+                        if let Some(module_str) = module_str {
+                            format!("no `{ident}` in `{module_str}`")
+                        } else {
+                            format!("no `{ident}` in the root")
+                        }
+                    }
+                    _ => {
+                        if !ident.is_path_segment_keyword() {
+                            format!("no external crate `{ident}`")
+                        } else {
+                            // HACK(eddyb) this shows up for `self` & `super`, which
+                            // should work instead - for now keep the same error message.
+                            format!("no `{ident}` in the root")
+                        }
+                    }
+                };
+
+                let parent_suggestion =
+                    self.lookup_import_candidates(ident, TypeNS, &import.parent_scope, |_| true);
+
+                Some(UnresolvedImportError {
+                    span: import.span,
+                    label: Some(label),
+                    note,
+                    suggestion,
+                    candidates: if !parent_suggestion.is_empty() {
+                        Some(parent_suggestion)
+                    } else {
+                        None
+                    },
+                })
+            } else {
+                // `resolve_ident_in_module` reported a privacy error.
+                None
+            };
+        }
+
+        let mut reexport_error = None;
+        let mut any_successful_reexport = false;
+        let mut crate_private_reexport = false;
+        self.per_ns(|this, ns| {
+            if let Ok(binding) = source_bindings[ns].get() {
+                if !binding.vis.is_at_least(import.expect_vis(), this.tcx) {
+                    reexport_error = Some((ns, binding));
+                    if let ty::Visibility::Restricted(binding_def_id) = binding.vis {
+                        if binding_def_id.is_top_level_module() {
+                            crate_private_reexport = true;
+                        }
+                    }
+                } else {
+                    any_successful_reexport = true;
+                }
+            }
+        });
+
+        // All namespaces must be re-exported with extra visibility for an error to occur.
+        if !any_successful_reexport {
+            let (ns, binding) = reexport_error.unwrap();
+            if pub_use_of_private_extern_crate_hack(import, binding) {
+                let msg = format!(
+                    "extern crate `{ident}` is private, and cannot be \
+                                   re-exported (error E0365), consider declaring with \
+                                   `pub`"
+                );
+                self.lint_buffer.buffer_lint(
+                    PUB_USE_OF_PRIVATE_EXTERN_CRATE,
+                    import_id,
+                    import.span,
+                    msg,
+                );
+            } else {
+                if ns == TypeNS {
+                    let err = if crate_private_reexport {
+                        self.dcx().create_err(CannotBeReexportedCratePublicNS {
+                            span: import.span,
+                            ident,
+                        })
+                    } else {
+                        self.dcx()
+                            .create_err(CannotBeReexportedPrivateNS { span: import.span, ident })
+                    };
+                    err.emit();
+                } else {
+                    let mut err = if crate_private_reexport {
+                        self.dcx()
+                            .create_err(CannotBeReexportedCratePublic { span: import.span, ident })
+                    } else {
+                        self.dcx()
+                            .create_err(CannotBeReexportedPrivate { span: import.span, ident })
+                    };
+
+                    match binding.kind {
+                        NameBindingKind::Res(Res::Def(DefKind::Macro(_), def_id))
+                            // exclude decl_macro
+                            if self.get_macro_by_def_id(def_id).macro_rules =>
+                        {
+                            err.subdiagnostic(ConsiderAddingMacroExport {
+                                span: binding.span,
+                            });
+                        }
+                        _ => {
+                            err.subdiagnostic(ConsiderMarkingAsPub {
+                                span: import.span,
+                                ident,
+                            });
+                        }
+                    }
+                    err.emit();
+                }
+            }
+        }
+
+        if import.module_path.len() <= 1 {
+            // HACK(eddyb) `lint_if_path_starts_with_module` needs at least
+            // 2 segments, so the `resolve_path` above won't trigger it.
+            let mut full_path = import.module_path.clone();
+            full_path.push(Segment::from_ident(ident));
+            self.per_ns(|this, ns| {
+                if let Ok(binding) = source_bindings[ns].get() {
+                    this.lint_if_path_starts_with_module(Some(finalize), &full_path, Some(binding));
+                }
+            });
+        }
+
+        // Record what this import resolves to for later uses in documentation,
+        // this may resolve to either a value or a type, but for documentation
+        // purposes it's good enough to just favor one over the other.
+        self.per_ns(|this, ns| {
+            if let Ok(binding) = source_bindings[ns].get() {
+                this.import_res_map.entry(import_id).or_default()[ns] = Some(binding.res());
+            }
+        });
+
+        self.check_for_redundant_imports(ident, import, source_bindings, target_bindings, target);
+
+        debug!("(resolving single import) successfully resolved import");
+        None
+    }
+
+    fn check_for_redundant_imports(
+        &mut self,
+        ident: Ident,
+        import: Import<'a>,
+        source_bindings: &PerNS<Cell<Result<NameBinding<'a>, Determinacy>>>,
+        target_bindings: &PerNS<Cell<Option<NameBinding<'a>>>>,
+        target: Ident,
+    ) {
+        // This function is only called for single imports.
+        let ImportKind::Single { id, .. } = import.kind else { unreachable!() };
+
+        // Skip if the import was produced by a macro.
+        if import.parent_scope.expansion != LocalExpnId::ROOT {
+            return;
+        }
+
+        // Skip if we are inside a named module (in contrast to an anonymous
+        // module defined by a block).
+        if let ModuleKind::Def(..) = import.parent_scope.module.kind {
+            return;
+        }
+
+        let mut is_redundant = PerNS { value_ns: None, type_ns: None, macro_ns: None };
+
+        let mut redundant_span = PerNS { value_ns: None, type_ns: None, macro_ns: None };
+
+        self.per_ns(|this, ns| {
+            if let Ok(binding) = source_bindings[ns].get() {
+                if binding.res() == Res::Err {
+                    return;
+                }
+
+                match this.early_resolve_ident_in_lexical_scope(
+                    target,
+                    ScopeSet::All(ns),
+                    &import.parent_scope,
+                    None,
+                    false,
+                    target_bindings[ns].get(),
+                ) {
+                    Ok(other_binding) => {
+                        is_redundant[ns] = Some(
+                            binding.res() == other_binding.res() && !other_binding.is_ambiguity(),
+                        );
+                        redundant_span[ns] = Some((other_binding.span, other_binding.is_import()));
+                    }
+                    Err(_) => is_redundant[ns] = Some(false),
+                }
+            }
+        });
+
+        if !is_redundant.is_empty() && is_redundant.present_items().all(|is_redundant| is_redundant)
+        {
+            let mut redundant_spans: Vec<_> = redundant_span.present_items().collect();
+            redundant_spans.sort();
+            redundant_spans.dedup();
+            self.lint_buffer.buffer_lint_with_diagnostic(
+                UNUSED_IMPORTS,
+                id,
+                import.span,
+                format!("the item `{ident}` is imported redundantly"),
+                BuiltinLintDiagnostics::RedundantImport(redundant_spans, ident),
+            );
+        }
+    }
+
+    fn resolve_glob_import(&mut self, import: Import<'a>) {
+        // This function is only called for glob imports.
+        let ImportKind::Glob { id, is_prelude, .. } = import.kind else { unreachable!() };
+
+        let ModuleOrUniformRoot::Module(module) = import.imported_module.get().unwrap() else {
+            self.dcx().emit_err(CannotGlobImportAllCrates { span: import.span });
+            return;
+        };
+
+        if module.is_trait() {
+            self.dcx().emit_err(ItemsInTraitsAreNotImportable { span: import.span });
+            return;
+        } else if module == import.parent_scope.module {
+            return;
+        } else if is_prelude {
+            self.prelude = Some(module);
+            return;
+        }
+
+        // Add to module's glob_importers
+        module.glob_importers.borrow_mut().push(import);
+
+        // Ensure that `resolutions` isn't borrowed during `try_define`,
+        // since it might get updated via a glob cycle.
+        let bindings = self
+            .resolutions(module)
+            .borrow()
+            .iter()
+            .filter_map(|(key, resolution)| {
+                resolution.borrow().binding().map(|binding| (*key, binding))
+            })
+            .collect::<Vec<_>>();
+        for (mut key, binding) in bindings {
+            let scope = match key.ident.span.reverse_glob_adjust(module.expansion, import.span) {
+                Some(Some(def)) => self.expn_def_scope(def),
+                Some(None) => import.parent_scope.module,
+                None => continue,
+            };
+            if self.is_accessible_from(binding.vis, scope) {
+                let imported_binding = self.import(binding, import);
+                let warn_ambiguity = self
+                    .resolution(import.parent_scope.module, key)
+                    .borrow()
+                    .binding()
+                    .is_some_and(|binding| binding.is_warn_ambiguity());
+                let _ = self.try_define(
+                    import.parent_scope.module,
+                    key,
+                    imported_binding,
+                    warn_ambiguity,
+                );
+            }
+        }
+
+        // Record the destination of this import
+        self.record_partial_res(id, PartialRes::new(module.res().unwrap()));
+    }
+
+    // Miscellaneous post-processing, including recording re-exports,
+    // reporting conflicts, and reporting unresolved imports.
+    fn finalize_resolutions_in(&mut self, module: Module<'a>) {
+        // Since import resolution is finished, globs will not define any more names.
+        *module.globs.borrow_mut() = Vec::new();
+
+        if let Some(def_id) = module.opt_def_id() {
+            let mut children = Vec::new();
+
+            module.for_each_child(self, |this, ident, _, binding| {
+                let res = binding.res().expect_non_local();
+                let error_ambiguity = binding.is_ambiguity() && !binding.warn_ambiguity;
+                if res != def::Res::Err && !error_ambiguity {
+                    let mut reexport_chain = SmallVec::new();
+                    let mut next_binding = binding;
+                    while let NameBindingKind::Import { binding, import, .. } = next_binding.kind {
+                        reexport_chain.push(import.simplify(this));
+                        next_binding = binding;
+                    }
+
+                    children.push(ModChild { ident, res, vis: binding.vis, reexport_chain });
+                }
+            });
+
+            if !children.is_empty() {
+                // Should be fine because this code is only called for local modules.
+                self.module_children.insert(def_id.expect_local(), children);
+            }
+        }
+    }
+}
+
+fn import_path_to_string(names: &[Ident], import_kind: &ImportKind<'_>, span: Span) -> String {
+    let pos = names.iter().position(|p| span == p.span && p.name != kw::PathRoot);
+    let global = !names.is_empty() && names[0].name == kw::PathRoot;
+    if let Some(pos) = pos {
+        let names = if global { &names[1..pos + 1] } else { &names[..pos + 1] };
+        names_to_string(&names.iter().map(|ident| ident.name).collect::<Vec<_>>())
+    } else {
+        let names = if global { &names[1..] } else { names };
+        if names.is_empty() {
+            import_kind_to_string(import_kind)
+        } else {
+            format!(
+                "{}::{}",
+                names_to_string(&names.iter().map(|ident| ident.name).collect::<Vec<_>>()),
+                import_kind_to_string(import_kind),
+            )
+        }
+    }
+}
+
+fn import_kind_to_string(import_kind: &ImportKind<'_>) -> String {
+    match import_kind {
+        ImportKind::Single { source, .. } => source.to_string(),
+        ImportKind::Glob { .. } => "*".to_string(),
+        ImportKind::ExternCrate { .. } => "<extern crate>".to_string(),
+        ImportKind::MacroUse => "#[macro_use]".to_string(),
+        ImportKind::MacroExport => "#[macro_export]".to_string(),
+    }
+}
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
new file mode 100644
index 00000000000..82f50227911
--- /dev/null
+++ b/compiler/rustc_resolve/src/late.rs
@@ -0,0 +1,4689 @@
+// ignore-tidy-filelength
+//! "Late resolution" is the pass that resolves most of names in a crate beside imports and macros.
+//! It runs when the crate is fully expanded and its module structure is fully built.
+//! So it just walks through the crate and resolves all the expressions, types, etc.
+//!
+//! If you wonder why there's no `early.rs`, that's because it's split into three files -
+//! `build_reduced_graph.rs`, `macros.rs` and `imports.rs`.
+
+use crate::errors::ImportsCannotReferTo;
+use crate::BindingKey;
+use crate::{path_names_to_string, rustdoc, BindingError, Finalize, LexicalScopeBinding};
+use crate::{Module, ModuleOrUniformRoot, NameBinding, ParentScope, PathResult};
+use crate::{ResolutionError, Resolver, Segment, UseError};
+
+use rustc_ast::ptr::P;
+use rustc_ast::visit::{self, AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor};
+use rustc_ast::*;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
+use rustc_errors::{Applicability, DiagnosticArgValue, DiagnosticId, IntoDiagnosticArg};
+use rustc_hir::def::Namespace::{self, *};
+use rustc_hir::def::{self, CtorKind, DefKind, LifetimeRes, NonMacroAttrKind, PartialRes, PerNS};
+use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE};
+use rustc_hir::{BindingAnnotation, PrimTy, TraitCandidate};
+use rustc_middle::middle::resolve_bound_vars::Set1;
+use rustc_middle::{bug, span_bug};
+use rustc_session::config::{CrateType, ResolveDocLinks};
+use rustc_session::lint;
+use rustc_span::source_map::{respan, Spanned};
+use rustc_span::symbol::{kw, sym, Ident, Symbol};
+use rustc_span::{BytePos, Span, SyntaxContext};
+use smallvec::{smallvec, SmallVec};
+
+use std::assert_matches::debug_assert_matches;
+use std::borrow::Cow;
+use std::collections::{hash_map::Entry, BTreeSet};
+use std::mem::{replace, swap, take};
+
+mod diagnostics;
+
+type Res = def::Res<NodeId>;
+
+type IdentMap<T> = FxHashMap<Ident, T>;
+
+use diagnostics::{
+    ElisionFnParameter, LifetimeElisionCandidate, MissingLifetime, MissingLifetimeKind,
+};
+
+#[derive(Copy, Clone, Debug)]
+struct BindingInfo {
+    span: Span,
+    annotation: BindingAnnotation,
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+pub(crate) enum PatternSource {
+    Match,
+    Let,
+    For,
+    FnParam,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+enum IsRepeatExpr {
+    No,
+    Yes,
+}
+
+/// Describes whether an `AnonConst` is a type level const arg or
+/// some other form of anon const (i.e. inline consts or enum discriminants)
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+enum AnonConstKind {
+    EnumDiscriminant,
+    InlineConst,
+    ConstArg(IsRepeatExpr),
+}
+
+impl PatternSource {
+    fn descr(self) -> &'static str {
+        match self {
+            PatternSource::Match => "match binding",
+            PatternSource::Let => "let binding",
+            PatternSource::For => "for binding",
+            PatternSource::FnParam => "function parameter",
+        }
+    }
+}
+
+impl IntoDiagnosticArg for PatternSource {
+    fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
+        DiagnosticArgValue::Str(Cow::Borrowed(self.descr()))
+    }
+}
+
+/// Denotes whether the context for the set of already bound bindings is a `Product`
+/// or `Or` context. This is used in e.g., `fresh_binding` and `resolve_pattern_inner`.
+/// See those functions for more information.
+#[derive(PartialEq)]
+enum PatBoundCtx {
+    /// A product pattern context, e.g., `Variant(a, b)`.
+    Product,
+    /// An or-pattern context, e.g., `p_0 | ... | p_n`.
+    Or,
+}
+
+/// Does this the item (from the item rib scope) allow generic parameters?
+#[derive(Copy, Clone, Debug)]
+pub(crate) enum HasGenericParams {
+    Yes(Span),
+    No,
+}
+
+/// May this constant have generics?
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+pub(crate) enum ConstantHasGenerics {
+    Yes,
+    No(NoConstantGenericsReason),
+}
+
+impl ConstantHasGenerics {
+    fn force_yes_if(self, b: bool) -> Self {
+        if b { Self::Yes } else { self }
+    }
+}
+
+/// Reason for why an anon const is not allowed to reference generic parameters
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+pub(crate) enum NoConstantGenericsReason {
+    /// Const arguments are only allowed to use generic parameters when:
+    /// - `feature(generic_const_exprs)` is enabled
+    /// or
+    /// - the const argument is a sole const generic paramater, i.e. `foo::<{ N }>()`
+    ///
+    /// If neither of the above are true then this is used as the cause.
+    NonTrivialConstArg,
+    /// Enum discriminants are not allowed to reference generic parameters ever, this
+    /// is used when an anon const is in the following position:
+    ///
+    /// ```rust,compile_fail
+    /// enum Foo<const N: isize> {
+    ///     Variant = { N }, // this anon const is not allowed to use generics
+    /// }
+    /// ```
+    IsEnumDiscriminant,
+}
+
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+pub(crate) enum ConstantItemKind {
+    Const,
+    Static,
+}
+
+impl ConstantItemKind {
+    pub(crate) fn as_str(&self) -> &'static str {
+        match self {
+            Self::Const => "const",
+            Self::Static => "static",
+        }
+    }
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+enum RecordPartialRes {
+    Yes,
+    No,
+}
+
+/// The rib kind restricts certain accesses,
+/// e.g. to a `Res::Local` of an outer item.
+#[derive(Copy, Clone, Debug)]
+pub(crate) enum RibKind<'a> {
+    /// No restriction needs to be applied.
+    Normal,
+
+    /// We passed through an impl or trait and are now in one of its
+    /// methods or associated types. Allow references to ty params that impl or trait
+    /// binds. Disallow any other upvars (including other ty params that are
+    /// upvars).
+    AssocItem,
+
+    /// We passed through a function, closure or coroutine signature. Disallow labels.
+    FnOrCoroutine,
+
+    /// We passed through an item scope. Disallow upvars.
+    Item(HasGenericParams),
+
+    /// We're in a constant item. Can't refer to dynamic stuff.
+    ///
+    /// The item may reference generic parameters in trivial constant expressions.
+    /// All other constants aren't allowed to use generic params at all.
+    ConstantItem(ConstantHasGenerics, Option<(Ident, ConstantItemKind)>),
+
+    /// We passed through a module.
+    Module(Module<'a>),
+
+    /// We passed through a `macro_rules!` statement
+    MacroDefinition(DefId),
+
+    /// All bindings in this rib are generic parameters that can't be used
+    /// from the default of a generic parameter because they're not declared
+    /// before said generic parameter. Also see the `visit_generics` override.
+    ForwardGenericParamBan,
+
+    /// We are inside of the type of a const parameter. Can't refer to any
+    /// parameters.
+    ConstParamTy,
+
+    /// We are inside a `sym` inline assembly operand. Can only refer to
+    /// globals.
+    InlineAsmSym,
+}
+
+impl RibKind<'_> {
+    /// Whether this rib kind contains generic parameters, as opposed to local
+    /// variables.
+    pub(crate) fn contains_params(&self) -> bool {
+        match self {
+            RibKind::Normal
+            | RibKind::FnOrCoroutine
+            | RibKind::ConstantItem(..)
+            | RibKind::Module(_)
+            | RibKind::MacroDefinition(_)
+            | RibKind::ConstParamTy
+            | RibKind::InlineAsmSym => false,
+            RibKind::AssocItem | RibKind::Item(_) | RibKind::ForwardGenericParamBan => true,
+        }
+    }
+
+    /// This rib forbids referring to labels defined in upwards ribs.
+    fn is_label_barrier(self) -> bool {
+        match self {
+            RibKind::Normal | RibKind::MacroDefinition(..) => false,
+
+            RibKind::AssocItem
+            | RibKind::FnOrCoroutine
+            | RibKind::Item(..)
+            | RibKind::ConstantItem(..)
+            | RibKind::Module(..)
+            | RibKind::ForwardGenericParamBan
+            | RibKind::ConstParamTy
+            | RibKind::InlineAsmSym => true,
+        }
+    }
+}
+
+/// A single local scope.
+///
+/// A rib represents a scope names can live in. Note that these appear in many places, not just
+/// around braces. At any place where the list of accessible names (of the given namespace)
+/// changes or a new restrictions on the name accessibility are introduced, a new rib is put onto a
+/// stack. This may be, for example, a `let` statement (because it introduces variables), a macro,
+/// etc.
+///
+/// Different [rib kinds](enum@RibKind) are transparent for different names.
+///
+/// The resolution keeps a separate stack of ribs as it traverses the AST for each namespace. When
+/// resolving, the name is looked up from inside out.
+#[derive(Debug)]
+pub(crate) struct Rib<'a, R = Res> {
+    pub bindings: IdentMap<R>,
+    pub kind: RibKind<'a>,
+}
+
+impl<'a, R> Rib<'a, R> {
+    fn new(kind: RibKind<'a>) -> Rib<'a, R> {
+        Rib { bindings: Default::default(), kind }
+    }
+}
+
+#[derive(Clone, Copy, Debug)]
+enum LifetimeUseSet {
+    One { use_span: Span, use_ctxt: visit::LifetimeCtxt },
+    Many,
+}
+
+#[derive(Copy, Clone, Debug)]
+enum LifetimeRibKind {
+    // -- Ribs introducing named lifetimes
+    //
+    /// This rib declares generic parameters.
+    /// Only for this kind the `LifetimeRib::bindings` field can be non-empty.
+    Generics { binder: NodeId, span: Span, kind: LifetimeBinderKind },
+
+    // -- Ribs introducing unnamed lifetimes
+    //
+    /// Create a new anonymous lifetime parameter and reference it.
+    ///
+    /// If `report_in_path`, report an error when encountering lifetime elision in a path:
+    /// ```compile_fail
+    /// struct Foo<'a> { x: &'a () }
+    /// async fn foo(x: Foo) {}
+    /// ```
+    ///
+    /// Note: the error should not trigger when the elided lifetime is in a pattern or
+    /// expression-position path:
+    /// ```
+    /// struct Foo<'a> { x: &'a () }
+    /// async fn foo(Foo { x: _ }: Foo<'_>) {}
+    /// ```
+    AnonymousCreateParameter { binder: NodeId, report_in_path: bool },
+
+    /// Replace all anonymous lifetimes by provided lifetime.
+    Elided(LifetimeRes),
+
+    // -- Barrier ribs that stop lifetime lookup, or continue it but produce an error later.
+    //
+    /// Give a hard error when either `&` or `'_` is written. Used to
+    /// rule out things like `where T: Foo<'_>`. Does not imply an
+    /// error on default object bounds (e.g., `Box<dyn Foo>`).
+    AnonymousReportError,
+
+    /// Resolves elided lifetimes to `'static`, but gives a warning that this behavior
+    /// is a bug and will be reverted soon.
+    AnonymousWarn(NodeId),
+
+    /// Signal we cannot find which should be the anonymous lifetime.
+    ElisionFailure,
+
+    /// This rib forbids usage of generic parameters inside of const parameter types.
+    ///
+    /// While this is desirable to support eventually, it is difficult to do and so is
+    /// currently forbidden. See rust-lang/project-const-generics#28 for more info.
+    ConstParamTy,
+
+    /// Usage of generic parameters is forbidden in various positions for anon consts:
+    /// - const arguments when `generic_const_exprs` is not enabled
+    /// - enum discriminant values
+    ///
+    /// This rib emits an error when a lifetime would resolve to a lifetime parameter.
+    ConcreteAnonConst(NoConstantGenericsReason),
+
+    /// This rib acts as a barrier to forbid reference to lifetimes of a parent item.
+    Item,
+}
+
+#[derive(Copy, Clone, Debug)]
+enum LifetimeBinderKind {
+    BareFnType,
+    PolyTrait,
+    WhereBound,
+    Item,
+    ConstItem,
+    Function,
+    Closure,
+    ImplBlock,
+}
+
+impl LifetimeBinderKind {
+    fn descr(self) -> &'static str {
+        use LifetimeBinderKind::*;
+        match self {
+            BareFnType => "type",
+            PolyTrait => "bound",
+            WhereBound => "bound",
+            Item | ConstItem => "item",
+            ImplBlock => "impl block",
+            Function => "function",
+            Closure => "closure",
+        }
+    }
+}
+
+#[derive(Debug)]
+struct LifetimeRib {
+    kind: LifetimeRibKind,
+    // We need to preserve insertion order for async fns.
+    bindings: FxIndexMap<Ident, (NodeId, LifetimeRes)>,
+}
+
+impl LifetimeRib {
+    fn new(kind: LifetimeRibKind) -> LifetimeRib {
+        LifetimeRib { bindings: Default::default(), kind }
+    }
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+pub(crate) enum AliasPossibility {
+    No,
+    Maybe,
+}
+
+#[derive(Copy, Clone, Debug)]
+pub(crate) enum PathSource<'a> {
+    // Type paths `Path`.
+    Type,
+    // Trait paths in bounds or impls.
+    Trait(AliasPossibility),
+    // Expression paths `path`, with optional parent context.
+    Expr(Option<&'a Expr>),
+    // Paths in path patterns `Path`.
+    Pat,
+    // Paths in struct expressions and patterns `Path { .. }`.
+    Struct,
+    // Paths in tuple struct patterns `Path(..)`.
+    TupleStruct(Span, &'a [Span]),
+    // `m::A::B` in `<T as m::A>::B::C`.
+    TraitItem(Namespace),
+    // Paths in delegation item
+    Delegation,
+}
+
+impl<'a> PathSource<'a> {
+    fn namespace(self) -> Namespace {
+        match self {
+            PathSource::Type | PathSource::Trait(_) | PathSource::Struct => TypeNS,
+            PathSource::Expr(..)
+            | PathSource::Pat
+            | PathSource::TupleStruct(..)
+            | PathSource::Delegation => ValueNS,
+            PathSource::TraitItem(ns) => ns,
+        }
+    }
+
+    fn defer_to_typeck(self) -> bool {
+        match self {
+            PathSource::Type
+            | PathSource::Expr(..)
+            | PathSource::Pat
+            | PathSource::Struct
+            | PathSource::TupleStruct(..) => true,
+            PathSource::Trait(_) | PathSource::TraitItem(..) | PathSource::Delegation => false,
+        }
+    }
+
+    fn descr_expected(self) -> &'static str {
+        match &self {
+            PathSource::Type => "type",
+            PathSource::Trait(_) => "trait",
+            PathSource::Pat => "unit struct, unit variant or constant",
+            PathSource::Struct => "struct, variant or union type",
+            PathSource::TupleStruct(..) => "tuple struct or tuple variant",
+            PathSource::TraitItem(ns) => match ns {
+                TypeNS => "associated type",
+                ValueNS => "method or associated constant",
+                MacroNS => bug!("associated macro"),
+            },
+            PathSource::Expr(parent) => match parent.as_ref().map(|p| &p.kind) {
+                // "function" here means "anything callable" rather than `DefKind::Fn`,
+                // this is not precise but usually more helpful than just "value".
+                Some(ExprKind::Call(call_expr, _)) => match &call_expr.kind {
+                    // the case of `::some_crate()`
+                    ExprKind::Path(_, path)
+                        if path.segments.len() == 2
+                            && path.segments[0].ident.name == kw::PathRoot =>
+                    {
+                        "external crate"
+                    }
+                    ExprKind::Path(_, path) => {
+                        let mut msg = "function";
+                        if let Some(segment) = path.segments.iter().last() {
+                            if let Some(c) = segment.ident.to_string().chars().next() {
+                                if c.is_uppercase() {
+                                    msg = "function, tuple struct or tuple variant";
+                                }
+                            }
+                        }
+                        msg
+                    }
+                    _ => "function",
+                },
+                _ => "value",
+            },
+            PathSource::Delegation => "function",
+        }
+    }
+
+    fn is_call(self) -> bool {
+        matches!(self, PathSource::Expr(Some(&Expr { kind: ExprKind::Call(..), .. })))
+    }
+
+    pub(crate) fn is_expected(self, res: Res) -> bool {
+        match self {
+            PathSource::Type => matches!(
+                res,
+                Res::Def(
+                    DefKind::Struct
+                        | DefKind::Union
+                        | DefKind::Enum
+                        | DefKind::Trait
+                        | DefKind::TraitAlias
+                        | DefKind::TyAlias
+                        | DefKind::AssocTy
+                        | DefKind::TyParam
+                        | DefKind::OpaqueTy
+                        | DefKind::ForeignTy,
+                    _,
+                ) | Res::PrimTy(..)
+                    | Res::SelfTyParam { .. }
+                    | Res::SelfTyAlias { .. }
+            ),
+            PathSource::Trait(AliasPossibility::No) => matches!(res, Res::Def(DefKind::Trait, _)),
+            PathSource::Trait(AliasPossibility::Maybe) => {
+                matches!(res, Res::Def(DefKind::Trait | DefKind::TraitAlias, _))
+            }
+            PathSource::Expr(..) => matches!(
+                res,
+                Res::Def(
+                    DefKind::Ctor(_, CtorKind::Const | CtorKind::Fn)
+                        | DefKind::Const
+                        | DefKind::Static(_)
+                        | DefKind::Fn
+                        | DefKind::AssocFn
+                        | DefKind::AssocConst
+                        | DefKind::ConstParam,
+                    _,
+                ) | Res::Local(..)
+                    | Res::SelfCtor(..)
+            ),
+            PathSource::Pat => {
+                res.expected_in_unit_struct_pat()
+                    || matches!(res, Res::Def(DefKind::Const | DefKind::AssocConst, _))
+            }
+            PathSource::TupleStruct(..) => res.expected_in_tuple_struct_pat(),
+            PathSource::Struct => matches!(
+                res,
+                Res::Def(
+                    DefKind::Struct
+                        | DefKind::Union
+                        | DefKind::Variant
+                        | DefKind::TyAlias
+                        | DefKind::AssocTy,
+                    _,
+                ) | Res::SelfTyParam { .. }
+                    | Res::SelfTyAlias { .. }
+            ),
+            PathSource::TraitItem(ns) => match res {
+                Res::Def(DefKind::AssocConst | DefKind::AssocFn, _) if ns == ValueNS => true,
+                Res::Def(DefKind::AssocTy, _) if ns == TypeNS => true,
+                _ => false,
+            },
+            PathSource::Delegation => matches!(res, Res::Def(DefKind::Fn | DefKind::AssocFn, _)),
+        }
+    }
+
+    fn error_code(self, has_unexpected_resolution: bool) -> DiagnosticId {
+        use rustc_errors::error_code;
+        match (self, has_unexpected_resolution) {
+            (PathSource::Trait(_), true) => error_code!(E0404),
+            (PathSource::Trait(_), false) => error_code!(E0405),
+            (PathSource::Type, true) => error_code!(E0573),
+            (PathSource::Type, false) => error_code!(E0412),
+            (PathSource::Struct, true) => error_code!(E0574),
+            (PathSource::Struct, false) => error_code!(E0422),
+            (PathSource::Expr(..), true) | (PathSource::Delegation, true) => error_code!(E0423),
+            (PathSource::Expr(..), false) | (PathSource::Delegation, false) => error_code!(E0425),
+            (PathSource::Pat | PathSource::TupleStruct(..), true) => error_code!(E0532),
+            (PathSource::Pat | PathSource::TupleStruct(..), false) => error_code!(E0531),
+            (PathSource::TraitItem(..), true) => error_code!(E0575),
+            (PathSource::TraitItem(..), false) => error_code!(E0576),
+        }
+    }
+}
+
+/// At this point for most items we can answer whether that item is exported or not,
+/// but some items like impls require type information to determine exported-ness, so we make a
+/// conservative estimate for them (e.g. based on nominal visibility).
+#[derive(Clone, Copy)]
+enum MaybeExported<'a> {
+    Ok(NodeId),
+    Impl(Option<DefId>),
+    ImplItem(Result<DefId, &'a Visibility>),
+    NestedUse(&'a Visibility),
+}
+
+impl MaybeExported<'_> {
+    fn eval(self, r: &Resolver<'_, '_>) -> bool {
+        let def_id = match self {
+            MaybeExported::Ok(node_id) => Some(r.local_def_id(node_id)),
+            MaybeExported::Impl(Some(trait_def_id)) | MaybeExported::ImplItem(Ok(trait_def_id)) => {
+                trait_def_id.as_local()
+            }
+            MaybeExported::Impl(None) => return true,
+            MaybeExported::ImplItem(Err(vis)) | MaybeExported::NestedUse(vis) => {
+                return vis.kind.is_pub();
+            }
+        };
+        def_id.map_or(true, |def_id| r.effective_visibilities.is_exported(def_id))
+    }
+}
+
+#[derive(Default)]
+struct DiagnosticMetadata<'ast> {
+    /// The current trait's associated items' ident, used for diagnostic suggestions.
+    current_trait_assoc_items: Option<&'ast [P<AssocItem>]>,
+
+    /// The current self type if inside an impl (used for better errors).
+    current_self_type: Option<Ty>,
+
+    /// The current self item if inside an ADT (used for better errors).
+    current_self_item: Option<NodeId>,
+
+    /// The current trait (used to suggest).
+    current_item: Option<&'ast Item>,
+
+    /// When processing generics and encountering a type not found, suggest introducing a type
+    /// param.
+    currently_processing_generics: bool,
+
+    /// The current enclosing (non-closure) function (used for better errors).
+    current_function: Option<(FnKind<'ast>, Span)>,
+
+    /// A list of labels as of yet unused. Labels will be removed from this map when
+    /// they are used (in a `break` or `continue` statement)
+    unused_labels: FxHashMap<NodeId, Span>,
+
+    /// Only used for better errors on `let x = { foo: bar };`.
+    /// In the case of a parse error with `let x = { foo: bar, };`, this isn't needed, it's only
+    /// needed for cases where this parses as a correct type ascription.
+    current_block_could_be_bare_struct_literal: Option<Span>,
+
+    /// Only used for better errors on `let <pat>: <expr, not type>;`.
+    current_let_binding: Option<(Span, Option<Span>, Option<Span>)>,
+
+    current_pat: Option<&'ast Pat>,
+
+    /// Used to detect possible `if let` written without `let` and to provide structured suggestion.
+    in_if_condition: Option<&'ast Expr>,
+
+    /// Used to detect possible new binding written without `let` and to provide structured suggestion.
+    in_assignment: Option<&'ast Expr>,
+    is_assign_rhs: bool,
+
+    /// Used to detect possible `.` -> `..` typo when calling methods.
+    in_range: Option<(&'ast Expr, &'ast Expr)>,
+
+    /// If we are currently in a trait object definition. Used to point at the bounds when
+    /// encountering a struct or enum.
+    current_trait_object: Option<&'ast [ast::GenericBound]>,
+
+    /// Given `where <T as Bar>::Baz: String`, suggest `where T: Bar<Baz = String>`.
+    current_where_predicate: Option<&'ast WherePredicate>,
+
+    current_type_path: Option<&'ast Ty>,
+
+    /// The current impl items (used to suggest).
+    current_impl_items: Option<&'ast [P<AssocItem>]>,
+
+    /// When processing impl trait
+    currently_processing_impl_trait: Option<(TraitRef, Ty)>,
+
+    /// Accumulate the errors due to missed lifetime elision,
+    /// and report them all at once for each function.
+    current_elision_failures: Vec<MissingLifetime>,
+}
+
+struct LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
+    r: &'b mut Resolver<'a, 'tcx>,
+
+    /// The module that represents the current item scope.
+    parent_scope: ParentScope<'a>,
+
+    /// The current set of local scopes for types and values.
+    ribs: PerNS<Vec<Rib<'a>>>,
+
+    /// Previous popped `rib`, only used for diagnostic.
+    last_block_rib: Option<Rib<'a>>,
+
+    /// The current set of local scopes, for labels.
+    label_ribs: Vec<Rib<'a, NodeId>>,
+
+    /// The current set of local scopes for lifetimes.
+    lifetime_ribs: Vec<LifetimeRib>,
+
+    /// We are looking for lifetimes in an elision context.
+    /// The set contains all the resolutions that we encountered so far.
+    /// They will be used to determine the correct lifetime for the fn return type.
+    /// The `LifetimeElisionCandidate` is used for diagnostics, to suggest introducing named
+    /// lifetimes.
+    lifetime_elision_candidates: Option<Vec<(LifetimeRes, LifetimeElisionCandidate)>>,
+
+    /// The trait that the current context can refer to.
+    current_trait_ref: Option<(Module<'a>, TraitRef)>,
+
+    /// Fields used to add information to diagnostic errors.
+    diagnostic_metadata: Box<DiagnosticMetadata<'ast>>,
+
+    /// State used to know whether to ignore resolution errors for function bodies.
+    ///
+    /// In particular, rustdoc uses this to avoid giving errors for `cfg()` items.
+    /// In most cases this will be `None`, in which case errors will always be reported.
+    /// If it is `true`, then it will be updated when entering a nested function or trait body.
+    in_func_body: bool,
+
+    /// Count the number of places a lifetime is used.
+    lifetime_uses: FxHashMap<LocalDefId, LifetimeUseSet>,
+}
+
+/// Walks the whole crate in DFS order, visiting each item, resolving names as it goes.
+impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
+    fn visit_attribute(&mut self, _: &'ast Attribute) {
+        // We do not want to resolve expressions that appear in attributes,
+        // as they do not correspond to actual code.
+    }
+    fn visit_item(&mut self, item: &'ast Item) {
+        let prev = replace(&mut self.diagnostic_metadata.current_item, Some(item));
+        // Always report errors in items we just entered.
+        let old_ignore = replace(&mut self.in_func_body, false);
+        self.with_lifetime_rib(LifetimeRibKind::Item, |this| this.resolve_item(item));
+        self.in_func_body = old_ignore;
+        self.diagnostic_metadata.current_item = prev;
+    }
+    fn visit_arm(&mut self, arm: &'ast Arm) {
+        self.resolve_arm(arm);
+    }
+    fn visit_block(&mut self, block: &'ast Block) {
+        let old_macro_rules = self.parent_scope.macro_rules;
+        self.resolve_block(block);
+        self.parent_scope.macro_rules = old_macro_rules;
+    }
+    fn visit_anon_const(&mut self, _constant: &'ast AnonConst) {
+        bug!("encountered anon const without a manual call to `resolve_anon_const`");
+    }
+    fn visit_expr(&mut self, expr: &'ast Expr) {
+        self.resolve_expr(expr, None);
+    }
+    fn visit_pat(&mut self, p: &'ast Pat) {
+        let prev = self.diagnostic_metadata.current_pat;
+        self.diagnostic_metadata.current_pat = Some(p);
+        visit::walk_pat(self, p);
+        self.diagnostic_metadata.current_pat = prev;
+    }
+    fn visit_local(&mut self, local: &'ast Local) {
+        let local_spans = match local.pat.kind {
+            // We check for this to avoid tuple struct fields.
+            PatKind::Wild => None,
+            _ => Some((
+                local.pat.span,
+                local.ty.as_ref().map(|ty| ty.span),
+                local.kind.init().map(|init| init.span),
+            )),
+        };
+        let original = replace(&mut self.diagnostic_metadata.current_let_binding, local_spans);
+        self.resolve_local(local);
+        self.diagnostic_metadata.current_let_binding = original;
+    }
+    fn visit_ty(&mut self, ty: &'ast Ty) {
+        let prev = self.diagnostic_metadata.current_trait_object;
+        let prev_ty = self.diagnostic_metadata.current_type_path;
+        match &ty.kind {
+            TyKind::Ref(None, _) => {
+                // Elided lifetime in reference: we resolve as if there was some lifetime `'_` with
+                // NodeId `ty.id`.
+                // This span will be used in case of elision failure.
+                let span = self.r.tcx.sess.source_map().start_point(ty.span);
+                self.resolve_elided_lifetime(ty.id, span);
+                visit::walk_ty(self, ty);
+            }
+            TyKind::Path(qself, path) => {
+                self.diagnostic_metadata.current_type_path = Some(ty);
+                self.smart_resolve_path(ty.id, qself, path, PathSource::Type);
+
+                // Check whether we should interpret this as a bare trait object.
+                if qself.is_none()
+                    && let Some(partial_res) = self.r.partial_res_map.get(&ty.id)
+                    && let Some(Res::Def(DefKind::Trait | DefKind::TraitAlias, _)) =
+                        partial_res.full_res()
+                {
+                    // This path is actually a bare trait object. In case of a bare `Fn`-trait
+                    // object with anonymous lifetimes, we need this rib to correctly place the
+                    // synthetic lifetimes.
+                    let span = ty.span.shrink_to_lo().to(path.span.shrink_to_lo());
+                    self.with_generic_param_rib(
+                        &[],
+                        RibKind::Normal,
+                        LifetimeRibKind::Generics {
+                            binder: ty.id,
+                            kind: LifetimeBinderKind::PolyTrait,
+                            span,
+                        },
+                        |this| this.visit_path(path, ty.id),
+                    );
+                } else {
+                    visit::walk_ty(self, ty)
+                }
+            }
+            TyKind::ImplicitSelf => {
+                let self_ty = Ident::with_dummy_span(kw::SelfUpper);
+                let res = self
+                    .resolve_ident_in_lexical_scope(
+                        self_ty,
+                        TypeNS,
+                        Some(Finalize::new(ty.id, ty.span)),
+                        None,
+                    )
+                    .map_or(Res::Err, |d| d.res());
+                self.r.record_partial_res(ty.id, PartialRes::new(res));
+                visit::walk_ty(self, ty)
+            }
+            TyKind::ImplTrait(node_id, _) => {
+                let candidates = self.lifetime_elision_candidates.take();
+                visit::walk_ty(self, ty);
+                self.record_lifetime_params_for_impl_trait(*node_id);
+                self.lifetime_elision_candidates = candidates;
+            }
+            TyKind::TraitObject(bounds, ..) => {
+                self.diagnostic_metadata.current_trait_object = Some(&bounds[..]);
+                visit::walk_ty(self, ty)
+            }
+            TyKind::BareFn(bare_fn) => {
+                let span = ty.span.shrink_to_lo().to(bare_fn.decl_span.shrink_to_lo());
+                self.with_generic_param_rib(
+                    &bare_fn.generic_params,
+                    RibKind::Normal,
+                    LifetimeRibKind::Generics {
+                        binder: ty.id,
+                        kind: LifetimeBinderKind::BareFnType,
+                        span,
+                    },
+                    |this| {
+                        this.visit_generic_params(&bare_fn.generic_params, false);
+                        this.with_lifetime_rib(
+                            LifetimeRibKind::AnonymousCreateParameter {
+                                binder: ty.id,
+                                report_in_path: false,
+                            },
+                            |this| {
+                                this.resolve_fn_signature(
+                                    ty.id,
+                                    false,
+                                    // We don't need to deal with patterns in parameters, because
+                                    // they are not possible for foreign or bodiless functions.
+                                    bare_fn
+                                        .decl
+                                        .inputs
+                                        .iter()
+                                        .map(|Param { ty, .. }| (None, &**ty)),
+                                    &bare_fn.decl.output,
+                                )
+                            },
+                        );
+                    },
+                )
+            }
+            TyKind::Array(element_ty, length) => {
+                self.visit_ty(element_ty);
+                self.resolve_anon_const(length, AnonConstKind::ConstArg(IsRepeatExpr::No));
+            }
+            TyKind::Typeof(ct) => {
+                self.resolve_anon_const(ct, AnonConstKind::ConstArg(IsRepeatExpr::No))
+            }
+            _ => visit::walk_ty(self, ty),
+        }
+        self.diagnostic_metadata.current_trait_object = prev;
+        self.diagnostic_metadata.current_type_path = prev_ty;
+    }
+    fn visit_poly_trait_ref(&mut self, tref: &'ast PolyTraitRef) {
+        let span = tref.span.shrink_to_lo().to(tref.trait_ref.path.span.shrink_to_lo());
+        self.with_generic_param_rib(
+            &tref.bound_generic_params,
+            RibKind::Normal,
+            LifetimeRibKind::Generics {
+                binder: tref.trait_ref.ref_id,
+                kind: LifetimeBinderKind::PolyTrait,
+                span,
+            },
+            |this| {
+                this.visit_generic_params(&tref.bound_generic_params, false);
+                this.smart_resolve_path(
+                    tref.trait_ref.ref_id,
+                    &None,
+                    &tref.trait_ref.path,
+                    PathSource::Trait(AliasPossibility::Maybe),
+                );
+                this.visit_trait_ref(&tref.trait_ref);
+            },
+        );
+    }
+    fn visit_foreign_item(&mut self, foreign_item: &'ast ForeignItem) {
+        self.resolve_doc_links(&foreign_item.attrs, MaybeExported::Ok(foreign_item.id));
+        match foreign_item.kind {
+            ForeignItemKind::TyAlias(box TyAlias { ref generics, .. }) => {
+                self.with_generic_param_rib(
+                    &generics.params,
+                    RibKind::Item(HasGenericParams::Yes(generics.span)),
+                    LifetimeRibKind::Generics {
+                        binder: foreign_item.id,
+                        kind: LifetimeBinderKind::Item,
+                        span: generics.span,
+                    },
+                    |this| visit::walk_foreign_item(this, foreign_item),
+                );
+            }
+            ForeignItemKind::Fn(box Fn { ref generics, .. }) => {
+                self.with_generic_param_rib(
+                    &generics.params,
+                    RibKind::Item(HasGenericParams::Yes(generics.span)),
+                    LifetimeRibKind::Generics {
+                        binder: foreign_item.id,
+                        kind: LifetimeBinderKind::Function,
+                        span: generics.span,
+                    },
+                    |this| visit::walk_foreign_item(this, foreign_item),
+                );
+            }
+            ForeignItemKind::Static(..) => {
+                self.with_static_rib(|this| {
+                    visit::walk_foreign_item(this, foreign_item);
+                });
+            }
+            ForeignItemKind::MacCall(..) => {
+                panic!("unexpanded macro in resolve!")
+            }
+        }
+    }
+    fn visit_fn(&mut self, fn_kind: FnKind<'ast>, sp: Span, fn_id: NodeId) {
+        let previous_value = self.diagnostic_metadata.current_function;
+        match fn_kind {
+            // Bail if the function is foreign, and thus cannot validly have
+            // a body, or if there's no body for some other reason.
+            FnKind::Fn(FnCtxt::Foreign, _, sig, _, generics, _)
+            | FnKind::Fn(_, _, sig, _, generics, None) => {
+                self.visit_fn_header(&sig.header);
+                self.visit_generics(generics);
+                self.with_lifetime_rib(
+                    LifetimeRibKind::AnonymousCreateParameter {
+                        binder: fn_id,
+                        report_in_path: false,
+                    },
+                    |this| {
+                        this.resolve_fn_signature(
+                            fn_id,
+                            sig.decl.has_self(),
+                            sig.decl.inputs.iter().map(|Param { ty, .. }| (None, &**ty)),
+                            &sig.decl.output,
+                        );
+
+                        if let Some((coro_node_id, _)) = sig
+                            .header
+                            .coroutine_kind
+                            .map(|coroutine_kind| coroutine_kind.return_id())
+                        {
+                            this.record_lifetime_params_for_impl_trait(coro_node_id);
+                        }
+                    },
+                );
+                return;
+            }
+            FnKind::Fn(..) => {
+                self.diagnostic_metadata.current_function = Some((fn_kind, sp));
+            }
+            // Do not update `current_function` for closures: it suggests `self` parameters.
+            FnKind::Closure(..) => {}
+        };
+        debug!("(resolving function) entering function");
+
+        // Create a value rib for the function.
+        self.with_rib(ValueNS, RibKind::FnOrCoroutine, |this| {
+            // Create a label rib for the function.
+            this.with_label_rib(RibKind::FnOrCoroutine, |this| {
+                match fn_kind {
+                    FnKind::Fn(_, _, sig, _, generics, body) => {
+                        this.visit_generics(generics);
+
+                        let declaration = &sig.decl;
+                        let coro_node_id = sig
+                            .header
+                            .coroutine_kind
+                            .map(|coroutine_kind| coroutine_kind.return_id());
+
+                        this.with_lifetime_rib(
+                            LifetimeRibKind::AnonymousCreateParameter {
+                                binder: fn_id,
+                                report_in_path: coro_node_id.is_some(),
+                            },
+                            |this| {
+                                this.resolve_fn_signature(
+                                    fn_id,
+                                    declaration.has_self(),
+                                    declaration
+                                        .inputs
+                                        .iter()
+                                        .map(|Param { pat, ty, .. }| (Some(&**pat), &**ty)),
+                                    &declaration.output,
+                                );
+
+                                if let Some((async_node_id, _)) = coro_node_id {
+                                    this.record_lifetime_params_for_impl_trait(async_node_id);
+                                }
+                            },
+                        );
+
+                        if let Some(body) = body {
+                            // Ignore errors in function bodies if this is rustdoc
+                            // Be sure not to set this until the function signature has been resolved.
+                            let previous_state = replace(&mut this.in_func_body, true);
+                            // We only care block in the same function
+                            this.last_block_rib = None;
+                            // Resolve the function body, potentially inside the body of an async closure
+                            this.with_lifetime_rib(
+                                LifetimeRibKind::Elided(LifetimeRes::Infer),
+                                |this| this.visit_block(body),
+                            );
+
+                            debug!("(resolving function) leaving function");
+                            this.in_func_body = previous_state;
+                        }
+                    }
+                    FnKind::Closure(binder, declaration, body) => {
+                        this.visit_closure_binder(binder);
+
+                        this.with_lifetime_rib(
+                            match binder {
+                                // We do not have any explicit generic lifetime parameter.
+                                ClosureBinder::NotPresent => {
+                                    LifetimeRibKind::AnonymousCreateParameter {
+                                        binder: fn_id,
+                                        report_in_path: false,
+                                    }
+                                }
+                                ClosureBinder::For { .. } => LifetimeRibKind::AnonymousReportError,
+                            },
+                            // Add each argument to the rib.
+                            |this| this.resolve_params(&declaration.inputs),
+                        );
+                        this.with_lifetime_rib(
+                            match binder {
+                                ClosureBinder::NotPresent => {
+                                    LifetimeRibKind::Elided(LifetimeRes::Infer)
+                                }
+                                ClosureBinder::For { .. } => LifetimeRibKind::AnonymousReportError,
+                            },
+                            |this| visit::walk_fn_ret_ty(this, &declaration.output),
+                        );
+
+                        // Ignore errors in function bodies if this is rustdoc
+                        // Be sure not to set this until the function signature has been resolved.
+                        let previous_state = replace(&mut this.in_func_body, true);
+                        // Resolve the function body, potentially inside the body of an async closure
+                        this.with_lifetime_rib(
+                            LifetimeRibKind::Elided(LifetimeRes::Infer),
+                            |this| this.visit_expr(body),
+                        );
+
+                        debug!("(resolving function) leaving function");
+                        this.in_func_body = previous_state;
+                    }
+                }
+            })
+        });
+        self.diagnostic_metadata.current_function = previous_value;
+    }
+    fn visit_lifetime(&mut self, lifetime: &'ast Lifetime, use_ctxt: visit::LifetimeCtxt) {
+        self.resolve_lifetime(lifetime, use_ctxt)
+    }
+
+    fn visit_generics(&mut self, generics: &'ast Generics) {
+        self.visit_generic_params(
+            &generics.params,
+            self.diagnostic_metadata.current_self_item.is_some(),
+        );
+        for p in &generics.where_clause.predicates {
+            self.visit_where_predicate(p);
+        }
+    }
+
+    fn visit_closure_binder(&mut self, b: &'ast ClosureBinder) {
+        match b {
+            ClosureBinder::NotPresent => {}
+            ClosureBinder::For { generic_params, .. } => {
+                self.visit_generic_params(
+                    generic_params,
+                    self.diagnostic_metadata.current_self_item.is_some(),
+                );
+            }
+        }
+    }
+
+    fn visit_generic_arg(&mut self, arg: &'ast GenericArg) {
+        debug!("visit_generic_arg({:?})", arg);
+        let prev = replace(&mut self.diagnostic_metadata.currently_processing_generics, true);
+        match arg {
+            GenericArg::Type(ref ty) => {
+                // We parse const arguments as path types as we cannot distinguish them during
+                // parsing. We try to resolve that ambiguity by attempting resolution the type
+                // namespace first, and if that fails we try again in the value namespace. If
+                // resolution in the value namespace succeeds, we have an generic const argument on
+                // our hands.
+                if let TyKind::Path(None, ref path) = ty.kind {
+                    // We cannot disambiguate multi-segment paths right now as that requires type
+                    // checking.
+                    if path.is_potential_trivial_const_arg() {
+                        let mut check_ns = |ns| {
+                            self.maybe_resolve_ident_in_lexical_scope(path.segments[0].ident, ns)
+                                .is_some()
+                        };
+                        if !check_ns(TypeNS) && check_ns(ValueNS) {
+                            self.resolve_anon_const_manual(
+                                true,
+                                AnonConstKind::ConstArg(IsRepeatExpr::No),
+                                |this| {
+                                    this.smart_resolve_path(
+                                        ty.id,
+                                        &None,
+                                        path,
+                                        PathSource::Expr(None),
+                                    );
+                                    this.visit_path(path, ty.id);
+                                },
+                            );
+
+                            self.diagnostic_metadata.currently_processing_generics = prev;
+                            return;
+                        }
+                    }
+                }
+
+                self.visit_ty(ty);
+            }
+            GenericArg::Lifetime(lt) => self.visit_lifetime(lt, visit::LifetimeCtxt::GenericArg),
+            GenericArg::Const(ct) => {
+                self.resolve_anon_const(ct, AnonConstKind::ConstArg(IsRepeatExpr::No))
+            }
+        }
+        self.diagnostic_metadata.currently_processing_generics = prev;
+    }
+
+    fn visit_assoc_constraint(&mut self, constraint: &'ast AssocConstraint) {
+        self.visit_ident(constraint.ident);
+        if let Some(ref gen_args) = constraint.gen_args {
+            // Forbid anonymous lifetimes in GAT parameters until proper semantics are decided.
+            self.with_lifetime_rib(LifetimeRibKind::AnonymousReportError, |this| {
+                this.visit_generic_args(gen_args)
+            });
+        }
+        match constraint.kind {
+            AssocConstraintKind::Equality { ref term } => match term {
+                Term::Ty(ty) => self.visit_ty(ty),
+                Term::Const(c) => {
+                    self.resolve_anon_const(c, AnonConstKind::ConstArg(IsRepeatExpr::No))
+                }
+            },
+            AssocConstraintKind::Bound { ref bounds } => {
+                self.record_lifetime_params_for_impl_trait(constraint.id);
+                walk_list!(self, visit_param_bound, bounds, BoundKind::Bound);
+            }
+        }
+    }
+
+    fn visit_path_segment(&mut self, path_segment: &'ast PathSegment) {
+        if let Some(ref args) = path_segment.args {
+            match &**args {
+                GenericArgs::AngleBracketed(..) => visit::walk_generic_args(self, args),
+                GenericArgs::Parenthesized(p_args) => {
+                    // Probe the lifetime ribs to know how to behave.
+                    for rib in self.lifetime_ribs.iter().rev() {
+                        match rib.kind {
+                            // We are inside a `PolyTraitRef`. The lifetimes are
+                            // to be introduced in that (maybe implicit) `for<>` binder.
+                            LifetimeRibKind::Generics {
+                                binder,
+                                kind: LifetimeBinderKind::PolyTrait,
+                                ..
+                            } => {
+                                self.with_lifetime_rib(
+                                    LifetimeRibKind::AnonymousCreateParameter {
+                                        binder,
+                                        report_in_path: false,
+                                    },
+                                    |this| {
+                                        this.resolve_fn_signature(
+                                            binder,
+                                            false,
+                                            p_args.inputs.iter().map(|ty| (None, &**ty)),
+                                            &p_args.output,
+                                        )
+                                    },
+                                );
+                                break;
+                            }
+                            // We have nowhere to introduce generics. Code is malformed,
+                            // so use regular lifetime resolution to avoid spurious errors.
+                            LifetimeRibKind::Item | LifetimeRibKind::Generics { .. } => {
+                                visit::walk_generic_args(self, args);
+                                break;
+                            }
+                            LifetimeRibKind::AnonymousCreateParameter { .. }
+                            | LifetimeRibKind::AnonymousReportError
+                            | LifetimeRibKind::AnonymousWarn(_)
+                            | LifetimeRibKind::Elided(_)
+                            | LifetimeRibKind::ElisionFailure
+                            | LifetimeRibKind::ConcreteAnonConst(_)
+                            | LifetimeRibKind::ConstParamTy => {}
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    fn visit_where_predicate(&mut self, p: &'ast WherePredicate) {
+        debug!("visit_where_predicate {:?}", p);
+        let previous_value =
+            replace(&mut self.diagnostic_metadata.current_where_predicate, Some(p));
+        self.with_lifetime_rib(LifetimeRibKind::AnonymousReportError, |this| {
+            if let WherePredicate::BoundPredicate(WhereBoundPredicate {
+                ref bounded_ty,
+                ref bounds,
+                ref bound_generic_params,
+                span: predicate_span,
+                ..
+            }) = p
+            {
+                let span = predicate_span.shrink_to_lo().to(bounded_ty.span.shrink_to_lo());
+                this.with_generic_param_rib(
+                    bound_generic_params,
+                    RibKind::Normal,
+                    LifetimeRibKind::Generics {
+                        binder: bounded_ty.id,
+                        kind: LifetimeBinderKind::WhereBound,
+                        span,
+                    },
+                    |this| {
+                        this.visit_generic_params(bound_generic_params, false);
+                        this.visit_ty(bounded_ty);
+                        for bound in bounds {
+                            this.visit_param_bound(bound, BoundKind::Bound)
+                        }
+                    },
+                );
+            } else {
+                visit::walk_where_predicate(this, p);
+            }
+        });
+        self.diagnostic_metadata.current_where_predicate = previous_value;
+    }
+
+    fn visit_inline_asm(&mut self, asm: &'ast InlineAsm) {
+        for (op, _) in &asm.operands {
+            match op {
+                InlineAsmOperand::In { expr, .. }
+                | InlineAsmOperand::Out { expr: Some(expr), .. }
+                | InlineAsmOperand::InOut { expr, .. } => self.visit_expr(expr),
+                InlineAsmOperand::Out { expr: None, .. } => {}
+                InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => {
+                    self.visit_expr(in_expr);
+                    if let Some(out_expr) = out_expr {
+                        self.visit_expr(out_expr);
+                    }
+                }
+                InlineAsmOperand::Const { anon_const, .. } => {
+                    // Although this is `DefKind::AnonConst`, it is allowed to reference outer
+                    // generic parameters like an inline const.
+                    self.resolve_anon_const(anon_const, AnonConstKind::InlineConst);
+                }
+                InlineAsmOperand::Sym { sym } => self.visit_inline_asm_sym(sym),
+            }
+        }
+    }
+
+    fn visit_inline_asm_sym(&mut self, sym: &'ast InlineAsmSym) {
+        // This is similar to the code for AnonConst.
+        self.with_rib(ValueNS, RibKind::InlineAsmSym, |this| {
+            this.with_rib(TypeNS, RibKind::InlineAsmSym, |this| {
+                this.with_label_rib(RibKind::InlineAsmSym, |this| {
+                    this.smart_resolve_path(sym.id, &sym.qself, &sym.path, PathSource::Expr(None));
+                    visit::walk_inline_asm_sym(this, sym);
+                });
+            })
+        });
+    }
+
+    fn visit_variant(&mut self, v: &'ast Variant) {
+        self.resolve_doc_links(&v.attrs, MaybeExported::Ok(v.id));
+        visit::walk_variant(self, v)
+    }
+
+    fn visit_variant_discr(&mut self, discr: &'ast AnonConst) {
+        self.resolve_anon_const(discr, AnonConstKind::EnumDiscriminant);
+    }
+
+    fn visit_field_def(&mut self, f: &'ast FieldDef) {
+        self.resolve_doc_links(&f.attrs, MaybeExported::Ok(f.id));
+        visit::walk_field_def(self, f)
+    }
+}
+
+impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
+    fn new(resolver: &'b mut Resolver<'a, 'tcx>) -> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
+        // During late resolution we only track the module component of the parent scope,
+        // although it may be useful to track other components as well for diagnostics.
+        let graph_root = resolver.graph_root;
+        let parent_scope = ParentScope::module(graph_root, resolver);
+        let start_rib_kind = RibKind::Module(graph_root);
+        LateResolutionVisitor {
+            r: resolver,
+            parent_scope,
+            ribs: PerNS {
+                value_ns: vec![Rib::new(start_rib_kind)],
+                type_ns: vec![Rib::new(start_rib_kind)],
+                macro_ns: vec![Rib::new(start_rib_kind)],
+            },
+            last_block_rib: None,
+            label_ribs: Vec::new(),
+            lifetime_ribs: Vec::new(),
+            lifetime_elision_candidates: None,
+            current_trait_ref: None,
+            diagnostic_metadata: Default::default(),
+            // errors at module scope should always be reported
+            in_func_body: false,
+            lifetime_uses: Default::default(),
+        }
+    }
+
+    fn maybe_resolve_ident_in_lexical_scope(
+        &mut self,
+        ident: Ident,
+        ns: Namespace,
+    ) -> Option<LexicalScopeBinding<'a>> {
+        self.r.resolve_ident_in_lexical_scope(
+            ident,
+            ns,
+            &self.parent_scope,
+            None,
+            &self.ribs[ns],
+            None,
+        )
+    }
+
+    fn resolve_ident_in_lexical_scope(
+        &mut self,
+        ident: Ident,
+        ns: Namespace,
+        finalize: Option<Finalize>,
+        ignore_binding: Option<NameBinding<'a>>,
+    ) -> Option<LexicalScopeBinding<'a>> {
+        self.r.resolve_ident_in_lexical_scope(
+            ident,
+            ns,
+            &self.parent_scope,
+            finalize,
+            &self.ribs[ns],
+            ignore_binding,
+        )
+    }
+
+    fn resolve_path(
+        &mut self,
+        path: &[Segment],
+        opt_ns: Option<Namespace>, // `None` indicates a module path in import
+        finalize: Option<Finalize>,
+    ) -> PathResult<'a> {
+        self.r.resolve_path_with_ribs(
+            path,
+            opt_ns,
+            &self.parent_scope,
+            finalize,
+            Some(&self.ribs),
+            None,
+        )
+    }
+
+    // AST resolution
+    //
+    // We maintain a list of value ribs and type ribs.
+    //
+    // Simultaneously, we keep track of the current position in the module
+    // graph in the `parent_scope.module` pointer. When we go to resolve a name in
+    // the value or type namespaces, we first look through all the ribs and
+    // then query the module graph. When we resolve a name in the module
+    // namespace, we can skip all the ribs (since nested modules are not
+    // allowed within blocks in Rust) and jump straight to the current module
+    // graph node.
+    //
+    // Named implementations are handled separately. When we find a method
+    // call, we consult the module node to find all of the implementations in
+    // scope. This information is lazily cached in the module node. We then
+    // generate a fake "implementation scope" containing all the
+    // implementations thus found, for compatibility with old resolve pass.
+
+    /// Do some `work` within a new innermost rib of the given `kind` in the given namespace (`ns`).
+    fn with_rib<T>(
+        &mut self,
+        ns: Namespace,
+        kind: RibKind<'a>,
+        work: impl FnOnce(&mut Self) -> T,
+    ) -> T {
+        self.ribs[ns].push(Rib::new(kind));
+        let ret = work(self);
+        self.ribs[ns].pop();
+        ret
+    }
+
+    fn with_scope<T>(&mut self, id: NodeId, f: impl FnOnce(&mut Self) -> T) -> T {
+        if let Some(module) = self.r.get_module(self.r.local_def_id(id).to_def_id()) {
+            // Move down in the graph.
+            let orig_module = replace(&mut self.parent_scope.module, module);
+            self.with_rib(ValueNS, RibKind::Module(module), |this| {
+                this.with_rib(TypeNS, RibKind::Module(module), |this| {
+                    let ret = f(this);
+                    this.parent_scope.module = orig_module;
+                    ret
+                })
+            })
+        } else {
+            f(self)
+        }
+    }
+
+    fn visit_generic_params(&mut self, params: &'ast [GenericParam], add_self_upper: bool) {
+        // For type parameter defaults, we have to ban access
+        // to following type parameters, as the GenericArgs can only
+        // provide previous type parameters as they're built. We
+        // put all the parameters on the ban list and then remove
+        // them one by one as they are processed and become available.
+        let mut forward_ty_ban_rib = Rib::new(RibKind::ForwardGenericParamBan);
+        let mut forward_const_ban_rib = Rib::new(RibKind::ForwardGenericParamBan);
+        for param in params.iter() {
+            match param.kind {
+                GenericParamKind::Type { .. } => {
+                    forward_ty_ban_rib
+                        .bindings
+                        .insert(Ident::with_dummy_span(param.ident.name), Res::Err);
+                }
+                GenericParamKind::Const { .. } => {
+                    forward_const_ban_rib
+                        .bindings
+                        .insert(Ident::with_dummy_span(param.ident.name), Res::Err);
+                }
+                GenericParamKind::Lifetime => {}
+            }
+        }
+
+        // rust-lang/rust#61631: The type `Self` is essentially
+        // another type parameter. For ADTs, we consider it
+        // well-defined only after all of the ADT type parameters have
+        // been provided. Therefore, we do not allow use of `Self`
+        // anywhere in ADT type parameter defaults.
+        //
+        // (We however cannot ban `Self` for defaults on *all* generic
+        // lists; e.g. trait generics can usefully refer to `Self`,
+        // such as in the case of `trait Add<Rhs = Self>`.)
+        if add_self_upper {
+            // (`Some` if + only if we are in ADT's generics.)
+            forward_ty_ban_rib.bindings.insert(Ident::with_dummy_span(kw::SelfUpper), Res::Err);
+        }
+
+        self.with_lifetime_rib(LifetimeRibKind::AnonymousReportError, |this| {
+            for param in params {
+                match param.kind {
+                    GenericParamKind::Lifetime => {
+                        for bound in &param.bounds {
+                            this.visit_param_bound(bound, BoundKind::Bound);
+                        }
+                    }
+                    GenericParamKind::Type { ref default } => {
+                        for bound in &param.bounds {
+                            this.visit_param_bound(bound, BoundKind::Bound);
+                        }
+
+                        if let Some(ref ty) = default {
+                            this.ribs[TypeNS].push(forward_ty_ban_rib);
+                            this.ribs[ValueNS].push(forward_const_ban_rib);
+                            this.visit_ty(ty);
+                            forward_const_ban_rib = this.ribs[ValueNS].pop().unwrap();
+                            forward_ty_ban_rib = this.ribs[TypeNS].pop().unwrap();
+                        }
+
+                        // Allow all following defaults to refer to this type parameter.
+                        forward_ty_ban_rib
+                            .bindings
+                            .remove(&Ident::with_dummy_span(param.ident.name));
+                    }
+                    GenericParamKind::Const { ref ty, kw_span: _, ref default } => {
+                        // Const parameters can't have param bounds.
+                        assert!(param.bounds.is_empty());
+
+                        this.ribs[TypeNS].push(Rib::new(RibKind::ConstParamTy));
+                        this.ribs[ValueNS].push(Rib::new(RibKind::ConstParamTy));
+                        this.with_lifetime_rib(LifetimeRibKind::ConstParamTy, |this| {
+                            this.visit_ty(ty)
+                        });
+                        this.ribs[TypeNS].pop().unwrap();
+                        this.ribs[ValueNS].pop().unwrap();
+
+                        if let Some(ref expr) = default {
+                            this.ribs[TypeNS].push(forward_ty_ban_rib);
+                            this.ribs[ValueNS].push(forward_const_ban_rib);
+                            this.resolve_anon_const(
+                                expr,
+                                AnonConstKind::ConstArg(IsRepeatExpr::No),
+                            );
+                            forward_const_ban_rib = this.ribs[ValueNS].pop().unwrap();
+                            forward_ty_ban_rib = this.ribs[TypeNS].pop().unwrap();
+                        }
+
+                        // Allow all following defaults to refer to this const parameter.
+                        forward_const_ban_rib
+                            .bindings
+                            .remove(&Ident::with_dummy_span(param.ident.name));
+                    }
+                }
+            }
+        })
+    }
+
+    #[instrument(level = "debug", skip(self, work))]
+    fn with_lifetime_rib<T>(
+        &mut self,
+        kind: LifetimeRibKind,
+        work: impl FnOnce(&mut Self) -> T,
+    ) -> T {
+        self.lifetime_ribs.push(LifetimeRib::new(kind));
+        let outer_elision_candidates = self.lifetime_elision_candidates.take();
+        let ret = work(self);
+        self.lifetime_elision_candidates = outer_elision_candidates;
+        self.lifetime_ribs.pop();
+        ret
+    }
+
+    #[instrument(level = "debug", skip(self))]
+    fn resolve_lifetime(&mut self, lifetime: &'ast Lifetime, use_ctxt: visit::LifetimeCtxt) {
+        let ident = lifetime.ident;
+
+        if ident.name == kw::StaticLifetime {
+            self.record_lifetime_res(
+                lifetime.id,
+                LifetimeRes::Static,
+                LifetimeElisionCandidate::Named,
+            );
+            return;
+        }
+
+        if ident.name == kw::UnderscoreLifetime {
+            return self.resolve_anonymous_lifetime(lifetime, false);
+        }
+
+        let mut lifetime_rib_iter = self.lifetime_ribs.iter().rev();
+        while let Some(rib) = lifetime_rib_iter.next() {
+            let normalized_ident = ident.normalize_to_macros_2_0();
+            if let Some(&(_, res)) = rib.bindings.get(&normalized_ident) {
+                self.record_lifetime_res(lifetime.id, res, LifetimeElisionCandidate::Named);
+
+                if let LifetimeRes::Param { param, binder } = res {
+                    match self.lifetime_uses.entry(param) {
+                        Entry::Vacant(v) => {
+                            debug!("First use of {:?} at {:?}", res, ident.span);
+                            let use_set = self
+                                .lifetime_ribs
+                                .iter()
+                                .rev()
+                                .find_map(|rib| match rib.kind {
+                                    // Do not suggest eliding a lifetime where an anonymous
+                                    // lifetime would be illegal.
+                                    LifetimeRibKind::Item
+                                    | LifetimeRibKind::AnonymousReportError
+                                    | LifetimeRibKind::AnonymousWarn(_)
+                                    | LifetimeRibKind::ElisionFailure => Some(LifetimeUseSet::Many),
+                                    // An anonymous lifetime is legal here, and bound to the right
+                                    // place, go ahead.
+                                    LifetimeRibKind::AnonymousCreateParameter {
+                                        binder: anon_binder,
+                                        ..
+                                    } => Some(if binder == anon_binder {
+                                        LifetimeUseSet::One { use_span: ident.span, use_ctxt }
+                                    } else {
+                                        LifetimeUseSet::Many
+                                    }),
+                                    // Only report if eliding the lifetime would have the same
+                                    // semantics.
+                                    LifetimeRibKind::Elided(r) => Some(if res == r {
+                                        LifetimeUseSet::One { use_span: ident.span, use_ctxt }
+                                    } else {
+                                        LifetimeUseSet::Many
+                                    }),
+                                    LifetimeRibKind::Generics { .. }
+                                    | LifetimeRibKind::ConstParamTy => None,
+                                    LifetimeRibKind::ConcreteAnonConst(_) => {
+                                        span_bug!(ident.span, "unexpected rib kind: {:?}", rib.kind)
+                                    }
+                                })
+                                .unwrap_or(LifetimeUseSet::Many);
+                            debug!(?use_ctxt, ?use_set);
+                            v.insert(use_set);
+                        }
+                        Entry::Occupied(mut o) => {
+                            debug!("Many uses of {:?} at {:?}", res, ident.span);
+                            *o.get_mut() = LifetimeUseSet::Many;
+                        }
+                    }
+                }
+                return;
+            }
+
+            match rib.kind {
+                LifetimeRibKind::Item => break,
+                LifetimeRibKind::ConstParamTy => {
+                    self.emit_non_static_lt_in_const_param_ty_error(lifetime);
+                    self.record_lifetime_res(
+                        lifetime.id,
+                        LifetimeRes::Error,
+                        LifetimeElisionCandidate::Ignore,
+                    );
+                    return;
+                }
+                LifetimeRibKind::ConcreteAnonConst(cause) => {
+                    self.emit_forbidden_non_static_lifetime_error(cause, lifetime);
+                    self.record_lifetime_res(
+                        lifetime.id,
+                        LifetimeRes::Error,
+                        LifetimeElisionCandidate::Ignore,
+                    );
+                    return;
+                }
+                LifetimeRibKind::AnonymousCreateParameter { .. }
+                | LifetimeRibKind::Elided(_)
+                | LifetimeRibKind::Generics { .. }
+                | LifetimeRibKind::ElisionFailure
+                | LifetimeRibKind::AnonymousReportError
+                | LifetimeRibKind::AnonymousWarn(_) => {}
+            }
+        }
+
+        let mut outer_res = None;
+        for rib in lifetime_rib_iter {
+            let normalized_ident = ident.normalize_to_macros_2_0();
+            if let Some((&outer, _)) = rib.bindings.get_key_value(&normalized_ident) {
+                outer_res = Some(outer);
+                break;
+            }
+        }
+
+        self.emit_undeclared_lifetime_error(lifetime, outer_res);
+        self.record_lifetime_res(lifetime.id, LifetimeRes::Error, LifetimeElisionCandidate::Named);
+    }
+
+    #[instrument(level = "debug", skip(self))]
+    fn resolve_anonymous_lifetime(&mut self, lifetime: &Lifetime, elided: bool) {
+        debug_assert_eq!(lifetime.ident.name, kw::UnderscoreLifetime);
+
+        let missing_lifetime = MissingLifetime {
+            id: lifetime.id,
+            span: lifetime.ident.span,
+            kind: if elided {
+                MissingLifetimeKind::Ampersand
+            } else {
+                MissingLifetimeKind::Underscore
+            },
+            count: 1,
+        };
+        let elision_candidate = LifetimeElisionCandidate::Missing(missing_lifetime);
+        for (i, rib) in self.lifetime_ribs.iter().enumerate().rev() {
+            debug!(?rib.kind);
+            match rib.kind {
+                LifetimeRibKind::AnonymousCreateParameter { binder, .. } => {
+                    let res = self.create_fresh_lifetime(lifetime.id, lifetime.ident, binder);
+                    self.record_lifetime_res(lifetime.id, res, elision_candidate);
+                    return;
+                }
+                LifetimeRibKind::AnonymousWarn(node_id) => {
+                    let msg = if elided {
+                        "`&` without an explicit lifetime name cannot be used here"
+                    } else {
+                        "`'_` cannot be used here"
+                    };
+                    self.r.lint_buffer.buffer_lint_with_diagnostic(
+                        lint::builtin::ELIDED_LIFETIMES_IN_ASSOCIATED_CONSTANT,
+                        node_id,
+                        lifetime.ident.span,
+                        msg,
+                        lint::BuiltinLintDiagnostics::AssociatedConstElidedLifetime {
+                            elided,
+                            span: lifetime.ident.span,
+                        },
+                    );
+                }
+                LifetimeRibKind::AnonymousReportError => {
+                    let (msg, note) = if elided {
+                        (
+                            "`&` without an explicit lifetime name cannot be used here",
+                            "explicit lifetime name needed here",
+                        )
+                    } else {
+                        ("`'_` cannot be used here", "`'_` is a reserved lifetime name")
+                    };
+                    let mut diag = rustc_errors::struct_span_code_err!(
+                        self.r.dcx(),
+                        lifetime.ident.span,
+                        E0637,
+                        "{}",
+                        msg,
+                    );
+                    diag.span_label(lifetime.ident.span, note);
+                    if elided {
+                        for rib in self.lifetime_ribs[i..].iter().rev() {
+                            if let LifetimeRibKind::Generics {
+                                span,
+                                kind: LifetimeBinderKind::PolyTrait | LifetimeBinderKind::WhereBound,
+                                ..
+                            } = &rib.kind
+                            {
+                                diag.multipart_suggestion(
+                                    "consider introducing a higher-ranked lifetime here",
+                                    vec![
+                                        (span.shrink_to_lo(), "for<'a> ".into()),
+                                        (lifetime.ident.span.shrink_to_hi(), "'a ".into()),
+                                    ],
+                                    Applicability::MachineApplicable,
+                                );
+                                break;
+                            }
+                        }
+                    }
+                    diag.emit();
+                    self.record_lifetime_res(lifetime.id, LifetimeRes::Error, elision_candidate);
+                    return;
+                }
+                LifetimeRibKind::Elided(res) => {
+                    self.record_lifetime_res(lifetime.id, res, elision_candidate);
+                    return;
+                }
+                LifetimeRibKind::ElisionFailure => {
+                    self.diagnostic_metadata.current_elision_failures.push(missing_lifetime);
+                    self.record_lifetime_res(lifetime.id, LifetimeRes::Error, elision_candidate);
+                    return;
+                }
+                LifetimeRibKind::Item => break,
+                LifetimeRibKind::Generics { .. } | LifetimeRibKind::ConstParamTy => {}
+                LifetimeRibKind::ConcreteAnonConst(_) => {
+                    // There is always an `Elided(LifetimeRes::Infer)` inside an `AnonConst`.
+                    span_bug!(lifetime.ident.span, "unexpected rib kind: {:?}", rib.kind)
+                }
+            }
+        }
+        self.record_lifetime_res(lifetime.id, LifetimeRes::Error, elision_candidate);
+        self.report_missing_lifetime_specifiers(vec![missing_lifetime], None);
+    }
+
+    #[instrument(level = "debug", skip(self))]
+    fn resolve_elided_lifetime(&mut self, anchor_id: NodeId, span: Span) {
+        let id = self.r.next_node_id();
+        let lt = Lifetime { id, ident: Ident::new(kw::UnderscoreLifetime, span) };
+
+        self.record_lifetime_res(
+            anchor_id,
+            LifetimeRes::ElidedAnchor { start: id, end: NodeId::from_u32(id.as_u32() + 1) },
+            LifetimeElisionCandidate::Ignore,
+        );
+        self.resolve_anonymous_lifetime(&lt, true);
+    }
+
+    #[instrument(level = "debug", skip(self))]
+    fn create_fresh_lifetime(&mut self, id: NodeId, ident: Ident, binder: NodeId) -> LifetimeRes {
+        debug_assert_eq!(ident.name, kw::UnderscoreLifetime);
+        debug!(?ident.span);
+
+        // Leave the responsibility to create the `LocalDefId` to lowering.
+        let param = self.r.next_node_id();
+        let res = LifetimeRes::Fresh { param, binder };
+        self.record_lifetime_param(param, res);
+
+        // Record the created lifetime parameter so lowering can pick it up and add it to HIR.
+        self.r
+            .extra_lifetime_params_map
+            .entry(binder)
+            .or_insert_with(Vec::new)
+            .push((ident, param, res));
+        res
+    }
+
+    #[instrument(level = "debug", skip(self))]
+    fn resolve_elided_lifetimes_in_path(
+        &mut self,
+        path_id: NodeId,
+        partial_res: PartialRes,
+        path: &[Segment],
+        source: PathSource<'_>,
+        path_span: Span,
+    ) {
+        let proj_start = path.len() - partial_res.unresolved_segments();
+        for (i, segment) in path.iter().enumerate() {
+            if segment.has_lifetime_args {
+                continue;
+            }
+            let Some(segment_id) = segment.id else {
+                continue;
+            };
+
+            // Figure out if this is a type/trait segment,
+            // which may need lifetime elision performed.
+            let type_def_id = match partial_res.base_res() {
+                Res::Def(DefKind::AssocTy, def_id) if i + 2 == proj_start => {
+                    self.r.tcx.parent(def_id)
+                }
+                Res::Def(DefKind::Variant, def_id) if i + 1 == proj_start => {
+                    self.r.tcx.parent(def_id)
+                }
+                Res::Def(DefKind::Struct, def_id)
+                | Res::Def(DefKind::Union, def_id)
+                | Res::Def(DefKind::Enum, def_id)
+                | Res::Def(DefKind::TyAlias, def_id)
+                | Res::Def(DefKind::Trait, def_id)
+                    if i + 1 == proj_start =>
+                {
+                    def_id
+                }
+                _ => continue,
+            };
+
+            let expected_lifetimes = self.r.item_generics_num_lifetimes(type_def_id);
+            if expected_lifetimes == 0 {
+                continue;
+            }
+
+            let node_ids = self.r.next_node_ids(expected_lifetimes);
+            self.record_lifetime_res(
+                segment_id,
+                LifetimeRes::ElidedAnchor { start: node_ids.start, end: node_ids.end },
+                LifetimeElisionCandidate::Ignore,
+            );
+
+            let inferred = match source {
+                PathSource::Trait(..) | PathSource::TraitItem(..) | PathSource::Type => false,
+                PathSource::Expr(..)
+                | PathSource::Pat
+                | PathSource::Struct
+                | PathSource::TupleStruct(..)
+                | PathSource::Delegation => true,
+            };
+            if inferred {
+                // Do not create a parameter for patterns and expressions: type checking can infer
+                // the appropriate lifetime for us.
+                for id in node_ids {
+                    self.record_lifetime_res(
+                        id,
+                        LifetimeRes::Infer,
+                        LifetimeElisionCandidate::Named,
+                    );
+                }
+                continue;
+            }
+
+            let elided_lifetime_span = if segment.has_generic_args {
+                // If there are brackets, but not generic arguments, then use the opening bracket
+                segment.args_span.with_hi(segment.args_span.lo() + BytePos(1))
+            } else {
+                // If there are no brackets, use the identifier span.
+                // HACK: we use find_ancestor_inside to properly suggest elided spans in paths
+                // originating from macros, since the segment's span might be from a macro arg.
+                segment.ident.span.find_ancestor_inside(path_span).unwrap_or(path_span)
+            };
+            let ident = Ident::new(kw::UnderscoreLifetime, elided_lifetime_span);
+
+            let missing_lifetime = MissingLifetime {
+                id: node_ids.start,
+                span: elided_lifetime_span,
+                kind: if segment.has_generic_args {
+                    MissingLifetimeKind::Comma
+                } else {
+                    MissingLifetimeKind::Brackets
+                },
+                count: expected_lifetimes,
+            };
+            let mut should_lint = true;
+            for rib in self.lifetime_ribs.iter().rev() {
+                match rib.kind {
+                    // In create-parameter mode we error here because we don't want to support
+                    // deprecated impl elision in new features like impl elision and `async fn`,
+                    // both of which work using the `CreateParameter` mode:
+                    //
+                    //     impl Foo for std::cell::Ref<u32> // note lack of '_
+                    //     async fn foo(_: std::cell::Ref<u32>) { ... }
+                    LifetimeRibKind::AnonymousCreateParameter { report_in_path: true, .. }
+                    | LifetimeRibKind::AnonymousWarn(_) => {
+                        let sess = self.r.tcx.sess;
+                        let mut err = rustc_errors::struct_span_code_err!(
+                            sess.dcx(),
+                            path_span,
+                            E0726,
+                            "implicit elided lifetime not allowed here"
+                        );
+                        rustc_errors::add_elided_lifetime_in_path_suggestion(
+                            sess.source_map(),
+                            &mut err,
+                            expected_lifetimes,
+                            path_span,
+                            !segment.has_generic_args,
+                            elided_lifetime_span,
+                        );
+                        err.emit();
+                        should_lint = false;
+
+                        for id in node_ids {
+                            self.record_lifetime_res(
+                                id,
+                                LifetimeRes::Error,
+                                LifetimeElisionCandidate::Named,
+                            );
+                        }
+                        break;
+                    }
+                    // Do not create a parameter for patterns and expressions.
+                    LifetimeRibKind::AnonymousCreateParameter { binder, .. } => {
+                        // Group all suggestions into the first record.
+                        let mut candidate = LifetimeElisionCandidate::Missing(missing_lifetime);
+                        for id in node_ids {
+                            let res = self.create_fresh_lifetime(id, ident, binder);
+                            self.record_lifetime_res(
+                                id,
+                                res,
+                                replace(&mut candidate, LifetimeElisionCandidate::Named),
+                            );
+                        }
+                        break;
+                    }
+                    LifetimeRibKind::Elided(res) => {
+                        let mut candidate = LifetimeElisionCandidate::Missing(missing_lifetime);
+                        for id in node_ids {
+                            self.record_lifetime_res(
+                                id,
+                                res,
+                                replace(&mut candidate, LifetimeElisionCandidate::Ignore),
+                            );
+                        }
+                        break;
+                    }
+                    LifetimeRibKind::ElisionFailure => {
+                        self.diagnostic_metadata.current_elision_failures.push(missing_lifetime);
+                        for id in node_ids {
+                            self.record_lifetime_res(
+                                id,
+                                LifetimeRes::Error,
+                                LifetimeElisionCandidate::Ignore,
+                            );
+                        }
+                        break;
+                    }
+                    // `LifetimeRes::Error`, which would usually be used in the case of
+                    // `ReportError`, is unsuitable here, as we don't emit an error yet. Instead,
+                    // we simply resolve to an implicit lifetime, which will be checked later, at
+                    // which point a suitable error will be emitted.
+                    LifetimeRibKind::AnonymousReportError | LifetimeRibKind::Item => {
+                        for id in node_ids {
+                            self.record_lifetime_res(
+                                id,
+                                LifetimeRes::Error,
+                                LifetimeElisionCandidate::Ignore,
+                            );
+                        }
+                        self.report_missing_lifetime_specifiers(vec![missing_lifetime], None);
+                        break;
+                    }
+                    LifetimeRibKind::Generics { .. } | LifetimeRibKind::ConstParamTy => {}
+                    LifetimeRibKind::ConcreteAnonConst(_) => {
+                        // There is always an `Elided(LifetimeRes::Infer)` inside an `AnonConst`.
+                        span_bug!(elided_lifetime_span, "unexpected rib kind: {:?}", rib.kind)
+                    }
+                }
+            }
+
+            if should_lint {
+                self.r.lint_buffer.buffer_lint_with_diagnostic(
+                    lint::builtin::ELIDED_LIFETIMES_IN_PATHS,
+                    segment_id,
+                    elided_lifetime_span,
+                    "hidden lifetime parameters in types are deprecated",
+                    lint::BuiltinLintDiagnostics::ElidedLifetimesInPaths(
+                        expected_lifetimes,
+                        path_span,
+                        !segment.has_generic_args,
+                        elided_lifetime_span,
+                    ),
+                );
+            }
+        }
+    }
+
+    #[instrument(level = "debug", skip(self))]
+    fn record_lifetime_res(
+        &mut self,
+        id: NodeId,
+        res: LifetimeRes,
+        candidate: LifetimeElisionCandidate,
+    ) {
+        if let Some(prev_res) = self.r.lifetimes_res_map.insert(id, res) {
+            panic!("lifetime {id:?} resolved multiple times ({prev_res:?} before, {res:?} now)")
+        }
+        match res {
+            LifetimeRes::Param { .. } | LifetimeRes::Fresh { .. } | LifetimeRes::Static => {
+                if let Some(ref mut candidates) = self.lifetime_elision_candidates {
+                    candidates.push((res, candidate));
+                }
+            }
+            LifetimeRes::Infer | LifetimeRes::Error | LifetimeRes::ElidedAnchor { .. } => {}
+        }
+    }
+
+    #[instrument(level = "debug", skip(self))]
+    fn record_lifetime_param(&mut self, id: NodeId, res: LifetimeRes) {
+        if let Some(prev_res) = self.r.lifetimes_res_map.insert(id, res) {
+            panic!(
+                "lifetime parameter {id:?} resolved multiple times ({prev_res:?} before, {res:?} now)"
+            )
+        }
+    }
+
+    /// Perform resolution of a function signature, accounting for lifetime elision.
+    #[instrument(level = "debug", skip(self, inputs))]
+    fn resolve_fn_signature(
+        &mut self,
+        fn_id: NodeId,
+        has_self: bool,
+        inputs: impl Iterator<Item = (Option<&'ast Pat>, &'ast Ty)> + Clone,
+        output_ty: &'ast FnRetTy,
+    ) {
+        // Add each argument to the rib.
+        let elision_lifetime = self.resolve_fn_params(has_self, inputs);
+        debug!(?elision_lifetime);
+
+        let outer_failures = take(&mut self.diagnostic_metadata.current_elision_failures);
+        let output_rib = if let Ok(res) = elision_lifetime.as_ref() {
+            self.r.lifetime_elision_allowed.insert(fn_id);
+            LifetimeRibKind::Elided(*res)
+        } else {
+            LifetimeRibKind::ElisionFailure
+        };
+        self.with_lifetime_rib(output_rib, |this| visit::walk_fn_ret_ty(this, output_ty));
+        let elision_failures =
+            replace(&mut self.diagnostic_metadata.current_elision_failures, outer_failures);
+        if !elision_failures.is_empty() {
+            let Err(failure_info) = elision_lifetime else { bug!() };
+            self.report_missing_lifetime_specifiers(elision_failures, Some(failure_info));
+        }
+    }
+
+    /// Resolve inside function parameters and parameter types.
+    /// Returns the lifetime for elision in fn return type,
+    /// or diagnostic information in case of elision failure.
+    fn resolve_fn_params(
+        &mut self,
+        has_self: bool,
+        inputs: impl Iterator<Item = (Option<&'ast Pat>, &'ast Ty)>,
+    ) -> Result<LifetimeRes, (Vec<MissingLifetime>, Vec<ElisionFnParameter>)> {
+        enum Elision {
+            /// We have not found any candidate.
+            None,
+            /// We have a candidate bound to `self`.
+            Self_(LifetimeRes),
+            /// We have a candidate bound to a parameter.
+            Param(LifetimeRes),
+            /// We failed elision.
+            Err,
+        }
+
+        // Save elision state to reinstate it later.
+        let outer_candidates = self.lifetime_elision_candidates.take();
+
+        // Result of elision.
+        let mut elision_lifetime = Elision::None;
+        // Information for diagnostics.
+        let mut parameter_info = Vec::new();
+        let mut all_candidates = Vec::new();
+
+        let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())];
+        for (index, (pat, ty)) in inputs.enumerate() {
+            debug!(?pat, ?ty);
+            self.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Infer), |this| {
+                if let Some(pat) = pat {
+                    this.resolve_pattern(pat, PatternSource::FnParam, &mut bindings);
+                }
+            });
+
+            // Record elision candidates only for this parameter.
+            debug_assert_matches!(self.lifetime_elision_candidates, None);
+            self.lifetime_elision_candidates = Some(Default::default());
+            self.visit_ty(ty);
+            let local_candidates = self.lifetime_elision_candidates.take();
+
+            if let Some(candidates) = local_candidates {
+                let distinct: FxHashSet<_> = candidates.iter().map(|(res, _)| *res).collect();
+                let lifetime_count = distinct.len();
+                if lifetime_count != 0 {
+                    parameter_info.push(ElisionFnParameter {
+                        index,
+                        ident: if let Some(pat) = pat
+                            && let PatKind::Ident(_, ident, _) = pat.kind
+                        {
+                            Some(ident)
+                        } else {
+                            None
+                        },
+                        lifetime_count,
+                        span: ty.span,
+                    });
+                    all_candidates.extend(candidates.into_iter().filter_map(|(_, candidate)| {
+                        match candidate {
+                            LifetimeElisionCandidate::Ignore | LifetimeElisionCandidate::Named => {
+                                None
+                            }
+                            LifetimeElisionCandidate::Missing(missing) => Some(missing),
+                        }
+                    }));
+                }
+                let mut distinct_iter = distinct.into_iter();
+                if let Some(res) = distinct_iter.next() {
+                    match elision_lifetime {
+                        // We are the first parameter to bind lifetimes.
+                        Elision::None => {
+                            if distinct_iter.next().is_none() {
+                                // We have a single lifetime => success.
+                                elision_lifetime = Elision::Param(res)
+                            } else {
+                                // We have multiple lifetimes => error.
+                                elision_lifetime = Elision::Err;
+                            }
+                        }
+                        // We have 2 parameters that bind lifetimes => error.
+                        Elision::Param(_) => elision_lifetime = Elision::Err,
+                        // `self` elision takes precedence over everything else.
+                        Elision::Self_(_) | Elision::Err => {}
+                    }
+                }
+            }
+
+            // Handle `self` specially.
+            if index == 0 && has_self {
+                let self_lifetime = self.find_lifetime_for_self(ty);
+                if let Set1::One(lifetime) = self_lifetime {
+                    // We found `self` elision.
+                    elision_lifetime = Elision::Self_(lifetime);
+                } else {
+                    // We do not have `self` elision: disregard the `Elision::Param` that we may
+                    // have found.
+                    elision_lifetime = Elision::None;
+                }
+            }
+            debug!("(resolving function / closure) recorded parameter");
+        }
+
+        // Reinstate elision state.
+        debug_assert_matches!(self.lifetime_elision_candidates, None);
+        self.lifetime_elision_candidates = outer_candidates;
+
+        if let Elision::Param(res) | Elision::Self_(res) = elision_lifetime {
+            return Ok(res);
+        }
+
+        // We do not have a candidate.
+        Err((all_candidates, parameter_info))
+    }
+
+    /// List all the lifetimes that appear in the provided type.
+    fn find_lifetime_for_self(&self, ty: &'ast Ty) -> Set1<LifetimeRes> {
+        struct SelfVisitor<'r, 'a, 'tcx> {
+            r: &'r Resolver<'a, 'tcx>,
+            impl_self: Option<Res>,
+            lifetime: Set1<LifetimeRes>,
+        }
+
+        impl SelfVisitor<'_, '_, '_> {
+            // Look for `self: &'a Self` - also desugared from `&'a self`,
+            // and if that matches, use it for elision and return early.
+            fn is_self_ty(&self, ty: &Ty) -> bool {
+                match ty.kind {
+                    TyKind::ImplicitSelf => true,
+                    TyKind::Path(None, _) => {
+                        let path_res = self.r.partial_res_map[&ty.id].full_res();
+                        if let Some(Res::SelfTyParam { .. } | Res::SelfTyAlias { .. }) = path_res {
+                            return true;
+                        }
+                        self.impl_self.is_some() && path_res == self.impl_self
+                    }
+                    _ => false,
+                }
+            }
+        }
+
+        impl<'a> Visitor<'a> for SelfVisitor<'_, '_, '_> {
+            fn visit_ty(&mut self, ty: &'a Ty) {
+                trace!("SelfVisitor considering ty={:?}", ty);
+                if let TyKind::Ref(lt, ref mt) = ty.kind
+                    && self.is_self_ty(&mt.ty)
+                {
+                    let lt_id = if let Some(lt) = lt {
+                        lt.id
+                    } else {
+                        let res = self.r.lifetimes_res_map[&ty.id];
+                        let LifetimeRes::ElidedAnchor { start, .. } = res else { bug!() };
+                        start
+                    };
+                    let lt_res = self.r.lifetimes_res_map[&lt_id];
+                    trace!("SelfVisitor inserting res={:?}", lt_res);
+                    self.lifetime.insert(lt_res);
+                }
+                visit::walk_ty(self, ty)
+            }
+
+            // A type may have an expression as a const generic argument.
+            // We do not want to recurse into those.
+            fn visit_expr(&mut self, _: &'a Expr) {}
+        }
+
+        let impl_self = self
+            .diagnostic_metadata
+            .current_self_type
+            .as_ref()
+            .and_then(|ty| {
+                if let TyKind::Path(None, _) = ty.kind {
+                    self.r.partial_res_map.get(&ty.id)
+                } else {
+                    None
+                }
+            })
+            .and_then(|res| res.full_res())
+            .filter(|res| {
+                // Permit the types that unambiguously always
+                // result in the same type constructor being used
+                // (it can't differ between `Self` and `self`).
+                matches!(
+                    res,
+                    Res::Def(DefKind::Struct | DefKind::Union | DefKind::Enum, _,) | Res::PrimTy(_)
+                )
+            });
+        let mut visitor = SelfVisitor { r: self.r, impl_self, lifetime: Set1::Empty };
+        visitor.visit_ty(ty);
+        trace!("SelfVisitor found={:?}", visitor.lifetime);
+        visitor.lifetime
+    }
+
+    /// Searches the current set of local scopes for labels. Returns the `NodeId` of the resolved
+    /// label and reports an error if the label is not found or is unreachable.
+    fn resolve_label(&mut self, mut label: Ident) -> Result<(NodeId, Span), ResolutionError<'a>> {
+        let mut suggestion = None;
+
+        for i in (0..self.label_ribs.len()).rev() {
+            let rib = &self.label_ribs[i];
+
+            if let RibKind::MacroDefinition(def) = rib.kind {
+                // If an invocation of this macro created `ident`, give up on `ident`
+                // and switch to `ident`'s source from the macro definition.
+                if def == self.r.macro_def(label.span.ctxt()) {
+                    label.span.remove_mark();
+                }
+            }
+
+            let ident = label.normalize_to_macro_rules();
+            if let Some((ident, id)) = rib.bindings.get_key_value(&ident) {
+                let definition_span = ident.span;
+                return if self.is_label_valid_from_rib(i) {
+                    Ok((*id, definition_span))
+                } else {
+                    Err(ResolutionError::UnreachableLabel {
+                        name: label.name,
+                        definition_span,
+                        suggestion,
+                    })
+                };
+            }
+
+            // Diagnostics: Check if this rib contains a label with a similar name, keep track of
+            // the first such label that is encountered.
+            suggestion = suggestion.or_else(|| self.suggestion_for_label_in_rib(i, label));
+        }
+
+        Err(ResolutionError::UndeclaredLabel { name: label.name, suggestion })
+    }
+
+    /// Determine whether or not a label from the `rib_index`th label rib is reachable.
+    fn is_label_valid_from_rib(&self, rib_index: usize) -> bool {
+        let ribs = &self.label_ribs[rib_index + 1..];
+
+        for rib in ribs {
+            if rib.kind.is_label_barrier() {
+                return false;
+            }
+        }
+
+        true
+    }
+
+    fn resolve_adt(&mut self, item: &'ast Item, generics: &'ast Generics) {
+        debug!("resolve_adt");
+        self.with_current_self_item(item, |this| {
+            this.with_generic_param_rib(
+                &generics.params,
+                RibKind::Item(HasGenericParams::Yes(generics.span)),
+                LifetimeRibKind::Generics {
+                    binder: item.id,
+                    kind: LifetimeBinderKind::Item,
+                    span: generics.span,
+                },
+                |this| {
+                    let item_def_id = this.r.local_def_id(item.id).to_def_id();
+                    this.with_self_rib(
+                        Res::SelfTyAlias {
+                            alias_to: item_def_id,
+                            forbid_generic: false,
+                            is_trait_impl: false,
+                        },
+                        |this| {
+                            visit::walk_item(this, item);
+                        },
+                    );
+                },
+            );
+        });
+    }
+
+    fn future_proof_import(&mut self, use_tree: &UseTree) {
+        let segments = &use_tree.prefix.segments;
+        if !segments.is_empty() {
+            let ident = segments[0].ident;
+            if ident.is_path_segment_keyword() || ident.span.is_rust_2015() {
+                return;
+            }
+
+            let nss = match use_tree.kind {
+                UseTreeKind::Simple(..) if segments.len() == 1 => &[TypeNS, ValueNS][..],
+                _ => &[TypeNS],
+            };
+            let report_error = |this: &Self, ns| {
+                if this.should_report_errs() {
+                    let what = if ns == TypeNS { "type parameters" } else { "local variables" };
+                    this.r.dcx().emit_err(ImportsCannotReferTo { span: ident.span, what });
+                }
+            };
+
+            for &ns in nss {
+                match self.maybe_resolve_ident_in_lexical_scope(ident, ns) {
+                    Some(LexicalScopeBinding::Res(..)) => {
+                        report_error(self, ns);
+                    }
+                    Some(LexicalScopeBinding::Item(binding)) => {
+                        if let Some(LexicalScopeBinding::Res(..)) =
+                            self.resolve_ident_in_lexical_scope(ident, ns, None, Some(binding))
+                        {
+                            report_error(self, ns);
+                        }
+                    }
+                    None => {}
+                }
+            }
+        } else if let UseTreeKind::Nested(use_trees) = &use_tree.kind {
+            for (use_tree, _) in use_trees {
+                self.future_proof_import(use_tree);
+            }
+        }
+    }
+
+    fn resolve_item(&mut self, item: &'ast Item) {
+        let mod_inner_docs =
+            matches!(item.kind, ItemKind::Mod(..)) && rustdoc::inner_docs(&item.attrs);
+        if !mod_inner_docs && !matches!(item.kind, ItemKind::Impl(..) | ItemKind::Use(..)) {
+            self.resolve_doc_links(&item.attrs, MaybeExported::Ok(item.id));
+        }
+
+        let name = item.ident.name;
+        debug!("(resolving item) resolving {} ({:?})", name, item.kind);
+
+        match item.kind {
+            ItemKind::TyAlias(box TyAlias { ref generics, .. }) => {
+                self.with_generic_param_rib(
+                    &generics.params,
+                    RibKind::Item(HasGenericParams::Yes(generics.span)),
+                    LifetimeRibKind::Generics {
+                        binder: item.id,
+                        kind: LifetimeBinderKind::Item,
+                        span: generics.span,
+                    },
+                    |this| visit::walk_item(this, item),
+                );
+            }
+
+            ItemKind::Fn(box Fn { ref generics, .. }) => {
+                self.with_generic_param_rib(
+                    &generics.params,
+                    RibKind::Item(HasGenericParams::Yes(generics.span)),
+                    LifetimeRibKind::Generics {
+                        binder: item.id,
+                        kind: LifetimeBinderKind::Function,
+                        span: generics.span,
+                    },
+                    |this| visit::walk_item(this, item),
+                );
+            }
+
+            ItemKind::Enum(_, ref generics)
+            | ItemKind::Struct(_, ref generics)
+            | ItemKind::Union(_, ref generics) => {
+                self.resolve_adt(item, generics);
+            }
+
+            ItemKind::Impl(box Impl {
+                ref generics,
+                ref of_trait,
+                ref self_ty,
+                items: ref impl_items,
+                ..
+            }) => {
+                self.diagnostic_metadata.current_impl_items = Some(impl_items);
+                self.resolve_implementation(
+                    &item.attrs,
+                    generics,
+                    of_trait,
+                    self_ty,
+                    item.id,
+                    impl_items,
+                );
+                self.diagnostic_metadata.current_impl_items = None;
+            }
+
+            ItemKind::Trait(box Trait { ref generics, ref bounds, ref items, .. }) => {
+                // Create a new rib for the trait-wide type parameters.
+                self.with_generic_param_rib(
+                    &generics.params,
+                    RibKind::Item(HasGenericParams::Yes(generics.span)),
+                    LifetimeRibKind::Generics {
+                        binder: item.id,
+                        kind: LifetimeBinderKind::Item,
+                        span: generics.span,
+                    },
+                    |this| {
+                        let local_def_id = this.r.local_def_id(item.id).to_def_id();
+                        this.with_self_rib(Res::SelfTyParam { trait_: local_def_id }, |this| {
+                            this.visit_generics(generics);
+                            walk_list!(this, visit_param_bound, bounds, BoundKind::SuperTraits);
+                            this.resolve_trait_items(items);
+                        });
+                    },
+                );
+            }
+
+            ItemKind::TraitAlias(ref generics, ref bounds) => {
+                // Create a new rib for the trait-wide type parameters.
+                self.with_generic_param_rib(
+                    &generics.params,
+                    RibKind::Item(HasGenericParams::Yes(generics.span)),
+                    LifetimeRibKind::Generics {
+                        binder: item.id,
+                        kind: LifetimeBinderKind::Item,
+                        span: generics.span,
+                    },
+                    |this| {
+                        let local_def_id = this.r.local_def_id(item.id).to_def_id();
+                        this.with_self_rib(Res::SelfTyParam { trait_: local_def_id }, |this| {
+                            this.visit_generics(generics);
+                            walk_list!(this, visit_param_bound, bounds, BoundKind::Bound);
+                        });
+                    },
+                );
+            }
+
+            ItemKind::Mod(..) => {
+                self.with_scope(item.id, |this| {
+                    if mod_inner_docs {
+                        this.resolve_doc_links(&item.attrs, MaybeExported::Ok(item.id));
+                    }
+                    let old_macro_rules = this.parent_scope.macro_rules;
+                    visit::walk_item(this, item);
+                    // Maintain macro_rules scopes in the same way as during early resolution
+                    // for diagnostics and doc links.
+                    if item.attrs.iter().all(|attr| {
+                        !attr.has_name(sym::macro_use) && !attr.has_name(sym::macro_escape)
+                    }) {
+                        this.parent_scope.macro_rules = old_macro_rules;
+                    }
+                });
+            }
+
+            ItemKind::Static(box ast::StaticItem { ref ty, ref expr, .. }) => {
+                self.with_static_rib(|this| {
+                    this.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Static), |this| {
+                        this.visit_ty(ty);
+                    });
+                    if let Some(expr) = expr {
+                        // We already forbid generic params because of the above item rib,
+                        // so it doesn't matter whether this is a trivial constant.
+                        this.resolve_const_body(expr, Some((item.ident, ConstantItemKind::Static)));
+                    }
+                });
+            }
+
+            ItemKind::Const(box ast::ConstItem { ref generics, ref ty, ref expr, .. }) => {
+                self.with_generic_param_rib(
+                    &generics.params,
+                    RibKind::Item(if self.r.tcx.features().generic_const_items {
+                        HasGenericParams::Yes(generics.span)
+                    } else {
+                        HasGenericParams::No
+                    }),
+                    LifetimeRibKind::Generics {
+                        binder: item.id,
+                        kind: LifetimeBinderKind::ConstItem,
+                        span: generics.span,
+                    },
+                    |this| {
+                        this.visit_generics(generics);
+
+                        this.with_lifetime_rib(
+                            LifetimeRibKind::Elided(LifetimeRes::Static),
+                            |this| this.visit_ty(ty),
+                        );
+
+                        if let Some(expr) = expr {
+                            this.resolve_const_body(
+                                expr,
+                                Some((item.ident, ConstantItemKind::Const)),
+                            );
+                        }
+                    },
+                );
+            }
+
+            ItemKind::Use(ref use_tree) => {
+                let maybe_exported = match use_tree.kind {
+                    UseTreeKind::Simple(_) | UseTreeKind::Glob => MaybeExported::Ok(item.id),
+                    UseTreeKind::Nested(_) => MaybeExported::NestedUse(&item.vis),
+                };
+                self.resolve_doc_links(&item.attrs, maybe_exported);
+
+                self.future_proof_import(use_tree);
+            }
+
+            ItemKind::MacroDef(ref macro_def) => {
+                // Maintain macro_rules scopes in the same way as during early resolution
+                // for diagnostics and doc links.
+                if macro_def.macro_rules {
+                    let def_id = self.r.local_def_id(item.id);
+                    self.parent_scope.macro_rules = self.r.macro_rules_scopes[&def_id];
+                }
+            }
+
+            ItemKind::ForeignMod(_) | ItemKind::GlobalAsm(_) => {
+                visit::walk_item(self, item);
+            }
+
+            ItemKind::Delegation(ref delegation) => {
+                self.resolve_delegation(delegation);
+            }
+
+            ItemKind::ExternCrate(..) => {}
+
+            ItemKind::MacCall(_) => panic!("unexpanded macro in resolve!"),
+        }
+    }
+
+    fn with_generic_param_rib<'c, F>(
+        &'c mut self,
+        params: &'c [GenericParam],
+        kind: RibKind<'a>,
+        lifetime_kind: LifetimeRibKind,
+        f: F,
+    ) where
+        F: FnOnce(&mut Self),
+    {
+        debug!("with_generic_param_rib");
+        let LifetimeRibKind::Generics { binder, span: generics_span, kind: generics_kind, .. } =
+            lifetime_kind
+        else {
+            panic!()
+        };
+
+        let mut function_type_rib = Rib::new(kind);
+        let mut function_value_rib = Rib::new(kind);
+        let mut function_lifetime_rib = LifetimeRib::new(lifetime_kind);
+        let mut seen_bindings = FxHashMap::default();
+        // Store all seen lifetimes names from outer scopes.
+        let mut seen_lifetimes = FxHashSet::default();
+
+        // We also can't shadow bindings from the parent item
+        if let RibKind::AssocItem = kind {
+            let mut add_bindings_for_ns = |ns| {
+                let parent_rib = self.ribs[ns]
+                    .iter()
+                    .rfind(|r| matches!(r.kind, RibKind::Item(_)))
+                    .expect("associated item outside of an item");
+                seen_bindings.extend(parent_rib.bindings.keys().map(|ident| (*ident, ident.span)));
+            };
+            add_bindings_for_ns(ValueNS);
+            add_bindings_for_ns(TypeNS);
+        }
+
+        // Forbid shadowing lifetime bindings
+        for rib in self.lifetime_ribs.iter().rev() {
+            seen_lifetimes.extend(rib.bindings.iter().map(|(ident, _)| *ident));
+            if let LifetimeRibKind::Item = rib.kind {
+                break;
+            }
+        }
+
+        for param in params {
+            let ident = param.ident.normalize_to_macros_2_0();
+            debug!("with_generic_param_rib: {}", param.id);
+
+            if let GenericParamKind::Lifetime = param.kind
+                && let Some(&original) = seen_lifetimes.get(&ident)
+            {
+                diagnostics::signal_lifetime_shadowing(self.r.tcx.sess, original, param.ident);
+                // Record lifetime res, so lowering knows there is something fishy.
+                self.record_lifetime_param(param.id, LifetimeRes::Error);
+                continue;
+            }
+
+            match seen_bindings.entry(ident) {
+                Entry::Occupied(entry) => {
+                    let span = *entry.get();
+                    let err = ResolutionError::NameAlreadyUsedInParameterList(ident.name, span);
+                    self.report_error(param.ident.span, err);
+                    if let GenericParamKind::Lifetime = param.kind {
+                        // Record lifetime res, so lowering knows there is something fishy.
+                        self.record_lifetime_param(param.id, LifetimeRes::Error);
+                    }
+                    continue;
+                }
+                Entry::Vacant(entry) => {
+                    entry.insert(param.ident.span);
+                }
+            }
+
+            if param.ident.name == kw::UnderscoreLifetime {
+                rustc_errors::struct_span_code_err!(
+                    self.r.dcx(),
+                    param.ident.span,
+                    E0637,
+                    "`'_` cannot be used here"
+                )
+                .with_span_label(param.ident.span, "`'_` is a reserved lifetime name")
+                .emit();
+                // Record lifetime res, so lowering knows there is something fishy.
+                self.record_lifetime_param(param.id, LifetimeRes::Error);
+                continue;
+            }
+
+            if param.ident.name == kw::StaticLifetime {
+                rustc_errors::struct_span_code_err!(
+                    self.r.dcx(),
+                    param.ident.span,
+                    E0262,
+                    "invalid lifetime parameter name: `{}`",
+                    param.ident,
+                )
+                .with_span_label(param.ident.span, "'static is a reserved lifetime name")
+                .emit();
+                // Record lifetime res, so lowering knows there is something fishy.
+                self.record_lifetime_param(param.id, LifetimeRes::Error);
+                continue;
+            }
+
+            let def_id = self.r.local_def_id(param.id);
+
+            // Plain insert (no renaming).
+            let (rib, def_kind) = match param.kind {
+                GenericParamKind::Type { .. } => (&mut function_type_rib, DefKind::TyParam),
+                GenericParamKind::Const { .. } => (&mut function_value_rib, DefKind::ConstParam),
+                GenericParamKind::Lifetime => {
+                    let res = LifetimeRes::Param { param: def_id, binder };
+                    self.record_lifetime_param(param.id, res);
+                    function_lifetime_rib.bindings.insert(ident, (param.id, res));
+                    continue;
+                }
+            };
+
+            let res = match kind {
+                RibKind::Item(..) | RibKind::AssocItem => Res::Def(def_kind, def_id.to_def_id()),
+                RibKind::Normal => {
+                    if self.r.tcx.features().non_lifetime_binders {
+                        Res::Def(def_kind, def_id.to_def_id())
+                    } else {
+                        Res::Err
+                    }
+                }
+                _ => span_bug!(param.ident.span, "Unexpected rib kind {:?}", kind),
+            };
+            self.r.record_partial_res(param.id, PartialRes::new(res));
+            rib.bindings.insert(ident, res);
+        }
+
+        self.lifetime_ribs.push(function_lifetime_rib);
+        self.ribs[ValueNS].push(function_value_rib);
+        self.ribs[TypeNS].push(function_type_rib);
+
+        f(self);
+
+        self.ribs[TypeNS].pop();
+        self.ribs[ValueNS].pop();
+        let function_lifetime_rib = self.lifetime_ribs.pop().unwrap();
+
+        // Do not account for the parameters we just bound for function lifetime elision.
+        if let Some(ref mut candidates) = self.lifetime_elision_candidates {
+            for (_, res) in function_lifetime_rib.bindings.values() {
+                candidates.retain(|(r, _)| r != res);
+            }
+        }
+
+        if let LifetimeBinderKind::BareFnType
+        | LifetimeBinderKind::WhereBound
+        | LifetimeBinderKind::Function
+        | LifetimeBinderKind::ImplBlock = generics_kind
+        {
+            self.maybe_report_lifetime_uses(generics_span, params)
+        }
+    }
+
+    fn with_label_rib(&mut self, kind: RibKind<'a>, f: impl FnOnce(&mut Self)) {
+        self.label_ribs.push(Rib::new(kind));
+        f(self);
+        self.label_ribs.pop();
+    }
+
+    fn with_static_rib(&mut self, f: impl FnOnce(&mut Self)) {
+        let kind = RibKind::Item(HasGenericParams::No);
+        self.with_rib(ValueNS, kind, |this| this.with_rib(TypeNS, kind, f))
+    }
+
+    // HACK(min_const_generics, generic_const_exprs): We
+    // want to keep allowing `[0; std::mem::size_of::<*mut T>()]`
+    // with a future compat lint for now. We do this by adding an
+    // additional special case for repeat expressions.
+    //
+    // Note that we intentionally still forbid `[0; N + 1]` during
+    // name resolution so that we don't extend the future
+    // compat lint to new cases.
+    #[instrument(level = "debug", skip(self, f))]
+    fn with_constant_rib(
+        &mut self,
+        is_repeat: IsRepeatExpr,
+        may_use_generics: ConstantHasGenerics,
+        item: Option<(Ident, ConstantItemKind)>,
+        f: impl FnOnce(&mut Self),
+    ) {
+        let f = |this: &mut Self| {
+            this.with_rib(ValueNS, RibKind::ConstantItem(may_use_generics, item), |this| {
+                this.with_rib(
+                    TypeNS,
+                    RibKind::ConstantItem(
+                        may_use_generics.force_yes_if(is_repeat == IsRepeatExpr::Yes),
+                        item,
+                    ),
+                    |this| {
+                        this.with_label_rib(RibKind::ConstantItem(may_use_generics, item), f);
+                    },
+                )
+            })
+        };
+
+        if let ConstantHasGenerics::No(cause) = may_use_generics {
+            self.with_lifetime_rib(LifetimeRibKind::ConcreteAnonConst(cause), f)
+        } else {
+            f(self)
+        }
+    }
+
+    fn with_current_self_type<T>(&mut self, self_type: &Ty, f: impl FnOnce(&mut Self) -> T) -> T {
+        // Handle nested impls (inside fn bodies)
+        let previous_value =
+            replace(&mut self.diagnostic_metadata.current_self_type, Some(self_type.clone()));
+        let result = f(self);
+        self.diagnostic_metadata.current_self_type = previous_value;
+        result
+    }
+
+    fn with_current_self_item<T>(&mut self, self_item: &Item, f: impl FnOnce(&mut Self) -> T) -> T {
+        let previous_value =
+            replace(&mut self.diagnostic_metadata.current_self_item, Some(self_item.id));
+        let result = f(self);
+        self.diagnostic_metadata.current_self_item = previous_value;
+        result
+    }
+
+    /// When evaluating a `trait` use its associated types' idents for suggestions in E0412.
+    fn resolve_trait_items(&mut self, trait_items: &'ast [P<AssocItem>]) {
+        let trait_assoc_items =
+            replace(&mut self.diagnostic_metadata.current_trait_assoc_items, Some(trait_items));
+
+        let walk_assoc_item =
+            |this: &mut Self, generics: &Generics, kind, item: &'ast AssocItem| {
+                this.with_generic_param_rib(
+                    &generics.params,
+                    RibKind::AssocItem,
+                    LifetimeRibKind::Generics { binder: item.id, span: generics.span, kind },
+                    |this| visit::walk_assoc_item(this, item, AssocCtxt::Trait),
+                );
+            };
+
+        for item in trait_items {
+            self.resolve_doc_links(&item.attrs, MaybeExported::Ok(item.id));
+            match &item.kind {
+                AssocItemKind::Const(box ast::ConstItem { generics, ty, expr, .. }) => {
+                    self.with_generic_param_rib(
+                        &generics.params,
+                        RibKind::AssocItem,
+                        LifetimeRibKind::Generics {
+                            binder: item.id,
+                            span: generics.span,
+                            kind: LifetimeBinderKind::ConstItem,
+                        },
+                        |this| {
+                            this.visit_generics(generics);
+                            this.visit_ty(ty);
+
+                            // Only impose the restrictions of `ConstRibKind` for an
+                            // actual constant expression in a provided default.
+                            if let Some(expr) = expr {
+                                // We allow arbitrary const expressions inside of associated consts,
+                                // even if they are potentially not const evaluatable.
+                                //
+                                // Type parameters can already be used and as associated consts are
+                                // not used as part of the type system, this is far less surprising.
+                                this.resolve_const_body(expr, None);
+                            }
+                        },
+                    );
+                }
+                AssocItemKind::Fn(box Fn { generics, .. }) => {
+                    walk_assoc_item(self, generics, LifetimeBinderKind::Function, item);
+                }
+                AssocItemKind::Delegation(delegation) => {
+                    self.resolve_delegation(delegation);
+                }
+                AssocItemKind::Type(box TyAlias { generics, .. }) => self
+                    .with_lifetime_rib(LifetimeRibKind::AnonymousReportError, |this| {
+                        walk_assoc_item(this, generics, LifetimeBinderKind::Item, item)
+                    }),
+                AssocItemKind::MacCall(_) => {
+                    panic!("unexpanded macro in resolve!")
+                }
+            };
+        }
+
+        self.diagnostic_metadata.current_trait_assoc_items = trait_assoc_items;
+    }
+
+    /// This is called to resolve a trait reference from an `impl` (i.e., `impl Trait for Foo`).
+    fn with_optional_trait_ref<T>(
+        &mut self,
+        opt_trait_ref: Option<&TraitRef>,
+        self_type: &'ast Ty,
+        f: impl FnOnce(&mut Self, Option<DefId>) -> T,
+    ) -> T {
+        let mut new_val = None;
+        let mut new_id = None;
+        if let Some(trait_ref) = opt_trait_ref {
+            let path: Vec<_> = Segment::from_path(&trait_ref.path);
+            self.diagnostic_metadata.currently_processing_impl_trait =
+                Some((trait_ref.clone(), self_type.clone()));
+            let res = self.smart_resolve_path_fragment(
+                &None,
+                &path,
+                PathSource::Trait(AliasPossibility::No),
+                Finalize::new(trait_ref.ref_id, trait_ref.path.span),
+                RecordPartialRes::Yes,
+            );
+            self.diagnostic_metadata.currently_processing_impl_trait = None;
+            if let Some(def_id) = res.expect_full_res().opt_def_id() {
+                new_id = Some(def_id);
+                new_val = Some((self.r.expect_module(def_id), trait_ref.clone()));
+            }
+        }
+        let original_trait_ref = replace(&mut self.current_trait_ref, new_val);
+        let result = f(self, new_id);
+        self.current_trait_ref = original_trait_ref;
+        result
+    }
+
+    fn with_self_rib_ns(&mut self, ns: Namespace, self_res: Res, f: impl FnOnce(&mut Self)) {
+        let mut self_type_rib = Rib::new(RibKind::Normal);
+
+        // Plain insert (no renaming, since types are not currently hygienic)
+        self_type_rib.bindings.insert(Ident::with_dummy_span(kw::SelfUpper), self_res);
+        self.ribs[ns].push(self_type_rib);
+        f(self);
+        self.ribs[ns].pop();
+    }
+
+    fn with_self_rib(&mut self, self_res: Res, f: impl FnOnce(&mut Self)) {
+        self.with_self_rib_ns(TypeNS, self_res, f)
+    }
+
+    fn resolve_implementation(
+        &mut self,
+        attrs: &[ast::Attribute],
+        generics: &'ast Generics,
+        opt_trait_reference: &'ast Option<TraitRef>,
+        self_type: &'ast Ty,
+        item_id: NodeId,
+        impl_items: &'ast [P<AssocItem>],
+    ) {
+        debug!("resolve_implementation");
+        // If applicable, create a rib for the type parameters.
+        self.with_generic_param_rib(
+            &generics.params,
+            RibKind::Item(HasGenericParams::Yes(generics.span)),
+            LifetimeRibKind::Generics {
+                span: generics.span,
+                binder: item_id,
+                kind: LifetimeBinderKind::ImplBlock,
+            },
+            |this| {
+                // Dummy self type for better errors if `Self` is used in the trait path.
+                this.with_self_rib(Res::SelfTyParam { trait_: LOCAL_CRATE.as_def_id() }, |this| {
+                    this.with_lifetime_rib(
+                        LifetimeRibKind::AnonymousCreateParameter {
+                            binder: item_id,
+                            report_in_path: true
+                        },
+                        |this| {
+                            // Resolve the trait reference, if necessary.
+                            this.with_optional_trait_ref(
+                                opt_trait_reference.as_ref(),
+                                self_type,
+                                |this, trait_id| {
+                                    this.resolve_doc_links(attrs, MaybeExported::Impl(trait_id));
+
+                                    let item_def_id = this.r.local_def_id(item_id);
+
+                                    // Register the trait definitions from here.
+                                    if let Some(trait_id) = trait_id {
+                                        this.r
+                                            .trait_impls
+                                            .entry(trait_id)
+                                            .or_default()
+                                            .push(item_def_id);
+                                    }
+
+                                    let item_def_id = item_def_id.to_def_id();
+                                    let res = Res::SelfTyAlias {
+                                        alias_to: item_def_id,
+                                        forbid_generic: false,
+                                        is_trait_impl: trait_id.is_some()
+                                    };
+                                    this.with_self_rib(res, |this| {
+                                        if let Some(trait_ref) = opt_trait_reference.as_ref() {
+                                            // Resolve type arguments in the trait path.
+                                            visit::walk_trait_ref(this, trait_ref);
+                                        }
+                                        // Resolve the self type.
+                                        this.visit_ty(self_type);
+                                        // Resolve the generic parameters.
+                                        this.visit_generics(generics);
+
+                                        // Resolve the items within the impl.
+                                        this.with_current_self_type(self_type, |this| {
+                                            this.with_self_rib_ns(ValueNS, Res::SelfCtor(item_def_id), |this| {
+                                                debug!("resolve_implementation with_self_rib_ns(ValueNS, ...)");
+                                                let mut seen_trait_items = Default::default();
+                                                for item in impl_items {
+                                                    this.resolve_impl_item(&**item, &mut seen_trait_items, trait_id);
+                                                }
+                                            });
+                                        });
+                                    });
+                                },
+                            )
+                        },
+                    );
+                });
+            },
+        );
+    }
+
+    fn resolve_impl_item(
+        &mut self,
+        item: &'ast AssocItem,
+        seen_trait_items: &mut FxHashMap<DefId, Span>,
+        trait_id: Option<DefId>,
+    ) {
+        use crate::ResolutionError::*;
+        self.resolve_doc_links(&item.attrs, MaybeExported::ImplItem(trait_id.ok_or(&item.vis)));
+        match &item.kind {
+            AssocItemKind::Const(box ast::ConstItem { generics, ty, expr, .. }) => {
+                debug!("resolve_implementation AssocItemKind::Const");
+                self.with_generic_param_rib(
+                    &generics.params,
+                    RibKind::AssocItem,
+                    LifetimeRibKind::Generics {
+                        binder: item.id,
+                        span: generics.span,
+                        kind: LifetimeBinderKind::ConstItem,
+                    },
+                    |this| {
+                        this.with_lifetime_rib(LifetimeRibKind::AnonymousWarn(item.id), |this| {
+                            // If this is a trait impl, ensure the const
+                            // exists in trait
+                            this.check_trait_item(
+                                item.id,
+                                item.ident,
+                                &item.kind,
+                                ValueNS,
+                                item.span,
+                                seen_trait_items,
+                                |i, s, c| ConstNotMemberOfTrait(i, s, c),
+                            );
+
+                            this.visit_generics(generics);
+                            this.visit_ty(ty);
+                            if let Some(expr) = expr {
+                                // We allow arbitrary const expressions inside of associated consts,
+                                // even if they are potentially not const evaluatable.
+                                //
+                                // Type parameters can already be used and as associated consts are
+                                // not used as part of the type system, this is far less surprising.
+                                this.resolve_const_body(expr, None);
+                            }
+                        });
+                    },
+                );
+            }
+            AssocItemKind::Fn(box Fn { generics, .. }) => {
+                debug!("resolve_implementation AssocItemKind::Fn");
+                // We also need a new scope for the impl item type parameters.
+                self.with_generic_param_rib(
+                    &generics.params,
+                    RibKind::AssocItem,
+                    LifetimeRibKind::Generics {
+                        binder: item.id,
+                        span: generics.span,
+                        kind: LifetimeBinderKind::Function,
+                    },
+                    |this| {
+                        // If this is a trait impl, ensure the method
+                        // exists in trait
+                        this.check_trait_item(
+                            item.id,
+                            item.ident,
+                            &item.kind,
+                            ValueNS,
+                            item.span,
+                            seen_trait_items,
+                            |i, s, c| MethodNotMemberOfTrait(i, s, c),
+                        );
+
+                        visit::walk_assoc_item(this, item, AssocCtxt::Impl)
+                    },
+                );
+            }
+            AssocItemKind::Type(box TyAlias { generics, .. }) => {
+                debug!("resolve_implementation AssocItemKind::Type");
+                // We also need a new scope for the impl item type parameters.
+                self.with_generic_param_rib(
+                    &generics.params,
+                    RibKind::AssocItem,
+                    LifetimeRibKind::Generics {
+                        binder: item.id,
+                        span: generics.span,
+                        kind: LifetimeBinderKind::Item,
+                    },
+                    |this| {
+                        this.with_lifetime_rib(LifetimeRibKind::AnonymousReportError, |this| {
+                            // If this is a trait impl, ensure the type
+                            // exists in trait
+                            this.check_trait_item(
+                                item.id,
+                                item.ident,
+                                &item.kind,
+                                TypeNS,
+                                item.span,
+                                seen_trait_items,
+                                |i, s, c| TypeNotMemberOfTrait(i, s, c),
+                            );
+
+                            visit::walk_assoc_item(this, item, AssocCtxt::Impl)
+                        });
+                    },
+                );
+            }
+            AssocItemKind::Delegation(box delegation) => {
+                debug!("resolve_implementation AssocItemKind::Delegation");
+                self.check_trait_item(
+                    item.id,
+                    item.ident,
+                    &item.kind,
+                    ValueNS,
+                    item.span,
+                    seen_trait_items,
+                    |i, s, c| MethodNotMemberOfTrait(i, s, c),
+                );
+                self.resolve_delegation(delegation);
+            }
+            AssocItemKind::MacCall(_) => {
+                panic!("unexpanded macro in resolve!")
+            }
+        }
+    }
+
+    fn check_trait_item<F>(
+        &mut self,
+        id: NodeId,
+        mut ident: Ident,
+        kind: &AssocItemKind,
+        ns: Namespace,
+        span: Span,
+        seen_trait_items: &mut FxHashMap<DefId, Span>,
+        err: F,
+    ) where
+        F: FnOnce(Ident, String, Option<Symbol>) -> ResolutionError<'a>,
+    {
+        // If there is a TraitRef in scope for an impl, then the method must be in the trait.
+        let Some((module, _)) = self.current_trait_ref else {
+            return;
+        };
+        ident.span.normalize_to_macros_2_0_and_adjust(module.expansion);
+        let key = BindingKey::new(ident, ns);
+        let mut binding = self.r.resolution(module, key).try_borrow().ok().and_then(|r| r.binding);
+        debug!(?binding);
+        if binding.is_none() {
+            // We could not find the trait item in the correct namespace.
+            // Check the other namespace to report an error.
+            let ns = match ns {
+                ValueNS => TypeNS,
+                TypeNS => ValueNS,
+                _ => ns,
+            };
+            let key = BindingKey::new(ident, ns);
+            binding = self.r.resolution(module, key).try_borrow().ok().and_then(|r| r.binding);
+            debug!(?binding);
+        }
+
+        let feed_visibility = |this: &mut Self, def_id| {
+            let vis = this.r.tcx.visibility(def_id);
+            let vis = if vis.is_visible_locally() {
+                vis.expect_local()
+            } else {
+                this.r.dcx().span_delayed_bug(
+                    span,
+                    "error should be emitted when an unexpected trait item is used",
+                );
+                rustc_middle::ty::Visibility::Public
+            };
+            this.r.feed_visibility(this.r.local_def_id(id), vis);
+        };
+
+        let Some(binding) = binding else {
+            // We could not find the method: report an error.
+            let candidate = self.find_similarly_named_assoc_item(ident.name, kind);
+            let path = &self.current_trait_ref.as_ref().unwrap().1.path;
+            let path_names = path_names_to_string(path);
+            self.report_error(span, err(ident, path_names, candidate));
+            feed_visibility(self, module.def_id());
+            return;
+        };
+
+        let res = binding.res();
+        let Res::Def(def_kind, id_in_trait) = res else { bug!() };
+        feed_visibility(self, id_in_trait);
+
+        match seen_trait_items.entry(id_in_trait) {
+            Entry::Occupied(entry) => {
+                self.report_error(
+                    span,
+                    ResolutionError::TraitImplDuplicate {
+                        name: ident.name,
+                        old_span: *entry.get(),
+                        trait_item_span: binding.span,
+                    },
+                );
+                return;
+            }
+            Entry::Vacant(entry) => {
+                entry.insert(span);
+            }
+        };
+
+        match (def_kind, kind) {
+            (DefKind::AssocTy, AssocItemKind::Type(..))
+            | (DefKind::AssocFn, AssocItemKind::Fn(..))
+            | (DefKind::AssocConst, AssocItemKind::Const(..))
+            | (DefKind::AssocFn, AssocItemKind::Delegation(..)) => {
+                self.r.record_partial_res(id, PartialRes::new(res));
+                return;
+            }
+            _ => {}
+        }
+
+        // The method kind does not correspond to what appeared in the trait, report.
+        let path = &self.current_trait_ref.as_ref().unwrap().1.path;
+        let (code, kind) = match kind {
+            AssocItemKind::Const(..) => (rustc_errors::error_code!(E0323), "const"),
+            AssocItemKind::Fn(..) => (rustc_errors::error_code!(E0324), "method"),
+            AssocItemKind::Type(..) => (rustc_errors::error_code!(E0325), "type"),
+            AssocItemKind::Delegation(..) => (rustc_errors::error_code!(E0324), "method"),
+            AssocItemKind::MacCall(..) => span_bug!(span, "unexpanded macro"),
+        };
+        let trait_path = path_names_to_string(path);
+        self.report_error(
+            span,
+            ResolutionError::TraitImplMismatch {
+                name: ident.name,
+                kind,
+                code,
+                trait_path,
+                trait_item_span: binding.span,
+            },
+        );
+    }
+
+    fn resolve_const_body(&mut self, expr: &'ast Expr, item: Option<(Ident, ConstantItemKind)>) {
+        self.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Infer), |this| {
+            this.with_constant_rib(IsRepeatExpr::No, ConstantHasGenerics::Yes, item, |this| {
+                this.visit_expr(expr)
+            });
+        })
+    }
+
+    fn resolve_delegation(&mut self, delegation: &'ast Delegation) {
+        self.smart_resolve_path(
+            delegation.id,
+            &delegation.qself,
+            &delegation.path,
+            PathSource::Delegation,
+        );
+        if let Some(qself) = &delegation.qself {
+            self.visit_ty(&qself.ty);
+        }
+        self.visit_path(&delegation.path, delegation.id);
+        if let Some(body) = &delegation.body {
+            // `PatBoundCtx` is not necessary in this context
+            let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())];
+
+            let span = delegation.path.segments.last().unwrap().ident.span;
+            self.fresh_binding(
+                Ident::new(kw::SelfLower, span),
+                delegation.id,
+                PatternSource::FnParam,
+                &mut bindings,
+            );
+            self.visit_block(body);
+        }
+    }
+
+    fn resolve_params(&mut self, params: &'ast [Param]) {
+        let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())];
+        self.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Infer), |this| {
+            for Param { pat, .. } in params {
+                this.resolve_pattern(pat, PatternSource::FnParam, &mut bindings);
+            }
+        });
+        for Param { ty, .. } in params {
+            self.visit_ty(ty);
+        }
+    }
+
+    fn resolve_local(&mut self, local: &'ast Local) {
+        debug!("resolving local ({:?})", local);
+        // Resolve the type.
+        walk_list!(self, visit_ty, &local.ty);
+
+        // Resolve the initializer.
+        if let Some((init, els)) = local.kind.init_else_opt() {
+            self.visit_expr(init);
+
+            // Resolve the `else` block
+            if let Some(els) = els {
+                self.visit_block(els);
+            }
+        }
+
+        // Resolve the pattern.
+        self.resolve_pattern_top(&local.pat, PatternSource::Let);
+    }
+
+    /// build a map from pattern identifiers to binding-info's.
+    /// this is done hygienically. This could arise for a macro
+    /// that expands into an or-pattern where one 'x' was from the
+    /// user and one 'x' came from the macro.
+    fn binding_mode_map(&mut self, pat: &Pat) -> FxIndexMap<Ident, BindingInfo> {
+        let mut binding_map = FxIndexMap::default();
+
+        pat.walk(&mut |pat| {
+            match pat.kind {
+                PatKind::Ident(annotation, ident, ref sub_pat)
+                    if sub_pat.is_some() || self.is_base_res_local(pat.id) =>
+                {
+                    binding_map.insert(ident, BindingInfo { span: ident.span, annotation });
+                }
+                PatKind::Or(ref ps) => {
+                    // Check the consistency of this or-pattern and
+                    // then add all bindings to the larger map.
+                    for bm in self.check_consistent_bindings(ps) {
+                        binding_map.extend(bm);
+                    }
+                    return false;
+                }
+                _ => {}
+            }
+
+            true
+        });
+
+        binding_map
+    }
+
+    fn is_base_res_local(&self, nid: NodeId) -> bool {
+        matches!(
+            self.r.partial_res_map.get(&nid).map(|res| res.expect_full_res()),
+            Some(Res::Local(..))
+        )
+    }
+
+    /// Checks that all of the arms in an or-pattern have exactly the
+    /// same set of bindings, with the same binding modes for each.
+    fn check_consistent_bindings(
+        &mut self,
+        pats: &[P<Pat>],
+    ) -> Vec<FxIndexMap<Ident, BindingInfo>> {
+        // pats is consistent.
+        let mut missing_vars = FxIndexMap::default();
+        let mut inconsistent_vars = FxIndexMap::default();
+
+        // 1) Compute the binding maps of all arms.
+        let maps = pats.iter().map(|pat| self.binding_mode_map(pat)).collect::<Vec<_>>();
+
+        // 2) Record any missing bindings or binding mode inconsistencies.
+        for (map_outer, pat_outer) in maps.iter().zip(pats.iter()) {
+            // Check against all arms except for the same pattern which is always self-consistent.
+            let inners = maps
+                .iter()
+                .zip(pats.iter())
+                .filter(|(_, pat)| pat.id != pat_outer.id)
+                .flat_map(|(map, _)| map)
+                .map(|(key, binding)| (key.name, map_outer.get(key), binding));
+
+            let inners = inners.collect::<Vec<_>>();
+
+            for (name, info, &binding_inner) in inners {
+                match info {
+                    None => {
+                        // The inner binding is missing in the outer.
+                        let binding_error =
+                            missing_vars.entry(name).or_insert_with(|| BindingError {
+                                name,
+                                origin: BTreeSet::new(),
+                                target: BTreeSet::new(),
+                                could_be_path: name.as_str().starts_with(char::is_uppercase),
+                            });
+                        binding_error.origin.insert(binding_inner.span);
+                        binding_error.target.insert(pat_outer.span);
+                    }
+                    Some(binding_outer) => {
+                        if binding_outer.annotation != binding_inner.annotation {
+                            // The binding modes in the outer and inner bindings differ.
+                            inconsistent_vars
+                                .entry(name)
+                                .or_insert((binding_inner.span, binding_outer.span));
+                        }
+                    }
+                }
+            }
+        }
+
+        // 3) Report all missing variables we found.
+        for (name, mut v) in missing_vars {
+            if inconsistent_vars.contains_key(&name) {
+                v.could_be_path = false;
+            }
+            self.report_error(
+                *v.origin.iter().next().unwrap(),
+                ResolutionError::VariableNotBoundInPattern(v, self.parent_scope),
+            );
+        }
+
+        // 4) Report all inconsistencies in binding modes we found.
+        for (name, v) in inconsistent_vars {
+            self.report_error(v.0, ResolutionError::VariableBoundWithDifferentMode(name, v.1));
+        }
+
+        // 5) Finally bubble up all the binding maps.
+        maps
+    }
+
+    /// Check the consistency of the outermost or-patterns.
+    fn check_consistent_bindings_top(&mut self, pat: &'ast Pat) {
+        pat.walk(&mut |pat| match pat.kind {
+            PatKind::Or(ref ps) => {
+                self.check_consistent_bindings(ps);
+                false
+            }
+            _ => true,
+        })
+    }
+
+    fn resolve_arm(&mut self, arm: &'ast Arm) {
+        self.with_rib(ValueNS, RibKind::Normal, |this| {
+            this.resolve_pattern_top(&arm.pat, PatternSource::Match);
+            walk_list!(this, visit_expr, &arm.guard);
+            walk_list!(this, visit_expr, &arm.body);
+        });
+    }
+
+    /// Arising from `source`, resolve a top level pattern.
+    fn resolve_pattern_top(&mut self, pat: &'ast Pat, pat_src: PatternSource) {
+        let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())];
+        self.resolve_pattern(pat, pat_src, &mut bindings);
+    }
+
+    fn resolve_pattern(
+        &mut self,
+        pat: &'ast Pat,
+        pat_src: PatternSource,
+        bindings: &mut SmallVec<[(PatBoundCtx, FxHashSet<Ident>); 1]>,
+    ) {
+        // We walk the pattern before declaring the pattern's inner bindings,
+        // so that we avoid resolving a literal expression to a binding defined
+        // by the pattern.
+        visit::walk_pat(self, pat);
+        self.resolve_pattern_inner(pat, pat_src, bindings);
+        // This has to happen *after* we determine which pat_idents are variants:
+        self.check_consistent_bindings_top(pat);
+    }
+
+    /// Resolve bindings in a pattern. This is a helper to `resolve_pattern`.
+    ///
+    /// ### `bindings`
+    ///
+    /// A stack of sets of bindings accumulated.
+    ///
+    /// In each set, `PatBoundCtx::Product` denotes that a found binding in it should
+    /// be interpreted as re-binding an already bound binding. This results in an error.
+    /// Meanwhile, `PatBound::Or` denotes that a found binding in the set should result
+    /// in reusing this binding rather than creating a fresh one.
+    ///
+    /// When called at the top level, the stack must have a single element
+    /// with `PatBound::Product`. Otherwise, pushing to the stack happens as
+    /// or-patterns (`p_0 | ... | p_n`) are encountered and the context needs
+    /// to be switched to `PatBoundCtx::Or` and then `PatBoundCtx::Product` for each `p_i`.
+    /// When each `p_i` has been dealt with, the top set is merged with its parent.
+    /// When a whole or-pattern has been dealt with, the thing happens.
+    ///
+    /// See the implementation and `fresh_binding` for more details.
+    fn resolve_pattern_inner(
+        &mut self,
+        pat: &Pat,
+        pat_src: PatternSource,
+        bindings: &mut SmallVec<[(PatBoundCtx, FxHashSet<Ident>); 1]>,
+    ) {
+        // Visit all direct subpatterns of this pattern.
+        pat.walk(&mut |pat| {
+            debug!("resolve_pattern pat={:?} node={:?}", pat, pat.kind);
+            match pat.kind {
+                PatKind::Ident(bmode, ident, ref sub) => {
+                    // First try to resolve the identifier as some existing entity,
+                    // then fall back to a fresh binding.
+                    let has_sub = sub.is_some();
+                    let res = self
+                        .try_resolve_as_non_binding(pat_src, bmode, ident, has_sub)
+                        .unwrap_or_else(|| self.fresh_binding(ident, pat.id, pat_src, bindings));
+                    self.r.record_partial_res(pat.id, PartialRes::new(res));
+                    self.r.record_pat_span(pat.id, pat.span);
+                }
+                PatKind::TupleStruct(ref qself, ref path, ref sub_patterns) => {
+                    self.smart_resolve_path(
+                        pat.id,
+                        qself,
+                        path,
+                        PathSource::TupleStruct(
+                            pat.span,
+                            self.r.arenas.alloc_pattern_spans(sub_patterns.iter().map(|p| p.span)),
+                        ),
+                    );
+                }
+                PatKind::Path(ref qself, ref path) => {
+                    self.smart_resolve_path(pat.id, qself, path, PathSource::Pat);
+                }
+                PatKind::Struct(ref qself, ref path, ..) => {
+                    self.smart_resolve_path(pat.id, qself, path, PathSource::Struct);
+                }
+                PatKind::Or(ref ps) => {
+                    // Add a new set of bindings to the stack. `Or` here records that when a
+                    // binding already exists in this set, it should not result in an error because
+                    // `V1(a) | V2(a)` must be allowed and are checked for consistency later.
+                    bindings.push((PatBoundCtx::Or, Default::default()));
+                    for p in ps {
+                        // Now we need to switch back to a product context so that each
+                        // part of the or-pattern internally rejects already bound names.
+                        // For example, `V1(a) | V2(a, a)` and `V1(a, a) | V2(a)` are bad.
+                        bindings.push((PatBoundCtx::Product, Default::default()));
+                        self.resolve_pattern_inner(p, pat_src, bindings);
+                        // Move up the non-overlapping bindings to the or-pattern.
+                        // Existing bindings just get "merged".
+                        let collected = bindings.pop().unwrap().1;
+                        bindings.last_mut().unwrap().1.extend(collected);
+                    }
+                    // This or-pattern itself can itself be part of a product,
+                    // e.g. `(V1(a) | V2(a), a)` or `(a, V1(a) | V2(a))`.
+                    // Both cases bind `a` again in a product pattern and must be rejected.
+                    let collected = bindings.pop().unwrap().1;
+                    bindings.last_mut().unwrap().1.extend(collected);
+
+                    // Prevent visiting `ps` as we've already done so above.
+                    return false;
+                }
+                _ => {}
+            }
+            true
+        });
+    }
+
+    fn fresh_binding(
+        &mut self,
+        ident: Ident,
+        pat_id: NodeId,
+        pat_src: PatternSource,
+        bindings: &mut SmallVec<[(PatBoundCtx, FxHashSet<Ident>); 1]>,
+    ) -> Res {
+        // Add the binding to the local ribs, if it doesn't already exist in the bindings map.
+        // (We must not add it if it's in the bindings map because that breaks the assumptions
+        // later passes make about or-patterns.)
+        let ident = ident.normalize_to_macro_rules();
+
+        let mut bound_iter = bindings.iter().filter(|(_, set)| set.contains(&ident));
+        // Already bound in a product pattern? e.g. `(a, a)` which is not allowed.
+        let already_bound_and = bound_iter.clone().any(|(ctx, _)| *ctx == PatBoundCtx::Product);
+        // Already bound in an or-pattern? e.g. `V1(a) | V2(a)`.
+        // This is *required* for consistency which is checked later.
+        let already_bound_or = bound_iter.any(|(ctx, _)| *ctx == PatBoundCtx::Or);
+
+        if already_bound_and {
+            // Overlap in a product pattern somewhere; report an error.
+            use ResolutionError::*;
+            let error = match pat_src {
+                // `fn f(a: u8, a: u8)`:
+                PatternSource::FnParam => IdentifierBoundMoreThanOnceInParameterList,
+                // `Variant(a, a)`:
+                _ => IdentifierBoundMoreThanOnceInSamePattern,
+            };
+            self.report_error(ident.span, error(ident.name));
+        }
+
+        // Record as bound if it's valid:
+        let ident_valid = ident.name != kw::Empty;
+        if ident_valid {
+            bindings.last_mut().unwrap().1.insert(ident);
+        }
+
+        if already_bound_or {
+            // `Variant1(a) | Variant2(a)`, ok
+            // Reuse definition from the first `a`.
+            self.innermost_rib_bindings(ValueNS)[&ident]
+        } else {
+            let res = Res::Local(pat_id);
+            if ident_valid {
+                // A completely fresh binding add to the set if it's valid.
+                self.innermost_rib_bindings(ValueNS).insert(ident, res);
+            }
+            res
+        }
+    }
+
+    fn innermost_rib_bindings(&mut self, ns: Namespace) -> &mut IdentMap<Res> {
+        &mut self.ribs[ns].last_mut().unwrap().bindings
+    }
+
+    fn try_resolve_as_non_binding(
+        &mut self,
+        pat_src: PatternSource,
+        ann: BindingAnnotation,
+        ident: Ident,
+        has_sub: bool,
+    ) -> Option<Res> {
+        // An immutable (no `mut`) by-value (no `ref`) binding pattern without
+        // a sub pattern (no `@ $pat`) is syntactically ambiguous as it could
+        // also be interpreted as a path to e.g. a constant, variant, etc.
+        let is_syntactic_ambiguity = !has_sub && ann == BindingAnnotation::NONE;
+
+        let ls_binding = self.maybe_resolve_ident_in_lexical_scope(ident, ValueNS)?;
+        let (res, binding) = match ls_binding {
+            LexicalScopeBinding::Item(binding)
+                if is_syntactic_ambiguity && binding.is_ambiguity() =>
+            {
+                // For ambiguous bindings we don't know all their definitions and cannot check
+                // whether they can be shadowed by fresh bindings or not, so force an error.
+                // issues/33118#issuecomment-233962221 (see below) still applies here,
+                // but we have to ignore it for backward compatibility.
+                self.r.record_use(ident, binding, false);
+                return None;
+            }
+            LexicalScopeBinding::Item(binding) => (binding.res(), Some(binding)),
+            LexicalScopeBinding::Res(res) => (res, None),
+        };
+
+        match res {
+            Res::SelfCtor(_) // See #70549.
+            | Res::Def(
+                DefKind::Ctor(_, CtorKind::Const) | DefKind::Const | DefKind::ConstParam,
+                _,
+            ) if is_syntactic_ambiguity => {
+                // Disambiguate in favor of a unit struct/variant or constant pattern.
+                if let Some(binding) = binding {
+                    self.r.record_use(ident, binding, false);
+                }
+                Some(res)
+            }
+            Res::Def(DefKind::Ctor(..) | DefKind::Const | DefKind::Static(_), _) => {
+                // This is unambiguously a fresh binding, either syntactically
+                // (e.g., `IDENT @ PAT` or `ref IDENT`) or because `IDENT` resolves
+                // to something unusable as a pattern (e.g., constructor function),
+                // but we still conservatively report an error, see
+                // issues/33118#issuecomment-233962221 for one reason why.
+                let binding = binding.expect("no binding for a ctor or static");
+                self.report_error(
+                    ident.span,
+                    ResolutionError::BindingShadowsSomethingUnacceptable {
+                        shadowing_binding: pat_src,
+                        name: ident.name,
+                        participle: if binding.is_import() { "imported" } else { "defined" },
+                        article: binding.res().article(),
+                        shadowed_binding: binding.res(),
+                        shadowed_binding_span: binding.span,
+                    },
+                );
+                None
+            }
+            Res::Def(DefKind::ConstParam, def_id) => {
+                // Same as for DefKind::Const above, but here, `binding` is `None`, so we
+                // have to construct the error differently
+                self.report_error(
+                    ident.span,
+                    ResolutionError::BindingShadowsSomethingUnacceptable {
+                        shadowing_binding: pat_src,
+                        name: ident.name,
+                        participle: "defined",
+                        article: res.article(),
+                        shadowed_binding: res,
+                        shadowed_binding_span: self.r.def_span(def_id),
+                    }
+                );
+                None
+            }
+            Res::Def(DefKind::Fn, _) | Res::Local(..) | Res::Err => {
+                // These entities are explicitly allowed to be shadowed by fresh bindings.
+                None
+            }
+            Res::SelfCtor(_) => {
+                // We resolve `Self` in pattern position as an ident sometimes during recovery,
+                // so delay a bug instead of ICEing.
+                self.r.dcx().span_delayed_bug(
+                    ident.span,
+                    "unexpected `SelfCtor` in pattern, expected identifier"
+                );
+                None
+            }
+            _ => span_bug!(
+                ident.span,
+                "unexpected resolution for an identifier in pattern: {:?}",
+                res,
+            ),
+        }
+    }
+
+    // High-level and context dependent path resolution routine.
+    // Resolves the path and records the resolution into definition map.
+    // If resolution fails tries several techniques to find likely
+    // resolution candidates, suggest imports or other help, and report
+    // errors in user friendly way.
+    fn smart_resolve_path(
+        &mut self,
+        id: NodeId,
+        qself: &Option<P<QSelf>>,
+        path: &Path,
+        source: PathSource<'ast>,
+    ) {
+        self.smart_resolve_path_fragment(
+            qself,
+            &Segment::from_path(path),
+            source,
+            Finalize::new(id, path.span),
+            RecordPartialRes::Yes,
+        );
+    }
+
+    #[instrument(level = "debug", skip(self))]
+    fn smart_resolve_path_fragment(
+        &mut self,
+        qself: &Option<P<QSelf>>,
+        path: &[Segment],
+        source: PathSource<'ast>,
+        finalize: Finalize,
+        record_partial_res: RecordPartialRes,
+    ) -> PartialRes {
+        let ns = source.namespace();
+
+        let Finalize { node_id, path_span, .. } = finalize;
+        let report_errors = |this: &mut Self, res: Option<Res>| {
+            if this.should_report_errs() {
+                let (err, candidates) =
+                    this.smart_resolve_report_errors(path, None, path_span, source, res);
+
+                let def_id = this.parent_scope.module.nearest_parent_mod();
+                let instead = res.is_some();
+                let suggestion = if let Some((start, end)) = this.diagnostic_metadata.in_range
+                    && path[0].ident.span.lo() == end.span.lo()
+                {
+                    let mut sugg = ".";
+                    let mut span = start.span.between(end.span);
+                    if span.lo() + BytePos(2) == span.hi() {
+                        // There's no space between the start, the range op and the end, suggest
+                        // removal which will look better.
+                        span = span.with_lo(span.lo() + BytePos(1));
+                        sugg = "";
+                    }
+                    Some((
+                        span,
+                        "you might have meant to write `.` instead of `..`",
+                        sugg.to_string(),
+                        Applicability::MaybeIncorrect,
+                    ))
+                } else if res.is_none()
+                    && let PathSource::Type | PathSource::Expr(_) = source
+                {
+                    this.suggest_adding_generic_parameter(path, source)
+                } else {
+                    None
+                };
+
+                let ue = UseError {
+                    err,
+                    candidates,
+                    def_id,
+                    instead,
+                    suggestion,
+                    path: path.into(),
+                    is_call: source.is_call(),
+                };
+
+                this.r.use_injections.push(ue);
+            }
+
+            PartialRes::new(Res::Err)
+        };
+
+        // For paths originating from calls (like in `HashMap::new()`), tries
+        // to enrich the plain `failed to resolve: ...` message with hints
+        // about possible missing imports.
+        //
+        // Similar thing, for types, happens in `report_errors` above.
+        let report_errors_for_call = |this: &mut Self, parent_err: Spanned<ResolutionError<'a>>| {
+            // Before we start looking for candidates, we have to get our hands
+            // on the type user is trying to perform invocation on; basically:
+            // we're transforming `HashMap::new` into just `HashMap`.
+            let (following_seg, prefix_path) = match path.split_last() {
+                Some((last, path)) if !path.is_empty() => (Some(last), path),
+                _ => return Some(parent_err),
+            };
+
+            let (mut err, candidates) = this.smart_resolve_report_errors(
+                prefix_path,
+                following_seg,
+                path_span,
+                PathSource::Type,
+                None,
+            );
+
+            // There are two different error messages user might receive at
+            // this point:
+            // - E0412 cannot find type `{}` in this scope
+            // - E0433 failed to resolve: use of undeclared type or module `{}`
+            //
+            // The first one is emitted for paths in type-position, and the
+            // latter one - for paths in expression-position.
+            //
+            // Thus (since we're in expression-position at this point), not to
+            // confuse the user, we want to keep the *message* from E0433 (so
+            // `parent_err`), but we want *hints* from E0412 (so `err`).
+            //
+            // And that's what happens below - we're just mixing both messages
+            // into a single one.
+            let mut parent_err = this.r.into_struct_error(parent_err.span, parent_err.node);
+
+            // overwrite all properties with the parent's error message
+            err.messages = take(&mut parent_err.messages);
+            err.code = take(&mut parent_err.code);
+            swap(&mut err.span, &mut parent_err.span);
+            err.children = take(&mut parent_err.children);
+            err.sort_span = parent_err.sort_span;
+            err.is_lint = parent_err.is_lint;
+
+            // merge the parent's suggestions with the typo suggestions
+            fn append_result<T, E>(res1: &mut Result<Vec<T>, E>, res2: Result<Vec<T>, E>) {
+                match res1 {
+                    Ok(vec1) => match res2 {
+                        Ok(mut vec2) => vec1.append(&mut vec2),
+                        Err(e) => *res1 = Err(e),
+                    },
+                    Err(_) => (),
+                };
+            }
+            append_result(&mut err.suggestions, parent_err.suggestions.clone());
+
+            parent_err.cancel();
+
+            let def_id = this.parent_scope.module.nearest_parent_mod();
+
+            if this.should_report_errs() {
+                if candidates.is_empty() {
+                    if path.len() == 2 && prefix_path.len() == 1 {
+                        // Delay to check whether methond name is an associated function or not
+                        // ```
+                        // let foo = Foo {};
+                        // foo::bar(); // possibly suggest to foo.bar();
+                        //```
+                        err.stash(
+                            prefix_path[0].ident.span,
+                            rustc_errors::StashKey::CallAssocMethod,
+                        );
+                    } else {
+                        // When there is no suggested imports, we can just emit the error
+                        // and suggestions immediately. Note that we bypass the usually error
+                        // reporting routine (ie via `self.r.report_error`) because we need
+                        // to post-process the `ResolutionError` above.
+                        err.emit();
+                    }
+                } else {
+                    // If there are suggested imports, the error reporting is delayed
+                    this.r.use_injections.push(UseError {
+                        err,
+                        candidates,
+                        def_id,
+                        instead: false,
+                        suggestion: None,
+                        path: prefix_path.into(),
+                        is_call: source.is_call(),
+                    });
+                }
+            } else {
+                err.cancel();
+            }
+
+            // We don't return `Some(parent_err)` here, because the error will
+            // be already printed either immediately or as part of the `use` injections
+            None
+        };
+
+        let partial_res = match self.resolve_qpath_anywhere(
+            qself,
+            path,
+            ns,
+            path_span,
+            source.defer_to_typeck(),
+            finalize,
+        ) {
+            Ok(Some(partial_res)) if let Some(res) = partial_res.full_res() => {
+                if source.is_expected(res) || res == Res::Err {
+                    partial_res
+                } else {
+                    report_errors(self, Some(res))
+                }
+            }
+
+            Ok(Some(partial_res)) if source.defer_to_typeck() => {
+                // Not fully resolved associated item `T::A::B` or `<T as Tr>::A::B`
+                // or `<T>::A::B`. If `B` should be resolved in value namespace then
+                // it needs to be added to the trait map.
+                if ns == ValueNS {
+                    let item_name = path.last().unwrap().ident;
+                    let traits = self.traits_in_scope(item_name, ns);
+                    self.r.trait_map.insert(node_id, traits);
+                }
+
+                if PrimTy::from_name(path[0].ident.name).is_some() {
+                    let mut std_path = Vec::with_capacity(1 + path.len());
+
+                    std_path.push(Segment::from_ident(Ident::with_dummy_span(sym::std)));
+                    std_path.extend(path);
+                    if let PathResult::Module(_) | PathResult::NonModule(_) =
+                        self.resolve_path(&std_path, Some(ns), None)
+                    {
+                        // Check if we wrote `str::from_utf8` instead of `std::str::from_utf8`
+                        let item_span =
+                            path.iter().last().map_or(path_span, |segment| segment.ident.span);
+
+                        self.r.confused_type_with_std_module.insert(item_span, path_span);
+                        self.r.confused_type_with_std_module.insert(path_span, path_span);
+                    }
+                }
+
+                partial_res
+            }
+
+            Err(err) => {
+                if let Some(err) = report_errors_for_call(self, err) {
+                    self.report_error(err.span, err.node);
+                }
+
+                PartialRes::new(Res::Err)
+            }
+
+            _ => report_errors(self, None),
+        };
+
+        if record_partial_res == RecordPartialRes::Yes {
+            // Avoid recording definition of `A::B` in `<T as A>::B::C`.
+            self.r.record_partial_res(node_id, partial_res);
+            self.resolve_elided_lifetimes_in_path(node_id, partial_res, path, source, path_span);
+        }
+
+        partial_res
+    }
+
+    fn self_type_is_available(&mut self) -> bool {
+        let binding = self
+            .maybe_resolve_ident_in_lexical_scope(Ident::with_dummy_span(kw::SelfUpper), TypeNS);
+        if let Some(LexicalScopeBinding::Res(res)) = binding { res != Res::Err } else { false }
+    }
+
+    fn self_value_is_available(&mut self, self_span: Span) -> bool {
+        let ident = Ident::new(kw::SelfLower, self_span);
+        let binding = self.maybe_resolve_ident_in_lexical_scope(ident, ValueNS);
+        if let Some(LexicalScopeBinding::Res(res)) = binding { res != Res::Err } else { false }
+    }
+
+    /// A wrapper around [`Resolver::report_error`].
+    ///
+    /// This doesn't emit errors for function bodies if this is rustdoc.
+    fn report_error(&mut self, span: Span, resolution_error: ResolutionError<'a>) {
+        if self.should_report_errs() {
+            self.r.report_error(span, resolution_error);
+        }
+    }
+
+    #[inline]
+    /// If we're actually rustdoc then avoid giving a name resolution error for `cfg()` items.
+    fn should_report_errs(&self) -> bool {
+        !(self.r.tcx.sess.opts.actually_rustdoc && self.in_func_body)
+    }
+
+    // Resolve in alternative namespaces if resolution in the primary namespace fails.
+    fn resolve_qpath_anywhere(
+        &mut self,
+        qself: &Option<P<QSelf>>,
+        path: &[Segment],
+        primary_ns: Namespace,
+        span: Span,
+        defer_to_typeck: bool,
+        finalize: Finalize,
+    ) -> Result<Option<PartialRes>, Spanned<ResolutionError<'a>>> {
+        let mut fin_res = None;
+
+        for (i, &ns) in [primary_ns, TypeNS, ValueNS].iter().enumerate() {
+            if i == 0 || ns != primary_ns {
+                match self.resolve_qpath(qself, path, ns, finalize)? {
+                    Some(partial_res)
+                        if partial_res.unresolved_segments() == 0 || defer_to_typeck =>
+                    {
+                        return Ok(Some(partial_res));
+                    }
+                    partial_res => {
+                        if fin_res.is_none() {
+                            fin_res = partial_res;
+                        }
+                    }
+                }
+            }
+        }
+
+        assert!(primary_ns != MacroNS);
+
+        if qself.is_none() {
+            let path_seg = |seg: &Segment| PathSegment::from_ident(seg.ident);
+            let path = Path { segments: path.iter().map(path_seg).collect(), span, tokens: None };
+            if let Ok((_, res)) =
+                self.r.resolve_macro_path(&path, None, &self.parent_scope, false, false)
+            {
+                return Ok(Some(PartialRes::new(res)));
+            }
+        }
+
+        Ok(fin_res)
+    }
+
+    /// Handles paths that may refer to associated items.
+    fn resolve_qpath(
+        &mut self,
+        qself: &Option<P<QSelf>>,
+        path: &[Segment],
+        ns: Namespace,
+        finalize: Finalize,
+    ) -> Result<Option<PartialRes>, Spanned<ResolutionError<'a>>> {
+        debug!(
+            "resolve_qpath(qself={:?}, path={:?}, ns={:?}, finalize={:?})",
+            qself, path, ns, finalize,
+        );
+
+        if let Some(qself) = qself {
+            if qself.position == 0 {
+                // This is a case like `<T>::B`, where there is no
+                // trait to resolve. In that case, we leave the `B`
+                // segment to be resolved by type-check.
+                return Ok(Some(PartialRes::with_unresolved_segments(
+                    Res::Def(DefKind::Mod, CRATE_DEF_ID.to_def_id()),
+                    path.len(),
+                )));
+            }
+
+            let num_privacy_errors = self.r.privacy_errors.len();
+            // Make sure that `A` in `<T as A>::B::C` is a trait.
+            let trait_res = self.smart_resolve_path_fragment(
+                &None,
+                &path[..qself.position],
+                PathSource::Trait(AliasPossibility::No),
+                Finalize::new(finalize.node_id, qself.path_span),
+                RecordPartialRes::No,
+            );
+
+            if trait_res.expect_full_res() == Res::Err {
+                return Ok(Some(trait_res));
+            }
+
+            // Truncate additional privacy errors reported above,
+            // because they'll be recomputed below.
+            self.r.privacy_errors.truncate(num_privacy_errors);
+
+            // Make sure `A::B` in `<T as A>::B::C` is a trait item.
+            //
+            // Currently, `path` names the full item (`A::B::C`, in
+            // our example). so we extract the prefix of that that is
+            // the trait (the slice upto and including
+            // `qself.position`). And then we recursively resolve that,
+            // but with `qself` set to `None`.
+            let ns = if qself.position + 1 == path.len() { ns } else { TypeNS };
+            let partial_res = self.smart_resolve_path_fragment(
+                &None,
+                &path[..=qself.position],
+                PathSource::TraitItem(ns),
+                Finalize::with_root_span(finalize.node_id, finalize.path_span, qself.path_span),
+                RecordPartialRes::No,
+            );
+
+            // The remaining segments (the `C` in our example) will
+            // have to be resolved by type-check, since that requires doing
+            // trait resolution.
+            return Ok(Some(PartialRes::with_unresolved_segments(
+                partial_res.base_res(),
+                partial_res.unresolved_segments() + path.len() - qself.position - 1,
+            )));
+        }
+
+        let result = match self.resolve_path(path, Some(ns), Some(finalize)) {
+            PathResult::NonModule(path_res) => path_res,
+            PathResult::Module(ModuleOrUniformRoot::Module(module)) if !module.is_normal() => {
+                PartialRes::new(module.res().unwrap())
+            }
+            // In `a(::assoc_item)*` `a` cannot be a module. If `a` does resolve to a module we
+            // don't report an error right away, but try to fallback to a primitive type.
+            // So, we are still able to successfully resolve something like
+            //
+            // use std::u8; // bring module u8 in scope
+            // fn f() -> u8 { // OK, resolves to primitive u8, not to std::u8
+            //     u8::max_value() // OK, resolves to associated function <u8>::max_value,
+            //                     // not to nonexistent std::u8::max_value
+            // }
+            //
+            // Such behavior is required for backward compatibility.
+            // The same fallback is used when `a` resolves to nothing.
+            PathResult::Module(ModuleOrUniformRoot::Module(_)) | PathResult::Failed { .. }
+                if (ns == TypeNS || path.len() > 1)
+                    && PrimTy::from_name(path[0].ident.name).is_some() =>
+            {
+                let prim = PrimTy::from_name(path[0].ident.name).unwrap();
+                PartialRes::with_unresolved_segments(Res::PrimTy(prim), path.len() - 1)
+            }
+            PathResult::Module(ModuleOrUniformRoot::Module(module)) => {
+                PartialRes::new(module.res().unwrap())
+            }
+            PathResult::Failed {
+                is_error_from_last_segment: false,
+                span,
+                label,
+                suggestion,
+                module,
+                segment_name,
+            } => {
+                return Err(respan(
+                    span,
+                    ResolutionError::FailedToResolve {
+                        segment: Some(segment_name),
+                        label,
+                        suggestion,
+                        module,
+                    },
+                ));
+            }
+            PathResult::Module(..) | PathResult::Failed { .. } => return Ok(None),
+            PathResult::Indeterminate => bug!("indeterminate path result in resolve_qpath"),
+        };
+
+        if path.len() > 1
+            && let Some(res) = result.full_res()
+            && let Some((&last_segment, prev_segs)) = path.split_last()
+            && prev_segs.iter().all(|seg| !seg.has_generic_args)
+            && res != Res::Err
+            && path[0].ident.name != kw::PathRoot
+            && path[0].ident.name != kw::DollarCrate
+        {
+            let unqualified_result = {
+                match self.resolve_path(&[last_segment], Some(ns), None) {
+                    PathResult::NonModule(path_res) => path_res.expect_full_res(),
+                    PathResult::Module(ModuleOrUniformRoot::Module(module)) => {
+                        module.res().unwrap()
+                    }
+                    _ => return Ok(Some(result)),
+                }
+            };
+            if res == unqualified_result {
+                let lint = lint::builtin::UNUSED_QUALIFICATIONS;
+                self.r.lint_buffer.buffer_lint_with_diagnostic(
+                    lint,
+                    finalize.node_id,
+                    finalize.path_span,
+                    "unnecessary qualification",
+                    lint::BuiltinLintDiagnostics::UnusedQualifications {
+                        removal_span: finalize.path_span.until(last_segment.ident.span),
+                    },
+                )
+            }
+        }
+
+        Ok(Some(result))
+    }
+
+    fn with_resolved_label(&mut self, label: Option<Label>, id: NodeId, f: impl FnOnce(&mut Self)) {
+        if let Some(label) = label {
+            if label.ident.as_str().as_bytes()[1] != b'_' {
+                self.diagnostic_metadata.unused_labels.insert(id, label.ident.span);
+            }
+
+            if let Ok((_, orig_span)) = self.resolve_label(label.ident) {
+                diagnostics::signal_label_shadowing(self.r.tcx.sess, orig_span, label.ident)
+            }
+
+            self.with_label_rib(RibKind::Normal, |this| {
+                let ident = label.ident.normalize_to_macro_rules();
+                this.label_ribs.last_mut().unwrap().bindings.insert(ident, id);
+                f(this);
+            });
+        } else {
+            f(self);
+        }
+    }
+
+    fn resolve_labeled_block(&mut self, label: Option<Label>, id: NodeId, block: &'ast Block) {
+        self.with_resolved_label(label, id, |this| this.visit_block(block));
+    }
+
+    fn resolve_block(&mut self, block: &'ast Block) {
+        debug!("(resolving block) entering block");
+        // Move down in the graph, if there's an anonymous module rooted here.
+        let orig_module = self.parent_scope.module;
+        let anonymous_module = self.r.block_map.get(&block.id).cloned(); // clones a reference
+
+        let mut num_macro_definition_ribs = 0;
+        if let Some(anonymous_module) = anonymous_module {
+            debug!("(resolving block) found anonymous module, moving down");
+            self.ribs[ValueNS].push(Rib::new(RibKind::Module(anonymous_module)));
+            self.ribs[TypeNS].push(Rib::new(RibKind::Module(anonymous_module)));
+            self.parent_scope.module = anonymous_module;
+        } else {
+            self.ribs[ValueNS].push(Rib::new(RibKind::Normal));
+        }
+
+        let prev = self.diagnostic_metadata.current_block_could_be_bare_struct_literal.take();
+        if let (true, [Stmt { kind: StmtKind::Expr(expr), .. }]) =
+            (block.could_be_bare_literal, &block.stmts[..])
+            && let ExprKind::Type(..) = expr.kind
+        {
+            self.diagnostic_metadata.current_block_could_be_bare_struct_literal = Some(block.span);
+        }
+        // Descend into the block.
+        for stmt in &block.stmts {
+            if let StmtKind::Item(ref item) = stmt.kind
+                && let ItemKind::MacroDef(..) = item.kind
+            {
+                num_macro_definition_ribs += 1;
+                let res = self.r.local_def_id(item.id).to_def_id();
+                self.ribs[ValueNS].push(Rib::new(RibKind::MacroDefinition(res)));
+                self.label_ribs.push(Rib::new(RibKind::MacroDefinition(res)));
+            }
+
+            self.visit_stmt(stmt);
+        }
+        self.diagnostic_metadata.current_block_could_be_bare_struct_literal = prev;
+
+        // Move back up.
+        self.parent_scope.module = orig_module;
+        for _ in 0..num_macro_definition_ribs {
+            self.ribs[ValueNS].pop();
+            self.label_ribs.pop();
+        }
+        self.last_block_rib = self.ribs[ValueNS].pop();
+        if anonymous_module.is_some() {
+            self.ribs[TypeNS].pop();
+        }
+        debug!("(resolving block) leaving block");
+    }
+
+    fn resolve_anon_const(&mut self, constant: &'ast AnonConst, anon_const_kind: AnonConstKind) {
+        debug!(
+            "resolve_anon_const(constant: {:?}, anon_const_kind: {:?})",
+            constant, anon_const_kind
+        );
+
+        self.resolve_anon_const_manual(
+            constant.value.is_potential_trivial_const_arg(),
+            anon_const_kind,
+            |this| this.resolve_expr(&constant.value, None),
+        )
+    }
+
+    /// There are a few places that we need to resolve an anon const but we did not parse an
+    /// anon const so cannot provide an `&'ast AnonConst`. Right now this is just unbraced
+    /// const arguments that were parsed as type arguments, and `legact_const_generics` which
+    /// parse as normal function argument expressions. To avoid duplicating the code for resolving
+    /// an anon const we have this function which lets the caller manually call `resolve_expr` or
+    /// `smart_resolve_path`.
+    fn resolve_anon_const_manual(
+        &mut self,
+        is_trivial_const_arg: bool,
+        anon_const_kind: AnonConstKind,
+        resolve_expr: impl FnOnce(&mut Self),
+    ) {
+        let is_repeat_expr = match anon_const_kind {
+            AnonConstKind::ConstArg(is_repeat_expr) => is_repeat_expr,
+            _ => IsRepeatExpr::No,
+        };
+
+        let may_use_generics = match anon_const_kind {
+            AnonConstKind::EnumDiscriminant => {
+                ConstantHasGenerics::No(NoConstantGenericsReason::IsEnumDiscriminant)
+            }
+            AnonConstKind::InlineConst => ConstantHasGenerics::Yes,
+            AnonConstKind::ConstArg(_) => {
+                if self.r.tcx.features().generic_const_exprs || is_trivial_const_arg {
+                    ConstantHasGenerics::Yes
+                } else {
+                    ConstantHasGenerics::No(NoConstantGenericsReason::NonTrivialConstArg)
+                }
+            }
+        };
+
+        self.with_constant_rib(is_repeat_expr, may_use_generics, None, |this| {
+            this.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Infer), |this| {
+                resolve_expr(this);
+            });
+        });
+    }
+
+    fn resolve_expr_field(&mut self, f: &'ast ExprField, e: &'ast Expr) {
+        self.resolve_expr(&f.expr, Some(e));
+        self.visit_ident(f.ident);
+        walk_list!(self, visit_attribute, f.attrs.iter());
+    }
+
+    fn resolve_expr(&mut self, expr: &'ast Expr, parent: Option<&'ast Expr>) {
+        // First, record candidate traits for this expression if it could
+        // result in the invocation of a method call.
+
+        self.record_candidate_traits_for_expr_if_necessary(expr);
+
+        // Next, resolve the node.
+        match expr.kind {
+            ExprKind::Path(ref qself, ref path) => {
+                self.smart_resolve_path(expr.id, qself, path, PathSource::Expr(parent));
+                visit::walk_expr(self, expr);
+            }
+
+            ExprKind::Struct(ref se) => {
+                self.smart_resolve_path(expr.id, &se.qself, &se.path, PathSource::Struct);
+                // This is the same as `visit::walk_expr(self, expr);`, but we want to pass the
+                // parent in for accurate suggestions when encountering `Foo { bar }` that should
+                // have been `Foo { bar: self.bar }`.
+                if let Some(qself) = &se.qself {
+                    self.visit_ty(&qself.ty);
+                }
+                self.visit_path(&se.path, expr.id);
+                walk_list!(self, resolve_expr_field, &se.fields, expr);
+                match &se.rest {
+                    StructRest::Base(expr) => self.visit_expr(expr),
+                    StructRest::Rest(_span) => {}
+                    StructRest::None => {}
+                }
+            }
+
+            ExprKind::Break(Some(label), _) | ExprKind::Continue(Some(label)) => {
+                match self.resolve_label(label.ident) {
+                    Ok((node_id, _)) => {
+                        // Since this res is a label, it is never read.
+                        self.r.label_res_map.insert(expr.id, node_id);
+                        self.diagnostic_metadata.unused_labels.remove(&node_id);
+                    }
+                    Err(error) => {
+                        self.report_error(label.ident.span, error);
+                    }
+                }
+
+                // visit `break` argument if any
+                visit::walk_expr(self, expr);
+            }
+
+            ExprKind::Break(None, Some(ref e)) => {
+                // We use this instead of `visit::walk_expr` to keep the parent expr around for
+                // better diagnostics.
+                self.resolve_expr(e, Some(expr));
+            }
+
+            ExprKind::Let(ref pat, ref scrutinee, _, _) => {
+                self.visit_expr(scrutinee);
+                self.resolve_pattern_top(pat, PatternSource::Let);
+            }
+
+            ExprKind::If(ref cond, ref then, ref opt_else) => {
+                self.with_rib(ValueNS, RibKind::Normal, |this| {
+                    let old = this.diagnostic_metadata.in_if_condition.replace(cond);
+                    this.visit_expr(cond);
+                    this.diagnostic_metadata.in_if_condition = old;
+                    this.visit_block(then);
+                });
+                if let Some(expr) = opt_else {
+                    self.visit_expr(expr);
+                }
+            }
+
+            ExprKind::Loop(ref block, label, _) => {
+                self.resolve_labeled_block(label, expr.id, block)
+            }
+
+            ExprKind::While(ref cond, ref block, label) => {
+                self.with_resolved_label(label, expr.id, |this| {
+                    this.with_rib(ValueNS, RibKind::Normal, |this| {
+                        let old = this.diagnostic_metadata.in_if_condition.replace(cond);
+                        this.visit_expr(cond);
+                        this.diagnostic_metadata.in_if_condition = old;
+                        this.visit_block(block);
+                    })
+                });
+            }
+
+            ExprKind::ForLoop { ref pat, ref iter, ref body, label, kind: _ } => {
+                self.visit_expr(iter);
+                self.with_rib(ValueNS, RibKind::Normal, |this| {
+                    this.resolve_pattern_top(pat, PatternSource::For);
+                    this.resolve_labeled_block(label, expr.id, body);
+                });
+            }
+
+            ExprKind::Block(ref block, label) => self.resolve_labeled_block(label, block.id, block),
+
+            // Equivalent to `visit::walk_expr` + passing some context to children.
+            ExprKind::Field(ref subexpression, _) => {
+                self.resolve_expr(subexpression, Some(expr));
+            }
+            ExprKind::MethodCall(box MethodCall { ref seg, ref receiver, ref args, .. }) => {
+                self.resolve_expr(receiver, Some(expr));
+                for arg in args {
+                    self.resolve_expr(arg, None);
+                }
+                self.visit_path_segment(seg);
+            }
+
+            ExprKind::Call(ref callee, ref arguments) => {
+                self.resolve_expr(callee, Some(expr));
+                let const_args = self.r.legacy_const_generic_args(callee).unwrap_or_default();
+                for (idx, argument) in arguments.iter().enumerate() {
+                    // Constant arguments need to be treated as AnonConst since
+                    // that is how they will be later lowered to HIR.
+                    if const_args.contains(&idx) {
+                        self.resolve_anon_const_manual(
+                            argument.is_potential_trivial_const_arg(),
+                            AnonConstKind::ConstArg(IsRepeatExpr::No),
+                            |this| this.resolve_expr(argument, None),
+                        );
+                    } else {
+                        self.resolve_expr(argument, None);
+                    }
+                }
+            }
+            ExprKind::Type(ref _type_expr, ref _ty) => {
+                visit::walk_expr(self, expr);
+            }
+            // `async |x| ...` gets desugared to `|x| async {...}`, so we need to
+            // resolve the arguments within the proper scopes so that usages of them inside the
+            // closure are detected as upvars rather than normal closure arg usages.
+            //
+            // Similarly, `gen |x| ...` gets desugared to `|x| gen {...}`, so we handle that too.
+            ExprKind::Closure(box ast::Closure {
+                coroutine_kind: Some(_),
+                ref fn_decl,
+                ref body,
+                ..
+            }) => {
+                self.with_rib(ValueNS, RibKind::Normal, |this| {
+                    this.with_label_rib(RibKind::FnOrCoroutine, |this| {
+                        // Resolve arguments:
+                        this.resolve_params(&fn_decl.inputs);
+                        // No need to resolve return type --
+                        // the outer closure return type is `FnRetTy::Default`.
+
+                        // Now resolve the inner closure
+                        {
+                            // No need to resolve arguments: the inner closure has none.
+                            // Resolve the return type:
+                            visit::walk_fn_ret_ty(this, &fn_decl.output);
+                            // Resolve the body
+                            this.visit_expr(body);
+                        }
+                    })
+                });
+            }
+            // For closures, RibKind::FnOrCoroutine is added in visit_fn
+            ExprKind::Closure(box ast::Closure {
+                binder: ClosureBinder::For { ref generic_params, span },
+                ..
+            }) => {
+                self.with_generic_param_rib(
+                    generic_params,
+                    RibKind::Normal,
+                    LifetimeRibKind::Generics {
+                        binder: expr.id,
+                        kind: LifetimeBinderKind::Closure,
+                        span,
+                    },
+                    |this| visit::walk_expr(this, expr),
+                );
+            }
+            ExprKind::Closure(..) => visit::walk_expr(self, expr),
+            ExprKind::Gen(..) => {
+                self.with_label_rib(RibKind::FnOrCoroutine, |this| visit::walk_expr(this, expr));
+            }
+            ExprKind::Repeat(ref elem, ref ct) => {
+                self.visit_expr(elem);
+                self.resolve_anon_const(ct, AnonConstKind::ConstArg(IsRepeatExpr::Yes));
+            }
+            ExprKind::ConstBlock(ref ct) => {
+                self.resolve_anon_const(ct, AnonConstKind::InlineConst);
+            }
+            ExprKind::Index(ref elem, ref idx, _) => {
+                self.resolve_expr(elem, Some(expr));
+                self.visit_expr(idx);
+            }
+            ExprKind::Assign(ref lhs, ref rhs, _) => {
+                if !self.diagnostic_metadata.is_assign_rhs {
+                    self.diagnostic_metadata.in_assignment = Some(expr);
+                }
+                self.visit_expr(lhs);
+                self.diagnostic_metadata.is_assign_rhs = true;
+                self.diagnostic_metadata.in_assignment = None;
+                self.visit_expr(rhs);
+                self.diagnostic_metadata.is_assign_rhs = false;
+            }
+            ExprKind::Range(Some(ref start), Some(ref end), RangeLimits::HalfOpen) => {
+                self.diagnostic_metadata.in_range = Some((start, end));
+                self.resolve_expr(start, Some(expr));
+                self.resolve_expr(end, Some(expr));
+                self.diagnostic_metadata.in_range = None;
+            }
+            _ => {
+                visit::walk_expr(self, expr);
+            }
+        }
+    }
+
+    fn record_candidate_traits_for_expr_if_necessary(&mut self, expr: &'ast Expr) {
+        match expr.kind {
+            ExprKind::Field(_, ident) => {
+                // #6890: Even though you can't treat a method like a field,
+                // we need to add any trait methods we find that match the
+                // field name so that we can do some nice error reporting
+                // later on in typeck.
+                let traits = self.traits_in_scope(ident, ValueNS);
+                self.r.trait_map.insert(expr.id, traits);
+            }
+            ExprKind::MethodCall(ref call) => {
+                debug!("(recording candidate traits for expr) recording traits for {}", expr.id);
+                let traits = self.traits_in_scope(call.seg.ident, ValueNS);
+                self.r.trait_map.insert(expr.id, traits);
+            }
+            _ => {
+                // Nothing to do.
+            }
+        }
+    }
+
+    fn traits_in_scope(&mut self, ident: Ident, ns: Namespace) -> Vec<TraitCandidate> {
+        self.r.traits_in_scope(
+            self.current_trait_ref.as_ref().map(|(module, _)| *module),
+            &self.parent_scope,
+            ident.span.ctxt(),
+            Some((ident.name, ns)),
+        )
+    }
+
+    /// Construct the list of in-scope lifetime parameters for impl trait lowering.
+    /// We include all lifetime parameters, either named or "Fresh".
+    /// The order of those parameters does not matter, as long as it is
+    /// deterministic.
+    fn record_lifetime_params_for_impl_trait(&mut self, impl_trait_node_id: NodeId) {
+        let mut extra_lifetime_params = vec![];
+
+        for rib in self.lifetime_ribs.iter().rev() {
+            extra_lifetime_params
+                .extend(rib.bindings.iter().map(|(&ident, &(node_id, res))| (ident, node_id, res)));
+            match rib.kind {
+                LifetimeRibKind::Item => break,
+                LifetimeRibKind::AnonymousCreateParameter { binder, .. } => {
+                    if let Some(earlier_fresh) = self.r.extra_lifetime_params_map.get(&binder) {
+                        extra_lifetime_params.extend(earlier_fresh);
+                    }
+                }
+                _ => {}
+            }
+        }
+
+        self.r.extra_lifetime_params_map.insert(impl_trait_node_id, extra_lifetime_params);
+    }
+
+    fn resolve_and_cache_rustdoc_path(&mut self, path_str: &str, ns: Namespace) -> Option<Res> {
+        // FIXME: This caching may be incorrect in case of multiple `macro_rules`
+        // items with the same name in the same module.
+        // Also hygiene is not considered.
+        let mut doc_link_resolutions = std::mem::take(&mut self.r.doc_link_resolutions);
+        let res = *doc_link_resolutions
+            .entry(self.parent_scope.module.nearest_parent_mod().expect_local())
+            .or_default()
+            .entry((Symbol::intern(path_str), ns))
+            .or_insert_with_key(|(path, ns)| {
+                let res = self.r.resolve_rustdoc_path(path.as_str(), *ns, self.parent_scope);
+                if let Some(res) = res
+                    && let Some(def_id) = res.opt_def_id()
+                    && !def_id.is_local()
+                    && self.r.tcx.crate_types().contains(&CrateType::ProcMacro)
+                    && matches!(
+                        self.r.tcx.sess.opts.resolve_doc_links,
+                        ResolveDocLinks::ExportedMetadata
+                    )
+                {
+                    // Encoding foreign def ids in proc macro crate metadata will ICE.
+                    return None;
+                }
+                res
+            });
+        self.r.doc_link_resolutions = doc_link_resolutions;
+        res
+    }
+
+    fn resolve_doc_links(&mut self, attrs: &[Attribute], maybe_exported: MaybeExported<'_>) {
+        match self.r.tcx.sess.opts.resolve_doc_links {
+            ResolveDocLinks::None => return,
+            ResolveDocLinks::ExportedMetadata
+                if !self.r.tcx.crate_types().iter().copied().any(CrateType::has_metadata)
+                    || !maybe_exported.eval(self.r) =>
+            {
+                return;
+            }
+            ResolveDocLinks::Exported
+                if !maybe_exported.eval(self.r)
+                    && !rustdoc::has_primitive_or_keyword_docs(attrs) =>
+            {
+                return;
+            }
+            ResolveDocLinks::ExportedMetadata
+            | ResolveDocLinks::Exported
+            | ResolveDocLinks::All => {}
+        }
+
+        if !attrs.iter().any(|attr| attr.may_have_doc_links()) {
+            return;
+        }
+
+        let mut need_traits_in_scope = false;
+        for path_str in rustdoc::attrs_to_preprocessed_links(attrs) {
+            // Resolve all namespaces due to no disambiguator or for diagnostics.
+            let mut any_resolved = false;
+            let mut need_assoc = false;
+            for ns in [TypeNS, ValueNS, MacroNS] {
+                if let Some(res) = self.resolve_and_cache_rustdoc_path(&path_str, ns) {
+                    // Rustdoc ignores tool attribute resolutions and attempts
+                    // to resolve their prefixes for diagnostics.
+                    any_resolved = !matches!(res, Res::NonMacroAttr(NonMacroAttrKind::Tool));
+                } else if ns != MacroNS {
+                    need_assoc = true;
+                }
+            }
+
+            // Resolve all prefixes for type-relative resolution or for diagnostics.
+            if need_assoc || !any_resolved {
+                let mut path = &path_str[..];
+                while let Some(idx) = path.rfind("::") {
+                    path = &path[..idx];
+                    need_traits_in_scope = true;
+                    for ns in [TypeNS, ValueNS, MacroNS] {
+                        self.resolve_and_cache_rustdoc_path(path, ns);
+                    }
+                }
+            }
+        }
+
+        if need_traits_in_scope {
+            // FIXME: hygiene is not considered.
+            let mut doc_link_traits_in_scope = std::mem::take(&mut self.r.doc_link_traits_in_scope);
+            doc_link_traits_in_scope
+                .entry(self.parent_scope.module.nearest_parent_mod().expect_local())
+                .or_insert_with(|| {
+                    self.r
+                        .traits_in_scope(None, &self.parent_scope, SyntaxContext::root(), None)
+                        .into_iter()
+                        .filter_map(|tr| {
+                            if !tr.def_id.is_local()
+                                && self.r.tcx.crate_types().contains(&CrateType::ProcMacro)
+                                && matches!(
+                                    self.r.tcx.sess.opts.resolve_doc_links,
+                                    ResolveDocLinks::ExportedMetadata
+                                )
+                            {
+                                // Encoding foreign def ids in proc macro crate metadata will ICE.
+                                return None;
+                            }
+                            Some(tr.def_id)
+                        })
+                        .collect()
+                });
+            self.r.doc_link_traits_in_scope = doc_link_traits_in_scope;
+        }
+    }
+}
+
+/// Walks the whole crate in DFS order, visiting each item, counting the declared number of
+/// lifetime generic parameters and function parameters.
+struct ItemInfoCollector<'a, 'b, 'tcx> {
+    r: &'b mut Resolver<'a, 'tcx>,
+}
+
+impl ItemInfoCollector<'_, '_, '_> {
+    fn collect_fn_info(&mut self, sig: &FnSig, id: NodeId) {
+        let def_id = self.r.local_def_id(id);
+        self.r.fn_parameter_counts.insert(def_id, sig.decl.inputs.len());
+
+        if sig.decl.has_self() {
+            self.r.has_self.insert(def_id);
+        }
+    }
+}
+
+impl<'ast> Visitor<'ast> for ItemInfoCollector<'_, '_, '_> {
+    fn visit_item(&mut self, item: &'ast Item) {
+        match &item.kind {
+            ItemKind::TyAlias(box TyAlias { ref generics, .. })
+            | ItemKind::Const(box ConstItem { ref generics, .. })
+            | ItemKind::Fn(box Fn { ref generics, .. })
+            | ItemKind::Enum(_, ref generics)
+            | ItemKind::Struct(_, ref generics)
+            | ItemKind::Union(_, ref generics)
+            | ItemKind::Impl(box Impl { ref generics, .. })
+            | ItemKind::Trait(box Trait { ref generics, .. })
+            | ItemKind::TraitAlias(ref generics, _) => {
+                if let ItemKind::Fn(box Fn { ref sig, .. }) = &item.kind {
+                    self.collect_fn_info(sig, item.id);
+                }
+
+                let def_id = self.r.local_def_id(item.id);
+                let count = generics
+                    .params
+                    .iter()
+                    .filter(|param| matches!(param.kind, ast::GenericParamKind::Lifetime { .. }))
+                    .count();
+                self.r.item_generics_num_lifetimes.insert(def_id, count);
+            }
+
+            ItemKind::Mod(..)
+            | ItemKind::ForeignMod(..)
+            | ItemKind::Static(..)
+            | ItemKind::Use(..)
+            | ItemKind::ExternCrate(..)
+            | ItemKind::MacroDef(..)
+            | ItemKind::GlobalAsm(..)
+            | ItemKind::MacCall(..) => {}
+            ItemKind::Delegation(..) => {
+                // Delegated functions have lifetimes, their count is not necessarily zero.
+                // But skipping the delegation items here doesn't mean that the count will be considered zero,
+                // it means there will be a panic when retrieving the count,
+                // but for delegation items we are never actually retrieving that count in practice.
+            }
+        }
+        visit::walk_item(self, item)
+    }
+
+    fn visit_assoc_item(&mut self, item: &'ast AssocItem, ctxt: AssocCtxt) {
+        if let AssocItemKind::Fn(box Fn { ref sig, .. }) = &item.kind {
+            self.collect_fn_info(sig, item.id);
+        }
+        visit::walk_assoc_item(self, item, ctxt);
+    }
+}
+
+impl<'a, 'tcx> Resolver<'a, 'tcx> {
+    pub(crate) fn late_resolve_crate(&mut self, krate: &Crate) {
+        visit::walk_crate(&mut ItemInfoCollector { r: self }, krate);
+        let mut late_resolution_visitor = LateResolutionVisitor::new(self);
+        late_resolution_visitor.resolve_doc_links(&krate.attrs, MaybeExported::Ok(CRATE_NODE_ID));
+        visit::walk_crate(&mut late_resolution_visitor, krate);
+        for (id, span) in late_resolution_visitor.diagnostic_metadata.unused_labels.iter() {
+            self.lint_buffer.buffer_lint(lint::builtin::UNUSED_LABELS, *id, *span, "unused label");
+        }
+    }
+}
diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs
new file mode 100644
index 00000000000..58ff4d8c793
--- /dev/null
+++ b/compiler/rustc_resolve/src/late/diagnostics.rs
@@ -0,0 +1,3338 @@
+use crate::diagnostics::{ImportSuggestion, LabelSuggestion, TypoSuggestion};
+use crate::late::{AliasPossibility, LateResolutionVisitor, RibKind};
+use crate::late::{LifetimeBinderKind, LifetimeRes, LifetimeRibKind, LifetimeUseSet};
+use crate::ty::fast_reject::SimplifiedType;
+use crate::{errors, path_names_to_string};
+use crate::{Module, ModuleKind, ModuleOrUniformRoot};
+use crate::{PathResult, PathSource, Segment};
+use rustc_hir::def::Namespace::{self, *};
+
+use rustc_ast::ptr::P;
+use rustc_ast::visit::{walk_ty, FnCtxt, FnKind, LifetimeCtxt, Visitor};
+use rustc_ast::{
+    self as ast, AssocItemKind, Expr, ExprKind, GenericParam, GenericParamKind, Item, ItemKind,
+    MethodCall, NodeId, Path, Ty, TyKind, DUMMY_NODE_ID,
+};
+use rustc_ast_pretty::pprust::where_bound_predicate_to_string;
+use rustc_data_structures::fx::FxHashSet;
+use rustc_errors::{
+    pluralize, struct_span_code_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
+    MultiSpan, SuggestionStyle,
+};
+use rustc_hir as hir;
+use rustc_hir::def::{self, CtorKind, CtorOf, DefKind};
+use rustc_hir::def_id::{DefId, CRATE_DEF_ID};
+use rustc_hir::PrimTy;
+use rustc_session::lint;
+use rustc_session::Session;
+use rustc_span::edit_distance::find_best_match_for_name;
+use rustc_span::edition::Edition;
+use rustc_span::hygiene::MacroKind;
+use rustc_span::symbol::{kw, sym, Ident, Symbol};
+use rustc_span::Span;
+
+use rustc_middle::ty;
+
+use std::borrow::Cow;
+use std::iter;
+use std::ops::Deref;
+
+use thin_vec::ThinVec;
+
+use super::NoConstantGenericsReason;
+
+type Res = def::Res<ast::NodeId>;
+
+/// A field or associated item from self type suggested in case of resolution failure.
+enum AssocSuggestion {
+    Field(Span),
+    MethodWithSelf { called: bool },
+    AssocFn { called: bool },
+    AssocType,
+    AssocConst,
+}
+
+impl AssocSuggestion {
+    fn action(&self) -> &'static str {
+        match self {
+            AssocSuggestion::Field(_) => "use the available field",
+            AssocSuggestion::MethodWithSelf { called: true } => {
+                "call the method with the fully-qualified path"
+            }
+            AssocSuggestion::MethodWithSelf { called: false } => {
+                "refer to the method with the fully-qualified path"
+            }
+            AssocSuggestion::AssocFn { called: true } => "call the associated function",
+            AssocSuggestion::AssocFn { called: false } => "refer to the associated function",
+            AssocSuggestion::AssocConst => "use the associated `const`",
+            AssocSuggestion::AssocType => "use the associated type",
+        }
+    }
+}
+
+fn is_self_type(path: &[Segment], namespace: Namespace) -> bool {
+    namespace == TypeNS && path.len() == 1 && path[0].ident.name == kw::SelfUpper
+}
+
+fn is_self_value(path: &[Segment], namespace: Namespace) -> bool {
+    namespace == ValueNS && path.len() == 1 && path[0].ident.name == kw::SelfLower
+}
+
+/// Gets the stringified path for an enum from an `ImportSuggestion` for an enum variant.
+fn import_candidate_to_enum_paths(suggestion: &ImportSuggestion) -> (String, String) {
+    let variant_path = &suggestion.path;
+    let variant_path_string = path_names_to_string(variant_path);
+
+    let path_len = suggestion.path.segments.len();
+    let enum_path = ast::Path {
+        span: suggestion.path.span,
+        segments: suggestion.path.segments[0..path_len - 1].iter().cloned().collect(),
+        tokens: None,
+    };
+    let enum_path_string = path_names_to_string(&enum_path);
+
+    (variant_path_string, enum_path_string)
+}
+
+/// Description of an elided lifetime.
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
+pub(super) struct MissingLifetime {
+    /// Used to overwrite the resolution with the suggestion, to avoid cascading errors.
+    pub id: NodeId,
+    /// Where to suggest adding the lifetime.
+    pub span: Span,
+    /// How the lifetime was introduced, to have the correct space and comma.
+    pub kind: MissingLifetimeKind,
+    /// Number of elided lifetimes, used for elision in path.
+    pub count: usize,
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
+pub(super) enum MissingLifetimeKind {
+    /// An explicit `'_`.
+    Underscore,
+    /// An elided lifetime `&' ty`.
+    Ampersand,
+    /// An elided lifetime in brackets with written brackets.
+    Comma,
+    /// An elided lifetime with elided brackets.
+    Brackets,
+}
+
+/// Description of the lifetimes appearing in a function parameter.
+/// This is used to provide a literal explanation to the elision failure.
+#[derive(Clone, Debug)]
+pub(super) struct ElisionFnParameter {
+    /// The index of the argument in the original definition.
+    pub index: usize,
+    /// The name of the argument if it's a simple ident.
+    pub ident: Option<Ident>,
+    /// The number of lifetimes in the parameter.
+    pub lifetime_count: usize,
+    /// The span of the parameter.
+    pub span: Span,
+}
+
+/// Description of lifetimes that appear as candidates for elision.
+/// This is used to suggest introducing an explicit lifetime.
+#[derive(Debug)]
+pub(super) enum LifetimeElisionCandidate {
+    /// This is not a real lifetime.
+    Ignore,
+    /// There is a named lifetime, we won't suggest anything.
+    Named,
+    Missing(MissingLifetime),
+}
+
+/// Only used for diagnostics.
+#[derive(Debug)]
+struct BaseError {
+    msg: String,
+    fallback_label: String,
+    span: Span,
+    span_label: Option<(Span, &'static str)>,
+    could_be_expr: bool,
+    suggestion: Option<(Span, &'static str, String)>,
+    module: Option<DefId>,
+}
+
+#[derive(Debug)]
+enum TypoCandidate {
+    Typo(TypoSuggestion),
+    Shadowed(Res, Option<Span>),
+    None,
+}
+
+impl TypoCandidate {
+    fn to_opt_suggestion(self) -> Option<TypoSuggestion> {
+        match self {
+            TypoCandidate::Typo(sugg) => Some(sugg),
+            TypoCandidate::Shadowed(_, _) | TypoCandidate::None => None,
+        }
+    }
+}
+
+impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
+    fn make_base_error(
+        &mut self,
+        path: &[Segment],
+        span: Span,
+        source: PathSource<'_>,
+        res: Option<Res>,
+    ) -> BaseError {
+        // Make the base error.
+        let mut expected = source.descr_expected();
+        let path_str = Segment::names_to_string(path);
+        let item_str = path.last().unwrap().ident;
+        if let Some(res) = res {
+            BaseError {
+                msg: format!("expected {}, found {} `{}`", expected, res.descr(), path_str),
+                fallback_label: format!("not a {expected}"),
+                span,
+                span_label: match res {
+                    Res::Def(DefKind::TyParam, def_id) => {
+                        Some((self.r.def_span(def_id), "found this type parameter"))
+                    }
+                    _ => None,
+                },
+                could_be_expr: match res {
+                    Res::Def(DefKind::Fn, _) => {
+                        // Verify whether this is a fn call or an Fn used as a type.
+                        self.r
+                            .tcx
+                            .sess
+                            .source_map()
+                            .span_to_snippet(span)
+                            .is_ok_and(|snippet| snippet.ends_with(')'))
+                    }
+                    Res::Def(
+                        DefKind::Ctor(..) | DefKind::AssocFn | DefKind::Const | DefKind::AssocConst,
+                        _,
+                    )
+                    | Res::SelfCtor(_)
+                    | Res::PrimTy(_)
+                    | Res::Local(_) => true,
+                    _ => false,
+                },
+                suggestion: None,
+                module: None,
+            }
+        } else {
+            let mut span_label = None;
+            let item_ident = path.last().unwrap().ident;
+            let item_span = item_ident.span;
+            let (mod_prefix, mod_str, module, suggestion) = if path.len() == 1 {
+                debug!(?self.diagnostic_metadata.current_impl_items);
+                debug!(?self.diagnostic_metadata.current_function);
+                let suggestion = if self.current_trait_ref.is_none()
+                    && let Some((fn_kind, _)) = self.diagnostic_metadata.current_function
+                    && let Some(FnCtxt::Assoc(_)) = fn_kind.ctxt()
+                    && let FnKind::Fn(_, _, sig, ..) = fn_kind
+                    && let Some(items) = self.diagnostic_metadata.current_impl_items
+                    && let Some(item) = items.iter().find(|i| {
+                        i.ident.name == item_str.name
+                            // Don't suggest if the item is in Fn signature arguments (#112590).
+                            && !sig.span.contains(item_span)
+                    }) {
+                    let sp = item_span.shrink_to_lo();
+
+                    // Account for `Foo { field }` when suggesting `self.field` so we result on
+                    // `Foo { field: self.field }`.
+                    let field = match source {
+                        PathSource::Expr(Some(Expr { kind: ExprKind::Struct(expr), .. })) => {
+                            expr.fields.iter().find(|f| f.ident == item_ident)
+                        }
+                        _ => None,
+                    };
+                    let pre = if let Some(field) = field
+                        && field.is_shorthand
+                    {
+                        format!("{item_ident}: ")
+                    } else {
+                        String::new()
+                    };
+                    // Ensure we provide a structured suggestion for an assoc fn only for
+                    // expressions that are actually a fn call.
+                    let is_call = match field {
+                        Some(ast::ExprField { expr, .. }) => {
+                            matches!(expr.kind, ExprKind::Call(..))
+                        }
+                        _ => matches!(
+                            source,
+                            PathSource::Expr(Some(Expr { kind: ExprKind::Call(..), .. })),
+                        ),
+                    };
+
+                    match &item.kind {
+                        AssocItemKind::Fn(fn_)
+                            if (!sig.decl.has_self() || !is_call) && fn_.sig.decl.has_self() =>
+                        {
+                            // Ensure that we only suggest `self.` if `self` is available,
+                            // you can't call `fn foo(&self)` from `fn bar()` (#115992).
+                            // We also want to mention that the method exists.
+                            span_label = Some((
+                                item.ident.span,
+                                "a method by that name is available on `Self` here",
+                            ));
+                            None
+                        }
+                        AssocItemKind::Fn(fn_) if !fn_.sig.decl.has_self() && !is_call => {
+                            span_label = Some((
+                                item.ident.span,
+                                "an associated function by that name is available on `Self` here",
+                            ));
+                            None
+                        }
+                        AssocItemKind::Fn(fn_) if fn_.sig.decl.has_self() => {
+                            Some((sp, "consider using the method on `Self`", format!("{pre}self.")))
+                        }
+                        AssocItemKind::Fn(_) => Some((
+                            sp,
+                            "consider using the associated function on `Self`",
+                            format!("{pre}Self::"),
+                        )),
+                        AssocItemKind::Const(..) => Some((
+                            sp,
+                            "consider using the associated constant on `Self`",
+                            format!("{pre}Self::"),
+                        )),
+                        _ => None,
+                    }
+                } else {
+                    None
+                };
+                (String::new(), "this scope".to_string(), None, suggestion)
+            } else if path.len() == 2 && path[0].ident.name == kw::PathRoot {
+                if self.r.tcx.sess.edition() > Edition::Edition2015 {
+                    // In edition 2018 onwards, the `::foo` syntax may only pull from the extern prelude
+                    // which overrides all other expectations of item type
+                    expected = "crate";
+                    (String::new(), "the list of imported crates".to_string(), None, None)
+                } else {
+                    (
+                        String::new(),
+                        "the crate root".to_string(),
+                        Some(CRATE_DEF_ID.to_def_id()),
+                        None,
+                    )
+                }
+            } else if path.len() == 2 && path[0].ident.name == kw::Crate {
+                (String::new(), "the crate root".to_string(), Some(CRATE_DEF_ID.to_def_id()), None)
+            } else {
+                let mod_path = &path[..path.len() - 1];
+                let mod_res = self.resolve_path(mod_path, Some(TypeNS), None);
+                let mod_prefix = match mod_res {
+                    PathResult::Module(ModuleOrUniformRoot::Module(module)) => module.res(),
+                    _ => None,
+                };
+
+                let module_did = mod_prefix.as_ref().and_then(Res::mod_def_id);
+
+                let mod_prefix =
+                    mod_prefix.map_or_else(String::new, |res| (format!("{} ", res.descr())));
+
+                (mod_prefix, format!("`{}`", Segment::names_to_string(mod_path)), module_did, None)
+            };
+
+            let (fallback_label, suggestion) = if path_str == "async"
+                && expected.starts_with("struct")
+            {
+                ("`async` blocks are only allowed in Rust 2018 or later".to_string(), suggestion)
+            } else {
+                // check if we are in situation of typo like `True` instead of `true`.
+                let override_suggestion =
+                    if ["true", "false"].contains(&item_str.to_string().to_lowercase().as_str()) {
+                        let item_typo = item_str.to_string().to_lowercase();
+                        Some((item_span, "you may want to use a bool value instead", item_typo))
+                    // FIXME(vincenzopalazzo): make the check smarter,
+                    // and maybe expand with levenshtein distance checks
+                    } else if item_str.as_str() == "printf" {
+                        Some((
+                            item_span,
+                            "you may have meant to use the `print` macro",
+                            "print!".to_owned(),
+                        ))
+                    } else {
+                        suggestion
+                    };
+                (format!("not found in {mod_str}"), override_suggestion)
+            };
+
+            BaseError {
+                msg: format!("cannot find {expected} `{item_str}` in {mod_prefix}{mod_str}"),
+                fallback_label,
+                span: item_span,
+                span_label,
+                could_be_expr: false,
+                suggestion,
+                module,
+            }
+        }
+    }
+
+    /// Try to suggest for a module path that cannot be resolved.
+    /// Such as `fmt::Debug` where `fmt` is not resolved without importing,
+    /// here we search with `lookup_import_candidates` for a module named `fmt`
+    /// with `TypeNS` as namespace.
+    ///
+    /// We need a separate function here because we won't suggest for a path with single segment
+    /// and we won't change `SourcePath` api `is_expected` to match `Type` with `DefKind::Mod`
+    pub(crate) fn smart_resolve_partial_mod_path_errors(
+        &mut self,
+        prefix_path: &[Segment],
+        following_seg: Option<&Segment>,
+    ) -> Vec<ImportSuggestion> {
+        if let Some(segment) = prefix_path.last()
+            && let Some(following_seg) = following_seg
+        {
+            let candidates = self.r.lookup_import_candidates(
+                segment.ident,
+                Namespace::TypeNS,
+                &self.parent_scope,
+                &|res: Res| matches!(res, Res::Def(DefKind::Mod, _)),
+            );
+            // double check next seg is valid
+            candidates
+                .into_iter()
+                .filter(|candidate| {
+                    if let Some(def_id) = candidate.did
+                        && let Some(module) = self.r.get_module(def_id)
+                    {
+                        Some(def_id) != self.parent_scope.module.opt_def_id()
+                            && self
+                                .r
+                                .resolutions(module)
+                                .borrow()
+                                .iter()
+                                .any(|(key, _r)| key.ident.name == following_seg.ident.name)
+                    } else {
+                        false
+                    }
+                })
+                .collect::<Vec<_>>()
+        } else {
+            Vec::new()
+        }
+    }
+
+    /// Handles error reporting for `smart_resolve_path_fragment` function.
+    /// Creates base error and amends it with one short label and possibly some longer helps/notes.
+    pub(crate) fn smart_resolve_report_errors(
+        &mut self,
+        path: &[Segment],
+        following_seg: Option<&Segment>,
+        span: Span,
+        source: PathSource<'_>,
+        res: Option<Res>,
+    ) -> (DiagnosticBuilder<'tcx>, Vec<ImportSuggestion>) {
+        debug!(?res, ?source);
+        let base_error = self.make_base_error(path, span, source, res);
+
+        let code = source.error_code(res.is_some());
+        let mut err = self.r.dcx().struct_span_err(base_error.span, base_error.msg.clone());
+        err.code(code);
+
+        self.suggest_at_operator_in_slice_pat_with_range(&mut err, path);
+        self.suggest_swapping_misplaced_self_ty_and_trait(&mut err, source, res, base_error.span);
+
+        if let Some((span, label)) = base_error.span_label {
+            err.span_label(span, label);
+        }
+
+        if let Some(ref sugg) = base_error.suggestion {
+            err.span_suggestion_verbose(sugg.0, sugg.1, &sugg.2, Applicability::MaybeIncorrect);
+        }
+
+        self.suggest_bare_struct_literal(&mut err);
+
+        if self.suggest_pattern_match_with_let(&mut err, source, span) {
+            // Fallback label.
+            err.span_label(base_error.span, base_error.fallback_label);
+            return (err, Vec::new());
+        }
+
+        self.suggest_self_or_self_ref(&mut err, path, span);
+        self.detect_assoct_type_constraint_meant_as_path(&mut err, &base_error);
+        if self.suggest_self_ty(&mut err, source, path, span)
+            || self.suggest_self_value(&mut err, source, path, span)
+        {
+            return (err, Vec::new());
+        }
+
+        let (found, candidates) = self.try_lookup_name_relaxed(
+            &mut err,
+            source,
+            path,
+            following_seg,
+            span,
+            res,
+            &base_error,
+        );
+        if found {
+            return (err, candidates);
+        }
+
+        let mut fallback = self.suggest_trait_and_bounds(&mut err, source, res, span, &base_error);
+
+        // if we have suggested using pattern matching, then don't add needless suggestions
+        // for typos.
+        fallback |= self.suggest_typo(&mut err, source, path, following_seg, span, &base_error);
+
+        if fallback {
+            // Fallback label.
+            err.span_label(base_error.span, base_error.fallback_label);
+        }
+        self.err_code_special_cases(&mut err, source, path, span);
+
+        if let Some(module) = base_error.module {
+            self.r.find_cfg_stripped(&mut err, &path.last().unwrap().ident.name, module);
+        }
+
+        (err, candidates)
+    }
+
+    fn detect_assoct_type_constraint_meant_as_path(
+        &self,
+        err: &mut Diagnostic,
+        base_error: &BaseError,
+    ) {
+        let Some(ty) = self.diagnostic_metadata.current_type_path else {
+            return;
+        };
+        let TyKind::Path(_, path) = &ty.kind else {
+            return;
+        };
+        for segment in &path.segments {
+            let Some(params) = &segment.args else {
+                continue;
+            };
+            let ast::GenericArgs::AngleBracketed(ref params) = params.deref() else {
+                continue;
+            };
+            for param in &params.args {
+                let ast::AngleBracketedArg::Constraint(constraint) = param else {
+                    continue;
+                };
+                let ast::AssocConstraintKind::Bound { bounds } = &constraint.kind else {
+                    continue;
+                };
+                for bound in bounds {
+                    let ast::GenericBound::Trait(trait_ref, ast::TraitBoundModifiers::NONE) = bound
+                    else {
+                        continue;
+                    };
+                    if base_error.span == trait_ref.span {
+                        err.span_suggestion_verbose(
+                            constraint.ident.span.between(trait_ref.span),
+                            "you might have meant to write a path instead of an associated type bound",
+                            "::",
+                            Applicability::MachineApplicable,
+                        );
+                    }
+                }
+            }
+        }
+    }
+
+    fn suggest_self_or_self_ref(&mut self, err: &mut Diagnostic, path: &[Segment], span: Span) {
+        if !self.self_type_is_available() {
+            return;
+        }
+        let Some(path_last_segment) = path.last() else { return };
+        let item_str = path_last_segment.ident;
+        // Emit help message for fake-self from other languages (e.g., `this` in JavaScript).
+        if ["this", "my"].contains(&item_str.as_str()) {
+            err.span_suggestion_short(
+                span,
+                "you might have meant to use `self` here instead",
+                "self",
+                Applicability::MaybeIncorrect,
+            );
+            if !self.self_value_is_available(path[0].ident.span) {
+                if let Some((FnKind::Fn(_, _, sig, ..), fn_span)) =
+                    &self.diagnostic_metadata.current_function
+                {
+                    let (span, sugg) = if let Some(param) = sig.decl.inputs.get(0) {
+                        (param.span.shrink_to_lo(), "&self, ")
+                    } else {
+                        (
+                            self.r
+                                .tcx
+                                .sess
+                                .source_map()
+                                .span_through_char(*fn_span, '(')
+                                .shrink_to_hi(),
+                            "&self",
+                        )
+                    };
+                    err.span_suggestion_verbose(
+                        span,
+                        "if you meant to use `self`, you are also missing a `self` receiver \
+                         argument",
+                        sugg,
+                        Applicability::MaybeIncorrect,
+                    );
+                }
+            }
+        }
+    }
+
+    fn try_lookup_name_relaxed(
+        &mut self,
+        err: &mut Diagnostic,
+        source: PathSource<'_>,
+        path: &[Segment],
+        following_seg: Option<&Segment>,
+        span: Span,
+        res: Option<Res>,
+        base_error: &BaseError,
+    ) -> (bool, Vec<ImportSuggestion>) {
+        // Try to lookup name in more relaxed fashion for better error reporting.
+        let ident = path.last().unwrap().ident;
+        let is_expected = &|res| source.is_expected(res);
+        let ns = source.namespace();
+        let is_enum_variant = &|res| matches!(res, Res::Def(DefKind::Variant, _));
+        let path_str = Segment::names_to_string(path);
+        let ident_span = path.last().map_or(span, |ident| ident.ident.span);
+        let mut candidates = self
+            .r
+            .lookup_import_candidates(ident, ns, &self.parent_scope, is_expected)
+            .into_iter()
+            .filter(|ImportSuggestion { did, .. }| {
+                match (did, res.and_then(|res| res.opt_def_id())) {
+                    (Some(suggestion_did), Some(actual_did)) => *suggestion_did != actual_did,
+                    _ => true,
+                }
+            })
+            .collect::<Vec<_>>();
+        // Try to filter out intrinsics candidates, as long as we have
+        // some other candidates to suggest.
+        let intrinsic_candidates: Vec<_> = candidates
+            .extract_if(|sugg| {
+                let path = path_names_to_string(&sugg.path);
+                path.starts_with("core::intrinsics::") || path.starts_with("std::intrinsics::")
+            })
+            .collect();
+        if candidates.is_empty() {
+            // Put them back if we have no more candidates to suggest...
+            candidates = intrinsic_candidates;
+        }
+        let crate_def_id = CRATE_DEF_ID.to_def_id();
+        if candidates.is_empty() && is_expected(Res::Def(DefKind::Enum, crate_def_id)) {
+            let mut enum_candidates: Vec<_> = self
+                .r
+                .lookup_import_candidates(ident, ns, &self.parent_scope, is_enum_variant)
+                .into_iter()
+                .map(|suggestion| import_candidate_to_enum_paths(&suggestion))
+                .filter(|(_, enum_ty_path)| !enum_ty_path.starts_with("std::prelude::"))
+                .collect();
+            if !enum_candidates.is_empty() {
+                enum_candidates.sort();
+
+                // Contextualize for E0412 "cannot find type", but don't belabor the point
+                // (that it's a variant) for E0573 "expected type, found variant".
+                let preamble = if res.is_none() {
+                    let others = match enum_candidates.len() {
+                        1 => String::new(),
+                        2 => " and 1 other".to_owned(),
+                        n => format!(" and {n} others"),
+                    };
+                    format!("there is an enum variant `{}`{}; ", enum_candidates[0].0, others)
+                } else {
+                    String::new()
+                };
+                let msg = format!("{preamble}try using the variant's enum");
+
+                err.span_suggestions(
+                    span,
+                    msg,
+                    enum_candidates.into_iter().map(|(_variant_path, enum_ty_path)| enum_ty_path),
+                    Applicability::MachineApplicable,
+                );
+            }
+        }
+
+        // Try finding a suitable replacement.
+        let typo_sugg = self
+            .lookup_typo_candidate(path, following_seg, source.namespace(), is_expected)
+            .to_opt_suggestion();
+        if path.len() == 1
+            && !matches!(source, PathSource::Delegation)
+            && self.self_type_is_available()
+        {
+            if let Some(candidate) =
+                self.lookup_assoc_candidate(ident, ns, is_expected, source.is_call())
+            {
+                let self_is_available = self.self_value_is_available(path[0].ident.span);
+                // Account for `Foo { field }` when suggesting `self.field` so we result on
+                // `Foo { field: self.field }`.
+                let pre = match source {
+                    PathSource::Expr(Some(Expr { kind: ExprKind::Struct(expr), .. }))
+                        if expr
+                            .fields
+                            .iter()
+                            .any(|f| f.ident == path[0].ident && f.is_shorthand) =>
+                    {
+                        format!("{path_str}: ")
+                    }
+                    _ => String::new(),
+                };
+                match candidate {
+                    AssocSuggestion::Field(field_span) => {
+                        if self_is_available {
+                            err.span_suggestion_verbose(
+                                span.shrink_to_lo(),
+                                "you might have meant to use the available field",
+                                format!("{pre}self."),
+                                Applicability::MachineApplicable,
+                            );
+                        } else {
+                            err.span_label(field_span, "a field by that name exists in `Self`");
+                        }
+                    }
+                    AssocSuggestion::MethodWithSelf { called } if self_is_available => {
+                        let msg = if called {
+                            "you might have meant to call the method"
+                        } else {
+                            "you might have meant to refer to the method"
+                        };
+                        err.span_suggestion_verbose(
+                            span.shrink_to_lo(),
+                            msg,
+                            "self.".to_string(),
+                            Applicability::MachineApplicable,
+                        );
+                    }
+                    AssocSuggestion::MethodWithSelf { .. }
+                    | AssocSuggestion::AssocFn { .. }
+                    | AssocSuggestion::AssocConst
+                    | AssocSuggestion::AssocType => {
+                        err.span_suggestion_verbose(
+                            span.shrink_to_lo(),
+                            format!("you might have meant to {}", candidate.action()),
+                            "Self::".to_string(),
+                            Applicability::MachineApplicable,
+                        );
+                    }
+                }
+                self.r.add_typo_suggestion(err, typo_sugg, ident_span);
+                return (true, candidates);
+            }
+
+            // If the first argument in call is `self` suggest calling a method.
+            if let Some((call_span, args_span)) = self.call_has_self_arg(source) {
+                let mut args_snippet = String::new();
+                if let Some(args_span) = args_span {
+                    if let Ok(snippet) = self.r.tcx.sess.source_map().span_to_snippet(args_span) {
+                        args_snippet = snippet;
+                    }
+                }
+
+                err.span_suggestion(
+                    call_span,
+                    format!("try calling `{ident}` as a method"),
+                    format!("self.{path_str}({args_snippet})"),
+                    Applicability::MachineApplicable,
+                );
+                return (true, candidates);
+            }
+        }
+
+        // Try context-dependent help if relaxed lookup didn't work.
+        if let Some(res) = res {
+            if self.smart_resolve_context_dependent_help(
+                err,
+                span,
+                source,
+                path,
+                res,
+                &path_str,
+                &base_error.fallback_label,
+            ) {
+                // We do this to avoid losing a secondary span when we override the main error span.
+                self.r.add_typo_suggestion(err, typo_sugg, ident_span);
+                return (true, candidates);
+            }
+        }
+
+        // Try to find in last block rib
+        if let Some(rib) = &self.last_block_rib
+            && let RibKind::Normal = rib.kind
+        {
+            for (ident, &res) in &rib.bindings {
+                if let Res::Local(_) = res
+                    && path.len() == 1
+                    && ident.span.eq_ctxt(path[0].ident.span)
+                    && ident.name == path[0].ident.name
+                {
+                    err.span_help(
+                        ident.span,
+                        format!("the binding `{path_str}` is available in a different scope in the same function"),
+                    );
+                    return (true, candidates);
+                }
+            }
+        }
+
+        if candidates.is_empty() {
+            candidates = self.smart_resolve_partial_mod_path_errors(path, following_seg);
+        }
+
+        return (false, candidates);
+    }
+
+    fn suggest_trait_and_bounds(
+        &mut self,
+        err: &mut Diagnostic,
+        source: PathSource<'_>,
+        res: Option<Res>,
+        span: Span,
+        base_error: &BaseError,
+    ) -> bool {
+        let is_macro =
+            base_error.span.from_expansion() && base_error.span.desugaring_kind().is_none();
+        let mut fallback = false;
+
+        if let (
+            PathSource::Trait(AliasPossibility::Maybe),
+            Some(Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, _)),
+            false,
+        ) = (source, res, is_macro)
+        {
+            if let Some(bounds @ [_, .., _]) = self.diagnostic_metadata.current_trait_object {
+                fallback = true;
+                let spans: Vec<Span> = bounds
+                    .iter()
+                    .map(|bound| bound.span())
+                    .filter(|&sp| sp != base_error.span)
+                    .collect();
+
+                let start_span = bounds[0].span();
+                // `end_span` is the end of the poly trait ref (Foo + 'baz + Bar><)
+                let end_span = bounds.last().unwrap().span();
+                // `last_bound_span` is the last bound of the poly trait ref (Foo + >'baz< + Bar)
+                let last_bound_span = spans.last().cloned().unwrap();
+                let mut multi_span: MultiSpan = spans.clone().into();
+                for sp in spans {
+                    let msg = if sp == last_bound_span {
+                        format!(
+                            "...because of {these} bound{s}",
+                            these = pluralize!("this", bounds.len() - 1),
+                            s = pluralize!(bounds.len() - 1),
+                        )
+                    } else {
+                        String::new()
+                    };
+                    multi_span.push_span_label(sp, msg);
+                }
+                multi_span.push_span_label(base_error.span, "expected this type to be a trait...");
+                err.span_help(
+                    multi_span,
+                    "`+` is used to constrain a \"trait object\" type with lifetimes or \
+                        auto-traits; structs and enums can't be bound in that way",
+                );
+                if bounds.iter().all(|bound| match bound {
+                    ast::GenericBound::Outlives(_) => true,
+                    ast::GenericBound::Trait(tr, _) => tr.span == base_error.span,
+                }) {
+                    let mut sugg = vec![];
+                    if base_error.span != start_span {
+                        sugg.push((start_span.until(base_error.span), String::new()));
+                    }
+                    if base_error.span != end_span {
+                        sugg.push((base_error.span.shrink_to_hi().to(end_span), String::new()));
+                    }
+
+                    err.multipart_suggestion(
+                        "if you meant to use a type and not a trait here, remove the bounds",
+                        sugg,
+                        Applicability::MaybeIncorrect,
+                    );
+                }
+            }
+        }
+
+        fallback |= self.restrict_assoc_type_in_where_clause(span, err);
+        fallback
+    }
+
+    fn suggest_typo(
+        &mut self,
+        err: &mut Diagnostic,
+        source: PathSource<'_>,
+        path: &[Segment],
+        following_seg: Option<&Segment>,
+        span: Span,
+        base_error: &BaseError,
+    ) -> bool {
+        let is_expected = &|res| source.is_expected(res);
+        let ident_span = path.last().map_or(span, |ident| ident.ident.span);
+        let typo_sugg =
+            self.lookup_typo_candidate(path, following_seg, source.namespace(), is_expected);
+        let is_in_same_file = &|sp1, sp2| {
+            let source_map = self.r.tcx.sess.source_map();
+            let file1 = source_map.span_to_filename(sp1);
+            let file2 = source_map.span_to_filename(sp2);
+            file1 == file2
+        };
+        // print 'you might have meant' if the candidate is (1) is a shadowed name with
+        // accessible definition and (2) either defined in the same crate as the typo
+        // (could be in a different file) or introduced in the same file as the typo
+        // (could belong to a different crate)
+        if let TypoCandidate::Shadowed(res, Some(sugg_span)) = typo_sugg
+            && res.opt_def_id().is_some_and(|id| id.is_local() || is_in_same_file(span, sugg_span))
+        {
+            err.span_label(
+                sugg_span,
+                format!("you might have meant to refer to this {}", res.descr()),
+            );
+            return true;
+        }
+        let mut fallback = false;
+        let typo_sugg = typo_sugg.to_opt_suggestion();
+        if !self.r.add_typo_suggestion(err, typo_sugg, ident_span) {
+            fallback = true;
+            match self.diagnostic_metadata.current_let_binding {
+                Some((pat_sp, Some(ty_sp), None))
+                    if ty_sp.contains(base_error.span) && base_error.could_be_expr =>
+                {
+                    err.span_suggestion_short(
+                        pat_sp.between(ty_sp),
+                        "use `=` if you meant to assign",
+                        " = ",
+                        Applicability::MaybeIncorrect,
+                    );
+                }
+                _ => {}
+            }
+
+            // If the trait has a single item (which wasn't matched by the algorithm), suggest it
+            let suggestion = self.get_single_associated_item(path, &source, is_expected);
+            if !self.r.add_typo_suggestion(err, suggestion, ident_span) {
+                fallback = !self.let_binding_suggestion(err, ident_span);
+            }
+        }
+        fallback
+    }
+
+    fn err_code_special_cases(
+        &mut self,
+        err: &mut Diagnostic,
+        source: PathSource<'_>,
+        path: &[Segment],
+        span: Span,
+    ) {
+        if let Some(err_code) = &err.code {
+            if err_code == &rustc_errors::error_code!(E0425) {
+                for label_rib in &self.label_ribs {
+                    for (label_ident, node_id) in &label_rib.bindings {
+                        let ident = path.last().unwrap().ident;
+                        if format!("'{ident}") == label_ident.to_string() {
+                            err.span_label(label_ident.span, "a label with a similar name exists");
+                            if let PathSource::Expr(Some(Expr {
+                                kind: ExprKind::Break(None, Some(_)),
+                                ..
+                            })) = source
+                            {
+                                err.span_suggestion(
+                                    span,
+                                    "use the similarly named label",
+                                    label_ident.name,
+                                    Applicability::MaybeIncorrect,
+                                );
+                                // Do not lint against unused label when we suggest them.
+                                self.diagnostic_metadata.unused_labels.remove(node_id);
+                            }
+                        }
+                    }
+                }
+            } else if err_code == &rustc_errors::error_code!(E0412) {
+                if let Some(correct) = Self::likely_rust_type(path) {
+                    err.span_suggestion(
+                        span,
+                        "perhaps you intended to use this type",
+                        correct,
+                        Applicability::MaybeIncorrect,
+                    );
+                }
+            }
+        }
+    }
+
+    /// Emit special messages for unresolved `Self` and `self`.
+    fn suggest_self_ty(
+        &mut self,
+        err: &mut Diagnostic,
+        source: PathSource<'_>,
+        path: &[Segment],
+        span: Span,
+    ) -> bool {
+        if !is_self_type(path, source.namespace()) {
+            return false;
+        }
+        err.code(rustc_errors::error_code!(E0411));
+        err.span_label(span, "`Self` is only available in impls, traits, and type definitions");
+        if let Some(item_kind) = self.diagnostic_metadata.current_item {
+            if !item_kind.ident.span.is_dummy() {
+                err.span_label(
+                    item_kind.ident.span,
+                    format!(
+                        "`Self` not allowed in {} {}",
+                        item_kind.kind.article(),
+                        item_kind.kind.descr()
+                    ),
+                );
+            }
+        }
+        true
+    }
+
+    fn suggest_self_value(
+        &mut self,
+        err: &mut Diagnostic,
+        source: PathSource<'_>,
+        path: &[Segment],
+        span: Span,
+    ) -> bool {
+        if !is_self_value(path, source.namespace()) {
+            return false;
+        }
+
+        debug!("smart_resolve_path_fragment: E0424, source={:?}", source);
+        err.code(rustc_errors::error_code!(E0424));
+        err.span_label(
+            span,
+            match source {
+                PathSource::Pat => {
+                    "`self` value is a keyword and may not be bound to variables or shadowed"
+                }
+                _ => "`self` value is a keyword only available in methods with a `self` parameter",
+            },
+        );
+        let is_assoc_fn = self.self_type_is_available();
+        if let Some((fn_kind, span)) = &self.diagnostic_metadata.current_function {
+            // The current function has a `self` parameter, but we were unable to resolve
+            // a reference to `self`. This can only happen if the `self` identifier we
+            // are resolving came from a different hygiene context.
+            if fn_kind.decl().inputs.get(0).is_some_and(|p| p.is_self()) {
+                err.span_label(*span, "this function has a `self` parameter, but a macro invocation can only access identifiers it receives from parameters");
+            } else {
+                let doesnt = if is_assoc_fn {
+                    let (span, sugg) = fn_kind
+                        .decl()
+                        .inputs
+                        .get(0)
+                        .map(|p| (p.span.shrink_to_lo(), "&self, "))
+                        .unwrap_or_else(|| {
+                            // Try to look for the "(" after the function name, if possible.
+                            // This avoids placing the suggestion into the visibility specifier.
+                            let span = fn_kind
+                                .ident()
+                                .map_or(*span, |ident| span.with_lo(ident.span.hi()));
+                            (
+                                self.r
+                                    .tcx
+                                    .sess
+                                    .source_map()
+                                    .span_through_char(span, '(')
+                                    .shrink_to_hi(),
+                                "&self",
+                            )
+                        });
+                    err.span_suggestion_verbose(
+                        span,
+                        "add a `self` receiver parameter to make the associated `fn` a method",
+                        sugg,
+                        Applicability::MaybeIncorrect,
+                    );
+                    "doesn't"
+                } else {
+                    "can't"
+                };
+                if let Some(ident) = fn_kind.ident() {
+                    err.span_label(
+                        ident.span,
+                        format!("this function {doesnt} have a `self` parameter"),
+                    );
+                }
+            }
+        } else if let Some(item_kind) = self.diagnostic_metadata.current_item {
+            err.span_label(
+                item_kind.ident.span,
+                format!(
+                    "`self` not allowed in {} {}",
+                    item_kind.kind.article(),
+                    item_kind.kind.descr()
+                ),
+            );
+        }
+        true
+    }
+
+    fn suggest_at_operator_in_slice_pat_with_range(
+        &mut self,
+        err: &mut Diagnostic,
+        path: &[Segment],
+    ) {
+        if let Some(pat) = self.diagnostic_metadata.current_pat
+            && let ast::PatKind::Range(Some(start), None, range) = &pat.kind
+            && let ExprKind::Path(None, range_path) = &start.kind
+            && let [segment] = &range_path.segments[..]
+            && let [s] = path
+            && segment.ident == s.ident
+        {
+            // We've encountered `[first, rest..]` where the user might have meant
+            // `[first, rest @ ..]` (#88404).
+            err.span_suggestion_verbose(
+                segment.ident.span.between(range.span),
+                format!(
+                    "if you meant to collect the rest of the slice in `{}`, use the at operator",
+                    segment.ident,
+                ),
+                " @ ",
+                Applicability::MaybeIncorrect,
+            );
+        }
+    }
+
+    fn suggest_swapping_misplaced_self_ty_and_trait(
+        &mut self,
+        err: &mut Diagnostic,
+        source: PathSource<'_>,
+        res: Option<Res>,
+        span: Span,
+    ) {
+        if let Some((trait_ref, self_ty)) =
+            self.diagnostic_metadata.currently_processing_impl_trait.clone()
+            && let TyKind::Path(_, self_ty_path) = &self_ty.kind
+            && let PathResult::Module(ModuleOrUniformRoot::Module(module)) =
+                self.resolve_path(&Segment::from_path(self_ty_path), Some(TypeNS), None)
+            && let ModuleKind::Def(DefKind::Trait, ..) = module.kind
+            && trait_ref.path.span == span
+            && let PathSource::Trait(_) = source
+            && let Some(Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, _)) = res
+            && let Ok(self_ty_str) = self.r.tcx.sess.source_map().span_to_snippet(self_ty.span)
+            && let Ok(trait_ref_str) =
+                self.r.tcx.sess.source_map().span_to_snippet(trait_ref.path.span)
+        {
+            err.multipart_suggestion(
+                    "`impl` items mention the trait being implemented first and the type it is being implemented for second",
+                    vec![(trait_ref.path.span, self_ty_str), (self_ty.span, trait_ref_str)],
+                    Applicability::MaybeIncorrect,
+                );
+        }
+    }
+
+    fn suggest_bare_struct_literal(&mut self, err: &mut Diagnostic) {
+        if let Some(span) = self.diagnostic_metadata.current_block_could_be_bare_struct_literal {
+            err.multipart_suggestion(
+                "you might have meant to write a `struct` literal",
+                vec![
+                    (span.shrink_to_lo(), "{ SomeStruct ".to_string()),
+                    (span.shrink_to_hi(), "}".to_string()),
+                ],
+                Applicability::HasPlaceholders,
+            );
+        }
+    }
+
+    fn suggest_pattern_match_with_let(
+        &mut self,
+        err: &mut Diagnostic,
+        source: PathSource<'_>,
+        span: Span,
+    ) -> bool {
+        if let PathSource::Expr(_) = source
+            && let Some(Expr { span: expr_span, kind: ExprKind::Assign(lhs, _, _), .. }) =
+                self.diagnostic_metadata.in_if_condition
+        {
+            // Icky heuristic so we don't suggest:
+            // `if (i + 2) = 2` => `if let (i + 2) = 2` (approximately pattern)
+            // `if 2 = i` => `if let 2 = i` (lhs needs to contain error span)
+            if lhs.is_approximately_pattern() && lhs.span.contains(span) {
+                err.span_suggestion_verbose(
+                    expr_span.shrink_to_lo(),
+                    "you might have meant to use pattern matching",
+                    "let ",
+                    Applicability::MaybeIncorrect,
+                );
+                return true;
+            }
+        }
+        false
+    }
+
+    fn get_single_associated_item(
+        &mut self,
+        path: &[Segment],
+        source: &PathSource<'_>,
+        filter_fn: &impl Fn(Res) -> bool,
+    ) -> Option<TypoSuggestion> {
+        if let crate::PathSource::TraitItem(_) = source {
+            let mod_path = &path[..path.len() - 1];
+            if let PathResult::Module(ModuleOrUniformRoot::Module(module)) =
+                self.resolve_path(mod_path, None, None)
+            {
+                let resolutions = self.r.resolutions(module).borrow();
+                let targets: Vec<_> =
+                    resolutions
+                        .iter()
+                        .filter_map(|(key, resolution)| {
+                            resolution.borrow().binding.map(|binding| binding.res()).and_then(
+                                |res| if filter_fn(res) { Some((key, res)) } else { None },
+                            )
+                        })
+                        .collect();
+                if targets.len() == 1 {
+                    let target = targets[0];
+                    return Some(TypoSuggestion::single_item_from_ident(target.0.ident, target.1));
+                }
+            }
+        }
+        None
+    }
+
+    /// Given `where <T as Bar>::Baz: String`, suggest `where T: Bar<Baz = String>`.
+    fn restrict_assoc_type_in_where_clause(&mut self, span: Span, err: &mut Diagnostic) -> bool {
+        // Detect that we are actually in a `where` predicate.
+        let (bounded_ty, bounds, where_span) =
+            if let Some(ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate {
+                bounded_ty,
+                bound_generic_params,
+                bounds,
+                span,
+            })) = self.diagnostic_metadata.current_where_predicate
+            {
+                if !bound_generic_params.is_empty() {
+                    return false;
+                }
+                (bounded_ty, bounds, span)
+            } else {
+                return false;
+            };
+
+        // Confirm that the target is an associated type.
+        let (ty, _, path) = if let ast::TyKind::Path(Some(qself), path) = &bounded_ty.kind {
+            // use this to verify that ident is a type param.
+            let Some(partial_res) = self.r.partial_res_map.get(&bounded_ty.id) else {
+                return false;
+            };
+            if !matches!(
+                partial_res.full_res(),
+                Some(hir::def::Res::Def(hir::def::DefKind::AssocTy, _))
+            ) {
+                return false;
+            }
+            (&qself.ty, qself.position, path)
+        } else {
+            return false;
+        };
+
+        let peeled_ty = ty.peel_refs();
+        if let ast::TyKind::Path(None, type_param_path) = &peeled_ty.kind {
+            // Confirm that the `SelfTy` is a type parameter.
+            let Some(partial_res) = self.r.partial_res_map.get(&peeled_ty.id) else {
+                return false;
+            };
+            if !matches!(
+                partial_res.full_res(),
+                Some(hir::def::Res::Def(hir::def::DefKind::TyParam, _))
+            ) {
+                return false;
+            }
+            if let (
+                [ast::PathSegment { args: None, .. }],
+                [ast::GenericBound::Trait(poly_trait_ref, ast::TraitBoundModifiers::NONE)],
+            ) = (&type_param_path.segments[..], &bounds[..])
+            {
+                if let [ast::PathSegment { ident, args: None, .. }] =
+                    &poly_trait_ref.trait_ref.path.segments[..]
+                {
+                    if ident.span == span {
+                        let Some(new_where_bound_predicate) =
+                            mk_where_bound_predicate(path, poly_trait_ref, ty)
+                        else {
+                            return false;
+                        };
+                        err.span_suggestion_verbose(
+                            *where_span,
+                            format!("constrain the associated type to `{ident}`"),
+                            where_bound_predicate_to_string(&new_where_bound_predicate),
+                            Applicability::MaybeIncorrect,
+                        );
+                    }
+                    return true;
+                }
+            }
+        }
+        false
+    }
+
+    /// Check if the source is call expression and the first argument is `self`. If true,
+    /// return the span of whole call and the span for all arguments expect the first one (`self`).
+    fn call_has_self_arg(&self, source: PathSource<'_>) -> Option<(Span, Option<Span>)> {
+        let mut has_self_arg = None;
+        if let PathSource::Expr(Some(parent)) = source
+            && let ExprKind::Call(_, args) = &parent.kind
+            && !args.is_empty()
+        {
+            let mut expr_kind = &args[0].kind;
+            loop {
+                match expr_kind {
+                    ExprKind::Path(_, arg_name) if arg_name.segments.len() == 1 => {
+                        if arg_name.segments[0].ident.name == kw::SelfLower {
+                            let call_span = parent.span;
+                            let tail_args_span = if args.len() > 1 {
+                                Some(Span::new(
+                                    args[1].span.lo(),
+                                    args.last().unwrap().span.hi(),
+                                    call_span.ctxt(),
+                                    None,
+                                ))
+                            } else {
+                                None
+                            };
+                            has_self_arg = Some((call_span, tail_args_span));
+                        }
+                        break;
+                    }
+                    ExprKind::AddrOf(_, _, expr) => expr_kind = &expr.kind,
+                    _ => break,
+                }
+            }
+        }
+        has_self_arg
+    }
+
+    fn followed_by_brace(&self, span: Span) -> (bool, Option<Span>) {
+        // HACK(estebank): find a better way to figure out that this was a
+        // parser issue where a struct literal is being used on an expression
+        // where a brace being opened means a block is being started. Look
+        // ahead for the next text to see if `span` is followed by a `{`.
+        let sm = self.r.tcx.sess.source_map();
+        if let Some(followed_brace_span) = sm.span_look_ahead(span, "{", Some(50)) {
+            // In case this could be a struct literal that needs to be surrounded
+            // by parentheses, find the appropriate span.
+            let close_brace_span = sm.span_look_ahead(followed_brace_span, "}", Some(50));
+            let closing_brace = close_brace_span.map(|sp| span.to(sp));
+            (true, closing_brace)
+        } else {
+            (false, None)
+        }
+    }
+
+    /// Provides context-dependent help for errors reported by the `smart_resolve_path_fragment`
+    /// function.
+    /// Returns `true` if able to provide context-dependent help.
+    fn smart_resolve_context_dependent_help(
+        &mut self,
+        err: &mut Diagnostic,
+        span: Span,
+        source: PathSource<'_>,
+        path: &[Segment],
+        res: Res,
+        path_str: &str,
+        fallback_label: &str,
+    ) -> bool {
+        let ns = source.namespace();
+        let is_expected = &|res| source.is_expected(res);
+
+        let path_sep = |this: &mut Self, err: &mut Diagnostic, expr: &Expr, kind: DefKind| {
+            const MESSAGE: &str = "use the path separator to refer to an item";
+
+            let (lhs_span, rhs_span) = match &expr.kind {
+                ExprKind::Field(base, ident) => (base.span, ident.span),
+                ExprKind::MethodCall(box MethodCall { receiver, span, .. }) => {
+                    (receiver.span, *span)
+                }
+                _ => return false,
+            };
+
+            if lhs_span.eq_ctxt(rhs_span) {
+                err.span_suggestion(
+                    lhs_span.between(rhs_span),
+                    MESSAGE,
+                    "::",
+                    Applicability::MaybeIncorrect,
+                );
+                true
+            } else if kind == DefKind::Struct
+                && let Some(lhs_source_span) = lhs_span.find_ancestor_inside(expr.span)
+                && let Ok(snippet) = this.r.tcx.sess.source_map().span_to_snippet(lhs_source_span)
+            {
+                // The LHS is a type that originates from a macro call.
+                // We have to add angle brackets around it.
+
+                err.span_suggestion_verbose(
+                    lhs_source_span.until(rhs_span),
+                    MESSAGE,
+                    format!("<{snippet}>::"),
+                    Applicability::MaybeIncorrect,
+                );
+                true
+            } else {
+                // Either we were unable to obtain the source span / the snippet or
+                // the LHS originates from a macro call and it is not a type and thus
+                // there is no way to replace `.` with `::` and still somehow suggest
+                // valid Rust code.
+
+                false
+            }
+        };
+
+        let find_span = |source: &PathSource<'_>, err: &mut Diagnostic| {
+            match source {
+                PathSource::Expr(Some(Expr { span, kind: ExprKind::Call(_, _), .. }))
+                | PathSource::TupleStruct(span, _) => {
+                    // We want the main underline to cover the suggested code as well for
+                    // cleaner output.
+                    err.span(*span);
+                    *span
+                }
+                _ => span,
+            }
+        };
+
+        let mut bad_struct_syntax_suggestion = |this: &mut Self, def_id: DefId| {
+            let (followed_by_brace, closing_brace) = this.followed_by_brace(span);
+
+            match source {
+                PathSource::Expr(Some(
+                    parent @ Expr { kind: ExprKind::Field(..) | ExprKind::MethodCall(..), .. },
+                )) if path_sep(this, err, parent, DefKind::Struct) => {}
+                PathSource::Expr(
+                    None
+                    | Some(Expr {
+                        kind:
+                            ExprKind::Path(..)
+                            | ExprKind::Binary(..)
+                            | ExprKind::Unary(..)
+                            | ExprKind::If(..)
+                            | ExprKind::While(..)
+                            | ExprKind::ForLoop { .. }
+                            | ExprKind::Match(..),
+                        ..
+                    }),
+                ) if followed_by_brace => {
+                    if let Some(sp) = closing_brace {
+                        err.span_label(span, fallback_label.to_string());
+                        err.multipart_suggestion(
+                            "surround the struct literal with parentheses",
+                            vec![
+                                (sp.shrink_to_lo(), "(".to_string()),
+                                (sp.shrink_to_hi(), ")".to_string()),
+                            ],
+                            Applicability::MaybeIncorrect,
+                        );
+                    } else {
+                        err.span_label(
+                            span, // Note the parentheses surrounding the suggestion below
+                            format!(
+                                "you might want to surround a struct literal with parentheses: \
+                                 `({path_str} {{ /* fields */ }})`?"
+                            ),
+                        );
+                    }
+                }
+                PathSource::Expr(_) | PathSource::TupleStruct(..) | PathSource::Pat => {
+                    let span = find_span(&source, err);
+                    err.span_label(this.r.def_span(def_id), format!("`{path_str}` defined here"));
+
+                    let (tail, descr, applicability, old_fields) = match source {
+                        PathSource::Pat => ("", "pattern", Applicability::MachineApplicable, None),
+                        PathSource::TupleStruct(_, args) => (
+                            "",
+                            "pattern",
+                            Applicability::MachineApplicable,
+                            Some(
+                                args.iter()
+                                    .map(|a| this.r.tcx.sess.source_map().span_to_snippet(*a).ok())
+                                    .collect::<Vec<Option<String>>>(),
+                            ),
+                        ),
+                        _ => (": val", "literal", Applicability::HasPlaceholders, None),
+                    };
+
+                    if !this.has_private_fields(def_id) {
+                        // If the fields of the type are private, we shouldn't be suggesting using
+                        // the struct literal syntax at all, as that will cause a subsequent error.
+                        let field_ids = this.r.field_def_ids(def_id);
+                        let (fields, applicability) = match field_ids {
+                            Some(field_ids) => {
+                                let fields = field_ids.iter().map(|&id| this.r.tcx.item_name(id));
+
+                                let fields = if let Some(old_fields) = old_fields {
+                                    fields
+                                        .enumerate()
+                                        .map(|(idx, new)| (new, old_fields.get(idx)))
+                                        .map(|(new, old)| {
+                                            let new = new.to_ident_string();
+                                            if let Some(Some(old)) = old
+                                                && new != *old
+                                            {
+                                                format!("{new}: {old}")
+                                            } else {
+                                                new
+                                            }
+                                        })
+                                        .collect::<Vec<String>>()
+                                } else {
+                                    fields.map(|f| format!("{f}{tail}")).collect::<Vec<String>>()
+                                };
+
+                                (fields.join(", "), applicability)
+                            }
+                            None => ("/* fields */".to_string(), Applicability::HasPlaceholders),
+                        };
+                        let pad = match field_ids {
+                            Some(field_ids) if field_ids.is_empty() => "",
+                            _ => " ",
+                        };
+                        err.span_suggestion(
+                            span,
+                            format!("use struct {descr} syntax instead"),
+                            format!("{path_str} {{{pad}{fields}{pad}}}"),
+                            applicability,
+                        );
+                    }
+                    if let PathSource::Expr(Some(Expr {
+                        kind: ExprKind::Call(path, ref args),
+                        span: call_span,
+                        ..
+                    })) = source
+                    {
+                        this.suggest_alternative_construction_methods(
+                            def_id,
+                            err,
+                            path.span,
+                            *call_span,
+                            &args[..],
+                        );
+                    }
+                }
+                _ => {
+                    err.span_label(span, fallback_label.to_string());
+                }
+            }
+        };
+
+        match (res, source) {
+            (
+                Res::Def(DefKind::Macro(MacroKind::Bang), _),
+                PathSource::Expr(Some(Expr {
+                    kind: ExprKind::Index(..) | ExprKind::Call(..), ..
+                }))
+                | PathSource::Struct,
+            ) => {
+                err.span_label(span, fallback_label.to_string());
+
+                // Don't suggest `!` for a macro invocation if there are generic args
+                if path
+                    .last()
+                    .is_some_and(|segment| !segment.has_generic_args && !segment.has_lifetime_args)
+                {
+                    err.span_suggestion_verbose(
+                        span.shrink_to_hi(),
+                        "use `!` to invoke the macro",
+                        "!",
+                        Applicability::MaybeIncorrect,
+                    );
+                }
+
+                if path_str == "try" && span.is_rust_2015() {
+                    err.note("if you want the `try` keyword, you need Rust 2018 or later");
+                }
+            }
+            (Res::Def(DefKind::Macro(MacroKind::Bang), _), _) => {
+                err.span_label(span, fallback_label.to_string());
+            }
+            (Res::Def(DefKind::TyAlias, def_id), PathSource::Trait(_)) => {
+                err.span_label(span, "type aliases cannot be used as traits");
+                if self.r.tcx.sess.is_nightly_build() {
+                    let msg = "you might have meant to use `#![feature(trait_alias)]` instead of a \
+                               `type` alias";
+                    let span = self.r.def_span(def_id);
+                    if let Ok(snip) = self.r.tcx.sess.source_map().span_to_snippet(span) {
+                        // The span contains a type alias so we should be able to
+                        // replace `type` with `trait`.
+                        let snip = snip.replacen("type", "trait", 1);
+                        err.span_suggestion(span, msg, snip, Applicability::MaybeIncorrect);
+                    } else {
+                        err.span_help(span, msg);
+                    }
+                }
+            }
+            (
+                Res::Def(kind @ (DefKind::Mod | DefKind::Trait), _),
+                PathSource::Expr(Some(parent)),
+            ) => {
+                if !path_sep(self, err, parent, kind) {
+                    return false;
+                }
+            }
+            (
+                Res::Def(DefKind::Enum, def_id),
+                PathSource::TupleStruct(..) | PathSource::Expr(..),
+            ) => {
+                self.suggest_using_enum_variant(err, source, def_id, span);
+            }
+            (Res::Def(DefKind::Struct, def_id), source) if ns == ValueNS => {
+                let struct_ctor = match def_id.as_local() {
+                    Some(def_id) => self.r.struct_constructors.get(&def_id).cloned(),
+                    None => {
+                        let ctor = self.r.cstore().ctor_untracked(def_id);
+                        ctor.map(|(ctor_kind, ctor_def_id)| {
+                            let ctor_res =
+                                Res::Def(DefKind::Ctor(CtorOf::Struct, ctor_kind), ctor_def_id);
+                            let ctor_vis = self.r.tcx.visibility(ctor_def_id);
+                            let field_visibilities = self
+                                .r
+                                .tcx
+                                .associated_item_def_ids(def_id)
+                                .iter()
+                                .map(|field_id| self.r.tcx.visibility(field_id))
+                                .collect();
+                            (ctor_res, ctor_vis, field_visibilities)
+                        })
+                    }
+                };
+
+                let (ctor_def, ctor_vis, fields) = if let Some(struct_ctor) = struct_ctor {
+                    if let PathSource::Expr(Some(parent)) = source {
+                        if let ExprKind::Field(..) | ExprKind::MethodCall(..) = parent.kind {
+                            bad_struct_syntax_suggestion(self, def_id);
+                            return true;
+                        }
+                    }
+                    struct_ctor
+                } else {
+                    bad_struct_syntax_suggestion(self, def_id);
+                    return true;
+                };
+
+                let is_accessible = self.r.is_accessible_from(ctor_vis, self.parent_scope.module);
+                if !is_expected(ctor_def) || is_accessible {
+                    return true;
+                }
+
+                let field_spans = match source {
+                    // e.g. `if let Enum::TupleVariant(field1, field2) = _`
+                    PathSource::TupleStruct(_, pattern_spans) => {
+                        err.primary_message(
+                            "cannot match against a tuple struct which contains private fields",
+                        );
+
+                        // Use spans of the tuple struct pattern.
+                        Some(Vec::from(pattern_spans))
+                    }
+                    // e.g. `let _ = Enum::TupleVariant(field1, field2);`
+                    PathSource::Expr(Some(Expr {
+                        kind: ExprKind::Call(path, ref args),
+                        span: call_span,
+                        ..
+                    })) => {
+                        err.primary_message(
+                            "cannot initialize a tuple struct which contains private fields",
+                        );
+                        self.suggest_alternative_construction_methods(
+                            def_id,
+                            err,
+                            path.span,
+                            *call_span,
+                            &args[..],
+                        );
+                        // Use spans of the tuple struct definition.
+                        self.r.field_def_ids(def_id).map(|field_ids| {
+                            field_ids
+                                .iter()
+                                .map(|&field_id| self.r.def_span(field_id))
+                                .collect::<Vec<_>>()
+                        })
+                    }
+                    _ => None,
+                };
+
+                if let Some(spans) =
+                    field_spans.filter(|spans| spans.len() > 0 && fields.len() == spans.len())
+                {
+                    let non_visible_spans: Vec<Span> = iter::zip(&fields, &spans)
+                        .filter(|(vis, _)| {
+                            !self.r.is_accessible_from(**vis, self.parent_scope.module)
+                        })
+                        .map(|(_, span)| *span)
+                        .collect();
+
+                    if non_visible_spans.len() > 0 {
+                        if let Some(fields) = self.r.field_visibility_spans.get(&def_id) {
+                            err.multipart_suggestion_verbose(
+                                format!(
+                                    "consider making the field{} publicly accessible",
+                                    pluralize!(fields.len())
+                                ),
+                                fields.iter().map(|span| (*span, "pub ".to_string())).collect(),
+                                Applicability::MaybeIncorrect,
+                            );
+                        }
+
+                        let mut m: MultiSpan = non_visible_spans.clone().into();
+                        non_visible_spans
+                            .into_iter()
+                            .for_each(|s| m.push_span_label(s, "private field"));
+                        err.span_note(m, "constructor is not visible here due to private fields");
+                    }
+
+                    return true;
+                }
+
+                err.span_label(span, "constructor is not visible here due to private fields");
+            }
+            (Res::Def(DefKind::Union | DefKind::Variant, def_id), _) if ns == ValueNS => {
+                bad_struct_syntax_suggestion(self, def_id);
+            }
+            (Res::Def(DefKind::Ctor(_, CtorKind::Const), def_id), _) if ns == ValueNS => {
+                match source {
+                    PathSource::Expr(_) | PathSource::TupleStruct(..) | PathSource::Pat => {
+                        let span = find_span(&source, err);
+                        err.span_label(
+                            self.r.def_span(def_id),
+                            format!("`{path_str}` defined here"),
+                        );
+                        err.span_suggestion(
+                            span,
+                            "use this syntax instead",
+                            path_str,
+                            Applicability::MaybeIncorrect,
+                        );
+                    }
+                    _ => return false,
+                }
+            }
+            (Res::Def(DefKind::Ctor(_, CtorKind::Fn), ctor_def_id), _) if ns == ValueNS => {
+                let def_id = self.r.tcx.parent(ctor_def_id);
+                err.span_label(self.r.def_span(def_id), format!("`{path_str}` defined here"));
+                let fields = self.r.field_def_ids(def_id).map_or_else(
+                    || "/* fields */".to_string(),
+                    |field_ids| vec!["_"; field_ids.len()].join(", "),
+                );
+                err.span_suggestion(
+                    span,
+                    "use the tuple variant pattern syntax instead",
+                    format!("{path_str}({fields})"),
+                    Applicability::HasPlaceholders,
+                );
+            }
+            (Res::SelfTyParam { .. } | Res::SelfTyAlias { .. }, _) if ns == ValueNS => {
+                err.span_label(span, fallback_label.to_string());
+                err.note("can't use `Self` as a constructor, you must use the implemented struct");
+            }
+            (Res::Def(DefKind::TyAlias | DefKind::AssocTy, _), _) if ns == ValueNS => {
+                err.note("can't use a type alias as a constructor");
+            }
+            _ => return false,
+        }
+        true
+    }
+
+    fn suggest_alternative_construction_methods(
+        &mut self,
+        def_id: DefId,
+        err: &mut Diagnostic,
+        path_span: Span,
+        call_span: Span,
+        args: &[P<Expr>],
+    ) {
+        if def_id.is_local() {
+            // Doing analysis on local `DefId`s would cause infinite recursion.
+            return;
+        }
+        // Look at all the associated functions without receivers in the type's
+        // inherent impls to look for builders that return `Self`
+        let mut items = self
+            .r
+            .tcx
+            .inherent_impls(def_id)
+            .iter()
+            .flat_map(|i| self.r.tcx.associated_items(i).in_definition_order())
+            // Only assoc fn with no receivers.
+            .filter(|item| matches!(item.kind, ty::AssocKind::Fn) && !item.fn_has_self_parameter)
+            .filter_map(|item| {
+                // Only assoc fns that return `Self`
+                let fn_sig = self.r.tcx.fn_sig(item.def_id).skip_binder();
+                // Don't normalize the return type, because that can cause cycle errors.
+                let ret_ty = fn_sig.output().skip_binder();
+                let ty::Adt(def, _args) = ret_ty.kind() else {
+                    return None;
+                };
+                let input_len = fn_sig.inputs().skip_binder().len();
+                if def.did() != def_id {
+                    return None;
+                }
+                let order = !item.name.as_str().starts_with("new");
+                Some((order, item.name, input_len))
+            })
+            .collect::<Vec<_>>();
+        items.sort_by_key(|(order, _, _)| *order);
+        let suggestion = |name, args| {
+            format!(
+                "::{name}({})",
+                std::iter::repeat("_").take(args).collect::<Vec<_>>().join(", ")
+            )
+        };
+        match &items[..] {
+            [] => {}
+            [(_, name, len)] if *len == args.len() => {
+                err.span_suggestion_verbose(
+                    path_span.shrink_to_hi(),
+                    format!("you might have meant to use the `{name}` associated function",),
+                    format!("::{name}"),
+                    Applicability::MaybeIncorrect,
+                );
+            }
+            [(_, name, len)] => {
+                err.span_suggestion_verbose(
+                    path_span.shrink_to_hi().with_hi(call_span.hi()),
+                    format!("you might have meant to use the `{name}` associated function",),
+                    suggestion(name, *len),
+                    Applicability::MaybeIncorrect,
+                );
+            }
+            _ => {
+                err.span_suggestions_with_style(
+                    path_span.shrink_to_hi().with_hi(call_span.hi()),
+                    "you might have meant to use an associated function to build this type",
+                    items
+                        .iter()
+                        .map(|(_, name, len)| suggestion(name, *len))
+                        .collect::<Vec<String>>(),
+                    Applicability::MaybeIncorrect,
+                    SuggestionStyle::ShowAlways,
+                );
+            }
+        }
+        // We'd ideally use `type_implements_trait` but don't have access to
+        // the trait solver here. We can't use `get_diagnostic_item` or
+        // `all_traits` in resolve either. So instead we abuse the import
+        // suggestion machinery to get `std::default::Default` and perform some
+        // checks to confirm that we got *only* that trait. We then see if the
+        // Adt we have has a direct implementation of `Default`. If so, we
+        // provide a structured suggestion.
+        let default_trait = self
+            .r
+            .lookup_import_candidates(
+                Ident::with_dummy_span(sym::Default),
+                Namespace::TypeNS,
+                &self.parent_scope,
+                &|res: Res| matches!(res, Res::Def(DefKind::Trait, _)),
+            )
+            .iter()
+            .filter_map(|candidate| candidate.did)
+            .find(|did| {
+                self.r
+                    .tcx
+                    .get_attrs(*did, sym::rustc_diagnostic_item)
+                    .any(|attr| attr.value_str() == Some(sym::Default))
+            });
+        let Some(default_trait) = default_trait else {
+            return;
+        };
+        if self
+            .r
+            .extern_crate_map
+            .iter()
+            // FIXME: This doesn't include impls like `impl Default for String`.
+            .flat_map(|(_, crate_)| self.r.tcx.implementations_of_trait((*crate_, default_trait)))
+            .filter_map(|(_, simplified_self_ty)| *simplified_self_ty)
+            .filter_map(|simplified_self_ty| match simplified_self_ty {
+                SimplifiedType::Adt(did) => Some(did),
+                _ => None,
+            })
+            .any(|did| did == def_id)
+        {
+            err.multipart_suggestion(
+                "consider using the `Default` trait",
+                vec![
+                    (path_span.shrink_to_lo(), "<".to_string()),
+                    (
+                        path_span.shrink_to_hi().with_hi(call_span.hi()),
+                        " as std::default::Default>::default()".to_string(),
+                    ),
+                ],
+                Applicability::MaybeIncorrect,
+            );
+        }
+    }
+
+    fn has_private_fields(&self, def_id: DefId) -> bool {
+        let fields = match def_id.as_local() {
+            Some(def_id) => self.r.struct_constructors.get(&def_id).cloned().map(|(_, _, f)| f),
+            None => Some(
+                self.r
+                    .tcx
+                    .associated_item_def_ids(def_id)
+                    .iter()
+                    .map(|field_id| self.r.tcx.visibility(field_id))
+                    .collect(),
+            ),
+        };
+
+        fields.is_some_and(|fields| {
+            fields.iter().any(|vis| !self.r.is_accessible_from(*vis, self.parent_scope.module))
+        })
+    }
+
+    /// Given the target `ident` and `kind`, search for the similarly named associated item
+    /// in `self.current_trait_ref`.
+    pub(crate) fn find_similarly_named_assoc_item(
+        &mut self,
+        ident: Symbol,
+        kind: &AssocItemKind,
+    ) -> Option<Symbol> {
+        let (module, _) = self.current_trait_ref.as_ref()?;
+        if ident == kw::Underscore {
+            // We do nothing for `_`.
+            return None;
+        }
+
+        let resolutions = self.r.resolutions(*module);
+        let targets = resolutions
+            .borrow()
+            .iter()
+            .filter_map(|(key, res)| res.borrow().binding.map(|binding| (key, binding.res())))
+            .filter(|(_, res)| match (kind, res) {
+                (AssocItemKind::Const(..), Res::Def(DefKind::AssocConst, _)) => true,
+                (AssocItemKind::Fn(_), Res::Def(DefKind::AssocFn, _)) => true,
+                (AssocItemKind::Type(..), Res::Def(DefKind::AssocTy, _)) => true,
+                (AssocItemKind::Delegation(_), Res::Def(DefKind::AssocFn, _)) => true,
+                _ => false,
+            })
+            .map(|(key, _)| key.ident.name)
+            .collect::<Vec<_>>();
+
+        find_best_match_for_name(&targets, ident, None)
+    }
+
+    fn lookup_assoc_candidate<FilterFn>(
+        &mut self,
+        ident: Ident,
+        ns: Namespace,
+        filter_fn: FilterFn,
+        called: bool,
+    ) -> Option<AssocSuggestion>
+    where
+        FilterFn: Fn(Res) -> bool,
+    {
+        fn extract_node_id(t: &Ty) -> Option<NodeId> {
+            match t.kind {
+                TyKind::Path(None, _) => Some(t.id),
+                TyKind::Ref(_, ref mut_ty) => extract_node_id(&mut_ty.ty),
+                // This doesn't handle the remaining `Ty` variants as they are not
+                // that commonly the self_type, it might be interesting to provide
+                // support for those in future.
+                _ => None,
+            }
+        }
+        // Fields are generally expected in the same contexts as locals.
+        if filter_fn(Res::Local(ast::DUMMY_NODE_ID)) {
+            if let Some(node_id) =
+                self.diagnostic_metadata.current_self_type.as_ref().and_then(extract_node_id)
+            {
+                // Look for a field with the same name in the current self_type.
+                if let Some(resolution) = self.r.partial_res_map.get(&node_id) {
+                    if let Some(Res::Def(DefKind::Struct | DefKind::Union, did)) =
+                        resolution.full_res()
+                    {
+                        if let Some(field_ids) = self.r.field_def_ids(did) {
+                            if let Some(field_id) = field_ids
+                                .iter()
+                                .find(|&&field_id| ident.name == self.r.tcx.item_name(field_id))
+                            {
+                                return Some(AssocSuggestion::Field(self.r.def_span(*field_id)));
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        if let Some(items) = self.diagnostic_metadata.current_trait_assoc_items {
+            for assoc_item in items {
+                if assoc_item.ident == ident {
+                    return Some(match &assoc_item.kind {
+                        ast::AssocItemKind::Const(..) => AssocSuggestion::AssocConst,
+                        ast::AssocItemKind::Fn(box ast::Fn { sig, .. }) if sig.decl.has_self() => {
+                            AssocSuggestion::MethodWithSelf { called }
+                        }
+                        ast::AssocItemKind::Fn(..) => AssocSuggestion::AssocFn { called },
+                        ast::AssocItemKind::Type(..) => AssocSuggestion::AssocType,
+                        ast::AssocItemKind::Delegation(..)
+                            if self.r.has_self.contains(&self.r.local_def_id(assoc_item.id)) =>
+                        {
+                            AssocSuggestion::MethodWithSelf { called }
+                        }
+                        ast::AssocItemKind::Delegation(..) => AssocSuggestion::AssocFn { called },
+                        ast::AssocItemKind::MacCall(_) => continue,
+                    });
+                }
+            }
+        }
+
+        // Look for associated items in the current trait.
+        if let Some((module, _)) = self.current_trait_ref {
+            if let Ok(binding) = self.r.maybe_resolve_ident_in_module(
+                ModuleOrUniformRoot::Module(module),
+                ident,
+                ns,
+                &self.parent_scope,
+            ) {
+                let res = binding.res();
+                if filter_fn(res) {
+                    let def_id = res.def_id();
+                    let has_self = match def_id.as_local() {
+                        Some(def_id) => self.r.has_self.contains(&def_id),
+                        None => self
+                            .r
+                            .tcx
+                            .fn_arg_names(def_id)
+                            .first()
+                            .is_some_and(|ident| ident.name == kw::SelfLower),
+                    };
+                    if has_self {
+                        return Some(AssocSuggestion::MethodWithSelf { called });
+                    } else {
+                        match res {
+                            Res::Def(DefKind::AssocFn, _) => {
+                                return Some(AssocSuggestion::AssocFn { called });
+                            }
+                            Res::Def(DefKind::AssocConst, _) => {
+                                return Some(AssocSuggestion::AssocConst);
+                            }
+                            Res::Def(DefKind::AssocTy, _) => {
+                                return Some(AssocSuggestion::AssocType);
+                            }
+                            _ => {}
+                        }
+                    }
+                }
+            }
+        }
+
+        None
+    }
+
+    fn lookup_typo_candidate(
+        &mut self,
+        path: &[Segment],
+        following_seg: Option<&Segment>,
+        ns: Namespace,
+        filter_fn: &impl Fn(Res) -> bool,
+    ) -> TypoCandidate {
+        let mut names = Vec::new();
+        if path.len() == 1 {
+            let mut ctxt = path.last().unwrap().ident.span.ctxt();
+
+            // Search in lexical scope.
+            // Walk backwards up the ribs in scope and collect candidates.
+            for rib in self.ribs[ns].iter().rev() {
+                let rib_ctxt = if rib.kind.contains_params() {
+                    ctxt.normalize_to_macros_2_0()
+                } else {
+                    ctxt.normalize_to_macro_rules()
+                };
+
+                // Locals and type parameters
+                for (ident, &res) in &rib.bindings {
+                    if filter_fn(res) && ident.span.ctxt() == rib_ctxt {
+                        names.push(TypoSuggestion::typo_from_ident(*ident, res));
+                    }
+                }
+
+                if let RibKind::MacroDefinition(def) = rib.kind
+                    && def == self.r.macro_def(ctxt)
+                {
+                    // If an invocation of this macro created `ident`, give up on `ident`
+                    // and switch to `ident`'s source from the macro definition.
+                    ctxt.remove_mark();
+                    continue;
+                }
+
+                // Items in scope
+                if let RibKind::Module(module) = rib.kind {
+                    // Items from this module
+                    self.r.add_module_candidates(module, &mut names, &filter_fn, Some(ctxt));
+
+                    if let ModuleKind::Block = module.kind {
+                        // We can see through blocks
+                    } else {
+                        // Items from the prelude
+                        if !module.no_implicit_prelude {
+                            let extern_prelude = self.r.extern_prelude.clone();
+                            names.extend(extern_prelude.iter().flat_map(|(ident, _)| {
+                                self.r
+                                    .crate_loader(|c| c.maybe_process_path_extern(ident.name))
+                                    .and_then(|crate_id| {
+                                        let crate_mod =
+                                            Res::Def(DefKind::Mod, crate_id.as_def_id());
+
+                                        filter_fn(crate_mod).then(|| {
+                                            TypoSuggestion::typo_from_ident(*ident, crate_mod)
+                                        })
+                                    })
+                            }));
+
+                            if let Some(prelude) = self.r.prelude {
+                                self.r.add_module_candidates(prelude, &mut names, &filter_fn, None);
+                            }
+                        }
+                        break;
+                    }
+                }
+            }
+            // Add primitive types to the mix
+            if filter_fn(Res::PrimTy(PrimTy::Bool)) {
+                names.extend(PrimTy::ALL.iter().map(|prim_ty| {
+                    TypoSuggestion::typo_from_name(prim_ty.name(), Res::PrimTy(*prim_ty))
+                }))
+            }
+        } else {
+            // Search in module.
+            let mod_path = &path[..path.len() - 1];
+            if let PathResult::Module(ModuleOrUniformRoot::Module(module)) =
+                self.resolve_path(mod_path, Some(TypeNS), None)
+            {
+                self.r.add_module_candidates(module, &mut names, &filter_fn, None);
+            }
+        }
+
+        // if next_seg is present, let's filter everything that does not continue the path
+        if let Some(following_seg) = following_seg {
+            names.retain(|suggestion| match suggestion.res {
+                Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, _) => {
+                    // FIXME: this is not totally accurate, but mostly works
+                    suggestion.candidate != following_seg.ident.name
+                }
+                Res::Def(DefKind::Mod, def_id) => self.r.get_module(def_id).map_or_else(
+                    || false,
+                    |module| {
+                        self.r
+                            .resolutions(module)
+                            .borrow()
+                            .iter()
+                            .any(|(key, _)| key.ident.name == following_seg.ident.name)
+                    },
+                ),
+                _ => true,
+            });
+        }
+        let name = path[path.len() - 1].ident.name;
+        // Make sure error reporting is deterministic.
+        names.sort_by(|a, b| a.candidate.as_str().cmp(b.candidate.as_str()));
+
+        match find_best_match_for_name(
+            &names.iter().map(|suggestion| suggestion.candidate).collect::<Vec<Symbol>>(),
+            name,
+            None,
+        ) {
+            Some(found) => {
+                let Some(sugg) = names.into_iter().find(|suggestion| suggestion.candidate == found)
+                else {
+                    return TypoCandidate::None;
+                };
+                if found == name {
+                    TypoCandidate::Shadowed(sugg.res, sugg.span)
+                } else {
+                    TypoCandidate::Typo(sugg)
+                }
+            }
+            _ => TypoCandidate::None,
+        }
+    }
+
+    // Returns the name of the Rust type approximately corresponding to
+    // a type name in another programming language.
+    fn likely_rust_type(path: &[Segment]) -> Option<Symbol> {
+        let name = path[path.len() - 1].ident.as_str();
+        // Common Java types
+        Some(match name {
+            "byte" => sym::u8, // In Java, bytes are signed, but in practice one almost always wants unsigned bytes.
+            "short" => sym::i16,
+            "Bool" => sym::bool,
+            "Boolean" => sym::bool,
+            "boolean" => sym::bool,
+            "int" => sym::i32,
+            "long" => sym::i64,
+            "float" => sym::f32,
+            "double" => sym::f64,
+            _ => return None,
+        })
+    }
+
+    // try to give a suggestion for this pattern: `name = blah`, which is common in other languages
+    // suggest `let name = blah` to introduce a new binding
+    fn let_binding_suggestion(&mut self, err: &mut Diagnostic, ident_span: Span) -> bool {
+        if let Some(Expr { kind: ExprKind::Assign(lhs, ..), .. }) =
+            self.diagnostic_metadata.in_assignment
+            && let ast::ExprKind::Path(None, ref path) = lhs.kind
+        {
+            if !ident_span.from_expansion() {
+                let (span, text) = match path.segments.first() {
+                    Some(seg) if let Some(name) = seg.ident.as_str().strip_prefix("let") => {
+                        // a special case for #117894
+                        let name = name.strip_prefix('_').unwrap_or(name);
+                        (ident_span, format!("let {name}"))
+                    }
+                    _ => (ident_span.shrink_to_lo(), "let ".to_string()),
+                };
+
+                err.span_suggestion_verbose(
+                    span,
+                    "you might have meant to introduce a new binding",
+                    text,
+                    Applicability::MaybeIncorrect,
+                );
+                return true;
+            }
+        }
+        false
+    }
+
+    fn find_module(&mut self, def_id: DefId) -> Option<(Module<'a>, ImportSuggestion)> {
+        let mut result = None;
+        let mut seen_modules = FxHashSet::default();
+        let root_did = self.r.graph_root.def_id();
+        let mut worklist = vec![(
+            self.r.graph_root,
+            ThinVec::new(),
+            root_did.is_local() || !self.r.tcx.is_doc_hidden(root_did),
+        )];
+
+        while let Some((in_module, path_segments, doc_visible)) = worklist.pop() {
+            // abort if the module is already found
+            if result.is_some() {
+                break;
+            }
+
+            in_module.for_each_child(self.r, |r, ident, _, name_binding| {
+                // abort if the module is already found or if name_binding is private external
+                if result.is_some() || !name_binding.vis.is_visible_locally() {
+                    return;
+                }
+                if let Some(module) = name_binding.module() {
+                    // form the path
+                    let mut path_segments = path_segments.clone();
+                    path_segments.push(ast::PathSegment::from_ident(ident));
+                    let module_def_id = module.def_id();
+                    let doc_visible = doc_visible
+                        && (module_def_id.is_local() || !r.tcx.is_doc_hidden(module_def_id));
+                    if module_def_id == def_id {
+                        let path =
+                            Path { span: name_binding.span, segments: path_segments, tokens: None };
+                        result = Some((
+                            module,
+                            ImportSuggestion {
+                                did: Some(def_id),
+                                descr: "module",
+                                path,
+                                accessible: true,
+                                doc_visible,
+                                note: None,
+                                via_import: false,
+                            },
+                        ));
+                    } else {
+                        // add the module to the lookup
+                        if seen_modules.insert(module_def_id) {
+                            worklist.push((module, path_segments, doc_visible));
+                        }
+                    }
+                }
+            });
+        }
+
+        result
+    }
+
+    fn collect_enum_ctors(&mut self, def_id: DefId) -> Option<Vec<(Path, DefId, CtorKind)>> {
+        self.find_module(def_id).map(|(enum_module, enum_import_suggestion)| {
+            let mut variants = Vec::new();
+            enum_module.for_each_child(self.r, |_, ident, _, name_binding| {
+                if let Res::Def(DefKind::Ctor(CtorOf::Variant, kind), def_id) = name_binding.res() {
+                    let mut segms = enum_import_suggestion.path.segments.clone();
+                    segms.push(ast::PathSegment::from_ident(ident));
+                    let path = Path { span: name_binding.span, segments: segms, tokens: None };
+                    variants.push((path, def_id, kind));
+                }
+            });
+            variants
+        })
+    }
+
+    /// Adds a suggestion for using an enum's variant when an enum is used instead.
+    fn suggest_using_enum_variant(
+        &mut self,
+        err: &mut Diagnostic,
+        source: PathSource<'_>,
+        def_id: DefId,
+        span: Span,
+    ) {
+        let Some(variants) = self.collect_enum_ctors(def_id) else {
+            err.note("you might have meant to use one of the enum's variants");
+            return;
+        };
+
+        let suggest_only_tuple_variants =
+            matches!(source, PathSource::TupleStruct(..)) || source.is_call();
+        if suggest_only_tuple_variants {
+            // Suggest only tuple variants regardless of whether they have fields and do not
+            // suggest path with added parentheses.
+            let mut suggestable_variants = variants
+                .iter()
+                .filter(|(.., kind)| *kind == CtorKind::Fn)
+                .map(|(variant, ..)| path_names_to_string(variant))
+                .collect::<Vec<_>>();
+            suggestable_variants.sort();
+
+            let non_suggestable_variant_count = variants.len() - suggestable_variants.len();
+
+            let source_msg = if source.is_call() {
+                "to construct"
+            } else if matches!(source, PathSource::TupleStruct(..)) {
+                "to match against"
+            } else {
+                unreachable!()
+            };
+
+            if !suggestable_variants.is_empty() {
+                let msg = if non_suggestable_variant_count == 0 && suggestable_variants.len() == 1 {
+                    format!("try {source_msg} the enum's variant")
+                } else {
+                    format!("try {source_msg} one of the enum's variants")
+                };
+
+                err.span_suggestions(
+                    span,
+                    msg,
+                    suggestable_variants,
+                    Applicability::MaybeIncorrect,
+                );
+            }
+
+            // If the enum has no tuple variants..
+            if non_suggestable_variant_count == variants.len() {
+                err.help(format!("the enum has no tuple variants {source_msg}"));
+            }
+
+            // If there are also non-tuple variants..
+            if non_suggestable_variant_count == 1 {
+                err.help(format!("you might have meant {source_msg} the enum's non-tuple variant"));
+            } else if non_suggestable_variant_count >= 1 {
+                err.help(format!(
+                    "you might have meant {source_msg} one of the enum's non-tuple variants"
+                ));
+            }
+        } else {
+            let needs_placeholder = |ctor_def_id: DefId, kind: CtorKind| {
+                let def_id = self.r.tcx.parent(ctor_def_id);
+                match kind {
+                    CtorKind::Const => false,
+                    CtorKind::Fn => {
+                        !self.r.field_def_ids(def_id).is_some_and(|field_ids| field_ids.is_empty())
+                    }
+                }
+            };
+
+            let mut suggestable_variants = variants
+                .iter()
+                .filter(|(_, def_id, kind)| !needs_placeholder(*def_id, *kind))
+                .map(|(variant, _, kind)| (path_names_to_string(variant), kind))
+                .map(|(variant, kind)| match kind {
+                    CtorKind::Const => variant,
+                    CtorKind::Fn => format!("({variant}())"),
+                })
+                .collect::<Vec<_>>();
+            suggestable_variants.sort();
+            let no_suggestable_variant = suggestable_variants.is_empty();
+
+            if !no_suggestable_variant {
+                let msg = if suggestable_variants.len() == 1 {
+                    "you might have meant to use the following enum variant"
+                } else {
+                    "you might have meant to use one of the following enum variants"
+                };
+
+                err.span_suggestions(
+                    span,
+                    msg,
+                    suggestable_variants,
+                    Applicability::MaybeIncorrect,
+                );
+            }
+
+            let mut suggestable_variants_with_placeholders = variants
+                .iter()
+                .filter(|(_, def_id, kind)| needs_placeholder(*def_id, *kind))
+                .map(|(variant, _, kind)| (path_names_to_string(variant), kind))
+                .filter_map(|(variant, kind)| match kind {
+                    CtorKind::Fn => Some(format!("({variant}(/* fields */))")),
+                    _ => None,
+                })
+                .collect::<Vec<_>>();
+            suggestable_variants_with_placeholders.sort();
+
+            if !suggestable_variants_with_placeholders.is_empty() {
+                let msg =
+                    match (no_suggestable_variant, suggestable_variants_with_placeholders.len()) {
+                        (true, 1) => "the following enum variant is available",
+                        (true, _) => "the following enum variants are available",
+                        (false, 1) => "alternatively, the following enum variant is available",
+                        (false, _) => {
+                            "alternatively, the following enum variants are also available"
+                        }
+                    };
+
+                err.span_suggestions(
+                    span,
+                    msg,
+                    suggestable_variants_with_placeholders,
+                    Applicability::HasPlaceholders,
+                );
+            }
+        };
+
+        if def_id.is_local() {
+            err.span_note(self.r.def_span(def_id), "the enum is defined here");
+        }
+    }
+
+    pub(crate) fn suggest_adding_generic_parameter(
+        &self,
+        path: &[Segment],
+        source: PathSource<'_>,
+    ) -> Option<(Span, &'static str, String, Applicability)> {
+        let (ident, span) = match path {
+            [segment]
+                if !segment.has_generic_args
+                    && segment.ident.name != kw::SelfUpper
+                    && segment.ident.name != kw::Dyn =>
+            {
+                (segment.ident.to_string(), segment.ident.span)
+            }
+            _ => return None,
+        };
+        let mut iter = ident.chars().map(|c| c.is_uppercase());
+        let single_uppercase_char =
+            matches!(iter.next(), Some(true)) && matches!(iter.next(), None);
+        if !self.diagnostic_metadata.currently_processing_generics && !single_uppercase_char {
+            return None;
+        }
+        match (self.diagnostic_metadata.current_item, single_uppercase_char, self.diagnostic_metadata.currently_processing_generics) {
+            (Some(Item { kind: ItemKind::Fn(..), ident, .. }), _, _) if ident.name == sym::main => {
+                // Ignore `fn main()` as we don't want to suggest `fn main<T>()`
+            }
+            (
+                Some(Item {
+                    kind:
+                        kind @ ItemKind::Fn(..)
+                        | kind @ ItemKind::Enum(..)
+                        | kind @ ItemKind::Struct(..)
+                        | kind @ ItemKind::Union(..),
+                    ..
+                }),
+                true, _
+            )
+            // Without the 2nd `true`, we'd suggest `impl <T>` for `impl T` when a type `T` isn't found
+            | (Some(Item { kind: kind @ ItemKind::Impl(..), .. }), true, true)
+            | (Some(Item { kind, .. }), false, _) => {
+                if let Some(generics) = kind.generics() {
+                    if span.overlaps(generics.span) {
+                        // Avoid the following:
+                        // error[E0405]: cannot find trait `A` in this scope
+                        //  --> $DIR/typo-suggestion-named-underscore.rs:CC:LL
+                        //   |
+                        // L | fn foo<T: A>(x: T) {} // Shouldn't suggest underscore
+                        //   |           ^- help: you might be missing a type parameter: `, A`
+                        //   |           |
+                        //   |           not found in this scope
+                        return None;
+                    }
+
+                    let (msg, sugg) = match source {
+                        PathSource::Type => ("you might be missing a type parameter", ident),
+                        PathSource::Expr(_) => ("you might be missing a const parameter", format!("const {ident}: /* Type */")),
+                        _ => return None,
+                    };
+                    let (span, sugg) = if let [.., param] = &generics.params[..] {
+                        let span = if let [.., bound] = &param.bounds[..] {
+                            bound.span()
+                        } else if let GenericParam {
+                            kind: GenericParamKind::Const { ty, kw_span: _, default  }, ..
+                        } = param {
+                            default.as_ref().map(|def| def.value.span).unwrap_or(ty.span)
+                        } else {
+                            param.ident.span
+                        };
+                        (span, format!(", {sugg}"))
+                    } else {
+                        (generics.span, format!("<{sugg}>"))
+                    };
+                    // Do not suggest if this is coming from macro expansion.
+                    if span.can_be_used_for_suggestions() {
+                        return Some((
+                            span.shrink_to_hi(),
+                            msg,
+                            sugg,
+                            Applicability::MaybeIncorrect,
+                        ));
+                    }
+                }
+            }
+            _ => {}
+        }
+        None
+    }
+
+    /// Given the target `label`, search the `rib_index`th label rib for similarly named labels,
+    /// optionally returning the closest match and whether it is reachable.
+    pub(crate) fn suggestion_for_label_in_rib(
+        &self,
+        rib_index: usize,
+        label: Ident,
+    ) -> Option<LabelSuggestion> {
+        // Are ribs from this `rib_index` within scope?
+        let within_scope = self.is_label_valid_from_rib(rib_index);
+
+        let rib = &self.label_ribs[rib_index];
+        let names = rib
+            .bindings
+            .iter()
+            .filter(|(id, _)| id.span.eq_ctxt(label.span))
+            .map(|(id, _)| id.name)
+            .collect::<Vec<Symbol>>();
+
+        find_best_match_for_name(&names, label.name, None).map(|symbol| {
+            // Upon finding a similar name, get the ident that it was from - the span
+            // contained within helps make a useful diagnostic. In addition, determine
+            // whether this candidate is within scope.
+            let (ident, _) = rib.bindings.iter().find(|(ident, _)| ident.name == symbol).unwrap();
+            (*ident, within_scope)
+        })
+    }
+
+    pub(crate) fn maybe_report_lifetime_uses(
+        &mut self,
+        generics_span: Span,
+        params: &[ast::GenericParam],
+    ) {
+        for (param_index, param) in params.iter().enumerate() {
+            let GenericParamKind::Lifetime = param.kind else { continue };
+
+            let def_id = self.r.local_def_id(param.id);
+
+            let use_set = self.lifetime_uses.remove(&def_id);
+            debug!(
+                "Use set for {:?}({:?} at {:?}) is {:?}",
+                def_id, param.ident, param.ident.span, use_set
+            );
+
+            let deletion_span = || {
+                if params.len() == 1 {
+                    // if sole lifetime, remove the entire `<>` brackets
+                    Some(generics_span)
+                } else if param_index == 0 {
+                    // if removing within `<>` brackets, we also want to
+                    // delete a leading or trailing comma as appropriate
+                    match (
+                        param.span().find_ancestor_inside(generics_span),
+                        params[param_index + 1].span().find_ancestor_inside(generics_span),
+                    ) {
+                        (Some(param_span), Some(next_param_span)) => {
+                            Some(param_span.to(next_param_span.shrink_to_lo()))
+                        }
+                        _ => None,
+                    }
+                } else {
+                    // if removing within `<>` brackets, we also want to
+                    // delete a leading or trailing comma as appropriate
+                    match (
+                        param.span().find_ancestor_inside(generics_span),
+                        params[param_index - 1].span().find_ancestor_inside(generics_span),
+                    ) {
+                        (Some(param_span), Some(prev_param_span)) => {
+                            Some(prev_param_span.shrink_to_hi().to(param_span))
+                        }
+                        _ => None,
+                    }
+                }
+            };
+            match use_set {
+                Some(LifetimeUseSet::Many) => {}
+                Some(LifetimeUseSet::One { use_span, use_ctxt }) => {
+                    debug!(?param.ident, ?param.ident.span, ?use_span);
+
+                    let elidable = matches!(use_ctxt, LifetimeCtxt::Ref);
+
+                    let deletion_span = deletion_span();
+                    self.r.lint_buffer.buffer_lint_with_diagnostic(
+                        lint::builtin::SINGLE_USE_LIFETIMES,
+                        param.id,
+                        param.ident.span,
+                        format!("lifetime parameter `{}` only used once", param.ident),
+                        lint::BuiltinLintDiagnostics::SingleUseLifetime {
+                            param_span: param.ident.span,
+                            use_span: Some((use_span, elidable)),
+                            deletion_span,
+                        },
+                    );
+                }
+                None => {
+                    debug!(?param.ident, ?param.ident.span);
+                    let deletion_span = deletion_span();
+
+                    // if the lifetime originates from expanded code, we won't be able to remove it #104432
+                    if deletion_span.is_some_and(|sp| !sp.in_derive_expansion()) {
+                        self.r.lint_buffer.buffer_lint_with_diagnostic(
+                            lint::builtin::UNUSED_LIFETIMES,
+                            param.id,
+                            param.ident.span,
+                            format!("lifetime parameter `{}` never used", param.ident),
+                            lint::BuiltinLintDiagnostics::SingleUseLifetime {
+                                param_span: param.ident.span,
+                                use_span: None,
+                                deletion_span,
+                            },
+                        );
+                    }
+                }
+            }
+        }
+    }
+
+    pub(crate) fn emit_undeclared_lifetime_error(
+        &self,
+        lifetime_ref: &ast::Lifetime,
+        outer_lifetime_ref: Option<Ident>,
+    ) {
+        debug_assert_ne!(lifetime_ref.ident.name, kw::UnderscoreLifetime);
+        let mut err = if let Some(outer) = outer_lifetime_ref {
+            struct_span_code_err!(
+                self.r.dcx(),
+                lifetime_ref.ident.span,
+                E0401,
+                "can't use generic parameters from outer item",
+            )
+            .with_span_label(lifetime_ref.ident.span, "use of generic parameter from outer item")
+            .with_span_label(outer.span, "lifetime parameter from outer item")
+        } else {
+            struct_span_code_err!(
+                self.r.dcx(),
+                lifetime_ref.ident.span,
+                E0261,
+                "use of undeclared lifetime name `{}`",
+                lifetime_ref.ident
+            )
+            .with_span_label(lifetime_ref.ident.span, "undeclared lifetime")
+        };
+        self.suggest_introducing_lifetime(
+            &mut err,
+            Some(lifetime_ref.ident.name.as_str()),
+            |err, _, span, message, suggestion| {
+                err.span_suggestion(span, message, suggestion, Applicability::MaybeIncorrect);
+                true
+            },
+        );
+        err.emit();
+    }
+
+    fn suggest_introducing_lifetime(
+        &self,
+        err: &mut Diagnostic,
+        name: Option<&str>,
+        suggest: impl Fn(&mut Diagnostic, bool, Span, Cow<'static, str>, String) -> bool,
+    ) {
+        let mut suggest_note = true;
+        for rib in self.lifetime_ribs.iter().rev() {
+            let mut should_continue = true;
+            match rib.kind {
+                LifetimeRibKind::Generics { binder: _, span, kind } => {
+                    // Avoid suggesting placing lifetime parameters on constant items unless the relevant
+                    // feature is enabled. Suggest the parent item as a possible location if applicable.
+                    if let LifetimeBinderKind::ConstItem = kind
+                        && !self.r.tcx().features().generic_const_items
+                    {
+                        continue;
+                    }
+
+                    if !span.can_be_used_for_suggestions()
+                        && suggest_note
+                        && let Some(name) = name
+                    {
+                        suggest_note = false; // Avoid displaying the same help multiple times.
+                        err.span_label(
+                            span,
+                            format!(
+                                "lifetime `{name}` is missing in item created through this procedural macro",
+                            ),
+                        );
+                        continue;
+                    }
+
+                    let higher_ranked = matches!(
+                        kind,
+                        LifetimeBinderKind::BareFnType
+                            | LifetimeBinderKind::PolyTrait
+                            | LifetimeBinderKind::WhereBound
+                    );
+                    let (span, sugg) = if span.is_empty() {
+                        let sugg = format!(
+                            "{}<{}>{}",
+                            if higher_ranked { "for" } else { "" },
+                            name.unwrap_or("'a"),
+                            if higher_ranked { " " } else { "" },
+                        );
+                        (span, sugg)
+                    } else {
+                        let span = self
+                            .r
+                            .tcx
+                            .sess
+                            .source_map()
+                            .span_through_char(span, '<')
+                            .shrink_to_hi();
+                        let sugg = format!("{}, ", name.unwrap_or("'a"));
+                        (span, sugg)
+                    };
+                    if higher_ranked {
+                        let message = Cow::from(format!(
+                            "consider making the {} lifetime-generic with a new `{}` lifetime",
+                            kind.descr(),
+                            name.unwrap_or("'a"),
+                        ));
+                        should_continue = suggest(err, true, span, message, sugg);
+                        err.note_once(
+                            "for more information on higher-ranked polymorphism, visit \
+                             https://doc.rust-lang.org/nomicon/hrtb.html",
+                        );
+                    } else if let Some(name) = name {
+                        let message =
+                            Cow::from(format!("consider introducing lifetime `{name}` here"));
+                        should_continue = suggest(err, false, span, message, sugg);
+                    } else {
+                        let message = Cow::from("consider introducing a named lifetime parameter");
+                        should_continue = suggest(err, false, span, message, sugg);
+                    }
+                }
+                LifetimeRibKind::Item | LifetimeRibKind::ConstParamTy => break,
+                _ => {}
+            }
+            if !should_continue {
+                break;
+            }
+        }
+    }
+
+    pub(crate) fn emit_non_static_lt_in_const_param_ty_error(&self, lifetime_ref: &ast::Lifetime) {
+        self.r
+            .dcx()
+            .create_err(errors::ParamInTyOfConstParam {
+                span: lifetime_ref.ident.span,
+                name: lifetime_ref.ident.name,
+                param_kind: Some(errors::ParamKindInTyOfConstParam::Lifetime),
+            })
+            .emit();
+    }
+
+    /// Non-static lifetimes are prohibited in anonymous constants under `min_const_generics`.
+    /// This function will emit an error if `generic_const_exprs` is not enabled, the body identified by
+    /// `body_id` is an anonymous constant and `lifetime_ref` is non-static.
+    pub(crate) fn emit_forbidden_non_static_lifetime_error(
+        &self,
+        cause: NoConstantGenericsReason,
+        lifetime_ref: &ast::Lifetime,
+    ) {
+        match cause {
+            NoConstantGenericsReason::IsEnumDiscriminant => {
+                self.r
+                    .dcx()
+                    .create_err(errors::ParamInEnumDiscriminant {
+                        span: lifetime_ref.ident.span,
+                        name: lifetime_ref.ident.name,
+                        param_kind: errors::ParamKindInEnumDiscriminant::Lifetime,
+                    })
+                    .emit();
+            }
+            NoConstantGenericsReason::NonTrivialConstArg => {
+                assert!(!self.r.tcx.features().generic_const_exprs);
+                self.r
+                    .dcx()
+                    .create_err(errors::ParamInNonTrivialAnonConst {
+                        span: lifetime_ref.ident.span,
+                        name: lifetime_ref.ident.name,
+                        param_kind: errors::ParamKindInNonTrivialAnonConst::Lifetime,
+                        help: self
+                            .r
+                            .tcx
+                            .sess
+                            .is_nightly_build()
+                            .then_some(errors::ParamInNonTrivialAnonConstHelp),
+                    })
+                    .emit();
+            }
+        }
+    }
+
+    pub(crate) fn report_missing_lifetime_specifiers(
+        &mut self,
+        lifetime_refs: Vec<MissingLifetime>,
+        function_param_lifetimes: Option<(Vec<MissingLifetime>, Vec<ElisionFnParameter>)>,
+    ) -> ErrorGuaranteed {
+        let num_lifetimes: usize = lifetime_refs.iter().map(|lt| lt.count).sum();
+        let spans: Vec<_> = lifetime_refs.iter().map(|lt| lt.span).collect();
+
+        let mut err = struct_span_code_err!(
+            self.r.dcx(),
+            spans,
+            E0106,
+            "missing lifetime specifier{}",
+            pluralize!(num_lifetimes)
+        );
+        self.add_missing_lifetime_specifiers_label(
+            &mut err,
+            lifetime_refs,
+            function_param_lifetimes,
+        );
+        err.emit()
+    }
+
+    fn add_missing_lifetime_specifiers_label(
+        &mut self,
+        err: &mut Diagnostic,
+        lifetime_refs: Vec<MissingLifetime>,
+        function_param_lifetimes: Option<(Vec<MissingLifetime>, Vec<ElisionFnParameter>)>,
+    ) {
+        for &lt in &lifetime_refs {
+            err.span_label(
+                lt.span,
+                format!(
+                    "expected {} lifetime parameter{}",
+                    if lt.count == 1 { "named".to_string() } else { lt.count.to_string() },
+                    pluralize!(lt.count),
+                ),
+            );
+        }
+
+        let mut in_scope_lifetimes: Vec<_> = self
+            .lifetime_ribs
+            .iter()
+            .rev()
+            .take_while(|rib| {
+                !matches!(rib.kind, LifetimeRibKind::Item | LifetimeRibKind::ConstParamTy)
+            })
+            .flat_map(|rib| rib.bindings.iter())
+            .map(|(&ident, &res)| (ident, res))
+            .filter(|(ident, _)| ident.name != kw::UnderscoreLifetime)
+            .collect();
+        debug!(?in_scope_lifetimes);
+
+        let mut maybe_static = false;
+        debug!(?function_param_lifetimes);
+        if let Some((param_lifetimes, params)) = &function_param_lifetimes {
+            let elided_len = param_lifetimes.len();
+            let num_params = params.len();
+
+            let mut m = String::new();
+
+            for (i, info) in params.iter().enumerate() {
+                let ElisionFnParameter { ident, index, lifetime_count, span } = *info;
+                debug_assert_ne!(lifetime_count, 0);
+
+                err.span_label(span, "");
+
+                if i != 0 {
+                    if i + 1 < num_params {
+                        m.push_str(", ");
+                    } else if num_params == 2 {
+                        m.push_str(" or ");
+                    } else {
+                        m.push_str(", or ");
+                    }
+                }
+
+                let help_name = if let Some(ident) = ident {
+                    format!("`{ident}`")
+                } else {
+                    format!("argument {}", index + 1)
+                };
+
+                if lifetime_count == 1 {
+                    m.push_str(&help_name[..])
+                } else {
+                    m.push_str(&format!("one of {help_name}'s {lifetime_count} lifetimes")[..])
+                }
+            }
+
+            if num_params == 0 {
+                err.help(
+                    "this function's return type contains a borrowed value, but there is no value \
+                     for it to be borrowed from",
+                );
+                if in_scope_lifetimes.is_empty() {
+                    maybe_static = true;
+                    in_scope_lifetimes = vec![(
+                        Ident::with_dummy_span(kw::StaticLifetime),
+                        (DUMMY_NODE_ID, LifetimeRes::Static),
+                    )];
+                }
+            } else if elided_len == 0 {
+                err.help(
+                    "this function's return type contains a borrowed value with an elided \
+                     lifetime, but the lifetime cannot be derived from the arguments",
+                );
+                if in_scope_lifetimes.is_empty() {
+                    maybe_static = true;
+                    in_scope_lifetimes = vec![(
+                        Ident::with_dummy_span(kw::StaticLifetime),
+                        (DUMMY_NODE_ID, LifetimeRes::Static),
+                    )];
+                }
+            } else if num_params == 1 {
+                err.help(format!(
+                    "this function's return type contains a borrowed value, but the signature does \
+                     not say which {m} it is borrowed from",
+                ));
+            } else {
+                err.help(format!(
+                    "this function's return type contains a borrowed value, but the signature does \
+                     not say whether it is borrowed from {m}",
+                ));
+            }
+        }
+
+        let existing_name = match &in_scope_lifetimes[..] {
+            [] => Symbol::intern("'a"),
+            [(existing, _)] => existing.name,
+            _ => Symbol::intern("'lifetime"),
+        };
+
+        let mut spans_suggs: Vec<_> = Vec::new();
+        let build_sugg = |lt: MissingLifetime| match lt.kind {
+            MissingLifetimeKind::Underscore => {
+                debug_assert_eq!(lt.count, 1);
+                (lt.span, existing_name.to_string())
+            }
+            MissingLifetimeKind::Ampersand => {
+                debug_assert_eq!(lt.count, 1);
+                (lt.span.shrink_to_hi(), format!("{existing_name} "))
+            }
+            MissingLifetimeKind::Comma => {
+                let sugg: String = std::iter::repeat([existing_name.as_str(), ", "])
+                    .take(lt.count)
+                    .flatten()
+                    .collect();
+                (lt.span.shrink_to_hi(), sugg)
+            }
+            MissingLifetimeKind::Brackets => {
+                let sugg: String = std::iter::once("<")
+                    .chain(
+                        std::iter::repeat(existing_name.as_str()).take(lt.count).intersperse(", "),
+                    )
+                    .chain([">"])
+                    .collect();
+                (lt.span.shrink_to_hi(), sugg)
+            }
+        };
+        for &lt in &lifetime_refs {
+            spans_suggs.push(build_sugg(lt));
+        }
+        debug!(?spans_suggs);
+        match in_scope_lifetimes.len() {
+            0 => {
+                if let Some((param_lifetimes, _)) = function_param_lifetimes {
+                    for lt in param_lifetimes {
+                        spans_suggs.push(build_sugg(lt))
+                    }
+                }
+                self.suggest_introducing_lifetime(
+                    err,
+                    None,
+                    |err, higher_ranked, span, message, intro_sugg| {
+                        err.multipart_suggestion_verbose(
+                            message,
+                            std::iter::once((span, intro_sugg))
+                                .chain(spans_suggs.iter().cloned())
+                                .collect(),
+                            Applicability::MaybeIncorrect,
+                        );
+                        higher_ranked
+                    },
+                );
+            }
+            1 => {
+                let post = if maybe_static {
+                    let owned = if let [lt] = &lifetime_refs[..]
+                        && lt.kind != MissingLifetimeKind::Ampersand
+                    {
+                        ", or if you will only have owned values"
+                    } else {
+                        ""
+                    };
+                    format!(
+                        ", but this is uncommon unless you're returning a borrowed value from a \
+                         `const` or a `static`{owned}",
+                    )
+                } else {
+                    String::new()
+                };
+                err.multipart_suggestion_verbose(
+                    format!("consider using the `{existing_name}` lifetime{post}"),
+                    spans_suggs,
+                    Applicability::MaybeIncorrect,
+                );
+                if maybe_static {
+                    // FIXME: what follows are general suggestions, but we'd want to perform some
+                    // minimal flow analysis to provide more accurate suggestions. For example, if
+                    // we identified that the return expression references only one argument, we
+                    // would suggest borrowing only that argument, and we'd skip the prior
+                    // "use `'static`" suggestion entirely.
+                    if let [lt] = &lifetime_refs[..]
+                        && (lt.kind == MissingLifetimeKind::Ampersand
+                            || lt.kind == MissingLifetimeKind::Underscore)
+                    {
+                        let pre = if lt.kind == MissingLifetimeKind::Ampersand
+                            && let Some((kind, _span)) = self.diagnostic_metadata.current_function
+                            && let FnKind::Fn(_, _, sig, _, _, _) = kind
+                            && !sig.decl.inputs.is_empty()
+                            && let sugg = sig
+                                .decl
+                                .inputs
+                                .iter()
+                                .filter_map(|param| {
+                                    if param.ty.span.contains(lt.span) {
+                                        // We don't want to suggest `fn elision(_: &fn() -> &i32)`
+                                        // when we have `fn elision(_: fn() -> &i32)`
+                                        None
+                                    } else if let TyKind::CVarArgs = param.ty.kind {
+                                        // Don't suggest `&...` for ffi fn with varargs
+                                        None
+                                    } else if let TyKind::ImplTrait(..) = &param.ty.kind {
+                                        // We handle these in the next `else if` branch.
+                                        None
+                                    } else {
+                                        Some((param.ty.span.shrink_to_lo(), "&".to_string()))
+                                    }
+                                })
+                                .collect::<Vec<_>>()
+                            && !sugg.is_empty()
+                        {
+                            let (the, s) = if sig.decl.inputs.len() == 1 {
+                                ("the", "")
+                            } else {
+                                ("one of the", "s")
+                            };
+                            err.multipart_suggestion_verbose(
+                                format!(
+                                    "instead, you are more likely to want to change {the} \
+                                     argument{s} to be borrowed...",
+                                ),
+                                sugg,
+                                Applicability::MaybeIncorrect,
+                            );
+                            "...or alternatively, you might want"
+                        } else if (lt.kind == MissingLifetimeKind::Ampersand
+                            || lt.kind == MissingLifetimeKind::Underscore)
+                            && let Some((kind, _span)) = self.diagnostic_metadata.current_function
+                            && let FnKind::Fn(_, _, sig, _, _, _) = kind
+                            && let ast::FnRetTy::Ty(ret_ty) = &sig.decl.output
+                            && !sig.decl.inputs.is_empty()
+                            && let arg_refs = sig
+                                .decl
+                                .inputs
+                                .iter()
+                                .filter_map(|param| match &param.ty.kind {
+                                    TyKind::ImplTrait(_, bounds) => Some(bounds),
+                                    _ => None,
+                                })
+                                .flat_map(|bounds| bounds.into_iter())
+                                .collect::<Vec<_>>()
+                            && !arg_refs.is_empty()
+                        {
+                            // We have a situation like
+                            // fn g(mut x: impl Iterator<Item = &()>) -> Option<&()>
+                            // So we look at every ref in the trait bound. If there's any, we
+                            // suggest
+                            // fn g<'a>(mut x: impl Iterator<Item = &'a ()>) -> Option<&'a ()>
+                            let mut lt_finder =
+                                LifetimeFinder { lifetime: lt.span, found: None, seen: vec![] };
+                            for bound in arg_refs {
+                                if let ast::GenericBound::Trait(trait_ref, _) = bound {
+                                    lt_finder.visit_trait_ref(&trait_ref.trait_ref);
+                                }
+                            }
+                            lt_finder.visit_ty(ret_ty);
+                            let spans_suggs: Vec<_> = lt_finder
+                                .seen
+                                .iter()
+                                .filter_map(|ty| match &ty.kind {
+                                    TyKind::Ref(_, mut_ty) => {
+                                        let span = ty.span.with_hi(mut_ty.ty.span.lo());
+                                        Some((span, "&'a ".to_string()))
+                                    }
+                                    _ => None,
+                                })
+                                .collect();
+                            self.suggest_introducing_lifetime(
+                                err,
+                                None,
+                                |err, higher_ranked, span, message, intro_sugg| {
+                                    err.multipart_suggestion_verbose(
+                                        message,
+                                        std::iter::once((span, intro_sugg))
+                                            .chain(spans_suggs.iter().cloned())
+                                            .collect(),
+                                        Applicability::MaybeIncorrect,
+                                    );
+                                    higher_ranked
+                                },
+                            );
+                            "alternatively, you might want"
+                        } else {
+                            "instead, you are more likely to want"
+                        };
+                        let mut owned_sugg = lt.kind == MissingLifetimeKind::Ampersand;
+                        let mut sugg = vec![(lt.span, String::new())];
+                        if let Some((kind, _span)) = self.diagnostic_metadata.current_function
+                            && let FnKind::Fn(_, _, sig, _, _, _) = kind
+                            && let ast::FnRetTy::Ty(ty) = &sig.decl.output
+                        {
+                            let mut lt_finder =
+                                LifetimeFinder { lifetime: lt.span, found: None, seen: vec![] };
+                            lt_finder.visit_ty(&ty);
+
+                            if let [Ty { span, kind: TyKind::Ref(_, mut_ty), .. }] =
+                                &lt_finder.seen[..]
+                            {
+                                // We might have a situation like
+                                // fn g(mut x: impl Iterator<Item = &'_ ()>) -> Option<&'_ ()>
+                                // but `lt.span` only points at `'_`, so to suggest `-> Option<()>`
+                                // we need to find a more accurate span to end up with
+                                // fn g<'a>(mut x: impl Iterator<Item = &'_ ()>) -> Option<()>
+                                sugg = vec![(span.with_hi(mut_ty.ty.span.lo()), String::new())];
+                                owned_sugg = true;
+                            }
+                            if let Some(ty) = lt_finder.found {
+                                if let TyKind::Path(None, path) = &ty.kind {
+                                    // Check if the path being borrowed is likely to be owned.
+                                    let path: Vec<_> = Segment::from_path(path);
+                                    match self.resolve_path(&path, Some(TypeNS), None) {
+                                        PathResult::Module(ModuleOrUniformRoot::Module(module)) => {
+                                            match module.res() {
+                                                Some(Res::PrimTy(PrimTy::Str)) => {
+                                                    // Don't suggest `-> str`, suggest `-> String`.
+                                                    sugg = vec![(
+                                                        lt.span.with_hi(ty.span.hi()),
+                                                        "String".to_string(),
+                                                    )];
+                                                }
+                                                Some(Res::PrimTy(..)) => {}
+                                                Some(Res::Def(
+                                                    DefKind::Struct
+                                                    | DefKind::Union
+                                                    | DefKind::Enum
+                                                    | DefKind::ForeignTy
+                                                    | DefKind::AssocTy
+                                                    | DefKind::OpaqueTy
+                                                    | DefKind::TyParam,
+                                                    _,
+                                                )) => {}
+                                                _ => {
+                                                    // Do not suggest in all other cases.
+                                                    owned_sugg = false;
+                                                }
+                                            }
+                                        }
+                                        PathResult::NonModule(res) => {
+                                            match res.base_res() {
+                                                Res::PrimTy(PrimTy::Str) => {
+                                                    // Don't suggest `-> str`, suggest `-> String`.
+                                                    sugg = vec![(
+                                                        lt.span.with_hi(ty.span.hi()),
+                                                        "String".to_string(),
+                                                    )];
+                                                }
+                                                Res::PrimTy(..) => {}
+                                                Res::Def(
+                                                    DefKind::Struct
+                                                    | DefKind::Union
+                                                    | DefKind::Enum
+                                                    | DefKind::ForeignTy
+                                                    | DefKind::AssocTy
+                                                    | DefKind::OpaqueTy
+                                                    | DefKind::TyParam,
+                                                    _,
+                                                ) => {}
+                                                _ => {
+                                                    // Do not suggest in all other cases.
+                                                    owned_sugg = false;
+                                                }
+                                            }
+                                        }
+                                        _ => {
+                                            // Do not suggest in all other cases.
+                                            owned_sugg = false;
+                                        }
+                                    }
+                                }
+                                if let TyKind::Slice(inner_ty) = &ty.kind {
+                                    // Don't suggest `-> [T]`, suggest `-> Vec<T>`.
+                                    sugg = vec![
+                                        (lt.span.with_hi(inner_ty.span.lo()), "Vec<".to_string()),
+                                        (ty.span.with_lo(inner_ty.span.hi()), ">".to_string()),
+                                    ];
+                                }
+                            }
+                        }
+                        if owned_sugg {
+                            err.multipart_suggestion_verbose(
+                                format!("{pre} to return an owned value"),
+                                sugg,
+                                Applicability::MaybeIncorrect,
+                            );
+                        }
+                    }
+                }
+
+                // Record as using the suggested resolution.
+                let (_, (_, res)) = in_scope_lifetimes[0];
+                for &lt in &lifetime_refs {
+                    self.r.lifetimes_res_map.insert(lt.id, res);
+                }
+            }
+            _ => {
+                let lifetime_spans: Vec<_> =
+                    in_scope_lifetimes.iter().map(|(ident, _)| ident.span).collect();
+                err.span_note(lifetime_spans, "these named lifetimes are available to use");
+
+                if spans_suggs.len() > 0 {
+                    // This happens when we have `Foo<T>` where we point at the space before `T`,
+                    // but this can be confusing so we give a suggestion with placeholders.
+                    err.multipart_suggestion_verbose(
+                        "consider using one of the available lifetimes here",
+                        spans_suggs,
+                        Applicability::HasPlaceholders,
+                    );
+                }
+            }
+        }
+    }
+}
+
+fn mk_where_bound_predicate(
+    path: &Path,
+    poly_trait_ref: &ast::PolyTraitRef,
+    ty: &Ty,
+) -> Option<ast::WhereBoundPredicate> {
+    use rustc_span::DUMMY_SP;
+    let modified_segments = {
+        let mut segments = path.segments.clone();
+        let [preceding @ .., second_last, last] = segments.as_mut_slice() else {
+            return None;
+        };
+        let mut segments = ThinVec::from(preceding);
+
+        let added_constraint = ast::AngleBracketedArg::Constraint(ast::AssocConstraint {
+            id: DUMMY_NODE_ID,
+            ident: last.ident,
+            gen_args: None,
+            kind: ast::AssocConstraintKind::Equality {
+                term: ast::Term::Ty(ast::ptr::P(ast::Ty {
+                    kind: ast::TyKind::Path(None, poly_trait_ref.trait_ref.path.clone()),
+                    id: DUMMY_NODE_ID,
+                    span: DUMMY_SP,
+                    tokens: None,
+                })),
+            },
+            span: DUMMY_SP,
+        });
+
+        match second_last.args.as_deref_mut() {
+            Some(ast::GenericArgs::AngleBracketed(ast::AngleBracketedArgs { args, .. })) => {
+                args.push(added_constraint);
+            }
+            Some(_) => return None,
+            None => {
+                second_last.args =
+                    Some(ast::ptr::P(ast::GenericArgs::AngleBracketed(ast::AngleBracketedArgs {
+                        args: ThinVec::from([added_constraint]),
+                        span: DUMMY_SP,
+                    })));
+            }
+        }
+
+        segments.push(second_last.clone());
+        segments
+    };
+
+    let new_where_bound_predicate = ast::WhereBoundPredicate {
+        span: DUMMY_SP,
+        bound_generic_params: ThinVec::new(),
+        bounded_ty: ast::ptr::P(ty.clone()),
+        bounds: vec![ast::GenericBound::Trait(
+            ast::PolyTraitRef {
+                bound_generic_params: ThinVec::new(),
+                trait_ref: ast::TraitRef {
+                    path: ast::Path { segments: modified_segments, span: DUMMY_SP, tokens: None },
+                    ref_id: DUMMY_NODE_ID,
+                },
+                span: DUMMY_SP,
+            },
+            ast::TraitBoundModifiers::NONE,
+        )],
+    };
+
+    Some(new_where_bound_predicate)
+}
+
+/// Report lifetime/lifetime shadowing as an error.
+pub(super) fn signal_lifetime_shadowing(sess: &Session, orig: Ident, shadower: Ident) {
+    struct_span_code_err!(
+        sess.dcx(),
+        shadower.span,
+        E0496,
+        "lifetime name `{}` shadows a lifetime name that is already in scope",
+        orig.name,
+    )
+    .with_span_label(orig.span, "first declared here")
+    .with_span_label(shadower.span, format!("lifetime `{}` already in scope", orig.name))
+    .emit();
+}
+
+struct LifetimeFinder<'ast> {
+    lifetime: Span,
+    found: Option<&'ast Ty>,
+    seen: Vec<&'ast Ty>,
+}
+
+impl<'ast> Visitor<'ast> for LifetimeFinder<'ast> {
+    fn visit_ty(&mut self, t: &'ast Ty) {
+        if let TyKind::Ref(_, mut_ty) = &t.kind {
+            self.seen.push(t);
+            if t.span.lo() == self.lifetime.lo() {
+                self.found = Some(&mut_ty.ty);
+            }
+        }
+        walk_ty(self, t)
+    }
+}
+
+/// Shadowing involving a label is only a warning for historical reasons.
+//FIXME: make this a proper lint.
+pub(super) fn signal_label_shadowing(sess: &Session, orig: Span, shadower: Ident) {
+    let name = shadower.name;
+    let shadower = shadower.span;
+    sess.dcx()
+        .struct_span_warn(
+            shadower,
+            format!("label name `{name}` shadows a label name that is already in scope"),
+        )
+        .with_span_label(orig, "first declared here")
+        .with_span_label(shadower, format!("label `{name}` already in scope"))
+        .emit();
+}
diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs
new file mode 100644
index 00000000000..90aa7d79bf0
--- /dev/null
+++ b/compiler/rustc_resolve/src/lib.rs
@@ -0,0 +1,2175 @@
+//! This crate is responsible for the part of name resolution that doesn't require type checker.
+//!
+//! Module structure of the crate is built here.
+//! Paths in macros, imports, expressions, types, patterns are resolved here.
+//! Label and lifetime names are resolved here as well.
+//!
+//! Type-relative name resolution (methods, fields, associated items) happens in `rustc_hir_analysis`.
+
+#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
+#![doc(rust_logo)]
+#![feature(rustdoc_internals)]
+#![feature(assert_matches)]
+#![feature(box_patterns)]
+#![feature(extract_if)]
+#![feature(if_let_guard)]
+#![feature(iter_intersperse)]
+#![feature(let_chains)]
+#![feature(never_type)]
+#![feature(rustc_attrs)]
+#![recursion_limit = "256"]
+#![allow(rustdoc::private_intra_doc_links)]
+#![allow(rustc::potential_query_instability)]
+#![allow(internal_features)]
+
+#[macro_use]
+extern crate tracing;
+
+use errors::{
+    ParamKindInEnumDiscriminant, ParamKindInNonTrivialAnonConst, ParamKindInTyOfConstParam,
+};
+use rustc_arena::{DroplessArena, TypedArena};
+use rustc_ast::expand::StrippedCfgItem;
+use rustc_ast::node_id::NodeMap;
+use rustc_ast::{self as ast, attr, NodeId, CRATE_NODE_ID};
+use rustc_ast::{AngleBracketedArg, Crate, Expr, ExprKind, GenericArg, GenericArgs, LitKind, Path};
+use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet};
+use rustc_data_structures::intern::Interned;
+use rustc_data_structures::steal::Steal;
+use rustc_data_structures::sync::{FreezeReadGuard, Lrc};
+use rustc_errors::{Applicability, DiagnosticBuilder};
+use rustc_expand::base::{DeriveResolutions, SyntaxExtension, SyntaxExtensionKind};
+use rustc_feature::BUILTIN_ATTRIBUTES;
+use rustc_hir::def::Namespace::{self, *};
+use rustc_hir::def::NonMacroAttrKind;
+use rustc_hir::def::{self, CtorOf, DefKind, DocLinkResMap, LifetimeRes, PartialRes, PerNS};
+use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LocalDefIdMap, LocalDefIdSet};
+use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE};
+use rustc_hir::{PrimTy, TraitCandidate};
+use rustc_index::IndexVec;
+use rustc_metadata::creader::{CStore, CrateLoader};
+use rustc_middle::metadata::ModChild;
+use rustc_middle::middle::privacy::EffectiveVisibilities;
+use rustc_middle::query::Providers;
+use rustc_middle::span_bug;
+use rustc_middle::ty::{self, MainDefinition, RegisteredTools, TyCtxt};
+use rustc_middle::ty::{ResolverGlobalCtxt, ResolverOutputs};
+use rustc_query_system::ich::StableHashingContext;
+use rustc_session::lint::LintBuffer;
+use rustc_span::hygiene::{ExpnId, LocalExpnId, MacroKind, SyntaxContext, Transparency};
+use rustc_span::symbol::{kw, sym, Ident, Symbol};
+use rustc_span::{Span, DUMMY_SP};
+
+use smallvec::{smallvec, SmallVec};
+use std::cell::{Cell, RefCell};
+use std::collections::BTreeSet;
+use std::fmt;
+
+use diagnostics::{ImportSuggestion, LabelSuggestion, Suggestion};
+use imports::{Import, ImportData, ImportKind, NameResolution};
+use late::{HasGenericParams, PathSource, PatternSource};
+use macros::{MacroRulesBinding, MacroRulesScope, MacroRulesScopeRef};
+
+use crate::effective_visibilities::EffectiveVisibilitiesVisitor;
+
+type Res = def::Res<NodeId>;
+
+mod build_reduced_graph;
+mod check_unused;
+mod def_collector;
+mod diagnostics;
+mod effective_visibilities;
+mod errors;
+mod ident;
+mod imports;
+mod late;
+mod macros;
+pub mod rustdoc;
+
+rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
+
+#[derive(Debug)]
+enum Weak {
+    Yes,
+    No,
+}
+
+#[derive(Copy, Clone, PartialEq, Debug)]
+enum Determinacy {
+    Determined,
+    Undetermined,
+}
+
+impl Determinacy {
+    fn determined(determined: bool) -> Determinacy {
+        if determined { Determinacy::Determined } else { Determinacy::Undetermined }
+    }
+}
+
+/// A specific scope in which a name can be looked up.
+/// This enum is currently used only for early resolution (imports and macros),
+/// but not for late resolution yet.
+#[derive(Clone, Copy, Debug)]
+enum Scope<'a> {
+    DeriveHelpers(LocalExpnId),
+    DeriveHelpersCompat,
+    MacroRules(MacroRulesScopeRef<'a>),
+    CrateRoot,
+    // The node ID is for reporting the `PROC_MACRO_DERIVE_RESOLUTION_FALLBACK`
+    // lint if it should be reported.
+    Module(Module<'a>, Option<NodeId>),
+    MacroUsePrelude,
+    BuiltinAttrs,
+    ExternPrelude,
+    ToolPrelude,
+    StdLibPrelude,
+    BuiltinTypes,
+}
+
+/// Names from different contexts may want to visit different subsets of all specific scopes
+/// with different restrictions when looking up the resolution.
+/// This enum is currently used only for early resolution (imports and macros),
+/// but not for late resolution yet.
+#[derive(Clone, Copy, Debug)]
+enum ScopeSet<'a> {
+    /// All scopes with the given namespace.
+    All(Namespace),
+    /// Crate root, then extern prelude (used for mixed 2015-2018 mode in macros).
+    AbsolutePath(Namespace),
+    /// All scopes with macro namespace and the given macro kind restriction.
+    Macro(MacroKind),
+    /// All scopes with the given namespace, used for partially performing late resolution.
+    /// The node id enables lints and is used for reporting them.
+    Late(Namespace, Module<'a>, Option<NodeId>),
+}
+
+/// Everything you need to know about a name's location to resolve it.
+/// Serves as a starting point for the scope visitor.
+/// This struct is currently used only for early resolution (imports and macros),
+/// but not for late resolution yet.
+#[derive(Clone, Copy, Debug)]
+struct ParentScope<'a> {
+    module: Module<'a>,
+    expansion: LocalExpnId,
+    macro_rules: MacroRulesScopeRef<'a>,
+    derives: &'a [ast::Path],
+}
+
+impl<'a> ParentScope<'a> {
+    /// Creates a parent scope with the passed argument used as the module scope component,
+    /// and other scope components set to default empty values.
+    fn module(module: Module<'a>, resolver: &Resolver<'a, '_>) -> ParentScope<'a> {
+        ParentScope {
+            module,
+            expansion: LocalExpnId::ROOT,
+            macro_rules: resolver.arenas.alloc_macro_rules_scope(MacroRulesScope::Empty),
+            derives: &[],
+        }
+    }
+}
+
+#[derive(Copy, Debug, Clone)]
+enum ImplTraitContext {
+    Existential,
+    Universal,
+}
+
+#[derive(Debug)]
+struct BindingError {
+    name: Symbol,
+    origin: BTreeSet<Span>,
+    target: BTreeSet<Span>,
+    could_be_path: bool,
+}
+
+#[derive(Debug)]
+enum ResolutionError<'a> {
+    /// Error E0401: can't use type or const parameters from outer item.
+    GenericParamsFromOuterItem(Res, HasGenericParams),
+    /// Error E0403: the name is already used for a type or const parameter in this generic
+    /// parameter list.
+    NameAlreadyUsedInParameterList(Symbol, Span),
+    /// Error E0407: method is not a member of trait.
+    MethodNotMemberOfTrait(Ident, String, Option<Symbol>),
+    /// Error E0437: type is not a member of trait.
+    TypeNotMemberOfTrait(Ident, String, Option<Symbol>),
+    /// Error E0438: const is not a member of trait.
+    ConstNotMemberOfTrait(Ident, String, Option<Symbol>),
+    /// Error E0408: variable `{}` is not bound in all patterns.
+    VariableNotBoundInPattern(BindingError, ParentScope<'a>),
+    /// Error E0409: variable `{}` is bound in inconsistent ways within the same match arm.
+    VariableBoundWithDifferentMode(Symbol, Span),
+    /// Error E0415: identifier is bound more than once in this parameter list.
+    IdentifierBoundMoreThanOnceInParameterList(Symbol),
+    /// Error E0416: identifier is bound more than once in the same pattern.
+    IdentifierBoundMoreThanOnceInSamePattern(Symbol),
+    /// Error E0426: use of undeclared label.
+    UndeclaredLabel { name: Symbol, suggestion: Option<LabelSuggestion> },
+    /// Error E0429: `self` imports are only allowed within a `{ }` list.
+    SelfImportsOnlyAllowedWithin { root: bool, span_with_rename: Span },
+    /// Error E0430: `self` import can only appear once in the list.
+    SelfImportCanOnlyAppearOnceInTheList,
+    /// Error E0431: `self` import can only appear in an import list with a non-empty prefix.
+    SelfImportOnlyInImportListWithNonEmptyPrefix,
+    /// Error E0433: failed to resolve.
+    FailedToResolve {
+        segment: Option<Symbol>,
+        label: String,
+        suggestion: Option<Suggestion>,
+        module: Option<ModuleOrUniformRoot<'a>>,
+    },
+    /// Error E0434: can't capture dynamic environment in a fn item.
+    CannotCaptureDynamicEnvironmentInFnItem,
+    /// Error E0435: attempt to use a non-constant value in a constant.
+    AttemptToUseNonConstantValueInConstant(
+        Ident,
+        /* suggestion */ &'static str,
+        /* current */ &'static str,
+    ),
+    /// Error E0530: `X` bindings cannot shadow `Y`s.
+    BindingShadowsSomethingUnacceptable {
+        shadowing_binding: PatternSource,
+        name: Symbol,
+        participle: &'static str,
+        article: &'static str,
+        shadowed_binding: Res,
+        shadowed_binding_span: Span,
+    },
+    /// Error E0128: generic parameters with a default cannot use forward-declared identifiers.
+    ForwardDeclaredGenericParam,
+    /// ERROR E0770: the type of const parameters must not depend on other generic parameters.
+    ParamInTyOfConstParam { name: Symbol, param_kind: Option<ParamKindInTyOfConstParam> },
+    /// generic parameters must not be used inside const evaluations.
+    ///
+    /// This error is only emitted when using `min_const_generics`.
+    ParamInNonTrivialAnonConst { name: Symbol, param_kind: ParamKindInNonTrivialAnonConst },
+    /// generic parameters must not be used inside enum discriminants.
+    ///
+    /// This error is emitted even with `generic_const_exprs`.
+    ParamInEnumDiscriminant { name: Symbol, param_kind: ParamKindInEnumDiscriminant },
+    /// Error E0735: generic parameters with a default cannot use `Self`
+    SelfInGenericParamDefault,
+    /// Error E0767: use of unreachable label
+    UnreachableLabel { name: Symbol, definition_span: Span, suggestion: Option<LabelSuggestion> },
+    /// Error E0323, E0324, E0325: mismatch between trait item and impl item.
+    TraitImplMismatch {
+        name: Symbol,
+        kind: &'static str,
+        trait_path: String,
+        trait_item_span: Span,
+        code: rustc_errors::DiagnosticId,
+    },
+    /// Error E0201: multiple impl items for the same trait item.
+    TraitImplDuplicate { name: Symbol, trait_item_span: Span, old_span: Span },
+    /// Inline asm `sym` operand must refer to a `fn` or `static`.
+    InvalidAsmSym,
+    /// `self` used instead of `Self` in a generic parameter
+    LowercaseSelf,
+}
+
+enum VisResolutionError<'a> {
+    Relative2018(Span, &'a ast::Path),
+    AncestorOnly(Span),
+    FailedToResolve(Span, String, Option<Suggestion>),
+    ExpectedFound(Span, String, Res),
+    Indeterminate(Span),
+    ModuleOnly(Span),
+}
+
+/// 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)]
+struct Segment {
+    ident: Ident,
+    id: Option<NodeId>,
+    /// Signals whether this `PathSegment` has generic arguments. Used to avoid providing
+    /// nonsensical suggestions.
+    has_generic_args: bool,
+    /// Signals whether this `PathSegment` has lifetime arguments.
+    has_lifetime_args: bool,
+    args_span: Span,
+}
+
+impl Segment {
+    fn from_path(path: &Path) -> Vec<Segment> {
+        path.segments.iter().map(|s| s.into()).collect()
+    }
+
+    fn from_ident(ident: Ident) -> Segment {
+        Segment {
+            ident,
+            id: None,
+            has_generic_args: false,
+            has_lifetime_args: false,
+            args_span: DUMMY_SP,
+        }
+    }
+
+    fn from_ident_and_id(ident: Ident, id: NodeId) -> Segment {
+        Segment {
+            ident,
+            id: Some(id),
+            has_generic_args: false,
+            has_lifetime_args: false,
+            args_span: DUMMY_SP,
+        }
+    }
+
+    fn names_to_string(segments: &[Segment]) -> String {
+        names_to_string(&segments.iter().map(|seg| seg.ident.name).collect::<Vec<_>>())
+    }
+}
+
+impl<'a> From<&'a ast::PathSegment> for Segment {
+    fn from(seg: &'a ast::PathSegment) -> Segment {
+        let has_generic_args = seg.args.is_some();
+        let (args_span, has_lifetime_args) = if let Some(args) = seg.args.as_deref() {
+            match args {
+                GenericArgs::AngleBracketed(args) => {
+                    let found_lifetimes = args
+                        .args
+                        .iter()
+                        .any(|arg| matches!(arg, AngleBracketedArg::Arg(GenericArg::Lifetime(_))));
+                    (args.span, found_lifetimes)
+                }
+                GenericArgs::Parenthesized(args) => (args.span, true),
+            }
+        } else {
+            (DUMMY_SP, false)
+        };
+        Segment {
+            ident: seg.ident,
+            id: Some(seg.id),
+            has_generic_args,
+            has_lifetime_args,
+            args_span,
+        }
+    }
+}
+
+/// An intermediate resolution result.
+///
+/// This refers to the thing referred by a name. The difference between `Res` and `Item` is that
+/// items are visible in their whole block, while `Res`es only from the place they are defined
+/// forward.
+#[derive(Debug)]
+enum LexicalScopeBinding<'a> {
+    Item(NameBinding<'a>),
+    Res(Res),
+}
+
+impl<'a> LexicalScopeBinding<'a> {
+    fn res(self) -> Res {
+        match self {
+            LexicalScopeBinding::Item(binding) => binding.res(),
+            LexicalScopeBinding::Res(res) => res,
+        }
+    }
+}
+
+#[derive(Copy, Clone, PartialEq, Debug)]
+enum ModuleOrUniformRoot<'a> {
+    /// Regular module.
+    Module(Module<'a>),
+
+    /// Virtual module that denotes resolution in crate root with fallback to extern prelude.
+    CrateRootAndExternPrelude,
+
+    /// Virtual module that denotes resolution in extern prelude.
+    /// Used for paths starting with `::` on 2018 edition.
+    ExternPrelude,
+
+    /// Virtual module that denotes resolution in current scope.
+    /// Used only for resolving single-segment imports. The reason it exists is that import paths
+    /// are always split into two parts, the first of which should be some kind of module.
+    CurrentScope,
+}
+
+#[derive(Debug)]
+enum PathResult<'a> {
+    Module(ModuleOrUniformRoot<'a>),
+    NonModule(PartialRes),
+    Indeterminate,
+    Failed {
+        span: Span,
+        label: String,
+        suggestion: Option<Suggestion>,
+        is_error_from_last_segment: bool,
+        module: Option<ModuleOrUniformRoot<'a>>,
+        /// The segment name of target
+        segment_name: Symbol,
+    },
+}
+
+impl<'a> PathResult<'a> {
+    fn failed(
+        ident: Ident,
+        is_error_from_last_segment: bool,
+        finalize: bool,
+        module: Option<ModuleOrUniformRoot<'a>>,
+        label_and_suggestion: impl FnOnce() -> (String, Option<Suggestion>),
+    ) -> PathResult<'a> {
+        let (label, suggestion) =
+            if finalize { label_and_suggestion() } else { (String::new(), None) };
+        PathResult::Failed {
+            span: ident.span,
+            segment_name: ident.name,
+            label,
+            suggestion,
+            is_error_from_last_segment,
+            module,
+        }
+    }
+}
+
+#[derive(Debug)]
+enum ModuleKind {
+    /// An anonymous module; e.g., just a block.
+    ///
+    /// ```
+    /// fn main() {
+    ///     fn f() {} // (1)
+    ///     { // This is an anonymous module
+    ///         f(); // This resolves to (2) as we are inside the block.
+    ///         fn f() {} // (2)
+    ///     }
+    ///     f(); // Resolves to (1)
+    /// }
+    /// ```
+    Block,
+    /// Any module with a name.
+    ///
+    /// This could be:
+    ///
+    /// * A normal module – either `mod from_file;` or `mod from_block { }` –
+    ///   or the crate root (which is conceptually a top-level module).
+    ///   Note that the crate root's [name][Self::name] will be [`kw::Empty`].
+    /// * A trait or an enum (it implicitly contains associated types, methods and variant
+    ///   constructors).
+    Def(DefKind, DefId, Symbol),
+}
+
+impl ModuleKind {
+    /// Get name of the module.
+    fn name(&self) -> Option<Symbol> {
+        match self {
+            ModuleKind::Block => None,
+            ModuleKind::Def(.., name) => Some(*name),
+        }
+    }
+}
+
+/// A key that identifies a binding in a given `Module`.
+///
+/// Multiple bindings in the same module can have the same key (in a valid
+/// program) if all but one of them come from glob imports.
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
+struct BindingKey {
+    /// The identifier for the binding, always the `normalize_to_macros_2_0` version of the
+    /// identifier.
+    ident: Ident,
+    ns: Namespace,
+    /// 0 if ident is not `_`, otherwise a value that's unique to the specific
+    /// `_` in the expanded AST that introduced this binding.
+    disambiguator: u32,
+}
+
+impl BindingKey {
+    fn new(ident: Ident, ns: Namespace) -> Self {
+        let ident = ident.normalize_to_macros_2_0();
+        BindingKey { ident, ns, disambiguator: 0 }
+    }
+}
+
+type Resolutions<'a> = RefCell<FxIndexMap<BindingKey, &'a RefCell<NameResolution<'a>>>>;
+
+/// One node in the tree of modules.
+///
+/// Note that a "module" in resolve is broader than a `mod` that you declare in Rust code. It may be one of these:
+///
+/// * `mod`
+/// * crate root (aka, top-level anonymous module)
+/// * `enum`
+/// * `trait`
+/// * curly-braced block with statements
+///
+/// You can use [`ModuleData::kind`] to determine the kind of module this is.
+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`.
+    kind: ModuleKind,
+
+    /// Mapping between names and their (possibly in-progress) resolutions in this module.
+    /// Resolutions in modules from other crates are not populated until accessed.
+    lazy_resolutions: Resolutions<'a>,
+    /// True if this is a module from other crate that needs to be populated on access.
+    populate_on_access: Cell<bool>,
+
+    /// Macro invocations that can expand into items in this module.
+    unexpanded_invocations: RefCell<FxHashSet<LocalExpnId>>,
+
+    /// Whether `#[no_implicit_prelude]` is active.
+    no_implicit_prelude: bool,
+
+    glob_importers: RefCell<Vec<Import<'a>>>,
+    globs: RefCell<Vec<Import<'a>>>,
+
+    /// Used to memoize the traits in this module for faster searches through all traits in scope.
+    traits: RefCell<Option<Box<[(Ident, NameBinding<'a>)]>>>,
+
+    /// Span of the module itself. Used for error reporting.
+    span: Span,
+
+    expansion: ExpnId,
+}
+
+/// All modules are unique and allocated on a same arena,
+/// so we can use referential equality to compare them.
+#[derive(Clone, Copy, PartialEq, Eq, Hash)]
+#[rustc_pass_by_value]
+struct Module<'a>(Interned<'a, ModuleData<'a>>);
+
+impl<'a> ModuleData<'a> {
+    fn new(
+        parent: Option<Module<'a>>,
+        kind: ModuleKind,
+        expansion: ExpnId,
+        span: Span,
+        no_implicit_prelude: bool,
+    ) -> Self {
+        let is_foreign = match kind {
+            ModuleKind::Def(_, def_id, _) => !def_id.is_local(),
+            ModuleKind::Block => false,
+        };
+        ModuleData {
+            parent,
+            kind,
+            lazy_resolutions: Default::default(),
+            populate_on_access: Cell::new(is_foreign),
+            unexpanded_invocations: Default::default(),
+            no_implicit_prelude,
+            glob_importers: RefCell::new(Vec::new()),
+            globs: RefCell::new(Vec::new()),
+            traits: RefCell::new(None),
+            span,
+            expansion,
+        }
+    }
+}
+
+impl<'a> Module<'a> {
+    fn for_each_child<'tcx, R, F>(self, resolver: &mut R, mut f: F)
+    where
+        R: AsMut<Resolver<'a, 'tcx>>,
+        F: FnMut(&mut R, Ident, Namespace, NameBinding<'a>),
+    {
+        for (key, name_resolution) in resolver.as_mut().resolutions(self).borrow().iter() {
+            if let Some(binding) = name_resolution.borrow().binding {
+                f(resolver, key.ident, key.ns, binding);
+            }
+        }
+    }
+
+    /// This modifies `self` in place. The traits will be stored in `self.traits`.
+    fn ensure_traits<'tcx, R>(self, resolver: &mut R)
+    where
+        R: AsMut<Resolver<'a, 'tcx>>,
+    {
+        let mut traits = self.traits.borrow_mut();
+        if traits.is_none() {
+            let mut collected_traits = Vec::new();
+            self.for_each_child(resolver, |_, name, ns, binding| {
+                if ns != TypeNS {
+                    return;
+                }
+                if let Res::Def(DefKind::Trait | DefKind::TraitAlias, _) = binding.res() {
+                    collected_traits.push((name, binding))
+                }
+            });
+            *traits = Some(collected_traits.into_boxed_slice());
+        }
+    }
+
+    fn res(self) -> Option<Res> {
+        match self.kind {
+            ModuleKind::Def(kind, def_id, _) => Some(Res::Def(kind, def_id)),
+            _ => None,
+        }
+    }
+
+    // Public for rustdoc.
+    fn def_id(self) -> DefId {
+        self.opt_def_id().expect("`ModuleData::def_id` is called on a block module")
+    }
+
+    fn opt_def_id(self) -> Option<DefId> {
+        match self.kind {
+            ModuleKind::Def(_, def_id, _) => Some(def_id),
+            _ => None,
+        }
+    }
+
+    // `self` resolves to the first module ancestor that `is_normal`.
+    fn is_normal(self) -> bool {
+        matches!(self.kind, ModuleKind::Def(DefKind::Mod, _, _))
+    }
+
+    fn is_trait(self) -> bool {
+        matches!(self.kind, ModuleKind::Def(DefKind::Trait, _, _))
+    }
+
+    fn nearest_item_scope(self) -> Module<'a> {
+        match self.kind {
+            ModuleKind::Def(DefKind::Enum | DefKind::Trait, ..) => {
+                self.parent.expect("enum or trait module without a parent")
+            }
+            _ => self,
+        }
+    }
+
+    /// The [`DefId`] of the nearest `mod` item ancestor (which may be this module).
+    /// This may be the crate root.
+    fn nearest_parent_mod(self) -> DefId {
+        match self.kind {
+            ModuleKind::Def(DefKind::Mod, def_id, _) => def_id,
+            _ => self.parent.expect("non-root module without parent").nearest_parent_mod(),
+        }
+    }
+
+    fn is_ancestor_of(self, mut other: Self) -> bool {
+        while self != other {
+            if let Some(parent) = other.parent {
+                other = parent;
+            } else {
+                return false;
+            }
+        }
+        true
+    }
+}
+
+impl<'a> std::ops::Deref for Module<'a> {
+    type Target = ModuleData<'a>;
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+
+impl<'a> fmt::Debug for Module<'a> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "{:?}", self.res())
+    }
+}
+
+/// Records a possibly-private value, type, or module definition.
+#[derive(Clone, Debug)]
+struct NameBindingData<'a> {
+    kind: NameBindingKind<'a>,
+    ambiguity: Option<(NameBinding<'a>, AmbiguityKind)>,
+    warn_ambiguity: bool,
+    expansion: LocalExpnId,
+    span: Span,
+    vis: ty::Visibility<DefId>,
+}
+
+/// All name bindings are unique and allocated on a same arena,
+/// so we can use referential equality to compare them.
+type NameBinding<'a> = Interned<'a, NameBindingData<'a>>;
+
+trait ToNameBinding<'a> {
+    fn to_name_binding(self, arenas: &'a ResolverArenas<'a>) -> NameBinding<'a>;
+}
+
+impl<'a> ToNameBinding<'a> for NameBinding<'a> {
+    fn to_name_binding(self, _: &'a ResolverArenas<'a>) -> NameBinding<'a> {
+        self
+    }
+}
+
+#[derive(Clone, Debug)]
+enum NameBindingKind<'a> {
+    Res(Res),
+    Module(Module<'a>),
+    Import { binding: NameBinding<'a>, import: Import<'a>, used: Cell<bool> },
+}
+
+impl<'a> NameBindingKind<'a> {
+    /// Is this a name binding of an import?
+    fn is_import(&self) -> bool {
+        matches!(*self, NameBindingKind::Import { .. })
+    }
+}
+
+#[derive(Debug)]
+struct PrivacyError<'a> {
+    ident: Ident,
+    binding: NameBinding<'a>,
+    dedup_span: Span,
+    outermost_res: Option<(Res, Ident)>,
+    parent_scope: ParentScope<'a>,
+}
+
+#[derive(Debug)]
+struct UseError<'a> {
+    err: DiagnosticBuilder<'a>,
+    /// Candidates which user could `use` to access the missing type.
+    candidates: Vec<ImportSuggestion>,
+    /// The `DefId` of the module to place the use-statements in.
+    def_id: DefId,
+    /// Whether the diagnostic should say "instead" (as in `consider importing ... instead`).
+    instead: bool,
+    /// Extra free-form suggestion.
+    suggestion: Option<(Span, &'static str, String, Applicability)>,
+    /// Path `Segment`s at the place of use that failed. Used for accurate suggestion after telling
+    /// the user to import the item directly.
+    path: Vec<Segment>,
+    /// Whether the expected source is a call
+    is_call: bool,
+}
+
+#[derive(Clone, Copy, PartialEq, Debug)]
+enum AmbiguityKind {
+    BuiltinAttr,
+    DeriveHelper,
+    MacroRulesVsModularized,
+    GlobVsOuter,
+    GlobVsGlob,
+    GlobVsExpanded,
+    MoreExpandedVsOuter,
+}
+
+impl AmbiguityKind {
+    fn descr(self) -> &'static str {
+        match self {
+            AmbiguityKind::BuiltinAttr => "a name conflict with a builtin attribute",
+            AmbiguityKind::DeriveHelper => "a name conflict with a derive helper attribute",
+            AmbiguityKind::MacroRulesVsModularized => {
+                "a conflict between a `macro_rules` name and a non-`macro_rules` name from another module"
+            }
+            AmbiguityKind::GlobVsOuter => {
+                "a conflict between a name from a glob import and an outer scope during import or macro resolution"
+            }
+            AmbiguityKind::GlobVsGlob => "multiple glob imports of a name in the same module",
+            AmbiguityKind::GlobVsExpanded => {
+                "a conflict between a name from a glob import and a macro-expanded name in the same module during import or macro resolution"
+            }
+            AmbiguityKind::MoreExpandedVsOuter => {
+                "a conflict between a macro-expanded name and a less macro-expanded name from outer scope during import or macro resolution"
+            }
+        }
+    }
+}
+
+/// Miscellaneous bits of metadata for better ambiguity error reporting.
+#[derive(Clone, Copy, PartialEq)]
+enum AmbiguityErrorMisc {
+    SuggestCrate,
+    SuggestSelf,
+    FromPrelude,
+    None,
+}
+
+struct AmbiguityError<'a> {
+    kind: AmbiguityKind,
+    ident: Ident,
+    b1: NameBinding<'a>,
+    b2: NameBinding<'a>,
+    misc1: AmbiguityErrorMisc,
+    misc2: AmbiguityErrorMisc,
+    warning: bool,
+}
+
+impl<'a> NameBindingData<'a> {
+    fn module(&self) -> Option<Module<'a>> {
+        match self.kind {
+            NameBindingKind::Module(module) => Some(module),
+            NameBindingKind::Import { binding, .. } => binding.module(),
+            _ => None,
+        }
+    }
+
+    fn res(&self) -> Res {
+        match self.kind {
+            NameBindingKind::Res(res) => res,
+            NameBindingKind::Module(module) => module.res().unwrap(),
+            NameBindingKind::Import { binding, .. } => binding.res(),
+        }
+    }
+
+    fn is_ambiguity(&self) -> bool {
+        self.ambiguity.is_some()
+            || match self.kind {
+                NameBindingKind::Import { binding, .. } => binding.is_ambiguity(),
+                _ => false,
+            }
+    }
+
+    fn is_warn_ambiguity(&self) -> bool {
+        self.warn_ambiguity
+            || match self.kind {
+                NameBindingKind::Import { binding, .. } => binding.is_warn_ambiguity(),
+                _ => false,
+            }
+    }
+
+    fn is_possibly_imported_variant(&self) -> bool {
+        match self.kind {
+            NameBindingKind::Import { binding, .. } => binding.is_possibly_imported_variant(),
+            NameBindingKind::Res(Res::Def(
+                DefKind::Variant | DefKind::Ctor(CtorOf::Variant, ..),
+                _,
+            )) => true,
+            NameBindingKind::Res(..) | NameBindingKind::Module(..) => false,
+        }
+    }
+
+    fn is_extern_crate(&self) -> bool {
+        match self.kind {
+            NameBindingKind::Import { import, .. } => {
+                matches!(import.kind, ImportKind::ExternCrate { .. })
+            }
+            NameBindingKind::Module(module)
+                if let ModuleKind::Def(DefKind::Mod, def_id, _) = module.kind =>
+            {
+                def_id.is_crate_root()
+            }
+            _ => false,
+        }
+    }
+
+    fn is_import(&self) -> bool {
+        matches!(self.kind, NameBindingKind::Import { .. })
+    }
+
+    /// The binding introduced by `#[macro_export] macro_rules` is a public import, but it might
+    /// not be perceived as such by users, so treat it as a non-import in some diagnostics.
+    fn is_import_user_facing(&self) -> bool {
+        matches!(self.kind, NameBindingKind::Import { import, .. }
+            if !matches!(import.kind, ImportKind::MacroExport))
+    }
+
+    fn is_glob_import(&self) -> bool {
+        match self.kind {
+            NameBindingKind::Import { import, .. } => import.is_glob(),
+            _ => false,
+        }
+    }
+
+    fn is_importable(&self) -> bool {
+        !matches!(
+            self.res(),
+            Res::Def(DefKind::AssocConst | DefKind::AssocFn | DefKind::AssocTy, _)
+        )
+    }
+
+    fn macro_kind(&self) -> Option<MacroKind> {
+        self.res().macro_kind()
+    }
+
+    // Suppose that we resolved macro invocation with `invoc_parent_expansion` to binding `binding`
+    // at some expansion round `max(invoc, binding)` when they both emerged from macros.
+    // Then this function returns `true` if `self` may emerge from a macro *after* that
+    // in some later round and screw up our previously found resolution.
+    // See more detailed explanation in
+    // https://github.com/rust-lang/rust/pull/53778#issuecomment-419224049
+    fn may_appear_after(
+        &self,
+        invoc_parent_expansion: LocalExpnId,
+        binding: NameBinding<'_>,
+    ) -> bool {
+        // self > max(invoc, binding) => !(self <= invoc || self <= binding)
+        // Expansions are partially ordered, so "may appear after" is an inversion of
+        // "certainly appears before or simultaneously" and includes unordered cases.
+        let self_parent_expansion = self.expansion;
+        let other_parent_expansion = binding.expansion;
+        let certainly_before_other_or_simultaneously =
+            other_parent_expansion.is_descendant_of(self_parent_expansion);
+        let certainly_before_invoc_or_simultaneously =
+            invoc_parent_expansion.is_descendant_of(self_parent_expansion);
+        !(certainly_before_other_or_simultaneously || certainly_before_invoc_or_simultaneously)
+    }
+
+    // Its purpose is to postpone the determination of a single binding because
+    // we can't predict whether it will be overwritten by recently expanded macros.
+    // FIXME: How can we integrate it with the `update_resolution`?
+    fn determined(&self) -> bool {
+        match &self.kind {
+            NameBindingKind::Import { binding, import, .. } if import.is_glob() => {
+                import.parent_scope.module.unexpanded_invocations.borrow().is_empty()
+                    && binding.determined()
+            }
+            _ => true,
+        }
+    }
+}
+
+#[derive(Default, Clone)]
+struct ExternPreludeEntry<'a> {
+    binding: Option<NameBinding<'a>>,
+    introduced_by_item: bool,
+}
+
+impl ExternPreludeEntry<'_> {
+    fn is_import(&self) -> bool {
+        self.binding.is_some_and(|binding| binding.is_import())
+    }
+}
+
+/// Used for better errors for E0773
+enum BuiltinMacroState {
+    NotYetSeen(SyntaxExtensionKind),
+    AlreadySeen(Span),
+}
+
+struct DeriveData {
+    resolutions: DeriveResolutions,
+    helper_attrs: Vec<(usize, Ident)>,
+    has_derive_copy: bool,
+}
+
+#[derive(Clone)]
+struct MacroData {
+    ext: Lrc<SyntaxExtension>,
+    rule_spans: Vec<(usize, Span)>,
+    macro_rules: bool,
+}
+
+impl MacroData {
+    fn new(ext: Lrc<SyntaxExtension>) -> MacroData {
+        MacroData { ext, rule_spans: Vec::new(), macro_rules: false }
+    }
+}
+
+/// The main resolver class.
+///
+/// This is the visitor that walks the whole crate.
+pub struct Resolver<'a, 'tcx> {
+    tcx: TyCtxt<'tcx>,
+
+    /// Item with a given `LocalDefId` was defined during macro expansion with ID `ExpnId`.
+    expn_that_defined: FxHashMap<LocalDefId, ExpnId>,
+
+    graph_root: Module<'a>,
+
+    prelude: Option<Module<'a>>,
+    extern_prelude: FxHashMap<Ident, ExternPreludeEntry<'a>>,
+
+    /// N.B., this is used only for better diagnostics, not name resolution itself.
+    has_self: LocalDefIdSet,
+    field_def_ids: LocalDefIdMap<&'tcx [DefId]>,
+
+    /// Span of the privacy modifier in fields of an item `DefId` accessible with dot syntax.
+    /// Used for hints during error reporting.
+    field_visibility_spans: FxHashMap<DefId, Vec<Span>>,
+
+    /// All imports known to succeed or fail.
+    determined_imports: Vec<Import<'a>>,
+
+    /// All non-determined imports.
+    indeterminate_imports: Vec<Import<'a>>,
+
+    // Spans for local variables found during pattern resolution.
+    // Used for suggestions during error reporting.
+    pat_span_map: NodeMap<Span>,
+
+    /// Resolutions for nodes that have a single resolution.
+    partial_res_map: NodeMap<PartialRes>,
+    /// Resolutions for import nodes, which have multiple resolutions in different namespaces.
+    import_res_map: NodeMap<PerNS<Option<Res>>>,
+    /// Resolutions for labels (node IDs of their corresponding blocks or loops).
+    label_res_map: NodeMap<NodeId>,
+    /// Resolutions for lifetimes.
+    lifetimes_res_map: NodeMap<LifetimeRes>,
+    /// Lifetime parameters that lowering will have to introduce.
+    extra_lifetime_params_map: NodeMap<Vec<(Ident, NodeId, LifetimeRes)>>,
+
+    /// `CrateNum` resolutions of `extern crate` items.
+    extern_crate_map: FxHashMap<LocalDefId, CrateNum>,
+    module_children: LocalDefIdMap<Vec<ModChild>>,
+    trait_map: NodeMap<Vec<TraitCandidate>>,
+
+    /// A map from nodes to anonymous modules.
+    /// Anonymous modules are pseudo-modules that are implicitly created around items
+    /// contained within blocks.
+    ///
+    /// For example, if we have this:
+    ///
+    ///  fn f() {
+    ///      fn g() {
+    ///          ...
+    ///      }
+    ///  }
+    ///
+    /// There will be an anonymous module created around `g` with the ID of the
+    /// entry block for `f`.
+    block_map: NodeMap<Module<'a>>,
+    /// A fake module that contains no definition and no prelude. Used so that
+    /// some AST passes can generate identifiers that only resolve to local or
+    /// language items.
+    empty_module: Module<'a>,
+    module_map: FxHashMap<DefId, Module<'a>>,
+    binding_parent_modules: FxHashMap<NameBinding<'a>, Module<'a>>,
+
+    underscore_disambiguator: u32,
+
+    /// Maps glob imports to the names of items actually imported.
+    glob_map: FxHashMap<LocalDefId, FxHashSet<Symbol>>,
+    visibilities_for_hashing: Vec<(LocalDefId, ty::Visibility)>,
+    used_imports: FxHashSet<NodeId>,
+    maybe_unused_trait_imports: FxIndexSet<LocalDefId>,
+
+    /// Privacy errors are delayed until the end in order to deduplicate them.
+    privacy_errors: Vec<PrivacyError<'a>>,
+    /// Ambiguity errors are delayed for deduplication.
+    ambiguity_errors: Vec<AmbiguityError<'a>>,
+    /// `use` injections are delayed for better placement and deduplication.
+    use_injections: Vec<UseError<'tcx>>,
+    /// Crate-local macro expanded `macro_export` referred to by a module-relative path.
+    macro_expanded_macro_export_errors: BTreeSet<(Span, Span)>,
+
+    arenas: &'a ResolverArenas<'a>,
+    dummy_binding: NameBinding<'a>,
+    builtin_types_bindings: FxHashMap<Symbol, NameBinding<'a>>,
+    builtin_attrs_bindings: FxHashMap<Symbol, NameBinding<'a>>,
+    registered_tool_bindings: FxHashMap<Ident, NameBinding<'a>>,
+    /// Binding for implicitly declared names that come with a module,
+    /// like `self` (not yet used), or `crate`/`$crate` (for root modules).
+    module_self_bindings: FxHashMap<Module<'a>, NameBinding<'a>>,
+
+    used_extern_options: FxHashSet<Symbol>,
+    macro_names: FxHashSet<Ident>,
+    builtin_macros: FxHashMap<Symbol, BuiltinMacroState>,
+    registered_tools: &'tcx RegisteredTools,
+    macro_use_prelude: FxHashMap<Symbol, NameBinding<'a>>,
+    macro_map: FxHashMap<DefId, MacroData>,
+    dummy_ext_bang: Lrc<SyntaxExtension>,
+    dummy_ext_derive: Lrc<SyntaxExtension>,
+    non_macro_attr: MacroData,
+    local_macro_def_scopes: FxHashMap<LocalDefId, Module<'a>>,
+    ast_transform_scopes: FxHashMap<LocalExpnId, Module<'a>>,
+    unused_macros: FxHashMap<LocalDefId, (NodeId, Ident)>,
+    unused_macro_rules: FxHashMap<(LocalDefId, usize), (Ident, Span)>,
+    proc_macro_stubs: FxHashSet<LocalDefId>,
+    /// Traces collected during macro resolution and validated when it's complete.
+    single_segment_macro_resolutions:
+        Vec<(Ident, MacroKind, ParentScope<'a>, Option<NameBinding<'a>>)>,
+    multi_segment_macro_resolutions:
+        Vec<(Vec<Segment>, Span, MacroKind, ParentScope<'a>, Option<Res>)>,
+    builtin_attrs: Vec<(Ident, ParentScope<'a>)>,
+    /// `derive(Copy)` marks items they are applied to so they are treated specially later.
+    /// Derive macros cannot modify the item themselves and have to store the markers in the global
+    /// context, so they attach the markers to derive container IDs using this resolver table.
+    containers_deriving_copy: FxHashSet<LocalExpnId>,
+    /// Parent scopes in which the macros were invoked.
+    /// FIXME: `derives` are missing in these parent scopes and need to be taken from elsewhere.
+    invocation_parent_scopes: FxHashMap<LocalExpnId, ParentScope<'a>>,
+    /// `macro_rules` scopes *produced* by expanding the macro invocations,
+    /// include all the `macro_rules` items and other invocations generated by them.
+    output_macro_rules_scopes: FxHashMap<LocalExpnId, MacroRulesScopeRef<'a>>,
+    /// `macro_rules` scopes produced by `macro_rules` item definitions.
+    macro_rules_scopes: FxHashMap<LocalDefId, MacroRulesScopeRef<'a>>,
+    /// Helper attributes that are in scope for the given expansion.
+    helper_attrs: FxHashMap<LocalExpnId, Vec<(Ident, NameBinding<'a>)>>,
+    /// Ready or in-progress results of resolving paths inside the `#[derive(...)]` attribute
+    /// with the given `ExpnId`.
+    derive_data: FxHashMap<LocalExpnId, DeriveData>,
+
+    /// Avoid duplicated errors for "name already defined".
+    name_already_seen: FxHashMap<Symbol, Span>,
+
+    potentially_unused_imports: Vec<Import<'a>>,
+
+    /// Table for mapping struct IDs into struct constructor IDs,
+    /// it's not used during normal resolution, only for better error reporting.
+    /// Also includes of list of each fields visibility
+    struct_constructors: LocalDefIdMap<(Res, ty::Visibility<DefId>, Vec<ty::Visibility<DefId>>)>,
+
+    /// Features declared for this crate.
+    declared_features: FxHashSet<Symbol>,
+
+    lint_buffer: LintBuffer,
+
+    next_node_id: NodeId,
+
+    node_id_to_def_id: NodeMap<LocalDefId>,
+    def_id_to_node_id: IndexVec<LocalDefId, ast::NodeId>,
+
+    /// Indices of unnamed struct or variant fields with unresolved attributes.
+    placeholder_field_indices: FxHashMap<NodeId, usize>,
+    /// When collecting definitions from an AST fragment produced by a macro invocation `ExpnId`
+    /// we know what parent node that fragment should be attached to thanks to this table,
+    /// and how the `impl Trait` fragments were introduced.
+    invocation_parents: FxHashMap<LocalExpnId, (LocalDefId, ImplTraitContext)>,
+
+    /// Some way to know that we are in a *trait* impl in `visit_assoc_item`.
+    /// FIXME: Replace with a more general AST map (together with some other fields).
+    trait_impl_items: FxHashSet<LocalDefId>,
+
+    legacy_const_generic_args: FxHashMap<DefId, Option<Vec<usize>>>,
+    /// Amount of lifetime parameters for each item in the crate.
+    item_generics_num_lifetimes: FxHashMap<LocalDefId, usize>,
+    /// Amount of parameters for each function in the crate.
+    fn_parameter_counts: LocalDefIdMap<usize>,
+
+    main_def: Option<MainDefinition>,
+    trait_impls: FxIndexMap<DefId, Vec<LocalDefId>>,
+    /// A list of proc macro LocalDefIds, written out in the order in which
+    /// they are declared in the static array generated by proc_macro_harness.
+    proc_macros: Vec<NodeId>,
+    confused_type_with_std_module: FxHashMap<Span, Span>,
+    /// Whether lifetime elision was successful.
+    lifetime_elision_allowed: FxHashSet<NodeId>,
+
+    /// Names of items that were stripped out via cfg with their corresponding cfg meta item.
+    stripped_cfg_items: Vec<StrippedCfgItem<NodeId>>,
+
+    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.
+#[derive(Default)]
+pub struct ResolverArenas<'a> {
+    modules: TypedArena<ModuleData<'a>>,
+    local_modules: RefCell<Vec<Module<'a>>>,
+    imports: TypedArena<ImportData<'a>>,
+    name_resolutions: TypedArena<RefCell<NameResolution<'a>>>,
+    ast_paths: TypedArena<ast::Path>,
+    dropless: DroplessArena,
+}
+
+impl<'a> ResolverArenas<'a> {
+    fn new_module(
+        &'a self,
+        parent: Option<Module<'a>>,
+        kind: ModuleKind,
+        expn_id: ExpnId,
+        span: Span,
+        no_implicit_prelude: bool,
+        module_map: &mut FxHashMap<DefId, Module<'a>>,
+        module_self_bindings: &mut FxHashMap<Module<'a>, NameBinding<'a>>,
+    ) -> Module<'a> {
+        let module = Module(Interned::new_unchecked(self.modules.alloc(ModuleData::new(
+            parent,
+            kind,
+            expn_id,
+            span,
+            no_implicit_prelude,
+        ))));
+        let def_id = module.opt_def_id();
+        if def_id.map_or(true, |def_id| def_id.is_local()) {
+            self.local_modules.borrow_mut().push(module);
+        }
+        if let Some(def_id) = def_id {
+            module_map.insert(def_id, module);
+            let vis = ty::Visibility::<DefId>::Public;
+            let binding = (module, vis, module.span, LocalExpnId::ROOT).to_name_binding(self);
+            module_self_bindings.insert(module, binding);
+        }
+        module
+    }
+    fn local_modules(&'a self) -> std::cell::Ref<'a, Vec<Module<'a>>> {
+        self.local_modules.borrow()
+    }
+    fn alloc_name_binding(&'a self, name_binding: NameBindingData<'a>) -> NameBinding<'a> {
+        Interned::new_unchecked(self.dropless.alloc(name_binding))
+    }
+    fn alloc_import(&'a self, import: ImportData<'a>) -> Import<'a> {
+        Interned::new_unchecked(self.imports.alloc(import))
+    }
+    fn alloc_name_resolution(&'a self) -> &'a RefCell<NameResolution<'a>> {
+        self.name_resolutions.alloc(Default::default())
+    }
+    fn alloc_macro_rules_scope(&'a self, scope: MacroRulesScope<'a>) -> MacroRulesScopeRef<'a> {
+        Interned::new_unchecked(self.dropless.alloc(Cell::new(scope)))
+    }
+    fn alloc_macro_rules_binding(
+        &'a self,
+        binding: MacroRulesBinding<'a>,
+    ) -> &'a MacroRulesBinding<'a> {
+        self.dropless.alloc(binding)
+    }
+    fn alloc_ast_paths(&'a self, paths: &[ast::Path]) -> &'a [ast::Path] {
+        self.ast_paths.alloc_from_iter(paths.iter().cloned())
+    }
+    fn alloc_pattern_spans(&'a self, spans: impl Iterator<Item = Span>) -> &'a [Span] {
+        self.dropless.alloc_from_iter(spans)
+    }
+}
+
+impl<'a, 'tcx> AsMut<Resolver<'a, 'tcx>> for Resolver<'a, 'tcx> {
+    fn as_mut(&mut self) -> &mut Resolver<'a, 'tcx> {
+        self
+    }
+}
+
+impl<'tcx> Resolver<'_, 'tcx> {
+    fn opt_local_def_id(&self, node: NodeId) -> Option<LocalDefId> {
+        self.node_id_to_def_id.get(&node).copied()
+    }
+
+    fn local_def_id(&self, node: NodeId) -> LocalDefId {
+        self.opt_local_def_id(node).unwrap_or_else(|| panic!("no entry for node id: `{node:?}`"))
+    }
+
+    /// Adds a definition with a parent definition.
+    fn create_def(
+        &mut self,
+        parent: LocalDefId,
+        node_id: ast::NodeId,
+        name: Symbol,
+        def_kind: DefKind,
+        expn_id: ExpnId,
+        span: Span,
+    ) -> LocalDefId {
+        let data = def_kind.def_path_data(name);
+        assert!(
+            !self.node_id_to_def_id.contains_key(&node_id),
+            "adding a def'n for node-id {:?} and data {:?} but a previous def'n exists: {:?}",
+            node_id,
+            data,
+            self.tcx.definitions_untracked().def_key(self.node_id_to_def_id[&node_id]),
+        );
+
+        // FIXME: remove `def_span` body, pass in the right spans here and call `tcx.at().create_def()`
+        let def_id = self.tcx.create_def(parent, name, def_kind);
+
+        // Create the definition.
+        if expn_id != ExpnId::root() {
+            self.expn_that_defined.insert(def_id, expn_id);
+        }
+
+        // A relative span's parent must be an absolute span.
+        debug_assert_eq!(span.data_untracked().parent, None);
+        let _id = self.tcx.untracked().source_span.push(span);
+        debug_assert_eq!(_id, def_id);
+
+        // Some things for which we allocate `LocalDefId`s don't correspond to
+        // anything in the AST, so they don't have a `NodeId`. For these cases
+        // we don't need a mapping from `NodeId` to `LocalDefId`.
+        if node_id != ast::DUMMY_NODE_ID {
+            debug!("create_def: def_id_to_node_id[{:?}] <-> {:?}", def_id, node_id);
+            self.node_id_to_def_id.insert(node_id, def_id);
+        }
+        assert_eq!(self.def_id_to_node_id.push(node_id), def_id);
+
+        def_id
+    }
+
+    fn item_generics_num_lifetimes(&self, def_id: DefId) -> usize {
+        if let Some(def_id) = def_id.as_local() {
+            self.item_generics_num_lifetimes[&def_id]
+        } else {
+            self.tcx.generics_of(def_id).own_counts().lifetimes
+        }
+    }
+
+    pub fn tcx(&self) -> TyCtxt<'tcx> {
+        self.tcx
+    }
+}
+
+impl<'a, 'tcx> Resolver<'a, 'tcx> {
+    pub fn new(
+        tcx: TyCtxt<'tcx>,
+        attrs: &[ast::Attribute],
+        crate_span: Span,
+        arenas: &'a ResolverArenas<'a>,
+    ) -> Resolver<'a, 'tcx> {
+        let root_def_id = CRATE_DEF_ID.to_def_id();
+        let mut module_map = FxHashMap::default();
+        let mut module_self_bindings = FxHashMap::default();
+        let graph_root = arenas.new_module(
+            None,
+            ModuleKind::Def(DefKind::Mod, root_def_id, kw::Empty),
+            ExpnId::root(),
+            crate_span,
+            attr::contains_name(attrs, sym::no_implicit_prelude),
+            &mut module_map,
+            &mut module_self_bindings,
+        );
+        let empty_module = arenas.new_module(
+            None,
+            ModuleKind::Def(DefKind::Mod, root_def_id, kw::Empty),
+            ExpnId::root(),
+            DUMMY_SP,
+            true,
+            &mut FxHashMap::default(),
+            &mut FxHashMap::default(),
+        );
+
+        let mut def_id_to_node_id = IndexVec::default();
+        assert_eq!(def_id_to_node_id.push(CRATE_NODE_ID), CRATE_DEF_ID);
+        let mut node_id_to_def_id = NodeMap::default();
+        node_id_to_def_id.insert(CRATE_NODE_ID, CRATE_DEF_ID);
+
+        let mut invocation_parents = FxHashMap::default();
+        invocation_parents.insert(LocalExpnId::ROOT, (CRATE_DEF_ID, ImplTraitContext::Existential));
+
+        let mut extern_prelude: FxHashMap<Ident, ExternPreludeEntry<'_>> = tcx
+            .sess
+            .opts
+            .externs
+            .iter()
+            .filter(|(_, entry)| entry.add_prelude)
+            .map(|(name, _)| (Ident::from_str(name), Default::default()))
+            .collect();
+
+        if !attr::contains_name(attrs, sym::no_core) {
+            extern_prelude.insert(Ident::with_dummy_span(sym::core), Default::default());
+            if !attr::contains_name(attrs, sym::no_std) {
+                extern_prelude.insert(Ident::with_dummy_span(sym::std), Default::default());
+            }
+        }
+
+        let registered_tools = tcx.registered_tools(());
+
+        let features = tcx.features();
+        let pub_vis = ty::Visibility::<DefId>::Public;
+        let edition = tcx.sess.edition();
+
+        let mut resolver = Resolver {
+            tcx,
+
+            expn_that_defined: Default::default(),
+
+            // The outermost module has def ID 0; this is not reflected in the
+            // AST.
+            graph_root,
+            prelude: None,
+            extern_prelude,
+
+            has_self: Default::default(),
+            field_def_ids: Default::default(),
+            field_visibility_spans: FxHashMap::default(),
+
+            determined_imports: Vec::new(),
+            indeterminate_imports: Vec::new(),
+
+            pat_span_map: Default::default(),
+            partial_res_map: Default::default(),
+            import_res_map: Default::default(),
+            label_res_map: Default::default(),
+            lifetimes_res_map: Default::default(),
+            extra_lifetime_params_map: Default::default(),
+            extern_crate_map: Default::default(),
+            module_children: Default::default(),
+            trait_map: NodeMap::default(),
+            underscore_disambiguator: 0,
+            empty_module,
+            module_map,
+            block_map: Default::default(),
+            binding_parent_modules: FxHashMap::default(),
+            ast_transform_scopes: FxHashMap::default(),
+
+            glob_map: Default::default(),
+            visibilities_for_hashing: Default::default(),
+            used_imports: FxHashSet::default(),
+            maybe_unused_trait_imports: Default::default(),
+
+            privacy_errors: Vec::new(),
+            ambiguity_errors: Vec::new(),
+            use_injections: Vec::new(),
+            macro_expanded_macro_export_errors: BTreeSet::new(),
+
+            arenas,
+            dummy_binding: (Res::Err, pub_vis, DUMMY_SP, LocalExpnId::ROOT).to_name_binding(arenas),
+            builtin_types_bindings: PrimTy::ALL
+                .iter()
+                .map(|prim_ty| {
+                    let binding = (Res::PrimTy(*prim_ty), pub_vis, DUMMY_SP, LocalExpnId::ROOT)
+                        .to_name_binding(arenas);
+                    (prim_ty.name(), binding)
+                })
+                .collect(),
+            builtin_attrs_bindings: BUILTIN_ATTRIBUTES
+                .iter()
+                .map(|builtin_attr| {
+                    let res = Res::NonMacroAttr(NonMacroAttrKind::Builtin(builtin_attr.name));
+                    let binding =
+                        (res, pub_vis, DUMMY_SP, LocalExpnId::ROOT).to_name_binding(arenas);
+                    (builtin_attr.name, binding)
+                })
+                .collect(),
+            registered_tool_bindings: registered_tools
+                .iter()
+                .map(|ident| {
+                    let binding = (Res::ToolMod, pub_vis, ident.span, LocalExpnId::ROOT)
+                        .to_name_binding(arenas);
+                    (*ident, binding)
+                })
+                .collect(),
+            module_self_bindings,
+
+            used_extern_options: Default::default(),
+            macro_names: FxHashSet::default(),
+            builtin_macros: Default::default(),
+            registered_tools,
+            macro_use_prelude: FxHashMap::default(),
+            macro_map: FxHashMap::default(),
+            dummy_ext_bang: Lrc::new(SyntaxExtension::dummy_bang(edition)),
+            dummy_ext_derive: Lrc::new(SyntaxExtension::dummy_derive(edition)),
+            non_macro_attr: MacroData::new(Lrc::new(SyntaxExtension::non_macro_attr(edition))),
+            invocation_parent_scopes: Default::default(),
+            output_macro_rules_scopes: Default::default(),
+            macro_rules_scopes: Default::default(),
+            helper_attrs: Default::default(),
+            derive_data: Default::default(),
+            local_macro_def_scopes: FxHashMap::default(),
+            name_already_seen: FxHashMap::default(),
+            potentially_unused_imports: Vec::new(),
+            struct_constructors: Default::default(),
+            unused_macros: Default::default(),
+            unused_macro_rules: Default::default(),
+            proc_macro_stubs: Default::default(),
+            single_segment_macro_resolutions: Default::default(),
+            multi_segment_macro_resolutions: Default::default(),
+            builtin_attrs: Default::default(),
+            containers_deriving_copy: Default::default(),
+            declared_features: features.declared_features.clone(),
+            lint_buffer: LintBuffer::default(),
+            next_node_id: CRATE_NODE_ID,
+            node_id_to_def_id,
+            def_id_to_node_id,
+            placeholder_field_indices: Default::default(),
+            invocation_parents,
+            trait_impl_items: Default::default(),
+            legacy_const_generic_args: Default::default(),
+            item_generics_num_lifetimes: Default::default(),
+            main_def: Default::default(),
+            trait_impls: Default::default(),
+            proc_macros: Default::default(),
+            confused_type_with_std_module: Default::default(),
+            lifetime_elision_allowed: Default::default(),
+            stripped_cfg_items: Default::default(),
+            effective_visibilities: Default::default(),
+            doc_link_resolutions: Default::default(),
+            doc_link_traits_in_scope: Default::default(),
+            all_macro_rules: Default::default(),
+            fn_parameter_counts: Default::default(),
+        };
+
+        let root_parent_scope = ParentScope::module(graph_root, &resolver);
+        resolver.invocation_parent_scopes.insert(LocalExpnId::ROOT, root_parent_scope);
+        resolver.feed_visibility(CRATE_DEF_ID, ty::Visibility::Public);
+
+        resolver
+    }
+
+    fn new_module(
+        &mut self,
+        parent: Option<Module<'a>>,
+        kind: ModuleKind,
+        expn_id: ExpnId,
+        span: Span,
+        no_implicit_prelude: bool,
+    ) -> Module<'a> {
+        let module_map = &mut self.module_map;
+        let module_self_bindings = &mut self.module_self_bindings;
+        self.arenas.new_module(
+            parent,
+            kind,
+            expn_id,
+            span,
+            no_implicit_prelude,
+            module_map,
+            module_self_bindings,
+        )
+    }
+
+    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
+    }
+
+    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);
+        start..self.next_node_id
+    }
+
+    pub fn lint_buffer(&mut self) -> &mut LintBuffer {
+        &mut self.lint_buffer
+    }
+
+    pub fn arenas() -> ResolverArenas<'a> {
+        Default::default()
+    }
+
+    fn feed_visibility(&mut self, def_id: LocalDefId, vis: ty::Visibility) {
+        self.tcx.feed_local_def_id(def_id).visibility(vis.to_def_id());
+        self.visibilities_for_hashing.push((def_id, vis));
+    }
+
+    pub fn into_outputs(self) -> ResolverOutputs {
+        let proc_macros = self.proc_macros.iter().map(|id| self.local_def_id(*id)).collect();
+        let expn_that_defined = self.expn_that_defined;
+        let extern_crate_map = self.extern_crate_map;
+        let maybe_unused_trait_imports = self.maybe_unused_trait_imports;
+        let glob_map = self.glob_map;
+        let main_def = self.main_def;
+        let confused_type_with_std_module = self.confused_type_with_std_module;
+        let effective_visibilities = self.effective_visibilities;
+
+        self.tcx.feed_local_crate().stripped_cfg_items(self.tcx.arena.alloc_from_iter(
+            self.stripped_cfg_items.into_iter().filter_map(|item| {
+                let parent_module = self.node_id_to_def_id.get(&item.parent_module)?.to_def_id();
+                Some(StrippedCfgItem { parent_module, name: item.name, cfg: item.cfg })
+            }),
+        ));
+
+        let global_ctxt = ResolverGlobalCtxt {
+            expn_that_defined,
+            visibilities_for_hashing: self.visibilities_for_hashing,
+            effective_visibilities,
+            extern_crate_map,
+            module_children: self.module_children,
+            glob_map,
+            maybe_unused_trait_imports,
+            main_def,
+            trait_impls: self.trait_impls,
+            proc_macros,
+            confused_type_with_std_module,
+            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,
+            partial_res_map: self.partial_res_map,
+            import_res_map: self.import_res_map,
+            label_res_map: self.label_res_map,
+            lifetimes_res_map: self.lifetimes_res_map,
+            extra_lifetime_params_map: self.extra_lifetime_params_map,
+            next_node_id: self.next_node_id,
+            node_id_to_def_id: self.node_id_to_def_id,
+            def_id_to_node_id: self.def_id_to_node_id,
+            trait_map: self.trait_map,
+            lifetime_elision_allowed: self.lifetime_elision_allowed,
+            lint_buffer: Steal::new(self.lint_buffer),
+            has_self: self.has_self,
+            fn_parameter_counts: self.fn_parameter_counts,
+        };
+        ResolverOutputs { global_ctxt, ast_lowering }
+    }
+
+    fn create_stable_hashing_context(&self) -> StableHashingContext<'_> {
+        StableHashingContext::new(self.tcx.sess, self.tcx.untracked())
+    }
+
+    fn crate_loader<T>(&mut self, f: impl FnOnce(&mut CrateLoader<'_, '_>) -> T) -> T {
+        f(&mut CrateLoader::new(
+            self.tcx,
+            &mut CStore::from_tcx_mut(self.tcx),
+            &mut self.used_extern_options,
+        ))
+    }
+
+    fn cstore(&self) -> FreezeReadGuard<'_, CStore> {
+        CStore::from_tcx(self.tcx)
+    }
+
+    fn dummy_ext(&self, macro_kind: MacroKind) -> Lrc<SyntaxExtension> {
+        match macro_kind {
+            MacroKind::Bang => self.dummy_ext_bang.clone(),
+            MacroKind::Derive => self.dummy_ext_derive.clone(),
+            MacroKind::Attr => self.non_macro_attr.ext.clone(),
+        }
+    }
+
+    /// Runs the function on each namespace.
+    fn per_ns<F: FnMut(&mut Self, Namespace)>(&mut self, mut f: F) {
+        f(self, TypeNS);
+        f(self, ValueNS);
+        f(self, MacroNS);
+    }
+
+    fn is_builtin_macro(&mut self, res: Res) -> bool {
+        self.get_macro(res).is_some_and(|macro_data| macro_data.ext.builtin_name.is_some())
+    }
+
+    fn macro_def(&self, mut ctxt: SyntaxContext) -> DefId {
+        loop {
+            match ctxt.outer_expn_data().macro_def_id {
+                Some(def_id) => return def_id,
+                None => ctxt.remove_mark(),
+            };
+        }
+    }
+
+    /// Entry point to crate resolution.
+    pub fn resolve_crate(&mut self, krate: &Crate) {
+        self.tcx.sess.time("resolve_crate", || {
+            self.tcx.sess.time("finalize_imports", || self.finalize_imports());
+            let exported_ambiguities = self.tcx.sess.time("compute_effective_visibilities", || {
+                EffectiveVisibilitiesVisitor::compute_effective_visibilities(self, krate)
+            });
+            self.tcx.sess.time("check_hidden_glob_reexports", || {
+                self.check_hidden_glob_reexports(exported_ambiguities)
+            });
+            self.tcx
+                .sess
+                .time("finalize_macro_resolutions", || self.finalize_macro_resolutions(krate));
+            self.tcx.sess.time("late_resolve_crate", || self.late_resolve_crate(krate));
+            self.tcx.sess.time("resolve_main", || self.resolve_main());
+            self.tcx.sess.time("resolve_check_unused", || self.check_unused(krate));
+            self.tcx.sess.time("resolve_report_errors", || self.report_errors(krate));
+            self.tcx
+                .sess
+                .time("resolve_postprocess", || self.crate_loader(|c| c.postprocess(krate)));
+        });
+
+        // Make sure we don't mutate the cstore from here on.
+        self.tcx.untracked().cstore.freeze();
+    }
+
+    fn traits_in_scope(
+        &mut self,
+        current_trait: Option<Module<'a>>,
+        parent_scope: &ParentScope<'a>,
+        ctxt: SyntaxContext,
+        assoc_item: Option<(Symbol, Namespace)>,
+    ) -> Vec<TraitCandidate> {
+        let mut found_traits = Vec::new();
+
+        if let Some(module) = current_trait {
+            if self.trait_may_have_item(Some(module), assoc_item) {
+                let def_id = module.def_id();
+                found_traits.push(TraitCandidate { def_id, import_ids: smallvec![] });
+            }
+        }
+
+        self.visit_scopes(ScopeSet::All(TypeNS), parent_scope, ctxt, |this, scope, _, _| {
+            match scope {
+                Scope::Module(module, _) => {
+                    this.traits_in_module(module, assoc_item, &mut found_traits);
+                }
+                Scope::StdLibPrelude => {
+                    if let Some(module) = this.prelude {
+                        this.traits_in_module(module, assoc_item, &mut found_traits);
+                    }
+                }
+                Scope::ExternPrelude | Scope::ToolPrelude | Scope::BuiltinTypes => {}
+                _ => unreachable!(),
+            }
+            None::<()>
+        });
+
+        found_traits
+    }
+
+    fn traits_in_module(
+        &mut self,
+        module: Module<'a>,
+        assoc_item: Option<(Symbol, Namespace)>,
+        found_traits: &mut Vec<TraitCandidate>,
+    ) {
+        module.ensure_traits(self);
+        let traits = module.traits.borrow();
+        for (trait_name, trait_binding) in traits.as_ref().unwrap().iter() {
+            if self.trait_may_have_item(trait_binding.module(), assoc_item) {
+                let def_id = trait_binding.res().def_id();
+                let import_ids = self.find_transitive_imports(&trait_binding.kind, *trait_name);
+                found_traits.push(TraitCandidate { def_id, import_ids });
+            }
+        }
+    }
+
+    // List of traits in scope is pruned on best effort basis. We reject traits not having an
+    // associated item with the given name and namespace (if specified). This is a conservative
+    // optimization, proper hygienic type-based resolution of associated items is done in typeck.
+    // We don't reject trait aliases (`trait_module == None`) because we don't have access to their
+    // associated items.
+    fn trait_may_have_item(
+        &mut self,
+        trait_module: Option<Module<'a>>,
+        assoc_item: Option<(Symbol, Namespace)>,
+    ) -> bool {
+        match (trait_module, assoc_item) {
+            (Some(trait_module), Some((name, ns))) => {
+                self.resolutions(trait_module).borrow().iter().any(|resolution| {
+                    let (&BindingKey { ident: assoc_ident, ns: assoc_ns, .. }, _) = resolution;
+                    assoc_ns == ns && assoc_ident.name == name
+                })
+            }
+            _ => true,
+        }
+    }
+
+    fn find_transitive_imports(
+        &mut self,
+        mut kind: &NameBindingKind<'_>,
+        trait_name: Ident,
+    ) -> SmallVec<[LocalDefId; 1]> {
+        let mut import_ids = smallvec![];
+        while let NameBindingKind::Import { import, binding, .. } = kind {
+            if let Some(node_id) = import.id() {
+                let def_id = self.local_def_id(node_id);
+                self.maybe_unused_trait_imports.insert(def_id);
+                import_ids.push(def_id);
+            }
+            self.add_to_glob_map(*import, trait_name);
+            kind = &binding.kind;
+        }
+        import_ids
+    }
+
+    fn new_disambiguated_key(&mut self, ident: Ident, ns: Namespace) -> BindingKey {
+        let ident = ident.normalize_to_macros_2_0();
+        let disambiguator = if ident.name == kw::Underscore {
+            self.underscore_disambiguator += 1;
+            self.underscore_disambiguator
+        } else {
+            0
+        };
+        BindingKey { ident, ns, disambiguator }
+    }
+
+    fn resolutions(&mut self, module: Module<'a>) -> &'a Resolutions<'a> {
+        if module.populate_on_access.get() {
+            module.populate_on_access.set(false);
+            self.build_reduced_graph_external(module);
+        }
+        &module.0.0.lazy_resolutions
+    }
+
+    fn resolution(
+        &mut self,
+        module: Module<'a>,
+        key: BindingKey,
+    ) -> &'a RefCell<NameResolution<'a>> {
+        *self
+            .resolutions(module)
+            .borrow_mut()
+            .entry(key)
+            .or_insert_with(|| self.arenas.alloc_name_resolution())
+    }
+
+    /// Test if AmbiguityError ambi is any identical to any one inside ambiguity_errors
+    fn matches_previous_ambiguity_error(&mut self, ambi: &AmbiguityError<'_>) -> bool {
+        for ambiguity_error in &self.ambiguity_errors {
+            // if the span location and ident as well as its span are the same
+            if ambiguity_error.kind == ambi.kind
+                && ambiguity_error.ident == ambi.ident
+                && ambiguity_error.ident.span == ambi.ident.span
+                && ambiguity_error.b1.span == ambi.b1.span
+                && ambiguity_error.b2.span == ambi.b2.span
+                && ambiguity_error.misc1 == ambi.misc1
+                && ambiguity_error.misc2 == ambi.misc2
+            {
+                return true;
+            }
+        }
+        false
+    }
+
+    fn record_use(&mut self, ident: Ident, used_binding: NameBinding<'a>, is_lexical_scope: bool) {
+        self.record_use_inner(ident, used_binding, is_lexical_scope, used_binding.warn_ambiguity);
+    }
+
+    fn record_use_inner(
+        &mut self,
+        ident: Ident,
+        used_binding: NameBinding<'a>,
+        is_lexical_scope: bool,
+        warn_ambiguity: bool,
+    ) {
+        if let Some((b2, kind)) = used_binding.ambiguity {
+            let ambiguity_error = AmbiguityError {
+                kind,
+                ident,
+                b1: used_binding,
+                b2,
+                misc1: AmbiguityErrorMisc::None,
+                misc2: AmbiguityErrorMisc::None,
+                warning: warn_ambiguity,
+            };
+            if !self.matches_previous_ambiguity_error(&ambiguity_error) {
+                // avoid duplicated span information to be emit out
+                self.ambiguity_errors.push(ambiguity_error);
+            }
+        }
+        if let NameBindingKind::Import { import, binding, ref used } = used_binding.kind {
+            // Avoid marking `extern crate` items that refer to a name from extern prelude,
+            // but not introduce it, as used if they are accessed from lexical scope.
+            if is_lexical_scope {
+                if let Some(entry) = self.extern_prelude.get(&ident.normalize_to_macros_2_0()) {
+                    if !entry.introduced_by_item && entry.binding == Some(used_binding) {
+                        return;
+                    }
+                }
+            }
+            used.set(true);
+            import.used.set(true);
+            if let Some(id) = import.id() {
+                self.used_imports.insert(id);
+            }
+            self.add_to_glob_map(import, ident);
+            self.record_use_inner(ident, binding, false, warn_ambiguity || binding.warn_ambiguity);
+        }
+    }
+
+    #[inline]
+    fn add_to_glob_map(&mut self, import: Import<'_>, ident: Ident) {
+        if let ImportKind::Glob { id, .. } = import.kind {
+            let def_id = self.local_def_id(id);
+            self.glob_map.entry(def_id).or_default().insert(ident.name);
+        }
+    }
+
+    fn resolve_crate_root(&mut self, ident: Ident) -> Module<'a> {
+        debug!("resolve_crate_root({:?})", ident);
+        let mut ctxt = ident.span.ctxt();
+        let mark = if ident.name == kw::DollarCrate {
+            // When resolving `$crate` from a `macro_rules!` invoked in a `macro`,
+            // we don't want to pretend that the `macro_rules!` definition is in the `macro`
+            // as described in `SyntaxContext::apply_mark`, so we ignore prepended opaque marks.
+            // FIXME: This is only a guess and it doesn't work correctly for `macro_rules!`
+            // definitions actually produced by `macro` and `macro` definitions produced by
+            // `macro_rules!`, but at least such configurations are not stable yet.
+            ctxt = ctxt.normalize_to_macro_rules();
+            debug!(
+                "resolve_crate_root: marks={:?}",
+                ctxt.marks().into_iter().map(|(i, t)| (i.expn_data(), t)).collect::<Vec<_>>()
+            );
+            let mut iter = ctxt.marks().into_iter().rev().peekable();
+            let mut result = None;
+            // Find the last opaque mark from the end if it exists.
+            while let Some(&(mark, transparency)) = iter.peek() {
+                if transparency == Transparency::Opaque {
+                    result = Some(mark);
+                    iter.next();
+                } else {
+                    break;
+                }
+            }
+            debug!(
+                "resolve_crate_root: found opaque mark {:?} {:?}",
+                result,
+                result.map(|r| r.expn_data())
+            );
+            // Then find the last semi-transparent mark from the end if it exists.
+            for (mark, transparency) in iter {
+                if transparency == Transparency::SemiTransparent {
+                    result = Some(mark);
+                } else {
+                    break;
+                }
+            }
+            debug!(
+                "resolve_crate_root: found semi-transparent mark {:?} {:?}",
+                result,
+                result.map(|r| r.expn_data())
+            );
+            result
+        } else {
+            debug!("resolve_crate_root: not DollarCrate");
+            ctxt = ctxt.normalize_to_macros_2_0();
+            ctxt.adjust(ExpnId::root())
+        };
+        let module = match mark {
+            Some(def) => self.expn_def_scope(def),
+            None => {
+                debug!(
+                    "resolve_crate_root({:?}): found no mark (ident.span = {:?})",
+                    ident, ident.span
+                );
+                return self.graph_root;
+            }
+        };
+        let module = self.expect_module(
+            module.opt_def_id().map_or(LOCAL_CRATE, |def_id| def_id.krate).as_def_id(),
+        );
+        debug!(
+            "resolve_crate_root({:?}): got module {:?} ({:?}) (ident.span = {:?})",
+            ident,
+            module,
+            module.kind.name(),
+            ident.span
+        );
+        module
+    }
+
+    fn resolve_self(&mut self, ctxt: &mut SyntaxContext, module: Module<'a>) -> Module<'a> {
+        let mut module = self.expect_module(module.nearest_parent_mod());
+        while module.span.ctxt().normalize_to_macros_2_0() != *ctxt {
+            let parent = module.parent.unwrap_or_else(|| self.expn_def_scope(ctxt.remove_mark()));
+            module = self.expect_module(parent.nearest_parent_mod());
+        }
+        module
+    }
+
+    fn record_partial_res(&mut self, node_id: NodeId, resolution: PartialRes) {
+        debug!("(recording res) recording {:?} for {}", resolution, node_id);
+        if let Some(prev_res) = self.partial_res_map.insert(node_id, resolution) {
+            panic!("path resolved multiple times ({prev_res:?} before, {resolution:?} now)");
+        }
+    }
+
+    fn record_pat_span(&mut self, node: NodeId, span: Span) {
+        debug!("(recording pat) recording {:?} for {:?}", node, span);
+        self.pat_span_map.insert(node, span);
+    }
+
+    fn is_accessible_from(
+        &self,
+        vis: ty::Visibility<impl Into<DefId>>,
+        module: Module<'a>,
+    ) -> bool {
+        vis.is_accessible_from(module.nearest_parent_mod(), self.tcx)
+    }
+
+    fn set_binding_parent_module(&mut self, binding: NameBinding<'a>, module: Module<'a>) {
+        if let Some(old_module) = self.binding_parent_modules.insert(binding, module) {
+            if module != old_module {
+                span_bug!(binding.span, "parent module is reset for binding");
+            }
+        }
+    }
+
+    fn disambiguate_macro_rules_vs_modularized(
+        &self,
+        macro_rules: NameBinding<'a>,
+        modularized: NameBinding<'a>,
+    ) -> bool {
+        // Some non-controversial subset of ambiguities "modularized macro name" vs "macro_rules"
+        // is disambiguated to mitigate regressions from macro modularization.
+        // Scoping for `macro_rules` behaves like scoping for `let` at module level, in general.
+        match (
+            self.binding_parent_modules.get(&macro_rules),
+            self.binding_parent_modules.get(&modularized),
+        ) {
+            (Some(macro_rules), Some(modularized)) => {
+                macro_rules.nearest_parent_mod() == modularized.nearest_parent_mod()
+                    && modularized.is_ancestor_of(*macro_rules)
+            }
+            _ => false,
+        }
+    }
+
+    fn extern_prelude_get(&mut self, ident: Ident, finalize: bool) -> Option<NameBinding<'a>> {
+        if ident.is_path_segment_keyword() {
+            // Make sure `self`, `super` etc produce an error when passed to here.
+            return None;
+        }
+
+        let norm_ident = ident.normalize_to_macros_2_0();
+        let binding = self.extern_prelude.get(&norm_ident).cloned().and_then(|entry| {
+            Some(if let Some(binding) = entry.binding {
+                if finalize {
+                    if !entry.is_import() {
+                        self.crate_loader(|c| c.process_path_extern(ident.name, ident.span));
+                    } else if entry.introduced_by_item {
+                        self.record_use(ident, binding, false);
+                    }
+                }
+                binding
+            } else {
+                let crate_id = if finalize {
+                    let Some(crate_id) =
+                        self.crate_loader(|c| c.process_path_extern(ident.name, ident.span))
+                    else {
+                        return Some(self.dummy_binding);
+                    };
+                    crate_id
+                } else {
+                    self.crate_loader(|c| c.maybe_process_path_extern(ident.name))?
+                };
+                let crate_root = self.expect_module(crate_id.as_def_id());
+                let vis = ty::Visibility::<DefId>::Public;
+                (crate_root, vis, DUMMY_SP, LocalExpnId::ROOT).to_name_binding(self.arenas)
+            })
+        });
+
+        if let Some(entry) = self.extern_prelude.get_mut(&norm_ident) {
+            entry.binding = binding;
+        }
+
+        binding
+    }
+
+    /// Rustdoc uses this to resolve doc link paths in a recoverable way. `PathResult<'a>`
+    /// isn't something that can be returned because it can't be made to live that long,
+    /// and also it's a private type. Fortunately rustdoc doesn't need to know the error,
+    /// just that an error occurred.
+    fn resolve_rustdoc_path(
+        &mut self,
+        path_str: &str,
+        ns: Namespace,
+        parent_scope: ParentScope<'a>,
+    ) -> Option<Res> {
+        let mut segments =
+            Vec::from_iter(path_str.split("::").map(Ident::from_str).map(Segment::from_ident));
+        if let Some(segment) = segments.first_mut() {
+            if segment.ident.name == kw::Empty {
+                segment.ident.name = kw::PathRoot;
+            }
+        }
+
+        match self.maybe_resolve_path(&segments, Some(ns), &parent_scope) {
+            PathResult::Module(ModuleOrUniformRoot::Module(module)) => Some(module.res().unwrap()),
+            PathResult::NonModule(path_res) => path_res.full_res(),
+            PathResult::Module(ModuleOrUniformRoot::ExternPrelude) | PathResult::Failed { .. } => {
+                None
+            }
+            PathResult::Module(..) | PathResult::Indeterminate => unreachable!(),
+        }
+    }
+
+    /// Retrieves definition span of the given `DefId`.
+    fn def_span(&self, def_id: DefId) -> Span {
+        match def_id.as_local() {
+            Some(def_id) => self.tcx.source_span(def_id),
+            // Query `def_span` is not used because hashing its result span is expensive.
+            None => self.cstore().def_span_untracked(def_id, self.tcx.sess),
+        }
+    }
+
+    fn field_def_ids(&self, def_id: DefId) -> Option<&'tcx [DefId]> {
+        match def_id.as_local() {
+            Some(def_id) => self.field_def_ids.get(&def_id).copied(),
+            None => Some(self.tcx.associated_item_def_ids(def_id)),
+        }
+    }
+
+    /// Checks if an expression refers to a function marked with
+    /// `#[rustc_legacy_const_generics]` and returns the argument index list
+    /// from the attribute.
+    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.
+            if path.segments.last().unwrap().args.is_some() {
+                return None;
+            }
+
+            let res = self.partial_res_map.get(&expr.id)?.full_res()?;
+            if let Res::Def(def::DefKind::Fn, def_id) = res {
+                // We only support cross-crate argument rewriting. Uses
+                // within the same crate should be updated to use the new
+                // const generics style.
+                if def_id.is_local() {
+                    return None;
+                }
+
+                if let Some(v) = self.legacy_const_generic_args.get(&def_id) {
+                    return v.clone();
+                }
+
+                let attr = self.tcx.get_attr(def_id, sym::rustc_legacy_const_generics)?;
+                let mut ret = Vec::new();
+                for meta in attr.meta_item_list()? {
+                    match meta.lit()?.kind {
+                        LitKind::Int(a, _) => ret.push(a as usize),
+                        _ => panic!("invalid arg index"),
+                    }
+                }
+                // Cache the lookup to avoid parsing attributes for an item multiple times.
+                self.legacy_const_generic_args.insert(def_id, Some(ret.clone()));
+                return Some(ret);
+            }
+        }
+        None
+    }
+
+    fn resolve_main(&mut self) {
+        let module = self.graph_root;
+        let ident = Ident::with_dummy_span(sym::main);
+        let parent_scope = &ParentScope::module(module, self);
+
+        let Ok(name_binding) = self.maybe_resolve_ident_in_module(
+            ModuleOrUniformRoot::Module(module),
+            ident,
+            ValueNS,
+            parent_scope,
+        ) else {
+            return;
+        };
+
+        let res = name_binding.res();
+        let is_import = name_binding.is_import();
+        let span = name_binding.span;
+        if let Res::Def(DefKind::Fn, _) = res {
+            self.record_use(ident, name_binding, false);
+        }
+        self.main_def = Some(MainDefinition { res, is_import, span });
+    }
+}
+
+fn names_to_string(names: &[Symbol]) -> String {
+    let mut result = String::new();
+    for (i, name) in names.iter().filter(|name| **name != kw::PathRoot).enumerate() {
+        if i > 0 {
+            result.push_str("::");
+        }
+        if Ident::with_dummy_span(*name).is_raw_guess() {
+            result.push_str("r#");
+        }
+        result.push_str(name.as_str());
+    }
+    result
+}
+
+fn path_names_to_string(path: &Path) -> String {
+    names_to_string(&path.segments.iter().map(|seg| seg.ident.name).collect::<Vec<_>>())
+}
+
+/// A somewhat inefficient routine to obtain the name of a module.
+fn module_to_string(module: Module<'_>) -> Option<String> {
+    let mut names = Vec::new();
+
+    fn collect_mod(names: &mut Vec<Symbol>, module: Module<'_>) {
+        if let ModuleKind::Def(.., name) = module.kind {
+            if let Some(parent) = module.parent {
+                names.push(name);
+                collect_mod(names, parent);
+            }
+        } else {
+            names.push(Symbol::intern("<opaque>"));
+            collect_mod(names, module.parent.unwrap());
+        }
+    }
+    collect_mod(&mut names, module);
+
+    if names.is_empty() {
+        return None;
+    }
+    names.reverse();
+    Some(names_to_string(&names))
+}
+
+#[derive(Copy, Clone, Debug)]
+struct Finalize {
+    /// Node ID for linting.
+    node_id: NodeId,
+    /// Span of the whole path or some its characteristic fragment.
+    /// E.g. span of `b` in `foo::{a, b, c}`, or full span for regular paths.
+    path_span: Span,
+    /// Span of the path start, suitable for prepending something to it.
+    /// E.g. span of `foo` in `foo::{a, b, c}`, or full span for regular paths.
+    root_span: Span,
+    /// Whether to report privacy errors or silently return "no resolution" for them,
+    /// similarly to speculative resolution.
+    report_private: bool,
+}
+
+impl Finalize {
+    fn new(node_id: NodeId, path_span: Span) -> Finalize {
+        Finalize::with_root_span(node_id, path_span, path_span)
+    }
+
+    fn with_root_span(node_id: NodeId, path_span: Span, root_span: Span) -> Finalize {
+        Finalize { node_id, path_span, root_span, report_private: true }
+    }
+}
+
+pub fn provide(providers: &mut Providers) {
+    providers.registered_tools = macros::registered_tools;
+}
diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs
new file mode 100644
index 00000000000..1c085ddf57b
--- /dev/null
+++ b/compiler/rustc_resolve/src/macros.rs
@@ -0,0 +1,964 @@
+//! A bunch of methods and structures more or less related to resolving macros and
+//! interface provided by `Resolver` to macro expander.
+
+use crate::errors::{
+    self, AddAsNonDerive, CannotDetermineMacroResolution, CannotFindIdentInThisScope,
+    MacroExpectedFound, RemoveSurroundingDerive,
+};
+use crate::Namespace::*;
+use crate::{BuiltinMacroState, Determinacy, MacroData};
+use crate::{DeriveData, Finalize, ParentScope, ResolutionError, Resolver, ScopeSet};
+use crate::{ModuleKind, ModuleOrUniformRoot, NameBinding, PathResult, Segment, ToNameBinding};
+use rustc_ast::expand::StrippedCfgItem;
+use rustc_ast::{self as ast, attr, Crate, Inline, ItemKind, ModKind, NodeId};
+use rustc_ast_pretty::pprust;
+use rustc_attr::StabilityLevel;
+use rustc_data_structures::intern::Interned;
+use rustc_data_structures::sync::Lrc;
+use rustc_errors::{struct_span_code_err, Applicability};
+use rustc_expand::base::{Annotatable, DeriveResolutions, Indeterminate, ResolverExpand};
+use rustc_expand::base::{SyntaxExtension, SyntaxExtensionKind};
+use rustc_expand::compile_declarative_macro;
+use rustc_expand::expand::{AstFragment, Invocation, InvocationKind, SupportsMacroExpansion};
+use rustc_hir::def::{self, DefKind, NonMacroAttrKind};
+use rustc_hir::def_id::{CrateNum, DefId, LocalDefId};
+use rustc_middle::middle::stability;
+use rustc_middle::ty::RegisteredTools;
+use rustc_middle::ty::{TyCtxt, Visibility};
+use rustc_session::lint::builtin::{
+    LEGACY_DERIVE_HELPERS, SOFT_UNSTABLE, UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
+};
+use rustc_session::lint::builtin::{UNUSED_MACROS, UNUSED_MACRO_RULES};
+use rustc_session::lint::BuiltinLintDiagnostics;
+use rustc_session::parse::feature_err;
+use rustc_span::edition::Edition;
+use rustc_span::hygiene::{self, ExpnData, ExpnKind, LocalExpnId};
+use rustc_span::hygiene::{AstPass, MacroKind};
+use rustc_span::symbol::{kw, sym, Ident, Symbol};
+use rustc_span::{Span, DUMMY_SP};
+use std::cell::Cell;
+use std::mem;
+
+type Res = def::Res<NodeId>;
+
+/// Binding produced by a `macro_rules` item.
+/// Not modularized, can shadow previous `macro_rules` bindings, etc.
+#[derive(Debug)]
+pub(crate) struct MacroRulesBinding<'a> {
+    pub(crate) binding: NameBinding<'a>,
+    /// `macro_rules` scope into which the `macro_rules` item was planted.
+    pub(crate) parent_macro_rules_scope: MacroRulesScopeRef<'a>,
+    pub(crate) ident: Ident,
+}
+
+/// The scope introduced by a `macro_rules!` macro.
+/// This starts at the macro's definition and ends at the end of the macro's parent
+/// module (named or unnamed), or even further if it escapes with `#[macro_use]`.
+/// Some macro invocations need to introduce `macro_rules` scopes too because they
+/// can potentially expand into macro definitions.
+#[derive(Copy, Clone, Debug)]
+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.
+    Binding(&'a MacroRulesBinding<'a>),
+    /// The scope introduced by a macro invocation that can potentially
+    /// create a `macro_rules!` macro definition.
+    Invocation(LocalExpnId),
+}
+
+/// `macro_rules!` scopes are always kept by reference and inside a cell.
+/// The reason is that we update scopes with value `MacroRulesScope::Invocation(invoc_id)`
+/// in-place after `invoc_id` gets expanded.
+/// This helps to avoid uncontrollable growth of `macro_rules!` scope chains,
+/// which usually grow linearly with the number of macro invocations
+/// in a module (including derives) and hurt performance.
+pub(crate) type MacroRulesScopeRef<'a> = Interned<'a, Cell<MacroRulesScope<'a>>>;
+
+/// Macro namespace is separated into two sub-namespaces, one for bang macros and
+/// one for attribute-like macros (attributes, derives).
+/// We ignore resolutions from one sub-namespace when searching names in scope for another.
+pub(crate) fn sub_namespace_match(
+    candidate: Option<MacroKind>,
+    requirement: Option<MacroKind>,
+) -> bool {
+    #[derive(PartialEq)]
+    enum SubNS {
+        Bang,
+        AttrLike,
+    }
+    let sub_ns = |kind| match kind {
+        MacroKind::Bang => SubNS::Bang,
+        MacroKind::Attr | MacroKind::Derive => SubNS::AttrLike,
+    };
+    let candidate = candidate.map(sub_ns);
+    let requirement = requirement.map(sub_ns);
+    // "No specific sub-namespace" means "matches anything" for both requirements and candidates.
+    candidate.is_none() || requirement.is_none() || candidate == requirement
+}
+
+// We don't want to format a path using pretty-printing,
+// `format!("{}", path)`, because that tries to insert
+// line-breaks and is slow.
+fn fast_print_path(path: &ast::Path) -> Symbol {
+    if path.segments.len() == 1 {
+        path.segments[0].ident.name
+    } else {
+        let mut path_str = String::with_capacity(64);
+        for (i, segment) in path.segments.iter().enumerate() {
+            if i != 0 {
+                path_str.push_str("::");
+            }
+            if segment.ident.name != kw::PathRoot {
+                path_str.push_str(segment.ident.as_str())
+            }
+        }
+        Symbol::intern(&path_str)
+    }
+}
+
+pub(crate) fn registered_tools(tcx: TyCtxt<'_>, (): ()) -> RegisteredTools {
+    let mut registered_tools = RegisteredTools::default();
+    let (_, pre_configured_attrs) = &*tcx.crate_for_resolver(()).borrow();
+    for attr in attr::filter_by_name(pre_configured_attrs, sym::register_tool) {
+        for nested_meta in attr.meta_item_list().unwrap_or_default() {
+            match nested_meta.ident() {
+                Some(ident) => {
+                    if let Some(old_ident) = registered_tools.replace(ident) {
+                        let msg = format!("{} `{}` was already registered", "tool", ident);
+                        tcx.dcx()
+                            .struct_span_err(ident.span, msg)
+                            .with_span_label(old_ident.span, "already registered here")
+                            .emit();
+                    }
+                }
+                None => {
+                    let msg = format!("`{}` only accepts identifiers", sym::register_tool);
+                    let span = nested_meta.span();
+                    tcx.dcx()
+                        .struct_span_err(span, msg)
+                        .with_span_label(span, "not an identifier")
+                        .emit();
+                }
+            }
+        }
+    }
+    // We implicitly add `rustfmt`, `clippy`, `diagnostic` to known tools,
+    // but it's not an error to register them explicitly.
+    let predefined_tools = [sym::clippy, sym::rustfmt, sym::diagnostic];
+    registered_tools.extend(predefined_tools.iter().cloned().map(Ident::with_dummy_span));
+    registered_tools
+}
+
+// Some feature gates for inner attributes are reported as lints for backward compatibility.
+fn soft_custom_inner_attributes_gate(path: &ast::Path, invoc: &Invocation) -> bool {
+    match &path.segments[..] {
+        // `#![test]`
+        [seg] if seg.ident.name == sym::test => return true,
+        // `#![rustfmt::skip]` on out-of-line modules
+        [seg1, seg2] if seg1.ident.name == sym::rustfmt && seg2.ident.name == sym::skip => {
+            if let InvocationKind::Attr { item, .. } = &invoc.kind {
+                if let Annotatable::Item(item) = item {
+                    if let ItemKind::Mod(_, ModKind::Loaded(_, Inline::No, _)) = item.kind {
+                        return true;
+                    }
+                }
+            }
+        }
+        _ => {}
+    }
+    false
+}
+
+impl<'a, 'tcx> ResolverExpand for Resolver<'a, 'tcx> {
+    fn next_node_id(&mut self) -> NodeId {
+        self.next_node_id()
+    }
+
+    fn invocation_parent(&self, id: LocalExpnId) -> LocalDefId {
+        self.invocation_parents[&id].0
+    }
+
+    fn resolve_dollar_crates(&mut self) {
+        hygiene::update_dollar_crate_names(|ctxt| {
+            let ident = Ident::new(kw::DollarCrate, DUMMY_SP.with_ctxt(ctxt));
+            match self.resolve_crate_root(ident).kind {
+                ModuleKind::Def(.., name) if name != kw::Empty => name,
+                _ => kw::Crate,
+            }
+        });
+    }
+
+    fn visit_ast_fragment_with_placeholders(
+        &mut self,
+        expansion: LocalExpnId,
+        fragment: &AstFragment,
+    ) {
+        // Integrate the new AST fragment into all the definition and module structures.
+        // We are inside the `expansion` now, but other parent scope components are still the same.
+        let parent_scope = ParentScope { expansion, ..self.invocation_parent_scopes[&expansion] };
+        let output_macro_rules_scope = self.build_reduced_graph(fragment, parent_scope);
+        self.output_macro_rules_scopes.insert(expansion, output_macro_rules_scope);
+
+        parent_scope.module.unexpanded_invocations.borrow_mut().remove(&expansion);
+    }
+
+    fn register_builtin_macro(&mut self, name: Symbol, ext: SyntaxExtensionKind) {
+        if self.builtin_macros.insert(name, BuiltinMacroState::NotYetSeen(ext)).is_some() {
+            self.dcx().bug(format!("built-in macro `{name}` was already registered"));
+        }
+    }
+
+    // Create a new Expansion with a definition site of the provided module, or
+    // a fake empty `#[no_implicit_prelude]` module if no module is provided.
+    fn expansion_for_ast_pass(
+        &mut self,
+        call_site: Span,
+        pass: AstPass,
+        features: &[Symbol],
+        parent_module_id: Option<NodeId>,
+    ) -> LocalExpnId {
+        let parent_module =
+            parent_module_id.map(|module_id| self.local_def_id(module_id).to_def_id());
+        let expn_id = LocalExpnId::fresh(
+            ExpnData::allow_unstable(
+                ExpnKind::AstPass(pass),
+                call_site,
+                self.tcx.sess.edition(),
+                features.into(),
+                None,
+                parent_module,
+            ),
+            self.create_stable_hashing_context(),
+        );
+
+        let parent_scope =
+            parent_module.map_or(self.empty_module, |def_id| self.expect_module(def_id));
+        self.ast_transform_scopes.insert(expn_id, parent_scope);
+
+        expn_id
+    }
+
+    fn resolve_imports(&mut self) {
+        self.resolve_imports()
+    }
+
+    fn resolve_macro_invocation(
+        &mut self,
+        invoc: &Invocation,
+        eager_expansion_root: LocalExpnId,
+        force: bool,
+    ) -> Result<Lrc<SyntaxExtension>, Indeterminate> {
+        let invoc_id = invoc.expansion_data.id;
+        let parent_scope = match self.invocation_parent_scopes.get(&invoc_id) {
+            Some(parent_scope) => *parent_scope,
+            None => {
+                // If there's no entry in the table, then we are resolving an eagerly expanded
+                // macro, which should inherit its parent scope from its eager expansion root -
+                // the macro that requested this eager expansion.
+                let parent_scope = *self
+                    .invocation_parent_scopes
+                    .get(&eager_expansion_root)
+                    .expect("non-eager expansion without a parent scope");
+                self.invocation_parent_scopes.insert(invoc_id, parent_scope);
+                parent_scope
+            }
+        };
+
+        let (path, kind, inner_attr, derives) = match invoc.kind {
+            InvocationKind::Attr { ref attr, ref derives, .. } => (
+                &attr.get_normal_item().path,
+                MacroKind::Attr,
+                attr.style == ast::AttrStyle::Inner,
+                self.arenas.alloc_ast_paths(derives),
+            ),
+            InvocationKind::Bang { ref mac, .. } => (&mac.path, MacroKind::Bang, false, &[][..]),
+            InvocationKind::Derive { ref path, .. } => (path, MacroKind::Derive, false, &[][..]),
+        };
+
+        // Derives are not included when `invocations` are collected, so we have to add them here.
+        let parent_scope = &ParentScope { derives, ..parent_scope };
+        let supports_macro_expansion = invoc.fragment_kind.supports_macro_expansion();
+        let node_id = invoc.expansion_data.lint_node_id;
+        let (ext, res) = self.smart_resolve_macro_path(
+            path,
+            kind,
+            supports_macro_expansion,
+            inner_attr,
+            parent_scope,
+            node_id,
+            force,
+            soft_custom_inner_attributes_gate(path, invoc),
+        )?;
+
+        let span = invoc.span();
+        let def_id = res.opt_def_id();
+        invoc_id.set_expn_data(
+            ext.expn_data(
+                parent_scope.expansion,
+                span,
+                fast_print_path(path),
+                def_id,
+                def_id.map(|def_id| self.macro_def_scope(def_id).nearest_parent_mod()),
+            ),
+            self.create_stable_hashing_context(),
+        );
+
+        Ok(ext)
+    }
+
+    fn record_macro_rule_usage(&mut self, id: NodeId, rule_i: usize) {
+        let did = self.local_def_id(id);
+        self.unused_macro_rules.remove(&(did, rule_i));
+    }
+
+    fn check_unused_macros(&mut self) {
+        for (_, &(node_id, ident)) in self.unused_macros.iter() {
+            self.lint_buffer.buffer_lint(
+                UNUSED_MACROS,
+                node_id,
+                ident.span,
+                format!("unused macro definition: `{}`", ident.name),
+            );
+        }
+        for (&(def_id, arm_i), &(ident, rule_span)) in self.unused_macro_rules.iter() {
+            if self.unused_macros.contains_key(&def_id) {
+                // We already lint the entire macro as unused
+                continue;
+            }
+            let node_id = self.def_id_to_node_id[def_id];
+            self.lint_buffer.buffer_lint(
+                UNUSED_MACRO_RULES,
+                node_id,
+                rule_span,
+                format!(
+                    "{} rule of macro `{}` is never used",
+                    crate::diagnostics::ordinalize(arm_i + 1),
+                    ident.name
+                ),
+            );
+        }
+    }
+
+    fn has_derive_copy(&self, expn_id: LocalExpnId) -> bool {
+        self.containers_deriving_copy.contains(&expn_id)
+    }
+
+    fn resolve_derives(
+        &mut self,
+        expn_id: LocalExpnId,
+        force: bool,
+        derive_paths: &dyn Fn() -> DeriveResolutions,
+    ) -> Result<(), Indeterminate> {
+        // Block expansion of the container until we resolve all derives in it.
+        // This is required for two reasons:
+        // - Derive helper attributes are in scope for the item to which the `#[derive]`
+        //   is applied, so they have to be produced by the container's expansion rather
+        //   than by individual derives.
+        // - Derives in the container need to know whether one of them is a built-in `Copy`.
+        // Temporarily take the data to avoid borrow checker conflicts.
+        let mut derive_data = mem::take(&mut self.derive_data);
+        let entry = derive_data.entry(expn_id).or_insert_with(|| DeriveData {
+            resolutions: derive_paths(),
+            helper_attrs: Vec::new(),
+            has_derive_copy: false,
+        });
+        let parent_scope = self.invocation_parent_scopes[&expn_id];
+        for (i, (path, _, opt_ext, _)) in entry.resolutions.iter_mut().enumerate() {
+            if opt_ext.is_none() {
+                *opt_ext = Some(
+                    match self.resolve_macro_path(
+                        path,
+                        Some(MacroKind::Derive),
+                        &parent_scope,
+                        true,
+                        force,
+                    ) {
+                        Ok((Some(ext), _)) => {
+                            if !ext.helper_attrs.is_empty() {
+                                let last_seg = path.segments.last().unwrap();
+                                let span = last_seg.ident.span.normalize_to_macros_2_0();
+                                entry.helper_attrs.extend(
+                                    ext.helper_attrs
+                                        .iter()
+                                        .map(|name| (i, Ident::new(*name, span))),
+                                );
+                            }
+                            entry.has_derive_copy |= ext.builtin_name == Some(sym::Copy);
+                            ext
+                        }
+                        Ok(_) | Err(Determinacy::Determined) => self.dummy_ext(MacroKind::Derive),
+                        Err(Determinacy::Undetermined) => {
+                            assert!(self.derive_data.is_empty());
+                            self.derive_data = derive_data;
+                            return Err(Indeterminate);
+                        }
+                    },
+                );
+            }
+        }
+        // Sort helpers in a stable way independent from the derive resolution order.
+        entry.helper_attrs.sort_by_key(|(i, _)| *i);
+        let helper_attrs = entry
+            .helper_attrs
+            .iter()
+            .map(|(_, ident)| {
+                let res = Res::NonMacroAttr(NonMacroAttrKind::DeriveHelper);
+                let binding = (res, Visibility::<DefId>::Public, ident.span, expn_id)
+                    .to_name_binding(self.arenas);
+                (*ident, binding)
+            })
+            .collect();
+        self.helper_attrs.insert(expn_id, helper_attrs);
+        // Mark this derive as having `Copy` either if it has `Copy` itself or if its parent derive
+        // has `Copy`, to support cases like `#[derive(Clone, Copy)] #[derive(Debug)]`.
+        if entry.has_derive_copy || self.has_derive_copy(parent_scope.expansion) {
+            self.containers_deriving_copy.insert(expn_id);
+        }
+        assert!(self.derive_data.is_empty());
+        self.derive_data = derive_data;
+        Ok(())
+    }
+
+    fn take_derive_resolutions(&mut self, expn_id: LocalExpnId) -> Option<DeriveResolutions> {
+        self.derive_data.remove(&expn_id).map(|data| data.resolutions)
+    }
+
+    // The function that implements the resolution logic of `#[cfg_accessible(path)]`.
+    // Returns true if the path can certainly be resolved in one of three namespaces,
+    // returns false if the path certainly cannot be resolved in any of the three namespaces.
+    // Returns `Indeterminate` if we cannot give a certain answer yet.
+    fn cfg_accessible(
+        &mut self,
+        expn_id: LocalExpnId,
+        path: &ast::Path,
+    ) -> Result<bool, Indeterminate> {
+        let span = path.span;
+        let path = &Segment::from_path(path);
+        let parent_scope = self.invocation_parent_scopes[&expn_id];
+
+        let mut indeterminate = false;
+        for ns in [TypeNS, ValueNS, MacroNS].iter().copied() {
+            match self.maybe_resolve_path(path, Some(ns), &parent_scope) {
+                PathResult::Module(ModuleOrUniformRoot::Module(_)) => return Ok(true),
+                PathResult::NonModule(partial_res) if partial_res.unresolved_segments() == 0 => {
+                    return Ok(true);
+                }
+                PathResult::NonModule(..) |
+                // HACK(Urgau): This shouldn't be necessary
+                PathResult::Failed { is_error_from_last_segment: false, .. } => {
+                    self.dcx()
+                        .emit_err(errors::CfgAccessibleUnsure { span });
+
+                    // If we get a partially resolved NonModule in one namespace, we should get the
+                    // same result in any other namespaces, so we can return early.
+                    return Ok(false);
+                }
+                PathResult::Indeterminate => indeterminate = true,
+                // We can only be sure that a path doesn't exist after having tested all the
+                // possibilities, only at that time we can return false.
+                PathResult::Failed { .. } => {}
+                PathResult::Module(_) => panic!("unexpected path resolution"),
+            }
+        }
+
+        if indeterminate {
+            return Err(Indeterminate);
+        }
+
+        Ok(false)
+    }
+
+    fn get_proc_macro_quoted_span(&self, krate: CrateNum, id: usize) -> Span {
+        self.cstore().get_proc_macro_quoted_span_untracked(krate, id, self.tcx.sess)
+    }
+
+    fn declare_proc_macro(&mut self, id: NodeId) {
+        self.proc_macros.push(id)
+    }
+
+    fn append_stripped_cfg_item(&mut self, parent_node: NodeId, name: Ident, cfg: ast::MetaItem) {
+        self.stripped_cfg_items.push(StrippedCfgItem { parent_module: parent_node, name, cfg });
+    }
+
+    fn registered_tools(&self) -> &RegisteredTools {
+        self.registered_tools
+    }
+}
+
+impl<'a, 'tcx> Resolver<'a, 'tcx> {
+    /// Resolve macro path with error reporting and recovery.
+    /// Uses dummy syntax extensions for unresolved macros or macros with unexpected resolutions
+    /// for better error recovery.
+    fn smart_resolve_macro_path(
+        &mut self,
+        path: &ast::Path,
+        kind: MacroKind,
+        supports_macro_expansion: SupportsMacroExpansion,
+        inner_attr: bool,
+        parent_scope: &ParentScope<'a>,
+        node_id: NodeId,
+        force: bool,
+        soft_custom_inner_attributes_gate: bool,
+    ) -> Result<(Lrc<SyntaxExtension>, Res), Indeterminate> {
+        let (ext, res) = match self.resolve_macro_path(path, Some(kind), parent_scope, true, force)
+        {
+            Ok((Some(ext), res)) => (ext, res),
+            Ok((None, res)) => (self.dummy_ext(kind), res),
+            Err(Determinacy::Determined) => (self.dummy_ext(kind), Res::Err),
+            Err(Determinacy::Undetermined) => return Err(Indeterminate),
+        };
+
+        // Report errors for the resolved macro.
+        for segment in &path.segments {
+            if let Some(args) = &segment.args {
+                self.dcx().span_err(args.span(), "generic arguments in macro path");
+            }
+            if kind == MacroKind::Attr && segment.ident.as_str().starts_with("rustc") {
+                self.dcx().span_err(
+                    segment.ident.span,
+                    "attributes starting with `rustc` are reserved for use by the `rustc` compiler",
+                );
+            }
+        }
+
+        match res {
+            Res::Def(DefKind::Macro(_), def_id) => {
+                if let Some(def_id) = def_id.as_local() {
+                    self.unused_macros.remove(&def_id);
+                    if self.proc_macro_stubs.contains(&def_id) {
+                        self.dcx().emit_err(errors::ProcMacroSameCrate {
+                            span: path.span,
+                            is_test: self.tcx.sess.is_test_crate(),
+                        });
+                    }
+                }
+            }
+            Res::NonMacroAttr(..) | Res::Err => {}
+            _ => panic!("expected `DefKind::Macro` or `Res::NonMacroAttr`"),
+        };
+
+        self.check_stability_and_deprecation(&ext, path, node_id);
+
+        let unexpected_res = if ext.macro_kind() != kind {
+            Some((kind.article(), kind.descr_expected()))
+        } else if matches!(res, Res::Def(..)) {
+            match supports_macro_expansion {
+                SupportsMacroExpansion::No => Some(("a", "non-macro attribute")),
+                SupportsMacroExpansion::Yes { supports_inner_attrs } => {
+                    if inner_attr && !supports_inner_attrs {
+                        Some(("a", "non-macro inner attribute"))
+                    } else {
+                        None
+                    }
+                }
+            }
+        } else {
+            None
+        };
+        if let Some((article, expected)) = unexpected_res {
+            let path_str = pprust::path_to_string(path);
+
+            let mut err = MacroExpectedFound {
+                span: path.span,
+                expected,
+                found: res.descr(),
+                macro_path: &path_str,
+                ..Default::default() // Subdiagnostics default to None
+            };
+
+            // Suggest moving the macro out of the derive() if the macro isn't Derive
+            if !path.span.from_expansion()
+                && kind == MacroKind::Derive
+                && ext.macro_kind() != MacroKind::Derive
+            {
+                err.remove_surrounding_derive = Some(RemoveSurroundingDerive { span: path.span });
+                err.add_as_non_derive = Some(AddAsNonDerive { macro_path: &path_str });
+            }
+
+            self.dcx()
+                .create_err(err)
+                .with_span_label(path.span, format!("not {article} {expected}"))
+                .emit();
+
+            return Ok((self.dummy_ext(kind), Res::Err));
+        }
+
+        // We are trying to avoid reporting this error if other related errors were reported.
+        if res != Res::Err && inner_attr && !self.tcx.features().custom_inner_attributes {
+            let msg = match res {
+                Res::Def(..) => "inner macro attributes are unstable",
+                Res::NonMacroAttr(..) => "custom inner attributes are unstable",
+                _ => unreachable!(),
+            };
+            if soft_custom_inner_attributes_gate {
+                self.tcx.sess.parse_sess.buffer_lint(SOFT_UNSTABLE, path.span, node_id, msg);
+            } else {
+                feature_err(&self.tcx.sess, sym::custom_inner_attributes, path.span, msg).emit();
+            }
+        }
+
+        if res == Res::NonMacroAttr(NonMacroAttrKind::Tool)
+            && path.segments.len() >= 2
+            && path.segments[0].ident.name == sym::diagnostic
+            && path.segments[1].ident.name != sym::on_unimplemented
+        {
+            self.tcx.sess.parse_sess.buffer_lint(
+                UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
+                path.segments[1].span(),
+                node_id,
+                "unknown diagnostic attribute",
+            );
+        }
+
+        Ok((ext, res))
+    }
+
+    pub(crate) fn resolve_macro_path(
+        &mut self,
+        path: &ast::Path,
+        kind: Option<MacroKind>,
+        parent_scope: &ParentScope<'a>,
+        trace: bool,
+        force: bool,
+    ) -> Result<(Option<Lrc<SyntaxExtension>>, Res), Determinacy> {
+        let path_span = path.span;
+        let mut path = Segment::from_path(path);
+
+        // Possibly apply the macro helper hack
+        if kind == Some(MacroKind::Bang)
+            && path.len() == 1
+            && path[0].ident.span.ctxt().outer_expn_data().local_inner_macros
+        {
+            let root = Ident::new(kw::DollarCrate, path[0].ident.span);
+            path.insert(0, Segment::from_ident(root));
+        }
+
+        let res = if path.len() > 1 {
+            let res = match self.maybe_resolve_path(&path, Some(MacroNS), parent_scope) {
+                PathResult::NonModule(path_res) if let Some(res) = path_res.full_res() => Ok(res),
+                PathResult::Indeterminate if !force => return Err(Determinacy::Undetermined),
+                PathResult::NonModule(..)
+                | PathResult::Indeterminate
+                | PathResult::Failed { .. } => Err(Determinacy::Determined),
+                PathResult::Module(..) => unreachable!(),
+            };
+
+            if trace {
+                let kind = kind.expect("macro kind must be specified if tracing is enabled");
+                self.multi_segment_macro_resolutions.push((
+                    path,
+                    path_span,
+                    kind,
+                    *parent_scope,
+                    res.ok(),
+                ));
+            }
+
+            self.prohibit_imported_non_macro_attrs(None, res.ok(), path_span);
+            res
+        } else {
+            let scope_set = kind.map_or(ScopeSet::All(MacroNS), ScopeSet::Macro);
+            let binding = self.early_resolve_ident_in_lexical_scope(
+                path[0].ident,
+                scope_set,
+                parent_scope,
+                None,
+                force,
+                None,
+            );
+            if let Err(Determinacy::Undetermined) = binding {
+                return Err(Determinacy::Undetermined);
+            }
+
+            if trace {
+                let kind = kind.expect("macro kind must be specified if tracing is enabled");
+                self.single_segment_macro_resolutions.push((
+                    path[0].ident,
+                    kind,
+                    *parent_scope,
+                    binding.ok(),
+                ));
+            }
+
+            let res = binding.map(|binding| binding.res());
+            self.prohibit_imported_non_macro_attrs(binding.ok(), res.ok(), path_span);
+            res
+        };
+
+        res.map(|res| (self.get_macro(res).map(|macro_data| macro_data.ext.clone()), res))
+    }
+
+    pub(crate) fn finalize_macro_resolutions(&mut self, krate: &Crate) {
+        let check_consistency = |this: &mut Self,
+                                 path: &[Segment],
+                                 span,
+                                 kind: MacroKind,
+                                 initial_res: Option<Res>,
+                                 res: Res| {
+            if let Some(initial_res) = initial_res {
+                if res != initial_res {
+                    // Make sure compilation does not succeed if preferred macro resolution
+                    // has changed after the macro had been expanded. In theory all such
+                    // situations should be reported as errors, so this is a bug.
+                    this.dcx().span_delayed_bug(span, "inconsistent resolution for a macro");
+                }
+            } else {
+                // It's possible that the macro was unresolved (indeterminate) and silently
+                // expanded into a dummy fragment for recovery during expansion.
+                // Now, post-expansion, the resolution may succeed, but we can't change the
+                // past and need to report an error.
+                // However, non-speculative `resolve_path` can successfully return private items
+                // even if speculative `resolve_path` returned nothing previously, so we skip this
+                // less informative error if the privacy error is reported elsewhere.
+                if this.privacy_errors.is_empty() {
+                    this.dcx().emit_err(CannotDetermineMacroResolution {
+                        span,
+                        kind: kind.descr(),
+                        path: Segment::names_to_string(path),
+                    });
+                }
+            }
+        };
+
+        let macro_resolutions = mem::take(&mut self.multi_segment_macro_resolutions);
+        for (mut path, path_span, kind, parent_scope, initial_res) in macro_resolutions {
+            // FIXME: Path resolution will ICE if segment IDs present.
+            for seg in &mut path {
+                seg.id = None;
+            }
+            match self.resolve_path(
+                &path,
+                Some(MacroNS),
+                &parent_scope,
+                Some(Finalize::new(ast::CRATE_NODE_ID, path_span)),
+                None,
+            ) {
+                PathResult::NonModule(path_res) if let Some(res) = path_res.full_res() => {
+                    check_consistency(self, &path, path_span, kind, initial_res, res)
+                }
+                path_res @ (PathResult::NonModule(..) | PathResult::Failed { .. }) => {
+                    let mut suggestion = None;
+                    let (span, label, module) =
+                        if let PathResult::Failed { span, label, module, .. } = path_res {
+                            // try to suggest if it's not a macro, maybe a function
+                            if let PathResult::NonModule(partial_res) =
+                                self.maybe_resolve_path(&path, Some(ValueNS), &parent_scope)
+                                && partial_res.unresolved_segments() == 0
+                            {
+                                let sm = self.tcx.sess.source_map();
+                                let exclamation_span = sm.next_point(span);
+                                suggestion = Some((
+                                    vec![(exclamation_span, "".to_string())],
+                                    format!(
+                                        "{} is not a macro, but a {}, try to remove `!`",
+                                        Segment::names_to_string(&path),
+                                        partial_res.base_res().descr()
+                                    ),
+                                    Applicability::MaybeIncorrect,
+                                ));
+                            }
+                            (span, label, module)
+                        } else {
+                            (
+                                path_span,
+                                format!(
+                                    "partially resolved path in {} {}",
+                                    kind.article(),
+                                    kind.descr()
+                                ),
+                                None,
+                            )
+                        };
+                    self.report_error(
+                        span,
+                        ResolutionError::FailedToResolve {
+                            segment: path.last().map(|segment| segment.ident.name),
+                            label,
+                            suggestion,
+                            module,
+                        },
+                    );
+                }
+                PathResult::Module(..) | PathResult::Indeterminate => unreachable!(),
+            }
+        }
+
+        let macro_resolutions = mem::take(&mut self.single_segment_macro_resolutions);
+        for (ident, kind, parent_scope, initial_binding) in macro_resolutions {
+            match self.early_resolve_ident_in_lexical_scope(
+                ident,
+                ScopeSet::Macro(kind),
+                &parent_scope,
+                Some(Finalize::new(ast::CRATE_NODE_ID, ident.span)),
+                true,
+                None,
+            ) {
+                Ok(binding) => {
+                    let initial_res = initial_binding.map(|initial_binding| {
+                        self.record_use(ident, initial_binding, false);
+                        initial_binding.res()
+                    });
+                    let res = binding.res();
+                    let seg = Segment::from_ident(ident);
+                    check_consistency(self, &[seg], ident.span, kind, initial_res, res);
+                    if res == Res::NonMacroAttr(NonMacroAttrKind::DeriveHelperCompat) {
+                        let node_id = self
+                            .invocation_parents
+                            .get(&parent_scope.expansion)
+                            .map_or(ast::CRATE_NODE_ID, |id| self.def_id_to_node_id[id.0]);
+                        self.lint_buffer.buffer_lint_with_diagnostic(
+                            LEGACY_DERIVE_HELPERS,
+                            node_id,
+                            ident.span,
+                            "derive helper attribute is used before it is introduced",
+                            BuiltinLintDiagnostics::LegacyDeriveHelpers(binding.span),
+                        );
+                    }
+                }
+                Err(..) => {
+                    let expected = kind.descr_expected();
+
+                    let mut err = self.dcx().create_err(CannotFindIdentInThisScope {
+                        span: ident.span,
+                        expected,
+                        ident,
+                    });
+                    self.unresolved_macro_suggestions(&mut err, kind, &parent_scope, ident, krate);
+                    err.emit();
+                }
+            }
+        }
+
+        let builtin_attrs = mem::take(&mut self.builtin_attrs);
+        for (ident, parent_scope) in builtin_attrs {
+            let _ = self.early_resolve_ident_in_lexical_scope(
+                ident,
+                ScopeSet::Macro(MacroKind::Attr),
+                &parent_scope,
+                Some(Finalize::new(ast::CRATE_NODE_ID, ident.span)),
+                true,
+                None,
+            );
+        }
+    }
+
+    fn check_stability_and_deprecation(
+        &mut self,
+        ext: &SyntaxExtension,
+        path: &ast::Path,
+        node_id: NodeId,
+    ) {
+        let span = path.span;
+        if let Some(stability) = &ext.stability {
+            if let StabilityLevel::Unstable { reason, issue, is_soft, implied_by } = stability.level
+            {
+                let feature = stability.feature;
+
+                let is_allowed = |feature| {
+                    self.declared_features.contains(&feature) || span.allows_unstable(feature)
+                };
+                let allowed_by_implication = implied_by.is_some_and(|feature| is_allowed(feature));
+                if !is_allowed(feature) && !allowed_by_implication {
+                    let lint_buffer = &mut self.lint_buffer;
+                    let soft_handler =
+                        |lint, span, msg: String| lint_buffer.buffer_lint(lint, node_id, span, msg);
+                    stability::report_unstable(
+                        self.tcx.sess,
+                        feature,
+                        reason.to_opt_reason(),
+                        issue,
+                        None,
+                        is_soft,
+                        span,
+                        soft_handler,
+                    );
+                }
+            }
+        }
+        if let Some(depr) = &ext.deprecation {
+            let path = pprust::path_to_string(path);
+            let (message, lint) = stability::deprecation_message_and_lint(depr, "macro", &path);
+            stability::early_report_deprecation(
+                &mut self.lint_buffer,
+                message,
+                depr.suggestion,
+                lint,
+                span,
+                node_id,
+            );
+        }
+    }
+
+    fn prohibit_imported_non_macro_attrs(
+        &self,
+        binding: Option<NameBinding<'a>>,
+        res: Option<Res>,
+        span: Span,
+    ) {
+        if let Some(Res::NonMacroAttr(kind)) = res {
+            if kind != NonMacroAttrKind::Tool && binding.map_or(true, |b| b.is_import()) {
+                let msg =
+                    format!("cannot use {} {} through an import", kind.article(), kind.descr());
+                let mut err = self.dcx().struct_span_err(span, msg);
+                if let Some(binding) = binding {
+                    err.span_note(binding.span, format!("the {} imported here", kind.descr()));
+                }
+                err.emit();
+            }
+        }
+    }
+
+    pub(crate) fn check_reserved_macro_name(&mut self, ident: Ident, res: Res) {
+        // Reserve some names that are not quite covered by the general check
+        // performed on `Resolver::builtin_attrs`.
+        if ident.name == sym::cfg || ident.name == sym::cfg_attr {
+            let macro_kind = self.get_macro(res).map(|macro_data| macro_data.ext.macro_kind());
+            if macro_kind.is_some() && sub_namespace_match(macro_kind, Some(MacroKind::Attr)) {
+                self.dcx().span_err(
+                    ident.span,
+                    format!("name `{ident}` is reserved in attribute namespace"),
+                );
+            }
+        }
+    }
+
+    /// Compile the macro into a `SyntaxExtension` and its rule spans.
+    ///
+    /// Possibly replace its expander to a pre-defined one for built-in macros.
+    pub(crate) fn compile_macro(&mut self, item: &ast::Item, edition: Edition) -> MacroData {
+        let (mut ext, mut rule_spans) =
+            compile_declarative_macro(self.tcx.sess, self.tcx.features(), item, edition);
+
+        if let Some(builtin_name) = ext.builtin_name {
+            // The macro was marked with `#[rustc_builtin_macro]`.
+            if let Some(builtin_macro) = self.builtin_macros.get_mut(&builtin_name) {
+                // The macro is a built-in, replace its expander function
+                // while still taking everything else from the source code.
+                // If we already loaded this builtin macro, give a better error message than 'no such builtin macro'.
+                match mem::replace(builtin_macro, BuiltinMacroState::AlreadySeen(item.span)) {
+                    BuiltinMacroState::NotYetSeen(builtin_ext) => {
+                        ext.kind = builtin_ext;
+                        rule_spans = Vec::new();
+                    }
+                    BuiltinMacroState::AlreadySeen(span) => {
+                        struct_span_code_err!(
+                            self.dcx(),
+                            item.span,
+                            E0773,
+                            "attempted to define built-in macro more than once"
+                        )
+                        .with_span_note(span, "previously defined here")
+                        .emit();
+                    }
+                }
+            } else {
+                let msg = format!("cannot find a built-in macro with name `{}`", item.ident);
+                self.dcx().span_err(item.span, msg);
+            }
+        }
+
+        let ItemKind::MacroDef(def) = &item.kind else { unreachable!() };
+        MacroData { ext: Lrc::new(ext), rule_spans, macro_rules: def.macro_rules }
+    }
+}
diff --git a/compiler/rustc_resolve/src/rustdoc.rs b/compiler/rustc_resolve/src/rustdoc.rs
new file mode 100644
index 00000000000..4ff4ccf5e98
--- /dev/null
+++ b/compiler/rustc_resolve/src/rustdoc.rs
@@ -0,0 +1,552 @@
+use pulldown_cmark::{BrokenLink, CowStr, Event, LinkType, Options, Parser, Tag};
+use rustc_ast as ast;
+use rustc_ast::util::comments::beautify_doc_string;
+use rustc_data_structures::fx::FxHashMap;
+use rustc_middle::ty::TyCtxt;
+use rustc_span::def_id::DefId;
+use rustc_span::symbol::{kw, sym, Symbol};
+use rustc_span::{InnerSpan, Span, DUMMY_SP};
+use std::ops::Range;
+use std::{cmp, mem};
+
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+pub enum DocFragmentKind {
+    /// A doc fragment created from a `///` or `//!` doc comment.
+    SugaredDoc,
+    /// A doc fragment created from a "raw" `#[doc=""]` attribute.
+    RawDoc,
+}
+
+/// A portion of documentation, extracted from a `#[doc]` attribute.
+///
+/// Each variant contains the line number within the complete doc-comment where the fragment
+/// starts, as well as the Span where the corresponding doc comment or attribute is located.
+///
+/// Included files are kept separate from inline doc comments so that proper line-number
+/// information can be given when a doctest fails. Sugared doc comments and "raw" doc comments are
+/// kept separate because of issue #42760.
+#[derive(Clone, PartialEq, Eq, Debug)]
+pub struct DocFragment {
+    pub span: Span,
+    /// The item this doc-comment came from.
+    /// Used to determine the scope in which doc links in this fragment are resolved.
+    /// Typically filled for reexport docs when they are merged into the docs of the
+    /// original reexported item.
+    /// If the id is not filled, which happens for the original reexported item, then
+    /// it has to be taken from somewhere else during doc link resolution.
+    pub item_id: Option<DefId>,
+    pub doc: Symbol,
+    pub kind: DocFragmentKind,
+    pub indent: usize,
+}
+
+#[derive(Clone, Copy, Debug)]
+pub enum MalformedGenerics {
+    /// This link has unbalanced angle brackets.
+    ///
+    /// For example, `Vec<T` should trigger this, as should `Vec<T>>`.
+    UnbalancedAngleBrackets,
+    /// The generics are not attached to a type.
+    ///
+    /// For example, `<T>` should trigger this.
+    ///
+    /// This is detected by checking if the path is empty after the generics are stripped.
+    MissingType,
+    /// The link uses fully-qualified syntax, which is currently unsupported.
+    ///
+    /// For example, `<Vec as IntoIterator>::into_iter` should trigger this.
+    ///
+    /// This is detected by checking if ` as ` (the keyword `as` with spaces around it) is inside
+    /// angle brackets.
+    HasFullyQualifiedSyntax,
+    /// The link has an invalid path separator.
+    ///
+    /// For example, `Vec:<T>:new()` should trigger this. Note that `Vec:new()` will **not**
+    /// trigger this because it has no generics and thus [`strip_generics_from_path`] will not be
+    /// called.
+    ///
+    /// Note that this will also **not** be triggered if the invalid path separator is inside angle
+    /// brackets because rustdoc mostly ignores what's inside angle brackets (except for
+    /// [`HasFullyQualifiedSyntax`](MalformedGenerics::HasFullyQualifiedSyntax)).
+    ///
+    /// This is detected by checking if there is a colon followed by a non-colon in the link.
+    InvalidPathSeparator,
+    /// The link has too many angle brackets.
+    ///
+    /// For example, `Vec<<T>>` should trigger this.
+    TooManyAngleBrackets,
+    /// The link has empty angle brackets.
+    ///
+    /// For example, `Vec<>` should trigger this.
+    EmptyAngleBrackets,
+}
+
+/// Removes excess indentation on comments in order for the Markdown
+/// to be parsed correctly. This is necessary because the convention for
+/// writing documentation is to provide a space between the /// or //! marker
+/// and the doc text, but Markdown is whitespace-sensitive. For example,
+/// a block of text with four-space indentation is parsed as a code block,
+/// so if we didn't unindent comments, these list items
+///
+/// /// A list:
+/// ///
+/// ///    - Foo
+/// ///    - Bar
+///
+/// would be parsed as if they were in a code block, which is likely not what the user intended.
+pub fn unindent_doc_fragments(docs: &mut [DocFragment]) {
+    // `add` is used in case the most common sugared doc syntax is used ("/// "). The other
+    // fragments kind's lines are never starting with a whitespace unless they are using some
+    // markdown formatting requiring it. Therefore, if the doc block have a mix between the two,
+    // we need to take into account the fact that the minimum indent minus one (to take this
+    // whitespace into account).
+    //
+    // For example:
+    //
+    // /// hello!
+    // #[doc = "another"]
+    //
+    // In this case, you want "hello! another" and not "hello!  another".
+    let add = if docs.windows(2).any(|arr| arr[0].kind != arr[1].kind)
+        && docs.iter().any(|d| d.kind == DocFragmentKind::SugaredDoc)
+    {
+        // In case we have a mix of sugared doc comments and "raw" ones, we want the sugared one to
+        // "decide" how much the minimum indent will be.
+        1
+    } else {
+        0
+    };
+
+    // `min_indent` is used to know how much whitespaces from the start of each lines must be
+    // removed. Example:
+    //
+    // ///     hello!
+    // #[doc = "another"]
+    //
+    // In here, the `min_indent` is 1 (because non-sugared fragment are always counted with minimum
+    // 1 whitespace), meaning that "hello!" will be considered a codeblock because it starts with 4
+    // (5 - 1) whitespaces.
+    let Some(min_indent) = docs
+        .iter()
+        .map(|fragment| {
+            fragment.doc.as_str().lines().fold(usize::MAX, |min_indent, line| {
+                if line.chars().all(|c| c.is_whitespace()) {
+                    min_indent
+                } else {
+                    // Compare against either space or tab, ignoring whether they are
+                    // mixed or not.
+                    let whitespace = line.chars().take_while(|c| *c == ' ' || *c == '\t').count();
+                    cmp::min(min_indent, whitespace)
+                        + if fragment.kind == DocFragmentKind::SugaredDoc { 0 } else { add }
+                }
+            })
+        })
+        .min()
+    else {
+        return;
+    };
+
+    for fragment in docs {
+        if fragment.doc == kw::Empty {
+            continue;
+        }
+
+        let min_indent = if fragment.kind != DocFragmentKind::SugaredDoc && min_indent > 0 {
+            min_indent - add
+        } else {
+            min_indent
+        };
+
+        fragment.indent = min_indent;
+    }
+}
+
+/// The goal of this function is to apply the `DocFragment` transformation that is required when
+/// transforming into the final Markdown, which is applying the computed indent to each line in
+/// each doc fragment (a `DocFragment` can contain multiple lines in case of `#[doc = ""]`).
+///
+/// Note: remove the trailing newline where appropriate
+pub fn add_doc_fragment(out: &mut String, frag: &DocFragment) {
+    let s = frag.doc.as_str();
+    let mut iter = s.lines();
+    if s.is_empty() {
+        out.push('\n');
+        return;
+    }
+    while let Some(line) = iter.next() {
+        if line.chars().any(|c| !c.is_whitespace()) {
+            assert!(line.len() >= frag.indent);
+            out.push_str(&line[frag.indent..]);
+        } else {
+            out.push_str(line);
+        }
+        out.push('\n');
+    }
+}
+
+pub fn attrs_to_doc_fragments<'a>(
+    attrs: impl Iterator<Item = (&'a ast::Attribute, Option<DefId>)>,
+    doc_only: bool,
+) -> (Vec<DocFragment>, ast::AttrVec) {
+    let mut doc_fragments = Vec::new();
+    let mut other_attrs = ast::AttrVec::new();
+    for (attr, item_id) in attrs {
+        if let Some((doc_str, comment_kind)) = attr.doc_str_and_comment_kind() {
+            let doc = beautify_doc_string(doc_str, comment_kind);
+            let kind = if attr.is_doc_comment() {
+                DocFragmentKind::SugaredDoc
+            } else {
+                DocFragmentKind::RawDoc
+            };
+            let fragment = DocFragment { span: attr.span, doc, kind, item_id, indent: 0 };
+            doc_fragments.push(fragment);
+        } else if !doc_only {
+            other_attrs.push(attr.clone());
+        }
+    }
+
+    unindent_doc_fragments(&mut doc_fragments);
+
+    (doc_fragments, other_attrs)
+}
+
+/// Return the doc-comments on this item, grouped by the module they came from.
+/// The module can be different if this is a re-export with added documentation.
+///
+/// The last newline is not trimmed so the produced strings are reusable between
+/// early and late doc link resolution regardless of their position.
+pub fn prepare_to_doc_link_resolution(
+    doc_fragments: &[DocFragment],
+) -> FxHashMap<Option<DefId>, String> {
+    let mut res = FxHashMap::default();
+    for fragment in doc_fragments {
+        let out_str = res.entry(fragment.item_id).or_default();
+        add_doc_fragment(out_str, fragment);
+    }
+    res
+}
+
+/// Options for rendering Markdown in the main body of documentation.
+pub fn main_body_opts() -> Options {
+    Options::ENABLE_TABLES
+        | Options::ENABLE_FOOTNOTES
+        | Options::ENABLE_STRIKETHROUGH
+        | Options::ENABLE_TASKLISTS
+        | Options::ENABLE_SMART_PUNCTUATION
+}
+
+fn strip_generics_from_path_segment(segment: Vec<char>) -> Result<String, MalformedGenerics> {
+    let mut stripped_segment = String::new();
+    let mut param_depth = 0;
+
+    let mut latest_generics_chunk = String::new();
+
+    for c in segment {
+        if c == '<' {
+            param_depth += 1;
+            latest_generics_chunk.clear();
+        } else if c == '>' {
+            param_depth -= 1;
+            if latest_generics_chunk.contains(" as ") {
+                // The segment tries to use fully-qualified syntax, which is currently unsupported.
+                // Give a helpful error message instead of completely ignoring the angle brackets.
+                return Err(MalformedGenerics::HasFullyQualifiedSyntax);
+            }
+        } else {
+            if param_depth == 0 {
+                stripped_segment.push(c);
+            } else {
+                latest_generics_chunk.push(c);
+            }
+        }
+    }
+
+    if param_depth == 0 {
+        Ok(stripped_segment)
+    } else {
+        // The segment has unbalanced angle brackets, e.g. `Vec<T` or `Vec<T>>`
+        Err(MalformedGenerics::UnbalancedAngleBrackets)
+    }
+}
+
+pub fn strip_generics_from_path(path_str: &str) -> Result<Box<str>, MalformedGenerics> {
+    if !path_str.contains(['<', '>']) {
+        return Ok(path_str.into());
+    }
+    let mut stripped_segments = vec![];
+    let mut path = path_str.chars().peekable();
+    let mut segment = Vec::new();
+
+    while let Some(chr) = path.next() {
+        match chr {
+            ':' => {
+                if path.next_if_eq(&':').is_some() {
+                    let stripped_segment =
+                        strip_generics_from_path_segment(mem::take(&mut segment))?;
+                    if !stripped_segment.is_empty() {
+                        stripped_segments.push(stripped_segment);
+                    }
+                } else {
+                    return Err(MalformedGenerics::InvalidPathSeparator);
+                }
+            }
+            '<' => {
+                segment.push(chr);
+
+                match path.next() {
+                    Some('<') => {
+                        return Err(MalformedGenerics::TooManyAngleBrackets);
+                    }
+                    Some('>') => {
+                        return Err(MalformedGenerics::EmptyAngleBrackets);
+                    }
+                    Some(chr) => {
+                        segment.push(chr);
+
+                        while let Some(chr) = path.next_if(|c| *c != '>') {
+                            segment.push(chr);
+                        }
+                    }
+                    None => break,
+                }
+            }
+            _ => segment.push(chr),
+        }
+        trace!("raw segment: {:?}", segment);
+    }
+
+    if !segment.is_empty() {
+        let stripped_segment = strip_generics_from_path_segment(segment)?;
+        if !stripped_segment.is_empty() {
+            stripped_segments.push(stripped_segment);
+        }
+    }
+
+    debug!("path_str: {:?}\nstripped segments: {:?}", path_str, &stripped_segments);
+
+    let stripped_path = stripped_segments.join("::");
+
+    if !stripped_path.is_empty() {
+        Ok(stripped_path.into())
+    } else {
+        Err(MalformedGenerics::MissingType)
+    }
+}
+
+/// Returns whether the first doc-comment is an inner attribute.
+///
+//// If there are no doc-comments, return true.
+/// FIXME(#78591): Support both inner and outer attributes on the same item.
+pub fn inner_docs(attrs: &[ast::Attribute]) -> bool {
+    attrs.iter().find(|a| a.doc_str().is_some()).map_or(true, |a| a.style == ast::AttrStyle::Inner)
+}
+
+/// Has `#[rustc_doc_primitive]` or `#[doc(keyword)]`.
+pub fn has_primitive_or_keyword_docs(attrs: &[ast::Attribute]) -> bool {
+    for attr in attrs {
+        if attr.has_name(sym::rustc_doc_primitive) {
+            return true;
+        } else if attr.has_name(sym::doc)
+            && let Some(items) = attr.meta_item_list()
+        {
+            for item in items {
+                if item.has_name(sym::keyword) {
+                    return true;
+                }
+            }
+        }
+    }
+    false
+}
+
+/// Simplified version of the corresponding function in rustdoc.
+/// If the rustdoc version returns a successful result, this function must return the same result.
+/// Otherwise this function may return anything.
+fn preprocess_link(link: &str) -> Box<str> {
+    let link = link.replace('`', "");
+    let link = link.split('#').next().unwrap();
+    let link = link.trim();
+    let link = link.rsplit('@').next().unwrap();
+    let link = link.strip_suffix("()").unwrap_or(link);
+    let link = link.strip_suffix("{}").unwrap_or(link);
+    let link = link.strip_suffix("[]").unwrap_or(link);
+    let link = if link != "!" { link.strip_suffix('!').unwrap_or(link) } else { link };
+    let link = link.trim();
+    strip_generics_from_path(link).unwrap_or_else(|_| link.into())
+}
+
+/// Keep inline and reference links `[]`,
+/// but skip autolinks `<>` which we never consider to be intra-doc links.
+pub fn may_be_doc_link(link_type: LinkType) -> bool {
+    match link_type {
+        LinkType::Inline
+        | LinkType::Reference
+        | LinkType::ReferenceUnknown
+        | LinkType::Collapsed
+        | LinkType::CollapsedUnknown
+        | LinkType::Shortcut
+        | LinkType::ShortcutUnknown => true,
+        LinkType::Autolink | LinkType::Email => false,
+    }
+}
+
+/// Simplified version of `preprocessed_markdown_links` from rustdoc.
+/// Must return at least the same links as it, but may add some more links on top of that.
+pub(crate) fn attrs_to_preprocessed_links(attrs: &[ast::Attribute]) -> Vec<Box<str>> {
+    let (doc_fragments, _) = attrs_to_doc_fragments(attrs.iter().map(|attr| (attr, None)), true);
+    let doc = prepare_to_doc_link_resolution(&doc_fragments).into_values().next().unwrap();
+
+    parse_links(&doc)
+}
+
+/// Similiar version of `markdown_links` from rustdoc.
+/// This will collect destination links and display text if exists.
+fn parse_links<'md>(doc: &'md str) -> Vec<Box<str>> {
+    let mut broken_link_callback = |link: BrokenLink<'md>| Some((link.reference, "".into()));
+    let mut event_iter = Parser::new_with_broken_link_callback(
+        doc,
+        main_body_opts(),
+        Some(&mut broken_link_callback),
+    );
+    let mut links = Vec::new();
+
+    while let Some(event) = event_iter.next() {
+        match event {
+            Event::Start(Tag::Link(link_type, dest, _)) if may_be_doc_link(link_type) => {
+                if matches!(
+                    link_type,
+                    LinkType::Inline
+                        | LinkType::ReferenceUnknown
+                        | LinkType::Reference
+                        | LinkType::Shortcut
+                        | LinkType::ShortcutUnknown
+                ) {
+                    if let Some(display_text) = collect_link_data(&mut event_iter) {
+                        links.push(display_text);
+                    }
+                }
+
+                links.push(preprocess_link(&dest));
+            }
+            _ => {}
+        }
+    }
+
+    links
+}
+
+/// Collects additional data of link.
+fn collect_link_data<'input, 'callback>(
+    event_iter: &mut Parser<'input, 'callback>,
+) -> Option<Box<str>> {
+    let mut display_text: Option<String> = None;
+    let mut append_text = |text: CowStr<'_>| {
+        if let Some(display_text) = &mut display_text {
+            display_text.push_str(&text);
+        } else {
+            display_text = Some(text.to_string());
+        }
+    };
+
+    while let Some(event) = event_iter.next() {
+        match event {
+            Event::Text(text) => {
+                append_text(text);
+            }
+            Event::Code(code) => {
+                append_text(code);
+            }
+            Event::End(_) => {
+                break;
+            }
+            _ => {}
+        }
+    }
+
+    display_text.map(String::into_boxed_str)
+}
+
+/// Returns a span encompassing all the document fragments.
+pub fn span_of_fragments(fragments: &[DocFragment]) -> Option<Span> {
+    if fragments.is_empty() {
+        return None;
+    }
+    let start = fragments[0].span;
+    if start == DUMMY_SP {
+        return None;
+    }
+    let end = fragments.last().expect("no doc strings provided").span;
+    Some(start.to(end))
+}
+
+/// Attempts to match a range of bytes from parsed markdown to a `Span` in the source code.
+///
+/// This method will return `None` if we cannot construct a span from the source map or if the
+/// fragments are not all sugared doc comments. It's difficult to calculate the correct span in
+/// that case due to escaping and other source features.
+pub fn source_span_for_markdown_range(
+    tcx: TyCtxt<'_>,
+    markdown: &str,
+    md_range: &Range<usize>,
+    fragments: &[DocFragment],
+) -> Option<Span> {
+    let is_all_sugared_doc = fragments.iter().all(|frag| frag.kind == DocFragmentKind::SugaredDoc);
+
+    if !is_all_sugared_doc {
+        return None;
+    }
+
+    let snippet = tcx.sess.source_map().span_to_snippet(span_of_fragments(fragments)?).ok()?;
+
+    let starting_line = markdown[..md_range.start].matches('\n').count();
+    let ending_line = starting_line + markdown[md_range.start..md_range.end].matches('\n').count();
+
+    // We use `split_terminator('\n')` instead of `lines()` when counting bytes so that we treat
+    // CRLF and LF line endings the same way.
+    let mut src_lines = snippet.split_terminator('\n');
+    let md_lines = markdown.split_terminator('\n');
+
+    // The number of bytes from the source span to the markdown span that are not part
+    // of the markdown, like comment markers.
+    let mut start_bytes = 0;
+    let mut end_bytes = 0;
+
+    'outer: for (line_no, md_line) in md_lines.enumerate() {
+        loop {
+            let source_line = src_lines.next()?;
+            match source_line.find(md_line) {
+                Some(offset) => {
+                    if line_no == starting_line {
+                        start_bytes += offset;
+
+                        if starting_line == ending_line {
+                            break 'outer;
+                        }
+                    } else if line_no == ending_line {
+                        end_bytes += offset;
+                        break 'outer;
+                    } else if line_no < starting_line {
+                        start_bytes += source_line.len() - md_line.len();
+                    } else {
+                        end_bytes += source_line.len() - md_line.len();
+                    }
+                    break;
+                }
+                None => {
+                    // Since this is a source line that doesn't include a markdown line,
+                    // we have to count the newline that we split from earlier.
+                    if line_no <= starting_line {
+                        start_bytes += source_line.len() + 1;
+                    } else {
+                        end_bytes += source_line.len() + 1;
+                    }
+                }
+            }
+        }
+    }
+
+    Some(span_of_fragments(fragments)?.from_inner(InnerSpan::new(
+        md_range.start + start_bytes,
+        md_range.end + start_bytes + end_bytes,
+    )))
+}