From e0f305a6bf710f64f789f909da93a8c362823b67 Mon Sep 17 00:00:00 2001 From: uHOOCCOOHu Date: Sat, 7 Sep 2019 00:55:58 +0800 Subject: Support textual scoped macros --- crates/ra_hir/src/nameres.rs | 5 ++ crates/ra_hir/src/nameres/collector.rs | 84 +++++++++++++++++----------- crates/ra_hir/src/nameres/raw.rs | 12 +++- crates/ra_hir/src/nameres/tests/macros.rs | 91 +++++++++++++++++++++++++++++++ 4 files changed, 158 insertions(+), 34 deletions(-) (limited to 'crates') diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs index 7de422128..e6bf0e90c 100644 --- a/crates/ra_hir/src/nameres.rs +++ b/crates/ra_hir/src/nameres.rs @@ -139,6 +139,7 @@ pub(crate) struct ModuleData { pub struct ModuleScope { items: FxHashMap, macros: FxHashMap, + textual_macros: FxHashMap, } static BUILTIN_SCOPE: Lazy> = Lazy::new(|| { @@ -164,6 +165,7 @@ impl ModuleScope { _ => None, }) } + /// It resolves in module scope. Textual scoped macros are ignored here. fn get_item_or_macro(&self, name: &Name) -> Option { match (self.get(name), self.macros.get(name)) { (Some(item), _) if !item.def.is_none() => Some(Either::A(item.def)), @@ -171,6 +173,9 @@ impl ModuleScope { _ => None, } } + fn get_textual_macro(&self, name: &Name) -> Option { + self.textual_macros.get(name).copied() + } } type ItemOrMacro = Either, MacroDef>; diff --git a/crates/ra_hir/src/nameres/collector.rs b/crates/ra_hir/src/nameres/collector.rs index 1c0d4369e..f897547e3 100644 --- a/crates/ra_hir/src/nameres/collector.rs +++ b/crates/ra_hir/src/nameres/collector.rs @@ -40,7 +40,6 @@ pub(super) fn collect_defs(db: &impl DefDatabase, mut def_map: CrateDefMap) -> C glob_imports: FxHashMap::default(), unresolved_imports: Vec::new(), unexpanded_macros: Vec::new(), - global_macro_scope: FxHashMap::default(), macro_stack_monitor: MacroStackMonitor::default(), }; collector.collect(); @@ -82,7 +81,6 @@ struct DefCollector { glob_imports: FxHashMap>, unresolved_imports: Vec<(CrateModuleId, raw::ImportId, raw::ImportData)>, unexpanded_macros: Vec<(CrateModuleId, AstId, Path)>, - global_macro_scope: FxHashMap, /// 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. @@ -136,20 +134,6 @@ where 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 @@ -162,13 +146,35 @@ where 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, @@ -184,14 +190,14 @@ where 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); } } @@ -528,7 +534,7 @@ where 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); } } @@ -539,7 +545,7 @@ where 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); } } } @@ -561,10 +567,11 @@ where 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, @@ -573,9 +580,12 @@ where 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( @@ -596,7 +606,10 @@ where 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 { @@ -621,6 +634,7 @@ where 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( @@ -674,12 +688,12 @@ where 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); @@ -689,6 +703,13 @@ where // 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 { @@ -715,7 +736,6 @@ mod tests { glob_imports: FxHashMap::default(), unresolved_imports: Vec::new(), unexpanded_macros: Vec::new(), - global_macro_scope: FxHashMap::default(), macro_stack_monitor: monitor, }; collector.collect(); diff --git a/crates/ra_hir/src/nameres/raw.rs b/crates/ra_hir/src/nameres/raw.rs index 7963736e0..c646d3d00 100644 --- a/crates/ra_hir/src/nameres/raw.rs +++ b/crates/ra_hir/src/nameres/raw.rs @@ -134,12 +134,14 @@ pub(super) enum ModuleData { name: Name, ast_id: FileAstId, attr_path: Option, + is_macro_use: bool, }, Definition { name: Name, ast_id: FileAstId, items: Vec, attr_path: Option, + is_macro_use: bool, }, } @@ -267,10 +269,15 @@ impl RawItemsCollector { }; let ast_id = self.source_ast_id_map.ast_id(&module); + let is_macro_use = module.has_atom_attr("macro_use"); if module.has_semi() { let attr_path = extract_mod_path_attribute(&module); - let item = - self.raw_items.modules.alloc(ModuleData::Declaration { name, ast_id, attr_path }); + let item = self.raw_items.modules.alloc(ModuleData::Declaration { + name, + ast_id, + attr_path, + is_macro_use, + }); self.push_item(current_module, RawItem::Module(item)); return; } @@ -282,6 +289,7 @@ impl RawItemsCollector { ast_id, items: Vec::new(), attr_path, + is_macro_use, }); self.process_module(Some(item), item_list); self.push_item(current_module, RawItem::Module(item)); diff --git a/crates/ra_hir/src/nameres/tests/macros.rs b/crates/ra_hir/src/nameres/tests/macros.rs index c62152d26..8f0db95f2 100644 --- a/crates/ra_hir/src/nameres/tests/macros.rs +++ b/crates/ra_hir/src/nameres/tests/macros.rs @@ -277,3 +277,94 @@ fn prelude_cycle() { ⋮crate::foo "###); } + +#[test] +fn plain_macros_are_textual_scoped_between_modules() { + let map = def_map( + r#" + //- /main.rs + mod m1; + bar!(NotFoundNotMacroUse); + + mod m2 { + foo!(NotFoundBeforeInside2); + } + + macro_rules! foo { + ($x:ident) => { struct $x; } + } + foo!(Ok); + + mod m3; + foo!(OkShadowStop); + bar!(NotFoundMacroUseStop); + + #[macro_use] + mod m5 { + #[macro_use] + mod m6 { + macro_rules! foo { + ($x:ident) => { fn $x() {} } + } + } + } + foo!(ok_double_macro_use_shadow); + + //- /m1.rs + foo!(NotFoundBeforeInside1); + macro_rules! bar { + ($x:ident) => { struct $x; } + } + + //- /m3/mod.rs + foo!(OkAfterInside); + macro_rules! foo { + ($x:ident) => { fn $x() {} } + } + foo!(ok_shadow); + + #[macro_use] + mod m4; + bar!(OkMacroUse); + + //- /m3/m4.rs + foo!(ok_shadow_deep); + macro_rules! bar { + ($x:ident) => { struct $x; } + } + "#, + ); + assert_snapshot!(map, @r###" + ⋮crate + ⋮Ok: t v + ⋮OkShadowStop: t v + ⋮foo: m + ⋮m1: t + ⋮m2: t + ⋮m3: t + ⋮m5: t + ⋮ok_double_macro_use_shadow: v + ⋮ + ⋮crate::m1 + ⋮bar: m + ⋮ + ⋮crate::m5 + ⋮m6: t + ⋮ + ⋮crate::m5::m6 + ⋮foo: m + ⋮ + ⋮crate::m2 + ⋮ + ⋮crate::m3 + ⋮OkAfterInside: t v + ⋮OkMacroUse: t v + ⋮foo: m + ⋮m4: t + ⋮ok_shadow: v + ⋮ + ⋮crate::m3::m4 + ⋮bar: m + ⋮ok_shadow_deep: v + "###); +} -- cgit v1.2.3