Add macro_use prelude to DefMap

This commit is contained in:
Ryo Yoshida 2023-05-11 15:41:24 +09:00
parent 96113b7b8e
commit 3203ea896d
No known key found for this signature in database
GPG key ID: E25698A930586171
4 changed files with 84 additions and 20 deletions

View file

@ -105,6 +105,9 @@ pub struct DefMap {
prelude: Option<ModuleId>, prelude: Option<ModuleId>,
/// The extern prelude is only populated for non-block DefMaps /// The extern prelude is only populated for non-block DefMaps
extern_prelude: FxHashMap<Name, ModuleId>, extern_prelude: FxHashMap<Name, ModuleId>,
/// `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>,
/// Side table for resolving derive helpers. /// Side table for resolving derive helpers.
exported_derives: FxHashMap<MacroDefId, Box<[Name]>>, exported_derives: FxHashMap<MacroDefId, Box<[Name]>>,
@ -277,6 +280,7 @@ impl DefMap {
edition, edition,
recursion_limit: None, recursion_limit: None,
extern_prelude: FxHashMap::default(), extern_prelude: FxHashMap::default(),
macro_use_prelude: FxHashMap::default(),
exported_derives: FxHashMap::default(), exported_derives: FxHashMap::default(),
fn_proc_macro_mapping: FxHashMap::default(), fn_proc_macro_mapping: FxHashMap::default(),
proc_macro_loading_error: None, proc_macro_loading_error: None,
@ -489,6 +493,7 @@ impl DefMap {
_c: _, _c: _,
exported_derives, exported_derives,
extern_prelude, extern_prelude,
macro_use_prelude,
diagnostics, diagnostics,
modules, modules,
registered_attrs, registered_attrs,
@ -507,6 +512,7 @@ impl DefMap {
} = self; } = self;
extern_prelude.shrink_to_fit(); extern_prelude.shrink_to_fit();
macro_use_prelude.shrink_to_fit();
exported_derives.shrink_to_fit(); exported_derives.shrink_to_fit();
diagnostics.shrink_to_fit(); diagnostics.shrink_to_fit();
modules.shrink_to_fit(); modules.shrink_to_fit();

View file

@ -707,6 +707,7 @@ impl DefCollector<'_> {
} }
/// Import macros from `#[macro_use] extern crate`. /// Import macros from `#[macro_use] extern crate`.
// FIXME: Support `#[macro_rules(macro_name, ...)]`.
fn import_macros_from_extern_crate( fn import_macros_from_extern_crate(
&mut self, &mut self,
current_module_id: LocalModuleId, current_module_id: LocalModuleId,
@ -725,7 +726,7 @@ impl DefCollector<'_> {
} }
cov_mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use); 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. /// 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 /// 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`. /// 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); let def_map = self.db.crate_def_map(krate);
for (name, def) in def_map[def_map.root].scope.macros() { for (name, def) in def_map[def_map.root].scope.macros() {
// `#[macro_use]` brings macros into legacy scope. Yes, even non-`macro_rules!` macros. // `#[macro_use]` brings macros into macro_use prelude. Yes, even non-`macro_rules!`
self.define_legacy_macro(current_module_id, name.clone(), def); // 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) { fn collect(&mut self, items: &[ModItem], container: ItemContainerId) {
let krate = self.def_collector.def_map.krate; 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 // Note: don't assert that inserted value is fresh: it's simply not true
// for macros. // for macros.
@ -1516,19 +1519,24 @@ impl ModCollector<'_, '_> {
// Prelude module is always considered to be `#[macro_use]`. // 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) = 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); 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. // This should be processed eagerly instead of deferred to resolving.
// `#[macro_use] extern crate` is hoisted to imports macros before collecting // `#[macro_use] extern crate` is hoisted to imports macros before collecting
// any other items. // 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.
// FIXME: Support `#[macro_rules(macro_name, ...)]`.
if is_crate_root {
for &item in items { for &item in items {
let ModItem::ExternCrate(id) = item else { continue; };
let attrs = self.item_tree.attrs(self.def_collector.db, krate, item.into()); 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 attrs.cfg().map_or(true, |cfg| self.is_cfg_enabled(&cfg)) {
if let ModItem::ExternCrate(id) = item {
let import = &self.item_tree[id]; let import = &self.item_tree[id];
let attrs = self.item_tree.attrs( let attrs = self.item_tree.attrs(
self.def_collector.db, self.def_collector.db,

View file

@ -385,7 +385,7 @@ impl DefMap {
// Resolve in: // Resolve in:
// - legacy scope of macro // - legacy scope of macro
// - current module / scope // - current module / scope
// - extern prelude // - extern prelude / macro_use prelude
// - std prelude // - std prelude
let from_legacy_macro = self[module] let from_legacy_macro = self[module]
.scope .scope
@ -414,9 +414,18 @@ impl DefMap {
.get(name) .get(name)
.map_or(PerNs::none(), |&it| PerNs::types(it.into(), Visibility::Public)) .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); 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( fn resolve_name_in_crate_root_or_extern_prelude(

View file

@ -1216,17 +1216,58 @@ fn proc_attr(a: TokenStream, b: TokenStream) -> TokenStream { a }
"#, "#,
); );
let root = &def_map[def_map.root()].scope; let root_module = &def_map[def_map.root()].scope;
let actual = root assert!(
.legacy_macros() root_module.legacy_macros().count() == 0,
.sorted_by(|a, b| std::cmp::Ord::cmp(&a.0, &b.0)) "`#[macro_use]` shouldn't bring macros into textual macro scope",
.map(|(name, _)| format!("{name}\n")) );
.collect::<String>();
let actual = def_map.macro_use_prelude.iter().map(|(name, _)| name).sorted().join("\n");
expect![[r#" expect![[r#"
legacy legacy
macro20 macro20
proc_attr proc_attr"#]]
"#]]
.assert_eq(&actual); .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
"#]],
);
}