From 3203ea896d1ed9f6b8a8af25a8028df51a6bd5b3 Mon Sep 17 00:00:00 2001 From: Ryo Yoshida Date: Thu, 11 May 2023 15:41:24 +0900 Subject: [PATCH] Add `macro_use` prelude to `DefMap` --- crates/hir-def/src/nameres.rs | 6 ++ crates/hir-def/src/nameres/collector.rs | 28 +++++---- crates/hir-def/src/nameres/path_resolution.rs | 13 ++++- crates/hir-def/src/nameres/tests/macros.rs | 57 ++++++++++++++++--- 4 files changed, 84 insertions(+), 20 deletions(-) diff --git a/crates/hir-def/src/nameres.rs b/crates/hir-def/src/nameres.rs index 803342fdab..2e5f090ce7 100644 --- a/crates/hir-def/src/nameres.rs +++ b/crates/hir-def/src/nameres.rs @@ -105,6 +105,9 @@ pub struct DefMap { prelude: Option, /// The extern prelude is only populated for non-block DefMaps extern_prelude: FxHashMap, + /// `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, /// Side table for resolving derive helpers. exported_derives: FxHashMap>, @@ -277,6 +280,7 @@ impl DefMap { edition, recursion_limit: None, extern_prelude: FxHashMap::default(), + macro_use_prelude: FxHashMap::default(), exported_derives: FxHashMap::default(), fn_proc_macro_mapping: FxHashMap::default(), proc_macro_loading_error: None, @@ -489,6 +493,7 @@ impl DefMap { _c: _, exported_derives, extern_prelude, + macro_use_prelude, diagnostics, modules, registered_attrs, @@ -507,6 +512,7 @@ impl DefMap { } = self; extern_prelude.shrink_to_fit(); + macro_use_prelude.shrink_to_fit(); exported_derives.shrink_to_fit(); diagnostics.shrink_to_fit(); modules.shrink_to_fit(); diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs index 2d8870af9f..84e9b9f571 100644 --- a/crates/hir-def/src/nameres/collector.rs +++ b/crates/hir-def/src/nameres/collector.rs @@ -707,6 +707,7 @@ impl DefCollector<'_> { } /// Import macros from `#[macro_use] extern crate`. + // FIXME: Support `#[macro_rules(macro_name, ...)]`. fn import_macros_from_extern_crate( &mut self, current_module_id: LocalModuleId, @@ -725,7 +726,7 @@ impl DefCollector<'_> { } cov_mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use); - self.import_all_macros_exported(current_module_id, m.krate); + self.import_all_macros_exported(m.krate); } } @@ -734,11 +735,12 @@ 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_all_macros_exported(&mut self, current_module_id: LocalModuleId, krate: CrateId) { + fn import_all_macros_exported(&mut self, krate: CrateId) { let def_map = self.db.crate_def_map(krate); for (name, def) in def_map[def_map.root].scope.macros() { - // `#[macro_use]` brings macros into legacy scope. Yes, even non-`macro_rules!` macros. - self.define_legacy_macro(current_module_id, name.clone(), def); + // `#[macro_use]` brings macros into macro_use prelude. Yes, even non-`macro_rules!` + // macros. + self.def_map.macro_use_prelude.insert(name.clone(), def); } } @@ -1509,6 +1511,7 @@ impl ModCollector<'_, '_> { fn collect(&mut self, items: &[ModItem], container: ItemContainerId) { let krate = self.def_collector.def_map.krate; + let is_crate_root = self.module_id == self.def_collector.def_map.root; // Note: don't assert that inserted value is fresh: it's simply not true // for macros. @@ -1516,19 +1519,24 @@ impl ModCollector<'_, '_> { // Prelude module is always considered to be `#[macro_use]`. if let Some(prelude_module) = self.def_collector.def_map.prelude { - if prelude_module.krate != krate { + if prelude_module.krate != krate && is_crate_root { cov_mark::hit!(prelude_is_macro_use); - self.def_collector.import_all_macros_exported(self.module_id, prelude_module.krate); + self.def_collector.import_all_macros_exported(prelude_module.krate); } } // This should be processed eagerly instead of deferred to resolving. // `#[macro_use] extern crate` is hoisted to imports macros before collecting // any other items. - for &item in items { - let attrs = self.item_tree.attrs(self.def_collector.db, krate, item.into()); - if attrs.cfg().map_or(true, |cfg| self.is_cfg_enabled(&cfg)) { - if let ModItem::ExternCrate(id) = item { + // + // If we're not at the crate root, `macro_use`d extern crates are an error so let's just + // ignore them. + // FIXME: Support `#[macro_rules(macro_name, ...)]`. + if is_crate_root { + for &item in items { + let ModItem::ExternCrate(id) = item else { continue; }; + let attrs = self.item_tree.attrs(self.def_collector.db, krate, item.into()); + if attrs.cfg().map_or(true, |cfg| self.is_cfg_enabled(&cfg)) { let import = &self.item_tree[id]; let attrs = self.item_tree.attrs( self.def_collector.db, diff --git a/crates/hir-def/src/nameres/path_resolution.rs b/crates/hir-def/src/nameres/path_resolution.rs index 8299d9684b..5b454620e7 100644 --- a/crates/hir-def/src/nameres/path_resolution.rs +++ b/crates/hir-def/src/nameres/path_resolution.rs @@ -385,7 +385,7 @@ impl DefMap { // Resolve in: // - legacy scope of macro // - current module / scope - // - extern prelude + // - extern prelude / macro_use prelude // - std prelude let from_legacy_macro = self[module] .scope @@ -414,9 +414,18 @@ impl DefMap { .get(name) .map_or(PerNs::none(), |&it| 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)) + }; let prelude = || self.resolve_in_prelude(db, name); - from_legacy_macro.or(from_scope_or_builtin).or_else(extern_prelude).or_else(prelude) + from_legacy_macro + .or(from_scope_or_builtin) + .or_else(extern_prelude) + .or_else(macro_use_prelude) + .or_else(prelude) } fn resolve_name_in_crate_root_or_extern_prelude( diff --git a/crates/hir-def/src/nameres/tests/macros.rs b/crates/hir-def/src/nameres/tests/macros.rs index 6ee56c9368..ea3209b9c2 100644 --- a/crates/hir-def/src/nameres/tests/macros.rs +++ b/crates/hir-def/src/nameres/tests/macros.rs @@ -1216,17 +1216,58 @@ fn proc_attr(a: TokenStream, b: TokenStream) -> TokenStream { a } "#, ); - let root = &def_map[def_map.root()].scope; - let actual = root - .legacy_macros() - .sorted_by(|a, b| std::cmp::Ord::cmp(&a.0, &b.0)) - .map(|(name, _)| format!("{name}\n")) - .collect::(); + let root_module = &def_map[def_map.root()].scope; + assert!( + root_module.legacy_macros().count() == 0, + "`#[macro_use]` shouldn't bring macros into textual macro scope", + ); + + let actual = def_map.macro_use_prelude.iter().map(|(name, _)| name).sorted().join("\n"); expect![[r#" legacy macro20 - proc_attr - "#]] + proc_attr"#]] .assert_eq(&actual); } + +#[test] +fn non_prelude_macros_take_precedence_over_macro_use_prelude() { + check( + r#" +//- /lib.rs edition:2021 crate:lib deps:dep,core +#[macro_use] +extern crate dep; + +macro foo() { struct Ok; } +macro bar() { fn ok() {} } + +foo!(); +bar!(); + +//- /dep.rs crate:dep +#[macro_export] +macro_rules! foo { + () => { struct NotOk; } +} + +//- /core.rs crate:core +pub mod prelude { + pub mod rust_2021 { + #[macro_export] + macro_rules! bar { + () => { fn not_ok() {} } + } + } +} + "#, + expect![[r#" + crate + Ok: t v + bar: m + dep: t + foo: m + ok: v + "#]], + ); +}