Auto merge of #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
This commit is contained in:
commit
9eb26a9375
@ -35,8 +35,8 @@
|
|||||||
derive_macro_as_call_id,
|
derive_macro_as_call_id,
|
||||||
item_scope::{ImportType, PerNsGlobImports},
|
item_scope::{ImportType, PerNsGlobImports},
|
||||||
item_tree::{
|
item_tree::{
|
||||||
self, Fields, FileItemTreeId, ImportKind, ItemTree, ItemTreeId, ItemTreeNode, MacroCall,
|
self, ExternCrate, Fields, FileItemTreeId, ImportKind, ItemTree, ItemTreeId, ItemTreeNode,
|
||||||
MacroDef, MacroRules, Mod, ModItem, ModKind, TreeId,
|
MacroCall, MacroDef, MacroRules, Mod, ModItem, ModKind, TreeId,
|
||||||
},
|
},
|
||||||
macro_call_as_call_id, macro_id_to_def_id,
|
macro_call_as_call_id, macro_id_to_def_id,
|
||||||
nameres::{
|
nameres::{
|
||||||
@ -712,43 +712,30 @@ fn define_proc_macro(&mut self, name: Name, macro_: ProcMacroId) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Import macros from `#[macro_use] extern crate`.
|
/// Import exported macros from another crate. `names`, if `Some(_)`, specifies the name of
|
||||||
// FIXME: Support `#[macro_rules(macro_name, ...)]`.
|
/// macros to be imported. Otherwise this method imports all exported macros.
|
||||||
fn import_macros_from_extern_crate(
|
|
||||||
&mut self,
|
|
||||||
current_module_id: LocalModuleId,
|
|
||||||
extern_crate: &item_tree::ExternCrate,
|
|
||||||
) {
|
|
||||||
tracing::debug!(
|
|
||||||
"importing macros from extern crate: {:?} ({:?})",
|
|
||||||
extern_crate,
|
|
||||||
self.def_map.edition,
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Some(m) = self.resolve_extern_crate(&extern_crate.name) {
|
|
||||||
if m == self.def_map.module_id(current_module_id) {
|
|
||||||
cov_mark::hit!(ignore_macro_use_extern_crate_self);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
cov_mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use);
|
|
||||||
self.import_all_macros_exported(m.krate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Import all exported macros from another crate
|
|
||||||
///
|
///
|
||||||
/// 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, krate: CrateId) {
|
fn import_macros_from_extern_crate(&mut self, krate: CrateId, names: Option<Vec<Name>>) {
|
||||||
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() {
|
|
||||||
// `#[macro_use]` brings macros into macro_use prelude. Yes, even non-`macro_rules!`
|
// `#[macro_use]` brings macros into macro_use prelude. Yes, even non-`macro_rules!`
|
||||||
// macros.
|
// macros.
|
||||||
|
let root_scope = &def_map[def_map.root].scope;
|
||||||
|
if let Some(names) = names {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Tries to resolve every currently unresolved import.
|
/// Tries to resolve every currently unresolved import.
|
||||||
fn resolve_imports(&mut self) -> ReachedFixedPoint {
|
fn resolve_imports(&mut self) -> ReachedFixedPoint {
|
||||||
@ -1537,7 +1524,7 @@ fn collect(&mut self, items: &[ModItem], container: ItemContainerId) {
|
|||||||
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 && is_crate_root {
|
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(prelude_module.krate);
|
self.def_collector.import_macros_from_extern_crate(prelude_module.krate, None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1547,21 +1534,10 @@ fn collect(&mut self, items: &[ModItem], container: ItemContainerId) {
|
|||||||
//
|
//
|
||||||
// If we're not at the crate root, `macro_use`d extern crates are an error so let's just
|
// If we're not at the crate root, `macro_use`d extern crates are an error so let's just
|
||||||
// ignore them.
|
// ignore them.
|
||||||
// FIXME: Support `#[macro_rules(macro_name, ...)]`.
|
|
||||||
if is_crate_root {
|
if is_crate_root {
|
||||||
for &item in items {
|
for &item in items {
|
||||||
let ModItem::ExternCrate(id) = item else { continue; };
|
if let ModItem::ExternCrate(id) = item {
|
||||||
let attrs = self.item_tree.attrs(self.def_collector.db, krate, item.into());
|
self.process_macro_use_extern_crate(id);
|
||||||
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,
|
|
||||||
krate,
|
|
||||||
ModItem::from(id).into(),
|
|
||||||
);
|
|
||||||
if attrs.by_key("macro_use").exists() {
|
|
||||||
self.def_collector.import_macros_from_extern_crate(self.module_id, import);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1788,6 +1764,52 @@ fn collect(&mut self, items: &[ModItem], container: ItemContainerId) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn process_macro_use_extern_crate(&mut self, extern_crate: FileItemTreeId<ExternCrate>) {
|
||||||
|
let db = self.def_collector.db;
|
||||||
|
let attrs = self.item_tree.attrs(
|
||||||
|
db,
|
||||||
|
self.def_collector.def_map.krate,
|
||||||
|
ModItem::from(extern_crate).into(),
|
||||||
|
);
|
||||||
|
if let Some(cfg) = attrs.cfg() {
|
||||||
|
if !self.is_cfg_enabled(&cfg) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let target_crate =
|
||||||
|
match self.def_collector.resolve_extern_crate(&self.item_tree[extern_crate].name) {
|
||||||
|
Some(m) => {
|
||||||
|
if m == self.def_collector.def_map.module_id(self.module_id) {
|
||||||
|
cov_mark::hit!(ignore_macro_use_extern_crate_self);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m.krate
|
||||||
|
}
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
cov_mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use);
|
||||||
|
|
||||||
|
let mut single_imports = Vec::new();
|
||||||
|
let hygiene = Hygiene::new_unhygienic();
|
||||||
|
for attr in attrs.by_key("macro_use").attrs() {
|
||||||
|
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);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
for path in paths {
|
||||||
|
if let Some(name) = path.as_ident() {
|
||||||
|
single_imports.push(name.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.def_collector.import_macros_from_extern_crate(target_crate, Some(single_imports));
|
||||||
|
}
|
||||||
|
|
||||||
fn collect_module(&mut self, module_id: FileItemTreeId<Mod>, attrs: &Attrs) {
|
fn collect_module(&mut self, module_id: FileItemTreeId<Mod>, attrs: &Attrs) {
|
||||||
let path_attr = attrs.by_key("path").string_value();
|
let path_attr = attrs.by_key("path").string_value();
|
||||||
let is_macro_use = attrs.by_key("macro_use").exists();
|
let is_macro_use = attrs.by_key("macro_use").exists();
|
||||||
|
@ -259,6 +259,72 @@ macro_rules! structs_priv {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn macro_use_filter() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
//- /main.rs crate:main deps:empty,multiple,all
|
||||||
|
#[macro_use()]
|
||||||
|
extern crate empty;
|
||||||
|
|
||||||
|
foo_not_imported!();
|
||||||
|
|
||||||
|
#[macro_use(bar1)]
|
||||||
|
#[macro_use()]
|
||||||
|
#[macro_use(bar2, bar3)]
|
||||||
|
extern crate multiple;
|
||||||
|
|
||||||
|
bar1!();
|
||||||
|
bar2!();
|
||||||
|
bar3!();
|
||||||
|
bar_not_imported!();
|
||||||
|
|
||||||
|
#[macro_use(baz1)]
|
||||||
|
#[macro_use]
|
||||||
|
#[macro_use(baz2)]
|
||||||
|
extern crate all;
|
||||||
|
|
||||||
|
baz1!();
|
||||||
|
baz2!();
|
||||||
|
baz3!();
|
||||||
|
|
||||||
|
//- /empty.rs crate:empty
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! foo_not_imported { () => { struct NotOkFoo; } }
|
||||||
|
|
||||||
|
//- /multiple.rs crate:multiple
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! bar1 { () => { struct OkBar1; } }
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! bar2 { () => { struct OkBar2; } }
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! bar3 { () => { struct OkBar3; } }
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! bar_not_imported { () => { struct NotOkBar; } }
|
||||||
|
|
||||||
|
//- /all.rs crate:all
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! baz1 { () => { struct OkBaz1; } }
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! baz2 { () => { struct OkBaz2; } }
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! baz3 { () => { struct OkBaz3; } }
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
crate
|
||||||
|
OkBar1: t v
|
||||||
|
OkBar2: t v
|
||||||
|
OkBar3: t v
|
||||||
|
OkBaz1: t v
|
||||||
|
OkBaz2: t v
|
||||||
|
OkBaz3: t v
|
||||||
|
all: t
|
||||||
|
empty: t
|
||||||
|
multiple: t
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn prelude_is_macro_use() {
|
fn prelude_is_macro_use() {
|
||||||
cov_mark::check!(prelude_is_macro_use);
|
cov_mark::check!(prelude_is_macro_use);
|
||||||
|
Loading…
Reference in New Issue
Block a user