]> git.lizzy.rs Git - rust.git/commitdiff
Support textual scoped macros
authoruHOOCCOOHu <hooccooh1896@gmail.com>
Fri, 6 Sep 2019 16:55:58 +0000 (00:55 +0800)
committeruHOOCCOOHu <hooccooh1896@gmail.com>
Sun, 8 Sep 2019 17:33:28 +0000 (01:33 +0800)
crates/ra_hir/src/nameres.rs
crates/ra_hir/src/nameres/collector.rs
crates/ra_hir/src/nameres/raw.rs
crates/ra_hir/src/nameres/tests/macros.rs

index 7de422128f1e94cfdbbad78279a6929049c120d1..e6bf0e90c978e506a9ef4bbee6d5a1efd911541f 100644 (file)
@@ -139,6 +139,7 @@ pub(crate) struct ModuleData {
 pub struct ModuleScope {
     items: FxHashMap<Name, Resolution>,
     macros: FxHashMap<Name, MacroDef>,
+    textual_macros: FxHashMap<Name, MacroDef>,
 }
 
 static BUILTIN_SCOPE: Lazy<FxHashMap<Name, Resolution>> = Lazy::new(|| {
@@ -164,6 +165,7 @@ pub fn traits<'a>(&'a self) -> impl Iterator<Item = Trait> + 'a {
             _ => None,
         })
     }
+    /// It resolves in module scope. Textual scoped macros are ignored here.
     fn get_item_or_macro(&self, name: &Name) -> Option<ItemOrMacro> {
         match (self.get(name), self.macros.get(name)) {
             (Some(item), _) if !item.def.is_none() => Some(Either::A(item.def)),
@@ -171,6 +173,9 @@ fn get_item_or_macro(&self, name: &Name) -> Option<ItemOrMacro> {
             _ => None,
         }
     }
+    fn get_textual_macro(&self, name: &Name) -> Option<MacroDef> {
+        self.textual_macros.get(name).copied()
+    }
 }
 
 type ItemOrMacro = Either<PerNs<ModuleDef>, MacroDef>;
index 1c0d4369e4ca12857c151506982c5c6f87b7d1f6..f897547e39c160eb4c5dbb15dec7d5b7f268caa2 100644 (file)
@@ -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<DB> {
     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.
@@ -136,20 +134,6 @@ fn define_macro(
         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 @@ fn define_macro(
             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 @@ fn import_macros_from_extern_crate(&mut self, import: &raw::ImportData) {
 
         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, &macro_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 @@ fn collect(&mut self, items: &[raw::RawItem]) {
         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 @@ fn collect(&mut self, items: &[raw::RawItem]) {
             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 @@ fn collect(&mut self, items: &[raw::RawItem]) {
     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 @@ fn collect_module(&mut self, module: &raw::ModuleData) {
                     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 @@ fn collect_module(&mut self, module: &raw::ModuleData) {
                             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 @@ fn push_child_module(
         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 @@ fn collect_macro(&mut self, mac: &raw::MacroData) {
 
         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 @@ fn collect_macro(&mut self, mac: &raw::MacroData) {
         // 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 @@ fn do_collect_defs(
             glob_imports: FxHashMap::default(),
             unresolved_imports: Vec::new(),
             unexpanded_macros: Vec::new(),
-            global_macro_scope: FxHashMap::default(),
             macro_stack_monitor: monitor,
         };
         collector.collect();
index 7963736e0d2a466cf85ce3a0271c6f61533a45c0..c646d3d005f2c1bc5f01792f63a9a7abebd4dc42 100644 (file)
@@ -134,12 +134,14 @@ pub(super) enum ModuleData {
         name: Name,
         ast_id: FileAstId<ast::Module>,
         attr_path: Option<SmolStr>,
+        is_macro_use: bool,
     },
     Definition {
         name: Name,
         ast_id: FileAstId<ast::Module>,
         items: Vec<RawItem>,
         attr_path: Option<SmolStr>,
+        is_macro_use: bool,
     },
 }
 
@@ -267,10 +269,15 @@ fn add_module(&mut self, current_module: Option<Module>, module: ast::Module) {
         };
 
         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 @@ fn add_module(&mut self, current_module: Option<Module>, module: ast::Module) {
                 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));
index c62152d26a09eb217ea5afdce0850242da061593..8f0db95f23fa18880bad25e479871bcd3f3e179e 100644 (file)
@@ -277,3 +277,94 @@ macro_rules! declare_mod {
         ⋮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
+    "###);
+}