Repository: rust-analyzer/rust-analyzer
Pull request #8212 (2021-03-27 06:48:14)

Basic support macro 2.0
Turn out it is quite straight forward :)

r @jonas-schievink 

![m2](https://user-images.githubusercontent.com/11014119/112712565-6eb99380-8f0b-11eb-88de-5d7f974dfe6d.png)



Patch:
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 3ff135f411..d3caeef4ee 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -768,7 +768,7 @@ to_def_impls![
     (crate::TypeParam, ast::TypeParam, type_param_to_def),
     (crate::LifetimeParam, ast::LifetimeParam, lifetime_param_to_def),
     (crate::ConstParam, ast::ConstParam, const_param_to_def),
-    (crate::MacroDef, ast::MacroRules, macro_rules_to_def),
+    (crate::MacroDef, ast::Macro, macro_to_def),
     (crate::Local, ast::IdentPat, bind_pat_to_def),
     (crate::Local, ast::SelfParam, self_param_to_def),
     (crate::Label, ast::Label, label_to_def),
diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs
index 762809fcd0..9a5a2255fa 100644
--- a/crates/hir/src/semantics/source_to_def.rs
+++ b/crates/hir/src/semantics/source_to_def.rs
@@ -191,10 +191,7 @@ impl SourceToDefCtx<'_, '_> {
     }
 
     // FIXME: use DynMap as well?
-    pub(super) fn macro_rules_to_def(
-        &mut self,
-        src: InFile<ast::MacroRules>,
-    ) -> Option<MacroDefId> {
+    pub(super) fn macro_to_def(&mut self, src: InFile<ast::Macro>) -> Option<MacroDefId> {
         let file_ast_id = self.db.ast_id_map(src.file_id).ast_id(&src.value);
         let ast_id = AstId::new(src.file_id, file_ast_id.upcast());
         let kind = MacroDefKind::Declarative(ast_id);
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index d8fabe49ba..d58135ec98 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -25,8 +25,8 @@ use crate::{
     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 @@ impl DefCollector<'_> {
     /// 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 @@ impl DefCollector<'_> {
         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 @@ impl ModCollector<'_, '_> {
                 }
                 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 @@ impl ModCollector<'_, '_> {
         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 @@ impl ModCollector<'_, '_> {
             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) {
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs
index d672f67238..c0ab70b607 100644
--- a/crates/hir_expand/src/db.rs
+++ b/crates/hir_expand/src/db.rs
@@ -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 @@ const TOKEN_LIMIT: usize = 524288;
 #[derive(Debug, Clone, Eq, PartialEq)]
 pub enum TokenExpander {
     MacroRules(mbe::MacroRules),
+    MacroDef(mbe::MacroDef),
     Builtin(BuiltinFnLikeExpander),
     BuiltinDerive(BuiltinDeriveExpander),
     ProcMacro(ProcMacroExpander),
@@ -42,6 +43,7 @@ impl TokenExpander {
     ) -> 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 @@ impl TokenExpander {
     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 @@ impl TokenExpander {
     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())))
         }
diff --git a/crates/hir_expand/src/hygiene.rs b/crates/hir_expand/src/hygiene.rs
index 0e0f7214a2..779725629a 100644
--- a/crates/hir_expand/src/hygiene.rs
+++ b/crates/hir_expand/src/hygiene.rs
@@ -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))
     });
diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs
index b8045fda95..3e332ee47e 100644
--- a/crates/hir_expand/src/lib.rs
+++ b/crates/hir_expand/src/lib.rs
@@ -151,7 +151,7 @@ impl HirFileId {
                 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))
                 });
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs
index 0cee741acf..9301cdeffe 100644
--- a/crates/ide/src/doc_links.rs
+++ b/crates/ide/src/doc_links.rs
@@ -161,7 +161,7 @@ pub(crate) fn doc_owner_to_def(
             ast::Variant(it) => sema.to_def(&it)?.into(),
             ast::Trait(it) => sema.to_def(&it)?.into(),
             ast::Impl(it) => return sema.to_def(&it).map(Definition::SelfType),
-            ast::MacroRules(it) => return sema.to_def(&it).map(Definition::Macro),
+            ast::Macro(it) => return sema.to_def(&it).map(Definition::Macro),
             ast::TupleField(it) => return sema.to_def(&it).map(Definition::Field),
             ast::RecordField(it) => return sema.to_def(&it).map(Definition::Field),
             _ => return None,
diff --git a/crates/ide/src/file_structure.rs b/crates/ide/src/file_structure.rs
index 9f879a66e7..2c898eae85 100644
--- a/crates/ide/src/file_structure.rs
+++ b/crates/ide/src/file_structure.rs
@@ -172,7 +172,7 @@ fn structure_node(node: &SyntaxNode) -> Option<StructureNode> {
                 };
                 Some(node)
             },
