]> git.lizzy.rs Git - rust.git/commitdiff
Basic Support Macro 2.0
authorEdwin Cheng <edwin0cheng@gmail.com>
Sat, 27 Mar 2021 05:44:54 +0000 (13:44 +0800)
committerEdwin Cheng <edwin0cheng@gmail.com>
Sat, 27 Mar 2021 05:44:54 +0000 (13:44 +0800)
crates/hir_def/src/nameres/collector.rs
crates/hir_def/src/nameres/tests/macros.rs
crates/hir_expand/src/db.rs
crates/hir_expand/src/hygiene.rs
crates/hir_expand/src/lib.rs
crates/hir_ty/src/tests/macros.rs
crates/syntax/src/ast/node_ext.rs

index d8fabe49ba5c252a73c683950688b4be384ed019..d58135ec98ced336c294c660776506f38d721946 100644 (file)
@@ -25,8 +25,8 @@
     derive_macro_as_call_id,
     item_scope::{ImportType, PerNsGlobImports},
     item_tree::{
-        self, FileItemTreeId, ItemTree, ItemTreeId, MacroCall, MacroRules, Mod, ModItem, ModKind,
-        StructDefKind,
+        self, FileItemTreeId, ItemTree, ItemTreeId, MacroCall, MacroDef, MacroRules, Mod, ModItem,
+        ModKind, StructDefKind,
     },
     macro_call_as_call_id,
     nameres::{
@@ -395,7 +395,7 @@ fn export_proc_macro(&mut self, def: ProcMacroDef, ast_id: AstId<ast::Fn>) {
     /// macro_rules! foo { () => {} }
     /// use foo as bar;
     /// ```
-    fn define_macro(
+    fn define_macro_rules(
         &mut self,
         module_id: LocalModuleId,
         name: Name,
@@ -430,6 +430,21 @@ fn define_legacy_macro(&mut self, module_id: LocalModuleId, name: Name, mac: Mac
         self.def_map.modules[module_id].scope.define_legacy_macro(name, mac);
     }
 
+    /// Define a macro 2.0 macro
+    ///
+    /// The scoped of macro 2.0 macro is equal to normal function
+    fn define_macro_def(
+        &mut self,
+        module_id: LocalModuleId,
+        name: Name,
+        macro_: MacroDefId,
+        vis: &RawVisibility,
+    ) {
+        let vis =
+            self.def_map.resolve_visibility(self.db, module_id, vis).unwrap_or(Visibility::Public);
+        self.update(module_id, &[(Some(name), PerNs::macros(macro_, vis))], vis, ImportType::Named);
+    }
+
     /// Define a proc macro
     ///
     /// A proc macro is similar to normal macro scope, but it would not visible in legacy textual scoped.
@@ -1067,40 +1082,7 @@ fn collect(&mut self, items: &[ModItem]) {
                 }
                 ModItem::MacroCall(mac) => self.collect_macro_call(&self.item_tree[mac]),
                 ModItem::MacroRules(id) => self.collect_macro_rules(id),
-                ModItem::MacroDef(id) => {
-                    let mac = &self.item_tree[id];
-                    let ast_id = InFile::new(self.file_id, mac.ast_id.upcast());
-
-                    // "Macro 2.0" is not currently supported by rust-analyzer, but libcore uses it
-                    // to define builtin macros, so we support at least that part.
-                    let attrs = self.item_tree.attrs(
-                        self.def_collector.db,
-                        krate,
-                        ModItem::from(id).into(),
-                    );
-                    if attrs.by_key("rustc_builtin_macro").exists() {
-                        let krate = self.def_collector.def_map.krate;
-                        let macro_id = find_builtin_macro(&mac.name, krate, ast_id)
-                            .or_else(|| find_builtin_derive(&mac.name, krate, ast_id));
-                        if let Some(macro_id) = macro_id {
-                            let vis = self
-                                .def_collector
-                                .def_map
-                                .resolve_visibility(
-                                    self.def_collector.db,
-                                    self.module_id,
-                                    &self.item_tree[mac.visibility],
-                                )
-                                .unwrap_or(Visibility::Public);
-                            self.def_collector.update(
-                                self.module_id,
-                                &[(Some(mac.name.clone()), PerNs::macros(macro_id, vis))],
-                                vis,
-                                ImportType::Named,
-                            );
-                        }
-                    }
-                }
+                ModItem::MacroDef(id) => self.collect_macro_def(id),
                 ModItem::Impl(imp) => {
                     let module = self.def_collector.def_map.module_id(self.module_id);
                     let impl_id =
@@ -1420,7 +1402,7 @@ fn collect_macro_rules(&mut self, id: FileItemTreeId<MacroRules>) {
         if attrs.by_key("rustc_builtin_macro").exists() {
             let krate = self.def_collector.def_map.krate;
             if let Some(macro_id) = find_builtin_macro(&mac.name, krate, ast_id) {
-                self.def_collector.define_macro(
+                self.def_collector.define_macro_rules(
                     self.module_id,
                     mac.name.clone(),
                     macro_id,
@@ -1436,7 +1418,49 @@ fn collect_macro_rules(&mut self, id: FileItemTreeId<MacroRules>) {
             kind: MacroDefKind::Declarative(ast_id),
             local_inner: is_local_inner,
         };
-        self.def_collector.define_macro(self.module_id, mac.name.clone(), macro_id, is_export);
+        self.def_collector.define_macro_rules(
+            self.module_id,
+            mac.name.clone(),
+            macro_id,
+            is_export,
+        );
+    }
+
+    fn collect_macro_def(&mut self, id: FileItemTreeId<MacroDef>) {
+        let krate = self.def_collector.def_map.krate;
+        let mac = &self.item_tree[id];
+        let ast_id = InFile::new(self.file_id, mac.ast_id.upcast());
+
+        // Case 1: bulitin macros
+        let attrs = self.item_tree.attrs(self.def_collector.db, krate, ModItem::from(id).into());
+        if attrs.by_key("rustc_builtin_macro").exists() {
+            let macro_id = find_builtin_macro(&mac.name, krate, ast_id)
+                .or_else(|| find_builtin_derive(&mac.name, krate, ast_id));
+
+            if let Some(macro_id) = macro_id {
+                self.def_collector.define_macro_def(
+                    self.module_id,
+                    mac.name.clone(),
+                    macro_id,
+                    &self.item_tree[mac.visibility],
+                );
+            }
+            return;
+        }
+
+        // Case 2: normal `macro`
+        let macro_id = MacroDefId {
+            krate: self.def_collector.def_map.krate,
+            kind: MacroDefKind::Declarative(ast_id),
+            local_inner: false,
+        };
+
+        self.def_collector.define_macro_def(
+            self.module_id,
+            mac.name.clone(),
+            macro_id,
+            &self.item_tree[mac.visibility],
+        );
     }
 
     fn collect_macro_call(&mut self, mac: &MacroCall) {
index 6d3cb8d7afaf23ef67a70582292b41e8d992b6fc..9986e99e411bbed157336c9fefd3015230870069 100644 (file)
@@ -837,3 +837,25 @@ pub fn derive_macro_2(_item: TokenStream) -> TokenStream {
         _ => unreachable!(),
     }
 }
+
+#[test]
+fn resolve_macro_def() {
+    check(
+        r#"
+//- /lib.rs
+pub macro structs($($i:ident),*) {
+    $(struct $i { field: u32 } )*
+}
+
+structs!(Foo);
+
+//- /nested.rs
+structs!(Bar, Baz);
+"#,
+        expect![[r#"
+            crate
+            Foo: t
+            structs: m
+        "#]],
+    );
+}
index d672f67238a2a0bb985988957145721227909cba..c0ab70b607ce2b96d19ab4ca2db452e321f41377 100644 (file)
@@ -3,7 +3,7 @@
 use std::sync::Arc;
 
 use base_db::{salsa, SourceDatabase};
-use mbe::{ExpandError, ExpandResult, MacroRules};
+use mbe::{ExpandError, ExpandResult, MacroDef, MacroRules};
 use parser::FragmentKind;
 use syntax::{
     algo::diff,
@@ -28,6 +28,7 @@
 #[derive(Debug, Clone, Eq, PartialEq)]
 pub enum TokenExpander {
     MacroRules(mbe::MacroRules),
+    MacroDef(mbe::MacroDef),
     Builtin(BuiltinFnLikeExpander),
     BuiltinDerive(BuiltinDeriveExpander),
     ProcMacro(ProcMacroExpander),
@@ -42,6 +43,7 @@ pub fn expand(
     ) -> mbe::ExpandResult<tt::Subtree> {
         match self {
             TokenExpander::MacroRules(it) => it.expand(tt),
+            TokenExpander::MacroDef(it) => it.expand(tt),
             TokenExpander::Builtin(it) => it.expand(db, id, tt),
             // FIXME switch these to ExpandResult as well
             TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt).into(),
@@ -57,6 +59,7 @@ pub fn expand(
     pub fn map_id_down(&self, id: tt::TokenId) -> tt::TokenId {
         match self {
             TokenExpander::MacroRules(it) => it.map_id_down(id),
+            TokenExpander::MacroDef(it) => it.map_id_down(id),
             TokenExpander::Builtin(..) => id,
             TokenExpander::BuiltinDerive(..) => id,
             TokenExpander::ProcMacro(..) => id,
@@ -66,6 +69,7 @@ pub fn map_id_down(&self, id: tt::TokenId) -> tt::TokenId {
     pub fn map_id_up(&self, id: tt::TokenId) -> (tt::TokenId, mbe::Origin) {
         match self {
             TokenExpander::MacroRules(it) => it.map_id_up(id),
+            TokenExpander::MacroDef(it) => it.map_id_up(id),
             TokenExpander::Builtin(..) => (id, mbe::Origin::Call),
             TokenExpander::BuiltinDerive(..) => (id, mbe::Origin::Call),
             TokenExpander::ProcMacro(..) => (id, mbe::Origin::Call),
@@ -136,26 +140,40 @@ fn ast_id_map(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<AstIdMap> {
 
 fn macro_def(db: &dyn AstDatabase, id: MacroDefId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> {
     match id.kind {
-        MacroDefKind::Declarative(ast_id) => {
-            let macro_rules = match ast_id.to_node(db) {
-                syntax::ast::Macro::MacroRules(mac) => mac,
-                syntax::ast::Macro::MacroDef(_) => return None,
-            };
-            let arg = macro_rules.token_tree()?;
-            let (tt, tmap) = mbe::ast_to_token_tree(&arg).or_else(|| {
-                log::warn!("fail on macro_def to token tree: {:#?}", arg);
-                None
-            })?;
-            let rules = match MacroRules::parse(&tt) {
-                Ok(it) => it,
-                Err(err) => {
-                    let name = macro_rules.name().map(|n| n.to_string()).unwrap_or_default();
-                    log::warn!("fail on macro_def parse ({}): {:?} {:#?}", name, err, tt);
-                    return None;
-                }
-            };
-            Some(Arc::new((TokenExpander::MacroRules(rules), tmap)))
-        }
+        MacroDefKind::Declarative(ast_id) => match ast_id.to_node(db) {
+            syntax::ast::Macro::MacroRules(macro_rules) => {
+                let arg = macro_rules.token_tree()?;
+                let (tt, tmap) = mbe::ast_to_token_tree(&arg).or_else(|| {
+                    log::warn!("fail on macro_rules to token tree: {:#?}", arg);
+                    None
+                })?;
+                let rules = match MacroRules::parse(&tt) {
+                    Ok(it) => it,
+                    Err(err) => {
+                        let name = macro_rules.name().map(|n| n.to_string()).unwrap_or_default();
+                        log::warn!("fail on macro_def parse ({}): {:?} {:#?}", name, err, tt);
+                        return None;
+                    }
+                };
+                Some(Arc::new((TokenExpander::MacroRules(rules), tmap)))
+            }
+            syntax::ast::Macro::MacroDef(macro_def) => {
+                let arg = macro_def.body()?;
+                let (tt, tmap) = mbe::ast_to_token_tree(&arg).or_else(|| {
+                    log::warn!("fail on macro_def to token tree: {:#?}", arg);
+                    None
+                })?;
+                let rules = match MacroDef::parse(&tt) {
+                    Ok(it) => it,
+                    Err(err) => {
+                        let name = macro_def.name().map(|n| n.to_string()).unwrap_or_default();
+                        log::warn!("fail on macro_def parse ({}): {:?} {:#?}", name, err, tt);
+                        return None;
+                    }
+                };
+                Some(Arc::new((TokenExpander::MacroDef(rules), tmap)))
+            }
+        },
         MacroDefKind::BuiltIn(expander, _) => {
             Some(Arc::new((TokenExpander::Builtin(expander), mbe::TokenMap::default())))
         }
index 0e0f7214a24d7de06f1cff91c45ff303e88a4d32..779725629a0003a47377ca709a292103229b743b 100644 (file)
@@ -148,7 +148,7 @@ fn make_hygiene_info(
     let def_offset = loc.def.ast_id().left().and_then(|id| {
         let def_tt = match id.to_node(db) {
             ast::Macro::MacroRules(mac) => mac.token_tree()?.syntax().text_range().start(),
-            ast::Macro::MacroDef(_) => return None,
+            ast::Macro::MacroDef(mac) => mac.body()?.syntax().text_range().start(),
         };
         Some(InFile::new(id.file_id, def_tt))
     });
index b8045fda95cb53016c7b4a16ec6b0fcb27a048b4..3e332ee47ea1c54fd4913f6e38154182c8b3ddc4 100644 (file)
@@ -151,7 +151,7 @@ pub fn expansion_info(self, db: &dyn db::AstDatabase) -> Option<ExpansionInfo> {
                 let def = loc.def.ast_id().left().and_then(|id| {
                     let def_tt = match id.to_node(db) {
                         ast::Macro::MacroRules(mac) => mac.token_tree()?,
-                        ast::Macro::MacroDef(_) => return None,
+                        ast::Macro::MacroDef(mac) => mac.body()?,
                     };
                     Some(InFile::new(id.file_id, def_tt))
                 });
index 01935ec99431542826b1edd8f7e308cff53a89d7..3eb01dbd0fa3b3be4868f78126aab9aeb40565b1 100644 (file)
@@ -135,7 +135,88 @@ fn main() {
 }
 
 #[test]
-fn expr_macro_expanded_in_various_places() {
+fn expr_macro_def_expanded_in_various_places() {
+    check_infer(
+        r#"
+        macro spam() {
+            1isize
+        }
+
+        fn spam() {
+            spam!();
+            (spam!());
+            spam!().spam(spam!());
+            for _ in spam!() {}
+            || spam!();
+            while spam!() {}
+            break spam!();
+            return spam!();
+            match spam!() {
+                _ if spam!() => spam!(),
+            }
+            spam!()(spam!());
+            Spam { spam: spam!() };
+            spam!()[spam!()];
+            await spam!();
+            spam!() as usize;
+            &spam!();
+            -spam!();
+            spam!()..spam!();
+            spam!() + spam!();
+        }
+        "#,
+        expect![[r#"
+            !0..6 '1isize': isize
+            !0..6 '1isize': isize
+            !0..6 '1isize': isize
+            !0..6 '1isize': isize
+            !0..6 '1isize': isize
+            !0..6 '1isize': isize
+            !0..6 '1isize': isize
+            !0..6 '1isize': isize
+            !0..6 '1isize': isize
+            !0..6 '1isize': isize
+            !0..6 '1isize': isize
+            !0..6 '1isize': isize
+            !0..6 '1isize': isize
+            !0..6 '1isize': isize
+            !0..6 '1isize': isize
+            !0..6 '1isize': isize
+            !0..6 '1isize': isize
+            !0..6 '1isize': isize
+            !0..6 '1isize': isize
+            !0..6 '1isize': isize
+            !0..6 '1isize': isize
+            !0..6 '1isize': isize
+            !0..6 '1isize': isize
+            !0..6 '1isize': isize
+            !0..6 '1isize': isize
+            39..442 '{     ...!(); }': ()
+            73..94 'spam!(...am!())': {unknown}
+            100..119 'for _ ...!() {}': ()
+            104..105 '_': {unknown}
+            117..119 '{}': ()
+            124..134 '|| spam!()': || -> isize
+            140..156 'while ...!() {}': ()
+            154..156 '{}': ()
+            161..174 'break spam!()': !
+            180..194 'return spam!()': !
+            200..254 'match ...     }': isize
+            224..225 '_': isize
+            259..275 'spam!(...am!())': {unknown}
+            281..303 'Spam {...m!() }': {unknown}
+            309..325 'spam!(...am!()]': {unknown}
+            350..366 'spam!(... usize': usize
+            372..380 '&spam!()': &isize
+            386..394 '-spam!()': isize
+            400..416 'spam!(...pam!()': {unknown}
+            422..439 'spam!(...pam!()': isize
+        "#]],
+    );
+}
+
+#[test]
+fn expr_macro_rules_expanded_in_various_places() {
     check_infer(
         r#"
         macro_rules! spam {
index 2772d7364442d033a91510fdd5e6cc7c74a2dc13..ae98dbd268951076f425017c6603b0b74512407b 100644 (file)
@@ -40,6 +40,7 @@ fn text_of_first_token(node: &SyntaxNode) -> TokenText {
     TokenText(first_token)
 }
 
+#[derive(Debug, PartialEq, Eq, Clone)]
 pub enum Macro {
     MacroRules(ast::MacroRules),
     MacroDef(ast::MacroDef),