glob_imports: FxHashMap::default(),
unresolved_imports: Vec::new(),
unexpanded_macros: Vec::new(),
- global_macro_scope: FxHashMap::default(),
macro_stack_monitor: MacroStackMonitor::default(),
};
collector.collect();
glob_imports: FxHashMap<CrateModuleId, Vec<(CrateModuleId, raw::ImportId)>>,
unresolved_imports: Vec<(CrateModuleId, raw::ImportId, raw::ImportData)>,
unexpanded_macros: Vec<(CrateModuleId, AstId<ast::MacroCall>, Path)>,
- global_macro_scope: FxHashMap<Name, MacroDefId>,
/// Some macro use `$tt:tt which mean we have to handle the macro perfectly
/// To prevent stack overflow, we add a deep counter here for prevent that.
macro_id: MacroDefId,
export: bool,
) {
- // macro-by-example in Rust have completely weird name resolution logic,
- // unlike anything else in the language. We'd don't fully implement yet,
- // just give a somewhat precise approximation.
- //
- // Specifically, we store a set of visible macros in each module, just
- // like how we do with usual items. This is wrong, however, because
- // macros can be shadowed and their scopes are mostly unrelated to
- // modules. To paper over the second problem, we also maintain
- // `global_macro_scope` which works when we construct `CrateDefMap`, but
- // is completely ignored in expressions.
- //
- // What we should do is that, in CrateDefMap, we should maintain a
- // separate tower of macro scopes, with ids. Then, for each item in the
- // module, we need to store it's macro scope.
let def = Either::B(MacroDef { id: macro_id });
// In Rust, `#[macro_export]` macros are unconditionally visible at the
self.def_map.exported_macros.insert(name.clone(), macro_id);
}
self.update(module_id, None, &[(name.clone(), def)]);
- self.global_macro_scope.insert(name, macro_id);
+ self.define_textual_macro(module_id, name.clone(), macro_id);
+ }
+
+ /// Define a macro in current textual scope.
+ ///
+ /// We use a map `textual_macros` to store all textual macros visable per module.
+ /// It will clone all macros from parent textual scope, whose definition is prior to
+ /// the definition of current module.
+ /// And also, `macro_use` on a module will import all textual macros visable inside to
+ /// current textual scope, with possible shadowing.
+ ///
+ /// In a single module, the order of definition/usage of textual scoped macros matters.
+ /// But we ignore it here to make it easy to implement.
+ fn define_textual_macro(&mut self, module_id: CrateModuleId, name: Name, macro_id: MacroDefId) {
+ // Always shadowing
+ self.def_map.modules[module_id]
+ .scope
+ .textual_macros
+ .insert(name, MacroDef { id: 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) {
+ fn import_macros_from_extern_crate(
+ &mut self,
+ current_module_id: CrateModuleId,
+ import: &raw::ImportData,
+ ) {
log::debug!(
"importing macros from extern crate: {:?} ({:?})",
import,
if let Some(ModuleDef::Module(m)) = res.take_types() {
tested_by!(macro_rules_from_other_crates_are_visible_with_macro_use);
- self.import_all_macros_exported(m);
+ self.import_all_macros_exported(current_module_id, m);
}
}
- fn import_all_macros_exported(&mut self, module: Module) {
+ fn import_all_macros_exported(&mut self, current_module_id: CrateModuleId, module: Module) {
let item_map = self.db.crate_def_map(module.krate);
for (name, ¯o_id) in &item_map.exported_macros {
- self.global_macro_scope.insert(name.clone(), macro_id);
+ self.define_textual_macro(current_module_id, name.clone(), macro_id);
}
}
if let Some(prelude_module) = self.def_collector.def_map.prelude {
if prelude_module.krate != self.def_collector.def_map.krate {
tested_by!(prelude_is_macro_use);
- self.def_collector.import_all_macros_exported(prelude_module);
+ self.def_collector.import_all_macros_exported(self.module_id, prelude_module);
}
}
if let raw::RawItem::Import(import_id) = *item {
let import = self.raw_items[import_id].clone();
if import.is_extern_crate && import.is_macro_use {
- self.def_collector.import_macros_from_extern_crate(&import);
+ self.def_collector.import_macros_from_extern_crate(self.module_id, &import);
}
}
}
fn collect_module(&mut self, module: &raw::ModuleData) {
match module {
// inline module, just recurse
- raw::ModuleData::Definition { name, items, ast_id, attr_path } => {
+ raw::ModuleData::Definition { name, items, ast_id, attr_path, is_macro_use } => {
let module_id =
self.push_child_module(name.clone(), ast_id.with_file_id(self.file_id), None);
let parent_module = ParentModule { name, attr_path: attr_path.as_ref() };
+
ModCollector {
def_collector: &mut *self.def_collector,
module_id,
parent_module: Some(parent_module),
}
.collect(&*items);
+ if *is_macro_use {
+ self.import_all_textual_macros(module_id);
+ }
}
// out of line module, resolve, parse and recurse
- raw::ModuleData::Declaration { name, ast_id, attr_path } => {
+ raw::ModuleData::Declaration { name, ast_id, attr_path, is_macro_use } => {
let ast_id = ast_id.with_file_id(self.file_id);
let is_root = self.def_collector.def_map.modules[self.module_id].parent.is_none();
match resolve_submodule(
raw_items: &raw_items,
parent_module: None,
}
- .collect(raw_items.items())
+ .collect(raw_items.items());
+ if *is_macro_use {
+ self.import_all_textual_macros(module_id);
+ }
}
Err(candidate) => self.def_collector.def_map.diagnostics.push(
DefDiagnostic::UnresolvedModule {
modules[res].parent = Some(self.module_id);
modules[res].declaration = Some(declaration);
modules[res].definition = definition;
+ modules[res].scope.textual_macros = modules[self.module_id].scope.textual_macros.clone();
modules[self.module_id].children.insert(name.clone(), res);
let resolution = Resolution {
def: PerNs::types(
let ast_id = mac.ast_id.with_file_id(self.file_id);
- // Case 2: try to expand macro_rules from this crate, triggering
+ // Case 2: try to resolve in textual scope and expand macro_rules, triggering
// recursive item collection.
- if let Some(macro_id) =
- mac.path.as_ident().and_then(|name| self.def_collector.global_macro_scope.get(&name))
- {
- let def = *macro_id;
+ if let Some(macro_def) = mac.path.as_ident().and_then(|name| {
+ self.def_collector.def_map[self.module_id].scope.get_textual_macro(&name)
+ }) {
+ let def = macro_def.id;
let macro_call_id = MacroCallLoc { def, ast_id }.id(self.def_collector.db);
self.def_collector.collect_macro_expansion(self.module_id, macro_call_id, def);
// Case 3: path to a macro from another crate, expand during name resolution
self.def_collector.unexpanded_macros.push((self.module_id, ast_id, mac.path.clone()))
}
+
+ fn import_all_textual_macros(&mut self, module_id: CrateModuleId) {
+ let macros = self.def_collector.def_map[module_id].scope.textual_macros.clone();
+ for (name, macro_) in macros {
+ self.def_collector.define_textual_macro(self.module_id, name.clone(), macro_.id);
+ }
+ }
}
fn is_macro_rules(path: &Path) -> bool {
glob_imports: FxHashMap::default(),
unresolved_imports: Vec::new(),
unexpanded_macros: Vec::new(),
- global_macro_scope: FxHashMap::default(),
macro_stack_monitor: monitor,
};
collector.collect();