Skip to content

Commit 8cf6071

Browse files
committed
Add macro_use prelude to DefMap
1 parent 3d33dc2 commit 8cf6071

File tree

4 files changed

+84
-20
lines changed

4 files changed

+84
-20
lines changed

crates/hir-def/src/nameres.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,9 @@ pub struct DefMap {
105105
prelude: Option<ModuleId>,
106106
/// The extern prelude is only populated for non-block DefMaps
107107
extern_prelude: FxHashMap<Name, ModuleId>,
108+
/// `macro_use` prelude that contains macros from `#[macro_use]`'d external crates. Note that
109+
/// this contains all kinds of macro, not just `macro_rules!` macro.
110+
macro_use_prelude: FxHashMap<Name, MacroId>,
108111

109112
/// Side table for resolving derive helpers.
110113
exported_derives: FxHashMap<MacroDefId, Box<[Name]>>,
@@ -277,6 +280,7 @@ impl DefMap {
277280
edition,
278281
recursion_limit: None,
279282
extern_prelude: FxHashMap::default(),
283+
macro_use_prelude: FxHashMap::default(),
280284
exported_derives: FxHashMap::default(),
281285
fn_proc_macro_mapping: FxHashMap::default(),
282286
proc_macro_loading_error: None,
@@ -489,6 +493,7 @@ impl DefMap {
489493
_c: _,
490494
exported_derives,
491495
extern_prelude,
496+
macro_use_prelude,
492497
diagnostics,
493498
modules,
494499
registered_attrs,
@@ -507,6 +512,7 @@ impl DefMap {
507512
} = self;
508513

509514
extern_prelude.shrink_to_fit();
515+
macro_use_prelude.shrink_to_fit();
510516
exported_derives.shrink_to_fit();
511517
diagnostics.shrink_to_fit();
512518
modules.shrink_to_fit();

crates/hir-def/src/nameres/collector.rs

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -707,6 +707,7 @@ impl DefCollector<'_> {
707707
}
708708

709709
/// Import macros from `#[macro_use] extern crate`.
710+
// FIXME: Support `#[macro_rules(macro_name, ...)]`.
710711
fn import_macros_from_extern_crate(
711712
&mut self,
712713
current_module_id: LocalModuleId,
@@ -725,7 +726,7 @@ impl DefCollector<'_> {
725726
}
726727

727728
cov_mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use);
728-
self.import_all_macros_exported(current_module_id, m.krate);
729+
self.import_all_macros_exported(m.krate);
729730
}
730731
}
731732