-            ast::MacroRules(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Macro)),
+            ast::Macro(it) => decl(it, StructureNodeKind::SymbolKind(SymbolKind::Macro)),
             _ => None,
         }
     }
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index c430894765..3c951c5073 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -1234,6 +1234,37 @@ fn f() { fo$0o!(); }
         )
     }
 
+    #[test]
+    fn test_hover_macro2_invocation() {
+        check(
+            r#"
+/// foo bar
+///
+/// foo bar baz
+macro foo() {}
+
+fn f() { fo$0o!(); }
+"#,
+            expect![[r#"
+                *foo*
+
+                ```rust
+                test
+                ```
+
+                ```rust
+                macro foo
+                ```
+
+                ---
+
+                foo bar
+
+                foo bar baz
+            "#]],
+        )
+    }
+
     #[test]
     fn test_hover_tuple_field() {
         check(
diff --git a/crates/ide/src/move_item.rs b/crates/ide/src/move_item.rs
index 48690b0731..05fa8fc134 100644
--- a/crates/ide/src/move_item.rs
+++ b/crates/ide/src/move_item.rs
@@ -66,6 +66,7 @@ fn find_ancestors(item: SyntaxElement, direction: Direction, range: TextRange) -
         SyntaxKind::STATIC,
         SyntaxKind::CONST,
         SyntaxKind::MACRO_RULES,
+        SyntaxKind::MACRO_DEF,
     ];
 
     let ancestor = once(root.clone())
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs
index e25b698e0c..67a10766b2 100644
--- a/crates/ide/src/syntax_highlighting.rs
+++ b/crates/ide/src/syntax_highlighting.rs
@@ -5,7 +5,7 @@ mod injector;
 
 mod highlight;
 mod format;
-mod macro_rules;
+mod macro_;
 mod inject;
 
 mod html;
@@ -24,8 +24,8 @@ use syntax::{
 
 use crate::{
     syntax_highlighting::{
-        format::highlight_format_string, highlights::Highlights,
-        macro_rules::MacroRulesHighlighter, tags::Highlight,
+        format::highlight_format_string, highlights::Highlights, macro_::MacroHighlighter,
+        tags::Highlight,
     },
     FileId, HlMod, HlTag,
 };
@@ -93,8 +93,8 @@ fn traverse(
     let mut bindings_shadow_count: FxHashMap<Name, u32> = FxHashMap::default();
 
     let mut current_macro_call: Option<ast::MacroCall> = None;
-    let mut current_macro_rules: Option<ast::MacroRules> = None;
-    let mut macro_rules_highlighter = MacroRulesHighlighter::default();
+    let mut current_macro: Option<ast::Macro> = None;
+    let mut macro_highlighter = MacroHighlighter::default();
     let mut inside_attribute = false;
 
     // Walk all nodes, keeping track of whether we are inside a macro or not.
@@ -129,16 +129,16 @@ fn traverse(
             _ => (),
         }
 
-        match event.clone().map(|it| it.into_node().and_then(ast::MacroRules::cast)) {
+        match event.clone().map(|it| it.into_node().and_then(ast::Macro::cast)) {
             WalkEvent::Enter(Some(mac)) => {
-                macro_rules_highlighter.init();
-                current_macro_rules = Some(mac);
+                macro_highlighter.init();
+                current_macro = Some(mac);
                 continue;
             }
             WalkEvent::Leave(Some(mac)) => {
-                assert_eq!(current_macro_rules, Some(mac));
-                current_macro_rules = None;
-                macro_rules_highlighter = MacroRulesHighlighter::default();
+                assert_eq!(current_macro, Some(mac));
+                current_macro = None;
+                macro_highlighter = MacroHighlighter::default();
             }
             _ => (),
         }
@@ -164,9 +164,9 @@ fn traverse(
 
         let range = element.text_range();
 
-        if current_macro_rules.is_some() {
+        if current_macro.is_some() {
             if let Some(tok) = element.as_token() {
-                macro_rules_highlighter.advance(tok);
+                macro_highlighter.advance(tok);
             }
         }
 
@@ -200,7 +200,7 @@ fn traverse(
             }
         }
 
-        if let Some(_) = macro_rules_highlighter.highlight(element_to_highlight.clone()) {
+        if let Some(_) = macro_highlighter.highlight(element_to_highlight.clone()) {
             continue;
         }
 
diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs
index 38bf493489..963c3fb594 100644
--- a/crates/ide/src/syntax_highlighting/inject.rs
+++ b/crates/ide/src/syntax_highlighting/inject.rs
@@ -109,8 +109,7 @@ fn doc_attributes<'node>(
             ast::Impl(it)        => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::SelfType(def))),
             ast::RecordField(it) => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Field(def))),
             ast::TupleField(it)  => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Field(def))),
-            ast::MacroRules(it)  => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Macro(def))),
-            // ast::MacroDef(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))),
+            ast::Macro(it)       => sema.to_def(&it).map(|def| (def.attrs(sema.db), Definition::Macro(def))),
             // ast::Use(it) => sema.to_def(&it).map(|def| (Box::new(it) as _, def.attrs(sema.db))),
             _ => return None
         }
