diff --git a/crates/ra_hir/src/nameres/collector.rs b/crates/ra_hir/src/nameres/collector.rs index dbd68723673..5d1c4292656 100644 --- a/crates/ra_hir/src/nameres/collector.rs +++ b/crates/ra_hir/src/nameres/collector.rs @@ -166,6 +166,33 @@ where 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 { let mut imports = std::mem::replace(&mut self.unresolved_imports, Vec::new()); let mut resolved = Vec::new(); @@ -299,21 +326,6 @@ where } } - // `#[macro_use] extern crate` glob imports all macros exported, - // ignoring their scopes - if import.is_extern_crate && import.is_macro_use { - if let Some(ModuleDef::Module(m)) = - def.a().and_then(|item| item.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.define_macro(module_id, name.clone(), macro_id, false); - } - } - } - let resolution = match def { Either::A(item) => { Either::A(Resolution { def: item, import: Some(import_id) }) @@ -513,11 +525,17 @@ where for item in items { match *item { raw::RawItem::Module(m) => self.collect_module(&self.raw_items[m]), - raw::RawItem::Import(import) => self.def_collector.unresolved_imports.push(( - self.module_id, - import, - self.raw_items[import].clone(), - )), + raw::RawItem::Import(import_id) => { + let import = self.raw_items[import_id].clone(); + // This should be processed eagerly instead of deferred to resolving. + // 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::Macro(mac) => self.collect_macro(&self.raw_items[mac]), } diff --git a/crates/ra_hir/src/nameres/tests/macros.rs b/crates/ra_hir/src/nameres/tests/macros.rs index cfddf302994..ff762ee3002 100644 --- a/crates/ra_hir/src/nameres/tests/macros.rs +++ b/crates/ra_hir/src/nameres/tests/macros.rs @@ -147,25 +147,31 @@ fn macro_rules_from_other_crates_are_visible_with_macro_use() { #[macro_use] extern crate foo; - structs!(Foo, Bar) + structs!(Foo); + structs_priv!(Bar); + structs_not_exported!(MacroNotResolved1); + crates::structs!(MacroNotResolved2); mod bar; //- /bar.rs - use crate::*; + structs!(Baz); + crates::structs!(MacroNotResolved3); //- /lib.rs #[macro_export] macro_rules! structs { - ($($i:ident),*) => { - $(struct $i { field: u32 } )* - } + ($i:ident) => { struct $i; } + } + + macro_rules! structs_not_exported { + ($i:ident) => { struct $i; } } mod priv_mod { #[macro_export] - macro_rules! baz { - () => {}; + macro_rules! structs_priv { + ($i:ident) => { struct $i; } } } ", @@ -179,16 +185,9 @@ fn macro_rules_from_other_crates_are_visible_with_macro_use() { ⋮Bar: t v ⋮Foo: t v ⋮bar: t - ⋮baz: m ⋮foo: t - ⋮structs: m ⋮ ⋮crate::bar - ⋮Bar: t v - ⋮Foo: t v - ⋮bar: t - ⋮baz: m - ⋮foo: t - ⋮structs: m + ⋮Baz: t v "###); }