about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLukas Wirth <lukastw97@gmail.com>2023-08-09 15:20:42 +0200
committerLukas Wirth <lukastw97@gmail.com>2023-08-09 15:20:42 +0200
commit992b928a930c58348c55263e13f140c06912e58d (patch)
tree2021c841f046a1c8a19a38590c3a6cf85cd3afb0
parent4bed01c36e01caf8b287cb0f62b4edff8ad52f19 (diff)
downloadrust-992b928a930c58348c55263e13f140c06912e58d.tar.gz
rust-992b928a930c58348c55263e13f140c06912e58d.zip
Record import source IDs
-rw-r--r--crates/hir-def/src/data.rs2
-rw-r--r--crates/hir-def/src/find_path.rs14
-rw-r--r--crates/hir-def/src/lib.rs11
-rw-r--r--crates/hir-def/src/nameres.rs22
-rw-r--r--crates/hir-def/src/nameres/collector.rs292
-rw-r--r--crates/hir-def/src/nameres/path_resolution.rs21
-rw-r--r--crates/hir-def/src/resolver.rs10
7 files changed, 216 insertions, 156 deletions
diff --git a/crates/hir-def/src/data.rs b/crates/hir-def/src/data.rs
index 91db68058b0..68defa3858f 100644
--- a/crates/hir-def/src/data.rs
+++ b/crates/hir-def/src/data.rs
@@ -487,7 +487,7 @@ impl ExternCrateDeclData {
             db.crate_def_map(loc.container.krate())
                 .extern_prelude()
                 .find(|&(prelude_name, ..)| *prelude_name == name)
-                .map(|(_, root)| root.krate())
+                .map(|(_, (root, _))| root.krate())
         };
 
         Arc::new(Self {
diff --git a/crates/hir-def/src/find_path.rs b/crates/hir-def/src/find_path.rs
index df2af4c89b0..59c250d7506 100644
--- a/crates/hir-def/src/find_path.rs
+++ b/crates/hir-def/src/find_path.rs
@@ -11,7 +11,7 @@ use crate::{
     nameres::DefMap,
     path::{ModPath, PathKind},
     visibility::Visibility,
-    ModuleDefId, ModuleId,
+    CrateRootModuleId, ModuleDefId, ModuleId,
 };
 
 /// Find a path that can be used to refer to a certain item. This can depend on
@@ -81,7 +81,7 @@ fn find_path_inner(
     }
 
     let def_map = from.def_map(db);
-    let crate_root = def_map.crate_root().into();
+    let crate_root = def_map.crate_root();
     // - if the item is a module, jump straight to module search
     if let ItemInNs::Types(ModuleDefId::ModuleId(module_id)) = item {
         let mut visited_modules = FxHashSet::default();
@@ -149,7 +149,7 @@ fn find_path_for_module(
     db: &dyn DefDatabase,
     def_map: &DefMap,
     visited_modules: &mut FxHashSet<ModuleId>,
-    crate_root: ModuleId,
+    crate_root: CrateRootModuleId,
     from: ModuleId,
     module_id: ModuleId,
     max_len: usize,
@@ -183,7 +183,7 @@ fn find_path_for_module(
 
     // - if the item is the crate root of a dependency crate, return the name from the extern prelude
     let root_def_map = crate_root.def_map(db);
-    for (name, def_id) in root_def_map.extern_prelude() {
+    for (name, (def_id, _extern_crate)) in root_def_map.extern_prelude() {
         if module_id == def_id {
             let name = scope_name.unwrap_or_else(|| name.clone());
 
@@ -192,7 +192,7 @@ fn find_path_for_module(
                     def_map[local_id]
                         .scope
                         .type_(&name)
-                        .filter(|&(id, _)| id != ModuleDefId::ModuleId(def_id))
+                        .filter(|&(id, _)| id != ModuleDefId::ModuleId(def_id.into()))
                 })
                 .is_some();
             let kind = if name_already_occupied_in_type_ns {
@@ -244,7 +244,7 @@ fn find_in_prelude(
     item: ItemInNs,
     from: ModuleId,
 ) -> Option<ModPath> {
-    let prelude_module = root_def_map.prelude()?;
+    let (prelude_module, _) = root_def_map.prelude()?;
     // Preludes in block DefMaps are ignored, only the crate DefMap is searched
     let prelude_def_map = prelude_module.def_map(db);
     let prelude_scope = &prelude_def_map[prelude_module.local_id].scope;
@@ -293,7 +293,7 @@ fn calculate_best_path(
     db: &dyn DefDatabase,
     def_map: &DefMap,
     visited_modules: &mut FxHashSet<ModuleId>,
-    crate_root: ModuleId,
+    crate_root: CrateRootModuleId,
     max_len: usize,
     item: ItemInNs,
     from: ModuleId,
diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs
index 1901db8a0f9..c40bbc0380e 100644
--- a/crates/hir-def/src/lib.rs
+++ b/crates/hir-def/src/lib.rs
@@ -109,6 +109,17 @@ impl CrateRootModuleId {
     }
 }
 
+impl PartialEq<ModuleId> for CrateRootModuleId {
+    fn eq(&self, other: &ModuleId) -> bool {
+        other.block.is_none() && other.local_id == DefMap::ROOT && self.krate == other.krate
+    }
+}
+impl PartialEq<CrateRootModuleId> for ModuleId {
+    fn eq(&self, other: &CrateRootModuleId) -> bool {
+        other == self
+    }
+}
+
 impl From<CrateRootModuleId> for ModuleId {
     fn from(CrateRootModuleId { krate }: CrateRootModuleId) -> Self {
         ModuleId { krate, block: None, local_id: DefMap::ROOT }
diff --git a/crates/hir-def/src/nameres.rs b/crates/hir-def/src/nameres.rs
index 86818ce26dd..5c99f691a64 100644
--- a/crates/hir-def/src/nameres.rs
+++ b/crates/hir-def/src/nameres.rs
@@ -77,8 +77,8 @@ use crate::{
     path::ModPath,
     per_ns::PerNs,
     visibility::Visibility,
-    AstId, BlockId, BlockLoc, CrateRootModuleId, FunctionId, LocalModuleId, Lookup, MacroExpander,
-    MacroId, ModuleId, ProcMacroId,
+    AstId, BlockId, BlockLoc, CrateRootModuleId, ExternCrateId, FunctionId, LocalModuleId, Lookup,
+    MacroExpander, MacroId, ModuleId, ProcMacroId, UseId,
 };
 
 /// Contains the results of (early) name resolution.
@@ -105,10 +105,10 @@ pub struct DefMap {
     /// The prelude is empty for non-block DefMaps (unless `#[prelude_import]` was used,
     /// but that attribute is nightly and when used in a block, it affects resolution globally
     /// so we aren't handling this correctly anyways).
-    prelude: Option<ModuleId>,
+    prelude: Option<(ModuleId, Option<UseId>)>,
     /// `macro_use` prelude that contains macros from `#[macro_use]`'d external crates. Note that
     /// this contains all kinds of macro, not just `macro_rules!` macro.
-    macro_use_prelude: FxHashMap<Name, MacroId>,
+    macro_use_prelude: FxHashMap<Name, (MacroId, Option<ExternCrateId>)>,
 
     /// Tracks which custom derives are in scope for an item, to allow resolution of derive helper
     /// attributes.
@@ -125,7 +125,7 @@ pub struct DefMap {
 #[derive(Clone, Debug, PartialEq, Eq)]
 struct DefMapCrateData {
     /// The extern prelude which contains all root modules of external crates that are in scope.
-    extern_prelude: FxHashMap<Name, CrateRootModuleId>,
+    extern_prelude: FxHashMap<Name, (CrateRootModuleId, Option<ExternCrateId>)>,
 
     /// Side table for resolving derive helpers.
     exported_derives: FxHashMap<MacroDefId, Box<[Name]>>,
@@ -427,15 +427,19 @@ impl DefMap {
         self.block.map(|block| block.block)
     }
 
-    pub(crate) fn prelude(&self) -> Option<ModuleId> {
+    pub(crate) fn prelude(&self) -> Option<(ModuleId, Option<UseId>)> {
         self.prelude
     }
 
-    pub(crate) fn extern_prelude(&self) -> impl Iterator<Item = (&Name, ModuleId)> + '_ {
-        self.data.extern_prelude.iter().map(|(name, &def)| (name, def.into()))
+    pub(crate) fn extern_prelude(
+        &self,
+    ) -> impl Iterator<Item = (&Name, (CrateRootModuleId, Option<ExternCrateId>))> + '_ {
+        self.data.extern_prelude.iter().map(|(name, &def)| (name, def))
     }
 
-    pub(crate) fn macro_use_prelude(&self) -> impl Iterator<Item = (&Name, MacroId)> + '_ {
+    pub(crate) fn macro_use_prelude(
+        &self,
+    ) -> impl Iterator<Item = (&Name, (MacroId, Option<ExternCrateId>))> + '_ {
         self.macro_use_prelude.iter().map(|(name, &def)| (name, def))
     }
 
diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs
index eef54fc492e..f79f4ab0ba9 100644
--- a/crates/hir-def/src/nameres/collector.rs
+++ b/crates/hir-def/src/nameres/collector.rs
@@ -52,10 +52,10 @@ use crate::{
     tt,
     visibility::{RawVisibility, Visibility},
     AdtId, AstId, AstIdWithPath, ConstLoc, CrateRootModuleId, EnumLoc, EnumVariantId,
-    ExternBlockLoc, ExternCrateLoc, FunctionId, FunctionLoc, ImplLoc, Intern, ItemContainerId,
-    LocalModuleId, Macro2Id, Macro2Loc, MacroExpander, MacroId, MacroRulesId, MacroRulesLoc,
-    ModuleDefId, ModuleId, ProcMacroId, ProcMacroLoc, StaticLoc, StructLoc, TraitAliasLoc,
-    TraitLoc, TypeAliasLoc, UnionLoc, UnresolvedMacro, UseLoc,
+    ExternBlockLoc, ExternCrateId, ExternCrateLoc, FunctionId, FunctionLoc, ImplLoc, Intern,
+    ItemContainerId, LocalModuleId, Macro2Id, Macro2Loc, MacroExpander, MacroId, MacroRulesId,
+    MacroRulesLoc, ModuleDefId, ModuleId, ProcMacroId, ProcMacroLoc, StaticLoc, StructLoc,
+    TraitAliasLoc, TraitLoc, TypeAliasLoc, UnionLoc, UnresolvedMacro, UseId, UseLoc,
 };
 
 static GLOB_RECURSION_LIMIT: Limit = Limit::new(100);
@@ -144,10 +144,11 @@ impl PartialResolvedImport {
     }
 }
 
+// FIXME: `item_tree_id` can be derived from `id`, look into deduplicating this
 #[derive(Clone, Debug, Eq, PartialEq)]
 enum ImportSource {
-    Use { id: ItemTreeId<item_tree::Use>, use_tree: Idx<ast::UseTree> },
-    ExternCrate(ItemTreeId<item_tree::ExternCrate>),
+    Use { item_tree_id: ItemTreeId<item_tree::Use>, use_tree: Idx<ast::UseTree>, id: UseId },
+    ExternCrate { item_tree_id: ItemTreeId<item_tree::ExternCrate>, id: ExternCrateId },
 }
 
 #[derive(Debug, Eq, PartialEq)]
@@ -166,11 +167,12 @@ impl Import {
         db: &dyn DefDatabase,
         krate: CrateId,
         tree: &ItemTree,
-        id: ItemTreeId<item_tree::Use>,
+        item_tree_id: ItemTreeId<item_tree::Use>,
+        id: UseId,
         mut cb: impl FnMut(Self),
     ) {
-        let it = &tree[id.value];
-        let attrs = &tree.attrs(db, krate, ModItem::from(id.value).into());
+        let it = &tree[item_tree_id.value];
+        let attrs = &tree.attrs(db, krate, ModItem::from(item_tree_id.value).into());
         let visibility = &tree[it.visibility];
         let is_prelude = attrs.by_key("prelude_import").exists();
         it.use_tree.expand(|idx, path, kind, alias| {
@@ -181,7 +183,7 @@ impl Import {
                 kind,
                 is_prelude,
                 is_macro_use: false,
-                source: ImportSource::Use { id, use_tree: idx },
+                source: ImportSource::Use { item_tree_id, use_tree: idx, id },
             });
         });
     }
@@ -190,10 +192,11 @@ impl Import {
         db: &dyn DefDatabase,
         krate: CrateId,
         tree: &ItemTree,
-        id: ItemTreeId<item_tree::ExternCrate>,
+        item_tree_id: ItemTreeId<item_tree::ExternCrate>,
+        id: ExternCrateId,
     ) -> Self {
-        let it = &tree[id.value];
-        let attrs = &tree.attrs(db, krate, ModItem::from(id.value).into());
+        let it = &tree[item_tree_id.value];
+        let attrs = &tree.attrs(db, krate, ModItem::from(item_tree_id.value).into());
         let visibility = &tree[it.visibility];
         Self {
             path: ModPath::from_segments(PathKind::Plain, iter::once(it.name.clone())),
@@ -202,7 +205,7 @@ impl Import {
             kind: ImportKind::Plain,
             is_prelude: false,
             is_macro_use: attrs.by_key("macro_use").exists(),
-            source: ImportSource::ExternCrate(id),
+            source: ImportSource::ExternCrate { item_tree_id, id },
         }
     }
 }
@@ -280,7 +283,7 @@ impl DefCollector<'_> {
             if dep.is_prelude() {
                 crate_data
                     .extern_prelude
-                    .insert(name.clone(), CrateRootModuleId { krate: dep.crate_id });
+                    .insert(name.clone(), (CrateRootModuleId { krate: dep.crate_id }, None));
             }
         }
 
@@ -557,7 +560,7 @@ impl DefCollector<'_> {
 
         match per_ns.types {
             Some((ModuleDefId::ModuleId(m), _)) => {
-                self.def_map.prelude = Some(m);
+                self.def_map.prelude = Some((m, None));
             }
             types => {
                 tracing::debug!(
@@ -720,7 +723,13 @@ impl DefCollector<'_> {
     /// Exported macros are just all macros in the root module scope.
     /// Note that it contains not only all `#[macro_export]` macros, but also all aliases
     /// created by `use` in the root module, ignoring the visibility of `use`.
-    fn import_macros_from_extern_crate(&mut self, krate: CrateId, names: Option<Vec<Name>>) {
+    fn import_macros_from_extern_crate(
+        &mut self,
+        krate: CrateId,
+        names: Option<Vec<Name>>,
+
+        extern_crate: Option<ExternCrateId>,
+    ) {
         let def_map = self.db.crate_def_map(krate);
         // `#[macro_use]` brings macros into macro_use prelude. Yes, even non-`macro_rules!`
         // macros.
@@ -729,12 +738,12 @@ impl DefCollector<'_> {
             for name in names {
                 // FIXME: Report diagnostic on 404.
                 if let Some(def) = root_scope.get(&name).take_macros() {
-                    self.def_map.macro_use_prelude.insert(name, def);
+                    self.def_map.macro_use_prelude.insert(name, (def, extern_crate));
                 }
             }
         } else {
             for (name, def) in root_scope.macros() {
-                self.def_map.macro_use_prelude.insert(name.clone(), def);
+                self.def_map.macro_use_prelude.insert(name.clone(), (def, extern_crate));
             }
         }
     }
@@ -771,48 +780,52 @@ impl DefCollector<'_> {
         let _p = profile::span("resolve_import")
             .detail(|| format!("{}", import.path.display(self.db.upcast())));
         tracing::debug!("resolving import: {:?} ({:?})", import, self.def_map.data.edition);
-        if matches!(import.source, ImportSource::ExternCrate { .. }) {
-            let name = import
-                .path
-                .as_ident()
-                .expect("extern crate should have been desugared to one-element path");
-
-            let res = self.resolve_extern_crate(name);
-
-            match res {
-                Some(res) => {
-                    PartialResolvedImport::Resolved(PerNs::types(res.into(), Visibility::Public))
+        match import.source {
+            ImportSource::ExternCrate { .. } => {
+                let name = import
+                    .path
+                    .as_ident()
+                    .expect("extern crate should have been desugared to one-element path");
+
+                let res = self.resolve_extern_crate(name);
+
+                match res {
+                    Some(res) => PartialResolvedImport::Resolved(PerNs::types(
+                        res.into(),
+                        Visibility::Public,
+                    )),
+                    None => PartialResolvedImport::Unresolved,
                 }
-                None => PartialResolvedImport::Unresolved,
             }
-        } else {
-            let res = self.def_map.resolve_path_fp_with_macro(
-                self.db,
-                ResolveMode::Import,
-                module_id,
-                &import.path,
-                BuiltinShadowMode::Module,
-                None, // An import may resolve to any kind of macro.
-            );
+            ImportSource::Use { .. } => {
+                let res = self.def_map.resolve_path_fp_with_macro(
+                    self.db,
+                    ResolveMode::Import,
+                    module_id,
+                    &import.path,
+                    BuiltinShadowMode::Module,
+                    None, // An import may resolve to any kind of macro.
+                );
 
-            let def = res.resolved_def;
-            if res.reached_fixedpoint == ReachedFixedPoint::No || def.is_none() {
-                return PartialResolvedImport::Unresolved;
-            }
+                let def = res.resolved_def;
+                if res.reached_fixedpoint == ReachedFixedPoint::No || def.is_none() {
+                    return PartialResolvedImport::Unresolved;
+                }
 
-            if let Some(krate) = res.krate {
-                if krate != self.def_map.krate {
-                    return PartialResolvedImport::Resolved(
-                        def.filter_visibility(|v| matches!(v, Visibility::Public)),
-                    );
+                if let Some(krate) = res.krate {
+                    if krate != self.def_map.krate {
+                        return PartialResolvedImport::Resolved(
+                            def.filter_visibility(|v| matches!(v, Visibility::Public)),
+                        );
+                    }
                 }
-            }
 
-            // Check whether all namespaces are resolved.
-            if def.is_full() {
-                PartialResolvedImport::Resolved(def)
-            } else {
-                PartialResolvedImport::Indeterminate(def)
+                // Check whether all namespaces are resolved.
+                if def.is_full() {
+                    PartialResolvedImport::Resolved(def)
+                } else {
+                    PartialResolvedImport::Indeterminate(def)
+                }
             }
         }
     }
@@ -859,17 +872,17 @@ impl DefCollector<'_> {
                 tracing::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def);
 
                 // extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658
-                if matches!(import.source, ImportSource::ExternCrate { .. })
-                    && self.def_map.block.is_none()
-                    && module_id == DefMap::ROOT
-                {
-                    if let (Some(ModuleDefId::ModuleId(def)), Some(name)) = (def.take_types(), name)
-                    {
-                        if let Ok(def) = def.try_into() {
-                            Arc::get_mut(&mut self.def_map.data)
-                                .unwrap()
-                                .extern_prelude
-                                .insert(name.clone(), def);
+                if let ImportSource::ExternCrate { id, .. } = import.source {
+                    if self.def_map.block.is_none() && module_id == DefMap::ROOT {
+                        if let (Some(ModuleDefId::ModuleId(def)), Some(name)) =
+                            (def.take_types(), name)
+                        {
+                            if let Ok(def) = def.try_into() {
+                                Arc::get_mut(&mut self.def_map.data)
+                                    .unwrap()
+                                    .extern_prelude
+                                    .insert(name.clone(), (def, Some(id)));
+                            }
                         }
                     }
                 }
@@ -884,7 +897,13 @@ impl DefCollector<'_> {
                             // Note: This dodgily overrides the injected prelude. The rustc
                             // implementation seems to work the same though.
                             cov_mark::hit!(std_prelude);
-                            self.def_map.prelude = Some(m);
+                            self.def_map.prelude = Some((
+                                m,
+                                match import.source {
+                                    ImportSource::Use { id, .. } => Some(id),
+                                    ImportSource::ExternCrate { .. } => None,
+                                },
+                            ));
                         } else if m.krate != self.def_map.krate {
                             cov_mark::hit!(glob_across_crates);
                             // glob import from other crate => we can just import everything once
@@ -1460,21 +1479,21 @@ impl DefCollector<'_> {
         // heuristic, but it works in practice.
         let mut diagnosed_extern_crates = FxHashSet::default();
         for directive in &self.unresolved_imports {
-            if let ImportSource::ExternCrate(krate) = directive.import.source {
-                let item_tree = krate.item_tree(self.db);
-                let extern_crate = &item_tree[krate.value];
+            if let ImportSource::ExternCrate { item_tree_id, id: _ } = directive.import.source {
+                let item_tree = item_tree_id.item_tree(self.db);
+                let extern_crate = &item_tree[item_tree_id.value];
 
                 diagnosed_extern_crates.insert(extern_crate.name.clone());
 
                 self.def_map.diagnostics.push(DefDiagnostic::unresolved_extern_crate(
                     directive.module_id,
-                    InFile::new(krate.file_id(), extern_crate.ast_id),
+                    InFile::new(item_tree_id.file_id(), extern_crate.ast_id),
                 ));
             }
         }
 
         for directive in &self.unresolved_imports {
-            if let ImportSource::Use { id: import, use_tree } = directive.import.source {
+            if let ImportSource::Use { item_tree_id, use_tree, id: _ } = directive.import.source {
                 if matches!(
                     (directive.import.path.segments().first(), &directive.import.path.kind),
                     (Some(krate), PathKind::Plain | PathKind::Abs) if diagnosed_extern_crates.contains(krate)
@@ -1484,7 +1503,7 @@ impl DefCollector<'_> {
 
                 self.def_map.diagnostics.push(DefDiagnostic::unresolved_import(
                     directive.module_id,
-                    import,
+                    item_tree_id,
                     use_tree,
                 ));
             }
@@ -1519,72 +1538,66 @@ impl ModCollector<'_, '_> {
         self.def_collector.mod_dirs.insert(self.module_id, self.mod_dir.clone());
 
         // Prelude module is always considered to be `#[macro_use]`.
-        if let Some(prelude_module) = self.def_collector.def_map.prelude {
+        if let Some((prelude_module, _use)) = self.def_collector.def_map.prelude {
             if prelude_module.krate != krate && is_crate_root {
                 cov_mark::hit!(prelude_is_macro_use);
-                self.def_collector.import_macros_from_extern_crate(prelude_module.krate, None);
-            }
-        }
-
-        // This should be processed eagerly instead of deferred to resolving.
-        // `#[macro_use] extern crate` is hoisted to imports macros before collecting
-        // any other items.
-        //
-        // If we're not at the crate root, `macro_use`d extern crates are an error so let's just
-        // ignore them.
-        if is_crate_root {
-            for &item in items {
-                if let ModItem::ExternCrate(id) = item {
-                    self.process_macro_use_extern_crate(id);
-                }
+                self.def_collector.import_macros_from_extern_crate(
+                    prelude_module.krate,
+                    None,
+                    None,
+                );
             }
         }
+        let db = self.def_collector.db;
+        let module_id = self.module_id;
+        let update_def =
+            |def_collector: &mut DefCollector<'_>, id, name: &Name, vis, has_constructor| {
+                def_collector.def_map.modules[module_id].scope.declare(id);
+                def_collector.update(
+                    module_id,
+                    &[(Some(name.clone()), PerNs::from_def(id, vis, has_constructor))],
+                    vis,
+                    ImportType::Named,
+                )
+            };
+        let resolve_vis = |def_map: &DefMap, visibility| {
+            def_map
+                .resolve_visibility(db, module_id, visibility, false)
+                .unwrap_or(Visibility::Public)
+        };
 
-        for &item in items {
-            let attrs = self.item_tree.attrs(self.def_collector.db, krate, item.into());
+        let mut process_mod_item = |item: ModItem| {
+            let attrs = self.item_tree.attrs(db, krate, item.into());
             if let Some(cfg) = attrs.cfg() {
                 if !self.is_cfg_enabled(&cfg) {
                     self.emit_unconfigured_diagnostic(item, &cfg);
-                    continue;
+                    return;
                 }
             }
 
             if let Err(()) = self.resolve_attributes(&attrs, item, container) {
                 // Do not process the item. It has at least one non-builtin attribute, so the
                 // fixed-point algorithm is required to resolve the rest of them.
-                continue;
+                return;
             }
 
-            let db = self.def_collector.db;
-            let module = self.def_collector.def_map.module_id(self.module_id);
+            let module = self.def_collector.def_map.module_id(module_id);
             let def_map = &mut self.def_collector.def_map;
-            let update_def =
-                |def_collector: &mut DefCollector<'_>, id, name: &Name, vis, has_constructor| {
-                    def_collector.def_map.modules[self.module_id].scope.declare(id);
-                    def_collector.update(
-                        self.module_id,
-                        &[(Some(name.clone()), PerNs::from_def(id, vis, has_constructor))],
-                        vis,
-                        ImportType::Named,
-                    )
-                };
-            let resolve_vis = |def_map: &DefMap, visibility| {
-                def_map
-                    .resolve_visibility(db, self.module_id, visibility, false)
-                    .unwrap_or(Visibility::Public)
-            };
 
             match item {
                 ModItem::Mod(m) => self.collect_module(m, &attrs),
-                ModItem::Use(import_id) => {
-                    let _import_id =
-                        UseLoc { container: module, id: ItemTreeId::new(self.tree_id, import_id) }
-                            .intern(db);
+                ModItem::Use(item_tree_id) => {
+                    let id = UseLoc {
+                        container: module,
+                        id: ItemTreeId::new(self.tree_id, item_tree_id),
+                    }
+                    .intern(db);
                     Import::from_use(
                         db,
                         krate,
                         self.item_tree,
-                        ItemTreeId::new(self.tree_id, import_id),
+                        ItemTreeId::new(self.tree_id, item_tree_id),
+                        id,
                         |import| {
                             self.def_collector.unresolved_imports.push(ImportDirective {
                                 module_id: self.module_id,
@@ -1594,22 +1607,27 @@ impl ModCollector<'_, '_> {
                         },
                     )
                 }
-                ModItem::ExternCrate(import_id) => {
-                    let extern_crate_id = ExternCrateLoc {
+                ModItem::ExternCrate(item_tree_id) => {
+                    let id = ExternCrateLoc {
                         container: module,
-                        id: ItemTreeId::new(self.tree_id, import_id),
+                        id: ItemTreeId::new(self.tree_id, item_tree_id),
                     }
                     .intern(db);
+                    if is_crate_root {
+                        self.process_macro_use_extern_crate(item_tree_id, id);
+                    }
+
                     self.def_collector.def_map.modules[self.module_id]
                         .scope
-                        .define_extern_crate_decl(extern_crate_id);
+                        .define_extern_crate_decl(id);
                     self.def_collector.unresolved_imports.push(ImportDirective {
                         module_id: self.module_id,
                         import: Import::from_extern_crate(
                             db,
                             krate,
                             self.item_tree,
-                            ItemTreeId::new(self.tree_id, import_id),
+                            ItemTreeId::new(self.tree_id, item_tree_id),
+                            id,
                         ),
                         status: PartialResolvedImport::Unresolved,
                     })
@@ -1768,10 +1786,32 @@ impl ModCollector<'_, '_> {
                     );
                 }
             }
+        };
+
+        // extern crates should be processed eagerly instead of deferred to resolving.
+        // `#[macro_use] extern crate` is hoisted to imports macros before collecting
+        // any other items.
+        if is_crate_root {
+            items
+                .iter()
+                .filter(|it| matches!(it, ModItem::ExternCrate(..)))
+                .copied()
+                .for_each(&mut process_mod_item);
+            items
+                .iter()
+                .filter(|it| !matches!(it, ModItem::ExternCrate(..)))
+                .copied()
+                .for_each(process_mod_item);
+        } else {
+            items.iter().copied().for_each(process_mod_item);
         }
     }
 
-    fn process_macro_use_extern_crate(&mut self, extern_crate: FileItemTreeId<ExternCrate>) {
+    fn process_macro_use_extern_crate(
+        &mut self,
+        extern_crate: FileItemTreeId<ExternCrate>,
+        extern_crate_id: ExternCrateId,
+    ) {
         let db = self.def_collector.db;
         let attrs = self.item_tree.attrs(
             db,
@@ -1802,7 +1842,7 @@ impl ModCollector<'_, '_> {
             let Some(paths) = attr.parse_path_comma_token_tree(db.upcast(), &hygiene) else {
                 // `#[macro_use]` (without any paths) found, forget collected names and just import
                 // all visible macros.
-                self.def_collector.import_macros_from_extern_crate(target_crate, None);
+                self.def_collector.import_macros_from_extern_crate(target_crate, None, Some(extern_crate_id));
                 return;
             };
             for path in paths {
@@ -1812,7 +1852,11 @@ impl ModCollector<'_, '_> {
             }
         }
 
-        self.def_collector.import_macros_from_extern_crate(target_crate, Some(single_imports));
+        self.def_collector.import_macros_from_extern_crate(
+            target_crate,
+            Some(single_imports),
+            Some(extern_crate_id),
+        );
     }
 
     fn collect_module(&mut self, module_id: FileItemTreeId<Mod>, attrs: &Attrs) {
@@ -2198,7 +2242,7 @@ impl ModCollector<'_, '_> {
                             map[module].scope.get_legacy_macro(name)?.last().copied()
                         })
                         .or_else(|| def_map[self.module_id].scope.get(name).take_macros())
-                        .or_else(|| def_map.macro_use_prelude.get(name).copied())
+                        .or_else(|| Some(def_map.macro_use_prelude.get(name).copied()?.0))
                         .filter(|&id| {
                             sub_namespace_match(
                                 Some(MacroSubNs::from_id(db, id)),
diff --git a/crates/hir-def/src/nameres/path_resolution.rs b/crates/hir-def/src/nameres/path_resolution.rs
index de22ea10146..64cdbdce770 100644
--- a/crates/hir-def/src/nameres/path_resolution.rs
+++ b/crates/hir-def/src/nameres/path_resolution.rs
@@ -299,7 +299,7 @@ impl DefMap {
                     Some((_, segment)) => segment,
                     None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
                 };
-                if let Some(&def) = self.data.extern_prelude.get(segment) {
+                if let Some(&(def, _extern_crate)) = self.data.extern_prelude.get(segment) {
                     tracing::debug!("absolute path {:?} resolved to crate {:?}", path, def);
                     PerNs::types(def.into(), Visibility::Public)
                 } else {
@@ -452,15 +452,14 @@ impl DefMap {
                 // Don't resolve extern prelude in block `DefMap`s.
                 return PerNs::none();
             }
-            self.data
-                .extern_prelude
-                .get(name)
-                .map_or(PerNs::none(), |&it| PerNs::types(it.into(), Visibility::Public))
+            self.data.extern_prelude.get(name).map_or(PerNs::none(), |&(it, _extern_crate)| {
+                PerNs::types(it.into(), Visibility::Public)
+            })
         };
         let macro_use_prelude = || {
-            self.macro_use_prelude
-                .get(name)
-                .map_or(PerNs::none(), |&it| PerNs::macros(it.into(), Visibility::Public))
+            self.macro_use_prelude.get(name).map_or(PerNs::none(), |&(it, _extern_crate)| {
+                PerNs::macros(it.into(), Visibility::Public)
+            })
         };
         let prelude = || self.resolve_in_prelude(db, name);
 
@@ -492,14 +491,16 @@ impl DefMap {
                 .extern_prelude
                 .get(name)
                 .copied()
-                .map_or(PerNs::none(), |it| PerNs::types(it.into(), Visibility::Public))
+                .map_or(PerNs::none(), |(it, _extern_crate)| {
+                    PerNs::types(it.into(), Visibility::Public)
+                })
         };
 
         from_crate_root.or_else(from_extern_prelude)
     }
 
     fn resolve_in_prelude(&self, db: &dyn DefDatabase, name: &Name) -> PerNs {
-        if let Some(prelude) = self.prelude {
+        if let Some((prelude, _use)) = self.prelude {
             let keep;
             let def_map = if prelude.krate == self.krate {
                 self
diff --git a/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs
index d04d2fa0e97..17a1bf50b5e 100644
--- a/crates/hir-def/src/resolver.rs
+++ b/crates/hir-def/src/resolver.rs
@@ -433,16 +433,16 @@ impl Resolver {
                 res.add(name, ScopeDef::ModuleDef(ModuleDefId::MacroId(mac)));
             })
         });
-        def_map.macro_use_prelude().for_each(|(name, def)| {
+        def_map.macro_use_prelude().for_each(|(name, (def, _extern_crate))| {
             res.add(name, ScopeDef::ModuleDef(def.into()));
         });
-        def_map.extern_prelude().for_each(|(name, def)| {
-            res.add(name, ScopeDef::ModuleDef(ModuleDefId::ModuleId(def)));
+        def_map.extern_prelude().for_each(|(name, (def, _extern_crate))| {
+            res.add(name, ScopeDef::ModuleDef(ModuleDefId::ModuleId(def.into())));
         });
         BUILTIN_SCOPE.iter().for_each(|(name, &def)| {
             res.add_per_ns(name, def);
         });
-        if let Some(prelude) = def_map.prelude() {
+        if let Some((prelude, _use)) = def_map.prelude() {
             let prelude_def_map = prelude.def_map(db);
             for (name, def) in prelude_def_map[prelude.local_id].scope.entries() {
                 res.add_per_ns(name, def)
@@ -473,7 +473,7 @@ impl Resolver {
         }
 
         // Fill in the prelude traits
-        if let Some(prelude) = self.module_scope.def_map.prelude() {
+        if let Some((prelude, _use)) = self.module_scope.def_map.prelude() {
             let prelude_def_map = prelude.def_map(db);
             traits.extend(prelude_def_map[prelude.local_id].scope.traits());
         }