diff --git a/crates/ide/src/syntax_highlighting/macro_rules.rs b/crates/ide/src/syntax_highlighting/macro_.rs
similarity index 93%
rename from crates/ide/src/syntax_highlighting/macro_rules.rs
rename to crates/ide/src/syntax_highlighting/macro_.rs
index 44620e912d..8197042942 100644
--- a/crates/ide/src/syntax_highlighting/macro_rules.rs
+++ b/crates/ide/src/syntax_highlighting/macro_.rs
@@ -4,18 +4,18 @@ use syntax::{SyntaxElement, SyntaxKind, SyntaxToken, TextRange, T};
 use crate::{HlRange, HlTag};
 
 #[derive(Default)]
-pub(super) struct MacroRulesHighlighter {
+pub(super) struct MacroHighlighter {
     state: Option<MacroMatcherParseState>,
 }
 
-impl MacroRulesHighlighter {
+impl MacroHighlighter {
     pub(super) fn init(&mut self) {
         self.state = Some(MacroMatcherParseState::default());
     }
 
     pub(super) fn advance(&mut self, token: &SyntaxToken) {
         if let Some(state) = self.state.as_mut() {
-            update_macro_rules_state(state, token);
+            update_macro_state(state, token);
         }
     }
 
@@ -74,9 +74,9 @@ impl RuleState {
     }
 }
 
-fn update_macro_rules_state(state: &mut MacroMatcherParseState, tok: &SyntaxToken) {
+fn update_macro_state(state: &mut MacroMatcherParseState, tok: &SyntaxToken) {
     if !state.in_invoc_body {
-        if tok.kind() == T!['{'] {
+        if tok.kind() == T!['{'] || tok.kind() == T!['('] {
             state.in_invoc_body = true;
         }
         return;
diff --git a/crates/ide_db/src/defs.rs b/crates/ide_db/src/defs.rs
index ab23dd7ac8..0d9808d243 100644
--- a/crates/ide_db/src/defs.rs
+++ b/crates/ide_db/src/defs.rs
@@ -227,7 +227,7 @@ impl NameClass {
                     let def: hir::TypeAlias = sema.to_def(&it)?;
                     Some(NameClass::Definition(Definition::ModuleDef(def.into())))
                 },
-                ast::MacroRules(it) => {
+                ast::Macro(it) => {
                     let def = sema.to_def(&it)?;
                     Some(NameClass::Definition(Definition::Macro(def)))
                 },
diff --git a/crates/ide_db/src/symbol_index.rs b/crates/ide_db/src/symbol_index.rs
index 35e382b5cd..da427d6868 100644
--- a/crates/ide_db/src/symbol_index.rs
+++ b/crates/ide_db/src/symbol_index.rs
@@ -438,7 +438,7 @@ fn to_symbol(node: &SyntaxNode) -> Option<(SmolStr, SyntaxNodePtr, TextRange)> {
             ast::TypeAlias(it) => decl(it),
             ast::Const(it) => decl(it),
             ast::Static(it) => decl(it),
-            ast::MacroRules(it) => decl(it),
+            ast::Macro(it) => decl(it),
             ast::Union(it) => decl(it),
             _ => None,
         }
@@ -458,6 +458,7 @@ fn to_file_symbol(node: &SyntaxNode, file_id: FileId) -> Option<FileSymbol> {
             CONST => FileSymbolKind::Const,
             STATIC => FileSymbolKind::Static,
             MACRO_RULES => FileSymbolKind::Macro,
+            MACRO_DEF => FileSymbolKind::Macro,
             UNION => FileSymbolKind::Union,
             kind => unreachable!("{:?}", kind),
         },
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs
index 2772d73644..ae98dbd268 100644
--- a/crates/syntax/src/ast/node_ext.rs
+++ b/crates/syntax/src/ast/node_ext.rs
@@ -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),
diff --git a/crates/syntax/src/parsing/text_tree_sink.rs b/crates/syntax/src/parsing/text_tree_sink.rs
index bb10f20c97..1934204eab 100644
--- a/crates/syntax/src/parsing/text_tree_sink.rs
+++ b/crates/syntax/src/parsing/text_tree_sink.rs
@@ -147,8 +147,8 @@ fn n_attached_trivias<'a>(
     trivias: impl Iterator<Item = (SyntaxKind, &'a str)>,
 ) -> usize {
     match kind {
-        MACRO_CALL | MACRO_RULES | CONST | TYPE_ALIAS | STRUCT | UNION | ENUM | VARIANT | FN
-        | TRAIT | MODULE | RECORD_FIELD | STATIC | USE => {
+        MACRO_CALL | MACRO_RULES | MACRO_DEF | CONST | TYPE_ALIAS | STRUCT | UNION | ENUM
+        | VARIANT | FN | TRAIT | MODULE | RECORD_FIELD | STATIC | USE => {
             let mut res = 0;
             let mut trivias = trivias.enumerate().peekable();
 


Test patch:
diff --git a/crates/hir_def/src/nameres/tests/macros.rs b/crates/hir_def/src/nameres/tests/macros.rs
index 6d3cb8d7af..c37f915ab4 100644
--- a/crates/hir_def/src/nameres/tests/macros.rs
+++ b/crates/hir_def/src/nameres/tests/macros.rs
@@ -837,3 +837,21 @@ fn collects_derive_helpers() {
         _ => unreachable!(),
     }
 }
+
+#[test]
+fn resolve_macro_def() {
+    check(
+        r#"
+//- /lib.rs
+pub macro structs($($i:ident),*) {
+    $(struct $i { field: u32 } )*
+}
+structs!(Foo);
+"#,
+        expect![[r#"
+            crate
+            Foo: t
+            structs: m
+        "#]],
+    );
+}
diff --git a/crates/hir_ty/src/tests/macros.rs b/crates/hir_ty/src/tests/macros.rs
index 01935ec994..3eb01dbd0f 100644
--- a/crates/hir_ty/src/tests/macros.rs
+++ b/crates/hir_ty/src/tests/macros.rs
@@ -135,7 +135,88 @@ fn infer_path_qualified_macros_expanded() {
 }
 
 #[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 {
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
index 8b2dd3b70e..1eaa7b75bc 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
@@ -41,7 +41,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 <span class="keyword">mod</span> <span class="module declaration">inner</span> <span class="brace">{</span><span class="brace">}</span>
 
 <span class="attribute attribute">#</span><span class="attribute attribute">[</span><span class="function attribute">rustc_builtin_macro</span><span class="attribute attribute">]</span>
-<span class="keyword">macro</span> <span class="unresolved_reference declaration">Copy</span> <span class="brace">{</span><span class="brace">}</span>
+<span class="keyword">macro</span> <span class="macro declaration">Copy</span> <span class="brace">{</span><span class="brace">}</span>
 
 <span class="comment">// Needed for function consuming vs normal</span>
 <span class="keyword">pub</span> <span class="keyword">mod</span> <span class="module declaration">marker</span> <span class="brace">{</span>
@@ -158,6 +158,16 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <span class="parenthesis">(</span><span class="punctuation">$</span>type<span class="colon">:</span>ty<span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="parenthesis">(</span><span class="punctuation">$</span>type<span class="parenthesis">)</span>
 <span class="brace">}</span>
 
+<span class="keyword">macro</span> <span class="macro declaration">with_args</span><span class="parenthesis">(</span><span class="punctuation">$</span>i<span class="colon">:</span>ident<span class="parenthesis">)</span> <span class="brace">{</span>
+    <span class="punctuation">$</span>i
+<span class="brace">}</span>
+
+<span class="keyword">macro</span> <span class="macro declaration">without_args</span> <span class="brace">{</span>
+    <span class="parenthesis">(</span><span class="punctuation">$</span>i<span class="colon">:</span>ident<span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="brace">{</span>
+        <span class="punctuation">$</span>i
+    <span class="brace">}</span>
+<span class="brace">}</span>
+
 <span class="comment">// comment</span>
 <span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
     <span class="macro">println!</span><span class="parenthesis">(</span><span class="string_literal">"Hello, {}!"</span><span class="comma">,</span> <span class="numeric_literal">92</span><span class="parenthesis">)</span><span class="semicolon">;</span>
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index 7b2922b0d5..369ae0972b 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -129,6 +129,16 @@ macro_rules! keyword_frag {
     ($type:ty) => ($type)
 }
 
+macro with_args($i:ident) {
+    $i
+}
+
+macro without_args {
+    ($i:ident) => {
+        $i
+    }
+}
+
 // comment
 fn main() {
     println!("Hello, {}!", 92);