Skip to content

Commit 9eb26a9

Browse files
committed
Auto merge of rust-lang#14809 - lowr:patch/macro_use-filter, r=Veykril
Support `#[macro_use(name, ...)]` This PR adds support for another form of the `macro_use` attribute: `#[macro_use(name, ...)]` ([reference]). Note that this form of the attribute is only applicable to extern crate decls, not to mod decls. [reference]: https://doc.rust-lang.org/reference/macros-by-example.html#the-macro_use-attribute
2 parents 1e6bd6c + 1bc7f8a commit 9eb26a9

File tree

2 files changed

+134
-46
lines changed

2 files changed

+134
-46
lines changed

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

+68-46
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ use crate::{
3535
derive_macro_as_call_id,
3636
item_scope::{ImportType, PerNsGlobImports},
3737
item_tree::{
38-
self, Fields, FileItemTreeId, ImportKind, ItemTree, ItemTreeId, ItemTreeNode, MacroCall,
39-
MacroDef, MacroRules, Mod, ModItem, ModKind, TreeId,
38+
self, ExternCrate, Fields, FileItemTreeId, ImportKind, ItemTree, ItemTreeId, ItemTreeNode,
39+
MacroCall, MacroDef, MacroRules, Mod, ModItem, ModKind, TreeId,
4040
},
4141
macro_call_as_call_id, macro_id_to_def_id,
4242
nameres::{
@@ -712,41 +712,28 @@ impl DefCollector<'_> {
712712
);
713713
}
714714