@@ -734,11 +735,12 @@ impl DefCollector<'_> {
734735
/// Exported macros are just all macros in the root module scope.
735736
/// Note that it contains not only all `#[macro_export]` macros, but also all aliases
736737
/// created by `use` in the root module, ignoring the visibility of `use`.
737-
fn import_all_macros_exported(&mut self, current_module_id: LocalModuleId, krate: CrateId) {
738+
fn import_all_macros_exported(&mut self, krate: CrateId) {
738739
let def_map = self.db.crate_def_map(krate);
739740
for (name, def) in def_map[def_map.root].scope.macros() {
740-
// `#[macro_use]` brings macros into legacy scope. Yes, even non-`macro_rules!` macros.
741-
self.define_legacy_macro(current_module_id, name.clone(), def);
741+
// `#[macro_use]` brings macros into macro_use prelude. Yes, even non-`macro_rules!`
742+
// macros.
743+
self.def_map.macro_use_prelude.insert(name.clone(), def);
742744
}
743745
}
744746

@@ -1509,26 +1511,32 @@ impl ModCollector<'_, '_> {
15091511

15101512
fn collect(&mut self, items: &[ModItem], container: ItemContainerId) {
15111513
let krate = self.def_collector.def_map.krate;
1514+
let is_crate_root = self.module_id == self.def_collector.def_map.root;
15121515

15131516
// Note: don't assert that inserted value is fresh: it's simply not true
15141517
// for macros.
15151518
self.def_collector.mod_dirs.insert(self.module_id, self.mod_dir.clone());
15161519

15171520
// Prelude module is always considered to be `#[macro_use]`.
15181521
if let Some(prelude_module) = self.def_collector.def_map.prelude {
1519-
if prelude_module.krate != krate {
1522+
if prelude_module.krate != krate && is_crate_root {
15201523
cov_mark::hit!(prelude_is_macro_use);
1521-
self.def_collector.import_all_macros_exported(self.module_id, prelude_module.krate);
1524+
self.def_collector.import_all_macros_exported(prelude_module.krate);
15221525
}
15231526
}
15241527

15251528
// This should be processed eagerly instead of deferred to resolving.
15261529
// `#[macro_use] extern crate` is hoisted to imports macros before collecting
15271530
// any other items.
1528-
for &item in items {
1529-
let attrs = self.item_tree.attrs(self.def_collector.db, krate, item.into());
1530-
if attrs.cfg().map_or(true, |cfg| self.is_cfg_enabled(&cfg)) {
1531-
if let ModItem::ExternCrate(id) = item {
1531+
//
1532+
// If we're not at the crate root, `macro_use`d extern crates are an error so let's just
1533+
// ignore them.
1534+
// FIXME: Support `#[macro_rules(macro_name, ...)]`.
1535+
if is_crate_root {
1536+
for &item in items {
1537+
let ModItem::ExternCrate(id) = item else { continue; };
1538+
let attrs = self.item_tree.attrs(self.def_collector.db, krate, item.into());
1539+
if attrs.cfg().map_or(true, |cfg| self.is_cfg_enabled(&cfg)) {
15321540
let import = &self.item_tree[id];
15331541
let attrs = self.item_tree.attrs(
15341542
self.def_collector.db,

crates/hir-def/src/nameres/path_resolution.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,7 @@ impl DefMap {
385385
// Resolve in:
386386
// - legacy scope of macro
387387
// - current module / scope
388-
// - extern prelude
388+
// - extern prelude / macro_use prelude
389389
// - std prelude
390390
let from_legacy_macro = self[module]
391391
.scope
@@ -414,9 +414,18 @@ impl DefMap {
414414
.get(name)
415415
.map_or(PerNs::none(), |&it| PerNs::types(it.into(), Visibility::Public))
416416
};
417+
let macro_use_prelude = || {
418+
self.macro_use_prelude
419+
.get(name)
420+
.map_or(PerNs::none(), |&it| PerNs::macros(it.into(), Visibility::Public))
421+
};
417422
let prelude = || self.resolve_in_prelude(db, name);
418423

419-
from_legacy_macro.or(from_scope_or_builtin).or_else(extern_prelude).or_else(prelude)
424+
from_legacy_macro
425+
.or(from_scope_or_builtin)
426+
.or_else(extern_prelude)
427+
.or_else(macro_use_prelude)
428+
.or_else(prelude)
420429
}
421430

422431
fn resolve_name_in_crate_root_or_extern_prelude(

crates/hir-def/src/nameres/tests/macros.rs

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1216,17 +1216,58 @@ fn proc_attr(a: TokenStream, b: TokenStream) -> TokenStream { a }
12161216
"#,
12171217
);
12181218

1219-
let root = &def_map[def_map.root()].scope;
1220-
let actual = root
1221-
.legacy_macros()
1222-
.sorted_by(|a, b| std::cmp::Ord::cmp(&a.0, &b.0))
1223-
.map(|(name, _)| format!("{name}\n"))
1224-
.collect::<String>();
1219+
let root_module = &def_map[def_map.root()].scope;
1220+
assert!(
1221+
root_module.legacy_macros().count() == 0,
1222+
"`#[macro_use]` shouldn't bring macros into textual macro scope",
1223+
);
1224+
1225+
let actual = def_map.macro_use_prelude.iter().map(|(name, _)| name).sorted().join("\n");
12251226

12261227
expect![[r#"
12271228
legacy
12281229
macro20
1229-
proc_attr
1230-
"#]]
1230+
proc_attr"#]]
12311231
.assert_eq(&actual);
12321232
}
1233+
1234+
#[test]
1235+
fn non_prelude_macros_take_precedence_over_macro_use_prelude() {
1236+
check(
1237+
r#"
1238+
//- /lib.rs edition:2021 crate:lib deps:dep,core
1239+
#[macro_use]
1240+
extern crate dep;
1241+
1242+
macro foo() { struct Ok; }
1243+
macro bar() { fn ok() {} }
1244+
1245+
foo!();
1246+
bar!();
1247+
1248+
//- /dep.rs crate:dep
1249+
#[macro_export]
1250+
macro_rules! foo {
1251+
() => { struct NotOk; }
1252+
}
1253+
1254+
//- /core.rs crate:core
1255+
pub mod prelude {
1256+
pub mod rust_2021 {
1257+
#[macro_export]
1258+
macro_rules! bar {
1259+
() => { fn not_ok() {} }
1260+
}
1261+
}
1262+
}
1263+
"#,
1264+
expect![[r#"
1265+
crate
1266+
Ok: t v
1267+
bar: m
1268+
dep: t
1269+
foo: m
1270+
ok: v
1271+
"#]],
1272+
);
1273+
}

0 commit comments

Comments
 (0)