mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-27 21:43:37 +00:00
Merge #1743
1743: Support `#[macro_use]` on `extern crate` r=matklad a=uHOOCCOOHu Unfortunately, #1688 is still an issue. My guess is wrong :( Co-authored-by: uHOOCCOOHu <hooccooh1896@gmail.com>
This commit is contained in:
commit
6702f5b7b5
7 changed files with 119 additions and 12 deletions
|
@ -11,4 +11,5 @@ test_utils::marks!(
|
||||||
match_ergonomics_ref
|
match_ergonomics_ref
|
||||||
trait_resolution_on_fn_type
|
trait_resolution_on_fn_type
|
||||||
infer_while_let
|
infer_while_let
|
||||||
|
macro_rules_from_other_crates_are_visible_with_macro_use
|
||||||
);
|
);
|
||||||
|
|
|
@ -101,6 +101,8 @@ pub struct CrateDefMap {
|
||||||
/// However, do we want to put it as a global variable?
|
/// However, do we want to put it as a global variable?
|
||||||
poison_macros: FxHashSet<MacroDefId>,
|
poison_macros: FxHashSet<MacroDefId>,
|
||||||
|
|
||||||
|
exported_macros: FxHashMap<Name, MacroDefId>,
|
||||||
|
|
||||||
diagnostics: Vec<DefDiagnostic>,
|
diagnostics: Vec<DefDiagnostic>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -245,6 +247,7 @@ impl CrateDefMap {
|
||||||
root,
|
root,
|
||||||
modules,
|
modules,
|
||||||
poison_macros: FxHashSet::default(),
|
poison_macros: FxHashSet::default(),
|
||||||
|
exported_macros: FxHashMap::default(),
|
||||||
diagnostics: Vec::new(),
|
diagnostics: Vec::new(),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -157,11 +157,42 @@ where
|
||||||
// crate root, even if the parent modules is **not** visible.
|
// crate root, even if the parent modules is **not** visible.
|
||||||
if export {
|
if export {
|
||||||
self.update(self.def_map.root, None, &[(name.clone(), def.clone())]);
|
self.update(self.def_map.root, None, &[(name.clone(), def.clone())]);
|
||||||
|
|
||||||
|
// Exported macros are collected in crate level ready for
|
||||||
|
// glob import with `#[macro_use]`.
|
||||||
|
self.def_map.exported_macros.insert(name.clone(), macro_id);
|
||||||
}
|
}
|
||||||
self.update(module_id, None, &[(name.clone(), def)]);
|
self.update(module_id, None, &[(name.clone(), def)]);
|
||||||
self.global_macro_scope.insert(name, macro_id);
|
self.global_macro_scope.insert(name, macro_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Import macros from `#[macro_use] extern crate`.
|
||||||
|
///
|
||||||
|
/// They are non-scoped, and will only be inserted into mutable `global_macro_scope`.
|
||||||
|
fn import_macros_from_extern_crate(&mut self, import: &raw::ImportData) {
|
||||||
|
log::debug!(
|
||||||
|
"importing macros from extern crate: {:?} ({:?})",
|
||||||
|
import,
|
||||||
|
self.def_map.edition,
|
||||||
|
);
|
||||||
|
|
||||||
|
let res = self.def_map.resolve_name_in_extern_prelude(
|
||||||
|
&import
|
||||||
|
.path
|
||||||
|
.as_ident()
|
||||||
|
.expect("extern crate should have been desugared to one-element path"),
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Some(ModuleDef::Module(m)) = res.take_types() {
|
||||||
|
tested_by!(macro_rules_from_other_crates_are_visible_with_macro_use);
|
||||||
|
|
||||||
|
let item_map = self.db.crate_def_map(m.krate);
|
||||||
|
for (name, ¯o_id) in &item_map.exported_macros {
|
||||||
|
self.global_macro_scope.insert(name.clone(), macro_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn resolve_imports(&mut self) -> ReachedFixedPoint {
|
fn resolve_imports(&mut self) -> ReachedFixedPoint {
|
||||||
let mut imports = std::mem::replace(&mut self.unresolved_imports, Vec::new());
|
let mut imports = std::mem::replace(&mut self.unresolved_imports, Vec::new());
|
||||||
let mut resolved = Vec::new();
|
let mut resolved = Vec::new();
|
||||||
|
@ -494,11 +525,17 @@ where
|
||||||
for item in items {
|
for item in items {
|
||||||
match *item {
|
match *item {
|
||||||
raw::RawItem::Module(m) => self.collect_module(&self.raw_items[m]),
|
raw::RawItem::Module(m) => self.collect_module(&self.raw_items[m]),
|
||||||
raw::RawItem::Import(import) => self.def_collector.unresolved_imports.push((
|
raw::RawItem::Import(import_id) => {
|
||||||
self.module_id,
|
let import = self.raw_items[import_id].clone();
|
||||||
import,
|
// This should be processed eagerly instead of deferred to resolving.
|
||||||
self.raw_items[import].clone(),
|
// Otherwise, since it will only mutate `global_macro_scope`
|
||||||
)),
|
// without `update` names in `mod`s, unresolved macros cannot be expanded.
|
||||||
|
if import.is_extern_crate && import.is_macro_use {
|
||||||
|
self.def_collector.import_macros_from_extern_crate(&import);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.def_collector.unresolved_imports.push((self.module_id, import_id, import));
|
||||||
|
}
|
||||||
raw::RawItem::Def(def) => self.define_def(&self.raw_items[def]),
|
raw::RawItem::Def(def) => self.define_def(&self.raw_items[def]),
|
||||||
raw::RawItem::Macro(mac) => self.collect_macro(&self.raw_items[mac]),
|
raw::RawItem::Macro(mac) => self.collect_macro(&self.raw_items[mac]),
|
||||||
}
|
}
|
||||||
|
@ -860,6 +897,7 @@ mod tests {
|
||||||
root,
|
root,
|
||||||
modules,
|
modules,
|
||||||
poison_macros: FxHashSet::default(),
|
poison_macros: FxHashSet::default(),
|
||||||
|
exported_macros: FxHashMap::default(),
|
||||||
diagnostics: Vec::new(),
|
diagnostics: Vec::new(),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -154,6 +154,7 @@ pub struct ImportData {
|
||||||
pub(super) is_glob: bool,
|
pub(super) is_glob: bool,
|
||||||
pub(super) is_prelude: bool,
|
pub(super) is_prelude: bool,
|
||||||
pub(super) is_extern_crate: bool,
|
pub(super) is_extern_crate: bool,
|
||||||
|
pub(super) is_macro_use: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
@ -293,8 +294,14 @@ impl RawItemsCollector {
|
||||||
let is_prelude = use_item.has_atom_attr("prelude_import");
|
let is_prelude = use_item.has_atom_attr("prelude_import");
|
||||||
|
|
||||||
Path::expand_use_item(&use_item, |path, use_tree, is_glob, alias| {
|
Path::expand_use_item(&use_item, |path, use_tree, is_glob, alias| {
|
||||||
let import_data =
|
let import_data = ImportData {
|
||||||
ImportData { path, alias, is_glob, is_prelude, is_extern_crate: false };
|
path,
|
||||||
|
alias,
|
||||||
|
is_glob,
|
||||||
|
is_prelude,
|
||||||
|
is_extern_crate: false,
|
||||||
|
is_macro_use: false,
|
||||||
|
};
|
||||||
self.push_import(current_module, import_data, Either::A(AstPtr::new(use_tree)));
|
self.push_import(current_module, import_data, Either::A(AstPtr::new(use_tree)));
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -307,12 +314,14 @@ impl RawItemsCollector {
|
||||||
if let Some(name_ref) = extern_crate.name_ref() {
|
if let Some(name_ref) = extern_crate.name_ref() {
|
||||||
let path = Path::from_name_ref(&name_ref);
|
let path = Path::from_name_ref(&name_ref);
|
||||||
let alias = extern_crate.alias().and_then(|a| a.name()).map(|it| it.as_name());
|
let alias = extern_crate.alias().and_then(|a| a.name()).map(|it| it.as_name());
|
||||||
|
let is_macro_use = extern_crate.has_atom_attr("macro_use");
|
||||||
let import_data = ImportData {
|
let import_data = ImportData {
|
||||||
path,
|
path,
|
||||||
alias,
|
alias,
|
||||||
is_glob: false,
|
is_glob: false,
|
||||||
is_prelude: false,
|
is_prelude: false,
|
||||||
is_extern_crate: true,
|
is_extern_crate: true,
|
||||||
|
is_macro_use,
|
||||||
};
|
};
|
||||||
self.push_import(current_module, import_data, Either::B(AstPtr::new(&extern_crate)));
|
self.push_import(current_module, import_data, Either::B(AstPtr::new(&extern_crate)));
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,14 +99,14 @@ fn macro_rules_from_other_crates_are_visible() {
|
||||||
fn unexpanded_macro_should_expand_by_fixedpoint_loop() {
|
fn unexpanded_macro_should_expand_by_fixedpoint_loop() {
|
||||||
let map = def_map_with_crate_graph(
|
let map = def_map_with_crate_graph(
|
||||||
"
|
"
|
||||||
//- /main.rs
|
//- /main.rs
|
||||||
macro_rules! baz {
|
macro_rules! baz {
|
||||||
() => {
|
() => {
|
||||||
use foo::bar;
|
use foo::bar;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foo!();
|
foo!();
|
||||||
bar!();
|
bar!();
|
||||||
baz!();
|
baz!();
|
||||||
|
|
||||||
|
@ -114,7 +114,7 @@ fn unexpanded_macro_should_expand_by_fixedpoint_loop() {
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! foo {
|
macro_rules! foo {
|
||||||
() => {
|
() => {
|
||||||
struct Foo { field: u32 }
|
struct Foo { field: u32 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
|
@ -137,3 +137,57 @@ fn unexpanded_macro_should_expand_by_fixedpoint_loop() {
|
||||||
⋮foo: m
|
⋮foo: m
|
||||||
"###);
|
"###);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn macro_rules_from_other_crates_are_visible_with_macro_use() {
|
||||||
|
covers!(macro_rules_from_other_crates_are_visible_with_macro_use);
|
||||||
|
let map = def_map_with_crate_graph(
|
||||||
|
"
|
||||||
|
//- /main.rs
|
||||||
|
#[macro_use]
|
||||||
|
extern crate foo;
|
||||||
|
|
||||||
|
structs!(Foo);
|
||||||
|
structs_priv!(Bar);
|
||||||
|
structs_not_exported!(MacroNotResolved1);
|
||||||
|
crate::structs!(MacroNotResolved2);
|
||||||
|
|
||||||
|
mod bar;
|
||||||
|
|
||||||
|
//- /bar.rs
|
||||||
|
structs!(Baz);
|
||||||
|
crate::structs!(MacroNotResolved3);
|
||||||
|
|
||||||
|
//- /lib.rs
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! structs {
|
||||||
|
($i:ident) => { struct $i; }
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! structs_not_exported {
|
||||||
|
($i:ident) => { struct $i; }
|
||||||
|
}
|
||||||
|
|
||||||
|
mod priv_mod {
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! structs_priv {
|
||||||
|
($i:ident) => { struct $i; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
",
|
||||||
|
crate_graph! {
|
||||||
|
"main": ("/main.rs", ["foo"]),
|
||||||
|
"foo": ("/lib.rs", []),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
assert_snapshot!(map, @r###"
|
||||||
|
⋮crate
|
||||||
|
⋮Bar: t v
|
||||||
|
⋮Foo: t v
|
||||||
|
⋮bar: t
|
||||||
|
⋮foo: t
|
||||||
|
⋮
|
||||||
|
⋮crate::bar
|
||||||
|
⋮Baz: t v
|
||||||
|
"###);
|
||||||
|
}
|
||||||
|
|
|
@ -934,6 +934,7 @@ impl AstNode for ExternCrateItem {
|
||||||
&self.syntax
|
&self.syntax
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl ast::AttrsOwner for ExternCrateItem {}
|
||||||
impl ExternCrateItem {
|
impl ExternCrateItem {
|
||||||
pub fn name_ref(&self) -> Option<NameRef> {
|
pub fn name_ref(&self) -> Option<NameRef> {
|
||||||
AstChildren::new(&self.syntax).next()
|
AstChildren::new(&self.syntax).next()
|
||||||
|
|
|
@ -669,6 +669,7 @@ Grammar(
|
||||||
collections: [("use_trees", "UseTree")]
|
collections: [("use_trees", "UseTree")]
|
||||||
),
|
),
|
||||||
"ExternCrateItem": (
|
"ExternCrateItem": (
|
||||||
|
traits: ["AttrsOwner"],
|
||||||
options: ["NameRef", "Alias"],
|
options: ["NameRef", "Alias"],
|
||||||
),
|
),
|
||||||
"ArgList": (
|
"ArgList": (
|
||||||
|
|
Loading…
Reference in a new issue