715-
/// Import macros from `#[macro_use] extern crate`.
716-
// FIXME: Support `#[macro_rules(macro_name, ...)]`.
717-
fn import_macros_from_extern_crate(
718-
&mut self,
719-
current_module_id: LocalModuleId,
720-
extern_crate: &item_tree::ExternCrate,
721-
) {
722-
tracing::debug!(
723-
"importing macros from extern crate: {:?} ({:?})",
724-
extern_crate,
725-
self.def_map.edition,
726-
);
727-
728-
if let Some(m) = self.resolve_extern_crate(&extern_crate.name) {
729-
if m == self.def_map.module_id(current_module_id) {
730-
cov_mark::hit!(ignore_macro_use_extern_crate_self);
731-
return;
732-
}
733-
734-
cov_mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use);
735-
self.import_all_macros_exported(m.krate);
736-
}
737-
}
738-
739-
/// Import all exported macros from another crate
715+
/// Import exported macros from another crate. `names`, if `Some(_)`, specifies the name of
716+
/// macros to be imported. Otherwise this method imports all exported macros.
740717
///
741718
/// Exported macros are just all macros in the root module scope.
742719
/// Note that it contains not only all `#[macro_export]` macros, but also all aliases
743720
/// created by `use` in the root module, ignoring the visibility of `use`.
744-
fn import_all_macros_exported(&mut self, krate: CrateId) {
721+
fn import_macros_from_extern_crate(&mut self, krate: CrateId, names: Option<Vec<Name>>) {
745722
let def_map = self.db.crate_def_map(krate);
746-
for (name, def) in def_map[def_map.root].scope.macros() {
747-
// `#[macro_use]` brings macros into macro_use prelude. Yes, even non-`macro_rules!`
748-
// macros.
749-
self.def_map.macro_use_prelude.insert(name.clone(), def);
723+
// `#[macro_use]` brings macros into macro_use prelude. Yes, even non-`macro_rules!`
724+
// macros.
725+
let root_scope = &def_map[def_map.root].scope;
726+
if let Some(names) = names {
727+
for name in names {
728+
// FIXME: Report diagnostic on 404.
729+
if let Some(def) = root_scope.get(&name).take_macros() {
730+
self.def_map.macro_use_prelude.insert(name, def);
731+
}
732+
}
733+
} else {
734+
for (name, def) in root_scope.macros() {
735+
self.def_map.macro_use_prelude.insert(name.clone(), def);
736+
}
750737
}
751738
}
752739

@@ -1537,7 +1524,7 @@ impl ModCollector<'_, '_> {
15371524
if let Some(prelude_module) = self.def_collector.def_map.prelude {
15381525
if prelude_module.krate != krate && is_crate_root {
15391526
cov_mark::hit!(prelude_is_macro_use);
1540-
self.def_collector.import_all_macros_exported(prelude_module.krate);
1527+
self.def_collector.import_macros_from_extern_crate(prelude_module.krate, None);
15411528
}
15421529
}
15431530

@@ -1547,21 +1534,10 @@ impl ModCollector<'_, '_> {
15471534
//
15481535
// If we're not at the crate root, `macro_use`d extern crates are an error so let's just
15491536
// ignore them.
1550-
// FIXME: Support `#[macro_rules(macro_name, ...)]`.
15511537
if is_crate_root {
15521538
for &item in items {
1553-
let ModItem::ExternCrate(id) = item else { continue; };
1554-
let attrs = self.item_tree.attrs(self.def_collector.db, krate, item.into());
1555-
if attrs.cfg().map_or(true, |cfg| self.is_cfg_enabled(&cfg)) {
1556-
let import = &self.item_tree[id];
1557-
let attrs = self.item_tree.attrs(
1558-
self.def_collector.db,
1559-
krate,
1560-
ModItem::from(id).into(),
1561-
);
1562-
if attrs.by_key("macro_use").exists() {
1563-
self.def_collector.import_macros_from_extern_crate(self.module_id, import);
1564-
}
1539+
if let ModItem::ExternCrate(id) = item {
1540+
self.process_macro_use_extern_crate(id);
15651541
}
15661542
}
15671543
}
@@ -1788,6 +1764,52 @@ impl ModCollector<'_, '_> {
17881764
}
17891765
}
17901766

1767+
fn process_macro_use_extern_crate(&mut self, extern_crate: FileItemTreeId<ExternCrate>) {
1768+
let db = self.def_collector.db;
1769+
let attrs = self.item_tree.attrs(
1770+
db,
1771+
self.def_collector.def_map.krate,
1772+
ModItem::from(extern_crate).into(),
1773+
);
1774+
if let Some(cfg) = attrs.cfg() {
1775+
if !self.is_cfg_enabled(&cfg) {
1776+
return;
1777+
}
1778+
}
1779+
1780+
let target_crate =
1781+
match self.def_collector.resolve_extern_crate(&self.item_tree[extern_crate].name) {
1782+
Some(m) => {
1783+
if m == self.def_collector.def_map.module_id(self.module_id) {
1784+
cov_mark::hit!(ignore_macro_use_extern_crate_self);
1785+
return;
1786+
}
1787+
m.krate
1788+
}
1789+
None => return,
1790+
};
1791+
1792+
cov_mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use);
1793+
1794+
let mut single_imports = Vec::new();
1795+
let hygiene = Hygiene::new_unhygienic();
1796+
for attr in attrs.by_key("macro_use").attrs() {
1797+
let Some(paths) = attr.parse_path_comma_token_tree(db.upcast(), &hygiene) else {
1798+
// `#[macro_use]` (without any paths) found, forget collected names and just import
1799+
// all visible macros.
1800+
self.def_collector.import_macros_from_extern_crate(target_crate, None);
1801+
return;
1802+
};
1803+
for path in paths {
1804+
if let Some(name) = path.as_ident() {
1805+
single_imports.push(name.clone());
1806+
}
1807+
}
1808+
}
1809+
1810+
self.def_collector.import_macros_from_extern_crate(target_crate, Some(single_imports));
1811+
}
1812+
17911813
fn collect_module(&mut self, module_id: FileItemTreeId<Mod>, attrs: &Attrs) {
17921814
let path_attr = attrs.by_key("path").string_value();
17931815
let is_macro_use = attrs.by_key("macro_use").exists();

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

+66
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,72 @@ mod priv_mod {
259259
);
260260
}
261261

262+
#[test]
263+
fn macro_use_filter() {
264+
check(
265+
r#"
266+
//- /main.rs crate:main deps:empty,multiple,all
267+
#[macro_use()]
268+
extern crate empty;
269+
270+
foo_not_imported!();
271+
272+
#[macro_use(bar1)]
273+
#[macro_use()]
274+
#[macro_use(bar2, bar3)]
275+
extern crate multiple;
276+
277+
bar1!();
278+
bar2!();
279+
bar3!();
280+
bar_not_imported!();
281+
282+
#[macro_use(baz1)]
283+
#[macro_use]
284+
#[macro_use(baz2)]
285+
extern crate all;
286+
287+
baz1!();
288+
baz2!();
289+
baz3!();
290+
291+
//- /empty.rs crate:empty
292+
#[macro_export]
293+
macro_rules! foo_not_imported { () => { struct NotOkFoo; } }
294+
295+
//- /multiple.rs crate:multiple
296+
#[macro_export]
297+
macro_rules! bar1 { () => { struct OkBar1; } }
298+
#[macro_export]
299+
macro_rules! bar2 { () => { struct OkBar2; } }
300+
#[macro_export]
301+
macro_rules! bar3 { () => { struct OkBar3; } }
302+
#[macro_export]
303+
macro_rules! bar_not_imported { () => { struct NotOkBar; } }
304+
305+
//- /all.rs crate:all
306+
#[macro_export]
307+
macro_rules! baz1 { () => { struct OkBaz1; } }
308+
#[macro_export]
309+
macro_rules! baz2 { () => { struct OkBaz2; } }
310+
#[macro_export]
311+
macro_rules! baz3 { () => { struct OkBaz3; } }
312+
"#,
313+
expect![[r#"
314+
crate
315+
OkBar1: t v
316+
OkBar2: t v
317+
OkBar3: t v
318+
OkBaz1: t v
319+
OkBaz2: t v
320+
OkBaz3: t v
321+
all: t
322+
empty: t
323+
multiple: t
324+
"#]],
325+
);
326+
}
327+
262328
#[test]
263329
fn prelude_is_macro_use() {
264330
cov_mark::check!(prelude_is_macro_use);

0 commit comments

Comments
 (0)